blob: 55c1034f557e35da457b05d7485473f8436f31e1 [file] [log] [blame]
Eric Andersendf82f612001-06-28 07:46:40 +00001/* vi: set sw=4 ts=4: */
2/*
3 * ash shell port for busybox
4 *
Denys Vlasenko73067272010-01-12 22:11:24 +01005 * This code is derived from software contributed to Berkeley by
6 * Kenneth Almquist.
7 *
8 * Original BSD copyright notice is retained at the end of this file.
9 *
Eric Andersendf82f612001-06-28 07:46:40 +000010 * Copyright (c) 1989, 1991, 1993, 1994
Eric Andersen2870d962001-07-02 17:27:21 +000011 * The Regents of the University of California. All rights reserved.
Eric Andersencb57d552001-06-28 07:25:16 +000012 *
"Vladimir N. Oleynik"ddc280e2005-12-15 12:01:49 +000013 * Copyright (c) 1997-2005 Herbert Xu <herbert@gondor.apana.org.au>
Eric Andersen81fe1232003-07-29 06:38:40 +000014 * was re-ported from NetBSD and debianized.
15 *
Denys Vlasenko0ef64bd2010-08-16 20:14:46 +020016 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
Eric Andersencb57d552001-06-28 07:25:16 +000017 */
Denys Vlasenko67e15292020-06-24 13:39:13 +020018//config:config SHELL_ASH
19//config: bool #hidden option
20//config: depends on !NOMMU
21//config:
Denys Vlasenko771f1992010-07-16 14:31:34 +020022//config:config ASH
Denys Vlasenkob097a842018-12-28 03:20:17 +010023//config: bool "ash (78 kb)"
Denys Vlasenko771f1992010-07-16 14:31:34 +020024//config: default y
25//config: depends on !NOMMU
Denys Vlasenko67e15292020-06-24 13:39:13 +020026//config: select SHELL_ASH
Denys Vlasenko771f1992010-07-16 14:31:34 +020027//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +020028//config: The most complete and most pedantically correct shell included with
29//config: busybox. This shell is actually a derivative of the Debian 'dash'
30//config: shell (by Herbert Xu), which was created by porting the 'ash' shell
31//config: (written by Kenneth Almquist) from NetBSD.
Denys Vlasenko771f1992010-07-16 14:31:34 +020032//config:
Kang-Che Sung6cd02942017-01-06 17:02:03 +010033//config:# ash options
34//config:# note: Don't remove !NOMMU part in the next line; it would break
35//config:# menuconfig's indenting.
Denys Vlasenko67e15292020-06-24 13:39:13 +020036//config:if !NOMMU && (SHELL_ASH || ASH || SH_IS_ASH || BASH_IS_ASH)
Kang-Che Sung6cd02942017-01-06 17:02:03 +010037//config:
Denys Vlasenko514b51d2016-10-01 14:33:08 +020038//config:config ASH_OPTIMIZE_FOR_SIZE
39//config: bool "Optimize for size instead of speed"
40//config: default y
Denys Vlasenko67e15292020-06-24 13:39:13 +020041//config: depends on SHELL_ASH
Denys Vlasenko514b51d2016-10-01 14:33:08 +020042//config:
43//config:config ASH_INTERNAL_GLOB
44//config: bool "Use internal glob() implementation"
Denys Vlasenko326edc32016-12-22 14:36:49 +010045//config: default y # Y is bigger, but because of uclibc glob() bug, let Y be default for now
Denys Vlasenko67e15292020-06-24 13:39:13 +020046//config: depends on SHELL_ASH
Denys Vlasenko514b51d2016-10-01 14:33:08 +020047//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +020048//config: Do not use glob() function from libc, use internal implementation.
49//config: Use this if you are getting "glob.h: No such file or directory"
50//config: or similar build errors.
51//config: Note that as of now (2017-01), uclibc and musl glob() both have bugs
52//config: which would break ash if you select N here.
Denys Vlasenkof5604222017-01-10 14:58:54 +010053//config:
54//config:config ASH_BASH_COMPAT
55//config: bool "bash-compatible extensions"
56//config: default y
Denys Vlasenko67e15292020-06-24 13:39:13 +020057//config: depends on SHELL_ASH
Denys Vlasenkof5604222017-01-10 14:58:54 +010058//config:
Denys Vlasenko01f7b9e2018-01-26 15:15:43 +010059//config:config ASH_BASH_SOURCE_CURDIR
60//config: bool "'source' and '.' builtins search current directory after $PATH"
61//config: default n # do not encourage non-standard behavior
Denys Vlasenko54c21112018-01-27 20:46:45 +010062//config: depends on ASH_BASH_COMPAT
Denys Vlasenko01f7b9e2018-01-26 15:15:43 +010063//config: help
64//config: This is not compliant with standards. Avoid if possible.
65//config:
William Pitcockd8fd88a2018-01-24 18:33:18 +010066//config:config ASH_BASH_NOT_FOUND_HOOK
67//config: bool "command_not_found_handle hook support"
68//config: default y
Denys Vlasenko54c21112018-01-27 20:46:45 +010069//config: depends on ASH_BASH_COMPAT
William Pitcockd8fd88a2018-01-24 18:33:18 +010070//config: help
71//config: Enable support for the 'command_not_found_handle' hook function,
72//config: from GNU bash, which allows for alternative command not found
73//config: handling.
74//config:
Denys Vlasenkof5604222017-01-10 14:58:54 +010075//config:config ASH_JOB_CONTROL
76//config: bool "Job control"
77//config: default y
Denys Vlasenko67e15292020-06-24 13:39:13 +020078//config: depends on SHELL_ASH
Denys Vlasenkof5604222017-01-10 14:58:54 +010079//config:
80//config:config ASH_ALIAS
81//config: bool "Alias support"
82//config: default y
Denys Vlasenko67e15292020-06-24 13:39:13 +020083//config: depends on SHELL_ASH
Denys Vlasenko514b51d2016-10-01 14:33:08 +020084//config:
85//config:config ASH_RANDOM_SUPPORT
86//config: bool "Pseudorandom generator and $RANDOM variable"
87//config: default y
Denys Vlasenko67e15292020-06-24 13:39:13 +020088//config: depends on SHELL_ASH
Denys Vlasenko514b51d2016-10-01 14:33:08 +020089//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +020090//config: Enable pseudorandom generator and dynamic variable "$RANDOM".
91//config: Each read of "$RANDOM" will generate a new pseudorandom value.
92//config: You can reset the generator by using a specified start value.
93//config: After "unset RANDOM" the generator will switch off and this
94//config: variable will no longer have special treatment.
Denys Vlasenko514b51d2016-10-01 14:33:08 +020095//config:
96//config:config ASH_EXPAND_PRMT
97//config: bool "Expand prompt string"
98//config: default y
Denys Vlasenko67e15292020-06-24 13:39:13 +020099//config: depends on SHELL_ASH
Denys Vlasenko514b51d2016-10-01 14:33:08 +0200100//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +0200101//config: $PS# may contain volatile content, such as backquote commands.
102//config: This option recreates the prompt string from the environment
103//config: variable each time it is displayed.
Denys Vlasenko514b51d2016-10-01 14:33:08 +0200104//config:
Denys Vlasenko046341e2011-02-04 17:53:59 +0100105//config:config ASH_IDLE_TIMEOUT
Denys Vlasenkof5604222017-01-10 14:58:54 +0100106//config: bool "Idle timeout variable $TMOUT"
Denys Vlasenko771f1992010-07-16 14:31:34 +0200107//config: default y
Denys Vlasenko67e15292020-06-24 13:39:13 +0200108//config: depends on SHELL_ASH
Denys Vlasenko771f1992010-07-16 14:31:34 +0200109//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +0200110//config: Enable bash-like auto-logout after $TMOUT seconds of idle time.
Denys Vlasenko771f1992010-07-16 14:31:34 +0200111//config:
Denys Vlasenkof5604222017-01-10 14:58:54 +0100112//config:config ASH_MAIL
113//config: bool "Check for new mail in interactive shell"
Denys Vlasenko771f1992010-07-16 14:31:34 +0200114//config: default y
Denys Vlasenko67e15292020-06-24 13:39:13 +0200115//config: depends on SHELL_ASH
Denys Vlasenko771f1992010-07-16 14:31:34 +0200116//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +0200117//config: Enable "check for new mail" function:
118//config: if set, $MAIL file and $MAILPATH list of files
119//config: are checked for mtime changes, and "you have mail"
120//config: message is printed if change is detected.
Denys Vlasenko771f1992010-07-16 14:31:34 +0200121//config:
Denys Vlasenko265062d2017-01-10 15:13:30 +0100122//config:config ASH_ECHO
Denys Vlasenkof5604222017-01-10 14:58:54 +0100123//config: bool "echo builtin"
Denys Vlasenko771f1992010-07-16 14:31:34 +0200124//config: default y
Denys Vlasenko67e15292020-06-24 13:39:13 +0200125//config: depends on SHELL_ASH
Denys Vlasenko771f1992010-07-16 14:31:34 +0200126//config:
Denys Vlasenko265062d2017-01-10 15:13:30 +0100127//config:config ASH_PRINTF
Denys Vlasenkof5604222017-01-10 14:58:54 +0100128//config: bool "printf builtin"
Denys Vlasenko771f1992010-07-16 14:31:34 +0200129//config: default y
Denys Vlasenko67e15292020-06-24 13:39:13 +0200130//config: depends on SHELL_ASH
Denys Vlasenko771f1992010-07-16 14:31:34 +0200131//config:
Denys Vlasenko265062d2017-01-10 15:13:30 +0100132//config:config ASH_TEST
Denys Vlasenkof5604222017-01-10 14:58:54 +0100133//config: bool "test builtin"
Denys Vlasenko771f1992010-07-16 14:31:34 +0200134//config: default y
Denys Vlasenko67e15292020-06-24 13:39:13 +0200135//config: depends on SHELL_ASH
Denys Vlasenko771f1992010-07-16 14:31:34 +0200136//config:
Denys Vlasenko2ec34962014-09-08 16:52:39 +0200137//config:config ASH_HELP
138//config: bool "help builtin"
139//config: default y
Denys Vlasenko67e15292020-06-24 13:39:13 +0200140//config: depends on SHELL_ASH
Denys Vlasenkof5604222017-01-10 14:58:54 +0100141//config:
142//config:config ASH_GETOPTS
143//config: bool "getopts builtin"
144//config: default y
Denys Vlasenko67e15292020-06-24 13:39:13 +0200145//config: depends on SHELL_ASH
Denys Vlasenko2ec34962014-09-08 16:52:39 +0200146//config:
Denys Vlasenko771f1992010-07-16 14:31:34 +0200147//config:config ASH_CMDCMD
Denys Vlasenkof5604222017-01-10 14:58:54 +0100148//config: bool "command builtin"
Denys Vlasenko771f1992010-07-16 14:31:34 +0200149//config: default y
Denys Vlasenko67e15292020-06-24 13:39:13 +0200150//config: depends on SHELL_ASH
Denys Vlasenko771f1992010-07-16 14:31:34 +0200151//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +0200152//config: Enable support for the 'command' builtin, which allows
153//config: you to run the specified command or builtin,
154//config: even when there is a function with the same name.
Kang-Che Sung6cd02942017-01-06 17:02:03 +0100155//config:
156//config:endif # ash options
Denys Vlasenko771f1992010-07-16 14:31:34 +0200157
Denys Vlasenko20704f02011-03-23 17:59:27 +0100158//applet:IF_ASH(APPLET(ash, BB_DIR_BIN, BB_SUID_DROP))
Denys Vlasenko205d48e2017-01-29 14:57:33 +0100159// APPLET_ODDNAME:name main location suid_type help
160//applet:IF_SH_IS_ASH( APPLET_ODDNAME(sh, ash, BB_DIR_BIN, BB_SUID_DROP, ash))
Denys Vlasenko0b883582016-12-23 16:49:07 +0100161//applet:IF_BASH_IS_ASH(APPLET_ODDNAME(bash, ash, BB_DIR_BIN, BB_SUID_DROP, ash))
Denys Vlasenko20704f02011-03-23 17:59:27 +0100162
Denys Vlasenko67e15292020-06-24 13:39:13 +0200163//kbuild:lib-$(CONFIG_SHELL_ASH) += ash.o ash_ptr_hack.o shell_common.o
Denys Vlasenko20704f02011-03-23 17:59:27 +0100164//kbuild:lib-$(CONFIG_ASH_RANDOM_SUPPORT) += random.o
165
Denys Vlasenko67047462016-12-22 15:21:58 +0100166/*
Denys Vlasenko7d4aec02017-01-11 14:00:38 +0100167 * DEBUG=1 to compile in debugging ('set -o debug' turns on)
168 * DEBUG=2 to compile in and turn on debugging.
169 * When debugging is on ("set -o debug" was executed, or DEBUG=2),
170 * debugging info is written to ./trace, quit signal generates core dump.
Denys Vlasenko67047462016-12-22 15:21:58 +0100171 */
172#define DEBUG 0
173/* Tweak debug output verbosity here */
174#define DEBUG_TIME 0
175#define DEBUG_PID 1
176#define DEBUG_SIG 1
177#define DEBUG_INTONOFF 0
178
179#define PROFILE 0
180
181#define JOBS ENABLE_ASH_JOB_CONTROL
182
Denys Vlasenko67047462016-12-22 15:21:58 +0100183#include <fnmatch.h>
184#include <sys/times.h>
185#include <sys/utsname.h> /* for setting $HOSTNAME */
Denys Vlasenko67047462016-12-22 15:21:58 +0100186#include "busybox.h" /* for applet_names */
Ron Yorston71df2d32018-11-27 14:34:25 +0000187#if ENABLE_FEATURE_SH_EMBEDDED_SCRIPTS
Denys Vlasenko4f2ef4a2018-11-01 09:53:25 +0100188# include "embedded_scripts.h"
189#else
190# define NUM_SCRIPTS 0
191#endif
Denys Vlasenko67047462016-12-22 15:21:58 +0100192
Denys Vlasenko7d4aec02017-01-11 14:00:38 +0100193/* So far, all bash compat is controlled by one config option */
194/* Separate defines document which part of code implements what */
195/* function keyword */
196#define BASH_FUNCTION ENABLE_ASH_BASH_COMPAT
197#define IF_BASH_FUNCTION IF_ASH_BASH_COMPAT
198/* &>file */
199#define BASH_REDIR_OUTPUT ENABLE_ASH_BASH_COMPAT
200#define IF_BASH_REDIR_OUTPUT IF_ASH_BASH_COMPAT
201/* $'...' */
202#define BASH_DOLLAR_SQUOTE ENABLE_ASH_BASH_COMPAT
203#define IF_BASH_DOLLAR_SQUOTE IF_ASH_BASH_COMPAT
204#define BASH_PATTERN_SUBST ENABLE_ASH_BASH_COMPAT
205#define IF_BASH_PATTERN_SUBST IF_ASH_BASH_COMPAT
206#define BASH_SUBSTR ENABLE_ASH_BASH_COMPAT
207#define IF_BASH_SUBSTR IF_ASH_BASH_COMPAT
Denys Vlasenko3632cb12018-04-10 15:25:41 +0200208/* BASH_TEST2: [[ EXPR ]]
209 * Status of [[ support:
Denys Vlasenkod2241f52020-10-31 03:34:07 +0100210 * && and || work as they should
211 * = is glob match operator, not equality operator: STR = GLOB
Denys Vlasenkod2241f52020-10-31 03:34:07 +0100212 * == same as =
Denys Vlasenkoa7c06532020-10-31 04:32:34 +0100213 * =~ is regex match operator: STR =~ REGEX
Denys Vlasenko3632cb12018-04-10 15:25:41 +0200214 * TODO:
215 * singleword+noglob expansion:
216 * v='a b'; [[ $v = 'a b' ]]; echo 0:$?
Denys Vlasenko89e9d552018-04-11 01:15:33 +0200217 * [[ /bin/n* ]]; echo 0:$?
Denys Vlasenko3632cb12018-04-10 15:25:41 +0200218 * quoting needs to be considered (-f is an operator, "-f" and ""-f are not; etc)
Denys Vlasenkoa7c06532020-10-31 04:32:34 +0100219 * ( ) < > should not have special meaning (IOW: should not require quoting)
220 * in word = GLOB, quoting should be significant on char-by-char basis: a*cd"*"
Denys Vlasenko3632cb12018-04-10 15:25:41 +0200221 */
Denys Vlasenko7d4aec02017-01-11 14:00:38 +0100222#define BASH_TEST2 (ENABLE_ASH_BASH_COMPAT * ENABLE_ASH_TEST)
223#define BASH_SOURCE ENABLE_ASH_BASH_COMPAT
224#define BASH_PIPEFAIL ENABLE_ASH_BASH_COMPAT
225#define BASH_HOSTNAME_VAR ENABLE_ASH_BASH_COMPAT
Ron Yorston1d371862019-04-15 10:52:05 +0100226#define BASH_EPOCH_VARS ENABLE_ASH_BASH_COMPAT
Denys Vlasenko7d4aec02017-01-11 14:00:38 +0100227#define BASH_SHLVL_VAR ENABLE_ASH_BASH_COMPAT
Denys Vlasenkof8cdc7a2017-08-04 15:24:49 +0200228#define BASH_XTRACEFD ENABLE_ASH_BASH_COMPAT
Johannes Schindelin3bef5d82017-08-08 16:46:39 +0200229#define BASH_READ_D ENABLE_ASH_BASH_COMPAT
230#define IF_BASH_READ_D IF_ASH_BASH_COMPAT
Ron Yorstone48559e2019-03-31 09:27:09 +0100231#define BASH_WAIT_N ENABLE_ASH_BASH_COMPAT
Ron Yorstona1b0d382020-07-23 08:32:27 +0100232/* <(...) and >(...) */
233#if HAVE_DEV_FD
234# define BASH_PROCESS_SUBST ENABLE_ASH_BASH_COMPAT
235# define IF_BASH_PROCESS_SUBST IF_ASH_BASH_COMPAT
236#else
237# define BASH_PROCESS_SUBST 0
238# define IF_BASH_PROCESS_SUBST(...)
239#endif
Denys Vlasenko7d4aec02017-01-11 14:00:38 +0100240
Denys Vlasenko67047462016-12-22 15:21:58 +0100241#if defined(__ANDROID_API__) && __ANDROID_API__ <= 24
242/* Bionic at least up to version 24 has no glob() */
243# undef ENABLE_ASH_INTERNAL_GLOB
244# define ENABLE_ASH_INTERNAL_GLOB 1
245#endif
246
247#if !ENABLE_ASH_INTERNAL_GLOB && defined(__UCLIBC__)
248# error uClibc glob() is buggy, use ASH_INTERNAL_GLOB.
249# error The bug is: for "$PWD"/<pattern> ash will escape e.g. dashes in "$PWD"
250# error with backslash, even ones which do not need to be: "/a-b" -> "/a\-b"
251# error glob() should unbackslash them and match. uClibc does not unbackslash,
252# error fails to match dirname, subsequently not expanding <pattern> in it.
253// Testcase:
254// if (glob("/etc/polkit\\-1", 0, NULL, &pglob)) - this returns 0 on uclibc, no bug
255// if (glob("/etc/polkit\\-1/*", 0, NULL, &pglob)) printf("uclibc bug!\n");
256#endif
257
258#if !ENABLE_ASH_INTERNAL_GLOB
259# include <glob.h>
260#endif
261
262#include "unicode.h"
263#include "shell_common.h"
Denys Vlasenko0b883582016-12-23 16:49:07 +0100264#if ENABLE_FEATURE_SH_MATH
Denys Vlasenko67047462016-12-22 15:21:58 +0100265# include "math.h"
Denys Vlasenkocf3a7962017-07-26 14:38:19 +0200266#else
267typedef long arith_t;
268# define ARITH_FMT "%ld"
Denys Vlasenko67047462016-12-22 15:21:58 +0100269#endif
270#if ENABLE_ASH_RANDOM_SUPPORT
271# include "random.h"
272#else
273# define CLEAR_RANDOM_T(rnd) ((void)0)
274#endif
275
276#include "NUM_APPLETS.h"
277#if NUM_APPLETS == 1
278/* STANDALONE does not make sense, and won't compile */
279# undef CONFIG_FEATURE_SH_STANDALONE
280# undef ENABLE_FEATURE_SH_STANDALONE
281# undef IF_FEATURE_SH_STANDALONE
282# undef IF_NOT_FEATURE_SH_STANDALONE
283# define ENABLE_FEATURE_SH_STANDALONE 0
284# define IF_FEATURE_SH_STANDALONE(...)
285# define IF_NOT_FEATURE_SH_STANDALONE(...) __VA_ARGS__
286#endif
287
Denys Vlasenko9acd63c2018-03-28 18:35:07 +0200288#ifndef F_DUPFD_CLOEXEC
289# define F_DUPFD_CLOEXEC F_DUPFD
290#endif
Denys Vlasenko60fb98e2018-03-30 22:15:14 +0200291#ifndef O_CLOEXEC
292# define O_CLOEXEC 0
293#endif
Denys Vlasenko67047462016-12-22 15:21:58 +0100294#ifndef PIPE_BUF
295# define PIPE_BUF 4096 /* amount of buffering in a pipe */
296#endif
297
Denys Vlasenko48cb9832021-09-08 09:52:04 +0200298#ifndef unlikely
299# define unlikely(cond) (cond)
300#endif
301
Denys Vlasenko67047462016-12-22 15:21:58 +0100302#if !BB_MMU
303# error "Do not even bother, ash will not run on NOMMU machine"
304#endif
305
Denis Vlasenko01631112007-12-16 17:20:38 +0000306/* ============ Hash table sizes. Configurable. */
307
308#define VTABSIZE 39
309#define ATABSIZE 39
310#define CMDTABLESIZE 31 /* should be prime */
311
312
Denis Vlasenkob012b102007-02-19 22:43:01 +0000313/* ============ Shell options */
314
Denys Vlasenko4e039ba2021-01-04 03:50:38 +0100315/* If you add/change options hare, update --help text too */
Denys Vlasenkoca466f32022-02-06 19:53:10 +0100316static const char *const optletters_optnames[] ALIGN_PTR = {
Denis Vlasenkob012b102007-02-19 22:43:01 +0000317 "e" "errexit",
318 "f" "noglob",
Denys Vlasenko4e039ba2021-01-04 03:50:38 +0100319/* bash has '-o ignoreeof', but no short synonym -I for it */
320/* (in bash, set -I disables invisible variables (what's that?)) */
Denis Vlasenkob012b102007-02-19 22:43:01 +0000321 "I" "ignoreeof",
Denys Vlasenkof3634582019-06-03 12:21:04 +0200322/* The below allowed this invocation:
Denys Vlasenko897475a2019-06-01 16:35:09 +0200323 * ash -c 'set -i; echo $-; sleep 5; echo $-'
324 * to be ^C-ed and get to interactive ash prompt.
Denys Vlasenkof3634582019-06-03 12:21:04 +0200325 * bash does not support such "set -i".
326 * In our code, this is denoted by empty long name:
Denys Vlasenko897475a2019-06-01 16:35:09 +0200327 */
Denys Vlasenkof3634582019-06-03 12:21:04 +0200328 "i" "",
Denys Vlasenko4e039ba2021-01-04 03:50:38 +0100329/* (removing "i" altogether would remove it from "$-", not good) */
Denis Vlasenkob012b102007-02-19 22:43:01 +0000330 "m" "monitor",
331 "n" "noexec",
Denys Vlasenko4e039ba2021-01-04 03:50:38 +0100332/* Ditto: bash has no "set -s", "set -c" */
Denys Vlasenkof3634582019-06-03 12:21:04 +0200333 "s" "",
334 "c" "",
Denis Vlasenkob012b102007-02-19 22:43:01 +0000335 "x" "xtrace",
336 "v" "verbose",
337 "C" "noclobber",
338 "a" "allexport",
339 "b" "notify",
340 "u" "nounset",
Roberto A. Fogliettae0bf3df2021-09-07 01:19:31 +0200341 "E" "errtrace",
Denys Vlasenkoe9ac32a2009-12-05 02:01:25 +0100342 "\0" "vi"
Denys Vlasenko7d4aec02017-01-11 14:00:38 +0100343#if BASH_PIPEFAIL
Denys Vlasenkoe9ac32a2009-12-05 02:01:25 +0100344 ,"\0" "pipefail"
Michael Abbott359da5e2009-12-04 23:03:29 +0100345#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +0000346#if DEBUG
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000347 ,"\0" "nolog"
348 ,"\0" "debug"
Denis Vlasenkob012b102007-02-19 22:43:01 +0000349#endif
350};
Denys Vlasenko897475a2019-06-01 16:35:09 +0200351//bash 4.4.23 also has these opts (with these defaults):
352//braceexpand on
353//emacs on
354//errtrace off
355//functrace off
356//hashall on
357//histexpand off
358//history on
359//interactive-comments on
360//keyword off
361//onecmd off
362//physical off
363//posix off
364//privileged off
Denis Vlasenkob012b102007-02-19 22:43:01 +0000365
Denys Vlasenko285ad152009-12-04 23:02:27 +0100366#define optletters(n) optletters_optnames[n][0]
367#define optnames(n) (optletters_optnames[n] + 1)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000368
Denis Vlasenko80b8b392007-06-25 10:55:35 +0000369enum { NOPTS = ARRAY_SIZE(optletters_optnames) };
Denis Vlasenkob012b102007-02-19 22:43:01 +0000370
Eric Andersenc470f442003-07-28 09:56:35 +0000371
Denis Vlasenkob012b102007-02-19 22:43:01 +0000372/* ============ Misc data */
Eric Andersenc470f442003-07-28 09:56:35 +0000373
Denys Vlasenkoea8b2522010-06-02 12:57:26 +0200374#define msg_illnum "Illegal number: %s"
Denis Vlasenkoaa744452007-02-23 01:04:22 +0000375
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +0000376/*
Eric Andersenc470f442003-07-28 09:56:35 +0000377 * We enclose jmp_buf in a structure so that we can declare pointers to
378 * jump locations. The global variable handler contains the location to
Denis Vlasenkof1733952009-03-19 23:21:55 +0000379 * jump to when an exception occurs, and the global variable exception_type
Eric Andersenaff114c2004-04-14 17:51:38 +0000380 * contains a code identifying the exception. To implement nested
Eric Andersenc470f442003-07-28 09:56:35 +0000381 * exception handlers, the user should save the value of handler on entry
382 * to an inner scope, set handler to point to a jmploc structure for the
383 * inner scope, and restore handler on exit from the scope.
384 */
Eric Andersenc470f442003-07-28 09:56:35 +0000385struct jmploc {
386 jmp_buf loc;
387};
Denis Vlasenko01631112007-12-16 17:20:38 +0000388
389struct globals_misc {
Denys Vlasenko4d12e942016-10-01 16:03:11 +0200390 uint8_t exitstatus; /* exit status of last command */
391 uint8_t back_exitstatus;/* exit status of backquoted command */
392 smallint job_warning; /* user was warned about stopped jobs (can be 2, 1 or 0). */
Denys Vlasenkoeb607772021-09-09 16:26:41 +0200393 smallint inps4; /* Prevent PS4 nesting. */
Denys Vlasenko4ccddc82020-02-14 17:27:18 +0100394 int savestatus; /* exit status of last command outside traps */
Denys Vlasenko4d12e942016-10-01 16:03:11 +0200395 int rootpid; /* pid of main shell */
Denis Vlasenko01631112007-12-16 17:20:38 +0000396 /* shell level: 0 for the main shell, 1 for its children, and so on */
397 int shlvl;
398#define rootshell (!shlvl)
Denys Vlasenko675d24a2018-01-27 22:02:05 +0100399 int errlinno;
400
Denis Vlasenko01631112007-12-16 17:20:38 +0000401 char *minusc; /* argument to -c option */
402
403 char *curdir; // = nullstr; /* current working directory */
404 char *physdir; // = nullstr; /* physical working directory */
405
406 char *arg0; /* value of $0 */
407
408 struct jmploc *exception_handler;
Denis Vlasenko991a1da2008-02-10 19:02:53 +0000409
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200410 volatile int suppress_int; /* counter */
411 volatile /*sig_atomic_t*/ smallint pending_int; /* 1 = got SIGINT */
Denys Vlasenko458c1f22016-10-27 23:51:19 +0200412 volatile /*sig_atomic_t*/ smallint got_sigchld; /* 1 = got SIGCHLD */
Denys Vlasenko8f7b0242016-10-28 17:16:11 +0200413 volatile /*sig_atomic_t*/ smallint pending_sig; /* last pending signal */
Denys Vlasenkof977e002020-02-20 16:54:29 +0100414 smallint exception_type; /* kind of exception: */
Eric Andersenc470f442003-07-28 09:56:35 +0000415#define EXINT 0 /* SIGINT received */
416#define EXERROR 1 /* a generic error */
Denys Vlasenkof977e002020-02-20 16:54:29 +0100417#define EXEND 3 /* exit the shell */
418#define EXEXIT 4 /* exit the shell via exitcmd */
Eric Andersen2870d962001-07-02 17:27:21 +0000419
Denis Vlasenkob07a4962008-06-22 13:16:23 +0000420 char nullstr[1]; /* zero length string */
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000421
422 char optlist[NOPTS];
423#define eflag optlist[0]
424#define fflag optlist[1]
425#define Iflag optlist[2]
426#define iflag optlist[3]
427#define mflag optlist[4]
428#define nflag optlist[5]
429#define sflag optlist[6]
Denys Vlasenkof3634582019-06-03 12:21:04 +0200430#define cflag optlist[7]
431#define xflag optlist[8]
432#define vflag optlist[9]
433#define Cflag optlist[10]
434#define aflag optlist[11]
435#define bflag optlist[12]
436#define uflag optlist[13]
Roberto A. Fogliettae0bf3df2021-09-07 01:19:31 +0200437#define Eflag optlist[14]
438#define viflag optlist[15]
Denys Vlasenko7d4aec02017-01-11 14:00:38 +0100439#if BASH_PIPEFAIL
Roberto A. Fogliettae0bf3df2021-09-07 01:19:31 +0200440# define pipefail optlist[16]
Michael Abbott359da5e2009-12-04 23:03:29 +0100441#else
442# define pipefail 0
443#endif
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000444#if DEBUG
Roberto A. Fogliettae0bf3df2021-09-07 01:19:31 +0200445# define nolog optlist[16 + BASH_PIPEFAIL]
446# define debug optlist[17 + BASH_PIPEFAIL]
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000447#endif
448
449 /* trap handler commands */
Denis Vlasenko01631112007-12-16 17:20:38 +0000450 /*
451 * Sigmode records the current value of the signal handlers for the various
452 * modes. A value of zero means that the current handler is not known.
Denis Vlasenkof8535cc2008-12-03 10:36:26 +0000453 * S_HARD_IGN indicates that the signal was ignored on entry to the shell.
Denis Vlasenko01631112007-12-16 17:20:38 +0000454 */
455 char sigmode[NSIG - 1];
Denis Vlasenkof8535cc2008-12-03 10:36:26 +0000456#define S_DFL 1 /* default signal handling (SIG_DFL) */
457#define S_CATCH 2 /* signal is caught */
458#define S_IGN 3 /* signal is ignored (SIG_IGN) */
Denys Vlasenko0f14f412017-08-06 20:06:19 +0200459#define S_HARD_IGN 4 /* signal is ignored permanently (it was SIG_IGN on entry to shell) */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000460
Denis Vlasenko01631112007-12-16 17:20:38 +0000461 /* indicates specified signal received */
Denis Vlasenko4b875702009-03-19 13:30:04 +0000462 uint8_t gotsig[NSIG - 1]; /* offset by 1: "signal" 0 is meaningless */
Denys Vlasenko238bf182010-05-18 15:49:07 +0200463 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 +0200464 char *trap[NSIG + 1];
465/* trap[0] is EXIT trap, trap[NTRAP_ERR] is ERR trap, other trap[i] are signal traps */
466#define NTRAP_ERR NSIG
467#define NTRAP_LAST NSIG
468
Denys Vlasenko21d87d42009-09-25 00:06:51 +0200469 char **trap_ptr; /* used only by "trap hack" */
Denis Vlasenko448d30e2008-06-27 00:24:11 +0000470
471 /* Rarely referenced stuff */
472#if ENABLE_ASH_RANDOM_SUPPORT
Denys Vlasenko3ea2e822009-10-09 20:59:04 +0200473 random_t random_gen;
Denis Vlasenko448d30e2008-06-27 00:24:11 +0000474#endif
475 pid_t backgndpid; /* pid of last background process */
Denis Vlasenko01631112007-12-16 17:20:38 +0000476};
Denys Vlasenko6f9442f2018-01-28 20:41:23 +0100477extern struct globals_misc *BB_GLOBAL_CONST ash_ptr_to_globals_misc;
Denis Vlasenko574f2f42008-02-27 18:41:59 +0000478#define G_misc (*ash_ptr_to_globals_misc)
Denys Vlasenko4d12e942016-10-01 16:03:11 +0200479#define exitstatus (G_misc.exitstatus )
480#define back_exitstatus (G_misc.back_exitstatus )
481#define job_warning (G_misc.job_warning)
Denys Vlasenkoeb607772021-09-09 16:26:41 +0200482#define inps4 (G_misc.inps4 )
Denys Vlasenko4ccddc82020-02-14 17:27:18 +0100483#define savestatus (G_misc.savestatus )
Denis Vlasenko26bc57d2008-06-27 00:29:34 +0000484#define rootpid (G_misc.rootpid )
485#define shlvl (G_misc.shlvl )
Denys Vlasenko675d24a2018-01-27 22:02:05 +0100486#define errlinno (G_misc.errlinno )
Denis Vlasenko26bc57d2008-06-27 00:29:34 +0000487#define minusc (G_misc.minusc )
488#define curdir (G_misc.curdir )
489#define physdir (G_misc.physdir )
490#define arg0 (G_misc.arg0 )
Denis Vlasenko01631112007-12-16 17:20:38 +0000491#define exception_handler (G_misc.exception_handler)
Denis Vlasenko7f88e342009-03-19 03:36:18 +0000492#define exception_type (G_misc.exception_type )
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200493#define suppress_int (G_misc.suppress_int )
494#define pending_int (G_misc.pending_int )
Denys Vlasenko458c1f22016-10-27 23:51:19 +0200495#define got_sigchld (G_misc.got_sigchld )
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200496#define pending_sig (G_misc.pending_sig )
Denis Vlasenko26bc57d2008-06-27 00:29:34 +0000497#define nullstr (G_misc.nullstr )
498#define optlist (G_misc.optlist )
499#define sigmode (G_misc.sigmode )
500#define gotsig (G_misc.gotsig )
Denys Vlasenko238bf182010-05-18 15:49:07 +0200501#define may_have_traps (G_misc.may_have_traps )
Denis Vlasenko26bc57d2008-06-27 00:29:34 +0000502#define trap (G_misc.trap )
Denys Vlasenko21d87d42009-09-25 00:06:51 +0200503#define trap_ptr (G_misc.trap_ptr )
Denys Vlasenko3ea2e822009-10-09 20:59:04 +0200504#define random_gen (G_misc.random_gen )
Denis Vlasenko448d30e2008-06-27 00:24:11 +0000505#define backgndpid (G_misc.backgndpid )
Denis Vlasenko01631112007-12-16 17:20:38 +0000506#define INIT_G_misc() do { \
YU Jincheng5156b242021-10-10 02:19:51 +0800507 XZALLOC_CONST_PTR(&ash_ptr_to_globals_misc, sizeof(G_misc)); \
Denys Vlasenko4ccddc82020-02-14 17:27:18 +0100508 savestatus = -1; \
Denis Vlasenko01631112007-12-16 17:20:38 +0000509 curdir = nullstr; \
510 physdir = nullstr; \
Denys Vlasenko21d87d42009-09-25 00:06:51 +0200511 trap_ptr = trap; \
Denis Vlasenko01631112007-12-16 17:20:38 +0000512} while (0)
513
514
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000515/* ============ DEBUG */
516#if DEBUG
517static void trace_printf(const char *fmt, ...);
518static void trace_vprintf(const char *fmt, va_list va);
519# define TRACE(param) trace_printf param
520# define TRACEV(param) trace_vprintf param
Denis Vlasenko1bb3d7e2009-03-20 07:45:36 +0000521# define close(fd) do { \
522 int dfd = (fd); \
Denis Vlasenkob9e70dd2009-03-20 01:24:08 +0000523 if (close(dfd) < 0) \
Denys Vlasenko883cea42009-07-11 15:31:59 +0200524 bb_error_msg("bug on %d: closing %d(0x%x)", \
Denis Vlasenko1bb3d7e2009-03-20 07:45:36 +0000525 __LINE__, dfd, dfd); \
Denis Vlasenkob9e70dd2009-03-20 01:24:08 +0000526} while (0)
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000527#else
528# define TRACE(param)
529# define TRACEV(param)
530#endif
531
532
Denis Vlasenko559691a2008-10-05 18:39:31 +0000533/* ============ Utility functions */
Denys Vlasenko1961aea2013-02-26 00:36:53 +0100534#define is_name(c) ((c) == '_' || isalpha((unsigned char)(c)))
535#define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c)))
536
Denys Vlasenko37dc08b2016-10-02 04:38:07 +0200537static int
538isdigit_str9(const char *str)
Denis Vlasenko559691a2008-10-05 18:39:31 +0000539{
540 int maxlen = 9 + 1; /* max 9 digits: 999999999 */
541 while (--maxlen && isdigit(*str))
542 str++;
543 return (*str == '\0');
544}
Denis Vlasenko01631112007-12-16 17:20:38 +0000545
Denys Vlasenko37dc08b2016-10-02 04:38:07 +0200546static const char *
547var_end(const char *var)
Denys Vlasenko8837c5d2010-06-02 12:56:18 +0200548{
549 while (*var)
550 if (*var++ == '=')
551 break;
552 return var;
553}
554
Denis Vlasenko559691a2008-10-05 18:39:31 +0000555
Denys Vlasenko2124c0e2020-12-19 14:33:02 +0100556/* ============ Parser data */
557
558/*
559 * ash_vmsg() needs parsefile->fd, hence parsefile definition is moved up.
560 */
561struct strlist {
562 struct strlist *next;
563 char *text;
564};
565
566struct alias;
567
568struct strpush {
569 struct strpush *prev; /* preceding string on stack */
570 char *prev_string;
571 int prev_left_in_line;
572#if ENABLE_ASH_ALIAS
573 struct alias *ap; /* if push was associated with an alias */
574#endif
575 char *string; /* remember the string since it may change */
576
Denys Vlasenko48cb9832021-09-08 09:52:04 +0200577 /* Delay freeing so we can stop nested aliases. */
578 struct strpush *spfree;
579
Denys Vlasenko2124c0e2020-12-19 14:33:02 +0100580 /* Remember last two characters for pungetc. */
581 int lastc[2];
582
583 /* Number of outstanding calls to pungetc. */
584 int unget;
585};
586
587/*
588 * The parsefile structure pointed to by the global variable parsefile
589 * contains information about the current file being read.
590 */
591struct parsefile {
592 struct parsefile *prev; /* preceding file on stack */
593 int linno; /* current line */
594 int pf_fd; /* file descriptor (or -1 if string) */
595 int left_in_line; /* number of chars left in this line */
596 int left_in_buffer; /* number of chars left in this buffer past the line */
597 char *next_to_pgetc; /* next char in buffer */
598 char *buf; /* input buffer */
599 struct strpush *strpush; /* for pushing strings at this level */
600 struct strpush basestrpush; /* so pushing one is fast */
601
Denys Vlasenko48cb9832021-09-08 09:52:04 +0200602 /* Delay freeing so we can stop nested aliases. */
603 struct strpush *spfree;
604
Denys Vlasenko2124c0e2020-12-19 14:33:02 +0100605 /* Remember last two characters for pungetc. */
606 int lastc[2];
607
608 /* Number of outstanding calls to pungetc. */
609 int unget;
610};
611
612static struct parsefile basepf; /* top level input file */
613static struct parsefile *g_parsefile = &basepf; /* current input file */
614static char *commandname; /* currently executing command */
615
616
Denis Vlasenko559691a2008-10-05 18:39:31 +0000617/* ============ Interrupts / exceptions */
Denys Vlasenko66c5b122011-02-08 05:07:02 +0100618
619static void exitshell(void) NORETURN;
620
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000621/*
Eric Andersen2870d962001-07-02 17:27:21 +0000622 * These macros allow the user to suspend the handling of interrupt signals
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +0000623 * over a period of time. This is similar to SIGHOLD or to sigblock, but
Eric Andersen2870d962001-07-02 17:27:21 +0000624 * much more efficient and portable. (But hacking the kernel is so much
625 * more fun than worrying about efficiency and portability. :-))
626 */
Denys Vlasenko06b11492016-11-04 16:43:18 +0100627#if DEBUG_INTONOFF
628# define INT_OFF do { \
629 TRACE(("%s:%d INT_OFF(%d)\n", __func__, __LINE__, suppress_int)); \
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200630 suppress_int++; \
Denys Vlasenkode892052016-10-02 01:49:13 +0200631 barrier(); \
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000632} while (0)
Denys Vlasenko06b11492016-11-04 16:43:18 +0100633#else
634# define INT_OFF do { \
635 suppress_int++; \
636 barrier(); \
637} while (0)
638#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +0000639
640/*
641 * Called to raise an exception. Since C doesn't include exceptions, we
642 * just do a longjmp to the exception handler. The type of exception is
Denis Vlasenko4b875702009-03-19 13:30:04 +0000643 * stored in the global variable "exception_type".
Denis Vlasenkob012b102007-02-19 22:43:01 +0000644 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000645static void raise_exception(int) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000646static void
647raise_exception(int e)
648{
649#if DEBUG
Denis Vlasenko2da584f2007-02-19 22:44:05 +0000650 if (exception_handler == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000651 abort();
652#endif
653 INT_OFF;
Denis Vlasenko7f88e342009-03-19 03:36:18 +0000654 exception_type = e;
Denis Vlasenko2da584f2007-02-19 22:44:05 +0000655 longjmp(exception_handler->loc, 1);
Denis Vlasenkob012b102007-02-19 22:43:01 +0000656}
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000657#if DEBUG
658#define raise_exception(e) do { \
659 TRACE(("raising exception %d on line %d\n", (e), __LINE__)); \
660 raise_exception(e); \
661} while (0)
662#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +0000663
664/*
Denys Vlasenkof37e1152016-10-07 03:17:28 +0200665 * Called when a SIGINT is received. (If the user specifies
Denis Vlasenkob012b102007-02-19 22:43:01 +0000666 * that SIGINT is to be trapped or ignored using the trap builtin, then
Denys Vlasenkoa2775062022-01-16 23:54:46 +0100667 * this routine is not called.) suppress_int is nonzero when interrupts
Denis Vlasenkob012b102007-02-19 22:43:01 +0000668 * are held using the INT_OFF macro. (The test for iflag is just
669 * defensive programming.)
670 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000671static void raise_interrupt(void) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000672static void
673raise_interrupt(void)
674{
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200675 pending_int = 0;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +0000676 /* Signal is not automatically unmasked after it is raised,
677 * do it ourself - unmask all signals */
Denis Vlasenko3f165fa2008-03-17 08:29:08 +0000678 sigprocmask_allsigs(SIG_UNBLOCK);
Denys Vlasenko238bf182010-05-18 15:49:07 +0200679 /* pending_sig = 0; - now done in signal_handler() */
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000680
Denys Vlasenkoc0663c72016-10-27 21:09:01 +0200681 if (!(rootshell && iflag)) {
682 /* Kill ourself with SIGINT */
683 signal(SIGINT, SIG_DFL);
684 raise(SIGINT);
Denis Vlasenkob012b102007-02-19 22:43:01 +0000685 }
Denys Vlasenko4d12e942016-10-01 16:03:11 +0200686 /* bash: ^C even on empty command line sets $? */
687 exitstatus = SIGINT + 128;
Denys Vlasenkoc0663c72016-10-27 21:09:01 +0200688 raise_exception(EXINT);
Denis Vlasenkob012b102007-02-19 22:43:01 +0000689 /* NOTREACHED */
690}
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000691#if DEBUG
692#define raise_interrupt() do { \
693 TRACE(("raising interrupt on line %d\n", __LINE__)); \
694 raise_interrupt(); \
695} while (0)
696#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +0000697
Denys Vlasenkoa2775062022-01-16 23:54:46 +0100698static IF_NOT_ASH_OPTIMIZE_FOR_SIZE(inline) void
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000699int_on(void)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000700{
Denys Vlasenkode892052016-10-02 01:49:13 +0200701 barrier();
Denys Vlasenkoa2775062022-01-16 23:54:46 +0100702 if (--suppress_int == 0 && pending_int)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000703 raise_interrupt();
Denis Vlasenkob012b102007-02-19 22:43:01 +0000704}
Denys Vlasenko06b11492016-11-04 16:43:18 +0100705#if DEBUG_INTONOFF
706# define INT_ON do { \
707 TRACE(("%s:%d INT_ON(%d)\n", __func__, __LINE__, suppress_int-1)); \
708 int_on(); \
709} while (0)
710#else
711# define INT_ON int_on()
712#endif
Denys Vlasenkoa2775062022-01-16 23:54:46 +0100713static IF_NOT_ASH_OPTIMIZE_FOR_SIZE(inline) void
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000714force_int_on(void)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000715{
Denys Vlasenkode892052016-10-02 01:49:13 +0200716 barrier();
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200717 suppress_int = 0;
718 if (pending_int)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000719 raise_interrupt();
720}
721#define FORCE_INT_ON force_int_on()
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000722
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200723#define SAVE_INT(v) ((v) = suppress_int)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000724
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000725#define RESTORE_INT(v) do { \
Denys Vlasenkode892052016-10-02 01:49:13 +0200726 barrier(); \
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200727 suppress_int = (v); \
728 if (suppress_int == 0 && pending_int) \
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000729 raise_interrupt(); \
730} while (0)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000731
Glenn L McGrath9fef17d2002-08-22 18:41:20 +0000732
Denis Vlasenkobc54cff2007-02-23 01:05:52 +0000733/* ============ Stdout/stderr output */
Eric Andersenc470f442003-07-28 09:56:35 +0000734
Eric Andersenc470f442003-07-28 09:56:35 +0000735static void
Denis Vlasenkob012b102007-02-19 22:43:01 +0000736outstr(const char *p, FILE *file)
Denis Vlasenkoe5570da2007-02-19 22:41:55 +0000737{
Denis Vlasenkob012b102007-02-19 22:43:01 +0000738 INT_OFF;
739 fputs(p, file);
740 INT_ON;
741}
742
743static void
744flush_stdout_stderr(void)
745{
746 INT_OFF;
Denys Vlasenko8131eea2009-11-02 14:19:51 +0100747 fflush_all();
Denis Vlasenkob012b102007-02-19 22:43:01 +0000748 INT_ON;
749}
750
Denys Vlasenko9c541002015-10-07 15:44:36 +0200751/* Was called outcslow(c,FILE*), but c was always '\n' */
Denis Vlasenkob012b102007-02-19 22:43:01 +0000752static void
Denys Vlasenko9c541002015-10-07 15:44:36 +0200753newline_and_flush(FILE *dest)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000754{
755 INT_OFF;
Denys Vlasenko9c541002015-10-07 15:44:36 +0200756 putc('\n', dest);
Denis Vlasenkob012b102007-02-19 22:43:01 +0000757 fflush(dest);
758 INT_ON;
759}
760
761static int out1fmt(const char *, ...) __attribute__((__format__(__printf__,1,2)));
762static int
763out1fmt(const char *fmt, ...)
764{
765 va_list ap;
766 int r;
767
768 INT_OFF;
769 va_start(ap, fmt);
770 r = vprintf(fmt, ap);
771 va_end(ap);
772 INT_ON;
773 return r;
774}
775
776static int fmtstr(char *, size_t, const char *, ...) __attribute__((__format__(__printf__,3,4)));
777static int
778fmtstr(char *outbuf, size_t length, const char *fmt, ...)
779{
780 va_list ap;
781 int ret;
782
Denis Vlasenkob012b102007-02-19 22:43:01 +0000783 INT_OFF;
Denys Vlasenkocf3a7962017-07-26 14:38:19 +0200784 va_start(ap, fmt);
Denis Vlasenkob012b102007-02-19 22:43:01 +0000785 ret = vsnprintf(outbuf, length, fmt, ap);
786 va_end(ap);
787 INT_ON;
Denys Vlasenko3f7fb2c2020-02-16 18:06:20 +0100788 return ret > (int)length ? length : ret;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000789}
790
791static void
792out1str(const char *p)
793{
794 outstr(p, stdout);
795}
796
797static void
798out2str(const char *p)
799{
800 outstr(p, stderr);
Denys Vlasenko8131eea2009-11-02 14:19:51 +0100801 flush_stdout_stderr();
Denis Vlasenkob012b102007-02-19 22:43:01 +0000802}
803
804
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000805/* ============ Parser structures */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +0000806
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000807/* control characters in argument strings */
Denys Vlasenko2ce42e92009-11-29 02:18:13 +0100808#define CTL_FIRST CTLESC
Denys Vlasenkob6c84342009-08-29 20:23:20 +0200809#define CTLESC ((unsigned char)'\201') /* escape next character */
810#define CTLVAR ((unsigned char)'\202') /* variable defn */
811#define CTLENDVAR ((unsigned char)'\203')
812#define CTLBACKQ ((unsigned char)'\204')
Denys Vlasenkob6c84342009-08-29 20:23:20 +0200813#define CTLARI ((unsigned char)'\206') /* arithmetic expression */
814#define CTLENDARI ((unsigned char)'\207')
815#define CTLQUOTEMARK ((unsigned char)'\210')
Denys Vlasenko2ce42e92009-11-29 02:18:13 +0100816#define CTL_LAST CTLQUOTEMARK
Ron Yorstona1b0d382020-07-23 08:32:27 +0100817#if BASH_PROCESS_SUBST
818# define CTLTOPROC ((unsigned char)'\211')
819# define CTLFROMPROC ((unsigned char)'\212')
820# undef CTL_LAST
821# define CTL_LAST CTLFROMPROC
822#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000823
824/* variable substitution byte (follows CTLVAR) */
825#define VSTYPE 0x0f /* type of variable substitution */
826#define VSNUL 0x10 /* colon--treat the empty string as unset */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000827
828/* values of VSTYPE field */
Denis Vlasenko92e13c22008-03-25 01:17:40 +0000829#define VSNORMAL 0x1 /* normal variable: $var or ${var} */
830#define VSMINUS 0x2 /* ${var-text} */
831#define VSPLUS 0x3 /* ${var+text} */
832#define VSQUESTION 0x4 /* ${var?message} */
833#define VSASSIGN 0x5 /* ${var=text} */
834#define VSTRIMRIGHT 0x6 /* ${var%pattern} */
835#define VSTRIMRIGHTMAX 0x7 /* ${var%%pattern} */
836#define VSTRIMLEFT 0x8 /* ${var#pattern} */
837#define VSTRIMLEFTMAX 0x9 /* ${var##pattern} */
838#define VSLENGTH 0xa /* ${#var} */
Denys Vlasenko7d4aec02017-01-11 14:00:38 +0100839#if BASH_SUBSTR
Denis Vlasenko92e13c22008-03-25 01:17:40 +0000840#define VSSUBSTR 0xc /* ${var:position:length} */
Denys Vlasenko7d4aec02017-01-11 14:00:38 +0100841#endif
842#if BASH_PATTERN_SUBST
Denis Vlasenko92e13c22008-03-25 01:17:40 +0000843#define VSREPLACE 0xd /* ${var/pattern/replacement} */
844#define VSREPLACEALL 0xe /* ${var//pattern/replacement} */
845#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000846
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000847static const char dolatstr[] ALIGN1 = {
Ron Yorston549deab2015-05-18 09:57:51 +0200848 CTLQUOTEMARK, CTLVAR, VSNORMAL, '@', '=', CTLQUOTEMARK, '\0'
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000849};
Ron Yorston549deab2015-05-18 09:57:51 +0200850#define DOLATSTRLEN 6
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000851
Denis Vlasenko559691a2008-10-05 18:39:31 +0000852#define NCMD 0
853#define NPIPE 1
854#define NREDIR 2
855#define NBACKGND 3
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000856#define NSUBSHELL 4
Denis Vlasenko559691a2008-10-05 18:39:31 +0000857#define NAND 5
858#define NOR 6
859#define NSEMI 7
860#define NIF 8
861#define NWHILE 9
862#define NUNTIL 10
863#define NFOR 11
864#define NCASE 12
865#define NCLIST 13
866#define NDEFUN 14
867#define NARG 15
868#define NTO 16
Denys Vlasenko7d4aec02017-01-11 14:00:38 +0100869#if BASH_REDIR_OUTPUT
Denis Vlasenko559691a2008-10-05 18:39:31 +0000870#define NTO2 17
871#endif
872#define NCLOBBER 18
873#define NFROM 19
874#define NFROMTO 20
875#define NAPPEND 21
876#define NTOFD 22
877#define NFROMFD 23
878#define NHERE 24
879#define NXHERE 25
880#define NNOT 26
Denis Vlasenko340299a2008-11-21 10:36:36 +0000881#define N_NUMBER 27
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000882
883union node;
884
885struct ncmd {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000886 smallint type; /* Nxxxx */
Denys Vlasenko675d24a2018-01-27 22:02:05 +0100887 int linno;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000888 union node *assign;
889 union node *args;
890 union node *redirect;
891};
892
893struct npipe {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000894 smallint type;
895 smallint pipe_backgnd;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000896 struct nodelist *cmdlist;
897};
898
899struct nredir {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000900 smallint type;
Denys Vlasenko675d24a2018-01-27 22:02:05 +0100901 int linno;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000902 union node *n;
903 union node *redirect;
904};
905
906struct nbinary {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000907 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000908 union node *ch1;
909 union node *ch2;
910};
911
912struct nif {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000913 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000914 union node *test;
915 union node *ifpart;
916 union node *elsepart;
917};
918
919struct nfor {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000920 smallint type;
Denys Vlasenko675d24a2018-01-27 22:02:05 +0100921 int linno;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000922 union node *args;
923 union node *body;
924 char *var;
925};
926
927struct ncase {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000928 smallint type;
Denys Vlasenko675d24a2018-01-27 22:02:05 +0100929 int linno;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000930 union node *expr;
931 union node *cases;
932};
933
934struct nclist {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000935 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000936 union node *next;
937 union node *pattern;
938 union node *body;
939};
940
Denys Vlasenko675d24a2018-01-27 22:02:05 +0100941struct ndefun {
942 smallint type;
943 int linno;
944 char *text;
945 union node *body;
946};
947
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000948struct narg {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000949 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000950 union node *next;
951 char *text;
952 struct nodelist *backquote;
953};
954
Denis Vlasenko559691a2008-10-05 18:39:31 +0000955/* nfile and ndup layout must match!
956 * NTOFD (>&fdnum) uses ndup structure, but we may discover mid-flight
957 * that it is actually NTO2 (>&file), and change its type.
958 */
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000959struct nfile {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000960 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000961 union node *next;
962 int fd;
Denis Vlasenko559691a2008-10-05 18:39:31 +0000963 int _unused_dupfd;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000964 union node *fname;
965 char *expfname;
966};
967
968struct ndup {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000969 smallint type;
Denis Vlasenko559691a2008-10-05 18:39:31 +0000970 union node *next;
971 int fd;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000972 int dupfd;
973 union node *vname;
Denis Vlasenko559691a2008-10-05 18:39:31 +0000974 char *_unused_expfname;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000975};
976
977struct nhere {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000978 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000979 union node *next;
980 int fd;
981 union node *doc;
982};
983
984struct nnot {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000985 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000986 union node *com;
987};
988
989union node {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000990 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000991 struct ncmd ncmd;
992 struct npipe npipe;
993 struct nredir nredir;
994 struct nbinary nbinary;
995 struct nif nif;
996 struct nfor nfor;
997 struct ncase ncase;
998 struct nclist nclist;
Denys Vlasenko675d24a2018-01-27 22:02:05 +0100999 struct ndefun ndefun;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001000 struct narg narg;
1001 struct nfile nfile;
1002 struct ndup ndup;
1003 struct nhere nhere;
1004 struct nnot nnot;
1005};
1006
Denys Vlasenko86e83ec2009-07-23 22:07:07 +02001007/*
1008 * NODE_EOF is returned by parsecmd when it encounters an end of file.
1009 * It must be distinct from NULL.
1010 */
1011#define NODE_EOF ((union node *) -1L)
1012
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001013struct nodelist {
1014 struct nodelist *next;
1015 union node *n;
1016};
1017
1018struct funcnode {
1019 int count;
1020 union node n;
1021};
1022
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00001023/*
1024 * Free a parse tree.
1025 */
1026static void
1027freefunc(struct funcnode *f)
1028{
1029 if (f && --f->count < 0)
1030 free(f);
1031}
1032
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001033
1034/* ============ Debugging output */
1035
1036#if DEBUG
1037
1038static FILE *tracefile;
1039
1040static void
1041trace_printf(const char *fmt, ...)
1042{
1043 va_list va;
1044
1045 if (debug != 1)
1046 return;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00001047 if (DEBUG_TIME)
1048 fprintf(tracefile, "%u ", (int) time(NULL));
1049 if (DEBUG_PID)
1050 fprintf(tracefile, "[%u] ", (int) getpid());
1051 if (DEBUG_SIG)
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02001052 fprintf(tracefile, "pending s:%d i:%d(supp:%d) ", pending_sig, pending_int, suppress_int);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001053 va_start(va, fmt);
1054 vfprintf(tracefile, fmt, va);
1055 va_end(va);
1056}
1057
1058static void
1059trace_vprintf(const char *fmt, va_list va)
1060{
1061 if (debug != 1)
1062 return;
1063 vfprintf(tracefile, fmt, va);
Denys Vlasenko474ed062016-10-30 18:30:29 +01001064 fprintf(tracefile, "\n");
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001065}
1066
1067static void
1068trace_puts(const char *s)
1069{
1070 if (debug != 1)
1071 return;
1072 fputs(s, tracefile);
1073}
1074
1075static void
1076trace_puts_quoted(char *s)
1077{
1078 char *p;
1079 char c;
1080
1081 if (debug != 1)
1082 return;
1083 putc('"', tracefile);
1084 for (p = s; *p; p++) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01001085 switch ((unsigned char)*p) {
1086 case '\n': c = 'n'; goto backslash;
1087 case '\t': c = 't'; goto backslash;
1088 case '\r': c = 'r'; goto backslash;
1089 case '\"': c = '\"'; goto backslash;
1090 case '\\': c = '\\'; goto backslash;
1091 case CTLESC: c = 'e'; goto backslash;
1092 case CTLVAR: c = 'v'; goto backslash;
Denys Vlasenkocd716832009-11-28 22:14:02 +01001093 case CTLBACKQ: c = 'q'; goto backslash;
Ron Yorstona1b0d382020-07-23 08:32:27 +01001094#if BASH_PROCESS_SUBST
1095 case CTLTOPROC: c = 'p'; goto backslash;
1096 case CTLFROMPROC: c = 'P'; goto backslash;
1097#endif
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001098 backslash:
1099 putc('\\', tracefile);
1100 putc(c, tracefile);
1101 break;
1102 default:
1103 if (*p >= ' ' && *p <= '~')
1104 putc(*p, tracefile);
1105 else {
1106 putc('\\', tracefile);
Denys Vlasenkocd716832009-11-28 22:14:02 +01001107 putc((*p >> 6) & 03, tracefile);
1108 putc((*p >> 3) & 07, tracefile);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001109 putc(*p & 07, tracefile);
1110 }
1111 break;
1112 }
1113 }
1114 putc('"', tracefile);
1115}
1116
1117static void
1118trace_puts_args(char **ap)
1119{
1120 if (debug != 1)
1121 return;
1122 if (!*ap)
1123 return;
1124 while (1) {
1125 trace_puts_quoted(*ap);
1126 if (!*++ap) {
1127 putc('\n', tracefile);
1128 break;
1129 }
1130 putc(' ', tracefile);
1131 }
1132}
1133
1134static void
1135opentrace(void)
1136{
1137 char s[100];
1138#ifdef O_APPEND
1139 int flags;
1140#endif
1141
1142 if (debug != 1) {
1143 if (tracefile)
1144 fflush(tracefile);
1145 /* leave open because libedit might be using it */
1146 return;
1147 }
1148 strcpy(s, "./trace");
1149 if (tracefile) {
1150 if (!freopen(s, "a", tracefile)) {
1151 fprintf(stderr, "Can't re-open %s\n", s);
1152 debug = 0;
1153 return;
1154 }
1155 } else {
1156 tracefile = fopen(s, "a");
1157 if (tracefile == NULL) {
1158 fprintf(stderr, "Can't open %s\n", s);
1159 debug = 0;
1160 return;
1161 }
1162 }
1163#ifdef O_APPEND
Denis Vlasenkod37f2222007-08-19 13:42:08 +00001164 flags = fcntl(fileno(tracefile), F_GETFL);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001165 if (flags >= 0)
1166 fcntl(fileno(tracefile), F_SETFL, flags | O_APPEND);
1167#endif
1168 setlinebuf(tracefile);
1169 fputs("\nTracing started.\n", tracefile);
1170}
1171
1172static void
1173indent(int amount, char *pfx, FILE *fp)
1174{
1175 int i;
1176
1177 for (i = 0; i < amount; i++) {
1178 if (pfx && i == amount - 1)
1179 fputs(pfx, fp);
1180 putc('\t', fp);
1181 }
1182}
1183
1184/* little circular references here... */
1185static void shtree(union node *n, int ind, char *pfx, FILE *fp);
1186
1187static void
1188sharg(union node *arg, FILE *fp)
1189{
1190 char *p;
1191 struct nodelist *bqlist;
Denys Vlasenkocd716832009-11-28 22:14:02 +01001192 unsigned char subtype;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001193
1194 if (arg->type != NARG) {
1195 out1fmt("<node type %d>\n", arg->type);
1196 abort();
1197 }
1198 bqlist = arg->narg.backquote;
1199 for (p = arg->narg.text; *p; p++) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01001200 switch ((unsigned char)*p) {
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001201 case CTLESC:
Dan Fandrich77d48722010-09-07 23:38:28 -07001202 p++;
1203 putc(*p, fp);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001204 break;
1205 case CTLVAR:
1206 putc('$', fp);
1207 putc('{', fp);
1208 subtype = *++p;
1209 if (subtype == VSLENGTH)
1210 putc('#', fp);
1211
Dan Fandrich77d48722010-09-07 23:38:28 -07001212 while (*p != '=') {
1213 putc(*p, fp);
1214 p++;
1215 }
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001216
1217 if (subtype & VSNUL)
1218 putc(':', fp);
1219
1220 switch (subtype & VSTYPE) {
1221 case VSNORMAL:
1222 putc('}', fp);
1223 break;
1224 case VSMINUS:
1225 putc('-', fp);
1226 break;
1227 case VSPLUS:
1228 putc('+', fp);
1229 break;
1230 case VSQUESTION:
1231 putc('?', fp);
1232 break;
1233 case VSASSIGN:
1234 putc('=', fp);
1235 break;
1236 case VSTRIMLEFT:
1237 putc('#', fp);
1238 break;
1239 case VSTRIMLEFTMAX:
1240 putc('#', fp);
1241 putc('#', fp);
1242 break;
1243 case VSTRIMRIGHT:
1244 putc('%', fp);
1245 break;
1246 case VSTRIMRIGHTMAX:
1247 putc('%', fp);
1248 putc('%', fp);
1249 break;
1250 case VSLENGTH:
1251 break;
1252 default:
1253 out1fmt("<subtype %d>", subtype);
1254 }
1255 break;
1256 case CTLENDVAR:
1257 putc('}', fp);
1258 break;
Ron Yorstona1b0d382020-07-23 08:32:27 +01001259#if BASH_PROCESS_SUBST
1260 case CTLTOPROC:
1261 putc('>', fp);
1262 goto backq;
1263 case CTLFROMPROC:
1264 putc('<', fp);
1265 goto backq;
1266#endif
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001267 case CTLBACKQ:
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001268 putc('$', fp);
Ron Yorstona1b0d382020-07-23 08:32:27 +01001269 IF_BASH_PROCESS_SUBST(backq:)
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001270 putc('(', fp);
1271 shtree(bqlist->n, -1, NULL, fp);
1272 putc(')', fp);
1273 break;
1274 default:
1275 putc(*p, fp);
1276 break;
1277 }
1278 }
1279}
1280
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02001281static void
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001282shcmd(union node *cmd, FILE *fp)
1283{
1284 union node *np;
1285 int first;
1286 const char *s;
1287 int dftfd;
1288
1289 first = 1;
1290 for (np = cmd->ncmd.args; np; np = np->narg.next) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +00001291 if (!first)
1292 putc(' ', fp);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001293 sharg(np, fp);
1294 first = 0;
1295 }
1296 for (np = cmd->ncmd.redirect; np; np = np->nfile.next) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +00001297 if (!first)
1298 putc(' ', fp);
1299 dftfd = 0;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001300 switch (np->nfile.type) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +00001301 case NTO: s = ">>"+1; dftfd = 1; break;
1302 case NCLOBBER: s = ">|"; dftfd = 1; break;
1303 case NAPPEND: s = ">>"; dftfd = 1; break;
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01001304#if BASH_REDIR_OUTPUT
Denis Vlasenko559691a2008-10-05 18:39:31 +00001305 case NTO2:
1306#endif
Denis Vlasenko40ba9982007-07-14 00:48:29 +00001307 case NTOFD: s = ">&"; dftfd = 1; break;
Denis Vlasenko559691a2008-10-05 18:39:31 +00001308 case NFROM: s = "<"; break;
Denis Vlasenko40ba9982007-07-14 00:48:29 +00001309 case NFROMFD: s = "<&"; break;
1310 case NFROMTO: s = "<>"; break;
1311 default: s = "*error*"; break;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001312 }
1313 if (np->nfile.fd != dftfd)
1314 fprintf(fp, "%d", np->nfile.fd);
1315 fputs(s, fp);
1316 if (np->nfile.type == NTOFD || np->nfile.type == NFROMFD) {
1317 fprintf(fp, "%d", np->ndup.dupfd);
1318 } else {
1319 sharg(np->nfile.fname, fp);
1320 }
1321 first = 0;
1322 }
1323}
1324
1325static void
1326shtree(union node *n, int ind, char *pfx, FILE *fp)
1327{
1328 struct nodelist *lp;
1329 const char *s;
1330
1331 if (n == NULL)
1332 return;
1333
1334 indent(ind, pfx, fp);
Denys Vlasenko86e83ec2009-07-23 22:07:07 +02001335
1336 if (n == NODE_EOF) {
1337 fputs("<EOF>", fp);
1338 return;
1339 }
1340
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001341 switch (n->type) {
1342 case NSEMI:
1343 s = "; ";
1344 goto binop;
1345 case NAND:
1346 s = " && ";
1347 goto binop;
1348 case NOR:
1349 s = " || ";
1350 binop:
1351 shtree(n->nbinary.ch1, ind, NULL, fp);
1352 /* if (ind < 0) */
1353 fputs(s, fp);
1354 shtree(n->nbinary.ch2, ind, NULL, fp);
1355 break;
1356 case NCMD:
1357 shcmd(n, fp);
1358 if (ind >= 0)
1359 putc('\n', fp);
1360 break;
1361 case NPIPE:
1362 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
Denys Vlasenko7cee00e2009-07-24 01:08:03 +02001363 shtree(lp->n, 0, NULL, fp);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001364 if (lp->next)
1365 fputs(" | ", fp);
1366 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00001367 if (n->npipe.pipe_backgnd)
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001368 fputs(" &", fp);
1369 if (ind >= 0)
1370 putc('\n', fp);
1371 break;
1372 default:
1373 fprintf(fp, "<node type %d>", n->type);
1374 if (ind >= 0)
1375 putc('\n', fp);
1376 break;
1377 }
1378}
1379
1380static void
1381showtree(union node *n)
1382{
1383 trace_puts("showtree called\n");
Denys Vlasenko883cea42009-07-11 15:31:59 +02001384 shtree(n, 1, NULL, stderr);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001385}
1386
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001387#endif /* DEBUG */
1388
1389
Denis Vlasenkob012b102007-02-19 22:43:01 +00001390/* ============ Message printing */
1391
1392static void
1393ash_vmsg(const char *msg, va_list ap)
1394{
1395 fprintf(stderr, "%s: ", arg0);
1396 if (commandname) {
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001397 if (strcmp(arg0, commandname))
1398 fprintf(stderr, "%s: ", commandname);
Denys Vlasenko79b3d422010-06-03 04:29:08 +02001399 if (!iflag || g_parsefile->pf_fd > 0)
Denys Vlasenko675d24a2018-01-27 22:02:05 +01001400 fprintf(stderr, "line %d: ", errlinno);
Eric Andersenc470f442003-07-28 09:56:35 +00001401 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00001402 vfprintf(stderr, msg, ap);
Denys Vlasenko9c541002015-10-07 15:44:36 +02001403 newline_and_flush(stderr);
Eric Andersenc470f442003-07-28 09:56:35 +00001404}
Denis Vlasenkob012b102007-02-19 22:43:01 +00001405
1406/*
1407 * Exverror is called to raise the error exception. If the second argument
1408 * is not NULL then error prints an error message using printf style
1409 * formatting. It then raises the error exception.
1410 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001411static void ash_vmsg_and_raise(int, const char *, va_list) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +00001412static void
1413ash_vmsg_and_raise(int cond, const char *msg, va_list ap)
Eric Andersenc470f442003-07-28 09:56:35 +00001414{
Denis Vlasenkob012b102007-02-19 22:43:01 +00001415#if DEBUG
1416 if (msg) {
Denys Vlasenko474ed062016-10-30 18:30:29 +01001417 TRACE(("ash_vmsg_and_raise(%d):", cond));
Denis Vlasenkob012b102007-02-19 22:43:01 +00001418 TRACEV((msg, ap));
Denis Vlasenkob012b102007-02-19 22:43:01 +00001419 } else
Denys Vlasenko474ed062016-10-30 18:30:29 +01001420 TRACE(("ash_vmsg_and_raise(%d):NULL\n", cond));
Denis Vlasenkob012b102007-02-19 22:43:01 +00001421 if (msg)
1422#endif
1423 ash_vmsg(msg, ap);
1424
1425 flush_stdout_stderr();
1426 raise_exception(cond);
1427 /* NOTREACHED */
Eric Andersenc470f442003-07-28 09:56:35 +00001428}
Denis Vlasenkob012b102007-02-19 22:43:01 +00001429
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001430static void ash_msg_and_raise_error(const char *, ...) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +00001431static void
1432ash_msg_and_raise_error(const char *msg, ...)
1433{
1434 va_list ap;
1435
Ron Yorstonea7d2f62017-01-03 11:18:23 +01001436 exitstatus = 2;
1437
Denis Vlasenkob012b102007-02-19 22:43:01 +00001438 va_start(ap, msg);
1439 ash_vmsg_and_raise(EXERROR, msg, ap);
1440 /* NOTREACHED */
1441 va_end(ap);
1442}
1443
Ron Yorstonbe366e52017-07-27 13:53:39 +01001444/*
Ron Yorstonbe366e52017-07-27 13:53:39 +01001445 * 'fmt' must be a string literal.
1446 */
Denys Vlasenko6f97b302017-09-29 18:17:25 +02001447#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 +01001448
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00001449static void raise_error_syntax(const char *) NORETURN;
1450static void
1451raise_error_syntax(const char *msg)
1452{
Denys Vlasenko675d24a2018-01-27 22:02:05 +01001453 errlinno = g_parsefile->linno;
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00001454 ash_msg_and_raise_error("syntax error: %s", msg);
1455 /* NOTREACHED */
1456}
1457
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001458static void ash_msg_and_raise(int, const char *, ...) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +00001459static void
1460ash_msg_and_raise(int cond, const char *msg, ...)
1461{
1462 va_list ap;
1463
1464 va_start(ap, msg);
1465 ash_vmsg_and_raise(cond, msg, ap);
1466 /* NOTREACHED */
1467 va_end(ap);
1468}
1469
1470/*
1471 * error/warning routines for external builtins
1472 */
1473static void
1474ash_msg(const char *fmt, ...)
1475{
1476 va_list ap;
1477
1478 va_start(ap, fmt);
1479 ash_vmsg(fmt, ap);
1480 va_end(ap);
1481}
1482
1483/*
1484 * Return a string describing an error. The returned string may be a
1485 * pointer to a static buffer that will be overwritten on the next call.
1486 * Action describes the operation that got the error.
1487 */
1488static const char *
1489errmsg(int e, const char *em)
1490{
1491 if (e == ENOENT || e == ENOTDIR) {
1492 return em;
1493 }
1494 return strerror(e);
1495}
1496
1497
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001498/* ============ Memory allocation */
1499
Denys Vlasenkoe7670ff2009-10-11 00:45:25 +02001500#if 0
1501/* I consider these wrappers nearly useless:
1502 * ok, they return you to nearest exception handler, but
1503 * how much memory do you leak in the process, making
1504 * memory starvation worse?
1505 */
1506static void *
1507ckrealloc(void * p, size_t nbytes)
1508{
1509 p = realloc(p, nbytes);
1510 if (!p)
1511 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1512 return p;
1513}
1514
1515static void *
1516ckmalloc(size_t nbytes)
1517{
1518 return ckrealloc(NULL, nbytes);
1519}
1520
1521static void *
1522ckzalloc(size_t nbytes)
1523{
1524 return memset(ckmalloc(nbytes), 0, nbytes);
1525}
1526
1527static char *
1528ckstrdup(const char *s)
1529{
1530 char *p = strdup(s);
1531 if (!p)
1532 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1533 return p;
1534}
1535#else
1536/* Using bbox equivalents. They exit if out of memory */
1537# define ckrealloc xrealloc
1538# define ckmalloc xmalloc
1539# define ckzalloc xzalloc
1540# define ckstrdup xstrdup
1541#endif
1542
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001543/*
1544 * It appears that grabstackstr() will barf with such alignments
1545 * because stalloc() will return a string allocated in a new stackblock.
1546 */
1547#define SHELL_ALIGN(nbytes) (((nbytes) + SHELL_SIZE) & ~SHELL_SIZE)
1548enum {
1549 /* Most machines require the value returned from malloc to be aligned
1550 * in some way. The following macro will get this right
1551 * on many machines. */
Denys Vlasenko0e5e4ea2009-10-11 00:36:20 +02001552 SHELL_SIZE = sizeof(union { int i; char *cp; double d; }) - 1,
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001553 /* Minimum size of a block */
Denis Vlasenko01631112007-12-16 17:20:38 +00001554 MINSIZE = SHELL_ALIGN(504),
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001555};
1556
1557struct stack_block {
1558 struct stack_block *prev;
1559 char space[MINSIZE];
1560};
1561
1562struct stackmark {
1563 struct stack_block *stackp;
1564 char *stacknxt;
1565 size_t stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001566};
1567
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001568
Denis Vlasenko01631112007-12-16 17:20:38 +00001569struct globals_memstack {
1570 struct stack_block *g_stackp; // = &stackbase;
Denis Vlasenko01631112007-12-16 17:20:38 +00001571 char *g_stacknxt; // = stackbase.space;
1572 char *sstrend; // = stackbase.space + MINSIZE;
1573 size_t g_stacknleft; // = MINSIZE;
Denis Vlasenko01631112007-12-16 17:20:38 +00001574 struct stack_block stackbase;
1575};
Denys Vlasenko6f9442f2018-01-28 20:41:23 +01001576extern struct globals_memstack *BB_GLOBAL_CONST ash_ptr_to_globals_memstack;
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001577#define G_memstack (*ash_ptr_to_globals_memstack)
Denis Vlasenko01631112007-12-16 17:20:38 +00001578#define g_stackp (G_memstack.g_stackp )
Denis Vlasenko01631112007-12-16 17:20:38 +00001579#define g_stacknxt (G_memstack.g_stacknxt )
1580#define sstrend (G_memstack.sstrend )
1581#define g_stacknleft (G_memstack.g_stacknleft)
Denis Vlasenko01631112007-12-16 17:20:38 +00001582#define stackbase (G_memstack.stackbase )
1583#define INIT_G_memstack() do { \
YU Jincheng5156b242021-10-10 02:19:51 +08001584 XZALLOC_CONST_PTR(&ash_ptr_to_globals_memstack, sizeof(G_memstack)); \
Denis Vlasenko01631112007-12-16 17:20:38 +00001585 g_stackp = &stackbase; \
1586 g_stacknxt = stackbase.space; \
1587 g_stacknleft = MINSIZE; \
1588 sstrend = stackbase.space + MINSIZE; \
Denis Vlasenko01631112007-12-16 17:20:38 +00001589} while (0)
1590
Denys Vlasenkoe7670ff2009-10-11 00:45:25 +02001591
Denis Vlasenko01631112007-12-16 17:20:38 +00001592#define stackblock() ((void *)g_stacknxt)
1593#define stackblocksize() g_stacknleft
1594
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001595/*
1596 * Parse trees for commands are allocated in lifo order, so we use a stack
1597 * to make this more efficient, and also to avoid all sorts of exception
1598 * handling code to handle interrupts in the middle of a parse.
1599 *
1600 * The size 504 was chosen because the Ultrix malloc handles that size
1601 * well.
1602 */
1603static void *
1604stalloc(size_t nbytes)
1605{
1606 char *p;
1607 size_t aligned;
1608
1609 aligned = SHELL_ALIGN(nbytes);
Denis Vlasenko01631112007-12-16 17:20:38 +00001610 if (aligned > g_stacknleft) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001611 size_t len;
1612 size_t blocksize;
1613 struct stack_block *sp;
1614
1615 blocksize = aligned;
1616 if (blocksize < MINSIZE)
1617 blocksize = MINSIZE;
1618 len = sizeof(struct stack_block) - MINSIZE + blocksize;
1619 if (len < blocksize)
1620 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1621 INT_OFF;
1622 sp = ckmalloc(len);
Denis Vlasenko01631112007-12-16 17:20:38 +00001623 sp->prev = g_stackp;
1624 g_stacknxt = sp->space;
1625 g_stacknleft = blocksize;
1626 sstrend = g_stacknxt + blocksize;
1627 g_stackp = sp;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001628 INT_ON;
1629 }
Denis Vlasenko01631112007-12-16 17:20:38 +00001630 p = g_stacknxt;
1631 g_stacknxt += aligned;
1632 g_stacknleft -= aligned;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001633 return p;
1634}
1635
Denis Vlasenko597906c2008-02-20 16:38:54 +00001636static void *
1637stzalloc(size_t nbytes)
1638{
1639 return memset(stalloc(nbytes), 0, nbytes);
1640}
1641
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001642static void
1643stunalloc(void *p)
1644{
1645#if DEBUG
Denis Vlasenko01631112007-12-16 17:20:38 +00001646 if (!p || (g_stacknxt < (char *)p) || ((char *)p < g_stackp->space)) {
Bernhard Reutner-Fischer5e25ddb2008-05-19 09:48:17 +00001647 write(STDERR_FILENO, "stunalloc\n", 10);
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001648 abort();
1649 }
1650#endif
Denis Vlasenko01631112007-12-16 17:20:38 +00001651 g_stacknleft += g_stacknxt - (char *)p;
1652 g_stacknxt = p;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001653}
1654
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001655/*
1656 * Like strdup but works with the ash stack.
1657 */
1658static char *
Denys Vlasenko8e2bc472016-09-28 23:02:57 +02001659sstrdup(const char *p)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001660{
1661 size_t len = strlen(p) + 1;
1662 return memcpy(stalloc(len), p, len);
1663}
1664
Denys Vlasenko03c36e02018-01-10 15:18:35 +01001665static ALWAYS_INLINE void
Denys Vlasenko60ca8342016-09-30 11:21:21 +02001666grabstackblock(size_t len)
1667{
Denys Vlasenkoa318bba2016-10-26 18:26:27 +02001668 stalloc(len);
Denys Vlasenko60ca8342016-09-30 11:21:21 +02001669}
1670
1671static void
1672pushstackmark(struct stackmark *mark, size_t len)
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001673{
Denis Vlasenko01631112007-12-16 17:20:38 +00001674 mark->stackp = g_stackp;
1675 mark->stacknxt = g_stacknxt;
1676 mark->stacknleft = g_stacknleft;
Denys Vlasenko60ca8342016-09-30 11:21:21 +02001677 grabstackblock(len);
1678}
1679
1680static void
1681setstackmark(struct stackmark *mark)
1682{
1683 pushstackmark(mark, g_stacknxt == g_stackp->space && g_stackp != &stackbase);
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001684}
1685
1686static void
1687popstackmark(struct stackmark *mark)
1688{
1689 struct stack_block *sp;
1690
Denis Vlasenko93ebd4f2007-03-13 20:55:36 +00001691 if (!mark->stackp)
1692 return;
1693
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001694 INT_OFF;
Denis Vlasenko01631112007-12-16 17:20:38 +00001695 while (g_stackp != mark->stackp) {
1696 sp = g_stackp;
1697 g_stackp = sp->prev;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001698 free(sp);
1699 }
Denis Vlasenko01631112007-12-16 17:20:38 +00001700 g_stacknxt = mark->stacknxt;
1701 g_stacknleft = mark->stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001702 sstrend = mark->stacknxt + mark->stacknleft;
1703 INT_ON;
1704}
1705
1706/*
1707 * When the parser reads in a string, it wants to stick the string on the
1708 * stack and only adjust the stack pointer when it knows how big the
1709 * string is. Stackblock (defined in stack.h) returns a pointer to a block
1710 * of space on top of the stack and stackblocklen returns the length of
1711 * this block. Growstackblock will grow this space by at least one byte,
1712 * possibly moving it (like realloc). Grabstackblock actually allocates the
1713 * part of the block that has been used.
1714 */
1715static void
Denys Vlasenkoda2e46d2020-02-21 15:25:37 +01001716growstackblock(size_t min)
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001717{
1718 size_t newlen;
1719
Denis Vlasenko01631112007-12-16 17:20:38 +00001720 newlen = g_stacknleft * 2;
1721 if (newlen < g_stacknleft)
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001722 ash_msg_and_raise_error(bb_msg_memory_exhausted);
Denys Vlasenkoda2e46d2020-02-21 15:25:37 +01001723 min = SHELL_ALIGN(min | 128);
1724 if (newlen < min)
1725 newlen += min;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001726
Denis Vlasenko01631112007-12-16 17:20:38 +00001727 if (g_stacknxt == g_stackp->space && g_stackp != &stackbase) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001728 struct stack_block *sp;
1729 struct stack_block *prevstackp;
1730 size_t grosslen;
1731
1732 INT_OFF;
Denis Vlasenko01631112007-12-16 17:20:38 +00001733 sp = g_stackp;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001734 prevstackp = sp->prev;
1735 grosslen = newlen + sizeof(struct stack_block) - MINSIZE;
1736 sp = ckrealloc(sp, grosslen);
1737 sp->prev = prevstackp;
Denis Vlasenko01631112007-12-16 17:20:38 +00001738 g_stackp = sp;
1739 g_stacknxt = sp->space;
1740 g_stacknleft = newlen;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001741 sstrend = sp->space + newlen;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001742 INT_ON;
1743 } else {
Denis Vlasenko01631112007-12-16 17:20:38 +00001744 char *oldspace = g_stacknxt;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001745 size_t oldlen = g_stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001746 char *p = stalloc(newlen);
1747
1748 /* free the space we just allocated */
Denis Vlasenko01631112007-12-16 17:20:38 +00001749 g_stacknxt = memcpy(p, oldspace, oldlen);
1750 g_stacknleft += newlen;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001751 }
1752}
1753
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001754/*
1755 * The following routines are somewhat easier to use than the above.
1756 * The user declares a variable of type STACKSTR, which may be declared
1757 * to be a register. The macro STARTSTACKSTR initializes things. Then
1758 * the user uses the macro STPUTC to add characters to the string. In
1759 * effect, STPUTC(c, p) is the same as *p++ = c except that the stack is
1760 * grown as necessary. When the user is done, she can just leave the
1761 * string there and refer to it using stackblock(). Or she can allocate
1762 * the space for it using grabstackstr(). If it is necessary to allow
1763 * someone else to use the stack temporarily and then continue to grow
1764 * the string, the user should use grabstack to allocate the space, and
1765 * then call ungrabstr(p) to return to the previous mode of operation.
1766 *
1767 * USTPUTC is like STPUTC except that it doesn't check for overflow.
1768 * CHECKSTACKSPACE can be called before USTPUTC to ensure that there
1769 * is space for at least one character.
1770 */
1771static void *
1772growstackstr(void)
1773{
1774 size_t len = stackblocksize();
Denys Vlasenkoda2e46d2020-02-21 15:25:37 +01001775 growstackblock(0);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001776 return (char *)stackblock() + len;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001777}
1778
Denys Vlasenkoc55847f2020-02-17 15:59:08 +01001779static char *
1780growstackto(size_t len)
1781{
Denys Vlasenkoda2e46d2020-02-21 15:25:37 +01001782 if (stackblocksize() < len)
1783 growstackblock(len);
Denys Vlasenkoc55847f2020-02-17 15:59:08 +01001784 return stackblock();
1785}
1786
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001787/*
1788 * Called from CHECKSTRSPACE.
1789 */
1790static char *
1791makestrspace(size_t newlen, char *p)
1792{
Denis Vlasenko01631112007-12-16 17:20:38 +00001793 size_t len = p - g_stacknxt;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001794
Denys Vlasenkoc55847f2020-02-17 15:59:08 +01001795 return growstackto(len + newlen) + len;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001796}
1797
1798static char *
Denys Vlasenko538ee412020-02-22 19:11:41 +01001799stnputs(const char *s, size_t n, char *p)
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001800{
1801 p = makestrspace(n, p);
Denys Vlasenko5ace96a2017-07-23 21:46:02 +02001802 p = (char *)mempcpy(p, s, n);
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001803 return p;
1804}
1805
1806static char *
1807stack_putstr(const char *s, char *p)
1808{
Denys Vlasenko538ee412020-02-22 19:11:41 +01001809 return stnputs(s, strlen(s), p);
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001810}
1811
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001812static char *
1813_STPUTC(int c, char *p)
1814{
1815 if (p == sstrend)
1816 p = growstackstr();
1817 *p++ = c;
1818 return p;
1819}
1820
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001821#define STARTSTACKSTR(p) ((p) = stackblock())
1822#define STPUTC(c, p) ((p) = _STPUTC((c), (p)))
Denis Vlasenko843cbd52008-06-27 00:23:18 +00001823#define CHECKSTRSPACE(n, p) do { \
1824 char *q = (p); \
1825 size_t l = (n); \
1826 size_t m = sstrend - q; \
1827 if (l > m) \
1828 (p) = makestrspace(l, q); \
1829} while (0)
Denis Vlasenkoef527f52008-06-23 01:52:30 +00001830#define USTPUTC(c, p) (*(p)++ = (c))
Denis Vlasenko843cbd52008-06-27 00:23:18 +00001831#define STACKSTRNUL(p) do { \
1832 if ((p) == sstrend) \
1833 (p) = growstackstr(); \
1834 *(p) = '\0'; \
1835} while (0)
Denis Vlasenkoef527f52008-06-23 01:52:30 +00001836#define STUNPUTC(p) (--(p))
1837#define STTOPC(p) ((p)[-1])
1838#define STADJUST(amount, p) ((p) += (amount))
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001839
1840#define grabstackstr(p) stalloc((char *)(p) - (char *)stackblock())
Denis Vlasenkoef527f52008-06-23 01:52:30 +00001841#define ungrabstackstr(s, p) stunalloc(s)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001842#define stackstrend() ((void *)sstrend)
1843
1844
1845/* ============ String helpers */
1846
1847/*
1848 * prefix -- see if pfx is a prefix of string.
1849 */
1850static char *
1851prefix(const char *string, const char *pfx)
1852{
1853 while (*pfx) {
1854 if (*pfx++ != *string++)
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00001855 return NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001856 }
1857 return (char *) string;
1858}
1859
1860/*
1861 * Check for a valid number. This should be elsewhere.
1862 */
1863static int
1864is_number(const char *p)
1865{
1866 do {
1867 if (!isdigit(*p))
1868 return 0;
1869 } while (*++p != '\0');
1870 return 1;
1871}
1872
1873/*
1874 * Convert a string of digits to an integer, printing an error message on
1875 * failure.
1876 */
1877static int
1878number(const char *s)
1879{
1880 if (!is_number(s))
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02001881 ash_msg_and_raise_error(msg_illnum, s);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001882 return atoi(s);
1883}
1884
1885/*
Denys Vlasenko42ba7572017-07-21 13:20:14 +02001886 * Produce a single quoted string suitable as input to the shell.
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001887 * The return string is allocated on the stack.
1888 */
1889static char *
1890single_quote(const char *s)
1891{
1892 char *p;
1893
1894 STARTSTACKSTR(p);
1895
1896 do {
1897 char *q;
1898 size_t len;
1899
1900 len = strchrnul(s, '\'') - s;
1901
1902 q = p = makestrspace(len + 3, p);
1903
1904 *q++ = '\'';
Denys Vlasenko94af83e2017-07-23 21:55:40 +02001905 q = (char *)mempcpy(q, s, len);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001906 *q++ = '\'';
1907 s += len;
1908
1909 STADJUST(q - p, p);
1910
Denys Vlasenkocd716832009-11-28 22:14:02 +01001911 if (*s != '\'')
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001912 break;
Denys Vlasenkocd716832009-11-28 22:14:02 +01001913 len = 0;
1914 do len++; while (*++s == '\'');
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001915
1916 q = p = makestrspace(len + 3, p);
1917
1918 *q++ = '"';
Denys Vlasenko5ace96a2017-07-23 21:46:02 +02001919 q = (char *)mempcpy(q, s - len, len);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001920 *q++ = '"';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001921
1922 STADJUST(q - p, p);
1923 } while (*s);
1924
Denys Vlasenkocd716832009-11-28 22:14:02 +01001925 USTPUTC('\0', p);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001926
1927 return stackblock();
1928}
1929
Denys Vlasenko42ba7572017-07-21 13:20:14 +02001930/*
1931 * Produce a possibly single quoted string suitable as input to the shell.
Denys Vlasenko42ba7572017-07-21 13:20:14 +02001932 * If quoting was done, the return string is allocated on the stack,
1933 * otherwise a pointer to the original string is returned.
1934 */
1935static const char *
1936maybe_single_quote(const char *s)
1937{
1938 const char *p = s;
1939
1940 while (*p) {
1941 /* Assuming ACSII */
1942 /* quote ctrl_chars space !"#$%&'()* */
1943 if (*p < '+')
1944 goto need_quoting;
1945 /* quote ;<=>? */
1946 if (*p >= ';' && *p <= '?')
1947 goto need_quoting;
1948 /* quote `[\ */
1949 if (*p == '`')
1950 goto need_quoting;
1951 if (*p == '[')
1952 goto need_quoting;
1953 if (*p == '\\')
1954 goto need_quoting;
1955 /* quote {|}~ DEL and high bytes */
1956 if (*p > 'z')
1957 goto need_quoting;
1958 /* Not quoting these: +,-./ 0-9 :@ A-Z ]^_ a-z */
1959 /* TODO: maybe avoid quoting % */
1960 p++;
1961 }
1962 return s;
1963
1964 need_quoting:
1965 return single_quote(s);
1966}
1967
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001968
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00001969/* ============ nextopt */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001970
1971static char **argptr; /* argument list for builtin commands */
1972static char *optionarg; /* set by nextopt (like getopt) */
1973static char *optptr; /* used by nextopt */
1974
1975/*
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001976 * XXX - should get rid of. Have all builtins use getopt(3).
1977 * The library getopt must have the BSD extension static variable
1978 * "optreset", otherwise it can't be used within the shell safely.
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001979 *
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001980 * Standard option processing (a la getopt) for builtin routines.
1981 * The only argument that is passed to nextopt is the option string;
1982 * the other arguments are unnecessary. It returns the character,
1983 * or '\0' on end of input.
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001984 */
1985static int
1986nextopt(const char *optstring)
1987{
1988 char *p;
1989 const char *q;
1990 char c;
1991
1992 p = optptr;
1993 if (p == NULL || *p == '\0') {
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001994 /* We ate entire "-param", take next one */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001995 p = *argptr;
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001996 if (p == NULL)
1997 return '\0';
1998 if (*p != '-')
1999 return '\0';
2000 if (*++p == '\0') /* just "-" ? */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002001 return '\0';
2002 argptr++;
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00002003 if (LONE_DASH(p)) /* "--" ? */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002004 return '\0';
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00002005 /* p => next "-param" */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002006 }
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00002007 /* p => some option char in the middle of a "-param" */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002008 c = *p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00002009 for (q = optstring; *q != c;) {
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002010 if (*q == '\0')
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00002011 ash_msg_and_raise_error("illegal option -%c", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002012 if (*++q == ':')
2013 q++;
2014 }
2015 if (*++q == ':') {
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00002016 if (*p == '\0') {
2017 p = *argptr++;
2018 if (p == NULL)
2019 ash_msg_and_raise_error("no arg for -%c option", c);
2020 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002021 optionarg = p;
2022 p = NULL;
2023 }
2024 optptr = p;
2025 return c;
2026}
2027
2028
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00002029/* ============ Shell variables */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002030
Denis Vlasenko01631112007-12-16 17:20:38 +00002031struct shparam {
2032 int nparam; /* # of positional parameters (without $0) */
2033#if ENABLE_ASH_GETOPTS
2034 int optind; /* next parameter to be processed by getopts */
2035 int optoff; /* used by getopts */
2036#endif
2037 unsigned char malloced; /* if parameter list dynamically allocated */
2038 char **p; /* parameter list */
2039};
2040
2041/*
2042 * Free the list of positional parameters.
2043 */
2044static void
2045freeparam(volatile struct shparam *param)
2046{
Denis Vlasenko01631112007-12-16 17:20:38 +00002047 if (param->malloced) {
Denis Vlasenko3177ba02008-07-13 20:39:23 +00002048 char **ap, **ap1;
2049 ap = ap1 = param->p;
2050 while (*ap)
2051 free(*ap++);
2052 free(ap1);
Denis Vlasenko01631112007-12-16 17:20:38 +00002053 }
2054}
2055
2056#if ENABLE_ASH_GETOPTS
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02002057static void FAST_FUNC getoptsreset(const char *value);
Denis Vlasenko01631112007-12-16 17:20:38 +00002058#endif
2059
2060struct var {
2061 struct var *next; /* next entry in hash list */
2062 int flags; /* flags are defined above */
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002063 const char *var_text; /* name=value */
2064 void (*var_func)(const char *) FAST_FUNC; /* function to be called when */
Denis Vlasenko01631112007-12-16 17:20:38 +00002065 /* the variable gets set/unset */
2066};
2067
2068struct localvar {
2069 struct localvar *next; /* next local variable in list */
2070 struct var *vp; /* the variable that was made local */
2071 int flags; /* saved flags */
2072 const char *text; /* saved text */
2073};
2074
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002075/* flags */
2076#define VEXPORT 0x01 /* variable is exported */
2077#define VREADONLY 0x02 /* variable cannot be modified */
2078#define VSTRFIXED 0x04 /* variable struct is statically allocated */
2079#define VTEXTFIXED 0x08 /* text is statically allocated */
2080#define VSTACK 0x10 /* text is allocated on the stack */
2081#define VUNSET 0x20 /* the variable is not set */
2082#define VNOFUNC 0x40 /* don't call the callback function */
2083#define VNOSET 0x80 /* do not set variable - just readonly test */
2084#define VNOSAVE 0x100 /* when text is on the heap before setvareq */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00002085#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002086# define VDYNAMIC 0x200 /* dynamic variable */
2087#else
2088# define VDYNAMIC 0
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002089#endif
2090
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00002091
Denis Vlasenko01631112007-12-16 17:20:38 +00002092/* Need to be before varinit_data[] */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00002093#if ENABLE_LOCALE_SUPPORT
Denys Vlasenko2634bf32009-06-09 18:40:07 +02002094static void FAST_FUNC
Denis Vlasenkoa8915072007-02-23 21:10:06 +00002095change_lc_all(const char *value)
2096{
2097 if (value && *value != '\0')
2098 setlocale(LC_ALL, value);
2099}
Denys Vlasenko2634bf32009-06-09 18:40:07 +02002100static void FAST_FUNC
Denis Vlasenkoa8915072007-02-23 21:10:06 +00002101change_lc_ctype(const char *value)
2102{
2103 if (value && *value != '\0')
2104 setlocale(LC_CTYPE, value);
2105}
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00002106#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002107#if ENABLE_ASH_MAIL
2108static void chkmail(void);
Denys Vlasenko8c52f802011-02-04 17:36:21 +01002109static void changemail(const char *var_value) FAST_FUNC;
2110#else
2111# define chkmail() ((void)0)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002112#endif
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02002113static void changepath(const char *) FAST_FUNC;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002114#if ENABLE_ASH_RANDOM_SUPPORT
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02002115static void change_random(const char *) FAST_FUNC;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002116#endif
Ron Yorston1d371862019-04-15 10:52:05 +01002117#if BASH_EPOCH_VARS
2118static void change_seconds(const char *) FAST_FUNC;
2119static void change_realtime(const char *) FAST_FUNC;
2120#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002121
Denis Vlasenko01631112007-12-16 17:20:38 +00002122static const struct {
2123 int flags;
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002124 const char *var_text;
2125 void (*var_func)(const char *) FAST_FUNC;
Denys Vlasenko965b7952020-11-30 13:03:03 +01002126} varinit_data[] ALIGN_PTR = {
Denys Vlasenko566a3132012-07-07 21:40:35 +02002127 /*
2128 * Note: VEXPORT would not work correctly here for NOFORK applets:
2129 * some environment strings may be constant.
2130 */
Denis Vlasenko01631112007-12-16 17:20:38 +00002131 { VSTRFIXED|VTEXTFIXED , defifsvar , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002132#if ENABLE_ASH_MAIL
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002133 { VSTRFIXED|VTEXTFIXED|VUNSET, "MAIL" , changemail },
2134 { VSTRFIXED|VTEXTFIXED|VUNSET, "MAILPATH" , changemail },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002135#endif
Denis Vlasenko01631112007-12-16 17:20:38 +00002136 { VSTRFIXED|VTEXTFIXED , bb_PATH_root_path, changepath },
2137 { VSTRFIXED|VTEXTFIXED , "PS1=$ " , NULL },
2138 { VSTRFIXED|VTEXTFIXED , "PS2=> " , NULL },
2139 { VSTRFIXED|VTEXTFIXED , "PS4=+ " , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002140#if ENABLE_ASH_GETOPTS
Denys Vlasenkoe627ac92016-09-30 14:36:59 +02002141 { VSTRFIXED|VTEXTFIXED , defoptindvar, getoptsreset },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002142#endif
Denys Vlasenko675d24a2018-01-27 22:02:05 +01002143 { VSTRFIXED|VTEXTFIXED , NULL /* inited to linenovar */, NULL },
Denys Vlasenko704c5962021-09-15 19:31:44 +02002144 { VSTRFIXED|VTEXTFIXED , NULL /* inited to funcnamevar */, NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002145#if ENABLE_ASH_RANDOM_SUPPORT
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002146 { VSTRFIXED|VTEXTFIXED|VUNSET|VDYNAMIC, "RANDOM", change_random },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002147#endif
Ron Yorston1d371862019-04-15 10:52:05 +01002148#if BASH_EPOCH_VARS
2149 { VSTRFIXED|VTEXTFIXED|VUNSET|VDYNAMIC, "EPOCHSECONDS", change_seconds },
2150 { VSTRFIXED|VTEXTFIXED|VUNSET|VDYNAMIC, "EPOCHREALTIME", change_realtime },
2151#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002152#if ENABLE_LOCALE_SUPPORT
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002153 { VSTRFIXED|VTEXTFIXED|VUNSET, "LC_ALL" , change_lc_all },
2154 { VSTRFIXED|VTEXTFIXED|VUNSET, "LC_CTYPE" , change_lc_ctype },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002155#endif
2156#if ENABLE_FEATURE_EDITING_SAVEHISTORY
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002157 { VSTRFIXED|VTEXTFIXED|VUNSET, "HISTFILE" , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002158#endif
2159};
2160
Denis Vlasenko0b769642008-07-24 07:54:57 +00002161struct redirtab;
Denis Vlasenko01631112007-12-16 17:20:38 +00002162
2163struct globals_var {
2164 struct shparam shellparam; /* $@ current positional parameters */
2165 struct redirtab *redirlist;
Denys Vlasenkod07a15b2017-07-30 16:51:05 +02002166 int preverrout_fd; /* stderr fd: usually 2, unless redirect moved it */
Denis Vlasenko01631112007-12-16 17:20:38 +00002167 struct var *vartab[VTABSIZE];
2168 struct var varinit[ARRAY_SIZE(varinit_data)];
Denys Vlasenko675d24a2018-01-27 22:02:05 +01002169 int lineno;
2170 char linenovar[sizeof("LINENO=") + sizeof(int)*3];
Denys Vlasenko704c5962021-09-15 19:31:44 +02002171 char funcnamevar[sizeof("FUNCNAME=") + 64];
2172 char *funcname;
Roberto A. Fogliettae0bf3df2021-09-07 01:19:31 +02002173 unsigned trap_depth;
2174 bool in_trap_ERR; /* ERR cannot recurse, no need to be a counter */
Denis Vlasenko01631112007-12-16 17:20:38 +00002175};
Denys Vlasenko6f9442f2018-01-28 20:41:23 +01002176extern struct globals_var *BB_GLOBAL_CONST ash_ptr_to_globals_var;
Denis Vlasenko574f2f42008-02-27 18:41:59 +00002177#define G_var (*ash_ptr_to_globals_var)
Denis Vlasenko01631112007-12-16 17:20:38 +00002178#define shellparam (G_var.shellparam )
Denis Vlasenko0b769642008-07-24 07:54:57 +00002179//#define redirlist (G_var.redirlist )
Denis Vlasenko01631112007-12-16 17:20:38 +00002180#define preverrout_fd (G_var.preverrout_fd)
2181#define vartab (G_var.vartab )
2182#define varinit (G_var.varinit )
Denys Vlasenko675d24a2018-01-27 22:02:05 +01002183#define lineno (G_var.lineno )
2184#define linenovar (G_var.linenovar )
Denys Vlasenko704c5962021-09-15 19:31:44 +02002185#define funcnamevar (G_var.funcnamevar )
2186#define funcname (G_var.funcname )
Roberto A. Fogliettae0bf3df2021-09-07 01:19:31 +02002187#define trap_depth (G_var.trap_depth )
2188#define in_trap_ERR (G_var.in_trap_ERR )
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00002189#define vifs varinit[0]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002190#if ENABLE_ASH_MAIL
Ron Yorston1d371862019-04-15 10:52:05 +01002191# define vmail varinit[1]
2192# define vmpath varinit[2]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002193#endif
Ron Yorston1d371862019-04-15 10:52:05 +01002194#define VAR_OFFSET1 (ENABLE_ASH_MAIL*2)
2195#define vpath varinit[VAR_OFFSET1 + 1]
2196#define vps1 varinit[VAR_OFFSET1 + 2]
2197#define vps2 varinit[VAR_OFFSET1 + 3]
2198#define vps4 varinit[VAR_OFFSET1 + 4]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002199#if ENABLE_ASH_GETOPTS
Ron Yorston1d371862019-04-15 10:52:05 +01002200# define voptind varinit[VAR_OFFSET1 + 5]
2201#endif
2202#define VAR_OFFSET2 (VAR_OFFSET1 + ENABLE_ASH_GETOPTS)
2203#define vlineno varinit[VAR_OFFSET2 + 5]
Denys Vlasenko704c5962021-09-15 19:31:44 +02002204#define vfuncname varinit[VAR_OFFSET2 + 6]
Ron Yorston1d371862019-04-15 10:52:05 +01002205#if ENABLE_ASH_RANDOM_SUPPORT
Denys Vlasenko704c5962021-09-15 19:31:44 +02002206# define vrandom varinit[VAR_OFFSET2 + 7]
Ron Yorston1d371862019-04-15 10:52:05 +01002207#endif
2208#define VAR_OFFSET3 (VAR_OFFSET2 + ENABLE_ASH_RANDOM_SUPPORT)
2209#if BASH_EPOCH_VARS
Denys Vlasenko704c5962021-09-15 19:31:44 +02002210# define vepochs varinit[VAR_OFFSET3 + 7]
2211# define vepochr varinit[VAR_OFFSET3 + 8]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002212#endif
Denys Vlasenko675d24a2018-01-27 22:02:05 +01002213#define INIT_G_var() do { \
2214 unsigned i; \
YU Jincheng5156b242021-10-10 02:19:51 +08002215 XZALLOC_CONST_PTR(&ash_ptr_to_globals_var, sizeof(G_var)); \
Denys Vlasenko675d24a2018-01-27 22:02:05 +01002216 for (i = 0; i < ARRAY_SIZE(varinit_data); i++) { \
2217 varinit[i].flags = varinit_data[i].flags; \
2218 varinit[i].var_text = varinit_data[i].var_text; \
2219 varinit[i].var_func = varinit_data[i].var_func; \
2220 } \
2221 strcpy(linenovar, "LINENO="); \
2222 vlineno.var_text = linenovar; \
Denys Vlasenko704c5962021-09-15 19:31:44 +02002223 strcpy(funcnamevar, "FUNCNAME="); \
2224 vfuncname.var_text = funcnamevar; \
Denys Vlasenko675d24a2018-01-27 22:02:05 +01002225} while (0)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002226
2227/*
2228 * The following macros access the values of the above variables.
2229 * They have to skip over the name. They return the null string
2230 * for unset variables.
2231 */
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002232#define ifsval() (vifs.var_text + 4)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002233#define ifsset() ((vifs.flags & VUNSET) == 0)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00002234#if ENABLE_ASH_MAIL
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002235# define mailval() (vmail.var_text + 5)
2236# define mpathval() (vmpath.var_text + 9)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00002237# define mpathset() ((vmpath.flags & VUNSET) == 0)
2238#endif
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002239#define pathval() (vpath.var_text + 5)
2240#define ps1val() (vps1.var_text + 4)
2241#define ps2val() (vps2.var_text + 4)
2242#define ps4val() (vps4.var_text + 4)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00002243#if ENABLE_ASH_GETOPTS
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002244# define optindval() (voptind.var_text + 7)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00002245#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002246
Denis Vlasenko01631112007-12-16 17:20:38 +00002247#if ENABLE_ASH_GETOPTS
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02002248static void FAST_FUNC
Denis Vlasenko01631112007-12-16 17:20:38 +00002249getoptsreset(const char *value)
2250{
Denys Vlasenko46289452017-08-11 00:59:36 +02002251 shellparam.optind = 1;
2252 if (is_number(value))
2253 shellparam.optind = number(value) ?: 1;
Denis Vlasenko01631112007-12-16 17:20:38 +00002254 shellparam.optoff = -1;
2255}
2256#endif
2257
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002258/*
2259 * Compares two strings up to the first = or '\0'. The first
2260 * string must be terminated by '='; the second may be terminated by
2261 * either '=' or '\0'.
2262 */
2263static int
2264varcmp(const char *p, const char *q)
2265{
2266 int c, d;
2267
2268 while ((c = *p) == (d = *q)) {
Denys Vlasenko0a0acb52015-04-18 19:36:38 +02002269 if (c == '\0' || c == '=')
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002270 goto out;
2271 p++;
2272 q++;
2273 }
2274 if (c == '=')
Denis Vlasenko9650f362007-02-23 01:04:37 +00002275 c = '\0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002276 if (d == '=')
Denis Vlasenko9650f362007-02-23 01:04:37 +00002277 d = '\0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002278 out:
2279 return c - d;
2280}
2281
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002282/*
2283 * Find the appropriate entry in the hash table from the name.
2284 */
2285static struct var **
2286hashvar(const char *p)
2287{
2288 unsigned hashval;
2289
2290 hashval = ((unsigned char) *p) << 4;
2291 while (*p && *p != '=')
2292 hashval += (unsigned char) *p++;
2293 return &vartab[hashval % VTABSIZE];
2294}
2295
2296static int
2297vpcmp(const void *a, const void *b)
2298{
2299 return varcmp(*(const char **)a, *(const char **)b);
2300}
2301
2302/*
2303 * This routine initializes the builtin variables.
2304 */
2305static void
2306initvar(void)
2307{
2308 struct var *vp;
2309 struct var *end;
2310 struct var **vpp;
2311
2312 /*
2313 * PS1 depends on uid
2314 */
2315#if ENABLE_FEATURE_EDITING && ENABLE_FEATURE_EDITING_FANCY_PROMPT
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002316 vps1.var_text = "PS1=\\w \\$ ";
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002317#else
2318 if (!geteuid())
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002319 vps1.var_text = "PS1=# ";
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002320#endif
2321 vp = varinit;
Denis Vlasenko80b8b392007-06-25 10:55:35 +00002322 end = vp + ARRAY_SIZE(varinit);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002323 do {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002324 vpp = hashvar(vp->var_text);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002325 vp->next = *vpp;
2326 *vpp = vp;
2327 } while (++vp < end);
2328}
2329
2330static struct var **
2331findvar(struct var **vpp, const char *name)
2332{
2333 for (; *vpp; vpp = &(*vpp)->next) {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002334 if (varcmp((*vpp)->var_text, name) == 0) {
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002335 break;
2336 }
2337 }
2338 return vpp;
2339}
2340
2341/*
2342 * Find the value of a variable. Returns NULL if not set.
2343 */
Denys Vlasenko03dad222010-01-12 23:29:57 +01002344static const char* FAST_FUNC
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002345lookupvar(const char *name)
2346{
2347 struct var *v;
2348
2349 v = *findvar(hashvar(name), name);
2350 if (v) {
Ron Yorston1d371862019-04-15 10:52:05 +01002351#if ENABLE_ASH_RANDOM_SUPPORT || BASH_EPOCH_VARS
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002352 /*
2353 * Dynamic variables are implemented roughly the same way they are
2354 * in bash. Namely, they're "special" so long as they aren't unset.
2355 * As soon as they're unset, they're no longer dynamic, and dynamic
2356 * lookup will no longer happen at that point. -- PFM.
2357 */
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002358 if (v->flags & VDYNAMIC)
2359 v->var_func(NULL);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002360#endif
Denys Vlasenko675d24a2018-01-27 22:02:05 +01002361 if (!(v->flags & VUNSET)) {
Denys Vlasenkod6c9cbc2021-09-07 18:01:49 +02002362 if (v->var_text == linenovar) {
Denys Vlasenko675d24a2018-01-27 22:02:05 +01002363 fmtstr(linenovar+7, sizeof(linenovar)-7, "%d", lineno);
Denys Vlasenko704c5962021-09-15 19:31:44 +02002364 } else
2365 if (v->var_text == funcnamevar) {
2366 safe_strncpy(funcnamevar+9, funcname ? funcname : "", sizeof(funcnamevar)-9);
Denys Vlasenko675d24a2018-01-27 22:02:05 +01002367 }
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002368 return var_end(v->var_text);
Denys Vlasenko675d24a2018-01-27 22:02:05 +01002369 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002370 }
2371 return NULL;
2372}
2373
Denys Vlasenko0b883582016-12-23 16:49:07 +01002374#if ENABLE_UNICODE_SUPPORT
Denys Vlasenko37dc08b2016-10-02 04:38:07 +02002375static void
2376reinit_unicode_for_ash(void)
Denys Vlasenkoe9ab07c2014-08-13 18:00:08 +02002377{
2378 /* Unicode support should be activated even if LANG is set
2379 * _during_ shell execution, not only if it was set when
2380 * shell was started. Therefore, re-check LANG every time:
2381 */
2382 if (ENABLE_FEATURE_CHECK_UNICODE_IN_ENV
2383 || ENABLE_UNICODE_USING_LOCALE
2384 ) {
2385 const char *s = lookupvar("LC_ALL");
2386 if (!s) s = lookupvar("LC_CTYPE");
2387 if (!s) s = lookupvar("LANG");
2388 reinit_unicode(s);
2389 }
2390}
Denys Vlasenko0b883582016-12-23 16:49:07 +01002391#else
2392# define reinit_unicode_for_ash() ((void)0)
2393#endif
Denys Vlasenkoe9ab07c2014-08-13 18:00:08 +02002394
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002395/*
2396 * Search the environment of a builtin command.
2397 */
Denys Vlasenko488e6092017-07-26 23:08:36 +02002398static ALWAYS_INLINE const char *
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002399bltinlookup(const char *name)
2400{
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002401 return lookupvar(name);
2402}
2403
2404/*
2405 * Same as setvar except that the variable and value are passed in
2406 * the first argument as name=value. Since the first argument will
2407 * be actually stored in the table, it should not be a string that
2408 * will go away.
2409 * Called with interrupts off.
2410 */
Denys Vlasenkod04fc712017-07-26 20:06:48 +02002411static struct var *
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002412setvareq(char *s, int flags)
2413{
2414 struct var *vp, **vpp;
2415
2416 vpp = hashvar(s);
2417 flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1));
Denys Vlasenkod04fc712017-07-26 20:06:48 +02002418 vpp = findvar(vpp, s);
2419 vp = *vpp;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002420 if (vp) {
2421 if ((vp->flags & (VREADONLY|VDYNAMIC)) == VREADONLY) {
2422 const char *n;
2423
2424 if (flags & VNOSAVE)
2425 free(s);
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002426 n = vp->var_text;
Denys Vlasenkod81e9f52016-10-28 15:43:50 +02002427 exitstatus = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002428 ash_msg_and_raise_error("%.*s: is read only", strchrnul(n, '=') - n, n);
2429 }
2430
2431 if (flags & VNOSET)
Denys Vlasenkod04fc712017-07-26 20:06:48 +02002432 goto out;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002433
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002434 if (vp->var_func && !(flags & VNOFUNC))
2435 vp->var_func(var_end(s));
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002436
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002437 if (!(vp->flags & (VTEXTFIXED|VSTACK)))
2438 free((char*)vp->var_text);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002439
Denys Vlasenkob28d4c32017-07-25 16:29:36 +02002440 if (((flags & (VEXPORT|VREADONLY|VSTRFIXED|VUNSET)) | (vp->flags & VSTRFIXED)) == VUNSET) {
2441 *vpp = vp->next;
2442 free(vp);
2443 out_free:
2444 if ((flags & (VTEXTFIXED|VSTACK|VNOSAVE)) == VNOSAVE)
2445 free(s);
Denys Vlasenkod04fc712017-07-26 20:06:48 +02002446 goto out;
Denys Vlasenkob28d4c32017-07-25 16:29:36 +02002447 }
2448
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002449 flags |= vp->flags & ~(VTEXTFIXED|VSTACK|VNOSAVE|VUNSET);
Ron Yorston1d371862019-04-15 10:52:05 +01002450#if ENABLE_ASH_RANDOM_SUPPORT || BASH_EPOCH_VARS
Ron Yorstond96c69d2019-04-15 10:49:35 +01002451 if (flags & VUNSET)
2452 flags &= ~VDYNAMIC;
2453#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002454 } else {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002455 /* variable s is not found */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002456 if (flags & VNOSET)
Denys Vlasenkod04fc712017-07-26 20:06:48 +02002457 goto out;
Denys Vlasenkob28d4c32017-07-25 16:29:36 +02002458 if ((flags & (VEXPORT|VREADONLY|VSTRFIXED|VUNSET)) == VUNSET)
2459 goto out_free;
Denis Vlasenko597906c2008-02-20 16:38:54 +00002460 vp = ckzalloc(sizeof(*vp));
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002461 vp->next = *vpp;
Denis Vlasenko597906c2008-02-20 16:38:54 +00002462 /*vp->func = NULL; - ckzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002463 *vpp = vp;
2464 }
2465 if (!(flags & (VTEXTFIXED|VSTACK|VNOSAVE)))
2466 s = ckstrdup(s);
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002467 vp->var_text = s;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002468 vp->flags = flags;
Denys Vlasenkod04fc712017-07-26 20:06:48 +02002469
2470 out:
2471 return vp;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002472}
2473
2474/*
2475 * Set the value of a variable. The flags argument is ored with the
2476 * flags of the variable. If val is NULL, the variable is unset.
2477 */
Denys Vlasenkod04fc712017-07-26 20:06:48 +02002478static struct var *
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002479setvar(const char *name, const char *val, int flags)
2480{
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002481 const char *q;
2482 char *p;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002483 char *nameeq;
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002484 size_t namelen;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002485 size_t vallen;
Denys Vlasenkod04fc712017-07-26 20:06:48 +02002486 struct var *vp;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002487
2488 q = endofname(name);
2489 p = strchrnul(q, '=');
2490 namelen = p - name;
2491 if (!namelen || p != q)
2492 ash_msg_and_raise_error("%.*s: bad variable name", namelen, name);
2493 vallen = 0;
2494 if (val == NULL) {
2495 flags |= VUNSET;
2496 } else {
2497 vallen = strlen(val);
2498 }
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002499
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002500 INT_OFF;
Ron Yorstone6a63bf2018-11-12 21:10:54 +00002501 nameeq = ckzalloc(namelen + vallen + 2);
Denys Vlasenkoda2244f2017-07-21 18:51:29 +02002502 p = mempcpy(nameeq, name, namelen);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002503 if (val) {
2504 *p++ = '=';
Ron Yorstone6a63bf2018-11-12 21:10:54 +00002505 memcpy(p, val, vallen);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002506 }
Denys Vlasenkod04fc712017-07-26 20:06:48 +02002507 vp = setvareq(nameeq, flags | VNOSAVE);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002508 INT_ON;
Denys Vlasenkod04fc712017-07-26 20:06:48 +02002509
2510 return vp;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002511}
2512
Denys Vlasenko03dad222010-01-12 23:29:57 +01002513static void FAST_FUNC
Denys Vlasenko0a0acb52015-04-18 19:36:38 +02002514setvar0(const char *name, const char *val)
Denys Vlasenko03dad222010-01-12 23:29:57 +01002515{
2516 setvar(name, val, 0);
2517}
2518
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002519/*
2520 * Unset the specified variable.
2521 */
Denys Vlasenkob28d4c32017-07-25 16:29:36 +02002522static void
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002523unsetvar(const char *s)
2524{
Denys Vlasenkocf3a7962017-07-26 14:38:19 +02002525 setvar(s, NULL, 0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002526}
2527
2528/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002529 * Generate a list of variables satisfying the given conditions.
2530 */
Denys Vlasenkoa5060b82017-11-03 14:16:25 +01002531#if !ENABLE_FEATURE_SH_NOFORK
2532# define listvars(on, off, lp, end) listvars(on, off, end)
2533#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002534static char **
Denys Vlasenkoa5060b82017-11-03 14:16:25 +01002535listvars(int on, int off, struct strlist *lp, char ***end)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002536{
2537 struct var **vpp;
2538 struct var *vp;
2539 char **ep;
2540 int mask;
2541
2542 STARTSTACKSTR(ep);
2543 vpp = vartab;
2544 mask = on | off;
2545 do {
2546 for (vp = *vpp; vp; vp = vp->next) {
2547 if ((vp->flags & mask) == on) {
Denys Vlasenkoa5060b82017-11-03 14:16:25 +01002548#if ENABLE_FEATURE_SH_NOFORK
2549 /* If variable with the same name is both
2550 * exported and temporarily set for a command:
2551 * export ZVAR=5
2552 * ZVAR=6 printenv
2553 * then "ZVAR=6" will be both in vartab and
2554 * lp lists. Do not pass it twice to printenv.
2555 */
2556 struct strlist *lp1 = lp;
2557 while (lp1) {
2558 if (strcmp(lp1->text, vp->var_text) == 0)
2559 goto skip;
2560 lp1 = lp1->next;
2561 }
2562#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002563 if (ep == stackstrend())
2564 ep = growstackstr();
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002565 *ep++ = (char*)vp->var_text;
Denys Vlasenkoa5060b82017-11-03 14:16:25 +01002566#if ENABLE_FEATURE_SH_NOFORK
2567 skip: ;
2568#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002569 }
2570 }
2571 } while (++vpp < vartab + VTABSIZE);
Denys Vlasenkoa5060b82017-11-03 14:16:25 +01002572
2573#if ENABLE_FEATURE_SH_NOFORK
2574 while (lp) {
2575 if (ep == stackstrend())
2576 ep = growstackstr();
2577 *ep++ = lp->text;
2578 lp = lp->next;
2579 }
2580#endif
2581
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002582 if (ep == stackstrend())
2583 ep = growstackstr();
2584 if (end)
2585 *end = ep;
2586 *ep++ = NULL;
2587 return grabstackstr(ep);
2588}
2589
2590
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +01002591/* ============ Path search helper */
2592static const char *
2593legal_pathopt(const char *opt, const char *term, int magic)
2594{
2595 switch (magic) {
2596 case 0:
2597 opt = NULL;
2598 break;
2599
2600 case 1:
2601 opt = prefix(opt, "builtin") ?: prefix(opt, "func");
2602 break;
2603
2604 default:
2605 opt += strcspn(opt, term);
2606 break;
2607 }
2608
2609 if (opt && *opt == '%')
2610 opt++;
2611
2612 return opt;
2613}
2614
2615/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002616 * The variable path (passed by reference) should be set to the start
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +01002617 * of the path before the first call; padvance will update
2618 * this value as it proceeds. Successive calls to padvance will return
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002619 * the possible path expansions in sequence. If an option (indicated by
2620 * a percent sign) appears in the path entry then the global variable
2621 * pathopt will be set to point to it; otherwise pathopt will be set to
2622 * NULL.
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +01002623 *
2624 * If magic is 0 then pathopt recognition will be disabled. If magic is
2625 * 1 we shall recognise %builtin/%func. Otherwise we shall accept any
2626 * pathopt.
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002627 */
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +01002628static const char *pathopt; /* set by padvance */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002629
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +01002630static int
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +01002631padvance_magic(const char **path, const char *name, int magic)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002632{
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +01002633 const char *term = "%:";
2634 const char *lpathopt;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002635 const char *p;
2636 char *q;
2637 const char *start;
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +01002638 size_t qlen;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002639 size_t len;
2640
2641 if (*path == NULL)
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +01002642 return -1;
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +01002643
2644 lpathopt = NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002645 start = *path;
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +01002646
2647 if (*start == '%' && (p = legal_pathopt(start + 1, term, magic))) {
2648 lpathopt = start + 1;
2649 start = p;
2650 term = ":";
2651 }
2652
2653 len = strcspn(start, term);
2654 p = start + len;
2655
2656 if (*p == '%') {
2657 size_t extra = strchrnul(p, ':') - p;
2658
2659 if (legal_pathopt(p + 1, term, magic))
2660 lpathopt = p + 1;
2661 else
2662 len += extra;
2663
2664 p += extra;
2665 }
2666
2667 pathopt = lpathopt;
2668 *path = *p == ':' ? p + 1 : NULL;
2669
2670 /* "2" is for '/' and '\0' */
2671 qlen = len + strlen(name) + 2;
2672 q = growstackto(qlen);
2673
2674 if (len) {
2675 q = mempcpy(q, start, len);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002676 *q++ = '/';
2677 }
2678 strcpy(q, name);
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +01002679
2680 return qlen;
2681}
2682
2683static int
2684padvance(const char **path, const char *name)
2685{
2686 return padvance_magic(path, name, 1);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002687}
2688
2689
2690/* ============ Prompt */
2691
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00002692static smallint doprompt; /* if set, prompt the user */
2693static smallint needprompt; /* true if interactive and at start of line */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002694
2695#if ENABLE_FEATURE_EDITING
2696static line_input_t *line_input_state;
2697static const char *cmdedit_prompt;
2698static void
2699putprompt(const char *s)
2700{
2701 if (ENABLE_ASH_EXPAND_PRMT) {
2702 free((char*)cmdedit_prompt);
Denis Vlasenko4222ae42007-02-25 02:37:49 +00002703 cmdedit_prompt = ckstrdup(s);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002704 return;
2705 }
2706 cmdedit_prompt = s;
2707}
2708#else
2709static void
2710putprompt(const char *s)
2711{
2712 out2str(s);
2713}
2714#endif
2715
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002716/* expandstr() needs parsing machinery, so it is far away ahead... */
Denys Vlasenko46999802017-07-29 21:12:29 +02002717static const char *expandstr(const char *ps, int syntax_type);
2718/* Values for syntax param */
2719#define BASESYNTAX 0 /* not in quotes */
2720#define DQSYNTAX 1 /* in double quotes */
2721#define SQSYNTAX 2 /* in single quotes */
2722#define ARISYNTAX 3 /* in arithmetic */
Denys Vlasenko5f0a75f2017-07-29 22:58:44 +02002723#if ENABLE_ASH_EXPAND_PRMT
2724# define PSSYNTAX 4 /* prompt. never passed to SIT() */
2725#endif
Denys Vlasenko46999802017-07-29 21:12:29 +02002726/* PSSYNTAX expansion is identical to DQSYNTAX, except keeping '\$' as '\$' */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002727
Denys Vlasenko46999802017-07-29 21:12:29 +02002728/*
2729 * called by editline -- any expansions to the prompt should be added here.
2730 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002731static void
Denys Vlasenko958581a2010-09-12 15:04:27 +02002732setprompt_if(smallint do_set, int whichprompt)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002733{
2734 const char *prompt;
Denys Vlasenko958581a2010-09-12 15:04:27 +02002735 IF_ASH_EXPAND_PRMT(struct stackmark smark;)
2736
2737 if (!do_set)
2738 return;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002739
2740 needprompt = 0;
2741
2742 switch (whichprompt) {
2743 case 1:
2744 prompt = ps1val();
2745 break;
2746 case 2:
2747 prompt = ps2val();
2748 break;
2749 default: /* 0 */
2750 prompt = nullstr;
2751 }
2752#if ENABLE_ASH_EXPAND_PRMT
Denys Vlasenko60ca8342016-09-30 11:21:21 +02002753 pushstackmark(&smark, stackblocksize());
Denys Vlasenko46999802017-07-29 21:12:29 +02002754 putprompt(expandstr(prompt, PSSYNTAX));
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002755 popstackmark(&smark);
Denys Vlasenko48c803a2017-07-01 23:24:48 +02002756#else
2757 putprompt(prompt);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002758#endif
2759}
2760
2761
2762/* ============ The cd and pwd commands */
2763
2764#define CD_PHYSICAL 1
2765#define CD_PRINT 2
2766
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002767static int
2768cdopt(void)
2769{
2770 int flags = 0;
2771 int i, j;
2772
2773 j = 'L';
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02002774 while ((i = nextopt("LP")) != '\0') {
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002775 if (i != j) {
2776 flags ^= CD_PHYSICAL;
2777 j = i;
2778 }
2779 }
2780
2781 return flags;
2782}
2783
2784/*
2785 * Update curdir (the name of the current directory) in response to a
2786 * cd command.
2787 */
2788static const char *
2789updatepwd(const char *dir)
2790{
2791 char *new;
2792 char *p;
2793 char *cdcomppath;
2794 const char *lim;
2795
Denys Vlasenko8e2bc472016-09-28 23:02:57 +02002796 cdcomppath = sstrdup(dir);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002797 STARTSTACKSTR(new);
2798 if (*dir != '/') {
2799 if (curdir == nullstr)
2800 return 0;
2801 new = stack_putstr(curdir, new);
2802 }
2803 new = makestrspace(strlen(dir) + 2, new);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00002804 lim = (char *)stackblock() + 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002805 if (*dir != '/') {
2806 if (new[-1] != '/')
2807 USTPUTC('/', new);
2808 if (new > lim && *lim == '/')
2809 lim++;
2810 } else {
2811 USTPUTC('/', new);
2812 cdcomppath++;
2813 if (dir[1] == '/' && dir[2] != '/') {
2814 USTPUTC('/', new);
2815 cdcomppath++;
2816 lim++;
2817 }
2818 }
Denys Vlasenko24966162020-10-06 02:36:47 +02002819 p = strtok_r(cdcomppath, "/", &cdcomppath);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002820 while (p) {
2821 switch (*p) {
2822 case '.':
2823 if (p[1] == '.' && p[2] == '\0') {
2824 while (new > lim) {
2825 STUNPUTC(new);
2826 if (new[-1] == '/')
2827 break;
2828 }
2829 break;
Denis Vlasenko16abcd92007-04-13 23:59:52 +00002830 }
2831 if (p[1] == '\0')
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002832 break;
2833 /* fall through */
2834 default:
2835 new = stack_putstr(p, new);
2836 USTPUTC('/', new);
2837 }
Denys Vlasenko24966162020-10-06 02:36:47 +02002838 p = strtok_r(NULL, "/", &cdcomppath);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002839 }
2840 if (new > lim)
2841 STUNPUTC(new);
2842 *new = 0;
2843 return stackblock();
2844}
2845
2846/*
2847 * Find out what the current directory is. If we already know the current
2848 * directory, this routine returns immediately.
2849 */
2850static char *
2851getpwd(void)
2852{
Denis Vlasenko01631112007-12-16 17:20:38 +00002853 char *dir = getcwd(NULL, 0); /* huh, using glibc extension? */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002854 return dir ? dir : nullstr;
2855}
2856
2857static void
2858setpwd(const char *val, int setold)
2859{
2860 char *oldcur, *dir;
2861
2862 oldcur = dir = curdir;
2863
2864 if (setold) {
2865 setvar("OLDPWD", oldcur, VEXPORT);
2866 }
2867 INT_OFF;
2868 if (physdir != nullstr) {
2869 if (physdir != oldcur)
2870 free(physdir);
2871 physdir = nullstr;
2872 }
2873 if (oldcur == val || !val) {
2874 char *s = getpwd();
2875 physdir = s;
2876 if (!val)
2877 dir = s;
2878 } else
2879 dir = ckstrdup(val);
2880 if (oldcur != dir && oldcur != nullstr) {
2881 free(oldcur);
2882 }
2883 curdir = dir;
2884 INT_ON;
2885 setvar("PWD", dir, VEXPORT);
2886}
2887
2888static void hashcd(void);
2889
2890/*
Denys Vlasenko70392332016-10-27 02:31:55 +02002891 * Actually do the chdir. We also call hashcd to let other routines
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002892 * know that the current directory has changed.
2893 */
2894static int
2895docd(const char *dest, int flags)
2896{
Denys Vlasenko4b1100e2010-03-05 14:10:54 +01002897 const char *dir = NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002898 int err;
2899
2900 TRACE(("docd(\"%s\", %d) called\n", dest, flags));
2901
2902 INT_OFF;
2903 if (!(flags & CD_PHYSICAL)) {
2904 dir = updatepwd(dest);
2905 if (dir)
2906 dest = dir;
2907 }
2908 err = chdir(dest);
2909 if (err)
2910 goto out;
2911 setpwd(dir, 1);
2912 hashcd();
2913 out:
2914 INT_ON;
2915 return err;
2916}
2917
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02002918static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00002919cdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002920{
2921 const char *dest;
2922 const char *path;
2923 const char *p;
2924 char c;
2925 struct stat statb;
2926 int flags;
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +01002927 int len;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002928
2929 flags = cdopt();
2930 dest = *argptr;
2931 if (!dest)
Denys Vlasenkoea8b2522010-06-02 12:57:26 +02002932 dest = bltinlookup("HOME");
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002933 else if (LONE_DASH(dest)) {
2934 dest = bltinlookup("OLDPWD");
2935 flags |= CD_PRINT;
2936 }
2937 if (!dest)
2938 dest = nullstr;
2939 if (*dest == '/')
Denys Vlasenko0e081d02016-10-26 19:56:05 +02002940 goto step6;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002941 if (*dest == '.') {
2942 c = dest[1];
2943 dotdot:
2944 switch (c) {
2945 case '\0':
2946 case '/':
2947 goto step6;
2948 case '.':
2949 c = dest[2];
2950 if (c != '.')
2951 goto dotdot;
2952 }
2953 }
2954 if (!*dest)
2955 dest = ".";
2956 path = bltinlookup("CDPATH");
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +01002957 while (p = path, (len = padvance(&path, dest)) >= 0) {
2958 c = *p;
2959 p = stalloc(len);
2960
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002961 if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) {
2962 if (c && c != ':')
2963 flags |= CD_PRINT;
2964 docd:
2965 if (!docd(p, flags))
2966 goto out;
Denys Vlasenko0e081d02016-10-26 19:56:05 +02002967 goto err;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002968 }
Denys Vlasenko0e081d02016-10-26 19:56:05 +02002969 }
2970
2971 step6:
2972 p = dest;
2973 goto docd;
2974
2975 err:
Johannes Schindelin687aac02017-08-22 22:03:22 +02002976 ash_msg_and_raise_perror("can't cd to %s", dest);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002977 /* NOTREACHED */
2978 out:
2979 if (flags & CD_PRINT)
Denys Vlasenkoea8b2522010-06-02 12:57:26 +02002980 out1fmt("%s\n", curdir);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002981 return 0;
2982}
2983
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02002984static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00002985pwdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002986{
2987 int flags;
2988 const char *dir = curdir;
2989
2990 flags = cdopt();
2991 if (flags) {
2992 if (physdir == nullstr)
2993 setpwd(dir, 0);
2994 dir = physdir;
2995 }
Denys Vlasenkoea8b2522010-06-02 12:57:26 +02002996 out1fmt("%s\n", dir);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002997 return 0;
2998}
2999
Denis Vlasenko0c032a42007-02-23 01:03:40 +00003000
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00003001/* ============ ... */
Eric Andersenc470f442003-07-28 09:56:35 +00003002
Denis Vlasenko834dee72008-10-07 09:18:30 +00003003
Denys Vlasenko82dd14a2010-05-17 10:10:01 +02003004#define IBUFSIZ (ENABLE_FEATURE_EDITING ? CONFIG_FEATURE_EDITING_MAX_LEN : 1024)
Eric Andersenc470f442003-07-28 09:56:35 +00003005
Eric Andersenc470f442003-07-28 09:56:35 +00003006/* Syntax classes */
Denis Vlasenko834dee72008-10-07 09:18:30 +00003007#define CWORD 0 /* character is nothing special */
3008#define CNL 1 /* newline character */
3009#define CBACK 2 /* a backslash character */
3010#define CSQUOTE 3 /* single quote */
3011#define CDQUOTE 4 /* double quote */
Eric Andersenc470f442003-07-28 09:56:35 +00003012#define CENDQUOTE 5 /* a terminating quote */
Denis Vlasenko834dee72008-10-07 09:18:30 +00003013#define CBQUOTE 6 /* backwards single quote */
3014#define CVAR 7 /* a dollar sign */
3015#define CENDVAR 8 /* a '}' character */
3016#define CLP 9 /* a left paren in arithmetic */
3017#define CRP 10 /* a right paren in arithmetic */
Eric Andersenc470f442003-07-28 09:56:35 +00003018#define CENDFILE 11 /* end of file */
Denis Vlasenko834dee72008-10-07 09:18:30 +00003019#define CCTL 12 /* like CWORD, except it must be escaped */
3020#define CSPCL 13 /* these terminate a word */
Eric Andersenc470f442003-07-28 09:56:35 +00003021
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01003022#define PEOF 256
Eric Andersenc470f442003-07-28 09:56:35 +00003023
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01003024#define USE_SIT_FUNCTION ENABLE_ASH_OPTIMIZE_FOR_SIZE
Manuel Novoa III 16815d42001-08-10 19:36:07 +00003025
Denys Vlasenko0b883582016-12-23 16:49:07 +01003026#if ENABLE_FEATURE_SH_MATH
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01003027# define SIT_ITEM(a,b,c,d) (a | (b << 4) | (c << 8) | (d << 12))
Eric Andersenc470f442003-07-28 09:56:35 +00003028#else
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01003029# define SIT_ITEM(a,b,c,d) (a | (b << 4) | (c << 8))
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01003030#endif
Denys Vlasenko3e134eb2016-04-22 18:09:21 +02003031static const uint16_t S_I_T[] ALIGN2 = {
Denys Vlasenko48cb9832021-09-08 09:52:04 +02003032 SIT_ITEM(CSPCL , CWORD , CWORD, CWORD ), /* 0, ' ' */
3033 SIT_ITEM(CNL , CNL , CNL , CNL ), /* 1, \n */
3034 SIT_ITEM(CWORD , CCTL , CCTL , CWORD ), /* 2, !*-/:=?[]~ */
3035 SIT_ITEM(CDQUOTE , CENDQUOTE, CWORD, CWORD ), /* 3, '"' */
3036 SIT_ITEM(CVAR , CVAR , CWORD, CVAR ), /* 4, $ */
3037 SIT_ITEM(CSQUOTE , CWORD , CENDQUOTE, CWORD), /* 5, "'" */
3038 SIT_ITEM(CSPCL , CWORD , CWORD, CLP ), /* 6, ( */
3039 SIT_ITEM(CSPCL , CWORD , CWORD, CRP ), /* 7, ) */
3040 SIT_ITEM(CBACK , CBACK , CCTL , CBACK ), /* 8, \ */
3041 SIT_ITEM(CBQUOTE , CBQUOTE , CWORD, CBQUOTE), /* 9, ` */
3042 SIT_ITEM(CENDVAR , CENDVAR , CWORD, CENDVAR), /* 10, } */
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01003043#if !USE_SIT_FUNCTION
Denys Vlasenko48cb9832021-09-08 09:52:04 +02003044 SIT_ITEM(CENDFILE, CENDFILE , CENDFILE, CENDFILE),/* 11, PEOF */
3045 SIT_ITEM(CWORD , CWORD , CWORD, CWORD ), /* 12, 0-9A-Za-z */
3046 SIT_ITEM(CCTL , CCTL , CCTL , CCTL ) /* 13, CTLESC ... */
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01003047#endif
3048#undef SIT_ITEM
Eric Andersenc470f442003-07-28 09:56:35 +00003049};
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01003050/* Constants below must match table above */
3051enum {
Denys Vlasenko48cb9832021-09-08 09:52:04 +02003052 CSPCL_CWORD_CWORD_CWORD , /* 0 */
3053 CNL_CNL_CNL_CNL , /* 1 */
3054 CWORD_CCTL_CCTL_CWORD , /* 2 */
3055 CDQUOTE_CENDQUOTE_CWORD_CWORD , /* 3 */
3056 CVAR_CVAR_CWORD_CVAR , /* 4 */
3057 CSQUOTE_CWORD_CENDQUOTE_CWORD , /* 5 */
3058 CSPCL_CWORD_CWORD_CLP , /* 6 */
3059 CSPCL_CWORD_CWORD_CRP , /* 7 */
3060 CBACK_CBACK_CCTL_CBACK , /* 8 */
3061 CBQUOTE_CBQUOTE_CWORD_CBQUOTE , /* 9 */
3062 CENDVAR_CENDVAR_CWORD_CENDVAR , /* 10 */
3063 CENDFILE_CENDFILE_CENDFILE_CENDFILE, /* 11 */
3064 CWORD_CWORD_CWORD_CWORD , /* 12 */
3065 CCTL_CCTL_CCTL_CCTL , /* 13 */
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01003066};
Eric Andersen2870d962001-07-02 17:27:21 +00003067
Denys Vlasenko48cb9832021-09-08 09:52:04 +02003068/* c in SIT(c, syntax) must be an *unsigned char* or PEOF,
Denys Vlasenkocd716832009-11-28 22:14:02 +01003069 * caller must ensure proper cast on it if c is *char_ptr!
3070 */
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01003071#if USE_SIT_FUNCTION
Manuel Novoa III 16815d42001-08-10 19:36:07 +00003072
Denis Vlasenko0c032a42007-02-23 01:03:40 +00003073static int
3074SIT(int c, int syntax)
Manuel Novoa III 16815d42001-08-10 19:36:07 +00003075{
Denys Vlasenkob3f29b42016-09-21 16:25:58 +02003076 /* Used to also have '/' in this string: "\t\n !\"$&'()*-/:;<=>?[\\]`|}~" */
3077 static const char spec_symbls[] ALIGN1 = "\t\n !\"$&'()*-:;<=>?[\\]`|}~";
3078 /*
3079 * This causes '/' to be prepended with CTLESC in dquoted string,
3080 * making "./file"* treated incorrectly because we feed
3081 * ".\/file*" string to glob(), confusing it (see expandmeta func).
3082 * The "homegrown" glob implementation is okay with that,
3083 * but glibc one isn't. With '/' always treated as CWORD,
3084 * both work fine.
3085 */
Denys Vlasenkocd716832009-11-28 22:14:02 +01003086 static const uint8_t syntax_index_table[] ALIGN1 = {
Eric Andersenc470f442003-07-28 09:56:35 +00003087 0, 1, 0, 2, 3, 4, 0, 5, /* "\t\n !\"$&'" */
Denys Vlasenkob3f29b42016-09-21 16:25:58 +02003088 6, 7, 2, 2,/*2,*/2, 0, 0, /* "()*-/:;<" */
Eric Andersenc470f442003-07-28 09:56:35 +00003089 2, 0, 2, 2, 8, 2, 9, 0, /* "=>?[\\]`|" */
3090 10, 2 /* "}~" */
3091 };
Manuel Novoa III 16815d42001-08-10 19:36:07 +00003092 const char *s;
3093 int indx;
3094
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01003095 if (c == PEOF)
Manuel Novoa III 16815d42001-08-10 19:36:07 +00003096 return CENDFILE;
Denys Vlasenko48cb9832021-09-08 09:52:04 +02003097 /* Cast is purely for paranoia here,
3098 * just in case someone passed signed char to us */
3099 if ((unsigned char)c >= CTL_FIRST
3100 && (unsigned char)c <= CTL_LAST
3101 ) {
3102 return CCTL;
Denis Vlasenko0dfe1d22009-04-02 12:57:38 +00003103 }
Denys Vlasenko48cb9832021-09-08 09:52:04 +02003104 s = strchrnul(spec_symbls, c);
3105 if (*s == '\0')
3106 return CWORD;
3107 indx = syntax_index_table[s - spec_symbls];
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01003108 return (S_I_T[indx] >> (syntax*4)) & 0xf;
Manuel Novoa III 16815d42001-08-10 19:36:07 +00003109}
3110
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00003111#else /* !USE_SIT_FUNCTION */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00003112
Denys Vlasenko3e134eb2016-04-22 18:09:21 +02003113static const uint8_t syntax_index_table[] ALIGN1 = {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003114 /* BASESYNTAX_DQSYNTAX_SQSYNTAX_ARISYNTAX */
Denys Vlasenkocd716832009-11-28 22:14:02 +01003115 /* 0 */ CWORD_CWORD_CWORD_CWORD,
3116 /* 1 */ CWORD_CWORD_CWORD_CWORD,
3117 /* 2 */ CWORD_CWORD_CWORD_CWORD,
3118 /* 3 */ CWORD_CWORD_CWORD_CWORD,
3119 /* 4 */ CWORD_CWORD_CWORD_CWORD,
3120 /* 5 */ CWORD_CWORD_CWORD_CWORD,
3121 /* 6 */ CWORD_CWORD_CWORD_CWORD,
3122 /* 7 */ CWORD_CWORD_CWORD_CWORD,
3123 /* 8 */ CWORD_CWORD_CWORD_CWORD,
3124 /* 9 "\t" */ CSPCL_CWORD_CWORD_CWORD,
3125 /* 10 "\n" */ CNL_CNL_CNL_CNL,
3126 /* 11 */ CWORD_CWORD_CWORD_CWORD,
3127 /* 12 */ CWORD_CWORD_CWORD_CWORD,
3128 /* 13 */ CWORD_CWORD_CWORD_CWORD,
3129 /* 14 */ CWORD_CWORD_CWORD_CWORD,
3130 /* 15 */ CWORD_CWORD_CWORD_CWORD,
3131 /* 16 */ CWORD_CWORD_CWORD_CWORD,
3132 /* 17 */ CWORD_CWORD_CWORD_CWORD,
3133 /* 18 */ CWORD_CWORD_CWORD_CWORD,
3134 /* 19 */ CWORD_CWORD_CWORD_CWORD,
3135 /* 20 */ CWORD_CWORD_CWORD_CWORD,
3136 /* 21 */ CWORD_CWORD_CWORD_CWORD,
3137 /* 22 */ CWORD_CWORD_CWORD_CWORD,
3138 /* 23 */ CWORD_CWORD_CWORD_CWORD,
3139 /* 24 */ CWORD_CWORD_CWORD_CWORD,
3140 /* 25 */ CWORD_CWORD_CWORD_CWORD,
3141 /* 26 */ CWORD_CWORD_CWORD_CWORD,
3142 /* 27 */ CWORD_CWORD_CWORD_CWORD,
3143 /* 28 */ CWORD_CWORD_CWORD_CWORD,
3144 /* 29 */ CWORD_CWORD_CWORD_CWORD,
3145 /* 30 */ CWORD_CWORD_CWORD_CWORD,
3146 /* 31 */ CWORD_CWORD_CWORD_CWORD,
3147 /* 32 " " */ CSPCL_CWORD_CWORD_CWORD,
3148 /* 33 "!" */ CWORD_CCTL_CCTL_CWORD,
3149 /* 34 """ */ CDQUOTE_CENDQUOTE_CWORD_CWORD,
3150 /* 35 "#" */ CWORD_CWORD_CWORD_CWORD,
3151 /* 36 "$" */ CVAR_CVAR_CWORD_CVAR,
3152 /* 37 "%" */ CWORD_CWORD_CWORD_CWORD,
3153 /* 38 "&" */ CSPCL_CWORD_CWORD_CWORD,
3154 /* 39 "'" */ CSQUOTE_CWORD_CENDQUOTE_CWORD,
3155 /* 40 "(" */ CSPCL_CWORD_CWORD_CLP,
3156 /* 41 ")" */ CSPCL_CWORD_CWORD_CRP,
3157 /* 42 "*" */ CWORD_CCTL_CCTL_CWORD,
3158 /* 43 "+" */ CWORD_CWORD_CWORD_CWORD,
3159 /* 44 "," */ CWORD_CWORD_CWORD_CWORD,
3160 /* 45 "-" */ CWORD_CCTL_CCTL_CWORD,
3161 /* 46 "." */ CWORD_CWORD_CWORD_CWORD,
Denys Vlasenkob3f29b42016-09-21 16:25:58 +02003162/* "/" was CWORD_CCTL_CCTL_CWORD, see comment in SIT() function why this is changed: */
3163 /* 47 "/" */ CWORD_CWORD_CWORD_CWORD,
Denys Vlasenkocd716832009-11-28 22:14:02 +01003164 /* 48 "0" */ CWORD_CWORD_CWORD_CWORD,
3165 /* 49 "1" */ CWORD_CWORD_CWORD_CWORD,
3166 /* 50 "2" */ CWORD_CWORD_CWORD_CWORD,
3167 /* 51 "3" */ CWORD_CWORD_CWORD_CWORD,
3168 /* 52 "4" */ CWORD_CWORD_CWORD_CWORD,
3169 /* 53 "5" */ CWORD_CWORD_CWORD_CWORD,
3170 /* 54 "6" */ CWORD_CWORD_CWORD_CWORD,
3171 /* 55 "7" */ CWORD_CWORD_CWORD_CWORD,
3172 /* 56 "8" */ CWORD_CWORD_CWORD_CWORD,
3173 /* 57 "9" */ CWORD_CWORD_CWORD_CWORD,
3174 /* 58 ":" */ CWORD_CCTL_CCTL_CWORD,
3175 /* 59 ";" */ CSPCL_CWORD_CWORD_CWORD,
3176 /* 60 "<" */ CSPCL_CWORD_CWORD_CWORD,
3177 /* 61 "=" */ CWORD_CCTL_CCTL_CWORD,
3178 /* 62 ">" */ CSPCL_CWORD_CWORD_CWORD,
3179 /* 63 "?" */ CWORD_CCTL_CCTL_CWORD,
3180 /* 64 "@" */ CWORD_CWORD_CWORD_CWORD,
3181 /* 65 "A" */ CWORD_CWORD_CWORD_CWORD,
3182 /* 66 "B" */ CWORD_CWORD_CWORD_CWORD,
3183 /* 67 "C" */ CWORD_CWORD_CWORD_CWORD,
3184 /* 68 "D" */ CWORD_CWORD_CWORD_CWORD,
3185 /* 69 "E" */ CWORD_CWORD_CWORD_CWORD,
3186 /* 70 "F" */ CWORD_CWORD_CWORD_CWORD,
3187 /* 71 "G" */ CWORD_CWORD_CWORD_CWORD,
3188 /* 72 "H" */ CWORD_CWORD_CWORD_CWORD,
3189 /* 73 "I" */ CWORD_CWORD_CWORD_CWORD,
3190 /* 74 "J" */ CWORD_CWORD_CWORD_CWORD,
3191 /* 75 "K" */ CWORD_CWORD_CWORD_CWORD,
3192 /* 76 "L" */ CWORD_CWORD_CWORD_CWORD,
3193 /* 77 "M" */ CWORD_CWORD_CWORD_CWORD,
3194 /* 78 "N" */ CWORD_CWORD_CWORD_CWORD,
3195 /* 79 "O" */ CWORD_CWORD_CWORD_CWORD,
3196 /* 80 "P" */ CWORD_CWORD_CWORD_CWORD,
3197 /* 81 "Q" */ CWORD_CWORD_CWORD_CWORD,
3198 /* 82 "R" */ CWORD_CWORD_CWORD_CWORD,
3199 /* 83 "S" */ CWORD_CWORD_CWORD_CWORD,
3200 /* 84 "T" */ CWORD_CWORD_CWORD_CWORD,
3201 /* 85 "U" */ CWORD_CWORD_CWORD_CWORD,
3202 /* 86 "V" */ CWORD_CWORD_CWORD_CWORD,
3203 /* 87 "W" */ CWORD_CWORD_CWORD_CWORD,
3204 /* 88 "X" */ CWORD_CWORD_CWORD_CWORD,
3205 /* 89 "Y" */ CWORD_CWORD_CWORD_CWORD,
3206 /* 90 "Z" */ CWORD_CWORD_CWORD_CWORD,
3207 /* 91 "[" */ CWORD_CCTL_CCTL_CWORD,
3208 /* 92 "\" */ CBACK_CBACK_CCTL_CBACK,
3209 /* 93 "]" */ CWORD_CCTL_CCTL_CWORD,
3210 /* 94 "^" */ CWORD_CWORD_CWORD_CWORD,
3211 /* 95 "_" */ CWORD_CWORD_CWORD_CWORD,
3212 /* 96 "`" */ CBQUOTE_CBQUOTE_CWORD_CBQUOTE,
3213 /* 97 "a" */ CWORD_CWORD_CWORD_CWORD,
3214 /* 98 "b" */ CWORD_CWORD_CWORD_CWORD,
3215 /* 99 "c" */ CWORD_CWORD_CWORD_CWORD,
3216 /* 100 "d" */ CWORD_CWORD_CWORD_CWORD,
3217 /* 101 "e" */ CWORD_CWORD_CWORD_CWORD,
3218 /* 102 "f" */ CWORD_CWORD_CWORD_CWORD,
3219 /* 103 "g" */ CWORD_CWORD_CWORD_CWORD,
3220 /* 104 "h" */ CWORD_CWORD_CWORD_CWORD,
3221 /* 105 "i" */ CWORD_CWORD_CWORD_CWORD,
3222 /* 106 "j" */ CWORD_CWORD_CWORD_CWORD,
3223 /* 107 "k" */ CWORD_CWORD_CWORD_CWORD,
3224 /* 108 "l" */ CWORD_CWORD_CWORD_CWORD,
3225 /* 109 "m" */ CWORD_CWORD_CWORD_CWORD,
3226 /* 110 "n" */ CWORD_CWORD_CWORD_CWORD,
3227 /* 111 "o" */ CWORD_CWORD_CWORD_CWORD,
3228 /* 112 "p" */ CWORD_CWORD_CWORD_CWORD,
3229 /* 113 "q" */ CWORD_CWORD_CWORD_CWORD,
3230 /* 114 "r" */ CWORD_CWORD_CWORD_CWORD,
3231 /* 115 "s" */ CWORD_CWORD_CWORD_CWORD,
3232 /* 116 "t" */ CWORD_CWORD_CWORD_CWORD,
3233 /* 117 "u" */ CWORD_CWORD_CWORD_CWORD,
3234 /* 118 "v" */ CWORD_CWORD_CWORD_CWORD,
3235 /* 119 "w" */ CWORD_CWORD_CWORD_CWORD,
3236 /* 120 "x" */ CWORD_CWORD_CWORD_CWORD,
3237 /* 121 "y" */ CWORD_CWORD_CWORD_CWORD,
3238 /* 122 "z" */ CWORD_CWORD_CWORD_CWORD,
3239 /* 123 "{" */ CWORD_CWORD_CWORD_CWORD,
3240 /* 124 "|" */ CSPCL_CWORD_CWORD_CWORD,
3241 /* 125 "}" */ CENDVAR_CENDVAR_CWORD_CENDVAR,
3242 /* 126 "~" */ CWORD_CCTL_CCTL_CWORD,
3243 /* 127 del */ CWORD_CWORD_CWORD_CWORD,
3244 /* 128 0x80 */ CWORD_CWORD_CWORD_CWORD,
3245 /* 129 CTLESC */ CCTL_CCTL_CCTL_CCTL,
3246 /* 130 CTLVAR */ CCTL_CCTL_CCTL_CCTL,
3247 /* 131 CTLENDVAR */ CCTL_CCTL_CCTL_CCTL,
3248 /* 132 CTLBACKQ */ CCTL_CCTL_CCTL_CCTL,
3249 /* 133 CTLQUOTE */ CCTL_CCTL_CCTL_CCTL,
3250 /* 134 CTLARI */ CCTL_CCTL_CCTL_CCTL,
3251 /* 135 CTLENDARI */ CCTL_CCTL_CCTL_CCTL,
3252 /* 136 CTLQUOTEMARK */ CCTL_CCTL_CCTL_CCTL,
Ron Yorstona1b0d382020-07-23 08:32:27 +01003253#if BASH_PROCESS_SUBST
3254 /* 137 CTLTOPROC */ CCTL_CCTL_CCTL_CCTL,
3255 /* 138 CTLFROMPROC */ CCTL_CCTL_CCTL_CCTL,
3256#else
Denys Vlasenkocd716832009-11-28 22:14:02 +01003257 /* 137 */ CWORD_CWORD_CWORD_CWORD,
3258 /* 138 */ CWORD_CWORD_CWORD_CWORD,
Ron Yorstona1b0d382020-07-23 08:32:27 +01003259#endif
Denys Vlasenkocd716832009-11-28 22:14:02 +01003260 /* 139 */ CWORD_CWORD_CWORD_CWORD,
3261 /* 140 */ CWORD_CWORD_CWORD_CWORD,
3262 /* 141 */ CWORD_CWORD_CWORD_CWORD,
3263 /* 142 */ CWORD_CWORD_CWORD_CWORD,
3264 /* 143 */ CWORD_CWORD_CWORD_CWORD,
3265 /* 144 */ CWORD_CWORD_CWORD_CWORD,
3266 /* 145 */ CWORD_CWORD_CWORD_CWORD,
3267 /* 146 */ CWORD_CWORD_CWORD_CWORD,
3268 /* 147 */ CWORD_CWORD_CWORD_CWORD,
3269 /* 148 */ CWORD_CWORD_CWORD_CWORD,
3270 /* 149 */ CWORD_CWORD_CWORD_CWORD,
3271 /* 150 */ CWORD_CWORD_CWORD_CWORD,
3272 /* 151 */ CWORD_CWORD_CWORD_CWORD,
3273 /* 152 */ CWORD_CWORD_CWORD_CWORD,
3274 /* 153 */ CWORD_CWORD_CWORD_CWORD,
3275 /* 154 */ CWORD_CWORD_CWORD_CWORD,
3276 /* 155 */ CWORD_CWORD_CWORD_CWORD,
3277 /* 156 */ CWORD_CWORD_CWORD_CWORD,
3278 /* 157 */ CWORD_CWORD_CWORD_CWORD,
3279 /* 158 */ CWORD_CWORD_CWORD_CWORD,
3280 /* 159 */ CWORD_CWORD_CWORD_CWORD,
3281 /* 160 */ CWORD_CWORD_CWORD_CWORD,
3282 /* 161 */ CWORD_CWORD_CWORD_CWORD,
3283 /* 162 */ CWORD_CWORD_CWORD_CWORD,
3284 /* 163 */ CWORD_CWORD_CWORD_CWORD,
3285 /* 164 */ CWORD_CWORD_CWORD_CWORD,
3286 /* 165 */ CWORD_CWORD_CWORD_CWORD,
3287 /* 166 */ CWORD_CWORD_CWORD_CWORD,
3288 /* 167 */ CWORD_CWORD_CWORD_CWORD,
3289 /* 168 */ CWORD_CWORD_CWORD_CWORD,
3290 /* 169 */ CWORD_CWORD_CWORD_CWORD,
3291 /* 170 */ CWORD_CWORD_CWORD_CWORD,
3292 /* 171 */ CWORD_CWORD_CWORD_CWORD,
3293 /* 172 */ CWORD_CWORD_CWORD_CWORD,
3294 /* 173 */ CWORD_CWORD_CWORD_CWORD,
3295 /* 174 */ CWORD_CWORD_CWORD_CWORD,
3296 /* 175 */ CWORD_CWORD_CWORD_CWORD,
3297 /* 176 */ CWORD_CWORD_CWORD_CWORD,
3298 /* 177 */ CWORD_CWORD_CWORD_CWORD,
3299 /* 178 */ CWORD_CWORD_CWORD_CWORD,
3300 /* 179 */ CWORD_CWORD_CWORD_CWORD,
3301 /* 180 */ CWORD_CWORD_CWORD_CWORD,
3302 /* 181 */ CWORD_CWORD_CWORD_CWORD,
3303 /* 182 */ CWORD_CWORD_CWORD_CWORD,
3304 /* 183 */ CWORD_CWORD_CWORD_CWORD,
3305 /* 184 */ CWORD_CWORD_CWORD_CWORD,
3306 /* 185 */ CWORD_CWORD_CWORD_CWORD,
3307 /* 186 */ CWORD_CWORD_CWORD_CWORD,
3308 /* 187 */ CWORD_CWORD_CWORD_CWORD,
3309 /* 188 */ CWORD_CWORD_CWORD_CWORD,
3310 /* 189 */ CWORD_CWORD_CWORD_CWORD,
3311 /* 190 */ CWORD_CWORD_CWORD_CWORD,
3312 /* 191 */ CWORD_CWORD_CWORD_CWORD,
3313 /* 192 */ CWORD_CWORD_CWORD_CWORD,
3314 /* 193 */ CWORD_CWORD_CWORD_CWORD,
3315 /* 194 */ CWORD_CWORD_CWORD_CWORD,
3316 /* 195 */ CWORD_CWORD_CWORD_CWORD,
3317 /* 196 */ CWORD_CWORD_CWORD_CWORD,
3318 /* 197 */ CWORD_CWORD_CWORD_CWORD,
3319 /* 198 */ CWORD_CWORD_CWORD_CWORD,
3320 /* 199 */ CWORD_CWORD_CWORD_CWORD,
3321 /* 200 */ CWORD_CWORD_CWORD_CWORD,
3322 /* 201 */ CWORD_CWORD_CWORD_CWORD,
3323 /* 202 */ CWORD_CWORD_CWORD_CWORD,
3324 /* 203 */ CWORD_CWORD_CWORD_CWORD,
3325 /* 204 */ CWORD_CWORD_CWORD_CWORD,
3326 /* 205 */ CWORD_CWORD_CWORD_CWORD,
3327 /* 206 */ CWORD_CWORD_CWORD_CWORD,
3328 /* 207 */ CWORD_CWORD_CWORD_CWORD,
3329 /* 208 */ CWORD_CWORD_CWORD_CWORD,
3330 /* 209 */ CWORD_CWORD_CWORD_CWORD,
3331 /* 210 */ CWORD_CWORD_CWORD_CWORD,
3332 /* 211 */ CWORD_CWORD_CWORD_CWORD,
3333 /* 212 */ CWORD_CWORD_CWORD_CWORD,
3334 /* 213 */ CWORD_CWORD_CWORD_CWORD,
3335 /* 214 */ CWORD_CWORD_CWORD_CWORD,
3336 /* 215 */ CWORD_CWORD_CWORD_CWORD,
3337 /* 216 */ CWORD_CWORD_CWORD_CWORD,
3338 /* 217 */ CWORD_CWORD_CWORD_CWORD,
3339 /* 218 */ CWORD_CWORD_CWORD_CWORD,
3340 /* 219 */ CWORD_CWORD_CWORD_CWORD,
3341 /* 220 */ CWORD_CWORD_CWORD_CWORD,
3342 /* 221 */ CWORD_CWORD_CWORD_CWORD,
3343 /* 222 */ CWORD_CWORD_CWORD_CWORD,
3344 /* 223 */ CWORD_CWORD_CWORD_CWORD,
3345 /* 224 */ CWORD_CWORD_CWORD_CWORD,
3346 /* 225 */ CWORD_CWORD_CWORD_CWORD,
3347 /* 226 */ CWORD_CWORD_CWORD_CWORD,
3348 /* 227 */ CWORD_CWORD_CWORD_CWORD,
3349 /* 228 */ CWORD_CWORD_CWORD_CWORD,
3350 /* 229 */ CWORD_CWORD_CWORD_CWORD,
3351 /* 230 */ CWORD_CWORD_CWORD_CWORD,
3352 /* 231 */ CWORD_CWORD_CWORD_CWORD,
3353 /* 232 */ CWORD_CWORD_CWORD_CWORD,
3354 /* 233 */ CWORD_CWORD_CWORD_CWORD,
3355 /* 234 */ CWORD_CWORD_CWORD_CWORD,
3356 /* 235 */ CWORD_CWORD_CWORD_CWORD,
3357 /* 236 */ CWORD_CWORD_CWORD_CWORD,
3358 /* 237 */ CWORD_CWORD_CWORD_CWORD,
3359 /* 238 */ CWORD_CWORD_CWORD_CWORD,
3360 /* 239 */ CWORD_CWORD_CWORD_CWORD,
3361 /* 230 */ CWORD_CWORD_CWORD_CWORD,
3362 /* 241 */ CWORD_CWORD_CWORD_CWORD,
3363 /* 242 */ CWORD_CWORD_CWORD_CWORD,
3364 /* 243 */ CWORD_CWORD_CWORD_CWORD,
3365 /* 244 */ CWORD_CWORD_CWORD_CWORD,
3366 /* 245 */ CWORD_CWORD_CWORD_CWORD,
3367 /* 246 */ CWORD_CWORD_CWORD_CWORD,
3368 /* 247 */ CWORD_CWORD_CWORD_CWORD,
3369 /* 248 */ CWORD_CWORD_CWORD_CWORD,
3370 /* 249 */ CWORD_CWORD_CWORD_CWORD,
3371 /* 250 */ CWORD_CWORD_CWORD_CWORD,
3372 /* 251 */ CWORD_CWORD_CWORD_CWORD,
3373 /* 252 */ CWORD_CWORD_CWORD_CWORD,
3374 /* 253 */ CWORD_CWORD_CWORD_CWORD,
3375 /* 254 */ CWORD_CWORD_CWORD_CWORD,
3376 /* 255 */ CWORD_CWORD_CWORD_CWORD,
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01003377 /* PEOF */ CENDFILE_CENDFILE_CENDFILE_CENDFILE,
Eric Andersen2870d962001-07-02 17:27:21 +00003378};
3379
Denys Vlasenko2fe66b12016-12-12 17:39:12 +01003380#if 1
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01003381# define SIT(c, syntax) ((S_I_T[syntax_index_table[c]] >> ((syntax)*4)) & 0xf)
Denys Vlasenko2fe66b12016-12-12 17:39:12 +01003382#else /* debug version, caught one signed char bug */
3383# define SIT(c, syntax) \
3384 ({ \
3385 if ((c) < 0 || (c) > (PEOF + ENABLE_ASH_ALIAS)) \
3386 bb_error_msg_and_die("line:%d c:%d", __LINE__, (c)); \
Denys Vlasenko0b883582016-12-23 16:49:07 +01003387 if ((syntax) < 0 || (syntax) > (2 + ENABLE_FEATURE_SH_MATH)) \
Denys Vlasenko2fe66b12016-12-12 17:39:12 +01003388 bb_error_msg_and_die("line:%d c:%d", __LINE__, (c)); \
3389 ((S_I_T[syntax_index_table[c]] >> ((syntax)*4)) & 0xf); \
3390 })
3391#endif
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00003392
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01003393#endif /* !USE_SIT_FUNCTION */
Eric Andersenc470f442003-07-28 09:56:35 +00003394
Eric Andersen2870d962001-07-02 17:27:21 +00003395
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003396/* ============ Alias handling */
Denis Vlasenkofc06f292007-02-23 21:09:35 +00003397
Denis Vlasenko131ae172007-02-18 13:00:19 +00003398#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003399
3400#define ALIASINUSE 1
3401#define ALIASDEAD 2
3402
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00003403struct alias {
3404 struct alias *next;
3405 char *name;
3406 char *val;
3407 int flag;
3408};
3409
Denis Vlasenko01631112007-12-16 17:20:38 +00003410
3411static struct alias **atab; // [ATABSIZE];
3412#define INIT_G_alias() do { \
3413 atab = xzalloc(ATABSIZE * sizeof(atab[0])); \
3414} while (0)
3415
Eric Andersen2870d962001-07-02 17:27:21 +00003416
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00003417static struct alias **
Denys Vlasenko37dc08b2016-10-02 04:38:07 +02003418__lookupalias(const char *name)
3419{
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00003420 unsigned int hashval;
3421 struct alias **app;
3422 const char *p;
3423 unsigned int ch;
3424
3425 p = name;
3426
3427 ch = (unsigned char)*p;
3428 hashval = ch << 4;
3429 while (ch) {
3430 hashval += ch;
3431 ch = (unsigned char)*++p;
3432 }
3433 app = &atab[hashval % ATABSIZE];
3434
3435 for (; *app; app = &(*app)->next) {
3436 if (strcmp(name, (*app)->name) == 0) {
3437 break;
3438 }
3439 }
3440
3441 return app;
3442}
3443
3444static struct alias *
3445lookupalias(const char *name, int check)
3446{
3447 struct alias *ap = *__lookupalias(name);
3448
3449 if (check && ap && (ap->flag & ALIASINUSE))
3450 return NULL;
3451 return ap;
3452}
3453
3454static struct alias *
3455freealias(struct alias *ap)
3456{
3457 struct alias *next;
3458
3459 if (ap->flag & ALIASINUSE) {
3460 ap->flag |= ALIASDEAD;
3461 return ap;
3462 }
3463
3464 next = ap->next;
3465 free(ap->name);
3466 free(ap->val);
3467 free(ap);
3468 return next;
3469}
Eric Andersencb57d552001-06-28 07:25:16 +00003470
Eric Andersenc470f442003-07-28 09:56:35 +00003471static void
3472setalias(const char *name, const char *val)
Eric Andersencb57d552001-06-28 07:25:16 +00003473{
3474 struct alias *ap, **app;
3475
3476 app = __lookupalias(name);
3477 ap = *app;
Denis Vlasenkob012b102007-02-19 22:43:01 +00003478 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003479 if (ap) {
3480 if (!(ap->flag & ALIASINUSE)) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00003481 free(ap->val);
Eric Andersencb57d552001-06-28 07:25:16 +00003482 }
Denis Vlasenko0c032a42007-02-23 01:03:40 +00003483 ap->val = ckstrdup(val);
Eric Andersencb57d552001-06-28 07:25:16 +00003484 ap->flag &= ~ALIASDEAD;
3485 } else {
3486 /* not found */
Denis Vlasenko597906c2008-02-20 16:38:54 +00003487 ap = ckzalloc(sizeof(struct alias));
Denis Vlasenko0c032a42007-02-23 01:03:40 +00003488 ap->name = ckstrdup(name);
3489 ap->val = ckstrdup(val);
Denis Vlasenko597906c2008-02-20 16:38:54 +00003490 /*ap->flag = 0; - ckzalloc did it */
3491 /*ap->next = NULL;*/
Eric Andersencb57d552001-06-28 07:25:16 +00003492 *app = ap;
3493 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00003494 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00003495}
3496
Eric Andersenc470f442003-07-28 09:56:35 +00003497static int
3498unalias(const char *name)
Eric Andersen2870d962001-07-02 17:27:21 +00003499{
Eric Andersencb57d552001-06-28 07:25:16 +00003500 struct alias **app;
3501
3502 app = __lookupalias(name);
3503
3504 if (*app) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00003505 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003506 *app = freealias(*app);
Denis Vlasenkob012b102007-02-19 22:43:01 +00003507 INT_ON;
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003508 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003509 }
3510
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003511 return 1;
Eric Andersencb57d552001-06-28 07:25:16 +00003512}
3513
Eric Andersenc470f442003-07-28 09:56:35 +00003514static void
3515rmaliases(void)
Eric Andersen2870d962001-07-02 17:27:21 +00003516{
Eric Andersencb57d552001-06-28 07:25:16 +00003517 struct alias *ap, **app;
3518 int i;
3519
Denis Vlasenkob012b102007-02-19 22:43:01 +00003520 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003521 for (i = 0; i < ATABSIZE; i++) {
3522 app = &atab[i];
3523 for (ap = *app; ap; ap = *app) {
3524 *app = freealias(*app);
3525 if (ap == *app) {
3526 app = &ap->next;
3527 }
3528 }
3529 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00003530 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00003531}
3532
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003533static void
3534printalias(const struct alias *ap)
3535{
3536 out1fmt("%s=%s\n", ap->name, single_quote(ap->val));
3537}
3538
Eric Andersencb57d552001-06-28 07:25:16 +00003539/*
3540 * TODO - sort output
3541 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02003542static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00003543aliascmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00003544{
3545 char *n, *v;
3546 int ret = 0;
3547 struct alias *ap;
3548
Denis Vlasenko68404f12008-03-17 09:00:54 +00003549 if (!argv[1]) {
Eric Andersencb57d552001-06-28 07:25:16 +00003550 int i;
3551
Denis Vlasenko68404f12008-03-17 09:00:54 +00003552 for (i = 0; i < ATABSIZE; i++) {
Eric Andersencb57d552001-06-28 07:25:16 +00003553 for (ap = atab[i]; ap; ap = ap->next) {
3554 printalias(ap);
3555 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00003556 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003557 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003558 }
3559 while ((n = *++argv) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00003560 v = strchr(n+1, '=');
3561 if (v == NULL) { /* n+1: funny ksh stuff */
3562 ap = *__lookupalias(n);
3563 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +00003564 fprintf(stderr, "%s: %s not found\n", "alias", n);
Eric Andersencb57d552001-06-28 07:25:16 +00003565 ret = 1;
3566 } else
3567 printalias(ap);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003568 } else {
Eric Andersencb57d552001-06-28 07:25:16 +00003569 *v++ = '\0';
3570 setalias(n, v);
3571 }
3572 }
3573
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003574 return ret;
Eric Andersencb57d552001-06-28 07:25:16 +00003575}
3576
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02003577static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00003578unaliascmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +00003579{
3580 int i;
3581
Denys Vlasenko6c149f42017-04-12 21:31:32 +02003582 while (nextopt("a") != '\0') {
3583 rmaliases();
3584 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003585 }
3586 for (i = 0; *argptr; argptr++) {
3587 if (unalias(*argptr)) {
Eric Andersenc470f442003-07-28 09:56:35 +00003588 fprintf(stderr, "%s: %s not found\n", "unalias", *argptr);
Eric Andersencb57d552001-06-28 07:25:16 +00003589 i = 1;
3590 }
3591 }
3592
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003593 return i;
Eric Andersencb57d552001-06-28 07:25:16 +00003594}
Denis Vlasenkofc06f292007-02-23 21:09:35 +00003595
Denis Vlasenko131ae172007-02-18 13:00:19 +00003596#endif /* ASH_ALIAS */
Eric Andersencb57d552001-06-28 07:25:16 +00003597
Eric Andersenc470f442003-07-28 09:56:35 +00003598
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003599/* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */
Denys Vlasenko285ad152009-12-04 23:02:27 +01003600#define FORK_FG 0
3601#define FORK_BG 1
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003602#define FORK_NOJOB 2
3603
3604/* mode flags for showjob(s) */
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02003605#define SHOW_ONLY_PGID 0x01 /* show only pgid (jobs -p) */
3606#define SHOW_PIDS 0x02 /* show individual pids, not just one line per job */
3607#define SHOW_CHANGED 0x04 /* only jobs whose state has changed */
Denys Vlasenko9c541002015-10-07 15:44:36 +02003608#define SHOW_STDERR 0x08 /* print to stderr (else stdout) */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003609
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003610/*
3611 * A job structure contains information about a job. A job is either a
3612 * single process or a set of processes contained in a pipeline. In the
3613 * latter case, pidlist will be non-NULL, and will point to a -1 terminated
3614 * array of pids.
3615 */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003616struct procstat {
Denys Vlasenko285ad152009-12-04 23:02:27 +01003617 pid_t ps_pid; /* process id */
3618 int ps_status; /* last process status from wait() */
3619 char *ps_cmd; /* text of command being run */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003620};
3621
3622struct job {
3623 struct procstat ps0; /* status of process */
Denys Vlasenko966f0872019-03-27 15:51:42 +01003624 struct procstat *ps; /* status of processes when more than one */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003625#if JOBS
3626 int stopstatus; /* status of a stopped job */
3627#endif
Denys Vlasenko4c179372017-01-11 18:44:15 +01003628 unsigned nprocs; /* number of processes */
3629
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003630#define JOBRUNNING 0 /* at least one proc running */
3631#define JOBSTOPPED 1 /* all procs are stopped */
3632#define JOBDONE 2 /* all procs are completed */
Denys Vlasenko4c179372017-01-11 18:44:15 +01003633 unsigned
3634 state: 8,
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003635#if JOBS
3636 sigint: 1, /* job was killed by SIGINT */
3637 jobctl: 1, /* job running under job control */
3638#endif
3639 waited: 1, /* true if this entry has been waited for */
3640 used: 1, /* true if this entry is in used */
3641 changed: 1; /* true if status has changed */
3642 struct job *prev_job; /* previous job */
3643};
3644
Denis Vlasenko68404f12008-03-17 09:00:54 +00003645static struct job *makejob(/*union node *,*/ int);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003646static int forkshell(struct job *, union node *, int);
3647static int waitforjob(struct job *);
3648
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003649#if !JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003650enum { doing_jobctl = 0 };
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003651#define setjobctl(on) do {} while (0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003652#else
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003653static smallint doing_jobctl; //references:8
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003654static void setjobctl(int);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003655#endif
3656
3657/*
Denis Vlasenko4b875702009-03-19 13:30:04 +00003658 * Ignore a signal.
3659 */
3660static void
3661ignoresig(int signo)
3662{
3663 /* Avoid unnecessary system calls. Is it already SIG_IGNed? */
3664 if (sigmode[signo - 1] != S_IGN && sigmode[signo - 1] != S_HARD_IGN) {
3665 /* No, need to do it */
3666 signal(signo, SIG_IGN);
3667 }
3668 sigmode[signo - 1] = S_HARD_IGN;
3669}
3670
3671/*
Denys Vlasenko238bf182010-05-18 15:49:07 +02003672 * Only one usage site - in setsignal()
Denis Vlasenko4b875702009-03-19 13:30:04 +00003673 */
3674static void
Denys Vlasenko238bf182010-05-18 15:49:07 +02003675signal_handler(int signo)
Denis Vlasenko4b875702009-03-19 13:30:04 +00003676{
Denys Vlasenko458c1f22016-10-27 23:51:19 +02003677 if (signo == SIGCHLD) {
3678 got_sigchld = 1;
3679 if (!trap[SIGCHLD])
3680 return;
3681 }
Denys Vlasenko12566e72022-01-17 03:02:40 +01003682#if ENABLE_FEATURE_EDITING
3683 bb_got_signal = signo; /* for read_line_input: "we got a signal" */
3684#endif
Denis Vlasenko4b875702009-03-19 13:30:04 +00003685 gotsig[signo - 1] = 1;
Denys Vlasenko458c1f22016-10-27 23:51:19 +02003686 pending_sig = signo;
Denis Vlasenko4b875702009-03-19 13:30:04 +00003687
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02003688 if (signo == SIGINT && !trap[SIGINT]) {
3689 if (!suppress_int) {
3690 pending_sig = 0;
Denis Vlasenko4b875702009-03-19 13:30:04 +00003691 raise_interrupt(); /* does not return */
3692 }
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02003693 pending_int = 1;
Denis Vlasenko4b875702009-03-19 13:30:04 +00003694 }
3695}
3696
3697/*
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003698 * Set the signal handler for the specified signal. The routine figures
3699 * out what it should be set to.
3700 */
3701static void
3702setsignal(int signo)
3703{
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003704 char *t;
3705 char cur_act, new_act;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003706 struct sigaction act;
3707
3708 t = trap[signo];
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003709 new_act = S_DFL;
3710 if (t != NULL) { /* trap for this sig is set */
3711 new_act = S_CATCH;
3712 if (t[0] == '\0') /* trap is "": ignore this sig */
3713 new_act = S_IGN;
3714 }
3715
3716 if (rootshell && new_act == S_DFL) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003717 switch (signo) {
3718 case SIGINT:
3719 if (iflag || minusc || sflag == 0)
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003720 new_act = S_CATCH;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003721 break;
3722 case SIGQUIT:
3723#if DEBUG
3724 if (debug)
3725 break;
3726#endif
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003727 /* man bash:
3728 * "In all cases, bash ignores SIGQUIT. Non-builtin
3729 * commands run by bash have signal handlers
3730 * set to the values inherited by the shell
3731 * from its parent". */
3732 new_act = S_IGN;
3733 break;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003734 case SIGTERM:
3735 if (iflag)
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003736 new_act = S_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003737 break;
3738#if JOBS
3739 case SIGTSTP:
3740 case SIGTTOU:
3741 if (mflag)
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003742 new_act = S_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003743 break;
3744#endif
3745 }
3746 }
Denys Vlasenko49e6bf22017-08-04 14:28:16 +02003747 /* if !rootshell, we reset SIGQUIT to DFL,
3748 * whereas we have to restore it to what shell got on entry.
3749 * This is handled by the fact that if signal was IGNored on entry,
3750 * then cur_act is S_HARD_IGN and we never change its sigaction
3751 * (see code below).
3752 */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003753
Denys Vlasenko458c1f22016-10-27 23:51:19 +02003754 if (signo == SIGCHLD)
3755 new_act = S_CATCH;
3756
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003757 t = &sigmode[signo - 1];
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003758 cur_act = *t;
3759 if (cur_act == 0) {
3760 /* current setting is not yet known */
3761 if (sigaction(signo, NULL, &act)) {
3762 /* pretend it worked; maybe we should give a warning,
3763 * but other shells don't. We don't alter sigmode,
3764 * so we retry every time.
3765 * btw, in Linux it never fails. --vda */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003766 return;
3767 }
3768 if (act.sa_handler == SIG_IGN) {
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003769 cur_act = S_HARD_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003770 if (mflag
3771 && (signo == SIGTSTP || signo == SIGTTIN || signo == SIGTTOU)
3772 ) {
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003773 cur_act = S_IGN; /* don't hard ignore these */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003774 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003775 }
Denys Vlasenko0f14f412017-08-06 20:06:19 +02003776 if (act.sa_handler == SIG_DFL && new_act == S_DFL) {
3777 /* installing SIG_DFL over SIG_DFL is a no-op */
3778 /* saves one sigaction call in each "sh -c SCRIPT" invocation */
3779 *t = S_DFL;
3780 return;
3781 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003782 }
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003783 if (cur_act == S_HARD_IGN || cur_act == new_act)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003784 return;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003785
Denys Vlasenko49e6bf22017-08-04 14:28:16 +02003786 *t = new_act;
3787
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003788 act.sa_handler = SIG_DFL;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003789 switch (new_act) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003790 case S_CATCH:
Denys Vlasenko238bf182010-05-18 15:49:07 +02003791 act.sa_handler = signal_handler;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003792 break;
3793 case S_IGN:
3794 act.sa_handler = SIG_IGN;
3795 break;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003796 }
Ian Wienand89b3cba2011-04-16 20:05:14 +02003797 /* flags and mask matter only if !DFL and !IGN, but we do it
3798 * for all cases for more deterministic behavior:
3799 */
Denys Vlasenko49e6bf22017-08-04 14:28:16 +02003800 act.sa_flags = 0; //TODO: why not SA_RESTART?
Ian Wienand89b3cba2011-04-16 20:05:14 +02003801 sigfillset(&act.sa_mask);
3802
Denis Vlasenko8e2cfec2008-03-12 23:19:35 +00003803 sigaction_set(signo, &act);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003804}
3805
3806/* mode flags for set_curjob */
3807#define CUR_DELETE 2
3808#define CUR_RUNNING 1
3809#define CUR_STOPPED 0
3810
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003811#if JOBS
3812/* pgrp of shell on invocation */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003813static int initialpgrp; //references:2
3814static int ttyfd = -1; //5
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003815#endif
3816/* array of jobs */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003817static struct job *jobtab; //5
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003818/* size of array */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003819static unsigned njobs; //4
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003820/* current job */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003821static struct job *curjob; //lots
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003822
Denys Vlasenko098b7132017-01-11 19:59:03 +01003823#if 0
3824/* Bash has a feature: it restores termios after a successful wait for
3825 * a foreground job which had at least one stopped or sigkilled member.
3826 * The probable rationale is that SIGSTOP and SIGKILL can preclude task from
3827 * properly restoring tty state. Should we do this too?
3828 * A reproducer: ^Z an interactive python:
3829 *
3830 * # python
3831 * Python 2.7.12 (...)
3832 * >>> ^Z
3833 * { python leaves tty in -icanon -echo state. We do survive that... }
3834 * [1]+ Stopped python
3835 * { ...however, next program (python #2) does not survive it well: }
3836 * # python
3837 * Python 2.7.12 (...)
3838 * >>> Traceback (most recent call last):
3839 * { above, I typed "qwerty<CR>", but -echo state is still in effect }
3840 * File "<stdin>", line 1, in <module>
3841 * NameError: name 'qwerty' is not defined
3842 *
3843 * The implementation below is modeled on bash code and seems to work.
3844 * However, I'm not sure we should do this. For one: what if I'd fg
3845 * the stopped python instead? It'll be confused by "restored" tty state.
3846 */
3847static struct termios shell_tty_info;
3848static void
3849get_tty_state(void)
3850{
3851 if (rootshell && ttyfd >= 0)
3852 tcgetattr(ttyfd, &shell_tty_info);
3853}
3854static void
3855set_tty_state(void)
3856{
3857 /* if (rootshell) - caller ensures this */
3858 if (ttyfd >= 0)
3859 tcsetattr(ttyfd, TCSADRAIN, &shell_tty_info);
3860}
3861static int
3862job_signal_status(struct job *jp)
3863{
3864 int status;
3865 unsigned i;
3866 struct procstat *ps = jp->ps;
3867 for (i = 0; i < jp->nprocs; i++) {
3868 status = ps[i].ps_status;
3869 if (WIFSIGNALED(status) || WIFSTOPPED(status))
3870 return status;
3871 }
3872 return 0;
3873}
3874static void
3875restore_tty_if_stopped_or_signaled(struct job *jp)
3876{
3877//TODO: check what happens if we come from waitforjob() in expbackq()
3878 if (rootshell) {
3879 int s = job_signal_status(jp);
3880 if (s) /* WIFSIGNALED(s) || WIFSTOPPED(s) */
3881 set_tty_state();
3882 }
3883}
3884#else
3885# define get_tty_state() ((void)0)
3886# define restore_tty_if_stopped_or_signaled(jp) ((void)0)
3887#endif
3888
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003889static void
3890set_curjob(struct job *jp, unsigned mode)
3891{
3892 struct job *jp1;
3893 struct job **jpp, **curp;
3894
3895 /* first remove from list */
3896 jpp = curp = &curjob;
Denys Vlasenko940c7202011-03-02 04:07:14 +01003897 while (1) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003898 jp1 = *jpp;
3899 if (jp1 == jp)
3900 break;
3901 jpp = &jp1->prev_job;
Denys Vlasenko940c7202011-03-02 04:07:14 +01003902 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003903 *jpp = jp1->prev_job;
3904
3905 /* Then re-insert in correct position */
3906 jpp = curp;
3907 switch (mode) {
3908 default:
3909#if DEBUG
3910 abort();
3911#endif
3912 case CUR_DELETE:
3913 /* job being deleted */
3914 break;
3915 case CUR_RUNNING:
3916 /* newly created job or backgrounded job,
Denys Vlasenko60cb48c2013-01-14 15:57:44 +01003917 * put after all stopped jobs.
3918 */
Denys Vlasenko940c7202011-03-02 04:07:14 +01003919 while (1) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003920 jp1 = *jpp;
3921#if JOBS
3922 if (!jp1 || jp1->state != JOBSTOPPED)
3923#endif
3924 break;
3925 jpp = &jp1->prev_job;
Denys Vlasenko940c7202011-03-02 04:07:14 +01003926 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003927 /* FALLTHROUGH */
3928#if JOBS
3929 case CUR_STOPPED:
3930#endif
3931 /* newly stopped job - becomes curjob */
3932 jp->prev_job = *jpp;
3933 *jpp = jp;
3934 break;
3935 }
3936}
3937
3938#if JOBS || DEBUG
3939static int
3940jobno(const struct job *jp)
3941{
3942 return jp - jobtab + 1;
3943}
3944#endif
3945
3946/*
3947 * Convert a job name to a job structure.
3948 */
Denis Vlasenko85c24712008-03-17 09:04:04 +00003949#if !JOBS
3950#define getjob(name, getctl) getjob(name)
3951#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003952static struct job *
3953getjob(const char *name, int getctl)
3954{
3955 struct job *jp;
3956 struct job *found;
Denys Vlasenkoffc39202009-08-17 02:12:20 +02003957 const char *err_msg = "%s: no such job";
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003958 unsigned num;
3959 int c;
3960 const char *p;
3961 char *(*match)(const char *, const char *);
3962
3963 jp = curjob;
3964 p = name;
3965 if (!p)
3966 goto currentjob;
3967
3968 if (*p != '%')
3969 goto err;
3970
3971 c = *++p;
3972 if (!c)
3973 goto currentjob;
3974
3975 if (!p[1]) {
3976 if (c == '+' || c == '%') {
3977 currentjob:
3978 err_msg = "No current job";
3979 goto check;
3980 }
3981 if (c == '-') {
3982 if (jp)
3983 jp = jp->prev_job;
3984 err_msg = "No previous job";
3985 check:
3986 if (!jp)
3987 goto err;
3988 goto gotit;
3989 }
3990 }
3991
3992 if (is_number(p)) {
3993 num = atoi(p);
Denys Vlasenko46a45ce2016-09-29 01:10:08 +02003994 if (num > 0 && num <= njobs) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003995 jp = jobtab + num - 1;
3996 if (jp->used)
3997 goto gotit;
3998 goto err;
3999 }
4000 }
4001
4002 match = prefix;
4003 if (*p == '?') {
4004 match = strstr;
4005 p++;
4006 }
4007
Denys Vlasenkoffc39202009-08-17 02:12:20 +02004008 found = NULL;
4009 while (jp) {
Denys Vlasenko285ad152009-12-04 23:02:27 +01004010 if (match(jp->ps[0].ps_cmd, p)) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004011 if (found)
4012 goto err;
4013 found = jp;
4014 err_msg = "%s: ambiguous";
4015 }
4016 jp = jp->prev_job;
4017 }
Denys Vlasenkoffc39202009-08-17 02:12:20 +02004018 if (!found)
4019 goto err;
4020 jp = found;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004021
4022 gotit:
4023#if JOBS
4024 err_msg = "job %s not created under job control";
4025 if (getctl && jp->jobctl == 0)
4026 goto err;
4027#endif
4028 return jp;
4029 err:
4030 ash_msg_and_raise_error(err_msg, name);
4031}
4032
4033/*
4034 * Mark a job structure as unused.
4035 */
4036static void
4037freejob(struct job *jp)
4038{
4039 struct procstat *ps;
4040 int i;
4041
4042 INT_OFF;
4043 for (i = jp->nprocs, ps = jp->ps; --i >= 0; ps++) {
Denys Vlasenko285ad152009-12-04 23:02:27 +01004044 if (ps->ps_cmd != nullstr)
4045 free(ps->ps_cmd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004046 }
4047 if (jp->ps != &jp->ps0)
4048 free(jp->ps);
4049 jp->used = 0;
4050 set_curjob(jp, CUR_DELETE);
4051 INT_ON;
4052}
4053
4054#if JOBS
4055static void
4056xtcsetpgrp(int fd, pid_t pgrp)
4057{
4058 if (tcsetpgrp(fd, pgrp))
Ron Yorstonbe366e52017-07-27 13:53:39 +01004059 ash_msg_and_raise_perror("can't set tty process group");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004060}
4061
4062/*
4063 * Turn job control on and off.
4064 *
4065 * Note: This code assumes that the third arg to ioctl is a character
4066 * pointer, which is true on Berkeley systems but not System V. Since
4067 * System V doesn't have job control yet, this isn't a problem now.
4068 *
4069 * Called with interrupts off.
4070 */
4071static void
4072setjobctl(int on)
4073{
4074 int fd;
4075 int pgrp;
4076
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004077 if (on == doing_jobctl || rootshell == 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004078 return;
4079 if (on) {
4080 int ofd;
4081 ofd = fd = open(_PATH_TTY, O_RDWR);
4082 if (fd < 0) {
4083 /* BTW, bash will try to open(ttyname(0)) if open("/dev/tty") fails.
4084 * That sometimes helps to acquire controlling tty.
4085 * Obviously, a workaround for bugs when someone
4086 * failed to provide a controlling tty to bash! :) */
Denis Vlasenkoed270a52007-11-26 05:37:07 +00004087 fd = 2;
4088 while (!isatty(fd))
4089 if (--fd < 0)
4090 goto out;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004091 }
Denys Vlasenko64774602016-10-26 15:24:30 +02004092 /* fd is a tty at this point */
Denys Vlasenko60fb98e2018-03-30 22:15:14 +02004093 fd = fcntl(fd, F_DUPFD_CLOEXEC, 10);
Denys Vlasenko10ad6222017-04-17 16:13:32 +02004094 if (ofd >= 0) /* if it is "/dev/tty", close. If 0/1/2, don't */
Denis Vlasenkoed270a52007-11-26 05:37:07 +00004095 close(ofd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004096 if (fd < 0)
Denys Vlasenko64774602016-10-26 15:24:30 +02004097 goto out; /* F_DUPFD failed */
Denys Vlasenko60fb98e2018-03-30 22:15:14 +02004098 if (F_DUPFD_CLOEXEC == F_DUPFD) /* if old libc (w/o F_DUPFD_CLOEXEC) */
4099 close_on_exec_on(fd);
Denys Vlasenko940c7202011-03-02 04:07:14 +01004100 while (1) { /* while we are in the background */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004101 pgrp = tcgetpgrp(fd);
4102 if (pgrp < 0) {
4103 out:
4104 ash_msg("can't access tty; job control turned off");
4105 mflag = on = 0;
4106 goto close;
4107 }
4108 if (pgrp == getpgrp())
4109 break;
4110 killpg(0, SIGTTIN);
Denys Vlasenko940c7202011-03-02 04:07:14 +01004111 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004112 initialpgrp = pgrp;
4113
4114 setsignal(SIGTSTP);
4115 setsignal(SIGTTOU);
4116 setsignal(SIGTTIN);
4117 pgrp = rootpid;
4118 setpgid(0, pgrp);
4119 xtcsetpgrp(fd, pgrp);
4120 } else {
4121 /* turning job control off */
4122 fd = ttyfd;
4123 pgrp = initialpgrp;
Denis Vlasenko08c8c1d2007-04-28 22:39:02 +00004124 /* was xtcsetpgrp, but this can make exiting ash
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004125 * loop forever if pty is already deleted */
Denis Vlasenko08c8c1d2007-04-28 22:39:02 +00004126 tcsetpgrp(fd, pgrp);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004127 setpgid(0, pgrp);
4128 setsignal(SIGTSTP);
4129 setsignal(SIGTTOU);
4130 setsignal(SIGTTIN);
4131 close:
Denis Vlasenkoed270a52007-11-26 05:37:07 +00004132 if (fd >= 0)
4133 close(fd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004134 fd = -1;
4135 }
4136 ttyfd = fd;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004137 doing_jobctl = on;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004138}
4139
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02004140static int FAST_FUNC
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004141killcmd(int argc, char **argv)
4142{
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00004143 if (argv[1] && strcmp(argv[1], "-l") != 0) {
Denys Vlasenkob12553f2011-02-21 03:22:20 +01004144 int i = 1;
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00004145 do {
4146 if (argv[i][0] == '%') {
Denys Vlasenkob12553f2011-02-21 03:22:20 +01004147 /*
4148 * "kill %N" - job kill
4149 * Converting to pgrp / pid kill
4150 */
4151 struct job *jp;
4152 char *dst;
4153 int j, n;
4154
4155 jp = getjob(argv[i], 0);
4156 /*
4157 * In jobs started under job control, we signal
4158 * entire process group by kill -PGRP_ID.
4159 * This happens, f.e., in interactive shell.
4160 *
4161 * Otherwise, we signal each child via
4162 * kill PID1 PID2 PID3.
4163 * Testcases:
4164 * sh -c 'sleep 1|sleep 1 & kill %1'
4165 * sh -c 'true|sleep 2 & sleep 1; kill %1'
4166 * sh -c 'true|sleep 1 & sleep 2; kill %1'
4167 */
4168 n = jp->nprocs; /* can't be 0 (I hope) */
4169 if (jp->jobctl)
4170 n = 1;
4171 dst = alloca(n * sizeof(int)*4);
4172 argv[i] = dst;
4173 for (j = 0; j < n; j++) {
4174 struct procstat *ps = &jp->ps[j];
4175 /* Skip non-running and not-stopped members
4176 * (i.e. dead members) of the job
4177 */
4178 if (ps->ps_status != -1 && !WIFSTOPPED(ps->ps_status))
4179 continue;
4180 /*
4181 * kill_main has matching code to expect
4182 * leading space. Needed to not confuse
4183 * negative pids with "kill -SIGNAL_NO" syntax
4184 */
4185 dst += sprintf(dst, jp->jobctl ? " -%u" : " %u", (int)ps->ps_pid);
4186 }
4187 *dst = '\0';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004188 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00004189 } while (argv[++i]);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004190 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00004191 return kill_main(argc, argv);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004192}
4193
4194static void
Denys Vlasenko285ad152009-12-04 23:02:27 +01004195showpipe(struct job *jp /*, FILE *out*/)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004196{
Denys Vlasenko285ad152009-12-04 23:02:27 +01004197 struct procstat *ps;
4198 struct procstat *psend;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004199
Denys Vlasenko285ad152009-12-04 23:02:27 +01004200 psend = jp->ps + jp->nprocs;
4201 for (ps = jp->ps + 1; ps < psend; ps++)
4202 printf(" | %s", ps->ps_cmd);
Denys Vlasenko9c541002015-10-07 15:44:36 +02004203 newline_and_flush(stdout);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004204 flush_stdout_stderr();
4205}
4206
4207
4208static int
4209restartjob(struct job *jp, int mode)
4210{
4211 struct procstat *ps;
4212 int i;
4213 int status;
4214 pid_t pgid;
4215
4216 INT_OFF;
4217 if (jp->state == JOBDONE)
4218 goto out;
4219 jp->state = JOBRUNNING;
Denys Vlasenko285ad152009-12-04 23:02:27 +01004220 pgid = jp->ps[0].ps_pid;
Denys Vlasenko098b7132017-01-11 19:59:03 +01004221 if (mode == FORK_FG) {
4222 get_tty_state();
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004223 xtcsetpgrp(ttyfd, pgid);
Denys Vlasenko098b7132017-01-11 19:59:03 +01004224 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004225 killpg(pgid, SIGCONT);
4226 ps = jp->ps;
4227 i = jp->nprocs;
4228 do {
Denys Vlasenko285ad152009-12-04 23:02:27 +01004229 if (WIFSTOPPED(ps->ps_status)) {
4230 ps->ps_status = -1;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004231 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00004232 ps++;
4233 } while (--i);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004234 out:
4235 status = (mode == FORK_FG) ? waitforjob(jp) : 0;
4236 INT_ON;
4237 return status;
4238}
4239
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02004240static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00004241fg_bgcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004242{
4243 struct job *jp;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004244 int mode;
4245 int retval;
4246
4247 mode = (**argv == 'f') ? FORK_FG : FORK_BG;
4248 nextopt(nullstr);
4249 argv = argptr;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004250 do {
4251 jp = getjob(*argv, 1);
4252 if (mode == FORK_BG) {
4253 set_curjob(jp, CUR_RUNNING);
Denys Vlasenko285ad152009-12-04 23:02:27 +01004254 printf("[%d] ", jobno(jp));
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004255 }
Denys Vlasenko285ad152009-12-04 23:02:27 +01004256 out1str(jp->ps[0].ps_cmd);
4257 showpipe(jp /*, stdout*/);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004258 retval = restartjob(jp, mode);
4259 } while (*argv && *++argv);
4260 return retval;
4261}
4262#endif
4263
4264static int
Denys Vlasenko2bad3a32020-02-16 18:23:43 +01004265sprint_status48(char *os, int status, int sigonly)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004266{
Denys Vlasenko2bad3a32020-02-16 18:23:43 +01004267 char *s = os;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004268 int st;
4269
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004270 if (!WIFEXITED(status)) {
Johannes Schindelin9d4dc842017-07-14 22:25:58 +02004271#if JOBS
4272 if (WIFSTOPPED(status))
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004273 st = WSTOPSIG(status);
4274 else
Johannes Schindelin9d4dc842017-07-14 22:25:58 +02004275#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004276 st = WTERMSIG(status);
4277 if (sigonly) {
4278 if (st == SIGINT || st == SIGPIPE)
4279 goto out;
Johannes Schindelin9d4dc842017-07-14 22:25:58 +02004280#if JOBS
4281 if (WIFSTOPPED(status))
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004282 goto out;
Johannes Schindelin9d4dc842017-07-14 22:25:58 +02004283#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004284 }
4285 st &= 0x7f;
Denys Vlasenko33745b12021-02-18 13:44:27 +01004286 s = stpncpy(s, strsignal(st), 32);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004287 if (WCOREDUMP(status)) {
Denys Vlasenko2bad3a32020-02-16 18:23:43 +01004288 s = stpcpy(s, " (core dumped)");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004289 }
4290 } else if (!sigonly) {
4291 st = WEXITSTATUS(status);
Denys Vlasenko2bad3a32020-02-16 18:23:43 +01004292 s += fmtstr(s, 16, (st ? "Done(%d)" : "Done"), st);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004293 }
4294 out:
Denys Vlasenko2bad3a32020-02-16 18:23:43 +01004295 return s - os;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004296}
4297
Denys Vlasenko8f7b0242016-10-28 17:16:11 +02004298#define DOWAIT_NONBLOCK 0
4299#define DOWAIT_BLOCK 1
4300#define DOWAIT_BLOCK_OR_SIG 2
Ron Yorstone48559e2019-03-31 09:27:09 +01004301#if BASH_WAIT_N
Denys Vlasenko966f0872019-03-27 15:51:42 +01004302# define DOWAIT_JOBSTATUS 0x10 /* OR this to get job's exitstatus instead of pid */
4303#endif
Denys Vlasenko458c1f22016-10-27 23:51:19 +02004304
4305static int
Denys Vlasenko91e11eb2020-09-29 20:35:55 +02004306waitproc(int block, int *status)
4307{
4308 sigset_t oldmask;
4309 int flags = block == DOWAIT_BLOCK ? 0 : WNOHANG;
4310 int err;
4311
4312#if JOBS
4313 if (doing_jobctl)
4314 flags |= WUNTRACED;
4315#endif
4316
4317 do {
4318 got_sigchld = 0;
4319 do
4320 err = waitpid(-1, status, flags);
4321 while (err < 0 && errno == EINTR);
4322
4323 if (err || (err = -!block))
4324 break;
4325
4326 sigfillset(&oldmask);
4327 sigprocmask2(SIG_SETMASK, &oldmask); /* mask is updated */
4328 while (!got_sigchld && !pending_sig)
4329 sigsuspend(&oldmask);
4330 sigprocmask(SIG_SETMASK, &oldmask, NULL);
4331 //simpler, but unsafe: a signal can set pending_sig after check, but before pause():
4332 //while (!got_sigchld && !pending_sig)
4333 // pause();
4334
4335 } while (got_sigchld);
4336
4337 return err;
4338}
4339
4340static int
Denys Vlasenko47eb9792020-02-18 15:37:43 +01004341waitone(int block, struct job *job)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004342{
4343 int pid;
4344 int status;
4345 struct job *jp;
Denys Vlasenko91e11eb2020-09-29 20:35:55 +02004346 struct job *thisjob = NULL;
Ron Yorstone48559e2019-03-31 09:27:09 +01004347#if BASH_WAIT_N
Denys Vlasenko966f0872019-03-27 15:51:42 +01004348 bool want_jobexitstatus = (block & DOWAIT_JOBSTATUS);
4349 block = (block & ~DOWAIT_JOBSTATUS);
4350#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004351
Denys Vlasenkob543bda2016-10-27 20:08:28 +02004352 TRACE(("dowait(0x%x) called\n", block));
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00004353
Denys Vlasenko458c1f22016-10-27 23:51:19 +02004354 /* It's wrong to call waitpid() outside of INT_OFF region:
4355 * signal can arrive just after syscall return and handler can
4356 * longjmp away, losing stop/exit notification processing.
4357 * Thus, for "jobs" builtin, and for waiting for a fg job,
4358 * we call waitpid() (blocking or non-blocking) inside INT_OFF.
4359 *
4360 * However, for "wait" builtin it is wrong to simply call waitpid()
4361 * in INT_OFF region: "wait" needs to wait for any running job
4362 * to change state, but should exit on any trap too.
4363 * In INT_OFF region, a signal just before syscall entry can set
Denys Vlasenko8f7b0242016-10-28 17:16:11 +02004364 * pending_sig variables, but we can't check them, and we would
Denys Vlasenko458c1f22016-10-27 23:51:19 +02004365 * either enter a sleeping waitpid() (BUG), or need to busy-loop.
Denys Vlasenko8f7b0242016-10-28 17:16:11 +02004366 *
Denys Vlasenko458c1f22016-10-27 23:51:19 +02004367 * Because of this, we run inside INT_OFF, but use a special routine
Denys Vlasenko1ab7c2f2016-11-03 20:17:23 +01004368 * which combines waitpid() and sigsuspend().
Denys Vlasenko8f7b0242016-10-28 17:16:11 +02004369 * This is the reason why we need to have a handler for SIGCHLD:
Denys Vlasenko1ab7c2f2016-11-03 20:17:23 +01004370 * SIG_DFL handler does not wake sigsuspend().
Denys Vlasenko458c1f22016-10-27 23:51:19 +02004371 */
4372 INT_OFF;
Denys Vlasenko91e11eb2020-09-29 20:35:55 +02004373 pid = waitproc(block, &status);
4374 TRACE(("wait returns pid %d, status=%d\n", pid, status));
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004375 if (pid <= 0)
Denys Vlasenko458c1f22016-10-27 23:51:19 +02004376 goto out;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004377
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004378 for (jp = curjob; jp; jp = jp->prev_job) {
Denys Vlasenko4700fb52015-10-09 15:40:13 +02004379 int jobstate;
Denys Vlasenko285ad152009-12-04 23:02:27 +01004380 struct procstat *ps;
4381 struct procstat *psend;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004382 if (jp->state == JOBDONE)
4383 continue;
Denys Vlasenko4700fb52015-10-09 15:40:13 +02004384 jobstate = JOBDONE;
Denys Vlasenko285ad152009-12-04 23:02:27 +01004385 ps = jp->ps;
4386 psend = ps + jp->nprocs;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004387 do {
Denys Vlasenko285ad152009-12-04 23:02:27 +01004388 if (ps->ps_pid == pid) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004389 TRACE(("Job %d: changing status of proc %d "
4390 "from 0x%x to 0x%x\n",
Denys Vlasenko285ad152009-12-04 23:02:27 +01004391 jobno(jp), pid, ps->ps_status, status));
4392 ps->ps_status = status;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004393 thisjob = jp;
4394 }
Denys Vlasenko285ad152009-12-04 23:02:27 +01004395 if (ps->ps_status == -1)
Denys Vlasenko4700fb52015-10-09 15:40:13 +02004396 jobstate = JOBRUNNING;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004397#if JOBS
Denys Vlasenko4700fb52015-10-09 15:40:13 +02004398 if (jobstate == JOBRUNNING)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004399 continue;
Denys Vlasenko285ad152009-12-04 23:02:27 +01004400 if (WIFSTOPPED(ps->ps_status)) {
4401 jp->stopstatus = ps->ps_status;
Denys Vlasenko4700fb52015-10-09 15:40:13 +02004402 jobstate = JOBSTOPPED;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004403 }
4404#endif
Denys Vlasenko285ad152009-12-04 23:02:27 +01004405 } while (++ps < psend);
Denys Vlasenko4700fb52015-10-09 15:40:13 +02004406 if (!thisjob)
4407 continue;
4408
4409 /* Found the job where one of its processes changed its state.
4410 * Is there at least one live and running process in this job? */
4411 if (jobstate != JOBRUNNING) {
4412 /* No. All live processes in the job are stopped
4413 * (JOBSTOPPED) or there are no live processes (JOBDONE)
4414 */
4415 thisjob->changed = 1;
4416 if (thisjob->state != jobstate) {
4417 TRACE(("Job %d: changing state from %d to %d\n",
4418 jobno(thisjob), thisjob->state, jobstate));
4419 thisjob->state = jobstate;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004420#if JOBS
Denys Vlasenko4700fb52015-10-09 15:40:13 +02004421 if (jobstate == JOBSTOPPED)
4422 set_curjob(thisjob, CUR_STOPPED);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004423#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004424 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004425 }
Denys Vlasenko4700fb52015-10-09 15:40:13 +02004426 goto out;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004427 }
Denys Vlasenko4700fb52015-10-09 15:40:13 +02004428 /* The process wasn't found in job list */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004429 out:
4430 INT_ON;
4431
Ron Yorstone48559e2019-03-31 09:27:09 +01004432#if BASH_WAIT_N
Denys Vlasenko966f0872019-03-27 15:51:42 +01004433 if (want_jobexitstatus) {
4434 pid = -1;
4435 if (thisjob && thisjob->state == JOBDONE)
4436 pid = thisjob->ps[thisjob->nprocs - 1].ps_status;
4437 }
4438#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004439 if (thisjob && thisjob == job) {
4440 char s[48 + 1];
4441 int len;
4442
Denys Vlasenko9c541002015-10-07 15:44:36 +02004443 len = sprint_status48(s, status, 1);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004444 if (len) {
4445 s[len] = '\n';
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004446 s[len + 1] = '\0';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004447 out2str(s);
4448 }
4449 }
4450 return pid;
4451}
4452
Denys Vlasenko47eb9792020-02-18 15:37:43 +01004453static int
4454dowait(int block, struct job *jp)
4455{
Denys Vlasenko8d5f4652020-09-29 16:44:46 +02004456 smallint gotchld = *(volatile smallint *)&got_sigchld;
4457 int rpid;
4458 int pid;
Denys Vlasenko47eb9792020-02-18 15:37:43 +01004459
Denys Vlasenko8d5f4652020-09-29 16:44:46 +02004460 if (jp && jp->state != JOBRUNNING)
4461 block = DOWAIT_NONBLOCK;
4462
4463 if (block == DOWAIT_NONBLOCK && !gotchld)
4464 return 1;
4465
4466 rpid = 1;
4467
4468 do {
Denys Vlasenko47eb9792020-02-18 15:37:43 +01004469 pid = waitone(block, jp);
Denys Vlasenko8d5f4652020-09-29 16:44:46 +02004470 rpid &= !!pid;
Denys Vlasenko47eb9792020-02-18 15:37:43 +01004471
Denys Vlasenko8d5f4652020-09-29 16:44:46 +02004472 if (!pid || (jp && jp->state != JOBRUNNING))
4473 block = DOWAIT_NONBLOCK;
4474 } while (pid >= 0);
4475
4476 return rpid;
Denys Vlasenko47eb9792020-02-18 15:37:43 +01004477}
4478
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004479#if JOBS
4480static void
Denys Vlasenko9c541002015-10-07 15:44:36 +02004481showjob(struct job *jp, int mode)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004482{
4483 struct procstat *ps;
4484 struct procstat *psend;
4485 int col;
Denis Vlasenko40ba9982007-07-14 00:48:29 +00004486 int indent_col;
Denys Vlasenko9c541002015-10-07 15:44:36 +02004487 char s[16 + 16 + 48];
4488 FILE *out = (mode & SHOW_STDERR ? stderr : stdout);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004489
4490 ps = jp->ps;
4491
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004492 if (mode & SHOW_ONLY_PGID) { /* jobs -p */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004493 /* just output process (group) id of pipeline */
Denys Vlasenko285ad152009-12-04 23:02:27 +01004494 fprintf(out, "%d\n", ps->ps_pid);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004495 return;
4496 }
4497
4498 col = fmtstr(s, 16, "[%d] ", jobno(jp));
Denis Vlasenko40ba9982007-07-14 00:48:29 +00004499 indent_col = col;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004500
4501 if (jp == curjob)
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004502 s[col - 3] = '+';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004503 else if (curjob && jp == curjob->prev_job)
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004504 s[col - 3] = '-';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004505
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004506 if (mode & SHOW_PIDS)
Denys Vlasenko285ad152009-12-04 23:02:27 +01004507 col += fmtstr(s + col, 16, "%d ", ps->ps_pid);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004508
4509 psend = ps + jp->nprocs;
4510
4511 if (jp->state == JOBRUNNING) {
4512 strcpy(s + col, "Running");
4513 col += sizeof("Running") - 1;
4514 } else {
Denys Vlasenko285ad152009-12-04 23:02:27 +01004515 int status = psend[-1].ps_status;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004516 if (jp->state == JOBSTOPPED)
4517 status = jp->stopstatus;
Denys Vlasenko9c541002015-10-07 15:44:36 +02004518 col += sprint_status48(s + col, status, 0);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004519 }
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004520 /* By now, "[JOBID]* [maybe PID] STATUS" is printed */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004521
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004522 /* This loop either prints "<cmd1> | <cmd2> | <cmd3>" line
4523 * or prints several "PID | <cmdN>" lines,
4524 * depending on SHOW_PIDS bit.
4525 * We do not print status of individual processes
4526 * between PID and <cmdN>. bash does it, but not very well:
4527 * first line shows overall job status, not process status,
4528 * making it impossible to know 1st process status.
4529 */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004530 goto start;
Denys Vlasenko285ad152009-12-04 23:02:27 +01004531 do {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004532 /* for each process */
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004533 s[0] = '\0';
4534 col = 33;
4535 if (mode & SHOW_PIDS)
Denys Vlasenko285ad152009-12-04 23:02:27 +01004536 col = fmtstr(s, 48, "\n%*c%d ", indent_col, ' ', ps->ps_pid) - 1;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004537 start:
Denys Vlasenko285ad152009-12-04 23:02:27 +01004538 fprintf(out, "%s%*c%s%s",
4539 s,
4540 33 - col >= 0 ? 33 - col : 0, ' ',
4541 ps == jp->ps ? "" : "| ",
4542 ps->ps_cmd
4543 );
4544 } while (++ps != psend);
Denys Vlasenko9c541002015-10-07 15:44:36 +02004545 newline_and_flush(out);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004546
4547 jp->changed = 0;
4548
4549 if (jp->state == JOBDONE) {
4550 TRACE(("showjob: freeing job %d\n", jobno(jp)));
4551 freejob(jp);
4552 }
4553}
4554
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004555/*
4556 * Print a list of jobs. If "change" is nonzero, only print jobs whose
4557 * statuses have changed since the last call to showjobs.
4558 */
4559static void
Denys Vlasenko9c541002015-10-07 15:44:36 +02004560showjobs(int mode)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004561{
4562 struct job *jp;
4563
Denys Vlasenko883cea42009-07-11 15:31:59 +02004564 TRACE(("showjobs(0x%x) called\n", mode));
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004565
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004566 /* Handle all finished jobs */
Denys Vlasenko47eb9792020-02-18 15:37:43 +01004567 dowait(DOWAIT_NONBLOCK, NULL);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004568
4569 for (jp = curjob; jp; jp = jp->prev_job) {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004570 if (!(mode & SHOW_CHANGED) || jp->changed) {
Denys Vlasenko9c541002015-10-07 15:44:36 +02004571 showjob(jp, mode);
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004572 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004573 }
4574}
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004575
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02004576static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00004577jobscmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004578{
4579 int mode, m;
4580
4581 mode = 0;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02004582 while ((m = nextopt("lp")) != '\0') {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004583 if (m == 'l')
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004584 mode |= SHOW_PIDS;
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004585 else
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004586 mode |= SHOW_ONLY_PGID;
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004587 }
4588
4589 argv = argptr;
4590 if (*argv) {
4591 do
Denys Vlasenko9c541002015-10-07 15:44:36 +02004592 showjob(getjob(*argv, 0), mode);
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004593 while (*++argv);
Denys Vlasenko285ad152009-12-04 23:02:27 +01004594 } else {
Denys Vlasenko9c541002015-10-07 15:44:36 +02004595 showjobs(mode);
Denys Vlasenko285ad152009-12-04 23:02:27 +01004596 }
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004597
4598 return 0;
4599}
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004600#endif /* JOBS */
4601
Michael Abbott359da5e2009-12-04 23:03:29 +01004602/* Called only on finished or stopped jobs (no members are running) */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004603static int
4604getstatus(struct job *job)
4605{
4606 int status;
4607 int retval;
Michael Abbott359da5e2009-12-04 23:03:29 +01004608 struct procstat *ps;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004609
Michael Abbott359da5e2009-12-04 23:03:29 +01004610 /* Fetch last member's status */
4611 ps = job->ps + job->nprocs - 1;
4612 status = ps->ps_status;
4613 if (pipefail) {
4614 /* "set -o pipefail" mode: use last _nonzero_ status */
4615 while (status == 0 && --ps >= job->ps)
4616 status = ps->ps_status;
4617 }
4618
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004619 retval = WEXITSTATUS(status);
4620 if (!WIFEXITED(status)) {
4621#if JOBS
4622 retval = WSTOPSIG(status);
4623 if (!WIFSTOPPED(status))
4624#endif
4625 {
4626 /* XXX: limits number of signals */
4627 retval = WTERMSIG(status);
4628#if JOBS
4629 if (retval == SIGINT)
4630 job->sigint = 1;
4631#endif
4632 }
Denys Vlasenko93e2a222020-12-23 12:23:21 +01004633 retval |= 128;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004634 }
Denys Vlasenko883cea42009-07-11 15:31:59 +02004635 TRACE(("getstatus: job %d, nproc %d, status 0x%x, retval 0x%x\n",
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004636 jobno(job), job->nprocs, status, retval));
4637 return retval;
4638}
4639
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02004640static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00004641waitcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004642{
4643 struct job *job;
4644 int retval;
4645 struct job *jp;
Ron Yorstone48559e2019-03-31 09:27:09 +01004646#if BASH_WAIT_N
Denys Vlasenko966f0872019-03-27 15:51:42 +01004647 int status;
4648 char one = nextopt("n");
4649#else
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004650 nextopt(nullstr);
Denys Vlasenko966f0872019-03-27 15:51:42 +01004651#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004652 retval = 0;
4653
4654 argv = argptr;
Denys Vlasenko966f0872019-03-27 15:51:42 +01004655 if (!argv[0]) {
4656 /* wait for all jobs / one job if -n */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004657 for (;;) {
4658 jp = curjob;
Ron Yorstone48559e2019-03-31 09:27:09 +01004659#if BASH_WAIT_N
Denys Vlasenko966f0872019-03-27 15:51:42 +01004660 if (one && !jp)
4661 /* exitcode of "wait -n" with nothing to wait for is 127, not 0 */
4662 retval = 127;
4663#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004664 while (1) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004665 if (!jp) /* no running procs */
4666 goto ret;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004667 if (jp->state == JOBRUNNING)
4668 break;
4669 jp->waited = 1;
4670 jp = jp->prev_job;
4671 }
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004672 /* man bash:
4673 * "When bash is waiting for an asynchronous command via
4674 * the wait builtin, the reception of a signal for which a trap
4675 * has been set will cause the wait builtin to return immediately
4676 * with an exit status greater than 128, immediately after which
4677 * the trap is executed."
Denys Vlasenko69188112016-10-27 20:18:18 +02004678 */
Ron Yorstone48559e2019-03-31 09:27:09 +01004679#if BASH_WAIT_N
Denys Vlasenko966f0872019-03-27 15:51:42 +01004680 status = dowait(DOWAIT_BLOCK_OR_SIG | DOWAIT_JOBSTATUS, NULL);
4681#else
Denys Vlasenko458c1f22016-10-27 23:51:19 +02004682 dowait(DOWAIT_BLOCK_OR_SIG, NULL);
Denys Vlasenko966f0872019-03-27 15:51:42 +01004683#endif
Denys Vlasenko47eb9792020-02-18 15:37:43 +01004684 /* if child sends us a signal *and immediately exits*,
4685 * dowait() returns pid > 0. Check this case,
4686 * not "if (dowait() < 0)"!
4687 */
Denys Vlasenko7c1ed9f2010-05-17 04:42:40 +02004688 if (pending_sig)
Denys Vlasenkoc0663c72016-10-27 21:09:01 +02004689 goto sigout;
Ron Yorstone48559e2019-03-31 09:27:09 +01004690#if BASH_WAIT_N
Denys Vlasenko966f0872019-03-27 15:51:42 +01004691 if (one) {
4692 /* wait -n waits for one _job_, not one _process_.
4693 * date; sleep 3 & sleep 2 | sleep 1 & wait -n; date
4694 * should wait for 2 seconds. Not 1 or 3.
4695 */
4696 if (status != -1 && !WIFSTOPPED(status)) {
4697 retval = WEXITSTATUS(status);
4698 if (WIFSIGNALED(status))
Denys Vlasenko93e2a222020-12-23 12:23:21 +01004699 retval = 128 | WTERMSIG(status);
Denys Vlasenko966f0872019-03-27 15:51:42 +01004700 goto ret;
4701 }
4702 }
4703#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004704 }
4705 }
4706
4707 retval = 127;
4708 do {
4709 if (**argv != '%') {
4710 pid_t pid = number(*argv);
4711 job = curjob;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004712 while (1) {
4713 if (!job)
4714 goto repeat;
Denys Vlasenko285ad152009-12-04 23:02:27 +01004715 if (job->ps[job->nprocs - 1].ps_pid == pid)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004716 break;
4717 job = job->prev_job;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004718 }
Denys Vlasenkob12553f2011-02-21 03:22:20 +01004719 } else {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004720 job = getjob(*argv, 0);
Denys Vlasenkob12553f2011-02-21 03:22:20 +01004721 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004722 /* loop until process terminated or stopped */
Denys Vlasenko91e11eb2020-09-29 20:35:55 +02004723 dowait(DOWAIT_BLOCK_OR_SIG, job);
Denys Vlasenko47eb9792020-02-18 15:37:43 +01004724 if (pending_sig)
4725 goto sigout;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004726 job->waited = 1;
4727 retval = getstatus(job);
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004728 repeat: ;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004729 } while (*++argv);
4730
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004731 ret:
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004732 return retval;
Denys Vlasenkoc0663c72016-10-27 21:09:01 +02004733 sigout:
Denys Vlasenko93e2a222020-12-23 12:23:21 +01004734 retval = 128 | pending_sig;
Denys Vlasenkoc0663c72016-10-27 21:09:01 +02004735 return retval;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004736}
4737
4738static struct job *
4739growjobtab(void)
4740{
4741 size_t len;
4742 ptrdiff_t offset;
4743 struct job *jp, *jq;
4744
4745 len = njobs * sizeof(*jp);
4746 jq = jobtab;
4747 jp = ckrealloc(jq, len + 4 * sizeof(*jp));
4748
4749 offset = (char *)jp - (char *)jq;
4750 if (offset) {
4751 /* Relocate pointers */
4752 size_t l = len;
4753
4754 jq = (struct job *)((char *)jq + l);
4755 while (l) {
4756 l -= sizeof(*jp);
4757 jq--;
4758#define joff(p) ((struct job *)((char *)(p) + l))
4759#define jmove(p) (p) = (void *)((char *)(p) + offset)
4760 if (joff(jp)->ps == &jq->ps0)
4761 jmove(joff(jp)->ps);
4762 if (joff(jp)->prev_job)
4763 jmove(joff(jp)->prev_job);
4764 }
4765 if (curjob)
4766 jmove(curjob);
4767#undef joff
4768#undef jmove
4769 }
4770
4771 njobs += 4;
4772 jobtab = jp;
4773 jp = (struct job *)((char *)jp + len);
4774 jq = jp + 3;
4775 do {
4776 jq->used = 0;
4777 } while (--jq >= jp);
4778 return jp;
4779}
4780
4781/*
4782 * Return a new job structure.
4783 * Called with interrupts off.
4784 */
4785static struct job *
Denis Vlasenko68404f12008-03-17 09:00:54 +00004786makejob(/*union node *node,*/ int nprocs)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004787{
4788 int i;
4789 struct job *jp;
4790
4791 for (i = njobs, jp = jobtab; ; jp++) {
4792 if (--i < 0) {
4793 jp = growjobtab();
4794 break;
4795 }
4796 if (jp->used == 0)
4797 break;
4798 if (jp->state != JOBDONE || !jp->waited)
4799 continue;
4800#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004801 if (doing_jobctl)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004802 continue;
4803#endif
4804 freejob(jp);
4805 break;
4806 }
4807 memset(jp, 0, sizeof(*jp));
4808#if JOBS
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004809 /* jp->jobctl is a bitfield.
Denys Vlasenko098b7132017-01-11 19:59:03 +01004810 * "jp->jobctl |= doing_jobctl" likely to give awful code */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004811 if (doing_jobctl)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004812 jp->jobctl = 1;
4813#endif
4814 jp->prev_job = curjob;
4815 curjob = jp;
4816 jp->used = 1;
4817 jp->ps = &jp->ps0;
4818 if (nprocs > 1) {
4819 jp->ps = ckmalloc(nprocs * sizeof(struct procstat));
4820 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00004821 TRACE(("makejob(%d) returns %%%d\n", nprocs,
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004822 jobno(jp)));
4823 return jp;
4824}
4825
4826#if JOBS
4827/*
4828 * Return a string identifying a command (to be printed by the
4829 * jobs command).
4830 */
4831static char *cmdnextc;
4832
4833static void
4834cmdputs(const char *s)
4835{
Denys Vlasenko965b7952020-11-30 13:03:03 +01004836 static const char vstype[VSTYPE + 1][3] ALIGN1 = {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00004837 "", "}", "-", "+", "?", "=",
4838 "%", "%%", "#", "##"
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01004839 IF_BASH_SUBSTR(, ":")
4840 IF_BASH_PATTERN_SUBST(, "/", "//")
Denis Vlasenko92e13c22008-03-25 01:17:40 +00004841 };
4842
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004843 const char *p, *str;
Denys Vlasenko46a14772009-12-10 21:27:13 +01004844 char cc[2];
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004845 char *nextc;
Denys Vlasenkocd716832009-11-28 22:14:02 +01004846 unsigned char c;
4847 unsigned char subtype = 0;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004848 int quoted = 0;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004849
Denys Vlasenko46a14772009-12-10 21:27:13 +01004850 cc[1] = '\0';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004851 nextc = makestrspace((strlen(s) + 1) * 8, cmdnextc);
4852 p = s;
Denys Vlasenko46a14772009-12-10 21:27:13 +01004853 while ((c = *p++) != '\0') {
Denis Vlasenkoef527f52008-06-23 01:52:30 +00004854 str = NULL;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004855 switch (c) {
4856 case CTLESC:
4857 c = *p++;
4858 break;
4859 case CTLVAR:
4860 subtype = *p++;
4861 if ((subtype & VSTYPE) == VSLENGTH)
4862 str = "${#";
4863 else
4864 str = "${";
Ron Yorston549deab2015-05-18 09:57:51 +02004865 goto dostr;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004866 case CTLENDVAR:
Denys Vlasenko3f4847b2020-02-16 19:06:42 +01004867 str = "\"}";
4868 str += !(quoted & 1);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004869 quoted >>= 1;
4870 subtype = 0;
4871 goto dostr;
Ron Yorstona1b0d382020-07-23 08:32:27 +01004872#if BASH_PROCESS_SUBST
4873 case CTLBACKQ:
4874 c = '$';
4875 str = "(...)";
4876 break;
4877 case CTLTOPROC:
4878 c = '>';
4879 str = "(...)";
4880 break;
4881 case CTLFROMPROC:
4882 c = '<';
4883 str = "(...)";
4884 break;
4885#else
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004886 case CTLBACKQ:
4887 str = "$(...)";
4888 goto dostr;
Ron Yorstona1b0d382020-07-23 08:32:27 +01004889#endif
Denys Vlasenko0b883582016-12-23 16:49:07 +01004890#if ENABLE_FEATURE_SH_MATH
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004891 case CTLARI:
4892 str = "$((";
4893 goto dostr;
4894 case CTLENDARI:
4895 str = "))";
4896 goto dostr;
4897#endif
4898 case CTLQUOTEMARK:
4899 quoted ^= 1;
4900 c = '"';
4901 break;
4902 case '=':
4903 if (subtype == 0)
4904 break;
4905 if ((subtype & VSTYPE) != VSNORMAL)
4906 quoted <<= 1;
4907 str = vstype[subtype & VSTYPE];
4908 if (subtype & VSNUL)
4909 c = ':';
4910 else
4911 goto checkstr;
4912 break;
4913 case '\'':
4914 case '\\':
4915 case '"':
4916 case '$':
4917 /* These can only happen inside quotes */
4918 cc[0] = c;
4919 str = cc;
Denys Vlasenkod0fff912017-07-31 14:32:18 +02004920//FIXME:
4921// $ true $$ &
4922// $ <cr>
4923// [1]+ Done true ${\$} <<=== BUG: ${\$} is not a valid way to write $$ (${$} would be ok)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004924 c = '\\';
4925 break;
4926 default:
4927 break;
4928 }
4929 USTPUTC(c, nextc);
4930 checkstr:
4931 if (!str)
4932 continue;
4933 dostr:
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02004934 while ((c = *str++) != '\0') {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004935 USTPUTC(c, nextc);
4936 }
Denys Vlasenko46a14772009-12-10 21:27:13 +01004937 } /* while *p++ not NUL */
4938
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004939 if (quoted & 1) {
4940 USTPUTC('"', nextc);
4941 }
4942 *nextc = 0;
4943 cmdnextc = nextc;
4944}
4945
4946/* cmdtxt() and cmdlist() call each other */
4947static void cmdtxt(union node *n);
4948
4949static void
4950cmdlist(union node *np, int sep)
4951{
4952 for (; np; np = np->narg.next) {
4953 if (!sep)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004954 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004955 cmdtxt(np);
4956 if (sep && np->narg.next)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004957 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004958 }
4959}
4960
4961static void
4962cmdtxt(union node *n)
4963{
4964 union node *np;
4965 struct nodelist *lp;
4966 const char *p;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004967
4968 if (!n)
4969 return;
4970 switch (n->type) {
4971 default:
4972#if DEBUG
4973 abort();
4974#endif
4975 case NPIPE:
4976 lp = n->npipe.cmdlist;
4977 for (;;) {
4978 cmdtxt(lp->n);
4979 lp = lp->next;
4980 if (!lp)
4981 break;
4982 cmdputs(" | ");
4983 }
4984 break;
4985 case NSEMI:
4986 p = "; ";
4987 goto binop;
4988 case NAND:
4989 p = " && ";
4990 goto binop;
4991 case NOR:
4992 p = " || ";
4993 binop:
4994 cmdtxt(n->nbinary.ch1);
4995 cmdputs(p);
4996 n = n->nbinary.ch2;
4997 goto donode;
4998 case NREDIR:
4999 case NBACKGND:
5000 n = n->nredir.n;
5001 goto donode;
5002 case NNOT:
5003 cmdputs("!");
5004 n = n->nnot.com;
5005 donode:
5006 cmdtxt(n);
5007 break;
5008 case NIF:
5009 cmdputs("if ");
5010 cmdtxt(n->nif.test);
5011 cmdputs("; then ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005012 if (n->nif.elsepart) {
Denys Vlasenko7cee00e2009-07-24 01:08:03 +02005013 cmdtxt(n->nif.ifpart);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005014 cmdputs("; else ");
5015 n = n->nif.elsepart;
Denys Vlasenko7cee00e2009-07-24 01:08:03 +02005016 } else {
5017 n = n->nif.ifpart;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005018 }
5019 p = "; fi";
5020 goto dotail;
5021 case NSUBSHELL:
5022 cmdputs("(");
5023 n = n->nredir.n;
5024 p = ")";
5025 goto dotail;
5026 case NWHILE:
5027 p = "while ";
5028 goto until;
5029 case NUNTIL:
5030 p = "until ";
5031 until:
5032 cmdputs(p);
5033 cmdtxt(n->nbinary.ch1);
5034 n = n->nbinary.ch2;
5035 p = "; done";
5036 dodo:
5037 cmdputs("; do ");
5038 dotail:
5039 cmdtxt(n);
5040 goto dotail2;
5041 case NFOR:
5042 cmdputs("for ");
5043 cmdputs(n->nfor.var);
5044 cmdputs(" in ");
5045 cmdlist(n->nfor.args, 1);
5046 n = n->nfor.body;
5047 p = "; done";
5048 goto dodo;
5049 case NDEFUN:
Denys Vlasenko675d24a2018-01-27 22:02:05 +01005050 cmdputs(n->ndefun.text);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005051 p = "() { ... }";
5052 goto dotail2;
5053 case NCMD:
5054 cmdlist(n->ncmd.args, 1);
5055 cmdlist(n->ncmd.redirect, 0);
5056 break;
5057 case NARG:
5058 p = n->narg.text;
5059 dotail2:
5060 cmdputs(p);
5061 break;
5062 case NHERE:
5063 case NXHERE:
5064 p = "<<...";
5065 goto dotail2;
5066 case NCASE:
5067 cmdputs("case ");
5068 cmdputs(n->ncase.expr->narg.text);
5069 cmdputs(" in ");
5070 for (np = n->ncase.cases; np; np = np->nclist.next) {
5071 cmdtxt(np->nclist.pattern);
5072 cmdputs(") ");
5073 cmdtxt(np->nclist.body);
5074 cmdputs(";; ");
5075 }
5076 p = "esac";
5077 goto dotail2;
5078 case NTO:
5079 p = ">";
5080 goto redir;
5081 case NCLOBBER:
5082 p = ">|";
5083 goto redir;
5084 case NAPPEND:
5085 p = ">>";
5086 goto redir;
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01005087#if BASH_REDIR_OUTPUT
Denis Vlasenko559691a2008-10-05 18:39:31 +00005088 case NTO2:
5089#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005090 case NTOFD:
5091 p = ">&";
5092 goto redir;
5093 case NFROM:
5094 p = "<";
5095 goto redir;
5096 case NFROMFD:
5097 p = "<&";
5098 goto redir;
5099 case NFROMTO:
5100 p = "<>";
5101 redir:
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005102 cmdputs(utoa(n->nfile.fd));
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005103 cmdputs(p);
5104 if (n->type == NTOFD || n->type == NFROMFD) {
Denys Vlasenkod0fff912017-07-31 14:32:18 +02005105 if (n->ndup.dupfd >= 0)
5106 cmdputs(utoa(n->ndup.dupfd));
5107 else
5108 cmdputs("-");
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005109 break;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005110 }
5111 n = n->nfile.fname;
5112 goto donode;
5113 }
5114}
5115
5116static char *
5117commandtext(union node *n)
5118{
5119 char *name;
5120
5121 STARTSTACKSTR(cmdnextc);
5122 cmdtxt(n);
5123 name = stackblock();
Denys Vlasenko6a94cee2016-10-25 17:40:25 +02005124 TRACE(("commandtext: name %p, end %p\n", name, cmdnextc));
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005125 return ckstrdup(name);
5126}
5127#endif /* JOBS */
5128
5129/*
5130 * Fork off a subshell. If we are doing job control, give the subshell its
5131 * own process group. Jp is a job structure that the job is to be added to.
5132 * N is the command that will be evaluated by the child. Both jp and n may
5133 * be NULL. The mode parameter can be one of the following:
5134 * FORK_FG - Fork off a foreground process.
5135 * FORK_BG - Fork off a background process.
5136 * FORK_NOJOB - Like FORK_FG, but don't give the process its own
5137 * process group even if job control is on.
5138 *
5139 * When job control is turned off, background processes have their standard
5140 * input redirected to /dev/null (except for the second and later processes
5141 * in a pipeline).
5142 *
5143 * Called with interrupts off.
5144 */
5145/*
5146 * Clear traps on a fork.
5147 */
5148static void
5149clear_traps(void)
5150{
5151 char **tp;
5152
Denys Vlasenkob4f51d32016-10-27 12:55:09 +02005153 INT_OFF;
Roberto A. Fogliettae0bf3df2021-09-07 01:19:31 +02005154 for (tp = trap; tp <= &trap[NTRAP_LAST]; tp++) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00005155 if (*tp && **tp) { /* trap not NULL or "" (SIG_IGN) */
Denys Vlasenkoe305c282009-09-25 02:12:27 +02005156 if (trap_ptr == trap)
5157 free(*tp);
5158 /* else: it "belongs" to trap_ptr vector, don't free */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005159 *tp = NULL;
Roberto A. Fogliettae0bf3df2021-09-07 01:19:31 +02005160 if ((tp - trap) != 0 && (tp - trap) < NSIG)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005161 setsignal(tp - trap);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005162 }
5163 }
Alexander Shishkinccb97712010-07-25 13:07:39 +02005164 may_have_traps = 0;
Denys Vlasenkob4f51d32016-10-27 12:55:09 +02005165 INT_ON;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005166}
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00005167
5168/* Lives far away from here, needed for forkchild */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005169static void closescript(void);
Denis Vlasenko41770222007-10-07 18:02:52 +00005170
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00005171/* Called after fork(), in child */
Denys Vlasenko70392332016-10-27 02:31:55 +02005172/* jp and n are NULL when called by openhere() for heredoc support */
Denys Vlasenko21d87d42009-09-25 00:06:51 +02005173static NOINLINE void
Denys Vlasenkoe56f22a2009-07-24 00:16:59 +02005174forkchild(struct job *jp, union node *n, int mode)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005175{
5176 int oldlvl;
5177
5178 TRACE(("Child shell %d\n", getpid()));
5179 oldlvl = shlvl;
5180 shlvl++;
5181
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00005182 /* man bash: "Non-builtin commands run by bash have signal handlers
5183 * set to the values inherited by the shell from its parent".
5184 * Do we do it correctly? */
5185
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005186 closescript();
Denys Vlasenko844f9902009-09-23 03:25:52 +02005187
5188 if (mode == FORK_NOJOB /* is it `xxx` ? */
5189 && n && n->type == NCMD /* is it single cmd? */
5190 /* && n->ncmd.args->type == NARG - always true? */
Denys Vlasenko74269202010-02-21 01:26:42 +01005191 && n->ncmd.args && strcmp(n->ncmd.args->narg.text, "trap") == 0
Denys Vlasenko844f9902009-09-23 03:25:52 +02005192 && n->ncmd.args->narg.next == NULL /* "trap" with no arguments */
5193 /* && n->ncmd.args->narg.backquote == NULL - do we need to check this? */
5194 ) {
5195 TRACE(("Trap hack\n"));
5196 /* Awful hack for `trap` or $(trap).
5197 *
5198 * http://www.opengroup.org/onlinepubs/009695399/utilities/trap.html
5199 * contains an example where "trap" is executed in a subshell:
5200 *
5201 * save_traps=$(trap)
5202 * ...
5203 * eval "$save_traps"
5204 *
5205 * Standard does not say that "trap" in subshell shall print
5206 * parent shell's traps. It only says that its output
5207 * must have suitable form, but then, in the above example
5208 * (which is not supposed to be normative), it implies that.
5209 *
5210 * bash (and probably other shell) does implement it
5211 * (traps are reset to defaults, but "trap" still shows them),
5212 * but as a result, "trap" logic is hopelessly messed up:
5213 *
5214 * # trap
5215 * trap -- 'echo Ho' SIGWINCH <--- we have a handler
5216 * # (trap) <--- trap is in subshell - no output (correct, traps are reset)
5217 * # true | trap <--- trap is in subshell - no output (ditto)
5218 * # echo `true | trap` <--- in subshell - output (but traps are reset!)
5219 * trap -- 'echo Ho' SIGWINCH
5220 * # echo `(trap)` <--- in subshell in subshell - output
5221 * trap -- 'echo Ho' SIGWINCH
5222 * # echo `true | (trap)` <--- in subshell in subshell in subshell - output!
5223 * trap -- 'echo Ho' SIGWINCH
5224 *
5225 * The rules when to forget and when to not forget traps
5226 * get really complex and nonsensical.
5227 *
5228 * Our solution: ONLY bare $(trap) or `trap` is special.
5229 */
Denys Vlasenko8f88d852009-09-25 12:12:53 +02005230 /* Save trap handler strings for trap builtin to print */
Ron Yorstond840c5d2015-07-19 23:05:20 +02005231 trap_ptr = xmemdup(trap, sizeof(trap));
Denys Vlasenko8f88d852009-09-25 12:12:53 +02005232 /* Fall through into clearing traps */
Denys Vlasenko844f9902009-09-23 03:25:52 +02005233 }
Denys Vlasenkoe305c282009-09-25 02:12:27 +02005234 clear_traps();
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005235#if JOBS
5236 /* do job control only in root shell */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00005237 doing_jobctl = 0;
Denys Vlasenkob12553f2011-02-21 03:22:20 +01005238 if (mode != FORK_NOJOB && jp->jobctl && oldlvl == 0) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005239 pid_t pgrp;
5240
5241 if (jp->nprocs == 0)
5242 pgrp = getpid();
5243 else
Denys Vlasenko285ad152009-12-04 23:02:27 +01005244 pgrp = jp->ps[0].ps_pid;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00005245 /* this can fail because we are doing it in the parent also */
5246 setpgid(0, pgrp);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005247 if (mode == FORK_FG)
5248 xtcsetpgrp(ttyfd, pgrp);
5249 setsignal(SIGTSTP);
5250 setsignal(SIGTTOU);
5251 } else
5252#endif
5253 if (mode == FORK_BG) {
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00005254 /* man bash: "When job control is not in effect,
5255 * asynchronous commands ignore SIGINT and SIGQUIT" */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005256 ignoresig(SIGINT);
5257 ignoresig(SIGQUIT);
5258 if (jp->nprocs == 0) {
5259 close(0);
5260 if (open(bb_dev_null, O_RDONLY) != 0)
Denys Vlasenko12ffefb2017-08-23 14:52:56 +02005261 ash_msg_and_raise_perror("can't open '%s'", bb_dev_null);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005262 }
5263 }
Denys Vlasenkob12553f2011-02-21 03:22:20 +01005264 if (oldlvl == 0) {
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00005265 if (iflag) { /* why if iflag only? */
5266 setsignal(SIGINT);
5267 setsignal(SIGTERM);
5268 }
5269 /* man bash:
5270 * "In all cases, bash ignores SIGQUIT. Non-builtin
5271 * commands run by bash have signal handlers
5272 * set to the values inherited by the shell
5273 * from its parent".
5274 * Take care of the second rule: */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005275 setsignal(SIGQUIT);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005276 }
Denys Vlasenkoe56f22a2009-07-24 00:16:59 +02005277#if JOBS
Denys Vlasenko844f9902009-09-23 03:25:52 +02005278 if (n && n->type == NCMD
Denys Vlasenko74269202010-02-21 01:26:42 +01005279 && n->ncmd.args && strcmp(n->ncmd.args->narg.text, "jobs") == 0
Denys Vlasenko844f9902009-09-23 03:25:52 +02005280 ) {
Denys Vlasenkoe56f22a2009-07-24 00:16:59 +02005281 TRACE(("Job hack\n"));
Denys Vlasenko844f9902009-09-23 03:25:52 +02005282 /* "jobs": we do not want to clear job list for it,
5283 * instead we remove only _its_ own_ job from job list.
5284 * This makes "jobs .... | cat" more useful.
5285 */
Denys Vlasenkoe56f22a2009-07-24 00:16:59 +02005286 freejob(curjob);
5287 return;
5288 }
5289#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005290 for (jp = curjob; jp; jp = jp->prev_job)
5291 freejob(jp);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005292}
5293
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00005294/* Called after fork(), in parent */
Denis Vlasenko85c24712008-03-17 09:04:04 +00005295#if !JOBS
5296#define forkparent(jp, n, mode, pid) forkparent(jp, mode, pid)
5297#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005298static void
5299forkparent(struct job *jp, union node *n, int mode, pid_t pid)
5300{
5301 TRACE(("In parent shell: child = %d\n", pid));
Denys Vlasenko47eb9792020-02-18 15:37:43 +01005302 if (!jp) /* jp is NULL when called by openhere() for heredoc support */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005303 return;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005304#if JOBS
5305 if (mode != FORK_NOJOB && jp->jobctl) {
5306 int pgrp;
5307
5308 if (jp->nprocs == 0)
5309 pgrp = pid;
5310 else
Denys Vlasenko285ad152009-12-04 23:02:27 +01005311 pgrp = jp->ps[0].ps_pid;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005312 /* This can fail because we are doing it in the child also */
5313 setpgid(pid, pgrp);
5314 }
5315#endif
5316 if (mode == FORK_BG) {
5317 backgndpid = pid; /* set $! */
5318 set_curjob(jp, CUR_RUNNING);
5319 }
5320 if (jp) {
5321 struct procstat *ps = &jp->ps[jp->nprocs++];
Denys Vlasenko285ad152009-12-04 23:02:27 +01005322 ps->ps_pid = pid;
5323 ps->ps_status = -1;
5324 ps->ps_cmd = nullstr;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005325#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00005326 if (doing_jobctl && n)
Denys Vlasenko285ad152009-12-04 23:02:27 +01005327 ps->ps_cmd = commandtext(n);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005328#endif
5329 }
5330}
5331
Denys Vlasenko70392332016-10-27 02:31:55 +02005332/* jp and n are NULL when called by openhere() for heredoc support */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005333static int
5334forkshell(struct job *jp, union node *n, int mode)
5335{
5336 int pid;
5337
5338 TRACE(("forkshell(%%%d, %p, %d) called\n", jobno(jp), n, mode));
5339 pid = fork();
5340 if (pid < 0) {
5341 TRACE(("Fork failed, errno=%d", errno));
5342 if (jp)
5343 freejob(jp);
Denys Vlasenko12ffefb2017-08-23 14:52:56 +02005344 ash_msg_and_raise_perror("can't fork");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005345 }
Denys Vlasenko76ace252009-10-12 15:25:01 +02005346 if (pid == 0) {
5347 CLEAR_RANDOM_T(&random_gen); /* or else $RANDOM repeats in child */
Denys Vlasenkoe56f22a2009-07-24 00:16:59 +02005348 forkchild(jp, n, mode);
Denys Vlasenko76ace252009-10-12 15:25:01 +02005349 } else {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005350 forkparent(jp, n, mode, pid);
Denys Vlasenko76ace252009-10-12 15:25:01 +02005351 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005352 return pid;
5353}
5354
5355/*
5356 * Wait for job to finish.
5357 *
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00005358 * Under job control we have the problem that while a child process
5359 * is running interrupts generated by the user are sent to the child
5360 * but not to the shell. This means that an infinite loop started by
5361 * an interactive user may be hard to kill. With job control turned off,
5362 * an interactive user may place an interactive program inside a loop.
5363 * If the interactive program catches interrupts, the user doesn't want
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005364 * these interrupts to also abort the loop. The approach we take here
5365 * is to have the shell ignore interrupt signals while waiting for a
5366 * foreground process to terminate, and then send itself an interrupt
5367 * signal if the child process was terminated by an interrupt signal.
5368 * Unfortunately, some programs want to do a bit of cleanup and then
5369 * exit on interrupt; unless these processes terminate themselves by
5370 * sending a signal to themselves (instead of calling exit) they will
5371 * confuse this approach.
5372 *
5373 * Called with interrupts off.
5374 */
5375static int
5376waitforjob(struct job *jp)
5377{
5378 int st;
5379
Denys Vlasenkod81af722020-02-18 14:28:30 +01005380 TRACE(("waitforjob(%%%d) called\n", jp ? jobno(jp) : 0));
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00005381
Denys Vlasenko47eb9792020-02-18 15:37:43 +01005382 /* In non-interactive shells, we _can_ get
5383 * a keyboard signal here and be EINTRed, but we just loop
5384 * inside dowait(), waiting for command to complete.
5385 *
5386 * man bash:
5387 * "If bash is waiting for a command to complete and receives
5388 * a signal for which a trap has been set, the trap
5389 * will not be executed until the command completes."
5390 *
5391 * Reality is that even if trap is not set, bash
5392 * will not act on the signal until command completes.
5393 * Try this. sleep5intoff.c:
5394 * #include <signal.h>
5395 * #include <unistd.h>
5396 * int main() {
5397 * sigset_t set;
5398 * sigemptyset(&set);
5399 * sigaddset(&set, SIGINT);
5400 * sigaddset(&set, SIGQUIT);
5401 * sigprocmask(SIG_BLOCK, &set, NULL);
5402 * sleep(5);
5403 * return 0;
5404 * }
5405 * $ bash -c './sleep5intoff; echo hi'
5406 * ^C^C^C^C <--- pressing ^C once a second
5407 * $ _
5408 * $ bash -c './sleep5intoff; echo hi'
5409 * ^\^\^\^\hi <--- pressing ^\ (SIGQUIT)
5410 * $ _
5411 */
5412 dowait(jp ? DOWAIT_BLOCK : DOWAIT_NONBLOCK, jp);
5413 if (!jp)
Denys Vlasenko97edfc42020-02-18 14:37:56 +01005414 return exitstatus;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00005415
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005416 st = getstatus(jp);
5417#if JOBS
5418 if (jp->jobctl) {
5419 xtcsetpgrp(ttyfd, rootpid);
Denys Vlasenko098b7132017-01-11 19:59:03 +01005420 restore_tty_if_stopped_or_signaled(jp);
5421
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005422 /*
5423 * This is truly gross.
5424 * If we're doing job control, then we did a TIOCSPGRP which
5425 * caused us (the shell) to no longer be in the controlling
5426 * session -- so we wouldn't have seen any ^C/SIGINT. So, we
5427 * intuit from the subprocess exit status whether a SIGINT
5428 * occurred, and if so interrupt ourselves. Yuck. - mycroft
5429 */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00005430 if (jp->sigint) /* TODO: do the same with all signals */
5431 raise(SIGINT); /* ... by raise(jp->sig) instead? */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005432 }
5433 if (jp->state == JOBDONE)
5434#endif
5435 freejob(jp);
5436 return st;
5437}
5438
5439/*
5440 * return 1 if there are stopped jobs, otherwise 0
5441 */
5442static int
5443stoppedjobs(void)
5444{
5445 struct job *jp;
5446 int retval;
5447
5448 retval = 0;
Ron Yorston50239a62021-09-12 11:21:08 +01005449 if (!iflag || job_warning)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005450 goto out;
5451 jp = curjob;
5452 if (jp && jp->state == JOBSTOPPED) {
5453 out2str("You have stopped jobs.\n");
5454 job_warning = 2;
5455 retval++;
5456 }
5457 out:
5458 return retval;
5459}
5460
5461
Denys Vlasenko70392332016-10-27 02:31:55 +02005462/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005463 * Code for dealing with input/output redirection.
5464 */
5465
Denys Vlasenko8d0e0cd2011-01-25 23:21:46 +01005466#undef EMPTY
5467#undef CLOSED
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005468#define EMPTY -2 /* marks an unused slot in redirtab */
Denys Vlasenko035486c2017-07-31 04:09:19 +02005469#define CLOSED -1 /* marks a slot of previously-closed fd */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005470
5471/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005472 * Handle here documents. Normally we fork off a process to write the
5473 * data to a pipe. If the document is short, we can stuff the data in
5474 * the pipe without forking.
5475 */
5476/* openhere needs this forward reference */
Denys Vlasenkoc2058ec2020-02-22 20:25:03 +01005477static void expandhere(union node *arg);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005478static int
5479openhere(union node *redir)
5480{
Denys Vlasenkoc2058ec2020-02-22 20:25:03 +01005481 char *p;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005482 int pip[2];
5483 size_t len = 0;
5484
5485 if (pipe(pip) < 0)
Denys Vlasenko12ffefb2017-08-23 14:52:56 +02005486 ash_msg_and_raise_perror("can't create pipe");
Denys Vlasenkoc2058ec2020-02-22 20:25:03 +01005487
5488 p = redir->nhere.doc->narg.text;
5489 if (redir->type == NXHERE) {
5490 expandhere(redir->nhere.doc);
5491 p = stackblock();
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005492 }
Denys Vlasenkoc2058ec2020-02-22 20:25:03 +01005493
5494 len = strlen(p);
5495 if (len <= PIPE_BUF) {
5496 xwrite(pip[1], p, len);
5497 goto out;
5498 }
5499
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005500 if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) {
Denis Vlasenko0b769642008-07-24 07:54:57 +00005501 /* child */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005502 close(pip[0]);
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00005503 ignoresig(SIGINT); //signal(SIGINT, SIG_IGN);
5504 ignoresig(SIGQUIT); //signal(SIGQUIT, SIG_IGN);
5505 ignoresig(SIGHUP); //signal(SIGHUP, SIG_IGN);
5506 ignoresig(SIGTSTP); //signal(SIGTSTP, SIG_IGN);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005507 signal(SIGPIPE, SIG_DFL);
Denys Vlasenkoc2058ec2020-02-22 20:25:03 +01005508 xwrite(pip[1], p, len);
Denys Vlasenkodb5546c2022-01-05 22:16:06 +01005509 _exit_SUCCESS();
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005510 }
5511 out:
5512 close(pip[1]);
5513 return pip[0];
5514}
5515
5516static int
5517openredirect(union node *redir)
5518{
Denys Vlasenkof1a5cb02017-07-25 17:47:48 +02005519 struct stat sb;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005520 char *fname;
5521 int f;
5522
5523 switch (redir->nfile.type) {
Denys Vlasenko557482c2016-09-25 21:24:04 +02005524/* Can't happen, our single caller does this itself */
5525// case NTOFD:
5526// case NFROMFD:
5527// return -1;
5528 case NHERE:
5529 case NXHERE:
5530 return openhere(redir);
5531 }
5532
5533 /* For N[X]HERE, reading redir->nfile.expfname would touch beyond
5534 * allocated space. Do it only when we know it is safe.
5535 */
5536 fname = redir->nfile.expfname;
5537
5538 switch (redir->nfile.type) {
5539 default:
5540#if DEBUG
5541 abort();
5542#endif
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005543 case NFROM:
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005544 f = open(fname, O_RDONLY);
5545 if (f < 0)
5546 goto eopen;
5547 break;
5548 case NFROMTO:
Andreas Bühmannda75f442010-06-24 04:32:37 +02005549 f = open(fname, O_RDWR|O_CREAT, 0666);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005550 if (f < 0)
5551 goto ecreate;
5552 break;
5553 case NTO:
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01005554#if BASH_REDIR_OUTPUT
Denis Vlasenko559691a2008-10-05 18:39:31 +00005555 case NTO2:
5556#endif
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005557 /* Take care of noclobber mode. */
5558 if (Cflag) {
Denys Vlasenkof1a5cb02017-07-25 17:47:48 +02005559 if (stat(fname, &sb) < 0) {
5560 f = open(fname, O_WRONLY|O_CREAT|O_EXCL, 0666);
5561 if (f < 0)
5562 goto ecreate;
5563 } else if (!S_ISREG(sb.st_mode)) {
5564 f = open(fname, O_WRONLY, 0666);
5565 if (f < 0)
5566 goto ecreate;
Denys Vlasenko355ec352018-04-02 13:34:57 +02005567 if (!fstat(f, &sb) && S_ISREG(sb.st_mode)) {
Denys Vlasenkof1a5cb02017-07-25 17:47:48 +02005568 close(f);
5569 errno = EEXIST;
5570 goto ecreate;
5571 }
5572 } else {
5573 errno = EEXIST;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005574 goto ecreate;
Denys Vlasenkof1a5cb02017-07-25 17:47:48 +02005575 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005576 break;
5577 }
5578 /* FALLTHROUGH */
5579 case NCLOBBER:
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005580 f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666);
5581 if (f < 0)
5582 goto ecreate;
5583 break;
5584 case NAPPEND:
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005585 f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666);
5586 if (f < 0)
5587 goto ecreate;
5588 break;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005589 }
5590
5591 return f;
5592 ecreate:
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +00005593 ash_msg_and_raise_error("can't create %s: %s", fname, errmsg(errno, "nonexistent directory"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005594 eopen:
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +00005595 ash_msg_and_raise_error("can't open %s: %s", fname, errmsg(errno, "no such file"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005596}
5597
5598/*
Denys Vlasenko64774602016-10-26 15:24:30 +02005599 * Copy a file descriptor to be >= 10. Throws exception on error.
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005600 */
5601static int
Denys Vlasenko64774602016-10-26 15:24:30 +02005602savefd(int from)
5603{
5604 int newfd;
5605 int err;
5606
Denys Vlasenko60fb98e2018-03-30 22:15:14 +02005607 newfd = fcntl(from, F_DUPFD_CLOEXEC, 10);
Denys Vlasenko64774602016-10-26 15:24:30 +02005608 err = newfd < 0 ? errno : 0;
5609 if (err != EBADF) {
5610 if (err)
Ron Yorstonbe366e52017-07-27 13:53:39 +01005611 ash_msg_and_raise_perror("%d", from);
Denys Vlasenko64774602016-10-26 15:24:30 +02005612 close(from);
Denys Vlasenko60fb98e2018-03-30 22:15:14 +02005613 if (F_DUPFD_CLOEXEC == F_DUPFD)
5614 close_on_exec_on(newfd);
Denys Vlasenko64774602016-10-26 15:24:30 +02005615 }
5616
5617 return newfd;
5618}
5619static int
5620dup2_or_raise(int from, int to)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005621{
5622 int newfd;
5623
Denys Vlasenko64774602016-10-26 15:24:30 +02005624 newfd = (from != to) ? dup2(from, to) : to;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005625 if (newfd < 0) {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005626 /* Happens when source fd is not open: try "echo >&99" */
Ron Yorstonbe366e52017-07-27 13:53:39 +01005627 ash_msg_and_raise_perror("%d", from);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005628 }
5629 return newfd;
5630}
Denys Vlasenko035486c2017-07-31 04:09:19 +02005631static int
Denys Vlasenko9acd63c2018-03-28 18:35:07 +02005632dup_CLOEXEC(int fd, int avoid_fd)
Denys Vlasenko035486c2017-07-31 04:09:19 +02005633{
5634 int newfd;
5635 repeat:
Denys Vlasenko9acd63c2018-03-28 18:35:07 +02005636 newfd = fcntl(fd, F_DUPFD_CLOEXEC, avoid_fd + 1);
5637 if (newfd >= 0) {
5638 if (F_DUPFD_CLOEXEC == F_DUPFD) /* if old libc (w/o F_DUPFD_CLOEXEC) */
Denys Vlasenko60fb98e2018-03-30 22:15:14 +02005639 close_on_exec_on(newfd);
Denys Vlasenko9acd63c2018-03-28 18:35:07 +02005640 } else { /* newfd < 0 */
Denys Vlasenko035486c2017-07-31 04:09:19 +02005641 if (errno == EBUSY)
5642 goto repeat;
5643 if (errno == EINTR)
5644 goto repeat;
5645 }
5646 return newfd;
5647}
5648static int
5649xdup_CLOEXEC_and_close(int fd, int avoid_fd)
5650{
5651 int newfd;
5652 repeat:
Denys Vlasenko60fb98e2018-03-30 22:15:14 +02005653 newfd = fcntl(fd, F_DUPFD_CLOEXEC, avoid_fd + 1);
Denys Vlasenko035486c2017-07-31 04:09:19 +02005654 if (newfd < 0) {
5655 if (errno == EBUSY)
5656 goto repeat;
5657 if (errno == EINTR)
5658 goto repeat;
5659 /* fd was not open? */
5660 if (errno == EBADF)
5661 return fd;
5662 ash_msg_and_raise_perror("%d", newfd);
5663 }
Denys Vlasenko60fb98e2018-03-30 22:15:14 +02005664 if (F_DUPFD_CLOEXEC == F_DUPFD)
5665 close_on_exec_on(newfd);
Denys Vlasenko035486c2017-07-31 04:09:19 +02005666 close(fd);
5667 return newfd;
5668}
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005669
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005670/* Struct def and variable are moved down to the first usage site */
Denys Vlasenko035486c2017-07-31 04:09:19 +02005671struct squirrel {
5672 int orig_fd;
5673 int moved_to;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005674};
Denis Vlasenko0b769642008-07-24 07:54:57 +00005675struct redirtab {
5676 struct redirtab *next;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005677 int pair_count;
Denys Vlasenko035486c2017-07-31 04:09:19 +02005678 struct squirrel two_fd[];
Denis Vlasenko0b769642008-07-24 07:54:57 +00005679};
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005680#define redirlist (G_var.redirlist)
Denis Vlasenko0b769642008-07-24 07:54:57 +00005681
Denys Vlasenko035486c2017-07-31 04:09:19 +02005682static void
5683add_squirrel_closed(struct redirtab *sq, int fd)
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005684{
5685 int i;
5686
Denys Vlasenko035486c2017-07-31 04:09:19 +02005687 if (!sq)
5688 return;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005689
Denys Vlasenko035486c2017-07-31 04:09:19 +02005690 for (i = 0; sq->two_fd[i].orig_fd != EMPTY; i++) {
5691 /* If we collide with an already moved fd... */
5692 if (fd == sq->two_fd[i].orig_fd) {
5693 /* Examples:
5694 * "echo 3>FILE 3>&- 3>FILE"
5695 * "echo 3>&- 3>FILE"
5696 * No need for last redirect to insert
5697 * another "need to close 3" indicator.
5698 */
5699 TRACE(("redirect_fd %d: already moved or closed\n", fd));
5700 return;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005701 }
5702 }
Denys Vlasenko035486c2017-07-31 04:09:19 +02005703 TRACE(("redirect_fd %d: previous fd was closed\n", fd));
5704 sq->two_fd[i].orig_fd = fd;
5705 sq->two_fd[i].moved_to = CLOSED;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005706}
5707
Denys Vlasenko37dc08b2016-10-02 04:38:07 +02005708static int
Denys Vlasenko035486c2017-07-31 04:09:19 +02005709save_fd_on_redirect(int fd, int avoid_fd, struct redirtab *sq)
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005710{
Denys Vlasenko035486c2017-07-31 04:09:19 +02005711 int i, new_fd;
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005712
Denys Vlasenko035486c2017-07-31 04:09:19 +02005713 if (avoid_fd < 9) /* the important case here is that it can be -1 */
5714 avoid_fd = 9;
5715
5716#if JOBS
5717 if (fd == ttyfd) {
5718 /* Testcase: "ls -l /proc/$$/fd 10>&-" should work */
5719 ttyfd = xdup_CLOEXEC_and_close(ttyfd, avoid_fd);
5720 TRACE(("redirect_fd %d: matches ttyfd, moving it to %d\n", fd, ttyfd));
5721 return 1; /* "we closed fd" */
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005722 }
Denys Vlasenko035486c2017-07-31 04:09:19 +02005723#endif
5724 /* Are we called from redirect(0)? E.g. redirect
5725 * in a forked child. No need to save fds,
5726 * we aren't going to use them anymore, ok to trash.
5727 */
5728 if (!sq)
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005729 return 0;
Denys Vlasenko035486c2017-07-31 04:09:19 +02005730
5731 /* If this one of script's fds? */
5732 if (fd != 0) {
5733 struct parsefile *pf = g_parsefile;
5734 while (pf) {
5735 /* We skip fd == 0 case because of the following:
5736 * $ ash # running ash interactively
5737 * $ . ./script.sh
5738 * and in script.sh: "exec 9>&0".
5739 * Even though top-level pf_fd _is_ 0,
5740 * it's still ok to use it: "read" builtin uses it,
5741 * why should we cripple "exec" builtin?
5742 */
5743 if (fd == pf->pf_fd) {
5744 pf->pf_fd = xdup_CLOEXEC_and_close(fd, avoid_fd);
5745 return 1; /* "we closed fd" */
5746 }
5747 pf = pf->prev;
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005748 }
5749 }
Denys Vlasenko035486c2017-07-31 04:09:19 +02005750
5751 /* Check whether it collides with any open fds (e.g. stdio), save fds as needed */
5752
5753 /* First: do we collide with some already moved fds? */
5754 for (i = 0; sq->two_fd[i].orig_fd != EMPTY; i++) {
5755 /* If we collide with an already moved fd... */
5756 if (fd == sq->two_fd[i].moved_to) {
Denys Vlasenko9acd63c2018-03-28 18:35:07 +02005757 new_fd = dup_CLOEXEC(fd, avoid_fd);
Denys Vlasenko035486c2017-07-31 04:09:19 +02005758 sq->two_fd[i].moved_to = new_fd;
5759 TRACE(("redirect_fd %d: already busy, moving to %d\n", fd, new_fd));
5760 if (new_fd < 0) /* what? */
5761 xfunc_die();
5762 return 0; /* "we did not close fd" */
5763 }
5764 if (fd == sq->two_fd[i].orig_fd) {
5765 /* Example: echo Hello >/dev/null 1>&2 */
5766 TRACE(("redirect_fd %d: already moved\n", fd));
5767 return 0; /* "we did not close fd" */
5768 }
5769 }
5770
5771 /* 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 +02005772 new_fd = dup_CLOEXEC(fd, avoid_fd);
Denys Vlasenko035486c2017-07-31 04:09:19 +02005773 TRACE(("redirect_fd %d: previous fd is moved to %d (-1 if it was closed)\n", fd, new_fd));
5774 if (new_fd < 0) {
5775 if (errno != EBADF)
5776 xfunc_die();
5777 /* new_fd = CLOSED; - already is -1 */
5778 }
5779 sq->two_fd[i].moved_to = new_fd;
5780 sq->two_fd[i].orig_fd = fd;
5781
5782 /* if we move stderr, let "set -x" code know */
5783 if (fd == preverrout_fd)
5784 preverrout_fd = new_fd;
5785
5786 return 0; /* "we did not close fd" */
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005787}
5788
Denys Vlasenko32fdf2f2017-07-31 04:32:06 +02005789static int
5790internally_opened_fd(int fd, struct redirtab *sq)
5791{
5792 int i;
5793#if JOBS
5794 if (fd == ttyfd)
5795 return 1;
5796#endif
5797 /* If this one of script's fds? */
5798 if (fd != 0) {
5799 struct parsefile *pf = g_parsefile;
5800 while (pf) {
5801 if (fd == pf->pf_fd)
5802 return 1;
5803 pf = pf->prev;
5804 }
5805 }
5806
5807 if (sq) for (i = 0; i < sq->pair_count && sq->two_fd[i].orig_fd != EMPTY; i++) {
5808 if (fd == sq->two_fd[i].moved_to)
5809 return 1;
5810 }
5811 return 0;
5812}
5813
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005814/*
5815 * Process a list of redirection commands. If the REDIR_PUSH flag is set,
5816 * old file descriptors are stashed away so that the redirection can be
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005817 * undone by calling popredir.
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005818 */
5819/* flags passed to redirect */
5820#define REDIR_PUSH 01 /* save previous values of file descriptors */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005821static void
5822redirect(union node *redir, int flags)
5823{
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005824 struct redirtab *sv;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005825
Denys Vlasenko1c79aeb2017-07-29 22:51:52 +02005826 if (!redir)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005827 return;
Denys Vlasenko035486c2017-07-31 04:09:19 +02005828
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005829 sv = NULL;
5830 INT_OFF;
Denys Vlasenko1c79aeb2017-07-29 22:51:52 +02005831 if (flags & REDIR_PUSH)
5832 sv = redirlist;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005833 do {
Denys Vlasenko035486c2017-07-31 04:09:19 +02005834 int fd;
5835 int newfd;
5836 int close_fd;
5837 int closed;
5838
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00005839 fd = redir->nfile.fd;
Denis Vlasenko0b769642008-07-24 07:54:57 +00005840 if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) {
Denys Vlasenko035486c2017-07-31 04:09:19 +02005841 //bb_error_msg("doing %d > %d", fd, newfd);
5842 newfd = redir->ndup.dupfd;
5843 close_fd = -1;
Denis Vlasenko0b769642008-07-24 07:54:57 +00005844 } else {
5845 newfd = openredirect(redir); /* always >= 0 */
5846 if (fd == newfd) {
Denys Vlasenko035486c2017-07-31 04:09:19 +02005847 /* open() gave us precisely the fd we wanted.
5848 * This means that this fd was not busy
5849 * (not opened to anywhere).
5850 * Remember to close it on restore:
5851 */
5852 add_squirrel_closed(sv, fd);
Denis Vlasenko0b769642008-07-24 07:54:57 +00005853 continue;
5854 }
Denys Vlasenko035486c2017-07-31 04:09:19 +02005855 close_fd = newfd;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005856 }
Denys Vlasenko035486c2017-07-31 04:09:19 +02005857
5858 if (fd == newfd)
5859 continue;
5860
5861 /* if "N>FILE": move newfd to fd */
5862 /* if "N>&M": dup newfd to fd */
5863 /* if "N>&-": close fd (newfd is -1) */
5864
5865 IF_BASH_REDIR_OUTPUT(redirect_more:)
5866
5867 closed = save_fd_on_redirect(fd, /*avoid:*/ newfd, sv);
5868 if (newfd == -1) {
5869 /* "N>&-" means "close me" */
5870 if (!closed) {
5871 /* ^^^ optimization: saving may already
5872 * have closed it. If not... */
5873 close(fd);
Denis Vlasenko22f74142008-07-24 22:34:43 +00005874 }
Denys Vlasenko035486c2017-07-31 04:09:19 +02005875 } else {
Denys Vlasenko32fdf2f2017-07-31 04:32:06 +02005876 /* if newfd is a script fd or saved fd, simulate EBADF */
5877 if (internally_opened_fd(newfd, sv)) {
5878 errno = EBADF;
5879 ash_msg_and_raise_perror("%d", newfd);
5880 }
Denys Vlasenko64774602016-10-26 15:24:30 +02005881 dup2_or_raise(newfd, fd);
Denys Vlasenko035486c2017-07-31 04:09:19 +02005882 if (close_fd >= 0) /* "N>FILE" or ">&FILE" or heredoc? */
5883 close(close_fd);
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01005884#if BASH_REDIR_OUTPUT
Denys Vlasenko035486c2017-07-31 04:09:19 +02005885 if (redir->nfile.type == NTO2 && fd == 1) {
5886 /* ">&FILE". we already redirected to 1, now copy 1 to 2 */
5887 fd = 2;
5888 newfd = 1;
5889 close_fd = -1;
5890 goto redirect_more;
5891 }
Denis Vlasenko559691a2008-10-05 18:39:31 +00005892#endif
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005893 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00005894 } while ((redir = redir->nfile.next) != NULL);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005895 INT_ON;
Denys Vlasenkod07a15b2017-07-30 16:51:05 +02005896
5897//dash:#define REDIR_SAVEFD2 03 /* set preverrout */
5898#define REDIR_SAVEFD2 0
5899 // dash has a bug: since REDIR_SAVEFD2=3 and REDIR_PUSH=1, this test
5900 // triggers for pure REDIR_PUSH too. Thus, this is done almost always,
5901 // not only for calls with flags containing REDIR_SAVEFD2.
Denys Vlasenko035486c2017-07-31 04:09:19 +02005902 // We do this unconditionally (see save_fd_on_redirect()).
Denys Vlasenkod07a15b2017-07-30 16:51:05 +02005903 //if ((flags & REDIR_SAVEFD2) && copied_fd2 >= 0)
5904 // preverrout_fd = copied_fd2;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005905}
5906
Denys Vlasenko170f93e2017-07-29 18:54:53 +02005907static int
5908redirectsafe(union node *redir, int flags)
5909{
5910 int err;
5911 volatile int saveint;
5912 struct jmploc *volatile savehandler = exception_handler;
5913 struct jmploc jmploc;
5914
5915 SAVE_INT(saveint);
5916 /* "echo 9>/dev/null; echo >&9; echo result: $?" - result should be 1, not 2! */
Denys Vlasenko035486c2017-07-31 04:09:19 +02005917 err = setjmp(jmploc.loc); /* was = setjmp(jmploc.loc) * 2; */
Denys Vlasenko170f93e2017-07-29 18:54:53 +02005918 if (!err) {
5919 exception_handler = &jmploc;
5920 redirect(redir, flags);
5921 }
5922 exception_handler = savehandler;
5923 if (err && exception_type != EXERROR)
5924 longjmp(exception_handler->loc, 1);
5925 RESTORE_INT(saveint);
5926 return err;
5927}
5928
Ron Yorstona1b0d382020-07-23 08:32:27 +01005929#if BASH_PROCESS_SUBST
5930static void
5931pushfd(int fd)
5932{
5933 struct redirtab *sv;
5934
5935 sv = ckzalloc(sizeof(*sv) + sizeof(sv->two_fd[0]));
5936 sv->pair_count = 1;
5937 sv->two_fd[0].orig_fd = fd;
5938 sv->two_fd[0].moved_to = CLOSED;
5939 sv->next = redirlist;
5940 redirlist = sv;
5941}
5942#endif
5943
Denys Vlasenko1c79aeb2017-07-29 22:51:52 +02005944static struct redirtab*
5945pushredir(union node *redir)
5946{
5947 struct redirtab *sv;
5948 int i;
5949
5950 if (!redir)
5951 return redirlist;
5952
5953 i = 0;
5954 do {
5955 i++;
5956#if BASH_REDIR_OUTPUT
5957 if (redir->nfile.type == NTO2)
5958 i++;
5959#endif
5960 redir = redir->nfile.next;
5961 } while (redir);
5962
5963 sv = ckzalloc(sizeof(*sv) + i * sizeof(sv->two_fd[0]));
5964 sv->pair_count = i;
5965 while (--i >= 0)
Denys Vlasenko035486c2017-07-31 04:09:19 +02005966 sv->two_fd[i].orig_fd = sv->two_fd[i].moved_to = EMPTY;
Denys Vlasenko1c79aeb2017-07-29 22:51:52 +02005967 sv->next = redirlist;
5968 redirlist = sv;
5969 return sv->next;
5970}
5971
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005972/*
5973 * Undo the effects of the last redirection.
5974 */
5975static void
Denys Vlasenko035486c2017-07-31 04:09:19 +02005976popredir(int drop)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005977{
5978 struct redirtab *rp;
5979 int i;
5980
Denys Vlasenkoeaf94362016-10-25 21:46:03 +02005981 if (redirlist == NULL)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005982 return;
5983 INT_OFF;
5984 rp = redirlist;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005985 for (i = 0; i < rp->pair_count; i++) {
Denys Vlasenko035486c2017-07-31 04:09:19 +02005986 int fd = rp->two_fd[i].orig_fd;
5987 int copy = rp->two_fd[i].moved_to;
Denis Vlasenko22f74142008-07-24 22:34:43 +00005988 if (copy == CLOSED) {
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005989 if (!drop)
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005990 close(fd);
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005991 continue;
5992 }
Denis Vlasenko22f74142008-07-24 22:34:43 +00005993 if (copy != EMPTY) {
Denys Vlasenko035486c2017-07-31 04:09:19 +02005994 if (!drop) {
Denis Vlasenko5a867312008-07-24 19:46:38 +00005995 /*close(fd);*/
Denys Vlasenko64774602016-10-26 15:24:30 +02005996 dup2_or_raise(copy, fd);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005997 }
Denys Vlasenko035486c2017-07-31 04:09:19 +02005998 close(copy);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005999 }
6000 }
6001 redirlist = rp->next;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00006002 free(rp);
6003 INT_ON;
6004}
6005
Denys Vlasenko1c79aeb2017-07-29 22:51:52 +02006006static void
6007unwindredir(struct redirtab *stop)
6008{
6009 while (redirlist != stop)
Denys Vlasenko035486c2017-07-31 04:09:19 +02006010 popredir(/*drop:*/ 0);
Denys Vlasenko1c79aeb2017-07-29 22:51:52 +02006011}
6012
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00006013
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006014/* ============ Routines to expand arguments to commands
6015 *
6016 * We have to deal with backquotes, shell variables, and file metacharacters.
6017 */
6018
Denys Vlasenko0b883582016-12-23 16:49:07 +01006019#if ENABLE_FEATURE_SH_MATH
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00006020static arith_t
6021ash_arith(const char *s)
6022{
Denys Vlasenko06d44d72010-09-13 12:49:03 +02006023 arith_state_t math_state;
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00006024 arith_t result;
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00006025
Denys Vlasenko06d44d72010-09-13 12:49:03 +02006026 math_state.lookupvar = lookupvar;
Denys Vlasenko0a0acb52015-04-18 19:36:38 +02006027 math_state.setvar = setvar0;
Denys Vlasenko06d44d72010-09-13 12:49:03 +02006028 //math_state.endofname = endofname;
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00006029
6030 INT_OFF;
Denys Vlasenko06d44d72010-09-13 12:49:03 +02006031 result = arith(&math_state, s);
Denys Vlasenko063847d2010-09-15 13:33:02 +02006032 if (math_state.errmsg)
6033 ash_msg_and_raise_error(math_state.errmsg);
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00006034 INT_ON;
6035
6036 return result;
6037}
Denis Vlasenko448d30e2008-06-27 00:24:11 +00006038#endif
Denys Vlasenkobaa41c72018-01-10 13:22:25 +01006039#if BASH_SUBSTR
6040# if ENABLE_FEATURE_SH_MATH
6041static int substr_atoi(const char *s)
6042{
6043 arith_t t = ash_arith(s);
6044 if (sizeof(t) > sizeof(int)) {
6045 /* clamp very large or very large negative nums for ${v:N:M}:
6046 * else "${v:0:0x100000001}" would work as "${v:0:1}"
6047 */
6048 if (t > INT_MAX)
6049 t = INT_MAX;
6050 if (t < INT_MIN)
6051 t = INT_MIN;
6052 }
6053 return t;
6054}
6055# else
6056# define substr_atoi(s) number(s)
6057# endif
6058#endif
Denis Vlasenko448d30e2008-06-27 00:24:11 +00006059
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006060/*
6061 * expandarg flags
6062 */
6063#define EXP_FULL 0x1 /* perform word splitting & file globbing */
6064#define EXP_TILDE 0x2 /* do normal tilde expansion */
6065#define EXP_VARTILDE 0x4 /* expand tildes in an assignment */
6066#define EXP_REDIR 0x8 /* file glob for a redirection (1 match only) */
Denys Vlasenkodb74c6c2016-10-24 21:12:33 +02006067/* ^^^^^^^^^^^^^^ this is meant to support constructs such as "cmd >file*.txt"
6068 * POSIX says for this case:
6069 * Pathname expansion shall not be performed on the word by a
6070 * non-interactive shell; an interactive shell may perform it, but shall
6071 * do so only when the expansion would result in one word.
6072 * Currently, our code complies to the above rule by never globbing
6073 * redirection filenames.
6074 * Bash performs globbing, unless it is non-interactive and in POSIX mode.
6075 * (this means that on a typical Linux distro, bash almost always
6076 * performs globbing, and thus diverges from what we do).
6077 */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006078#define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */
Denys Vlasenko216913c2018-04-02 12:35:04 +02006079#define EXP_VARTILDE2 0x20 /* expand tildes after colons only */
6080#define EXP_WORD 0x40 /* expand word in parameter expansion */
Denys Vlasenko440da972018-08-05 14:29:58 +02006081#define EXP_QUOTED 0x100 /* expand word in double quotes */
Denys Vlasenkoecc85832020-02-20 10:06:20 +01006082#define EXP_KEEPNUL 0x200 /* do not skip NUL characters */
Denys Vlasenko82331882020-02-24 10:02:50 +01006083#define EXP_DISCARD 0x400 /* discard result of expansion */
Denys Vlasenkoecc85832020-02-20 10:06:20 +01006084
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006085/*
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006086 * rmescape() flags
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006087 */
6088#define RMESCAPE_ALLOC 0x1 /* Allocate a new string */
6089#define RMESCAPE_GLOB 0x2 /* Add backslashes for glob */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006090#define RMESCAPE_GROW 0x8 /* Grow strings instead of stalloc */
6091#define RMESCAPE_HEAP 0x10 /* Malloc strings instead of stalloc */
6092
Ron Yorstond68d1fb2015-05-18 09:49:28 +02006093/* Add CTLESC when necessary. */
Denys Vlasenko216913c2018-04-02 12:35:04 +02006094#define QUOTES_ESC (EXP_FULL | EXP_CASE)
Ron Yorstond68d1fb2015-05-18 09:49:28 +02006095
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006096/*
6097 * Structure specifying which parts of the string should be searched
6098 * for IFS characters.
6099 */
6100struct ifsregion {
6101 struct ifsregion *next; /* next region in list */
6102 int begoff; /* offset of start of region */
6103 int endoff; /* offset of end of region */
6104 int nulonly; /* search for nul bytes only */
6105};
6106
6107struct arglist {
6108 struct strlist *list;
6109 struct strlist **lastp;
6110};
6111
6112/* output of current string */
6113static char *expdest;
6114/* list of back quote expressions */
6115static struct nodelist *argbackq;
6116/* first struct in list of ifs regions */
6117static struct ifsregion ifsfirst;
6118/* last struct in list */
6119static struct ifsregion *ifslastp;
6120/* holds expanded arg list */
6121static struct arglist exparg;
6122
6123/*
Denys Vlasenko455e4222016-10-27 14:45:13 +02006124 * Break the argument string into pieces based upon IFS and add the
6125 * strings to the argument list. The regions of the string to be
6126 * searched for IFS characters have been stored by recordregion.
6127 */
6128static void
6129ifsbreakup(char *string, struct arglist *arglist)
6130{
6131 struct ifsregion *ifsp;
6132 struct strlist *sp;
6133 char *start;
6134 char *p;
6135 char *q;
6136 const char *ifs, *realifs;
6137 int ifsspc;
6138 int nulonly;
6139
6140 start = string;
6141 if (ifslastp != NULL) {
6142 ifsspc = 0;
6143 nulonly = 0;
6144 realifs = ifsset() ? ifsval() : defifs;
6145 ifsp = &ifsfirst;
6146 do {
Denys Vlasenko9a95df92018-04-02 14:27:50 +02006147 int afternul;
6148
Denys Vlasenko455e4222016-10-27 14:45:13 +02006149 p = string + ifsp->begoff;
Denys Vlasenko9a95df92018-04-02 14:27:50 +02006150 afternul = nulonly;
Denys Vlasenko455e4222016-10-27 14:45:13 +02006151 nulonly = ifsp->nulonly;
6152 ifs = nulonly ? nullstr : realifs;
6153 ifsspc = 0;
6154 while (p < string + ifsp->endoff) {
6155 q = p;
6156 if ((unsigned char)*p == CTLESC)
6157 p++;
6158 if (!strchr(ifs, *p)) {
6159 p++;
6160 continue;
6161 }
Denys Vlasenko9a95df92018-04-02 14:27:50 +02006162 if (!(afternul || nulonly))
Denys Vlasenko455e4222016-10-27 14:45:13 +02006163 ifsspc = (strchr(defifs, *p) != NULL);
6164 /* Ignore IFS whitespace at start */
6165 if (q == start && ifsspc) {
6166 p++;
6167 start = p;
6168 continue;
6169 }
6170 *q = '\0';
6171 sp = stzalloc(sizeof(*sp));
6172 sp->text = start;
6173 *arglist->lastp = sp;
6174 arglist->lastp = &sp->next;
6175 p++;
6176 if (!nulonly) {
6177 for (;;) {
6178 if (p >= string + ifsp->endoff) {
6179 break;
6180 }
6181 q = p;
6182 if ((unsigned char)*p == CTLESC)
6183 p++;
6184 if (strchr(ifs, *p) == NULL) {
6185 p = q;
6186 break;
6187 }
6188 if (strchr(defifs, *p) == NULL) {
6189 if (ifsspc) {
6190 p++;
6191 ifsspc = 0;
6192 } else {
6193 p = q;
6194 break;
6195 }
6196 } else
6197 p++;
6198 }
6199 }
6200 start = p;
6201 } /* while */
6202 ifsp = ifsp->next;
6203 } while (ifsp != NULL);
6204 if (nulonly)
6205 goto add;
6206 }
6207
6208 if (!*start)
6209 return;
6210
6211 add:
6212 sp = stzalloc(sizeof(*sp));
6213 sp->text = start;
6214 *arglist->lastp = sp;
6215 arglist->lastp = &sp->next;
6216}
6217
6218static void
6219ifsfree(void)
6220{
Denys Vlasenko5ac04f22016-10-27 14:46:50 +02006221 struct ifsregion *p = ifsfirst.next;
6222
6223 if (!p)
6224 goto out;
Denys Vlasenko455e4222016-10-27 14:45:13 +02006225
6226 INT_OFF;
Denys Vlasenko455e4222016-10-27 14:45:13 +02006227 do {
6228 struct ifsregion *ifsp;
6229 ifsp = p->next;
6230 free(p);
6231 p = ifsp;
6232 } while (p);
Denys Vlasenko455e4222016-10-27 14:45:13 +02006233 ifsfirst.next = NULL;
6234 INT_ON;
Denys Vlasenko5ac04f22016-10-27 14:46:50 +02006235 out:
6236 ifslastp = NULL;
Denys Vlasenko455e4222016-10-27 14:45:13 +02006237}
6238
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006239static size_t
6240esclen(const char *start, const char *p)
6241{
6242 size_t esc = 0;
6243
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006244 while (p > start && (unsigned char)*--p == CTLESC) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006245 esc++;
6246 }
6247 return esc;
6248}
6249
6250/*
6251 * Remove any CTLESC characters from a string.
6252 */
Denys Vlasenko740058b2018-01-09 17:01:00 +01006253#if !BASH_PATTERN_SUBST
6254#define rmescapes(str, flag, slash_position) \
6255 rmescapes(str, flag)
6256#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006257static char *
Denys Vlasenko740058b2018-01-09 17:01:00 +01006258rmescapes(char *str, int flag, int *slash_position)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006259{
Ron Yorston417622c2015-05-18 09:59:14 +02006260 static const char qchars[] ALIGN1 = {
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01006261 IF_BASH_PATTERN_SUBST('/',) CTLESC, CTLQUOTEMARK, '\0' };
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00006262
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006263 char *p, *q, *r;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006264 unsigned protect_against_glob;
6265 unsigned globbing;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006266
Denys Vlasenko740058b2018-01-09 17:01:00 +01006267 p = strpbrk(str, qchars IF_BASH_PATTERN_SUBST(+ !slash_position));
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006268 if (!p)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006269 return str;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006270
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006271 q = p;
6272 r = str;
6273 if (flag & RMESCAPE_ALLOC) {
6274 size_t len = p - str;
6275 size_t fulllen = len + strlen(p) + 1;
6276
6277 if (flag & RMESCAPE_GROW) {
Colin Watson3963d942010-04-26 14:21:27 +02006278 int strloc = str - (char *)stackblock();
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006279 r = makestrspace(fulllen, expdest);
Colin Watson3963d942010-04-26 14:21:27 +02006280 /* p and str may be invalidated by makestrspace */
6281 str = (char *)stackblock() + strloc;
6282 p = str + len;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006283 } else if (flag & RMESCAPE_HEAP) {
6284 r = ckmalloc(fulllen);
6285 } else {
6286 r = stalloc(fulllen);
6287 }
6288 q = r;
6289 if (len > 0) {
Denys Vlasenko5ace96a2017-07-23 21:46:02 +02006290 q = (char *)mempcpy(q, str, len);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006291 }
6292 }
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006293
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006294 globbing = flag & RMESCAPE_GLOB;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006295 protect_against_glob = globbing;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006296 while (*p) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006297 if ((unsigned char)*p == CTLQUOTEMARK) {
Denys Vlasenko216913c2018-04-02 12:35:04 +02006298// Note: protect_against_glob only affect whether
Denys Vlasenkofda9faf2017-07-05 19:10:21 +02006299// CTLESC,<ch> gets converted to <ch> or to \<ch>
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006300 p++;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006301 protect_against_glob = globbing;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006302 continue;
6303 }
Denys Vlasenko216913c2018-04-02 12:35:04 +02006304 if (*p == '\\') {
6305 /* naked back slash */
6306 protect_against_glob = 0;
6307 goto copy;
6308 }
Ron Yorston549deab2015-05-18 09:57:51 +02006309 if ((unsigned char)*p == CTLESC) {
6310 p++;
Denys Vlasenko13f20912016-09-25 20:54:25 +02006311#if DEBUG
6312 if (*p == '\0')
6313 ash_msg_and_raise_error("CTLESC at EOL (shouldn't happen)");
6314#endif
Ron Yorston549deab2015-05-18 09:57:51 +02006315 if (protect_against_glob) {
Denys Vlasenkofda9faf2017-07-05 19:10:21 +02006316 /*
6317 * We used to trust glob() and fnmatch() to eat
6318 * superfluous escapes (\z where z has no
6319 * special meaning anyway). But this causes
6320 * bugs such as string of one greek letter rho
Denys Vlasenkoed79a632017-07-05 19:20:43 +02006321 * (unicode-encoded as two bytes "cf,81")
Denys Vlasenkofda9faf2017-07-05 19:10:21 +02006322 * getting encoded as "cf,CTLESC,81"
6323 * and here, converted to "cf,\,81" -
6324 * which does not go well with some flavors
Denys Vlasenko92b8d9c2017-07-05 19:13:44 +02006325 * of fnmatch() in unicode locales
6326 * (for example, glibc <= 2.22).
Denys Vlasenkofda9faf2017-07-05 19:10:21 +02006327 *
6328 * Lets add "\" only on the chars which need it.
Denys Vlasenko4142f012017-07-05 22:19:28 +02006329 * Testcases for less obvious chars are shown.
Denys Vlasenkofda9faf2017-07-05 19:10:21 +02006330 */
6331 if (*p == '*'
6332 || *p == '?'
6333 || *p == '['
Denys Vlasenko8de5b9f2018-02-13 14:43:29 +01006334 || *p == '\\' /* case '\' in \\ ) echo ok;; *) echo WRONG;; esac */
6335 || *p == ']' /* case ']' in [a\]] ) echo ok;; *) echo WRONG;; esac */
6336 || *p == '-' /* case '-' in [a\-c]) echo ok;; *) echo WRONG;; esac */
6337 || *p == '!' /* case '!' in [\!] ) echo ok;; *) echo WRONG;; esac */
Denys Vlasenko4142f012017-07-05 22:19:28 +02006338 /* Some libc support [^negate], that's why "^" also needs love */
Denys Vlasenko8de5b9f2018-02-13 14:43:29 +01006339 || *p == '^' /* case '^' in [\^] ) echo ok;; *) echo WRONG;; esac */
Denys Vlasenkofda9faf2017-07-05 19:10:21 +02006340 ) {
6341 *q++ = '\\';
6342 }
Ron Yorston549deab2015-05-18 09:57:51 +02006343 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006344 }
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01006345#if BASH_PATTERN_SUBST
Denys Vlasenko740058b2018-01-09 17:01:00 +01006346 else if (slash_position && p == str + *slash_position) {
6347 /* stop handling globbing */
6348 globbing = 0;
6349 *slash_position = q - r;
6350 slash_position = NULL;
Ron Yorston417622c2015-05-18 09:59:14 +02006351 }
6352#endif
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006353 protect_against_glob = globbing;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006354 copy:
6355 *q++ = *p++;
6356 }
6357 *q = '\0';
6358 if (flag & RMESCAPE_GROW) {
6359 expdest = r;
6360 STADJUST(q - r + 1, expdest);
6361 }
6362 return r;
6363}
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006364#define pmatch(a, b) !fnmatch((a), (b), 0)
6365
6366/*
6367 * Prepare a pattern for a expmeta (internal glob(3)) call.
6368 *
6369 * Returns an stalloced string.
6370 */
6371static char *
Ron Yorston549deab2015-05-18 09:57:51 +02006372preglob(const char *pattern, int flag)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006373{
Denys Vlasenko740058b2018-01-09 17:01:00 +01006374 return rmescapes((char *)pattern, flag | RMESCAPE_GLOB, NULL);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006375}
6376
6377/*
6378 * Put a string on the stack.
6379 */
Denys Vlasenko45dd87a2020-02-21 16:30:44 +01006380static size_t
Denys Vlasenkoecc85832020-02-20 10:06:20 +01006381memtodest(const char *p, size_t len, int flags)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006382{
Denys Vlasenkoecc85832020-02-20 10:06:20 +01006383 int syntax = flags & EXP_QUOTED ? DQSYNTAX : BASESYNTAX;
Ron Yorstond68d1fb2015-05-18 09:49:28 +02006384 char *q;
Denys Vlasenko45dd87a2020-02-21 16:30:44 +01006385 char *s;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006386
Ron Yorstond68d1fb2015-05-18 09:49:28 +02006387 if (!len)
Denys Vlasenko45dd87a2020-02-21 16:30:44 +01006388 return 0;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006389
Denys Vlasenkoecc85832020-02-20 10:06:20 +01006390 q = makestrspace(len * 2, expdest);
Denys Vlasenko45dd87a2020-02-21 16:30:44 +01006391 s = q;
Ron Yorstond68d1fb2015-05-18 09:49:28 +02006392
6393 do {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006394 unsigned char c = *p++;
Ron Yorstond68d1fb2015-05-18 09:49:28 +02006395 if (c) {
Denys Vlasenkoecc85832020-02-20 10:06:20 +01006396 if (flags & QUOTES_ESC) {
Denys Vlasenko2fe66b12016-12-12 17:39:12 +01006397 int n = SIT(c, syntax);
6398 if (n == CCTL
Denys Vlasenkoecc85832020-02-20 10:06:20 +01006399 || ((flags & EXP_QUOTED) && n == CBACK)
Denys Vlasenko2fe66b12016-12-12 17:39:12 +01006400 ) {
6401 USTPUTC(CTLESC, q);
6402 }
Denys Vlasenkob3f29b42016-09-21 16:25:58 +02006403 }
Denys Vlasenkoecc85832020-02-20 10:06:20 +01006404 } else if (!(flags & EXP_KEEPNUL))
Ron Yorstond68d1fb2015-05-18 09:49:28 +02006405 continue;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006406 USTPUTC(c, q);
Ron Yorstond68d1fb2015-05-18 09:49:28 +02006407 } while (--len);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006408
6409 expdest = q;
Denys Vlasenko45dd87a2020-02-21 16:30:44 +01006410 return q - s;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006411}
6412
Ron Yorstond68d1fb2015-05-18 09:49:28 +02006413static size_t
Denys Vlasenkoecc85832020-02-20 10:06:20 +01006414strtodest(const char *p, int flags)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006415{
Ron Yorstond68d1fb2015-05-18 09:49:28 +02006416 size_t len = strlen(p);
Denys Vlasenkoecc85832020-02-20 10:06:20 +01006417 memtodest(p, len, flags);
Ron Yorstond68d1fb2015-05-18 09:49:28 +02006418 return len;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006419}
6420
6421/*
Denys Vlasenko45dd87a2020-02-21 16:30:44 +01006422 * Our own itoa().
6423 * cvtnum() is used even if math support is off (to prepare $? values and such).
6424 */
6425static int
6426cvtnum(arith_t num, int flags)
6427{
6428 /* 32-bit and wider ints require buffer size of bytes*3 (or less) */
6429 /* If narrower: worst case, 1-byte ints: need 5 bytes: "-127<NUL>" */
6430 int len = (sizeof(arith_t) >= 4) ? sizeof(arith_t) * 3 : sizeof(arith_t) * 3 + 2;
6431 char buf[len];
6432
6433 len = fmtstr(buf, len, ARITH_FMT, num);
6434 return memtodest(buf, len, flags);
6435}
6436
6437/*
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006438 * Record the fact that we have to scan this region of the
6439 * string for IFS characters.
6440 */
6441static void
6442recordregion(int start, int end, int nulonly)
6443{
6444 struct ifsregion *ifsp;
6445
6446 if (ifslastp == NULL) {
6447 ifsp = &ifsfirst;
6448 } else {
6449 INT_OFF;
Denis Vlasenko597906c2008-02-20 16:38:54 +00006450 ifsp = ckzalloc(sizeof(*ifsp));
6451 /*ifsp->next = NULL; - ckzalloc did it */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006452 ifslastp->next = ifsp;
6453 INT_ON;
6454 }
6455 ifslastp = ifsp;
6456 ifslastp->begoff = start;
6457 ifslastp->endoff = end;
6458 ifslastp->nulonly = nulonly;
6459}
6460
6461static void
6462removerecordregions(int endoff)
6463{
6464 if (ifslastp == NULL)
6465 return;
6466
6467 if (ifsfirst.endoff > endoff) {
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006468 while (ifsfirst.next) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006469 struct ifsregion *ifsp;
6470 INT_OFF;
6471 ifsp = ifsfirst.next->next;
6472 free(ifsfirst.next);
6473 ifsfirst.next = ifsp;
6474 INT_ON;
6475 }
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006476 if (ifsfirst.begoff > endoff) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006477 ifslastp = NULL;
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006478 } else {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006479 ifslastp = &ifsfirst;
6480 ifsfirst.endoff = endoff;
6481 }
6482 return;
6483 }
6484
6485 ifslastp = &ifsfirst;
6486 while (ifslastp->next && ifslastp->next->begoff < endoff)
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006487 ifslastp = ifslastp->next;
6488 while (ifslastp->next) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006489 struct ifsregion *ifsp;
6490 INT_OFF;
6491 ifsp = ifslastp->next->next;
6492 free(ifslastp->next);
6493 ifslastp->next = ifsp;
6494 INT_ON;
6495 }
6496 if (ifslastp->endoff > endoff)
6497 ifslastp->endoff = endoff;
6498}
6499
6500static char *
Denys Vlasenko82331882020-02-24 10:02:50 +01006501exptilde(char *startp, int flag)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006502{
Denys Vlasenkocd716832009-11-28 22:14:02 +01006503 unsigned char c;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006504 char *name;
6505 struct passwd *pw;
6506 const char *home;
Denys Vlasenko82331882020-02-24 10:02:50 +01006507 char *p;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006508
Denys Vlasenko82331882020-02-24 10:02:50 +01006509 p = startp;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006510 name = p + 1;
6511
6512 while ((c = *++p) != '\0') {
6513 switch (c) {
6514 case CTLESC:
6515 return startp;
6516 case CTLQUOTEMARK:
6517 return startp;
6518 case ':':
Denys Vlasenkoecc85832020-02-20 10:06:20 +01006519 if (flag & EXP_VARTILDE)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006520 goto done;
6521 break;
6522 case '/':
6523 case CTLENDVAR:
6524 goto done;
6525 }
6526 }
6527 done:
Denys Vlasenko82331882020-02-24 10:02:50 +01006528 if (flag & EXP_DISCARD)
6529 goto out;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006530 *p = '\0';
6531 if (*name == '\0') {
Denys Vlasenkoea8b2522010-06-02 12:57:26 +02006532 home = lookupvar("HOME");
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006533 } else {
6534 pw = getpwnam(name);
Denys Vlasenko5fe20cf2022-03-01 10:08:59 +01006535 home = pw ? pw->pw_dir : NULL;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006536 }
Denys Vlasenko82331882020-02-24 10:02:50 +01006537 *p = c;
Denys Vlasenkoe880b1f2020-02-16 18:31:05 +01006538 if (!home)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006539 goto lose;
Denys Vlasenkoecc85832020-02-20 10:06:20 +01006540 strtodest(home, flag | EXP_QUOTED);
Denys Vlasenko82331882020-02-24 10:02:50 +01006541 out:
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006542 return p;
6543 lose:
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006544 return startp;
6545}
6546
6547/*
6548 * Execute a command inside back quotes. If it's a builtin command, we
6549 * want to save its output in a block obtained from malloc. Otherwise
6550 * we fork off a subprocess and get the output of the command via a pipe.
6551 * Should be called with interrupts off.
6552 */
6553struct backcmd { /* result of evalbackcmd */
6554 int fd; /* file descriptor to read from */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006555 int nleft; /* number of chars in buffer */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00006556 char *buf; /* buffer */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006557 struct job *jp; /* job structure for command */
6558};
6559
6560/* These forward decls are needed to use "eval" code for backticks handling: */
Denys Vlasenko1e3e2cc2017-07-25 20:31:14 +02006561/* flags in argument to evaltree */
6562#define EV_EXIT 01 /* exit after evaluating tree */
6563#define EV_TESTED 02 /* exit status is checked; ignore -e flag */
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02006564static int evaltree(union node *, int);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006565
Denys Vlasenko619d9b52017-07-28 15:28:33 +02006566/* An evaltree() which is known to never return.
6567 * Used to use an alias:
6568 * static int evaltreenr(union node *, int) __attribute__((alias("evaltree"),__noreturn__));
6569 * but clang was reported to "transfer" noreturn-ness to evaltree() as well.
6570 */
6571static ALWAYS_INLINE NORETURN void
6572evaltreenr(union node *n, int flags)
6573{
6574 evaltree(n, flags);
6575 bb_unreachable(abort());
6576 /* NOTREACHED */
6577}
6578
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02006579static void FAST_FUNC
Ron Yorstona1b0d382020-07-23 08:32:27 +01006580evalbackcmd(union node *n, struct backcmd *result
6581 IF_BASH_PROCESS_SUBST(, int ctl))
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006582{
Denys Vlasenko579ad102016-10-25 21:10:20 +02006583 int pip[2];
6584 struct job *jp;
Ron Yorstona1b0d382020-07-23 08:32:27 +01006585#if BASH_PROCESS_SUBST
6586 /* determine end of pipe used by parent (ip) and child (ic) */
6587 const int ip = (ctl == CTLTOPROC);
6588 const int ic = !(ctl == CTLTOPROC);
6589#else
6590 const int ctl = CTLBACKQ;
6591 const int ip = 0;
6592 const int ic = 1;
6593#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006594
6595 result->fd = -1;
6596 result->buf = NULL;
6597 result->nleft = 0;
6598 result->jp = NULL;
Denys Vlasenko579ad102016-10-25 21:10:20 +02006599 if (n == NULL) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006600 goto out;
Denys Vlasenko579ad102016-10-25 21:10:20 +02006601 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006602
Denys Vlasenko579ad102016-10-25 21:10:20 +02006603 if (pipe(pip) < 0)
Denys Vlasenko12ffefb2017-08-23 14:52:56 +02006604 ash_msg_and_raise_perror("can't create pipe");
Ron Yorstona1b0d382020-07-23 08:32:27 +01006605 /* process substitution uses NULL job/node, like openhere() */
6606 jp = (ctl == CTLBACKQ) ? makejob(/*n,*/ 1) : NULL;
6607 if (forkshell(jp, (ctl == CTLBACKQ) ? n : NULL, FORK_NOJOB) == 0) {
Denys Vlasenko70392332016-10-27 02:31:55 +02006608 /* child */
Denys Vlasenko579ad102016-10-25 21:10:20 +02006609 FORCE_INT_ON;
Ron Yorstona1b0d382020-07-23 08:32:27 +01006610 close(pip[ip]);
6611 /* ic is index of child end of pipe *and* fd to connect it to */
6612 if (pip[ic] != ic) {
6613 /*close(ic);*/
6614 dup2_or_raise(pip[ic], ic);
6615 close(pip[ic]);
Denys Vlasenko579ad102016-10-25 21:10:20 +02006616 }
Denys Vlasenko960ca382016-10-25 18:12:15 +02006617/* TODO: eflag clearing makes the following not abort:
6618 * ash -c 'set -e; z=$(false;echo foo); echo $z'
6619 * which is what bash does (unless it is in POSIX mode).
6620 * dash deleted "eflag = 0" line in the commit
6621 * Date: Mon, 28 Jun 2010 17:11:58 +1000
6622 * [EVAL] Don't clear eflag in evalbackcmd
6623 * For now, preserve bash-like behavior, it seems to be somewhat more useful:
6624 */
Denys Vlasenko579ad102016-10-25 21:10:20 +02006625 eflag = 0;
Denys Vlasenko5ac04f22016-10-27 14:46:50 +02006626 ifsfree();
Denys Vlasenko619d9b52017-07-28 15:28:33 +02006627 evaltreenr(n, EV_EXIT);
Denys Vlasenko579ad102016-10-25 21:10:20 +02006628 /* NOTREACHED */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006629 }
Denys Vlasenko70392332016-10-27 02:31:55 +02006630 /* parent */
Ron Yorstona1b0d382020-07-23 08:32:27 +01006631#if BASH_PROCESS_SUBST
6632 if (ctl != CTLBACKQ) {
6633 int fd = fcntl(pip[ip], F_DUPFD, 64);
6634 if (fd > 0) {
6635 close(pip[ip]);
6636 pip[ip] = fd;
6637 }
6638 pushfd(pip[ip]);
6639 }
6640#endif
6641 close(pip[ic]);
6642 result->fd = pip[ip];
Denys Vlasenko579ad102016-10-25 21:10:20 +02006643 result->jp = jp;
6644
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006645 out:
6646 TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n",
6647 result->fd, result->buf, result->nleft, result->jp));
6648}
6649
6650/*
6651 * Expand stuff in backwards quotes.
6652 */
6653static void
Ron Yorstona1b0d382020-07-23 08:32:27 +01006654expbackq(union node *cmd, int flag IF_BASH_PROCESS_SUBST(, int ctl))
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006655{
Ron Yorstona1b0d382020-07-23 08:32:27 +01006656#if !BASH_PROCESS_SUBST
6657 const int ctl = CTLBACKQ;
6658#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006659 struct backcmd in;
6660 int i;
6661 char buf[128];
6662 char *p;
6663 char *dest;
6664 int startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006665 struct stackmark smark;
6666
Denys Vlasenko82331882020-02-24 10:02:50 +01006667 if (flag & EXP_DISCARD)
6668 goto out;
6669
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006670 INT_OFF;
Denys Vlasenko60ca8342016-09-30 11:21:21 +02006671 startloc = expdest - (char *)stackblock();
6672 pushstackmark(&smark, startloc);
Ron Yorstona1b0d382020-07-23 08:32:27 +01006673 evalbackcmd(cmd, &in IF_BASH_PROCESS_SUBST(, ctl));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006674 popstackmark(&smark);
6675
Ron Yorstona1b0d382020-07-23 08:32:27 +01006676 if (ctl != CTLBACKQ) {
6677 sprintf(buf, DEV_FD_PREFIX"%d", in.fd);
6678 strtodest(buf, BASESYNTAX);
6679 goto done;
6680 }
6681
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006682 p = in.buf;
6683 i = in.nleft;
6684 if (i == 0)
6685 goto read;
6686 for (;;) {
Denys Vlasenkoecc85832020-02-20 10:06:20 +01006687 memtodest(p, i, flag);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006688 read:
6689 if (in.fd < 0)
6690 break;
Ron Yorston61d6ae22015-04-19 10:50:25 +01006691 i = nonblock_immune_read(in.fd, buf, sizeof(buf));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006692 TRACE(("expbackq: read returns %d\n", i));
6693 if (i <= 0)
6694 break;
6695 p = buf;
6696 }
6697
Denis Vlasenko60818682007-09-28 22:07:23 +00006698 free(in.buf);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006699 if (in.fd >= 0) {
6700 close(in.fd);
6701 back_exitstatus = waitforjob(in.jp);
6702 }
Ron Yorstona1b0d382020-07-23 08:32:27 +01006703 done:
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006704 INT_ON;
6705
6706 /* Eat all trailing newlines */
6707 dest = expdest;
Denys Vlasenko9ee58922020-02-17 10:24:32 +01006708 for (; dest > ((char *)stackblock() + startloc) && dest[-1] == '\n';)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006709 STUNPUTC(dest);
6710 expdest = dest;
6711
Ron Yorston549deab2015-05-18 09:57:51 +02006712 if (!(flag & EXP_QUOTED))
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006713 recordregion(startloc, dest - (char *)stackblock(), 0);
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006714 TRACE(("evalbackq: size:%d:'%.*s'\n",
6715 (int)((dest - (char *)stackblock()) - startloc),
6716 (int)((dest - (char *)stackblock()) - startloc),
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006717 stackblock() + startloc));
Denys Vlasenko82331882020-02-24 10:02:50 +01006718
6719 out:
6720 argbackq = argbackq->next;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006721}
6722
Denys Vlasenko82331882020-02-24 10:02:50 +01006723/* expari needs it */
6724static char *argstr(char *p, int flag);
6725
Denys Vlasenko0b883582016-12-23 16:49:07 +01006726#if ENABLE_FEATURE_SH_MATH
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006727/*
6728 * Expand arithmetic expression. Backup to start of expression,
6729 * evaluate, place result in (backed up) result, adjust string position.
6730 */
Denys Vlasenko82331882020-02-24 10:02:50 +01006731static char *
6732expari(char *start, int flag)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006733{
Denys Vlasenko82331882020-02-24 10:02:50 +01006734 struct stackmark sm;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006735 int begoff;
Denys Vlasenko82331882020-02-24 10:02:50 +01006736 int endoff;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006737 int len;
Denys Vlasenko82331882020-02-24 10:02:50 +01006738 arith_t result;
6739 char *p;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006740
Denys Vlasenko82331882020-02-24 10:02:50 +01006741 p = stackblock();
6742 begoff = expdest - p;
6743 p = argstr(start, flag & EXP_DISCARD);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006744
Denys Vlasenko82331882020-02-24 10:02:50 +01006745 if (flag & EXP_DISCARD)
6746 goto out;
6747
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006748 start = stackblock();
Denys Vlasenko82331882020-02-24 10:02:50 +01006749 endoff = expdest - start;
6750 start += begoff;
6751 STADJUST(start - expdest, expdest);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006752
6753 removerecordregions(begoff);
6754
Ron Yorston549deab2015-05-18 09:57:51 +02006755 if (flag & QUOTES_ESC)
Denys Vlasenko82331882020-02-24 10:02:50 +01006756 rmescapes(start, 0, NULL);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006757
Denys Vlasenko82331882020-02-24 10:02:50 +01006758 pushstackmark(&sm, endoff);
6759 result = ash_arith(start);
6760 popstackmark(&sm);
6761
6762 len = cvtnum(result, flag);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006763
Ron Yorston549deab2015-05-18 09:57:51 +02006764 if (!(flag & EXP_QUOTED))
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006765 recordregion(begoff, begoff + len, 0);
Denys Vlasenko82331882020-02-24 10:02:50 +01006766
6767 out:
6768 return p;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006769}
6770#endif
6771
6772/* argstr needs it */
Denys Vlasenkob8c0bc12017-07-26 23:03:21 +02006773static char *evalvar(char *p, int flags);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006774
6775/*
6776 * Perform variable and command substitution. If EXP_FULL is set, output CTLESC
6777 * characters to allow for further processing. Otherwise treat
6778 * $@ like $* since no splitting will be performed.
6779 */
Denys Vlasenko82331882020-02-24 10:02:50 +01006780static char *
Denys Vlasenko7f198482020-02-24 09:57:08 +01006781argstr(char *p, int flag)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006782{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00006783 static const char spclchars[] ALIGN1 = {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006784 '=',
6785 ':',
6786 CTLQUOTEMARK,
6787 CTLENDVAR,
6788 CTLESC,
6789 CTLVAR,
6790 CTLBACKQ,
Ron Yorstona1b0d382020-07-23 08:32:27 +01006791#if BASH_PROCESS_SUBST
6792 CTLTOPROC,
6793 CTLFROMPROC,
6794#endif
Denys Vlasenko0b883582016-12-23 16:49:07 +01006795#if ENABLE_FEATURE_SH_MATH
Denys Vlasenko82331882020-02-24 10:02:50 +01006796 CTLARI,
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006797 CTLENDARI,
6798#endif
Denys Vlasenkocd716832009-11-28 22:14:02 +01006799 '\0'
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006800 };
6801 const char *reject = spclchars;
Denys Vlasenko7f198482020-02-24 09:57:08 +01006802 int breakall = (flag & (EXP_WORD | EXP_QUOTED)) == EXP_WORD;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006803 int inquotes;
6804 size_t length;
6805 int startloc;
6806
Denys Vlasenko82331882020-02-24 10:02:50 +01006807 reject += !!(flag & EXP_VARTILDE2);
6808 reject += flag & EXP_VARTILDE ? 0 : 2;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006809 inquotes = 0;
6810 length = 0;
Denys Vlasenko7f198482020-02-24 09:57:08 +01006811 if (flag & EXP_TILDE) {
Denys Vlasenko7f198482020-02-24 09:57:08 +01006812 flag &= ~EXP_TILDE;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006813 tilde:
Denys Vlasenko82331882020-02-24 10:02:50 +01006814 if (*p == '~')
6815 p = exptilde(p, flag);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006816 }
6817 start:
6818 startloc = expdest - (char *)stackblock();
6819 for (;;) {
Denys Vlasenko82331882020-02-24 10:02:50 +01006820 int end;
Denys Vlasenkocd716832009-11-28 22:14:02 +01006821 unsigned char c;
6822
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006823 length += strcspn(p + length, reject);
Denys Vlasenko82331882020-02-24 10:02:50 +01006824 end = 0;
Denys Vlasenkocd716832009-11-28 22:14:02 +01006825 c = p[length];
Denys Vlasenko82331882020-02-24 10:02:50 +01006826 if (!(c & 0x80)
6827 IF_FEATURE_SH_MATH(|| c == CTLENDARI)
6828 || c == CTLENDVAR
6829 ) {
6830 /*
6831 * c == '=' || c == ':' || c == '\0' ||
6832 * c == CTLENDARI || c == CTLENDVAR
6833 */
6834 length++;
6835 /* c == '\0' || c == CTLENDARI || c == CTLENDVAR */
6836 end = !!((c - 1) & 0x80);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006837 }
Denys Vlasenko82331882020-02-24 10:02:50 +01006838 if (length > 0 && !(flag & EXP_DISCARD)) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006839 int newloc;
Denys Vlasenko82331882020-02-24 10:02:50 +01006840 char *q;
6841
6842 q = stnputs(p, length, expdest);
6843 q[-1] &= end - 1;
6844 expdest = q - (flag & EXP_WORD ? end : 0);
6845 newloc = q - (char *)stackblock() - end;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006846 if (breakall && !inquotes && newloc > startloc) {
6847 recordregion(startloc, newloc, 0);
6848 }
6849 startloc = newloc;
6850 }
6851 p += length + 1;
6852 length = 0;
6853
Denys Vlasenko82331882020-02-24 10:02:50 +01006854 if (end)
6855 break;
6856
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006857 switch (c) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006858 case '=':
Denys Vlasenko7f198482020-02-24 09:57:08 +01006859 flag |= EXP_VARTILDE2;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006860 reject++;
6861 /* fall through */
6862 case ':':
6863 /*
6864 * sort of a hack - expand tildes in variable
6865 * assignments (after the first '=' and after ':'s).
6866 */
6867 if (*--p == '~') {
6868 goto tilde;
6869 }
6870 continue;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006871 case CTLQUOTEMARK:
6872 /* "$@" syntax adherence hack */
Denys Vlasenko9a95df92018-04-02 14:27:50 +02006873 if (!inquotes && !memcmp(p, dolatstr + 1, DOLATSTRLEN - 1)) {
Denys Vlasenko7f198482020-02-24 09:57:08 +01006874 p = evalvar(p + 1, flag | EXP_QUOTED) + 1;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006875 goto start;
6876 }
Denys Vlasenko9a95df92018-04-02 14:27:50 +02006877 inquotes ^= EXP_QUOTED;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006878 addquote:
Denys Vlasenko7f198482020-02-24 09:57:08 +01006879 if (flag & QUOTES_ESC) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006880 p--;
6881 length++;
6882 startloc++;
6883 }
6884 break;
6885 case CTLESC:
6886 startloc++;
6887 length++;
6888 goto addquote;
6889 case CTLVAR:
Denys Vlasenkof451b2c2012-06-09 02:06:57 +02006890 TRACE(("argstr: evalvar('%s')\n", p));
Denys Vlasenko7f198482020-02-24 09:57:08 +01006891 p = evalvar(p, flag | inquotes);
Denys Vlasenkof451b2c2012-06-09 02:06:57 +02006892 TRACE(("argstr: evalvar:'%s'\n", (char *)stackblock()));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006893 goto start;
Ron Yorstona1b0d382020-07-23 08:32:27 +01006894#if BASH_PROCESS_SUBST
6895 case CTLTOPROC:
6896 case CTLFROMPROC:
6897#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006898 case CTLBACKQ:
Ron Yorstona1b0d382020-07-23 08:32:27 +01006899 expbackq(argbackq->n, flag | inquotes IF_BASH_PROCESS_SUBST(, c));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006900 goto start;
Denys Vlasenko0b883582016-12-23 16:49:07 +01006901#if ENABLE_FEATURE_SH_MATH
Denys Vlasenko82331882020-02-24 10:02:50 +01006902 case CTLARI:
6903 p = expari(p, flag | inquotes);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006904 goto start;
6905#endif
6906 }
6907 }
Denys Vlasenko82331882020-02-24 10:02:50 +01006908 return p - 1;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006909}
6910
6911static char *
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006912scanleft(char *startp, char *rmesc, char *rmescend UNUSED_PARAM,
6913 char *pattern, int quotes, int zero)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006914{
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006915 char *loc, *loc2;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006916 char c;
6917
6918 loc = startp;
6919 loc2 = rmesc;
6920 do {
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006921 int match;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006922 const char *s = loc2;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006923
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006924 c = *loc2;
6925 if (zero) {
6926 *loc2 = '\0';
6927 s = rmesc;
6928 }
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006929 match = pmatch(pattern, s);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006930
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006931 *loc2 = c;
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006932 if (match)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006933 return loc;
Denys Vlasenkocd716832009-11-28 22:14:02 +01006934 if (quotes && (unsigned char)*loc == CTLESC)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006935 loc++;
6936 loc++;
6937 loc2++;
6938 } while (c);
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006939 return NULL;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006940}
6941
6942static char *
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006943scanright(char *startp, char *rmesc, char *rmescend,
6944 char *pattern, int quotes, int match_at_start)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006945{
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006946#if !ENABLE_ASH_OPTIMIZE_FOR_SIZE
6947 int try2optimize = match_at_start;
6948#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006949 int esc = 0;
6950 char *loc;
6951 char *loc2;
6952
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006953 /* If we called by "${v/pattern/repl}" or "${v//pattern/repl}":
6954 * startp="escaped_value_of_v" rmesc="raw_value_of_v"
6955 * rmescend=""(ptr to NUL in rmesc) pattern="pattern" quotes=match_at_start=1
6956 * Logic:
6957 * loc starts at NUL at the end of startp, loc2 starts at the end of rmesc,
6958 * and on each iteration they go back two/one char until they reach the beginning.
Denys Vlasenko1310d7b2021-07-22 18:15:59 +02006959 * We try to match "raw_value_of_v", "raw_value_of_", "raw_value_of" etc.
6960 * If one of these matches, return pointer past last matched char in startp.
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006961 */
6962 /* TODO: document in what other circumstances we are called. */
6963
6964 for (loc = pattern - 1, loc2 = rmescend; loc >= startp; loc2--) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006965 int match;
6966 char c = *loc2;
6967 const char *s = loc2;
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006968 if (match_at_start) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006969 *loc2 = '\0';
6970 s = rmesc;
6971 }
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006972 match = pmatch(pattern, s);
6973 //bb_error_msg("pmatch(pattern:'%s',s:'%s'):%d", pattern, s, match);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006974 *loc2 = c;
6975 if (match)
6976 return loc;
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006977#if !ENABLE_ASH_OPTIMIZE_FOR_SIZE
6978 if (try2optimize) {
6979 /* Maybe we can optimize this:
6980 * if pattern ends with unescaped *, we can avoid checking
Denys Vlasenko10ad6222017-04-17 16:13:32 +02006981 * shorter strings: if "foo*" doesn't match "raw_value_of_v",
6982 * it won't match truncated "raw_value_of_" strings too.
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006983 */
6984 unsigned plen = strlen(pattern);
6985 /* Does it end with "*"? */
6986 if (plen != 0 && pattern[--plen] == '*') {
6987 /* "xxxx*" is not escaped */
6988 /* "xxx\*" is escaped */
6989 /* "xx\\*" is not escaped */
6990 /* "x\\\*" is escaped */
6991 int slashes = 0;
6992 while (plen != 0 && pattern[--plen] == '\\')
6993 slashes++;
6994 if (!(slashes & 1))
6995 break; /* ends with unescaped "*" */
6996 }
6997 try2optimize = 0;
6998 }
6999#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007000 loc--;
7001 if (quotes) {
7002 if (--esc < 0) {
7003 esc = esclen(startp, loc);
7004 }
7005 if (esc % 2) {
7006 esc--;
7007 loc--;
7008 }
7009 }
7010 }
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02007011 return NULL;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007012}
7013
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007014static void varunset(const char *, const char *, const char *, int) NORETURN;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007015static void
7016varunset(const char *end, const char *var, const char *umsg, int varflags)
7017{
7018 const char *msg;
7019 const char *tail;
7020
7021 tail = nullstr;
7022 msg = "parameter not set";
7023 if (umsg) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01007024 if ((unsigned char)*end == CTLENDVAR) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007025 if (varflags & VSNUL)
7026 tail = " or null";
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00007027 } else {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007028 msg = umsg;
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00007029 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007030 }
Denys Vlasenko1c545522022-08-02 11:13:44 +02007031 ifsfree();
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02007032 ash_msg_and_raise_error("%.*s: %s%s", (int)(end - var - 1), var, msg, tail);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007033}
7034
Denys Vlasenko82331882020-02-24 10:02:50 +01007035static char *
7036subevalvar(char *start, char *str, int strloc,
Denys Vlasenkob8c0bc12017-07-26 23:03:21 +02007037 int startloc, int varflags, int flag)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007038{
Denys Vlasenko82331882020-02-24 10:02:50 +01007039 int subtype = varflags & VSTYPE;
Ron Yorston549deab2015-05-18 09:57:51 +02007040 int quotes = flag & QUOTES_ESC;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007041 char *startp;
7042 char *loc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007043 char *rmesc, *rmescend;
Denys Vlasenko82331882020-02-24 10:02:50 +01007044 long amount;
7045 int resetloc;
Denys Vlasenko740058b2018-01-09 17:01:00 +01007046 int argstr_flags;
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01007047 IF_BASH_PATTERN_SUBST(int workloc;)
Denys Vlasenko740058b2018-01-09 17:01:00 +01007048 IF_BASH_PATTERN_SUBST(int slash_pos;)
7049 IF_BASH_PATTERN_SUBST(char *repl;)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007050 int zero;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007051 char *(*scan)(char*, char*, char*, char*, int, int);
Denys Vlasenko82331882020-02-24 10:02:50 +01007052 char *p;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007053
Denys Vlasenko82331882020-02-24 10:02:50 +01007054 //bb_error_msg("subevalvar(start:'%s',str:'%s',strloc:%d,startloc:%d,varflags:%x,quotes:%d)",
7055 // start, str, strloc, startloc, varflags, quotes);
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02007056
Denys Vlasenko740058b2018-01-09 17:01:00 +01007057#if BASH_PATTERN_SUBST
Denys Vlasenkod1df1a72018-01-09 17:25:58 +01007058 /* For "${v/pattern/repl}", we must find the delimiter _before_
7059 * argstr() call expands possible variable references in pattern:
7060 * think about "v=a; a=a/; echo ${v/$a/r}" case.
7061 */
Denys Vlasenko740058b2018-01-09 17:01:00 +01007062 repl = NULL;
7063 if (subtype == VSREPLACE || subtype == VSREPLACEALL) {
7064 /* Find '/' and replace with NUL */
Denys Vlasenko82331882020-02-24 10:02:50 +01007065 repl = start;
Denys Vlasenkoc2aa2182018-08-04 22:25:28 +02007066 /* The pattern can't be empty.
7067 * IOW: if the first char after "${v//" is a slash,
7068 * it does not terminate the pattern - it's the first char of the pattern:
7069 * v=/dev/ram; echo ${v////-} prints -dev-ram (pattern is "/")
7070 * v=/dev/ram; echo ${v///r/-} prints /dev-am (pattern is "/r")
7071 */
7072 if (*repl == '/')
7073 repl++;
Denys Vlasenko740058b2018-01-09 17:01:00 +01007074 for (;;) {
Denys Vlasenko740058b2018-01-09 17:01:00 +01007075 if (*repl == '\0') {
7076 repl = NULL;
7077 break;
7078 }
7079 if (*repl == '/') {
7080 *repl = '\0';
7081 break;
7082 }
Sören Tempelfa52ac92022-02-28 08:36:50 +01007083 if ((unsigned char)*repl == CTLENDVAR) { /* ${v/pattern} (no trailing /, no repl) */
7084 repl = NULL;
7085 break;
7086 }
Denys Vlasenkoc2aa2182018-08-04 22:25:28 +02007087 /* Handle escaped slashes, e.g. "${v/\//_}" (they are CTLESC'ed by this point) */
Denys Vlasenkod1df1a72018-01-09 17:25:58 +01007088 if ((unsigned char)*repl == CTLESC && repl[1])
Denys Vlasenko740058b2018-01-09 17:01:00 +01007089 repl++;
Denys Vlasenko740058b2018-01-09 17:01:00 +01007090 repl++;
7091 }
7092 }
7093#endif
Denys Vlasenko82331882020-02-24 10:02:50 +01007094 argstr_flags = (flag & EXP_DISCARD) | EXP_TILDE;
7095 if (!str
Denys Vlasenko216913c2018-04-02 12:35:04 +02007096#if BASH_SUBSTR
7097 && subtype != VSSUBSTR
7098#endif
7099 ) {
7100 /* EXP_CASE keeps CTLESC's */
Denys Vlasenko82331882020-02-24 10:02:50 +01007101 argstr_flags |= EXP_CASE;
Denys Vlasenko216913c2018-04-02 12:35:04 +02007102 }
Denys Vlasenko82331882020-02-24 10:02:50 +01007103 p = argstr(start, argstr_flags);
7104
Denys Vlasenko216913c2018-04-02 12:35:04 +02007105 //bb_error_msg("str0:'%s'", (char *)stackblock() + strloc);
Denys Vlasenko740058b2018-01-09 17:01:00 +01007106#if BASH_PATTERN_SUBST
7107 slash_pos = -1;
7108 if (repl) {
7109 slash_pos = expdest - ((char *)stackblock() + strloc);
Denys Vlasenko883cdb72021-01-09 08:27:37 +01007110 if (!(flag & EXP_DISCARD))
7111 STPUTC('/', expdest);
Denys Vlasenko216913c2018-04-02 12:35:04 +02007112 //bb_error_msg("repl+1:'%s'", repl + 1);
Denys Vlasenko82331882020-02-24 10:02:50 +01007113 p = argstr(repl + 1, (flag & EXP_DISCARD) | EXP_TILDE); /* EXP_TILDE: echo "${v/x/~}" expands ~ ! */
Denys Vlasenko740058b2018-01-09 17:01:00 +01007114 *repl = '/';
7115 }
7116#endif
Denys Vlasenko82331882020-02-24 10:02:50 +01007117 if (flag & EXP_DISCARD)
7118 return p;
7119
Denis Vlasenko29eb3592008-05-18 14:06:08 +00007120 startp = (char *)stackblock() + startloc;
Denys Vlasenko740058b2018-01-09 17:01:00 +01007121 //bb_error_msg("str1:'%s'", (char *)stackblock() + strloc);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007122
7123 switch (subtype) {
7124 case VSASSIGN:
Denys Vlasenko7f198482020-02-24 09:57:08 +01007125 setvar0(str, startp);
Denys Vlasenko82331882020-02-24 10:02:50 +01007126
7127 loc = startp;
7128 goto out;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007129
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02007130 case VSQUESTION:
Denys Vlasenko82331882020-02-24 10:02:50 +01007131 varunset(start, str, startp, varflags);
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02007132 /* NOTREACHED */
7133
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01007134#if BASH_SUBSTR
Denys Vlasenko826360f2017-07-17 17:49:11 +02007135 case VSSUBSTR: {
7136 int pos, len, orig_len;
7137 char *colon;
Denys Vlasenko7f198482020-02-24 09:57:08 +01007138 char *vstr;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007139
Denys Vlasenko7f198482020-02-24 09:57:08 +01007140 loc = vstr = stackblock() + strloc;
Denys Vlasenko826360f2017-07-17 17:49:11 +02007141
Denys Vlasenko826360f2017-07-17 17:49:11 +02007142 /* Read POS in ${var:POS:LEN} */
7143 colon = strchr(loc, ':');
7144 if (colon) *colon = '\0';
Denys Vlasenkobaa41c72018-01-10 13:22:25 +01007145 pos = substr_atoi(loc);
Denys Vlasenko826360f2017-07-17 17:49:11 +02007146 if (colon) *colon = ':';
7147
7148 /* Read LEN in ${var:POS:LEN} */
Denys Vlasenko7f198482020-02-24 09:57:08 +01007149 len = vstr - startp - 1;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007150 /* *loc != '\0', guaranteed by parser */
7151 if (quotes) {
7152 char *ptr;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02007153 /* Adjust the length by the number of escapes */
Denys Vlasenko7f198482020-02-24 09:57:08 +01007154 for (ptr = startp; ptr < (vstr - 1); ptr++) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01007155 if ((unsigned char)*ptr == CTLESC) {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007156 len--;
7157 ptr++;
7158 }
7159 }
7160 }
7161 orig_len = len;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007162 if (*loc++ == ':') {
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02007163 /* ${var::LEN} */
Denys Vlasenkobaa41c72018-01-10 13:22:25 +01007164 len = substr_atoi(loc);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007165 } else {
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02007166 /* Skip POS in ${var:POS:LEN} */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007167 len = orig_len;
Denys Vlasenkobaa41c72018-01-10 13:22:25 +01007168 while (*loc && *loc != ':')
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007169 loc++;
Denys Vlasenkobaa41c72018-01-10 13:22:25 +01007170 if (*loc++ == ':')
7171 len = substr_atoi(loc);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007172 }
Denys Vlasenko08a5dab2014-11-17 20:27:18 +01007173 if (pos < 0) {
7174 /* ${VAR:$((-n)):l} starts n chars from the end */
7175 pos = orig_len + pos;
7176 }
7177 if ((unsigned)pos >= orig_len) {
7178 /* apart from obvious ${VAR:999999:l},
7179 * covers ${VAR:$((-9999999)):l} - result is ""
Denys Vlasenko826360f2017-07-17 17:49:11 +02007180 * (bash compat)
Denys Vlasenko08a5dab2014-11-17 20:27:18 +01007181 */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007182 pos = 0;
7183 len = 0;
7184 }
Denys Vlasenko826360f2017-07-17 17:49:11 +02007185 if (len < 0) {
7186 /* ${VAR:N:-M} sets LEN to strlen()-M */
7187 len = (orig_len - pos) + len;
7188 }
7189 if ((unsigned)len > (orig_len - pos))
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007190 len = orig_len - pos;
7191
Alin Mr21e8dbf2021-07-28 11:40:01 +03007192 if (!quotes) {
Denys Vlasenko7750b5a2022-03-01 09:56:54 +01007193 /* want: loc = mempcpy(startp, startp + pos, len)
7194 * but it does not allow overlapping arguments */
7195 loc = startp;
7196 while (--len >= 0) {
7197 *loc = loc[pos];
7198 loc++;
7199 }
Alin Mr21e8dbf2021-07-28 11:40:01 +03007200 } else {
7201 for (vstr = startp; pos != 0; pos--) {
7202 if ((unsigned char)*vstr == CTLESC)
7203 vstr++;
Denys Vlasenko7f198482020-02-24 09:57:08 +01007204 vstr++;
Alin Mr21e8dbf2021-07-28 11:40:01 +03007205 }
7206 for (loc = startp; len != 0; len--) {
7207 if ((unsigned char)*vstr == CTLESC)
7208 *loc++ = *vstr++;
Denys Vlasenko7f198482020-02-24 09:57:08 +01007209 *loc++ = *vstr++;
Alin Mr21e8dbf2021-07-28 11:40:01 +03007210 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007211 }
7212 *loc = '\0';
Denys Vlasenko82331882020-02-24 10:02:50 +01007213 goto out;
Denys Vlasenko826360f2017-07-17 17:49:11 +02007214 }
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01007215#endif /* BASH_SUBSTR */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007216 }
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02007217
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007218 resetloc = expdest - (char *)stackblock();
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007219
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01007220#if BASH_PATTERN_SUBST
Denys Vlasenko740058b2018-01-09 17:01:00 +01007221 repl = NULL;
7222
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007223 /* We'll comeback here if we grow the stack while handling
7224 * a VSREPLACE or VSREPLACEALL, since our pointers into the
7225 * stack will need rebasing, and we'll need to remove our work
7226 * areas each time
7227 */
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01007228 restart:
7229#endif
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007230
7231 amount = expdest - ((char *)stackblock() + resetloc);
7232 STADJUST(-amount, expdest);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00007233 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007234
7235 rmesc = startp;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00007236 rmescend = (char *)stackblock() + strloc;
Denys Vlasenko740058b2018-01-09 17:01:00 +01007237 //bb_error_msg("str7:'%s'", rmescend);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007238 if (quotes) {
Denys Vlasenko740058b2018-01-09 17:01:00 +01007239//TODO: how to handle slash_pos here if string changes (shortens?)
7240 rmesc = rmescapes(startp, RMESCAPE_ALLOC | RMESCAPE_GROW, NULL);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007241 if (rmesc != startp) {
7242 rmescend = expdest;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00007243 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007244 }
7245 }
7246 rmescend--;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00007247 str = (char *)stackblock() + strloc;
Ron Yorston417622c2015-05-18 09:59:14 +02007248 /*
7249 * Example: v='a\bc'; echo ${v/\\b/_\\_\z_}
7250 * The result is a_\_z_c (not a\_\_z_c)!
7251 *
7252 * The search pattern and replace string treat backslashes differently!
Denys Vlasenko740058b2018-01-09 17:01:00 +01007253 * "&slash_pos" causes rmescapes() to work differently on the pattern
Ron Yorston417622c2015-05-18 09:59:14 +02007254 * and string. It's only used on the first call.
7255 */
Denys Vlasenko740058b2018-01-09 17:01:00 +01007256 //bb_error_msg("str8:'%s' slash_pos:%d", str, slash_pos);
7257 rmescapes(str, RMESCAPE_GLOB,
7258 repl ? NULL : (slash_pos < 0 ? NULL : &slash_pos)
7259 );
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007260
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01007261#if BASH_PATTERN_SUBST
Denys Vlasenko0b4980c2012-09-25 12:49:29 +02007262 workloc = expdest - (char *)stackblock();
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007263 if (subtype == VSREPLACE || subtype == VSREPLACEALL) {
Denys Vlasenko5aaeb552021-10-09 03:32:20 +02007264 size_t no_meta_len, first_escaped;
Denys Vlasenko826360f2017-07-17 17:49:11 +02007265 int len;
Denys Vlasenkob76356b2010-03-13 16:19:04 +01007266 char *idx, *end;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007267
Denis Vlasenkod6855d12008-09-27 14:03:25 +00007268 if (!repl) {
Denys Vlasenko740058b2018-01-09 17:01:00 +01007269 //bb_error_msg("str9:'%s' slash_pos:%d", str, slash_pos);
Denys Vlasenkod1df1a72018-01-09 17:25:58 +01007270 repl = nullstr;
Denys Vlasenko740058b2018-01-09 17:01:00 +01007271 if (slash_pos >= 0) {
7272 repl = str + slash_pos;
Ron Yorston417622c2015-05-18 09:59:14 +02007273 *repl++ = '\0';
Denys Vlasenko740058b2018-01-09 17:01:00 +01007274 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007275 }
Ron Yorston417622c2015-05-18 09:59:14 +02007276 //bb_error_msg("str:'%s' repl:'%s'", str, repl);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007277
7278 /* If there's no pattern to match, return the expansion unmolested */
Denys Vlasenkob76356b2010-03-13 16:19:04 +01007279 if (str[0] == '\0')
Denys Vlasenko82331882020-02-24 10:02:50 +01007280 goto out1;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007281
Denys Vlasenko5aaeb552021-10-09 03:32:20 +02007282 first_escaped = (str[0] == '\\' && str[1]);
7283 /* "first_escaped" trick allows to treat e.g. "\*no_glob_chars"
7284 * as literal too (as it is semi-common, and easy to accomodate
7285 * by just using str + 1).
7286 */
7287 no_meta_len = strpbrk(str + first_escaped * 2, "*?[\\") ? 0 : strlen(str);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007288 len = 0;
7289 idx = startp;
7290 end = str - 1;
Denys Vlasenko2b7c1aa2021-01-09 08:46:54 +01007291 while (idx <= end) {
Denys Vlasenkob76356b2010-03-13 16:19:04 +01007292 try_to_match:
Denys Vlasenko1310d7b2021-07-22 18:15:59 +02007293 if (no_meta_len == 0) {
Denys Vlasenko5aaeb552021-10-09 03:32:20 +02007294 /* pattern has meta chars, have to glob */
Denys Vlasenko1310d7b2021-07-22 18:15:59 +02007295 loc = scanright(idx, rmesc, rmescend, str, quotes, /*match_at_start:*/ 1);
7296 } else {
7297 /* Testcase for very slow replace (performs about 22k replaces):
7298 * x=::::::::::::::::::::::
7299 * 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}
7300 * echo "${x//:/|}"
Denys Vlasenko5aaeb552021-10-09 03:32:20 +02007301 * To test "first_escaped" logic, replace : with *.
Denys Vlasenko1310d7b2021-07-22 18:15:59 +02007302 */
Denys Vlasenko5aaeb552021-10-09 03:32:20 +02007303 if (strncmp(rmesc, str + first_escaped, no_meta_len - first_escaped) != 0)
Denys Vlasenko1310d7b2021-07-22 18:15:59 +02007304 goto no_match;
Denys Vlasenko1310d7b2021-07-22 18:15:59 +02007305 loc = idx;
Denys Vlasenko53d45c92021-07-25 21:54:14 +02007306 if (!quotes) {
Denys Vlasenko5aaeb552021-10-09 03:32:20 +02007307 loc += no_meta_len - first_escaped;
Denys Vlasenko53d45c92021-07-25 21:54:14 +02007308 } else {
Denys Vlasenko5aaeb552021-10-09 03:32:20 +02007309 size_t n = no_meta_len - first_escaped;
Denys Vlasenko53d45c92021-07-25 21:54:14 +02007310 do {
7311 if ((unsigned char)*loc == CTLESC)
7312 loc++;
Denys Vlasenko1310d7b2021-07-22 18:15:59 +02007313 loc++;
Denys Vlasenko53d45c92021-07-25 21:54:14 +02007314 } while (--n != 0);
7315 }
Denys Vlasenko1310d7b2021-07-22 18:15:59 +02007316 }
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02007317 //bb_error_msg("scanright('%s'):'%s'", str, loc);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007318 if (!loc) {
Denys Vlasenko1310d7b2021-07-22 18:15:59 +02007319 char *restart_detect;
7320 no_match:
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007321 /* No match, advance */
Denys Vlasenko1310d7b2021-07-22 18:15:59 +02007322 restart_detect = stackblock();
Denys Vlasenkob76356b2010-03-13 16:19:04 +01007323 skip_matching:
Denys Vlasenko2b7c1aa2021-01-09 08:46:54 +01007324 if (idx >= end)
7325 break;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007326 STPUTC(*idx, expdest);
Denys Vlasenkodaa66ed2022-08-02 12:41:18 +02007327 if (stackblock() != restart_detect)
7328 goto restart;
Denys Vlasenkocd716832009-11-28 22:14:02 +01007329 if (quotes && (unsigned char)*idx == CTLESC) {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007330 idx++;
7331 len++;
7332 STPUTC(*idx, expdest);
Denys Vlasenkodaa66ed2022-08-02 12:41:18 +02007333 if (stackblock() != restart_detect)
7334 goto restart;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007335 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007336 idx++;
7337 len++;
7338 rmesc++;
Denys Vlasenkob76356b2010-03-13 16:19:04 +01007339 /* continue; - prone to quadratic behavior, smarter code: */
Denys Vlasenkob76356b2010-03-13 16:19:04 +01007340 if (str[0] == '*') {
7341 /* Pattern is "*foo". If "*foo" does not match "long_string",
7342 * it would never match "ong_string" etc, no point in trying.
7343 */
7344 goto skip_matching;
7345 }
7346 goto try_to_match;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007347 }
7348
7349 if (subtype == VSREPLACEALL) {
7350 while (idx < loc) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01007351 if (quotes && (unsigned char)*idx == CTLESC)
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007352 idx++;
7353 idx++;
7354 rmesc++;
7355 }
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00007356 } else {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007357 idx = loc;
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00007358 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007359
Sören Tempel7c2a3bd2022-08-02 18:23:32 +02007360 /* The STPUTC invocations above may resize and move the
7361 * stack via realloc(3). Since repl is a pointer into the
7362 * stack, we need to reconstruct it relative to stackblock().
7363 */
7364 if (slash_pos >= 0)
7365 repl = (char *)stackblock() + strloc + slash_pos + 1;
7366
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02007367 //bb_error_msg("repl:'%s'", repl);
Denys Vlasenkofd33e172010-06-26 22:55:44 +02007368 for (loc = (char*)repl; *loc; loc++) {
Denys Vlasenkob76356b2010-03-13 16:19:04 +01007369 char *restart_detect = stackblock();
Denys Vlasenkofd33e172010-06-26 22:55:44 +02007370 if (quotes && *loc == '\\') {
7371 STPUTC(CTLESC, expdest);
7372 len++;
7373 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007374 STPUTC(*loc, expdest);
7375 if (stackblock() != restart_detect)
7376 goto restart;
7377 len++;
7378 }
7379
7380 if (subtype == VSREPLACE) {
Denys Vlasenkof02c82f2010-08-06 19:14:47 +02007381 //bb_error_msg("tail:'%s', quotes:%x", idx, quotes);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007382 while (*idx) {
Denys Vlasenkob76356b2010-03-13 16:19:04 +01007383 char *restart_detect = stackblock();
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007384 STPUTC(*idx, expdest);
7385 if (stackblock() != restart_detect)
7386 goto restart;
7387 len++;
7388 idx++;
7389 }
7390 break;
7391 }
7392 }
7393
7394 /* We've put the replaced text into a buffer at workloc, now
7395 * move it to the right place and adjust the stack.
7396 */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007397 STPUTC('\0', expdest);
Denys Vlasenkofd33e172010-06-26 22:55:44 +02007398 startp = (char *)stackblock() + startloc;
7399 memmove(startp, (char *)stackblock() + workloc, len + 1);
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02007400 //bb_error_msg("startp:'%s'", startp);
Denys Vlasenko82331882020-02-24 10:02:50 +01007401 loc = startp + len;
7402 goto out;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007403 }
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01007404#endif /* BASH_PATTERN_SUBST */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007405
7406 subtype -= VSTRIMRIGHT;
7407#if DEBUG
7408 if (subtype < 0 || subtype > 7)
7409 abort();
7410#endif
Denys Vlasenkob76356b2010-03-13 16:19:04 +01007411 /* zero = (subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX) */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007412 zero = subtype >> 1;
7413 /* VSTRIMLEFT/VSTRIMRIGHTMAX -> scanleft */
7414 scan = (subtype & 1) ^ zero ? scanleft : scanright;
7415
7416 loc = scan(startp, rmesc, rmescend, str, quotes, zero);
7417 if (loc) {
7418 if (zero) {
7419 memmove(startp, loc, str - loc);
7420 loc = startp + (str - loc) - 1;
7421 }
7422 *loc = '\0';
Denys Vlasenko82331882020-02-24 10:02:50 +01007423 } else
7424 loc = str - 1;
7425
7426 out:
7427 amount = loc - expdest;
7428 STADJUST(amount, expdest);
Cristian Ionescu-Idbohrnddfdf682020-11-18 10:41:14 +01007429#if BASH_PATTERN_SUBST
Denys Vlasenko82331882020-02-24 10:02:50 +01007430 out1:
Cristian Ionescu-Idbohrnddfdf682020-11-18 10:41:14 +01007431#endif
Denys Vlasenko82331882020-02-24 10:02:50 +01007432 /* Remove any recorded regions beyond start of variable */
7433 removerecordregions(startloc);
7434
7435 return p;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007436}
7437
7438/*
7439 * Add the value of a specialized variable to the stack string.
Denys Vlasenko4d8873f2009-10-04 03:14:41 +02007440 * name parameter (examples):
7441 * ash -c 'echo $1' name:'1='
7442 * ash -c 'echo $qwe' name:'qwe='
7443 * ash -c 'echo $$' name:'$='
7444 * ash -c 'echo ${$}' name:'$='
7445 * ash -c 'echo ${$##q}' name:'$=q'
7446 * ash -c 'echo ${#$}' name:'$='
7447 * note: examples with bad shell syntax:
7448 * ash -c 'echo ${#$1}' name:'$=1'
7449 * ash -c 'echo ${#1#}' name:'1=#'
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007450 */
Denys Vlasenkoadf922e2009-10-08 14:35:37 +02007451static NOINLINE ssize_t
Denys Vlasenko440da972018-08-05 14:29:58 +02007452varvalue(char *name, int varflags, int flags, int quoted)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007453{
Mike Frysinger98c52642009-04-02 10:02:37 +00007454 const char *p;
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01007455 int num;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007456 int i;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007457 ssize_t len = 0;
Ron Yorstond68d1fb2015-05-18 09:49:28 +02007458 int sep;
Ron Yorstond68d1fb2015-05-18 09:49:28 +02007459 int subtype = varflags & VSTYPE;
Denys Vlasenko82331882020-02-24 10:02:50 +01007460 int discard = (subtype == VSPLUS || subtype == VSLENGTH) | (flags & EXP_DISCARD);
7461
7462 if (!subtype) {
7463 if (discard)
7464 return -1;
7465
Denys Vlasenko1c545522022-08-02 11:13:44 +02007466 ifsfree();
Denys Vlasenko82331882020-02-24 10:02:50 +01007467 raise_error_syntax("bad substitution");
7468 }
Denys Vlasenko0aaaa502016-10-02 02:46:56 +02007469
Denys Vlasenkoecc85832020-02-20 10:06:20 +01007470 flags |= EXP_KEEPNUL;
7471 flags &= discard ? ~QUOTES_ESC : ~0;
Denys Vlasenko0aaaa502016-10-02 02:46:56 +02007472 sep = (flags & EXP_FULL) << CHAR_BIT;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007473
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007474 switch (*name) {
7475 case '$':
7476 num = rootpid;
7477 goto numvar;
7478 case '?':
7479 num = exitstatus;
7480 goto numvar;
7481 case '#':
7482 num = shellparam.nparam;
7483 goto numvar;
7484 case '!':
7485 num = backgndpid;
7486 if (num == 0)
7487 return -1;
7488 numvar:
Denys Vlasenko45dd87a2020-02-21 16:30:44 +01007489 len = cvtnum(num, flags);
Denys Vlasenko4d8873f2009-10-04 03:14:41 +02007490 goto check_1char_name;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007491 case '-':
Mike Frysinger98c52642009-04-02 10:02:37 +00007492 expdest = makestrspace(NOPTS, expdest);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007493 for (i = NOPTS - 1; i >= 0; i--) {
Martijn Dekkerad4e9612018-03-31 18:15:59 +02007494 if (optlist[i] && optletters(i)) {
Mike Frysinger98c52642009-04-02 10:02:37 +00007495 USTPUTC(optletters(i), expdest);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007496 len++;
7497 }
7498 }
Denys Vlasenko4d8873f2009-10-04 03:14:41 +02007499 check_1char_name:
7500#if 0
7501 /* handles cases similar to ${#$1} */
7502 if (name[2] != '\0')
7503 raise_error_syntax("bad substitution");
7504#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007505 break;
Denys Vlasenko0aaaa502016-10-02 02:46:56 +02007506 case '@':
7507 if (quoted && sep)
7508 goto param;
7509 /* fall through */
7510 case '*': {
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01007511 char **ap;
Ron Yorstond68d1fb2015-05-18 09:49:28 +02007512 char sepc;
Denys Vlasenko440da972018-08-05 14:29:58 +02007513 char c;
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01007514
Denys Vlasenko440da972018-08-05 14:29:58 +02007515 /* We will set c to 0 or ~0 depending on whether
7516 * we're doing field splitting. We won't do field
7517 * splitting if either we're quoted or sep is zero.
7518 *
7519 * Instead of testing (quoted || !sep) the following
7520 * trick optimises away any branches by using the
7521 * fact that EXP_QUOTED (which is the only bit that
7522 * can be set in quoted) is the same as EXP_FULL <<
7523 * CHAR_BIT (which is the only bit that can be set
7524 * in sep).
7525 */
7526#if EXP_QUOTED >> CHAR_BIT != EXP_FULL
7527#error The following two lines expect EXP_QUOTED == EXP_FULL << CHAR_BIT
7528#endif
7529 c = !((quoted | ~sep) & EXP_QUOTED) - 1;
7530 sep &= ~quoted;
7531 sep |= ifsset() ? (unsigned char)(c & ifsval()[0]) : ' ';
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007532 param:
Ron Yorstond68d1fb2015-05-18 09:49:28 +02007533 sepc = sep;
Denys Vlasenko0dd8e452016-10-01 21:02:06 +02007534 ap = shellparam.p;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007535 if (!ap)
7536 return -1;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02007537 while ((p = *ap++) != NULL) {
Denys Vlasenkoecc85832020-02-20 10:06:20 +01007538 len += strtodest(p, flags);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007539
7540 if (*ap && sep) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007541 len++;
Denys Vlasenkoecc85832020-02-20 10:06:20 +01007542 memtodest(&sepc, 1, flags);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007543 }
7544 }
Ron Yorstond68d1fb2015-05-18 09:49:28 +02007545 break;
Denys Vlasenko0aaaa502016-10-02 02:46:56 +02007546 } /* case '*' */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007547 case '0':
7548 case '1':
7549 case '2':
7550 case '3':
7551 case '4':
7552 case '5':
7553 case '6':
7554 case '7':
7555 case '8':
7556 case '9':
Denys Vlasenkoa00329c2009-08-30 20:05:10 +02007557 num = atoi(name); /* number(name) fails on ${N#str} etc */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007558 if (num < 0 || num > shellparam.nparam)
7559 return -1;
7560 p = num ? shellparam.p[num - 1] : arg0;
7561 goto value;
7562 default:
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00007563 /* NB: name has form "VAR=..." */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007564 p = lookupvar(name);
7565 value:
7566 if (!p)
7567 return -1;
7568
Denys Vlasenkoecc85832020-02-20 10:06:20 +01007569 len = strtodest(p, flags);
Denys Vlasenkoc76236f2014-12-29 00:04:18 +01007570#if ENABLE_UNICODE_SUPPORT
7571 if (subtype == VSLENGTH && len > 0) {
7572 reinit_unicode_for_ash();
7573 if (unicode_status == UNICODE_ON) {
Ron Yorston3e3bfb82016-03-18 11:29:19 +00007574 STADJUST(-len, expdest);
7575 discard = 0;
Denys Vlasenkoc76236f2014-12-29 00:04:18 +01007576 len = unicode_strlen(p);
7577 }
7578 }
7579#endif
Ron Yorstond68d1fb2015-05-18 09:49:28 +02007580 break;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007581 }
7582
Ron Yorstond68d1fb2015-05-18 09:49:28 +02007583 if (discard)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007584 STADJUST(-len, expdest);
Denys Vlasenko82331882020-02-24 10:02:50 +01007585
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007586 return len;
7587}
7588
7589/*
7590 * Expand a variable, and return a pointer to the next character in the
7591 * input string.
7592 */
7593static char *
Denys Vlasenkob8c0bc12017-07-26 23:03:21 +02007594evalvar(char *p, int flag)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007595{
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00007596 char varflags;
7597 char subtype;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007598 char *var;
7599 int patloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007600 int startloc;
7601 ssize_t varlen;
Denys Vlasenko15558952020-02-22 19:38:40 +01007602 int discard;
Denys Vlasenko82331882020-02-24 10:02:50 +01007603 int quoted;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007604
Denys Vlasenkob0d63382009-09-16 16:18:32 +02007605 varflags = (unsigned char) *p++;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007606 subtype = varflags & VSTYPE;
Denys Vlasenko88e15702016-10-26 01:55:56 +02007607
Denys Vlasenko88ac97d2016-10-01 20:55:02 +02007608 quoted = flag & EXP_QUOTED;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007609 var = p;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007610 startloc = expdest - (char *)stackblock();
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02007611 p = strchr(p, '=') + 1; //TODO: use var_end(p)?
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007612
7613 again:
Denys Vlasenko440da972018-08-05 14:29:58 +02007614 varlen = varvalue(var, varflags, flag, quoted);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007615 if (varflags & VSNUL)
7616 varlen--;
7617
Denys Vlasenko15558952020-02-22 19:38:40 +01007618 discard = varlen < 0 ? EXP_DISCARD : 0;
7619
Denys Vlasenko82331882020-02-24 10:02:50 +01007620 switch (subtype) {
7621 case VSPLUS:
Denys Vlasenko15558952020-02-22 19:38:40 +01007622 discard ^= EXP_DISCARD;
Denys Vlasenko82331882020-02-24 10:02:50 +01007623 /* fall through */
7624 case 0:
7625 case VSMINUS:
Denys Vlasenko15558952020-02-22 19:38:40 +01007626 p = argstr(p, flag | EXP_TILDE | EXP_WORD | (discard ^ EXP_DISCARD));
Denys Vlasenko88ac97d2016-10-01 20:55:02 +02007627 goto record;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007628
Denys Vlasenko82331882020-02-24 10:02:50 +01007629 case VSASSIGN:
7630 case VSQUESTION:
Denys Vlasenko15558952020-02-22 19:38:40 +01007631 p = subevalvar(p, var, 0, startloc, varflags,
7632 (flag & ~QUOTES_ESC) | (discard ^ EXP_DISCARD));
7633
7634 if ((flag | ~discard) & EXP_DISCARD)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007635 goto record;
Denys Vlasenko88ac97d2016-10-01 20:55:02 +02007636
Denys Vlasenko88ac97d2016-10-01 20:55:02 +02007637 varflags &= ~VSNUL;
Denys Vlasenko15558952020-02-22 19:38:40 +01007638 subtype = VSNORMAL;
Denys Vlasenko88ac97d2016-10-01 20:55:02 +02007639 goto again;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007640 }
7641
Denys Vlasenko15558952020-02-22 19:38:40 +01007642 if ((discard & ~flag) && uflag)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007643 varunset(p, var, 0, 0);
7644
7645 if (subtype == VSLENGTH) {
Denys Vlasenko82331882020-02-24 10:02:50 +01007646 p++;
7647 if (flag & EXP_DISCARD)
7648 return p;
Denys Vlasenko45dd87a2020-02-21 16:30:44 +01007649 cvtnum(varlen > 0 ? varlen : 0, flag);
Denys Vlasenko15558952020-02-22 19:38:40 +01007650 goto really_record;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007651 }
7652
Denys Vlasenko82331882020-02-24 10:02:50 +01007653 if (subtype == VSNORMAL)
7654 goto record;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007655
7656#if DEBUG
7657 switch (subtype) {
7658 case VSTRIMLEFT:
7659 case VSTRIMLEFTMAX:
7660 case VSTRIMRIGHT:
7661 case VSTRIMRIGHTMAX:
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01007662#if BASH_SUBSTR
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007663 case VSSUBSTR:
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01007664#endif
7665#if BASH_PATTERN_SUBST
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007666 case VSREPLACE:
7667 case VSREPLACEALL:
7668#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007669 break;
7670 default:
7671 abort();
7672 }
7673#endif
7674
Denys Vlasenko15558952020-02-22 19:38:40 +01007675 flag |= discard;
Denys Vlasenko82331882020-02-24 10:02:50 +01007676 if (!(flag & EXP_DISCARD)) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007677 /*
7678 * Terminate the string and start recording the pattern
7679 * right after it
7680 */
7681 STPUTC('\0', expdest);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007682 }
7683
Denys Vlasenko82331882020-02-24 10:02:50 +01007684 patloc = expdest - (char *)stackblock();
7685 p = subevalvar(p, NULL, patloc, startloc, varflags, flag);
Denys Vlasenko4ace3852020-02-16 18:42:50 +01007686
Denys Vlasenko82331882020-02-24 10:02:50 +01007687 record:
Denys Vlasenko15558952020-02-22 19:38:40 +01007688 if ((flag | discard) & EXP_DISCARD)
Denys Vlasenko82331882020-02-24 10:02:50 +01007689 return p;
7690
Denys Vlasenko15558952020-02-22 19:38:40 +01007691 really_record:
Denys Vlasenko82331882020-02-24 10:02:50 +01007692 if (quoted) {
7693 quoted = *var == '@' && shellparam.nparam;
7694 if (!quoted)
7695 return p;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007696 }
Denys Vlasenko82331882020-02-24 10:02:50 +01007697 recordregion(startloc, expdest - (char *)stackblock(), quoted);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007698 return p;
7699}
7700
7701/*
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007702 * Add a file name to the list.
7703 */
7704static void
7705addfname(const char *name)
7706{
7707 struct strlist *sp;
7708
Denis Vlasenko597906c2008-02-20 16:38:54 +00007709 sp = stzalloc(sizeof(*sp));
Denys Vlasenko8e2bc472016-09-28 23:02:57 +02007710 sp->text = sstrdup(name);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007711 *exparg.lastp = sp;
7712 exparg.lastp = &sp->next;
7713}
7714
Felix Fietkaub5b21122017-01-31 21:58:55 +01007715/* Avoid glob() (and thus, stat() et al) for words like "echo" */
7716static int
7717hasmeta(const char *p)
7718{
7719 static const char chars[] ALIGN1 = {
7720 '*', '?', '[', '\\', CTLQUOTEMARK, CTLESC, 0
7721 };
7722
7723 for (;;) {
7724 p = strpbrk(p, chars);
7725 if (!p)
7726 break;
Denys Vlasenkoac61f442018-03-30 23:03:29 +02007727 switch ((unsigned char)*p) {
Felix Fietkaub5b21122017-01-31 21:58:55 +01007728 case CTLQUOTEMARK:
7729 for (;;) {
7730 p++;
Denys Vlasenkoac61f442018-03-30 23:03:29 +02007731 if ((unsigned char)*p == CTLQUOTEMARK)
Felix Fietkaub5b21122017-01-31 21:58:55 +01007732 break;
Denys Vlasenkoac61f442018-03-30 23:03:29 +02007733 if ((unsigned char)*p == CTLESC)
Felix Fietkaub5b21122017-01-31 21:58:55 +01007734 p++;
7735 if (*p == '\0') /* huh? */
7736 return 0;
7737 }
7738 break;
7739 case '\\':
7740 case CTLESC:
7741 p++;
7742 if (*p == '\0')
7743 return 0;
7744 break;
7745 case '[':
7746 if (!strchr(p + 1, ']')) {
7747 /* It's not a properly closed [] pattern,
7748 * but other metas may follow. Continue checking.
7749 * my[file* _is_ globbed by bash
7750 * and matches filenames like "my[file1".
7751 */
7752 break;
7753 }
7754 /* fallthrough */
7755 default:
7756 /* case '*': */
7757 /* case '?': */
7758 return 1;
7759 }
7760 p++;
7761 }
7762
7763 return 0;
7764}
7765
Denys Vlasenkob3f29b42016-09-21 16:25:58 +02007766/* If we want to use glob() from libc... */
Denys Vlasenko514b51d2016-10-01 14:33:08 +02007767#if !ENABLE_ASH_INTERNAL_GLOB
Denys Vlasenkob3f29b42016-09-21 16:25:58 +02007768
7769/* Add the result of glob() to the list */
7770static void
7771addglob(const glob_t *pglob)
7772{
7773 char **p = pglob->gl_pathv;
7774
7775 do {
7776 addfname(*p);
7777 } while (*++p);
7778}
7779static void
7780expandmeta(struct strlist *str /*, int flag*/)
7781{
7782 /* TODO - EXP_REDIR */
7783
7784 while (str) {
7785 char *p;
7786 glob_t pglob;
7787 int i;
7788
7789 if (fflag)
7790 goto nometa;
Denys Vlasenkod4f3db92016-10-30 18:41:01 +01007791
Felix Fietkaub5b21122017-01-31 21:58:55 +01007792 if (!hasmeta(str->text))
7793 goto nometa;
Denys Vlasenkod4f3db92016-10-30 18:41:01 +01007794
Denys Vlasenkob3f29b42016-09-21 16:25:58 +02007795 INT_OFF;
7796 p = preglob(str->text, RMESCAPE_ALLOC | RMESCAPE_HEAP);
Denys Vlasenko8e2c9cc2016-10-02 15:17:15 +02007797// GLOB_NOMAGIC (GNU): if no *?[ chars in pattern, return it even if no match
7798// GLOB_NOCHECK: if no match, return unchanged pattern (sans \* escapes?)
7799//
7800// glibc 2.24.90 glob(GLOB_NOMAGIC) does not remove backslashes used for escaping:
7801// if you pass it "file\?", it returns "file\?", not "file?", if no match.
7802// Which means you need to unescape the string, right? Not so fast:
7803// if there _is_ a file named "file\?" (with backslash), it is returned
7804// as "file\?" too (whichever pattern you used to find it, say, "file*").
Denys Vlasenko10ad6222017-04-17 16:13:32 +02007805// You DON'T KNOW by looking at the result whether you need to unescape it.
Denys Vlasenko8e2c9cc2016-10-02 15:17:15 +02007806//
7807// Worse, globbing of "file\?" in a directory with two files, "file?" and "file\?",
7808// returns "file\?" - which is WRONG: "file\?" pattern matches "file?" file.
7809// Without GLOB_NOMAGIC, this works correctly ("file?" is returned as a match).
7810// With GLOB_NOMAGIC | GLOB_NOCHECK, this also works correctly.
7811// i = glob(p, GLOB_NOMAGIC | GLOB_NOCHECK, NULL, &pglob);
7812// i = glob(p, GLOB_NOMAGIC, NULL, &pglob);
7813 i = glob(p, 0, NULL, &pglob);
7814 //bb_error_msg("glob('%s'):%d '%s'...", p, i, pglob.gl_pathv ? pglob.gl_pathv[0] : "-");
Denys Vlasenkob3f29b42016-09-21 16:25:58 +02007815 if (p != str->text)
7816 free(p);
7817 switch (i) {
7818 case 0:
Denys Vlasenko8e2c9cc2016-10-02 15:17:15 +02007819#if 0 // glibc 2.24.90 bug? Patterns like "*/file", when match, don't set GLOB_MAGCHAR
Denys Vlasenkob3f29b42016-09-21 16:25:58 +02007820 /* GLOB_MAGCHAR is set if *?[ chars were seen (GNU) */
7821 if (!(pglob.gl_flags & GLOB_MAGCHAR))
7822 goto nometa2;
Denys Vlasenko8e2c9cc2016-10-02 15:17:15 +02007823#endif
Denys Vlasenkob3f29b42016-09-21 16:25:58 +02007824 addglob(&pglob);
7825 globfree(&pglob);
7826 INT_ON;
7827 break;
7828 case GLOB_NOMATCH:
Denys Vlasenko8e2c9cc2016-10-02 15:17:15 +02007829 //nometa2:
Denys Vlasenkob3f29b42016-09-21 16:25:58 +02007830 globfree(&pglob);
7831 INT_ON;
Denys Vlasenko8e2c9cc2016-10-02 15:17:15 +02007832 nometa:
Denys Vlasenkob3f29b42016-09-21 16:25:58 +02007833 *exparg.lastp = str;
Denys Vlasenko740058b2018-01-09 17:01:00 +01007834 rmescapes(str->text, 0, NULL);
Denys Vlasenkob3f29b42016-09-21 16:25:58 +02007835 exparg.lastp = &str->next;
7836 break;
7837 default: /* GLOB_NOSPACE */
7838 globfree(&pglob);
7839 INT_ON;
7840 ash_msg_and_raise_error(bb_msg_memory_exhausted);
7841 }
7842 str = str->next;
7843 }
7844}
7845
7846#else
Denys Vlasenko514b51d2016-10-01 14:33:08 +02007847/* ENABLE_ASH_INTERNAL_GLOB: Homegrown globbing code. (dash also has both, uses homegrown one.) */
Denys Vlasenkob3f29b42016-09-21 16:25:58 +02007848
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007849/*
7850 * Do metacharacter (i.e. *, ?, [...]) expansion.
7851 */
Denys Vlasenkod5f50452018-04-14 14:50:47 +02007852typedef struct exp_t {
7853 char *dir;
7854 unsigned dir_max;
7855} exp_t;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007856static void
Denys Vlasenkod5f50452018-04-14 14:50:47 +02007857expmeta(exp_t *exp, char *name, unsigned name_len, unsigned expdir_len)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007858{
Denys Vlasenkod5f50452018-04-14 14:50:47 +02007859#define expdir exp->dir
7860#define expdir_max exp->dir_max
7861 char *enddir = expdir + expdir_len;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007862 char *p;
7863 const char *cp;
7864 char *start;
7865 char *endname;
7866 int metaflag;
7867 struct stat statb;
7868 DIR *dirp;
7869 struct dirent *dp;
7870 int atend;
7871 int matchdot;
Ron Yorstonca25af92015-09-04 10:32:41 +01007872 int esc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007873
7874 metaflag = 0;
7875 start = name;
Ron Yorstonca25af92015-09-04 10:32:41 +01007876 for (p = name; esc = 0, *p; p += esc + 1) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007877 if (*p == '*' || *p == '?')
7878 metaflag = 1;
7879 else if (*p == '[') {
7880 char *q = p + 1;
7881 if (*q == '!')
7882 q++;
7883 for (;;) {
7884 if (*q == '\\')
7885 q++;
7886 if (*q == '/' || *q == '\0')
7887 break;
7888 if (*++q == ']') {
7889 metaflag = 1;
7890 break;
7891 }
7892 }
Ron Yorstonca25af92015-09-04 10:32:41 +01007893 } else {
Denys Vlasenkoeb54ca82018-08-07 18:54:52 +02007894 if (*p == '\\' && p[1])
Ron Yorstonca25af92015-09-04 10:32:41 +01007895 esc++;
7896 if (p[esc] == '/') {
7897 if (metaflag)
7898 break;
7899 start = p + esc + 1;
7900 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007901 }
7902 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007903 if (metaflag == 0) { /* we've reached the end of the file name */
Denys Vlasenkod5f50452018-04-14 14:50:47 +02007904 if (!expdir_len)
7905 return;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007906 p = name;
7907 do {
Denys Vlasenkoeb54ca82018-08-07 18:54:52 +02007908 if (*p == '\\' && p[1])
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007909 p++;
7910 *enddir++ = *p;
7911 } while (*p++);
Denys Vlasenkod5f50452018-04-14 14:50:47 +02007912 if (lstat(expdir, &statb) == 0)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007913 addfname(expdir);
7914 return;
7915 }
7916 endname = p;
7917 if (name < start) {
7918 p = name;
7919 do {
Denys Vlasenkoeb54ca82018-08-07 18:54:52 +02007920 if (*p == '\\' && p[1])
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007921 p++;
7922 *enddir++ = *p++;
7923 } while (p < start);
7924 }
Denys Vlasenkod5f50452018-04-14 14:50:47 +02007925 *enddir = '\0';
7926 cp = expdir;
7927 expdir_len = enddir - cp;
7928 if (!expdir_len)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007929 cp = ".";
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007930 dirp = opendir(cp);
7931 if (dirp == NULL)
7932 return;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007933 if (*endname == 0) {
7934 atend = 1;
7935 } else {
7936 atend = 0;
Ron Yorstonca25af92015-09-04 10:32:41 +01007937 *endname = '\0';
7938 endname += esc + 1;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007939 }
Denys Vlasenkod5f50452018-04-14 14:50:47 +02007940 name_len -= endname - name;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007941 matchdot = 0;
7942 p = start;
7943 if (*p == '\\')
7944 p++;
7945 if (*p == '.')
7946 matchdot++;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02007947 while (!pending_int && (dp = readdir(dirp)) != NULL) {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00007948 if (dp->d_name[0] == '.' && !matchdot)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007949 continue;
7950 if (pmatch(start, dp->d_name)) {
7951 if (atend) {
7952 strcpy(enddir, dp->d_name);
7953 addfname(expdir);
7954 } else {
Denys Vlasenkod5f50452018-04-14 14:50:47 +02007955 unsigned offset;
7956 unsigned len;
7957
7958 p = stpcpy(enddir, dp->d_name);
7959 *p = '/';
7960
7961 offset = p - expdir + 1;
7962 len = offset + name_len + NAME_MAX;
7963 if (len > expdir_max) {
7964 len += PATH_MAX;
7965 expdir = ckrealloc(expdir, len);
7966 expdir_max = len;
7967 }
7968
7969 expmeta(exp, endname, name_len, offset);
7970 enddir = expdir + expdir_len;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007971 }
7972 }
7973 }
7974 closedir(dirp);
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00007975 if (!atend)
Ron Yorstonca25af92015-09-04 10:32:41 +01007976 endname[-esc - 1] = esc ? '\\' : '/';
Denys Vlasenkod5f50452018-04-14 14:50:47 +02007977#undef expdir
7978#undef expdir_max
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007979}
7980
7981static struct strlist *
7982msort(struct strlist *list, int len)
7983{
7984 struct strlist *p, *q = NULL;
7985 struct strlist **lpp;
7986 int half;
7987 int n;
7988
7989 if (len <= 1)
7990 return list;
7991 half = len >> 1;
7992 p = list;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00007993 for (n = half; --n >= 0;) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007994 q = p;
7995 p = p->next;
7996 }
7997 q->next = NULL; /* terminate first half of list */
7998 q = msort(list, half); /* sort first half of list */
7999 p = msort(p, len - half); /* sort second half */
8000 lpp = &list;
8001 for (;;) {
8002#if ENABLE_LOCALE_SUPPORT
8003 if (strcoll(p->text, q->text) < 0)
8004#else
8005 if (strcmp(p->text, q->text) < 0)
8006#endif
8007 {
8008 *lpp = p;
8009 lpp = &p->next;
8010 p = *lpp;
8011 if (p == NULL) {
8012 *lpp = q;
8013 break;
8014 }
8015 } else {
8016 *lpp = q;
8017 lpp = &q->next;
8018 q = *lpp;
8019 if (q == NULL) {
8020 *lpp = p;
8021 break;
8022 }
8023 }
8024 }
8025 return list;
8026}
8027
8028/*
8029 * Sort the results of file name expansion. It calculates the number of
8030 * strings to sort and then calls msort (short for merge sort) to do the
8031 * work.
8032 */
8033static struct strlist *
8034expsort(struct strlist *str)
8035{
8036 int len;
8037 struct strlist *sp;
8038
8039 len = 0;
8040 for (sp = str; sp; sp = sp->next)
8041 len++;
8042 return msort(str, len);
8043}
8044
8045static void
Denis Vlasenko68404f12008-03-17 09:00:54 +00008046expandmeta(struct strlist *str /*, int flag*/)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008047{
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008048 /* TODO - EXP_REDIR */
8049
8050 while (str) {
Denys Vlasenkod5f50452018-04-14 14:50:47 +02008051 exp_t exp;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008052 struct strlist **savelastp;
8053 struct strlist *sp;
8054 char *p;
Denys Vlasenkod5f50452018-04-14 14:50:47 +02008055 unsigned len;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008056
8057 if (fflag)
8058 goto nometa;
Felix Fietkaub5b21122017-01-31 21:58:55 +01008059 if (!hasmeta(str->text))
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008060 goto nometa;
8061 savelastp = exparg.lastp;
8062
8063 INT_OFF;
Ron Yorston549deab2015-05-18 09:57:51 +02008064 p = preglob(str->text, RMESCAPE_ALLOC | RMESCAPE_HEAP);
Denys Vlasenkod5f50452018-04-14 14:50:47 +02008065 len = strlen(p);
8066 exp.dir_max = len + PATH_MAX;
8067 exp.dir = ckmalloc(exp.dir_max);
8068
8069 expmeta(&exp, p, len, 0);
8070 free(exp.dir);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008071 if (p != str->text)
8072 free(p);
8073 INT_ON;
8074 if (exparg.lastp == savelastp) {
8075 /*
8076 * no matches
8077 */
8078 nometa:
8079 *exparg.lastp = str;
Denys Vlasenko740058b2018-01-09 17:01:00 +01008080 rmescapes(str->text, 0, NULL);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008081 exparg.lastp = &str->next;
8082 } else {
8083 *exparg.lastp = NULL;
8084 *savelastp = sp = expsort(*savelastp);
8085 while (sp->next != NULL)
8086 sp = sp->next;
8087 exparg.lastp = &sp->next;
8088 }
8089 str = str->next;
8090 }
8091}
Denys Vlasenko514b51d2016-10-01 14:33:08 +02008092#endif /* ENABLE_ASH_INTERNAL_GLOB */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008093
8094/*
8095 * Perform variable substitution and command substitution on an argument,
8096 * placing the resulting list of arguments in arglist. If EXP_FULL is true,
8097 * perform splitting and file name expansion. When arglist is NULL, perform
8098 * here document expansion.
8099 */
8100static void
8101expandarg(union node *arg, struct arglist *arglist, int flag)
8102{
8103 struct strlist *sp;
8104 char *p;
8105
8106 argbackq = arg->narg.backquote;
8107 STARTSTACKSTR(expdest);
Denys Vlasenkof451b2c2012-06-09 02:06:57 +02008108 TRACE(("expandarg: argstr('%s',flags:%x)\n", arg->narg.text, flag));
Denys Vlasenkob8c0bc12017-07-26 23:03:21 +02008109 argstr(arg->narg.text, flag);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008110 if (arglist == NULL) {
Denys Vlasenko5ac04f22016-10-27 14:46:50 +02008111 /* here document expanded */
8112 goto out;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008113 }
Denys Vlasenko82331882020-02-24 10:02:50 +01008114 p = grabstackstr(expdest);
Denys Vlasenkof451b2c2012-06-09 02:06:57 +02008115 TRACE(("expandarg: p:'%s'\n", p));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008116 exparg.lastp = &exparg.list;
8117 /*
8118 * TODO - EXP_REDIR
8119 */
8120 if (flag & EXP_FULL) {
8121 ifsbreakup(p, &exparg);
8122 *exparg.lastp = NULL;
8123 exparg.lastp = &exparg.list;
Denis Vlasenko68404f12008-03-17 09:00:54 +00008124 expandmeta(exparg.list /*, flag*/);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008125 } else {
Denis Vlasenko597906c2008-02-20 16:38:54 +00008126 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008127 sp->text = p;
8128 *exparg.lastp = sp;
8129 exparg.lastp = &sp->next;
8130 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008131 *exparg.lastp = NULL;
8132 if (exparg.list) {
8133 *arglist->lastp = exparg.list;
8134 arglist->lastp = exparg.lastp;
8135 }
Denys Vlasenko5ac04f22016-10-27 14:46:50 +02008136
8137 out:
8138 ifsfree();
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008139}
8140
8141/*
8142 * Expand shell variables and backquotes inside a here document.
8143 */
8144static void
Denys Vlasenkoc2058ec2020-02-22 20:25:03 +01008145expandhere(union node *arg)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008146{
Ron Yorston549deab2015-05-18 09:57:51 +02008147 expandarg(arg, (struct arglist *)NULL, EXP_QUOTED);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008148}
8149
8150/*
8151 * Returns true if the pattern matches the string.
8152 */
8153static int
8154patmatch(char *pattern, const char *string)
8155{
Denys Vlasenkobd43c672017-07-05 23:12:15 +02008156 char *p = preglob(pattern, 0);
Denys Vlasenko4476c702017-08-15 15:27:41 +02008157 int r = pmatch(p, string);
8158 //bb_error_msg("!fnmatch(pattern:'%s',str:'%s',0):%d", p, string, r);
8159 return r;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008160}
8161
8162/*
8163 * See if a pattern matches in a case statement.
8164 */
8165static int
8166casematch(union node *pattern, char *val)
8167{
8168 struct stackmark smark;
8169 int result;
8170
8171 setstackmark(&smark);
8172 argbackq = pattern->narg.backquote;
8173 STARTSTACKSTR(expdest);
Denys Vlasenkob8c0bc12017-07-26 23:03:21 +02008174 argstr(pattern->narg.text, EXP_TILDE | EXP_CASE);
Denys Vlasenko5ac04f22016-10-27 14:46:50 +02008175 ifsfree();
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008176 result = patmatch(stackblock(), val);
8177 popstackmark(&smark);
8178 return result;
8179}
8180
8181
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008182/* ============ find_command */
8183
8184struct builtincmd {
8185 const char *name;
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008186 int (*builtin)(int, char **) FAST_FUNC;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008187 /* unsigned flags; */
8188};
8189#define IS_BUILTIN_SPECIAL(b) ((b)->name[0] & 1)
Denis Vlasenkoe26b2782008-02-12 07:40:29 +00008190/* "regular" builtins always take precedence over commands,
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00008191 * regardless of PATH=....%builtin... position */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008192#define IS_BUILTIN_REGULAR(b) ((b)->name[0] & 2)
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00008193#define IS_BUILTIN_ASSIGN(b) ((b)->name[0] & 4)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008194
8195struct cmdentry {
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00008196 smallint cmdtype; /* CMDxxx */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008197 union param {
8198 int index;
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00008199 /* index >= 0 for commands without path (slashes) */
8200 /* (TODO: what exactly does the value mean? PATH position?) */
8201 /* index == -1 for commands with slashes */
8202 /* index == (-2 - applet_no) for NOFORK applets */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008203 const struct builtincmd *cmd;
8204 struct funcnode *func;
8205 } u;
8206};
8207/* values of cmdtype */
8208#define CMDUNKNOWN -1 /* no entry in table for command */
8209#define CMDNORMAL 0 /* command is an executable program */
8210#define CMDFUNCTION 1 /* command is a shell function */
8211#define CMDBUILTIN 2 /* command is a shell builtin */
8212
8213/* action to find_command() */
8214#define DO_ERR 0x01 /* prints errors */
8215#define DO_ABS 0x02 /* checks absolute paths */
8216#define DO_NOFUNC 0x04 /* don't return shell functions, for command */
8217#define DO_ALTPATH 0x08 /* using alternate path */
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +01008218#define DO_REGBLTIN 0x10 /* regular built-ins and functions only */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008219
8220static void find_command(char *, struct cmdentry *, int, const char *);
8221
8222
8223/* ============ Hashing commands */
8224
8225/*
8226 * When commands are first encountered, they are entered in a hash table.
8227 * This ensures that a full path search will not have to be done for them
8228 * on each invocation.
8229 *
8230 * We should investigate converting to a linear search, even though that
8231 * would make the command name "hash" a misnomer.
8232 */
8233
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008234struct tblentry {
8235 struct tblentry *next; /* next entry in hash chain */
8236 union param param; /* definition of builtin function */
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00008237 smallint cmdtype; /* CMDxxx */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008238 char rehash; /* if set, cd done since entry created */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008239 char cmdname[1]; /* name of command */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008240};
8241
Denis Vlasenko01631112007-12-16 17:20:38 +00008242static struct tblentry **cmdtable;
8243#define INIT_G_cmdtable() do { \
8244 cmdtable = xzalloc(CMDTABLESIZE * sizeof(cmdtable[0])); \
8245} while (0)
8246
8247static int builtinloc = -1; /* index in path of %builtin, or -1 */
8248
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008249
8250static void
Denys Vlasenko00a1dbd2017-07-29 01:20:53 +02008251tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) const char *cmd, char **argv, char **envp)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008252{
Denis Vlasenko80d14be2007-04-10 23:03:30 +00008253#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00008254 if (applet_no >= 0) {
Denis Vlasenkob7304742008-10-20 08:15:51 +00008255 if (APPLET_IS_NOEXEC(applet_no)) {
Denys Vlasenko7df28bb2010-06-18 14:23:47 +02008256 clearenv();
Denis Vlasenkob7304742008-10-20 08:15:51 +00008257 while (*envp)
8258 putenv(*envp++);
Denys Vlasenko035486c2017-07-31 04:09:19 +02008259 popredir(/*drop:*/ 1);
Denys Vlasenko80e8e3c2017-08-07 19:24:57 +02008260 run_noexec_applet_and_exit(applet_no, cmd, argv);
Denis Vlasenkob7304742008-10-20 08:15:51 +00008261 }
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00008262 /* re-exec ourselves with the new arguments */
8263 execve(bb_busybox_exec_path, argv, envp);
8264 /* If they called chroot or otherwise made the binary no longer
8265 * executable, fall through */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008266 }
8267#endif
8268
8269 repeat:
8270#ifdef SYSV
8271 do {
8272 execve(cmd, argv, envp);
8273 } while (errno == EINTR);
8274#else
8275 execve(cmd, argv, envp);
8276#endif
Ron Yorstonca82b532018-11-01 11:45:03 +01008277
Denys Vlasenko00a1dbd2017-07-29 01:20:53 +02008278 if (cmd != bb_busybox_exec_path && errno == ENOEXEC) {
Denys Vlasenkoaefe1c22011-03-07 12:02:40 +01008279 /* Run "cmd" as a shell script:
8280 * http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html
8281 * "If the execve() function fails with ENOEXEC, the shell
8282 * shall execute a command equivalent to having a shell invoked
8283 * with the command name as its first operand,
8284 * with any remaining arguments passed to the new shell"
8285 *
8286 * That is, do not use $SHELL, user's shell, or /bin/sh;
8287 * just call ourselves.
Denys Vlasenko2bef5262011-12-16 00:25:17 +01008288 *
8289 * Note that bash reads ~80 chars of the file, and if it sees
8290 * a zero byte before it sees newline, it doesn't try to
8291 * interpret it, but fails with "cannot execute binary file"
Denys Vlasenkocda6ea92011-12-16 00:44:36 +01008292 * message and exit code 126. For one, this prevents attempts
8293 * to interpret foreign ELF binaries as shell scripts.
Denys Vlasenkoaefe1c22011-03-07 12:02:40 +01008294 */
Denys Vlasenko00a1dbd2017-07-29 01:20:53 +02008295 argv[0] = (char*) cmd;
8296 cmd = bb_busybox_exec_path;
Denys Vlasenko65a8b852016-10-26 22:29:11 +02008297 /* NB: this is only possible because all callers of shellexec()
8298 * ensure that the argv[-1] slot exists!
8299 */
8300 argv--;
8301 argv[0] = (char*) "ash";
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008302 goto repeat;
8303 }
8304}
8305
8306/*
8307 * Exec a program. Never returns. If you change this routine, you may
8308 * have to change the find_command routine as well.
Denys Vlasenko65a8b852016-10-26 22:29:11 +02008309 * argv[-1] must exist and be writable! See tryexec() for why.
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008310 */
Denys Vlasenkoe139ae32017-04-12 21:02:33 +02008311static void shellexec(char *prog, char **argv, const char *path, int idx) NORETURN;
8312static void shellexec(char *prog, char **argv, const char *path, int idx)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008313{
8314 char *cmdname;
8315 int e;
8316 char **envp;
8317 int exerrno;
Denys Vlasenko83f103b2011-12-20 06:10:35 +01008318 int applet_no = -1; /* used only by FEATURE_SH_STANDALONE */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008319
Denys Vlasenkoa5060b82017-11-03 14:16:25 +01008320 envp = listvars(VEXPORT, VUNSET, /*strlist:*/ NULL, /*end:*/ NULL);
Denys Vlasenkoe139ae32017-04-12 21:02:33 +02008321 if (strchr(prog, '/') != NULL
Denis Vlasenko80d14be2007-04-10 23:03:30 +00008322#if ENABLE_FEATURE_SH_STANDALONE
Denys Vlasenkoe139ae32017-04-12 21:02:33 +02008323 || (applet_no = find_applet_by_name(prog)) >= 0
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008324#endif
8325 ) {
Denys Vlasenkoe139ae32017-04-12 21:02:33 +02008326 tryexec(IF_FEATURE_SH_STANDALONE(applet_no,) prog, argv, envp);
Denys Vlasenko83f103b2011-12-20 06:10:35 +01008327 if (applet_no >= 0) {
8328 /* We tried execing ourself, but it didn't work.
8329 * Maybe /proc/self/exe doesn't exist?
8330 * Try $PATH search.
8331 */
8332 goto try_PATH;
8333 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008334 e = errno;
8335 } else {
Denys Vlasenko83f103b2011-12-20 06:10:35 +01008336 try_PATH:
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008337 e = ENOENT;
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +01008338 while (padvance(&path, argv[0]) >= 0) {
8339 cmdname = stackblock();
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008340 if (--idx < 0 && pathopt == NULL) {
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00008341 tryexec(IF_FEATURE_SH_STANDALONE(-1,) cmdname, argv, envp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008342 if (errno != ENOENT && errno != ENOTDIR)
8343 e = errno;
8344 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008345 }
8346 }
8347
8348 /* Map to POSIX errors */
8349 switch (e) {
Denys Vlasenko2596f412018-08-05 18:04:09 +02008350 default:
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008351 exerrno = 126;
8352 break;
Denys Vlasenko2596f412018-08-05 18:04:09 +02008353 case ELOOP:
8354 case ENAMETOOLONG:
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008355 case ENOENT:
Denys Vlasenko2596f412018-08-05 18:04:09 +02008356 case ENOTDIR:
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008357 exerrno = 127;
8358 break;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008359 }
8360 exitstatus = exerrno;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02008361 TRACE(("shellexec failed for %s, errno %d, suppress_int %d\n",
Denys Vlasenkoe139ae32017-04-12 21:02:33 +02008362 prog, e, suppress_int));
Denys Vlasenkof977e002020-02-20 16:54:29 +01008363 ash_msg_and_raise(EXEND, "%s: %s", prog, errmsg(e, "not found"));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008364 /* NOTREACHED */
8365}
8366
8367static void
8368printentry(struct tblentry *cmdp)
8369{
8370 int idx;
8371 const char *path;
8372 char *name;
8373
8374 idx = cmdp->param.index;
8375 path = pathval();
8376 do {
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +01008377 padvance(&path, cmdp->cmdname);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008378 } while (--idx >= 0);
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +01008379 name = stackblock();
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008380 out1fmt("%s%s\n", name, (cmdp->rehash ? "*" : nullstr));
8381}
8382
8383/*
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +01008384 * Clear out command entries.
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008385 */
8386static void
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +01008387clearcmdentry(void)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008388{
8389 struct tblentry **tblp;
8390 struct tblentry **pp;
8391 struct tblentry *cmdp;
8392
8393 INT_OFF;
8394 for (tblp = cmdtable; tblp < &cmdtable[CMDTABLESIZE]; tblp++) {
8395 pp = tblp;
8396 while ((cmdp = *pp) != NULL) {
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +01008397 if (cmdp->cmdtype == CMDNORMAL
Denys Vlasenko22c75922020-02-17 16:20:05 +01008398 || (cmdp->cmdtype == CMDBUILTIN
8399 && !IS_BUILTIN_REGULAR(cmdp->param.cmd)
8400 && builtinloc > 0
8401 )
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008402 ) {
8403 *pp = cmdp->next;
8404 free(cmdp);
8405 } else {
8406 pp = &cmdp->next;
8407 }
8408 }
8409 }
8410 INT_ON;
8411}
8412
8413/*
8414 * Locate a command in the command hash table. If "add" is nonzero,
8415 * add the command to the table if it is not already present. The
8416 * variable "lastcmdentry" is set to point to the address of the link
8417 * pointing to the entry, so that delete_cmd_entry can delete the
8418 * entry.
8419 *
8420 * Interrupts must be off if called with add != 0.
8421 */
8422static struct tblentry **lastcmdentry;
8423
8424static struct tblentry *
8425cmdlookup(const char *name, int add)
8426{
8427 unsigned int hashval;
8428 const char *p;
8429 struct tblentry *cmdp;
8430 struct tblentry **pp;
8431
8432 p = name;
8433 hashval = (unsigned char)*p << 4;
8434 while (*p)
8435 hashval += (unsigned char)*p++;
8436 hashval &= 0x7FFF;
8437 pp = &cmdtable[hashval % CMDTABLESIZE];
8438 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
8439 if (strcmp(cmdp->cmdname, name) == 0)
8440 break;
8441 pp = &cmdp->next;
8442 }
8443 if (add && cmdp == NULL) {
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008444 cmdp = *pp = ckzalloc(sizeof(struct tblentry)
8445 + strlen(name)
8446 /* + 1 - already done because
8447 * tblentry::cmdname is char[1] */);
Denis Vlasenko597906c2008-02-20 16:38:54 +00008448 /*cmdp->next = NULL; - ckzalloc did it */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008449 cmdp->cmdtype = CMDUNKNOWN;
8450 strcpy(cmdp->cmdname, name);
8451 }
8452 lastcmdentry = pp;
8453 return cmdp;
8454}
8455
8456/*
8457 * Delete the command entry returned on the last lookup.
8458 */
8459static void
8460delete_cmd_entry(void)
8461{
8462 struct tblentry *cmdp;
8463
8464 INT_OFF;
8465 cmdp = *lastcmdentry;
8466 *lastcmdentry = cmdp->next;
8467 if (cmdp->cmdtype == CMDFUNCTION)
8468 freefunc(cmdp->param.func);
8469 free(cmdp);
8470 INT_ON;
8471}
8472
8473/*
8474 * Add a new command entry, replacing any existing command entry for
8475 * the same name - except special builtins.
8476 */
8477static void
8478addcmdentry(char *name, struct cmdentry *entry)
8479{
8480 struct tblentry *cmdp;
8481
8482 cmdp = cmdlookup(name, 1);
8483 if (cmdp->cmdtype == CMDFUNCTION) {
8484 freefunc(cmdp->param.func);
8485 }
8486 cmdp->cmdtype = entry->cmdtype;
8487 cmdp->param = entry->u;
8488 cmdp->rehash = 0;
8489}
8490
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008491static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008492hashcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008493{
8494 struct tblentry **pp;
8495 struct tblentry *cmdp;
8496 int c;
8497 struct cmdentry entry;
8498 char *name;
8499
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00008500 if (nextopt("r") != '\0') {
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +01008501 clearcmdentry();
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008502 return 0;
8503 }
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00008504
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008505 if (*argptr == NULL) {
8506 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
8507 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
8508 if (cmdp->cmdtype == CMDNORMAL)
8509 printentry(cmdp);
8510 }
8511 }
8512 return 0;
8513 }
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00008514
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008515 c = 0;
8516 while ((name = *argptr) != NULL) {
8517 cmdp = cmdlookup(name, 0);
8518 if (cmdp != NULL
8519 && (cmdp->cmdtype == CMDNORMAL
Denys Vlasenko22c75922020-02-17 16:20:05 +01008520 || (cmdp->cmdtype == CMDBUILTIN
8521 && !IS_BUILTIN_REGULAR(cmdp->param.cmd)
8522 && builtinloc > 0
8523 )
8524 )
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00008525 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008526 delete_cmd_entry();
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00008527 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008528 find_command(name, &entry, DO_ERR, pathval());
8529 if (entry.cmdtype == CMDUNKNOWN)
8530 c = 1;
8531 argptr++;
8532 }
8533 return c;
8534}
8535
8536/*
8537 * Called when a cd is done. Marks all commands so the next time they
8538 * are executed they will be rehashed.
8539 */
8540static void
8541hashcd(void)
8542{
8543 struct tblentry **pp;
8544 struct tblentry *cmdp;
8545
8546 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
8547 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00008548 if (cmdp->cmdtype == CMDNORMAL
8549 || (cmdp->cmdtype == CMDBUILTIN
Denys Vlasenkoe4dcba12010-10-28 18:57:19 +02008550 && !IS_BUILTIN_REGULAR(cmdp->param.cmd)
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00008551 && builtinloc > 0)
8552 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008553 cmdp->rehash = 1;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00008554 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008555 }
8556 }
8557}
8558
8559/*
8560 * Fix command hash table when PATH changed.
8561 * Called before PATH is changed. The argument is the new value of PATH;
8562 * pathval() still returns the old value at this point.
8563 * Called with interrupts off.
8564 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008565static void FAST_FUNC
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +01008566changepath(const char *newval)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008567{
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +01008568 const char *new;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00008569 int idx;
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +01008570 int bltin;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008571
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +01008572 new = newval;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008573 idx = 0;
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +01008574 bltin = -1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008575 for (;;) {
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +01008576 if (*new == '%' && prefix(new + 1, "builtin")) {
8577 bltin = idx;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008578 break;
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +01008579 }
8580 new = strchr(new, ':');
8581 if (!new)
8582 break;
8583 idx++;
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +02008584 new++;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008585 }
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +01008586 builtinloc = bltin;
8587 clearcmdentry();
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008588}
Ron Yorston95ebcf72015-11-03 09:42:23 +00008589enum {
8590 TEOF,
8591 TNL,
8592 TREDIR,
8593 TWORD,
8594 TSEMI,
8595 TBACKGND,
8596 TAND,
8597 TOR,
8598 TPIPE,
8599 TLP,
8600 TRP,
8601 TENDCASE,
8602 TENDBQUOTE,
8603 TNOT,
8604 TCASE,
8605 TDO,
8606 TDONE,
8607 TELIF,
8608 TELSE,
8609 TESAC,
8610 TFI,
8611 TFOR,
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01008612#if BASH_FUNCTION
Ron Yorston95ebcf72015-11-03 09:42:23 +00008613 TFUNCTION,
8614#endif
8615 TIF,
8616 TIN,
8617 TTHEN,
8618 TUNTIL,
8619 TWHILE,
8620 TBEGIN,
8621 TEND
8622};
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008623typedef smallint token_id_t;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008624
Denys Vlasenko888527c2016-10-02 16:54:17 +02008625/* Nth bit indicates if token marks the end of a list */
8626enum {
8627 tokendlist = 0
8628 /* 0 */ | (1u << TEOF)
8629 /* 1 */ | (0u << TNL)
8630 /* 2 */ | (0u << TREDIR)
8631 /* 3 */ | (0u << TWORD)
8632 /* 4 */ | (0u << TSEMI)
8633 /* 5 */ | (0u << TBACKGND)
8634 /* 6 */ | (0u << TAND)
8635 /* 7 */ | (0u << TOR)
8636 /* 8 */ | (0u << TPIPE)
8637 /* 9 */ | (0u << TLP)
8638 /* 10 */ | (1u << TRP)
8639 /* 11 */ | (1u << TENDCASE)
8640 /* 12 */ | (1u << TENDBQUOTE)
8641 /* 13 */ | (0u << TNOT)
8642 /* 14 */ | (0u << TCASE)
8643 /* 15 */ | (1u << TDO)
8644 /* 16 */ | (1u << TDONE)
8645 /* 17 */ | (1u << TELIF)
8646 /* 18 */ | (1u << TELSE)
8647 /* 19 */ | (1u << TESAC)
8648 /* 20 */ | (1u << TFI)
8649 /* 21 */ | (0u << TFOR)
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01008650#if BASH_FUNCTION
Denys Vlasenko888527c2016-10-02 16:54:17 +02008651 /* 22 */ | (0u << TFUNCTION)
Denys Vlasenko80729a42016-10-02 22:33:15 +02008652#endif
Denys Vlasenko888527c2016-10-02 16:54:17 +02008653 /* 23 */ | (0u << TIF)
8654 /* 24 */ | (0u << TIN)
8655 /* 25 */ | (1u << TTHEN)
8656 /* 26 */ | (0u << TUNTIL)
8657 /* 27 */ | (0u << TWHILE)
8658 /* 28 */ | (0u << TBEGIN)
8659 /* 29 */ | (1u << TEND)
8660 , /* thus far 29 bits used */
8661};
8662
Denys Vlasenko965b7952020-11-30 13:03:03 +01008663static const char *const tokname_array[] ALIGN_PTR = {
Denys Vlasenko888527c2016-10-02 16:54:17 +02008664 "end of file",
8665 "newline",
8666 "redirection",
8667 "word",
8668 ";",
8669 "&",
8670 "&&",
8671 "||",
8672 "|",
8673 "(",
8674 ")",
8675 ";;",
8676 "`",
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008677#define KWDOFFSET 13
8678 /* the following are keywords */
Denys Vlasenko888527c2016-10-02 16:54:17 +02008679 "!",
8680 "case",
8681 "do",
8682 "done",
8683 "elif",
8684 "else",
8685 "esac",
8686 "fi",
8687 "for",
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01008688#if BASH_FUNCTION
Denys Vlasenko888527c2016-10-02 16:54:17 +02008689 "function",
Ron Yorston95ebcf72015-11-03 09:42:23 +00008690#endif
Denys Vlasenko888527c2016-10-02 16:54:17 +02008691 "if",
8692 "in",
8693 "then",
8694 "until",
8695 "while",
8696 "{",
8697 "}",
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008698};
8699
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008700/* Wrapper around strcmp for qsort/bsearch/... */
8701static int
8702pstrcmp(const void *a, const void *b)
8703{
Denys Vlasenko888527c2016-10-02 16:54:17 +02008704 return strcmp((char*)a, *(char**)b);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008705}
8706
8707static const char *const *
8708findkwd(const char *s)
8709{
8710 return bsearch(s, tokname_array + KWDOFFSET,
Denis Vlasenko80b8b392007-06-25 10:55:35 +00008711 ARRAY_SIZE(tokname_array) - KWDOFFSET,
8712 sizeof(tokname_array[0]), pstrcmp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008713}
8714
8715/*
8716 * Locate and print what a word is...
8717 */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008718static int
Ron Yorston3f221112015-08-03 13:47:33 +01008719describe_command(char *command, const char *path, int describe_command_verbose)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008720{
8721 struct cmdentry entry;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008722#if ENABLE_ASH_ALIAS
8723 const struct alias *ap;
8724#endif
Ron Yorston3f221112015-08-03 13:47:33 +01008725
8726 path = path ? path : pathval();
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008727
8728 if (describe_command_verbose) {
8729 out1str(command);
8730 }
8731
8732 /* First look at the keywords */
8733 if (findkwd(command)) {
8734 out1str(describe_command_verbose ? " is a shell keyword" : command);
8735 goto out;
8736 }
8737
8738#if ENABLE_ASH_ALIAS
8739 /* Then look at the aliases */
8740 ap = lookupalias(command, 0);
8741 if (ap != NULL) {
Denis Vlasenko46846e22007-05-20 13:08:31 +00008742 if (!describe_command_verbose) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008743 out1str("alias ");
8744 printalias(ap);
8745 return 0;
8746 }
Denis Vlasenko46846e22007-05-20 13:08:31 +00008747 out1fmt(" is an alias for %s", ap->val);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008748 goto out;
8749 }
8750#endif
Youfu Zhang6683d1c2017-05-26 15:31:29 +08008751 /* Brute force */
8752 find_command(command, &entry, DO_ABS, path);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008753
8754 switch (entry.cmdtype) {
8755 case CMDNORMAL: {
8756 int j = entry.u.index;
8757 char *p;
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00008758 if (j < 0) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008759 p = command;
8760 } else {
8761 do {
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +01008762 padvance(&path, command);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008763 } while (--j >= 0);
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +01008764 p = stackblock();
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008765 }
8766 if (describe_command_verbose) {
Youfu Zhang6683d1c2017-05-26 15:31:29 +08008767 out1fmt(" is %s", p);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008768 } else {
8769 out1str(p);
8770 }
8771 break;
8772 }
8773
8774 case CMDFUNCTION:
8775 if (describe_command_verbose) {
Denys Vlasenko63c42af2018-07-24 17:08:04 +02008776 /*out1str(" is a shell function");*/
8777 out1str(" is a function"); /* bash says this */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008778 } else {
8779 out1str(command);
8780 }
8781 break;
8782
8783 case CMDBUILTIN:
8784 if (describe_command_verbose) {
8785 out1fmt(" is a %sshell builtin",
8786 IS_BUILTIN_SPECIAL(entry.u.cmd) ?
8787 "special " : nullstr
8788 );
8789 } else {
8790 out1str(command);
8791 }
8792 break;
8793
8794 default:
8795 if (describe_command_verbose) {
8796 out1str(": not found\n");
8797 }
8798 return 127;
8799 }
8800 out:
Denys Vlasenko285ad152009-12-04 23:02:27 +01008801 out1str("\n");
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008802 return 0;
8803}
8804
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008805static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008806typecmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008807{
Denis Vlasenko46846e22007-05-20 13:08:31 +00008808 int i = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008809 int err = 0;
Denis Vlasenko46846e22007-05-20 13:08:31 +00008810 int verbose = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008811
Denis Vlasenko46846e22007-05-20 13:08:31 +00008812 /* type -p ... ? (we don't bother checking for 'p') */
Denis Vlasenko1fc62382007-06-25 22:55:34 +00008813 if (argv[1] && argv[1][0] == '-') {
Denis Vlasenko46846e22007-05-20 13:08:31 +00008814 i++;
8815 verbose = 0;
8816 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00008817 while (argv[i]) {
Ron Yorston3f221112015-08-03 13:47:33 +01008818 err |= describe_command(argv[i++], NULL, verbose);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008819 }
8820 return err;
8821}
8822
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +01008823static struct strlist *
8824fill_arglist(struct arglist *arglist, union node **argpp)
Denys Vlasenkocac4d002016-10-01 03:02:25 +02008825{
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +01008826 struct strlist **lastp = arglist->lastp;
8827 union node *argp;
8828
8829 while ((argp = *argpp) != NULL) {
8830 expandarg(argp, arglist, EXP_FULL | EXP_TILDE);
8831 *argpp = argp->narg.next;
8832 if (*lastp)
8833 break;
8834 }
8835
8836 return *lastp;
8837}
8838
Ron Yorstonda7a6db2020-02-27 09:50:18 +00008839#if ENABLE_ASH_CMDCMD
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +01008840/* Is it "command [-p] PROG ARGS" bltin, no other opts? Return ptr to "PROG" if yes */
8841static int
8842parse_command_args(struct arglist *arglist, union node **argpp, const char **path)
8843{
8844 struct strlist *sp = arglist->list;
Denys Vlasenkocac4d002016-10-01 03:02:25 +02008845 char *cp, c;
8846
8847 for (;;) {
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +01008848 sp = sp->next ? sp->next : fill_arglist(arglist, argpp);
8849 if (!sp)
8850 return 0;
8851 cp = sp->text;
Denys Vlasenkocac4d002016-10-01 03:02:25 +02008852 if (*cp++ != '-')
8853 break;
8854 c = *cp++;
8855 if (!c)
8856 break;
8857 if (c == '-' && !*cp) {
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +01008858 if (!sp->next && !fill_arglist(arglist, argpp))
8859 return 0;
8860 sp = sp->next;
Denys Vlasenkocac4d002016-10-01 03:02:25 +02008861 break;
8862 }
8863 do {
8864 switch (c) {
8865 case 'p':
8866 *path = bb_default_path;
8867 break;
8868 default:
8869 /* run 'typecmd' for other options */
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +01008870 return 0;
Denys Vlasenkocac4d002016-10-01 03:02:25 +02008871 }
8872 c = *cp++;
8873 } while (c);
8874 }
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +01008875
8876 arglist->list = sp;
8877 return DO_NOFUNC;
Denys Vlasenkocac4d002016-10-01 03:02:25 +02008878}
8879
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008880static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008881commandcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008882{
Denys Vlasenkocac4d002016-10-01 03:02:25 +02008883 char *cmd;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008884 int c;
8885 enum {
8886 VERIFY_BRIEF = 1,
8887 VERIFY_VERBOSE = 2,
8888 } verify = 0;
Ron Yorston3f221112015-08-03 13:47:33 +01008889 const char *path = NULL;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008890
Denys Vlasenkocac4d002016-10-01 03:02:25 +02008891 /* "command [-p] PROG ARGS" (that is, without -V or -v)
8892 * never reaches this function.
8893 */
8894
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008895 while ((c = nextopt("pvV")) != '\0')
8896 if (c == 'V')
8897 verify |= VERIFY_VERBOSE;
8898 else if (c == 'v')
Denys Vlasenkocac4d002016-10-01 03:02:25 +02008899 /*verify |= VERIFY_BRIEF*/;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008900#if DEBUG
8901 else if (c != 'p')
8902 abort();
8903#endif
Ron Yorston3f221112015-08-03 13:47:33 +01008904 else
8905 path = bb_default_path;
Denys Vlasenkocac4d002016-10-01 03:02:25 +02008906
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00008907 /* Mimic bash: just "command -v" doesn't complain, it's a nop */
Denys Vlasenkocac4d002016-10-01 03:02:25 +02008908 cmd = *argptr;
8909 if (/*verify && */ cmd)
8910 return describe_command(cmd, path, verify /* - VERIFY_BRIEF*/);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008911
8912 return 0;
8913}
8914#endif
8915
8916
Denys Vlasenko4c438b52016-10-07 04:05:15 +02008917/*static int funcblocksize; // size of structures in function */
8918/*static int funcstringsize; // size of strings in node */
Denis Vlasenko340299a2008-11-21 10:36:36 +00008919static void *funcblock; /* block to allocate function from */
Denys Vlasenko4c438b52016-10-07 04:05:15 +02008920static char *funcstring_end; /* end of block to allocate strings from */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008921
Denys Vlasenko3e134eb2016-04-22 18:09:21 +02008922static const uint8_t nodesize[N_NUMBER] ALIGN1 = {
Denis Vlasenko340299a2008-11-21 10:36:36 +00008923 [NCMD ] = SHELL_ALIGN(sizeof(struct ncmd)),
8924 [NPIPE ] = SHELL_ALIGN(sizeof(struct npipe)),
8925 [NREDIR ] = SHELL_ALIGN(sizeof(struct nredir)),
8926 [NBACKGND ] = SHELL_ALIGN(sizeof(struct nredir)),
8927 [NSUBSHELL] = SHELL_ALIGN(sizeof(struct nredir)),
8928 [NAND ] = SHELL_ALIGN(sizeof(struct nbinary)),
8929 [NOR ] = SHELL_ALIGN(sizeof(struct nbinary)),
8930 [NSEMI ] = SHELL_ALIGN(sizeof(struct nbinary)),
8931 [NIF ] = SHELL_ALIGN(sizeof(struct nif)),
8932 [NWHILE ] = SHELL_ALIGN(sizeof(struct nbinary)),
8933 [NUNTIL ] = SHELL_ALIGN(sizeof(struct nbinary)),
8934 [NFOR ] = SHELL_ALIGN(sizeof(struct nfor)),
8935 [NCASE ] = SHELL_ALIGN(sizeof(struct ncase)),
8936 [NCLIST ] = SHELL_ALIGN(sizeof(struct nclist)),
8937 [NDEFUN ] = SHELL_ALIGN(sizeof(struct narg)),
8938 [NARG ] = SHELL_ALIGN(sizeof(struct narg)),
8939 [NTO ] = SHELL_ALIGN(sizeof(struct nfile)),
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01008940#if BASH_REDIR_OUTPUT
Denis Vlasenko340299a2008-11-21 10:36:36 +00008941 [NTO2 ] = SHELL_ALIGN(sizeof(struct nfile)),
Denis Vlasenkocc5feab2008-11-22 01:32:40 +00008942#endif
Denis Vlasenko340299a2008-11-21 10:36:36 +00008943 [NCLOBBER ] = SHELL_ALIGN(sizeof(struct nfile)),
8944 [NFROM ] = SHELL_ALIGN(sizeof(struct nfile)),
8945 [NFROMTO ] = SHELL_ALIGN(sizeof(struct nfile)),
8946 [NAPPEND ] = SHELL_ALIGN(sizeof(struct nfile)),
8947 [NTOFD ] = SHELL_ALIGN(sizeof(struct ndup)),
8948 [NFROMFD ] = SHELL_ALIGN(sizeof(struct ndup)),
8949 [NHERE ] = SHELL_ALIGN(sizeof(struct nhere)),
8950 [NXHERE ] = SHELL_ALIGN(sizeof(struct nhere)),
8951 [NNOT ] = SHELL_ALIGN(sizeof(struct nnot)),
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008952};
8953
Denys Vlasenko4c438b52016-10-07 04:05:15 +02008954static int calcsize(int funcblocksize, union node *n);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008955
Denys Vlasenko4c438b52016-10-07 04:05:15 +02008956static int
8957sizenodelist(int funcblocksize, struct nodelist *lp)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008958{
8959 while (lp) {
8960 funcblocksize += SHELL_ALIGN(sizeof(struct nodelist));
Denys Vlasenko4c438b52016-10-07 04:05:15 +02008961 funcblocksize = calcsize(funcblocksize, lp->n);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008962 lp = lp->next;
8963 }
Denys Vlasenko4c438b52016-10-07 04:05:15 +02008964 return funcblocksize;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008965}
8966
Denys Vlasenko4c438b52016-10-07 04:05:15 +02008967static int
8968calcsize(int funcblocksize, union node *n)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008969{
8970 if (n == NULL)
Denys Vlasenko4c438b52016-10-07 04:05:15 +02008971 return funcblocksize;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008972 funcblocksize += nodesize[n->type];
8973 switch (n->type) {
8974 case NCMD:
Denys Vlasenko4c438b52016-10-07 04:05:15 +02008975 funcblocksize = calcsize(funcblocksize, n->ncmd.redirect);
8976 funcblocksize = calcsize(funcblocksize, n->ncmd.args);
8977 funcblocksize = calcsize(funcblocksize, n->ncmd.assign);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008978 break;
8979 case NPIPE:
Denys Vlasenko4c438b52016-10-07 04:05:15 +02008980 funcblocksize = sizenodelist(funcblocksize, n->npipe.cmdlist);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008981 break;
8982 case NREDIR:
8983 case NBACKGND:
8984 case NSUBSHELL:
Denys Vlasenko4c438b52016-10-07 04:05:15 +02008985 funcblocksize = calcsize(funcblocksize, n->nredir.redirect);
8986 funcblocksize = calcsize(funcblocksize, n->nredir.n);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008987 break;
8988 case NAND:
8989 case NOR:
8990 case NSEMI:
8991 case NWHILE:
8992 case NUNTIL:
Denys Vlasenko4c438b52016-10-07 04:05:15 +02008993 funcblocksize = calcsize(funcblocksize, n->nbinary.ch2);
8994 funcblocksize = calcsize(funcblocksize, n->nbinary.ch1);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008995 break;
8996 case NIF:
Denys Vlasenko4c438b52016-10-07 04:05:15 +02008997 funcblocksize = calcsize(funcblocksize, n->nif.elsepart);
8998 funcblocksize = calcsize(funcblocksize, n->nif.ifpart);
8999 funcblocksize = calcsize(funcblocksize, n->nif.test);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009000 break;
9001 case NFOR:
Denys Vlasenko561639a2016-10-07 04:28:33 +02009002 funcblocksize += SHELL_ALIGN(strlen(n->nfor.var) + 1); /* was funcstringsize += ... */
Denys Vlasenko4c438b52016-10-07 04:05:15 +02009003 funcblocksize = calcsize(funcblocksize, n->nfor.body);
9004 funcblocksize = calcsize(funcblocksize, n->nfor.args);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009005 break;
9006 case NCASE:
Denys Vlasenko4c438b52016-10-07 04:05:15 +02009007 funcblocksize = calcsize(funcblocksize, n->ncase.cases);
9008 funcblocksize = calcsize(funcblocksize, n->ncase.expr);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009009 break;
9010 case NCLIST:
Denys Vlasenko4c438b52016-10-07 04:05:15 +02009011 funcblocksize = calcsize(funcblocksize, n->nclist.body);
9012 funcblocksize = calcsize(funcblocksize, n->nclist.pattern);
9013 funcblocksize = calcsize(funcblocksize, n->nclist.next);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009014 break;
9015 case NDEFUN:
Denys Vlasenko675d24a2018-01-27 22:02:05 +01009016 funcblocksize = calcsize(funcblocksize, n->ndefun.body);
9017 funcblocksize += SHELL_ALIGN(strlen(n->ndefun.text) + 1);
9018 break;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009019 case NARG:
Denys Vlasenko4c438b52016-10-07 04:05:15 +02009020 funcblocksize = sizenodelist(funcblocksize, n->narg.backquote);
Denys Vlasenko561639a2016-10-07 04:28:33 +02009021 funcblocksize += SHELL_ALIGN(strlen(n->narg.text) + 1); /* was funcstringsize += ... */
Denys Vlasenko4c438b52016-10-07 04:05:15 +02009022 funcblocksize = calcsize(funcblocksize, n->narg.next);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009023 break;
9024 case NTO:
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01009025#if BASH_REDIR_OUTPUT
Denis Vlasenko559691a2008-10-05 18:39:31 +00009026 case NTO2:
9027#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009028 case NCLOBBER:
9029 case NFROM:
9030 case NFROMTO:
9031 case NAPPEND:
Denys Vlasenko4c438b52016-10-07 04:05:15 +02009032 funcblocksize = calcsize(funcblocksize, n->nfile.fname);
9033 funcblocksize = calcsize(funcblocksize, n->nfile.next);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009034 break;
9035 case NTOFD:
9036 case NFROMFD:
Denys Vlasenko4c438b52016-10-07 04:05:15 +02009037 funcblocksize = calcsize(funcblocksize, n->ndup.vname);
9038 funcblocksize = calcsize(funcblocksize, n->ndup.next);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009039 break;
9040 case NHERE:
9041 case NXHERE:
Denys Vlasenko4c438b52016-10-07 04:05:15 +02009042 funcblocksize = calcsize(funcblocksize, n->nhere.doc);
9043 funcblocksize = calcsize(funcblocksize, n->nhere.next);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009044 break;
9045 case NNOT:
Denys Vlasenko4c438b52016-10-07 04:05:15 +02009046 funcblocksize = calcsize(funcblocksize, n->nnot.com);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009047 break;
9048 };
Denys Vlasenko4c438b52016-10-07 04:05:15 +02009049 return funcblocksize;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009050}
9051
9052static char *
9053nodeckstrdup(char *s)
9054{
Denys Vlasenko561639a2016-10-07 04:28:33 +02009055 funcstring_end -= SHELL_ALIGN(strlen(s) + 1);
Denys Vlasenko4c438b52016-10-07 04:05:15 +02009056 return strcpy(funcstring_end, s);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009057}
9058
9059static union node *copynode(union node *);
9060
9061static struct nodelist *
9062copynodelist(struct nodelist *lp)
9063{
9064 struct nodelist *start;
9065 struct nodelist **lpp;
9066
9067 lpp = &start;
9068 while (lp) {
9069 *lpp = funcblock;
9070 funcblock = (char *) funcblock + SHELL_ALIGN(sizeof(struct nodelist));
9071 (*lpp)->n = copynode(lp->n);
9072 lp = lp->next;
9073 lpp = &(*lpp)->next;
9074 }
9075 *lpp = NULL;
9076 return start;
9077}
9078
9079static union node *
9080copynode(union node *n)
9081{
9082 union node *new;
9083
9084 if (n == NULL)
9085 return NULL;
9086 new = funcblock;
9087 funcblock = (char *) funcblock + nodesize[n->type];
9088
9089 switch (n->type) {
9090 case NCMD:
9091 new->ncmd.redirect = copynode(n->ncmd.redirect);
9092 new->ncmd.args = copynode(n->ncmd.args);
9093 new->ncmd.assign = copynode(n->ncmd.assign);
Denys Vlasenko675d24a2018-01-27 22:02:05 +01009094 new->ncmd.linno = n->ncmd.linno;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009095 break;
9096 case NPIPE:
9097 new->npipe.cmdlist = copynodelist(n->npipe.cmdlist);
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00009098 new->npipe.pipe_backgnd = n->npipe.pipe_backgnd;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009099 break;
9100 case NREDIR:
9101 case NBACKGND:
9102 case NSUBSHELL:
9103 new->nredir.redirect = copynode(n->nredir.redirect);
9104 new->nredir.n = copynode(n->nredir.n);
Denys Vlasenko675d24a2018-01-27 22:02:05 +01009105 new->nredir.linno = n->nredir.linno;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009106 break;
9107 case NAND:
9108 case NOR:
9109 case NSEMI:
9110 case NWHILE:
9111 case NUNTIL:
9112 new->nbinary.ch2 = copynode(n->nbinary.ch2);
9113 new->nbinary.ch1 = copynode(n->nbinary.ch1);
9114 break;
9115 case NIF:
9116 new->nif.elsepart = copynode(n->nif.elsepart);
9117 new->nif.ifpart = copynode(n->nif.ifpart);
9118 new->nif.test = copynode(n->nif.test);
9119 break;
9120 case NFOR:
9121 new->nfor.var = nodeckstrdup(n->nfor.var);
9122 new->nfor.body = copynode(n->nfor.body);
9123 new->nfor.args = copynode(n->nfor.args);
Denys Vlasenko675d24a2018-01-27 22:02:05 +01009124 new->nfor.linno = n->nfor.linno;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009125 break;
9126 case NCASE:
9127 new->ncase.cases = copynode(n->ncase.cases);
9128 new->ncase.expr = copynode(n->ncase.expr);
Denys Vlasenko675d24a2018-01-27 22:02:05 +01009129 new->ncase.linno = n->ncase.linno;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009130 break;
9131 case NCLIST:
9132 new->nclist.body = copynode(n->nclist.body);
9133 new->nclist.pattern = copynode(n->nclist.pattern);
9134 new->nclist.next = copynode(n->nclist.next);
9135 break;
9136 case NDEFUN:
Denys Vlasenko675d24a2018-01-27 22:02:05 +01009137 new->ndefun.body = copynode(n->ndefun.body);
9138 new->ndefun.text = nodeckstrdup(n->ndefun.text);
9139 new->ndefun.linno = n->ndefun.linno;
9140 break;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009141 case NARG:
9142 new->narg.backquote = copynodelist(n->narg.backquote);
9143 new->narg.text = nodeckstrdup(n->narg.text);
9144 new->narg.next = copynode(n->narg.next);
9145 break;
9146 case NTO:
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01009147#if BASH_REDIR_OUTPUT
Denis Vlasenko559691a2008-10-05 18:39:31 +00009148 case NTO2:
9149#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009150 case NCLOBBER:
9151 case NFROM:
9152 case NFROMTO:
9153 case NAPPEND:
9154 new->nfile.fname = copynode(n->nfile.fname);
9155 new->nfile.fd = n->nfile.fd;
9156 new->nfile.next = copynode(n->nfile.next);
9157 break;
9158 case NTOFD:
9159 case NFROMFD:
9160 new->ndup.vname = copynode(n->ndup.vname);
9161 new->ndup.dupfd = n->ndup.dupfd;
9162 new->ndup.fd = n->ndup.fd;
9163 new->ndup.next = copynode(n->ndup.next);
9164 break;
9165 case NHERE:
9166 case NXHERE:
9167 new->nhere.doc = copynode(n->nhere.doc);
9168 new->nhere.fd = n->nhere.fd;
9169 new->nhere.next = copynode(n->nhere.next);
9170 break;
9171 case NNOT:
9172 new->nnot.com = copynode(n->nnot.com);
9173 break;
9174 };
9175 new->type = n->type;
9176 return new;
9177}
9178
9179/*
9180 * Make a copy of a parse tree.
9181 */
9182static struct funcnode *
9183copyfunc(union node *n)
9184{
9185 struct funcnode *f;
9186 size_t blocksize;
9187
Denys Vlasenko4c438b52016-10-07 04:05:15 +02009188 /*funcstringsize = 0;*/
9189 blocksize = offsetof(struct funcnode, n) + calcsize(0, n);
9190 f = ckzalloc(blocksize /* + funcstringsize */);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009191 funcblock = (char *) f + offsetof(struct funcnode, n);
Denys Vlasenko4c438b52016-10-07 04:05:15 +02009192 funcstring_end = (char *) f + blocksize;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009193 copynode(n);
Denys Vlasenko4c438b52016-10-07 04:05:15 +02009194 /* f->count = 0; - ckzalloc did it */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009195 return f;
9196}
9197
9198/*
9199 * Define a shell function.
9200 */
9201static void
Denys Vlasenko7aec8682016-10-25 20:26:02 +02009202defun(union node *func)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009203{
9204 struct cmdentry entry;
9205
9206 INT_OFF;
9207 entry.cmdtype = CMDFUNCTION;
9208 entry.u.func = copyfunc(func);
Denys Vlasenko675d24a2018-01-27 22:02:05 +01009209 addcmdentry(func->ndefun.text, &entry);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009210 INT_ON;
9211}
9212
Denis Vlasenko4b875702009-03-19 13:30:04 +00009213/* Reasons for skipping commands (see comment on breakcmd routine) */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009214#define SKIPBREAK (1 << 0)
9215#define SKIPCONT (1 << 1)
9216#define SKIPFUNC (1 << 2)
Denys Vlasenkocd24a502020-02-20 16:47:01 +01009217#define SKIPFUNCDEF (1 << 3)
Denis Vlasenko4b875702009-03-19 13:30:04 +00009218static smallint evalskip; /* set to SKIPxxx if we are skipping commands */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009219static int skipcount; /* number of levels to skip */
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00009220static int loopnest; /* current loop nesting level */
Denys Vlasenko675d24a2018-01-27 22:02:05 +01009221static int funcline; /* starting line number of current function, or 0 if not in a function */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009222
Denis Vlasenko4b875702009-03-19 13:30:04 +00009223/* Forward decl way out to parsing code - dotrap needs it */
Denys Vlasenko7b3fa1e2016-10-01 15:10:16 +02009224static int evalstring(char *s, int flags);
Denis Vlasenkofc06f292007-02-23 21:09:35 +00009225
Denis Vlasenko4b875702009-03-19 13:30:04 +00009226/* Called to execute a trap.
9227 * Single callsite - at the end of evaltree().
Denys Vlasenkob563f622010-09-25 17:15:13 +02009228 * If we return non-zero, evaltree raises EXEXIT exception.
Denis Vlasenko4b875702009-03-19 13:30:04 +00009229 *
9230 * Perhaps we should avoid entering new trap handlers
9231 * while we are executing a trap handler. [is it a TODO?]
Denis Vlasenkofc06f292007-02-23 21:09:35 +00009232 */
Denys Vlasenkob98b4c12016-10-01 23:25:12 +02009233static void
Denis Vlasenkofc06f292007-02-23 21:09:35 +00009234dotrap(void)
9235{
Denis Vlasenko4b875702009-03-19 13:30:04 +00009236 uint8_t *g;
9237 int sig;
Denys Vlasenko4ccddc82020-02-14 17:27:18 +01009238 int status, last_status;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00009239
Denys Vlasenkob98b4c12016-10-01 23:25:12 +02009240 if (!pending_sig)
9241 return;
9242
Denys Vlasenko4ccddc82020-02-14 17:27:18 +01009243 status = savestatus;
9244 last_status = status;
9245 if (status < 0) {
9246 status = exitstatus;
9247 savestatus = status;
9248 }
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02009249 pending_sig = 0;
Denys Vlasenkode892052016-10-02 01:49:13 +02009250 barrier();
Denis Vlasenkofc06f292007-02-23 21:09:35 +00009251
Denis Vlasenko653d8e72009-03-19 21:59:35 +00009252 TRACE(("dotrap entered\n"));
Denis Vlasenko4b875702009-03-19 13:30:04 +00009253 for (sig = 1, g = gotsig; sig < NSIG; sig++, g++) {
Denys Vlasenkob98b4c12016-10-01 23:25:12 +02009254 char *p;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00009255
Denys Vlasenkob98b4c12016-10-01 23:25:12 +02009256 if (!*g)
Denis Vlasenkofc06f292007-02-23 21:09:35 +00009257 continue;
Denys Vlasenkob98b4c12016-10-01 23:25:12 +02009258
9259 if (evalskip) {
9260 pending_sig = sig;
9261 break;
9262 }
9263
9264 p = trap[sig];
Denis Vlasenko4b875702009-03-19 13:30:04 +00009265 /* non-trapped SIGINT is handled separately by raise_interrupt,
9266 * don't upset it by resetting gotsig[SIGINT-1] */
Denys Vlasenkob98b4c12016-10-01 23:25:12 +02009267 if (sig == SIGINT && !p)
Denis Vlasenko4b875702009-03-19 13:30:04 +00009268 continue;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00009269
Denys Vlasenkob98b4c12016-10-01 23:25:12 +02009270 TRACE(("sig %d is active, will run handler '%s'\n", sig, p));
Denis Vlasenko4b875702009-03-19 13:30:04 +00009271 *g = 0;
Denys Vlasenkob98b4c12016-10-01 23:25:12 +02009272 if (!p)
Denis Vlasenko4b875702009-03-19 13:30:04 +00009273 continue;
Roberto A. Fogliettae0bf3df2021-09-07 01:19:31 +02009274 trap_depth++;
Denys Vlasenkob98b4c12016-10-01 23:25:12 +02009275 evalstring(p, 0);
Roberto A. Fogliettae0bf3df2021-09-07 01:19:31 +02009276 trap_depth--;
Denys Vlasenkocd24a502020-02-20 16:47:01 +01009277 if (evalskip != SKIPFUNC)
9278 exitstatus = status;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00009279 }
Denys Vlasenko4ccddc82020-02-14 17:27:18 +01009280
9281 savestatus = last_status;
Denys Vlasenkob98b4c12016-10-01 23:25:12 +02009282 TRACE(("dotrap returns\n"));
Denis Vlasenkofc06f292007-02-23 21:09:35 +00009283}
9284
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009285/* forward declarations - evaluation is fairly recursive business... */
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009286static int evalloop(union node *, int);
9287static int evalfor(union node *, int);
9288static int evalcase(union node *, int);
9289static int evalsubshell(union node *, int);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009290static void expredir(union node *);
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009291static int evalpipe(union node *, int);
9292static int evalcommand(union node *, int);
Denys Vlasenko7b3fa1e2016-10-01 15:10:16 +02009293static int evalbltin(const struct builtincmd *, int, char **, int);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009294static void prehash(union node *);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009295
Eric Andersen62483552001-07-10 06:09:16 +00009296/*
Eric Andersenc470f442003-07-28 09:56:35 +00009297 * Evaluate a parse tree. The value is left in the global variable
9298 * exitstatus.
Eric Andersen62483552001-07-10 06:09:16 +00009299 */
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009300static int
Eric Andersenc470f442003-07-28 09:56:35 +00009301evaltree(union node *n, int flags)
Eric Andersen62483552001-07-10 06:09:16 +00009302{
Eric Andersenc470f442003-07-28 09:56:35 +00009303 int checkexit = 0;
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009304 int (*evalfn)(union node *, int);
Ron Yorstonf55161a2019-02-25 08:29:38 +00009305 struct stackmark smark;
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009306 int status = 0;
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00009307
Ron Yorstonf55161a2019-02-25 08:29:38 +00009308 setstackmark(&smark);
9309
Denys Vlasenko41beb532021-09-07 01:52:21 +02009310 if (nflag)
9311 goto out;
9312
Eric Andersenc470f442003-07-28 09:56:35 +00009313 if (n == NULL) {
9314 TRACE(("evaltree(NULL) called\n"));
Denys Vlasenkoc0663c72016-10-27 21:09:01 +02009315 goto out;
Eric Andersen62483552001-07-10 06:09:16 +00009316 }
Denis Vlasenko653d8e72009-03-19 21:59:35 +00009317 TRACE(("evaltree(%p: %d, %d) called\n", n, n->type, flags));
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00009318
Denys Vlasenkob98b4c12016-10-01 23:25:12 +02009319 dotrap();
9320
Eric Andersenc470f442003-07-28 09:56:35 +00009321 switch (n->type) {
9322 default:
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00009323#if DEBUG
Eric Andersenc470f442003-07-28 09:56:35 +00009324 out1fmt("Node type = %d\n", n->type);
Denys Vlasenko8131eea2009-11-02 14:19:51 +01009325 fflush_all();
Eric Andersenc470f442003-07-28 09:56:35 +00009326 break;
9327#endif
9328 case NNOT:
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009329 status = !evaltree(n->nnot.com, EV_TESTED);
Eric Andersenc470f442003-07-28 09:56:35 +00009330 goto setstatus;
9331 case NREDIR:
Denys Vlasenko675d24a2018-01-27 22:02:05 +01009332 errlinno = lineno = n->nredir.linno;
Eric Andersenc470f442003-07-28 09:56:35 +00009333 expredir(n->nredir.redirect);
Denys Vlasenko1c79aeb2017-07-29 22:51:52 +02009334 pushredir(n->nredir.redirect);
Eric Andersenc470f442003-07-28 09:56:35 +00009335 status = redirectsafe(n->nredir.redirect, REDIR_PUSH);
9336 if (!status) {
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009337 status = evaltree(n->nredir.n, flags & EV_TESTED);
Eric Andersenc470f442003-07-28 09:56:35 +00009338 }
Denys Vlasenkoeaf94362016-10-25 21:46:03 +02009339 if (n->nredir.redirect)
Denys Vlasenko035486c2017-07-31 04:09:19 +02009340 popredir(/*drop:*/ 0);
Eric Andersenc470f442003-07-28 09:56:35 +00009341 goto setstatus;
9342 case NCMD:
9343 evalfn = evalcommand;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009344 checkexit:
Denys Vlasenkof415e212021-09-07 01:54:23 +02009345 checkexit = ~flags & EV_TESTED;
Eric Andersenc470f442003-07-28 09:56:35 +00009346 goto calleval;
9347 case NFOR:
9348 evalfn = evalfor;
9349 goto calleval;
9350 case NWHILE:
9351 case NUNTIL:
9352 evalfn = evalloop;
9353 goto calleval;
9354 case NSUBSHELL:
9355 case NBACKGND:
9356 evalfn = evalsubshell;
Denys Vlasenkocf98b0c2016-10-25 18:19:39 +02009357 goto checkexit;
Eric Andersenc470f442003-07-28 09:56:35 +00009358 case NPIPE:
9359 evalfn = evalpipe;
9360 goto checkexit;
9361 case NCASE:
9362 evalfn = evalcase;
9363 goto calleval;
9364 case NAND:
9365 case NOR:
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00009366 case NSEMI: {
Eric Andersenc470f442003-07-28 09:56:35 +00009367#if NAND + 1 != NOR
9368#error NAND + 1 != NOR
9369#endif
9370#if NOR + 1 != NSEMI
9371#error NOR + 1 != NSEMI
9372#endif
Denis Vlasenko87d5fd92008-07-26 13:48:35 +00009373 unsigned is_or = n->type - NAND;
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009374 status = evaltree(
Eric Andersenc470f442003-07-28 09:56:35 +00009375 n->nbinary.ch1,
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00009376 (flags | ((is_or >> 1) - 1)) & EV_TESTED
Eric Andersenc470f442003-07-28 09:56:35 +00009377 );
Denys Vlasenkobc1a0082016-10-02 15:31:33 +02009378 if ((!status) == is_or || evalskip)
Eric Andersenc470f442003-07-28 09:56:35 +00009379 break;
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009380 n = n->nbinary.ch2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009381 evaln:
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009382 evalfn = evaltree;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009383 calleval:
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009384 status = evalfn(n, flags);
9385 goto setstatus;
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00009386 }
Eric Andersenc470f442003-07-28 09:56:35 +00009387 case NIF:
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009388 status = evaltree(n->nif.test, EV_TESTED);
Eric Andersenc470f442003-07-28 09:56:35 +00009389 if (evalskip)
9390 break;
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009391 if (!status) {
Eric Andersenc470f442003-07-28 09:56:35 +00009392 n = n->nif.ifpart;
9393 goto evaln;
Denys Vlasenkof415e212021-09-07 01:54:23 +02009394 } else if (n->nif.elsepart) {
Eric Andersenc470f442003-07-28 09:56:35 +00009395 n = n->nif.elsepart;
9396 goto evaln;
9397 }
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009398 status = 0;
9399 goto setstatus;
Eric Andersenc470f442003-07-28 09:56:35 +00009400 case NDEFUN:
Denys Vlasenko7aec8682016-10-25 20:26:02 +02009401 defun(n);
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009402 /* Not necessary. To test it:
9403 * "false; f() { qwerty; }; echo $?" should print 0.
9404 */
9405 /* status = 0; */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009406 setstatus:
Eric Andersenc470f442003-07-28 09:56:35 +00009407 exitstatus = status;
9408 break;
9409 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009410 out:
Denys Vlasenkob563f622010-09-25 17:15:13 +02009411 /* Order of checks below is important:
9412 * signal handlers trigger before exit caused by "set -e".
9413 */
Denys Vlasenkob98b4c12016-10-01 23:25:12 +02009414 dotrap();
9415
Denys Vlasenkof415e212021-09-07 01:54:23 +02009416 if (checkexit && status) {
Roberto A. Fogliettae0bf3df2021-09-07 01:19:31 +02009417 if (trap[NTRAP_ERR] && !in_trap_ERR) {
9418 int err;
9419 struct jmploc *volatile savehandler = exception_handler;
9420 struct jmploc jmploc;
9421
9422 in_trap_ERR = 1;
9423 trap_depth++;
9424 err = setjmp(jmploc.loc);
9425 if (!err) {
9426 exception_handler = &jmploc;
9427 savestatus = exitstatus;
9428 evalstring(trap[NTRAP_ERR], 0);
9429 }
9430 trap_depth--;
9431 in_trap_ERR = 0;
9432
9433 exception_handler = savehandler;
9434 if (err && exception_type != EXERROR)
9435 longjmp(exception_handler->loc, 1);
9436
9437 exitstatus = savestatus;
9438 }
9439 if (eflag)
Denys Vlasenkof415e212021-09-07 01:54:23 +02009440 goto exexit;
Roberto A. Fogliettae0bf3df2021-09-07 01:19:31 +02009441 }
Denys Vlasenkof415e212021-09-07 01:54:23 +02009442 if (flags & EV_EXIT) {
9443 exexit:
Denys Vlasenkof977e002020-02-20 16:54:29 +01009444 raise_exception(EXEND);
Denys Vlasenkof415e212021-09-07 01:54:23 +02009445 }
Denis Vlasenko653d8e72009-03-19 21:59:35 +00009446
Ron Yorstonf55161a2019-02-25 08:29:38 +00009447 popstackmark(&smark);
Denis Vlasenko653d8e72009-03-19 21:59:35 +00009448 TRACE(("leaving evaltree (no interrupts)\n"));
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009449 return exitstatus;
Eric Andersen62483552001-07-10 06:09:16 +00009450}
9451
Denys Vlasenko37dc08b2016-10-02 04:38:07 +02009452static int
9453skiploop(void)
Denys Vlasenko35ec8182016-10-01 19:56:52 +02009454{
9455 int skip = evalskip;
9456
9457 switch (skip) {
9458 case 0:
9459 break;
9460 case SKIPBREAK:
9461 case SKIPCONT:
9462 if (--skipcount <= 0) {
9463 evalskip = 0;
9464 break;
9465 }
9466 skip = SKIPBREAK;
9467 break;
9468 }
9469 return skip;
9470}
9471
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009472static int
Eric Andersenc470f442003-07-28 09:56:35 +00009473evalloop(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00009474{
Denys Vlasenko35ec8182016-10-01 19:56:52 +02009475 int skip;
Eric Andersencb57d552001-06-28 07:25:16 +00009476 int status;
9477
9478 loopnest++;
9479 status = 0;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009480 flags &= EV_TESTED;
Denys Vlasenko35ec8182016-10-01 19:56:52 +02009481 do {
Eric Andersenc470f442003-07-28 09:56:35 +00009482 int i;
9483
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009484 i = evaltree(n->nbinary.ch1, EV_TESTED);
Denys Vlasenko35ec8182016-10-01 19:56:52 +02009485 skip = skiploop();
9486 if (skip == SKIPFUNC)
9487 status = i;
9488 if (skip)
9489 continue;
Eric Andersenc470f442003-07-28 09:56:35 +00009490 if (n->type != NWHILE)
9491 i = !i;
9492 if (i != 0)
9493 break;
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009494 status = evaltree(n->nbinary.ch2, flags);
Denys Vlasenko35ec8182016-10-01 19:56:52 +02009495 skip = skiploop();
9496 } while (!(skip & ~SKIPCONT));
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009497 loopnest--;
9498
9499 return status;
Eric Andersencb57d552001-06-28 07:25:16 +00009500}
9501
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009502static int
Eric Andersenc470f442003-07-28 09:56:35 +00009503evalfor(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00009504{
9505 struct arglist arglist;
9506 union node *argp;
9507 struct strlist *sp;
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009508 int status = 0;
Eric Andersencb57d552001-06-28 07:25:16 +00009509
Denys Vlasenko675d24a2018-01-27 22:02:05 +01009510 errlinno = lineno = n->ncase.linno;
Denys Vlasenko675d24a2018-01-27 22:02:05 +01009511
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00009512 arglist.list = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00009513 arglist.lastp = &arglist.list;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009514 for (argp = n->nfor.args; argp; argp = argp->narg.next) {
Ron Yorston549deab2015-05-18 09:57:51 +02009515 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
Eric Andersencb57d552001-06-28 07:25:16 +00009516 }
9517 *arglist.lastp = NULL;
9518
Eric Andersencb57d552001-06-28 07:25:16 +00009519 loopnest++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009520 flags &= EV_TESTED;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009521 for (sp = arglist.list; sp; sp = sp->next) {
Denys Vlasenko0a0acb52015-04-18 19:36:38 +02009522 setvar0(n->nfor.var, sp->text);
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009523 status = evaltree(n->nfor.body, flags);
Denys Vlasenko35ec8182016-10-01 19:56:52 +02009524 if (skiploop() & ~SKIPCONT)
Eric Andersencb57d552001-06-28 07:25:16 +00009525 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009526 }
9527 loopnest--;
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009528
9529 return status;
Eric Andersencb57d552001-06-28 07:25:16 +00009530}
9531
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009532static int
Eric Andersenc470f442003-07-28 09:56:35 +00009533evalcase(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00009534{
9535 union node *cp;
9536 union node *patp;
9537 struct arglist arglist;
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009538 int status = 0;
Eric Andersencb57d552001-06-28 07:25:16 +00009539
Denys Vlasenko675d24a2018-01-27 22:02:05 +01009540 errlinno = lineno = n->ncase.linno;
Denys Vlasenko675d24a2018-01-27 22:02:05 +01009541
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00009542 arglist.list = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00009543 arglist.lastp = &arglist.list;
Eric Andersencb57d552001-06-28 07:25:16 +00009544 expandarg(n->ncase.expr, &arglist, EXP_TILDE);
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009545 for (cp = n->ncase.cases; cp && evalskip == 0; cp = cp->nclist.next) {
9546 for (patp = cp->nclist.pattern; patp; patp = patp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00009547 if (casematch(patp, arglist.list->text)) {
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009548 /* Ensure body is non-empty as otherwise
9549 * EV_EXIT may prevent us from setting the
9550 * exit status.
9551 */
9552 if (evalskip == 0 && cp->nclist.body) {
9553 status = evaltree(cp->nclist.body, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00009554 }
9555 goto out;
9556 }
9557 }
9558 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009559 out:
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009560 return status;
Eric Andersencb57d552001-06-28 07:25:16 +00009561}
9562
Eric Andersenc470f442003-07-28 09:56:35 +00009563/*
9564 * Kick off a subshell to evaluate a tree.
9565 */
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009566static int
Eric Andersenc470f442003-07-28 09:56:35 +00009567evalsubshell(union node *n, int flags)
9568{
9569 struct job *jp;
Denys Vlasenko098b7132017-01-11 19:59:03 +01009570 int backgnd = (n->type == NBACKGND); /* FORK_BG(1) if yes, else FORK_FG(0) */
Eric Andersenc470f442003-07-28 09:56:35 +00009571 int status;
9572
Denys Vlasenko675d24a2018-01-27 22:02:05 +01009573 errlinno = lineno = n->nredir.linno;
Denys Vlasenko675d24a2018-01-27 22:02:05 +01009574
Eric Andersenc470f442003-07-28 09:56:35 +00009575 expredir(n->nredir.redirect);
Denys Vlasenko238bf182010-05-18 15:49:07 +02009576 if (!backgnd && (flags & EV_EXIT) && !may_have_traps)
Eric Andersenc470f442003-07-28 09:56:35 +00009577 goto nofork;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009578 INT_OFF;
Denys Vlasenko098b7132017-01-11 19:59:03 +01009579 if (backgnd == FORK_FG)
9580 get_tty_state();
Denis Vlasenko68404f12008-03-17 09:00:54 +00009581 jp = makejob(/*n,*/ 1);
Eric Andersenc470f442003-07-28 09:56:35 +00009582 if (forkshell(jp, n, backgnd) == 0) {
Denys Vlasenko238bf182010-05-18 15:49:07 +02009583 /* child */
Denis Vlasenkob012b102007-02-19 22:43:01 +00009584 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00009585 flags |= EV_EXIT;
9586 if (backgnd)
Denys Vlasenko238bf182010-05-18 15:49:07 +02009587 flags &= ~EV_TESTED;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00009588 nofork:
Eric Andersenc470f442003-07-28 09:56:35 +00009589 redirect(n->nredir.redirect, 0);
9590 evaltreenr(n->nredir.n, flags);
9591 /* never returns */
9592 }
Denys Vlasenko70392332016-10-27 02:31:55 +02009593 /* parent */
Eric Andersenc470f442003-07-28 09:56:35 +00009594 status = 0;
Denys Vlasenko098b7132017-01-11 19:59:03 +01009595 if (backgnd == FORK_FG)
Eric Andersenc470f442003-07-28 09:56:35 +00009596 status = waitforjob(jp);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009597 INT_ON;
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009598 return status;
Eric Andersenc470f442003-07-28 09:56:35 +00009599}
9600
Eric Andersenc470f442003-07-28 09:56:35 +00009601/*
9602 * Compute the names of the files in a redirection list.
9603 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009604static void fixredir(union node *, const char *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00009605static void
9606expredir(union node *n)
9607{
9608 union node *redir;
9609
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009610 for (redir = n; redir; redir = redir->nfile.next) {
Eric Andersenc470f442003-07-28 09:56:35 +00009611 struct arglist fn;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009612
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00009613 fn.list = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00009614 fn.lastp = &fn.list;
9615 switch (redir->type) {
9616 case NFROMTO:
9617 case NFROM:
9618 case NTO:
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01009619#if BASH_REDIR_OUTPUT
Denis Vlasenko559691a2008-10-05 18:39:31 +00009620 case NTO2:
9621#endif
Eric Andersenc470f442003-07-28 09:56:35 +00009622 case NCLOBBER:
9623 case NAPPEND:
9624 expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
Denys Vlasenkof451b2c2012-06-09 02:06:57 +02009625 TRACE(("expredir expanded to '%s'\n", fn.list->text));
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01009626#if BASH_REDIR_OUTPUT
Denis Vlasenko559691a2008-10-05 18:39:31 +00009627 store_expfname:
9628#endif
Denys Vlasenko7c4b13e2013-01-17 13:02:27 +01009629#if 0
9630// By the design of stack allocator, the loop of this kind:
9631// while true; do while true; do break; done </dev/null; done
9632// will look like a memory leak: ash plans to free expfname's
9633// of "/dev/null" as soon as it finishes running the loop
9634// (in this case, never).
9635// This "fix" is wrong:
Jon Tollefson4ba6c5d2012-11-13 19:26:53 +01009636 if (redir->nfile.expfname)
9637 stunalloc(redir->nfile.expfname);
Denys Vlasenko7c4b13e2013-01-17 13:02:27 +01009638// It results in corrupted state of stacked allocations.
9639#endif
Eric Andersenc470f442003-07-28 09:56:35 +00009640 redir->nfile.expfname = fn.list->text;
9641 break;
9642 case NFROMFD:
Denis Vlasenko559691a2008-10-05 18:39:31 +00009643 case NTOFD: /* >& */
Eric Andersenc470f442003-07-28 09:56:35 +00009644 if (redir->ndup.vname) {
Denys Vlasenkoe368d852020-02-16 19:02:22 +01009645 expandarg(redir->ndup.vname, &fn, EXP_TILDE | EXP_REDIR);
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009646 if (fn.list == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +00009647 ash_msg_and_raise_error("redir error");
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01009648#if BASH_REDIR_OUTPUT
Denis Vlasenko559691a2008-10-05 18:39:31 +00009649 if (!isdigit_str9(fn.list->text)) {
9650 /* >&file, not >&fd */
9651 if (redir->nfile.fd != 1) /* 123>&file - BAD */
9652 ash_msg_and_raise_error("redir error");
9653 redir->type = NTO2;
9654 goto store_expfname;
9655 }
9656#endif
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009657 fixredir(redir, fn.list->text, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00009658 }
9659 break;
9660 }
9661 }
9662}
9663
Eric Andersencb57d552001-06-28 07:25:16 +00009664/*
Eric Andersencb57d552001-06-28 07:25:16 +00009665 * Evaluate a pipeline. All the processes in the pipeline are children
9666 * of the process creating the pipeline. (This differs from some versions
9667 * of the shell, which make the last process in a pipeline the parent
9668 * of all the rest.)
9669 */
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009670static int
Eric Andersenc470f442003-07-28 09:56:35 +00009671evalpipe(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00009672{
9673 struct job *jp;
9674 struct nodelist *lp;
9675 int pipelen;
9676 int prevfd;
9677 int pip[2];
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009678 int status = 0;
Eric Andersencb57d552001-06-28 07:25:16 +00009679
Eric Andersenc470f442003-07-28 09:56:35 +00009680 TRACE(("evalpipe(0x%lx) called\n", (long)n));
Eric Andersencb57d552001-06-28 07:25:16 +00009681 pipelen = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009682 for (lp = n->npipe.cmdlist; lp; lp = lp->next)
Eric Andersencb57d552001-06-28 07:25:16 +00009683 pipelen++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009684 flags |= EV_EXIT;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009685 INT_OFF;
Denys Vlasenko098b7132017-01-11 19:59:03 +01009686 if (n->npipe.pipe_backgnd == 0)
9687 get_tty_state();
Denis Vlasenko68404f12008-03-17 09:00:54 +00009688 jp = makejob(/*n,*/ pipelen);
Eric Andersencb57d552001-06-28 07:25:16 +00009689 prevfd = -1;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009690 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009691 prehash(lp->n);
Eric Andersencb57d552001-06-28 07:25:16 +00009692 pip[1] = -1;
9693 if (lp->next) {
9694 if (pipe(pip) < 0) {
9695 close(prevfd);
Denys Vlasenko12ffefb2017-08-23 14:52:56 +02009696 ash_msg_and_raise_perror("can't create pipe");
Eric Andersencb57d552001-06-28 07:25:16 +00009697 }
9698 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00009699 if (forkshell(jp, lp->n, n->npipe.pipe_backgnd) == 0) {
Denys Vlasenko70392332016-10-27 02:31:55 +02009700 /* child */
Denis Vlasenkob012b102007-02-19 22:43:01 +00009701 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009702 if (pip[1] >= 0) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009703 close(pip[0]);
Eric Andersencb57d552001-06-28 07:25:16 +00009704 }
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009705 if (prevfd > 0) {
9706 dup2(prevfd, 0);
9707 close(prevfd);
9708 }
9709 if (pip[1] > 1) {
9710 dup2(pip[1], 1);
9711 close(pip[1]);
9712 }
Eric Andersenc470f442003-07-28 09:56:35 +00009713 evaltreenr(lp->n, flags);
9714 /* never returns */
Eric Andersencb57d552001-06-28 07:25:16 +00009715 }
Denys Vlasenko70392332016-10-27 02:31:55 +02009716 /* parent */
Eric Andersencb57d552001-06-28 07:25:16 +00009717 if (prevfd >= 0)
9718 close(prevfd);
9719 prevfd = pip[0];
Denis Vlasenkob9e70dd2009-03-20 01:24:08 +00009720 /* Don't want to trigger debugging */
9721 if (pip[1] != -1)
9722 close(pip[1]);
Eric Andersencb57d552001-06-28 07:25:16 +00009723 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00009724 if (n->npipe.pipe_backgnd == 0) {
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009725 status = waitforjob(jp);
9726 TRACE(("evalpipe: job done exit status %d\n", status));
Eric Andersencb57d552001-06-28 07:25:16 +00009727 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00009728 INT_ON;
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009729
9730 return status;
Eric Andersencb57d552001-06-28 07:25:16 +00009731}
9732
Ron Yorston9e2a5662020-01-21 16:01:58 +00009733/* setinteractive needs this forward reference */
Ron Yorston7d1c7d82022-03-24 12:17:25 +00009734#if ENABLE_FEATURE_EDITING
Ron Yorston9e2a5662020-01-21 16:01:58 +00009735static const char *get_builtin_name(int i) FAST_FUNC;
9736#endif
9737
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009738/*
9739 * Controls whether the shell is interactive or not.
9740 */
9741static void
9742setinteractive(int on)
9743{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009744 static smallint is_interactive;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009745
9746 if (++on == is_interactive)
9747 return;
9748 is_interactive = on;
9749 setsignal(SIGINT);
9750 setsignal(SIGQUIT);
9751 setsignal(SIGTERM);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009752 if (is_interactive > 1) {
Denys Vlasenko897475a2019-06-01 16:35:09 +02009753#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009754 /* Looks like they want an interactive shell */
Denis Vlasenkoca525b42007-06-13 12:27:17 +00009755 static smallint did_banner;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009756
Denis Vlasenkoca525b42007-06-13 12:27:17 +00009757 if (!did_banner) {
Denys Vlasenkoc34c0332009-09-29 12:25:30 +02009758 /* note: ash and hush share this string */
9759 out1fmt("\n\n%s %s\n"
Denys Vlasenko2ec34962014-09-08 16:52:39 +02009760 IF_ASH_HELP("Enter 'help' for a list of built-in commands.\n")
9761 "\n",
Denys Vlasenkoc34c0332009-09-29 12:25:30 +02009762 bb_banner,
9763 "built-in shell (ash)"
9764 );
Denis Vlasenkoca525b42007-06-13 12:27:17 +00009765 did_banner = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009766 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009767#endif
Denys Vlasenko897475a2019-06-01 16:35:09 +02009768#if ENABLE_FEATURE_EDITING
Ron Yorston9e2a5662020-01-21 16:01:58 +00009769 if (!line_input_state) {
Denys Vlasenko897475a2019-06-01 16:35:09 +02009770 line_input_state = new_line_input_t(FOR_SHELL | WITH_PATH_LOOKUP);
Ron Yorston9e2a5662020-01-21 16:01:58 +00009771 line_input_state->get_exe_name = get_builtin_name;
Ron Yorston7d1c7d82022-03-24 12:17:25 +00009772 line_input_state->sh_get_var = lookupvar;
Ron Yorston9e2a5662020-01-21 16:01:58 +00009773 }
Denys Vlasenko897475a2019-06-01 16:35:09 +02009774#endif
9775 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009776}
9777
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009778static void
9779optschanged(void)
9780{
9781#if DEBUG
9782 opentrace();
9783#endif
9784 setinteractive(iflag);
9785 setjobctl(mflag);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009786#if ENABLE_FEATURE_EDITING_VI
Denys Vlasenko897475a2019-06-01 16:35:09 +02009787 if (line_input_state) {
9788 if (viflag)
9789 line_input_state->flags |= VI_MODE;
9790 else
9791 line_input_state->flags &= ~VI_MODE;
9792 }
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009793#else
9794 viflag = 0; /* forcibly keep the option off */
9795#endif
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009796}
9797
Denys Vlasenkob8ab27b2017-07-26 19:22:34 +02009798struct localvar_list {
9799 struct localvar_list *next;
9800 struct localvar *lv;
9801};
9802
9803static struct localvar_list *localvar_stack;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009804
9805/*
9806 * Called after a function returns.
9807 * Interrupts must be off.
9808 */
9809static void
Denys Vlasenko981a0562017-07-26 19:53:11 +02009810poplocalvars(int keep)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009811{
Denys Vlasenkob8ab27b2017-07-26 19:22:34 +02009812 struct localvar_list *ll;
9813 struct localvar *lvp, *next;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009814 struct var *vp;
9815
Denys Vlasenkob8ab27b2017-07-26 19:22:34 +02009816 INT_OFF;
9817 ll = localvar_stack;
9818 localvar_stack = ll->next;
9819
9820 next = ll->lv;
9821 free(ll);
9822
9823 while ((lvp = next) != NULL) {
9824 next = lvp->next;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009825 vp = lvp->vp;
Denys Vlasenkob563f622010-09-25 17:15:13 +02009826 TRACE(("poplocalvar %s\n", vp ? vp->var_text : "-"));
Denys Vlasenko981a0562017-07-26 19:53:11 +02009827 if (keep) {
9828 int bits = VSTRFIXED;
9829
9830 if (lvp->flags != VUNSET) {
9831 if (vp->var_text == lvp->text)
9832 bits |= VTEXTFIXED;
9833 else if (!(lvp->flags & (VTEXTFIXED|VSTACK)))
9834 free((char*)lvp->text);
9835 }
9836
9837 vp->flags &= ~bits;
9838 vp->flags |= (lvp->flags & bits);
9839
9840 if ((vp->flags &
9841 (VEXPORT|VREADONLY|VSTRFIXED|VUNSET)) == VUNSET)
9842 unsetvar(vp->var_text);
9843 } else if (vp == NULL) { /* $- saved */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009844 memcpy(optlist, lvp->text, sizeof(optlist));
9845 free((char*)lvp->text);
9846 optschanged();
Denys Vlasenkod5b500c2017-07-26 19:25:40 +02009847 } else if (lvp->flags == VUNSET) {
9848 vp->flags &= ~(VSTRFIXED|VREADONLY);
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02009849 unsetvar(vp->var_text);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009850 } else {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02009851 if (vp->var_func)
9852 vp->var_func(var_end(lvp->text));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009853 if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02009854 free((char*)vp->var_text);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009855 vp->flags = lvp->flags;
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02009856 vp->var_text = lvp->text;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009857 }
9858 free(lvp);
9859 }
Denys Vlasenkob8ab27b2017-07-26 19:22:34 +02009860 INT_ON;
9861}
9862
9863/*
9864 * Create a new localvar environment.
9865 */
Denys Vlasenko484fc202017-07-26 19:55:31 +02009866static struct localvar_list *
Denys Vlasenkoe2dd2af2020-02-20 10:33:38 +01009867pushlocalvars(int push)
Denys Vlasenkob8ab27b2017-07-26 19:22:34 +02009868{
9869 struct localvar_list *ll;
Denys Vlasenkoe2dd2af2020-02-20 10:33:38 +01009870 struct localvar_list *top;
9871
9872 top = localvar_stack;
9873 if (!push)
9874 goto out;
Denys Vlasenkob8ab27b2017-07-26 19:22:34 +02009875
9876 INT_OFF;
9877 ll = ckzalloc(sizeof(*ll));
9878 /*ll->lv = NULL; - zalloc did it */
Denys Vlasenkoe2dd2af2020-02-20 10:33:38 +01009879 ll->next = top;
Denys Vlasenkob8ab27b2017-07-26 19:22:34 +02009880 localvar_stack = ll;
9881 INT_ON;
Denys Vlasenkoe2dd2af2020-02-20 10:33:38 +01009882 out:
9883 return top;
Denys Vlasenko484fc202017-07-26 19:55:31 +02009884}
9885
9886static void
9887unwindlocalvars(struct localvar_list *stop)
9888{
9889 while (localvar_stack != stop)
9890 poplocalvars(0);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009891}
9892
9893static int
9894evalfun(struct funcnode *func, int argc, char **argv, int flags)
9895{
9896 volatile struct shparam saveparam;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009897 struct jmploc *volatile savehandler;
9898 struct jmploc jmploc;
9899 int e;
Denys Vlasenkod6c9cbc2021-09-07 18:01:49 +02009900 int savelineno;
Denys Vlasenko675d24a2018-01-27 22:02:05 +01009901 int savefuncline;
Denys Vlasenko704c5962021-09-15 19:31:44 +02009902 char *savefuncname;
Roberto A. Fogliettae0bf3df2021-09-07 01:19:31 +02009903 char *savetrap = NULL;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009904
Roberto A. Fogliettae0bf3df2021-09-07 01:19:31 +02009905 if (!Eflag) {
9906 savetrap = trap[NTRAP_ERR];
9907 trap[NTRAP_ERR] = NULL;
9908 }
Denys Vlasenkod6c9cbc2021-09-07 18:01:49 +02009909 savelineno = lineno;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009910 saveparam = shellparam;
Denys Vlasenko675d24a2018-01-27 22:02:05 +01009911 savefuncline = funcline;
Denys Vlasenko704c5962021-09-15 19:31:44 +02009912 savefuncname = funcname;
Denys Vlasenkoa2d121c2016-09-30 11:30:11 +02009913 savehandler = exception_handler;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009914 e = setjmp(jmploc.loc);
9915 if (e) {
9916 goto funcdone;
9917 }
9918 INT_OFF;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009919 exception_handler = &jmploc;
Denis Vlasenko01631112007-12-16 17:20:38 +00009920 shellparam.malloced = 0;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009921 func->count++;
Denys Vlasenko704c5962021-09-15 19:31:44 +02009922 funcname = func->n.ndefun.text;
Denys Vlasenko675d24a2018-01-27 22:02:05 +01009923 funcline = func->n.ndefun.linno;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009924 INT_ON;
9925 shellparam.nparam = argc - 1;
9926 shellparam.p = argv + 1;
9927#if ENABLE_ASH_GETOPTS
9928 shellparam.optind = 1;
9929 shellparam.optoff = -1;
9930#endif
Denys Vlasenko675d24a2018-01-27 22:02:05 +01009931 evaltree(func->n.ndefun.body, flags & EV_TESTED);
Denis Vlasenko01631112007-12-16 17:20:38 +00009932 funcdone:
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009933 INT_OFF;
Denys Vlasenko704c5962021-09-15 19:31:44 +02009934 funcname = savefuncname;
Roberto A. Fogliettae0bf3df2021-09-07 01:19:31 +02009935 if (savetrap) {
9936 if (!trap[NTRAP_ERR])
9937 trap[NTRAP_ERR] = savetrap;
9938 else
9939 free(savetrap);
9940 }
Denys Vlasenko675d24a2018-01-27 22:02:05 +01009941 funcline = savefuncline;
Denys Vlasenkod6c9cbc2021-09-07 18:01:49 +02009942 lineno = savelineno;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009943 freefunc(func);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009944 freeparam(&shellparam);
9945 shellparam = saveparam;
9946 exception_handler = savehandler;
9947 INT_ON;
Denys Vlasenkocd24a502020-02-20 16:47:01 +01009948 evalskip &= ~(SKIPFUNC | SKIPFUNCDEF);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009949 return e;
9950}
9951
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009952/*
9953 * Make a variable a local variable. When a variable is made local, it's
9954 * value and flags are saved in a localvar structure. The saved values
9955 * will be restored when the shell function returns. We handle the name
Denys Vlasenkoe0a4e102015-05-13 02:20:14 +02009956 * "-" as a special case: it makes changes to "set +-options" local
9957 * (options will be restored on return from the function).
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009958 */
9959static void
Denys Vlasenko3e729102020-02-19 17:33:44 +01009960mklocal(char *name, int flags)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009961{
9962 struct localvar *lvp;
9963 struct var **vpp;
9964 struct var *vp;
Denys Vlasenko0a0acb52015-04-18 19:36:38 +02009965 char *eq = strchr(name, '=');
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009966
9967 INT_OFF;
Denys Vlasenko0a0acb52015-04-18 19:36:38 +02009968 /* Cater for duplicate "local". Examples:
9969 * x=0; f() { local x=1; echo $x; local x; echo $x; }; f; echo $x
9970 * x=0; f() { local x=1; echo $x; local x=2; echo $x; }; f; echo $x
9971 */
Denys Vlasenkob8ab27b2017-07-26 19:22:34 +02009972 lvp = localvar_stack->lv;
Denys Vlasenko0a0acb52015-04-18 19:36:38 +02009973 while (lvp) {
Eugene Rudoy1285aa62015-04-26 23:32:00 +02009974 if (lvp->vp && varcmp(lvp->vp->var_text, name) == 0) {
Denys Vlasenko0a0acb52015-04-18 19:36:38 +02009975 if (eq)
9976 setvareq(name, 0);
9977 /* else:
9978 * it's a duplicate "local VAR" declaration, do nothing
9979 */
Denys Vlasenko06b11492016-11-04 16:43:18 +01009980 goto ret;
Denys Vlasenko0a0acb52015-04-18 19:36:38 +02009981 }
9982 lvp = lvp->next;
9983 }
9984
9985 lvp = ckzalloc(sizeof(*lvp));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009986 if (LONE_DASH(name)) {
9987 char *p;
9988 p = ckmalloc(sizeof(optlist));
9989 lvp->text = memcpy(p, optlist, sizeof(optlist));
9990 vp = NULL;
9991 } else {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009992 vpp = hashvar(name);
9993 vp = *findvar(vpp, name);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009994 if (vp == NULL) {
Denys Vlasenko0a0acb52015-04-18 19:36:38 +02009995 /* variable did not exist yet */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009996 if (eq)
Denys Vlasenko3e729102020-02-19 17:33:44 +01009997 vp = setvareq(name, VSTRFIXED | flags);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009998 else
Denys Vlasenko3e729102020-02-19 17:33:44 +01009999 vp = setvar(name, NULL, VSTRFIXED | flags);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000010000 lvp->flags = VUNSET;
10001 } else {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +020010002 lvp->text = vp->var_text;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000010003 lvp->flags = vp->flags;
Denys Vlasenko0a0acb52015-04-18 19:36:38 +020010004 /* make sure neither "struct var" nor string gets freed
10005 * during (un)setting:
10006 */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000010007 vp->flags |= VSTRFIXED|VTEXTFIXED;
10008 if (eq)
Denys Vlasenko3e729102020-02-19 17:33:44 +010010009 setvareq(name, flags);
Denys Vlasenko109ee5d2014-03-16 18:41:11 +010010010 else
10011 /* "local VAR" unsets VAR: */
Denys Vlasenko0a0acb52015-04-18 19:36:38 +020010012 setvar0(name, NULL);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000010013 }
10014 }
10015 lvp->vp = vp;
Denys Vlasenkob8ab27b2017-07-26 19:22:34 +020010016 lvp->next = localvar_stack->lv;
10017 localvar_stack->lv = lvp;
Denys Vlasenko06b11492016-11-04 16:43:18 +010010018 ret:
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000010019 INT_ON;
10020}
10021
10022/*
10023 * The "local" command.
10024 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010025static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000010026localcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000010027{
10028 char *name;
10029
Denys Vlasenkob8ab27b2017-07-26 19:22:34 +020010030 if (!localvar_stack)
Ron Yorstonef2386b2015-10-29 16:19:14 +000010031 ash_msg_and_raise_error("not in a function");
10032
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000010033 argv = argptr;
10034 while ((name = *argv++) != NULL) {
Denys Vlasenko3e729102020-02-19 17:33:44 +010010035 mklocal(name, 0);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000010036 }
10037 return 0;
10038}
10039
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010040static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000010041falsecmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000010042{
10043 return 1;
10044}
10045
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010046static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000010047truecmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000010048{
10049 return 0;
10050}
10051
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010052static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000010053execcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000010054{
Denys Vlasenko6c149f42017-04-12 21:31:32 +020010055 optionarg = NULL;
10056 while (nextopt("a:") != '\0')
10057 /* nextopt() sets optionarg to "-a ARGV0" */;
10058
10059 argv = argptr;
10060 if (argv[0]) {
10061 char *prog;
10062
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000010063 iflag = 0; /* exit on error */
10064 mflag = 0;
10065 optschanged();
Denys Vlasenkoe5814a52016-07-16 18:33:55 +020010066 /* We should set up signals for "exec CMD"
10067 * the same way as for "CMD" without "exec".
10068 * But optschanged->setinteractive->setsignal
10069 * still thought we are a root shell. Therefore, for example,
10070 * SIGQUIT is still set to IGN. Fix it:
10071 */
10072 shlvl++;
10073 setsignal(SIGQUIT);
10074 /*setsignal(SIGTERM); - unnecessary because of iflag=0 */
10075 /*setsignal(SIGTSTP); - unnecessary because of mflag=0 */
10076 /*setsignal(SIGTTOU); - unnecessary because of mflag=0 */
10077
Denys Vlasenko6c149f42017-04-12 21:31:32 +020010078 prog = argv[0];
10079 if (optionarg)
10080 argv[0] = optionarg;
10081 shellexec(prog, argv, pathval(), 0);
Denys Vlasenkoe5814a52016-07-16 18:33:55 +020010082 /* NOTREACHED */
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000010083 }
10084 return 0;
10085}
10086
10087/*
10088 * The return command.
10089 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010090static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000010091returncmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000010092{
Denys Vlasenkocd24a502020-02-20 16:47:01 +010010093 int skip;
10094 int status;
10095
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000010096 /*
10097 * If called outside a function, do what ksh does;
10098 * skip the rest of the file.
10099 */
Denys Vlasenkocd24a502020-02-20 16:47:01 +010010100 if (argv[1]) {
10101 skip = SKIPFUNC;
10102 status = number(argv[1]);
10103 } else {
10104 skip = SKIPFUNCDEF;
10105 status = exitstatus;
10106 }
10107 evalskip = skip;
10108
10109 return status;
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000010110}
10111
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010112/* Forward declarations for builtintab[] */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010113static int breakcmd(int, char **) FAST_FUNC;
10114static int dotcmd(int, char **) FAST_FUNC;
Denys Vlasenko7b3fa1e2016-10-01 15:10:16 +020010115static int evalcmd(int, char **, int) FAST_FUNC;
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010116static int exitcmd(int, char **) FAST_FUNC;
10117static int exportcmd(int, char **) FAST_FUNC;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010118#if ENABLE_ASH_GETOPTS
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010119static int getoptscmd(int, char **) FAST_FUNC;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010120#endif
Denys Vlasenko2ec34962014-09-08 16:52:39 +020010121#if ENABLE_ASH_HELP
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010122static int helpcmd(int, char **) FAST_FUNC;
Denis Vlasenko52764022007-02-24 13:42:56 +000010123#endif
Flemming Madsend96ffda2013-04-07 18:47:24 +020010124#if MAX_HISTORY
10125static int historycmd(int, char **) FAST_FUNC;
10126#endif
Denys Vlasenko0b883582016-12-23 16:49:07 +010010127#if ENABLE_FEATURE_SH_MATH
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010128static int letcmd(int, char **) FAST_FUNC;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010129#endif
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010130static int readcmd(int, char **) FAST_FUNC;
10131static int setcmd(int, char **) FAST_FUNC;
10132static int shiftcmd(int, char **) FAST_FUNC;
10133static int timescmd(int, char **) FAST_FUNC;
10134static int trapcmd(int, char **) FAST_FUNC;
10135static int umaskcmd(int, char **) FAST_FUNC;
10136static int unsetcmd(int, char **) FAST_FUNC;
10137static int ulimitcmd(int, char **) FAST_FUNC;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010138
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000010139#define BUILTIN_NOSPEC "0"
10140#define BUILTIN_SPECIAL "1"
10141#define BUILTIN_REGULAR "2"
10142#define BUILTIN_SPEC_REG "3"
10143#define BUILTIN_ASSIGN "4"
10144#define BUILTIN_SPEC_ASSG "5"
10145#define BUILTIN_REG_ASSG "6"
Denis Vlasenko4fe15f32007-02-23 01:05:26 +000010146#define BUILTIN_SPEC_REG_ASSG "7"
10147
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010148/* Stubs for calling non-FAST_FUNC's */
Denys Vlasenko265062d2017-01-10 15:13:30 +010010149#if ENABLE_ASH_ECHO
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010150static int FAST_FUNC echocmd(int argc, char **argv) { return echo_main(argc, argv); }
Denys Vlasenko2634bf32009-06-09 18:40:07 +020010151#endif
Denys Vlasenko265062d2017-01-10 15:13:30 +010010152#if ENABLE_ASH_PRINTF
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010153static int FAST_FUNC printfcmd(int argc, char **argv) { return printf_main(argc, argv); }
Denys Vlasenko2634bf32009-06-09 18:40:07 +020010154#endif
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010010155#if ENABLE_ASH_TEST || BASH_TEST2
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010156static int FAST_FUNC testcmd(int argc, char **argv) { return test_main(argc, argv); }
Denys Vlasenko2634bf32009-06-09 18:40:07 +020010157#endif
Denis Vlasenko468aea22008-04-01 14:47:57 +000010158
Denis Vlasenkof7d56652008-03-25 05:51:41 +000010159/* Keep these in proper order since it is searched via bsearch() */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +000010160static const struct builtincmd builtintab[] = {
Denys Vlasenko023a08f2010-03-26 15:53:33 +010010161 { BUILTIN_SPEC_REG "." , dotcmd },
10162 { BUILTIN_SPEC_REG ":" , truecmd },
Denys Vlasenko265062d2017-01-10 15:13:30 +010010163#if ENABLE_ASH_TEST
Denys Vlasenko023a08f2010-03-26 15:53:33 +010010164 { BUILTIN_REGULAR "[" , testcmd },
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010010165#endif
10166#if BASH_TEST2
Denys Vlasenko023a08f2010-03-26 15:53:33 +010010167 { BUILTIN_REGULAR "[[" , testcmd },
Denis Vlasenko80591b02008-03-25 07:49:43 +000010168#endif
Denis Vlasenko4fe15f32007-02-23 01:05:26 +000010169#if ENABLE_ASH_ALIAS
Denys Vlasenko023a08f2010-03-26 15:53:33 +010010170 { BUILTIN_REG_ASSG "alias" , aliascmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +000010171#endif
10172#if JOBS
Denys Vlasenko023a08f2010-03-26 15:53:33 +010010173 { BUILTIN_REGULAR "bg" , fg_bgcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +000010174#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +010010175 { BUILTIN_SPEC_REG "break" , breakcmd },
10176 { BUILTIN_REGULAR "cd" , cdcmd },
10177 { BUILTIN_NOSPEC "chdir" , cdcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +000010178#if ENABLE_ASH_CMDCMD
Denys Vlasenko023a08f2010-03-26 15:53:33 +010010179 { BUILTIN_REGULAR "command" , commandcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +000010180#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +010010181 { BUILTIN_SPEC_REG "continue", breakcmd },
Denys Vlasenko265062d2017-01-10 15:13:30 +010010182#if ENABLE_ASH_ECHO
Denys Vlasenko023a08f2010-03-26 15:53:33 +010010183 { BUILTIN_REGULAR "echo" , echocmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +000010184#endif
Denys Vlasenko7b3fa1e2016-10-01 15:10:16 +020010185 { BUILTIN_SPEC_REG "eval" , NULL }, /*evalcmd() has a differing prototype*/
Denys Vlasenko023a08f2010-03-26 15:53:33 +010010186 { BUILTIN_SPEC_REG "exec" , execcmd },
10187 { BUILTIN_SPEC_REG "exit" , exitcmd },
10188 { BUILTIN_SPEC_REG_ASSG "export" , exportcmd },
10189 { BUILTIN_REGULAR "false" , falsecmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +000010190#if JOBS
Denys Vlasenko023a08f2010-03-26 15:53:33 +010010191 { BUILTIN_REGULAR "fg" , fg_bgcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +000010192#endif
10193#if ENABLE_ASH_GETOPTS
Denys Vlasenko023a08f2010-03-26 15:53:33 +010010194 { BUILTIN_REGULAR "getopts" , getoptscmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +000010195#endif
Denys Vlasenkoa7b97e32020-02-16 18:29:52 +010010196 { BUILTIN_REGULAR "hash" , hashcmd },
Denys Vlasenko2ec34962014-09-08 16:52:39 +020010197#if ENABLE_ASH_HELP
Denys Vlasenko023a08f2010-03-26 15:53:33 +010010198 { BUILTIN_NOSPEC "help" , helpcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +000010199#endif
Flemming Madsend96ffda2013-04-07 18:47:24 +020010200#if MAX_HISTORY
10201 { BUILTIN_NOSPEC "history" , historycmd },
10202#endif
Denis Vlasenko4fe15f32007-02-23 01:05:26 +000010203#if JOBS
Denys Vlasenko023a08f2010-03-26 15:53:33 +010010204 { BUILTIN_REGULAR "jobs" , jobscmd },
10205 { BUILTIN_REGULAR "kill" , killcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +000010206#endif
Denys Vlasenko0b883582016-12-23 16:49:07 +010010207#if ENABLE_FEATURE_SH_MATH
Denys Vlasenko023a08f2010-03-26 15:53:33 +010010208 { BUILTIN_NOSPEC "let" , letcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +000010209#endif
Denys Vlasenko85241c72017-07-26 20:00:08 +020010210 { BUILTIN_SPEC_REG_ASSG "local" , localcmd },
Denys Vlasenko265062d2017-01-10 15:13:30 +010010211#if ENABLE_ASH_PRINTF
Denys Vlasenko023a08f2010-03-26 15:53:33 +010010212 { BUILTIN_REGULAR "printf" , printfcmd },
Denis Vlasenkocd2663f2008-06-01 22:36:39 +000010213#endif
Denys Vlasenkoa7b97e32020-02-16 18:29:52 +010010214 { BUILTIN_REGULAR "pwd" , pwdcmd },
Denys Vlasenko023a08f2010-03-26 15:53:33 +010010215 { BUILTIN_REGULAR "read" , readcmd },
10216 { BUILTIN_SPEC_REG_ASSG "readonly", exportcmd },
10217 { BUILTIN_SPEC_REG "return" , returncmd },
10218 { BUILTIN_SPEC_REG "set" , setcmd },
10219 { BUILTIN_SPEC_REG "shift" , shiftcmd },
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010010220#if BASH_SOURCE
Denys Vlasenko023a08f2010-03-26 15:53:33 +010010221 { BUILTIN_SPEC_REG "source" , dotcmd },
Denys Vlasenko82731b42010-05-17 17:49:52 +020010222#endif
Denys Vlasenko265062d2017-01-10 15:13:30 +010010223#if ENABLE_ASH_TEST
Denys Vlasenko023a08f2010-03-26 15:53:33 +010010224 { BUILTIN_REGULAR "test" , testcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +000010225#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +010010226 { BUILTIN_SPEC_REG "times" , timescmd },
10227 { BUILTIN_SPEC_REG "trap" , trapcmd },
10228 { BUILTIN_REGULAR "true" , truecmd },
Denys Vlasenkoa7b97e32020-02-16 18:29:52 +010010229 { BUILTIN_REGULAR "type" , typecmd },
10230 { BUILTIN_REGULAR "ulimit" , ulimitcmd },
Denys Vlasenko023a08f2010-03-26 15:53:33 +010010231 { BUILTIN_REGULAR "umask" , umaskcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +000010232#if ENABLE_ASH_ALIAS
Denys Vlasenko023a08f2010-03-26 15:53:33 +010010233 { BUILTIN_REGULAR "unalias" , unaliascmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +000010234#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +010010235 { BUILTIN_SPEC_REG "unset" , unsetcmd },
10236 { BUILTIN_REGULAR "wait" , waitcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +000010237};
10238
Denis Vlasenko80591b02008-03-25 07:49:43 +000010239/* Should match the above table! */
10240#define COMMANDCMD (builtintab + \
Denys Vlasenko928e2a72016-09-29 00:30:31 +020010241 /* . : */ 2 + \
Denys Vlasenko265062d2017-01-10 15:13:30 +010010242 /* [ */ 1 * ENABLE_ASH_TEST + \
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010010243 /* [[ */ 1 * BASH_TEST2 + \
Denys Vlasenko928e2a72016-09-29 00:30:31 +020010244 /* alias */ 1 * ENABLE_ASH_ALIAS + \
10245 /* bg */ 1 * ENABLE_ASH_JOB_CONTROL + \
10246 /* break cd cddir */ 3)
10247#define EVALCMD (COMMANDCMD + \
10248 /* command */ 1 * ENABLE_ASH_CMDCMD + \
10249 /* continue */ 1 + \
Denys Vlasenko265062d2017-01-10 15:13:30 +010010250 /* echo */ 1 * ENABLE_ASH_ECHO + \
Denys Vlasenko928e2a72016-09-29 00:30:31 +020010251 0)
10252#define EXECCMD (EVALCMD + \
10253 /* eval */ 1)
Denis Vlasenko4fe15f32007-02-23 01:05:26 +000010254
10255/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000010256 * Search the table of builtin commands.
10257 */
Denys Vlasenko888527c2016-10-02 16:54:17 +020010258static int
10259pstrcmp1(const void *a, const void *b)
10260{
10261 return strcmp((char*)a, *(char**)b + 1);
10262}
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000010263static struct builtincmd *
10264find_builtin(const char *name)
10265{
10266 struct builtincmd *bp;
10267
10268 bp = bsearch(
Denis Vlasenko80b8b392007-06-25 10:55:35 +000010269 name, builtintab, ARRAY_SIZE(builtintab), sizeof(builtintab[0]),
Denys Vlasenko888527c2016-10-02 16:54:17 +020010270 pstrcmp1
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000010271 );
10272 return bp;
10273}
10274
Ron Yorston7d1c7d82022-03-24 12:17:25 +000010275#if ENABLE_FEATURE_EDITING
Ron Yorston9e2a5662020-01-21 16:01:58 +000010276static const char * FAST_FUNC
10277get_builtin_name(int i)
10278{
10279 return /*i >= 0 &&*/ i < ARRAY_SIZE(builtintab) ? builtintab[i].name + 1 : NULL;
10280}
10281#endif
10282
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000010283/*
Denis Vlasenko4fe15f32007-02-23 01:05:26 +000010284 * Execute a simple command.
10285 */
Denys Vlasenko1c5eb882018-08-05 17:07:26 +020010286static void unwindfiles(struct parsefile *stop);
Denis Vlasenko4fe15f32007-02-23 01:05:26 +000010287static int
10288isassignment(const char *p)
Paul Foxc3850c82005-07-20 18:23:39 +000010289{
10290 const char *q = endofname(p);
10291 if (p == q)
10292 return 0;
10293 return *q == '=';
10294}
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010295static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000010296bltincmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko4fe15f32007-02-23 01:05:26 +000010297{
10298 /* Preserve exitstatus of a previous possible redirection
10299 * as POSIX mandates */
10300 return back_exitstatus;
10301}
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +020010302static int
Eric Andersenc470f442003-07-28 09:56:35 +000010303evalcommand(union node *cmd, int flags)
10304{
Denis Vlasenko0e6f6612008-02-15 15:02:15 +000010305 static const struct builtincmd null_bltin = {
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010010306 BUILTIN_REGULAR "", bltincmd
Denis Vlasenko4fe15f32007-02-23 01:05:26 +000010307 };
Denys Vlasenko484fc202017-07-26 19:55:31 +020010308 struct localvar_list *localvar_stop;
Denys Vlasenko1c5eb882018-08-05 17:07:26 +020010309 struct parsefile *file_stop;
Denys Vlasenko1c79aeb2017-07-29 22:51:52 +020010310 struct redirtab *redir_stop;
Eric Andersenc470f442003-07-28 09:56:35 +000010311 union node *argp;
10312 struct arglist arglist;
10313 struct arglist varlist;
10314 char **argv;
10315 int argc;
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010010316 struct strlist *osp;
Glenn L McGrath7b8765c2003-08-29 07:29:30 +000010317 const struct strlist *sp;
Eric Andersenc470f442003-07-28 09:56:35 +000010318 struct cmdentry cmdentry;
10319 struct job *jp;
10320 char *lastarg;
10321 const char *path;
10322 int spclbltin;
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010010323 int cmd_flag;
Eric Andersenc470f442003-07-28 09:56:35 +000010324 int status;
10325 char **nargv;
Denis Vlasenko34c73c42008-08-16 11:48:02 +000010326 smallint cmd_is_exec;
Denys Vlasenko3e729102020-02-19 17:33:44 +010010327 int vflags;
10328 int vlocal;
Eric Andersenc470f442003-07-28 09:56:35 +000010329
Denys Vlasenko675d24a2018-01-27 22:02:05 +010010330 errlinno = lineno = cmd->ncmd.linno;
Denys Vlasenko675d24a2018-01-27 22:02:05 +010010331
Eric Andersenc470f442003-07-28 09:56:35 +000010332 /* First expand the arguments. */
10333 TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags));
Ron Yorston4a36ef12021-08-30 20:31:42 +010010334#if BASH_PROCESS_SUBST
10335 redir_stop = redirlist;
10336#endif
Denys Vlasenko1c5eb882018-08-05 17:07:26 +020010337 file_stop = g_parsefile;
Eric Andersenc470f442003-07-28 09:56:35 +000010338 back_exitstatus = 0;
10339
10340 cmdentry.cmdtype = CMDBUILTIN;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +000010341 cmdentry.u.cmd = &null_bltin;
Eric Andersenc470f442003-07-28 09:56:35 +000010342 varlist.lastp = &varlist.list;
10343 *varlist.lastp = NULL;
10344 arglist.lastp = &arglist.list;
10345 *arglist.lastp = NULL;
10346
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010010347 cmd_flag = 0;
10348 cmd_is_exec = 0;
10349 spclbltin = -1;
Denys Vlasenko3e729102020-02-19 17:33:44 +010010350 vflags = 0;
10351 vlocal = 0;
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010010352 path = NULL;
10353
Eric Andersenc470f442003-07-28 09:56:35 +000010354 argc = 0;
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010010355 argp = cmd->ncmd.args;
10356 osp = fill_arglist(&arglist, &argp);
10357 if (osp) {
10358 int pseudovarflag = 0;
Denys Vlasenkod07a15b2017-07-30 16:51:05 +020010359
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010010360 for (;;) {
10361 find_command(arglist.list->text, &cmdentry,
10362 cmd_flag | DO_REGBLTIN, pathval());
Paul Foxc3850c82005-07-20 18:23:39 +000010363
Denys Vlasenko3e729102020-02-19 17:33:44 +010010364 vlocal++;
10365
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010010366 /* implement bltin and command here */
10367 if (cmdentry.cmdtype != CMDBUILTIN)
10368 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010369
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010010370 pseudovarflag = IS_BUILTIN_ASSIGN(cmdentry.u.cmd);
10371 if (spclbltin < 0) {
10372 spclbltin = IS_BUILTIN_SPECIAL(cmdentry.u.cmd);
Denys Vlasenko3e729102020-02-19 17:33:44 +010010373 vlocal = !spclbltin;
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010010374 }
10375 cmd_is_exec = cmdentry.u.cmd == EXECCMD;
Ron Yorstonda7a6db2020-02-27 09:50:18 +000010376#if ENABLE_ASH_CMDCMD
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010010377 if (cmdentry.u.cmd != COMMANDCMD)
10378 break;
Paul Foxc3850c82005-07-20 18:23:39 +000010379
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010010380 cmd_flag = parse_command_args(&arglist, &argp, &path);
10381 if (!cmd_flag)
Ron Yorstonda7a6db2020-02-27 09:50:18 +000010382#endif
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010010383 break;
Denys Vlasenkod07a15b2017-07-30 16:51:05 +020010384 }
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010010385
10386 for (; argp; argp = argp->narg.next)
10387 expandarg(argp, &arglist,
10388 pseudovarflag &&
10389 isassignment(argp->narg.text) ?
10390 EXP_VARTILDE : EXP_FULL | EXP_TILDE);
10391
10392 for (sp = arglist.list; sp; sp = sp->next)
10393 argc++;
Denys Vlasenko3e729102020-02-19 17:33:44 +010010394
10395 if (cmd_is_exec && argc > 1)
10396 vflags = VEXPORT;
Eric Andersenc470f442003-07-28 09:56:35 +000010397 }
10398
Denys Vlasenkoe2dd2af2020-02-20 10:33:38 +010010399 localvar_stop = pushlocalvars(vlocal);
10400
Denys Vlasenko65a8b852016-10-26 22:29:11 +020010401 /* Reserve one extra spot at the front for shellexec. */
10402 nargv = stalloc(sizeof(char *) * (argc + 2));
10403 argv = ++nargv;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010404 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersenc470f442003-07-28 09:56:35 +000010405 TRACE(("evalcommand arg: %s\n", sp->text));
10406 *nargv++ = sp->text;
10407 }
10408 *nargv = NULL;
10409
10410 lastarg = NULL;
Denys Vlasenko675d24a2018-01-27 22:02:05 +010010411 if (iflag && funcline == 0 && argc > 0)
Eric Andersenc470f442003-07-28 09:56:35 +000010412 lastarg = nargv[-1];
10413
10414 expredir(cmd->ncmd.redirect);
Ron Yorston4a36ef12021-08-30 20:31:42 +010010415#if !BASH_PROCESS_SUBST
Denys Vlasenko1c79aeb2017-07-29 22:51:52 +020010416 redir_stop = pushredir(cmd->ncmd.redirect);
Ron Yorston4a36ef12021-08-30 20:31:42 +010010417#else
10418 pushredir(cmd->ncmd.redirect);
10419#endif
Denys Vlasenkod07a15b2017-07-30 16:51:05 +020010420 preverrout_fd = 2;
Denys Vlasenkof8cdc7a2017-08-04 15:24:49 +020010421 if (BASH_XTRACEFD && xflag) {
10422 /* NB: bash closes fd == $BASH_XTRACEFD when it is changed.
10423 * we do not emulate this. We only use its value.
10424 */
10425 const char *xtracefd = lookupvar("BASH_XTRACEFD");
10426 if (xtracefd && is_number(xtracefd))
10427 preverrout_fd = atoi(xtracefd);
10428
10429 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010430 status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH | REDIR_SAVEFD2);
Eric Andersenc470f442003-07-28 09:56:35 +000010431
Denys Vlasenko54bef2a2020-02-19 15:30:20 +010010432 if (status) {
10433 bail:
10434 exitstatus = status;
10435
10436 /* We have a redirection error. */
10437 if (spclbltin > 0)
10438 raise_exception(EXERROR);
10439
10440 goto out;
10441 }
10442
Eric Andersenc470f442003-07-28 09:56:35 +000010443 for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) {
10444 struct strlist **spp;
Eric Andersenc470f442003-07-28 09:56:35 +000010445
10446 spp = varlist.lastp;
10447 expandarg(argp, &varlist, EXP_VARTILDE);
10448
Denys Vlasenko3e729102020-02-19 17:33:44 +010010449 if (vlocal)
10450 mklocal((*spp)->text, VEXPORT);
10451 else
10452 setvareq((*spp)->text, vflags);
Eric Andersenc470f442003-07-28 09:56:35 +000010453 }
10454
10455 /* Print the command if xflag is set. */
Denys Vlasenkoeb607772021-09-09 16:26:41 +020010456 if (xflag && !inps4) {
Denys Vlasenko42ba7572017-07-21 13:20:14 +020010457 const char *pfx = "";
Eric Andersenc470f442003-07-28 09:56:35 +000010458
Denys Vlasenkoeb607772021-09-09 16:26:41 +020010459 inps4 = 1;
Denys Vlasenko46999802017-07-29 21:12:29 +020010460 fdprintf(preverrout_fd, "%s", expandstr(ps4val(), DQSYNTAX));
Denys Vlasenkoeb607772021-09-09 16:26:41 +020010461 inps4 = 0;
Denys Vlasenko42ba7572017-07-21 13:20:14 +020010462
Glenn L McGrath7b8765c2003-08-29 07:29:30 +000010463 sp = varlist.list;
Denys Vlasenko42ba7572017-07-21 13:20:14 +020010464 while (sp) {
10465 char *varval = sp->text;
10466 char *eq = strchrnul(varval, '=');
10467 if (*eq)
10468 eq++;
10469 fdprintf(preverrout_fd, "%s%.*s%s",
10470 pfx,
10471 (int)(eq - varval), varval,
10472 maybe_single_quote(eq)
10473 );
10474 sp = sp->next;
10475 pfx = " ";
10476 }
10477
10478 sp = arglist.list;
10479 while (sp) {
10480 fdprintf(preverrout_fd, "%s%s",
10481 pfx,
10482 /* always quote if matches reserved word: */
10483 findkwd(sp->text)
10484 ? single_quote(sp->text)
10485 : maybe_single_quote(sp->text)
10486 );
10487 sp = sp->next;
10488 pfx = " ";
Glenn L McGrath7b8765c2003-08-29 07:29:30 +000010489 }
Denis Vlasenko0e6f6612008-02-15 15:02:15 +000010490 safe_write(preverrout_fd, "\n", 1);
Eric Andersenc470f442003-07-28 09:56:35 +000010491 }
10492
Eric Andersenc470f442003-07-28 09:56:35 +000010493 /* Now locate the command. */
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010010494 if (cmdentry.cmdtype != CMDBUILTIN
10495 || !(IS_BUILTIN_REGULAR(cmdentry.u.cmd))
10496 ) {
Denys Vlasenko3e729102020-02-19 17:33:44 +010010497 path = path ? path : pathval();
10498 find_command(argv[0], &cmdentry, cmd_flag | DO_ERR, path);
Eric Andersenc470f442003-07-28 09:56:35 +000010499 }
10500
Denys Vlasenkod81af722020-02-18 14:28:30 +010010501 jp = NULL;
10502
Eric Andersenc470f442003-07-28 09:56:35 +000010503 /* Execute the command. */
10504 switch (cmdentry.cmdtype) {
Denys Vlasenko54bef2a2020-02-19 15:30:20 +010010505 case CMDUNKNOWN:
10506 status = 127;
10507 flush_stdout_stderr();
10508 goto bail;
10509
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +020010510 default: {
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +000010511
Denys Vlasenko1750d3a2018-01-15 00:41:04 +010010512#if ENABLE_FEATURE_SH_STANDALONE \
10513 && ENABLE_FEATURE_SH_NOFORK \
10514 && NUM_APPLETS > 1
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +020010515/* (1) BUG: if variables are set, we need to fork, or save/restore them
10516 * around run_nofork_applet() call.
10517 * (2) Should this check also be done in forkshell()?
10518 * (perhaps it should, so that "VAR=VAL nofork" at least avoids exec...)
10519 */
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000010520 /* find_command() encodes applet_no as (-2 - applet_no) */
10521 int applet_no = (- cmdentry.u.index - 2);
Denis Vlasenko9bc80d72008-04-12 20:07:53 +000010522 if (applet_no >= 0 && APPLET_IS_NOFORK(applet_no)) {
Denys Vlasenkoa5060b82017-11-03 14:16:25 +010010523 char **sv_environ;
10524
10525 INT_OFF;
10526 sv_environ = environ;
10527 environ = listvars(VEXPORT, VUNSET, varlist.list, /*end:*/ NULL);
Denys Vlasenkod329e342017-08-04 14:50:03 +020010528 /*
10529 * Run <applet>_main().
10530 * Signals (^C) can't interrupt here.
10531 * Otherwise we can mangle stdio or malloc internal state.
10532 * This makes applets which can run for a long time
10533 * and/or wait for user input ineligible for NOFORK:
10534 * for example, "yes" or "rm" (rm -i waits for input).
10535 */
Ron Yorstond5bfe262020-02-20 08:23:03 +000010536 exitstatus = run_nofork_applet(applet_no, argv);
Denys Vlasenkoa5060b82017-11-03 14:16:25 +010010537 environ = sv_environ;
Denys Vlasenkod329e342017-08-04 14:50:03 +020010538 /*
10539 * Try enabling NOFORK for "yes" applet.
10540 * ^C _will_ stop it (write returns EINTR),
10541 * but this causes stdout FILE to be stuck
10542 * and needing clearerr(). What if other applets
10543 * also can get EINTRs? Do we need to switch
10544 * our signals to SA_RESTART?
10545 */
10546 /*clearerr(stdout);*/
10547 INT_ON;
Denis Vlasenko9bc80d72008-04-12 20:07:53 +000010548 break;
10549 }
Denis Vlasenko9bc80d72008-04-12 20:07:53 +000010550#endif
Denys Vlasenkocfd392b2017-08-03 19:56:29 +020010551 /* Can we avoid forking? For example, very last command
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +020010552 * in a script or a subshell does not need forking,
10553 * we can just exec it.
10554 */
Denys Vlasenko238bf182010-05-18 15:49:07 +020010555 if (!(flags & EV_EXIT) || may_have_traps) {
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +020010556 /* No, forking off a child is necessary */
Denis Vlasenkob012b102007-02-19 22:43:01 +000010557 INT_OFF;
Denys Vlasenko098b7132017-01-11 19:59:03 +010010558 get_tty_state();
Denis Vlasenko68404f12008-03-17 09:00:54 +000010559 jp = makejob(/*cmd,*/ 1);
Eric Andersenc470f442003-07-28 09:56:35 +000010560 if (forkshell(jp, cmd, FORK_FG) != 0) {
Denys Vlasenko238bf182010-05-18 15:49:07 +020010561 /* parent */
Eric Andersenc470f442003-07-28 09:56:35 +000010562 break;
10563 }
Denys Vlasenko238bf182010-05-18 15:49:07 +020010564 /* child */
Denis Vlasenkob012b102007-02-19 22:43:01 +000010565 FORCE_INT_ON;
Denys Vlasenkoc7f95d22010-05-18 15:52:23 +020010566 /* fall through to exec'ing external program */
Eric Andersenc470f442003-07-28 09:56:35 +000010567 }
Denys Vlasenkoe139ae32017-04-12 21:02:33 +020010568 shellexec(argv[0], argv, path, cmdentry.u.index);
Eric Andersenc470f442003-07-28 09:56:35 +000010569 /* NOTREACHED */
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +020010570 } /* default */
Eric Andersenc470f442003-07-28 09:56:35 +000010571 case CMDBUILTIN:
Denys Vlasenko97edfc42020-02-18 14:37:56 +010010572 if (evalbltin(cmdentry.u.cmd, argc, argv, flags)
10573 && !(exception_type == EXERROR && spclbltin <= 0)
10574 ) {
Denys Vlasenkoc0663c72016-10-27 21:09:01 +020010575 raise:
10576 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +000010577 }
Denys Vlasenkod81af722020-02-18 14:28:30 +010010578 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010579
10580 case CMDFUNCTION:
Eric Andersenc470f442003-07-28 09:56:35 +000010581 if (evalfun(cmdentry.u.func, argc, argv, flags))
10582 goto raise;
10583 break;
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +020010584 } /* switch */
Eric Andersenc470f442003-07-28 09:56:35 +000010585
Denys Vlasenkod81af722020-02-18 14:28:30 +010010586 status = waitforjob(jp);
Ron Yorston6cda0b02020-02-21 16:16:56 +000010587 if (jp)
10588 TRACE(("forked child exited with %d\n", status));
Denys Vlasenko97edfc42020-02-18 14:37:56 +010010589 FORCE_INT_ON;
Denys Vlasenkod81af722020-02-18 14:28:30 +010010590
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010591 out:
Denys Vlasenkoeaf94362016-10-25 21:46:03 +020010592 if (cmd->ncmd.redirect)
Denys Vlasenko035486c2017-07-31 04:09:19 +020010593 popredir(/*drop:*/ cmd_is_exec);
Denys Vlasenko1c79aeb2017-07-29 22:51:52 +020010594 unwindredir(redir_stop);
Denys Vlasenko1c5eb882018-08-05 17:07:26 +020010595 unwindfiles(file_stop);
Denys Vlasenkoe2dd2af2020-02-20 10:33:38 +010010596 unwindlocalvars(localvar_stop);
Denis Vlasenko6514c5e2008-07-24 13:41:37 +000010597 if (lastarg) {
Eric Andersenc470f442003-07-28 09:56:35 +000010598 /* dsl: I think this is intended to be used to support
10599 * '_' in 'vi' command mode during line editing...
10600 * However I implemented that within libedit itself.
10601 */
Denys Vlasenko0a0acb52015-04-18 19:36:38 +020010602 setvar0("_", lastarg);
Denis Vlasenko6514c5e2008-07-24 13:41:37 +000010603 }
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +020010604
10605 return status;
Eric Andersenc470f442003-07-28 09:56:35 +000010606}
10607
10608static int
Denys Vlasenko7b3fa1e2016-10-01 15:10:16 +020010609evalbltin(const struct builtincmd *cmd, int argc, char **argv, int flags)
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010610{
Eric Andersenc470f442003-07-28 09:56:35 +000010611 char *volatile savecmdname;
10612 struct jmploc *volatile savehandler;
10613 struct jmploc jmploc;
Denys Vlasenko7b3fa1e2016-10-01 15:10:16 +020010614 int status;
Eric Andersenc470f442003-07-28 09:56:35 +000010615 int i;
10616
10617 savecmdname = commandname;
Denys Vlasenkoa2d121c2016-09-30 11:30:11 +020010618 savehandler = exception_handler;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010619 i = setjmp(jmploc.loc);
10620 if (i)
Eric Andersenc470f442003-07-28 09:56:35 +000010621 goto cmddone;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010622 exception_handler = &jmploc;
Eric Andersenc470f442003-07-28 09:56:35 +000010623 commandname = argv[0];
10624 argptr = argv + 1;
10625 optptr = NULL; /* initialize nextopt */
Denys Vlasenko7b3fa1e2016-10-01 15:10:16 +020010626 if (cmd == EVALCMD)
10627 status = evalcmd(argc, argv, flags);
10628 else
10629 status = (*cmd->builtin)(argc, argv);
Denis Vlasenkob012b102007-02-19 22:43:01 +000010630 flush_stdout_stderr();
Denys Vlasenko7b3fa1e2016-10-01 15:10:16 +020010631 status |= ferror(stdout);
10632 exitstatus = status;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010633 cmddone:
Rob Landleyf296f0b2006-07-06 01:09:21 +000010634 clearerr(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +000010635 commandname = savecmdname;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010636 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +000010637
10638 return i;
10639}
10640
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010641static int
10642goodname(const char *p)
Glenn L McGrath16e45d72004-02-04 08:24:39 +000010643{
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +020010644 return endofname(p)[0] == '\0';
Glenn L McGrath16e45d72004-02-04 08:24:39 +000010645}
10646
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010647
Glenn L McGrath50812ff2002-08-23 13:14:48 +000010648/*
10649 * Search for a command. This is called before we fork so that the
10650 * location of the command will be available in the parent as well as
Glenn L McGrath16e45d72004-02-04 08:24:39 +000010651 * the child. The check for "goodname" is an overly conservative
10652 * check that the name will not be subject to expansion.
Glenn L McGrath50812ff2002-08-23 13:14:48 +000010653 */
Eric Andersenc470f442003-07-28 09:56:35 +000010654static void
10655prehash(union node *n)
Glenn L McGrath50812ff2002-08-23 13:14:48 +000010656{
10657 struct cmdentry entry;
10658
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010659 if (n->type == NCMD && n->ncmd.args && goodname(n->ncmd.args->narg.text))
10660 find_command(n->ncmd.args->narg.text, &entry, 0, pathval());
Glenn L McGrath50812ff2002-08-23 13:14:48 +000010661}
10662
Eric Andersencb57d552001-06-28 07:25:16 +000010663
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000010664/* ============ Builtin commands
10665 *
10666 * Builtin commands whose functions are closely tied to evaluation
10667 * are implemented here.
Eric Andersencb57d552001-06-28 07:25:16 +000010668 */
10669
10670/*
Eric Andersencb57d552001-06-28 07:25:16 +000010671 * Handle break and continue commands. Break, continue, and return are
10672 * all handled by setting the evalskip flag. The evaluation routines
10673 * above all check this flag, and if it is set they start skipping
10674 * commands rather than executing them. The variable skipcount is
10675 * the number of loops to break/continue, or the number of function
10676 * levels to return. (The latter is always 1.) It should probably
10677 * be an error to break out of more loops than exist, but it isn't
10678 * in the standard shell so we don't make it one here.
10679 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010680static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000010681breakcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000010682{
Denis Vlasenko68404f12008-03-17 09:00:54 +000010683 int n = argv[1] ? number(argv[1]) : 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010684
Aaron Lehmann2aef3a62001-12-31 06:03:12 +000010685 if (n <= 0)
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +020010686 ash_msg_and_raise_error(msg_illnum, argv[1]);
Eric Andersencb57d552001-06-28 07:25:16 +000010687 if (n > loopnest)
10688 n = loopnest;
10689 if (n > 0) {
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000010690 evalskip = (**argv == 'c') ? SKIPCONT : SKIPBREAK;
Eric Andersencb57d552001-06-28 07:25:16 +000010691 skipcount = n;
10692 }
10693 return 0;
10694}
10695
Eric Andersenc470f442003-07-28 09:56:35 +000010696
Denys Vlasenko70392332016-10-27 02:31:55 +020010697/*
Eric Andersen90898442003-08-06 11:20:52 +000010698 * This implements the input routines used by the parser.
Eric Andersencb57d552001-06-28 07:25:16 +000010699 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +000010700
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010701enum {
10702 INPUT_PUSH_FILE = 1,
10703 INPUT_NOFILE_OK = 2,
10704};
Eric Andersencb57d552001-06-28 07:25:16 +000010705
Denis Vlasenkob07a4962008-06-22 13:16:23 +000010706static smallint checkkwd;
Denis Vlasenko99eb8502007-02-23 21:09:49 +000010707/* values of checkkwd variable */
10708#define CHKALIAS 0x1
10709#define CHKKWD 0x2
10710#define CHKNL 0x4
Denys Vlasenkoa7328982017-07-29 19:57:28 +020010711#define CHKEOFMARK 0x8
Denis Vlasenko99eb8502007-02-23 21:09:49 +000010712
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010713/*
10714 * Push a string back onto the input at this current parsefile level.
10715 * We handle aliases this way.
10716 */
10717#if !ENABLE_ASH_ALIAS
10718#define pushstring(s, ap) pushstring(s)
10719#endif
10720static void
10721pushstring(char *s, struct alias *ap)
10722{
10723 struct strpush *sp;
10724 int len;
10725
10726 len = strlen(s);
10727 INT_OFF;
Denys Vlasenko48cb9832021-09-08 09:52:04 +020010728 if (g_parsefile->strpush || g_parsefile->spfree) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010729 sp = ckzalloc(sizeof(*sp));
10730 sp->prev = g_parsefile->strpush;
10731 } else {
10732 sp = &(g_parsefile->basestrpush);
10733 }
10734 g_parsefile->strpush = sp;
10735 sp->prev_string = g_parsefile->next_to_pgetc;
10736 sp->prev_left_in_line = g_parsefile->left_in_line;
Denys Vlasenko3b4d04b2016-09-29 02:11:19 +020010737 sp->unget = g_parsefile->unget;
Denys Vlasenko48cb9832021-09-08 09:52:04 +020010738 sp->spfree = g_parsefile->spfree;
Denys Vlasenko3b4d04b2016-09-29 02:11:19 +020010739 memcpy(sp->lastc, g_parsefile->lastc, sizeof(sp->lastc));
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010740#if ENABLE_ASH_ALIAS
10741 sp->ap = ap;
10742 if (ap) {
10743 ap->flag |= ALIASINUSE;
10744 sp->string = s;
10745 }
10746#endif
10747 g_parsefile->next_to_pgetc = s;
10748 g_parsefile->left_in_line = len;
Denys Vlasenko3b4d04b2016-09-29 02:11:19 +020010749 g_parsefile->unget = 0;
Denys Vlasenko48cb9832021-09-08 09:52:04 +020010750 g_parsefile->spfree = NULL;
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010751 INT_ON;
10752}
10753
Denys Vlasenko48cb9832021-09-08 09:52:04 +020010754static void popstring(void)
Eric Andersenc470f442003-07-28 09:56:35 +000010755{
Denis Vlasenkob07a4962008-06-22 13:16:23 +000010756 struct strpush *sp = g_parsefile->strpush;
Eric Andersenc470f442003-07-28 09:56:35 +000010757
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010758 INT_OFF;
Denis Vlasenko131ae172007-02-18 13:00:19 +000010759#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010760 if (sp->ap) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010761 if (g_parsefile->next_to_pgetc[-1] == ' '
10762 || g_parsefile->next_to_pgetc[-1] == '\t'
10763 ) {
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010764 checkkwd |= CHKALIAS;
Glenn L McGrath28939ad2004-07-21 10:20:19 +000010765 }
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010766 if (sp->string != sp->ap->val) {
10767 free(sp->string);
10768 }
Glenn L McGrath28939ad2004-07-21 10:20:19 +000010769 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +000010770#endif
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010771 g_parsefile->next_to_pgetc = sp->prev_string;
10772 g_parsefile->left_in_line = sp->prev_left_in_line;
Denys Vlasenko3b4d04b2016-09-29 02:11:19 +020010773 g_parsefile->unget = sp->unget;
10774 memcpy(g_parsefile->lastc, sp->lastc, sizeof(sp->lastc));
Denis Vlasenkob07a4962008-06-22 13:16:23 +000010775 g_parsefile->strpush = sp->prev;
Denys Vlasenko48cb9832021-09-08 09:52:04 +020010776 g_parsefile->spfree = sp;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010777 INT_ON;
10778}
Denis Vlasenko8e1c7152007-01-22 07:21:38 +000010779
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010780static int
10781preadfd(void)
Eric Andersencb57d552001-06-28 07:25:16 +000010782{
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010783 int nr;
Denis Vlasenko6a0ad252008-07-25 13:34:05 +000010784 char *buf = g_parsefile->buf;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010785
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010786 g_parsefile->next_to_pgetc = buf;
Denis Vlasenko38f63192007-01-22 09:03:07 +000010787#if ENABLE_FEATURE_EDITING
Denys Vlasenko4ac35a32020-11-16 13:09:37 +010010788 /* retry: */
Denys Vlasenko79b3d422010-06-03 04:29:08 +020010789 if (!iflag || g_parsefile->pf_fd != STDIN_FILENO)
Ron Yorston61d6ae22015-04-19 10:50:25 +010010790 nr = nonblock_immune_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1);
Eric Andersenc470f442003-07-28 09:56:35 +000010791 else {
Denys Vlasenko66c5b122011-02-08 05:07:02 +010010792# if ENABLE_ASH_IDLE_TIMEOUT
Denys Vlasenko84ea60e2017-08-02 17:27:28 +020010793 int timeout = -1;
Denys Vlasenko897475a2019-06-01 16:35:09 +020010794 const char *tmout_var = lookupvar("TMOUT");
10795 if (tmout_var) {
10796 timeout = atoi(tmout_var) * 1000;
10797 if (timeout <= 0)
10798 timeout = -1;
Denys Vlasenko66c5b122011-02-08 05:07:02 +010010799 }
Denys Vlasenko84ea60e2017-08-02 17:27:28 +020010800 line_input_state->timeout = timeout;
Denys Vlasenko66c5b122011-02-08 05:07:02 +010010801# endif
Denys Vlasenko8c52f802011-02-04 17:36:21 +010010802# if ENABLE_FEATURE_TAB_COMPLETION
Denis Vlasenko8e1c7152007-01-22 07:21:38 +000010803 line_input_state->path_lookup = pathval();
Denys Vlasenko8c52f802011-02-04 17:36:21 +010010804# endif
Denys Vlasenkoe9ab07c2014-08-13 18:00:08 +020010805 reinit_unicode_for_ash();
Denys Vlasenko68b402e2022-01-13 01:05:03 +010010806 again:
Denys Vlasenko12566e72022-01-17 03:02:40 +010010807 /* For shell, LI_INTERRUPTIBLE is set:
10808 * read_line_input will abort on either
10809 * getting EINTR in poll(), or if it sees bb_got_signal != 0
10810 * (IOW: if signal arrives before poll() is reached).
10811 * Interactive testcases:
10812 * (while kill -INT $$; do sleep 1; done) &
10813 * #^^^ prints ^C, prints prompt, repeats
10814 * trap 'echo I' int; (while kill -INT $$; do sleep 1; done) &
10815 * #^^^ prints ^C, prints "I", prints prompt, repeats
10816 * trap 'echo T' term; (while kill $$; do sleep 1; done) &
10817 * #^^^ prints "T", prints prompt, repeats
10818 * #(bash 5.0.17 exits after first "T", looks like a bug)
10819 */
10820 bb_got_signal = 0;
10821 INT_OFF; /* no longjmp'ing out of read_line_input please */
Denys Vlasenko84ea60e2017-08-02 17:27:28 +020010822 nr = read_line_input(line_input_state, cmdedit_prompt, buf, IBUFSIZ);
Denys Vlasenko12566e72022-01-17 03:02:40 +010010823 if (bb_got_signal == SIGINT)
10824 write(STDOUT_FILENO, "^C\n", 3);
10825 INT_ON; /* here non-blocked SIGINT will longjmp */
Denis Vlasenko8e1c7152007-01-22 07:21:38 +000010826 if (nr == 0) {
Denys Vlasenko4b89d512016-11-25 03:41:03 +010010827 /* ^C pressed, "convert" to SIGINT */
Denys Vlasenko12566e72022-01-17 03:02:40 +010010828 write(STDOUT_FILENO, "^C\n", 3);
10829 raise(SIGINT); /* here non-blocked SIGINT will longjmp */
Denys Vlasenko68b402e2022-01-13 01:05:03 +010010830 /* raise(SIGINT) did not work! (e.g. if SIGINT
Denys Vlasenko931c55f2022-01-13 12:50:48 +010010831 * is SIG_IGNed on startup, it stays SIG_IGNed)
Denys Vlasenko68b402e2022-01-13 01:05:03 +010010832 */
Denis Vlasenko8e1c7152007-01-22 07:21:38 +000010833 if (trap[SIGINT]) {
Denys Vlasenko12566e72022-01-17 03:02:40 +010010834 empty_line_input:
Glenn L McGrath16e45d72004-02-04 08:24:39 +000010835 buf[0] = '\n';
Denis Vlasenko8e1c7152007-01-22 07:21:38 +000010836 buf[1] = '\0';
Glenn L McGrath16e45d72004-02-04 08:24:39 +000010837 return 1;
10838 }
Denys Vlasenko8660aeb2016-11-24 17:44:02 +010010839 exitstatus = 128 + SIGINT;
Denys Vlasenko68b402e2022-01-13 01:05:03 +010010840 /* bash behavior on ^C + ignored SIGINT: */
Denys Vlasenko68b402e2022-01-13 01:05:03 +010010841 goto again;
Eric Andersenc470f442003-07-28 09:56:35 +000010842 }
Denys Vlasenko66c5b122011-02-08 05:07:02 +010010843 if (nr < 0) {
10844 if (errno == 0) {
Denys Vlasenko12566e72022-01-17 03:02:40 +010010845 /* ^D pressed */
Denys Vlasenko66c5b122011-02-08 05:07:02 +010010846 nr = 0;
10847 }
Denys Vlasenko12566e72022-01-17 03:02:40 +010010848 else if (errno == EINTR) { /* got signal? */
10849 if (bb_got_signal != SIGINT)
10850 write(STDOUT_FILENO, "\n", 1);
10851 goto empty_line_input;
10852 }
Denys Vlasenko66c5b122011-02-08 05:07:02 +010010853# if ENABLE_ASH_IDLE_TIMEOUT
10854 else if (errno == EAGAIN && timeout > 0) {
Denys Vlasenkod60752f2015-10-07 22:42:45 +020010855 puts("\007timed out waiting for input: auto-logout");
Denys Vlasenko66c5b122011-02-08 05:07:02 +010010856 exitshell();
10857 }
10858# endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010859 }
Eric Andersencb57d552001-06-28 07:25:16 +000010860 }
10861#else
Ron Yorston61d6ae22015-04-19 10:50:25 +010010862 nr = nonblock_immune_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1);
Eric Andersencb57d552001-06-28 07:25:16 +000010863#endif
10864
Denys Vlasenko80c5b682011-05-08 21:21:10 +020010865#if 0 /* disabled: nonblock_immune_read() handles this problem */
Eric Andersencb57d552001-06-28 07:25:16 +000010866 if (nr < 0) {
Eric Andersencb57d552001-06-28 07:25:16 +000010867 if (parsefile->fd == 0 && errno == EWOULDBLOCK) {
Denis Vlasenkod37f2222007-08-19 13:42:08 +000010868 int flags = fcntl(0, F_GETFL);
Denis Vlasenko9cb220b2007-12-09 10:03:28 +000010869 if (flags >= 0 && (flags & O_NONBLOCK)) {
10870 flags &= ~O_NONBLOCK;
Eric Andersencb57d552001-06-28 07:25:16 +000010871 if (fcntl(0, F_SETFL, flags) >= 0) {
10872 out2str("sh: turning off NDELAY mode\n");
10873 goto retry;
10874 }
10875 }
10876 }
10877 }
Denis Vlasenkoe376d452008-02-20 22:23:24 +000010878#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010879 return nr;
10880}
10881
10882/*
10883 * Refill the input buffer and return the next input character:
10884 *
10885 * 1) If a string was pushed back on the input, pop it;
Ron Yorston0beee202021-09-12 11:20:33 +010010886 * 2) If we are reading from a string we can't refill the buffer, return EOF.
Denys Vlasenko883cea42009-07-11 15:31:59 +020010887 * 3) If there is more stuff in this buffer, use it else call read to fill it.
Eric Andersencb57d552001-06-28 07:25:16 +000010888 * 4) Process input up to the next newline, deleting nul characters.
10889 */
Denis Vlasenko727752d2008-11-28 03:41:47 +000010890//#define pgetc_debug(...) bb_error_msg(__VA_ARGS__)
10891#define pgetc_debug(...) ((void)0)
Denys Vlasenko48cb9832021-09-08 09:52:04 +020010892static int __pgetc(void);
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010893static int
Eric Andersenc470f442003-07-28 09:56:35 +000010894preadbuffer(void)
Eric Andersencb57d552001-06-28 07:25:16 +000010895{
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000010896 char *q;
Eric Andersencb57d552001-06-28 07:25:16 +000010897 int more;
Eric Andersencb57d552001-06-28 07:25:16 +000010898
Denys Vlasenko48cb9832021-09-08 09:52:04 +020010899 if (unlikely(g_parsefile->strpush)) {
Eric Andersencb57d552001-06-28 07:25:16 +000010900 popstring();
Denys Vlasenko48cb9832021-09-08 09:52:04 +020010901 return __pgetc();
Eric Andersencb57d552001-06-28 07:25:16 +000010902 }
Denis Vlasenko727752d2008-11-28 03:41:47 +000010903
Ron Yorston0beee202021-09-12 11:20:33 +010010904 if (g_parsefile->buf == NULL) {
Denis Vlasenko727752d2008-11-28 03:41:47 +000010905 pgetc_debug("preadbuffer PEOF1");
Eric Andersencb57d552001-06-28 07:25:16 +000010906 return PEOF;
Denis Vlasenko727752d2008-11-28 03:41:47 +000010907 }
Eric Andersencb57d552001-06-28 07:25:16 +000010908
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010909 more = g_parsefile->left_in_buffer;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000010910 if (more <= 0) {
Denis Vlasenko727752d2008-11-28 03:41:47 +000010911 flush_stdout_stderr();
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010912 again:
10913 more = preadfd();
10914 if (more <= 0) {
Ron Yorston0beee202021-09-12 11:20:33 +010010915 g_parsefile->left_in_buffer = g_parsefile->left_in_line = 0;
Denis Vlasenko727752d2008-11-28 03:41:47 +000010916 pgetc_debug("preadbuffer PEOF2");
Eric Andersencb57d552001-06-28 07:25:16 +000010917 return PEOF;
10918 }
10919 }
10920
Denis Vlasenko727752d2008-11-28 03:41:47 +000010921 /* Find out where's the end of line.
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010922 * Set g_parsefile->left_in_line
10923 * and g_parsefile->left_in_buffer acordingly.
Denis Vlasenko727752d2008-11-28 03:41:47 +000010924 * NUL chars are deleted.
10925 */
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010926 q = g_parsefile->next_to_pgetc;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000010927 for (;;) {
Denis Vlasenko727752d2008-11-28 03:41:47 +000010928 char c;
Eric Andersencb57d552001-06-28 07:25:16 +000010929
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000010930 more--;
Eric Andersenc470f442003-07-28 09:56:35 +000010931
Denis Vlasenko727752d2008-11-28 03:41:47 +000010932 c = *q;
10933 if (c == '\0') {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000010934 memmove(q, q + 1, more);
Denis Vlasenko727752d2008-11-28 03:41:47 +000010935 } else {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000010936 q++;
10937 if (c == '\n') {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010938 g_parsefile->left_in_line = q - g_parsefile->next_to_pgetc - 1;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000010939 break;
10940 }
Eric Andersencb57d552001-06-28 07:25:16 +000010941 }
10942
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000010943 if (more <= 0) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010944 g_parsefile->left_in_line = q - g_parsefile->next_to_pgetc - 1;
10945 if (g_parsefile->left_in_line < 0)
Eric Andersencb57d552001-06-28 07:25:16 +000010946 goto again;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000010947 break;
Eric Andersencb57d552001-06-28 07:25:16 +000010948 }
10949 }
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010950 g_parsefile->left_in_buffer = more;
Eric Andersencb57d552001-06-28 07:25:16 +000010951
Eric Andersencb57d552001-06-28 07:25:16 +000010952 if (vflag) {
Denis Vlasenko727752d2008-11-28 03:41:47 +000010953 char save = *q;
10954 *q = '\0';
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010955 out2str(g_parsefile->next_to_pgetc);
Denis Vlasenko727752d2008-11-28 03:41:47 +000010956 *q = save;
Eric Andersencb57d552001-06-28 07:25:16 +000010957 }
10958
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010959 pgetc_debug("preadbuffer at %d:%p'%s'",
10960 g_parsefile->left_in_line,
10961 g_parsefile->next_to_pgetc,
10962 g_parsefile->next_to_pgetc);
Denys Vlasenkocd716832009-11-28 22:14:02 +010010963 return (unsigned char)*g_parsefile->next_to_pgetc++;
Eric Andersencb57d552001-06-28 07:25:16 +000010964}
10965
Denys Vlasenkoce332a22016-10-02 23:47:34 +020010966static void
10967nlprompt(void)
10968{
Roberto A. Fogliettae0bf3df2021-09-07 01:19:31 +020010969 if (trap_depth == 0)
10970 g_parsefile->linno++;
Denys Vlasenkoce332a22016-10-02 23:47:34 +020010971 setprompt_if(doprompt, 2);
10972}
10973static void
10974nlnoprompt(void)
10975{
Roberto A. Fogliettae0bf3df2021-09-07 01:19:31 +020010976 if (trap_depth == 0)
10977 g_parsefile->linno++;
Denys Vlasenkoce332a22016-10-02 23:47:34 +020010978 needprompt = doprompt;
10979}
10980
Denys Vlasenko48cb9832021-09-08 09:52:04 +020010981static void freestrings(struct strpush *sp)
10982{
10983 INT_OFF;
10984 do {
10985 struct strpush *psp;
Denys Vlasenko5b026d12021-09-28 17:41:56 +020010986#if ENABLE_ASH_ALIAS
Denys Vlasenko48cb9832021-09-08 09:52:04 +020010987 if (sp->ap) {
10988 sp->ap->flag &= ~ALIASINUSE;
10989 if (sp->ap->flag & ALIASDEAD) {
10990 unalias(sp->ap->name);
10991 }
10992 }
Denys Vlasenko5b026d12021-09-28 17:41:56 +020010993#endif
Denys Vlasenko48cb9832021-09-08 09:52:04 +020010994 psp = sp;
10995 sp = sp->spfree;
10996
10997 if (psp != &(g_parsefile->basestrpush))
10998 free(psp);
10999 } while (sp);
11000
11001 g_parsefile->spfree = NULL;
11002 INT_ON;
11003}
11004
11005static int __pgetc(void)
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000011006{
Denys Vlasenko3b4d04b2016-09-29 02:11:19 +020011007 int c;
11008
11009 pgetc_debug("pgetc at %d:%p'%s'",
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011010 g_parsefile->left_in_line,
11011 g_parsefile->next_to_pgetc,
11012 g_parsefile->next_to_pgetc);
Denys Vlasenko3b4d04b2016-09-29 02:11:19 +020011013 if (g_parsefile->unget)
11014 return g_parsefile->lastc[--g_parsefile->unget];
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +000011015
Denys Vlasenko3b4d04b2016-09-29 02:11:19 +020011016 if (--g_parsefile->left_in_line >= 0)
Denys Vlasenko2fe66b12016-12-12 17:39:12 +010011017 c = (unsigned char)*g_parsefile->next_to_pgetc++;
Denys Vlasenko3b4d04b2016-09-29 02:11:19 +020011018 else
11019 c = preadbuffer();
11020
11021 g_parsefile->lastc[1] = g_parsefile->lastc[0];
11022 g_parsefile->lastc[0] = c;
11023
11024 return c;
11025}
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000011026
Denys Vlasenko48cb9832021-09-08 09:52:04 +020011027/*
11028 * Read a character from the script, returning PEOF on end of file.
11029 * Nul characters in the input are silently discarded.
11030 */
11031static int pgetc(void)
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000011032{
Denys Vlasenko48cb9832021-09-08 09:52:04 +020011033 struct strpush *sp = g_parsefile->spfree;
11034
11035 if (unlikely(sp))
11036 freestrings(sp);
11037
11038 return __pgetc();
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000011039}
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000011040
11041/*
Denys Vlasenko3b4d04b2016-09-29 02:11:19 +020011042 * Undo a call to pgetc. Only two characters may be pushed back.
Eric Andersenc470f442003-07-28 09:56:35 +000011043 * PEOF may be pushed back.
11044 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011045static void
Eric Andersenc470f442003-07-28 09:56:35 +000011046pungetc(void)
11047{
Denys Vlasenko3b4d04b2016-09-29 02:11:19 +020011048 g_parsefile->unget++;
Eric Andersencb57d552001-06-28 07:25:16 +000011049}
11050
Denys Vlasenko73c3e072016-09-29 17:17:04 +020011051/* This one eats backslash+newline */
11052static int
11053pgetc_eatbnl(void)
11054{
11055 int c;
11056
11057 while ((c = pgetc()) == '\\') {
11058 if (pgetc() != '\n') {
11059 pungetc();
11060 break;
11061 }
11062
Denys Vlasenkoce332a22016-10-02 23:47:34 +020011063 nlprompt();
Denys Vlasenko73c3e072016-09-29 17:17:04 +020011064 }
11065
11066 return c;
11067}
11068
Denys Vlasenko216913c2018-04-02 12:35:04 +020011069struct synstack {
11070 smalluint syntax;
11071 uint8_t innerdq :1;
11072 uint8_t varpushed :1;
11073 uint8_t dblquote :1;
11074 int varnest; /* levels of variables expansion */
11075 int dqvarnest; /* levels of variables expansion within double quotes */
11076 int parenlevel; /* levels of parens in arithmetic */
11077 struct synstack *prev;
11078 struct synstack *next;
11079};
11080
Denys Vlasenkof7eea8c2020-02-14 16:16:34 +010011081static int
11082pgetc_top(struct synstack *stack)
11083{
11084 return stack->syntax == SQSYNTAX ? pgetc() : pgetc_eatbnl();
11085}
11086
Denys Vlasenko216913c2018-04-02 12:35:04 +020011087static void
11088synstack_push(struct synstack **stack, struct synstack *next, int syntax)
11089{
11090 memset(next, 0, sizeof(*next));
11091 next->syntax = syntax;
11092 next->next = *stack;
11093 (*stack)->prev = next;
11094 *stack = next;
11095}
11096
11097static ALWAYS_INLINE void
11098synstack_pop(struct synstack **stack)
11099{
11100 *stack = (*stack)->next;
11101}
11102
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000011103/*
11104 * To handle the "." command, a stack of input files is used. Pushfile
11105 * adds a new entry to the stack and popfile restores the previous level.
11106 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011107static void
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000011108pushfile(void)
Eric Andersenc470f442003-07-28 09:56:35 +000011109{
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000011110 struct parsefile *pf;
11111
Denis Vlasenko597906c2008-02-20 16:38:54 +000011112 pf = ckzalloc(sizeof(*pf));
Denis Vlasenkob07a4962008-06-22 13:16:23 +000011113 pf->prev = g_parsefile;
Denys Vlasenko79b3d422010-06-03 04:29:08 +020011114 pf->pf_fd = -1;
Denis Vlasenko597906c2008-02-20 16:38:54 +000011115 /*pf->strpush = NULL; - ckzalloc did it */
Denys Vlasenko48cb9832021-09-08 09:52:04 +020011116 /*pf->spfree = NULL;*/
Denis Vlasenko597906c2008-02-20 16:38:54 +000011117 /*pf->basestrpush.prev = NULL;*/
Denys Vlasenko3b4d04b2016-09-29 02:11:19 +020011118 /*pf->unget = 0;*/
Denis Vlasenkob07a4962008-06-22 13:16:23 +000011119 g_parsefile = pf;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000011120}
11121
11122static void
11123popfile(void)
11124{
Denis Vlasenkob07a4962008-06-22 13:16:23 +000011125 struct parsefile *pf = g_parsefile;
Eric Andersenc470f442003-07-28 09:56:35 +000011126
Denys Vlasenko493b9ca2016-10-30 18:27:14 +010011127 if (pf == &basepf)
11128 return;
11129
Denis Vlasenkob012b102007-02-19 22:43:01 +000011130 INT_OFF;
Denys Vlasenko79b3d422010-06-03 04:29:08 +020011131 if (pf->pf_fd >= 0)
11132 close(pf->pf_fd);
Denis Vlasenko60818682007-09-28 22:07:23 +000011133 free(pf->buf);
Denys Vlasenko48cb9832021-09-08 09:52:04 +020011134 if (g_parsefile->spfree)
11135 freestrings(g_parsefile->spfree);
11136 while (pf->strpush) {
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000011137 popstring();
Denys Vlasenko48cb9832021-09-08 09:52:04 +020011138 freestrings(g_parsefile->spfree);
11139 }
Denis Vlasenkob07a4962008-06-22 13:16:23 +000011140 g_parsefile = pf->prev;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000011141 free(pf);
Denis Vlasenkob012b102007-02-19 22:43:01 +000011142 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000011143}
11144
Denys Vlasenko1c5eb882018-08-05 17:07:26 +020011145static void
11146unwindfiles(struct parsefile *stop)
11147{
11148 while (g_parsefile != stop)
11149 popfile();
11150}
11151
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000011152/*
11153 * Return to top level.
11154 */
11155static void
11156popallfiles(void)
11157{
Denys Vlasenko1c5eb882018-08-05 17:07:26 +020011158 unwindfiles(&basepf);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000011159}
11160
11161/*
11162 * Close the file(s) that the shell is reading commands from. Called
11163 * after a fork is done.
11164 */
11165static void
11166closescript(void)
11167{
11168 popallfiles();
Denys Vlasenko79b3d422010-06-03 04:29:08 +020011169 if (g_parsefile->pf_fd > 0) {
11170 close(g_parsefile->pf_fd);
11171 g_parsefile->pf_fd = 0;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000011172 }
11173}
11174
11175/*
11176 * Like setinputfile, but takes an open file descriptor. Call this with
11177 * interrupts off.
11178 */
11179static void
11180setinputfd(int fd, int push)
11181{
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000011182 if (push) {
11183 pushfile();
Denis Vlasenko727752d2008-11-28 03:41:47 +000011184 g_parsefile->buf = NULL;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000011185 }
Denys Vlasenko79b3d422010-06-03 04:29:08 +020011186 g_parsefile->pf_fd = fd;
Denis Vlasenkob07a4962008-06-22 13:16:23 +000011187 if (g_parsefile->buf == NULL)
11188 g_parsefile->buf = ckmalloc(IBUFSIZ);
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011189 g_parsefile->left_in_buffer = 0;
11190 g_parsefile->left_in_line = 0;
11191 g_parsefile->linno = 1;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000011192}
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011193
Eric Andersenc470f442003-07-28 09:56:35 +000011194/*
11195 * Set the input to take input from a file. If push is set, push the
11196 * old input onto the stack first.
11197 */
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000011198static int
11199setinputfile(const char *fname, int flags)
Eric Andersenc470f442003-07-28 09:56:35 +000011200{
11201 int fd;
Eric Andersenc470f442003-07-28 09:56:35 +000011202
Denis Vlasenkob012b102007-02-19 22:43:01 +000011203 INT_OFF;
Denys Vlasenko60fb98e2018-03-30 22:15:14 +020011204 fd = open(fname, O_RDONLY | O_CLOEXEC);
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011205 if (fd < 0) {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000011206 if (flags & INPUT_NOFILE_OK)
11207 goto out;
Denys Vlasenkob7adf7a2016-10-25 17:00:13 +020011208 exitstatus = 127;
Johannes Schindelin20a63b22017-08-22 22:03:17 +020011209 ash_msg_and_raise_perror("can't open '%s'", fname);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000011210 }
Denys Vlasenko64774602016-10-26 15:24:30 +020011211 if (fd < 10)
11212 fd = savefd(fd);
Denys Vlasenko60fb98e2018-03-30 22:15:14 +020011213 else if (O_CLOEXEC == 0) /* old libc */
Denys Vlasenkoe19923f2016-10-26 15:38:44 +020011214 close_on_exec_on(fd);
Denys Vlasenko60fb98e2018-03-30 22:15:14 +020011215
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000011216 setinputfd(fd, flags & INPUT_PUSH_FILE);
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011217 out:
Denis Vlasenkob012b102007-02-19 22:43:01 +000011218 INT_ON;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000011219 return fd;
Eric Andersenc470f442003-07-28 09:56:35 +000011220}
11221
Eric Andersencb57d552001-06-28 07:25:16 +000011222/*
11223 * Like setinputfile, but takes input from a string.
11224 */
Eric Andersenc470f442003-07-28 09:56:35 +000011225static void
11226setinputstring(char *string)
Eric Andersen62483552001-07-10 06:09:16 +000011227{
Denis Vlasenkob012b102007-02-19 22:43:01 +000011228 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +000011229 pushfile();
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011230 g_parsefile->next_to_pgetc = string;
11231 g_parsefile->left_in_line = strlen(string);
Denis Vlasenkob07a4962008-06-22 13:16:23 +000011232 g_parsefile->buf = NULL;
Denys Vlasenkod6c9cbc2021-09-07 18:01:49 +020011233 g_parsefile->linno = lineno;
Denis Vlasenkob012b102007-02-19 22:43:01 +000011234 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +000011235}
11236
11237
Denys Vlasenko70392332016-10-27 02:31:55 +020011238/*
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011239 * Routines to check for mail.
Eric Andersencb57d552001-06-28 07:25:16 +000011240 */
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +000011241
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011242#if ENABLE_ASH_MAIL
Eric Andersencb57d552001-06-28 07:25:16 +000011243
Denys Vlasenko23841622015-10-09 15:52:03 +020011244/* Hash of mtimes of mailboxes */
11245static unsigned mailtime_hash;
Eric Andersenc470f442003-07-28 09:56:35 +000011246/* Set if MAIL or MAILPATH is changed. */
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011247static smallint mail_var_path_changed;
Eric Andersencb57d552001-06-28 07:25:16 +000011248
Eric Andersencb57d552001-06-28 07:25:16 +000011249/*
Eric Andersenc470f442003-07-28 09:56:35 +000011250 * Print appropriate message(s) if mail has arrived.
11251 * If mail_var_path_changed is set,
11252 * then the value of MAIL has mail_var_path_changed,
11253 * so we just update the values.
Eric Andersencb57d552001-06-28 07:25:16 +000011254 */
Eric Andersenc470f442003-07-28 09:56:35 +000011255static void
11256chkmail(void)
Eric Andersencb57d552001-06-28 07:25:16 +000011257{
Eric Andersencb57d552001-06-28 07:25:16 +000011258 const char *mpath;
11259 char *p;
11260 char *q;
Denys Vlasenko23841622015-10-09 15:52:03 +020011261 unsigned new_hash;
Eric Andersencb57d552001-06-28 07:25:16 +000011262 struct stackmark smark;
11263 struct stat statb;
11264
Eric Andersencb57d552001-06-28 07:25:16 +000011265 setstackmark(&smark);
Eric Andersenc470f442003-07-28 09:56:35 +000011266 mpath = mpathset() ? mpathval() : mailval();
Denys Vlasenko23841622015-10-09 15:52:03 +020011267 new_hash = 0;
11268 for (;;) {
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +010011269 int len;
11270
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +010011271 len = padvance_magic(&mpath, nullstr, 2);
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +010011272 if (!len)
11273 break;
11274 p = stackblock();
Eric Andersencb57d552001-06-28 07:25:16 +000011275 break;
11276 if (*p == '\0')
11277 continue;
Denis Vlasenkof7d56652008-03-25 05:51:41 +000011278 for (q = p; *q; q++)
11279 continue;
Denis Vlasenkoa7189f02006-11-17 20:29:00 +000011280#if DEBUG
Eric Andersencb57d552001-06-28 07:25:16 +000011281 if (q[-1] != '/')
11282 abort();
11283#endif
Eric Andersenc470f442003-07-28 09:56:35 +000011284 q[-1] = '\0'; /* delete trailing '/' */
11285 if (stat(p, &statb) < 0) {
Eric Andersenc470f442003-07-28 09:56:35 +000011286 continue;
Eric Andersencb57d552001-06-28 07:25:16 +000011287 }
Denys Vlasenko23841622015-10-09 15:52:03 +020011288 /* Very simplistic "hash": just a sum of all mtimes */
11289 new_hash += (unsigned)statb.st_mtime;
11290 }
11291 if (!mail_var_path_changed && mailtime_hash != new_hash) {
Denys Vlasenko4cd99e72015-10-09 16:02:53 +020011292 if (mailtime_hash != 0)
11293 out2str("you have mail\n");
Denys Vlasenko23841622015-10-09 15:52:03 +020011294 mailtime_hash = new_hash;
Eric Andersencb57d552001-06-28 07:25:16 +000011295 }
Eric Andersenc470f442003-07-28 09:56:35 +000011296 mail_var_path_changed = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000011297 popstackmark(&smark);
11298}
Eric Andersencb57d552001-06-28 07:25:16 +000011299
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020011300static void FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000011301changemail(const char *val UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000011302{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011303 mail_var_path_changed = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000011304}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +000011305
Denis Vlasenko131ae172007-02-18 13:00:19 +000011306#endif /* ASH_MAIL */
Eric Andersenc470f442003-07-28 09:56:35 +000011307
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011308
11309/* ============ ??? */
11310
Eric Andersencb57d552001-06-28 07:25:16 +000011311/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000011312 * Set the shell parameters.
Eric Andersencb57d552001-06-28 07:25:16 +000011313 */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000011314static void
11315setparam(char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000011316{
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000011317 char **newparam;
11318 char **ap;
11319 int nparam;
Eric Andersencb57d552001-06-28 07:25:16 +000011320
Denis Vlasenkof7d56652008-03-25 05:51:41 +000011321 for (nparam = 0; argv[nparam]; nparam++)
11322 continue;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000011323 ap = newparam = ckmalloc((nparam + 1) * sizeof(*ap));
11324 while (*argv) {
11325 *ap++ = ckstrdup(*argv++);
Eric Andersencb57d552001-06-28 07:25:16 +000011326 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000011327 *ap = NULL;
11328 freeparam(&shellparam);
Denis Vlasenko01631112007-12-16 17:20:38 +000011329 shellparam.malloced = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000011330 shellparam.nparam = nparam;
11331 shellparam.p = newparam;
11332#if ENABLE_ASH_GETOPTS
11333 shellparam.optind = 1;
11334 shellparam.optoff = -1;
11335#endif
Eric Andersencb57d552001-06-28 07:25:16 +000011336}
11337
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011338/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000011339 * Process shell options. The global variable argptr contains a pointer
11340 * to the argument list; we advance it past the options.
Denis Vlasenko94e87bc2008-02-14 16:51:58 +000011341 *
11342 * SUSv3 section 2.8.1 "Consequences of Shell Errors" says:
11343 * For a non-interactive shell, an error condition encountered
11344 * by a special built-in ... shall cause the shell to write a diagnostic message
11345 * to standard error and exit as shown in the following table:
Denis Vlasenko56244732008-02-17 15:14:04 +000011346 * Error Special Built-In
Denis Vlasenko94e87bc2008-02-14 16:51:58 +000011347 * ...
11348 * Utility syntax error (option or operand error) Shall exit
11349 * ...
11350 * However, in bug 1142 (http://busybox.net/bugs/view.php?id=1142)
11351 * we see that bash does not do that (set "finishes" with error code 1 instead,
11352 * and shell continues), and people rely on this behavior!
11353 * Testcase:
11354 * set -o barfoo 2>/dev/null
11355 * echo $?
11356 *
11357 * Oh well. Let's mimic that.
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011358 */
Denis Vlasenko28bf6712008-02-14 15:01:47 +000011359static int
Denis Vlasenkodddfaff2008-05-06 15:30:27 +000011360plus_minus_o(char *name, int val)
Eric Andersen62483552001-07-10 06:09:16 +000011361{
11362 int i;
11363
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011364 if (name) {
11365 for (i = 0; i < NOPTS; i++) {
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000011366 if (strcmp(name, optnames(i)) == 0) {
Eric Andersenc470f442003-07-28 09:56:35 +000011367 optlist[i] = val;
Denis Vlasenko28bf6712008-02-14 15:01:47 +000011368 return 0;
Eric Andersen62483552001-07-10 06:09:16 +000011369 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011370 }
Denis Vlasenkodddfaff2008-05-06 15:30:27 +000011371 ash_msg("illegal option %co %s", val ? '-' : '+', name);
Denis Vlasenko28bf6712008-02-14 15:01:47 +000011372 return 1;
Eric Andersen62483552001-07-10 06:09:16 +000011373 }
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000011374 for (i = 0; i < NOPTS; i++) {
Denys Vlasenko2f9c1242019-08-02 16:43:36 +020011375 if (optnames(i)[0] == '\0')
11376 continue;
Denis Vlasenkodddfaff2008-05-06 15:30:27 +000011377 if (val) {
11378 out1fmt("%-16s%s\n", optnames(i), optlist[i] ? "on" : "off");
11379 } else {
11380 out1fmt("set %co %s\n", optlist[i] ? '-' : '+', optnames(i));
11381 }
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000011382 }
Denis Vlasenko28bf6712008-02-14 15:01:47 +000011383 return 0;
Eric Andersen62483552001-07-10 06:09:16 +000011384}
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011385static void
11386setoption(int flag, int val)
11387{
11388 int i;
11389
11390 for (i = 0; i < NOPTS; i++) {
Denys Vlasenkof3634582019-06-03 12:21:04 +020011391 if (optletters(i) == flag && optnames(i)[0] != '\0') {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011392 optlist[i] = val;
11393 return;
11394 }
11395 }
Denis Vlasenkodddfaff2008-05-06 15:30:27 +000011396 ash_msg_and_raise_error("illegal option %c%c", val ? '-' : '+', flag);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011397 /* NOTREACHED */
11398}
Denys Vlasenko897475a2019-06-01 16:35:09 +020011399/* If login_sh is not NULL, we are called to parse command line opts,
11400 * not "set -opts"
11401 */
Denis Vlasenko28bf6712008-02-14 15:01:47 +000011402static int
Denys Vlasenko897475a2019-06-01 16:35:09 +020011403options(int *login_sh)
Eric Andersencb57d552001-06-28 07:25:16 +000011404{
11405 char *p;
11406 int val;
11407 int c;
11408
Denys Vlasenko004cefa2022-01-12 17:21:14 +010011409 if (login_sh != NULL) /* if we came from startup code */
Eric Andersencb57d552001-06-28 07:25:16 +000011410 minusc = NULL;
11411 while ((p = *argptr) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011412 c = *p++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +000011413 if (c != '-' && c != '+')
11414 break;
11415 argptr++;
11416 val = 0; /* val = 0 if c == '+' */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011417 if (c == '-') {
Eric Andersencb57d552001-06-28 07:25:16 +000011418 val = 1;
Denis Vlasenko9f739442006-12-16 23:49:13 +000011419 if (p[0] == '\0' || LONE_DASH(p)) {
Denys Vlasenko004cefa2022-01-12 17:21:14 +010011420 if (login_sh == NULL) { /* we came from setcmd() */
Eric Andersen2870d962001-07-02 17:27:21 +000011421 /* "-" means turn off -x and -v */
11422 if (p[0] == '\0')
11423 xflag = vflag = 0;
11424 /* "--" means reset params */
11425 else if (*argptr == NULL)
Eric Andersencb57d552001-06-28 07:25:16 +000011426 setparam(argptr);
Eric Andersen2870d962001-07-02 17:27:21 +000011427 }
Denys Vlasenkob0b83432011-03-07 12:34:59 +010011428 break; /* "-" or "--" terminates options */
Eric Andersencb57d552001-06-28 07:25:16 +000011429 }
Eric Andersencb57d552001-06-28 07:25:16 +000011430 }
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +000011431 /* first char was + or - */
Eric Andersencb57d552001-06-28 07:25:16 +000011432 while ((c = *p++) != '\0') {
Denys Vlasenko004cefa2022-01-12 17:21:14 +010011433 if (login_sh != NULL) { /* if we came from startup code */
Denys Vlasenko897475a2019-06-01 16:35:09 +020011434 /* bash 3.2 indeed handles -c CMD and +c CMD the same */
11435 if (c == 'c') {
11436 minusc = p; /* command is after shell args */
Denys Vlasenkof3634582019-06-03 12:21:04 +020011437 cflag = 1;
11438 continue;
11439 }
11440 if (c == 's') { /* -s, +s */
11441 sflag = 1;
11442 continue;
11443 }
11444 if (c == 'i') { /* -i, +i */
11445 iflag = 1;
Denys Vlasenko897475a2019-06-01 16:35:09 +020011446 continue;
11447 }
11448 if (c == 'l') {
11449 *login_sh = 1; /* -l or +l == --login */
11450 continue;
11451 }
11452 /* bash does not accept +-login, we also won't */
11453 if (val && (c == '-')) { /* long options */
11454 if (strcmp(p, "login") == 0) {
11455 *login_sh = 1;
11456 }
Denys Vlasenko004cefa2022-01-12 17:21:14 +010011457/* TODO: --noprofile: e.g. if I want to run emergency shell from sulogin,
11458 * I want minimal/no shell init scripts - but it insists on running it as "-ash"...
11459 */
Denys Vlasenko897475a2019-06-01 16:35:09 +020011460 break;
11461 }
11462 }
11463 if (c == 'o') {
Denis Vlasenkodddfaff2008-05-06 15:30:27 +000011464 if (plus_minus_o(*argptr, val)) {
Denis Vlasenko28bf6712008-02-14 15:01:47 +000011465 /* it already printed err message */
11466 return 1; /* error */
11467 }
Eric Andersencb57d552001-06-28 07:25:16 +000011468 if (*argptr)
11469 argptr++;
11470 } else {
11471 setoption(c, val);
11472 }
11473 }
11474 }
Denis Vlasenko28bf6712008-02-14 15:01:47 +000011475 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +000011476}
11477
Eric Andersencb57d552001-06-28 07:25:16 +000011478/*
Eric Andersencb57d552001-06-28 07:25:16 +000011479 * The shift builtin command.
11480 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020011481static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000011482shiftcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000011483{
11484 int n;
11485 char **ap1, **ap2;
11486
11487 n = 1;
Denis Vlasenko68404f12008-03-17 09:00:54 +000011488 if (argv[1])
Eric Andersencb57d552001-06-28 07:25:16 +000011489 n = number(argv[1]);
11490 if (n > shellparam.nparam)
Ingo van Lil9c8e94b2018-01-05 15:04:23 +010011491 return 1;
Denis Vlasenkob012b102007-02-19 22:43:01 +000011492 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +000011493 shellparam.nparam -= n;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011494 for (ap1 = shellparam.p; --n >= 0; ap1++) {
Denis Vlasenko01631112007-12-16 17:20:38 +000011495 if (shellparam.malloced)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011496 free(*ap1);
Eric Andersencb57d552001-06-28 07:25:16 +000011497 }
11498 ap2 = shellparam.p;
Denis Vlasenkof7d56652008-03-25 05:51:41 +000011499 while ((*ap2++ = *ap1++) != NULL)
11500 continue;
Denis Vlasenko131ae172007-02-18 13:00:19 +000011501#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +000011502 shellparam.optind = 1;
11503 shellparam.optoff = -1;
Eric Andersenc470f442003-07-28 09:56:35 +000011504#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +000011505 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +000011506 return 0;
11507}
11508
Eric Andersencb57d552001-06-28 07:25:16 +000011509/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011510 * POSIX requires that 'set' (but not export or readonly) output the
11511 * variables in lexicographic order - by the locale's collating order (sigh).
11512 * Maybe we could keep them in an ordered balanced binary tree
11513 * instead of hashed lists.
11514 * For now just roll 'em through qsort for printing...
11515 */
11516static int
11517showvars(const char *sep_prefix, int on, int off)
11518{
11519 const char *sep;
11520 char **ep, **epend;
11521
Denys Vlasenkoa5060b82017-11-03 14:16:25 +010011522 ep = listvars(on, off, /*strlist:*/ NULL, &epend);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011523 qsort(ep, epend - ep, sizeof(char *), vpcmp);
11524
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +000011525 sep = *sep_prefix ? " " : sep_prefix;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011526
11527 for (; ep < epend; ep++) {
11528 const char *p;
11529 const char *q;
11530
Denys Vlasenko9c143ce2017-11-02 12:56:24 +010011531 p = endofname(*ep);
11532/* Used to have simple "p = strchrnul(*ep, '=')" here instead, but this
11533 * makes "export -p" to have output not suitable for "eval":
11534 * import os
11535 * os.environ["test-test"]="test"
11536 * if os.fork() == 0:
11537 * os.execv("ash", [ 'ash', '-c', 'eval $(export -p); echo OK' ]) # fixes this
11538 * os.execv("ash", [ 'ash', '-c', 'env | grep test-test' ])
11539 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011540 q = nullstr;
Denys Vlasenko9c143ce2017-11-02 12:56:24 +010011541 if (*p == '=')
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011542 q = single_quote(++p);
11543 out1fmt("%s%s%.*s%s\n", sep_prefix, sep, (int)(p - *ep), *ep, q);
11544 }
11545 return 0;
11546}
11547
11548/*
Eric Andersencb57d552001-06-28 07:25:16 +000011549 * The set command builtin.
11550 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020011551static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000011552setcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +000011553{
Denis Vlasenko28bf6712008-02-14 15:01:47 +000011554 int retval;
11555
Denis Vlasenko68404f12008-03-17 09:00:54 +000011556 if (!argv[1])
Eric Andersenc470f442003-07-28 09:56:35 +000011557 return showvars(nullstr, 0, VUNSET);
Denys Vlasenkob0b83432011-03-07 12:34:59 +010011558
Denis Vlasenkob012b102007-02-19 22:43:01 +000011559 INT_OFF;
Denys Vlasenko897475a2019-06-01 16:35:09 +020011560 retval = options(/*login_sh:*/ NULL);
Denys Vlasenkob0b83432011-03-07 12:34:59 +010011561 if (retval == 0) { /* if no parse error... */
Denis Vlasenko28bf6712008-02-14 15:01:47 +000011562 optschanged();
11563 if (*argptr != NULL) {
11564 setparam(argptr);
11565 }
Eric Andersencb57d552001-06-28 07:25:16 +000011566 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000011567 INT_ON;
Denis Vlasenko28bf6712008-02-14 15:01:47 +000011568 return retval;
Eric Andersencb57d552001-06-28 07:25:16 +000011569}
11570
Denis Vlasenko131ae172007-02-18 13:00:19 +000011571#if ENABLE_ASH_RANDOM_SUPPORT
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020011572static void FAST_FUNC
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000011573change_random(const char *value)
Eric Andersenef02f822004-03-11 13:34:24 +000011574{
Denys Vlasenko3ea2e822009-10-09 20:59:04 +020011575 uint32_t t;
11576
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011577 if (value == NULL) {
Eric Andersen16767e22004-03-16 05:14:10 +000011578 /* "get", generate */
Denys Vlasenko3ea2e822009-10-09 20:59:04 +020011579 t = next_random(&random_gen);
Eric Andersen16767e22004-03-16 05:14:10 +000011580 /* set without recursion */
Denys Vlasenko8837c5d2010-06-02 12:56:18 +020011581 setvar(vrandom.var_text, utoa(t), VNOFUNC);
Eric Andersen16767e22004-03-16 05:14:10 +000011582 vrandom.flags &= ~VNOFUNC;
11583 } else {
11584 /* set/reset */
Denys Vlasenko3ea2e822009-10-09 20:59:04 +020011585 t = strtoul(value, NULL, 10);
11586 INIT_RANDOM_T(&random_gen, (t ? t : 1), t);
Eric Andersen16767e22004-03-16 05:14:10 +000011587 }
Eric Andersenef02f822004-03-11 13:34:24 +000011588}
Eric Andersen16767e22004-03-16 05:14:10 +000011589#endif
11590
Ron Yorston1d371862019-04-15 10:52:05 +010011591#if BASH_EPOCH_VARS
11592static void FAST_FUNC
11593change_epoch(struct var *vepoch, const char *fmt)
11594{
11595 struct timeval tv;
Denys Vlasenko3c13da32020-12-30 23:48:01 +010011596 char buffer[sizeof("%llu.nnnnnn") + sizeof(long long)*3];
Ron Yorston1d371862019-04-15 10:52:05 +010011597
Denys Vlasenko3c13da32020-12-30 23:48:01 +010011598 xgettimeofday(&tv);
11599 sprintf(buffer, fmt, (unsigned long long)tv.tv_sec, (unsigned)tv.tv_usec);
Ron Yorston1d371862019-04-15 10:52:05 +010011600 setvar(vepoch->var_text, buffer, VNOFUNC);
11601 vepoch->flags &= ~VNOFUNC;
11602}
11603
11604static void FAST_FUNC
11605change_seconds(const char *value UNUSED_PARAM)
11606{
Denys Vlasenko3c13da32020-12-30 23:48:01 +010011607 change_epoch(&vepochs, "%llu");
Ron Yorston1d371862019-04-15 10:52:05 +010011608}
11609
11610static void FAST_FUNC
11611change_realtime(const char *value UNUSED_PARAM)
11612{
Denys Vlasenko3c13da32020-12-30 23:48:01 +010011613 change_epoch(&vepochr, "%llu.%06u");
Ron Yorston1d371862019-04-15 10:52:05 +010011614}
11615#endif
11616
Denis Vlasenko131ae172007-02-18 13:00:19 +000011617#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +000011618static int
Denys Vlasenko35c2a132016-10-26 17:34:26 +020011619getopts(char *optstr, char *optvar, char **optfirst)
Eric Andersencb57d552001-06-28 07:25:16 +000011620{
11621 char *p, *q;
11622 char c = '?';
11623 int done = 0;
Denys Vlasenko9c541002015-10-07 15:44:36 +020011624 char sbuf[2];
Eric Andersena48b0a32003-10-22 10:56:47 +000011625 char **optnext;
Denys Vlasenkodbef38a2016-10-26 17:54:32 +020011626 int ind = shellparam.optind;
11627 int off = shellparam.optoff;
Eric Andersencb57d552001-06-28 07:25:16 +000011628
Denys Vlasenko9c541002015-10-07 15:44:36 +020011629 sbuf[1] = '\0';
11630
Denys Vlasenkodbef38a2016-10-26 17:54:32 +020011631 shellparam.optind = -1;
11632 optnext = optfirst + ind - 1;
Eric Andersena48b0a32003-10-22 10:56:47 +000011633
Denys Vlasenkodbef38a2016-10-26 17:54:32 +020011634 if (ind <= 1 || off < 0 || (int)strlen(optnext[-1]) < off)
Eric Andersencb57d552001-06-28 07:25:16 +000011635 p = NULL;
Denys Vlasenkodbef38a2016-10-26 17:54:32 +020011636 else
11637 p = optnext[-1] + off;
Eric Andersencb57d552001-06-28 07:25:16 +000011638 if (p == NULL || *p == '\0') {
11639 /* Current word is done, advance */
Eric Andersencb57d552001-06-28 07:25:16 +000011640 p = *optnext;
11641 if (p == NULL || *p != '-' || *++p == '\0') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011642 atend:
Denys Vlasenko9832bba2017-08-15 15:44:41 +020011643 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +000011644 p = NULL;
11645 done = 1;
11646 goto out;
11647 }
11648 optnext++;
Denis Vlasenko9f739442006-12-16 23:49:13 +000011649 if (LONE_DASH(p)) /* check for "--" */
Eric Andersencb57d552001-06-28 07:25:16 +000011650 goto atend;
11651 }
11652
11653 c = *p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +000011654 for (q = optstr; *q != c;) {
Eric Andersencb57d552001-06-28 07:25:16 +000011655 if (*q == '\0') {
Denys Vlasenko9832bba2017-08-15 15:44:41 +020011656 /* OPTERR is a bashism */
11657 const char *cp = lookupvar("OPTERR");
11658 if ((cp && LONE_CHAR(cp, '0'))
11659 || (optstr[0] == ':')
11660 ) {
Denys Vlasenko9c541002015-10-07 15:44:36 +020011661 sbuf[0] = c;
11662 /*sbuf[1] = '\0'; - already is */
Denys Vlasenkodbef38a2016-10-26 17:54:32 +020011663 setvar0("OPTARG", sbuf);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011664 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000011665 fprintf(stderr, "Illegal option -%c\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011666 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +000011667 }
11668 c = '?';
Eric Andersenc470f442003-07-28 09:56:35 +000011669 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +000011670 }
11671 if (*++q == ':')
11672 q++;
11673 }
11674
11675 if (*++q == ':') {
11676 if (*p == '\0' && (p = *optnext) == NULL) {
Denys Vlasenko9832bba2017-08-15 15:44:41 +020011677 /* OPTERR is a bashism */
11678 const char *cp = lookupvar("OPTERR");
11679 if ((cp && LONE_CHAR(cp, '0'))
11680 || (optstr[0] == ':')
11681 ) {
Denys Vlasenko9c541002015-10-07 15:44:36 +020011682 sbuf[0] = c;
11683 /*sbuf[1] = '\0'; - already is */
Denys Vlasenkodbef38a2016-10-26 17:54:32 +020011684 setvar0("OPTARG", sbuf);
Eric Andersencb57d552001-06-28 07:25:16 +000011685 c = ':';
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011686 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000011687 fprintf(stderr, "No arg for -%c option\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011688 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +000011689 c = '?';
11690 }
Eric Andersenc470f442003-07-28 09:56:35 +000011691 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +000011692 }
11693
11694 if (p == *optnext)
11695 optnext++;
Denys Vlasenkodbef38a2016-10-26 17:54:32 +020011696 setvar0("OPTARG", p);
Eric Andersencb57d552001-06-28 07:25:16 +000011697 p = NULL;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011698 } else
Denys Vlasenkodbef38a2016-10-26 17:54:32 +020011699 setvar0("OPTARG", nullstr);
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011700 out:
Denys Vlasenkodbef38a2016-10-26 17:54:32 +020011701 ind = optnext - optfirst + 1;
11702 setvar("OPTIND", itoa(ind), VNOFUNC);
Denys Vlasenko9c541002015-10-07 15:44:36 +020011703 sbuf[0] = c;
11704 /*sbuf[1] = '\0'; - already is */
Denys Vlasenkodbef38a2016-10-26 17:54:32 +020011705 setvar0(optvar, sbuf);
11706
11707 shellparam.optoff = p ? p - *(optnext - 1) : -1;
11708 shellparam.optind = ind;
11709
Eric Andersencb57d552001-06-28 07:25:16 +000011710 return done;
11711}
Eric Andersenc470f442003-07-28 09:56:35 +000011712
11713/*
11714 * The getopts builtin. Shellparam.optnext points to the next argument
11715 * to be processed. Shellparam.optptr points to the next character to
11716 * be processed in the current argument. If shellparam.optnext is NULL,
11717 * then it's the first time getopts has been called.
11718 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020011719static int FAST_FUNC
Eric Andersenc470f442003-07-28 09:56:35 +000011720getoptscmd(int argc, char **argv)
11721{
11722 char **optbase;
11723
11724 if (argc < 3)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +000011725 ash_msg_and_raise_error("usage: getopts optstring var [arg]");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011726 if (argc == 3) {
Eric Andersenc470f442003-07-28 09:56:35 +000011727 optbase = shellparam.p;
Denys Vlasenkodbef38a2016-10-26 17:54:32 +020011728 if ((unsigned)shellparam.optind > shellparam.nparam + 1) {
Eric Andersenc470f442003-07-28 09:56:35 +000011729 shellparam.optind = 1;
11730 shellparam.optoff = -1;
11731 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011732 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000011733 optbase = &argv[3];
Denys Vlasenkodbef38a2016-10-26 17:54:32 +020011734 if ((unsigned)shellparam.optind > argc - 2) {
Eric Andersenc470f442003-07-28 09:56:35 +000011735 shellparam.optind = 1;
11736 shellparam.optoff = -1;
11737 }
11738 }
11739
Denys Vlasenko35c2a132016-10-26 17:34:26 +020011740 return getopts(argv[1], argv[2], optbase);
Eric Andersenc470f442003-07-28 09:56:35 +000011741}
Denis Vlasenko131ae172007-02-18 13:00:19 +000011742#endif /* ASH_GETOPTS */
Eric Andersencb57d552001-06-28 07:25:16 +000011743
Eric Andersencb57d552001-06-28 07:25:16 +000011744
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011745/* ============ Shell parser */
Eric Andersencb57d552001-06-28 07:25:16 +000011746
Denis Vlasenkob07a4962008-06-22 13:16:23 +000011747struct heredoc {
11748 struct heredoc *next; /* next here document in list */
11749 union node *here; /* redirection node */
11750 char *eofmark; /* string indicating end of input */
11751 smallint striptabs; /* if set, strip leading tabs */
11752};
11753
11754static smallint tokpushback; /* last token pushed back */
Denis Vlasenkob07a4962008-06-22 13:16:23 +000011755static smallint quoteflag; /* set if (part of) last token was quoted */
11756static token_id_t lasttoken; /* last token read (integer id Txxx) */
11757static struct heredoc *heredoclist; /* list of here documents to read */
11758static char *wordtext; /* text of last word returned by readtoken */
11759static struct nodelist *backquotelist;
11760static union node *redirnode;
11761static struct heredoc *heredoc;
Denis Vlasenko99eb8502007-02-23 21:09:49 +000011762
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020011763static const char *
11764tokname(char *buf, int tok)
11765{
11766 if (tok < TSEMI)
Denys Vlasenko888527c2016-10-02 16:54:17 +020011767 return tokname_array[tok];
11768 sprintf(buf, "\"%s\"", tokname_array[tok]);
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020011769 return buf;
11770}
11771
11772/* raise_error_unexpected_syntax:
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011773 * Called when an unexpected token is read during the parse. The argument
11774 * is the token that is expected, or -1 if more than one type of token can
11775 * occur at this point.
11776 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000011777static void raise_error_unexpected_syntax(int) NORETURN;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011778static void
11779raise_error_unexpected_syntax(int token)
11780{
11781 char msg[64];
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020011782 char buf[16];
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011783 int l;
11784
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020011785 l = sprintf(msg, "unexpected %s", tokname(buf, lasttoken));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011786 if (token >= 0)
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020011787 sprintf(msg + l, " (expecting %s)", tokname(buf, token));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011788 raise_error_syntax(msg);
11789 /* NOTREACHED */
11790}
Eric Andersencb57d552001-06-28 07:25:16 +000011791
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011792/* parsing is heavily cross-recursive, need these forward decls */
11793static union node *andor(void);
11794static union node *pipeline(void);
11795static union node *parse_command(void);
11796static void parseheredoc(void);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011797static int readtoken(void);
Eric Andersencb57d552001-06-28 07:25:16 +000011798
Eric Andersenc470f442003-07-28 09:56:35 +000011799static union node *
11800list(int nlflag)
Eric Andersencb57d552001-06-28 07:25:16 +000011801{
Denys Vlasenko30af5932021-09-08 00:39:16 +020011802 int chknl = nlflag & 1 ? 0 : CHKNL;
Eric Andersencb57d552001-06-28 07:25:16 +000011803 union node *n1, *n2, *n3;
11804 int tok;
11805
Eric Andersencb57d552001-06-28 07:25:16 +000011806 n1 = NULL;
11807 for (;;) {
Denys Vlasenko30af5932021-09-08 00:39:16 +020011808 checkkwd = chknl | CHKKWD | CHKALIAS;
11809 tok = readtoken();
11810 switch (tok) {
Ron Yorstonc0e00762015-10-29 11:30:55 +000011811 case TNL:
Ron Yorstonc0e00762015-10-29 11:30:55 +000011812 parseheredoc();
11813 return n1;
11814
11815 case TEOF:
Denys Vlasenko30af5932021-09-08 00:39:16 +020011816 if (!n1 && !chknl)
Ron Yorstonc0e00762015-10-29 11:30:55 +000011817 n1 = NODE_EOF;
Denys Vlasenko30af5932021-09-08 00:39:16 +020011818 out_eof:
Ron Yorstonc0e00762015-10-29 11:30:55 +000011819 parseheredoc();
Denys Vlasenkoc08993f2020-02-22 17:26:23 +010011820 tokpushback++;
11821 lasttoken = TEOF;
Ron Yorstonc0e00762015-10-29 11:30:55 +000011822 return n1;
11823 }
11824
Denys Vlasenkoc08993f2020-02-22 17:26:23 +010011825 tokpushback++;
Denys Vlasenko30af5932021-09-08 00:39:16 +020011826 if (nlflag == 2 && ((1 << tok) & tokendlist))
Ron Yorstonc0e00762015-10-29 11:30:55 +000011827 return n1;
11828 nlflag |= 2;
11829
Eric Andersencb57d552001-06-28 07:25:16 +000011830 n2 = andor();
11831 tok = readtoken();
11832 if (tok == TBACKGND) {
Eric Andersenc470f442003-07-28 09:56:35 +000011833 if (n2->type == NPIPE) {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000011834 n2->npipe.pipe_backgnd = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000011835 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000011836 if (n2->type != NREDIR) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000011837 n3 = stzalloc(sizeof(struct nredir));
Eric Andersenc470f442003-07-28 09:56:35 +000011838 n3->nredir.n = n2;
Denis Vlasenko597906c2008-02-20 16:38:54 +000011839 /*n3->nredir.redirect = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000011840 n2 = n3;
11841 }
11842 n2->type = NBACKGND;
Eric Andersencb57d552001-06-28 07:25:16 +000011843 }
11844 }
11845 if (n1 == NULL) {
11846 n1 = n2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011847 } else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011848 n3 = stzalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +000011849 n3->type = NSEMI;
11850 n3->nbinary.ch1 = n1;
11851 n3->nbinary.ch2 = n2;
11852 n1 = n3;
11853 }
11854 switch (tok) {
Ron Yorstonc0e00762015-10-29 11:30:55 +000011855 case TEOF:
Denys Vlasenko30af5932021-09-08 00:39:16 +020011856 goto out_eof;
11857 case TNL:
Ron Yorstonc0e00762015-10-29 11:30:55 +000011858 tokpushback = 1;
11859 /* fall through */
Eric Andersencb57d552001-06-28 07:25:16 +000011860 case TBACKGND:
11861 case TSEMI:
Eric Andersencb57d552001-06-28 07:25:16 +000011862 break;
Eric Andersencb57d552001-06-28 07:25:16 +000011863 default:
Denys Vlasenko30af5932021-09-08 00:39:16 +020011864 if (!chknl)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011865 raise_error_unexpected_syntax(-1);
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011866 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000011867 return n1;
11868 }
11869 }
11870}
11871
Eric Andersenc470f442003-07-28 09:56:35 +000011872static union node *
11873andor(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011874{
Eric Andersencb57d552001-06-28 07:25:16 +000011875 union node *n1, *n2, *n3;
11876 int t;
11877
Eric Andersencb57d552001-06-28 07:25:16 +000011878 n1 = pipeline();
11879 for (;;) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011880 t = readtoken();
11881 if (t == TAND) {
Eric Andersencb57d552001-06-28 07:25:16 +000011882 t = NAND;
11883 } else if (t == TOR) {
11884 t = NOR;
11885 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011886 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000011887 return n1;
11888 }
Eric Andersenc470f442003-07-28 09:56:35 +000011889 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000011890 n2 = pipeline();
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011891 n3 = stzalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +000011892 n3->type = t;
11893 n3->nbinary.ch1 = n1;
11894 n3->nbinary.ch2 = n2;
11895 n1 = n3;
11896 }
11897}
11898
Eric Andersenc470f442003-07-28 09:56:35 +000011899static union node *
11900pipeline(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011901{
Eric Andersencb57d552001-06-28 07:25:16 +000011902 union node *n1, *n2, *pipenode;
11903 struct nodelist *lp, *prev;
11904 int negate;
11905
11906 negate = 0;
11907 TRACE(("pipeline: entered\n"));
11908 if (readtoken() == TNOT) {
11909 negate = !negate;
Eric Andersenc470f442003-07-28 09:56:35 +000011910 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000011911 } else
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011912 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011913 n1 = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +000011914 if (readtoken() == TPIPE) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000011915 pipenode = stzalloc(sizeof(struct npipe));
Eric Andersencb57d552001-06-28 07:25:16 +000011916 pipenode->type = NPIPE;
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000011917 /*pipenode->npipe.pipe_backgnd = 0; - stzalloc did it */
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011918 lp = stzalloc(sizeof(struct nodelist));
Eric Andersencb57d552001-06-28 07:25:16 +000011919 pipenode->npipe.cmdlist = lp;
11920 lp->n = n1;
11921 do {
11922 prev = lp;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011923 lp = stzalloc(sizeof(struct nodelist));
Eric Andersenc470f442003-07-28 09:56:35 +000011924 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011925 lp->n = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +000011926 prev->next = lp;
11927 } while (readtoken() == TPIPE);
11928 lp->next = NULL;
11929 n1 = pipenode;
11930 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011931 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000011932 if (negate) {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011933 n2 = stzalloc(sizeof(struct nnot));
Eric Andersencb57d552001-06-28 07:25:16 +000011934 n2->type = NNOT;
11935 n2->nnot.com = n1;
11936 return n2;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011937 }
11938 return n1;
Eric Andersencb57d552001-06-28 07:25:16 +000011939}
11940
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011941static union node *
11942makename(void)
11943{
11944 union node *n;
11945
Denis Vlasenko597906c2008-02-20 16:38:54 +000011946 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011947 n->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000011948 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011949 n->narg.text = wordtext;
11950 n->narg.backquote = backquotelist;
11951 return n;
11952}
11953
11954static void
11955fixredir(union node *n, const char *text, int err)
11956{
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000011957 int fd;
11958
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011959 TRACE(("Fix redir %s %d\n", text, err));
11960 if (!err)
11961 n->ndup.vname = NULL;
11962
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000011963 fd = bb_strtou(text, NULL, 10);
11964 if (!errno && fd >= 0)
11965 n->ndup.dupfd = fd;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011966 else if (LONE_DASH(text))
11967 n->ndup.dupfd = -1;
11968 else {
11969 if (err)
Denis Vlasenko559691a2008-10-05 18:39:31 +000011970 raise_error_syntax("bad fd number");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011971 n->ndup.vname = makename();
11972 }
11973}
11974
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011975static void
11976parsefname(void)
11977{
11978 union node *n = redirnode;
11979
Denys Vlasenkoa7328982017-07-29 19:57:28 +020011980 if (n->type == NHERE)
11981 checkkwd = CHKEOFMARK;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011982 if (readtoken() != TWORD)
11983 raise_error_unexpected_syntax(-1);
11984 if (n->type == NHERE) {
11985 struct heredoc *here = heredoc;
11986 struct heredoc *p;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011987
11988 if (quoteflag == 0)
11989 n->type = NXHERE;
11990 TRACE(("Here document %d\n", n->type));
Denys Vlasenko740058b2018-01-09 17:01:00 +010011991 rmescapes(wordtext, 0, NULL);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011992 here->eofmark = wordtext;
11993 here->next = NULL;
11994 if (heredoclist == NULL)
11995 heredoclist = here;
11996 else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011997 for (p = heredoclist; p->next; p = p->next)
11998 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011999 p->next = here;
12000 }
12001 } else if (n->type == NTOFD || n->type == NFROMFD) {
12002 fixredir(n, wordtext, 0);
12003 } else {
12004 n->nfile.fname = makename();
12005 }
12006}
Eric Andersencb57d552001-06-28 07:25:16 +000012007
Eric Andersenc470f442003-07-28 09:56:35 +000012008static union node *
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012009simplecmd(void)
12010{
12011 union node *args, **app;
12012 union node *n = NULL;
12013 union node *vars, **vpp;
12014 union node **rpp, *redir;
12015 int savecheckkwd;
Denys Vlasenko675d24a2018-01-27 22:02:05 +010012016 int savelinno;
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010012017#if BASH_TEST2
Denis Vlasenko80591b02008-03-25 07:49:43 +000012018 smallint double_brackets_flag = 0;
12019#endif
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010012020 IF_BASH_FUNCTION(smallint function_flag = 0;)
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012021
12022 args = NULL;
12023 app = &args;
12024 vars = NULL;
12025 vpp = &vars;
12026 redir = NULL;
12027 rpp = &redir;
12028
12029 savecheckkwd = CHKALIAS;
Denys Vlasenko675d24a2018-01-27 22:02:05 +010012030 savelinno = g_parsefile->linno;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012031 for (;;) {
Denis Vlasenko80591b02008-03-25 07:49:43 +000012032 int t;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012033 checkkwd = savecheckkwd;
Denis Vlasenko80591b02008-03-25 07:49:43 +000012034 t = readtoken();
12035 switch (t) {
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010012036#if BASH_FUNCTION
Ron Yorston95ebcf72015-11-03 09:42:23 +000012037 case TFUNCTION:
Denys Vlasenko30af5932021-09-08 00:39:16 +020012038 if (readtoken() != TWORD)
Ron Yorston95ebcf72015-11-03 09:42:23 +000012039 raise_error_unexpected_syntax(TWORD);
Denys Vlasenko30af5932021-09-08 00:39:16 +020012040 tokpushback = 1;
Ron Yorston95ebcf72015-11-03 09:42:23 +000012041 function_flag = 1;
12042 break;
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010012043#endif
12044#if BASH_TEST2
Denis Vlasenko80591b02008-03-25 07:49:43 +000012045 case TAND: /* "&&" */
12046 case TOR: /* "||" */
12047 if (!double_brackets_flag) {
12048 tokpushback = 1;
12049 goto out;
12050 }
Denys Vlasenkod2241f52020-10-31 03:34:07 +010012051 /* pass "&&" or "||" to [[ ]] as literal args */
12052 wordtext = (char *) (t == TAND ? "&&" : "||");
Denis Vlasenko80591b02008-03-25 07:49:43 +000012053#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012054 case TWORD:
Denis Vlasenko597906c2008-02-20 16:38:54 +000012055 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012056 n->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000012057 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012058 n->narg.text = wordtext;
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010012059#if BASH_TEST2
Denis Vlasenko80591b02008-03-25 07:49:43 +000012060 if (strcmp("[[", wordtext) == 0)
12061 double_brackets_flag = 1;
12062 else if (strcmp("]]", wordtext) == 0)
12063 double_brackets_flag = 0;
12064#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012065 n->narg.backquote = backquotelist;
12066 if (savecheckkwd && isassignment(wordtext)) {
12067 *vpp = n;
12068 vpp = &n->narg.next;
12069 } else {
12070 *app = n;
12071 app = &n->narg.next;
12072 savecheckkwd = 0;
12073 }
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010012074#if BASH_FUNCTION
Ron Yorston95ebcf72015-11-03 09:42:23 +000012075 if (function_flag) {
12076 checkkwd = CHKNL | CHKKWD;
Denys Vlasenko30af5932021-09-08 00:39:16 +020012077 t = readtoken();
12078 tokpushback = 1;
12079 switch (t) {
Ron Yorston95ebcf72015-11-03 09:42:23 +000012080 case TBEGIN:
12081 case TIF:
12082 case TCASE:
12083 case TUNTIL:
12084 case TWHILE:
12085 case TFOR:
12086 goto do_func;
12087 case TLP:
12088 function_flag = 0;
12089 break;
Denys Vlasenkoe93031e2018-04-10 01:23:19 +020012090# if BASH_TEST2
Ron Yorston95ebcf72015-11-03 09:42:23 +000012091 case TWORD:
12092 if (strcmp("[[", wordtext) == 0)
12093 goto do_func;
12094 /* fall through */
Denys Vlasenkoe93031e2018-04-10 01:23:19 +020012095# endif
Ron Yorston95ebcf72015-11-03 09:42:23 +000012096 default:
12097 raise_error_unexpected_syntax(-1);
12098 }
12099 }
12100#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012101 break;
12102 case TREDIR:
12103 *rpp = n = redirnode;
12104 rpp = &n->nfile.next;
12105 parsefname(); /* read name of redirection file */
12106 break;
12107 case TLP:
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010012108 IF_BASH_FUNCTION(do_func:)
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012109 if (args && app == &args->narg.next
12110 && !vars && !redir
12111 ) {
12112 struct builtincmd *bcmd;
12113 const char *name;
12114
12115 /* We have a function */
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010012116 if (IF_BASH_FUNCTION(!function_flag &&) readtoken() != TRP)
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012117 raise_error_unexpected_syntax(TRP);
12118 name = n->narg.text;
12119 if (!goodname(name)
12120 || ((bcmd = find_builtin(name)) && IS_BUILTIN_SPECIAL(bcmd))
12121 ) {
Denis Vlasenko559691a2008-10-05 18:39:31 +000012122 raise_error_syntax("bad function name");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012123 }
12124 n->type = NDEFUN;
12125 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Denys Vlasenko675d24a2018-01-27 22:02:05 +010012126 n->ndefun.text = n->narg.text;
12127 n->ndefun.linno = g_parsefile->linno;
12128 n->ndefun.body = parse_command();
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012129 return n;
12130 }
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010012131 IF_BASH_FUNCTION(function_flag = 0;)
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012132 /* fall through */
12133 default:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000012134 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012135 goto out;
12136 }
12137 }
12138 out:
12139 *app = NULL;
12140 *vpp = NULL;
12141 *rpp = NULL;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000012142 n = stzalloc(sizeof(struct ncmd));
Denys Vlasenko57b7efb2018-04-10 01:20:26 +020012143 if (NCMD != 0)
12144 n->type = NCMD;
Denys Vlasenko675d24a2018-01-27 22:02:05 +010012145 n->ncmd.linno = savelinno;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012146 n->ncmd.args = args;
12147 n->ncmd.assign = vars;
12148 n->ncmd.redirect = redir;
12149 return n;
12150}
12151
12152static union node *
12153parse_command(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000012154{
Eric Andersencb57d552001-06-28 07:25:16 +000012155 union node *n1, *n2;
12156 union node *ap, **app;
12157 union node *cp, **cpp;
12158 union node *redir, **rpp;
Eric Andersenc470f442003-07-28 09:56:35 +000012159 union node **rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +000012160 int t;
Denys Vlasenko675d24a2018-01-27 22:02:05 +010012161 int savelinno;
Eric Andersencb57d552001-06-28 07:25:16 +000012162
12163 redir = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +000012164 rpp2 = &redir;
Eric Andersen88cec252001-09-06 17:35:20 +000012165
Denys Vlasenko675d24a2018-01-27 22:02:05 +010012166 savelinno = g_parsefile->linno;
12167
Eric Andersencb57d552001-06-28 07:25:16 +000012168 switch (readtoken()) {
Eric Andersenc470f442003-07-28 09:56:35 +000012169 default:
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012170 raise_error_unexpected_syntax(-1);
Eric Andersenc470f442003-07-28 09:56:35 +000012171 /* NOTREACHED */
Eric Andersencb57d552001-06-28 07:25:16 +000012172 case TIF:
Denis Vlasenko838ffd52008-02-21 04:32:08 +000012173 n1 = stzalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +000012174 n1->type = NIF;
12175 n1->nif.test = list(0);
12176 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012177 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +000012178 n1->nif.ifpart = list(0);
12179 n2 = n1;
12180 while (readtoken() == TELIF) {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000012181 n2->nif.elsepart = stzalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +000012182 n2 = n2->nif.elsepart;
12183 n2->type = NIF;
12184 n2->nif.test = list(0);
12185 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012186 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +000012187 n2->nif.ifpart = list(0);
12188 }
12189 if (lasttoken == TELSE)
12190 n2->nif.elsepart = list(0);
12191 else {
12192 n2->nif.elsepart = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000012193 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000012194 }
Eric Andersenc470f442003-07-28 09:56:35 +000012195 t = TFI;
Eric Andersencb57d552001-06-28 07:25:16 +000012196 break;
12197 case TWHILE:
Eric Andersenc470f442003-07-28 09:56:35 +000012198 case TUNTIL: {
Eric Andersencb57d552001-06-28 07:25:16 +000012199 int got;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000012200 n1 = stzalloc(sizeof(struct nbinary));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012201 n1->type = (lasttoken == TWHILE) ? NWHILE : NUNTIL;
Eric Andersencb57d552001-06-28 07:25:16 +000012202 n1->nbinary.ch1 = list(0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012203 got = readtoken();
12204 if (got != TDO) {
Denys Vlasenko888527c2016-10-02 16:54:17 +020012205 TRACE(("expecting DO got '%s' %s\n", tokname_array[got],
Denis Vlasenko131ae172007-02-18 13:00:19 +000012206 got == TWORD ? wordtext : ""));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012207 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +000012208 }
12209 n1->nbinary.ch2 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000012210 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +000012211 break;
12212 }
12213 case TFOR:
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000012214 if (readtoken() != TWORD || quoteflag || !goodname(wordtext))
Denis Vlasenko559691a2008-10-05 18:39:31 +000012215 raise_error_syntax("bad for loop variable");
Denis Vlasenko838ffd52008-02-21 04:32:08 +000012216 n1 = stzalloc(sizeof(struct nfor));
Eric Andersencb57d552001-06-28 07:25:16 +000012217 n1->type = NFOR;
Denys Vlasenko675d24a2018-01-27 22:02:05 +010012218 n1->nfor.linno = savelinno;
Eric Andersencb57d552001-06-28 07:25:16 +000012219 n1->nfor.var = wordtext;
Ron Yorstonab80e012015-08-03 13:46:00 +010012220 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000012221 if (readtoken() == TIN) {
12222 app = &ap;
12223 while (readtoken() == TWORD) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000012224 n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000012225 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000012226 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000012227 n2->narg.text = wordtext;
12228 n2->narg.backquote = backquotelist;
12229 *app = n2;
12230 app = &n2->narg.next;
12231 }
12232 *app = NULL;
12233 n1->nfor.args = ap;
12234 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012235 raise_error_unexpected_syntax(-1);
Eric Andersencb57d552001-06-28 07:25:16 +000012236 } else {
Denis Vlasenko597906c2008-02-20 16:38:54 +000012237 n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000012238 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000012239 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000012240 n2->narg.text = (char *)dolatstr;
Denis Vlasenko597906c2008-02-20 16:38:54 +000012241 /*n2->narg.backquote = NULL;*/
Eric Andersencb57d552001-06-28 07:25:16 +000012242 n1->nfor.args = n2;
12243 /*
12244 * Newline or semicolon here is optional (but note
12245 * that the original Bourne shell only allowed NL).
12246 */
Ron Yorstonab80e012015-08-03 13:46:00 +010012247 if (lasttoken != TSEMI)
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000012248 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000012249 }
Eric Andersenc470f442003-07-28 09:56:35 +000012250 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000012251 if (readtoken() != TDO)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012252 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +000012253 n1->nfor.body = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000012254 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +000012255 break;
12256 case TCASE:
Denis Vlasenko838ffd52008-02-21 04:32:08 +000012257 n1 = stzalloc(sizeof(struct ncase));
Eric Andersencb57d552001-06-28 07:25:16 +000012258 n1->type = NCASE;
Denys Vlasenko675d24a2018-01-27 22:02:05 +010012259 n1->ncase.linno = savelinno;
Eric Andersencb57d552001-06-28 07:25:16 +000012260 if (readtoken() != TWORD)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012261 raise_error_unexpected_syntax(TWORD);
Denis Vlasenko597906c2008-02-20 16:38:54 +000012262 n1->ncase.expr = n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000012263 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000012264 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000012265 n2->narg.text = wordtext;
12266 n2->narg.backquote = backquotelist;
Ron Yorston383b8852015-08-03 13:46:25 +010012267 checkkwd = CHKNL | CHKKWD | CHKALIAS;
12268 if (readtoken() != TIN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012269 raise_error_unexpected_syntax(TIN);
Eric Andersencb57d552001-06-28 07:25:16 +000012270 cpp = &n1->ncase.cases;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012271 next_case:
Eric Andersenc470f442003-07-28 09:56:35 +000012272 checkkwd = CHKNL | CHKKWD;
12273 t = readtoken();
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012274 while (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +000012275 if (lasttoken == TLP)
12276 readtoken();
Denis Vlasenko838ffd52008-02-21 04:32:08 +000012277 *cpp = cp = stzalloc(sizeof(struct nclist));
Eric Andersencb57d552001-06-28 07:25:16 +000012278 cp->type = NCLIST;
12279 app = &cp->nclist.pattern;
12280 for (;;) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000012281 *app = ap = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000012282 ap->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000012283 /*ap->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000012284 ap->narg.text = wordtext;
12285 ap->narg.backquote = backquotelist;
Eric Andersenc470f442003-07-28 09:56:35 +000012286 if (readtoken() != TPIPE)
Eric Andersencb57d552001-06-28 07:25:16 +000012287 break;
12288 app = &ap->narg.next;
12289 readtoken();
12290 }
Denis Vlasenko597906c2008-02-20 16:38:54 +000012291 //ap->narg.next = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +000012292 if (lasttoken != TRP)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012293 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +000012294 cp->nclist.body = list(2);
Eric Andersencb57d552001-06-28 07:25:16 +000012295
Eric Andersenc470f442003-07-28 09:56:35 +000012296 cpp = &cp->nclist.next;
12297
12298 checkkwd = CHKNL | CHKKWD;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012299 t = readtoken();
12300 if (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +000012301 if (t != TENDCASE)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012302 raise_error_unexpected_syntax(TENDCASE);
12303 goto next_case;
Eric Andersencb57d552001-06-28 07:25:16 +000012304 }
Eric Andersenc470f442003-07-28 09:56:35 +000012305 }
Eric Andersencb57d552001-06-28 07:25:16 +000012306 *cpp = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +000012307 goto redir;
Eric Andersencb57d552001-06-28 07:25:16 +000012308 case TLP:
Denis Vlasenko597906c2008-02-20 16:38:54 +000012309 n1 = stzalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +000012310 n1->type = NSUBSHELL;
Denys Vlasenko675d24a2018-01-27 22:02:05 +010012311 n1->nredir.linno = savelinno;
Eric Andersencb57d552001-06-28 07:25:16 +000012312 n1->nredir.n = list(0);
Denis Vlasenko597906c2008-02-20 16:38:54 +000012313 /*n1->nredir.redirect = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000012314 t = TRP;
Eric Andersencb57d552001-06-28 07:25:16 +000012315 break;
12316 case TBEGIN:
12317 n1 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000012318 t = TEND;
Eric Andersencb57d552001-06-28 07:25:16 +000012319 break;
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010012320 IF_BASH_FUNCTION(case TFUNCTION:)
Eric Andersencb57d552001-06-28 07:25:16 +000012321 case TWORD:
Eric Andersenc470f442003-07-28 09:56:35 +000012322 case TREDIR:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000012323 tokpushback = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000012324 return simplecmd();
Eric Andersencb57d552001-06-28 07:25:16 +000012325 }
12326
Eric Andersenc470f442003-07-28 09:56:35 +000012327 if (readtoken() != t)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012328 raise_error_unexpected_syntax(t);
Eric Andersenc470f442003-07-28 09:56:35 +000012329
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012330 redir:
Eric Andersencb57d552001-06-28 07:25:16 +000012331 /* Now check for redirection which may follow command */
Eric Andersenc470f442003-07-28 09:56:35 +000012332 checkkwd = CHKKWD | CHKALIAS;
12333 rpp = rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +000012334 while (readtoken() == TREDIR) {
12335 *rpp = n2 = redirnode;
12336 rpp = &n2->nfile.next;
12337 parsefname();
12338 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000012339 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000012340 *rpp = NULL;
12341 if (redir) {
12342 if (n1->type != NSUBSHELL) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000012343 n2 = stzalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +000012344 n2->type = NREDIR;
Denys Vlasenko675d24a2018-01-27 22:02:05 +010012345 n2->nredir.linno = savelinno;
Eric Andersencb57d552001-06-28 07:25:16 +000012346 n2->nredir.n = n1;
12347 n1 = n2;
12348 }
12349 n1->nredir.redirect = redir;
12350 }
Eric Andersencb57d552001-06-28 07:25:16 +000012351 return n1;
12352}
12353
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010012354#if BASH_DOLLAR_SQUOTE
Denys Vlasenko37dc08b2016-10-02 04:38:07 +020012355static int
12356decode_dollar_squote(void)
Denis Vlasenkoef527f52008-06-23 01:52:30 +000012357{
12358 static const char C_escapes[] ALIGN1 = "nrbtfav""x\\01234567";
12359 int c, cnt;
12360 char *p;
12361 char buf[4];
12362
12363 c = pgetc();
12364 p = strchr(C_escapes, c);
12365 if (p) {
12366 buf[0] = c;
12367 p = buf;
12368 cnt = 3;
12369 if ((unsigned char)(c - '0') <= 7) { /* \ooo */
12370 do {
12371 c = pgetc();
12372 *++p = c;
12373 } while ((unsigned char)(c - '0') <= 7 && --cnt);
12374 pungetc();
12375 } else if (c == 'x') { /* \xHH */
12376 do {
12377 c = pgetc();
12378 *++p = c;
12379 } while (isxdigit(c) && --cnt);
12380 pungetc();
12381 if (cnt == 3) { /* \x but next char is "bad" */
12382 c = 'x';
12383 goto unrecognized;
12384 }
12385 } else { /* simple seq like \\ or \t */
12386 p++;
12387 }
12388 *p = '\0';
12389 p = buf;
12390 c = bb_process_escape_sequence((void*)&p);
12391 } else { /* unrecognized "\z": print both chars unless ' or " */
12392 if (c != '\'' && c != '"') {
12393 unrecognized:
12394 c |= 0x100; /* "please encode \, then me" */
12395 }
12396 }
12397 return c;
12398}
12399#endif
12400
Denys Vlasenko46999802017-07-29 21:12:29 +020012401/* Used by expandstr to get here-doc like behaviour. */
12402#define FAKEEOFMARK ((char*)(uintptr_t)1)
12403
12404static ALWAYS_INLINE int
12405realeofmark(const char *eofmark)
12406{
12407 return eofmark && eofmark != FAKEEOFMARK;
12408}
12409
Eric Andersencb57d552001-06-28 07:25:16 +000012410/*
12411 * If eofmark is NULL, read a word or a redirection symbol. If eofmark
12412 * is not NULL, read a here document. In the latter case, eofmark is the
12413 * word which marks the end of the document and striptabs is true if
Denys Vlasenkocd716832009-11-28 22:14:02 +010012414 * leading tabs should be stripped from the document. The argument c
Eric Andersencb57d552001-06-28 07:25:16 +000012415 * is the first character of the input token or document.
12416 *
12417 * Because C does not have internal subroutines, I have simulated them
12418 * using goto's to implement the subroutine linkage. The following macros
12419 * will run code that appears at the end of readtoken1.
12420 */
Eric Andersen2870d962001-07-02 17:27:21 +000012421#define CHECKEND() {goto checkend; checkend_return:;}
12422#define PARSEREDIR() {goto parseredir; parseredir_return:;}
12423#define PARSESUB() {goto parsesub; parsesub_return:;}
Ron Yorstona1b0d382020-07-23 08:32:27 +010012424#define PARSEBACKQOLD() {style = OLD; goto parsebackq; parsebackq_oldreturn:;}
12425#define PARSEBACKQNEW() {style = NEW; goto parsebackq; parsebackq_newreturn:;}
12426#define PARSEPROCSUB() {style = PSUB; goto parsebackq; parsebackq_psreturn:;}
Eric Andersen2870d962001-07-02 17:27:21 +000012427#define PARSEARITH() {goto parsearith; parsearith_return:;}
Eric Andersencb57d552001-06-28 07:25:16 +000012428static int
Denys Vlasenkocd716832009-11-28 22:14:02 +010012429readtoken1(int c, int syntax, char *eofmark, int striptabs)
Manuel Novoa III 16815d42001-08-10 19:36:07 +000012430{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000012431 /* NB: syntax parameter fits into smallint */
Denys Vlasenko48cb9832021-09-08 09:52:04 +020012432 /* c parameter is an unsigned char or PEOF */
Eric Andersencb57d552001-06-28 07:25:16 +000012433 char *out;
Denys Vlasenko50e6d422016-09-30 11:35:54 +020012434 size_t len;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000012435 struct nodelist *bqlist;
12436 smallint quotef;
Ron Yorstona1b0d382020-07-23 08:32:27 +010012437 smallint style;
12438 enum { OLD, NEW, PSUB };
12439#define oldstyle (style == OLD)
Denis Vlasenko46a53062007-09-24 18:30:02 +000012440 smallint pssyntax; /* we are expanding a prompt string */
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010012441 IF_BASH_DOLLAR_SQUOTE(smallint bash_dollar_squote = 0;)
Denys Vlasenko216913c2018-04-02 12:35:04 +020012442 /* syntax stack */
Denys Vlasenkoee1fd122018-04-04 13:59:53 +020012443 struct synstack synbase = { };
Denys Vlasenko216913c2018-04-02 12:35:04 +020012444 struct synstack *synstack = &synbase;
Denis Vlasenkoef527f52008-06-23 01:52:30 +000012445
Denys Vlasenko5f0a75f2017-07-29 22:58:44 +020012446#if ENABLE_ASH_EXPAND_PRMT
Denis Vlasenko46a53062007-09-24 18:30:02 +000012447 pssyntax = (syntax == PSSYNTAX);
12448 if (pssyntax)
12449 syntax = DQSYNTAX;
Denys Vlasenko5f0a75f2017-07-29 22:58:44 +020012450#else
12451 pssyntax = 0; /* constant */
12452#endif
Denys Vlasenkoee1fd122018-04-04 13:59:53 +020012453 synstack->syntax = syntax;
12454
Denys Vlasenko216913c2018-04-02 12:35:04 +020012455 if (syntax == DQSYNTAX)
12456 synstack->dblquote = 1;
12457 quotef = 0;
12458 bqlist = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +000012459
12460 STARTSTACKSTR(out);
Denis Vlasenko176d49d2008-10-06 09:51:47 +000012461 loop:
12462 /* For each line, until end of word */
Denys Vlasenko958581a2010-09-12 15:04:27 +020012463 CHECKEND(); /* set c to PEOF if at end of here document */
12464 for (;;) { /* until end of line or end of word */
12465 CHECKSTRSPACE(4, out); /* permit 4 calls to USTPUTC */
Denys Vlasenko216913c2018-04-02 12:35:04 +020012466 switch (SIT(c, synstack->syntax)) {
Denys Vlasenko958581a2010-09-12 15:04:27 +020012467 case CNL: /* '\n' */
Denys Vlasenko680c3012018-04-11 12:39:18 +020012468 if (synstack->syntax == BASESYNTAX
12469 && !synstack->varnest
12470 ) {
Denys Vlasenko958581a2010-09-12 15:04:27 +020012471 goto endword; /* exit outer loop */
Denys Vlasenko680c3012018-04-11 12:39:18 +020012472 }
Denys Vlasenko958581a2010-09-12 15:04:27 +020012473 USTPUTC(c, out);
Denys Vlasenkoce332a22016-10-02 23:47:34 +020012474 nlprompt();
Denys Vlasenkof7eea8c2020-02-14 16:16:34 +010012475 c = pgetc_top(synstack);
Denys Vlasenko958581a2010-09-12 15:04:27 +020012476 goto loop; /* continue outer loop */
12477 case CWORD:
12478 USTPUTC(c, out);
12479 break;
12480 case CCTL:
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010012481#if BASH_DOLLAR_SQUOTE
Denys Vlasenko958581a2010-09-12 15:04:27 +020012482 if (c == '\\' && bash_dollar_squote) {
12483 c = decode_dollar_squote();
Denys Vlasenko13f20912016-09-25 20:54:25 +020012484 if (c == '\0') {
12485 /* skip $'\000', $'\x00' (like bash) */
12486 break;
12487 }
Denys Vlasenko958581a2010-09-12 15:04:27 +020012488 if (c & 0x100) {
Denys Vlasenko13f20912016-09-25 20:54:25 +020012489 /* Unknown escape. Encode as '\z' */
Denys Vlasenko958581a2010-09-12 15:04:27 +020012490 c = (unsigned char)c;
Denys Vlasenko216913c2018-04-02 12:35:04 +020012491 if (eofmark == NULL || synstack->dblquote)
Denys Vlasenko13f20912016-09-25 20:54:25 +020012492 USTPUTC(CTLESC, out);
12493 USTPUTC('\\', out);
Denis Vlasenkoef527f52008-06-23 01:52:30 +000012494 }
Denys Vlasenko958581a2010-09-12 15:04:27 +020012495 }
Denis Vlasenkoef527f52008-06-23 01:52:30 +000012496#endif
Denys Vlasenkoc4c20122018-04-02 13:29:20 +020012497 if (!eofmark || synstack->dblquote || synstack->varnest)
Denys Vlasenko13f20912016-09-25 20:54:25 +020012498 USTPUTC(CTLESC, out);
Denys Vlasenko958581a2010-09-12 15:04:27 +020012499 USTPUTC(c, out);
12500 break;
12501 case CBACK: /* backslash */
Denys Vlasenko48cb9832021-09-08 09:52:04 +020012502 c = pgetc();
Denys Vlasenko958581a2010-09-12 15:04:27 +020012503 if (c == PEOF) {
12504 USTPUTC(CTLESC, out);
12505 USTPUTC('\\', out);
12506 pungetc();
Denys Vlasenko958581a2010-09-12 15:04:27 +020012507 } else {
Denys Vlasenko5f0a75f2017-07-29 22:58:44 +020012508 if (pssyntax && c == '$') {
Eric Andersenc470f442003-07-28 09:56:35 +000012509 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000012510 USTPUTC('\\', out);
Denys Vlasenko958581a2010-09-12 15:04:27 +020012511 }
Denys Vlasenko8de5b9f2018-02-13 14:43:29 +010012512 /* Backslash is retained if we are in "str"
12513 * and next char isn't dquote-special.
12514 */
Denys Vlasenko216913c2018-04-02 12:35:04 +020012515 if (synstack->dblquote
Denys Vlasenko958581a2010-09-12 15:04:27 +020012516 && c != '\\'
12517 && c != '`'
12518 && c != '$'
Denys Vlasenko216913c2018-04-02 12:35:04 +020012519 && (c != '"' || (eofmark != NULL && !synstack->varnest))
12520 && (c != '}' || !synstack->varnest)
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012521 ) {
Denys Vlasenko8de5b9f2018-02-13 14:43:29 +010012522 USTPUTC(CTLESC, out); /* protect '\' from glob */
Denys Vlasenko958581a2010-09-12 15:04:27 +020012523 USTPUTC('\\', out);
Eric Andersencb57d552001-06-28 07:25:16 +000012524 }
Ron Yorston549deab2015-05-18 09:57:51 +020012525 USTPUTC(CTLESC, out);
Denys Vlasenko0ff78a02010-08-30 15:20:07 +020012526 USTPUTC(c, out);
Denys Vlasenko958581a2010-09-12 15:04:27 +020012527 quotef = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000012528 }
Denys Vlasenko958581a2010-09-12 15:04:27 +020012529 break;
12530 case CSQUOTE:
Denys Vlasenko216913c2018-04-02 12:35:04 +020012531 synstack->syntax = SQSYNTAX;
Denys Vlasenko958581a2010-09-12 15:04:27 +020012532 quotemark:
12533 if (eofmark == NULL) {
12534 USTPUTC(CTLQUOTEMARK, out);
12535 }
12536 break;
12537 case CDQUOTE:
Denys Vlasenko216913c2018-04-02 12:35:04 +020012538 synstack->syntax = DQSYNTAX;
12539 synstack->dblquote = 1;
12540 toggledq:
12541 if (synstack->varnest)
12542 synstack->innerdq ^= 1;
Denys Vlasenko958581a2010-09-12 15:04:27 +020012543 goto quotemark;
12544 case CENDQUOTE:
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010012545 IF_BASH_DOLLAR_SQUOTE(bash_dollar_squote = 0;)
Denys Vlasenko216913c2018-04-02 12:35:04 +020012546 if (eofmark != NULL && synstack->varnest == 0) {
Denys Vlasenko958581a2010-09-12 15:04:27 +020012547 USTPUTC(c, out);
Denys Vlasenko216913c2018-04-02 12:35:04 +020012548 break;
Denys Vlasenko958581a2010-09-12 15:04:27 +020012549 }
Denys Vlasenko216913c2018-04-02 12:35:04 +020012550
12551 if (synstack->dqvarnest == 0) {
12552 synstack->syntax = BASESYNTAX;
12553 synstack->dblquote = 0;
12554 }
12555
12556 quotef = 1;
12557
12558 if (c == '"')
12559 goto toggledq;
12560
12561 goto quotemark;
Denys Vlasenko958581a2010-09-12 15:04:27 +020012562 case CVAR: /* '$' */
12563 PARSESUB(); /* parse substitution */
12564 break;
12565 case CENDVAR: /* '}' */
Denys Vlasenko216913c2018-04-02 12:35:04 +020012566 if (!synstack->innerdq && synstack->varnest > 0) {
12567 if (!--synstack->varnest && synstack->varpushed)
12568 synstack_pop(&synstack);
12569 else if (synstack->dqvarnest > 0)
12570 synstack->dqvarnest--;
Denys Vlasenko958581a2010-09-12 15:04:27 +020012571 c = CTLENDVAR;
12572 }
12573 USTPUTC(c, out);
12574 break;
Denys Vlasenko0b883582016-12-23 16:49:07 +010012575#if ENABLE_FEATURE_SH_MATH
Denys Vlasenko958581a2010-09-12 15:04:27 +020012576 case CLP: /* '(' in arithmetic */
Denys Vlasenko216913c2018-04-02 12:35:04 +020012577 synstack->parenlevel++;
Denys Vlasenko958581a2010-09-12 15:04:27 +020012578 USTPUTC(c, out);
12579 break;
12580 case CRP: /* ')' in arithmetic */
Denys Vlasenko216913c2018-04-02 12:35:04 +020012581 if (synstack->parenlevel > 0) {
12582 synstack->parenlevel--;
Denys Vlasenko958581a2010-09-12 15:04:27 +020012583 } else {
Denys Vlasenko459293b2016-09-29 17:58:58 +020012584 if (pgetc_eatbnl() == ')') {
Ron Yorstonad88bde2015-05-18 09:56:16 +020012585 c = CTLENDARI;
Denys Vlasenko216913c2018-04-02 12:35:04 +020012586 synstack_pop(&synstack);
Denys Vlasenko958581a2010-09-12 15:04:27 +020012587 } else {
12588 /*
12589 * unbalanced parens
12590 * (don't 2nd guess - no error)
12591 */
12592 pungetc();
12593 }
12594 }
12595 USTPUTC(c, out);
12596 break;
12597#endif
12598 case CBQUOTE: /* '`' */
Denys Vlasenko41fddb42018-04-01 16:38:32 +020012599 if (checkkwd & CHKEOFMARK) {
12600 quotef = 1;
12601 USTPUTC('`', out);
12602 break;
12603 }
12604
Denys Vlasenko958581a2010-09-12 15:04:27 +020012605 PARSEBACKQOLD();
12606 break;
12607 case CENDFILE:
12608 goto endword; /* exit outer loop */
Denys Vlasenko958581a2010-09-12 15:04:27 +020012609 default:
Denys Vlasenko216913c2018-04-02 12:35:04 +020012610 if (synstack->varnest == 0) {
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010012611#if BASH_REDIR_OUTPUT
Denys Vlasenko958581a2010-09-12 15:04:27 +020012612 if (c == '&') {
Denys Vlasenko459293b2016-09-29 17:58:58 +020012613//Can't call pgetc_eatbnl() here, this requires three-deep pungetc()
Denys Vlasenko958581a2010-09-12 15:04:27 +020012614 if (pgetc() == '>')
12615 c = 0x100 + '>'; /* flag &> */
12616 pungetc();
12617 }
12618#endif
Ron Yorstona1b0d382020-07-23 08:32:27 +010012619#if BASH_PROCESS_SUBST
12620 if (c == '<' || c == '>') {
12621 if (pgetc() == '(') {
12622 PARSEPROCSUB();
12623 break;
12624 }
12625 pungetc();
12626 }
12627#endif
Denys Vlasenko958581a2010-09-12 15:04:27 +020012628 goto endword; /* exit outer loop */
12629 }
Denys Vlasenko48cb9832021-09-08 09:52:04 +020012630 USTPUTC(c, out);
Denys Vlasenko958581a2010-09-12 15:04:27 +020012631 }
Denys Vlasenkof7eea8c2020-02-14 16:16:34 +010012632 c = pgetc_top(synstack);
Denys Vlasenko958581a2010-09-12 15:04:27 +020012633 } /* for (;;) */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012634 endword:
Denys Vlasenko958581a2010-09-12 15:04:27 +020012635
Denys Vlasenko0b883582016-12-23 16:49:07 +010012636#if ENABLE_FEATURE_SH_MATH
Denys Vlasenko216913c2018-04-02 12:35:04 +020012637 if (synstack->syntax == ARISYNTAX)
Denis Vlasenko559691a2008-10-05 18:39:31 +000012638 raise_error_syntax("missing '))'");
Eric Andersenc470f442003-07-28 09:56:35 +000012639#endif
Denys Vlasenko216913c2018-04-02 12:35:04 +020012640 if (synstack->syntax != BASESYNTAX && eofmark == NULL)
Denis Vlasenko559691a2008-10-05 18:39:31 +000012641 raise_error_syntax("unterminated quoted string");
Denys Vlasenko216913c2018-04-02 12:35:04 +020012642 if (synstack->varnest != 0) {
Eric Andersenc470f442003-07-28 09:56:35 +000012643 /* { */
Denis Vlasenko559691a2008-10-05 18:39:31 +000012644 raise_error_syntax("missing '}'");
Eric Andersencb57d552001-06-28 07:25:16 +000012645 }
12646 USTPUTC('\0', out);
Eric Andersenc470f442003-07-28 09:56:35 +000012647 len = out - (char *)stackblock();
Eric Andersencb57d552001-06-28 07:25:16 +000012648 out = stackblock();
12649 if (eofmark == NULL) {
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010012650 if ((c == '>' || c == '<' IF_BASH_REDIR_OUTPUT( || c == 0x100 + '>'))
Denis Vlasenko834dee72008-10-07 09:18:30 +000012651 && quotef == 0
12652 ) {
Denis Vlasenko559691a2008-10-05 18:39:31 +000012653 if (isdigit_str9(out)) {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000012654 PARSEREDIR(); /* passed as params: out, c */
12655 lasttoken = TREDIR;
12656 return lasttoken;
12657 }
12658 /* else: non-number X seen, interpret it
12659 * as "NNNX>file" = "NNNX >file" */
Eric Andersencb57d552001-06-28 07:25:16 +000012660 }
Denis Vlasenkoef527f52008-06-23 01:52:30 +000012661 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000012662 }
12663 quoteflag = quotef;
12664 backquotelist = bqlist;
12665 grabstackblock(len);
12666 wordtext = out;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012667 lasttoken = TWORD;
12668 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000012669/* end of readtoken routine */
12670
Eric Andersencb57d552001-06-28 07:25:16 +000012671/*
12672 * Check to see whether we are at the end of the here document. When this
12673 * is called, c is set to the first character of the next input line. If
12674 * we are at the end of the here document, this routine sets the c to PEOF.
12675 */
Eric Andersenc470f442003-07-28 09:56:35 +000012676checkend: {
Denys Vlasenko46999802017-07-29 21:12:29 +020012677 if (realeofmark(eofmark)) {
Denys Vlasenkoa7328982017-07-29 19:57:28 +020012678 int markloc;
12679 char *p;
12680
Eric Andersenc470f442003-07-28 09:56:35 +000012681 if (striptabs) {
Denys Vlasenko48cb9832021-09-08 09:52:04 +020012682 while (c == '\t')
12683 c = pgetc();
Eric Andersenc470f442003-07-28 09:56:35 +000012684 }
Eric Andersencb57d552001-06-28 07:25:16 +000012685
Denys Vlasenkoa7328982017-07-29 19:57:28 +020012686 markloc = out - (char *)stackblock();
12687 for (p = eofmark; STPUTC(c, out), *p; p++) {
12688 if (c != *p)
12689 goto more_heredoc;
Denys Vlasenko35e349d2019-09-05 14:31:49 +020012690 /* FIXME: fails for backslash-newlined terminator:
12691 * cat <<EOF
12692 * ...
12693 * EO\
12694 * F
12695 * (see heredoc_bkslash_newline2.tests)
12696 */
Denys Vlasenko48cb9832021-09-08 09:52:04 +020012697 c = pgetc();
Denys Vlasenkoa7328982017-07-29 19:57:28 +020012698 }
12699
12700 if (c == '\n' || c == PEOF) {
12701 c = PEOF;
Roberto A. Fogliettae0bf3df2021-09-07 01:19:31 +020012702 if (trap_depth == 0)
12703 g_parsefile->linno++;
Denys Vlasenkoa7328982017-07-29 19:57:28 +020012704 needprompt = doprompt;
12705 } else {
12706 int len_here;
12707
12708 more_heredoc:
12709 p = (char *)stackblock() + markloc + 1;
12710 len_here = out - p;
12711
12712 if (len_here) {
12713 len_here -= (c >= PEOF);
12714 c = p[-1];
12715
12716 if (len_here) {
12717 char *str;
12718
12719 str = alloca(len_here + 1);
12720 *(char *)mempcpy(str, p, len_here) = '\0';
12721
12722 pushstring(str, NULL);
Eric Andersencb57d552001-06-28 07:25:16 +000012723 }
12724 }
12725 }
Denys Vlasenkoa7328982017-07-29 19:57:28 +020012726
12727 STADJUST((char *)stackblock() + markloc - out, out);
Eric Andersencb57d552001-06-28 07:25:16 +000012728 }
Eric Andersenc470f442003-07-28 09:56:35 +000012729 goto checkend_return;
12730}
Eric Andersencb57d552001-06-28 07:25:16 +000012731
Eric Andersencb57d552001-06-28 07:25:16 +000012732/*
12733 * Parse a redirection operator. The variable "out" points to a string
12734 * specifying the fd to be redirected. The variable "c" contains the
12735 * first character of the redirection operator.
12736 */
Eric Andersenc470f442003-07-28 09:56:35 +000012737parseredir: {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000012738 /* out is already checked to be a valid number or "" */
12739 int fd = (*out == '\0' ? -1 : atoi(out));
Eric Andersenc470f442003-07-28 09:56:35 +000012740 union node *np;
Eric Andersencb57d552001-06-28 07:25:16 +000012741
Denis Vlasenko597906c2008-02-20 16:38:54 +000012742 np = stzalloc(sizeof(struct nfile));
Eric Andersenc470f442003-07-28 09:56:35 +000012743 if (c == '>') {
12744 np->nfile.fd = 1;
Denys Vlasenko220be532018-03-31 19:21:31 +020012745 c = pgetc_eatbnl();
Eric Andersenc470f442003-07-28 09:56:35 +000012746 if (c == '>')
12747 np->type = NAPPEND;
12748 else if (c == '|')
12749 np->type = NCLOBBER;
12750 else if (c == '&')
12751 np->type = NTOFD;
Denis Vlasenko559691a2008-10-05 18:39:31 +000012752 /* it also can be NTO2 (>&file), but we can't figure it out yet */
Eric Andersenc470f442003-07-28 09:56:35 +000012753 else {
12754 np->type = NTO;
12755 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000012756 }
Denis Vlasenko834dee72008-10-07 09:18:30 +000012757 }
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010012758#if BASH_REDIR_OUTPUT
Denis Vlasenko834dee72008-10-07 09:18:30 +000012759 else if (c == 0x100 + '>') { /* this flags &> redirection */
12760 np->nfile.fd = 1;
12761 pgetc(); /* this is '>', no need to check */
12762 np->type = NTO2;
12763 }
12764#endif
12765 else { /* c == '<' */
Denis Vlasenko597906c2008-02-20 16:38:54 +000012766 /*np->nfile.fd = 0; - stzalloc did it */
Denys Vlasenko220be532018-03-31 19:21:31 +020012767 c = pgetc_eatbnl();
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012768 switch (c) {
Eric Andersenc470f442003-07-28 09:56:35 +000012769 case '<':
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012770 if (sizeof(struct nfile) != sizeof(struct nhere)) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000012771 np = stzalloc(sizeof(struct nhere));
12772 /*np->nfile.fd = 0; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000012773 }
12774 np->type = NHERE;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000012775 heredoc = stzalloc(sizeof(struct heredoc));
Eric Andersenc470f442003-07-28 09:56:35 +000012776 heredoc->here = np;
Denys Vlasenko220be532018-03-31 19:21:31 +020012777 c = pgetc_eatbnl();
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012778 if (c == '-') {
Eric Andersenc470f442003-07-28 09:56:35 +000012779 heredoc->striptabs = 1;
12780 } else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000012781 /*heredoc->striptabs = 0; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000012782 pungetc();
12783 }
12784 break;
12785
12786 case '&':
12787 np->type = NFROMFD;
12788 break;
12789
12790 case '>':
12791 np->type = NFROMTO;
12792 break;
12793
12794 default:
12795 np->type = NFROM;
12796 pungetc();
12797 break;
12798 }
Eric Andersencb57d552001-06-28 07:25:16 +000012799 }
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000012800 if (fd >= 0)
12801 np->nfile.fd = fd;
Eric Andersenc470f442003-07-28 09:56:35 +000012802 redirnode = np;
12803 goto parseredir_return;
12804}
Eric Andersencb57d552001-06-28 07:25:16 +000012805
Eric Andersencb57d552001-06-28 07:25:16 +000012806/*
12807 * Parse a substitution. At this point, we have read the dollar sign
12808 * and nothing else.
12809 */
Denis Vlasenkocc571512007-02-23 21:10:35 +000012810
12811/* is_special(c) evaluates to 1 for c in "!#$*-0123456789?@"; 0 otherwise
12812 * (assuming ascii char codes, as the original implementation did) */
12813#define is_special(c) \
Denis Vlasenkoef527f52008-06-23 01:52:30 +000012814 (((unsigned)(c) - 33 < 32) \
12815 && ((0xc1ff920dU >> ((unsigned)(c) - 33)) & 1))
Eric Andersenc470f442003-07-28 09:56:35 +000012816parsesub: {
Denys Vlasenkocd716832009-11-28 22:14:02 +010012817 unsigned char subtype;
Eric Andersenc470f442003-07-28 09:56:35 +000012818 int typeloc;
Eric Andersencb57d552001-06-28 07:25:16 +000012819
Denys Vlasenko73c3e072016-09-29 17:17:04 +020012820 c = pgetc_eatbnl();
Denys Vlasenkoa7328982017-07-29 19:57:28 +020012821 if ((checkkwd & CHKEOFMARK)
Denis Vlasenkoef527f52008-06-23 01:52:30 +000012822 || (c != '(' && c != '{' && !is_name(c) && !is_special(c))
Eric Andersenc470f442003-07-28 09:56:35 +000012823 ) {
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010012824#if BASH_DOLLAR_SQUOTE
Denys Vlasenko216913c2018-04-02 12:35:04 +020012825 if (synstack->syntax != DQSYNTAX && c == '\'')
Denis Vlasenkoef527f52008-06-23 01:52:30 +000012826 bash_dollar_squote = 1;
12827 else
12828#endif
12829 USTPUTC('$', out);
Eric Andersenc470f442003-07-28 09:56:35 +000012830 pungetc();
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020012831 } else if (c == '(') {
12832 /* $(command) or $((arith)) */
Denys Vlasenko73c3e072016-09-29 17:17:04 +020012833 if (pgetc_eatbnl() == '(') {
Denys Vlasenko0b883582016-12-23 16:49:07 +010012834#if ENABLE_FEATURE_SH_MATH
Eric Andersenc470f442003-07-28 09:56:35 +000012835 PARSEARITH();
12836#else
Denys Vlasenko4f8079d2017-07-17 17:11:48 +020012837 raise_error_syntax("support for $((arith)) is disabled");
Eric Andersenc470f442003-07-28 09:56:35 +000012838#endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000012839 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000012840 pungetc();
12841 PARSEBACKQNEW();
12842 }
12843 } else {
Denys Vlasenko48cb9832021-09-08 09:52:04 +020012844 /* $VAR, $<specialchar>, ${...}, or PEOF */
Denys Vlasenko216913c2018-04-02 12:35:04 +020012845 smalluint newsyn = synstack->syntax;
12846
Eric Andersenc470f442003-07-28 09:56:35 +000012847 USTPUTC(CTLVAR, out);
12848 typeloc = out - (char *)stackblock();
Denys Vlasenko3df14102016-10-26 16:41:13 +020012849 STADJUST(1, out);
Eric Andersenc470f442003-07-28 09:56:35 +000012850 subtype = VSNORMAL;
12851 if (c == '{') {
Denys Vlasenko73c3e072016-09-29 17:17:04 +020012852 c = pgetc_eatbnl();
Denys Vlasenkof15aa572016-10-26 15:56:53 +020012853 subtype = 0;
Eric Andersenc470f442003-07-28 09:56:35 +000012854 }
Denys Vlasenkof15aa572016-10-26 15:56:53 +020012855 varname:
Denys Vlasenko3df14102016-10-26 16:41:13 +020012856 if (is_name(c)) {
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020012857 /* $[{[#]]NAME[}] */
Eric Andersenc470f442003-07-28 09:56:35 +000012858 do {
12859 STPUTC(c, out);
Denys Vlasenko73c3e072016-09-29 17:17:04 +020012860 c = pgetc_eatbnl();
Denys Vlasenko3df14102016-10-26 16:41:13 +020012861 } while (is_in_name(c));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012862 } else if (isdigit(c)) {
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020012863 /* $[{[#]]NUM[}] */
Eric Andersenc470f442003-07-28 09:56:35 +000012864 do {
12865 STPUTC(c, out);
Denys Vlasenko73c3e072016-09-29 17:17:04 +020012866 c = pgetc_eatbnl();
Denys Vlasenko53a7a9c2021-06-25 02:09:41 +020012867 } while ((subtype == 0 || subtype == VSLENGTH) && isdigit(c));
Denys Vlasenko58eb8052018-08-05 15:58:13 +020012868 } else if (c != '}') {
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020012869 /* $[{[#]]<specialchar>[}] */
Denys Vlasenkof15aa572016-10-26 15:56:53 +020012870 int cc = c;
12871
Denys Vlasenko73c3e072016-09-29 17:17:04 +020012872 c = pgetc_eatbnl();
Denys Vlasenkof15aa572016-10-26 15:56:53 +020012873 if (!subtype && cc == '#') {
12874 subtype = VSLENGTH;
12875 if (c == '_' || isalnum(c))
12876 goto varname;
12877 cc = c;
12878 c = pgetc_eatbnl();
12879 if (cc == '}' || c != '}') {
12880 pungetc();
12881 subtype = 0;
12882 c = cc;
12883 cc = '#';
12884 }
12885 }
Denys Vlasenko452cc1d2017-08-14 14:23:45 +020012886
12887 if (!is_special(cc)) {
12888 if (subtype == VSLENGTH)
12889 subtype = 0;
12890 goto badsub;
12891 }
12892
Denys Vlasenkof15aa572016-10-26 15:56:53 +020012893 USTPUTC(cc, out);
Denys Vlasenko58eb8052018-08-05 15:58:13 +020012894 } else
12895 goto badsub;
Denys Vlasenko452cc1d2017-08-14 14:23:45 +020012896
Eric Andersenc470f442003-07-28 09:56:35 +000012897 if (subtype == 0) {
Denys Vlasenkof8ddbe12016-07-25 03:56:00 +020012898 static const char types[] ALIGN1 = "}-+?=";
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020012899 /* ${VAR...} but not $VAR or ${#VAR} */
12900 /* c == first char after VAR */
Denys Vlasenko216913c2018-04-02 12:35:04 +020012901 int cc = c;
12902
Eric Andersenc470f442003-07-28 09:56:35 +000012903 switch (c) {
12904 case ':':
Denys Vlasenko73c3e072016-09-29 17:17:04 +020012905 c = pgetc_eatbnl();
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010012906#if BASH_SUBSTR
Denys Vlasenkof8ddbe12016-07-25 03:56:00 +020012907 /* This check is only needed to not misinterpret
12908 * ${VAR:-WORD}, ${VAR:+WORD}, ${VAR:=WORD}, ${VAR:?WORD}
12909 * constructs.
12910 */
12911 if (!strchr(types, c)) {
Denis Vlasenko92e13c22008-03-25 01:17:40 +000012912 subtype = VSSUBSTR;
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020012913 pungetc();
Denys Vlasenko88e15702016-10-26 01:55:56 +020012914 break; /* "goto badsub" is bigger (!) */
Denis Vlasenko92e13c22008-03-25 01:17:40 +000012915 }
12916#endif
Denys Vlasenko3df14102016-10-26 16:41:13 +020012917 subtype = VSNUL;
Eric Andersenc470f442003-07-28 09:56:35 +000012918 /*FALLTHROUGH*/
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020012919 default: {
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020012920 const char *p = strchr(types, c);
Eric Andersenc470f442003-07-28 09:56:35 +000012921 if (p == NULL)
Denys Vlasenko88e15702016-10-26 01:55:56 +020012922 break;
Denys Vlasenko3df14102016-10-26 16:41:13 +020012923 subtype |= p - types + VSNORMAL;
Eric Andersenc470f442003-07-28 09:56:35 +000012924 break;
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020012925 }
Eric Andersenc470f442003-07-28 09:56:35 +000012926 case '%':
Denys Vlasenko216913c2018-04-02 12:35:04 +020012927 case '#':
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020012928 subtype = (c == '#' ? VSTRIMLEFT : VSTRIMRIGHT);
Denys Vlasenko73c3e072016-09-29 17:17:04 +020012929 c = pgetc_eatbnl();
Denys Vlasenko216913c2018-04-02 12:35:04 +020012930 if (c == cc)
12931 subtype++;
12932 else
12933 pungetc();
12934
12935 newsyn = BASESYNTAX;
Denis Vlasenko92e13c22008-03-25 01:17:40 +000012936 break;
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010012937#if BASH_PATTERN_SUBST
Denis Vlasenko92e13c22008-03-25 01:17:40 +000012938 case '/':
Denys Vlasenko6040fe82010-09-12 15:03:16 +020012939 /* ${v/[/]pattern/repl} */
12940//TODO: encode pattern and repl separately.
Denys Vlasenko216913c2018-04-02 12:35:04 +020012941// Currently cases like: v=1;echo ${v/$((1/1))/ONE}
12942// are broken (should print "ONE")
Denis Vlasenko92e13c22008-03-25 01:17:40 +000012943 subtype = VSREPLACE;
Denys Vlasenko216913c2018-04-02 12:35:04 +020012944 newsyn = BASESYNTAX;
Denys Vlasenko73c3e072016-09-29 17:17:04 +020012945 c = pgetc_eatbnl();
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020012946 if (c != '/')
Denys Vlasenko88e15702016-10-26 01:55:56 +020012947 goto badsub;
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020012948 subtype++; /* VSREPLACEALL */
Denis Vlasenko92e13c22008-03-25 01:17:40 +000012949 break;
12950#endif
Eric Andersencb57d552001-06-28 07:25:16 +000012951 }
Eric Andersenc470f442003-07-28 09:56:35 +000012952 } else {
Denys Vlasenko53a7a9c2021-06-25 02:09:41 +020012953 if (subtype == VSLENGTH && c != '}')
12954 subtype = 0;
Denys Vlasenko88e15702016-10-26 01:55:56 +020012955 badsub:
Eric Andersenc470f442003-07-28 09:56:35 +000012956 pungetc();
12957 }
Denys Vlasenko216913c2018-04-02 12:35:04 +020012958
Denys Vlasenkof50e1462018-04-02 21:00:59 +020012959 if (newsyn == ARISYNTAX)
Denys Vlasenko216913c2018-04-02 12:35:04 +020012960 newsyn = DQSYNTAX;
12961
Denys Vlasenkof50e1462018-04-02 21:00:59 +020012962 if ((newsyn != synstack->syntax || synstack->innerdq)
12963 && subtype != VSNORMAL
12964 ) {
Denys Vlasenko216913c2018-04-02 12:35:04 +020012965 synstack_push(&synstack,
12966 synstack->prev ?: alloca(sizeof(*synstack)),
12967 newsyn);
12968
12969 synstack->varpushed = 1;
12970 synstack->dblquote = newsyn != BASESYNTAX;
12971 }
12972
Denys Vlasenko3df14102016-10-26 16:41:13 +020012973 ((unsigned char *)stackblock())[typeloc] = subtype;
Eric Andersenc470f442003-07-28 09:56:35 +000012974 if (subtype != VSNORMAL) {
Denys Vlasenko216913c2018-04-02 12:35:04 +020012975 synstack->varnest++;
12976 if (synstack->dblquote)
12977 synstack->dqvarnest++;
Eric Andersencb57d552001-06-28 07:25:16 +000012978 }
Denys Vlasenko88e15702016-10-26 01:55:56 +020012979 STPUTC('=', out);
Eric Andersencb57d552001-06-28 07:25:16 +000012980 }
Eric Andersenc470f442003-07-28 09:56:35 +000012981 goto parsesub_return;
12982}
Eric Andersencb57d552001-06-28 07:25:16 +000012983
Eric Andersencb57d552001-06-28 07:25:16 +000012984/*
12985 * Called to parse command substitutions. Newstyle is set if the command
12986 * is enclosed inside $(...); nlpp is a pointer to the head of the linked
12987 * list of commands (passed by reference), and savelen is the number of
12988 * characters on the top of the stack which must be preserved.
12989 */
Eric Andersenc470f442003-07-28 09:56:35 +000012990parsebackq: {
12991 struct nodelist **nlpp;
Eric Andersenc470f442003-07-28 09:56:35 +000012992 union node *n;
Ron Yorston072fc602015-07-01 16:46:18 +010012993 char *str;
Eric Andersenc470f442003-07-28 09:56:35 +000012994 size_t savelen;
Denys Vlasenko74aaf052020-02-17 12:11:26 +010012995 struct heredoc *saveheredoclist;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000012996 smallint saveprompt = 0;
12997
Eric Andersenc470f442003-07-28 09:56:35 +000012998 str = NULL;
12999 savelen = out - (char *)stackblock();
13000 if (savelen > 0) {
Denys Vlasenko7bc3d392016-09-17 20:53:47 +020013001 /*
13002 * FIXME: this can allocate very large block on stack and SEGV.
13003 * Example:
13004 * echo "..<100kbytes>..`true` $(true) `true` ..."
Denys Vlasenko73737592016-09-17 20:58:22 +020013005 * allocates 100kb for every command subst. With about
Denys Vlasenko7bc3d392016-09-17 20:53:47 +020013006 * a hundred command substitutions stack overflows.
13007 * With larger prepended string, SEGV happens sooner.
13008 */
Ron Yorston072fc602015-07-01 16:46:18 +010013009 str = alloca(savelen);
Eric Andersenc470f442003-07-28 09:56:35 +000013010 memcpy(str, stackblock(), savelen);
13011 }
Denys Vlasenko7bc3d392016-09-17 20:53:47 +020013012
Eric Andersenc470f442003-07-28 09:56:35 +000013013 if (oldstyle) {
13014 /* We must read until the closing backquote, giving special
Denys Vlasenko60cb48c2013-01-14 15:57:44 +010013015 * treatment to some slashes, and then push the string and
13016 * reread it as input, interpreting it normally.
13017 */
Eric Andersenc470f442003-07-28 09:56:35 +000013018 char *pout;
Eric Andersenc470f442003-07-28 09:56:35 +000013019 size_t psavelen;
13020 char *pstr;
13021
Eric Andersenc470f442003-07-28 09:56:35 +000013022 STARTSTACKSTR(pout);
13023 for (;;) {
Denys Vlasenko958581a2010-09-12 15:04:27 +020013024 int pc;
13025
13026 setprompt_if(needprompt, 2);
Denys Vlasenko220be532018-03-31 19:21:31 +020013027 pc = pgetc_eatbnl();
Denis Vlasenko5cedb752007-02-18 19:56:41 +000013028 switch (pc) {
Eric Andersenc470f442003-07-28 09:56:35 +000013029 case '`':
13030 goto done;
13031
13032 case '\\':
Denys Vlasenko777a6352020-09-29 16:25:32 +020013033 pc = pgetc(); /* not pgetc_eatbnl! */
Eric Andersenc470f442003-07-28 09:56:35 +000013034 if (pc != '\\' && pc != '`' && pc != '$'
Denys Vlasenko216913c2018-04-02 12:35:04 +020013035 && (!synstack->dblquote || pc != '"')
Denys Vlasenko76bc2d62009-11-29 01:37:46 +010013036 ) {
Eric Andersenc470f442003-07-28 09:56:35 +000013037 STPUTC('\\', pout);
Denys Vlasenko76bc2d62009-11-29 01:37:46 +010013038 }
Denys Vlasenko48cb9832021-09-08 09:52:04 +020013039 break;
Eric Andersenc470f442003-07-28 09:56:35 +000013040
13041 case PEOF:
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013042 raise_error_syntax("EOF in backquote substitution");
Eric Andersenc470f442003-07-28 09:56:35 +000013043
13044 case '\n':
Denys Vlasenkoce332a22016-10-02 23:47:34 +020013045 nlnoprompt();
Eric Andersenc470f442003-07-28 09:56:35 +000013046 break;
13047
13048 default:
13049 break;
13050 }
13051 STPUTC(pc, pout);
13052 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000013053 done:
Eric Andersenc470f442003-07-28 09:56:35 +000013054 STPUTC('\0', pout);
13055 psavelen = pout - (char *)stackblock();
13056 if (psavelen > 0) {
13057 pstr = grabstackstr(pout);
13058 setinputstring(pstr);
13059 }
13060 }
13061 nlpp = &bqlist;
13062 while (*nlpp)
13063 nlpp = &(*nlpp)->next;
Denis Vlasenko597906c2008-02-20 16:38:54 +000013064 *nlpp = stzalloc(sizeof(**nlpp));
13065 /* (*nlpp)->next = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000013066
Denys Vlasenko74aaf052020-02-17 12:11:26 +010013067 saveheredoclist = heredoclist;
13068 heredoclist = NULL;
13069
Eric Andersenc470f442003-07-28 09:56:35 +000013070 if (oldstyle) {
13071 saveprompt = doprompt;
13072 doprompt = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000013073 }
13074
Eric Andersenc470f442003-07-28 09:56:35 +000013075 n = list(2);
13076
13077 if (oldstyle)
13078 doprompt = saveprompt;
Denys Vlasenko74aaf052020-02-17 12:11:26 +010013079 else {
13080 if (readtoken() != TRP)
13081 raise_error_unexpected_syntax(TRP);
13082 setinputstring(nullstr);
Denys Vlasenko74aaf052020-02-17 12:11:26 +010013083 }
13084
Denys Vlasenko9a1a6592020-02-22 16:39:27 +010013085 parseheredoc();
Denys Vlasenko74aaf052020-02-17 12:11:26 +010013086 heredoclist = saveheredoclist;
Eric Andersenc470f442003-07-28 09:56:35 +000013087
13088 (*nlpp)->n = n;
Denys Vlasenko74aaf052020-02-17 12:11:26 +010013089 /* Start reading from old file again. */
13090 popfile();
13091 /* Ignore any pushed back tokens left from the backquote parsing. */
13092 if (oldstyle)
Eric Andersenc470f442003-07-28 09:56:35 +000013093 tokpushback = 0;
Denys Vlasenkoc55847f2020-02-17 15:59:08 +010013094 out = growstackto(savelen + 1);
Eric Andersenc470f442003-07-28 09:56:35 +000013095 if (str) {
13096 memcpy(out, str, savelen);
13097 STADJUST(savelen, out);
Eric Andersenc470f442003-07-28 09:56:35 +000013098 }
Ron Yorstona1b0d382020-07-23 08:32:27 +010013099#if BASH_PROCESS_SUBST
13100 if (style == PSUB)
13101 USTPUTC(c == '<' ? CTLFROMPROC : CTLTOPROC, out);
13102 else
13103#endif
13104 USTPUTC(CTLBACKQ, out);
Eric Andersenc470f442003-07-28 09:56:35 +000013105 if (oldstyle)
13106 goto parsebackq_oldreturn;
Ron Yorstona1b0d382020-07-23 08:32:27 +010013107#if BASH_PROCESS_SUBST
13108 else if (style == PSUB)
13109 goto parsebackq_psreturn;
13110#endif
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013111 goto parsebackq_newreturn;
Eric Andersenc470f442003-07-28 09:56:35 +000013112}
13113
Denys Vlasenko0b883582016-12-23 16:49:07 +010013114#if ENABLE_FEATURE_SH_MATH
Eric Andersencb57d552001-06-28 07:25:16 +000013115/*
13116 * Parse an arithmetic expansion (indicate start of one and set state)
13117 */
Eric Andersenc470f442003-07-28 09:56:35 +000013118parsearith: {
Denys Vlasenko216913c2018-04-02 12:35:04 +020013119
13120 synstack_push(&synstack,
13121 synstack->prev ?: alloca(sizeof(*synstack)),
13122 ARISYNTAX);
13123 synstack->dblquote = 1;
Ron Yorstonad88bde2015-05-18 09:56:16 +020013124 USTPUTC(CTLARI, out);
Eric Andersenc470f442003-07-28 09:56:35 +000013125 goto parsearith_return;
13126}
13127#endif
Eric Andersenc470f442003-07-28 09:56:35 +000013128} /* end of readtoken */
13129
Eric Andersencb57d552001-06-28 07:25:16 +000013130/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013131 * Read the next input token.
13132 * If the token is a word, we set backquotelist to the list of cmds in
13133 * backquotes. We set quoteflag to true if any part of the word was
13134 * quoted.
13135 * If the token is TREDIR, then we set redirnode to a structure containing
13136 * the redirection.
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013137 *
13138 * [Change comment: here documents and internal procedures]
13139 * [Readtoken shouldn't have any arguments. Perhaps we should make the
13140 * word parsing code into a separate routine. In this case, readtoken
13141 * doesn't need to have any internal procedures, but parseword does.
13142 * We could also make parseoperator in essence the main routine, and
13143 * have parseword (readtoken1?) handle both words and redirection.]
Eric Andersencb57d552001-06-28 07:25:16 +000013144 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013145#define NEW_xxreadtoken
13146#ifdef NEW_xxreadtoken
13147/* singles must be first! */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000013148static const char xxreadtoken_chars[7] ALIGN1 = {
Denis Vlasenko834dee72008-10-07 09:18:30 +000013149 '\n', '(', ')', /* singles */
13150 '&', '|', ';', /* doubles */
13151 0
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000013152};
Eric Andersencb57d552001-06-28 07:25:16 +000013153
Denis Vlasenko834dee72008-10-07 09:18:30 +000013154#define xxreadtoken_singles 3
13155#define xxreadtoken_doubles 3
13156
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000013157static const char xxreadtoken_tokens[] ALIGN1 = {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013158 TNL, TLP, TRP, /* only single occurrence allowed */
13159 TBACKGND, TPIPE, TSEMI, /* if single occurrence */
13160 TEOF, /* corresponds to trailing nul */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000013161 TAND, TOR, TENDCASE /* if double occurrence */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013162};
13163
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013164static int
13165xxreadtoken(void)
13166{
13167 int c;
13168
13169 if (tokpushback) {
13170 tokpushback = 0;
13171 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000013172 }
Denys Vlasenko958581a2010-09-12 15:04:27 +020013173 setprompt_if(needprompt, 2);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013174 for (;;) { /* until token or start of word found */
Denys Vlasenko220be532018-03-31 19:21:31 +020013175 c = pgetc_eatbnl();
Denys Vlasenko48cb9832021-09-08 09:52:04 +020013176 if (c == ' ' || c == '\t')
Denis Vlasenko176d49d2008-10-06 09:51:47 +000013177 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013178
Denis Vlasenko176d49d2008-10-06 09:51:47 +000013179 if (c == '#') {
13180 while ((c = pgetc()) != '\n' && c != PEOF)
13181 continue;
13182 pungetc();
13183 } else if (c == '\\') {
Denys Vlasenko220be532018-03-31 19:21:31 +020013184 break; /* return readtoken1(...) */
Denis Vlasenko176d49d2008-10-06 09:51:47 +000013185 } else {
13186 const char *p;
13187
13188 p = xxreadtoken_chars + sizeof(xxreadtoken_chars) - 1;
13189 if (c != PEOF) {
13190 if (c == '\n') {
Denys Vlasenkoce332a22016-10-02 23:47:34 +020013191 nlnoprompt();
Denis Vlasenko176d49d2008-10-06 09:51:47 +000013192 }
13193
13194 p = strchr(xxreadtoken_chars, c);
Denis Vlasenko834dee72008-10-07 09:18:30 +000013195 if (p == NULL)
13196 break; /* return readtoken1(...) */
Denis Vlasenko176d49d2008-10-06 09:51:47 +000013197
Denis Vlasenko834dee72008-10-07 09:18:30 +000013198 if ((int)(p - xxreadtoken_chars) >= xxreadtoken_singles) {
Denys Vlasenko1e5111b2018-04-01 03:04:55 +020013199 int cc = pgetc_eatbnl();
Denis Vlasenko834dee72008-10-07 09:18:30 +000013200 if (cc == c) { /* double occurrence? */
Denis Vlasenko176d49d2008-10-06 09:51:47 +000013201 p += xxreadtoken_doubles + 1;
13202 } else {
13203 pungetc();
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010013204#if BASH_REDIR_OUTPUT
Denis Vlasenko834dee72008-10-07 09:18:30 +000013205 if (c == '&' && cc == '>') /* &> */
13206 break; /* return readtoken1(...) */
13207#endif
Denis Vlasenko176d49d2008-10-06 09:51:47 +000013208 }
13209 }
13210 }
13211 lasttoken = xxreadtoken_tokens[p - xxreadtoken_chars];
13212 return lasttoken;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013213 }
Denis Vlasenko176d49d2008-10-06 09:51:47 +000013214 } /* for (;;) */
Denis Vlasenko834dee72008-10-07 09:18:30 +000013215
13216 return readtoken1(c, BASESYNTAX, (char *) NULL, 0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013217}
Denis Vlasenko176d49d2008-10-06 09:51:47 +000013218#else /* old xxreadtoken */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013219#define RETURN(token) return lasttoken = token
13220static int
13221xxreadtoken(void)
13222{
13223 int c;
13224
13225 if (tokpushback) {
13226 tokpushback = 0;
13227 return lasttoken;
13228 }
Denys Vlasenko958581a2010-09-12 15:04:27 +020013229 setprompt_if(needprompt, 2);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013230 for (;;) { /* until token or start of word found */
Denys Vlasenko220be532018-03-31 19:21:31 +020013231 c = pgetc_eatbnl();
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013232 switch (c) {
13233 case ' ': case '\t':
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013234 continue;
13235 case '#':
Denis Vlasenkof7d56652008-03-25 05:51:41 +000013236 while ((c = pgetc()) != '\n' && c != PEOF)
13237 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013238 pungetc();
13239 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013240 case '\n':
Denys Vlasenkoce332a22016-10-02 23:47:34 +020013241 nlnoprompt();
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013242 RETURN(TNL);
13243 case PEOF:
13244 RETURN(TEOF);
13245 case '&':
Denys Vlasenko220be532018-03-31 19:21:31 +020013246 if (pgetc_eatbnl() == '&')
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013247 RETURN(TAND);
13248 pungetc();
13249 RETURN(TBACKGND);
13250 case '|':
Denys Vlasenko220be532018-03-31 19:21:31 +020013251 if (pgetc_eatbnl() == '|')
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013252 RETURN(TOR);
13253 pungetc();
13254 RETURN(TPIPE);
13255 case ';':
Denys Vlasenko220be532018-03-31 19:21:31 +020013256 if (pgetc_eatbnl() == ';')
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013257 RETURN(TENDCASE);
13258 pungetc();
13259 RETURN(TSEMI);
13260 case '(':
13261 RETURN(TLP);
13262 case ')':
13263 RETURN(TRP);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013264 }
Denys Vlasenko220be532018-03-31 19:21:31 +020013265 break;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013266 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013267 return readtoken1(c, BASESYNTAX, (char *)NULL, 0);
13268#undef RETURN
13269}
Denis Vlasenko176d49d2008-10-06 09:51:47 +000013270#endif /* old xxreadtoken */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013271
13272static int
13273readtoken(void)
13274{
13275 int t;
Ron Yorston713f07d2015-10-29 16:44:56 +000013276 int kwd = checkkwd;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013277#if DEBUG
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000013278 smallint alreadyseen = tokpushback;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013279#endif
13280
13281#if ENABLE_ASH_ALIAS
13282 top:
13283#endif
13284
13285 t = xxreadtoken();
13286
13287 /*
13288 * eat newlines
13289 */
Ron Yorston713f07d2015-10-29 16:44:56 +000013290 if (kwd & CHKNL) {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013291 while (t == TNL) {
13292 parseheredoc();
Denys Vlasenko8c68ae82021-09-08 01:43:12 +020013293 checkkwd = 0;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013294 t = xxreadtoken();
13295 }
13296 }
13297
Denys Vlasenko8c68ae82021-09-08 01:43:12 +020013298 kwd |= checkkwd;
13299 checkkwd = 0;
13300
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013301 if (t != TWORD || quoteflag) {
13302 goto out;
13303 }
13304
13305 /*
13306 * check for keywords
13307 */
Ron Yorston713f07d2015-10-29 16:44:56 +000013308 if (kwd & CHKKWD) {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013309 const char *const *pp;
13310
13311 pp = findkwd(wordtext);
13312 if (pp) {
13313 lasttoken = t = pp - tokname_array;
Denys Vlasenko888527c2016-10-02 16:54:17 +020013314 TRACE(("keyword '%s' recognized\n", tokname_array[t]));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013315 goto out;
13316 }
13317 }
13318
Denys Vlasenko8c68ae82021-09-08 01:43:12 +020013319 if (kwd & CHKALIAS) {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013320#if ENABLE_ASH_ALIAS
13321 struct alias *ap;
13322 ap = lookupalias(wordtext, 1);
13323 if (ap != NULL) {
13324 if (*ap->val) {
13325 pushstring(ap->val, ap);
13326 }
13327 goto top;
13328 }
13329#endif
13330 }
13331 out:
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013332#if DEBUG
13333 if (!alreadyseen)
Denys Vlasenko888527c2016-10-02 16:54:17 +020013334 TRACE(("token '%s' %s\n", tokname_array[t], t == TWORD ? wordtext : ""));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013335 else
Denys Vlasenko888527c2016-10-02 16:54:17 +020013336 TRACE(("reread token '%s' %s\n", tokname_array[t], t == TWORD ? wordtext : ""));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013337#endif
13338 return t;
Eric Andersencb57d552001-06-28 07:25:16 +000013339}
13340
Eric Andersencb57d552001-06-28 07:25:16 +000013341/*
Denys Vlasenko86e83ec2009-07-23 22:07:07 +020013342 * Read and parse a command. Returns NODE_EOF on end of file.
13343 * (NULL is a valid parse tree indicating a blank line.)
Eric Andersencb57d552001-06-28 07:25:16 +000013344 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013345static union node *
13346parsecmd(int interact)
Eric Andersen90898442003-08-06 11:20:52 +000013347{
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013348 tokpushback = 0;
Ron Yorstonc0e00762015-10-29 11:30:55 +000013349 checkkwd = 0;
13350 heredoclist = 0;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013351 doprompt = interact;
Denys Vlasenko958581a2010-09-12 15:04:27 +020013352 setprompt_if(doprompt, doprompt);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013353 needprompt = 0;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013354 return list(1);
13355}
13356
13357/*
13358 * Input any here documents.
13359 */
13360static void
13361parseheredoc(void)
13362{
13363 struct heredoc *here;
13364 union node *n;
13365
13366 here = heredoclist;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000013367 heredoclist = NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013368
13369 while (here) {
Christoph Schulz03ad7ae2018-11-20 17:45:52 +010013370 tokpushback = 0;
Denys Vlasenko958581a2010-09-12 15:04:27 +020013371 setprompt_if(needprompt, 2);
Denys Vlasenkoacf79f92020-02-14 16:12:06 +010013372 if (here->here->type == NHERE)
13373 readtoken1(pgetc(), SQSYNTAX, here->eofmark, here->striptabs);
13374 else
13375 readtoken1(pgetc_eatbnl(), DQSYNTAX, here->eofmark, here->striptabs);
Denis Vlasenko597906c2008-02-20 16:38:54 +000013376 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013377 n->narg.type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000013378 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013379 n->narg.text = wordtext;
13380 n->narg.backquote = backquotelist;
13381 here->here->nhere.doc = n;
13382 here = here->next;
Eric Andersencb57d552001-06-28 07:25:16 +000013383 }
Eric Andersencb57d552001-06-28 07:25:16 +000013384}
13385
13386
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000013387static const char *
Denys Vlasenko46999802017-07-29 21:12:29 +020013388expandstr(const char *ps, int syntax_type)
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000013389{
Denys Vlasenko1c06ddd2021-09-08 00:56:53 +020013390 struct parsefile *file_stop;
13391 struct jmploc *volatile savehandler;
13392 struct heredoc *saveheredoclist;
13393 const char *result;
Denys Vlasenko2a6d29a2016-10-25 21:17:52 +020013394 int saveprompt;
Ron Yorstond1a2fa22019-04-18 09:49:13 +010013395 struct jmploc jmploc;
Denys Vlasenko1c06ddd2021-09-08 00:56:53 +020013396 union node n;
Ron Yorstonb0c711e2020-01-23 11:26:08 +000013397 int err;
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000013398
Denys Vlasenko1c06ddd2021-09-08 00:56:53 +020013399 file_stop = g_parsefile;
13400
Denys Vlasenko46999802017-07-29 21:12:29 +020013401 /* XXX Fix (char *) cast. */
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000013402 setinputstring((char *)ps);
Denys Vlasenko2a6d29a2016-10-25 21:17:52 +020013403
Denys Vlasenko1c06ddd2021-09-08 00:56:53 +020013404 saveheredoclist = heredoclist;
13405 heredoclist = NULL;
Denys Vlasenko2a6d29a2016-10-25 21:17:52 +020013406 saveprompt = doprompt;
13407 doprompt = 0;
Ron Yorstonb0c711e2020-01-23 11:26:08 +000013408 result = ps;
Denys Vlasenko1c06ddd2021-09-08 00:56:53 +020013409 savehandler = exception_handler;
Ron Yorstonb0c711e2020-01-23 11:26:08 +000013410 err = setjmp(jmploc.loc);
13411 if (err)
13412 goto out;
Denys Vlasenkoa2e32b32017-10-12 19:20:13 +020013413
13414 /* readtoken1() might die horribly.
Denys Vlasenko3c183a82017-10-12 19:35:42 +020013415 * Try a prompt with syntactically wrong command:
Denys Vlasenkoa2e32b32017-10-12 19:20:13 +020013416 * PS1='$(date "+%H:%M:%S) > '
13417 */
Ron Yorstonb0c711e2020-01-23 11:26:08 +000013418 exception_handler = &jmploc;
Denys Vlasenkoc5402562021-09-08 01:03:57 +020013419 readtoken1(pgetc_eatbnl(), syntax_type, FAKEEOFMARK, 0);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000013420
13421 n.narg.type = NARG;
13422 n.narg.next = NULL;
13423 n.narg.text = wordtext;
13424 n.narg.backquote = backquotelist;
13425
Ron Yorstond1a2fa22019-04-18 09:49:13 +010013426 /* expandarg() might fail too:
13427 * PS1='$((123+))'
13428 */
Ron Yorstonb0c711e2020-01-23 11:26:08 +000013429 expandarg(&n, NULL, EXP_QUOTED);
13430 result = stackblock();
13431
13432out:
Ron Yorstond1a2fa22019-04-18 09:49:13 +010013433 exception_handler = savehandler;
Ron Yorstonb0c711e2020-01-23 11:26:08 +000013434 if (err && exception_type != EXERROR)
13435 longjmp(exception_handler->loc, 1);
Ron Yorstond1a2fa22019-04-18 09:49:13 +010013436
Ron Yorstonb0c711e2020-01-23 11:26:08 +000013437 doprompt = saveprompt;
13438 /* Try: PS1='`xxx(`' */
13439 unwindfiles(file_stop);
Denys Vlasenko1c06ddd2021-09-08 00:56:53 +020013440 heredoclist = saveheredoclist;
Ron Yorstonb0c711e2020-01-23 11:26:08 +000013441
13442 return result;
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000013443}
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000013444
Denys Vlasenko1e3e2cc2017-07-25 20:31:14 +020013445static inline int
13446parser_eof(void)
13447{
13448 return tokpushback && lasttoken == TEOF;
13449}
13450
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013451/*
13452 * Execute a command or commands contained in a string.
13453 */
13454static int
Denys Vlasenko7b3fa1e2016-10-01 15:10:16 +020013455evalstring(char *s, int flags)
Eric Andersenc470f442003-07-28 09:56:35 +000013456{
Denys Vlasenko493b9ca2016-10-30 18:27:14 +010013457 struct jmploc *volatile savehandler;
Denys Vlasenkod81e9f52016-10-28 15:43:50 +020013458 struct jmploc jmploc;
13459 int ex;
13460
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013461 union node *n;
13462 struct stackmark smark;
Denys Vlasenko928e2a72016-09-29 00:30:31 +020013463 int status;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013464
Denys Vlasenko8e2bc472016-09-28 23:02:57 +020013465 s = sstrdup(s);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013466 setinputstring(s);
13467 setstackmark(&smark);
13468
Denys Vlasenko928e2a72016-09-29 00:30:31 +020013469 status = 0;
Denys Vlasenkod81e9f52016-10-28 15:43:50 +020013470 /* On exception inside execution loop, we must popfile().
13471 * Try interactively:
13472 * readonly a=a
13473 * command eval "a=b" # throws "is read only" error
13474 * "command BLTIN" is not supposed to abort (even in non-interactive use).
13475 * But if we skip popfile(), we hit EOF in eval's string, and exit.
13476 */
13477 savehandler = exception_handler;
Denys Vlasenkod81e9f52016-10-28 15:43:50 +020013478 ex = setjmp(jmploc.loc);
13479 if (ex)
13480 goto out;
Denys Vlasenko493b9ca2016-10-30 18:27:14 +010013481 exception_handler = &jmploc;
Denys Vlasenkod81e9f52016-10-28 15:43:50 +020013482
Denys Vlasenko86e83ec2009-07-23 22:07:07 +020013483 while ((n = parsecmd(0)) != NODE_EOF) {
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +020013484 int i;
13485
Denys Vlasenko1e3e2cc2017-07-25 20:31:14 +020013486 i = evaltree(n, flags & ~(parser_eof() ? 0 : EV_EXIT));
Denys Vlasenko928e2a72016-09-29 00:30:31 +020013487 if (n)
13488 status = i;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013489 popstackmark(&smark);
Denys Vlasenko928e2a72016-09-29 00:30:31 +020013490 if (evalskip)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013491 break;
13492 }
Denys Vlasenkod81e9f52016-10-28 15:43:50 +020013493 out:
Denys Vlasenko8e2bc472016-09-28 23:02:57 +020013494 popstackmark(&smark);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013495 popfile();
Denys Vlasenko8e2bc472016-09-28 23:02:57 +020013496 stunalloc(s);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013497
Denys Vlasenkod81e9f52016-10-28 15:43:50 +020013498 exception_handler = savehandler;
13499 if (ex)
Denys Vlasenko14c85eb2017-10-12 19:40:47 +020013500 longjmp(exception_handler->loc, ex);
Denys Vlasenkod81e9f52016-10-28 15:43:50 +020013501
Denys Vlasenko928e2a72016-09-29 00:30:31 +020013502 return status;
Eric Andersenc470f442003-07-28 09:56:35 +000013503}
13504
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013505/*
13506 * The eval command.
13507 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020013508static int FAST_FUNC
Denys Vlasenko7b3fa1e2016-10-01 15:10:16 +020013509evalcmd(int argc UNUSED_PARAM, char **argv, int flags)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013510{
13511 char *p;
13512 char *concat;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013513
Denis Vlasenko68404f12008-03-17 09:00:54 +000013514 if (argv[1]) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013515 p = argv[1];
Denis Vlasenko68404f12008-03-17 09:00:54 +000013516 argv += 2;
13517 if (argv[0]) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013518 STARTSTACKSTR(concat);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013519 for (;;) {
13520 concat = stack_putstr(p, concat);
Denis Vlasenko68404f12008-03-17 09:00:54 +000013521 p = *argv++;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013522 if (p == NULL)
13523 break;
13524 STPUTC(' ', concat);
13525 }
13526 STPUTC('\0', concat);
13527 p = grabstackstr(concat);
13528 }
Denys Vlasenko7b3fa1e2016-10-01 15:10:16 +020013529 return evalstring(p, flags & EV_TESTED);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013530 }
Denys Vlasenko928e2a72016-09-29 00:30:31 +020013531 return 0;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013532}
13533
13534/*
Denys Vlasenko285ad152009-12-04 23:02:27 +010013535 * Read and execute commands.
13536 * "Top" is nonzero for the top level command loop;
13537 * it turns on prompting if the shell is interactive.
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013538 */
13539static int
13540cmdloop(int top)
13541{
13542 union node *n;
13543 struct stackmark smark;
13544 int inter;
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +020013545 int status = 0;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013546 int numeof = 0;
13547
13548 TRACE(("cmdloop(%d) called\n", top));
13549 for (;;) {
13550 int skip;
13551
13552 setstackmark(&smark);
13553#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +000013554 if (doing_jobctl)
Denys Vlasenko9c541002015-10-07 15:44:36 +020013555 showjobs(SHOW_CHANGED|SHOW_STDERR);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013556#endif
13557 inter = 0;
13558 if (iflag && top) {
13559 inter++;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013560 chkmail();
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013561 }
13562 n = parsecmd(inter);
Denys Vlasenko7cee00e2009-07-24 01:08:03 +020013563#if DEBUG
13564 if (DEBUG > 2 && debug && (n != NODE_EOF))
Denys Vlasenko883cea42009-07-11 15:31:59 +020013565 showtree(n);
Denis Vlasenko135cecb2009-04-12 00:00:57 +000013566#endif
Denys Vlasenko86e83ec2009-07-23 22:07:07 +020013567 if (n == NODE_EOF) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013568 if (!top || numeof >= 50)
13569 break;
13570 if (!stoppedjobs()) {
Ron Yorston5726df52021-09-12 11:21:48 +010013571 if (!iflag)
13572 break;
Denys Vlasenko226b8a12020-02-16 18:57:53 +010013573 if (!Iflag) {
Ron Yorston5726df52021-09-12 11:21:48 +010013574 newline_and_flush(stderr);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013575 break;
Denys Vlasenko226b8a12020-02-16 18:57:53 +010013576 }
Ron Yorston5726df52021-09-12 11:21:48 +010013577 /* "set -o ignoreeof" active, do not exit command loop on ^D */
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013578 out2str("\nUse \"exit\" to leave shell.\n");
13579 }
13580 numeof++;
Denys Vlasenko41beb532021-09-07 01:52:21 +020013581 } else {
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +020013582 int i;
13583
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +000013584 /* job_warning can only be 2,1,0. Here 2->1, 1/0->0 */
13585 job_warning >>= 1;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013586 numeof = 0;
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +020013587 i = evaltree(n, 0);
13588 if (n)
13589 status = i;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013590 }
13591 popstackmark(&smark);
13592 skip = evalskip;
13593
13594 if (skip) {
Denys Vlasenkocd24a502020-02-20 16:47:01 +010013595 evalskip &= ~(SKIPFUNC | SKIPFUNCDEF);
Denys Vlasenko0840c912016-10-01 15:27:44 +020013596 break;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013597 }
13598 }
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +020013599 return status;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013600}
13601
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000013602/*
13603 * Take commands from a file. To be compatible we should do a path
13604 * search for the file, which is necessary to find sub-commands.
13605 */
13606static char *
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +010013607find_dot_file(char *basename)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000013608{
13609 char *fullname;
13610 const char *path = pathval();
13611 struct stat statb;
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +010013612 int len;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000013613
13614 /* don't try this for absolute or relative paths */
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +010013615 if (strchr(basename, '/'))
13616 return basename;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000013617
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +010013618 while ((len = padvance(&path, basename)) >= 0) {
13619 fullname = stackblock();
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +010013620 if ((!pathopt || *pathopt == 'f')
13621 && !stat(fullname, &statb) && S_ISREG(statb.st_mode)
13622 ) {
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +010013623 /* This will be freed by the caller. */
13624 return stalloc(len);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000013625 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000013626 }
Denys Vlasenko01f7b9e2018-01-26 15:15:43 +010013627 /* not found in PATH */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000013628
Denys Vlasenko01f7b9e2018-01-26 15:15:43 +010013629#if ENABLE_ASH_BASH_SOURCE_CURDIR
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +010013630 return basename;
Denys Vlasenko01f7b9e2018-01-26 15:15:43 +010013631#else
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +010013632 ash_msg_and_raise_error("%s: not found", basename);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000013633 /* NOTREACHED */
Denys Vlasenko01f7b9e2018-01-26 15:15:43 +010013634#endif
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000013635}
13636
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020013637static int FAST_FUNC
Denys Vlasenko0cdb7ea2016-10-02 03:16:00 +020013638dotcmd(int argc_ UNUSED_PARAM, char **argv_ UNUSED_PARAM)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013639{
Denys Vlasenko0cdb7ea2016-10-02 03:16:00 +020013640 /* "false; . empty_file; echo $?" should print 0, not 1: */
13641 int status = 0;
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020013642 char *fullname;
Denys Vlasenko0cdb7ea2016-10-02 03:16:00 +020013643 char **argv;
Denys Vlasenkofb87d932017-01-09 08:22:06 +010013644 char *args_need_save;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013645 volatile struct shparam saveparam;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013646
Denys Vlasenko981a0562017-07-26 19:53:11 +020013647//???
13648// struct strlist *sp;
13649// for (sp = cmdenviron; sp; sp = sp->next)
13650// setvareq(ckstrdup(sp->text), VSTRFIXED | VTEXTFIXED);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013651
Denys Vlasenko0cdb7ea2016-10-02 03:16:00 +020013652 nextopt(nullstr); /* handle possible "--" */
13653 argv = argptr;
13654
13655 if (!argv[0]) {
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020013656 /* bash says: "bash: .: filename argument required" */
13657 return 2; /* bash compat */
13658 }
13659
Denys Vlasenko091f8312013-03-17 14:25:22 +010013660 /* This aborts if file isn't found, which is POSIXly correct.
13661 * bash returns exitcode 1 instead.
13662 */
Denys Vlasenko0cdb7ea2016-10-02 03:16:00 +020013663 fullname = find_dot_file(argv[0]);
13664 argv++;
Denys Vlasenkofb87d932017-01-09 08:22:06 +010013665 args_need_save = argv[0];
Denys Vlasenko5f7c82b2017-02-03 13:00:06 +010013666 if (args_need_save) { /* ". FILE ARGS", and ARGS are not empty */
Denys Vlasenko0cdb7ea2016-10-02 03:16:00 +020013667 int argc;
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020013668 saveparam = shellparam;
13669 shellparam.malloced = 0;
Denys Vlasenko0cdb7ea2016-10-02 03:16:00 +020013670 argc = 1;
13671 while (argv[argc])
13672 argc++;
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020013673 shellparam.nparam = argc;
13674 shellparam.p = argv;
13675 };
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013676
Denys Vlasenko091f8312013-03-17 14:25:22 +010013677 /* This aborts if file can't be opened, which is POSIXly correct.
13678 * bash returns exitcode 1 instead.
13679 */
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020013680 setinputfile(fullname, INPUT_PUSH_FILE);
13681 commandname = fullname;
Denys Vlasenko0cdb7ea2016-10-02 03:16:00 +020013682 status = cmdloop(0);
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020013683 popfile();
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013684
Denys Vlasenkofb87d932017-01-09 08:22:06 +010013685 if (args_need_save) {
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020013686 freeparam(&shellparam);
13687 shellparam = saveparam;
13688 };
13689
Denys Vlasenko0cdb7ea2016-10-02 03:16:00 +020013690 return status;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013691}
13692
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020013693static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000013694exitcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013695{
13696 if (stoppedjobs())
13697 return 0;
Denys Vlasenko4ccddc82020-02-14 17:27:18 +010013698
Denys Vlasenko970470e2020-02-14 17:32:22 +010013699 if (argv[1])
13700 savestatus = number(argv[1]);
Denys Vlasenko4ccddc82020-02-14 17:27:18 +010013701
Denys Vlasenko704c5962021-09-15 19:31:44 +020013702//TODO: this script
13703// trap 'echo trap:$FUNCNAME' EXIT
13704// f() { exit; }
13705// f
13706//prints "trap:f" in bash. We can call exitshell() here to achieve this.
13707//For now, keeping dash code:
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013708 raise_exception(EXEXIT);
13709 /* NOTREACHED */
13710}
13711
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013712/*
13713 * Read a file containing shell functions.
13714 */
13715static void
13716readcmdfile(char *name)
13717{
13718 setinputfile(name, INPUT_PUSH_FILE);
13719 cmdloop(0);
13720 popfile();
13721}
13722
13723
Denis Vlasenkocc571512007-02-23 21:10:35 +000013724/* ============ find_command inplementation */
13725
13726/*
13727 * Resolve a command name. If you change this routine, you may have to
13728 * change the shellexec routine as well.
13729 */
13730static void
13731find_command(char *name, struct cmdentry *entry, int act, const char *path)
13732{
13733 struct tblentry *cmdp;
13734 int idx;
13735 int prev;
13736 char *fullname;
13737 struct stat statb;
13738 int e;
13739 int updatetbl;
13740 struct builtincmd *bcmd;
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +010013741 int len;
Denis Vlasenkocc571512007-02-23 21:10:35 +000013742
13743 /* If name contains a slash, don't use PATH or hash table */
13744 if (strchr(name, '/') != NULL) {
13745 entry->u.index = -1;
13746 if (act & DO_ABS) {
13747 while (stat(name, &statb) < 0) {
13748#ifdef SYSV
13749 if (errno == EINTR)
13750 continue;
13751#endif
13752 entry->cmdtype = CMDUNKNOWN;
13753 return;
13754 }
13755 }
13756 entry->cmdtype = CMDNORMAL;
13757 return;
13758 }
13759
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000013760/* #if ENABLE_FEATURE_SH_STANDALONE... moved after builtin check */
Denis Vlasenkocc571512007-02-23 21:10:35 +000013761
13762 updatetbl = (path == pathval());
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010013763 if (!updatetbl)
Denis Vlasenkocc571512007-02-23 21:10:35 +000013764 act |= DO_ALTPATH;
Denis Vlasenkocc571512007-02-23 21:10:35 +000013765
13766 /* If name is in the table, check answer will be ok */
13767 cmdp = cmdlookup(name, 0);
13768 if (cmdp != NULL) {
13769 int bit;
13770
13771 switch (cmdp->cmdtype) {
13772 default:
13773#if DEBUG
13774 abort();
13775#endif
13776 case CMDNORMAL:
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010013777 bit = DO_ALTPATH | DO_REGBLTIN;
Denis Vlasenkocc571512007-02-23 21:10:35 +000013778 break;
13779 case CMDFUNCTION:
13780 bit = DO_NOFUNC;
13781 break;
13782 case CMDBUILTIN:
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010013783 bit = IS_BUILTIN_REGULAR(cmdp->param.cmd) ? 0 : DO_REGBLTIN;
Denis Vlasenkocc571512007-02-23 21:10:35 +000013784 break;
13785 }
13786 if (act & bit) {
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010013787 if (act & bit & DO_REGBLTIN)
13788 goto fail;
13789
Denis Vlasenkocc571512007-02-23 21:10:35 +000013790 updatetbl = 0;
13791 cmdp = NULL;
13792 } else if (cmdp->rehash == 0)
13793 /* if not invalidated by cd, we're done */
13794 goto success;
13795 }
13796
13797 /* If %builtin not in path, check for builtin next */
13798 bcmd = find_builtin(name);
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000013799 if (bcmd) {
13800 if (IS_BUILTIN_REGULAR(bcmd))
13801 goto builtin_success;
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010013802 if (act & DO_ALTPATH)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000013803 goto builtin_success;
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010013804 if (builtinloc <= 0)
13805 goto builtin_success;
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000013806 }
Denis Vlasenkocc571512007-02-23 21:10:35 +000013807
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010013808 if (act & DO_REGBLTIN)
13809 goto fail;
13810
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000013811#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000013812 {
13813 int applet_no = find_applet_by_name(name);
13814 if (applet_no >= 0) {
13815 entry->cmdtype = CMDNORMAL;
13816 entry->u.index = -2 - applet_no;
13817 return;
13818 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000013819 }
13820#endif
13821
Denis Vlasenkocc571512007-02-23 21:10:35 +000013822 /* We have to search path. */
13823 prev = -1; /* where to start */
13824 if (cmdp && cmdp->rehash) { /* doing a rehash */
13825 if (cmdp->cmdtype == CMDBUILTIN)
13826 prev = builtinloc;
13827 else
13828 prev = cmdp->param.index;
13829 }
13830
13831 e = ENOENT;
13832 idx = -1;
13833 loop:
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +010013834 while ((len = padvance(&path, name)) >= 0) {
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +010013835 const char *lpathopt = pathopt;
13836
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +010013837 fullname = stackblock();
Denis Vlasenkocc571512007-02-23 21:10:35 +000013838 idx++;
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +010013839 if (lpathopt) {
13840 if (*lpathopt == 'b') {
Denis Vlasenkocc571512007-02-23 21:10:35 +000013841 if (bcmd)
13842 goto builtin_success;
13843 continue;
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +010013844 } else if (!(act & DO_NOFUNC)) {
13845 /* handled below */
13846 } else {
13847 /* ignore unimplemented options */
Denis Vlasenkocc571512007-02-23 21:10:35 +000013848 continue;
13849 }
13850 }
13851 /* if rehash, don't redo absolute path names */
13852 if (fullname[0] == '/' && idx <= prev) {
13853 if (idx < prev)
13854 continue;
13855 TRACE(("searchexec \"%s\": no change\n", name));
13856 goto success;
13857 }
13858 while (stat(fullname, &statb) < 0) {
13859#ifdef SYSV
13860 if (errno == EINTR)
13861 continue;
13862#endif
13863 if (errno != ENOENT && errno != ENOTDIR)
13864 e = errno;
13865 goto loop;
13866 }
13867 e = EACCES; /* if we fail, this will be the error */
13868 if (!S_ISREG(statb.st_mode))
13869 continue;
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +010013870 if (lpathopt) { /* this is a %func directory */
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +010013871 stalloc(len);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000013872 /* NB: stalloc will return space pointed by fullname
13873 * (because we don't have any intervening allocations
13874 * between stunalloc above and this stalloc) */
Denis Vlasenkocc571512007-02-23 21:10:35 +000013875 readcmdfile(fullname);
13876 cmdp = cmdlookup(name, 0);
13877 if (cmdp == NULL || cmdp->cmdtype != CMDFUNCTION)
13878 ash_msg_and_raise_error("%s not defined in %s", name, fullname);
13879 stunalloc(fullname);
13880 goto success;
13881 }
13882 TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
13883 if (!updatetbl) {
13884 entry->cmdtype = CMDNORMAL;
13885 entry->u.index = idx;
13886 return;
13887 }
13888 INT_OFF;
13889 cmdp = cmdlookup(name, 1);
13890 cmdp->cmdtype = CMDNORMAL;
13891 cmdp->param.index = idx;
13892 INT_ON;
13893 goto success;
13894 }
13895
13896 /* We failed. If there was an entry for this command, delete it */
13897 if (cmdp && updatetbl)
13898 delete_cmd_entry();
William Pitcockd8fd88a2018-01-24 18:33:18 +010013899 if (act & DO_ERR) {
13900#if ENABLE_ASH_BASH_NOT_FOUND_HOOK
13901 struct tblentry *hookp = cmdlookup("command_not_found_handle", 0);
13902 if (hookp && hookp->cmdtype == CMDFUNCTION) {
13903 char *argv[3];
13904 argv[0] = (char*) "command_not_found_handle";
13905 argv[1] = name;
13906 argv[2] = NULL;
13907 evalfun(hookp->param.func, 2, argv, 0);
13908 entry->cmdtype = CMDUNKNOWN;
13909 return;
13910 }
13911#endif
Denis Vlasenkocc571512007-02-23 21:10:35 +000013912 ash_msg("%s: %s", name, errmsg(e, "not found"));
William Pitcockd8fd88a2018-01-24 18:33:18 +010013913 }
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010013914 fail:
Denis Vlasenkocc571512007-02-23 21:10:35 +000013915 entry->cmdtype = CMDUNKNOWN;
13916 return;
13917
13918 builtin_success:
13919 if (!updatetbl) {
13920 entry->cmdtype = CMDBUILTIN;
13921 entry->u.cmd = bcmd;
13922 return;
13923 }
13924 INT_OFF;
13925 cmdp = cmdlookup(name, 1);
13926 cmdp->cmdtype = CMDBUILTIN;
13927 cmdp->param.cmd = bcmd;
13928 INT_ON;
13929 success:
13930 cmdp->rehash = 0;
13931 entry->cmdtype = cmdp->cmdtype;
13932 entry->u = cmdp->param;
13933}
13934
13935
Eric Andersencb57d552001-06-28 07:25:16 +000013936/*
Eric Andersencb57d552001-06-28 07:25:16 +000013937 * The trap builtin.
13938 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020013939static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000013940trapcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +000013941{
13942 char *action;
13943 char **ap;
Denys Vlasenko496d5bf2010-03-26 15:52:24 +010013944 int signo, exitcode;
Eric Andersencb57d552001-06-28 07:25:16 +000013945
Eric Andersenc470f442003-07-28 09:56:35 +000013946 nextopt(nullstr);
13947 ap = argptr;
13948 if (!*ap) {
Roberto A. Fogliettae0bf3df2021-09-07 01:19:31 +020013949 for (signo = 0; signo <= NTRAP_LAST; signo++) {
Denys Vlasenko21d87d42009-09-25 00:06:51 +020013950 char *tr = trap_ptr[signo];
13951 if (tr) {
Denys Vlasenkoe74aaf92009-09-27 02:05:45 +020013952 /* note: bash adds "SIG", but only if invoked
13953 * as "bash". If called as "sh", or if set -o posix,
13954 * then it prints short signal names.
13955 * We are printing short names: */
13956 out1fmt("trap -- %s %s\n",
Denys Vlasenko21d87d42009-09-25 00:06:51 +020013957 single_quote(tr),
Roberto A. Fogliettae0bf3df2021-09-07 01:19:31 +020013958 (signo == NTRAP_ERR) ? "ERR" : get_signame(signo));
Denys Vlasenko726e1a02009-09-25 02:58:20 +020013959 /* trap_ptr != trap only if we are in special-cased `trap` code.
13960 * In this case, we will exit very soon, no need to free(). */
Denys Vlasenkoe74aaf92009-09-27 02:05:45 +020013961 /* if (trap_ptr != trap && tp[0]) */
Denys Vlasenko726e1a02009-09-25 02:58:20 +020013962 /* free(tr); */
Eric Andersencb57d552001-06-28 07:25:16 +000013963 }
13964 }
Denys Vlasenko726e1a02009-09-25 02:58:20 +020013965 /*
Denys Vlasenko21d87d42009-09-25 00:06:51 +020013966 if (trap_ptr != trap) {
13967 free(trap_ptr);
13968 trap_ptr = trap;
13969 }
Denys Vlasenko726e1a02009-09-25 02:58:20 +020013970 */
Eric Andersencb57d552001-06-28 07:25:16 +000013971 return 0;
13972 }
Denys Vlasenko21d87d42009-09-25 00:06:51 +020013973
Denys Vlasenko86981e32017-07-25 20:06:17 +020013974 /* Why the second check?
13975 * "trap NUM [sig2]..." is the same as "trap - NUM [sig2]..."
13976 * In this case, NUM is signal no, not an action.
13977 */
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +000013978 action = NULL;
Denys Vlasenko86981e32017-07-25 20:06:17 +020013979 if (ap[1] && !is_number(ap[0]))
Eric Andersencb57d552001-06-28 07:25:16 +000013980 action = *ap++;
Denys Vlasenko86981e32017-07-25 20:06:17 +020013981
Denys Vlasenko496d5bf2010-03-26 15:52:24 +010013982 exitcode = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000013983 while (*ap) {
Roberto A. Fogliettae0bf3df2021-09-07 01:19:31 +020013984 signo = strcmp(*ap, "ERR") == 0 ? NTRAP_ERR : get_signum(*ap);
Denys Vlasenko86981e32017-07-25 20:06:17 +020013985 if (signo < 0) {
Denys Vlasenko496d5bf2010-03-26 15:52:24 +010013986 /* Mimic bash message exactly */
13987 ash_msg("%s: invalid signal specification", *ap);
13988 exitcode = 1;
13989 goto next;
13990 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000013991 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +000013992 if (action) {
Denis Vlasenko9f739442006-12-16 23:49:13 +000013993 if (LONE_DASH(action))
Eric Andersencb57d552001-06-28 07:25:16 +000013994 action = NULL;
Denys Vlasenkob4f51d32016-10-27 12:55:09 +020013995 else {
13996 if (action[0]) /* not NULL and not "" and not "-" */
13997 may_have_traps = 1;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013998 action = ckstrdup(action);
Denys Vlasenkob4f51d32016-10-27 12:55:09 +020013999 }
Eric Andersencb57d552001-06-28 07:25:16 +000014000 }
Denis Vlasenko60818682007-09-28 22:07:23 +000014001 free(trap[signo]);
Eric Andersencb57d552001-06-28 07:25:16 +000014002 trap[signo] = action;
Roberto A. Fogliettae0bf3df2021-09-07 01:19:31 +020014003 if (signo != 0 && signo < NSIG)
Eric Andersencb57d552001-06-28 07:25:16 +000014004 setsignal(signo);
Denis Vlasenkob012b102007-02-19 22:43:01 +000014005 INT_ON;
Denys Vlasenko496d5bf2010-03-26 15:52:24 +010014006 next:
Eric Andersencb57d552001-06-28 07:25:16 +000014007 ap++;
14008 }
Denys Vlasenko496d5bf2010-03-26 15:52:24 +010014009 return exitcode;
Eric Andersencb57d552001-06-28 07:25:16 +000014010}
14011
Eric Andersenc470f442003-07-28 09:56:35 +000014012
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000014013/* ============ Builtins */
Eric Andersenc470f442003-07-28 09:56:35 +000014014
Denys Vlasenko2ec34962014-09-08 16:52:39 +020014015#if ENABLE_ASH_HELP
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020014016static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000014017helpcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000014018{
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000014019 unsigned col;
14020 unsigned i;
Eric Andersenc470f442003-07-28 09:56:35 +000014021
Denys Vlasenkod6b05eb2009-06-06 20:59:55 +020014022 out1fmt(
Denis Vlasenko34d4d892009-04-04 20:24:37 +000014023 "Built-in commands:\n"
14024 "------------------\n");
Denis Vlasenkob71c6682007-07-21 15:08:09 +000014025 for (col = 0, i = 0; i < ARRAY_SIZE(builtintab); i++) {
Eric Andersenc470f442003-07-28 09:56:35 +000014026 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '),
Denis Vlasenko52764022007-02-24 13:42:56 +000014027 builtintab[i].name + 1);
Eric Andersenc470f442003-07-28 09:56:35 +000014028 if (col > 60) {
14029 out1fmt("\n");
14030 col = 0;
14031 }
14032 }
Denys Vlasenko2ec34962014-09-08 16:52:39 +020014033# if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko1aa7e472007-11-28 06:49:03 +000014034 {
14035 const char *a = applet_names;
14036 while (*a) {
14037 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '), a);
14038 if (col > 60) {
14039 out1fmt("\n");
14040 col = 0;
14041 }
Ron Yorston2b919582016-04-08 11:57:20 +010014042 while (*a++ != '\0')
14043 continue;
Eric Andersenc470f442003-07-28 09:56:35 +000014044 }
14045 }
Denys Vlasenko2ec34962014-09-08 16:52:39 +020014046# endif
Denys Vlasenkoebedb942016-10-02 18:45:09 +020014047 newline_and_flush(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +000014048 return EXIT_SUCCESS;
14049}
Denys Vlasenko2ec34962014-09-08 16:52:39 +020014050#endif
Eric Andersenc470f442003-07-28 09:56:35 +000014051
Flemming Madsend96ffda2013-04-07 18:47:24 +020014052#if MAX_HISTORY
14053static int FAST_FUNC
14054historycmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
14055{
Ron Yorston9f3b4102019-12-16 09:31:10 +000014056 show_history(line_input_state);
Flemming Madsend96ffda2013-04-07 18:47:24 +020014057 return EXIT_SUCCESS;
14058}
14059#endif
14060
Eric Andersencb57d552001-06-28 07:25:16 +000014061/*
Eric Andersencb57d552001-06-28 07:25:16 +000014062 * The export and readonly commands.
14063 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020014064static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000014065exportcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000014066{
14067 struct var *vp;
14068 char *name;
14069 const char *p;
Eric Andersenc470f442003-07-28 09:56:35 +000014070 char **aptr;
Denys Vlasenkod5275882012-10-01 13:41:17 +020014071 char opt;
14072 int flag;
14073 int flag_off;
Eric Andersencb57d552001-06-28 07:25:16 +000014074
Denys Vlasenkod5275882012-10-01 13:41:17 +020014075 /* "readonly" in bash accepts, but ignores -n.
14076 * We do the same: it saves a conditional in nextopt's param.
14077 */
14078 flag_off = 0;
14079 while ((opt = nextopt("np")) != '\0') {
14080 if (opt == 'n')
14081 flag_off = VEXPORT;
14082 }
14083 flag = VEXPORT;
14084 if (argv[0][0] == 'r') {
14085 flag = VREADONLY;
14086 flag_off = 0; /* readonly ignores -n */
14087 }
14088 flag_off = ~flag_off;
14089
Denys Vlasenko10ad6222017-04-17 16:13:32 +020014090 /*if (opt_p_not_specified) - bash doesn't check this. Try "export -p NAME" */
Denys Vlasenkod5275882012-10-01 13:41:17 +020014091 {
Denis Vlasenko2da584f2007-02-19 22:44:05 +000014092 aptr = argptr;
14093 name = *aptr;
14094 if (name) {
14095 do {
14096 p = strchr(name, '=');
14097 if (p != NULL) {
14098 p++;
14099 } else {
14100 vp = *findvar(hashvar(name), name);
14101 if (vp) {
Denys Vlasenkod5275882012-10-01 13:41:17 +020014102 vp->flags = ((vp->flags | flag) & flag_off);
Denis Vlasenko2da584f2007-02-19 22:44:05 +000014103 continue;
14104 }
Eric Andersencb57d552001-06-28 07:25:16 +000014105 }
Denys Vlasenkod5275882012-10-01 13:41:17 +020014106 setvar(name, p, (flag & flag_off));
Denis Vlasenko2da584f2007-02-19 22:44:05 +000014107 } while ((name = *++aptr) != NULL);
14108 return 0;
14109 }
Eric Andersencb57d552001-06-28 07:25:16 +000014110 }
Denys Vlasenkod5275882012-10-01 13:41:17 +020014111
14112 /* No arguments. Show the list of exported or readonly vars.
14113 * -n is ignored.
14114 */
Denis Vlasenko2da584f2007-02-19 22:44:05 +000014115 showvars(argv[0], flag, 0);
Eric Andersencb57d552001-06-28 07:25:16 +000014116 return 0;
14117}
14118
Eric Andersencb57d552001-06-28 07:25:16 +000014119/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000014120 * Delete a function if it exists.
Eric Andersencb57d552001-06-28 07:25:16 +000014121 */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000014122static void
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000014123unsetfunc(const char *name)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000014124{
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000014125 struct tblentry *cmdp;
Eric Andersencb57d552001-06-28 07:25:16 +000014126
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000014127 cmdp = cmdlookup(name, 0);
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +020014128 if (cmdp != NULL && cmdp->cmdtype == CMDFUNCTION)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000014129 delete_cmd_entry();
Eric Andersenc470f442003-07-28 09:56:35 +000014130}
14131
Eric Andersencb57d552001-06-28 07:25:16 +000014132/*
Eric Andersencb57d552001-06-28 07:25:16 +000014133 * The unset builtin command. We unset the function before we unset the
14134 * variable to allow a function to be unset when there is a readonly variable
14135 * with the same name.
14136 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020014137static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000014138unsetcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +000014139{
14140 char **ap;
14141 int i;
Eric Andersenc470f442003-07-28 09:56:35 +000014142 int flag = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000014143
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +020014144 while ((i = nextopt("vf")) != 0) {
Eric Andersenc470f442003-07-28 09:56:35 +000014145 flag = i;
Eric Andersencb57d552001-06-28 07:25:16 +000014146 }
Eric Andersencb57d552001-06-28 07:25:16 +000014147
Denis Vlasenko2da584f2007-02-19 22:44:05 +000014148 for (ap = argptr; *ap; ap++) {
Eric Andersenc470f442003-07-28 09:56:35 +000014149 if (flag != 'f') {
Denys Vlasenkob28d4c32017-07-25 16:29:36 +020014150 unsetvar(*ap);
14151 continue;
Eric Andersenc470f442003-07-28 09:56:35 +000014152 }
14153 if (flag != 'v')
Eric Andersencb57d552001-06-28 07:25:16 +000014154 unsetfunc(*ap);
Eric Andersencb57d552001-06-28 07:25:16 +000014155 }
Denys Vlasenkob28d4c32017-07-25 16:29:36 +020014156 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +000014157}
14158
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000014159static const unsigned char timescmd_str[] ALIGN1 = {
Manuel Novoa III 4456f252003-08-13 17:48:47 +000014160 ' ', offsetof(struct tms, tms_utime),
14161 '\n', offsetof(struct tms, tms_stime),
14162 ' ', offsetof(struct tms, tms_cutime),
14163 '\n', offsetof(struct tms, tms_cstime),
14164 0
14165};
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020014166static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000014167timescmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Manuel Novoa III 4456f252003-08-13 17:48:47 +000014168{
Denys Vlasenko11f2e992017-08-10 16:34:03 +020014169 unsigned clk_tck;
Manuel Novoa III 4456f252003-08-13 17:48:47 +000014170 const unsigned char *p;
14171 struct tms buf;
14172
Bartosz Golaszewski5d2e4092014-06-22 14:01:13 +020014173 clk_tck = bb_clk_tck();
Manuel Novoa III 4456f252003-08-13 17:48:47 +000014174
Denys Vlasenko11f2e992017-08-10 16:34:03 +020014175 times(&buf);
Manuel Novoa III 4456f252003-08-13 17:48:47 +000014176 p = timescmd_str;
14177 do {
Denys Vlasenko11f2e992017-08-10 16:34:03 +020014178 unsigned sec, frac;
14179 unsigned long t;
Manuel Novoa III 4456f252003-08-13 17:48:47 +000014180 t = *(clock_t *)(((char *) &buf) + p[1]);
Denys Vlasenko11f2e992017-08-10 16:34:03 +020014181 sec = t / clk_tck;
14182 frac = t % clk_tck;
14183 out1fmt("%um%u.%03us%c",
14184 sec / 60, sec % 60,
14185 (frac * 1000) / clk_tck,
Manuel Novoa III 4456f252003-08-13 17:48:47 +000014186 p[0]);
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +020014187 p += 2;
14188 } while (*p);
Manuel Novoa III 4456f252003-08-13 17:48:47 +000014189
Eric Andersencb57d552001-06-28 07:25:16 +000014190 return 0;
14191}
14192
Denys Vlasenko0b883582016-12-23 16:49:07 +010014193#if ENABLE_FEATURE_SH_MATH
Eric Andersenc470f442003-07-28 09:56:35 +000014194/*
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +020014195 * The let builtin. Partially stolen from GNU Bash, the Bourne Again SHell.
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +000014196 * Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc.
Eric Andersen90898442003-08-06 11:20:52 +000014197 *
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +000014198 * Copyright (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
Eric Andersenc470f442003-07-28 09:56:35 +000014199 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020014200static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000014201letcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000014202{
Denis Vlasenko68404f12008-03-17 09:00:54 +000014203 arith_t i;
Eric Andersenc470f442003-07-28 09:56:35 +000014204
Denis Vlasenko68404f12008-03-17 09:00:54 +000014205 argv++;
14206 if (!*argv)
Denis Vlasenkob012b102007-02-19 22:43:01 +000014207 ash_msg_and_raise_error("expression expected");
Denis Vlasenko68404f12008-03-17 09:00:54 +000014208 do {
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +000014209 i = ash_arith(*argv);
Denis Vlasenko68404f12008-03-17 09:00:54 +000014210 } while (*++argv);
Eric Andersenc470f442003-07-28 09:56:35 +000014211
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000014212 return !i;
Eric Andersenc470f442003-07-28 09:56:35 +000014213}
Eric Andersenc470f442003-07-28 09:56:35 +000014214#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000014215
Eric Andersenc470f442003-07-28 09:56:35 +000014216/*
Denis Vlasenko59f351c2008-03-25 00:07:12 +000014217 * The read builtin. Options:
14218 * -r Do not interpret '\' specially
14219 * -s Turn off echo (tty only)
14220 * -n NCHARS Read NCHARS max
14221 * -p PROMPT Display PROMPT on stderr (if input is from tty)
14222 * -t SECONDS Timeout after SECONDS (tty or pipe only)
14223 * -u FD Read from given FD instead of fd 0
Johannes Schindelin3bef5d82017-08-08 16:46:39 +020014224 * -d DELIM End on DELIM char, not newline
Eric Andersenc470f442003-07-28 09:56:35 +000014225 * This uses unbuffered input, which may be avoidable in some cases.
Denis Vlasenko59f351c2008-03-25 00:07:12 +000014226 * TODO: bash also has:
14227 * -a ARRAY Read into array[0],[1],etc
Denis Vlasenko59f351c2008-03-25 00:07:12 +000014228 * -e Use line editing (tty only)
Eric Andersenc470f442003-07-28 09:56:35 +000014229 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020014230static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000014231readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000014232{
Denys Vlasenko19358cc2018-08-05 15:42:29 +020014233 struct builtin_read_params params;
Denys Vlasenko73067272010-01-12 22:11:24 +010014234 const char *r;
Eric Andersenc470f442003-07-28 09:56:35 +000014235 int i;
14236
Denys Vlasenko19358cc2018-08-05 15:42:29 +020014237 memset(&params, 0, sizeof(params));
14238
Johannes Schindelin3bef5d82017-08-08 16:46:39 +020014239 while ((i = nextopt("p:u:rt:n:sd:")) != '\0') {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000014240 switch (i) {
Paul Fox02eb9342005-09-07 16:56:02 +000014241 case 'p':
Denys Vlasenko19358cc2018-08-05 15:42:29 +020014242 params.opt_p = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000014243 break;
Paul Fox02eb9342005-09-07 16:56:02 +000014244 case 'n':
Denys Vlasenko19358cc2018-08-05 15:42:29 +020014245 params.opt_n = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000014246 break;
14247 case 's':
Denys Vlasenko19358cc2018-08-05 15:42:29 +020014248 params.read_flags |= BUILTIN_READ_SILENT;
Paul Fox02eb9342005-09-07 16:56:02 +000014249 break;
Paul Fox02eb9342005-09-07 16:56:02 +000014250 case 't':
Denys Vlasenko19358cc2018-08-05 15:42:29 +020014251 params.opt_t = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000014252 break;
Paul Fox02eb9342005-09-07 16:56:02 +000014253 case 'r':
Denys Vlasenko19358cc2018-08-05 15:42:29 +020014254 params.read_flags |= BUILTIN_READ_RAW;
Paul Fox02eb9342005-09-07 16:56:02 +000014255 break;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000014256 case 'u':
Denys Vlasenko19358cc2018-08-05 15:42:29 +020014257 params.opt_u = optionarg;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000014258 break;
Johannes Schindelin3bef5d82017-08-08 16:46:39 +020014259#if BASH_READ_D
14260 case 'd':
Denys Vlasenko19358cc2018-08-05 15:42:29 +020014261 params.opt_d = optionarg;
Johannes Schindelin3bef5d82017-08-08 16:46:39 +020014262 break;
14263#endif
Paul Fox02eb9342005-09-07 16:56:02 +000014264 default:
14265 break;
14266 }
Eric Andersenc470f442003-07-28 09:56:35 +000014267 }
Paul Fox02eb9342005-09-07 16:56:02 +000014268
Denys Vlasenko457825f2021-06-06 12:07:11 +020014269 if (!ENABLE_ASH_BASH_COMPAT && !argptr) {
14270 bb_simple_error_msg("read: need variable name");
14271 return 1;
14272 }
Denys Vlasenko19358cc2018-08-05 15:42:29 +020014273 params.argv = argptr;
14274 params.setvar = setvar0;
14275 params.ifs = bltinlookup("IFS"); /* can be NULL */
14276
Denys Vlasenko9e71e3c2012-09-06 13:28:10 +020014277 /* "read -s" needs to save/restore termios, can't allow ^C
14278 * to jump out of it.
14279 */
Denys Vlasenkof5470412017-05-22 19:34:45 +020014280 again:
Denys Vlasenko9e71e3c2012-09-06 13:28:10 +020014281 INT_OFF;
Denys Vlasenko19358cc2018-08-05 15:42:29 +020014282 r = shell_builtin_read(&params);
Denys Vlasenko9e71e3c2012-09-06 13:28:10 +020014283 INT_ON;
Denis Vlasenko46aeab92009-03-31 19:18:17 +000014284
Denys Vlasenkof5470412017-05-22 19:34:45 +020014285 if ((uintptr_t)r == 1 && errno == EINTR) {
Denys Vlasenko49e6bf22017-08-04 14:28:16 +020014286 /* To get SIGCHLD: sleep 1 & read x; echo $x
14287 * Correct behavior is to not exit "read"
14288 */
Denys Vlasenkof5470412017-05-22 19:34:45 +020014289 if (pending_sig == 0)
14290 goto again;
14291 }
14292
Denys Vlasenko73067272010-01-12 22:11:24 +010014293 if ((uintptr_t)r > 1)
14294 ash_msg_and_raise_error(r);
Denis Vlasenko037576d2007-10-20 18:30:38 +000014295
Denys Vlasenko73067272010-01-12 22:11:24 +010014296 return (uintptr_t)r;
Eric Andersenc470f442003-07-28 09:56:35 +000014297}
14298
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020014299static int FAST_FUNC
Denys Vlasenko6283f982015-10-07 16:56:20 +020014300umaskcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000014301{
Denys Vlasenkoc1e2e002015-10-07 17:32:56 +020014302 static const char permuser[3] ALIGN1 = "ogu";
Eric Andersenc470f442003-07-28 09:56:35 +000014303
Eric Andersenc470f442003-07-28 09:56:35 +000014304 mode_t mask;
Eric Andersenc470f442003-07-28 09:56:35 +000014305 int symbolic_mode = 0;
14306
14307 while (nextopt("S") != '\0') {
14308 symbolic_mode = 1;
14309 }
14310
Denis Vlasenkob012b102007-02-19 22:43:01 +000014311 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000014312 mask = umask(0);
14313 umask(mask);
Denis Vlasenkob012b102007-02-19 22:43:01 +000014314 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000014315
Denys Vlasenko6283f982015-10-07 16:56:20 +020014316 if (*argptr == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000014317 if (symbolic_mode) {
Denys Vlasenko005c4922015-10-10 20:17:12 +020014318 char buf[sizeof(",u=rwx,g=rwx,o=rwx")];
Eric Andersenc470f442003-07-28 09:56:35 +000014319 char *p = buf;
Denys Vlasenkoc1e2e002015-10-07 17:32:56 +020014320 int i;
Eric Andersenc470f442003-07-28 09:56:35 +000014321
Denys Vlasenkoc1e2e002015-10-07 17:32:56 +020014322 i = 2;
14323 for (;;) {
Denys Vlasenko005c4922015-10-10 20:17:12 +020014324 *p++ = ',';
Eric Andersenc470f442003-07-28 09:56:35 +000014325 *p++ = permuser[i];
14326 *p++ = '=';
Denys Vlasenkoc1e2e002015-10-07 17:32:56 +020014327 /* mask is 0..0uuugggooo. i=2 selects uuu bits */
Denys Vlasenko005c4922015-10-10 20:17:12 +020014328 if (!(mask & 0400)) *p++ = 'r';
14329 if (!(mask & 0200)) *p++ = 'w';
14330 if (!(mask & 0100)) *p++ = 'x';
14331 mask <<= 3;
Denys Vlasenkoc1e2e002015-10-07 17:32:56 +020014332 if (--i < 0)
14333 break;
Eric Andersenc470f442003-07-28 09:56:35 +000014334 }
Denys Vlasenkoc1e2e002015-10-07 17:32:56 +020014335 *p = '\0';
Denys Vlasenko005c4922015-10-10 20:17:12 +020014336 puts(buf + 1);
Eric Andersenc470f442003-07-28 09:56:35 +000014337 } else {
Denys Vlasenkoec046f72015-10-07 17:57:53 +020014338 out1fmt("%04o\n", mask);
Eric Andersenc470f442003-07-28 09:56:35 +000014339 }
14340 } else {
Denys Vlasenko6283f982015-10-07 16:56:20 +020014341 char *modestr = *argptr;
Denys Vlasenko14c85eb2017-10-12 19:40:47 +020014342 /* numeric umasks are taken as-is */
14343 /* symbolic umasks are inverted: "umask a=rx" calls umask(222) */
Denys Vlasenko6283f982015-10-07 16:56:20 +020014344 if (!isdigit(modestr[0]))
14345 mask ^= 0777;
Denys Vlasenko5711a2a2015-10-07 17:55:33 +020014346 mask = bb_parse_mode(modestr, mask);
14347 if ((unsigned)mask > 0777) {
Denys Vlasenko6283f982015-10-07 16:56:20 +020014348 ash_msg_and_raise_error("illegal mode: %s", modestr);
Eric Andersenc470f442003-07-28 09:56:35 +000014349 }
Denys Vlasenko6283f982015-10-07 16:56:20 +020014350 if (!isdigit(modestr[0]))
14351 mask ^= 0777;
14352 umask(mask);
Eric Andersenc470f442003-07-28 09:56:35 +000014353 }
14354 return 0;
14355}
14356
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020014357static int FAST_FUNC
Denys Vlasenkof3c742f2010-03-06 20:12:00 +010014358ulimitcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000014359{
Denys Vlasenkof3c742f2010-03-06 20:12:00 +010014360 return shell_builtin_ulimit(argv);
Eric Andersenc470f442003-07-28 09:56:35 +000014361}
14362
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000014363/* ============ main() and helpers */
14364
14365/*
Denys Vlasenkof977e002020-02-20 16:54:29 +010014366 * This routine is called when an error or an interrupt occurs in an
14367 * interactive shell and control is returned to the main command loop
14368 * but prior to exitshell.
14369 */
14370static void
14371exitreset(void)
14372{
14373 /* from eval.c: */
14374 if (savestatus >= 0) {
14375 if (exception_type == EXEXIT || evalskip == SKIPFUNCDEF)
14376 exitstatus = savestatus;
14377 savestatus = -1;
14378 }
14379 evalskip = 0;
14380 loopnest = 0;
Denys Vlasenkoeb607772021-09-09 16:26:41 +020014381 inps4 = 0;
Denys Vlasenkof977e002020-02-20 16:54:29 +010014382
14383 /* from expand.c: */
14384 ifsfree();
14385
14386 /* from redir.c: */
14387 unwindredir(NULL);
14388}
14389
14390/*
14391 * This routine is called when an error or an interrupt occurs in an
14392 * interactive shell and control is returned to the main command loop.
14393 * (In dash, this function is auto-generated by build machinery).
14394 */
14395static void
14396reset(void)
14397{
14398 /* from input.c: */
14399 g_parsefile->left_in_buffer = 0;
14400 g_parsefile->left_in_line = 0; /* clear input buffer */
Denys Vlasenko51a471d2020-12-24 00:22:24 +010014401 g_parsefile->unget = 0;
Denys Vlasenkof977e002020-02-20 16:54:29 +010014402 popallfiles();
14403
14404 /* from var.c: */
14405 unwindlocalvars(NULL);
14406}
14407
14408/*
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000014409 * Called to exit the shell.
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014410 */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000014411static void
14412exitshell(void)
14413{
14414 struct jmploc loc;
14415 char *p;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000014416
Denys Vlasenkobede2152011-09-04 16:12:33 +020014417#if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT
Denys Vlasenko00eb23b2020-12-21 21:36:58 +010014418 save_history(line_input_state); /* may be NULL */
Denys Vlasenkobede2152011-09-04 16:12:33 +020014419#endif
Denys Vlasenko4ccddc82020-02-14 17:27:18 +010014420 savestatus = exitstatus;
14421 TRACE(("pid %d, exitshell(%d)\n", getpid(), savestatus));
14422 if (setjmp(loc.loc))
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000014423 goto out;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000014424 exception_handler = &loc;
14425 p = trap[0];
14426 if (p) {
14427 trap[0] = NULL;
Denys Vlasenko7b3fa1e2016-10-01 15:10:16 +020014428 evalskip = 0;
Roberto A. Fogliettae0bf3df2021-09-07 01:19:31 +020014429 trap_depth++;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000014430 evalstring(p, 0);
Roberto A. Fogliettae0bf3df2021-09-07 01:19:31 +020014431 trap_depth--;
Denys Vlasenkof977e002020-02-20 16:54:29 +010014432 evalskip = SKIPFUNCDEF;
Denys Vlasenkof37e1152016-10-07 03:17:28 +020014433 /*free(p); - we'll exit soon */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000014434 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000014435 out:
Denys Vlasenkof977e002020-02-20 16:54:29 +010014436 exitreset();
Denys Vlasenkof37e1152016-10-07 03:17:28 +020014437 /* dash wraps setjobctl(0) in "if (setjmp(loc.loc) == 0) {...}".
14438 * our setjobctl(0) does not panic if tcsetpgrp fails inside it.
14439 */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000014440 setjobctl(0);
Denys Vlasenkocaee80c2016-10-25 20:49:53 +020014441 flush_stdout_stderr();
Denys Vlasenkof977e002020-02-20 16:54:29 +010014442 _exit(exitstatus);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000014443 /* NOTREACHED */
14444}
14445
Denys Vlasenko49e6bf22017-08-04 14:28:16 +020014446/* Don't inline: conserve stack of caller from having our locals too */
14447static NOINLINE void
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000014448init(void)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014449{
Denys Vlasenko82dd14a2010-05-17 10:10:01 +020014450 /* we will never free this */
14451 basepf.next_to_pgetc = basepf.buf = ckmalloc(IBUFSIZ);
Denys Vlasenko0485b672017-08-14 19:46:56 +020014452 basepf.linno = 1;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014453
Denys Vlasenko49e6bf22017-08-04 14:28:16 +020014454 sigmode[SIGCHLD - 1] = S_DFL; /* ensure we install handler even if it is SIG_IGNed */
Denys Vlasenko458c1f22016-10-27 23:51:19 +020014455 setsignal(SIGCHLD);
14456
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014457 {
14458 char **envp;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014459 const char *p;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014460
14461 initvar();
14462 for (envp = environ; envp && *envp; envp++) {
Denys Vlasenko9c143ce2017-11-02 12:56:24 +010014463/* Used to have
14464 * p = endofname(*envp);
14465 * if (p != *envp && *p == '=') {
14466 * here to weed out badly-named variables, but this breaks
14467 * scenarios where people do want them passed to children:
14468 * import os
14469 * os.environ["test-test"]="test"
14470 * if os.fork() == 0:
14471 * os.execv("ash", [ 'ash', '-c', 'eval $(export -p); echo OK' ]) # fixes this
14472 * os.execv("ash", [ 'ash', '-c', 'env | grep test-test' ]) # breaks this
14473 */
14474 if (strchr(*envp, '=')) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014475 setvareq(*envp, VEXPORT|VTEXTFIXED);
14476 }
14477 }
14478
Denys Vlasenko67dae152018-08-05 13:59:35 +020014479 setvareq((char*)defifsvar, VTEXTFIXED);
Denys Vlasenkoe627ac92016-09-30 14:36:59 +020014480 setvareq((char*)defoptindvar, VTEXTFIXED);
14481
Denys Vlasenko0a0acb52015-04-18 19:36:38 +020014482 setvar0("PPID", utoa(getppid()));
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010014483#if BASH_SHLVL_VAR
Bernhard Reutner-Fischer80f8cdf2013-11-08 14:25:24 +010014484 p = lookupvar("SHLVL");
Denys Vlasenko5680e982014-01-07 16:12:48 +010014485 setvar("SHLVL", utoa((p ? atoi(p) : 0) + 1), VEXPORT);
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010014486#endif
14487#if BASH_HOSTNAME_VAR
Denys Vlasenko3fa97af2014-04-15 11:43:29 +020014488 if (!lookupvar("HOSTNAME")) {
14489 struct utsname uts;
14490 uname(&uts);
Denys Vlasenko0a0acb52015-04-18 19:36:38 +020014491 setvar0("HOSTNAME", uts.nodename);
Denys Vlasenko3fa97af2014-04-15 11:43:29 +020014492 }
Bernhard Reutner-Fischer80f8cdf2013-11-08 14:25:24 +010014493#endif
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014494 p = lookupvar("PWD");
Denys Vlasenkob0b83432011-03-07 12:34:59 +010014495 if (p) {
Denys Vlasenko49e6bf22017-08-04 14:28:16 +020014496 struct stat st1, st2;
Denys Vlasenkoef159702016-09-01 11:16:22 +020014497 if (p[0] != '/' || stat(p, &st1) || stat(".", &st2)
Denys Vlasenkob0b83432011-03-07 12:34:59 +010014498 || st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino
14499 ) {
Denys Vlasenkoef159702016-09-01 11:16:22 +020014500 p = NULL;
Denys Vlasenkob0b83432011-03-07 12:34:59 +010014501 }
14502 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014503 setpwd(p, 0);
14504 }
14505}
14506
Denys Vlasenkob0b83432011-03-07 12:34:59 +010014507
14508//usage:#define ash_trivial_usage
Denys Vlasenkoaaf3d5b2021-10-13 11:15:52 +020014509//usage: "[-il] [-|+Cabefmnuvx] [-|+o OPT]... [-c 'SCRIPT' [ARG0 ARGS] | FILE ARGS | -s ARGS]"
Denys Vlasenko4e039ba2021-01-04 03:50:38 +010014510//////// comes from ^^^^^^^^^^optletters
Denys Vlasenkob0b83432011-03-07 12:34:59 +010014511//usage:#define ash_full_usage "\n\n"
14512//usage: "Unix shell interpreter"
14513
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014514/*
14515 * Process the shell command line arguments.
14516 */
Denys Vlasenkoec05df12017-07-31 19:43:47 +020014517static int
Denis Vlasenko68404f12008-03-17 09:00:54 +000014518procargs(char **argv)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014519{
14520 int i;
14521 const char *xminusc;
14522 char **xargv;
Denys Vlasenkoec05df12017-07-31 19:43:47 +020014523 int login_sh;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014524
14525 xargv = argv;
Ron Yorston8767c122018-11-05 13:13:08 +000014526 login_sh = xargv[0] && xargv[0][0] == '-';
Denys Vlasenko4f2ef4a2018-11-01 09:53:25 +010014527#if NUM_SCRIPTS > 0
14528 if (minusc)
14529 goto setarg0;
14530#endif
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014531 arg0 = xargv[0];
Denis Vlasenko68404f12008-03-17 09:00:54 +000014532 /* if (xargv[0]) - mmm, this is always true! */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014533 xargv++;
Denys Vlasenko4f2ef4a2018-11-01 09:53:25 +010014534 argptr = xargv;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014535 for (i = 0; i < NOPTS; i++)
14536 optlist[i] = 2;
Denys Vlasenko897475a2019-06-01 16:35:09 +020014537 if (options(&login_sh)) {
Denis Vlasenko28bf6712008-02-14 15:01:47 +000014538 /* it already printed err message */
14539 raise_exception(EXERROR);
14540 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014541 xargv = argptr;
14542 xminusc = minusc;
14543 if (*xargv == NULL) {
14544 if (xminusc)
14545 ash_msg_and_raise_error(bb_msg_requires_arg, "-c");
14546 sflag = 1;
14547 }
Denys Vlasenkof3634582019-06-03 12:21:04 +020014548 if (iflag == 2 /* no explicit -i given */
14549 && sflag == 1 /* -s given (or implied) */
14550 && !minusc /* bash compat: ash -sc 'echo $-' is not interactive (dash is) */
14551 && isatty(0) && isatty(1) /* we are on tty */
14552 ) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014553 iflag = 1;
Denys Vlasenkof3634582019-06-03 12:21:04 +020014554 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014555 if (mflag == 2)
14556 mflag = iflag;
Denys Vlasenko85158b62021-01-03 12:14:58 +010014557 /* Unset options which weren't explicitly set or unset */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014558 for (i = 0; i < NOPTS; i++)
Denys Vlasenko85158b62021-01-03 12:14:58 +010014559 optlist[i] &= 1; /* same effect as "if (optlist[i] == 2) optlist[i] = 0;" */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014560#if DEBUG == 2
14561 debug = 1;
14562#endif
Denys Vlasenko5f7c82b2017-02-03 13:00:06 +010014563 /* POSIX 1003.2: first arg after "-c CMD" is $0, remainder $1... */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014564 if (xminusc) {
14565 minusc = *xargv++;
14566 if (*xargv)
14567 goto setarg0;
14568 } else if (!sflag) {
14569 setinputfile(*xargv, 0);
14570 setarg0:
14571 arg0 = *xargv++;
14572 commandname = arg0;
14573 }
14574
14575 shellparam.p = xargv;
14576#if ENABLE_ASH_GETOPTS
14577 shellparam.optind = 1;
14578 shellparam.optoff = -1;
14579#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000014580 /* assert(shellparam.malloced == 0 && shellparam.nparam == 0); */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014581 while (*xargv) {
14582 shellparam.nparam++;
14583 xargv++;
14584 }
Denys Vlasenko31df5a32020-12-13 16:36:28 +010014585
14586 /* Interactive bash re-enables SIGHUP which is SIG_IGNed on entry.
14587 * Try:
14588 * trap '' hup; bash; echo RET # type "kill -hup $$", see SIGHUP having effect
14589 * trap '' hup; bash -c 'kill -hup $$; echo ALIVE' # here SIGHUP is SIG_IGNed
14590 * NB: must do it before setting up signals (in optschanged())
14591 * and reading .profile etc (after we return from here):
14592 */
14593 if (iflag)
14594 signal(SIGHUP, SIG_DFL);
14595
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014596 optschanged();
Denys Vlasenkoec05df12017-07-31 19:43:47 +020014597
14598 return login_sh;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014599}
14600
14601/*
Denys Vlasenko2eb0a7e2016-10-27 11:28:59 +020014602 * Read /etc/profile, ~/.profile, $ENV.
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014603 */
14604static void
14605read_profile(const char *name)
14606{
Denys Vlasenko46999802017-07-29 21:12:29 +020014607 name = expandstr(name, DQSYNTAX);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014608 if (setinputfile(name, INPUT_PUSH_FILE | INPUT_NOFILE_OK) < 0)
14609 return;
Denys Vlasenko0840c912016-10-01 15:27:44 +020014610 cmdloop(0);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014611 popfile();
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014612}
14613
14614#if PROFILE
14615static short profile_buf[16384];
14616extern int etext();
14617#endif
14618
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000014619/*
14620 * Main routine. We initialize things, parse the arguments, execute
14621 * profiles if we're a login shell, and then call cmdloop to execute
14622 * commands. The setjmp call sets up the location to jump to when an
14623 * exception occurs. When an exception occurs the variable "state"
14624 * is used to figure out how far we had gotten.
14625 */
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +000014626int ash_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Ron Yorston8767c122018-11-05 13:13:08 +000014627#if NUM_SCRIPTS > 0
14628int ash_main(int argc, char **argv)
14629#else
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000014630int ash_main(int argc UNUSED_PARAM, char **argv)
Ron Yorston8767c122018-11-05 13:13:08 +000014631#endif
Denys Vlasenko4f2ef4a2018-11-01 09:53:25 +010014632/* note: 'argc' is used only if embedded scripts are enabled */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014633{
Denis Vlasenko4e12b1a2008-12-23 23:36:47 +000014634 volatile smallint state;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014635 struct jmploc jmploc;
14636 struct stackmark smark;
Denys Vlasenkoec05df12017-07-31 19:43:47 +020014637 int login_sh;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014638
Denis Vlasenko01631112007-12-16 17:20:38 +000014639 /* Initialize global data */
14640 INIT_G_misc();
14641 INIT_G_memstack();
14642 INIT_G_var();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000014643#if ENABLE_ASH_ALIAS
Denis Vlasenko01631112007-12-16 17:20:38 +000014644 INIT_G_alias();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000014645#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000014646 INIT_G_cmdtable();
14647
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014648#if PROFILE
14649 monitor(4, etext, profile_buf, sizeof(profile_buf), 50);
14650#endif
14651
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014652 state = 0;
14653 if (setjmp(jmploc.loc)) {
Denis Vlasenko7f88e342009-03-19 03:36:18 +000014654 smallint e;
Denis Vlasenko4e12b1a2008-12-23 23:36:47 +000014655 smallint s;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014656
Denys Vlasenkoafc91fa2020-02-17 11:22:59 +010014657 exitreset();
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014658
Denis Vlasenko7f88e342009-03-19 03:36:18 +000014659 e = exception_type;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014660 s = state;
Denys Vlasenkof977e002020-02-20 16:54:29 +010014661 if (e == EXEND || e == EXEXIT || s == 0 || iflag == 0 || shlvl) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014662 exitshell();
Denys Vlasenkob563f622010-09-25 17:15:13 +020014663 }
Denys Vlasenkoafc91fa2020-02-17 11:22:59 +010014664
14665 reset();
14666
Denys Vlasenkob563f622010-09-25 17:15:13 +020014667 if (e == EXINT) {
Denys Vlasenko9c541002015-10-07 15:44:36 +020014668 newline_and_flush(stderr);
Denys Vlasenkob563f622010-09-25 17:15:13 +020014669 }
Denis Vlasenko7f88e342009-03-19 03:36:18 +000014670
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014671 popstackmark(&smark);
14672 FORCE_INT_ON; /* enable interrupts */
14673 if (s == 1)
14674 goto state1;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000014675 if (s == 2)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014676 goto state2;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000014677 if (s == 3)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014678 goto state3;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000014679 goto state4;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014680 }
14681 exception_handler = &jmploc;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014682 rootpid = getpid();
14683
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014684 init();
14685 setstackmark(&smark);
Denys Vlasenko4f2ef4a2018-11-01 09:53:25 +010014686
14687#if NUM_SCRIPTS > 0
14688 if (argc < 0)
14689 /* Non-NULL minusc tells procargs that an embedded script is being run */
14690 minusc = get_script_content(-argc - 1);
14691#endif
Denys Vlasenkoec05df12017-07-31 19:43:47 +020014692 login_sh = procargs(argv);
Denys Vlasenko474ed062016-10-30 18:30:29 +010014693#if DEBUG
14694 TRACE(("Shell args: "));
14695 trace_puts_args(argv);
14696#endif
Denis Vlasenko68404f12008-03-17 09:00:54 +000014697
Denys Vlasenkoec05df12017-07-31 19:43:47 +020014698 if (login_sh) {
Stefan Hellermann4ef14392013-03-15 02:45:50 +010014699 const char *hp;
14700
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014701 state = 1;
14702 read_profile("/etc/profile");
14703 state1:
14704 state = 2;
Stefan Hellermann4ef14392013-03-15 02:45:50 +010014705 hp = lookupvar("HOME");
Denys Vlasenko2eb0a7e2016-10-27 11:28:59 +020014706 if (hp)
14707 read_profile("$HOME/.profile");
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014708 }
14709 state2:
14710 state = 3;
Denys Vlasenko62f1eed2021-10-12 22:39:11 +020014711 if (iflag
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014712#ifndef linux
Denys Vlasenko62f1eed2021-10-12 22:39:11 +020014713 && getuid() == geteuid() && getgid() == getegid()
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014714#endif
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014715 ) {
Denys Vlasenko2eb0a7e2016-10-27 11:28:59 +020014716 const char *shinit = lookupvar("ENV");
14717 if (shinit != NULL && *shinit != '\0')
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014718 read_profile(shinit);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014719 }
Denys Vlasenko2eb0a7e2016-10-27 11:28:59 +020014720 popstackmark(&smark);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014721 state3:
14722 state = 4;
Denis Vlasenko5c2b8142009-03-19 01:59:59 +000014723 if (minusc) {
14724 /* evalstring pushes parsefile stack.
14725 * Ensure we don't falsely claim that 0 (stdin)
Denis Vlasenko5368ad52009-03-20 10:20:08 +000014726 * is one of stacked source fds.
14727 * Testcase: ash -c 'exec 1>&0' must not complain. */
Denys Vlasenkof3634582019-06-03 12:21:04 +020014728
Denys Vlasenko79b3d422010-06-03 04:29:08 +020014729 // if (!sflag) g_parsefile->pf_fd = -1;
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +020014730 // ^^ not necessary since now we special-case fd 0
Denys Vlasenko035486c2017-07-31 04:09:19 +020014731 // in save_fd_on_redirect()
Denys Vlasenkof3634582019-06-03 12:21:04 +020014732
Denys Vlasenko64aa86b2021-09-07 18:16:45 +020014733 lineno = 0; // bash compat
Denys Vlasenkof3634582019-06-03 12:21:04 +020014734 // dash: evalstring(minusc, sflag ? 0 : EV_EXIT);
14735 // The above makes
14736 // ash -sc 'echo $-'
14737 // continue reading input from stdin after running 'echo'.
14738 // bash does not do this: it prints "hBcs" and exits.
14739 evalstring(minusc, EV_EXIT);
Denis Vlasenko5c2b8142009-03-19 01:59:59 +000014740 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014741
14742 if (sflag || minusc == NULL) {
Denys Vlasenko4840ae82011-09-04 15:28:03 +020014743#if MAX_HISTORY > 0 && ENABLE_FEATURE_EDITING_SAVEHISTORY
Denys Vlasenko3f8ec002021-01-03 10:55:39 +010014744 if (line_input_state) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014745 const char *hp = lookupvar("HISTFILE");
Stefan Hellermannaeb717a2013-03-03 15:29:32 +010014746 if (!hp) {
14747 hp = lookupvar("HOME");
Stefan Hellermann4ef14392013-03-15 02:45:50 +010014748 if (hp) {
Denys Vlasenko5f7c82b2017-02-03 13:00:06 +010014749 INT_OFF;
Stefan Hellermannaeb717a2013-03-03 15:29:32 +010014750 hp = concat_path_file(hp, ".ash_history");
Denys Vlasenko0a0acb52015-04-18 19:36:38 +020014751 setvar0("HISTFILE", hp);
Stefan Hellermannaeb717a2013-03-03 15:29:32 +010014752 free((char*)hp);
Denys Vlasenko5f7c82b2017-02-03 13:00:06 +010014753 INT_ON;
Stefan Hellermannaeb717a2013-03-03 15:29:32 +010014754 hp = lookupvar("HISTFILE");
14755 }
14756 }
Denis Vlasenko5c2b8142009-03-19 01:59:59 +000014757 if (hp)
Denys Vlasenko3f8ec002021-01-03 10:55:39 +010014758 line_input_state->hist_file = xstrdup(hp);
Denys Vlasenko2c4de5b2011-03-31 13:16:52 +020014759# if ENABLE_FEATURE_SH_HISTFILESIZE
14760 hp = lookupvar("HISTFILESIZE");
14761 line_input_state->max_history = size_from_HISTFILESIZE(hp);
14762# endif
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014763 }
14764#endif
14765 state4: /* XXX ??? - why isn't this before the "if" statement */
14766 cmdloop(1);
14767 }
14768#if PROFILE
14769 monitor(0);
14770#endif
14771#ifdef GPROF
14772 {
14773 extern void _mcleanup(void);
14774 _mcleanup();
14775 }
14776#endif
Denys Vlasenkob563f622010-09-25 17:15:13 +020014777 TRACE(("End of main reached\n"));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014778 exitshell();
14779 /* NOTREACHED */
14780}
14781
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014782
Eric Andersendf82f612001-06-28 07:46:40 +000014783/*-
14784 * Copyright (c) 1989, 1991, 1993, 1994
Eric Andersen2870d962001-07-02 17:27:21 +000014785 * The Regents of the University of California. All rights reserved.
Eric Andersendf82f612001-06-28 07:46:40 +000014786 *
14787 * This code is derived from software contributed to Berkeley by
14788 * Kenneth Almquist.
14789 *
14790 * Redistribution and use in source and binary forms, with or without
14791 * modification, are permitted provided that the following conditions
14792 * are met:
14793 * 1. Redistributions of source code must retain the above copyright
14794 * notice, this list of conditions and the following disclaimer.
14795 * 2. Redistributions in binary form must reproduce the above copyright
14796 * notice, this list of conditions and the following disclaimer in the
14797 * documentation and/or other materials provided with the distribution.
"Vladimir N. Oleynik"ddc280e2005-12-15 12:01:49 +000014798 * 3. Neither the name of the University nor the names of its contributors
Eric Andersendf82f612001-06-28 07:46:40 +000014799 * may be used to endorse or promote products derived from this software
14800 * without specific prior written permission.
14801 *
Denys Vlasenko95f79532017-08-02 14:26:33 +020014802 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND
Eric Andersendf82f612001-06-28 07:46:40 +000014803 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
14804 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
14805 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
14806 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
14807 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
14808 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
14809 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
14810 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
14811 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
14812 * SUCH DAMAGE.
14813 */