blob: 6a16833b186caca8932c87495fbf79ce4d09d20d [file] [log] [blame]
Eric Andersendf82f612001-06-28 07:46:40 +00001/* vi: set sw=4 ts=4: */
2/*
3 * ash shell port for busybox
4 *
Denys Vlasenko73067272010-01-12 22:11:24 +01005 * This code is derived from software contributed to Berkeley by
6 * Kenneth Almquist.
7 *
8 * Original BSD copyright notice is retained at the end of this file.
9 *
Eric Andersendf82f612001-06-28 07:46:40 +000010 * Copyright (c) 1989, 1991, 1993, 1994
Eric Andersen2870d962001-07-02 17:27:21 +000011 * The Regents of the University of California. All rights reserved.
Eric Andersencb57d552001-06-28 07:25:16 +000012 *
"Vladimir N. Oleynik"ddc280e2005-12-15 12:01:49 +000013 * Copyright (c) 1997-2005 Herbert Xu <herbert@gondor.apana.org.au>
Eric Andersen81fe1232003-07-29 06:38:40 +000014 * was re-ported from NetBSD and debianized.
15 *
Denys Vlasenko0ef64bd2010-08-16 20:14:46 +020016 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
Eric Andersencb57d552001-06-28 07:25:16 +000017 */
Denys Vlasenko67e15292020-06-24 13:39:13 +020018//config:config SHELL_ASH
19//config: bool #hidden option
20//config: depends on !NOMMU
21//config:
Denys Vlasenko771f1992010-07-16 14:31:34 +020022//config:config ASH
Denys Vlasenkob097a842018-12-28 03:20:17 +010023//config: bool "ash (78 kb)"
Denys Vlasenko771f1992010-07-16 14:31:34 +020024//config: default y
25//config: depends on !NOMMU
Denys Vlasenko67e15292020-06-24 13:39:13 +020026//config: select SHELL_ASH
Denys Vlasenko771f1992010-07-16 14:31:34 +020027//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +020028//config: The most complete and most pedantically correct shell included with
29//config: busybox. This shell is actually a derivative of the Debian 'dash'
30//config: shell (by Herbert Xu), which was created by porting the 'ash' shell
31//config: (written by Kenneth Almquist) from NetBSD.
Denys Vlasenko771f1992010-07-16 14:31:34 +020032//config:
Kang-Che Sung6cd02942017-01-06 17:02:03 +010033//config:# ash options
34//config:# note: Don't remove !NOMMU part in the next line; it would break
35//config:# menuconfig's indenting.
Denys Vlasenko67e15292020-06-24 13:39:13 +020036//config:if !NOMMU && (SHELL_ASH || ASH || SH_IS_ASH || BASH_IS_ASH)
Kang-Che Sung6cd02942017-01-06 17:02:03 +010037//config:
Denys Vlasenko514b51d2016-10-01 14:33:08 +020038//config:config ASH_OPTIMIZE_FOR_SIZE
39//config: bool "Optimize for size instead of speed"
40//config: default y
Denys Vlasenko67e15292020-06-24 13:39:13 +020041//config: depends on SHELL_ASH
Denys Vlasenko514b51d2016-10-01 14:33:08 +020042//config:
43//config:config ASH_INTERNAL_GLOB
44//config: bool "Use internal glob() implementation"
Denys Vlasenko326edc32016-12-22 14:36:49 +010045//config: default y # Y is bigger, but because of uclibc glob() bug, let Y be default for now
Denys Vlasenko67e15292020-06-24 13:39:13 +020046//config: depends on SHELL_ASH
Denys Vlasenko514b51d2016-10-01 14:33:08 +020047//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +020048//config: Do not use glob() function from libc, use internal implementation.
49//config: Use this if you are getting "glob.h: No such file or directory"
50//config: or similar build errors.
51//config: Note that as of now (2017-01), uclibc and musl glob() both have bugs
52//config: which would break ash if you select N here.
Denys Vlasenkof5604222017-01-10 14:58:54 +010053//config:
54//config:config ASH_BASH_COMPAT
55//config: bool "bash-compatible extensions"
56//config: default y
Denys Vlasenko67e15292020-06-24 13:39:13 +020057//config: depends on SHELL_ASH
Denys Vlasenkof5604222017-01-10 14:58:54 +010058//config:
Denys Vlasenko01f7b9e2018-01-26 15:15:43 +010059//config:config ASH_BASH_SOURCE_CURDIR
60//config: bool "'source' and '.' builtins search current directory after $PATH"
61//config: default n # do not encourage non-standard behavior
Denys Vlasenko54c21112018-01-27 20:46:45 +010062//config: depends on ASH_BASH_COMPAT
Denys Vlasenko01f7b9e2018-01-26 15:15:43 +010063//config: help
64//config: This is not compliant with standards. Avoid if possible.
65//config:
William Pitcockd8fd88a2018-01-24 18:33:18 +010066//config:config ASH_BASH_NOT_FOUND_HOOK
67//config: bool "command_not_found_handle hook support"
68//config: default y
Denys Vlasenko54c21112018-01-27 20:46:45 +010069//config: depends on ASH_BASH_COMPAT
William Pitcockd8fd88a2018-01-24 18:33:18 +010070//config: help
71//config: Enable support for the 'command_not_found_handle' hook function,
72//config: from GNU bash, which allows for alternative command not found
73//config: handling.
74//config:
Denys Vlasenkof5604222017-01-10 14:58:54 +010075//config:config ASH_JOB_CONTROL
76//config: bool "Job control"
77//config: default y
Denys Vlasenko67e15292020-06-24 13:39:13 +020078//config: depends on SHELL_ASH
Denys Vlasenkof5604222017-01-10 14:58:54 +010079//config:
80//config:config ASH_ALIAS
81//config: bool "Alias support"
82//config: default y
Denys Vlasenko67e15292020-06-24 13:39:13 +020083//config: depends on SHELL_ASH
Denys Vlasenko514b51d2016-10-01 14:33:08 +020084//config:
85//config:config ASH_RANDOM_SUPPORT
86//config: bool "Pseudorandom generator and $RANDOM variable"
87//config: default y
Denys Vlasenko67e15292020-06-24 13:39:13 +020088//config: depends on SHELL_ASH
Denys Vlasenko514b51d2016-10-01 14:33:08 +020089//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +020090//config: Enable pseudorandom generator and dynamic variable "$RANDOM".
91//config: Each read of "$RANDOM" will generate a new pseudorandom value.
92//config: You can reset the generator by using a specified start value.
93//config: After "unset RANDOM" the generator will switch off and this
94//config: variable will no longer have special treatment.
Denys Vlasenko514b51d2016-10-01 14:33:08 +020095//config:
96//config:config ASH_EXPAND_PRMT
97//config: bool "Expand prompt string"
98//config: default y
Denys Vlasenko67e15292020-06-24 13:39:13 +020099//config: depends on SHELL_ASH
Denys Vlasenko514b51d2016-10-01 14:33:08 +0200100//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +0200101//config: $PS# may contain volatile content, such as backquote commands.
102//config: This option recreates the prompt string from the environment
103//config: variable each time it is displayed.
Denys Vlasenko514b51d2016-10-01 14:33:08 +0200104//config:
Denys Vlasenko046341e2011-02-04 17:53:59 +0100105//config:config ASH_IDLE_TIMEOUT
Denys Vlasenkof5604222017-01-10 14:58:54 +0100106//config: bool "Idle timeout variable $TMOUT"
Denys Vlasenko771f1992010-07-16 14:31:34 +0200107//config: default y
Denys Vlasenko67e15292020-06-24 13:39:13 +0200108//config: depends on SHELL_ASH
Denys Vlasenko771f1992010-07-16 14:31:34 +0200109//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +0200110//config: Enable bash-like auto-logout after $TMOUT seconds of idle time.
Denys Vlasenko771f1992010-07-16 14:31:34 +0200111//config:
Denys Vlasenkof5604222017-01-10 14:58:54 +0100112//config:config ASH_MAIL
113//config: bool "Check for new mail in interactive shell"
Denys Vlasenko771f1992010-07-16 14:31:34 +0200114//config: default y
Denys Vlasenko67e15292020-06-24 13:39:13 +0200115//config: depends on SHELL_ASH
Denys Vlasenko771f1992010-07-16 14:31:34 +0200116//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +0200117//config: Enable "check for new mail" function:
118//config: if set, $MAIL file and $MAILPATH list of files
119//config: are checked for mtime changes, and "you have mail"
120//config: message is printed if change is detected.
Denys Vlasenko771f1992010-07-16 14:31:34 +0200121//config:
Denys Vlasenko265062d2017-01-10 15:13:30 +0100122//config:config ASH_ECHO
Denys Vlasenkof5604222017-01-10 14:58:54 +0100123//config: bool "echo builtin"
Denys Vlasenko771f1992010-07-16 14:31:34 +0200124//config: default y
Denys Vlasenko67e15292020-06-24 13:39:13 +0200125//config: depends on SHELL_ASH
Denys Vlasenko771f1992010-07-16 14:31:34 +0200126//config:
Denys Vlasenko265062d2017-01-10 15:13:30 +0100127//config:config ASH_PRINTF
Denys Vlasenkof5604222017-01-10 14:58:54 +0100128//config: bool "printf builtin"
Denys Vlasenko771f1992010-07-16 14:31:34 +0200129//config: default y
Denys Vlasenko67e15292020-06-24 13:39:13 +0200130//config: depends on SHELL_ASH
Denys Vlasenko771f1992010-07-16 14:31:34 +0200131//config:
Denys Vlasenko265062d2017-01-10 15:13:30 +0100132//config:config ASH_TEST
Denys Vlasenkof5604222017-01-10 14:58:54 +0100133//config: bool "test builtin"
Denys Vlasenko771f1992010-07-16 14:31:34 +0200134//config: default y
Denys Vlasenko67e15292020-06-24 13:39:13 +0200135//config: depends on SHELL_ASH
Denys Vlasenko771f1992010-07-16 14:31:34 +0200136//config:
Denys Vlasenko2ec34962014-09-08 16:52:39 +0200137//config:config ASH_HELP
138//config: bool "help builtin"
139//config: default y
Denys Vlasenko67e15292020-06-24 13:39:13 +0200140//config: depends on SHELL_ASH
Denys Vlasenkof5604222017-01-10 14:58:54 +0100141//config:
142//config:config ASH_GETOPTS
143//config: bool "getopts builtin"
144//config: default y
Denys Vlasenko67e15292020-06-24 13:39:13 +0200145//config: depends on SHELL_ASH
Denys Vlasenko2ec34962014-09-08 16:52:39 +0200146//config:
Denys Vlasenko771f1992010-07-16 14:31:34 +0200147//config:config ASH_CMDCMD
Denys Vlasenkof5604222017-01-10 14:58:54 +0100148//config: bool "command builtin"
Denys Vlasenko771f1992010-07-16 14:31:34 +0200149//config: default y
Denys Vlasenko67e15292020-06-24 13:39:13 +0200150//config: depends on SHELL_ASH
Denys Vlasenko771f1992010-07-16 14:31:34 +0200151//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +0200152//config: Enable support for the 'command' builtin, which allows
153//config: you to run the specified command or builtin,
154//config: even when there is a function with the same name.
Kang-Che Sung6cd02942017-01-06 17:02:03 +0100155//config:
156//config:endif # ash options
Denys Vlasenko771f1992010-07-16 14:31:34 +0200157
Denys Vlasenko20704f02011-03-23 17:59:27 +0100158//applet:IF_ASH(APPLET(ash, BB_DIR_BIN, BB_SUID_DROP))
Denys Vlasenko205d48e2017-01-29 14:57:33 +0100159// APPLET_ODDNAME:name main location suid_type help
160//applet:IF_SH_IS_ASH( APPLET_ODDNAME(sh, ash, BB_DIR_BIN, BB_SUID_DROP, ash))
Denys Vlasenko0b883582016-12-23 16:49:07 +0100161//applet:IF_BASH_IS_ASH(APPLET_ODDNAME(bash, ash, BB_DIR_BIN, BB_SUID_DROP, ash))
Denys Vlasenko20704f02011-03-23 17:59:27 +0100162
Denys Vlasenko67e15292020-06-24 13:39:13 +0200163//kbuild:lib-$(CONFIG_SHELL_ASH) += ash.o ash_ptr_hack.o shell_common.o
Denys Vlasenko20704f02011-03-23 17:59:27 +0100164//kbuild:lib-$(CONFIG_ASH_RANDOM_SUPPORT) += random.o
165
Denys Vlasenko67047462016-12-22 15:21:58 +0100166/*
Denys Vlasenko7d4aec02017-01-11 14:00:38 +0100167 * DEBUG=1 to compile in debugging ('set -o debug' turns on)
168 * DEBUG=2 to compile in and turn on debugging.
169 * When debugging is on ("set -o debug" was executed, or DEBUG=2),
170 * debugging info is written to ./trace, quit signal generates core dump.
Denys Vlasenko67047462016-12-22 15:21:58 +0100171 */
172#define DEBUG 0
173/* Tweak debug output verbosity here */
174#define DEBUG_TIME 0
175#define DEBUG_PID 1
176#define DEBUG_SIG 1
177#define DEBUG_INTONOFF 0
178
179#define PROFILE 0
180
181#define JOBS ENABLE_ASH_JOB_CONTROL
182
Denys Vlasenko67047462016-12-22 15:21:58 +0100183#include <fnmatch.h>
184#include <sys/times.h>
185#include <sys/utsname.h> /* for setting $HOSTNAME */
Denys Vlasenko67047462016-12-22 15:21:58 +0100186#include "busybox.h" /* for applet_names */
Ron Yorston71df2d32018-11-27 14:34:25 +0000187#if ENABLE_FEATURE_SH_EMBEDDED_SCRIPTS
Denys Vlasenko4f2ef4a2018-11-01 09:53:25 +0100188# include "embedded_scripts.h"
189#else
190# define NUM_SCRIPTS 0
191#endif
Denys Vlasenko67047462016-12-22 15:21:58 +0100192
Denys Vlasenko7d4aec02017-01-11 14:00:38 +0100193/* So far, all bash compat is controlled by one config option */
194/* Separate defines document which part of code implements what */
195/* function keyword */
196#define BASH_FUNCTION ENABLE_ASH_BASH_COMPAT
197#define IF_BASH_FUNCTION IF_ASH_BASH_COMPAT
198/* &>file */
199#define BASH_REDIR_OUTPUT ENABLE_ASH_BASH_COMPAT
200#define IF_BASH_REDIR_OUTPUT IF_ASH_BASH_COMPAT
201/* $'...' */
202#define BASH_DOLLAR_SQUOTE ENABLE_ASH_BASH_COMPAT
203#define IF_BASH_DOLLAR_SQUOTE IF_ASH_BASH_COMPAT
204#define BASH_PATTERN_SUBST ENABLE_ASH_BASH_COMPAT
205#define IF_BASH_PATTERN_SUBST IF_ASH_BASH_COMPAT
206#define BASH_SUBSTR ENABLE_ASH_BASH_COMPAT
207#define IF_BASH_SUBSTR IF_ASH_BASH_COMPAT
Denys Vlasenko3632cb12018-04-10 15:25:41 +0200208/* BASH_TEST2: [[ EXPR ]]
209 * Status of [[ support:
Denys Vlasenkod2241f52020-10-31 03:34:07 +0100210 * && and || work as they should
211 * = is glob match operator, not equality operator: STR = GLOB
Denys Vlasenkod2241f52020-10-31 03:34:07 +0100212 * == same as =
Denys Vlasenkoa7c06532020-10-31 04:32:34 +0100213 * =~ is regex match operator: STR =~ REGEX
Denys Vlasenko3632cb12018-04-10 15:25:41 +0200214 * TODO:
215 * singleword+noglob expansion:
216 * v='a b'; [[ $v = 'a b' ]]; echo 0:$?
Denys Vlasenko89e9d552018-04-11 01:15:33 +0200217 * [[ /bin/n* ]]; echo 0:$?
Denys Vlasenko3632cb12018-04-10 15:25:41 +0200218 * quoting needs to be considered (-f is an operator, "-f" and ""-f are not; etc)
Denys Vlasenkoa7c06532020-10-31 04:32:34 +0100219 * ( ) < > should not have special meaning (IOW: should not require quoting)
220 * in word = GLOB, quoting should be significant on char-by-char basis: a*cd"*"
Denys Vlasenko3632cb12018-04-10 15:25:41 +0200221 */
Denys Vlasenko7d4aec02017-01-11 14:00:38 +0100222#define BASH_TEST2 (ENABLE_ASH_BASH_COMPAT * ENABLE_ASH_TEST)
223#define BASH_SOURCE ENABLE_ASH_BASH_COMPAT
224#define BASH_PIPEFAIL ENABLE_ASH_BASH_COMPAT
225#define BASH_HOSTNAME_VAR ENABLE_ASH_BASH_COMPAT
Ron Yorston1d371862019-04-15 10:52:05 +0100226#define BASH_EPOCH_VARS ENABLE_ASH_BASH_COMPAT
Denys Vlasenko7d4aec02017-01-11 14:00:38 +0100227#define BASH_SHLVL_VAR ENABLE_ASH_BASH_COMPAT
Denys Vlasenkof8cdc7a2017-08-04 15:24:49 +0200228#define BASH_XTRACEFD ENABLE_ASH_BASH_COMPAT
Johannes Schindelin3bef5d82017-08-08 16:46:39 +0200229#define BASH_READ_D ENABLE_ASH_BASH_COMPAT
230#define IF_BASH_READ_D IF_ASH_BASH_COMPAT
Ron Yorstone48559e2019-03-31 09:27:09 +0100231#define BASH_WAIT_N ENABLE_ASH_BASH_COMPAT
Denys Vlasenko7d4aec02017-01-11 14:00:38 +0100232
Denys Vlasenko67047462016-12-22 15:21:58 +0100233#if defined(__ANDROID_API__) && __ANDROID_API__ <= 24
234/* Bionic at least up to version 24 has no glob() */
235# undef ENABLE_ASH_INTERNAL_GLOB
236# define ENABLE_ASH_INTERNAL_GLOB 1
237#endif
238
239#if !ENABLE_ASH_INTERNAL_GLOB && defined(__UCLIBC__)
240# error uClibc glob() is buggy, use ASH_INTERNAL_GLOB.
241# error The bug is: for "$PWD"/<pattern> ash will escape e.g. dashes in "$PWD"
242# error with backslash, even ones which do not need to be: "/a-b" -> "/a\-b"
243# error glob() should unbackslash them and match. uClibc does not unbackslash,
244# error fails to match dirname, subsequently not expanding <pattern> in it.
245// Testcase:
246// if (glob("/etc/polkit\\-1", 0, NULL, &pglob)) - this returns 0 on uclibc, no bug
247// if (glob("/etc/polkit\\-1/*", 0, NULL, &pglob)) printf("uclibc bug!\n");
248#endif
249
250#if !ENABLE_ASH_INTERNAL_GLOB
251# include <glob.h>
252#endif
253
254#include "unicode.h"
255#include "shell_common.h"
Denys Vlasenko0b883582016-12-23 16:49:07 +0100256#if ENABLE_FEATURE_SH_MATH
Denys Vlasenko67047462016-12-22 15:21:58 +0100257# include "math.h"
Denys Vlasenkocf3a7962017-07-26 14:38:19 +0200258#else
259typedef long arith_t;
260# define ARITH_FMT "%ld"
Denys Vlasenko67047462016-12-22 15:21:58 +0100261#endif
262#if ENABLE_ASH_RANDOM_SUPPORT
263# include "random.h"
264#else
265# define CLEAR_RANDOM_T(rnd) ((void)0)
266#endif
267
268#include "NUM_APPLETS.h"
269#if NUM_APPLETS == 1
270/* STANDALONE does not make sense, and won't compile */
271# undef CONFIG_FEATURE_SH_STANDALONE
272# undef ENABLE_FEATURE_SH_STANDALONE
273# undef IF_FEATURE_SH_STANDALONE
274# undef IF_NOT_FEATURE_SH_STANDALONE
275# define ENABLE_FEATURE_SH_STANDALONE 0
276# define IF_FEATURE_SH_STANDALONE(...)
277# define IF_NOT_FEATURE_SH_STANDALONE(...) __VA_ARGS__
278#endif
279
Denys Vlasenko9acd63c2018-03-28 18:35:07 +0200280#ifndef F_DUPFD_CLOEXEC
281# define F_DUPFD_CLOEXEC F_DUPFD
282#endif
Denys Vlasenko60fb98e2018-03-30 22:15:14 +0200283#ifndef O_CLOEXEC
284# define O_CLOEXEC 0
285#endif
Denys Vlasenko67047462016-12-22 15:21:58 +0100286#ifndef PIPE_BUF
287# define PIPE_BUF 4096 /* amount of buffering in a pipe */
288#endif
289
290#if !BB_MMU
291# error "Do not even bother, ash will not run on NOMMU machine"
292#endif
293
Denys Vlasenko6f9442f2018-01-28 20:41:23 +0100294/* We use a trick to have more optimized code (fewer pointer reloads):
295 * ash.c: extern struct globals *const ash_ptr_to_globals;
296 * ash_ptr_hack.c: struct globals *ash_ptr_to_globals;
297 * This way, compiler in ash.c knows the pointer can not change.
298 *
299 * However, this may break on weird arches or toolchains. In this case,
300 * set "-DBB_GLOBAL_CONST=''" in CONFIG_EXTRA_CFLAGS to disable
301 * this optimization.
302 */
303#ifndef BB_GLOBAL_CONST
304# define BB_GLOBAL_CONST const
305#endif
306
Denis Vlasenkob012b102007-02-19 22:43:01 +0000307
Denis Vlasenko01631112007-12-16 17:20:38 +0000308/* ============ Hash table sizes. Configurable. */
309
310#define VTABSIZE 39
311#define ATABSIZE 39
312#define CMDTABLESIZE 31 /* should be prime */
313
314
Denis Vlasenkob012b102007-02-19 22:43:01 +0000315/* ============ Shell options */
316
Denys Vlasenko4e039ba2021-01-04 03:50:38 +0100317/* If you add/change options hare, update --help text too */
Denis Vlasenkob012b102007-02-19 22:43:01 +0000318static const char *const optletters_optnames[] = {
319 "e" "errexit",
320 "f" "noglob",
Denys Vlasenko4e039ba2021-01-04 03:50:38 +0100321/* bash has '-o ignoreeof', but no short synonym -I for it */
322/* (in bash, set -I disables invisible variables (what's that?)) */
Denis Vlasenkob012b102007-02-19 22:43:01 +0000323 "I" "ignoreeof",
Denys Vlasenkof3634582019-06-03 12:21:04 +0200324/* The below allowed this invocation:
Denys Vlasenko897475a2019-06-01 16:35:09 +0200325 * ash -c 'set -i; echo $-; sleep 5; echo $-'
326 * to be ^C-ed and get to interactive ash prompt.
Denys Vlasenkof3634582019-06-03 12:21:04 +0200327 * bash does not support such "set -i".
328 * In our code, this is denoted by empty long name:
Denys Vlasenko897475a2019-06-01 16:35:09 +0200329 */
Denys Vlasenkof3634582019-06-03 12:21:04 +0200330 "i" "",
Denys Vlasenko4e039ba2021-01-04 03:50:38 +0100331/* (removing "i" altogether would remove it from "$-", not good) */
Denis Vlasenkob012b102007-02-19 22:43:01 +0000332 "m" "monitor",
333 "n" "noexec",
Denys Vlasenko4e039ba2021-01-04 03:50:38 +0100334/* Ditto: bash has no "set -s", "set -c" */
Denys Vlasenkof3634582019-06-03 12:21:04 +0200335 "s" "",
336 "c" "",
Denis Vlasenkob012b102007-02-19 22:43:01 +0000337 "x" "xtrace",
338 "v" "verbose",
339 "C" "noclobber",
340 "a" "allexport",
341 "b" "notify",
342 "u" "nounset",
Denys Vlasenkoe9ac32a2009-12-05 02:01:25 +0100343 "\0" "vi"
Denys Vlasenko7d4aec02017-01-11 14:00:38 +0100344#if BASH_PIPEFAIL
Denys Vlasenkoe9ac32a2009-12-05 02:01:25 +0100345 ,"\0" "pipefail"
Michael Abbott359da5e2009-12-04 23:03:29 +0100346#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +0000347#if DEBUG
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000348 ,"\0" "nolog"
349 ,"\0" "debug"
Denis Vlasenkob012b102007-02-19 22:43:01 +0000350#endif
351};
Denys Vlasenko897475a2019-06-01 16:35:09 +0200352//bash 4.4.23 also has these opts (with these defaults):
353//braceexpand on
354//emacs on
355//errtrace off
356//functrace off
357//hashall on
358//histexpand off
359//history on
360//interactive-comments on
361//keyword off
362//onecmd off
363//physical off
364//posix off
365//privileged off
Denis Vlasenkob012b102007-02-19 22:43:01 +0000366
Denys Vlasenko285ad152009-12-04 23:02:27 +0100367#define optletters(n) optletters_optnames[n][0]
368#define optnames(n) (optletters_optnames[n] + 1)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000369
Denis Vlasenko80b8b392007-06-25 10:55:35 +0000370enum { NOPTS = ARRAY_SIZE(optletters_optnames) };
Denis Vlasenkob012b102007-02-19 22:43:01 +0000371
Eric Andersenc470f442003-07-28 09:56:35 +0000372
Denis Vlasenkob012b102007-02-19 22:43:01 +0000373/* ============ Misc data */
Eric Andersenc470f442003-07-28 09:56:35 +0000374
Denys Vlasenkoea8b2522010-06-02 12:57:26 +0200375#define msg_illnum "Illegal number: %s"
Denis Vlasenkoaa744452007-02-23 01:04:22 +0000376
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +0000377/*
Eric Andersenc470f442003-07-28 09:56:35 +0000378 * We enclose jmp_buf in a structure so that we can declare pointers to
379 * jump locations. The global variable handler contains the location to
Denis Vlasenkof1733952009-03-19 23:21:55 +0000380 * jump to when an exception occurs, and the global variable exception_type
Eric Andersenaff114c2004-04-14 17:51:38 +0000381 * contains a code identifying the exception. To implement nested
Eric Andersenc470f442003-07-28 09:56:35 +0000382 * exception handlers, the user should save the value of handler on entry
383 * to an inner scope, set handler to point to a jmploc structure for the
384 * inner scope, and restore handler on exit from the scope.
385 */
Eric Andersenc470f442003-07-28 09:56:35 +0000386struct jmploc {
387 jmp_buf loc;
388};
Denis Vlasenko01631112007-12-16 17:20:38 +0000389
390struct globals_misc {
Denys Vlasenko4d12e942016-10-01 16:03:11 +0200391 uint8_t exitstatus; /* exit status of last command */
392 uint8_t back_exitstatus;/* exit status of backquoted command */
393 smallint job_warning; /* user was warned about stopped jobs (can be 2, 1 or 0). */
Denys Vlasenko4ccddc82020-02-14 17:27:18 +0100394 int savestatus; /* exit status of last command outside traps */
Denys Vlasenko4d12e942016-10-01 16:03:11 +0200395 int rootpid; /* pid of main shell */
Denis Vlasenko01631112007-12-16 17:20:38 +0000396 /* shell level: 0 for the main shell, 1 for its children, and so on */
397 int shlvl;
398#define rootshell (!shlvl)
Denys Vlasenko675d24a2018-01-27 22:02:05 +0100399 int errlinno;
400
Denis Vlasenko01631112007-12-16 17:20:38 +0000401 char *minusc; /* argument to -c option */
402
403 char *curdir; // = nullstr; /* current working directory */
404 char *physdir; // = nullstr; /* physical working directory */
405
406 char *arg0; /* value of $0 */
407
408 struct jmploc *exception_handler;
Denis Vlasenko991a1da2008-02-10 19:02:53 +0000409
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200410 volatile int suppress_int; /* counter */
411 volatile /*sig_atomic_t*/ smallint pending_int; /* 1 = got SIGINT */
Denys Vlasenko458c1f22016-10-27 23:51:19 +0200412 volatile /*sig_atomic_t*/ smallint got_sigchld; /* 1 = got SIGCHLD */
Denys Vlasenko8f7b0242016-10-28 17:16:11 +0200413 volatile /*sig_atomic_t*/ smallint pending_sig; /* last pending signal */
Denys Vlasenkof977e002020-02-20 16:54:29 +0100414 smallint exception_type; /* kind of exception: */
Eric Andersenc470f442003-07-28 09:56:35 +0000415#define EXINT 0 /* SIGINT received */
416#define EXERROR 1 /* a generic error */
Denys Vlasenkof977e002020-02-20 16:54:29 +0100417#define EXEND 3 /* exit the shell */
418#define EXEXIT 4 /* exit the shell via exitcmd */
Eric Andersen2870d962001-07-02 17:27:21 +0000419
Denis Vlasenkob07a4962008-06-22 13:16:23 +0000420 char nullstr[1]; /* zero length string */
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000421
422 char optlist[NOPTS];
423#define eflag optlist[0]
424#define fflag optlist[1]
425#define Iflag optlist[2]
426#define iflag optlist[3]
427#define mflag optlist[4]
428#define nflag optlist[5]
429#define sflag optlist[6]
Denys Vlasenkof3634582019-06-03 12:21:04 +0200430#define cflag optlist[7]
431#define xflag optlist[8]
432#define vflag optlist[9]
433#define Cflag optlist[10]
434#define aflag optlist[11]
435#define bflag optlist[12]
436#define uflag optlist[13]
437#define viflag optlist[14]
Denys Vlasenko7d4aec02017-01-11 14:00:38 +0100438#if BASH_PIPEFAIL
Denys Vlasenkof3634582019-06-03 12:21:04 +0200439# define pipefail optlist[15]
Michael Abbott359da5e2009-12-04 23:03:29 +0100440#else
441# define pipefail 0
442#endif
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000443#if DEBUG
Denys Vlasenkof3634582019-06-03 12:21:04 +0200444# define nolog optlist[15 + BASH_PIPEFAIL]
445# define debug optlist[16 + BASH_PIPEFAIL]
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000446#endif
447
448 /* trap handler commands */
Denis Vlasenko01631112007-12-16 17:20:38 +0000449 /*
450 * Sigmode records the current value of the signal handlers for the various
451 * modes. A value of zero means that the current handler is not known.
Denis Vlasenkof8535cc2008-12-03 10:36:26 +0000452 * S_HARD_IGN indicates that the signal was ignored on entry to the shell.
Denis Vlasenko01631112007-12-16 17:20:38 +0000453 */
454 char sigmode[NSIG - 1];
Denis Vlasenkof8535cc2008-12-03 10:36:26 +0000455#define S_DFL 1 /* default signal handling (SIG_DFL) */
456#define S_CATCH 2 /* signal is caught */
457#define S_IGN 3 /* signal is ignored (SIG_IGN) */
Denys Vlasenko0f14f412017-08-06 20:06:19 +0200458#define S_HARD_IGN 4 /* signal is ignored permanently (it was SIG_IGN on entry to shell) */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000459
Denis Vlasenko01631112007-12-16 17:20:38 +0000460 /* indicates specified signal received */
Denis Vlasenko4b875702009-03-19 13:30:04 +0000461 uint8_t gotsig[NSIG - 1]; /* offset by 1: "signal" 0 is meaningless */
Denys Vlasenko238bf182010-05-18 15:49:07 +0200462 uint8_t may_have_traps; /* 0: definitely no traps are set, 1: some traps may be set */
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000463 char *trap[NSIG];
Denys Vlasenko21d87d42009-09-25 00:06:51 +0200464 char **trap_ptr; /* used only by "trap hack" */
Denis Vlasenko448d30e2008-06-27 00:24:11 +0000465
466 /* Rarely referenced stuff */
467#if ENABLE_ASH_RANDOM_SUPPORT
Denys Vlasenko3ea2e822009-10-09 20:59:04 +0200468 random_t random_gen;
Denis Vlasenko448d30e2008-06-27 00:24:11 +0000469#endif
470 pid_t backgndpid; /* pid of last background process */
Denis Vlasenko01631112007-12-16 17:20:38 +0000471};
Denys Vlasenko6f9442f2018-01-28 20:41:23 +0100472extern struct globals_misc *BB_GLOBAL_CONST ash_ptr_to_globals_misc;
Denis Vlasenko574f2f42008-02-27 18:41:59 +0000473#define G_misc (*ash_ptr_to_globals_misc)
Denys Vlasenko4d12e942016-10-01 16:03:11 +0200474#define exitstatus (G_misc.exitstatus )
475#define back_exitstatus (G_misc.back_exitstatus )
476#define job_warning (G_misc.job_warning)
Denys Vlasenko4ccddc82020-02-14 17:27:18 +0100477#define savestatus (G_misc.savestatus )
Denis Vlasenko26bc57d2008-06-27 00:29:34 +0000478#define rootpid (G_misc.rootpid )
479#define shlvl (G_misc.shlvl )
Denys Vlasenko675d24a2018-01-27 22:02:05 +0100480#define errlinno (G_misc.errlinno )
Denis Vlasenko26bc57d2008-06-27 00:29:34 +0000481#define minusc (G_misc.minusc )
482#define curdir (G_misc.curdir )
483#define physdir (G_misc.physdir )
484#define arg0 (G_misc.arg0 )
Denis Vlasenko01631112007-12-16 17:20:38 +0000485#define exception_handler (G_misc.exception_handler)
Denis Vlasenko7f88e342009-03-19 03:36:18 +0000486#define exception_type (G_misc.exception_type )
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200487#define suppress_int (G_misc.suppress_int )
488#define pending_int (G_misc.pending_int )
Denys Vlasenko458c1f22016-10-27 23:51:19 +0200489#define got_sigchld (G_misc.got_sigchld )
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200490#define pending_sig (G_misc.pending_sig )
Denis Vlasenko26bc57d2008-06-27 00:29:34 +0000491#define nullstr (G_misc.nullstr )
492#define optlist (G_misc.optlist )
493#define sigmode (G_misc.sigmode )
494#define gotsig (G_misc.gotsig )
Denys Vlasenko238bf182010-05-18 15:49:07 +0200495#define may_have_traps (G_misc.may_have_traps )
Denis Vlasenko26bc57d2008-06-27 00:29:34 +0000496#define trap (G_misc.trap )
Denys Vlasenko21d87d42009-09-25 00:06:51 +0200497#define trap_ptr (G_misc.trap_ptr )
Denys Vlasenko3ea2e822009-10-09 20:59:04 +0200498#define random_gen (G_misc.random_gen )
Denis Vlasenko448d30e2008-06-27 00:24:11 +0000499#define backgndpid (G_misc.backgndpid )
Denis Vlasenko01631112007-12-16 17:20:38 +0000500#define INIT_G_misc() do { \
Denys Vlasenkoaf7169b2019-10-25 12:12:22 +0200501 (*(struct globals_misc**)not_const_pp(&ash_ptr_to_globals_misc)) = xzalloc(sizeof(G_misc)); \
Denis Vlasenko574f2f42008-02-27 18:41:59 +0000502 barrier(); \
Denys Vlasenko4ccddc82020-02-14 17:27:18 +0100503 savestatus = -1; \
Denis Vlasenko01631112007-12-16 17:20:38 +0000504 curdir = nullstr; \
505 physdir = nullstr; \
Denys Vlasenko21d87d42009-09-25 00:06:51 +0200506 trap_ptr = trap; \
Denis Vlasenko01631112007-12-16 17:20:38 +0000507} while (0)
508
509
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000510/* ============ DEBUG */
511#if DEBUG
512static void trace_printf(const char *fmt, ...);
513static void trace_vprintf(const char *fmt, va_list va);
514# define TRACE(param) trace_printf param
515# define TRACEV(param) trace_vprintf param
Denis Vlasenko1bb3d7e2009-03-20 07:45:36 +0000516# define close(fd) do { \
517 int dfd = (fd); \
Denis Vlasenkob9e70dd2009-03-20 01:24:08 +0000518 if (close(dfd) < 0) \
Denys Vlasenko883cea42009-07-11 15:31:59 +0200519 bb_error_msg("bug on %d: closing %d(0x%x)", \
Denis Vlasenko1bb3d7e2009-03-20 07:45:36 +0000520 __LINE__, dfd, dfd); \
Denis Vlasenkob9e70dd2009-03-20 01:24:08 +0000521} while (0)
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000522#else
523# define TRACE(param)
524# define TRACEV(param)
525#endif
526
527
Denis Vlasenko559691a2008-10-05 18:39:31 +0000528/* ============ Utility functions */
Denys Vlasenko1961aea2013-02-26 00:36:53 +0100529#define is_name(c) ((c) == '_' || isalpha((unsigned char)(c)))
530#define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c)))
531
Denys Vlasenko37dc08b2016-10-02 04:38:07 +0200532static int
533isdigit_str9(const char *str)
Denis Vlasenko559691a2008-10-05 18:39:31 +0000534{
535 int maxlen = 9 + 1; /* max 9 digits: 999999999 */
536 while (--maxlen && isdigit(*str))
537 str++;
538 return (*str == '\0');
539}
Denis Vlasenko01631112007-12-16 17:20:38 +0000540
Denys Vlasenko37dc08b2016-10-02 04:38:07 +0200541static const char *
542var_end(const char *var)
Denys Vlasenko8837c5d2010-06-02 12:56:18 +0200543{
544 while (*var)
545 if (*var++ == '=')
546 break;
547 return var;
548}
549
Denis Vlasenko559691a2008-10-05 18:39:31 +0000550
Denys Vlasenko2124c0e2020-12-19 14:33:02 +0100551/* ============ Parser data */
552
553/*
554 * ash_vmsg() needs parsefile->fd, hence parsefile definition is moved up.
555 */
556struct strlist {
557 struct strlist *next;
558 char *text;
559};
560
561struct alias;
562
563struct strpush {
564 struct strpush *prev; /* preceding string on stack */
565 char *prev_string;
566 int prev_left_in_line;
567#if ENABLE_ASH_ALIAS
568 struct alias *ap; /* if push was associated with an alias */
569#endif
570 char *string; /* remember the string since it may change */
571
572 /* Remember last two characters for pungetc. */
573 int lastc[2];
574
575 /* Number of outstanding calls to pungetc. */
576 int unget;
577};
578
579/*
580 * The parsefile structure pointed to by the global variable parsefile
581 * contains information about the current file being read.
582 */
583struct parsefile {
584 struct parsefile *prev; /* preceding file on stack */
585 int linno; /* current line */
586 int pf_fd; /* file descriptor (or -1 if string) */
587 int left_in_line; /* number of chars left in this line */
588 int left_in_buffer; /* number of chars left in this buffer past the line */
589 char *next_to_pgetc; /* next char in buffer */
590 char *buf; /* input buffer */
591 struct strpush *strpush; /* for pushing strings at this level */
592 struct strpush basestrpush; /* so pushing one is fast */
593
594 /* Remember last two characters for pungetc. */
595 int lastc[2];
596
597 /* Number of outstanding calls to pungetc. */
598 int unget;
599};
600
601static struct parsefile basepf; /* top level input file */
602static struct parsefile *g_parsefile = &basepf; /* current input file */
603static char *commandname; /* currently executing command */
604
605
Denis Vlasenko559691a2008-10-05 18:39:31 +0000606/* ============ Interrupts / exceptions */
Denys Vlasenko66c5b122011-02-08 05:07:02 +0100607
608static void exitshell(void) NORETURN;
609
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000610/*
Eric Andersen2870d962001-07-02 17:27:21 +0000611 * These macros allow the user to suspend the handling of interrupt signals
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +0000612 * over a period of time. This is similar to SIGHOLD or to sigblock, but
Eric Andersen2870d962001-07-02 17:27:21 +0000613 * much more efficient and portable. (But hacking the kernel is so much
614 * more fun than worrying about efficiency and portability. :-))
615 */
Denys Vlasenko06b11492016-11-04 16:43:18 +0100616#if DEBUG_INTONOFF
617# define INT_OFF do { \
618 TRACE(("%s:%d INT_OFF(%d)\n", __func__, __LINE__, suppress_int)); \
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200619 suppress_int++; \
Denys Vlasenkode892052016-10-02 01:49:13 +0200620 barrier(); \
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000621} while (0)
Denys Vlasenko06b11492016-11-04 16:43:18 +0100622#else
623# define INT_OFF do { \
624 suppress_int++; \
625 barrier(); \
626} while (0)
627#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +0000628
629/*
630 * Called to raise an exception. Since C doesn't include exceptions, we
631 * just do a longjmp to the exception handler. The type of exception is
Denis Vlasenko4b875702009-03-19 13:30:04 +0000632 * stored in the global variable "exception_type".
Denis Vlasenkob012b102007-02-19 22:43:01 +0000633 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000634static void raise_exception(int) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000635static void
636raise_exception(int e)
637{
638#if DEBUG
Denis Vlasenko2da584f2007-02-19 22:44:05 +0000639 if (exception_handler == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000640 abort();
641#endif
642 INT_OFF;
Denis Vlasenko7f88e342009-03-19 03:36:18 +0000643 exception_type = e;
Denis Vlasenko2da584f2007-02-19 22:44:05 +0000644 longjmp(exception_handler->loc, 1);
Denis Vlasenkob012b102007-02-19 22:43:01 +0000645}
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000646#if DEBUG
647#define raise_exception(e) do { \
648 TRACE(("raising exception %d on line %d\n", (e), __LINE__)); \
649 raise_exception(e); \
650} while (0)
651#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +0000652
653/*
Denys Vlasenkof37e1152016-10-07 03:17:28 +0200654 * Called when a SIGINT is received. (If the user specifies
Denis Vlasenkob012b102007-02-19 22:43:01 +0000655 * that SIGINT is to be trapped or ignored using the trap builtin, then
656 * this routine is not called.) Suppressint is nonzero when interrupts
657 * are held using the INT_OFF macro. (The test for iflag is just
658 * defensive programming.)
659 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000660static void raise_interrupt(void) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000661static void
662raise_interrupt(void)
663{
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200664 pending_int = 0;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +0000665 /* Signal is not automatically unmasked after it is raised,
666 * do it ourself - unmask all signals */
Denis Vlasenko3f165fa2008-03-17 08:29:08 +0000667 sigprocmask_allsigs(SIG_UNBLOCK);
Denys Vlasenko238bf182010-05-18 15:49:07 +0200668 /* pending_sig = 0; - now done in signal_handler() */
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000669
Denys Vlasenkoc0663c72016-10-27 21:09:01 +0200670 if (!(rootshell && iflag)) {
671 /* Kill ourself with SIGINT */
672 signal(SIGINT, SIG_DFL);
673 raise(SIGINT);
Denis Vlasenkob012b102007-02-19 22:43:01 +0000674 }
Denys Vlasenko4d12e942016-10-01 16:03:11 +0200675 /* bash: ^C even on empty command line sets $? */
676 exitstatus = SIGINT + 128;
Denys Vlasenkoc0663c72016-10-27 21:09:01 +0200677 raise_exception(EXINT);
Denis Vlasenkob012b102007-02-19 22:43:01 +0000678 /* NOTREACHED */
679}
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000680#if DEBUG
681#define raise_interrupt() do { \
682 TRACE(("raising interrupt on line %d\n", __LINE__)); \
683 raise_interrupt(); \
684} while (0)
685#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +0000686
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000687static IF_ASH_OPTIMIZE_FOR_SIZE(inline) void
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000688int_on(void)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000689{
Denys Vlasenkode892052016-10-02 01:49:13 +0200690 barrier();
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200691 if (--suppress_int == 0 && pending_int) {
Denis Vlasenkob012b102007-02-19 22:43:01 +0000692 raise_interrupt();
693 }
694}
Denys Vlasenko06b11492016-11-04 16:43:18 +0100695#if DEBUG_INTONOFF
696# define INT_ON do { \
697 TRACE(("%s:%d INT_ON(%d)\n", __func__, __LINE__, suppress_int-1)); \
698 int_on(); \
699} while (0)
700#else
701# define INT_ON int_on()
702#endif
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000703static IF_ASH_OPTIMIZE_FOR_SIZE(inline) void
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000704force_int_on(void)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000705{
Denys Vlasenkode892052016-10-02 01:49:13 +0200706 barrier();
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200707 suppress_int = 0;
708 if (pending_int)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000709 raise_interrupt();
710}
711#define FORCE_INT_ON force_int_on()
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000712
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200713#define SAVE_INT(v) ((v) = suppress_int)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000714
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000715#define RESTORE_INT(v) do { \
Denys Vlasenkode892052016-10-02 01:49:13 +0200716 barrier(); \
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200717 suppress_int = (v); \
718 if (suppress_int == 0 && pending_int) \
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000719 raise_interrupt(); \
720} while (0)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000721
Glenn L McGrath9fef17d2002-08-22 18:41:20 +0000722
Denis Vlasenkobc54cff2007-02-23 01:05:52 +0000723/* ============ Stdout/stderr output */
Eric Andersenc470f442003-07-28 09:56:35 +0000724
Eric Andersenc470f442003-07-28 09:56:35 +0000725static void
Denis Vlasenkob012b102007-02-19 22:43:01 +0000726outstr(const char *p, FILE *file)
Denis Vlasenkoe5570da2007-02-19 22:41:55 +0000727{
Denis Vlasenkob012b102007-02-19 22:43:01 +0000728 INT_OFF;
729 fputs(p, file);
730 INT_ON;
731}
732
733static void
734flush_stdout_stderr(void)
735{
736 INT_OFF;
Denys Vlasenko8131eea2009-11-02 14:19:51 +0100737 fflush_all();
Denis Vlasenkob012b102007-02-19 22:43:01 +0000738 INT_ON;
739}
740
Denys Vlasenko9c541002015-10-07 15:44:36 +0200741/* Was called outcslow(c,FILE*), but c was always '\n' */
Denis Vlasenkob012b102007-02-19 22:43:01 +0000742static void
Denys Vlasenko9c541002015-10-07 15:44:36 +0200743newline_and_flush(FILE *dest)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000744{
745 INT_OFF;
Denys Vlasenko9c541002015-10-07 15:44:36 +0200746 putc('\n', dest);
Denis Vlasenkob012b102007-02-19 22:43:01 +0000747 fflush(dest);
748 INT_ON;
749}
750
751static int out1fmt(const char *, ...) __attribute__((__format__(__printf__,1,2)));
752static int
753out1fmt(const char *fmt, ...)
754{
755 va_list ap;
756 int r;
757
758 INT_OFF;
759 va_start(ap, fmt);
760 r = vprintf(fmt, ap);
761 va_end(ap);
762 INT_ON;
763 return r;
764}
765
766static int fmtstr(char *, size_t, const char *, ...) __attribute__((__format__(__printf__,3,4)));
767static int
768fmtstr(char *outbuf, size_t length, const char *fmt, ...)
769{
770 va_list ap;
771 int ret;
772
Denis Vlasenkob012b102007-02-19 22:43:01 +0000773 INT_OFF;
Denys Vlasenkocf3a7962017-07-26 14:38:19 +0200774 va_start(ap, fmt);
Denis Vlasenkob012b102007-02-19 22:43:01 +0000775 ret = vsnprintf(outbuf, length, fmt, ap);
776 va_end(ap);
777 INT_ON;
Denys Vlasenko3f7fb2c2020-02-16 18:06:20 +0100778 return ret > (int)length ? length : ret;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000779}
780
781static void
782out1str(const char *p)
783{
784 outstr(p, stdout);
785}
786
787static void
788out2str(const char *p)
789{
790 outstr(p, stderr);
Denys Vlasenko8131eea2009-11-02 14:19:51 +0100791 flush_stdout_stderr();
Denis Vlasenkob012b102007-02-19 22:43:01 +0000792}
793
794
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000795/* ============ Parser structures */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +0000796
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000797/* control characters in argument strings */
Denys Vlasenko2ce42e92009-11-29 02:18:13 +0100798#define CTL_FIRST CTLESC
Denys Vlasenkob6c84342009-08-29 20:23:20 +0200799#define CTLESC ((unsigned char)'\201') /* escape next character */
800#define CTLVAR ((unsigned char)'\202') /* variable defn */
801#define CTLENDVAR ((unsigned char)'\203')
802#define CTLBACKQ ((unsigned char)'\204')
Denys Vlasenkob6c84342009-08-29 20:23:20 +0200803#define CTLARI ((unsigned char)'\206') /* arithmetic expression */
804#define CTLENDARI ((unsigned char)'\207')
805#define CTLQUOTEMARK ((unsigned char)'\210')
Denys Vlasenko2ce42e92009-11-29 02:18:13 +0100806#define CTL_LAST CTLQUOTEMARK
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000807
808/* variable substitution byte (follows CTLVAR) */
809#define VSTYPE 0x0f /* type of variable substitution */
810#define VSNUL 0x10 /* colon--treat the empty string as unset */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000811
812/* values of VSTYPE field */
Denis Vlasenko92e13c22008-03-25 01:17:40 +0000813#define VSNORMAL 0x1 /* normal variable: $var or ${var} */
814#define VSMINUS 0x2 /* ${var-text} */
815#define VSPLUS 0x3 /* ${var+text} */
816#define VSQUESTION 0x4 /* ${var?message} */
817#define VSASSIGN 0x5 /* ${var=text} */
818#define VSTRIMRIGHT 0x6 /* ${var%pattern} */
819#define VSTRIMRIGHTMAX 0x7 /* ${var%%pattern} */
820#define VSTRIMLEFT 0x8 /* ${var#pattern} */
821#define VSTRIMLEFTMAX 0x9 /* ${var##pattern} */
822#define VSLENGTH 0xa /* ${#var} */
Denys Vlasenko7d4aec02017-01-11 14:00:38 +0100823#if BASH_SUBSTR
Denis Vlasenko92e13c22008-03-25 01:17:40 +0000824#define VSSUBSTR 0xc /* ${var:position:length} */
Denys Vlasenko7d4aec02017-01-11 14:00:38 +0100825#endif
826#if BASH_PATTERN_SUBST
Denis Vlasenko92e13c22008-03-25 01:17:40 +0000827#define VSREPLACE 0xd /* ${var/pattern/replacement} */
828#define VSREPLACEALL 0xe /* ${var//pattern/replacement} */
829#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000830
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000831static const char dolatstr[] ALIGN1 = {
Ron Yorston549deab2015-05-18 09:57:51 +0200832 CTLQUOTEMARK, CTLVAR, VSNORMAL, '@', '=', CTLQUOTEMARK, '\0'
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000833};
Ron Yorston549deab2015-05-18 09:57:51 +0200834#define DOLATSTRLEN 6
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000835
Denis Vlasenko559691a2008-10-05 18:39:31 +0000836#define NCMD 0
837#define NPIPE 1
838#define NREDIR 2
839#define NBACKGND 3
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000840#define NSUBSHELL 4
Denis Vlasenko559691a2008-10-05 18:39:31 +0000841#define NAND 5
842#define NOR 6
843#define NSEMI 7
844#define NIF 8
845#define NWHILE 9
846#define NUNTIL 10
847#define NFOR 11
848#define NCASE 12
849#define NCLIST 13
850#define NDEFUN 14
851#define NARG 15
852#define NTO 16
Denys Vlasenko7d4aec02017-01-11 14:00:38 +0100853#if BASH_REDIR_OUTPUT
Denis Vlasenko559691a2008-10-05 18:39:31 +0000854#define NTO2 17
855#endif
856#define NCLOBBER 18
857#define NFROM 19
858#define NFROMTO 20
859#define NAPPEND 21
860#define NTOFD 22
861#define NFROMFD 23
862#define NHERE 24
863#define NXHERE 25
864#define NNOT 26
Denis Vlasenko340299a2008-11-21 10:36:36 +0000865#define N_NUMBER 27
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000866
867union node;
868
869struct ncmd {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000870 smallint type; /* Nxxxx */
Denys Vlasenko675d24a2018-01-27 22:02:05 +0100871 int linno;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000872 union node *assign;
873 union node *args;
874 union node *redirect;
875};
876
877struct npipe {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000878 smallint type;
879 smallint pipe_backgnd;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000880 struct nodelist *cmdlist;
881};
882
883struct nredir {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000884 smallint type;
Denys Vlasenko675d24a2018-01-27 22:02:05 +0100885 int linno;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000886 union node *n;
887 union node *redirect;
888};
889
890struct nbinary {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000891 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000892 union node *ch1;
893 union node *ch2;
894};
895
896struct nif {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000897 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000898 union node *test;
899 union node *ifpart;
900 union node *elsepart;
901};
902
903struct nfor {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000904 smallint type;
Denys Vlasenko675d24a2018-01-27 22:02:05 +0100905 int linno;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000906 union node *args;
907 union node *body;
908 char *var;
909};
910
911struct ncase {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000912 smallint type;
Denys Vlasenko675d24a2018-01-27 22:02:05 +0100913 int linno;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000914 union node *expr;
915 union node *cases;
916};
917
918struct nclist {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000919 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000920 union node *next;
921 union node *pattern;
922 union node *body;
923};
924
Denys Vlasenko675d24a2018-01-27 22:02:05 +0100925struct ndefun {
926 smallint type;
927 int linno;
928 char *text;
929 union node *body;
930};
931
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000932struct narg {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000933 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000934 union node *next;
935 char *text;
936 struct nodelist *backquote;
937};
938
Denis Vlasenko559691a2008-10-05 18:39:31 +0000939/* nfile and ndup layout must match!
940 * NTOFD (>&fdnum) uses ndup structure, but we may discover mid-flight
941 * that it is actually NTO2 (>&file), and change its type.
942 */
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000943struct nfile {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000944 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000945 union node *next;
946 int fd;
Denis Vlasenko559691a2008-10-05 18:39:31 +0000947 int _unused_dupfd;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000948 union node *fname;
949 char *expfname;
950};
951
952struct ndup {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000953 smallint type;
Denis Vlasenko559691a2008-10-05 18:39:31 +0000954 union node *next;
955 int fd;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000956 int dupfd;
957 union node *vname;
Denis Vlasenko559691a2008-10-05 18:39:31 +0000958 char *_unused_expfname;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000959};
960
961struct nhere {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000962 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000963 union node *next;
964 int fd;
965 union node *doc;
966};
967
968struct nnot {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000969 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000970 union node *com;
971};
972
973union node {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000974 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000975 struct ncmd ncmd;
976 struct npipe npipe;
977 struct nredir nredir;
978 struct nbinary nbinary;
979 struct nif nif;
980 struct nfor nfor;
981 struct ncase ncase;
982 struct nclist nclist;
Denys Vlasenko675d24a2018-01-27 22:02:05 +0100983 struct ndefun ndefun;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000984 struct narg narg;
985 struct nfile nfile;
986 struct ndup ndup;
987 struct nhere nhere;
988 struct nnot nnot;
989};
990
Denys Vlasenko86e83ec2009-07-23 22:07:07 +0200991/*
992 * NODE_EOF is returned by parsecmd when it encounters an end of file.
993 * It must be distinct from NULL.
994 */
995#define NODE_EOF ((union node *) -1L)
996
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000997struct nodelist {
998 struct nodelist *next;
999 union node *n;
1000};
1001
1002struct funcnode {
1003 int count;
1004 union node n;
1005};
1006
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00001007/*
1008 * Free a parse tree.
1009 */
1010static void
1011freefunc(struct funcnode *f)
1012{
1013 if (f && --f->count < 0)
1014 free(f);
1015}
1016
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001017
1018/* ============ Debugging output */
1019
1020#if DEBUG
1021
1022static FILE *tracefile;
1023
1024static void
1025trace_printf(const char *fmt, ...)
1026{
1027 va_list va;
1028
1029 if (debug != 1)
1030 return;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00001031 if (DEBUG_TIME)
1032 fprintf(tracefile, "%u ", (int) time(NULL));
1033 if (DEBUG_PID)
1034 fprintf(tracefile, "[%u] ", (int) getpid());
1035 if (DEBUG_SIG)
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02001036 fprintf(tracefile, "pending s:%d i:%d(supp:%d) ", pending_sig, pending_int, suppress_int);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001037 va_start(va, fmt);
1038 vfprintf(tracefile, fmt, va);
1039 va_end(va);
1040}
1041
1042static void
1043trace_vprintf(const char *fmt, va_list va)
1044{
1045 if (debug != 1)
1046 return;
1047 vfprintf(tracefile, fmt, va);
Denys Vlasenko474ed062016-10-30 18:30:29 +01001048 fprintf(tracefile, "\n");
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001049}
1050
1051static void
1052trace_puts(const char *s)
1053{
1054 if (debug != 1)
1055 return;
1056 fputs(s, tracefile);
1057}
1058
1059static void
1060trace_puts_quoted(char *s)
1061{
1062 char *p;
1063 char c;
1064
1065 if (debug != 1)
1066 return;
1067 putc('"', tracefile);
1068 for (p = s; *p; p++) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01001069 switch ((unsigned char)*p) {
1070 case '\n': c = 'n'; goto backslash;
1071 case '\t': c = 't'; goto backslash;
1072 case '\r': c = 'r'; goto backslash;
1073 case '\"': c = '\"'; goto backslash;
1074 case '\\': c = '\\'; goto backslash;
1075 case CTLESC: c = 'e'; goto backslash;
1076 case CTLVAR: c = 'v'; goto backslash;
Denys Vlasenkocd716832009-11-28 22:14:02 +01001077 case CTLBACKQ: c = 'q'; goto backslash;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001078 backslash:
1079 putc('\\', tracefile);
1080 putc(c, tracefile);
1081 break;
1082 default:
1083 if (*p >= ' ' && *p <= '~')
1084 putc(*p, tracefile);
1085 else {
1086 putc('\\', tracefile);
Denys Vlasenkocd716832009-11-28 22:14:02 +01001087 putc((*p >> 6) & 03, tracefile);
1088 putc((*p >> 3) & 07, tracefile);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001089 putc(*p & 07, tracefile);
1090 }
1091 break;
1092 }
1093 }
1094 putc('"', tracefile);
1095}
1096
1097static void
1098trace_puts_args(char **ap)
1099{
1100 if (debug != 1)
1101 return;
1102 if (!*ap)
1103 return;
1104 while (1) {
1105 trace_puts_quoted(*ap);
1106 if (!*++ap) {
1107 putc('\n', tracefile);
1108 break;
1109 }
1110 putc(' ', tracefile);
1111 }
1112}
1113
1114static void
1115opentrace(void)
1116{
1117 char s[100];
1118#ifdef O_APPEND
1119 int flags;
1120#endif
1121
1122 if (debug != 1) {
1123 if (tracefile)
1124 fflush(tracefile);
1125 /* leave open because libedit might be using it */
1126 return;
1127 }
1128 strcpy(s, "./trace");
1129 if (tracefile) {
1130 if (!freopen(s, "a", tracefile)) {
1131 fprintf(stderr, "Can't re-open %s\n", s);
1132 debug = 0;
1133 return;
1134 }
1135 } else {
1136 tracefile = fopen(s, "a");
1137 if (tracefile == NULL) {
1138 fprintf(stderr, "Can't open %s\n", s);
1139 debug = 0;
1140 return;
1141 }
1142 }
1143#ifdef O_APPEND
Denis Vlasenkod37f2222007-08-19 13:42:08 +00001144 flags = fcntl(fileno(tracefile), F_GETFL);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001145 if (flags >= 0)
1146 fcntl(fileno(tracefile), F_SETFL, flags | O_APPEND);
1147#endif
1148 setlinebuf(tracefile);
1149 fputs("\nTracing started.\n", tracefile);
1150}
1151
1152static void
1153indent(int amount, char *pfx, FILE *fp)
1154{
1155 int i;
1156
1157 for (i = 0; i < amount; i++) {
1158 if (pfx && i == amount - 1)
1159 fputs(pfx, fp);
1160 putc('\t', fp);
1161 }
1162}
1163
1164/* little circular references here... */
1165static void shtree(union node *n, int ind, char *pfx, FILE *fp);
1166
1167static void
1168sharg(union node *arg, FILE *fp)
1169{
1170 char *p;
1171 struct nodelist *bqlist;
Denys Vlasenkocd716832009-11-28 22:14:02 +01001172 unsigned char subtype;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001173
1174 if (arg->type != NARG) {
1175 out1fmt("<node type %d>\n", arg->type);
1176 abort();
1177 }
1178 bqlist = arg->narg.backquote;
1179 for (p = arg->narg.text; *p; p++) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01001180 switch ((unsigned char)*p) {
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001181 case CTLESC:
Dan Fandrich77d48722010-09-07 23:38:28 -07001182 p++;
1183 putc(*p, fp);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001184 break;
1185 case CTLVAR:
1186 putc('$', fp);
1187 putc('{', fp);
1188 subtype = *++p;
1189 if (subtype == VSLENGTH)
1190 putc('#', fp);
1191
Dan Fandrich77d48722010-09-07 23:38:28 -07001192 while (*p != '=') {
1193 putc(*p, fp);
1194 p++;
1195 }
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001196
1197 if (subtype & VSNUL)
1198 putc(':', fp);
1199
1200 switch (subtype & VSTYPE) {
1201 case VSNORMAL:
1202 putc('}', fp);
1203 break;
1204 case VSMINUS:
1205 putc('-', fp);
1206 break;
1207 case VSPLUS:
1208 putc('+', fp);
1209 break;
1210 case VSQUESTION:
1211 putc('?', fp);
1212 break;
1213 case VSASSIGN:
1214 putc('=', fp);
1215 break;
1216 case VSTRIMLEFT:
1217 putc('#', fp);
1218 break;
1219 case VSTRIMLEFTMAX:
1220 putc('#', fp);
1221 putc('#', fp);
1222 break;
1223 case VSTRIMRIGHT:
1224 putc('%', fp);
1225 break;
1226 case VSTRIMRIGHTMAX:
1227 putc('%', fp);
1228 putc('%', fp);
1229 break;
1230 case VSLENGTH:
1231 break;
1232 default:
1233 out1fmt("<subtype %d>", subtype);
1234 }
1235 break;
1236 case CTLENDVAR:
1237 putc('}', fp);
1238 break;
1239 case CTLBACKQ:
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001240 putc('$', fp);
1241 putc('(', fp);
1242 shtree(bqlist->n, -1, NULL, fp);
1243 putc(')', fp);
1244 break;
1245 default:
1246 putc(*p, fp);
1247 break;
1248 }
1249 }
1250}
1251
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02001252static void
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001253shcmd(union node *cmd, FILE *fp)
1254{
1255 union node *np;
1256 int first;
1257 const char *s;
1258 int dftfd;
1259
1260 first = 1;
1261 for (np = cmd->ncmd.args; np; np = np->narg.next) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +00001262 if (!first)
1263 putc(' ', fp);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001264 sharg(np, fp);
1265 first = 0;
1266 }
1267 for (np = cmd->ncmd.redirect; np; np = np->nfile.next) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +00001268 if (!first)
1269 putc(' ', fp);
1270 dftfd = 0;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001271 switch (np->nfile.type) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +00001272 case NTO: s = ">>"+1; dftfd = 1; break;
1273 case NCLOBBER: s = ">|"; dftfd = 1; break;
1274 case NAPPEND: s = ">>"; dftfd = 1; break;
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01001275#if BASH_REDIR_OUTPUT
Denis Vlasenko559691a2008-10-05 18:39:31 +00001276 case NTO2:
1277#endif
Denis Vlasenko40ba9982007-07-14 00:48:29 +00001278 case NTOFD: s = ">&"; dftfd = 1; break;
Denis Vlasenko559691a2008-10-05 18:39:31 +00001279 case NFROM: s = "<"; break;
Denis Vlasenko40ba9982007-07-14 00:48:29 +00001280 case NFROMFD: s = "<&"; break;
1281 case NFROMTO: s = "<>"; break;
1282 default: s = "*error*"; break;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001283 }
1284 if (np->nfile.fd != dftfd)
1285 fprintf(fp, "%d", np->nfile.fd);
1286 fputs(s, fp);
1287 if (np->nfile.type == NTOFD || np->nfile.type == NFROMFD) {
1288 fprintf(fp, "%d", np->ndup.dupfd);
1289 } else {
1290 sharg(np->nfile.fname, fp);
1291 }
1292 first = 0;
1293 }
1294}
1295
1296static void
1297shtree(union node *n, int ind, char *pfx, FILE *fp)
1298{
1299 struct nodelist *lp;
1300 const char *s;
1301
1302 if (n == NULL)
1303 return;
1304
1305 indent(ind, pfx, fp);
Denys Vlasenko86e83ec2009-07-23 22:07:07 +02001306
1307 if (n == NODE_EOF) {
1308 fputs("<EOF>", fp);
1309 return;
1310 }
1311
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001312 switch (n->type) {
1313 case NSEMI:
1314 s = "; ";
1315 goto binop;
1316 case NAND:
1317 s = " && ";
1318 goto binop;
1319 case NOR:
1320 s = " || ";
1321 binop:
1322 shtree(n->nbinary.ch1, ind, NULL, fp);
1323 /* if (ind < 0) */
1324 fputs(s, fp);
1325 shtree(n->nbinary.ch2, ind, NULL, fp);
1326 break;
1327 case NCMD:
1328 shcmd(n, fp);
1329 if (ind >= 0)
1330 putc('\n', fp);
1331 break;
1332 case NPIPE:
1333 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
Denys Vlasenko7cee00e2009-07-24 01:08:03 +02001334 shtree(lp->n, 0, NULL, fp);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001335 if (lp->next)
1336 fputs(" | ", fp);
1337 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00001338 if (n->npipe.pipe_backgnd)
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001339 fputs(" &", fp);
1340 if (ind >= 0)
1341 putc('\n', fp);
1342 break;
1343 default:
1344 fprintf(fp, "<node type %d>", n->type);
1345 if (ind >= 0)
1346 putc('\n', fp);
1347 break;
1348 }
1349}
1350
1351static void
1352showtree(union node *n)
1353{
1354 trace_puts("showtree called\n");
Denys Vlasenko883cea42009-07-11 15:31:59 +02001355 shtree(n, 1, NULL, stderr);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001356}
1357
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001358#endif /* DEBUG */
1359
1360
Denis Vlasenkob012b102007-02-19 22:43:01 +00001361/* ============ Message printing */
1362
1363static void
1364ash_vmsg(const char *msg, va_list ap)
1365{
1366 fprintf(stderr, "%s: ", arg0);
1367 if (commandname) {
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001368 if (strcmp(arg0, commandname))
1369 fprintf(stderr, "%s: ", commandname);
Denys Vlasenko79b3d422010-06-03 04:29:08 +02001370 if (!iflag || g_parsefile->pf_fd > 0)
Denys Vlasenko675d24a2018-01-27 22:02:05 +01001371 fprintf(stderr, "line %d: ", errlinno);
Eric Andersenc470f442003-07-28 09:56:35 +00001372 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00001373 vfprintf(stderr, msg, ap);
Denys Vlasenko9c541002015-10-07 15:44:36 +02001374 newline_and_flush(stderr);
Eric Andersenc470f442003-07-28 09:56:35 +00001375}
Denis Vlasenkob012b102007-02-19 22:43:01 +00001376
1377/*
1378 * Exverror is called to raise the error exception. If the second argument
1379 * is not NULL then error prints an error message using printf style
1380 * formatting. It then raises the error exception.
1381 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001382static void ash_vmsg_and_raise(int, const char *, va_list) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +00001383static void
1384ash_vmsg_and_raise(int cond, const char *msg, va_list ap)
Eric Andersenc470f442003-07-28 09:56:35 +00001385{
Denis Vlasenkob012b102007-02-19 22:43:01 +00001386#if DEBUG
1387 if (msg) {
Denys Vlasenko474ed062016-10-30 18:30:29 +01001388 TRACE(("ash_vmsg_and_raise(%d):", cond));
Denis Vlasenkob012b102007-02-19 22:43:01 +00001389 TRACEV((msg, ap));
Denis Vlasenkob012b102007-02-19 22:43:01 +00001390 } else
Denys Vlasenko474ed062016-10-30 18:30:29 +01001391 TRACE(("ash_vmsg_and_raise(%d):NULL\n", cond));
Denis Vlasenkob012b102007-02-19 22:43:01 +00001392 if (msg)
1393#endif
1394 ash_vmsg(msg, ap);
1395
1396 flush_stdout_stderr();
1397 raise_exception(cond);
1398 /* NOTREACHED */
Eric Andersenc470f442003-07-28 09:56:35 +00001399}
Denis Vlasenkob012b102007-02-19 22:43:01 +00001400
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001401static void ash_msg_and_raise_error(const char *, ...) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +00001402static void
1403ash_msg_and_raise_error(const char *msg, ...)
1404{
1405 va_list ap;
1406
Ron Yorstonea7d2f62017-01-03 11:18:23 +01001407 exitstatus = 2;
1408
Denis Vlasenkob012b102007-02-19 22:43:01 +00001409 va_start(ap, msg);
1410 ash_vmsg_and_raise(EXERROR, msg, ap);
1411 /* NOTREACHED */
1412 va_end(ap);
1413}
1414
Ron Yorstonbe366e52017-07-27 13:53:39 +01001415/*
Ron Yorstonbe366e52017-07-27 13:53:39 +01001416 * 'fmt' must be a string literal.
1417 */
Denys Vlasenko6f97b302017-09-29 18:17:25 +02001418#define ash_msg_and_raise_perror(fmt, ...) ash_msg_and_raise_error(fmt ": "STRERROR_FMT, ##__VA_ARGS__ STRERROR_ERRNO)
Ron Yorstonbe366e52017-07-27 13:53:39 +01001419
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00001420static void raise_error_syntax(const char *) NORETURN;
1421static void
1422raise_error_syntax(const char *msg)
1423{
Denys Vlasenko675d24a2018-01-27 22:02:05 +01001424 errlinno = g_parsefile->linno;
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00001425 ash_msg_and_raise_error("syntax error: %s", msg);
1426 /* NOTREACHED */
1427}
1428
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001429static void ash_msg_and_raise(int, const char *, ...) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +00001430static void
1431ash_msg_and_raise(int cond, const char *msg, ...)
1432{
1433 va_list ap;
1434
1435 va_start(ap, msg);
1436 ash_vmsg_and_raise(cond, msg, ap);
1437 /* NOTREACHED */
1438 va_end(ap);
1439}
1440
1441/*
1442 * error/warning routines for external builtins
1443 */
1444static void
1445ash_msg(const char *fmt, ...)
1446{
1447 va_list ap;
1448
1449 va_start(ap, fmt);
1450 ash_vmsg(fmt, ap);
1451 va_end(ap);
1452}
1453
1454/*
1455 * Return a string describing an error. The returned string may be a
1456 * pointer to a static buffer that will be overwritten on the next call.
1457 * Action describes the operation that got the error.
1458 */
1459static const char *
1460errmsg(int e, const char *em)
1461{
1462 if (e == ENOENT || e == ENOTDIR) {
1463 return em;
1464 }
1465 return strerror(e);
1466}
1467
1468
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001469/* ============ Memory allocation */
1470
Denys Vlasenkoe7670ff2009-10-11 00:45:25 +02001471#if 0
1472/* I consider these wrappers nearly useless:
1473 * ok, they return you to nearest exception handler, but
1474 * how much memory do you leak in the process, making
1475 * memory starvation worse?
1476 */
1477static void *
1478ckrealloc(void * p, size_t nbytes)
1479{
1480 p = realloc(p, nbytes);
1481 if (!p)
1482 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1483 return p;
1484}
1485
1486static void *
1487ckmalloc(size_t nbytes)
1488{
1489 return ckrealloc(NULL, nbytes);
1490}
1491
1492static void *
1493ckzalloc(size_t nbytes)
1494{
1495 return memset(ckmalloc(nbytes), 0, nbytes);
1496}
1497
1498static char *
1499ckstrdup(const char *s)
1500{
1501 char *p = strdup(s);
1502 if (!p)
1503 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1504 return p;
1505}
1506#else
1507/* Using bbox equivalents. They exit if out of memory */
1508# define ckrealloc xrealloc
1509# define ckmalloc xmalloc
1510# define ckzalloc xzalloc
1511# define ckstrdup xstrdup
1512#endif
1513
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001514/*
1515 * It appears that grabstackstr() will barf with such alignments
1516 * because stalloc() will return a string allocated in a new stackblock.
1517 */
1518#define SHELL_ALIGN(nbytes) (((nbytes) + SHELL_SIZE) & ~SHELL_SIZE)
1519enum {
1520 /* Most machines require the value returned from malloc to be aligned
1521 * in some way. The following macro will get this right
1522 * on many machines. */
Denys Vlasenko0e5e4ea2009-10-11 00:36:20 +02001523 SHELL_SIZE = sizeof(union { int i; char *cp; double d; }) - 1,
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001524 /* Minimum size of a block */
Denis Vlasenko01631112007-12-16 17:20:38 +00001525 MINSIZE = SHELL_ALIGN(504),
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001526};
1527
1528struct stack_block {
1529 struct stack_block *prev;
1530 char space[MINSIZE];
1531};
1532
1533struct stackmark {
1534 struct stack_block *stackp;
1535 char *stacknxt;
1536 size_t stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001537};
1538
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001539
Denis Vlasenko01631112007-12-16 17:20:38 +00001540struct globals_memstack {
1541 struct stack_block *g_stackp; // = &stackbase;
Denis Vlasenko01631112007-12-16 17:20:38 +00001542 char *g_stacknxt; // = stackbase.space;
1543 char *sstrend; // = stackbase.space + MINSIZE;
1544 size_t g_stacknleft; // = MINSIZE;
Denis Vlasenko01631112007-12-16 17:20:38 +00001545 struct stack_block stackbase;
1546};
Denys Vlasenko6f9442f2018-01-28 20:41:23 +01001547extern struct globals_memstack *BB_GLOBAL_CONST ash_ptr_to_globals_memstack;
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001548#define G_memstack (*ash_ptr_to_globals_memstack)
Denis Vlasenko01631112007-12-16 17:20:38 +00001549#define g_stackp (G_memstack.g_stackp )
Denis Vlasenko01631112007-12-16 17:20:38 +00001550#define g_stacknxt (G_memstack.g_stacknxt )
1551#define sstrend (G_memstack.sstrend )
1552#define g_stacknleft (G_memstack.g_stacknleft)
Denis Vlasenko01631112007-12-16 17:20:38 +00001553#define stackbase (G_memstack.stackbase )
1554#define INIT_G_memstack() do { \
Denys Vlasenkoaf7169b2019-10-25 12:12:22 +02001555 (*(struct globals_memstack**)not_const_pp(&ash_ptr_to_globals_memstack)) = xzalloc(sizeof(G_memstack)); \
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001556 barrier(); \
Denis Vlasenko01631112007-12-16 17:20:38 +00001557 g_stackp = &stackbase; \
1558 g_stacknxt = stackbase.space; \
1559 g_stacknleft = MINSIZE; \
1560 sstrend = stackbase.space + MINSIZE; \
Denis Vlasenko01631112007-12-16 17:20:38 +00001561} while (0)
1562
Denys Vlasenkoe7670ff2009-10-11 00:45:25 +02001563
Denis Vlasenko01631112007-12-16 17:20:38 +00001564#define stackblock() ((void *)g_stacknxt)
1565#define stackblocksize() g_stacknleft
1566
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001567/*
1568 * Parse trees for commands are allocated in lifo order, so we use a stack
1569 * to make this more efficient, and also to avoid all sorts of exception
1570 * handling code to handle interrupts in the middle of a parse.
1571 *
1572 * The size 504 was chosen because the Ultrix malloc handles that size
1573 * well.
1574 */
1575static void *
1576stalloc(size_t nbytes)
1577{
1578 char *p;
1579 size_t aligned;
1580
1581 aligned = SHELL_ALIGN(nbytes);
Denis Vlasenko01631112007-12-16 17:20:38 +00001582 if (aligned > g_stacknleft) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001583 size_t len;
1584 size_t blocksize;
1585 struct stack_block *sp;
1586
1587 blocksize = aligned;
1588 if (blocksize < MINSIZE)
1589 blocksize = MINSIZE;
1590 len = sizeof(struct stack_block) - MINSIZE + blocksize;
1591 if (len < blocksize)
1592 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1593 INT_OFF;
1594 sp = ckmalloc(len);
Denis Vlasenko01631112007-12-16 17:20:38 +00001595 sp->prev = g_stackp;
1596 g_stacknxt = sp->space;
1597 g_stacknleft = blocksize;
1598 sstrend = g_stacknxt + blocksize;
1599 g_stackp = sp;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001600 INT_ON;
1601 }
Denis Vlasenko01631112007-12-16 17:20:38 +00001602 p = g_stacknxt;
1603 g_stacknxt += aligned;
1604 g_stacknleft -= aligned;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001605 return p;
1606}
1607
Denis Vlasenko597906c2008-02-20 16:38:54 +00001608static void *
1609stzalloc(size_t nbytes)
1610{
1611 return memset(stalloc(nbytes), 0, nbytes);
1612}
1613
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001614static void
1615stunalloc(void *p)
1616{
1617#if DEBUG
Denis Vlasenko01631112007-12-16 17:20:38 +00001618 if (!p || (g_stacknxt < (char *)p) || ((char *)p < g_stackp->space)) {
Bernhard Reutner-Fischer5e25ddb2008-05-19 09:48:17 +00001619 write(STDERR_FILENO, "stunalloc\n", 10);
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001620 abort();
1621 }
1622#endif
Denis Vlasenko01631112007-12-16 17:20:38 +00001623 g_stacknleft += g_stacknxt - (char *)p;
1624 g_stacknxt = p;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001625}
1626
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001627/*
1628 * Like strdup but works with the ash stack.
1629 */
1630static char *
Denys Vlasenko8e2bc472016-09-28 23:02:57 +02001631sstrdup(const char *p)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001632{
1633 size_t len = strlen(p) + 1;
1634 return memcpy(stalloc(len), p, len);
1635}
1636
Denys Vlasenko03c36e02018-01-10 15:18:35 +01001637static ALWAYS_INLINE void
Denys Vlasenko60ca8342016-09-30 11:21:21 +02001638grabstackblock(size_t len)
1639{
Denys Vlasenkoa318bba2016-10-26 18:26:27 +02001640 stalloc(len);
Denys Vlasenko60ca8342016-09-30 11:21:21 +02001641}
1642
1643static void
1644pushstackmark(struct stackmark *mark, size_t len)
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001645{
Denis Vlasenko01631112007-12-16 17:20:38 +00001646 mark->stackp = g_stackp;
1647 mark->stacknxt = g_stacknxt;
1648 mark->stacknleft = g_stacknleft;
Denys Vlasenko60ca8342016-09-30 11:21:21 +02001649 grabstackblock(len);
1650}
1651
1652static void
1653setstackmark(struct stackmark *mark)
1654{
1655 pushstackmark(mark, g_stacknxt == g_stackp->space && g_stackp != &stackbase);
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001656}
1657
1658static void
1659popstackmark(struct stackmark *mark)
1660{
1661 struct stack_block *sp;
1662
Denis Vlasenko93ebd4f2007-03-13 20:55:36 +00001663 if (!mark->stackp)
1664 return;
1665
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001666 INT_OFF;
Denis Vlasenko01631112007-12-16 17:20:38 +00001667 while (g_stackp != mark->stackp) {
1668 sp = g_stackp;
1669 g_stackp = sp->prev;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001670 free(sp);
1671 }
Denis Vlasenko01631112007-12-16 17:20:38 +00001672 g_stacknxt = mark->stacknxt;
1673 g_stacknleft = mark->stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001674 sstrend = mark->stacknxt + mark->stacknleft;
1675 INT_ON;
1676}
1677
1678/*
1679 * When the parser reads in a string, it wants to stick the string on the
1680 * stack and only adjust the stack pointer when it knows how big the
1681 * string is. Stackblock (defined in stack.h) returns a pointer to a block
1682 * of space on top of the stack and stackblocklen returns the length of
1683 * this block. Growstackblock will grow this space by at least one byte,
1684 * possibly moving it (like realloc). Grabstackblock actually allocates the
1685 * part of the block that has been used.
1686 */
1687static void
Denys Vlasenkoda2e46d2020-02-21 15:25:37 +01001688growstackblock(size_t min)
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001689{
1690 size_t newlen;
1691
Denis Vlasenko01631112007-12-16 17:20:38 +00001692 newlen = g_stacknleft * 2;
1693 if (newlen < g_stacknleft)
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001694 ash_msg_and_raise_error(bb_msg_memory_exhausted);
Denys Vlasenkoda2e46d2020-02-21 15:25:37 +01001695 min = SHELL_ALIGN(min | 128);
1696 if (newlen < min)
1697 newlen += min;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001698
Denis Vlasenko01631112007-12-16 17:20:38 +00001699 if (g_stacknxt == g_stackp->space && g_stackp != &stackbase) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001700 struct stack_block *sp;
1701 struct stack_block *prevstackp;
1702 size_t grosslen;
1703
1704 INT_OFF;
Denis Vlasenko01631112007-12-16 17:20:38 +00001705 sp = g_stackp;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001706 prevstackp = sp->prev;
1707 grosslen = newlen + sizeof(struct stack_block) - MINSIZE;
1708 sp = ckrealloc(sp, grosslen);
1709 sp->prev = prevstackp;
Denis Vlasenko01631112007-12-16 17:20:38 +00001710 g_stackp = sp;
1711 g_stacknxt = sp->space;
1712 g_stacknleft = newlen;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001713 sstrend = sp->space + newlen;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001714 INT_ON;
1715 } else {
Denis Vlasenko01631112007-12-16 17:20:38 +00001716 char *oldspace = g_stacknxt;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001717 size_t oldlen = g_stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001718 char *p = stalloc(newlen);
1719
1720 /* free the space we just allocated */
Denis Vlasenko01631112007-12-16 17:20:38 +00001721 g_stacknxt = memcpy(p, oldspace, oldlen);
1722 g_stacknleft += newlen;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001723 }
1724}
1725
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001726/*
1727 * The following routines are somewhat easier to use than the above.
1728 * The user declares a variable of type STACKSTR, which may be declared
1729 * to be a register. The macro STARTSTACKSTR initializes things. Then
1730 * the user uses the macro STPUTC to add characters to the string. In
1731 * effect, STPUTC(c, p) is the same as *p++ = c except that the stack is
1732 * grown as necessary. When the user is done, she can just leave the
1733 * string there and refer to it using stackblock(). Or she can allocate
1734 * the space for it using grabstackstr(). If it is necessary to allow
1735 * someone else to use the stack temporarily and then continue to grow
1736 * the string, the user should use grabstack to allocate the space, and
1737 * then call ungrabstr(p) to return to the previous mode of operation.
1738 *
1739 * USTPUTC is like STPUTC except that it doesn't check for overflow.
1740 * CHECKSTACKSPACE can be called before USTPUTC to ensure that there
1741 * is space for at least one character.
1742 */
1743static void *
1744growstackstr(void)
1745{
1746 size_t len = stackblocksize();
Denys Vlasenkoda2e46d2020-02-21 15:25:37 +01001747 growstackblock(0);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001748 return (char *)stackblock() + len;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001749}
1750
Denys Vlasenkoc55847f2020-02-17 15:59:08 +01001751static char *
1752growstackto(size_t len)
1753{
Denys Vlasenkoda2e46d2020-02-21 15:25:37 +01001754 if (stackblocksize() < len)
1755 growstackblock(len);
Denys Vlasenkoc55847f2020-02-17 15:59:08 +01001756 return stackblock();
1757}
1758
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001759/*
1760 * Called from CHECKSTRSPACE.
1761 */
1762static char *
1763makestrspace(size_t newlen, char *p)
1764{
Denis Vlasenko01631112007-12-16 17:20:38 +00001765 size_t len = p - g_stacknxt;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001766
Denys Vlasenkoc55847f2020-02-17 15:59:08 +01001767 return growstackto(len + newlen) + len;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001768}
1769
1770static char *
Denys Vlasenko538ee412020-02-22 19:11:41 +01001771stnputs(const char *s, size_t n, char *p)
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001772{
1773 p = makestrspace(n, p);
Denys Vlasenko5ace96a2017-07-23 21:46:02 +02001774 p = (char *)mempcpy(p, s, n);
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001775 return p;
1776}
1777
1778static char *
1779stack_putstr(const char *s, char *p)
1780{
Denys Vlasenko538ee412020-02-22 19:11:41 +01001781 return stnputs(s, strlen(s), p);
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001782}
1783
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001784static char *
1785_STPUTC(int c, char *p)
1786{
1787 if (p == sstrend)
1788 p = growstackstr();
1789 *p++ = c;
1790 return p;
1791}
1792
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001793#define STARTSTACKSTR(p) ((p) = stackblock())
1794#define STPUTC(c, p) ((p) = _STPUTC((c), (p)))
Denis Vlasenko843cbd52008-06-27 00:23:18 +00001795#define CHECKSTRSPACE(n, p) do { \
1796 char *q = (p); \
1797 size_t l = (n); \
1798 size_t m = sstrend - q; \
1799 if (l > m) \
1800 (p) = makestrspace(l, q); \
1801} while (0)
Denis Vlasenkoef527f52008-06-23 01:52:30 +00001802#define USTPUTC(c, p) (*(p)++ = (c))
Denis Vlasenko843cbd52008-06-27 00:23:18 +00001803#define STACKSTRNUL(p) do { \
1804 if ((p) == sstrend) \
1805 (p) = growstackstr(); \
1806 *(p) = '\0'; \
1807} while (0)
Denis Vlasenkoef527f52008-06-23 01:52:30 +00001808#define STUNPUTC(p) (--(p))
1809#define STTOPC(p) ((p)[-1])
1810#define STADJUST(amount, p) ((p) += (amount))
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001811
1812#define grabstackstr(p) stalloc((char *)(p) - (char *)stackblock())
Denis Vlasenkoef527f52008-06-23 01:52:30 +00001813#define ungrabstackstr(s, p) stunalloc(s)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001814#define stackstrend() ((void *)sstrend)
1815
1816
1817/* ============ String helpers */
1818
1819/*
1820 * prefix -- see if pfx is a prefix of string.
1821 */
1822static char *
1823prefix(const char *string, const char *pfx)
1824{
1825 while (*pfx) {
1826 if (*pfx++ != *string++)
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00001827 return NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001828 }
1829 return (char *) string;
1830}
1831
1832/*
1833 * Check for a valid number. This should be elsewhere.
1834 */
1835static int
1836is_number(const char *p)
1837{
1838 do {
1839 if (!isdigit(*p))
1840 return 0;
1841 } while (*++p != '\0');
1842 return 1;
1843}
1844
1845/*
1846 * Convert a string of digits to an integer, printing an error message on
1847 * failure.
1848 */
1849static int
1850number(const char *s)
1851{
1852 if (!is_number(s))
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02001853 ash_msg_and_raise_error(msg_illnum, s);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001854 return atoi(s);
1855}
1856
1857/*
Denys Vlasenko42ba7572017-07-21 13:20:14 +02001858 * Produce a single quoted string suitable as input to the shell.
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001859 * The return string is allocated on the stack.
1860 */
1861static char *
1862single_quote(const char *s)
1863{
1864 char *p;
1865
1866 STARTSTACKSTR(p);
1867
1868 do {
1869 char *q;
1870 size_t len;
1871
1872 len = strchrnul(s, '\'') - s;
1873
1874 q = p = makestrspace(len + 3, p);
1875
1876 *q++ = '\'';
Denys Vlasenko94af83e2017-07-23 21:55:40 +02001877 q = (char *)mempcpy(q, s, len);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001878 *q++ = '\'';
1879 s += len;
1880
1881 STADJUST(q - p, p);
1882
Denys Vlasenkocd716832009-11-28 22:14:02 +01001883 if (*s != '\'')
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001884 break;
Denys Vlasenkocd716832009-11-28 22:14:02 +01001885 len = 0;
1886 do len++; while (*++s == '\'');
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001887
1888 q = p = makestrspace(len + 3, p);
1889
1890 *q++ = '"';
Denys Vlasenko5ace96a2017-07-23 21:46:02 +02001891 q = (char *)mempcpy(q, s - len, len);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001892 *q++ = '"';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001893
1894 STADJUST(q - p, p);
1895 } while (*s);
1896
Denys Vlasenkocd716832009-11-28 22:14:02 +01001897 USTPUTC('\0', p);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001898
1899 return stackblock();
1900}
1901
Denys Vlasenko42ba7572017-07-21 13:20:14 +02001902/*
1903 * Produce a possibly single quoted string suitable as input to the shell.
Denys Vlasenko42ba7572017-07-21 13:20:14 +02001904 * If quoting was done, the return string is allocated on the stack,
1905 * otherwise a pointer to the original string is returned.
1906 */
1907static const char *
1908maybe_single_quote(const char *s)
1909{
1910 const char *p = s;
1911
1912 while (*p) {
1913 /* Assuming ACSII */
1914 /* quote ctrl_chars space !"#$%&'()* */
1915 if (*p < '+')
1916 goto need_quoting;
1917 /* quote ;<=>? */
1918 if (*p >= ';' && *p <= '?')
1919 goto need_quoting;
1920 /* quote `[\ */
1921 if (*p == '`')
1922 goto need_quoting;
1923 if (*p == '[')
1924 goto need_quoting;
1925 if (*p == '\\')
1926 goto need_quoting;
1927 /* quote {|}~ DEL and high bytes */
1928 if (*p > 'z')
1929 goto need_quoting;
1930 /* Not quoting these: +,-./ 0-9 :@ A-Z ]^_ a-z */
1931 /* TODO: maybe avoid quoting % */
1932 p++;
1933 }
1934 return s;
1935
1936 need_quoting:
1937 return single_quote(s);
1938}
1939
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001940
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00001941/* ============ nextopt */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001942
1943static char **argptr; /* argument list for builtin commands */
1944static char *optionarg; /* set by nextopt (like getopt) */
1945static char *optptr; /* used by nextopt */
1946
1947/*
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001948 * XXX - should get rid of. Have all builtins use getopt(3).
1949 * The library getopt must have the BSD extension static variable
1950 * "optreset", otherwise it can't be used within the shell safely.
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001951 *
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001952 * Standard option processing (a la getopt) for builtin routines.
1953 * The only argument that is passed to nextopt is the option string;
1954 * the other arguments are unnecessary. It returns the character,
1955 * or '\0' on end of input.
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001956 */
1957static int
1958nextopt(const char *optstring)
1959{
1960 char *p;
1961 const char *q;
1962 char c;
1963
1964 p = optptr;
1965 if (p == NULL || *p == '\0') {
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001966 /* We ate entire "-param", take next one */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001967 p = *argptr;
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001968 if (p == NULL)
1969 return '\0';
1970 if (*p != '-')
1971 return '\0';
1972 if (*++p == '\0') /* just "-" ? */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001973 return '\0';
1974 argptr++;
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001975 if (LONE_DASH(p)) /* "--" ? */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001976 return '\0';
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001977 /* p => next "-param" */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001978 }
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001979 /* p => some option char in the middle of a "-param" */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001980 c = *p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00001981 for (q = optstring; *q != c;) {
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001982 if (*q == '\0')
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001983 ash_msg_and_raise_error("illegal option -%c", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001984 if (*++q == ':')
1985 q++;
1986 }
1987 if (*++q == ':') {
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001988 if (*p == '\0') {
1989 p = *argptr++;
1990 if (p == NULL)
1991 ash_msg_and_raise_error("no arg for -%c option", c);
1992 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001993 optionarg = p;
1994 p = NULL;
1995 }
1996 optptr = p;
1997 return c;
1998}
1999
2000
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00002001/* ============ Shell variables */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002002
Denis Vlasenko01631112007-12-16 17:20:38 +00002003struct shparam {
2004 int nparam; /* # of positional parameters (without $0) */
2005#if ENABLE_ASH_GETOPTS
2006 int optind; /* next parameter to be processed by getopts */
2007 int optoff; /* used by getopts */
2008#endif
2009 unsigned char malloced; /* if parameter list dynamically allocated */
2010 char **p; /* parameter list */
2011};
2012
2013/*
2014 * Free the list of positional parameters.
2015 */
2016static void
2017freeparam(volatile struct shparam *param)
2018{
Denis Vlasenko01631112007-12-16 17:20:38 +00002019 if (param->malloced) {
Denis Vlasenko3177ba02008-07-13 20:39:23 +00002020 char **ap, **ap1;
2021 ap = ap1 = param->p;
2022 while (*ap)
2023 free(*ap++);
2024 free(ap1);
Denis Vlasenko01631112007-12-16 17:20:38 +00002025 }
2026}
2027
2028#if ENABLE_ASH_GETOPTS
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02002029static void FAST_FUNC getoptsreset(const char *value);
Denis Vlasenko01631112007-12-16 17:20:38 +00002030#endif
2031
2032struct var {
2033 struct var *next; /* next entry in hash list */
2034 int flags; /* flags are defined above */
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002035 const char *var_text; /* name=value */
2036 void (*var_func)(const char *) FAST_FUNC; /* function to be called when */
Denis Vlasenko01631112007-12-16 17:20:38 +00002037 /* the variable gets set/unset */
2038};
2039
2040struct localvar {
2041 struct localvar *next; /* next local variable in list */
2042 struct var *vp; /* the variable that was made local */
2043 int flags; /* saved flags */
2044 const char *text; /* saved text */
2045};
2046
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002047/* flags */
2048#define VEXPORT 0x01 /* variable is exported */
2049#define VREADONLY 0x02 /* variable cannot be modified */
2050#define VSTRFIXED 0x04 /* variable struct is statically allocated */
2051#define VTEXTFIXED 0x08 /* text is statically allocated */
2052#define VSTACK 0x10 /* text is allocated on the stack */
2053#define VUNSET 0x20 /* the variable is not set */
2054#define VNOFUNC 0x40 /* don't call the callback function */
2055#define VNOSET 0x80 /* do not set variable - just readonly test */
2056#define VNOSAVE 0x100 /* when text is on the heap before setvareq */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00002057#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002058# define VDYNAMIC 0x200 /* dynamic variable */
2059#else
2060# define VDYNAMIC 0
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002061#endif
2062
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00002063
Denis Vlasenko01631112007-12-16 17:20:38 +00002064/* Need to be before varinit_data[] */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00002065#if ENABLE_LOCALE_SUPPORT
Denys Vlasenko2634bf32009-06-09 18:40:07 +02002066static void FAST_FUNC
Denis Vlasenkoa8915072007-02-23 21:10:06 +00002067change_lc_all(const char *value)
2068{
2069 if (value && *value != '\0')
2070 setlocale(LC_ALL, value);
2071}
Denys Vlasenko2634bf32009-06-09 18:40:07 +02002072static void FAST_FUNC
Denis Vlasenkoa8915072007-02-23 21:10:06 +00002073change_lc_ctype(const char *value)
2074{
2075 if (value && *value != '\0')
2076 setlocale(LC_CTYPE, value);
2077}
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00002078#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002079#if ENABLE_ASH_MAIL
2080static void chkmail(void);
Denys Vlasenko8c52f802011-02-04 17:36:21 +01002081static void changemail(const char *var_value) FAST_FUNC;
2082#else
2083# define chkmail() ((void)0)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002084#endif
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02002085static void changepath(const char *) FAST_FUNC;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002086#if ENABLE_ASH_RANDOM_SUPPORT
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02002087static void change_random(const char *) FAST_FUNC;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002088#endif
Ron Yorston1d371862019-04-15 10:52:05 +01002089#if BASH_EPOCH_VARS
2090static void change_seconds(const char *) FAST_FUNC;
2091static void change_realtime(const char *) FAST_FUNC;
2092#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002093
Denis Vlasenko01631112007-12-16 17:20:38 +00002094static const struct {
2095 int flags;
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002096 const char *var_text;
2097 void (*var_func)(const char *) FAST_FUNC;
Denys Vlasenko965b7952020-11-30 13:03:03 +01002098} varinit_data[] ALIGN_PTR = {
Denys Vlasenko566a3132012-07-07 21:40:35 +02002099 /*
2100 * Note: VEXPORT would not work correctly here for NOFORK applets:
2101 * some environment strings may be constant.
2102 */
Denis Vlasenko01631112007-12-16 17:20:38 +00002103 { VSTRFIXED|VTEXTFIXED , defifsvar , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002104#if ENABLE_ASH_MAIL
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002105 { VSTRFIXED|VTEXTFIXED|VUNSET, "MAIL" , changemail },
2106 { VSTRFIXED|VTEXTFIXED|VUNSET, "MAILPATH" , changemail },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002107#endif
Denis Vlasenko01631112007-12-16 17:20:38 +00002108 { VSTRFIXED|VTEXTFIXED , bb_PATH_root_path, changepath },
2109 { VSTRFIXED|VTEXTFIXED , "PS1=$ " , NULL },
2110 { VSTRFIXED|VTEXTFIXED , "PS2=> " , NULL },
2111 { VSTRFIXED|VTEXTFIXED , "PS4=+ " , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002112#if ENABLE_ASH_GETOPTS
Denys Vlasenkoe627ac92016-09-30 14:36:59 +02002113 { VSTRFIXED|VTEXTFIXED , defoptindvar, getoptsreset },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002114#endif
Denys Vlasenko675d24a2018-01-27 22:02:05 +01002115 { VSTRFIXED|VTEXTFIXED , NULL /* inited to linenovar */, NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002116#if ENABLE_ASH_RANDOM_SUPPORT
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002117 { VSTRFIXED|VTEXTFIXED|VUNSET|VDYNAMIC, "RANDOM", change_random },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002118#endif
Ron Yorston1d371862019-04-15 10:52:05 +01002119#if BASH_EPOCH_VARS
2120 { VSTRFIXED|VTEXTFIXED|VUNSET|VDYNAMIC, "EPOCHSECONDS", change_seconds },
2121 { VSTRFIXED|VTEXTFIXED|VUNSET|VDYNAMIC, "EPOCHREALTIME", change_realtime },
2122#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002123#if ENABLE_LOCALE_SUPPORT
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002124 { VSTRFIXED|VTEXTFIXED|VUNSET, "LC_ALL" , change_lc_all },
2125 { VSTRFIXED|VTEXTFIXED|VUNSET, "LC_CTYPE" , change_lc_ctype },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002126#endif
2127#if ENABLE_FEATURE_EDITING_SAVEHISTORY
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002128 { VSTRFIXED|VTEXTFIXED|VUNSET, "HISTFILE" , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002129#endif
2130};
2131
Denis Vlasenko0b769642008-07-24 07:54:57 +00002132struct redirtab;
Denis Vlasenko01631112007-12-16 17:20:38 +00002133
2134struct globals_var {
2135 struct shparam shellparam; /* $@ current positional parameters */
2136 struct redirtab *redirlist;
Denys Vlasenkod07a15b2017-07-30 16:51:05 +02002137 int preverrout_fd; /* stderr fd: usually 2, unless redirect moved it */
Denis Vlasenko01631112007-12-16 17:20:38 +00002138 struct var *vartab[VTABSIZE];
2139 struct var varinit[ARRAY_SIZE(varinit_data)];
Denys Vlasenko675d24a2018-01-27 22:02:05 +01002140 int lineno;
2141 char linenovar[sizeof("LINENO=") + sizeof(int)*3];
Denis Vlasenko01631112007-12-16 17:20:38 +00002142};
Denys Vlasenko6f9442f2018-01-28 20:41:23 +01002143extern struct globals_var *BB_GLOBAL_CONST ash_ptr_to_globals_var;
Denis Vlasenko574f2f42008-02-27 18:41:59 +00002144#define G_var (*ash_ptr_to_globals_var)
Denis Vlasenko01631112007-12-16 17:20:38 +00002145#define shellparam (G_var.shellparam )
Denis Vlasenko0b769642008-07-24 07:54:57 +00002146//#define redirlist (G_var.redirlist )
Denis Vlasenko01631112007-12-16 17:20:38 +00002147#define preverrout_fd (G_var.preverrout_fd)
2148#define vartab (G_var.vartab )
2149#define varinit (G_var.varinit )
Denys Vlasenko675d24a2018-01-27 22:02:05 +01002150#define lineno (G_var.lineno )
2151#define linenovar (G_var.linenovar )
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00002152#define vifs varinit[0]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002153#if ENABLE_ASH_MAIL
Ron Yorston1d371862019-04-15 10:52:05 +01002154# define vmail varinit[1]
2155# define vmpath varinit[2]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002156#endif
Ron Yorston1d371862019-04-15 10:52:05 +01002157#define VAR_OFFSET1 (ENABLE_ASH_MAIL*2)
2158#define vpath varinit[VAR_OFFSET1 + 1]
2159#define vps1 varinit[VAR_OFFSET1 + 2]
2160#define vps2 varinit[VAR_OFFSET1 + 3]
2161#define vps4 varinit[VAR_OFFSET1 + 4]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002162#if ENABLE_ASH_GETOPTS
Ron Yorston1d371862019-04-15 10:52:05 +01002163# define voptind varinit[VAR_OFFSET1 + 5]
2164#endif
2165#define VAR_OFFSET2 (VAR_OFFSET1 + ENABLE_ASH_GETOPTS)
2166#define vlineno varinit[VAR_OFFSET2 + 5]
2167#if ENABLE_ASH_RANDOM_SUPPORT
2168# define vrandom varinit[VAR_OFFSET2 + 6]
2169#endif
2170#define VAR_OFFSET3 (VAR_OFFSET2 + ENABLE_ASH_RANDOM_SUPPORT)
2171#if BASH_EPOCH_VARS
2172# define vepochs varinit[VAR_OFFSET3 + 6]
2173# define vepochr varinit[VAR_OFFSET3 + 7]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002174#endif
Denys Vlasenko675d24a2018-01-27 22:02:05 +01002175#define INIT_G_var() do { \
2176 unsigned i; \
Denys Vlasenkoaf7169b2019-10-25 12:12:22 +02002177 (*(struct globals_var**)not_const_pp(&ash_ptr_to_globals_var)) = xzalloc(sizeof(G_var)); \
Denys Vlasenko675d24a2018-01-27 22:02:05 +01002178 barrier(); \
2179 for (i = 0; i < ARRAY_SIZE(varinit_data); i++) { \
2180 varinit[i].flags = varinit_data[i].flags; \
2181 varinit[i].var_text = varinit_data[i].var_text; \
2182 varinit[i].var_func = varinit_data[i].var_func; \
2183 } \
2184 strcpy(linenovar, "LINENO="); \
2185 vlineno.var_text = linenovar; \
2186} while (0)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002187
2188/*
2189 * The following macros access the values of the above variables.
2190 * They have to skip over the name. They return the null string
2191 * for unset variables.
2192 */
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002193#define ifsval() (vifs.var_text + 4)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002194#define ifsset() ((vifs.flags & VUNSET) == 0)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00002195#if ENABLE_ASH_MAIL
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002196# define mailval() (vmail.var_text + 5)
2197# define mpathval() (vmpath.var_text + 9)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00002198# define mpathset() ((vmpath.flags & VUNSET) == 0)
2199#endif
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002200#define pathval() (vpath.var_text + 5)
2201#define ps1val() (vps1.var_text + 4)
2202#define ps2val() (vps2.var_text + 4)
2203#define ps4val() (vps4.var_text + 4)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00002204#if ENABLE_ASH_GETOPTS
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002205# define optindval() (voptind.var_text + 7)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00002206#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002207
Denis Vlasenko01631112007-12-16 17:20:38 +00002208#if ENABLE_ASH_GETOPTS
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02002209static void FAST_FUNC
Denis Vlasenko01631112007-12-16 17:20:38 +00002210getoptsreset(const char *value)
2211{
Denys Vlasenko46289452017-08-11 00:59:36 +02002212 shellparam.optind = 1;
2213 if (is_number(value))
2214 shellparam.optind = number(value) ?: 1;
Denis Vlasenko01631112007-12-16 17:20:38 +00002215 shellparam.optoff = -1;
2216}
2217#endif
2218
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002219/*
2220 * Compares two strings up to the first = or '\0'. The first
2221 * string must be terminated by '='; the second may be terminated by
2222 * either '=' or '\0'.
2223 */
2224static int
2225varcmp(const char *p, const char *q)
2226{
2227 int c, d;
2228
2229 while ((c = *p) == (d = *q)) {
Denys Vlasenko0a0acb52015-04-18 19:36:38 +02002230 if (c == '\0' || c == '=')
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002231 goto out;
2232 p++;
2233 q++;
2234 }
2235 if (c == '=')
Denis Vlasenko9650f362007-02-23 01:04:37 +00002236 c = '\0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002237 if (d == '=')
Denis Vlasenko9650f362007-02-23 01:04:37 +00002238 d = '\0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002239 out:
2240 return c - d;
2241}
2242
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002243/*
2244 * Find the appropriate entry in the hash table from the name.
2245 */
2246static struct var **
2247hashvar(const char *p)
2248{
2249 unsigned hashval;
2250
2251 hashval = ((unsigned char) *p) << 4;
2252 while (*p && *p != '=')
2253 hashval += (unsigned char) *p++;
2254 return &vartab[hashval % VTABSIZE];
2255}
2256
2257static int
2258vpcmp(const void *a, const void *b)
2259{
2260 return varcmp(*(const char **)a, *(const char **)b);
2261}
2262
2263/*
2264 * This routine initializes the builtin variables.
2265 */
2266static void
2267initvar(void)
2268{
2269 struct var *vp;
2270 struct var *end;
2271 struct var **vpp;
2272
2273 /*
2274 * PS1 depends on uid
2275 */
2276#if ENABLE_FEATURE_EDITING && ENABLE_FEATURE_EDITING_FANCY_PROMPT
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002277 vps1.var_text = "PS1=\\w \\$ ";
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002278#else
2279 if (!geteuid())
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002280 vps1.var_text = "PS1=# ";
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002281#endif
2282 vp = varinit;
Denis Vlasenko80b8b392007-06-25 10:55:35 +00002283 end = vp + ARRAY_SIZE(varinit);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002284 do {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002285 vpp = hashvar(vp->var_text);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002286 vp->next = *vpp;
2287 *vpp = vp;
2288 } while (++vp < end);
2289}
2290
2291static struct var **
2292findvar(struct var **vpp, const char *name)
2293{
2294 for (; *vpp; vpp = &(*vpp)->next) {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002295 if (varcmp((*vpp)->var_text, name) == 0) {
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002296 break;
2297 }
2298 }
2299 return vpp;
2300}
2301
2302/*
2303 * Find the value of a variable. Returns NULL if not set.
2304 */
Denys Vlasenko03dad222010-01-12 23:29:57 +01002305static const char* FAST_FUNC
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002306lookupvar(const char *name)
2307{
2308 struct var *v;
2309
2310 v = *findvar(hashvar(name), name);
2311 if (v) {
Ron Yorston1d371862019-04-15 10:52:05 +01002312#if ENABLE_ASH_RANDOM_SUPPORT || BASH_EPOCH_VARS
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002313 /*
2314 * Dynamic variables are implemented roughly the same way they are
2315 * in bash. Namely, they're "special" so long as they aren't unset.
2316 * As soon as they're unset, they're no longer dynamic, and dynamic
2317 * lookup will no longer happen at that point. -- PFM.
2318 */
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002319 if (v->flags & VDYNAMIC)
2320 v->var_func(NULL);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002321#endif
Denys Vlasenko675d24a2018-01-27 22:02:05 +01002322 if (!(v->flags & VUNSET)) {
2323 if (v == &vlineno && v->var_text == linenovar) {
2324 fmtstr(linenovar+7, sizeof(linenovar)-7, "%d", lineno);
2325 }
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002326 return var_end(v->var_text);
Denys Vlasenko675d24a2018-01-27 22:02:05 +01002327 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002328 }
2329 return NULL;
2330}
2331
Denys Vlasenko0b883582016-12-23 16:49:07 +01002332#if ENABLE_UNICODE_SUPPORT
Denys Vlasenko37dc08b2016-10-02 04:38:07 +02002333static void
2334reinit_unicode_for_ash(void)
Denys Vlasenkoe9ab07c2014-08-13 18:00:08 +02002335{
2336 /* Unicode support should be activated even if LANG is set
2337 * _during_ shell execution, not only if it was set when
2338 * shell was started. Therefore, re-check LANG every time:
2339 */
2340 if (ENABLE_FEATURE_CHECK_UNICODE_IN_ENV
2341 || ENABLE_UNICODE_USING_LOCALE
2342 ) {
2343 const char *s = lookupvar("LC_ALL");
2344 if (!s) s = lookupvar("LC_CTYPE");
2345 if (!s) s = lookupvar("LANG");
2346 reinit_unicode(s);
2347 }
2348}
Denys Vlasenko0b883582016-12-23 16:49:07 +01002349#else
2350# define reinit_unicode_for_ash() ((void)0)
2351#endif
Denys Vlasenkoe9ab07c2014-08-13 18:00:08 +02002352
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002353/*
2354 * Search the environment of a builtin command.
2355 */
Denys Vlasenko488e6092017-07-26 23:08:36 +02002356static ALWAYS_INLINE const char *
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002357bltinlookup(const char *name)
2358{
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002359 return lookupvar(name);
2360}
2361
2362/*
2363 * Same as setvar except that the variable and value are passed in
2364 * the first argument as name=value. Since the first argument will
2365 * be actually stored in the table, it should not be a string that
2366 * will go away.
2367 * Called with interrupts off.
2368 */
Denys Vlasenkod04fc712017-07-26 20:06:48 +02002369static struct var *
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002370setvareq(char *s, int flags)
2371{
2372 struct var *vp, **vpp;
2373
2374 vpp = hashvar(s);
2375 flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1));
Denys Vlasenkod04fc712017-07-26 20:06:48 +02002376 vpp = findvar(vpp, s);
2377 vp = *vpp;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002378 if (vp) {
2379 if ((vp->flags & (VREADONLY|VDYNAMIC)) == VREADONLY) {
2380 const char *n;
2381
2382 if (flags & VNOSAVE)
2383 free(s);
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002384 n = vp->var_text;
Denys Vlasenkod81e9f52016-10-28 15:43:50 +02002385 exitstatus = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002386 ash_msg_and_raise_error("%.*s: is read only", strchrnul(n, '=') - n, n);
2387 }
2388
2389 if (flags & VNOSET)
Denys Vlasenkod04fc712017-07-26 20:06:48 +02002390 goto out;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002391
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002392 if (vp->var_func && !(flags & VNOFUNC))
2393 vp->var_func(var_end(s));
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002394
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002395 if (!(vp->flags & (VTEXTFIXED|VSTACK)))
2396 free((char*)vp->var_text);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002397
Denys Vlasenkob28d4c32017-07-25 16:29:36 +02002398 if (((flags & (VEXPORT|VREADONLY|VSTRFIXED|VUNSET)) | (vp->flags & VSTRFIXED)) == VUNSET) {
2399 *vpp = vp->next;
2400 free(vp);
2401 out_free:
2402 if ((flags & (VTEXTFIXED|VSTACK|VNOSAVE)) == VNOSAVE)
2403 free(s);
Denys Vlasenkod04fc712017-07-26 20:06:48 +02002404 goto out;
Denys Vlasenkob28d4c32017-07-25 16:29:36 +02002405 }
2406
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002407 flags |= vp->flags & ~(VTEXTFIXED|VSTACK|VNOSAVE|VUNSET);
Ron Yorston1d371862019-04-15 10:52:05 +01002408#if ENABLE_ASH_RANDOM_SUPPORT || BASH_EPOCH_VARS
Ron Yorstond96c69d2019-04-15 10:49:35 +01002409 if (flags & VUNSET)
2410 flags &= ~VDYNAMIC;
2411#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002412 } else {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002413 /* variable s is not found */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002414 if (flags & VNOSET)
Denys Vlasenkod04fc712017-07-26 20:06:48 +02002415 goto out;
Denys Vlasenkob28d4c32017-07-25 16:29:36 +02002416 if ((flags & (VEXPORT|VREADONLY|VSTRFIXED|VUNSET)) == VUNSET)
2417 goto out_free;
Denis Vlasenko597906c2008-02-20 16:38:54 +00002418 vp = ckzalloc(sizeof(*vp));
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002419 vp->next = *vpp;
Denis Vlasenko597906c2008-02-20 16:38:54 +00002420 /*vp->func = NULL; - ckzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002421 *vpp = vp;
2422 }
2423 if (!(flags & (VTEXTFIXED|VSTACK|VNOSAVE)))
2424 s = ckstrdup(s);
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002425 vp->var_text = s;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002426 vp->flags = flags;
Denys Vlasenkod04fc712017-07-26 20:06:48 +02002427
2428 out:
2429 return vp;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002430}
2431
2432/*
2433 * Set the value of a variable. The flags argument is ored with the
2434 * flags of the variable. If val is NULL, the variable is unset.
2435 */
Denys Vlasenkod04fc712017-07-26 20:06:48 +02002436static struct var *
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002437setvar(const char *name, const char *val, int flags)
2438{
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002439 const char *q;
2440 char *p;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002441 char *nameeq;
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002442 size_t namelen;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002443 size_t vallen;
Denys Vlasenkod04fc712017-07-26 20:06:48 +02002444 struct var *vp;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002445
2446 q = endofname(name);
2447 p = strchrnul(q, '=');
2448 namelen = p - name;
2449 if (!namelen || p != q)
2450 ash_msg_and_raise_error("%.*s: bad variable name", namelen, name);
2451 vallen = 0;
2452 if (val == NULL) {
2453 flags |= VUNSET;
2454 } else {
2455 vallen = strlen(val);
2456 }
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002457
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002458 INT_OFF;
Ron Yorstone6a63bf2018-11-12 21:10:54 +00002459 nameeq = ckzalloc(namelen + vallen + 2);
Denys Vlasenkoda2244f2017-07-21 18:51:29 +02002460 p = mempcpy(nameeq, name, namelen);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002461 if (val) {
2462 *p++ = '=';
Ron Yorstone6a63bf2018-11-12 21:10:54 +00002463 memcpy(p, val, vallen);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002464 }
Denys Vlasenkod04fc712017-07-26 20:06:48 +02002465 vp = setvareq(nameeq, flags | VNOSAVE);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002466 INT_ON;
Denys Vlasenkod04fc712017-07-26 20:06:48 +02002467
2468 return vp;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002469}
2470
Denys Vlasenko03dad222010-01-12 23:29:57 +01002471static void FAST_FUNC
Denys Vlasenko0a0acb52015-04-18 19:36:38 +02002472setvar0(const char *name, const char *val)
Denys Vlasenko03dad222010-01-12 23:29:57 +01002473{
2474 setvar(name, val, 0);
2475}
2476
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002477/*
2478 * Unset the specified variable.
2479 */
Denys Vlasenkob28d4c32017-07-25 16:29:36 +02002480static void
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002481unsetvar(const char *s)
2482{
Denys Vlasenkocf3a7962017-07-26 14:38:19 +02002483 setvar(s, NULL, 0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002484}
2485
2486/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002487 * Generate a list of variables satisfying the given conditions.
2488 */
Denys Vlasenkoa5060b82017-11-03 14:16:25 +01002489#if !ENABLE_FEATURE_SH_NOFORK
2490# define listvars(on, off, lp, end) listvars(on, off, end)
2491#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002492static char **
Denys Vlasenkoa5060b82017-11-03 14:16:25 +01002493listvars(int on, int off, struct strlist *lp, char ***end)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002494{
2495 struct var **vpp;
2496 struct var *vp;
2497 char **ep;
2498 int mask;
2499
2500 STARTSTACKSTR(ep);
2501 vpp = vartab;
2502 mask = on | off;
2503 do {
2504 for (vp = *vpp; vp; vp = vp->next) {
2505 if ((vp->flags & mask) == on) {
Denys Vlasenkoa5060b82017-11-03 14:16:25 +01002506#if ENABLE_FEATURE_SH_NOFORK
2507 /* If variable with the same name is both
2508 * exported and temporarily set for a command:
2509 * export ZVAR=5
2510 * ZVAR=6 printenv
2511 * then "ZVAR=6" will be both in vartab and
2512 * lp lists. Do not pass it twice to printenv.
2513 */
2514 struct strlist *lp1 = lp;
2515 while (lp1) {
2516 if (strcmp(lp1->text, vp->var_text) == 0)
2517 goto skip;
2518 lp1 = lp1->next;
2519 }
2520#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002521 if (ep == stackstrend())
2522 ep = growstackstr();
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002523 *ep++ = (char*)vp->var_text;
Denys Vlasenkoa5060b82017-11-03 14:16:25 +01002524#if ENABLE_FEATURE_SH_NOFORK
2525 skip: ;
2526#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002527 }
2528 }
2529 } while (++vpp < vartab + VTABSIZE);
Denys Vlasenkoa5060b82017-11-03 14:16:25 +01002530
2531#if ENABLE_FEATURE_SH_NOFORK
2532 while (lp) {
2533 if (ep == stackstrend())
2534 ep = growstackstr();
2535 *ep++ = lp->text;
2536 lp = lp->next;
2537 }
2538#endif
2539
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002540 if (ep == stackstrend())
2541 ep = growstackstr();
2542 if (end)
2543 *end = ep;
2544 *ep++ = NULL;
2545 return grabstackstr(ep);
2546}
2547
2548
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +01002549/* ============ Path search helper */
2550static const char *
2551legal_pathopt(const char *opt, const char *term, int magic)
2552{
2553 switch (magic) {
2554 case 0:
2555 opt = NULL;
2556 break;
2557
2558 case 1:
2559 opt = prefix(opt, "builtin") ?: prefix(opt, "func");
2560 break;
2561
2562 default:
2563 opt += strcspn(opt, term);
2564 break;
2565 }
2566
2567 if (opt && *opt == '%')
2568 opt++;
2569
2570 return opt;
2571}
2572
2573/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002574 * The variable path (passed by reference) should be set to the start
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +01002575 * of the path before the first call; padvance will update
2576 * this value as it proceeds. Successive calls to padvance will return
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002577 * the possible path expansions in sequence. If an option (indicated by
2578 * a percent sign) appears in the path entry then the global variable
2579 * pathopt will be set to point to it; otherwise pathopt will be set to
2580 * NULL.
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +01002581 *
2582 * If magic is 0 then pathopt recognition will be disabled. If magic is
2583 * 1 we shall recognise %builtin/%func. Otherwise we shall accept any
2584 * pathopt.
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002585 */
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +01002586static const char *pathopt; /* set by padvance */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002587
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +01002588static int
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +01002589padvance_magic(const char **path, const char *name, int magic)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002590{
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +01002591 const char *term = "%:";
2592 const char *lpathopt;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002593 const char *p;
2594 char *q;
2595 const char *start;
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +01002596 size_t qlen;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002597 size_t len;
2598
2599 if (*path == NULL)
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +01002600 return -1;
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +01002601
2602 lpathopt = NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002603 start = *path;
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +01002604
2605 if (*start == '%' && (p = legal_pathopt(start + 1, term, magic))) {
2606 lpathopt = start + 1;
2607 start = p;
2608 term = ":";
2609 }
2610
2611 len = strcspn(start, term);
2612 p = start + len;
2613
2614 if (*p == '%') {
2615 size_t extra = strchrnul(p, ':') - p;
2616
2617 if (legal_pathopt(p + 1, term, magic))
2618 lpathopt = p + 1;
2619 else
2620 len += extra;
2621
2622 p += extra;
2623 }
2624
2625 pathopt = lpathopt;
2626 *path = *p == ':' ? p + 1 : NULL;
2627
2628 /* "2" is for '/' and '\0' */
2629 qlen = len + strlen(name) + 2;
2630 q = growstackto(qlen);
2631
2632 if (len) {
2633 q = mempcpy(q, start, len);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002634 *q++ = '/';
2635 }
2636 strcpy(q, name);
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +01002637
2638 return qlen;
2639}
2640
2641static int
2642padvance(const char **path, const char *name)
2643{
2644 return padvance_magic(path, name, 1);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002645}
2646
2647
2648/* ============ Prompt */
2649
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00002650static smallint doprompt; /* if set, prompt the user */
2651static smallint needprompt; /* true if interactive and at start of line */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002652
2653#if ENABLE_FEATURE_EDITING
2654static line_input_t *line_input_state;
2655static const char *cmdedit_prompt;
2656static void
2657putprompt(const char *s)
2658{
2659 if (ENABLE_ASH_EXPAND_PRMT) {
2660 free((char*)cmdedit_prompt);
Denis Vlasenko4222ae42007-02-25 02:37:49 +00002661 cmdedit_prompt = ckstrdup(s);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002662 return;
2663 }
2664 cmdedit_prompt = s;
2665}
2666#else
2667static void
2668putprompt(const char *s)
2669{
2670 out2str(s);
2671}
2672#endif
2673
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002674/* expandstr() needs parsing machinery, so it is far away ahead... */
Denys Vlasenko46999802017-07-29 21:12:29 +02002675static const char *expandstr(const char *ps, int syntax_type);
2676/* Values for syntax param */
2677#define BASESYNTAX 0 /* not in quotes */
2678#define DQSYNTAX 1 /* in double quotes */
2679#define SQSYNTAX 2 /* in single quotes */
2680#define ARISYNTAX 3 /* in arithmetic */
Denys Vlasenko5f0a75f2017-07-29 22:58:44 +02002681#if ENABLE_ASH_EXPAND_PRMT
2682# define PSSYNTAX 4 /* prompt. never passed to SIT() */
2683#endif
Denys Vlasenko46999802017-07-29 21:12:29 +02002684/* PSSYNTAX expansion is identical to DQSYNTAX, except keeping '\$' as '\$' */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002685
Denys Vlasenko46999802017-07-29 21:12:29 +02002686/*
2687 * called by editline -- any expansions to the prompt should be added here.
2688 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002689static void
Denys Vlasenko958581a2010-09-12 15:04:27 +02002690setprompt_if(smallint do_set, int whichprompt)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002691{
2692 const char *prompt;
Denys Vlasenko958581a2010-09-12 15:04:27 +02002693 IF_ASH_EXPAND_PRMT(struct stackmark smark;)
2694
2695 if (!do_set)
2696 return;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002697
2698 needprompt = 0;
2699
2700 switch (whichprompt) {
2701 case 1:
2702 prompt = ps1val();
2703 break;
2704 case 2:
2705 prompt = ps2val();
2706 break;
2707 default: /* 0 */
2708 prompt = nullstr;
2709 }
2710#if ENABLE_ASH_EXPAND_PRMT
Denys Vlasenko60ca8342016-09-30 11:21:21 +02002711 pushstackmark(&smark, stackblocksize());
Denys Vlasenko46999802017-07-29 21:12:29 +02002712 putprompt(expandstr(prompt, PSSYNTAX));
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002713 popstackmark(&smark);
Denys Vlasenko48c803a2017-07-01 23:24:48 +02002714#else
2715 putprompt(prompt);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002716#endif
2717}
2718
2719
2720/* ============ The cd and pwd commands */
2721
2722#define CD_PHYSICAL 1
2723#define CD_PRINT 2
2724
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002725static int
2726cdopt(void)
2727{
2728 int flags = 0;
2729 int i, j;
2730
2731 j = 'L';
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02002732 while ((i = nextopt("LP")) != '\0') {
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002733 if (i != j) {
2734 flags ^= CD_PHYSICAL;
2735 j = i;
2736 }
2737 }
2738
2739 return flags;
2740}
2741
2742/*
2743 * Update curdir (the name of the current directory) in response to a
2744 * cd command.
2745 */
2746static const char *
2747updatepwd(const char *dir)
2748{
2749 char *new;
2750 char *p;
2751 char *cdcomppath;
2752 const char *lim;
2753
Denys Vlasenko8e2bc472016-09-28 23:02:57 +02002754 cdcomppath = sstrdup(dir);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002755 STARTSTACKSTR(new);
2756 if (*dir != '/') {
2757 if (curdir == nullstr)
2758 return 0;
2759 new = stack_putstr(curdir, new);
2760 }
2761 new = makestrspace(strlen(dir) + 2, new);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00002762 lim = (char *)stackblock() + 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002763 if (*dir != '/') {
2764 if (new[-1] != '/')
2765 USTPUTC('/', new);
2766 if (new > lim && *lim == '/')
2767 lim++;
2768 } else {
2769 USTPUTC('/', new);
2770 cdcomppath++;
2771 if (dir[1] == '/' && dir[2] != '/') {
2772 USTPUTC('/', new);
2773 cdcomppath++;
2774 lim++;
2775 }
2776 }
Denys Vlasenko24966162020-10-06 02:36:47 +02002777 p = strtok_r(cdcomppath, "/", &cdcomppath);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002778 while (p) {
2779 switch (*p) {
2780 case '.':
2781 if (p[1] == '.' && p[2] == '\0') {
2782 while (new > lim) {
2783 STUNPUTC(new);
2784 if (new[-1] == '/')
2785 break;
2786 }
2787 break;
Denis Vlasenko16abcd92007-04-13 23:59:52 +00002788 }
2789 if (p[1] == '\0')
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002790 break;
2791 /* fall through */
2792 default:
2793 new = stack_putstr(p, new);
2794 USTPUTC('/', new);
2795 }
Denys Vlasenko24966162020-10-06 02:36:47 +02002796 p = strtok_r(NULL, "/", &cdcomppath);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002797 }
2798 if (new > lim)
2799 STUNPUTC(new);
2800 *new = 0;
2801 return stackblock();
2802}
2803
2804/*
2805 * Find out what the current directory is. If we already know the current
2806 * directory, this routine returns immediately.
2807 */
2808static char *
2809getpwd(void)
2810{
Denis Vlasenko01631112007-12-16 17:20:38 +00002811 char *dir = getcwd(NULL, 0); /* huh, using glibc extension? */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002812 return dir ? dir : nullstr;
2813}
2814
2815static void
2816setpwd(const char *val, int setold)
2817{
2818 char *oldcur, *dir;
2819
2820 oldcur = dir = curdir;
2821
2822 if (setold) {
2823 setvar("OLDPWD", oldcur, VEXPORT);
2824 }
2825 INT_OFF;
2826 if (physdir != nullstr) {
2827 if (physdir != oldcur)
2828 free(physdir);
2829 physdir = nullstr;
2830 }
2831 if (oldcur == val || !val) {
2832 char *s = getpwd();
2833 physdir = s;
2834 if (!val)
2835 dir = s;
2836 } else
2837 dir = ckstrdup(val);
2838 if (oldcur != dir && oldcur != nullstr) {
2839 free(oldcur);
2840 }
2841 curdir = dir;
2842 INT_ON;
2843 setvar("PWD", dir, VEXPORT);
2844}
2845
2846static void hashcd(void);
2847
2848/*
Denys Vlasenko70392332016-10-27 02:31:55 +02002849 * Actually do the chdir. We also call hashcd to let other routines
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002850 * know that the current directory has changed.
2851 */
2852static int
2853docd(const char *dest, int flags)
2854{
Denys Vlasenko4b1100e2010-03-05 14:10:54 +01002855 const char *dir = NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002856 int err;
2857
2858 TRACE(("docd(\"%s\", %d) called\n", dest, flags));
2859
2860 INT_OFF;
2861 if (!(flags & CD_PHYSICAL)) {
2862 dir = updatepwd(dest);
2863 if (dir)
2864 dest = dir;
2865 }
2866 err = chdir(dest);
2867 if (err)
2868 goto out;
2869 setpwd(dir, 1);
2870 hashcd();
2871 out:
2872 INT_ON;
2873 return err;
2874}
2875
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02002876static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00002877cdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002878{
2879 const char *dest;
2880 const char *path;
2881 const char *p;
2882 char c;
2883 struct stat statb;
2884 int flags;
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +01002885 int len;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002886
2887 flags = cdopt();
2888 dest = *argptr;
2889 if (!dest)
Denys Vlasenkoea8b2522010-06-02 12:57:26 +02002890 dest = bltinlookup("HOME");
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002891 else if (LONE_DASH(dest)) {
2892 dest = bltinlookup("OLDPWD");
2893 flags |= CD_PRINT;
2894 }
2895 if (!dest)
2896 dest = nullstr;
2897 if (*dest == '/')
Denys Vlasenko0e081d02016-10-26 19:56:05 +02002898 goto step6;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002899 if (*dest == '.') {
2900 c = dest[1];
2901 dotdot:
2902 switch (c) {
2903 case '\0':
2904 case '/':
2905 goto step6;
2906 case '.':
2907 c = dest[2];
2908 if (c != '.')
2909 goto dotdot;
2910 }
2911 }
2912 if (!*dest)
2913 dest = ".";
2914 path = bltinlookup("CDPATH");
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +01002915 while (p = path, (len = padvance(&path, dest)) >= 0) {
2916 c = *p;
2917 p = stalloc(len);
2918
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002919 if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) {
2920 if (c && c != ':')
2921 flags |= CD_PRINT;
2922 docd:
2923 if (!docd(p, flags))
2924 goto out;
Denys Vlasenko0e081d02016-10-26 19:56:05 +02002925 goto err;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002926 }
Denys Vlasenko0e081d02016-10-26 19:56:05 +02002927 }
2928
2929 step6:
2930 p = dest;
2931 goto docd;
2932
2933 err:
Johannes Schindelin687aac02017-08-22 22:03:22 +02002934 ash_msg_and_raise_perror("can't cd to %s", dest);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002935 /* NOTREACHED */
2936 out:
2937 if (flags & CD_PRINT)
Denys Vlasenkoea8b2522010-06-02 12:57:26 +02002938 out1fmt("%s\n", curdir);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002939 return 0;
2940}
2941
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02002942static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00002943pwdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002944{
2945 int flags;
2946 const char *dir = curdir;
2947
2948 flags = cdopt();
2949 if (flags) {
2950 if (physdir == nullstr)
2951 setpwd(dir, 0);
2952 dir = physdir;
2953 }
Denys Vlasenkoea8b2522010-06-02 12:57:26 +02002954 out1fmt("%s\n", dir);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002955 return 0;
2956}
2957
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002958
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00002959/* ============ ... */
Eric Andersenc470f442003-07-28 09:56:35 +00002960
Denis Vlasenko834dee72008-10-07 09:18:30 +00002961
Denys Vlasenko82dd14a2010-05-17 10:10:01 +02002962#define IBUFSIZ (ENABLE_FEATURE_EDITING ? CONFIG_FEATURE_EDITING_MAX_LEN : 1024)
Eric Andersenc470f442003-07-28 09:56:35 +00002963
Eric Andersenc470f442003-07-28 09:56:35 +00002964/* Syntax classes */
Denis Vlasenko834dee72008-10-07 09:18:30 +00002965#define CWORD 0 /* character is nothing special */
2966#define CNL 1 /* newline character */
2967#define CBACK 2 /* a backslash character */
2968#define CSQUOTE 3 /* single quote */
2969#define CDQUOTE 4 /* double quote */
Eric Andersenc470f442003-07-28 09:56:35 +00002970#define CENDQUOTE 5 /* a terminating quote */
Denis Vlasenko834dee72008-10-07 09:18:30 +00002971#define CBQUOTE 6 /* backwards single quote */
2972#define CVAR 7 /* a dollar sign */
2973#define CENDVAR 8 /* a '}' character */
2974#define CLP 9 /* a left paren in arithmetic */
2975#define CRP 10 /* a right paren in arithmetic */
Eric Andersenc470f442003-07-28 09:56:35 +00002976#define CENDFILE 11 /* end of file */
Denis Vlasenko834dee72008-10-07 09:18:30 +00002977#define CCTL 12 /* like CWORD, except it must be escaped */
2978#define CSPCL 13 /* these terminate a word */
2979#define CIGN 14 /* character should be ignored */
Eric Andersenc470f442003-07-28 09:56:35 +00002980
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002981#define PEOF 256
Denis Vlasenko131ae172007-02-18 13:00:19 +00002982#if ENABLE_ASH_ALIAS
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002983# define PEOA 257
Eric Andersenc470f442003-07-28 09:56:35 +00002984#endif
2985
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01002986#define USE_SIT_FUNCTION ENABLE_ASH_OPTIMIZE_FOR_SIZE
Manuel Novoa III 16815d42001-08-10 19:36:07 +00002987
Denys Vlasenko0b883582016-12-23 16:49:07 +01002988#if ENABLE_FEATURE_SH_MATH
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002989# define SIT_ITEM(a,b,c,d) (a | (b << 4) | (c << 8) | (d << 12))
Eric Andersenc470f442003-07-28 09:56:35 +00002990#else
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002991# define SIT_ITEM(a,b,c,d) (a | (b << 4) | (c << 8))
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002992#endif
Denys Vlasenko3e134eb2016-04-22 18:09:21 +02002993static const uint16_t S_I_T[] ALIGN2 = {
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01002994#if ENABLE_ASH_ALIAS
2995 SIT_ITEM(CSPCL , CIGN , CIGN , CIGN ), /* 0, PEOA */
2996#endif
2997 SIT_ITEM(CSPCL , CWORD , CWORD, CWORD ), /* 1, ' ' */
2998 SIT_ITEM(CNL , CNL , CNL , CNL ), /* 2, \n */
2999 SIT_ITEM(CWORD , CCTL , CCTL , CWORD ), /* 3, !*-/:=?[]~ */
3000 SIT_ITEM(CDQUOTE , CENDQUOTE, CWORD, CWORD ), /* 4, '"' */
3001 SIT_ITEM(CVAR , CVAR , CWORD, CVAR ), /* 5, $ */
3002 SIT_ITEM(CSQUOTE , CWORD , CENDQUOTE, CWORD), /* 6, "'" */
3003 SIT_ITEM(CSPCL , CWORD , CWORD, CLP ), /* 7, ( */
3004 SIT_ITEM(CSPCL , CWORD , CWORD, CRP ), /* 8, ) */
3005 SIT_ITEM(CBACK , CBACK , CCTL , CBACK ), /* 9, \ */
3006 SIT_ITEM(CBQUOTE , CBQUOTE , CWORD, CBQUOTE), /* 10, ` */
3007 SIT_ITEM(CENDVAR , CENDVAR , CWORD, CENDVAR), /* 11, } */
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01003008#if !USE_SIT_FUNCTION
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01003009 SIT_ITEM(CENDFILE, CENDFILE , CENDFILE, CENDFILE),/* 12, PEOF */
3010 SIT_ITEM(CWORD , CWORD , CWORD, CWORD ), /* 13, 0-9A-Za-z */
3011 SIT_ITEM(CCTL , CCTL , CCTL , CCTL ) /* 14, CTLESC ... */
3012#endif
3013#undef SIT_ITEM
Eric Andersenc470f442003-07-28 09:56:35 +00003014};
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01003015/* Constants below must match table above */
3016enum {
3017#if ENABLE_ASH_ALIAS
3018 CSPCL_CIGN_CIGN_CIGN , /* 0 */
3019#endif
3020 CSPCL_CWORD_CWORD_CWORD , /* 1 */
3021 CNL_CNL_CNL_CNL , /* 2 */
3022 CWORD_CCTL_CCTL_CWORD , /* 3 */
3023 CDQUOTE_CENDQUOTE_CWORD_CWORD , /* 4 */
3024 CVAR_CVAR_CWORD_CVAR , /* 5 */
3025 CSQUOTE_CWORD_CENDQUOTE_CWORD , /* 6 */
3026 CSPCL_CWORD_CWORD_CLP , /* 7 */
3027 CSPCL_CWORD_CWORD_CRP , /* 8 */
3028 CBACK_CBACK_CCTL_CBACK , /* 9 */
3029 CBQUOTE_CBQUOTE_CWORD_CBQUOTE , /* 10 */
3030 CENDVAR_CENDVAR_CWORD_CENDVAR , /* 11 */
3031 CENDFILE_CENDFILE_CENDFILE_CENDFILE, /* 12 */
3032 CWORD_CWORD_CWORD_CWORD , /* 13 */
3033 CCTL_CCTL_CCTL_CCTL , /* 14 */
3034};
Eric Andersen2870d962001-07-02 17:27:21 +00003035
Denys Vlasenkocd716832009-11-28 22:14:02 +01003036/* c in SIT(c, syntax) must be an *unsigned char* or PEOA or PEOF,
3037 * caller must ensure proper cast on it if c is *char_ptr!
3038 */
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01003039#if USE_SIT_FUNCTION
Manuel Novoa III 16815d42001-08-10 19:36:07 +00003040
Denis Vlasenko0c032a42007-02-23 01:03:40 +00003041static int
3042SIT(int c, int syntax)
Manuel Novoa III 16815d42001-08-10 19:36:07 +00003043{
Denys Vlasenkob3f29b42016-09-21 16:25:58 +02003044 /* Used to also have '/' in this string: "\t\n !\"$&'()*-/:;<=>?[\\]`|}~" */
3045 static const char spec_symbls[] ALIGN1 = "\t\n !\"$&'()*-:;<=>?[\\]`|}~";
3046 /*
3047 * This causes '/' to be prepended with CTLESC in dquoted string,
3048 * making "./file"* treated incorrectly because we feed
3049 * ".\/file*" string to glob(), confusing it (see expandmeta func).
3050 * The "homegrown" glob implementation is okay with that,
3051 * but glibc one isn't. With '/' always treated as CWORD,
3052 * both work fine.
3053 */
Denys Vlasenkocd716832009-11-28 22:14:02 +01003054# if ENABLE_ASH_ALIAS
3055 static const uint8_t syntax_index_table[] ALIGN1 = {
Eric Andersenc470f442003-07-28 09:56:35 +00003056 1, 2, 1, 3, 4, 5, 1, 6, /* "\t\n !\"$&'" */
Denys Vlasenkob3f29b42016-09-21 16:25:58 +02003057 7, 8, 3, 3,/*3,*/3, 1, 1, /* "()*-/:;<" */
Eric Andersenc470f442003-07-28 09:56:35 +00003058 3, 1, 3, 3, 9, 3, 10, 1, /* "=>?[\\]`|" */
3059 11, 3 /* "}~" */
3060 };
Denys Vlasenkocd716832009-11-28 22:14:02 +01003061# else
3062 static const uint8_t syntax_index_table[] ALIGN1 = {
Eric Andersenc470f442003-07-28 09:56:35 +00003063 0, 1, 0, 2, 3, 4, 0, 5, /* "\t\n !\"$&'" */
Denys Vlasenkob3f29b42016-09-21 16:25:58 +02003064 6, 7, 2, 2,/*2,*/2, 0, 0, /* "()*-/:;<" */
Eric Andersenc470f442003-07-28 09:56:35 +00003065 2, 0, 2, 2, 8, 2, 9, 0, /* "=>?[\\]`|" */
3066 10, 2 /* "}~" */
3067 };
Denys Vlasenkocd716832009-11-28 22:14:02 +01003068# endif
Manuel Novoa III 16815d42001-08-10 19:36:07 +00003069 const char *s;
3070 int indx;
3071
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01003072 if (c == PEOF)
Manuel Novoa III 16815d42001-08-10 19:36:07 +00003073 return CENDFILE;
Denys Vlasenkocd716832009-11-28 22:14:02 +01003074# if ENABLE_ASH_ALIAS
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01003075 if (c == PEOA)
Denis Vlasenko0dfe1d22009-04-02 12:57:38 +00003076 indx = 0;
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01003077 else
Denys Vlasenkocd716832009-11-28 22:14:02 +01003078# endif
Denis Vlasenko0dfe1d22009-04-02 12:57:38 +00003079 {
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01003080 /* Cast is purely for paranoia here,
3081 * just in case someone passed signed char to us */
3082 if ((unsigned char)c >= CTL_FIRST
3083 && (unsigned char)c <= CTL_LAST
Denis Vlasenko0dfe1d22009-04-02 12:57:38 +00003084 ) {
3085 return CCTL;
3086 }
3087 s = strchrnul(spec_symbls, c);
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01003088 if (*s == '\0')
Denis Vlasenko0dfe1d22009-04-02 12:57:38 +00003089 return CWORD;
Denis Vlasenko0dfe1d22009-04-02 12:57:38 +00003090 indx = syntax_index_table[s - spec_symbls];
3091 }
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01003092 return (S_I_T[indx] >> (syntax*4)) & 0xf;
Manuel Novoa III 16815d42001-08-10 19:36:07 +00003093}
3094
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00003095#else /* !USE_SIT_FUNCTION */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00003096
Denys Vlasenko3e134eb2016-04-22 18:09:21 +02003097static const uint8_t syntax_index_table[] ALIGN1 = {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003098 /* BASESYNTAX_DQSYNTAX_SQSYNTAX_ARISYNTAX */
Denys Vlasenkocd716832009-11-28 22:14:02 +01003099 /* 0 */ CWORD_CWORD_CWORD_CWORD,
3100 /* 1 */ CWORD_CWORD_CWORD_CWORD,
3101 /* 2 */ CWORD_CWORD_CWORD_CWORD,
3102 /* 3 */ CWORD_CWORD_CWORD_CWORD,
3103 /* 4 */ CWORD_CWORD_CWORD_CWORD,
3104 /* 5 */ CWORD_CWORD_CWORD_CWORD,
3105 /* 6 */ CWORD_CWORD_CWORD_CWORD,
3106 /* 7 */ CWORD_CWORD_CWORD_CWORD,
3107 /* 8 */ CWORD_CWORD_CWORD_CWORD,
3108 /* 9 "\t" */ CSPCL_CWORD_CWORD_CWORD,
3109 /* 10 "\n" */ CNL_CNL_CNL_CNL,
3110 /* 11 */ CWORD_CWORD_CWORD_CWORD,
3111 /* 12 */ CWORD_CWORD_CWORD_CWORD,
3112 /* 13 */ CWORD_CWORD_CWORD_CWORD,
3113 /* 14 */ CWORD_CWORD_CWORD_CWORD,
3114 /* 15 */ CWORD_CWORD_CWORD_CWORD,
3115 /* 16 */ CWORD_CWORD_CWORD_CWORD,
3116 /* 17 */ CWORD_CWORD_CWORD_CWORD,
3117 /* 18 */ CWORD_CWORD_CWORD_CWORD,
3118 /* 19 */ CWORD_CWORD_CWORD_CWORD,
3119 /* 20 */ CWORD_CWORD_CWORD_CWORD,
3120 /* 21 */ CWORD_CWORD_CWORD_CWORD,
3121 /* 22 */ CWORD_CWORD_CWORD_CWORD,
3122 /* 23 */ CWORD_CWORD_CWORD_CWORD,
3123 /* 24 */ CWORD_CWORD_CWORD_CWORD,
3124 /* 25 */ CWORD_CWORD_CWORD_CWORD,
3125 /* 26 */ CWORD_CWORD_CWORD_CWORD,
3126 /* 27 */ CWORD_CWORD_CWORD_CWORD,
3127 /* 28 */ CWORD_CWORD_CWORD_CWORD,
3128 /* 29 */ CWORD_CWORD_CWORD_CWORD,
3129 /* 30 */ CWORD_CWORD_CWORD_CWORD,
3130 /* 31 */ CWORD_CWORD_CWORD_CWORD,
3131 /* 32 " " */ CSPCL_CWORD_CWORD_CWORD,
3132 /* 33 "!" */ CWORD_CCTL_CCTL_CWORD,
3133 /* 34 """ */ CDQUOTE_CENDQUOTE_CWORD_CWORD,
3134 /* 35 "#" */ CWORD_CWORD_CWORD_CWORD,
3135 /* 36 "$" */ CVAR_CVAR_CWORD_CVAR,
3136 /* 37 "%" */ CWORD_CWORD_CWORD_CWORD,
3137 /* 38 "&" */ CSPCL_CWORD_CWORD_CWORD,
3138 /* 39 "'" */ CSQUOTE_CWORD_CENDQUOTE_CWORD,
3139 /* 40 "(" */ CSPCL_CWORD_CWORD_CLP,
3140 /* 41 ")" */ CSPCL_CWORD_CWORD_CRP,
3141 /* 42 "*" */ CWORD_CCTL_CCTL_CWORD,
3142 /* 43 "+" */ CWORD_CWORD_CWORD_CWORD,
3143 /* 44 "," */ CWORD_CWORD_CWORD_CWORD,
3144 /* 45 "-" */ CWORD_CCTL_CCTL_CWORD,
3145 /* 46 "." */ CWORD_CWORD_CWORD_CWORD,
Denys Vlasenkob3f29b42016-09-21 16:25:58 +02003146/* "/" was CWORD_CCTL_CCTL_CWORD, see comment in SIT() function why this is changed: */
3147 /* 47 "/" */ CWORD_CWORD_CWORD_CWORD,
Denys Vlasenkocd716832009-11-28 22:14:02 +01003148 /* 48 "0" */ CWORD_CWORD_CWORD_CWORD,
3149 /* 49 "1" */ CWORD_CWORD_CWORD_CWORD,
3150 /* 50 "2" */ CWORD_CWORD_CWORD_CWORD,
3151 /* 51 "3" */ CWORD_CWORD_CWORD_CWORD,
3152 /* 52 "4" */ CWORD_CWORD_CWORD_CWORD,
3153 /* 53 "5" */ CWORD_CWORD_CWORD_CWORD,
3154 /* 54 "6" */ CWORD_CWORD_CWORD_CWORD,
3155 /* 55 "7" */ CWORD_CWORD_CWORD_CWORD,
3156 /* 56 "8" */ CWORD_CWORD_CWORD_CWORD,
3157 /* 57 "9" */ CWORD_CWORD_CWORD_CWORD,
3158 /* 58 ":" */ CWORD_CCTL_CCTL_CWORD,
3159 /* 59 ";" */ CSPCL_CWORD_CWORD_CWORD,
3160 /* 60 "<" */ CSPCL_CWORD_CWORD_CWORD,
3161 /* 61 "=" */ CWORD_CCTL_CCTL_CWORD,
3162 /* 62 ">" */ CSPCL_CWORD_CWORD_CWORD,
3163 /* 63 "?" */ CWORD_CCTL_CCTL_CWORD,
3164 /* 64 "@" */ CWORD_CWORD_CWORD_CWORD,
3165 /* 65 "A" */ CWORD_CWORD_CWORD_CWORD,
3166 /* 66 "B" */ CWORD_CWORD_CWORD_CWORD,
3167 /* 67 "C" */ CWORD_CWORD_CWORD_CWORD,
3168 /* 68 "D" */ CWORD_CWORD_CWORD_CWORD,
3169 /* 69 "E" */ CWORD_CWORD_CWORD_CWORD,
3170 /* 70 "F" */ CWORD_CWORD_CWORD_CWORD,
3171 /* 71 "G" */ CWORD_CWORD_CWORD_CWORD,
3172 /* 72 "H" */ CWORD_CWORD_CWORD_CWORD,
3173 /* 73 "I" */ CWORD_CWORD_CWORD_CWORD,
3174 /* 74 "J" */ CWORD_CWORD_CWORD_CWORD,
3175 /* 75 "K" */ CWORD_CWORD_CWORD_CWORD,
3176 /* 76 "L" */ CWORD_CWORD_CWORD_CWORD,
3177 /* 77 "M" */ CWORD_CWORD_CWORD_CWORD,
3178 /* 78 "N" */ CWORD_CWORD_CWORD_CWORD,
3179 /* 79 "O" */ CWORD_CWORD_CWORD_CWORD,
3180 /* 80 "P" */ CWORD_CWORD_CWORD_CWORD,
3181 /* 81 "Q" */ CWORD_CWORD_CWORD_CWORD,
3182 /* 82 "R" */ CWORD_CWORD_CWORD_CWORD,
3183 /* 83 "S" */ CWORD_CWORD_CWORD_CWORD,
3184 /* 84 "T" */ CWORD_CWORD_CWORD_CWORD,
3185 /* 85 "U" */ CWORD_CWORD_CWORD_CWORD,
3186 /* 86 "V" */ CWORD_CWORD_CWORD_CWORD,
3187 /* 87 "W" */ CWORD_CWORD_CWORD_CWORD,
3188 /* 88 "X" */ CWORD_CWORD_CWORD_CWORD,
3189 /* 89 "Y" */ CWORD_CWORD_CWORD_CWORD,
3190 /* 90 "Z" */ CWORD_CWORD_CWORD_CWORD,
3191 /* 91 "[" */ CWORD_CCTL_CCTL_CWORD,
3192 /* 92 "\" */ CBACK_CBACK_CCTL_CBACK,
3193 /* 93 "]" */ CWORD_CCTL_CCTL_CWORD,
3194 /* 94 "^" */ CWORD_CWORD_CWORD_CWORD,
3195 /* 95 "_" */ CWORD_CWORD_CWORD_CWORD,
3196 /* 96 "`" */ CBQUOTE_CBQUOTE_CWORD_CBQUOTE,
3197 /* 97 "a" */ CWORD_CWORD_CWORD_CWORD,
3198 /* 98 "b" */ CWORD_CWORD_CWORD_CWORD,
3199 /* 99 "c" */ CWORD_CWORD_CWORD_CWORD,
3200 /* 100 "d" */ CWORD_CWORD_CWORD_CWORD,
3201 /* 101 "e" */ CWORD_CWORD_CWORD_CWORD,
3202 /* 102 "f" */ CWORD_CWORD_CWORD_CWORD,
3203 /* 103 "g" */ CWORD_CWORD_CWORD_CWORD,
3204 /* 104 "h" */ CWORD_CWORD_CWORD_CWORD,
3205 /* 105 "i" */ CWORD_CWORD_CWORD_CWORD,
3206 /* 106 "j" */ CWORD_CWORD_CWORD_CWORD,
3207 /* 107 "k" */ CWORD_CWORD_CWORD_CWORD,
3208 /* 108 "l" */ CWORD_CWORD_CWORD_CWORD,
3209 /* 109 "m" */ CWORD_CWORD_CWORD_CWORD,
3210 /* 110 "n" */ CWORD_CWORD_CWORD_CWORD,
3211 /* 111 "o" */ CWORD_CWORD_CWORD_CWORD,
3212 /* 112 "p" */ CWORD_CWORD_CWORD_CWORD,
3213 /* 113 "q" */ CWORD_CWORD_CWORD_CWORD,
3214 /* 114 "r" */ CWORD_CWORD_CWORD_CWORD,
3215 /* 115 "s" */ CWORD_CWORD_CWORD_CWORD,
3216 /* 116 "t" */ CWORD_CWORD_CWORD_CWORD,
3217 /* 117 "u" */ CWORD_CWORD_CWORD_CWORD,
3218 /* 118 "v" */ CWORD_CWORD_CWORD_CWORD,
3219 /* 119 "w" */ CWORD_CWORD_CWORD_CWORD,
3220 /* 120 "x" */ CWORD_CWORD_CWORD_CWORD,
3221 /* 121 "y" */ CWORD_CWORD_CWORD_CWORD,
3222 /* 122 "z" */ CWORD_CWORD_CWORD_CWORD,
3223 /* 123 "{" */ CWORD_CWORD_CWORD_CWORD,
3224 /* 124 "|" */ CSPCL_CWORD_CWORD_CWORD,
3225 /* 125 "}" */ CENDVAR_CENDVAR_CWORD_CENDVAR,
3226 /* 126 "~" */ CWORD_CCTL_CCTL_CWORD,
3227 /* 127 del */ CWORD_CWORD_CWORD_CWORD,
3228 /* 128 0x80 */ CWORD_CWORD_CWORD_CWORD,
3229 /* 129 CTLESC */ CCTL_CCTL_CCTL_CCTL,
3230 /* 130 CTLVAR */ CCTL_CCTL_CCTL_CCTL,
3231 /* 131 CTLENDVAR */ CCTL_CCTL_CCTL_CCTL,
3232 /* 132 CTLBACKQ */ CCTL_CCTL_CCTL_CCTL,
3233 /* 133 CTLQUOTE */ CCTL_CCTL_CCTL_CCTL,
3234 /* 134 CTLARI */ CCTL_CCTL_CCTL_CCTL,
3235 /* 135 CTLENDARI */ CCTL_CCTL_CCTL_CCTL,
3236 /* 136 CTLQUOTEMARK */ CCTL_CCTL_CCTL_CCTL,
3237 /* 137 */ CWORD_CWORD_CWORD_CWORD,
3238 /* 138 */ CWORD_CWORD_CWORD_CWORD,
3239 /* 139 */ CWORD_CWORD_CWORD_CWORD,
3240 /* 140 */ CWORD_CWORD_CWORD_CWORD,
3241 /* 141 */ CWORD_CWORD_CWORD_CWORD,
3242 /* 142 */ CWORD_CWORD_CWORD_CWORD,
3243 /* 143 */ CWORD_CWORD_CWORD_CWORD,
3244 /* 144 */ CWORD_CWORD_CWORD_CWORD,
3245 /* 145 */ CWORD_CWORD_CWORD_CWORD,
3246 /* 146 */ CWORD_CWORD_CWORD_CWORD,
3247 /* 147 */ CWORD_CWORD_CWORD_CWORD,
3248 /* 148 */ CWORD_CWORD_CWORD_CWORD,
3249 /* 149 */ CWORD_CWORD_CWORD_CWORD,
3250 /* 150 */ CWORD_CWORD_CWORD_CWORD,
3251 /* 151 */ CWORD_CWORD_CWORD_CWORD,
3252 /* 152 */ CWORD_CWORD_CWORD_CWORD,
3253 /* 153 */ CWORD_CWORD_CWORD_CWORD,
3254 /* 154 */ CWORD_CWORD_CWORD_CWORD,
3255 /* 155 */ CWORD_CWORD_CWORD_CWORD,
3256 /* 156 */ CWORD_CWORD_CWORD_CWORD,
3257 /* 157 */ CWORD_CWORD_CWORD_CWORD,
3258 /* 158 */ CWORD_CWORD_CWORD_CWORD,
3259 /* 159 */ CWORD_CWORD_CWORD_CWORD,
3260 /* 160 */ CWORD_CWORD_CWORD_CWORD,
3261 /* 161 */ CWORD_CWORD_CWORD_CWORD,
3262 /* 162 */ CWORD_CWORD_CWORD_CWORD,
3263 /* 163 */ CWORD_CWORD_CWORD_CWORD,
3264 /* 164 */ CWORD_CWORD_CWORD_CWORD,
3265 /* 165 */ CWORD_CWORD_CWORD_CWORD,
3266 /* 166 */ CWORD_CWORD_CWORD_CWORD,
3267 /* 167 */ CWORD_CWORD_CWORD_CWORD,
3268 /* 168 */ CWORD_CWORD_CWORD_CWORD,
3269 /* 169 */ CWORD_CWORD_CWORD_CWORD,
3270 /* 170 */ CWORD_CWORD_CWORD_CWORD,
3271 /* 171 */ CWORD_CWORD_CWORD_CWORD,
3272 /* 172 */ CWORD_CWORD_CWORD_CWORD,
3273 /* 173 */ CWORD_CWORD_CWORD_CWORD,
3274 /* 174 */ CWORD_CWORD_CWORD_CWORD,
3275 /* 175 */ CWORD_CWORD_CWORD_CWORD,
3276 /* 176 */ CWORD_CWORD_CWORD_CWORD,
3277 /* 177 */ CWORD_CWORD_CWORD_CWORD,
3278 /* 178 */ CWORD_CWORD_CWORD_CWORD,
3279 /* 179 */ CWORD_CWORD_CWORD_CWORD,
3280 /* 180 */ CWORD_CWORD_CWORD_CWORD,
3281 /* 181 */ CWORD_CWORD_CWORD_CWORD,
3282 /* 182 */ CWORD_CWORD_CWORD_CWORD,
3283 /* 183 */ CWORD_CWORD_CWORD_CWORD,
3284 /* 184 */ CWORD_CWORD_CWORD_CWORD,
3285 /* 185 */ CWORD_CWORD_CWORD_CWORD,
3286 /* 186 */ CWORD_CWORD_CWORD_CWORD,
3287 /* 187 */ CWORD_CWORD_CWORD_CWORD,
3288 /* 188 */ CWORD_CWORD_CWORD_CWORD,
3289 /* 189 */ CWORD_CWORD_CWORD_CWORD,
3290 /* 190 */ CWORD_CWORD_CWORD_CWORD,
3291 /* 191 */ CWORD_CWORD_CWORD_CWORD,
3292 /* 192 */ CWORD_CWORD_CWORD_CWORD,
3293 /* 193 */ CWORD_CWORD_CWORD_CWORD,
3294 /* 194 */ CWORD_CWORD_CWORD_CWORD,
3295 /* 195 */ CWORD_CWORD_CWORD_CWORD,
3296 /* 196 */ CWORD_CWORD_CWORD_CWORD,
3297 /* 197 */ CWORD_CWORD_CWORD_CWORD,
3298 /* 198 */ CWORD_CWORD_CWORD_CWORD,
3299 /* 199 */ CWORD_CWORD_CWORD_CWORD,
3300 /* 200 */ CWORD_CWORD_CWORD_CWORD,
3301 /* 201 */ CWORD_CWORD_CWORD_CWORD,
3302 /* 202 */ CWORD_CWORD_CWORD_CWORD,
3303 /* 203 */ CWORD_CWORD_CWORD_CWORD,
3304 /* 204 */ CWORD_CWORD_CWORD_CWORD,
3305 /* 205 */ CWORD_CWORD_CWORD_CWORD,
3306 /* 206 */ CWORD_CWORD_CWORD_CWORD,
3307 /* 207 */ CWORD_CWORD_CWORD_CWORD,
3308 /* 208 */ CWORD_CWORD_CWORD_CWORD,
3309 /* 209 */ CWORD_CWORD_CWORD_CWORD,
3310 /* 210 */ CWORD_CWORD_CWORD_CWORD,
3311 /* 211 */ CWORD_CWORD_CWORD_CWORD,
3312 /* 212 */ CWORD_CWORD_CWORD_CWORD,
3313 /* 213 */ CWORD_CWORD_CWORD_CWORD,
3314 /* 214 */ CWORD_CWORD_CWORD_CWORD,
3315 /* 215 */ CWORD_CWORD_CWORD_CWORD,
3316 /* 216 */ CWORD_CWORD_CWORD_CWORD,
3317 /* 217 */ CWORD_CWORD_CWORD_CWORD,
3318 /* 218 */ CWORD_CWORD_CWORD_CWORD,
3319 /* 219 */ CWORD_CWORD_CWORD_CWORD,
3320 /* 220 */ CWORD_CWORD_CWORD_CWORD,
3321 /* 221 */ CWORD_CWORD_CWORD_CWORD,
3322 /* 222 */ CWORD_CWORD_CWORD_CWORD,
3323 /* 223 */ CWORD_CWORD_CWORD_CWORD,
3324 /* 224 */ CWORD_CWORD_CWORD_CWORD,
3325 /* 225 */ CWORD_CWORD_CWORD_CWORD,
3326 /* 226 */ CWORD_CWORD_CWORD_CWORD,
3327 /* 227 */ CWORD_CWORD_CWORD_CWORD,
3328 /* 228 */ CWORD_CWORD_CWORD_CWORD,
3329 /* 229 */ CWORD_CWORD_CWORD_CWORD,
3330 /* 230 */ CWORD_CWORD_CWORD_CWORD,
3331 /* 231 */ CWORD_CWORD_CWORD_CWORD,
3332 /* 232 */ CWORD_CWORD_CWORD_CWORD,
3333 /* 233 */ CWORD_CWORD_CWORD_CWORD,
3334 /* 234 */ CWORD_CWORD_CWORD_CWORD,
3335 /* 235 */ CWORD_CWORD_CWORD_CWORD,
3336 /* 236 */ CWORD_CWORD_CWORD_CWORD,
3337 /* 237 */ CWORD_CWORD_CWORD_CWORD,
3338 /* 238 */ CWORD_CWORD_CWORD_CWORD,
3339 /* 239 */ CWORD_CWORD_CWORD_CWORD,
3340 /* 230 */ CWORD_CWORD_CWORD_CWORD,
3341 /* 241 */ CWORD_CWORD_CWORD_CWORD,
3342 /* 242 */ CWORD_CWORD_CWORD_CWORD,
3343 /* 243 */ CWORD_CWORD_CWORD_CWORD,
3344 /* 244 */ CWORD_CWORD_CWORD_CWORD,
3345 /* 245 */ CWORD_CWORD_CWORD_CWORD,
3346 /* 246 */ CWORD_CWORD_CWORD_CWORD,
3347 /* 247 */ CWORD_CWORD_CWORD_CWORD,
3348 /* 248 */ CWORD_CWORD_CWORD_CWORD,
3349 /* 249 */ CWORD_CWORD_CWORD_CWORD,
3350 /* 250 */ CWORD_CWORD_CWORD_CWORD,
3351 /* 251 */ CWORD_CWORD_CWORD_CWORD,
3352 /* 252 */ CWORD_CWORD_CWORD_CWORD,
3353 /* 253 */ CWORD_CWORD_CWORD_CWORD,
3354 /* 254 */ CWORD_CWORD_CWORD_CWORD,
3355 /* 255 */ CWORD_CWORD_CWORD_CWORD,
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01003356 /* PEOF */ CENDFILE_CENDFILE_CENDFILE_CENDFILE,
Denys Vlasenkocd716832009-11-28 22:14:02 +01003357# if ENABLE_ASH_ALIAS
3358 /* PEOA */ CSPCL_CIGN_CIGN_CIGN,
3359# endif
Eric Andersen2870d962001-07-02 17:27:21 +00003360};
3361
Denys Vlasenko2fe66b12016-12-12 17:39:12 +01003362#if 1
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01003363# define SIT(c, syntax) ((S_I_T[syntax_index_table[c]] >> ((syntax)*4)) & 0xf)
Denys Vlasenko2fe66b12016-12-12 17:39:12 +01003364#else /* debug version, caught one signed char bug */
3365# define SIT(c, syntax) \
3366 ({ \
3367 if ((c) < 0 || (c) > (PEOF + ENABLE_ASH_ALIAS)) \
3368 bb_error_msg_and_die("line:%d c:%d", __LINE__, (c)); \
Denys Vlasenko0b883582016-12-23 16:49:07 +01003369 if ((syntax) < 0 || (syntax) > (2 + ENABLE_FEATURE_SH_MATH)) \
Denys Vlasenko2fe66b12016-12-12 17:39:12 +01003370 bb_error_msg_and_die("line:%d c:%d", __LINE__, (c)); \
3371 ((S_I_T[syntax_index_table[c]] >> ((syntax)*4)) & 0xf); \
3372 })
3373#endif
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00003374
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01003375#endif /* !USE_SIT_FUNCTION */
Eric Andersenc470f442003-07-28 09:56:35 +00003376
Eric Andersen2870d962001-07-02 17:27:21 +00003377
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003378/* ============ Alias handling */
Denis Vlasenkofc06f292007-02-23 21:09:35 +00003379
Denis Vlasenko131ae172007-02-18 13:00:19 +00003380#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003381
3382#define ALIASINUSE 1
3383#define ALIASDEAD 2
3384
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00003385struct alias {
3386 struct alias *next;
3387 char *name;
3388 char *val;
3389 int flag;
3390};
3391
Denis Vlasenko01631112007-12-16 17:20:38 +00003392
3393static struct alias **atab; // [ATABSIZE];
3394#define INIT_G_alias() do { \
3395 atab = xzalloc(ATABSIZE * sizeof(atab[0])); \
3396} while (0)
3397
Eric Andersen2870d962001-07-02 17:27:21 +00003398
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00003399static struct alias **
Denys Vlasenko37dc08b2016-10-02 04:38:07 +02003400__lookupalias(const char *name)
3401{
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00003402 unsigned int hashval;
3403 struct alias **app;
3404 const char *p;
3405 unsigned int ch;
3406
3407 p = name;
3408
3409 ch = (unsigned char)*p;
3410 hashval = ch << 4;
3411 while (ch) {
3412 hashval += ch;
3413 ch = (unsigned char)*++p;
3414 }
3415 app = &atab[hashval % ATABSIZE];
3416
3417 for (; *app; app = &(*app)->next) {
3418 if (strcmp(name, (*app)->name) == 0) {
3419 break;
3420 }
3421 }
3422
3423 return app;
3424}
3425
3426static struct alias *
3427lookupalias(const char *name, int check)
3428{
3429 struct alias *ap = *__lookupalias(name);
3430
3431 if (check && ap && (ap->flag & ALIASINUSE))
3432 return NULL;
3433 return ap;
3434}
3435
3436static struct alias *
3437freealias(struct alias *ap)
3438{
3439 struct alias *next;
3440
3441 if (ap->flag & ALIASINUSE) {
3442 ap->flag |= ALIASDEAD;
3443 return ap;
3444 }
3445
3446 next = ap->next;
3447 free(ap->name);
3448 free(ap->val);
3449 free(ap);
3450 return next;
3451}
Eric Andersencb57d552001-06-28 07:25:16 +00003452
Eric Andersenc470f442003-07-28 09:56:35 +00003453static void
3454setalias(const char *name, const char *val)
Eric Andersencb57d552001-06-28 07:25:16 +00003455{
3456 struct alias *ap, **app;
3457
3458 app = __lookupalias(name);
3459 ap = *app;
Denis Vlasenkob012b102007-02-19 22:43:01 +00003460 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003461 if (ap) {
3462 if (!(ap->flag & ALIASINUSE)) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00003463 free(ap->val);
Eric Andersencb57d552001-06-28 07:25:16 +00003464 }
Denis Vlasenko0c032a42007-02-23 01:03:40 +00003465 ap->val = ckstrdup(val);
Eric Andersencb57d552001-06-28 07:25:16 +00003466 ap->flag &= ~ALIASDEAD;
3467 } else {
3468 /* not found */
Denis Vlasenko597906c2008-02-20 16:38:54 +00003469 ap = ckzalloc(sizeof(struct alias));
Denis Vlasenko0c032a42007-02-23 01:03:40 +00003470 ap->name = ckstrdup(name);
3471 ap->val = ckstrdup(val);
Denis Vlasenko597906c2008-02-20 16:38:54 +00003472 /*ap->flag = 0; - ckzalloc did it */
3473 /*ap->next = NULL;*/
Eric Andersencb57d552001-06-28 07:25:16 +00003474 *app = ap;
3475 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00003476 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00003477}
3478
Eric Andersenc470f442003-07-28 09:56:35 +00003479static int
3480unalias(const char *name)
Eric Andersen2870d962001-07-02 17:27:21 +00003481{
Eric Andersencb57d552001-06-28 07:25:16 +00003482 struct alias **app;
3483
3484 app = __lookupalias(name);
3485
3486 if (*app) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00003487 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003488 *app = freealias(*app);
Denis Vlasenkob012b102007-02-19 22:43:01 +00003489 INT_ON;
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003490 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003491 }
3492
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003493 return 1;
Eric Andersencb57d552001-06-28 07:25:16 +00003494}
3495
Eric Andersenc470f442003-07-28 09:56:35 +00003496static void
3497rmaliases(void)
Eric Andersen2870d962001-07-02 17:27:21 +00003498{
Eric Andersencb57d552001-06-28 07:25:16 +00003499 struct alias *ap, **app;
3500 int i;
3501
Denis Vlasenkob012b102007-02-19 22:43:01 +00003502 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003503 for (i = 0; i < ATABSIZE; i++) {
3504 app = &atab[i];
3505 for (ap = *app; ap; ap = *app) {
3506 *app = freealias(*app);
3507 if (ap == *app) {
3508 app = &ap->next;
3509 }
3510 }
3511 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00003512 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00003513}
3514
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003515static void
3516printalias(const struct alias *ap)
3517{
3518 out1fmt("%s=%s\n", ap->name, single_quote(ap->val));
3519}
3520
Eric Andersencb57d552001-06-28 07:25:16 +00003521/*
3522 * TODO - sort output
3523 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02003524static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00003525aliascmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00003526{
3527 char *n, *v;
3528 int ret = 0;
3529 struct alias *ap;
3530
Denis Vlasenko68404f12008-03-17 09:00:54 +00003531 if (!argv[1]) {
Eric Andersencb57d552001-06-28 07:25:16 +00003532 int i;
3533
Denis Vlasenko68404f12008-03-17 09:00:54 +00003534 for (i = 0; i < ATABSIZE; i++) {
Eric Andersencb57d552001-06-28 07:25:16 +00003535 for (ap = atab[i]; ap; ap = ap->next) {
3536 printalias(ap);
3537 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00003538 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003539 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003540 }
3541 while ((n = *++argv) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00003542 v = strchr(n+1, '=');
3543 if (v == NULL) { /* n+1: funny ksh stuff */
3544 ap = *__lookupalias(n);
3545 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +00003546 fprintf(stderr, "%s: %s not found\n", "alias", n);
Eric Andersencb57d552001-06-28 07:25:16 +00003547 ret = 1;
3548 } else
3549 printalias(ap);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003550 } else {
Eric Andersencb57d552001-06-28 07:25:16 +00003551 *v++ = '\0';
3552 setalias(n, v);
3553 }
3554 }
3555
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003556 return ret;
Eric Andersencb57d552001-06-28 07:25:16 +00003557}
3558
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02003559static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00003560unaliascmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +00003561{
3562 int i;
3563
Denys Vlasenko6c149f42017-04-12 21:31:32 +02003564 while (nextopt("a") != '\0') {
3565 rmaliases();
3566 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003567 }
3568 for (i = 0; *argptr; argptr++) {
3569 if (unalias(*argptr)) {
Eric Andersenc470f442003-07-28 09:56:35 +00003570 fprintf(stderr, "%s: %s not found\n", "unalias", *argptr);
Eric Andersencb57d552001-06-28 07:25:16 +00003571 i = 1;
3572 }
3573 }
3574
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003575 return i;
Eric Andersencb57d552001-06-28 07:25:16 +00003576}
Denis Vlasenkofc06f292007-02-23 21:09:35 +00003577
Denis Vlasenko131ae172007-02-18 13:00:19 +00003578#endif /* ASH_ALIAS */
Eric Andersencb57d552001-06-28 07:25:16 +00003579
Eric Andersenc470f442003-07-28 09:56:35 +00003580
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003581/* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */
Denys Vlasenko285ad152009-12-04 23:02:27 +01003582#define FORK_FG 0
3583#define FORK_BG 1
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003584#define FORK_NOJOB 2
3585
3586/* mode flags for showjob(s) */
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02003587#define SHOW_ONLY_PGID 0x01 /* show only pgid (jobs -p) */
3588#define SHOW_PIDS 0x02 /* show individual pids, not just one line per job */
3589#define SHOW_CHANGED 0x04 /* only jobs whose state has changed */
Denys Vlasenko9c541002015-10-07 15:44:36 +02003590#define SHOW_STDERR 0x08 /* print to stderr (else stdout) */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003591
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003592/*
3593 * A job structure contains information about a job. A job is either a
3594 * single process or a set of processes contained in a pipeline. In the
3595 * latter case, pidlist will be non-NULL, and will point to a -1 terminated
3596 * array of pids.
3597 */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003598struct procstat {
Denys Vlasenko285ad152009-12-04 23:02:27 +01003599 pid_t ps_pid; /* process id */
3600 int ps_status; /* last process status from wait() */
3601 char *ps_cmd; /* text of command being run */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003602};
3603
3604struct job {
3605 struct procstat ps0; /* status of process */
Denys Vlasenko966f0872019-03-27 15:51:42 +01003606 struct procstat *ps; /* status of processes when more than one */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003607#if JOBS
3608 int stopstatus; /* status of a stopped job */
3609#endif
Denys Vlasenko4c179372017-01-11 18:44:15 +01003610 unsigned nprocs; /* number of processes */
3611
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003612#define JOBRUNNING 0 /* at least one proc running */
3613#define JOBSTOPPED 1 /* all procs are stopped */
3614#define JOBDONE 2 /* all procs are completed */
Denys Vlasenko4c179372017-01-11 18:44:15 +01003615 unsigned
3616 state: 8,
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003617#if JOBS
3618 sigint: 1, /* job was killed by SIGINT */
3619 jobctl: 1, /* job running under job control */
3620#endif
3621 waited: 1, /* true if this entry has been waited for */
3622 used: 1, /* true if this entry is in used */
3623 changed: 1; /* true if status has changed */
3624 struct job *prev_job; /* previous job */
3625};
3626
Denis Vlasenko68404f12008-03-17 09:00:54 +00003627static struct job *makejob(/*union node *,*/ int);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003628static int forkshell(struct job *, union node *, int);
3629static int waitforjob(struct job *);
3630
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003631#if !JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003632enum { doing_jobctl = 0 };
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003633#define setjobctl(on) do {} while (0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003634#else
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003635static smallint doing_jobctl; //references:8
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003636static void setjobctl(int);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003637#endif
3638
3639/*
Denis Vlasenko4b875702009-03-19 13:30:04 +00003640 * Ignore a signal.
3641 */
3642static void
3643ignoresig(int signo)
3644{
3645 /* Avoid unnecessary system calls. Is it already SIG_IGNed? */
3646 if (sigmode[signo - 1] != S_IGN && sigmode[signo - 1] != S_HARD_IGN) {
3647 /* No, need to do it */
3648 signal(signo, SIG_IGN);
3649 }
3650 sigmode[signo - 1] = S_HARD_IGN;
3651}
3652
3653/*
Denys Vlasenko238bf182010-05-18 15:49:07 +02003654 * Only one usage site - in setsignal()
Denis Vlasenko4b875702009-03-19 13:30:04 +00003655 */
3656static void
Denys Vlasenko238bf182010-05-18 15:49:07 +02003657signal_handler(int signo)
Denis Vlasenko4b875702009-03-19 13:30:04 +00003658{
Denys Vlasenko458c1f22016-10-27 23:51:19 +02003659 if (signo == SIGCHLD) {
3660 got_sigchld = 1;
3661 if (!trap[SIGCHLD])
3662 return;
3663 }
3664
Denis Vlasenko4b875702009-03-19 13:30:04 +00003665 gotsig[signo - 1] = 1;
Denys Vlasenko458c1f22016-10-27 23:51:19 +02003666 pending_sig = signo;
Denis Vlasenko4b875702009-03-19 13:30:04 +00003667
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02003668 if (signo == SIGINT && !trap[SIGINT]) {
3669 if (!suppress_int) {
3670 pending_sig = 0;
Denis Vlasenko4b875702009-03-19 13:30:04 +00003671 raise_interrupt(); /* does not return */
3672 }
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02003673 pending_int = 1;
Denis Vlasenko4b875702009-03-19 13:30:04 +00003674 }
3675}
3676
3677/*
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003678 * Set the signal handler for the specified signal. The routine figures
3679 * out what it should be set to.
3680 */
3681static void
3682setsignal(int signo)
3683{
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003684 char *t;
3685 char cur_act, new_act;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003686 struct sigaction act;
3687
3688 t = trap[signo];
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003689 new_act = S_DFL;
3690 if (t != NULL) { /* trap for this sig is set */
3691 new_act = S_CATCH;
3692 if (t[0] == '\0') /* trap is "": ignore this sig */
3693 new_act = S_IGN;
3694 }
3695
3696 if (rootshell && new_act == S_DFL) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003697 switch (signo) {
3698 case SIGINT:
3699 if (iflag || minusc || sflag == 0)
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003700 new_act = S_CATCH;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003701 break;
3702 case SIGQUIT:
3703#if DEBUG
3704 if (debug)
3705 break;
3706#endif
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003707 /* man bash:
3708 * "In all cases, bash ignores SIGQUIT. Non-builtin
3709 * commands run by bash have signal handlers
3710 * set to the values inherited by the shell
3711 * from its parent". */
3712 new_act = S_IGN;
3713 break;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003714 case SIGTERM:
3715 if (iflag)
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003716 new_act = S_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003717 break;
3718#if JOBS
3719 case SIGTSTP:
3720 case SIGTTOU:
3721 if (mflag)
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003722 new_act = S_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003723 break;
3724#endif
3725 }
3726 }
Denys Vlasenko49e6bf22017-08-04 14:28:16 +02003727 /* if !rootshell, we reset SIGQUIT to DFL,
3728 * whereas we have to restore it to what shell got on entry.
3729 * This is handled by the fact that if signal was IGNored on entry,
3730 * then cur_act is S_HARD_IGN and we never change its sigaction
3731 * (see code below).
3732 */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003733
Denys Vlasenko458c1f22016-10-27 23:51:19 +02003734 if (signo == SIGCHLD)
3735 new_act = S_CATCH;
3736
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003737 t = &sigmode[signo - 1];
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003738 cur_act = *t;
3739 if (cur_act == 0) {
3740 /* current setting is not yet known */
3741 if (sigaction(signo, NULL, &act)) {
3742 /* pretend it worked; maybe we should give a warning,
3743 * but other shells don't. We don't alter sigmode,
3744 * so we retry every time.
3745 * btw, in Linux it never fails. --vda */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003746 return;
3747 }
3748 if (act.sa_handler == SIG_IGN) {
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003749 cur_act = S_HARD_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003750 if (mflag
3751 && (signo == SIGTSTP || signo == SIGTTIN || signo == SIGTTOU)
3752 ) {
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003753 cur_act = S_IGN; /* don't hard ignore these */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003754 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003755 }
Denys Vlasenko0f14f412017-08-06 20:06:19 +02003756 if (act.sa_handler == SIG_DFL && new_act == S_DFL) {
3757 /* installing SIG_DFL over SIG_DFL is a no-op */
3758 /* saves one sigaction call in each "sh -c SCRIPT" invocation */
3759 *t = S_DFL;
3760 return;
3761 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003762 }
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003763 if (cur_act == S_HARD_IGN || cur_act == new_act)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003764 return;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003765
Denys Vlasenko49e6bf22017-08-04 14:28:16 +02003766 *t = new_act;
3767
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003768 act.sa_handler = SIG_DFL;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003769 switch (new_act) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003770 case S_CATCH:
Denys Vlasenko238bf182010-05-18 15:49:07 +02003771 act.sa_handler = signal_handler;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003772 break;
3773 case S_IGN:
3774 act.sa_handler = SIG_IGN;
3775 break;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003776 }
Ian Wienand89b3cba2011-04-16 20:05:14 +02003777 /* flags and mask matter only if !DFL and !IGN, but we do it
3778 * for all cases for more deterministic behavior:
3779 */
Denys Vlasenko49e6bf22017-08-04 14:28:16 +02003780 act.sa_flags = 0; //TODO: why not SA_RESTART?
Ian Wienand89b3cba2011-04-16 20:05:14 +02003781 sigfillset(&act.sa_mask);
3782
Denis Vlasenko8e2cfec2008-03-12 23:19:35 +00003783 sigaction_set(signo, &act);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003784}
3785
3786/* mode flags for set_curjob */
3787#define CUR_DELETE 2
3788#define CUR_RUNNING 1
3789#define CUR_STOPPED 0
3790
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003791#if JOBS
3792/* pgrp of shell on invocation */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003793static int initialpgrp; //references:2
3794static int ttyfd = -1; //5
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003795#endif
3796/* array of jobs */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003797static struct job *jobtab; //5
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003798/* size of array */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003799static unsigned njobs; //4
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003800/* current job */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003801static struct job *curjob; //lots
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003802
Denys Vlasenko098b7132017-01-11 19:59:03 +01003803#if 0
3804/* Bash has a feature: it restores termios after a successful wait for
3805 * a foreground job which had at least one stopped or sigkilled member.
3806 * The probable rationale is that SIGSTOP and SIGKILL can preclude task from
3807 * properly restoring tty state. Should we do this too?
3808 * A reproducer: ^Z an interactive python:
3809 *
3810 * # python
3811 * Python 2.7.12 (...)
3812 * >>> ^Z
3813 * { python leaves tty in -icanon -echo state. We do survive that... }
3814 * [1]+ Stopped python
3815 * { ...however, next program (python #2) does not survive it well: }
3816 * # python
3817 * Python 2.7.12 (...)
3818 * >>> Traceback (most recent call last):
3819 * { above, I typed "qwerty<CR>", but -echo state is still in effect }
3820 * File "<stdin>", line 1, in <module>
3821 * NameError: name 'qwerty' is not defined
3822 *
3823 * The implementation below is modeled on bash code and seems to work.
3824 * However, I'm not sure we should do this. For one: what if I'd fg
3825 * the stopped python instead? It'll be confused by "restored" tty state.
3826 */
3827static struct termios shell_tty_info;
3828static void
3829get_tty_state(void)
3830{
3831 if (rootshell && ttyfd >= 0)
3832 tcgetattr(ttyfd, &shell_tty_info);
3833}
3834static void
3835set_tty_state(void)
3836{
3837 /* if (rootshell) - caller ensures this */
3838 if (ttyfd >= 0)
3839 tcsetattr(ttyfd, TCSADRAIN, &shell_tty_info);
3840}
3841static int
3842job_signal_status(struct job *jp)
3843{
3844 int status;
3845 unsigned i;
3846 struct procstat *ps = jp->ps;
3847 for (i = 0; i < jp->nprocs; i++) {
3848 status = ps[i].ps_status;
3849 if (WIFSIGNALED(status) || WIFSTOPPED(status))
3850 return status;
3851 }
3852 return 0;
3853}
3854static void
3855restore_tty_if_stopped_or_signaled(struct job *jp)
3856{
3857//TODO: check what happens if we come from waitforjob() in expbackq()
3858 if (rootshell) {
3859 int s = job_signal_status(jp);
3860 if (s) /* WIFSIGNALED(s) || WIFSTOPPED(s) */
3861 set_tty_state();
3862 }
3863}
3864#else
3865# define get_tty_state() ((void)0)
3866# define restore_tty_if_stopped_or_signaled(jp) ((void)0)
3867#endif
3868
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003869static void
3870set_curjob(struct job *jp, unsigned mode)
3871{
3872 struct job *jp1;
3873 struct job **jpp, **curp;
3874
3875 /* first remove from list */
3876 jpp = curp = &curjob;
Denys Vlasenko940c7202011-03-02 04:07:14 +01003877 while (1) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003878 jp1 = *jpp;
3879 if (jp1 == jp)
3880 break;
3881 jpp = &jp1->prev_job;
Denys Vlasenko940c7202011-03-02 04:07:14 +01003882 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003883 *jpp = jp1->prev_job;
3884
3885 /* Then re-insert in correct position */
3886 jpp = curp;
3887 switch (mode) {
3888 default:
3889#if DEBUG
3890 abort();
3891#endif
3892 case CUR_DELETE:
3893 /* job being deleted */
3894 break;
3895 case CUR_RUNNING:
3896 /* newly created job or backgrounded job,
Denys Vlasenko60cb48c2013-01-14 15:57:44 +01003897 * put after all stopped jobs.
3898 */
Denys Vlasenko940c7202011-03-02 04:07:14 +01003899 while (1) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003900 jp1 = *jpp;
3901#if JOBS
3902 if (!jp1 || jp1->state != JOBSTOPPED)
3903#endif
3904 break;
3905 jpp = &jp1->prev_job;
Denys Vlasenko940c7202011-03-02 04:07:14 +01003906 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003907 /* FALLTHROUGH */
3908#if JOBS
3909 case CUR_STOPPED:
3910#endif
3911 /* newly stopped job - becomes curjob */
3912 jp->prev_job = *jpp;
3913 *jpp = jp;
3914 break;
3915 }
3916}
3917
3918#if JOBS || DEBUG
3919static int
3920jobno(const struct job *jp)
3921{
3922 return jp - jobtab + 1;
3923}
3924#endif
3925
3926/*
3927 * Convert a job name to a job structure.
3928 */
Denis Vlasenko85c24712008-03-17 09:04:04 +00003929#if !JOBS
3930#define getjob(name, getctl) getjob(name)
3931#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003932static struct job *
3933getjob(const char *name, int getctl)
3934{
3935 struct job *jp;
3936 struct job *found;
Denys Vlasenkoffc39202009-08-17 02:12:20 +02003937 const char *err_msg = "%s: no such job";
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003938 unsigned num;
3939 int c;
3940 const char *p;
3941 char *(*match)(const char *, const char *);
3942
3943 jp = curjob;
3944 p = name;
3945 if (!p)
3946 goto currentjob;
3947
3948 if (*p != '%')
3949 goto err;
3950
3951 c = *++p;
3952 if (!c)
3953 goto currentjob;
3954
3955 if (!p[1]) {
3956 if (c == '+' || c == '%') {
3957 currentjob:
3958 err_msg = "No current job";
3959 goto check;
3960 }
3961 if (c == '-') {
3962 if (jp)
3963 jp = jp->prev_job;
3964 err_msg = "No previous job";
3965 check:
3966 if (!jp)
3967 goto err;
3968 goto gotit;
3969 }
3970 }
3971
3972 if (is_number(p)) {
3973 num = atoi(p);
Denys Vlasenko46a45ce2016-09-29 01:10:08 +02003974 if (num > 0 && num <= njobs) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003975 jp = jobtab + num - 1;
3976 if (jp->used)
3977 goto gotit;
3978 goto err;
3979 }
3980 }
3981
3982 match = prefix;
3983 if (*p == '?') {
3984 match = strstr;
3985 p++;
3986 }
3987
Denys Vlasenkoffc39202009-08-17 02:12:20 +02003988 found = NULL;
3989 while (jp) {
Denys Vlasenko285ad152009-12-04 23:02:27 +01003990 if (match(jp->ps[0].ps_cmd, p)) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003991 if (found)
3992 goto err;
3993 found = jp;
3994 err_msg = "%s: ambiguous";
3995 }
3996 jp = jp->prev_job;
3997 }
Denys Vlasenkoffc39202009-08-17 02:12:20 +02003998 if (!found)
3999 goto err;
4000 jp = found;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004001
4002 gotit:
4003#if JOBS
4004 err_msg = "job %s not created under job control";
4005 if (getctl && jp->jobctl == 0)
4006 goto err;
4007#endif
4008 return jp;
4009 err:
4010 ash_msg_and_raise_error(err_msg, name);
4011}
4012
4013/*
4014 * Mark a job structure as unused.
4015 */
4016static void
4017freejob(struct job *jp)
4018{
4019 struct procstat *ps;
4020 int i;
4021
4022 INT_OFF;
4023 for (i = jp->nprocs, ps = jp->ps; --i >= 0; ps++) {
Denys Vlasenko285ad152009-12-04 23:02:27 +01004024 if (ps->ps_cmd != nullstr)
4025 free(ps->ps_cmd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004026 }
4027 if (jp->ps != &jp->ps0)
4028 free(jp->ps);
4029 jp->used = 0;
4030 set_curjob(jp, CUR_DELETE);
4031 INT_ON;
4032}
4033
4034#if JOBS
4035static void
4036xtcsetpgrp(int fd, pid_t pgrp)
4037{
4038 if (tcsetpgrp(fd, pgrp))
Ron Yorstonbe366e52017-07-27 13:53:39 +01004039 ash_msg_and_raise_perror("can't set tty process group");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004040}
4041
4042/*
4043 * Turn job control on and off.
4044 *
4045 * Note: This code assumes that the third arg to ioctl is a character
4046 * pointer, which is true on Berkeley systems but not System V. Since
4047 * System V doesn't have job control yet, this isn't a problem now.
4048 *
4049 * Called with interrupts off.
4050 */
4051static void
4052setjobctl(int on)
4053{
4054 int fd;
4055 int pgrp;
4056
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004057 if (on == doing_jobctl || rootshell == 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004058 return;
4059 if (on) {
4060 int ofd;
4061 ofd = fd = open(_PATH_TTY, O_RDWR);
4062 if (fd < 0) {
4063 /* BTW, bash will try to open(ttyname(0)) if open("/dev/tty") fails.
4064 * That sometimes helps to acquire controlling tty.
4065 * Obviously, a workaround for bugs when someone
4066 * failed to provide a controlling tty to bash! :) */
Denis Vlasenkoed270a52007-11-26 05:37:07 +00004067 fd = 2;
4068 while (!isatty(fd))
4069 if (--fd < 0)
4070 goto out;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004071 }
Denys Vlasenko64774602016-10-26 15:24:30 +02004072 /* fd is a tty at this point */
Denys Vlasenko60fb98e2018-03-30 22:15:14 +02004073 fd = fcntl(fd, F_DUPFD_CLOEXEC, 10);
Denys Vlasenko10ad6222017-04-17 16:13:32 +02004074 if (ofd >= 0) /* if it is "/dev/tty", close. If 0/1/2, don't */
Denis Vlasenkoed270a52007-11-26 05:37:07 +00004075 close(ofd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004076 if (fd < 0)
Denys Vlasenko64774602016-10-26 15:24:30 +02004077 goto out; /* F_DUPFD failed */
Denys Vlasenko60fb98e2018-03-30 22:15:14 +02004078 if (F_DUPFD_CLOEXEC == F_DUPFD) /* if old libc (w/o F_DUPFD_CLOEXEC) */
4079 close_on_exec_on(fd);
Denys Vlasenko940c7202011-03-02 04:07:14 +01004080 while (1) { /* while we are in the background */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004081 pgrp = tcgetpgrp(fd);
4082 if (pgrp < 0) {
4083 out:
4084 ash_msg("can't access tty; job control turned off");
4085 mflag = on = 0;
4086 goto close;
4087 }
4088 if (pgrp == getpgrp())
4089 break;
4090 killpg(0, SIGTTIN);
Denys Vlasenko940c7202011-03-02 04:07:14 +01004091 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004092 initialpgrp = pgrp;
4093
4094 setsignal(SIGTSTP);
4095 setsignal(SIGTTOU);
4096 setsignal(SIGTTIN);
4097 pgrp = rootpid;
4098 setpgid(0, pgrp);
4099 xtcsetpgrp(fd, pgrp);
4100 } else {
4101 /* turning job control off */
4102 fd = ttyfd;
4103 pgrp = initialpgrp;
Denis Vlasenko08c8c1d2007-04-28 22:39:02 +00004104 /* was xtcsetpgrp, but this can make exiting ash
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004105 * loop forever if pty is already deleted */
Denis Vlasenko08c8c1d2007-04-28 22:39:02 +00004106 tcsetpgrp(fd, pgrp);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004107 setpgid(0, pgrp);
4108 setsignal(SIGTSTP);
4109 setsignal(SIGTTOU);
4110 setsignal(SIGTTIN);
4111 close:
Denis Vlasenkoed270a52007-11-26 05:37:07 +00004112 if (fd >= 0)
4113 close(fd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004114 fd = -1;
4115 }
4116 ttyfd = fd;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004117 doing_jobctl = on;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004118}
4119
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02004120static int FAST_FUNC
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004121killcmd(int argc, char **argv)
4122{
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00004123 if (argv[1] && strcmp(argv[1], "-l") != 0) {
Denys Vlasenkob12553f2011-02-21 03:22:20 +01004124 int i = 1;
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00004125 do {
4126 if (argv[i][0] == '%') {
Denys Vlasenkob12553f2011-02-21 03:22:20 +01004127 /*
4128 * "kill %N" - job kill
4129 * Converting to pgrp / pid kill
4130 */
4131 struct job *jp;
4132 char *dst;
4133 int j, n;
4134
4135 jp = getjob(argv[i], 0);
4136 /*
4137 * In jobs started under job control, we signal
4138 * entire process group by kill -PGRP_ID.
4139 * This happens, f.e., in interactive shell.
4140 *
4141 * Otherwise, we signal each child via
4142 * kill PID1 PID2 PID3.
4143 * Testcases:
4144 * sh -c 'sleep 1|sleep 1 & kill %1'
4145 * sh -c 'true|sleep 2 & sleep 1; kill %1'
4146 * sh -c 'true|sleep 1 & sleep 2; kill %1'
4147 */
4148 n = jp->nprocs; /* can't be 0 (I hope) */
4149 if (jp->jobctl)
4150 n = 1;
4151 dst = alloca(n * sizeof(int)*4);
4152 argv[i] = dst;
4153 for (j = 0; j < n; j++) {
4154 struct procstat *ps = &jp->ps[j];
4155 /* Skip non-running and not-stopped members
4156 * (i.e. dead members) of the job
4157 */
4158 if (ps->ps_status != -1 && !WIFSTOPPED(ps->ps_status))
4159 continue;
4160 /*
4161 * kill_main has matching code to expect
4162 * leading space. Needed to not confuse
4163 * negative pids with "kill -SIGNAL_NO" syntax
4164 */
4165 dst += sprintf(dst, jp->jobctl ? " -%u" : " %u", (int)ps->ps_pid);
4166 }
4167 *dst = '\0';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004168 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00004169 } while (argv[++i]);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004170 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00004171 return kill_main(argc, argv);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004172}
4173
4174static void
Denys Vlasenko285ad152009-12-04 23:02:27 +01004175showpipe(struct job *jp /*, FILE *out*/)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004176{
Denys Vlasenko285ad152009-12-04 23:02:27 +01004177 struct procstat *ps;
4178 struct procstat *psend;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004179
Denys Vlasenko285ad152009-12-04 23:02:27 +01004180 psend = jp->ps + jp->nprocs;
4181 for (ps = jp->ps + 1; ps < psend; ps++)
4182 printf(" | %s", ps->ps_cmd);
Denys Vlasenko9c541002015-10-07 15:44:36 +02004183 newline_and_flush(stdout);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004184 flush_stdout_stderr();
4185}
4186
4187
4188static int
4189restartjob(struct job *jp, int mode)
4190{
4191 struct procstat *ps;
4192 int i;
4193 int status;
4194 pid_t pgid;
4195
4196 INT_OFF;
4197 if (jp->state == JOBDONE)
4198 goto out;
4199 jp->state = JOBRUNNING;
Denys Vlasenko285ad152009-12-04 23:02:27 +01004200 pgid = jp->ps[0].ps_pid;
Denys Vlasenko098b7132017-01-11 19:59:03 +01004201 if (mode == FORK_FG) {
4202 get_tty_state();
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004203 xtcsetpgrp(ttyfd, pgid);
Denys Vlasenko098b7132017-01-11 19:59:03 +01004204 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004205 killpg(pgid, SIGCONT);
4206 ps = jp->ps;
4207 i = jp->nprocs;
4208 do {
Denys Vlasenko285ad152009-12-04 23:02:27 +01004209 if (WIFSTOPPED(ps->ps_status)) {
4210 ps->ps_status = -1;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004211 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00004212 ps++;
4213 } while (--i);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004214 out:
4215 status = (mode == FORK_FG) ? waitforjob(jp) : 0;
4216 INT_ON;
4217 return status;
4218}
4219
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02004220static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00004221fg_bgcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004222{
4223 struct job *jp;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004224 int mode;
4225 int retval;
4226
4227 mode = (**argv == 'f') ? FORK_FG : FORK_BG;
4228 nextopt(nullstr);
4229 argv = argptr;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004230 do {
4231 jp = getjob(*argv, 1);
4232 if (mode == FORK_BG) {
4233 set_curjob(jp, CUR_RUNNING);
Denys Vlasenko285ad152009-12-04 23:02:27 +01004234 printf("[%d] ", jobno(jp));
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004235 }
Denys Vlasenko285ad152009-12-04 23:02:27 +01004236 out1str(jp->ps[0].ps_cmd);
4237 showpipe(jp /*, stdout*/);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004238 retval = restartjob(jp, mode);
4239 } while (*argv && *++argv);
4240 return retval;
4241}
4242#endif
4243
4244static int
Denys Vlasenko2bad3a32020-02-16 18:23:43 +01004245sprint_status48(char *os, int status, int sigonly)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004246{
Denys Vlasenko2bad3a32020-02-16 18:23:43 +01004247 char *s = os;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004248 int st;
4249
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004250 if (!WIFEXITED(status)) {
Johannes Schindelin9d4dc842017-07-14 22:25:58 +02004251#if JOBS
4252 if (WIFSTOPPED(status))
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004253 st = WSTOPSIG(status);
4254 else
Johannes Schindelin9d4dc842017-07-14 22:25:58 +02004255#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004256 st = WTERMSIG(status);
4257 if (sigonly) {
4258 if (st == SIGINT || st == SIGPIPE)
4259 goto out;
Johannes Schindelin9d4dc842017-07-14 22:25:58 +02004260#if JOBS
4261 if (WIFSTOPPED(status))
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004262 goto out;
Johannes Schindelin9d4dc842017-07-14 22:25:58 +02004263#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004264 }
4265 st &= 0x7f;
Denys Vlasenko33745b12021-02-18 13:44:27 +01004266 s = stpncpy(s, strsignal(st), 32);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004267 if (WCOREDUMP(status)) {
Denys Vlasenko2bad3a32020-02-16 18:23:43 +01004268 s = stpcpy(s, " (core dumped)");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004269 }
4270 } else if (!sigonly) {
4271 st = WEXITSTATUS(status);
Denys Vlasenko2bad3a32020-02-16 18:23:43 +01004272 s += fmtstr(s, 16, (st ? "Done(%d)" : "Done"), st);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004273 }
4274 out:
Denys Vlasenko2bad3a32020-02-16 18:23:43 +01004275 return s - os;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004276}
4277
Denys Vlasenko8f7b0242016-10-28 17:16:11 +02004278#define DOWAIT_NONBLOCK 0
4279#define DOWAIT_BLOCK 1
4280#define DOWAIT_BLOCK_OR_SIG 2
Ron Yorstone48559e2019-03-31 09:27:09 +01004281#if BASH_WAIT_N
Denys Vlasenko966f0872019-03-27 15:51:42 +01004282# define DOWAIT_JOBSTATUS 0x10 /* OR this to get job's exitstatus instead of pid */
4283#endif
Denys Vlasenko458c1f22016-10-27 23:51:19 +02004284
4285static int
Denys Vlasenko91e11eb2020-09-29 20:35:55 +02004286waitproc(int block, int *status)
4287{
4288 sigset_t oldmask;
4289 int flags = block == DOWAIT_BLOCK ? 0 : WNOHANG;
4290 int err;
4291
4292#if JOBS
4293 if (doing_jobctl)
4294 flags |= WUNTRACED;
4295#endif
4296
4297 do {
4298 got_sigchld = 0;
4299 do
4300 err = waitpid(-1, status, flags);
4301 while (err < 0 && errno == EINTR);
4302
4303 if (err || (err = -!block))
4304 break;
4305
4306 sigfillset(&oldmask);
4307 sigprocmask2(SIG_SETMASK, &oldmask); /* mask is updated */
4308 while (!got_sigchld && !pending_sig)
4309 sigsuspend(&oldmask);
4310 sigprocmask(SIG_SETMASK, &oldmask, NULL);
4311 //simpler, but unsafe: a signal can set pending_sig after check, but before pause():
4312 //while (!got_sigchld && !pending_sig)
4313 // pause();
4314
4315 } while (got_sigchld);
4316
4317 return err;
4318}
4319
4320static int
Denys Vlasenko47eb9792020-02-18 15:37:43 +01004321waitone(int block, struct job *job)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004322{
4323 int pid;
4324 int status;
4325 struct job *jp;
Denys Vlasenko91e11eb2020-09-29 20:35:55 +02004326 struct job *thisjob = NULL;
Ron Yorstone48559e2019-03-31 09:27:09 +01004327#if BASH_WAIT_N
Denys Vlasenko966f0872019-03-27 15:51:42 +01004328 bool want_jobexitstatus = (block & DOWAIT_JOBSTATUS);
4329 block = (block & ~DOWAIT_JOBSTATUS);
4330#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004331
Denys Vlasenkob543bda2016-10-27 20:08:28 +02004332 TRACE(("dowait(0x%x) called\n", block));
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00004333
Denys Vlasenko458c1f22016-10-27 23:51:19 +02004334 /* It's wrong to call waitpid() outside of INT_OFF region:
4335 * signal can arrive just after syscall return and handler can
4336 * longjmp away, losing stop/exit notification processing.
4337 * Thus, for "jobs" builtin, and for waiting for a fg job,
4338 * we call waitpid() (blocking or non-blocking) inside INT_OFF.
4339 *
4340 * However, for "wait" builtin it is wrong to simply call waitpid()
4341 * in INT_OFF region: "wait" needs to wait for any running job
4342 * to change state, but should exit on any trap too.
4343 * In INT_OFF region, a signal just before syscall entry can set
Denys Vlasenko8f7b0242016-10-28 17:16:11 +02004344 * pending_sig variables, but we can't check them, and we would
Denys Vlasenko458c1f22016-10-27 23:51:19 +02004345 * either enter a sleeping waitpid() (BUG), or need to busy-loop.
Denys Vlasenko8f7b0242016-10-28 17:16:11 +02004346 *
Denys Vlasenko458c1f22016-10-27 23:51:19 +02004347 * Because of this, we run inside INT_OFF, but use a special routine
Denys Vlasenko1ab7c2f2016-11-03 20:17:23 +01004348 * which combines waitpid() and sigsuspend().
Denys Vlasenko8f7b0242016-10-28 17:16:11 +02004349 * This is the reason why we need to have a handler for SIGCHLD:
Denys Vlasenko1ab7c2f2016-11-03 20:17:23 +01004350 * SIG_DFL handler does not wake sigsuspend().
Denys Vlasenko458c1f22016-10-27 23:51:19 +02004351 */
4352 INT_OFF;
Denys Vlasenko91e11eb2020-09-29 20:35:55 +02004353 pid = waitproc(block, &status);
4354 TRACE(("wait returns pid %d, status=%d\n", pid, status));
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004355 if (pid <= 0)
Denys Vlasenko458c1f22016-10-27 23:51:19 +02004356 goto out;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004357
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004358 for (jp = curjob; jp; jp = jp->prev_job) {
Denys Vlasenko4700fb52015-10-09 15:40:13 +02004359 int jobstate;
Denys Vlasenko285ad152009-12-04 23:02:27 +01004360 struct procstat *ps;
4361 struct procstat *psend;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004362 if (jp->state == JOBDONE)
4363 continue;
Denys Vlasenko4700fb52015-10-09 15:40:13 +02004364 jobstate = JOBDONE;
Denys Vlasenko285ad152009-12-04 23:02:27 +01004365 ps = jp->ps;
4366 psend = ps + jp->nprocs;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004367 do {
Denys Vlasenko285ad152009-12-04 23:02:27 +01004368 if (ps->ps_pid == pid) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004369 TRACE(("Job %d: changing status of proc %d "
4370 "from 0x%x to 0x%x\n",
Denys Vlasenko285ad152009-12-04 23:02:27 +01004371 jobno(jp), pid, ps->ps_status, status));
4372 ps->ps_status = status;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004373 thisjob = jp;
4374 }
Denys Vlasenko285ad152009-12-04 23:02:27 +01004375 if (ps->ps_status == -1)
Denys Vlasenko4700fb52015-10-09 15:40:13 +02004376 jobstate = JOBRUNNING;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004377#if JOBS
Denys Vlasenko4700fb52015-10-09 15:40:13 +02004378 if (jobstate == JOBRUNNING)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004379 continue;
Denys Vlasenko285ad152009-12-04 23:02:27 +01004380 if (WIFSTOPPED(ps->ps_status)) {
4381 jp->stopstatus = ps->ps_status;
Denys Vlasenko4700fb52015-10-09 15:40:13 +02004382 jobstate = JOBSTOPPED;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004383 }
4384#endif
Denys Vlasenko285ad152009-12-04 23:02:27 +01004385 } while (++ps < psend);
Denys Vlasenko4700fb52015-10-09 15:40:13 +02004386 if (!thisjob)
4387 continue;
4388
4389 /* Found the job where one of its processes changed its state.
4390 * Is there at least one live and running process in this job? */
4391 if (jobstate != JOBRUNNING) {
4392 /* No. All live processes in the job are stopped
4393 * (JOBSTOPPED) or there are no live processes (JOBDONE)
4394 */
4395 thisjob->changed = 1;
4396 if (thisjob->state != jobstate) {
4397 TRACE(("Job %d: changing state from %d to %d\n",
4398 jobno(thisjob), thisjob->state, jobstate));
4399 thisjob->state = jobstate;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004400#if JOBS
Denys Vlasenko4700fb52015-10-09 15:40:13 +02004401 if (jobstate == JOBSTOPPED)
4402 set_curjob(thisjob, CUR_STOPPED);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004403#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004404 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004405 }
Denys Vlasenko4700fb52015-10-09 15:40:13 +02004406 goto out;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004407 }
Denys Vlasenko4700fb52015-10-09 15:40:13 +02004408 /* The process wasn't found in job list */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004409 out:
4410 INT_ON;
4411
Ron Yorstone48559e2019-03-31 09:27:09 +01004412#if BASH_WAIT_N
Denys Vlasenko966f0872019-03-27 15:51:42 +01004413 if (want_jobexitstatus) {
4414 pid = -1;
4415 if (thisjob && thisjob->state == JOBDONE)
4416 pid = thisjob->ps[thisjob->nprocs - 1].ps_status;
4417 }
4418#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004419 if (thisjob && thisjob == job) {
4420 char s[48 + 1];
4421 int len;
4422
Denys Vlasenko9c541002015-10-07 15:44:36 +02004423 len = sprint_status48(s, status, 1);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004424 if (len) {
4425 s[len] = '\n';
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004426 s[len + 1] = '\0';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004427 out2str(s);
4428 }
4429 }
4430 return pid;
4431}
4432
Denys Vlasenko47eb9792020-02-18 15:37:43 +01004433static int
4434dowait(int block, struct job *jp)
4435{
Denys Vlasenko8d5f4652020-09-29 16:44:46 +02004436 smallint gotchld = *(volatile smallint *)&got_sigchld;
4437 int rpid;
4438 int pid;
Denys Vlasenko47eb9792020-02-18 15:37:43 +01004439
Denys Vlasenko8d5f4652020-09-29 16:44:46 +02004440 if (jp && jp->state != JOBRUNNING)
4441 block = DOWAIT_NONBLOCK;
4442
4443 if (block == DOWAIT_NONBLOCK && !gotchld)
4444 return 1;
4445
4446 rpid = 1;
4447
4448 do {
Denys Vlasenko47eb9792020-02-18 15:37:43 +01004449 pid = waitone(block, jp);
Denys Vlasenko8d5f4652020-09-29 16:44:46 +02004450 rpid &= !!pid;
Denys Vlasenko47eb9792020-02-18 15:37:43 +01004451
Denys Vlasenko8d5f4652020-09-29 16:44:46 +02004452 if (!pid || (jp && jp->state != JOBRUNNING))
4453 block = DOWAIT_NONBLOCK;
4454 } while (pid >= 0);
4455
4456 return rpid;
Denys Vlasenko47eb9792020-02-18 15:37:43 +01004457}
4458
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004459#if JOBS
4460static void
Denys Vlasenko9c541002015-10-07 15:44:36 +02004461showjob(struct job *jp, int mode)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004462{
4463 struct procstat *ps;
4464 struct procstat *psend;
4465 int col;
Denis Vlasenko40ba9982007-07-14 00:48:29 +00004466 int indent_col;
Denys Vlasenko9c541002015-10-07 15:44:36 +02004467 char s[16 + 16 + 48];
4468 FILE *out = (mode & SHOW_STDERR ? stderr : stdout);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004469
4470 ps = jp->ps;
4471
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004472 if (mode & SHOW_ONLY_PGID) { /* jobs -p */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004473 /* just output process (group) id of pipeline */
Denys Vlasenko285ad152009-12-04 23:02:27 +01004474 fprintf(out, "%d\n", ps->ps_pid);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004475 return;
4476 }
4477
4478 col = fmtstr(s, 16, "[%d] ", jobno(jp));
Denis Vlasenko40ba9982007-07-14 00:48:29 +00004479 indent_col = col;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004480
4481 if (jp == curjob)
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004482 s[col - 3] = '+';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004483 else if (curjob && jp == curjob->prev_job)
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004484 s[col - 3] = '-';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004485
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004486 if (mode & SHOW_PIDS)
Denys Vlasenko285ad152009-12-04 23:02:27 +01004487 col += fmtstr(s + col, 16, "%d ", ps->ps_pid);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004488
4489 psend = ps + jp->nprocs;
4490
4491 if (jp->state == JOBRUNNING) {
4492 strcpy(s + col, "Running");
4493 col += sizeof("Running") - 1;
4494 } else {
Denys Vlasenko285ad152009-12-04 23:02:27 +01004495 int status = psend[-1].ps_status;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004496 if (jp->state == JOBSTOPPED)
4497 status = jp->stopstatus;
Denys Vlasenko9c541002015-10-07 15:44:36 +02004498 col += sprint_status48(s + col, status, 0);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004499 }
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004500 /* By now, "[JOBID]* [maybe PID] STATUS" is printed */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004501
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004502 /* This loop either prints "<cmd1> | <cmd2> | <cmd3>" line
4503 * or prints several "PID | <cmdN>" lines,
4504 * depending on SHOW_PIDS bit.
4505 * We do not print status of individual processes
4506 * between PID and <cmdN>. bash does it, but not very well:
4507 * first line shows overall job status, not process status,
4508 * making it impossible to know 1st process status.
4509 */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004510 goto start;
Denys Vlasenko285ad152009-12-04 23:02:27 +01004511 do {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004512 /* for each process */
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004513 s[0] = '\0';
4514 col = 33;
4515 if (mode & SHOW_PIDS)
Denys Vlasenko285ad152009-12-04 23:02:27 +01004516 col = fmtstr(s, 48, "\n%*c%d ", indent_col, ' ', ps->ps_pid) - 1;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004517 start:
Denys Vlasenko285ad152009-12-04 23:02:27 +01004518 fprintf(out, "%s%*c%s%s",
4519 s,
4520 33 - col >= 0 ? 33 - col : 0, ' ',
4521 ps == jp->ps ? "" : "| ",
4522 ps->ps_cmd
4523 );
4524 } while (++ps != psend);
Denys Vlasenko9c541002015-10-07 15:44:36 +02004525 newline_and_flush(out);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004526
4527 jp->changed = 0;
4528
4529 if (jp->state == JOBDONE) {
4530 TRACE(("showjob: freeing job %d\n", jobno(jp)));
4531 freejob(jp);
4532 }
4533}
4534
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004535/*
4536 * Print a list of jobs. If "change" is nonzero, only print jobs whose
4537 * statuses have changed since the last call to showjobs.
4538 */
4539static void
Denys Vlasenko9c541002015-10-07 15:44:36 +02004540showjobs(int mode)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004541{
4542 struct job *jp;
4543
Denys Vlasenko883cea42009-07-11 15:31:59 +02004544 TRACE(("showjobs(0x%x) called\n", mode));
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004545
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004546 /* Handle all finished jobs */
Denys Vlasenko47eb9792020-02-18 15:37:43 +01004547 dowait(DOWAIT_NONBLOCK, NULL);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004548
4549 for (jp = curjob; jp; jp = jp->prev_job) {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004550 if (!(mode & SHOW_CHANGED) || jp->changed) {
Denys Vlasenko9c541002015-10-07 15:44:36 +02004551 showjob(jp, mode);
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004552 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004553 }
4554}
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004555
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02004556static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00004557jobscmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004558{
4559 int mode, m;
4560
4561 mode = 0;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02004562 while ((m = nextopt("lp")) != '\0') {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004563 if (m == 'l')
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004564 mode |= SHOW_PIDS;
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004565 else
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004566 mode |= SHOW_ONLY_PGID;
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004567 }
4568
4569 argv = argptr;
4570 if (*argv) {
4571 do
Denys Vlasenko9c541002015-10-07 15:44:36 +02004572 showjob(getjob(*argv, 0), mode);
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004573 while (*++argv);
Denys Vlasenko285ad152009-12-04 23:02:27 +01004574 } else {
Denys Vlasenko9c541002015-10-07 15:44:36 +02004575 showjobs(mode);
Denys Vlasenko285ad152009-12-04 23:02:27 +01004576 }
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004577
4578 return 0;
4579}
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004580#endif /* JOBS */
4581
Michael Abbott359da5e2009-12-04 23:03:29 +01004582/* Called only on finished or stopped jobs (no members are running) */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004583static int
4584getstatus(struct job *job)
4585{
4586 int status;
4587 int retval;
Michael Abbott359da5e2009-12-04 23:03:29 +01004588 struct procstat *ps;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004589
Michael Abbott359da5e2009-12-04 23:03:29 +01004590 /* Fetch last member's status */
4591 ps = job->ps + job->nprocs - 1;
4592 status = ps->ps_status;
4593 if (pipefail) {
4594 /* "set -o pipefail" mode: use last _nonzero_ status */
4595 while (status == 0 && --ps >= job->ps)
4596 status = ps->ps_status;
4597 }
4598
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004599 retval = WEXITSTATUS(status);
4600 if (!WIFEXITED(status)) {
4601#if JOBS
4602 retval = WSTOPSIG(status);
4603 if (!WIFSTOPPED(status))
4604#endif
4605 {
4606 /* XXX: limits number of signals */
4607 retval = WTERMSIG(status);
4608#if JOBS
4609 if (retval == SIGINT)
4610 job->sigint = 1;
4611#endif
4612 }
Denys Vlasenko93e2a222020-12-23 12:23:21 +01004613 retval |= 128;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004614 }
Denys Vlasenko883cea42009-07-11 15:31:59 +02004615 TRACE(("getstatus: job %d, nproc %d, status 0x%x, retval 0x%x\n",
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004616 jobno(job), job->nprocs, status, retval));
4617 return retval;
4618}
4619
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02004620static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00004621waitcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004622{
4623 struct job *job;
4624 int retval;
4625 struct job *jp;
Ron Yorstone48559e2019-03-31 09:27:09 +01004626#if BASH_WAIT_N
Denys Vlasenko966f0872019-03-27 15:51:42 +01004627 int status;
4628 char one = nextopt("n");
4629#else
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004630 nextopt(nullstr);
Denys Vlasenko966f0872019-03-27 15:51:42 +01004631#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004632 retval = 0;
4633
4634 argv = argptr;
Denys Vlasenko966f0872019-03-27 15:51:42 +01004635 if (!argv[0]) {
4636 /* wait for all jobs / one job if -n */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004637 for (;;) {
4638 jp = curjob;
Ron Yorstone48559e2019-03-31 09:27:09 +01004639#if BASH_WAIT_N
Denys Vlasenko966f0872019-03-27 15:51:42 +01004640 if (one && !jp)
4641 /* exitcode of "wait -n" with nothing to wait for is 127, not 0 */
4642 retval = 127;
4643#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004644 while (1) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004645 if (!jp) /* no running procs */
4646 goto ret;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004647 if (jp->state == JOBRUNNING)
4648 break;
4649 jp->waited = 1;
4650 jp = jp->prev_job;
4651 }
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004652 /* man bash:
4653 * "When bash is waiting for an asynchronous command via
4654 * the wait builtin, the reception of a signal for which a trap
4655 * has been set will cause the wait builtin to return immediately
4656 * with an exit status greater than 128, immediately after which
4657 * the trap is executed."
Denys Vlasenko69188112016-10-27 20:18:18 +02004658 */
Ron Yorstone48559e2019-03-31 09:27:09 +01004659#if BASH_WAIT_N
Denys Vlasenko966f0872019-03-27 15:51:42 +01004660 status = dowait(DOWAIT_BLOCK_OR_SIG | DOWAIT_JOBSTATUS, NULL);
4661#else
Denys Vlasenko458c1f22016-10-27 23:51:19 +02004662 dowait(DOWAIT_BLOCK_OR_SIG, NULL);
Denys Vlasenko966f0872019-03-27 15:51:42 +01004663#endif
Denys Vlasenko47eb9792020-02-18 15:37:43 +01004664 /* if child sends us a signal *and immediately exits*,
4665 * dowait() returns pid > 0. Check this case,
4666 * not "if (dowait() < 0)"!
4667 */
Denys Vlasenko7c1ed9f2010-05-17 04:42:40 +02004668 if (pending_sig)
Denys Vlasenkoc0663c72016-10-27 21:09:01 +02004669 goto sigout;
Ron Yorstone48559e2019-03-31 09:27:09 +01004670#if BASH_WAIT_N
Denys Vlasenko966f0872019-03-27 15:51:42 +01004671 if (one) {
4672 /* wait -n waits for one _job_, not one _process_.
4673 * date; sleep 3 & sleep 2 | sleep 1 & wait -n; date
4674 * should wait for 2 seconds. Not 1 or 3.
4675 */
4676 if (status != -1 && !WIFSTOPPED(status)) {
4677 retval = WEXITSTATUS(status);
4678 if (WIFSIGNALED(status))
Denys Vlasenko93e2a222020-12-23 12:23:21 +01004679 retval = 128 | WTERMSIG(status);
Denys Vlasenko966f0872019-03-27 15:51:42 +01004680 goto ret;
4681 }
4682 }
4683#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004684 }
4685 }
4686
4687 retval = 127;
4688 do {
4689 if (**argv != '%') {
4690 pid_t pid = number(*argv);
4691 job = curjob;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004692 while (1) {
4693 if (!job)
4694 goto repeat;
Denys Vlasenko285ad152009-12-04 23:02:27 +01004695 if (job->ps[job->nprocs - 1].ps_pid == pid)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004696 break;
4697 job = job->prev_job;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004698 }
Denys Vlasenkob12553f2011-02-21 03:22:20 +01004699 } else {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004700 job = getjob(*argv, 0);
Denys Vlasenkob12553f2011-02-21 03:22:20 +01004701 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004702 /* loop until process terminated or stopped */
Denys Vlasenko91e11eb2020-09-29 20:35:55 +02004703 dowait(DOWAIT_BLOCK_OR_SIG, job);
Denys Vlasenko47eb9792020-02-18 15:37:43 +01004704 if (pending_sig)
4705 goto sigout;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004706 job->waited = 1;
4707 retval = getstatus(job);
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004708 repeat: ;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004709 } while (*++argv);
4710
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004711 ret:
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004712 return retval;
Denys Vlasenkoc0663c72016-10-27 21:09:01 +02004713 sigout:
Denys Vlasenko93e2a222020-12-23 12:23:21 +01004714 retval = 128 | pending_sig;
Denys Vlasenkoc0663c72016-10-27 21:09:01 +02004715 return retval;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004716}
4717
4718static struct job *
4719growjobtab(void)
4720{
4721 size_t len;
4722 ptrdiff_t offset;
4723 struct job *jp, *jq;
4724
4725 len = njobs * sizeof(*jp);
4726 jq = jobtab;
4727 jp = ckrealloc(jq, len + 4 * sizeof(*jp));
4728
4729 offset = (char *)jp - (char *)jq;
4730 if (offset) {
4731 /* Relocate pointers */
4732 size_t l = len;
4733
4734 jq = (struct job *)((char *)jq + l);
4735 while (l) {
4736 l -= sizeof(*jp);
4737 jq--;
4738#define joff(p) ((struct job *)((char *)(p) + l))
4739#define jmove(p) (p) = (void *)((char *)(p) + offset)
4740 if (joff(jp)->ps == &jq->ps0)
4741 jmove(joff(jp)->ps);
4742 if (joff(jp)->prev_job)
4743 jmove(joff(jp)->prev_job);
4744 }
4745 if (curjob)
4746 jmove(curjob);
4747#undef joff
4748#undef jmove
4749 }
4750
4751 njobs += 4;
4752 jobtab = jp;
4753 jp = (struct job *)((char *)jp + len);
4754 jq = jp + 3;
4755 do {
4756 jq->used = 0;
4757 } while (--jq >= jp);
4758 return jp;
4759}
4760
4761/*
4762 * Return a new job structure.
4763 * Called with interrupts off.
4764 */
4765static struct job *
Denis Vlasenko68404f12008-03-17 09:00:54 +00004766makejob(/*union node *node,*/ int nprocs)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004767{
4768 int i;
4769 struct job *jp;
4770
4771 for (i = njobs, jp = jobtab; ; jp++) {
4772 if (--i < 0) {
4773 jp = growjobtab();
4774 break;
4775 }
4776 if (jp->used == 0)
4777 break;
4778 if (jp->state != JOBDONE || !jp->waited)
4779 continue;
4780#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004781 if (doing_jobctl)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004782 continue;
4783#endif
4784 freejob(jp);
4785 break;
4786 }
4787 memset(jp, 0, sizeof(*jp));
4788#if JOBS
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004789 /* jp->jobctl is a bitfield.
Denys Vlasenko098b7132017-01-11 19:59:03 +01004790 * "jp->jobctl |= doing_jobctl" likely to give awful code */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004791 if (doing_jobctl)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004792 jp->jobctl = 1;
4793#endif
4794 jp->prev_job = curjob;
4795 curjob = jp;
4796 jp->used = 1;
4797 jp->ps = &jp->ps0;
4798 if (nprocs > 1) {
4799 jp->ps = ckmalloc(nprocs * sizeof(struct procstat));
4800 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00004801 TRACE(("makejob(%d) returns %%%d\n", nprocs,
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004802 jobno(jp)));
4803 return jp;
4804}
4805
4806#if JOBS
4807/*
4808 * Return a string identifying a command (to be printed by the
4809 * jobs command).
4810 */
4811static char *cmdnextc;
4812
4813static void
4814cmdputs(const char *s)
4815{
Denys Vlasenko965b7952020-11-30 13:03:03 +01004816 static const char vstype[VSTYPE + 1][3] ALIGN1 = {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00004817 "", "}", "-", "+", "?", "=",
4818 "%", "%%", "#", "##"
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01004819 IF_BASH_SUBSTR(, ":")
4820 IF_BASH_PATTERN_SUBST(, "/", "//")
Denis Vlasenko92e13c22008-03-25 01:17:40 +00004821 };
4822
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004823 const char *p, *str;
Denys Vlasenko46a14772009-12-10 21:27:13 +01004824 char cc[2];
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004825 char *nextc;
Denys Vlasenkocd716832009-11-28 22:14:02 +01004826 unsigned char c;
4827 unsigned char subtype = 0;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004828 int quoted = 0;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004829
Denys Vlasenko46a14772009-12-10 21:27:13 +01004830 cc[1] = '\0';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004831 nextc = makestrspace((strlen(s) + 1) * 8, cmdnextc);
4832 p = s;
Denys Vlasenko46a14772009-12-10 21:27:13 +01004833 while ((c = *p++) != '\0') {
Denis Vlasenkoef527f52008-06-23 01:52:30 +00004834 str = NULL;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004835 switch (c) {
4836 case CTLESC:
4837 c = *p++;
4838 break;
4839 case CTLVAR:
4840 subtype = *p++;
4841 if ((subtype & VSTYPE) == VSLENGTH)
4842 str = "${#";
4843 else
4844 str = "${";
Ron Yorston549deab2015-05-18 09:57:51 +02004845 goto dostr;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004846 case CTLENDVAR:
Denys Vlasenko3f4847b2020-02-16 19:06:42 +01004847 str = "\"}";
4848 str += !(quoted & 1);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004849 quoted >>= 1;
4850 subtype = 0;
4851 goto dostr;
4852 case CTLBACKQ:
4853 str = "$(...)";
4854 goto dostr;
Denys Vlasenko0b883582016-12-23 16:49:07 +01004855#if ENABLE_FEATURE_SH_MATH
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004856 case CTLARI:
4857 str = "$((";
4858 goto dostr;
4859 case CTLENDARI:
4860 str = "))";
4861 goto dostr;
4862#endif
4863 case CTLQUOTEMARK:
4864 quoted ^= 1;
4865 c = '"';
4866 break;
4867 case '=':
4868 if (subtype == 0)
4869 break;
4870 if ((subtype & VSTYPE) != VSNORMAL)
4871 quoted <<= 1;
4872 str = vstype[subtype & VSTYPE];
4873 if (subtype & VSNUL)
4874 c = ':';
4875 else
4876 goto checkstr;
4877 break;
4878 case '\'':
4879 case '\\':
4880 case '"':
4881 case '$':
4882 /* These can only happen inside quotes */
4883 cc[0] = c;
4884 str = cc;
Denys Vlasenkod0fff912017-07-31 14:32:18 +02004885//FIXME:
4886// $ true $$ &
4887// $ <cr>
4888// [1]+ Done true ${\$} <<=== BUG: ${\$} is not a valid way to write $$ (${$} would be ok)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004889 c = '\\';
4890 break;
4891 default:
4892 break;
4893 }
4894 USTPUTC(c, nextc);
4895 checkstr:
4896 if (!str)
4897 continue;
4898 dostr:
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02004899 while ((c = *str++) != '\0') {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004900 USTPUTC(c, nextc);
4901 }
Denys Vlasenko46a14772009-12-10 21:27:13 +01004902 } /* while *p++ not NUL */
4903
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004904 if (quoted & 1) {
4905 USTPUTC('"', nextc);
4906 }
4907 *nextc = 0;
4908 cmdnextc = nextc;
4909}
4910
4911/* cmdtxt() and cmdlist() call each other */
4912static void cmdtxt(union node *n);
4913
4914static void
4915cmdlist(union node *np, int sep)
4916{
4917 for (; np; np = np->narg.next) {
4918 if (!sep)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004919 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004920 cmdtxt(np);
4921 if (sep && np->narg.next)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004922 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004923 }
4924}
4925
4926static void
4927cmdtxt(union node *n)
4928{
4929 union node *np;
4930 struct nodelist *lp;
4931 const char *p;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004932
4933 if (!n)
4934 return;
4935 switch (n->type) {
4936 default:
4937#if DEBUG
4938 abort();
4939#endif
4940 case NPIPE:
4941 lp = n->npipe.cmdlist;
4942 for (;;) {
4943 cmdtxt(lp->n);
4944 lp = lp->next;
4945 if (!lp)
4946 break;
4947 cmdputs(" | ");
4948 }
4949 break;
4950 case NSEMI:
4951 p = "; ";
4952 goto binop;
4953 case NAND:
4954 p = " && ";
4955 goto binop;
4956 case NOR:
4957 p = " || ";
4958 binop:
4959 cmdtxt(n->nbinary.ch1);
4960 cmdputs(p);
4961 n = n->nbinary.ch2;
4962 goto donode;
4963 case NREDIR:
4964 case NBACKGND:
4965 n = n->nredir.n;
4966 goto donode;
4967 case NNOT:
4968 cmdputs("!");
4969 n = n->nnot.com;
4970 donode:
4971 cmdtxt(n);
4972 break;
4973 case NIF:
4974 cmdputs("if ");
4975 cmdtxt(n->nif.test);
4976 cmdputs("; then ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004977 if (n->nif.elsepart) {
Denys Vlasenko7cee00e2009-07-24 01:08:03 +02004978 cmdtxt(n->nif.ifpart);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004979 cmdputs("; else ");
4980 n = n->nif.elsepart;
Denys Vlasenko7cee00e2009-07-24 01:08:03 +02004981 } else {
4982 n = n->nif.ifpart;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004983 }
4984 p = "; fi";
4985 goto dotail;
4986 case NSUBSHELL:
4987 cmdputs("(");
4988 n = n->nredir.n;
4989 p = ")";
4990 goto dotail;
4991 case NWHILE:
4992 p = "while ";
4993 goto until;
4994 case NUNTIL:
4995 p = "until ";
4996 until:
4997 cmdputs(p);
4998 cmdtxt(n->nbinary.ch1);
4999 n = n->nbinary.ch2;
5000 p = "; done";
5001 dodo:
5002 cmdputs("; do ");
5003 dotail:
5004 cmdtxt(n);
5005 goto dotail2;
5006 case NFOR:
5007 cmdputs("for ");
5008 cmdputs(n->nfor.var);
5009 cmdputs(" in ");
5010 cmdlist(n->nfor.args, 1);
5011 n = n->nfor.body;
5012 p = "; done";
5013 goto dodo;
5014 case NDEFUN:
Denys Vlasenko675d24a2018-01-27 22:02:05 +01005015 cmdputs(n->ndefun.text);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005016 p = "() { ... }";
5017 goto dotail2;
5018 case NCMD:
5019 cmdlist(n->ncmd.args, 1);
5020 cmdlist(n->ncmd.redirect, 0);
5021 break;
5022 case NARG:
5023 p = n->narg.text;
5024 dotail2:
5025 cmdputs(p);
5026 break;
5027 case NHERE:
5028 case NXHERE:
5029 p = "<<...";
5030 goto dotail2;
5031 case NCASE:
5032 cmdputs("case ");
5033 cmdputs(n->ncase.expr->narg.text);
5034 cmdputs(" in ");
5035 for (np = n->ncase.cases; np; np = np->nclist.next) {
5036 cmdtxt(np->nclist.pattern);
5037 cmdputs(") ");
5038 cmdtxt(np->nclist.body);
5039 cmdputs(";; ");
5040 }
5041 p = "esac";
5042 goto dotail2;
5043 case NTO:
5044 p = ">";
5045 goto redir;
5046 case NCLOBBER:
5047 p = ">|";
5048 goto redir;
5049 case NAPPEND:
5050 p = ">>";
5051 goto redir;
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01005052#if BASH_REDIR_OUTPUT
Denis Vlasenko559691a2008-10-05 18:39:31 +00005053 case NTO2:
5054#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005055 case NTOFD:
5056 p = ">&";
5057 goto redir;
5058 case NFROM:
5059 p = "<";
5060 goto redir;
5061 case NFROMFD:
5062 p = "<&";
5063 goto redir;
5064 case NFROMTO:
5065 p = "<>";
5066 redir:
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005067 cmdputs(utoa(n->nfile.fd));
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005068 cmdputs(p);
5069 if (n->type == NTOFD || n->type == NFROMFD) {
Denys Vlasenkod0fff912017-07-31 14:32:18 +02005070 if (n->ndup.dupfd >= 0)
5071 cmdputs(utoa(n->ndup.dupfd));
5072 else
5073 cmdputs("-");
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005074 break;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005075 }
5076 n = n->nfile.fname;
5077 goto donode;
5078 }
5079}
5080
5081static char *
5082commandtext(union node *n)
5083{
5084 char *name;
5085
5086 STARTSTACKSTR(cmdnextc);
5087 cmdtxt(n);
5088 name = stackblock();
Denys Vlasenko6a94cee2016-10-25 17:40:25 +02005089 TRACE(("commandtext: name %p, end %p\n", name, cmdnextc));
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005090 return ckstrdup(name);
5091}
5092#endif /* JOBS */
5093
5094/*
5095 * Fork off a subshell. If we are doing job control, give the subshell its
5096 * own process group. Jp is a job structure that the job is to be added to.
5097 * N is the command that will be evaluated by the child. Both jp and n may
5098 * be NULL. The mode parameter can be one of the following:
5099 * FORK_FG - Fork off a foreground process.
5100 * FORK_BG - Fork off a background process.
5101 * FORK_NOJOB - Like FORK_FG, but don't give the process its own
5102 * process group even if job control is on.
5103 *
5104 * When job control is turned off, background processes have their standard
5105 * input redirected to /dev/null (except for the second and later processes
5106 * in a pipeline).
5107 *
5108 * Called with interrupts off.
5109 */
5110/*
5111 * Clear traps on a fork.
5112 */
5113static void
5114clear_traps(void)
5115{
5116 char **tp;
5117
Denys Vlasenkob4f51d32016-10-27 12:55:09 +02005118 INT_OFF;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005119 for (tp = trap; tp < &trap[NSIG]; tp++) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00005120 if (*tp && **tp) { /* trap not NULL or "" (SIG_IGN) */
Denys Vlasenkoe305c282009-09-25 02:12:27 +02005121 if (trap_ptr == trap)
5122 free(*tp);
5123 /* else: it "belongs" to trap_ptr vector, don't free */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005124 *tp = NULL;
Denys Vlasenko0800e3a2009-09-24 03:09:26 +02005125 if ((tp - trap) != 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005126 setsignal(tp - trap);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005127 }
5128 }
Alexander Shishkinccb97712010-07-25 13:07:39 +02005129 may_have_traps = 0;
Denys Vlasenkob4f51d32016-10-27 12:55:09 +02005130 INT_ON;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005131}
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00005132
5133/* Lives far away from here, needed for forkchild */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005134static void closescript(void);
Denis Vlasenko41770222007-10-07 18:02:52 +00005135
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00005136/* Called after fork(), in child */
Denys Vlasenko70392332016-10-27 02:31:55 +02005137/* jp and n are NULL when called by openhere() for heredoc support */
Denys Vlasenko21d87d42009-09-25 00:06:51 +02005138static NOINLINE void
Denys Vlasenkoe56f22a2009-07-24 00:16:59 +02005139forkchild(struct job *jp, union node *n, int mode)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005140{
5141 int oldlvl;
5142
5143 TRACE(("Child shell %d\n", getpid()));
5144 oldlvl = shlvl;
5145 shlvl++;
5146
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00005147 /* man bash: "Non-builtin commands run by bash have signal handlers
5148 * set to the values inherited by the shell from its parent".
5149 * Do we do it correctly? */
5150
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005151 closescript();
Denys Vlasenko844f9902009-09-23 03:25:52 +02005152
5153 if (mode == FORK_NOJOB /* is it `xxx` ? */
5154 && n && n->type == NCMD /* is it single cmd? */
5155 /* && n->ncmd.args->type == NARG - always true? */
Denys Vlasenko74269202010-02-21 01:26:42 +01005156 && n->ncmd.args && strcmp(n->ncmd.args->narg.text, "trap") == 0
Denys Vlasenko844f9902009-09-23 03:25:52 +02005157 && n->ncmd.args->narg.next == NULL /* "trap" with no arguments */
5158 /* && n->ncmd.args->narg.backquote == NULL - do we need to check this? */
5159 ) {
5160 TRACE(("Trap hack\n"));
5161 /* Awful hack for `trap` or $(trap).
5162 *
5163 * http://www.opengroup.org/onlinepubs/009695399/utilities/trap.html
5164 * contains an example where "trap" is executed in a subshell:
5165 *
5166 * save_traps=$(trap)
5167 * ...
5168 * eval "$save_traps"
5169 *
5170 * Standard does not say that "trap" in subshell shall print
5171 * parent shell's traps. It only says that its output
5172 * must have suitable form, but then, in the above example
5173 * (which is not supposed to be normative), it implies that.
5174 *
5175 * bash (and probably other shell) does implement it
5176 * (traps are reset to defaults, but "trap" still shows them),
5177 * but as a result, "trap" logic is hopelessly messed up:
5178 *
5179 * # trap
5180 * trap -- 'echo Ho' SIGWINCH <--- we have a handler
5181 * # (trap) <--- trap is in subshell - no output (correct, traps are reset)
5182 * # true | trap <--- trap is in subshell - no output (ditto)
5183 * # echo `true | trap` <--- in subshell - output (but traps are reset!)
5184 * trap -- 'echo Ho' SIGWINCH
5185 * # echo `(trap)` <--- in subshell in subshell - output
5186 * trap -- 'echo Ho' SIGWINCH
5187 * # echo `true | (trap)` <--- in subshell in subshell in subshell - output!
5188 * trap -- 'echo Ho' SIGWINCH
5189 *
5190 * The rules when to forget and when to not forget traps
5191 * get really complex and nonsensical.
5192 *
5193 * Our solution: ONLY bare $(trap) or `trap` is special.
5194 */
Denys Vlasenko8f88d852009-09-25 12:12:53 +02005195 /* Save trap handler strings for trap builtin to print */
Ron Yorstond840c5d2015-07-19 23:05:20 +02005196 trap_ptr = xmemdup(trap, sizeof(trap));
Denys Vlasenko8f88d852009-09-25 12:12:53 +02005197 /* Fall through into clearing traps */
Denys Vlasenko844f9902009-09-23 03:25:52 +02005198 }
Denys Vlasenkoe305c282009-09-25 02:12:27 +02005199 clear_traps();
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005200#if JOBS
5201 /* do job control only in root shell */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00005202 doing_jobctl = 0;
Denys Vlasenkob12553f2011-02-21 03:22:20 +01005203 if (mode != FORK_NOJOB && jp->jobctl && oldlvl == 0) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005204 pid_t pgrp;
5205
5206 if (jp->nprocs == 0)
5207 pgrp = getpid();
5208 else
Denys Vlasenko285ad152009-12-04 23:02:27 +01005209 pgrp = jp->ps[0].ps_pid;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00005210 /* this can fail because we are doing it in the parent also */
5211 setpgid(0, pgrp);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005212 if (mode == FORK_FG)
5213 xtcsetpgrp(ttyfd, pgrp);
5214 setsignal(SIGTSTP);
5215 setsignal(SIGTTOU);
5216 } else
5217#endif
5218 if (mode == FORK_BG) {
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00005219 /* man bash: "When job control is not in effect,
5220 * asynchronous commands ignore SIGINT and SIGQUIT" */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005221 ignoresig(SIGINT);
5222 ignoresig(SIGQUIT);
5223 if (jp->nprocs == 0) {
5224 close(0);
5225 if (open(bb_dev_null, O_RDONLY) != 0)
Denys Vlasenko12ffefb2017-08-23 14:52:56 +02005226 ash_msg_and_raise_perror("can't open '%s'", bb_dev_null);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005227 }
5228 }
Denys Vlasenkob12553f2011-02-21 03:22:20 +01005229 if (oldlvl == 0) {
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00005230 if (iflag) { /* why if iflag only? */
5231 setsignal(SIGINT);
5232 setsignal(SIGTERM);
5233 }
5234 /* man bash:
5235 * "In all cases, bash ignores SIGQUIT. Non-builtin
5236 * commands run by bash have signal handlers
5237 * set to the values inherited by the shell
5238 * from its parent".
5239 * Take care of the second rule: */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005240 setsignal(SIGQUIT);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005241 }
Denys Vlasenkoe56f22a2009-07-24 00:16:59 +02005242#if JOBS
Denys Vlasenko844f9902009-09-23 03:25:52 +02005243 if (n && n->type == NCMD
Denys Vlasenko74269202010-02-21 01:26:42 +01005244 && n->ncmd.args && strcmp(n->ncmd.args->narg.text, "jobs") == 0
Denys Vlasenko844f9902009-09-23 03:25:52 +02005245 ) {
Denys Vlasenkoe56f22a2009-07-24 00:16:59 +02005246 TRACE(("Job hack\n"));
Denys Vlasenko844f9902009-09-23 03:25:52 +02005247 /* "jobs": we do not want to clear job list for it,
5248 * instead we remove only _its_ own_ job from job list.
5249 * This makes "jobs .... | cat" more useful.
5250 */
Denys Vlasenkoe56f22a2009-07-24 00:16:59 +02005251 freejob(curjob);
5252 return;
5253 }
5254#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005255 for (jp = curjob; jp; jp = jp->prev_job)
5256 freejob(jp);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005257}
5258
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00005259/* Called after fork(), in parent */
Denis Vlasenko85c24712008-03-17 09:04:04 +00005260#if !JOBS
5261#define forkparent(jp, n, mode, pid) forkparent(jp, mode, pid)
5262#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005263static void
5264forkparent(struct job *jp, union node *n, int mode, pid_t pid)
5265{
5266 TRACE(("In parent shell: child = %d\n", pid));
Denys Vlasenko47eb9792020-02-18 15:37:43 +01005267 if (!jp) /* jp is NULL when called by openhere() for heredoc support */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005268 return;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005269#if JOBS
5270 if (mode != FORK_NOJOB && jp->jobctl) {
5271 int pgrp;
5272
5273 if (jp->nprocs == 0)
5274 pgrp = pid;
5275 else
Denys Vlasenko285ad152009-12-04 23:02:27 +01005276 pgrp = jp->ps[0].ps_pid;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005277 /* This can fail because we are doing it in the child also */
5278 setpgid(pid, pgrp);
5279 }
5280#endif
5281 if (mode == FORK_BG) {
5282 backgndpid = pid; /* set $! */
5283 set_curjob(jp, CUR_RUNNING);
5284 }
5285 if (jp) {
5286 struct procstat *ps = &jp->ps[jp->nprocs++];
Denys Vlasenko285ad152009-12-04 23:02:27 +01005287 ps->ps_pid = pid;
5288 ps->ps_status = -1;
5289 ps->ps_cmd = nullstr;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005290#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00005291 if (doing_jobctl && n)
Denys Vlasenko285ad152009-12-04 23:02:27 +01005292 ps->ps_cmd = commandtext(n);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005293#endif
5294 }
5295}
5296
Denys Vlasenko70392332016-10-27 02:31:55 +02005297/* jp and n are NULL when called by openhere() for heredoc support */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005298static int
5299forkshell(struct job *jp, union node *n, int mode)
5300{
5301 int pid;
5302
5303 TRACE(("forkshell(%%%d, %p, %d) called\n", jobno(jp), n, mode));
5304 pid = fork();
5305 if (pid < 0) {
5306 TRACE(("Fork failed, errno=%d", errno));
5307 if (jp)
5308 freejob(jp);
Denys Vlasenko12ffefb2017-08-23 14:52:56 +02005309 ash_msg_and_raise_perror("can't fork");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005310 }
Denys Vlasenko76ace252009-10-12 15:25:01 +02005311 if (pid == 0) {
5312 CLEAR_RANDOM_T(&random_gen); /* or else $RANDOM repeats in child */
Denys Vlasenkoe56f22a2009-07-24 00:16:59 +02005313 forkchild(jp, n, mode);
Denys Vlasenko76ace252009-10-12 15:25:01 +02005314 } else {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005315 forkparent(jp, n, mode, pid);
Denys Vlasenko76ace252009-10-12 15:25:01 +02005316 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005317 return pid;
5318}
5319
5320/*
5321 * Wait for job to finish.
5322 *
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00005323 * Under job control we have the problem that while a child process
5324 * is running interrupts generated by the user are sent to the child
5325 * but not to the shell. This means that an infinite loop started by
5326 * an interactive user may be hard to kill. With job control turned off,
5327 * an interactive user may place an interactive program inside a loop.
5328 * If the interactive program catches interrupts, the user doesn't want
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005329 * these interrupts to also abort the loop. The approach we take here
5330 * is to have the shell ignore interrupt signals while waiting for a
5331 * foreground process to terminate, and then send itself an interrupt
5332 * signal if the child process was terminated by an interrupt signal.
5333 * Unfortunately, some programs want to do a bit of cleanup and then
5334 * exit on interrupt; unless these processes terminate themselves by
5335 * sending a signal to themselves (instead of calling exit) they will
5336 * confuse this approach.
5337 *
5338 * Called with interrupts off.
5339 */
5340static int
5341waitforjob(struct job *jp)
5342{
5343 int st;
5344
Denys Vlasenkod81af722020-02-18 14:28:30 +01005345 TRACE(("waitforjob(%%%d) called\n", jp ? jobno(jp) : 0));
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00005346
Denys Vlasenko47eb9792020-02-18 15:37:43 +01005347 /* In non-interactive shells, we _can_ get
5348 * a keyboard signal here and be EINTRed, but we just loop
5349 * inside dowait(), waiting for command to complete.
5350 *
5351 * man bash:
5352 * "If bash is waiting for a command to complete and receives
5353 * a signal for which a trap has been set, the trap
5354 * will not be executed until the command completes."
5355 *
5356 * Reality is that even if trap is not set, bash
5357 * will not act on the signal until command completes.
5358 * Try this. sleep5intoff.c:
5359 * #include <signal.h>
5360 * #include <unistd.h>
5361 * int main() {
5362 * sigset_t set;
5363 * sigemptyset(&set);
5364 * sigaddset(&set, SIGINT);
5365 * sigaddset(&set, SIGQUIT);
5366 * sigprocmask(SIG_BLOCK, &set, NULL);
5367 * sleep(5);
5368 * return 0;
5369 * }
5370 * $ bash -c './sleep5intoff; echo hi'
5371 * ^C^C^C^C <--- pressing ^C once a second
5372 * $ _
5373 * $ bash -c './sleep5intoff; echo hi'
5374 * ^\^\^\^\hi <--- pressing ^\ (SIGQUIT)
5375 * $ _
5376 */
5377 dowait(jp ? DOWAIT_BLOCK : DOWAIT_NONBLOCK, jp);
5378 if (!jp)
Denys Vlasenko97edfc42020-02-18 14:37:56 +01005379 return exitstatus;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00005380
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005381 st = getstatus(jp);
5382#if JOBS
5383 if (jp->jobctl) {
5384 xtcsetpgrp(ttyfd, rootpid);
Denys Vlasenko098b7132017-01-11 19:59:03 +01005385 restore_tty_if_stopped_or_signaled(jp);
5386
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005387 /*
5388 * This is truly gross.
5389 * If we're doing job control, then we did a TIOCSPGRP which
5390 * caused us (the shell) to no longer be in the controlling
5391 * session -- so we wouldn't have seen any ^C/SIGINT. So, we
5392 * intuit from the subprocess exit status whether a SIGINT
5393 * occurred, and if so interrupt ourselves. Yuck. - mycroft
5394 */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00005395 if (jp->sigint) /* TODO: do the same with all signals */
5396 raise(SIGINT); /* ... by raise(jp->sig) instead? */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005397 }
5398 if (jp->state == JOBDONE)
5399#endif
5400 freejob(jp);
5401 return st;
5402}
5403
5404/*
5405 * return 1 if there are stopped jobs, otherwise 0
5406 */
5407static int
5408stoppedjobs(void)
5409{
5410 struct job *jp;
5411 int retval;
5412
5413 retval = 0;
5414 if (job_warning)
5415 goto out;
5416 jp = curjob;
5417 if (jp && jp->state == JOBSTOPPED) {
5418 out2str("You have stopped jobs.\n");
5419 job_warning = 2;
5420 retval++;
5421 }
5422 out:
5423 return retval;
5424}
5425
5426
Denys Vlasenko70392332016-10-27 02:31:55 +02005427/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005428 * Code for dealing with input/output redirection.
5429 */
5430
Denys Vlasenko8d0e0cd2011-01-25 23:21:46 +01005431#undef EMPTY
5432#undef CLOSED
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005433#define EMPTY -2 /* marks an unused slot in redirtab */
Denys Vlasenko035486c2017-07-31 04:09:19 +02005434#define CLOSED -1 /* marks a slot of previously-closed fd */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005435
5436/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005437 * Handle here documents. Normally we fork off a process to write the
5438 * data to a pipe. If the document is short, we can stuff the data in
5439 * the pipe without forking.
5440 */
5441/* openhere needs this forward reference */
Denys Vlasenkoc2058ec2020-02-22 20:25:03 +01005442static void expandhere(union node *arg);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005443static int
5444openhere(union node *redir)
5445{
Denys Vlasenkoc2058ec2020-02-22 20:25:03 +01005446 char *p;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005447 int pip[2];
5448 size_t len = 0;
5449
5450 if (pipe(pip) < 0)
Denys Vlasenko12ffefb2017-08-23 14:52:56 +02005451 ash_msg_and_raise_perror("can't create pipe");
Denys Vlasenkoc2058ec2020-02-22 20:25:03 +01005452
5453 p = redir->nhere.doc->narg.text;
5454 if (redir->type == NXHERE) {
5455 expandhere(redir->nhere.doc);
5456 p = stackblock();
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005457 }
Denys Vlasenkoc2058ec2020-02-22 20:25:03 +01005458
5459 len = strlen(p);
5460 if (len <= PIPE_BUF) {
5461 xwrite(pip[1], p, len);
5462 goto out;
5463 }
5464
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005465 if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) {
Denis Vlasenko0b769642008-07-24 07:54:57 +00005466 /* child */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005467 close(pip[0]);
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00005468 ignoresig(SIGINT); //signal(SIGINT, SIG_IGN);
5469 ignoresig(SIGQUIT); //signal(SIGQUIT, SIG_IGN);
5470 ignoresig(SIGHUP); //signal(SIGHUP, SIG_IGN);
5471 ignoresig(SIGTSTP); //signal(SIGTSTP, SIG_IGN);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005472 signal(SIGPIPE, SIG_DFL);
Denys Vlasenkoc2058ec2020-02-22 20:25:03 +01005473 xwrite(pip[1], p, len);
Bernhard Reutner-Fischer636a1f82008-05-19 09:29:47 +00005474 _exit(EXIT_SUCCESS);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005475 }
5476 out:
5477 close(pip[1]);
5478 return pip[0];
5479}
5480
5481static int
5482openredirect(union node *redir)
5483{
Denys Vlasenkof1a5cb02017-07-25 17:47:48 +02005484 struct stat sb;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005485 char *fname;
5486 int f;
5487
5488 switch (redir->nfile.type) {
Denys Vlasenko557482c2016-09-25 21:24:04 +02005489/* Can't happen, our single caller does this itself */
5490// case NTOFD:
5491// case NFROMFD:
5492// return -1;
5493 case NHERE:
5494 case NXHERE:
5495 return openhere(redir);
5496 }
5497
5498 /* For N[X]HERE, reading redir->nfile.expfname would touch beyond
5499 * allocated space. Do it only when we know it is safe.
5500 */
5501 fname = redir->nfile.expfname;
5502
5503 switch (redir->nfile.type) {
5504 default:
5505#if DEBUG
5506 abort();
5507#endif
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005508 case NFROM:
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005509 f = open(fname, O_RDONLY);
5510 if (f < 0)
5511 goto eopen;
5512 break;
5513 case NFROMTO:
Andreas Bühmannda75f442010-06-24 04:32:37 +02005514 f = open(fname, O_RDWR|O_CREAT, 0666);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005515 if (f < 0)
5516 goto ecreate;
5517 break;
5518 case NTO:
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01005519#if BASH_REDIR_OUTPUT
Denis Vlasenko559691a2008-10-05 18:39:31 +00005520 case NTO2:
5521#endif
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005522 /* Take care of noclobber mode. */
5523 if (Cflag) {
Denys Vlasenkof1a5cb02017-07-25 17:47:48 +02005524 if (stat(fname, &sb) < 0) {
5525 f = open(fname, O_WRONLY|O_CREAT|O_EXCL, 0666);
5526 if (f < 0)
5527 goto ecreate;
5528 } else if (!S_ISREG(sb.st_mode)) {
5529 f = open(fname, O_WRONLY, 0666);
5530 if (f < 0)
5531 goto ecreate;
Denys Vlasenko355ec352018-04-02 13:34:57 +02005532 if (!fstat(f, &sb) && S_ISREG(sb.st_mode)) {
Denys Vlasenkof1a5cb02017-07-25 17:47:48 +02005533 close(f);
5534 errno = EEXIST;
5535 goto ecreate;
5536 }
5537 } else {
5538 errno = EEXIST;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005539 goto ecreate;
Denys Vlasenkof1a5cb02017-07-25 17:47:48 +02005540 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005541 break;
5542 }
5543 /* FALLTHROUGH */
5544 case NCLOBBER:
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005545 f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666);
5546 if (f < 0)
5547 goto ecreate;
5548 break;
5549 case NAPPEND:
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005550 f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666);
5551 if (f < 0)
5552 goto ecreate;
5553 break;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005554 }
5555
5556 return f;
5557 ecreate:
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +00005558 ash_msg_and_raise_error("can't create %s: %s", fname, errmsg(errno, "nonexistent directory"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005559 eopen:
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +00005560 ash_msg_and_raise_error("can't open %s: %s", fname, errmsg(errno, "no such file"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005561}
5562
5563/*
Denys Vlasenko64774602016-10-26 15:24:30 +02005564 * Copy a file descriptor to be >= 10. Throws exception on error.
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005565 */
5566static int
Denys Vlasenko64774602016-10-26 15:24:30 +02005567savefd(int from)
5568{
5569 int newfd;
5570 int err;
5571
Denys Vlasenko60fb98e2018-03-30 22:15:14 +02005572 newfd = fcntl(from, F_DUPFD_CLOEXEC, 10);
Denys Vlasenko64774602016-10-26 15:24:30 +02005573 err = newfd < 0 ? errno : 0;
5574 if (err != EBADF) {
5575 if (err)
Ron Yorstonbe366e52017-07-27 13:53:39 +01005576 ash_msg_and_raise_perror("%d", from);
Denys Vlasenko64774602016-10-26 15:24:30 +02005577 close(from);
Denys Vlasenko60fb98e2018-03-30 22:15:14 +02005578 if (F_DUPFD_CLOEXEC == F_DUPFD)
5579 close_on_exec_on(newfd);
Denys Vlasenko64774602016-10-26 15:24:30 +02005580 }
5581
5582 return newfd;
5583}
5584static int
5585dup2_or_raise(int from, int to)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005586{
5587 int newfd;
5588
Denys Vlasenko64774602016-10-26 15:24:30 +02005589 newfd = (from != to) ? dup2(from, to) : to;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005590 if (newfd < 0) {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005591 /* Happens when source fd is not open: try "echo >&99" */
Ron Yorstonbe366e52017-07-27 13:53:39 +01005592 ash_msg_and_raise_perror("%d", from);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005593 }
5594 return newfd;
5595}
Denys Vlasenko035486c2017-07-31 04:09:19 +02005596static int
Denys Vlasenko9acd63c2018-03-28 18:35:07 +02005597dup_CLOEXEC(int fd, int avoid_fd)
Denys Vlasenko035486c2017-07-31 04:09:19 +02005598{
5599 int newfd;
5600 repeat:
Denys Vlasenko9acd63c2018-03-28 18:35:07 +02005601 newfd = fcntl(fd, F_DUPFD_CLOEXEC, avoid_fd + 1);
5602 if (newfd >= 0) {
5603 if (F_DUPFD_CLOEXEC == F_DUPFD) /* if old libc (w/o F_DUPFD_CLOEXEC) */
Denys Vlasenko60fb98e2018-03-30 22:15:14 +02005604 close_on_exec_on(newfd);
Denys Vlasenko9acd63c2018-03-28 18:35:07 +02005605 } else { /* newfd < 0 */
Denys Vlasenko035486c2017-07-31 04:09:19 +02005606 if (errno == EBUSY)
5607 goto repeat;
5608 if (errno == EINTR)
5609 goto repeat;
5610 }
5611 return newfd;
5612}
5613static int
5614xdup_CLOEXEC_and_close(int fd, int avoid_fd)
5615{
5616 int newfd;
5617 repeat:
Denys Vlasenko60fb98e2018-03-30 22:15:14 +02005618 newfd = fcntl(fd, F_DUPFD_CLOEXEC, avoid_fd + 1);
Denys Vlasenko035486c2017-07-31 04:09:19 +02005619 if (newfd < 0) {
5620 if (errno == EBUSY)
5621 goto repeat;
5622 if (errno == EINTR)
5623 goto repeat;
5624 /* fd was not open? */
5625 if (errno == EBADF)
5626 return fd;
5627 ash_msg_and_raise_perror("%d", newfd);
5628 }
Denys Vlasenko60fb98e2018-03-30 22:15:14 +02005629 if (F_DUPFD_CLOEXEC == F_DUPFD)
5630 close_on_exec_on(newfd);
Denys Vlasenko035486c2017-07-31 04:09:19 +02005631 close(fd);
5632 return newfd;
5633}
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005634
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005635/* Struct def and variable are moved down to the first usage site */
Denys Vlasenko035486c2017-07-31 04:09:19 +02005636struct squirrel {
5637 int orig_fd;
5638 int moved_to;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005639};
Denis Vlasenko0b769642008-07-24 07:54:57 +00005640struct redirtab {
5641 struct redirtab *next;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005642 int pair_count;
Denys Vlasenko035486c2017-07-31 04:09:19 +02005643 struct squirrel two_fd[];
Denis Vlasenko0b769642008-07-24 07:54:57 +00005644};
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005645#define redirlist (G_var.redirlist)
Denis Vlasenko0b769642008-07-24 07:54:57 +00005646
Denys Vlasenko035486c2017-07-31 04:09:19 +02005647static void
5648add_squirrel_closed(struct redirtab *sq, int fd)
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005649{
5650 int i;
5651
Denys Vlasenko035486c2017-07-31 04:09:19 +02005652 if (!sq)
5653 return;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005654
Denys Vlasenko035486c2017-07-31 04:09:19 +02005655 for (i = 0; sq->two_fd[i].orig_fd != EMPTY; i++) {
5656 /* If we collide with an already moved fd... */
5657 if (fd == sq->two_fd[i].orig_fd) {
5658 /* Examples:
5659 * "echo 3>FILE 3>&- 3>FILE"
5660 * "echo 3>&- 3>FILE"
5661 * No need for last redirect to insert
5662 * another "need to close 3" indicator.
5663 */
5664 TRACE(("redirect_fd %d: already moved or closed\n", fd));
5665 return;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005666 }
5667 }
Denys Vlasenko035486c2017-07-31 04:09:19 +02005668 TRACE(("redirect_fd %d: previous fd was closed\n", fd));
5669 sq->two_fd[i].orig_fd = fd;
5670 sq->two_fd[i].moved_to = CLOSED;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005671}
5672
Denys Vlasenko37dc08b2016-10-02 04:38:07 +02005673static int
Denys Vlasenko035486c2017-07-31 04:09:19 +02005674save_fd_on_redirect(int fd, int avoid_fd, struct redirtab *sq)
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005675{
Denys Vlasenko035486c2017-07-31 04:09:19 +02005676 int i, new_fd;
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005677
Denys Vlasenko035486c2017-07-31 04:09:19 +02005678 if (avoid_fd < 9) /* the important case here is that it can be -1 */
5679 avoid_fd = 9;
5680
5681#if JOBS
5682 if (fd == ttyfd) {
5683 /* Testcase: "ls -l /proc/$$/fd 10>&-" should work */
5684 ttyfd = xdup_CLOEXEC_and_close(ttyfd, avoid_fd);
5685 TRACE(("redirect_fd %d: matches ttyfd, moving it to %d\n", fd, ttyfd));
5686 return 1; /* "we closed fd" */
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005687 }
Denys Vlasenko035486c2017-07-31 04:09:19 +02005688#endif
5689 /* Are we called from redirect(0)? E.g. redirect
5690 * in a forked child. No need to save fds,
5691 * we aren't going to use them anymore, ok to trash.
5692 */
5693 if (!sq)
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005694 return 0;
Denys Vlasenko035486c2017-07-31 04:09:19 +02005695
5696 /* If this one of script's fds? */
5697 if (fd != 0) {
5698 struct parsefile *pf = g_parsefile;
5699 while (pf) {
5700 /* We skip fd == 0 case because of the following:
5701 * $ ash # running ash interactively
5702 * $ . ./script.sh
5703 * and in script.sh: "exec 9>&0".
5704 * Even though top-level pf_fd _is_ 0,
5705 * it's still ok to use it: "read" builtin uses it,
5706 * why should we cripple "exec" builtin?
5707 */
5708 if (fd == pf->pf_fd) {
5709 pf->pf_fd = xdup_CLOEXEC_and_close(fd, avoid_fd);
5710 return 1; /* "we closed fd" */
5711 }
5712 pf = pf->prev;
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005713 }
5714 }
Denys Vlasenko035486c2017-07-31 04:09:19 +02005715
5716 /* Check whether it collides with any open fds (e.g. stdio), save fds as needed */
5717
5718 /* First: do we collide with some already moved fds? */
5719 for (i = 0; sq->two_fd[i].orig_fd != EMPTY; i++) {
5720 /* If we collide with an already moved fd... */
5721 if (fd == sq->two_fd[i].moved_to) {
Denys Vlasenko9acd63c2018-03-28 18:35:07 +02005722 new_fd = dup_CLOEXEC(fd, avoid_fd);
Denys Vlasenko035486c2017-07-31 04:09:19 +02005723 sq->two_fd[i].moved_to = new_fd;
5724 TRACE(("redirect_fd %d: already busy, moving to %d\n", fd, new_fd));
5725 if (new_fd < 0) /* what? */
5726 xfunc_die();
5727 return 0; /* "we did not close fd" */
5728 }
5729 if (fd == sq->two_fd[i].orig_fd) {
5730 /* Example: echo Hello >/dev/null 1>&2 */
5731 TRACE(("redirect_fd %d: already moved\n", fd));
5732 return 0; /* "we did not close fd" */
5733 }
5734 }
5735
5736 /* 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 +02005737 new_fd = dup_CLOEXEC(fd, avoid_fd);
Denys Vlasenko035486c2017-07-31 04:09:19 +02005738 TRACE(("redirect_fd %d: previous fd is moved to %d (-1 if it was closed)\n", fd, new_fd));
5739 if (new_fd < 0) {
5740 if (errno != EBADF)
5741 xfunc_die();
5742 /* new_fd = CLOSED; - already is -1 */
5743 }
5744 sq->two_fd[i].moved_to = new_fd;
5745 sq->two_fd[i].orig_fd = fd;
5746
5747 /* if we move stderr, let "set -x" code know */
5748 if (fd == preverrout_fd)
5749 preverrout_fd = new_fd;
5750
5751 return 0; /* "we did not close fd" */
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005752}
5753
Denys Vlasenko32fdf2f2017-07-31 04:32:06 +02005754static int
5755internally_opened_fd(int fd, struct redirtab *sq)
5756{
5757 int i;
5758#if JOBS
5759 if (fd == ttyfd)
5760 return 1;
5761#endif
5762 /* If this one of script's fds? */
5763 if (fd != 0) {
5764 struct parsefile *pf = g_parsefile;
5765 while (pf) {
5766 if (fd == pf->pf_fd)
5767 return 1;
5768 pf = pf->prev;
5769 }
5770 }
5771
5772 if (sq) for (i = 0; i < sq->pair_count && sq->two_fd[i].orig_fd != EMPTY; i++) {
5773 if (fd == sq->two_fd[i].moved_to)
5774 return 1;
5775 }
5776 return 0;
5777}
5778
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005779/*
5780 * Process a list of redirection commands. If the REDIR_PUSH flag is set,
5781 * old file descriptors are stashed away so that the redirection can be
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005782 * undone by calling popredir.
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005783 */
5784/* flags passed to redirect */
5785#define REDIR_PUSH 01 /* save previous values of file descriptors */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005786static void
5787redirect(union node *redir, int flags)
5788{
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005789 struct redirtab *sv;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005790
Denys Vlasenko1c79aeb2017-07-29 22:51:52 +02005791 if (!redir)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005792 return;
Denys Vlasenko035486c2017-07-31 04:09:19 +02005793
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005794 sv = NULL;
5795 INT_OFF;
Denys Vlasenko1c79aeb2017-07-29 22:51:52 +02005796 if (flags & REDIR_PUSH)
5797 sv = redirlist;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005798 do {
Denys Vlasenko035486c2017-07-31 04:09:19 +02005799 int fd;
5800 int newfd;
5801 int close_fd;
5802 int closed;
5803
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00005804 fd = redir->nfile.fd;
Denis Vlasenko0b769642008-07-24 07:54:57 +00005805 if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) {
Denys Vlasenko035486c2017-07-31 04:09:19 +02005806 //bb_error_msg("doing %d > %d", fd, newfd);
5807 newfd = redir->ndup.dupfd;
5808 close_fd = -1;
Denis Vlasenko0b769642008-07-24 07:54:57 +00005809 } else {
5810 newfd = openredirect(redir); /* always >= 0 */
5811 if (fd == newfd) {
Denys Vlasenko035486c2017-07-31 04:09:19 +02005812 /* open() gave us precisely the fd we wanted.
5813 * This means that this fd was not busy
5814 * (not opened to anywhere).
5815 * Remember to close it on restore:
5816 */
5817 add_squirrel_closed(sv, fd);
Denis Vlasenko0b769642008-07-24 07:54:57 +00005818 continue;
5819 }
Denys Vlasenko035486c2017-07-31 04:09:19 +02005820 close_fd = newfd;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005821 }
Denys Vlasenko035486c2017-07-31 04:09:19 +02005822
5823 if (fd == newfd)
5824 continue;
5825
5826 /* if "N>FILE": move newfd to fd */
5827 /* if "N>&M": dup newfd to fd */
5828 /* if "N>&-": close fd (newfd is -1) */
5829
5830 IF_BASH_REDIR_OUTPUT(redirect_more:)
5831
5832 closed = save_fd_on_redirect(fd, /*avoid:*/ newfd, sv);
5833 if (newfd == -1) {
5834 /* "N>&-" means "close me" */
5835 if (!closed) {
5836 /* ^^^ optimization: saving may already
5837 * have closed it. If not... */
5838 close(fd);
Denis Vlasenko22f74142008-07-24 22:34:43 +00005839 }
Denys Vlasenko035486c2017-07-31 04:09:19 +02005840 } else {
Denys Vlasenko32fdf2f2017-07-31 04:32:06 +02005841 /* if newfd is a script fd or saved fd, simulate EBADF */
5842 if (internally_opened_fd(newfd, sv)) {
5843 errno = EBADF;
5844 ash_msg_and_raise_perror("%d", newfd);
5845 }
Denys Vlasenko64774602016-10-26 15:24:30 +02005846 dup2_or_raise(newfd, fd);
Denys Vlasenko035486c2017-07-31 04:09:19 +02005847 if (close_fd >= 0) /* "N>FILE" or ">&FILE" or heredoc? */
5848 close(close_fd);
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01005849#if BASH_REDIR_OUTPUT
Denys Vlasenko035486c2017-07-31 04:09:19 +02005850 if (redir->nfile.type == NTO2 && fd == 1) {
5851 /* ">&FILE". we already redirected to 1, now copy 1 to 2 */
5852 fd = 2;
5853 newfd = 1;
5854 close_fd = -1;
5855 goto redirect_more;
5856 }
Denis Vlasenko559691a2008-10-05 18:39:31 +00005857#endif
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005858 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00005859 } while ((redir = redir->nfile.next) != NULL);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005860 INT_ON;
Denys Vlasenkod07a15b2017-07-30 16:51:05 +02005861
5862//dash:#define REDIR_SAVEFD2 03 /* set preverrout */
5863#define REDIR_SAVEFD2 0
5864 // dash has a bug: since REDIR_SAVEFD2=3 and REDIR_PUSH=1, this test
5865 // triggers for pure REDIR_PUSH too. Thus, this is done almost always,
5866 // not only for calls with flags containing REDIR_SAVEFD2.
Denys Vlasenko035486c2017-07-31 04:09:19 +02005867 // We do this unconditionally (see save_fd_on_redirect()).
Denys Vlasenkod07a15b2017-07-30 16:51:05 +02005868 //if ((flags & REDIR_SAVEFD2) && copied_fd2 >= 0)
5869 // preverrout_fd = copied_fd2;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005870}
5871
Denys Vlasenko170f93e2017-07-29 18:54:53 +02005872static int
5873redirectsafe(union node *redir, int flags)
5874{
5875 int err;
5876 volatile int saveint;
5877 struct jmploc *volatile savehandler = exception_handler;
5878 struct jmploc jmploc;
5879
5880 SAVE_INT(saveint);
5881 /* "echo 9>/dev/null; echo >&9; echo result: $?" - result should be 1, not 2! */
Denys Vlasenko035486c2017-07-31 04:09:19 +02005882 err = setjmp(jmploc.loc); /* was = setjmp(jmploc.loc) * 2; */
Denys Vlasenko170f93e2017-07-29 18:54:53 +02005883 if (!err) {
5884 exception_handler = &jmploc;
5885 redirect(redir, flags);
5886 }
5887 exception_handler = savehandler;
5888 if (err && exception_type != EXERROR)
5889 longjmp(exception_handler->loc, 1);
5890 RESTORE_INT(saveint);
5891 return err;
5892}
5893
Denys Vlasenko1c79aeb2017-07-29 22:51:52 +02005894static struct redirtab*
5895pushredir(union node *redir)
5896{
5897 struct redirtab *sv;
5898 int i;
5899
5900 if (!redir)
5901 return redirlist;
5902
5903 i = 0;
5904 do {
5905 i++;
5906#if BASH_REDIR_OUTPUT
5907 if (redir->nfile.type == NTO2)
5908 i++;
5909#endif
5910 redir = redir->nfile.next;
5911 } while (redir);
5912
5913 sv = ckzalloc(sizeof(*sv) + i * sizeof(sv->two_fd[0]));
5914 sv->pair_count = i;
5915 while (--i >= 0)
Denys Vlasenko035486c2017-07-31 04:09:19 +02005916 sv->two_fd[i].orig_fd = sv->two_fd[i].moved_to = EMPTY;
Denys Vlasenko1c79aeb2017-07-29 22:51:52 +02005917 sv->next = redirlist;
5918 redirlist = sv;
5919 return sv->next;
5920}
5921
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005922/*
5923 * Undo the effects of the last redirection.
5924 */
5925static void
Denys Vlasenko035486c2017-07-31 04:09:19 +02005926popredir(int drop)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005927{
5928 struct redirtab *rp;
5929 int i;
5930
Denys Vlasenkoeaf94362016-10-25 21:46:03 +02005931 if (redirlist == NULL)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005932 return;
5933 INT_OFF;
5934 rp = redirlist;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005935 for (i = 0; i < rp->pair_count; i++) {
Denys Vlasenko035486c2017-07-31 04:09:19 +02005936 int fd = rp->two_fd[i].orig_fd;
5937 int copy = rp->two_fd[i].moved_to;
Denis Vlasenko22f74142008-07-24 22:34:43 +00005938 if (copy == CLOSED) {
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005939 if (!drop)
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005940 close(fd);
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005941 continue;
5942 }
Denis Vlasenko22f74142008-07-24 22:34:43 +00005943 if (copy != EMPTY) {
Denys Vlasenko035486c2017-07-31 04:09:19 +02005944 if (!drop) {
Denis Vlasenko5a867312008-07-24 19:46:38 +00005945 /*close(fd);*/
Denys Vlasenko64774602016-10-26 15:24:30 +02005946 dup2_or_raise(copy, fd);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005947 }
Denys Vlasenko035486c2017-07-31 04:09:19 +02005948 close(copy);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005949 }
5950 }
5951 redirlist = rp->next;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005952 free(rp);
5953 INT_ON;
5954}
5955
Denys Vlasenko1c79aeb2017-07-29 22:51:52 +02005956static void
5957unwindredir(struct redirtab *stop)
5958{
5959 while (redirlist != stop)
Denys Vlasenko035486c2017-07-31 04:09:19 +02005960 popredir(/*drop:*/ 0);
Denys Vlasenko1c79aeb2017-07-29 22:51:52 +02005961}
5962
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005963
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00005964/* ============ Routines to expand arguments to commands
5965 *
5966 * We have to deal with backquotes, shell variables, and file metacharacters.
5967 */
5968
Denys Vlasenko0b883582016-12-23 16:49:07 +01005969#if ENABLE_FEATURE_SH_MATH
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00005970static arith_t
5971ash_arith(const char *s)
5972{
Denys Vlasenko06d44d72010-09-13 12:49:03 +02005973 arith_state_t math_state;
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00005974 arith_t result;
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00005975
Denys Vlasenko06d44d72010-09-13 12:49:03 +02005976 math_state.lookupvar = lookupvar;
Denys Vlasenko0a0acb52015-04-18 19:36:38 +02005977 math_state.setvar = setvar0;
Denys Vlasenko06d44d72010-09-13 12:49:03 +02005978 //math_state.endofname = endofname;
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00005979
5980 INT_OFF;
Denys Vlasenko06d44d72010-09-13 12:49:03 +02005981 result = arith(&math_state, s);
Denys Vlasenko063847d2010-09-15 13:33:02 +02005982 if (math_state.errmsg)
5983 ash_msg_and_raise_error(math_state.errmsg);
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00005984 INT_ON;
5985
5986 return result;
5987}
Denis Vlasenko448d30e2008-06-27 00:24:11 +00005988#endif
Denys Vlasenkobaa41c72018-01-10 13:22:25 +01005989#if BASH_SUBSTR
5990# if ENABLE_FEATURE_SH_MATH
5991static int substr_atoi(const char *s)
5992{
5993 arith_t t = ash_arith(s);
5994 if (sizeof(t) > sizeof(int)) {
5995 /* clamp very large or very large negative nums for ${v:N:M}:
5996 * else "${v:0:0x100000001}" would work as "${v:0:1}"
5997 */
5998 if (t > INT_MAX)
5999 t = INT_MAX;
6000 if (t < INT_MIN)
6001 t = INT_MIN;
6002 }
6003 return t;
6004}
6005# else
6006# define substr_atoi(s) number(s)
6007# endif
6008#endif
Denis Vlasenko448d30e2008-06-27 00:24:11 +00006009
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006010/*
6011 * expandarg flags
6012 */
6013#define EXP_FULL 0x1 /* perform word splitting & file globbing */
6014#define EXP_TILDE 0x2 /* do normal tilde expansion */
6015#define EXP_VARTILDE 0x4 /* expand tildes in an assignment */
6016#define EXP_REDIR 0x8 /* file glob for a redirection (1 match only) */
Denys Vlasenkodb74c6c2016-10-24 21:12:33 +02006017/* ^^^^^^^^^^^^^^ this is meant to support constructs such as "cmd >file*.txt"
6018 * POSIX says for this case:
6019 * Pathname expansion shall not be performed on the word by a
6020 * non-interactive shell; an interactive shell may perform it, but shall
6021 * do so only when the expansion would result in one word.
6022 * Currently, our code complies to the above rule by never globbing
6023 * redirection filenames.
6024 * Bash performs globbing, unless it is non-interactive and in POSIX mode.
6025 * (this means that on a typical Linux distro, bash almost always
6026 * performs globbing, and thus diverges from what we do).
6027 */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006028#define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */
Denys Vlasenko216913c2018-04-02 12:35:04 +02006029#define EXP_VARTILDE2 0x20 /* expand tildes after colons only */
6030#define EXP_WORD 0x40 /* expand word in parameter expansion */
Denys Vlasenko440da972018-08-05 14:29:58 +02006031#define EXP_QUOTED 0x100 /* expand word in double quotes */
Denys Vlasenkoecc85832020-02-20 10:06:20 +01006032#define EXP_KEEPNUL 0x200 /* do not skip NUL characters */
Denys Vlasenko82331882020-02-24 10:02:50 +01006033#define EXP_DISCARD 0x400 /* discard result of expansion */
Denys Vlasenkoecc85832020-02-20 10:06:20 +01006034
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006035/*
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006036 * rmescape() flags
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006037 */
6038#define RMESCAPE_ALLOC 0x1 /* Allocate a new string */
6039#define RMESCAPE_GLOB 0x2 /* Add backslashes for glob */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006040#define RMESCAPE_GROW 0x8 /* Grow strings instead of stalloc */
6041#define RMESCAPE_HEAP 0x10 /* Malloc strings instead of stalloc */
6042
Ron Yorstond68d1fb2015-05-18 09:49:28 +02006043/* Add CTLESC when necessary. */
Denys Vlasenko216913c2018-04-02 12:35:04 +02006044#define QUOTES_ESC (EXP_FULL | EXP_CASE)
Ron Yorstond68d1fb2015-05-18 09:49:28 +02006045
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006046/*
6047 * Structure specifying which parts of the string should be searched
6048 * for IFS characters.
6049 */
6050struct ifsregion {
6051 struct ifsregion *next; /* next region in list */
6052 int begoff; /* offset of start of region */
6053 int endoff; /* offset of end of region */
6054 int nulonly; /* search for nul bytes only */
6055};
6056
6057struct arglist {
6058 struct strlist *list;
6059 struct strlist **lastp;
6060};
6061
6062/* output of current string */
6063static char *expdest;
6064/* list of back quote expressions */
6065static struct nodelist *argbackq;
6066/* first struct in list of ifs regions */
6067static struct ifsregion ifsfirst;
6068/* last struct in list */
6069static struct ifsregion *ifslastp;
6070/* holds expanded arg list */
6071static struct arglist exparg;
6072
6073/*
Denys Vlasenko455e4222016-10-27 14:45:13 +02006074 * Break the argument string into pieces based upon IFS and add the
6075 * strings to the argument list. The regions of the string to be
6076 * searched for IFS characters have been stored by recordregion.
6077 */
6078static void
6079ifsbreakup(char *string, struct arglist *arglist)
6080{
6081 struct ifsregion *ifsp;
6082 struct strlist *sp;
6083 char *start;
6084 char *p;
6085 char *q;
6086 const char *ifs, *realifs;
6087 int ifsspc;
6088 int nulonly;
6089
6090 start = string;
6091 if (ifslastp != NULL) {
6092 ifsspc = 0;
6093 nulonly = 0;
6094 realifs = ifsset() ? ifsval() : defifs;
6095 ifsp = &ifsfirst;
6096 do {
Denys Vlasenko9a95df92018-04-02 14:27:50 +02006097 int afternul;
6098
Denys Vlasenko455e4222016-10-27 14:45:13 +02006099 p = string + ifsp->begoff;
Denys Vlasenko9a95df92018-04-02 14:27:50 +02006100 afternul = nulonly;
Denys Vlasenko455e4222016-10-27 14:45:13 +02006101 nulonly = ifsp->nulonly;
6102 ifs = nulonly ? nullstr : realifs;
6103 ifsspc = 0;
6104 while (p < string + ifsp->endoff) {
6105 q = p;
6106 if ((unsigned char)*p == CTLESC)
6107 p++;
6108 if (!strchr(ifs, *p)) {
6109 p++;
6110 continue;
6111 }
Denys Vlasenko9a95df92018-04-02 14:27:50 +02006112 if (!(afternul || nulonly))
Denys Vlasenko455e4222016-10-27 14:45:13 +02006113 ifsspc = (strchr(defifs, *p) != NULL);
6114 /* Ignore IFS whitespace at start */
6115 if (q == start && ifsspc) {
6116 p++;
6117 start = p;
6118 continue;
6119 }
6120 *q = '\0';
6121 sp = stzalloc(sizeof(*sp));
6122 sp->text = start;
6123 *arglist->lastp = sp;
6124 arglist->lastp = &sp->next;
6125 p++;
6126 if (!nulonly) {
6127 for (;;) {
6128 if (p >= string + ifsp->endoff) {
6129 break;
6130 }
6131 q = p;
6132 if ((unsigned char)*p == CTLESC)
6133 p++;
6134 if (strchr(ifs, *p) == NULL) {
6135 p = q;
6136 break;
6137 }
6138 if (strchr(defifs, *p) == NULL) {
6139 if (ifsspc) {
6140 p++;
6141 ifsspc = 0;
6142 } else {
6143 p = q;
6144 break;
6145 }
6146 } else
6147 p++;
6148 }
6149 }
6150 start = p;
6151 } /* while */
6152 ifsp = ifsp->next;
6153 } while (ifsp != NULL);
6154 if (nulonly)
6155 goto add;
6156 }
6157
6158 if (!*start)
6159 return;
6160
6161 add:
6162 sp = stzalloc(sizeof(*sp));
6163 sp->text = start;
6164 *arglist->lastp = sp;
6165 arglist->lastp = &sp->next;
6166}
6167
6168static void
6169ifsfree(void)
6170{
Denys Vlasenko5ac04f22016-10-27 14:46:50 +02006171 struct ifsregion *p = ifsfirst.next;
6172
6173 if (!p)
6174 goto out;
Denys Vlasenko455e4222016-10-27 14:45:13 +02006175
6176 INT_OFF;
Denys Vlasenko455e4222016-10-27 14:45:13 +02006177 do {
6178 struct ifsregion *ifsp;
6179 ifsp = p->next;
6180 free(p);
6181 p = ifsp;
6182 } while (p);
Denys Vlasenko455e4222016-10-27 14:45:13 +02006183 ifsfirst.next = NULL;
6184 INT_ON;
Denys Vlasenko5ac04f22016-10-27 14:46:50 +02006185 out:
6186 ifslastp = NULL;
Denys Vlasenko455e4222016-10-27 14:45:13 +02006187}
6188
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006189static size_t
6190esclen(const char *start, const char *p)
6191{
6192 size_t esc = 0;
6193
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006194 while (p > start && (unsigned char)*--p == CTLESC) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006195 esc++;
6196 }
6197 return esc;
6198}
6199
6200/*
6201 * Remove any CTLESC characters from a string.
6202 */
Denys Vlasenko740058b2018-01-09 17:01:00 +01006203#if !BASH_PATTERN_SUBST
6204#define rmescapes(str, flag, slash_position) \
6205 rmescapes(str, flag)
6206#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006207static char *
Denys Vlasenko740058b2018-01-09 17:01:00 +01006208rmescapes(char *str, int flag, int *slash_position)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006209{
Ron Yorston417622c2015-05-18 09:59:14 +02006210 static const char qchars[] ALIGN1 = {
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01006211 IF_BASH_PATTERN_SUBST('/',) CTLESC, CTLQUOTEMARK, '\0' };
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00006212
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006213 char *p, *q, *r;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006214 unsigned protect_against_glob;
6215 unsigned globbing;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006216
Denys Vlasenko740058b2018-01-09 17:01:00 +01006217 p = strpbrk(str, qchars IF_BASH_PATTERN_SUBST(+ !slash_position));
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006218 if (!p)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006219 return str;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006220
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006221 q = p;
6222 r = str;
6223 if (flag & RMESCAPE_ALLOC) {
6224 size_t len = p - str;
6225 size_t fulllen = len + strlen(p) + 1;
6226
6227 if (flag & RMESCAPE_GROW) {
Colin Watson3963d942010-04-26 14:21:27 +02006228 int strloc = str - (char *)stackblock();
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006229 r = makestrspace(fulllen, expdest);
Colin Watson3963d942010-04-26 14:21:27 +02006230 /* p and str may be invalidated by makestrspace */
6231 str = (char *)stackblock() + strloc;
6232 p = str + len;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006233 } else if (flag & RMESCAPE_HEAP) {
6234 r = ckmalloc(fulllen);
6235 } else {
6236 r = stalloc(fulllen);
6237 }
6238 q = r;
6239 if (len > 0) {
Denys Vlasenko5ace96a2017-07-23 21:46:02 +02006240 q = (char *)mempcpy(q, str, len);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006241 }
6242 }
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006243
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006244 globbing = flag & RMESCAPE_GLOB;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006245 protect_against_glob = globbing;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006246 while (*p) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006247 if ((unsigned char)*p == CTLQUOTEMARK) {
Denys Vlasenko216913c2018-04-02 12:35:04 +02006248// Note: protect_against_glob only affect whether
Denys Vlasenkofda9faf2017-07-05 19:10:21 +02006249// CTLESC,<ch> gets converted to <ch> or to \<ch>
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006250 p++;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006251 protect_against_glob = globbing;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006252 continue;
6253 }
Denys Vlasenko216913c2018-04-02 12:35:04 +02006254 if (*p == '\\') {
6255 /* naked back slash */
6256 protect_against_glob = 0;
6257 goto copy;
6258 }
Ron Yorston549deab2015-05-18 09:57:51 +02006259 if ((unsigned char)*p == CTLESC) {
6260 p++;
Denys Vlasenko13f20912016-09-25 20:54:25 +02006261#if DEBUG
6262 if (*p == '\0')
6263 ash_msg_and_raise_error("CTLESC at EOL (shouldn't happen)");
6264#endif
Ron Yorston549deab2015-05-18 09:57:51 +02006265 if (protect_against_glob) {
Denys Vlasenkofda9faf2017-07-05 19:10:21 +02006266 /*
6267 * We used to trust glob() and fnmatch() to eat
6268 * superfluous escapes (\z where z has no
6269 * special meaning anyway). But this causes
6270 * bugs such as string of one greek letter rho
Denys Vlasenkoed79a632017-07-05 19:20:43 +02006271 * (unicode-encoded as two bytes "cf,81")
Denys Vlasenkofda9faf2017-07-05 19:10:21 +02006272 * getting encoded as "cf,CTLESC,81"
6273 * and here, converted to "cf,\,81" -
6274 * which does not go well with some flavors
Denys Vlasenko92b8d9c2017-07-05 19:13:44 +02006275 * of fnmatch() in unicode locales
6276 * (for example, glibc <= 2.22).
Denys Vlasenkofda9faf2017-07-05 19:10:21 +02006277 *
6278 * Lets add "\" only on the chars which need it.
Denys Vlasenko4142f012017-07-05 22:19:28 +02006279 * Testcases for less obvious chars are shown.
Denys Vlasenkofda9faf2017-07-05 19:10:21 +02006280 */
6281 if (*p == '*'
6282 || *p == '?'
6283 || *p == '['
Denys Vlasenko8de5b9f2018-02-13 14:43:29 +01006284 || *p == '\\' /* case '\' in \\ ) echo ok;; *) echo WRONG;; esac */
6285 || *p == ']' /* case ']' in [a\]] ) echo ok;; *) echo WRONG;; esac */
6286 || *p == '-' /* case '-' in [a\-c]) echo ok;; *) echo WRONG;; esac */
6287 || *p == '!' /* case '!' in [\!] ) echo ok;; *) echo WRONG;; esac */
Denys Vlasenko4142f012017-07-05 22:19:28 +02006288 /* Some libc support [^negate], that's why "^" also needs love */
Denys Vlasenko8de5b9f2018-02-13 14:43:29 +01006289 || *p == '^' /* case '^' in [\^] ) echo ok;; *) echo WRONG;; esac */
Denys Vlasenkofda9faf2017-07-05 19:10:21 +02006290 ) {
6291 *q++ = '\\';
6292 }
Ron Yorston549deab2015-05-18 09:57:51 +02006293 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006294 }
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01006295#if BASH_PATTERN_SUBST
Denys Vlasenko740058b2018-01-09 17:01:00 +01006296 else if (slash_position && p == str + *slash_position) {
6297 /* stop handling globbing */
6298 globbing = 0;
6299 *slash_position = q - r;
6300 slash_position = NULL;
Ron Yorston417622c2015-05-18 09:59:14 +02006301 }
6302#endif
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006303 protect_against_glob = globbing;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006304 copy:
6305 *q++ = *p++;
6306 }
6307 *q = '\0';
6308 if (flag & RMESCAPE_GROW) {
6309 expdest = r;
6310 STADJUST(q - r + 1, expdest);
6311 }
6312 return r;
6313}
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006314#define pmatch(a, b) !fnmatch((a), (b), 0)
6315
6316/*
6317 * Prepare a pattern for a expmeta (internal glob(3)) call.
6318 *
6319 * Returns an stalloced string.
6320 */
6321static char *
Ron Yorston549deab2015-05-18 09:57:51 +02006322preglob(const char *pattern, int flag)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006323{
Denys Vlasenko740058b2018-01-09 17:01:00 +01006324 return rmescapes((char *)pattern, flag | RMESCAPE_GLOB, NULL);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006325}
6326
6327/*
6328 * Put a string on the stack.
6329 */
Denys Vlasenko45dd87a2020-02-21 16:30:44 +01006330static size_t
Denys Vlasenkoecc85832020-02-20 10:06:20 +01006331memtodest(const char *p, size_t len, int flags)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006332{
Denys Vlasenkoecc85832020-02-20 10:06:20 +01006333 int syntax = flags & EXP_QUOTED ? DQSYNTAX : BASESYNTAX;
Ron Yorstond68d1fb2015-05-18 09:49:28 +02006334 char *q;
Denys Vlasenko45dd87a2020-02-21 16:30:44 +01006335 char *s;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006336
Ron Yorstond68d1fb2015-05-18 09:49:28 +02006337 if (!len)
Denys Vlasenko45dd87a2020-02-21 16:30:44 +01006338 return 0;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006339
Denys Vlasenkoecc85832020-02-20 10:06:20 +01006340 q = makestrspace(len * 2, expdest);
Denys Vlasenko45dd87a2020-02-21 16:30:44 +01006341 s = q;
Ron Yorstond68d1fb2015-05-18 09:49:28 +02006342
6343 do {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006344 unsigned char c = *p++;
Ron Yorstond68d1fb2015-05-18 09:49:28 +02006345 if (c) {
Denys Vlasenkoecc85832020-02-20 10:06:20 +01006346 if (flags & QUOTES_ESC) {
Denys Vlasenko2fe66b12016-12-12 17:39:12 +01006347 int n = SIT(c, syntax);
6348 if (n == CCTL
Denys Vlasenkoecc85832020-02-20 10:06:20 +01006349 || ((flags & EXP_QUOTED) && n == CBACK)
Denys Vlasenko2fe66b12016-12-12 17:39:12 +01006350 ) {
6351 USTPUTC(CTLESC, q);
6352 }
Denys Vlasenkob3f29b42016-09-21 16:25:58 +02006353 }
Denys Vlasenkoecc85832020-02-20 10:06:20 +01006354 } else if (!(flags & EXP_KEEPNUL))
Ron Yorstond68d1fb2015-05-18 09:49:28 +02006355 continue;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006356 USTPUTC(c, q);
Ron Yorstond68d1fb2015-05-18 09:49:28 +02006357 } while (--len);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006358
6359 expdest = q;
Denys Vlasenko45dd87a2020-02-21 16:30:44 +01006360 return q - s;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006361}
6362
Ron Yorstond68d1fb2015-05-18 09:49:28 +02006363static size_t
Denys Vlasenkoecc85832020-02-20 10:06:20 +01006364strtodest(const char *p, int flags)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006365{
Ron Yorstond68d1fb2015-05-18 09:49:28 +02006366 size_t len = strlen(p);
Denys Vlasenkoecc85832020-02-20 10:06:20 +01006367 memtodest(p, len, flags);
Ron Yorstond68d1fb2015-05-18 09:49:28 +02006368 return len;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006369}
6370
6371/*
Denys Vlasenko45dd87a2020-02-21 16:30:44 +01006372 * Our own itoa().
6373 * cvtnum() is used even if math support is off (to prepare $? values and such).
6374 */
6375static int
6376cvtnum(arith_t num, int flags)
6377{
6378 /* 32-bit and wider ints require buffer size of bytes*3 (or less) */
6379 /* If narrower: worst case, 1-byte ints: need 5 bytes: "-127<NUL>" */
6380 int len = (sizeof(arith_t) >= 4) ? sizeof(arith_t) * 3 : sizeof(arith_t) * 3 + 2;
6381 char buf[len];
6382
6383 len = fmtstr(buf, len, ARITH_FMT, num);
6384 return memtodest(buf, len, flags);
6385}
6386
6387/*
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006388 * Record the fact that we have to scan this region of the
6389 * string for IFS characters.
6390 */
6391static void
6392recordregion(int start, int end, int nulonly)
6393{
6394 struct ifsregion *ifsp;
6395
6396 if (ifslastp == NULL) {
6397 ifsp = &ifsfirst;
6398 } else {
6399 INT_OFF;
Denis Vlasenko597906c2008-02-20 16:38:54 +00006400 ifsp = ckzalloc(sizeof(*ifsp));
6401 /*ifsp->next = NULL; - ckzalloc did it */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006402 ifslastp->next = ifsp;
6403 INT_ON;
6404 }
6405 ifslastp = ifsp;
6406 ifslastp->begoff = start;
6407 ifslastp->endoff = end;
6408 ifslastp->nulonly = nulonly;
6409}
6410
6411static void
6412removerecordregions(int endoff)
6413{
6414 if (ifslastp == NULL)
6415 return;
6416
6417 if (ifsfirst.endoff > endoff) {
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006418 while (ifsfirst.next) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006419 struct ifsregion *ifsp;
6420 INT_OFF;
6421 ifsp = ifsfirst.next->next;
6422 free(ifsfirst.next);
6423 ifsfirst.next = ifsp;
6424 INT_ON;
6425 }
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006426 if (ifsfirst.begoff > endoff) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006427 ifslastp = NULL;
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006428 } else {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006429 ifslastp = &ifsfirst;
6430 ifsfirst.endoff = endoff;
6431 }
6432 return;
6433 }
6434
6435 ifslastp = &ifsfirst;
6436 while (ifslastp->next && ifslastp->next->begoff < endoff)
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006437 ifslastp = ifslastp->next;
6438 while (ifslastp->next) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006439 struct ifsregion *ifsp;
6440 INT_OFF;
6441 ifsp = ifslastp->next->next;
6442 free(ifslastp->next);
6443 ifslastp->next = ifsp;
6444 INT_ON;
6445 }
6446 if (ifslastp->endoff > endoff)
6447 ifslastp->endoff = endoff;
6448}
6449
6450static char *
Denys Vlasenko82331882020-02-24 10:02:50 +01006451exptilde(char *startp, int flag)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006452{
Denys Vlasenkocd716832009-11-28 22:14:02 +01006453 unsigned char c;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006454 char *name;
6455 struct passwd *pw;
6456 const char *home;
Denys Vlasenko82331882020-02-24 10:02:50 +01006457 char *p;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006458
Denys Vlasenko82331882020-02-24 10:02:50 +01006459 p = startp;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006460 name = p + 1;
6461
6462 while ((c = *++p) != '\0') {
6463 switch (c) {
6464 case CTLESC:
6465 return startp;
6466 case CTLQUOTEMARK:
6467 return startp;
6468 case ':':
Denys Vlasenkoecc85832020-02-20 10:06:20 +01006469 if (flag & EXP_VARTILDE)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006470 goto done;
6471 break;
6472 case '/':
6473 case CTLENDVAR:
6474 goto done;
6475 }
6476 }
6477 done:
Denys Vlasenko82331882020-02-24 10:02:50 +01006478 if (flag & EXP_DISCARD)
6479 goto out;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006480 *p = '\0';
6481 if (*name == '\0') {
Denys Vlasenkoea8b2522010-06-02 12:57:26 +02006482 home = lookupvar("HOME");
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006483 } else {
6484 pw = getpwnam(name);
6485 if (pw == NULL)
6486 goto lose;
6487 home = pw->pw_dir;
6488 }
Denys Vlasenko82331882020-02-24 10:02:50 +01006489 *p = c;
Denys Vlasenkoe880b1f2020-02-16 18:31:05 +01006490 if (!home)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006491 goto lose;
Denys Vlasenkoecc85832020-02-20 10:06:20 +01006492 strtodest(home, flag | EXP_QUOTED);
Denys Vlasenko82331882020-02-24 10:02:50 +01006493 out:
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006494 return p;
6495 lose:
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006496 return startp;
6497}
6498
6499/*
6500 * Execute a command inside back quotes. If it's a builtin command, we
6501 * want to save its output in a block obtained from malloc. Otherwise
6502 * we fork off a subprocess and get the output of the command via a pipe.
6503 * Should be called with interrupts off.
6504 */
6505struct backcmd { /* result of evalbackcmd */
6506 int fd; /* file descriptor to read from */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006507 int nleft; /* number of chars in buffer */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00006508 char *buf; /* buffer */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006509 struct job *jp; /* job structure for command */
6510};
6511
6512/* These forward decls are needed to use "eval" code for backticks handling: */
Denys Vlasenko1e3e2cc2017-07-25 20:31:14 +02006513/* flags in argument to evaltree */
6514#define EV_EXIT 01 /* exit after evaluating tree */
6515#define EV_TESTED 02 /* exit status is checked; ignore -e flag */
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02006516static int evaltree(union node *, int);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006517
Denys Vlasenko619d9b52017-07-28 15:28:33 +02006518/* An evaltree() which is known to never return.
6519 * Used to use an alias:
6520 * static int evaltreenr(union node *, int) __attribute__((alias("evaltree"),__noreturn__));
6521 * but clang was reported to "transfer" noreturn-ness to evaltree() as well.
6522 */
6523static ALWAYS_INLINE NORETURN void
6524evaltreenr(union node *n, int flags)
6525{
6526 evaltree(n, flags);
6527 bb_unreachable(abort());
6528 /* NOTREACHED */
6529}
6530
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02006531static void FAST_FUNC
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006532evalbackcmd(union node *n, struct backcmd *result)
6533{
Denys Vlasenko579ad102016-10-25 21:10:20 +02006534 int pip[2];
6535 struct job *jp;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006536
6537 result->fd = -1;
6538 result->buf = NULL;
6539 result->nleft = 0;
6540 result->jp = NULL;
Denys Vlasenko579ad102016-10-25 21:10:20 +02006541 if (n == NULL) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006542 goto out;
Denys Vlasenko579ad102016-10-25 21:10:20 +02006543 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006544
Denys Vlasenko579ad102016-10-25 21:10:20 +02006545 if (pipe(pip) < 0)
Denys Vlasenko12ffefb2017-08-23 14:52:56 +02006546 ash_msg_and_raise_perror("can't create pipe");
Denys Vlasenko579ad102016-10-25 21:10:20 +02006547 jp = makejob(/*n,*/ 1);
6548 if (forkshell(jp, n, FORK_NOJOB) == 0) {
Denys Vlasenko70392332016-10-27 02:31:55 +02006549 /* child */
Denys Vlasenko579ad102016-10-25 21:10:20 +02006550 FORCE_INT_ON;
6551 close(pip[0]);
6552 if (pip[1] != 1) {
6553 /*close(1);*/
Denys Vlasenko64774602016-10-26 15:24:30 +02006554 dup2_or_raise(pip[1], 1);
Denys Vlasenko579ad102016-10-25 21:10:20 +02006555 close(pip[1]);
6556 }
Denys Vlasenko960ca382016-10-25 18:12:15 +02006557/* TODO: eflag clearing makes the following not abort:
6558 * ash -c 'set -e; z=$(false;echo foo); echo $z'
6559 * which is what bash does (unless it is in POSIX mode).
6560 * dash deleted "eflag = 0" line in the commit
6561 * Date: Mon, 28 Jun 2010 17:11:58 +1000
6562 * [EVAL] Don't clear eflag in evalbackcmd
6563 * For now, preserve bash-like behavior, it seems to be somewhat more useful:
6564 */
Denys Vlasenko579ad102016-10-25 21:10:20 +02006565 eflag = 0;
Denys Vlasenko5ac04f22016-10-27 14:46:50 +02006566 ifsfree();
Denys Vlasenko619d9b52017-07-28 15:28:33 +02006567 evaltreenr(n, EV_EXIT);
Denys Vlasenko579ad102016-10-25 21:10:20 +02006568 /* NOTREACHED */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006569 }
Denys Vlasenko70392332016-10-27 02:31:55 +02006570 /* parent */
Denys Vlasenko579ad102016-10-25 21:10:20 +02006571 close(pip[1]);
6572 result->fd = pip[0];
6573 result->jp = jp;
6574
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006575 out:
6576 TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n",
6577 result->fd, result->buf, result->nleft, result->jp));
6578}
6579
6580/*
6581 * Expand stuff in backwards quotes.
6582 */
6583static void
Ron Yorston549deab2015-05-18 09:57:51 +02006584expbackq(union node *cmd, int flag)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006585{
6586 struct backcmd in;
6587 int i;
6588 char buf[128];
6589 char *p;
6590 char *dest;
6591 int startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006592 struct stackmark smark;
6593
Denys Vlasenko82331882020-02-24 10:02:50 +01006594 if (flag & EXP_DISCARD)
6595 goto out;
6596
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006597 INT_OFF;
Denys Vlasenko60ca8342016-09-30 11:21:21 +02006598 startloc = expdest - (char *)stackblock();
6599 pushstackmark(&smark, startloc);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006600 evalbackcmd(cmd, &in);
6601 popstackmark(&smark);
6602
6603 p = in.buf;
6604 i = in.nleft;
6605 if (i == 0)
6606 goto read;
6607 for (;;) {
Denys Vlasenkoecc85832020-02-20 10:06:20 +01006608 memtodest(p, i, flag);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006609 read:
6610 if (in.fd < 0)
6611 break;
Ron Yorston61d6ae22015-04-19 10:50:25 +01006612 i = nonblock_immune_read(in.fd, buf, sizeof(buf));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006613 TRACE(("expbackq: read returns %d\n", i));
6614 if (i <= 0)
6615 break;
6616 p = buf;
6617 }
6618
Denis Vlasenko60818682007-09-28 22:07:23 +00006619 free(in.buf);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006620 if (in.fd >= 0) {
6621 close(in.fd);
6622 back_exitstatus = waitforjob(in.jp);
6623 }
6624 INT_ON;
6625
6626 /* Eat all trailing newlines */
6627 dest = expdest;
Denys Vlasenko9ee58922020-02-17 10:24:32 +01006628 for (; dest > ((char *)stackblock() + startloc) && dest[-1] == '\n';)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006629 STUNPUTC(dest);
6630 expdest = dest;
6631
Ron Yorston549deab2015-05-18 09:57:51 +02006632 if (!(flag & EXP_QUOTED))
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006633 recordregion(startloc, dest - (char *)stackblock(), 0);
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006634 TRACE(("evalbackq: size:%d:'%.*s'\n",
6635 (int)((dest - (char *)stackblock()) - startloc),
6636 (int)((dest - (char *)stackblock()) - startloc),
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006637 stackblock() + startloc));
Denys Vlasenko82331882020-02-24 10:02:50 +01006638
6639 out:
6640 argbackq = argbackq->next;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006641}
6642
Denys Vlasenko82331882020-02-24 10:02:50 +01006643/* expari needs it */
6644static char *argstr(char *p, int flag);
6645
Denys Vlasenko0b883582016-12-23 16:49:07 +01006646#if ENABLE_FEATURE_SH_MATH
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006647/*
6648 * Expand arithmetic expression. Backup to start of expression,
6649 * evaluate, place result in (backed up) result, adjust string position.
6650 */
Denys Vlasenko82331882020-02-24 10:02:50 +01006651static char *
6652expari(char *start, int flag)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006653{
Denys Vlasenko82331882020-02-24 10:02:50 +01006654 struct stackmark sm;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006655 int begoff;
Denys Vlasenko82331882020-02-24 10:02:50 +01006656 int endoff;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006657 int len;
Denys Vlasenko82331882020-02-24 10:02:50 +01006658 arith_t result;
6659 char *p;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006660
Denys Vlasenko82331882020-02-24 10:02:50 +01006661 p = stackblock();
6662 begoff = expdest - p;
6663 p = argstr(start, flag & EXP_DISCARD);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006664
Denys Vlasenko82331882020-02-24 10:02:50 +01006665 if (flag & EXP_DISCARD)
6666 goto out;
6667
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006668 start = stackblock();
Denys Vlasenko82331882020-02-24 10:02:50 +01006669 endoff = expdest - start;
6670 start += begoff;
6671 STADJUST(start - expdest, expdest);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006672
6673 removerecordregions(begoff);
6674
Ron Yorston549deab2015-05-18 09:57:51 +02006675 if (flag & QUOTES_ESC)
Denys Vlasenko82331882020-02-24 10:02:50 +01006676 rmescapes(start, 0, NULL);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006677
Denys Vlasenko82331882020-02-24 10:02:50 +01006678 pushstackmark(&sm, endoff);
6679 result = ash_arith(start);
6680 popstackmark(&sm);
6681
6682 len = cvtnum(result, flag);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006683
Ron Yorston549deab2015-05-18 09:57:51 +02006684 if (!(flag & EXP_QUOTED))
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006685 recordregion(begoff, begoff + len, 0);
Denys Vlasenko82331882020-02-24 10:02:50 +01006686
6687 out:
6688 return p;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006689}
6690#endif
6691
6692/* argstr needs it */
Denys Vlasenkob8c0bc12017-07-26 23:03:21 +02006693static char *evalvar(char *p, int flags);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006694
6695/*
6696 * Perform variable and command substitution. If EXP_FULL is set, output CTLESC
6697 * characters to allow for further processing. Otherwise treat
6698 * $@ like $* since no splitting will be performed.
6699 */
Denys Vlasenko82331882020-02-24 10:02:50 +01006700static char *
Denys Vlasenko7f198482020-02-24 09:57:08 +01006701argstr(char *p, int flag)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006702{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00006703 static const char spclchars[] ALIGN1 = {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006704 '=',
6705 ':',
6706 CTLQUOTEMARK,
6707 CTLENDVAR,
6708 CTLESC,
6709 CTLVAR,
6710 CTLBACKQ,
Denys Vlasenko0b883582016-12-23 16:49:07 +01006711#if ENABLE_FEATURE_SH_MATH
Denys Vlasenko82331882020-02-24 10:02:50 +01006712 CTLARI,
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006713 CTLENDARI,
6714#endif
Denys Vlasenkocd716832009-11-28 22:14:02 +01006715 '\0'
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006716 };
6717 const char *reject = spclchars;
Denys Vlasenko7f198482020-02-24 09:57:08 +01006718 int breakall = (flag & (EXP_WORD | EXP_QUOTED)) == EXP_WORD;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006719 int inquotes;
6720 size_t length;
6721 int startloc;
6722
Denys Vlasenko82331882020-02-24 10:02:50 +01006723 reject += !!(flag & EXP_VARTILDE2);
6724 reject += flag & EXP_VARTILDE ? 0 : 2;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006725 inquotes = 0;
6726 length = 0;
Denys Vlasenko7f198482020-02-24 09:57:08 +01006727 if (flag & EXP_TILDE) {
Denys Vlasenko7f198482020-02-24 09:57:08 +01006728 flag &= ~EXP_TILDE;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006729 tilde:
Denys Vlasenko82331882020-02-24 10:02:50 +01006730 if (*p == '~')
6731 p = exptilde(p, flag);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006732 }
6733 start:
6734 startloc = expdest - (char *)stackblock();
6735 for (;;) {
Denys Vlasenko82331882020-02-24 10:02:50 +01006736 int end;
Denys Vlasenkocd716832009-11-28 22:14:02 +01006737 unsigned char c;
6738
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006739 length += strcspn(p + length, reject);
Denys Vlasenko82331882020-02-24 10:02:50 +01006740 end = 0;
Denys Vlasenkocd716832009-11-28 22:14:02 +01006741 c = p[length];
Denys Vlasenko82331882020-02-24 10:02:50 +01006742 if (!(c & 0x80)
6743 IF_FEATURE_SH_MATH(|| c == CTLENDARI)
6744 || c == CTLENDVAR
6745 ) {
6746 /*
6747 * c == '=' || c == ':' || c == '\0' ||
6748 * c == CTLENDARI || c == CTLENDVAR
6749 */
6750 length++;
6751 /* c == '\0' || c == CTLENDARI || c == CTLENDVAR */
6752 end = !!((c - 1) & 0x80);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006753 }
Denys Vlasenko82331882020-02-24 10:02:50 +01006754 if (length > 0 && !(flag & EXP_DISCARD)) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006755 int newloc;
Denys Vlasenko82331882020-02-24 10:02:50 +01006756 char *q;
6757
6758 q = stnputs(p, length, expdest);
6759 q[-1] &= end - 1;
6760 expdest = q - (flag & EXP_WORD ? end : 0);
6761 newloc = q - (char *)stackblock() - end;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006762 if (breakall && !inquotes && newloc > startloc) {
6763 recordregion(startloc, newloc, 0);
6764 }
6765 startloc = newloc;
6766 }
6767 p += length + 1;
6768 length = 0;
6769
Denys Vlasenko82331882020-02-24 10:02:50 +01006770 if (end)
6771 break;
6772
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006773 switch (c) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006774 case '=':
Denys Vlasenko7f198482020-02-24 09:57:08 +01006775 flag |= EXP_VARTILDE2;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006776 reject++;
6777 /* fall through */
6778 case ':':
6779 /*
6780 * sort of a hack - expand tildes in variable
6781 * assignments (after the first '=' and after ':'s).
6782 */
6783 if (*--p == '~') {
6784 goto tilde;
6785 }
6786 continue;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006787 case CTLQUOTEMARK:
6788 /* "$@" syntax adherence hack */
Denys Vlasenko9a95df92018-04-02 14:27:50 +02006789 if (!inquotes && !memcmp(p, dolatstr + 1, DOLATSTRLEN - 1)) {
Denys Vlasenko7f198482020-02-24 09:57:08 +01006790 p = evalvar(p + 1, flag | EXP_QUOTED) + 1;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006791 goto start;
6792 }
Denys Vlasenko9a95df92018-04-02 14:27:50 +02006793 inquotes ^= EXP_QUOTED;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006794 addquote:
Denys Vlasenko7f198482020-02-24 09:57:08 +01006795 if (flag & QUOTES_ESC) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006796 p--;
6797 length++;
6798 startloc++;
6799 }
6800 break;
6801 case CTLESC:
6802 startloc++;
6803 length++;
6804 goto addquote;
6805 case CTLVAR:
Denys Vlasenkof451b2c2012-06-09 02:06:57 +02006806 TRACE(("argstr: evalvar('%s')\n", p));
Denys Vlasenko7f198482020-02-24 09:57:08 +01006807 p = evalvar(p, flag | inquotes);
Denys Vlasenkof451b2c2012-06-09 02:06:57 +02006808 TRACE(("argstr: evalvar:'%s'\n", (char *)stackblock()));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006809 goto start;
6810 case CTLBACKQ:
Denys Vlasenko7f198482020-02-24 09:57:08 +01006811 expbackq(argbackq->n, flag | inquotes);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006812 goto start;
Denys Vlasenko0b883582016-12-23 16:49:07 +01006813#if ENABLE_FEATURE_SH_MATH
Denys Vlasenko82331882020-02-24 10:02:50 +01006814 case CTLARI:
6815 p = expari(p, flag | inquotes);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006816 goto start;
6817#endif
6818 }
6819 }
Denys Vlasenko82331882020-02-24 10:02:50 +01006820 return p - 1;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006821}
6822
6823static char *
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006824scanleft(char *startp, char *rmesc, char *rmescend UNUSED_PARAM,
6825 char *pattern, int quotes, int zero)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006826{
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006827 char *loc, *loc2;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006828 char c;
6829
6830 loc = startp;
6831 loc2 = rmesc;
6832 do {
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006833 int match;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006834 const char *s = loc2;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006835
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006836 c = *loc2;
6837 if (zero) {
6838 *loc2 = '\0';
6839 s = rmesc;
6840 }
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006841 match = pmatch(pattern, s);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006842
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006843 *loc2 = c;
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006844 if (match)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006845 return loc;
Denys Vlasenkocd716832009-11-28 22:14:02 +01006846 if (quotes && (unsigned char)*loc == CTLESC)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006847 loc++;
6848 loc++;
6849 loc2++;
6850 } while (c);
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006851 return NULL;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006852}
6853
6854static char *
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006855scanright(char *startp, char *rmesc, char *rmescend,
6856 char *pattern, int quotes, int match_at_start)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006857{
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006858#if !ENABLE_ASH_OPTIMIZE_FOR_SIZE
6859 int try2optimize = match_at_start;
6860#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006861 int esc = 0;
6862 char *loc;
6863 char *loc2;
6864
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006865 /* If we called by "${v/pattern/repl}" or "${v//pattern/repl}":
6866 * startp="escaped_value_of_v" rmesc="raw_value_of_v"
6867 * rmescend=""(ptr to NUL in rmesc) pattern="pattern" quotes=match_at_start=1
6868 * Logic:
6869 * loc starts at NUL at the end of startp, loc2 starts at the end of rmesc,
6870 * and on each iteration they go back two/one char until they reach the beginning.
6871 * We try to find a match in "raw_value_of_v", "raw_value_of_", "raw_value_of" etc.
6872 */
6873 /* TODO: document in what other circumstances we are called. */
6874
6875 for (loc = pattern - 1, loc2 = rmescend; loc >= startp; loc2--) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006876 int match;
6877 char c = *loc2;
6878 const char *s = loc2;
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006879 if (match_at_start) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006880 *loc2 = '\0';
6881 s = rmesc;
6882 }
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006883 match = pmatch(pattern, s);
6884 //bb_error_msg("pmatch(pattern:'%s',s:'%s'):%d", pattern, s, match);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006885 *loc2 = c;
6886 if (match)
6887 return loc;
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006888#if !ENABLE_ASH_OPTIMIZE_FOR_SIZE
6889 if (try2optimize) {
6890 /* Maybe we can optimize this:
6891 * if pattern ends with unescaped *, we can avoid checking
Denys Vlasenko10ad6222017-04-17 16:13:32 +02006892 * shorter strings: if "foo*" doesn't match "raw_value_of_v",
6893 * it won't match truncated "raw_value_of_" strings too.
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006894 */
6895 unsigned plen = strlen(pattern);
6896 /* Does it end with "*"? */
6897 if (plen != 0 && pattern[--plen] == '*') {
6898 /* "xxxx*" is not escaped */
6899 /* "xxx\*" is escaped */
6900 /* "xx\\*" is not escaped */
6901 /* "x\\\*" is escaped */
6902 int slashes = 0;
6903 while (plen != 0 && pattern[--plen] == '\\')
6904 slashes++;
6905 if (!(slashes & 1))
6906 break; /* ends with unescaped "*" */
6907 }
6908 try2optimize = 0;
6909 }
6910#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006911 loc--;
6912 if (quotes) {
6913 if (--esc < 0) {
6914 esc = esclen(startp, loc);
6915 }
6916 if (esc % 2) {
6917 esc--;
6918 loc--;
6919 }
6920 }
6921 }
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006922 return NULL;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006923}
6924
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00006925static void varunset(const char *, const char *, const char *, int) NORETURN;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006926static void
6927varunset(const char *end, const char *var, const char *umsg, int varflags)
6928{
6929 const char *msg;
6930 const char *tail;
6931
6932 tail = nullstr;
6933 msg = "parameter not set";
6934 if (umsg) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006935 if ((unsigned char)*end == CTLENDVAR) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006936 if (varflags & VSNUL)
6937 tail = " or null";
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00006938 } else {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006939 msg = umsg;
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00006940 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006941 }
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006942 ash_msg_and_raise_error("%.*s: %s%s", (int)(end - var - 1), var, msg, tail);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006943}
6944
Denys Vlasenko82331882020-02-24 10:02:50 +01006945static char *
6946subevalvar(char *start, char *str, int strloc,
Denys Vlasenkob8c0bc12017-07-26 23:03:21 +02006947 int startloc, int varflags, int flag)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006948{
Denys Vlasenko82331882020-02-24 10:02:50 +01006949 int subtype = varflags & VSTYPE;
Ron Yorston549deab2015-05-18 09:57:51 +02006950 int quotes = flag & QUOTES_ESC;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006951 char *startp;
6952 char *loc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006953 char *rmesc, *rmescend;
Denys Vlasenko82331882020-02-24 10:02:50 +01006954 long amount;
6955 int resetloc;
Denys Vlasenko740058b2018-01-09 17:01:00 +01006956 int argstr_flags;
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01006957 IF_BASH_PATTERN_SUBST(int workloc;)
Denys Vlasenko740058b2018-01-09 17:01:00 +01006958 IF_BASH_PATTERN_SUBST(int slash_pos;)
6959 IF_BASH_PATTERN_SUBST(char *repl;)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006960 int zero;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006961 char *(*scan)(char*, char*, char*, char*, int, int);
Denys Vlasenko82331882020-02-24 10:02:50 +01006962 char *p;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006963
Denys Vlasenko82331882020-02-24 10:02:50 +01006964 //bb_error_msg("subevalvar(start:'%s',str:'%s',strloc:%d,startloc:%d,varflags:%x,quotes:%d)",
6965 // start, str, strloc, startloc, varflags, quotes);
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02006966
Denys Vlasenko740058b2018-01-09 17:01:00 +01006967#if BASH_PATTERN_SUBST
Denys Vlasenkod1df1a72018-01-09 17:25:58 +01006968 /* For "${v/pattern/repl}", we must find the delimiter _before_
6969 * argstr() call expands possible variable references in pattern:
6970 * think about "v=a; a=a/; echo ${v/$a/r}" case.
6971 */
Denys Vlasenko740058b2018-01-09 17:01:00 +01006972 repl = NULL;
6973 if (subtype == VSREPLACE || subtype == VSREPLACEALL) {
6974 /* Find '/' and replace with NUL */
Denys Vlasenko82331882020-02-24 10:02:50 +01006975 repl = start;
Denys Vlasenkoc2aa2182018-08-04 22:25:28 +02006976 /* The pattern can't be empty.
6977 * IOW: if the first char after "${v//" is a slash,
6978 * it does not terminate the pattern - it's the first char of the pattern:
6979 * v=/dev/ram; echo ${v////-} prints -dev-ram (pattern is "/")
6980 * v=/dev/ram; echo ${v///r/-} prints /dev-am (pattern is "/r")
6981 */
6982 if (*repl == '/')
6983 repl++;
Denys Vlasenko740058b2018-01-09 17:01:00 +01006984 for (;;) {
Denys Vlasenko740058b2018-01-09 17:01:00 +01006985 if (*repl == '\0') {
6986 repl = NULL;
6987 break;
6988 }
6989 if (*repl == '/') {
6990 *repl = '\0';
6991 break;
6992 }
Denys Vlasenkoc2aa2182018-08-04 22:25:28 +02006993 /* Handle escaped slashes, e.g. "${v/\//_}" (they are CTLESC'ed by this point) */
Denys Vlasenkod1df1a72018-01-09 17:25:58 +01006994 if ((unsigned char)*repl == CTLESC && repl[1])
Denys Vlasenko740058b2018-01-09 17:01:00 +01006995 repl++;
Denys Vlasenko740058b2018-01-09 17:01:00 +01006996 repl++;
6997 }
6998 }
6999#endif
Denys Vlasenko82331882020-02-24 10:02:50 +01007000 argstr_flags = (flag & EXP_DISCARD) | EXP_TILDE;
7001 if (!str
Denys Vlasenko216913c2018-04-02 12:35:04 +02007002#if BASH_SUBSTR
7003 && subtype != VSSUBSTR
7004#endif
7005 ) {
7006 /* EXP_CASE keeps CTLESC's */
Denys Vlasenko82331882020-02-24 10:02:50 +01007007 argstr_flags |= EXP_CASE;
Denys Vlasenko216913c2018-04-02 12:35:04 +02007008 }
Denys Vlasenko82331882020-02-24 10:02:50 +01007009 p = argstr(start, argstr_flags);
7010
Denys Vlasenko216913c2018-04-02 12:35:04 +02007011 //bb_error_msg("str0:'%s'", (char *)stackblock() + strloc);
Denys Vlasenko740058b2018-01-09 17:01:00 +01007012#if BASH_PATTERN_SUBST
7013 slash_pos = -1;
7014 if (repl) {
7015 slash_pos = expdest - ((char *)stackblock() + strloc);
Denys Vlasenko883cdb72021-01-09 08:27:37 +01007016 if (!(flag & EXP_DISCARD))
7017 STPUTC('/', expdest);
Denys Vlasenko216913c2018-04-02 12:35:04 +02007018 //bb_error_msg("repl+1:'%s'", repl + 1);
Denys Vlasenko82331882020-02-24 10:02:50 +01007019 p = argstr(repl + 1, (flag & EXP_DISCARD) | EXP_TILDE); /* EXP_TILDE: echo "${v/x/~}" expands ~ ! */
Denys Vlasenko740058b2018-01-09 17:01:00 +01007020 *repl = '/';
7021 }
7022#endif
Denys Vlasenko82331882020-02-24 10:02:50 +01007023 if (flag & EXP_DISCARD)
7024 return p;
7025
Denis Vlasenko29eb3592008-05-18 14:06:08 +00007026 startp = (char *)stackblock() + startloc;
Denys Vlasenko740058b2018-01-09 17:01:00 +01007027 //bb_error_msg("str1:'%s'", (char *)stackblock() + strloc);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007028
7029 switch (subtype) {
7030 case VSASSIGN:
Denys Vlasenko7f198482020-02-24 09:57:08 +01007031 setvar0(str, startp);
Denys Vlasenko82331882020-02-24 10:02:50 +01007032
7033 loc = startp;
7034 goto out;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007035
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02007036 case VSQUESTION:
Denys Vlasenko82331882020-02-24 10:02:50 +01007037 varunset(start, str, startp, varflags);
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02007038 /* NOTREACHED */
7039
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01007040#if BASH_SUBSTR
Denys Vlasenko826360f2017-07-17 17:49:11 +02007041 case VSSUBSTR: {
7042 int pos, len, orig_len;
7043 char *colon;
Denys Vlasenko7f198482020-02-24 09:57:08 +01007044 char *vstr;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007045
Denys Vlasenko7f198482020-02-24 09:57:08 +01007046 loc = vstr = stackblock() + strloc;
Denys Vlasenko826360f2017-07-17 17:49:11 +02007047
Denys Vlasenko826360f2017-07-17 17:49:11 +02007048 /* Read POS in ${var:POS:LEN} */
7049 colon = strchr(loc, ':');
7050 if (colon) *colon = '\0';
Denys Vlasenkobaa41c72018-01-10 13:22:25 +01007051 pos = substr_atoi(loc);
Denys Vlasenko826360f2017-07-17 17:49:11 +02007052 if (colon) *colon = ':';
7053
7054 /* Read LEN in ${var:POS:LEN} */
Denys Vlasenko7f198482020-02-24 09:57:08 +01007055 len = vstr - startp - 1;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007056 /* *loc != '\0', guaranteed by parser */
7057 if (quotes) {
7058 char *ptr;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02007059 /* Adjust the length by the number of escapes */
Denys Vlasenko7f198482020-02-24 09:57:08 +01007060 for (ptr = startp; ptr < (vstr - 1); ptr++) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01007061 if ((unsigned char)*ptr == CTLESC) {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007062 len--;
7063 ptr++;
7064 }
7065 }
7066 }
7067 orig_len = len;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007068 if (*loc++ == ':') {
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02007069 /* ${var::LEN} */
Denys Vlasenkobaa41c72018-01-10 13:22:25 +01007070 len = substr_atoi(loc);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007071 } else {
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02007072 /* Skip POS in ${var:POS:LEN} */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007073 len = orig_len;
Denys Vlasenkobaa41c72018-01-10 13:22:25 +01007074 while (*loc && *loc != ':')
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007075 loc++;
Denys Vlasenkobaa41c72018-01-10 13:22:25 +01007076 if (*loc++ == ':')
7077 len = substr_atoi(loc);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007078 }
Denys Vlasenko08a5dab2014-11-17 20:27:18 +01007079 if (pos < 0) {
7080 /* ${VAR:$((-n)):l} starts n chars from the end */
7081 pos = orig_len + pos;
7082 }
7083 if ((unsigned)pos >= orig_len) {
7084 /* apart from obvious ${VAR:999999:l},
7085 * covers ${VAR:$((-9999999)):l} - result is ""
Denys Vlasenko826360f2017-07-17 17:49:11 +02007086 * (bash compat)
Denys Vlasenko08a5dab2014-11-17 20:27:18 +01007087 */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007088 pos = 0;
7089 len = 0;
7090 }
Denys Vlasenko826360f2017-07-17 17:49:11 +02007091 if (len < 0) {
7092 /* ${VAR:N:-M} sets LEN to strlen()-M */
7093 len = (orig_len - pos) + len;
7094 }
7095 if ((unsigned)len > (orig_len - pos))
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007096 len = orig_len - pos;
7097
Denys Vlasenko7f198482020-02-24 09:57:08 +01007098 for (vstr = startp; pos; vstr++, pos--) {
7099 if (quotes && (unsigned char)*vstr == CTLESC)
7100 vstr++;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007101 }
7102 for (loc = startp; len; len--) {
Denys Vlasenko7f198482020-02-24 09:57:08 +01007103 if (quotes && (unsigned char)*vstr == CTLESC)
7104 *loc++ = *vstr++;
7105 *loc++ = *vstr++;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007106 }
7107 *loc = '\0';
Denys Vlasenko82331882020-02-24 10:02:50 +01007108 goto out;
Denys Vlasenko826360f2017-07-17 17:49:11 +02007109 }
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01007110#endif /* BASH_SUBSTR */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007111 }
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02007112
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007113 resetloc = expdest - (char *)stackblock();
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007114
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01007115#if BASH_PATTERN_SUBST
Denys Vlasenko740058b2018-01-09 17:01:00 +01007116 repl = NULL;
7117
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007118 /* We'll comeback here if we grow the stack while handling
7119 * a VSREPLACE or VSREPLACEALL, since our pointers into the
7120 * stack will need rebasing, and we'll need to remove our work
7121 * areas each time
7122 */
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01007123 restart:
7124#endif
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007125
7126 amount = expdest - ((char *)stackblock() + resetloc);
7127 STADJUST(-amount, expdest);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00007128 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007129
7130 rmesc = startp;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00007131 rmescend = (char *)stackblock() + strloc;
Denys Vlasenko740058b2018-01-09 17:01:00 +01007132 //bb_error_msg("str7:'%s'", rmescend);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007133 if (quotes) {
Denys Vlasenko740058b2018-01-09 17:01:00 +01007134//TODO: how to handle slash_pos here if string changes (shortens?)
7135 rmesc = rmescapes(startp, RMESCAPE_ALLOC | RMESCAPE_GROW, NULL);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007136 if (rmesc != startp) {
7137 rmescend = expdest;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00007138 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007139 }
7140 }
7141 rmescend--;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00007142 str = (char *)stackblock() + strloc;
Ron Yorston417622c2015-05-18 09:59:14 +02007143 /*
7144 * Example: v='a\bc'; echo ${v/\\b/_\\_\z_}
7145 * The result is a_\_z_c (not a\_\_z_c)!
7146 *
7147 * The search pattern and replace string treat backslashes differently!
Denys Vlasenko740058b2018-01-09 17:01:00 +01007148 * "&slash_pos" causes rmescapes() to work differently on the pattern
Ron Yorston417622c2015-05-18 09:59:14 +02007149 * and string. It's only used on the first call.
7150 */
Denys Vlasenko740058b2018-01-09 17:01:00 +01007151 //bb_error_msg("str8:'%s' slash_pos:%d", str, slash_pos);
7152 rmescapes(str, RMESCAPE_GLOB,
7153 repl ? NULL : (slash_pos < 0 ? NULL : &slash_pos)
7154 );
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007155
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01007156#if BASH_PATTERN_SUBST
Denys Vlasenko0b4980c2012-09-25 12:49:29 +02007157 workloc = expdest - (char *)stackblock();
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007158 if (subtype == VSREPLACE || subtype == VSREPLACEALL) {
Denys Vlasenko826360f2017-07-17 17:49:11 +02007159 int len;
Denys Vlasenkob76356b2010-03-13 16:19:04 +01007160 char *idx, *end;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007161
Denis Vlasenkod6855d12008-09-27 14:03:25 +00007162 if (!repl) {
Denys Vlasenko740058b2018-01-09 17:01:00 +01007163 //bb_error_msg("str9:'%s' slash_pos:%d", str, slash_pos);
Denys Vlasenkod1df1a72018-01-09 17:25:58 +01007164 repl = nullstr;
Denys Vlasenko740058b2018-01-09 17:01:00 +01007165 if (slash_pos >= 0) {
7166 repl = str + slash_pos;
Ron Yorston417622c2015-05-18 09:59:14 +02007167 *repl++ = '\0';
Denys Vlasenko740058b2018-01-09 17:01:00 +01007168 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007169 }
Ron Yorston417622c2015-05-18 09:59:14 +02007170 //bb_error_msg("str:'%s' repl:'%s'", str, repl);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007171
7172 /* If there's no pattern to match, return the expansion unmolested */
Denys Vlasenkob76356b2010-03-13 16:19:04 +01007173 if (str[0] == '\0')
Denys Vlasenko82331882020-02-24 10:02:50 +01007174 goto out1;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007175
7176 len = 0;
7177 idx = startp;
7178 end = str - 1;
Denys Vlasenko2b7c1aa2021-01-09 08:46:54 +01007179 while (idx <= end) {
Denys Vlasenkob76356b2010-03-13 16:19:04 +01007180 try_to_match:
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007181 loc = scanright(idx, rmesc, rmescend, str, quotes, 1);
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02007182 //bb_error_msg("scanright('%s'):'%s'", str, loc);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007183 if (!loc) {
7184 /* No match, advance */
Denys Vlasenkob76356b2010-03-13 16:19:04 +01007185 char *restart_detect = stackblock();
7186 skip_matching:
Denys Vlasenko2b7c1aa2021-01-09 08:46:54 +01007187 if (idx >= end)
7188 break;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007189 STPUTC(*idx, expdest);
Denys Vlasenkocd716832009-11-28 22:14:02 +01007190 if (quotes && (unsigned char)*idx == CTLESC) {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007191 idx++;
7192 len++;
7193 STPUTC(*idx, expdest);
7194 }
7195 if (stackblock() != restart_detect)
7196 goto restart;
7197 idx++;
7198 len++;
7199 rmesc++;
Denys Vlasenkob76356b2010-03-13 16:19:04 +01007200 /* continue; - prone to quadratic behavior, smarter code: */
Denys Vlasenkob76356b2010-03-13 16:19:04 +01007201 if (str[0] == '*') {
7202 /* Pattern is "*foo". If "*foo" does not match "long_string",
7203 * it would never match "ong_string" etc, no point in trying.
7204 */
7205 goto skip_matching;
7206 }
7207 goto try_to_match;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007208 }
7209
7210 if (subtype == VSREPLACEALL) {
7211 while (idx < loc) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01007212 if (quotes && (unsigned char)*idx == CTLESC)
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007213 idx++;
7214 idx++;
7215 rmesc++;
7216 }
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00007217 } else {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007218 idx = loc;
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00007219 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007220
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02007221 //bb_error_msg("repl:'%s'", repl);
Denys Vlasenkofd33e172010-06-26 22:55:44 +02007222 for (loc = (char*)repl; *loc; loc++) {
Denys Vlasenkob76356b2010-03-13 16:19:04 +01007223 char *restart_detect = stackblock();
Denys Vlasenkofd33e172010-06-26 22:55:44 +02007224 if (quotes && *loc == '\\') {
7225 STPUTC(CTLESC, expdest);
7226 len++;
7227 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007228 STPUTC(*loc, expdest);
7229 if (stackblock() != restart_detect)
7230 goto restart;
7231 len++;
7232 }
7233
7234 if (subtype == VSREPLACE) {
Denys Vlasenkof02c82f2010-08-06 19:14:47 +02007235 //bb_error_msg("tail:'%s', quotes:%x", idx, quotes);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007236 while (*idx) {
Denys Vlasenkob76356b2010-03-13 16:19:04 +01007237 char *restart_detect = stackblock();
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007238 STPUTC(*idx, expdest);
7239 if (stackblock() != restart_detect)
7240 goto restart;
7241 len++;
7242 idx++;
7243 }
7244 break;
7245 }
7246 }
7247
7248 /* We've put the replaced text into a buffer at workloc, now
7249 * move it to the right place and adjust the stack.
7250 */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007251 STPUTC('\0', expdest);
Denys Vlasenkofd33e172010-06-26 22:55:44 +02007252 startp = (char *)stackblock() + startloc;
7253 memmove(startp, (char *)stackblock() + workloc, len + 1);
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02007254 //bb_error_msg("startp:'%s'", startp);
Denys Vlasenko82331882020-02-24 10:02:50 +01007255 loc = startp + len;
7256 goto out;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007257 }
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01007258#endif /* BASH_PATTERN_SUBST */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007259
7260 subtype -= VSTRIMRIGHT;
7261#if DEBUG
7262 if (subtype < 0 || subtype > 7)
7263 abort();
7264#endif
Denys Vlasenkob76356b2010-03-13 16:19:04 +01007265 /* zero = (subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX) */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007266 zero = subtype >> 1;
7267 /* VSTRIMLEFT/VSTRIMRIGHTMAX -> scanleft */
7268 scan = (subtype & 1) ^ zero ? scanleft : scanright;
7269
7270 loc = scan(startp, rmesc, rmescend, str, quotes, zero);
7271 if (loc) {
7272 if (zero) {
7273 memmove(startp, loc, str - loc);
7274 loc = startp + (str - loc) - 1;
7275 }
7276 *loc = '\0';
Denys Vlasenko82331882020-02-24 10:02:50 +01007277 } else
7278 loc = str - 1;
7279
7280 out:
7281 amount = loc - expdest;
7282 STADJUST(amount, expdest);
Cristian Ionescu-Idbohrnddfdf682020-11-18 10:41:14 +01007283#if BASH_PATTERN_SUBST
Denys Vlasenko82331882020-02-24 10:02:50 +01007284 out1:
Cristian Ionescu-Idbohrnddfdf682020-11-18 10:41:14 +01007285#endif
Denys Vlasenko82331882020-02-24 10:02:50 +01007286 /* Remove any recorded regions beyond start of variable */
7287 removerecordregions(startloc);
7288
7289 return p;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007290}
7291
7292/*
7293 * Add the value of a specialized variable to the stack string.
Denys Vlasenko4d8873f2009-10-04 03:14:41 +02007294 * name parameter (examples):
7295 * ash -c 'echo $1' name:'1='
7296 * ash -c 'echo $qwe' name:'qwe='
7297 * ash -c 'echo $$' name:'$='
7298 * ash -c 'echo ${$}' name:'$='
7299 * ash -c 'echo ${$##q}' name:'$=q'
7300 * ash -c 'echo ${#$}' name:'$='
7301 * note: examples with bad shell syntax:
7302 * ash -c 'echo ${#$1}' name:'$=1'
7303 * ash -c 'echo ${#1#}' name:'1=#'
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007304 */
Denys Vlasenkoadf922e2009-10-08 14:35:37 +02007305static NOINLINE ssize_t
Denys Vlasenko440da972018-08-05 14:29:58 +02007306varvalue(char *name, int varflags, int flags, int quoted)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007307{
Mike Frysinger98c52642009-04-02 10:02:37 +00007308 const char *p;
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01007309 int num;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007310 int i;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007311 ssize_t len = 0;
Ron Yorstond68d1fb2015-05-18 09:49:28 +02007312 int sep;
Ron Yorstond68d1fb2015-05-18 09:49:28 +02007313 int subtype = varflags & VSTYPE;
Denys Vlasenko82331882020-02-24 10:02:50 +01007314 int discard = (subtype == VSPLUS || subtype == VSLENGTH) | (flags & EXP_DISCARD);
7315
7316 if (!subtype) {
7317 if (discard)
7318 return -1;
7319
7320 raise_error_syntax("bad substitution");
7321 }
Denys Vlasenko0aaaa502016-10-02 02:46:56 +02007322
Denys Vlasenkoecc85832020-02-20 10:06:20 +01007323 flags |= EXP_KEEPNUL;
7324 flags &= discard ? ~QUOTES_ESC : ~0;
Denys Vlasenko0aaaa502016-10-02 02:46:56 +02007325 sep = (flags & EXP_FULL) << CHAR_BIT;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007326
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007327 switch (*name) {
7328 case '$':
7329 num = rootpid;
7330 goto numvar;
7331 case '?':
7332 num = exitstatus;
7333 goto numvar;
7334 case '#':
7335 num = shellparam.nparam;
7336 goto numvar;
7337 case '!':
7338 num = backgndpid;
7339 if (num == 0)
7340 return -1;
7341 numvar:
Denys Vlasenko45dd87a2020-02-21 16:30:44 +01007342 len = cvtnum(num, flags);
Denys Vlasenko4d8873f2009-10-04 03:14:41 +02007343 goto check_1char_name;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007344 case '-':
Mike Frysinger98c52642009-04-02 10:02:37 +00007345 expdest = makestrspace(NOPTS, expdest);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007346 for (i = NOPTS - 1; i >= 0; i--) {
Martijn Dekkerad4e9612018-03-31 18:15:59 +02007347 if (optlist[i] && optletters(i)) {
Mike Frysinger98c52642009-04-02 10:02:37 +00007348 USTPUTC(optletters(i), expdest);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007349 len++;
7350 }
7351 }
Denys Vlasenko4d8873f2009-10-04 03:14:41 +02007352 check_1char_name:
7353#if 0
7354 /* handles cases similar to ${#$1} */
7355 if (name[2] != '\0')
7356 raise_error_syntax("bad substitution");
7357#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007358 break;
Denys Vlasenko0aaaa502016-10-02 02:46:56 +02007359 case '@':
7360 if (quoted && sep)
7361 goto param;
7362 /* fall through */
7363 case '*': {
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01007364 char **ap;
Ron Yorstond68d1fb2015-05-18 09:49:28 +02007365 char sepc;
Denys Vlasenko440da972018-08-05 14:29:58 +02007366 char c;
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01007367
Denys Vlasenko440da972018-08-05 14:29:58 +02007368 /* We will set c to 0 or ~0 depending on whether
7369 * we're doing field splitting. We won't do field
7370 * splitting if either we're quoted or sep is zero.
7371 *
7372 * Instead of testing (quoted || !sep) the following
7373 * trick optimises away any branches by using the
7374 * fact that EXP_QUOTED (which is the only bit that
7375 * can be set in quoted) is the same as EXP_FULL <<
7376 * CHAR_BIT (which is the only bit that can be set
7377 * in sep).
7378 */
7379#if EXP_QUOTED >> CHAR_BIT != EXP_FULL
7380#error The following two lines expect EXP_QUOTED == EXP_FULL << CHAR_BIT
7381#endif
7382 c = !((quoted | ~sep) & EXP_QUOTED) - 1;
7383 sep &= ~quoted;
7384 sep |= ifsset() ? (unsigned char)(c & ifsval()[0]) : ' ';
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007385 param:
Ron Yorstond68d1fb2015-05-18 09:49:28 +02007386 sepc = sep;
Denys Vlasenko0dd8e452016-10-01 21:02:06 +02007387 ap = shellparam.p;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007388 if (!ap)
7389 return -1;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02007390 while ((p = *ap++) != NULL) {
Denys Vlasenkoecc85832020-02-20 10:06:20 +01007391 len += strtodest(p, flags);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007392
7393 if (*ap && sep) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007394 len++;
Denys Vlasenkoecc85832020-02-20 10:06:20 +01007395 memtodest(&sepc, 1, flags);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007396 }
7397 }
Ron Yorstond68d1fb2015-05-18 09:49:28 +02007398 break;
Denys Vlasenko0aaaa502016-10-02 02:46:56 +02007399 } /* case '*' */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007400 case '0':
7401 case '1':
7402 case '2':
7403 case '3':
7404 case '4':
7405 case '5':
7406 case '6':
7407 case '7':
7408 case '8':
7409 case '9':
Denys Vlasenkoa00329c2009-08-30 20:05:10 +02007410 num = atoi(name); /* number(name) fails on ${N#str} etc */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007411 if (num < 0 || num > shellparam.nparam)
7412 return -1;
7413 p = num ? shellparam.p[num - 1] : arg0;
7414 goto value;
7415 default:
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00007416 /* NB: name has form "VAR=..." */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007417 p = lookupvar(name);
7418 value:
7419 if (!p)
7420 return -1;
7421
Denys Vlasenkoecc85832020-02-20 10:06:20 +01007422 len = strtodest(p, flags);
Denys Vlasenkoc76236f2014-12-29 00:04:18 +01007423#if ENABLE_UNICODE_SUPPORT
7424 if (subtype == VSLENGTH && len > 0) {
7425 reinit_unicode_for_ash();
7426 if (unicode_status == UNICODE_ON) {
Ron Yorston3e3bfb82016-03-18 11:29:19 +00007427 STADJUST(-len, expdest);
7428 discard = 0;
Denys Vlasenkoc76236f2014-12-29 00:04:18 +01007429 len = unicode_strlen(p);
7430 }
7431 }
7432#endif
Ron Yorstond68d1fb2015-05-18 09:49:28 +02007433 break;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007434 }
7435
Ron Yorstond68d1fb2015-05-18 09:49:28 +02007436 if (discard)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007437 STADJUST(-len, expdest);
Denys Vlasenko82331882020-02-24 10:02:50 +01007438
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007439 return len;
7440}
7441
7442/*
7443 * Expand a variable, and return a pointer to the next character in the
7444 * input string.
7445 */
7446static char *
Denys Vlasenkob8c0bc12017-07-26 23:03:21 +02007447evalvar(char *p, int flag)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007448{
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00007449 char varflags;
7450 char subtype;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007451 char *var;
7452 int patloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007453 int startloc;
7454 ssize_t varlen;
Denys Vlasenko15558952020-02-22 19:38:40 +01007455 int discard;
Denys Vlasenko82331882020-02-24 10:02:50 +01007456 int quoted;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007457
Denys Vlasenkob0d63382009-09-16 16:18:32 +02007458 varflags = (unsigned char) *p++;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007459 subtype = varflags & VSTYPE;
Denys Vlasenko88e15702016-10-26 01:55:56 +02007460
Denys Vlasenko88ac97d2016-10-01 20:55:02 +02007461 quoted = flag & EXP_QUOTED;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007462 var = p;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007463 startloc = expdest - (char *)stackblock();
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02007464 p = strchr(p, '=') + 1; //TODO: use var_end(p)?
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007465
7466 again:
Denys Vlasenko440da972018-08-05 14:29:58 +02007467 varlen = varvalue(var, varflags, flag, quoted);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007468 if (varflags & VSNUL)
7469 varlen--;
7470
Denys Vlasenko15558952020-02-22 19:38:40 +01007471 discard = varlen < 0 ? EXP_DISCARD : 0;
7472
Denys Vlasenko82331882020-02-24 10:02:50 +01007473 switch (subtype) {
7474 case VSPLUS:
Denys Vlasenko15558952020-02-22 19:38:40 +01007475 discard ^= EXP_DISCARD;
Denys Vlasenko82331882020-02-24 10:02:50 +01007476 /* fall through */
7477 case 0:
7478 case VSMINUS:
Denys Vlasenko15558952020-02-22 19:38:40 +01007479 p = argstr(p, flag | EXP_TILDE | EXP_WORD | (discard ^ EXP_DISCARD));
Denys Vlasenko88ac97d2016-10-01 20:55:02 +02007480 goto record;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007481
Denys Vlasenko82331882020-02-24 10:02:50 +01007482 case VSASSIGN:
7483 case VSQUESTION:
Denys Vlasenko15558952020-02-22 19:38:40 +01007484 p = subevalvar(p, var, 0, startloc, varflags,
7485 (flag & ~QUOTES_ESC) | (discard ^ EXP_DISCARD));
7486
7487 if ((flag | ~discard) & EXP_DISCARD)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007488 goto record;
Denys Vlasenko88ac97d2016-10-01 20:55:02 +02007489
Denys Vlasenko88ac97d2016-10-01 20:55:02 +02007490 varflags &= ~VSNUL;
Denys Vlasenko15558952020-02-22 19:38:40 +01007491 subtype = VSNORMAL;
Denys Vlasenko88ac97d2016-10-01 20:55:02 +02007492 goto again;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007493 }
7494
Denys Vlasenko15558952020-02-22 19:38:40 +01007495 if ((discard & ~flag) && uflag)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007496 varunset(p, var, 0, 0);
7497
7498 if (subtype == VSLENGTH) {
Denys Vlasenko82331882020-02-24 10:02:50 +01007499 p++;
7500 if (flag & EXP_DISCARD)
7501 return p;
Denys Vlasenko45dd87a2020-02-21 16:30:44 +01007502 cvtnum(varlen > 0 ? varlen : 0, flag);
Denys Vlasenko15558952020-02-22 19:38:40 +01007503 goto really_record;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007504 }
7505
Denys Vlasenko82331882020-02-24 10:02:50 +01007506 if (subtype == VSNORMAL)
7507 goto record;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007508
7509#if DEBUG
7510 switch (subtype) {
7511 case VSTRIMLEFT:
7512 case VSTRIMLEFTMAX:
7513 case VSTRIMRIGHT:
7514 case VSTRIMRIGHTMAX:
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01007515#if BASH_SUBSTR
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007516 case VSSUBSTR:
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01007517#endif
7518#if BASH_PATTERN_SUBST
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007519 case VSREPLACE:
7520 case VSREPLACEALL:
7521#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007522 break;
7523 default:
7524 abort();
7525 }
7526#endif
7527
Denys Vlasenko15558952020-02-22 19:38:40 +01007528 flag |= discard;
Denys Vlasenko82331882020-02-24 10:02:50 +01007529 if (!(flag & EXP_DISCARD)) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007530 /*
7531 * Terminate the string and start recording the pattern
7532 * right after it
7533 */
7534 STPUTC('\0', expdest);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007535 }
7536
Denys Vlasenko82331882020-02-24 10:02:50 +01007537 patloc = expdest - (char *)stackblock();
7538 p = subevalvar(p, NULL, patloc, startloc, varflags, flag);
Denys Vlasenko4ace3852020-02-16 18:42:50 +01007539
Denys Vlasenko82331882020-02-24 10:02:50 +01007540 record:
Denys Vlasenko15558952020-02-22 19:38:40 +01007541 if ((flag | discard) & EXP_DISCARD)
Denys Vlasenko82331882020-02-24 10:02:50 +01007542 return p;
7543
Denys Vlasenko15558952020-02-22 19:38:40 +01007544 really_record:
Denys Vlasenko82331882020-02-24 10:02:50 +01007545 if (quoted) {
7546 quoted = *var == '@' && shellparam.nparam;
7547 if (!quoted)
7548 return p;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007549 }
Denys Vlasenko82331882020-02-24 10:02:50 +01007550 recordregion(startloc, expdest - (char *)stackblock(), quoted);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007551 return p;
7552}
7553
7554/*
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007555 * Add a file name to the list.
7556 */
7557static void
7558addfname(const char *name)
7559{
7560 struct strlist *sp;
7561
Denis Vlasenko597906c2008-02-20 16:38:54 +00007562 sp = stzalloc(sizeof(*sp));
Denys Vlasenko8e2bc472016-09-28 23:02:57 +02007563 sp->text = sstrdup(name);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007564 *exparg.lastp = sp;
7565 exparg.lastp = &sp->next;
7566}
7567
Felix Fietkaub5b21122017-01-31 21:58:55 +01007568/* Avoid glob() (and thus, stat() et al) for words like "echo" */
7569static int
7570hasmeta(const char *p)
7571{
7572 static const char chars[] ALIGN1 = {
7573 '*', '?', '[', '\\', CTLQUOTEMARK, CTLESC, 0
7574 };
7575
7576 for (;;) {
7577 p = strpbrk(p, chars);
7578 if (!p)
7579 break;
Denys Vlasenkoac61f442018-03-30 23:03:29 +02007580 switch ((unsigned char)*p) {
Felix Fietkaub5b21122017-01-31 21:58:55 +01007581 case CTLQUOTEMARK:
7582 for (;;) {
7583 p++;
Denys Vlasenkoac61f442018-03-30 23:03:29 +02007584 if ((unsigned char)*p == CTLQUOTEMARK)
Felix Fietkaub5b21122017-01-31 21:58:55 +01007585 break;
Denys Vlasenkoac61f442018-03-30 23:03:29 +02007586 if ((unsigned char)*p == CTLESC)
Felix Fietkaub5b21122017-01-31 21:58:55 +01007587 p++;
7588 if (*p == '\0') /* huh? */
7589 return 0;
7590 }
7591 break;
7592 case '\\':
7593 case CTLESC:
7594 p++;
7595 if (*p == '\0')
7596 return 0;
7597 break;
7598 case '[':
7599 if (!strchr(p + 1, ']')) {
7600 /* It's not a properly closed [] pattern,
7601 * but other metas may follow. Continue checking.
7602 * my[file* _is_ globbed by bash
7603 * and matches filenames like "my[file1".
7604 */
7605 break;
7606 }
7607 /* fallthrough */
7608 default:
7609 /* case '*': */
7610 /* case '?': */
7611 return 1;
7612 }
7613 p++;
7614 }
7615
7616 return 0;
7617}
7618
Denys Vlasenkob3f29b42016-09-21 16:25:58 +02007619/* If we want to use glob() from libc... */
Denys Vlasenko514b51d2016-10-01 14:33:08 +02007620#if !ENABLE_ASH_INTERNAL_GLOB
Denys Vlasenkob3f29b42016-09-21 16:25:58 +02007621
7622/* Add the result of glob() to the list */
7623static void
7624addglob(const glob_t *pglob)
7625{
7626 char **p = pglob->gl_pathv;
7627
7628 do {
7629 addfname(*p);
7630 } while (*++p);
7631}
7632static void
7633expandmeta(struct strlist *str /*, int flag*/)
7634{
7635 /* TODO - EXP_REDIR */
7636
7637 while (str) {
7638 char *p;
7639 glob_t pglob;
7640 int i;
7641
7642 if (fflag)
7643 goto nometa;
Denys Vlasenkod4f3db92016-10-30 18:41:01 +01007644
Felix Fietkaub5b21122017-01-31 21:58:55 +01007645 if (!hasmeta(str->text))
7646 goto nometa;
Denys Vlasenkod4f3db92016-10-30 18:41:01 +01007647
Denys Vlasenkob3f29b42016-09-21 16:25:58 +02007648 INT_OFF;
7649 p = preglob(str->text, RMESCAPE_ALLOC | RMESCAPE_HEAP);
Denys Vlasenko8e2c9cc2016-10-02 15:17:15 +02007650// GLOB_NOMAGIC (GNU): if no *?[ chars in pattern, return it even if no match
7651// GLOB_NOCHECK: if no match, return unchanged pattern (sans \* escapes?)
7652//
7653// glibc 2.24.90 glob(GLOB_NOMAGIC) does not remove backslashes used for escaping:
7654// if you pass it "file\?", it returns "file\?", not "file?", if no match.
7655// Which means you need to unescape the string, right? Not so fast:
7656// if there _is_ a file named "file\?" (with backslash), it is returned
7657// as "file\?" too (whichever pattern you used to find it, say, "file*").
Denys Vlasenko10ad6222017-04-17 16:13:32 +02007658// You DON'T KNOW by looking at the result whether you need to unescape it.
Denys Vlasenko8e2c9cc2016-10-02 15:17:15 +02007659//
7660// Worse, globbing of "file\?" in a directory with two files, "file?" and "file\?",
7661// returns "file\?" - which is WRONG: "file\?" pattern matches "file?" file.
7662// Without GLOB_NOMAGIC, this works correctly ("file?" is returned as a match).
7663// With GLOB_NOMAGIC | GLOB_NOCHECK, this also works correctly.
7664// i = glob(p, GLOB_NOMAGIC | GLOB_NOCHECK, NULL, &pglob);
7665// i = glob(p, GLOB_NOMAGIC, NULL, &pglob);
7666 i = glob(p, 0, NULL, &pglob);
7667 //bb_error_msg("glob('%s'):%d '%s'...", p, i, pglob.gl_pathv ? pglob.gl_pathv[0] : "-");
Denys Vlasenkob3f29b42016-09-21 16:25:58 +02007668 if (p != str->text)
7669 free(p);
7670 switch (i) {
7671 case 0:
Denys Vlasenko8e2c9cc2016-10-02 15:17:15 +02007672#if 0 // glibc 2.24.90 bug? Patterns like "*/file", when match, don't set GLOB_MAGCHAR
Denys Vlasenkob3f29b42016-09-21 16:25:58 +02007673 /* GLOB_MAGCHAR is set if *?[ chars were seen (GNU) */
7674 if (!(pglob.gl_flags & GLOB_MAGCHAR))
7675 goto nometa2;
Denys Vlasenko8e2c9cc2016-10-02 15:17:15 +02007676#endif
Denys Vlasenkob3f29b42016-09-21 16:25:58 +02007677 addglob(&pglob);
7678 globfree(&pglob);
7679 INT_ON;
7680 break;
7681 case GLOB_NOMATCH:
Denys Vlasenko8e2c9cc2016-10-02 15:17:15 +02007682 //nometa2:
Denys Vlasenkob3f29b42016-09-21 16:25:58 +02007683 globfree(&pglob);
7684 INT_ON;
Denys Vlasenko8e2c9cc2016-10-02 15:17:15 +02007685 nometa:
Denys Vlasenkob3f29b42016-09-21 16:25:58 +02007686 *exparg.lastp = str;
Denys Vlasenko740058b2018-01-09 17:01:00 +01007687 rmescapes(str->text, 0, NULL);
Denys Vlasenkob3f29b42016-09-21 16:25:58 +02007688 exparg.lastp = &str->next;
7689 break;
7690 default: /* GLOB_NOSPACE */
7691 globfree(&pglob);
7692 INT_ON;
7693 ash_msg_and_raise_error(bb_msg_memory_exhausted);
7694 }
7695 str = str->next;
7696 }
7697}
7698
7699#else
Denys Vlasenko514b51d2016-10-01 14:33:08 +02007700/* ENABLE_ASH_INTERNAL_GLOB: Homegrown globbing code. (dash also has both, uses homegrown one.) */
Denys Vlasenkob3f29b42016-09-21 16:25:58 +02007701
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007702/*
7703 * Do metacharacter (i.e. *, ?, [...]) expansion.
7704 */
Denys Vlasenkod5f50452018-04-14 14:50:47 +02007705typedef struct exp_t {
7706 char *dir;
7707 unsigned dir_max;
7708} exp_t;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007709static void
Denys Vlasenkod5f50452018-04-14 14:50:47 +02007710expmeta(exp_t *exp, char *name, unsigned name_len, unsigned expdir_len)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007711{
Denys Vlasenkod5f50452018-04-14 14:50:47 +02007712#define expdir exp->dir
7713#define expdir_max exp->dir_max
7714 char *enddir = expdir + expdir_len;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007715 char *p;
7716 const char *cp;
7717 char *start;
7718 char *endname;
7719 int metaflag;
7720 struct stat statb;
7721 DIR *dirp;
7722 struct dirent *dp;
7723 int atend;
7724 int matchdot;
Ron Yorstonca25af92015-09-04 10:32:41 +01007725 int esc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007726
7727 metaflag = 0;
7728 start = name;
Ron Yorstonca25af92015-09-04 10:32:41 +01007729 for (p = name; esc = 0, *p; p += esc + 1) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007730 if (*p == '*' || *p == '?')
7731 metaflag = 1;
7732 else if (*p == '[') {
7733 char *q = p + 1;
7734 if (*q == '!')
7735 q++;
7736 for (;;) {
7737 if (*q == '\\')
7738 q++;
7739 if (*q == '/' || *q == '\0')
7740 break;
7741 if (*++q == ']') {
7742 metaflag = 1;
7743 break;
7744 }
7745 }
Ron Yorstonca25af92015-09-04 10:32:41 +01007746 } else {
Denys Vlasenkoeb54ca82018-08-07 18:54:52 +02007747 if (*p == '\\' && p[1])
Ron Yorstonca25af92015-09-04 10:32:41 +01007748 esc++;
7749 if (p[esc] == '/') {
7750 if (metaflag)
7751 break;
7752 start = p + esc + 1;
7753 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007754 }
7755 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007756 if (metaflag == 0) { /* we've reached the end of the file name */
Denys Vlasenkod5f50452018-04-14 14:50:47 +02007757 if (!expdir_len)
7758 return;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007759 p = name;
7760 do {
Denys Vlasenkoeb54ca82018-08-07 18:54:52 +02007761 if (*p == '\\' && p[1])
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007762 p++;
7763 *enddir++ = *p;
7764 } while (*p++);
Denys Vlasenkod5f50452018-04-14 14:50:47 +02007765 if (lstat(expdir, &statb) == 0)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007766 addfname(expdir);
7767 return;
7768 }
7769 endname = p;
7770 if (name < start) {
7771 p = name;
7772 do {
Denys Vlasenkoeb54ca82018-08-07 18:54:52 +02007773 if (*p == '\\' && p[1])
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007774 p++;
7775 *enddir++ = *p++;
7776 } while (p < start);
7777 }
Denys Vlasenkod5f50452018-04-14 14:50:47 +02007778 *enddir = '\0';
7779 cp = expdir;
7780 expdir_len = enddir - cp;
7781 if (!expdir_len)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007782 cp = ".";
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007783 dirp = opendir(cp);
7784 if (dirp == NULL)
7785 return;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007786 if (*endname == 0) {
7787 atend = 1;
7788 } else {
7789 atend = 0;
Ron Yorstonca25af92015-09-04 10:32:41 +01007790 *endname = '\0';
7791 endname += esc + 1;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007792 }
Denys Vlasenkod5f50452018-04-14 14:50:47 +02007793 name_len -= endname - name;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007794 matchdot = 0;
7795 p = start;
7796 if (*p == '\\')
7797 p++;
7798 if (*p == '.')
7799 matchdot++;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02007800 while (!pending_int && (dp = readdir(dirp)) != NULL) {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00007801 if (dp->d_name[0] == '.' && !matchdot)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007802 continue;
7803 if (pmatch(start, dp->d_name)) {
7804 if (atend) {
7805 strcpy(enddir, dp->d_name);
7806 addfname(expdir);
7807 } else {
Denys Vlasenkod5f50452018-04-14 14:50:47 +02007808 unsigned offset;
7809 unsigned len;
7810
7811 p = stpcpy(enddir, dp->d_name);
7812 *p = '/';
7813
7814 offset = p - expdir + 1;
7815 len = offset + name_len + NAME_MAX;
7816 if (len > expdir_max) {
7817 len += PATH_MAX;
7818 expdir = ckrealloc(expdir, len);
7819 expdir_max = len;
7820 }
7821
7822 expmeta(exp, endname, name_len, offset);
7823 enddir = expdir + expdir_len;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007824 }
7825 }
7826 }
7827 closedir(dirp);
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00007828 if (!atend)
Ron Yorstonca25af92015-09-04 10:32:41 +01007829 endname[-esc - 1] = esc ? '\\' : '/';
Denys Vlasenkod5f50452018-04-14 14:50:47 +02007830#undef expdir
7831#undef expdir_max
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007832}
7833
7834static struct strlist *
7835msort(struct strlist *list, int len)
7836{
7837 struct strlist *p, *q = NULL;
7838 struct strlist **lpp;
7839 int half;
7840 int n;
7841
7842 if (len <= 1)
7843 return list;
7844 half = len >> 1;
7845 p = list;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00007846 for (n = half; --n >= 0;) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007847 q = p;
7848 p = p->next;
7849 }
7850 q->next = NULL; /* terminate first half of list */
7851 q = msort(list, half); /* sort first half of list */
7852 p = msort(p, len - half); /* sort second half */
7853 lpp = &list;
7854 for (;;) {
7855#if ENABLE_LOCALE_SUPPORT
7856 if (strcoll(p->text, q->text) < 0)
7857#else
7858 if (strcmp(p->text, q->text) < 0)
7859#endif
7860 {
7861 *lpp = p;
7862 lpp = &p->next;
7863 p = *lpp;
7864 if (p == NULL) {
7865 *lpp = q;
7866 break;
7867 }
7868 } else {
7869 *lpp = q;
7870 lpp = &q->next;
7871 q = *lpp;
7872 if (q == NULL) {
7873 *lpp = p;
7874 break;
7875 }
7876 }
7877 }
7878 return list;
7879}
7880
7881/*
7882 * Sort the results of file name expansion. It calculates the number of
7883 * strings to sort and then calls msort (short for merge sort) to do the
7884 * work.
7885 */
7886static struct strlist *
7887expsort(struct strlist *str)
7888{
7889 int len;
7890 struct strlist *sp;
7891
7892 len = 0;
7893 for (sp = str; sp; sp = sp->next)
7894 len++;
7895 return msort(str, len);
7896}
7897
7898static void
Denis Vlasenko68404f12008-03-17 09:00:54 +00007899expandmeta(struct strlist *str /*, int flag*/)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007900{
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007901 /* TODO - EXP_REDIR */
7902
7903 while (str) {
Denys Vlasenkod5f50452018-04-14 14:50:47 +02007904 exp_t exp;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007905 struct strlist **savelastp;
7906 struct strlist *sp;
7907 char *p;
Denys Vlasenkod5f50452018-04-14 14:50:47 +02007908 unsigned len;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007909
7910 if (fflag)
7911 goto nometa;
Felix Fietkaub5b21122017-01-31 21:58:55 +01007912 if (!hasmeta(str->text))
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007913 goto nometa;
7914 savelastp = exparg.lastp;
7915
7916 INT_OFF;
Ron Yorston549deab2015-05-18 09:57:51 +02007917 p = preglob(str->text, RMESCAPE_ALLOC | RMESCAPE_HEAP);
Denys Vlasenkod5f50452018-04-14 14:50:47 +02007918 len = strlen(p);
7919 exp.dir_max = len + PATH_MAX;
7920 exp.dir = ckmalloc(exp.dir_max);
7921
7922 expmeta(&exp, p, len, 0);
7923 free(exp.dir);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007924 if (p != str->text)
7925 free(p);
7926 INT_ON;
7927 if (exparg.lastp == savelastp) {
7928 /*
7929 * no matches
7930 */
7931 nometa:
7932 *exparg.lastp = str;
Denys Vlasenko740058b2018-01-09 17:01:00 +01007933 rmescapes(str->text, 0, NULL);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007934 exparg.lastp = &str->next;
7935 } else {
7936 *exparg.lastp = NULL;
7937 *savelastp = sp = expsort(*savelastp);
7938 while (sp->next != NULL)
7939 sp = sp->next;
7940 exparg.lastp = &sp->next;
7941 }
7942 str = str->next;
7943 }
7944}
Denys Vlasenko514b51d2016-10-01 14:33:08 +02007945#endif /* ENABLE_ASH_INTERNAL_GLOB */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007946
7947/*
7948 * Perform variable substitution and command substitution on an argument,
7949 * placing the resulting list of arguments in arglist. If EXP_FULL is true,
7950 * perform splitting and file name expansion. When arglist is NULL, perform
7951 * here document expansion.
7952 */
7953static void
7954expandarg(union node *arg, struct arglist *arglist, int flag)
7955{
7956 struct strlist *sp;
7957 char *p;
7958
7959 argbackq = arg->narg.backquote;
7960 STARTSTACKSTR(expdest);
Denys Vlasenkof451b2c2012-06-09 02:06:57 +02007961 TRACE(("expandarg: argstr('%s',flags:%x)\n", arg->narg.text, flag));
Denys Vlasenkob8c0bc12017-07-26 23:03:21 +02007962 argstr(arg->narg.text, flag);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007963 if (arglist == NULL) {
Denys Vlasenko5ac04f22016-10-27 14:46:50 +02007964 /* here document expanded */
7965 goto out;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007966 }
Denys Vlasenko82331882020-02-24 10:02:50 +01007967 p = grabstackstr(expdest);
Denys Vlasenkof451b2c2012-06-09 02:06:57 +02007968 TRACE(("expandarg: p:'%s'\n", p));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007969 exparg.lastp = &exparg.list;
7970 /*
7971 * TODO - EXP_REDIR
7972 */
7973 if (flag & EXP_FULL) {
7974 ifsbreakup(p, &exparg);
7975 *exparg.lastp = NULL;
7976 exparg.lastp = &exparg.list;
Denis Vlasenko68404f12008-03-17 09:00:54 +00007977 expandmeta(exparg.list /*, flag*/);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007978 } else {
Denis Vlasenko597906c2008-02-20 16:38:54 +00007979 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007980 sp->text = p;
7981 *exparg.lastp = sp;
7982 exparg.lastp = &sp->next;
7983 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007984 *exparg.lastp = NULL;
7985 if (exparg.list) {
7986 *arglist->lastp = exparg.list;
7987 arglist->lastp = exparg.lastp;
7988 }
Denys Vlasenko5ac04f22016-10-27 14:46:50 +02007989
7990 out:
7991 ifsfree();
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007992}
7993
7994/*
7995 * Expand shell variables and backquotes inside a here document.
7996 */
7997static void
Denys Vlasenkoc2058ec2020-02-22 20:25:03 +01007998expandhere(union node *arg)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007999{
Ron Yorston549deab2015-05-18 09:57:51 +02008000 expandarg(arg, (struct arglist *)NULL, EXP_QUOTED);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008001}
8002
8003/*
8004 * Returns true if the pattern matches the string.
8005 */
8006static int
8007patmatch(char *pattern, const char *string)
8008{
Denys Vlasenkobd43c672017-07-05 23:12:15 +02008009 char *p = preglob(pattern, 0);
Denys Vlasenko4476c702017-08-15 15:27:41 +02008010 int r = pmatch(p, string);
8011 //bb_error_msg("!fnmatch(pattern:'%s',str:'%s',0):%d", p, string, r);
8012 return r;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008013}
8014
8015/*
8016 * See if a pattern matches in a case statement.
8017 */
8018static int
8019casematch(union node *pattern, char *val)
8020{
8021 struct stackmark smark;
8022 int result;
8023
8024 setstackmark(&smark);
8025 argbackq = pattern->narg.backquote;
8026 STARTSTACKSTR(expdest);
Denys Vlasenkob8c0bc12017-07-26 23:03:21 +02008027 argstr(pattern->narg.text, EXP_TILDE | EXP_CASE);
Denys Vlasenko5ac04f22016-10-27 14:46:50 +02008028 ifsfree();
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008029 result = patmatch(stackblock(), val);
8030 popstackmark(&smark);
8031 return result;
8032}
8033
8034
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008035/* ============ find_command */
8036
8037struct builtincmd {
8038 const char *name;
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008039 int (*builtin)(int, char **) FAST_FUNC;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008040 /* unsigned flags; */
8041};
8042#define IS_BUILTIN_SPECIAL(b) ((b)->name[0] & 1)
Denis Vlasenkoe26b2782008-02-12 07:40:29 +00008043/* "regular" builtins always take precedence over commands,
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00008044 * regardless of PATH=....%builtin... position */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008045#define IS_BUILTIN_REGULAR(b) ((b)->name[0] & 2)
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00008046#define IS_BUILTIN_ASSIGN(b) ((b)->name[0] & 4)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008047
8048struct cmdentry {
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00008049 smallint cmdtype; /* CMDxxx */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008050 union param {
8051 int index;
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00008052 /* index >= 0 for commands without path (slashes) */
8053 /* (TODO: what exactly does the value mean? PATH position?) */
8054 /* index == -1 for commands with slashes */
8055 /* index == (-2 - applet_no) for NOFORK applets */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008056 const struct builtincmd *cmd;
8057 struct funcnode *func;
8058 } u;
8059};
8060/* values of cmdtype */
8061#define CMDUNKNOWN -1 /* no entry in table for command */
8062#define CMDNORMAL 0 /* command is an executable program */
8063#define CMDFUNCTION 1 /* command is a shell function */
8064#define CMDBUILTIN 2 /* command is a shell builtin */
8065
8066/* action to find_command() */
8067#define DO_ERR 0x01 /* prints errors */
8068#define DO_ABS 0x02 /* checks absolute paths */
8069#define DO_NOFUNC 0x04 /* don't return shell functions, for command */
8070#define DO_ALTPATH 0x08 /* using alternate path */
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +01008071#define DO_REGBLTIN 0x10 /* regular built-ins and functions only */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008072
8073static void find_command(char *, struct cmdentry *, int, const char *);
8074
8075
8076/* ============ Hashing commands */
8077
8078/*
8079 * When commands are first encountered, they are entered in a hash table.
8080 * This ensures that a full path search will not have to be done for them
8081 * on each invocation.
8082 *
8083 * We should investigate converting to a linear search, even though that
8084 * would make the command name "hash" a misnomer.
8085 */
8086
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008087struct tblentry {
8088 struct tblentry *next; /* next entry in hash chain */
8089 union param param; /* definition of builtin function */
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00008090 smallint cmdtype; /* CMDxxx */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008091 char rehash; /* if set, cd done since entry created */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008092 char cmdname[1]; /* name of command */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008093};
8094
Denis Vlasenko01631112007-12-16 17:20:38 +00008095static struct tblentry **cmdtable;
8096#define INIT_G_cmdtable() do { \
8097 cmdtable = xzalloc(CMDTABLESIZE * sizeof(cmdtable[0])); \
8098} while (0)
8099
8100static int builtinloc = -1; /* index in path of %builtin, or -1 */
8101
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008102
8103static void
Denys Vlasenko00a1dbd2017-07-29 01:20:53 +02008104tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) const char *cmd, char **argv, char **envp)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008105{
Denis Vlasenko80d14be2007-04-10 23:03:30 +00008106#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00008107 if (applet_no >= 0) {
Denis Vlasenkob7304742008-10-20 08:15:51 +00008108 if (APPLET_IS_NOEXEC(applet_no)) {
Denys Vlasenko7df28bb2010-06-18 14:23:47 +02008109 clearenv();
Denis Vlasenkob7304742008-10-20 08:15:51 +00008110 while (*envp)
8111 putenv(*envp++);
Denys Vlasenko035486c2017-07-31 04:09:19 +02008112 popredir(/*drop:*/ 1);
Denys Vlasenko80e8e3c2017-08-07 19:24:57 +02008113 run_noexec_applet_and_exit(applet_no, cmd, argv);
Denis Vlasenkob7304742008-10-20 08:15:51 +00008114 }
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00008115 /* re-exec ourselves with the new arguments */
8116 execve(bb_busybox_exec_path, argv, envp);
8117 /* If they called chroot or otherwise made the binary no longer
8118 * executable, fall through */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008119 }
8120#endif
8121
8122 repeat:
8123#ifdef SYSV
8124 do {
8125 execve(cmd, argv, envp);
8126 } while (errno == EINTR);
8127#else
8128 execve(cmd, argv, envp);
8129#endif
Ron Yorstonca82b532018-11-01 11:45:03 +01008130
Denys Vlasenko00a1dbd2017-07-29 01:20:53 +02008131 if (cmd != bb_busybox_exec_path && errno == ENOEXEC) {
Denys Vlasenkoaefe1c22011-03-07 12:02:40 +01008132 /* Run "cmd" as a shell script:
8133 * http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html
8134 * "If the execve() function fails with ENOEXEC, the shell
8135 * shall execute a command equivalent to having a shell invoked
8136 * with the command name as its first operand,
8137 * with any remaining arguments passed to the new shell"
8138 *
8139 * That is, do not use $SHELL, user's shell, or /bin/sh;
8140 * just call ourselves.
Denys Vlasenko2bef5262011-12-16 00:25:17 +01008141 *
8142 * Note that bash reads ~80 chars of the file, and if it sees
8143 * a zero byte before it sees newline, it doesn't try to
8144 * interpret it, but fails with "cannot execute binary file"
Denys Vlasenkocda6ea92011-12-16 00:44:36 +01008145 * message and exit code 126. For one, this prevents attempts
8146 * to interpret foreign ELF binaries as shell scripts.
Denys Vlasenkoaefe1c22011-03-07 12:02:40 +01008147 */
Denys Vlasenko00a1dbd2017-07-29 01:20:53 +02008148 argv[0] = (char*) cmd;
8149 cmd = bb_busybox_exec_path;
Denys Vlasenko65a8b852016-10-26 22:29:11 +02008150 /* NB: this is only possible because all callers of shellexec()
8151 * ensure that the argv[-1] slot exists!
8152 */
8153 argv--;
8154 argv[0] = (char*) "ash";
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008155 goto repeat;
8156 }
8157}
8158
8159/*
8160 * Exec a program. Never returns. If you change this routine, you may
8161 * have to change the find_command routine as well.
Denys Vlasenko65a8b852016-10-26 22:29:11 +02008162 * argv[-1] must exist and be writable! See tryexec() for why.
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008163 */
Denys Vlasenkoe139ae32017-04-12 21:02:33 +02008164static void shellexec(char *prog, char **argv, const char *path, int idx) NORETURN;
8165static void shellexec(char *prog, char **argv, const char *path, int idx)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008166{
8167 char *cmdname;
8168 int e;
8169 char **envp;
8170 int exerrno;
Denys Vlasenko83f103b2011-12-20 06:10:35 +01008171 int applet_no = -1; /* used only by FEATURE_SH_STANDALONE */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008172
Denys Vlasenkoa5060b82017-11-03 14:16:25 +01008173 envp = listvars(VEXPORT, VUNSET, /*strlist:*/ NULL, /*end:*/ NULL);
Denys Vlasenkoe139ae32017-04-12 21:02:33 +02008174 if (strchr(prog, '/') != NULL
Denis Vlasenko80d14be2007-04-10 23:03:30 +00008175#if ENABLE_FEATURE_SH_STANDALONE
Denys Vlasenkoe139ae32017-04-12 21:02:33 +02008176 || (applet_no = find_applet_by_name(prog)) >= 0
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008177#endif
8178 ) {
Denys Vlasenkoe139ae32017-04-12 21:02:33 +02008179 tryexec(IF_FEATURE_SH_STANDALONE(applet_no,) prog, argv, envp);
Denys Vlasenko83f103b2011-12-20 06:10:35 +01008180 if (applet_no >= 0) {
8181 /* We tried execing ourself, but it didn't work.
8182 * Maybe /proc/self/exe doesn't exist?
8183 * Try $PATH search.
8184 */
8185 goto try_PATH;
8186 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008187 e = errno;
8188 } else {
Denys Vlasenko83f103b2011-12-20 06:10:35 +01008189 try_PATH:
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008190 e = ENOENT;
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +01008191 while (padvance(&path, argv[0]) >= 0) {
8192 cmdname = stackblock();
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008193 if (--idx < 0 && pathopt == NULL) {
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00008194 tryexec(IF_FEATURE_SH_STANDALONE(-1,) cmdname, argv, envp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008195 if (errno != ENOENT && errno != ENOTDIR)
8196 e = errno;
8197 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008198 }
8199 }
8200
8201 /* Map to POSIX errors */
8202 switch (e) {
Denys Vlasenko2596f412018-08-05 18:04:09 +02008203 default:
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008204 exerrno = 126;
8205 break;
Denys Vlasenko2596f412018-08-05 18:04:09 +02008206 case ELOOP:
8207 case ENAMETOOLONG:
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008208 case ENOENT:
Denys Vlasenko2596f412018-08-05 18:04:09 +02008209 case ENOTDIR:
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008210 exerrno = 127;
8211 break;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008212 }
8213 exitstatus = exerrno;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02008214 TRACE(("shellexec failed for %s, errno %d, suppress_int %d\n",
Denys Vlasenkoe139ae32017-04-12 21:02:33 +02008215 prog, e, suppress_int));
Denys Vlasenkof977e002020-02-20 16:54:29 +01008216 ash_msg_and_raise(EXEND, "%s: %s", prog, errmsg(e, "not found"));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008217 /* NOTREACHED */
8218}
8219
8220static void
8221printentry(struct tblentry *cmdp)
8222{
8223 int idx;
8224 const char *path;
8225 char *name;
8226
8227 idx = cmdp->param.index;
8228 path = pathval();
8229 do {
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +01008230 padvance(&path, cmdp->cmdname);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008231 } while (--idx >= 0);
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +01008232 name = stackblock();
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008233 out1fmt("%s%s\n", name, (cmdp->rehash ? "*" : nullstr));
8234}
8235
8236/*
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +01008237 * Clear out command entries.
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008238 */
8239static void
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +01008240clearcmdentry(void)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008241{
8242 struct tblentry **tblp;
8243 struct tblentry **pp;
8244 struct tblentry *cmdp;
8245
8246 INT_OFF;
8247 for (tblp = cmdtable; tblp < &cmdtable[CMDTABLESIZE]; tblp++) {
8248 pp = tblp;
8249 while ((cmdp = *pp) != NULL) {
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +01008250 if (cmdp->cmdtype == CMDNORMAL
Denys Vlasenko22c75922020-02-17 16:20:05 +01008251 || (cmdp->cmdtype == CMDBUILTIN
8252 && !IS_BUILTIN_REGULAR(cmdp->param.cmd)
8253 && builtinloc > 0
8254 )
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008255 ) {
8256 *pp = cmdp->next;
8257 free(cmdp);
8258 } else {
8259 pp = &cmdp->next;
8260 }
8261 }
8262 }
8263 INT_ON;
8264}
8265
8266/*
8267 * Locate a command in the command hash table. If "add" is nonzero,
8268 * add the command to the table if it is not already present. The
8269 * variable "lastcmdentry" is set to point to the address of the link
8270 * pointing to the entry, so that delete_cmd_entry can delete the
8271 * entry.
8272 *
8273 * Interrupts must be off if called with add != 0.
8274 */
8275static struct tblentry **lastcmdentry;
8276
8277static struct tblentry *
8278cmdlookup(const char *name, int add)
8279{
8280 unsigned int hashval;
8281 const char *p;
8282 struct tblentry *cmdp;
8283 struct tblentry **pp;
8284
8285 p = name;
8286 hashval = (unsigned char)*p << 4;
8287 while (*p)
8288 hashval += (unsigned char)*p++;
8289 hashval &= 0x7FFF;
8290 pp = &cmdtable[hashval % CMDTABLESIZE];
8291 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
8292 if (strcmp(cmdp->cmdname, name) == 0)
8293 break;
8294 pp = &cmdp->next;
8295 }
8296 if (add && cmdp == NULL) {
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008297 cmdp = *pp = ckzalloc(sizeof(struct tblentry)
8298 + strlen(name)
8299 /* + 1 - already done because
8300 * tblentry::cmdname is char[1] */);
Denis Vlasenko597906c2008-02-20 16:38:54 +00008301 /*cmdp->next = NULL; - ckzalloc did it */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008302 cmdp->cmdtype = CMDUNKNOWN;
8303 strcpy(cmdp->cmdname, name);
8304 }
8305 lastcmdentry = pp;
8306 return cmdp;
8307}
8308
8309/*
8310 * Delete the command entry returned on the last lookup.
8311 */
8312static void
8313delete_cmd_entry(void)
8314{
8315 struct tblentry *cmdp;
8316
8317 INT_OFF;
8318 cmdp = *lastcmdentry;
8319 *lastcmdentry = cmdp->next;
8320 if (cmdp->cmdtype == CMDFUNCTION)
8321 freefunc(cmdp->param.func);
8322 free(cmdp);
8323 INT_ON;
8324}
8325
8326/*
8327 * Add a new command entry, replacing any existing command entry for
8328 * the same name - except special builtins.
8329 */
8330static void
8331addcmdentry(char *name, struct cmdentry *entry)
8332{
8333 struct tblentry *cmdp;
8334
8335 cmdp = cmdlookup(name, 1);
8336 if (cmdp->cmdtype == CMDFUNCTION) {
8337 freefunc(cmdp->param.func);
8338 }
8339 cmdp->cmdtype = entry->cmdtype;
8340 cmdp->param = entry->u;
8341 cmdp->rehash = 0;
8342}
8343
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008344static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008345hashcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008346{
8347 struct tblentry **pp;
8348 struct tblentry *cmdp;
8349 int c;
8350 struct cmdentry entry;
8351 char *name;
8352
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00008353 if (nextopt("r") != '\0') {
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +01008354 clearcmdentry();
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008355 return 0;
8356 }
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00008357
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008358 if (*argptr == NULL) {
8359 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
8360 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
8361 if (cmdp->cmdtype == CMDNORMAL)
8362 printentry(cmdp);
8363 }
8364 }
8365 return 0;
8366 }
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00008367
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008368 c = 0;
8369 while ((name = *argptr) != NULL) {
8370 cmdp = cmdlookup(name, 0);
8371 if (cmdp != NULL
8372 && (cmdp->cmdtype == CMDNORMAL
Denys Vlasenko22c75922020-02-17 16:20:05 +01008373 || (cmdp->cmdtype == CMDBUILTIN
8374 && !IS_BUILTIN_REGULAR(cmdp->param.cmd)
8375 && builtinloc > 0
8376 )
8377 )
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00008378 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008379 delete_cmd_entry();
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00008380 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008381 find_command(name, &entry, DO_ERR, pathval());
8382 if (entry.cmdtype == CMDUNKNOWN)
8383 c = 1;
8384 argptr++;
8385 }
8386 return c;
8387}
8388
8389/*
8390 * Called when a cd is done. Marks all commands so the next time they
8391 * are executed they will be rehashed.
8392 */
8393static void
8394hashcd(void)
8395{
8396 struct tblentry **pp;
8397 struct tblentry *cmdp;
8398
8399 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
8400 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00008401 if (cmdp->cmdtype == CMDNORMAL
8402 || (cmdp->cmdtype == CMDBUILTIN
Denys Vlasenkoe4dcba12010-10-28 18:57:19 +02008403 && !IS_BUILTIN_REGULAR(cmdp->param.cmd)
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00008404 && builtinloc > 0)
8405 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008406 cmdp->rehash = 1;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00008407 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008408 }
8409 }
8410}
8411
8412/*
8413 * Fix command hash table when PATH changed.
8414 * Called before PATH is changed. The argument is the new value of PATH;
8415 * pathval() still returns the old value at this point.
8416 * Called with interrupts off.
8417 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008418static void FAST_FUNC
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +01008419changepath(const char *newval)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008420{
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +01008421 const char *new;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00008422 int idx;
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +01008423 int bltin;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008424
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +01008425 new = newval;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008426 idx = 0;
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +01008427 bltin = -1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008428 for (;;) {
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +01008429 if (*new == '%' && prefix(new + 1, "builtin")) {
8430 bltin = idx;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008431 break;
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +01008432 }
8433 new = strchr(new, ':');
8434 if (!new)
8435 break;
8436 idx++;
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +02008437 new++;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008438 }
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +01008439 builtinloc = bltin;
8440 clearcmdentry();
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008441}
Ron Yorston95ebcf72015-11-03 09:42:23 +00008442enum {
8443 TEOF,
8444 TNL,
8445 TREDIR,
8446 TWORD,
8447 TSEMI,
8448 TBACKGND,
8449 TAND,
8450 TOR,
8451 TPIPE,
8452 TLP,
8453 TRP,
8454 TENDCASE,
8455 TENDBQUOTE,
8456 TNOT,
8457 TCASE,
8458 TDO,
8459 TDONE,
8460 TELIF,
8461 TELSE,
8462 TESAC,
8463 TFI,
8464 TFOR,
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01008465#if BASH_FUNCTION
Ron Yorston95ebcf72015-11-03 09:42:23 +00008466 TFUNCTION,
8467#endif
8468 TIF,
8469 TIN,
8470 TTHEN,
8471 TUNTIL,
8472 TWHILE,
8473 TBEGIN,
8474 TEND
8475};
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008476typedef smallint token_id_t;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008477
Denys Vlasenko888527c2016-10-02 16:54:17 +02008478/* Nth bit indicates if token marks the end of a list */
8479enum {
8480 tokendlist = 0
8481 /* 0 */ | (1u << TEOF)
8482 /* 1 */ | (0u << TNL)
8483 /* 2 */ | (0u << TREDIR)
8484 /* 3 */ | (0u << TWORD)
8485 /* 4 */ | (0u << TSEMI)
8486 /* 5 */ | (0u << TBACKGND)
8487 /* 6 */ | (0u << TAND)
8488 /* 7 */ | (0u << TOR)
8489 /* 8 */ | (0u << TPIPE)
8490 /* 9 */ | (0u << TLP)
8491 /* 10 */ | (1u << TRP)
8492 /* 11 */ | (1u << TENDCASE)
8493 /* 12 */ | (1u << TENDBQUOTE)
8494 /* 13 */ | (0u << TNOT)
8495 /* 14 */ | (0u << TCASE)
8496 /* 15 */ | (1u << TDO)
8497 /* 16 */ | (1u << TDONE)
8498 /* 17 */ | (1u << TELIF)
8499 /* 18 */ | (1u << TELSE)
8500 /* 19 */ | (1u << TESAC)
8501 /* 20 */ | (1u << TFI)
8502 /* 21 */ | (0u << TFOR)
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01008503#if BASH_FUNCTION
Denys Vlasenko888527c2016-10-02 16:54:17 +02008504 /* 22 */ | (0u << TFUNCTION)
Denys Vlasenko80729a42016-10-02 22:33:15 +02008505#endif
Denys Vlasenko888527c2016-10-02 16:54:17 +02008506 /* 23 */ | (0u << TIF)
8507 /* 24 */ | (0u << TIN)
8508 /* 25 */ | (1u << TTHEN)
8509 /* 26 */ | (0u << TUNTIL)
8510 /* 27 */ | (0u << TWHILE)
8511 /* 28 */ | (0u << TBEGIN)
8512 /* 29 */ | (1u << TEND)
8513 , /* thus far 29 bits used */
8514};
8515
Denys Vlasenko965b7952020-11-30 13:03:03 +01008516static const char *const tokname_array[] ALIGN_PTR = {
Denys Vlasenko888527c2016-10-02 16:54:17 +02008517 "end of file",
8518 "newline",
8519 "redirection",
8520 "word",
8521 ";",
8522 "&",
8523 "&&",
8524 "||",
8525 "|",
8526 "(",
8527 ")",
8528 ";;",
8529 "`",
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008530#define KWDOFFSET 13
8531 /* the following are keywords */
Denys Vlasenko888527c2016-10-02 16:54:17 +02008532 "!",
8533 "case",
8534 "do",
8535 "done",
8536 "elif",
8537 "else",
8538 "esac",
8539 "fi",
8540 "for",
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01008541#if BASH_FUNCTION
Denys Vlasenko888527c2016-10-02 16:54:17 +02008542 "function",
Ron Yorston95ebcf72015-11-03 09:42:23 +00008543#endif
Denys Vlasenko888527c2016-10-02 16:54:17 +02008544 "if",
8545 "in",
8546 "then",
8547 "until",
8548 "while",
8549 "{",
8550 "}",
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008551};
8552
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008553/* Wrapper around strcmp for qsort/bsearch/... */
8554static int
8555pstrcmp(const void *a, const void *b)
8556{
Denys Vlasenko888527c2016-10-02 16:54:17 +02008557 return strcmp((char*)a, *(char**)b);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008558}
8559
8560static const char *const *
8561findkwd(const char *s)
8562{
8563 return bsearch(s, tokname_array + KWDOFFSET,
Denis Vlasenko80b8b392007-06-25 10:55:35 +00008564 ARRAY_SIZE(tokname_array) - KWDOFFSET,
8565 sizeof(tokname_array[0]), pstrcmp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008566}
8567
8568/*
8569 * Locate and print what a word is...
8570 */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008571static int
Ron Yorston3f221112015-08-03 13:47:33 +01008572describe_command(char *command, const char *path, int describe_command_verbose)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008573{
8574 struct cmdentry entry;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008575#if ENABLE_ASH_ALIAS
8576 const struct alias *ap;
8577#endif
Ron Yorston3f221112015-08-03 13:47:33 +01008578
8579 path = path ? path : pathval();
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008580
8581 if (describe_command_verbose) {
8582 out1str(command);
8583 }
8584
8585 /* First look at the keywords */
8586 if (findkwd(command)) {
8587 out1str(describe_command_verbose ? " is a shell keyword" : command);
8588 goto out;
8589 }
8590
8591#if ENABLE_ASH_ALIAS
8592 /* Then look at the aliases */
8593 ap = lookupalias(command, 0);
8594 if (ap != NULL) {
Denis Vlasenko46846e22007-05-20 13:08:31 +00008595 if (!describe_command_verbose) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008596 out1str("alias ");
8597 printalias(ap);
8598 return 0;
8599 }
Denis Vlasenko46846e22007-05-20 13:08:31 +00008600 out1fmt(" is an alias for %s", ap->val);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008601 goto out;
8602 }
8603#endif
Youfu Zhang6683d1c2017-05-26 15:31:29 +08008604 /* Brute force */
8605 find_command(command, &entry, DO_ABS, path);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008606
8607 switch (entry.cmdtype) {
8608 case CMDNORMAL: {
8609 int j = entry.u.index;
8610 char *p;
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00008611 if (j < 0) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008612 p = command;
8613 } else {
8614 do {
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +01008615 padvance(&path, command);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008616 } while (--j >= 0);
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +01008617 p = stackblock();
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008618 }
8619 if (describe_command_verbose) {
Youfu Zhang6683d1c2017-05-26 15:31:29 +08008620 out1fmt(" is %s", p);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008621 } else {
8622 out1str(p);
8623 }
8624 break;
8625 }
8626
8627 case CMDFUNCTION:
8628 if (describe_command_verbose) {
Denys Vlasenko63c42af2018-07-24 17:08:04 +02008629 /*out1str(" is a shell function");*/
8630 out1str(" is a function"); /* bash says this */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008631 } else {
8632 out1str(command);
8633 }
8634 break;
8635
8636 case CMDBUILTIN:
8637 if (describe_command_verbose) {
8638 out1fmt(" is a %sshell builtin",
8639 IS_BUILTIN_SPECIAL(entry.u.cmd) ?
8640 "special " : nullstr
8641 );
8642 } else {
8643 out1str(command);
8644 }
8645 break;
8646
8647 default:
8648 if (describe_command_verbose) {
8649 out1str(": not found\n");
8650 }
8651 return 127;
8652 }
8653 out:
Denys Vlasenko285ad152009-12-04 23:02:27 +01008654 out1str("\n");
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008655 return 0;
8656}
8657
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008658static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008659typecmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008660{
Denis Vlasenko46846e22007-05-20 13:08:31 +00008661 int i = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008662 int err = 0;
Denis Vlasenko46846e22007-05-20 13:08:31 +00008663 int verbose = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008664
Denis Vlasenko46846e22007-05-20 13:08:31 +00008665 /* type -p ... ? (we don't bother checking for 'p') */
Denis Vlasenko1fc62382007-06-25 22:55:34 +00008666 if (argv[1] && argv[1][0] == '-') {
Denis Vlasenko46846e22007-05-20 13:08:31 +00008667 i++;
8668 verbose = 0;
8669 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00008670 while (argv[i]) {
Ron Yorston3f221112015-08-03 13:47:33 +01008671 err |= describe_command(argv[i++], NULL, verbose);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008672 }
8673 return err;
8674}
8675
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +01008676static struct strlist *
8677fill_arglist(struct arglist *arglist, union node **argpp)
Denys Vlasenkocac4d002016-10-01 03:02:25 +02008678{
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +01008679 struct strlist **lastp = arglist->lastp;
8680 union node *argp;
8681
8682 while ((argp = *argpp) != NULL) {
8683 expandarg(argp, arglist, EXP_FULL | EXP_TILDE);
8684 *argpp = argp->narg.next;
8685 if (*lastp)
8686 break;
8687 }
8688
8689 return *lastp;
8690}
8691
Ron Yorstonda7a6db2020-02-27 09:50:18 +00008692#if ENABLE_ASH_CMDCMD
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +01008693/* Is it "command [-p] PROG ARGS" bltin, no other opts? Return ptr to "PROG" if yes */
8694static int
8695parse_command_args(struct arglist *arglist, union node **argpp, const char **path)
8696{
8697 struct strlist *sp = arglist->list;
Denys Vlasenkocac4d002016-10-01 03:02:25 +02008698 char *cp, c;
8699
8700 for (;;) {
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +01008701 sp = sp->next ? sp->next : fill_arglist(arglist, argpp);
8702 if (!sp)
8703 return 0;
8704 cp = sp->text;
Denys Vlasenkocac4d002016-10-01 03:02:25 +02008705 if (*cp++ != '-')
8706 break;
8707 c = *cp++;
8708 if (!c)
8709 break;
8710 if (c == '-' && !*cp) {
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +01008711 if (!sp->next && !fill_arglist(arglist, argpp))
8712 return 0;
8713 sp = sp->next;
Denys Vlasenkocac4d002016-10-01 03:02:25 +02008714 break;
8715 }
8716 do {
8717 switch (c) {
8718 case 'p':
8719 *path = bb_default_path;
8720 break;
8721 default:
8722 /* run 'typecmd' for other options */
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +01008723 return 0;
Denys Vlasenkocac4d002016-10-01 03:02:25 +02008724 }
8725 c = *cp++;
8726 } while (c);
8727 }
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +01008728
8729 arglist->list = sp;
8730 return DO_NOFUNC;
Denys Vlasenkocac4d002016-10-01 03:02:25 +02008731}
8732
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008733static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008734commandcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008735{
Denys Vlasenkocac4d002016-10-01 03:02:25 +02008736 char *cmd;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008737 int c;
8738 enum {
8739 VERIFY_BRIEF = 1,
8740 VERIFY_VERBOSE = 2,
8741 } verify = 0;
Ron Yorston3f221112015-08-03 13:47:33 +01008742 const char *path = NULL;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008743
Denys Vlasenkocac4d002016-10-01 03:02:25 +02008744 /* "command [-p] PROG ARGS" (that is, without -V or -v)
8745 * never reaches this function.
8746 */
8747
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008748 while ((c = nextopt("pvV")) != '\0')
8749 if (c == 'V')
8750 verify |= VERIFY_VERBOSE;
8751 else if (c == 'v')
Denys Vlasenkocac4d002016-10-01 03:02:25 +02008752 /*verify |= VERIFY_BRIEF*/;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008753#if DEBUG
8754 else if (c != 'p')
8755 abort();
8756#endif
Ron Yorston3f221112015-08-03 13:47:33 +01008757 else
8758 path = bb_default_path;
Denys Vlasenkocac4d002016-10-01 03:02:25 +02008759
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00008760 /* Mimic bash: just "command -v" doesn't complain, it's a nop */
Denys Vlasenkocac4d002016-10-01 03:02:25 +02008761 cmd = *argptr;
8762 if (/*verify && */ cmd)
8763 return describe_command(cmd, path, verify /* - VERIFY_BRIEF*/);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008764
8765 return 0;
8766}
8767#endif
8768
8769
Denys Vlasenko4c438b52016-10-07 04:05:15 +02008770/*static int funcblocksize; // size of structures in function */
8771/*static int funcstringsize; // size of strings in node */
Denis Vlasenko340299a2008-11-21 10:36:36 +00008772static void *funcblock; /* block to allocate function from */
Denys Vlasenko4c438b52016-10-07 04:05:15 +02008773static char *funcstring_end; /* end of block to allocate strings from */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008774
Denys Vlasenko3e134eb2016-04-22 18:09:21 +02008775static const uint8_t nodesize[N_NUMBER] ALIGN1 = {
Denis Vlasenko340299a2008-11-21 10:36:36 +00008776 [NCMD ] = SHELL_ALIGN(sizeof(struct ncmd)),
8777 [NPIPE ] = SHELL_ALIGN(sizeof(struct npipe)),
8778 [NREDIR ] = SHELL_ALIGN(sizeof(struct nredir)),
8779 [NBACKGND ] = SHELL_ALIGN(sizeof(struct nredir)),
8780 [NSUBSHELL] = SHELL_ALIGN(sizeof(struct nredir)),
8781 [NAND ] = SHELL_ALIGN(sizeof(struct nbinary)),
8782 [NOR ] = SHELL_ALIGN(sizeof(struct nbinary)),
8783 [NSEMI ] = SHELL_ALIGN(sizeof(struct nbinary)),
8784 [NIF ] = SHELL_ALIGN(sizeof(struct nif)),
8785 [NWHILE ] = SHELL_ALIGN(sizeof(struct nbinary)),
8786 [NUNTIL ] = SHELL_ALIGN(sizeof(struct nbinary)),
8787 [NFOR ] = SHELL_ALIGN(sizeof(struct nfor)),
8788 [NCASE ] = SHELL_ALIGN(sizeof(struct ncase)),
8789 [NCLIST ] = SHELL_ALIGN(sizeof(struct nclist)),
8790 [NDEFUN ] = SHELL_ALIGN(sizeof(struct narg)),
8791 [NARG ] = SHELL_ALIGN(sizeof(struct narg)),
8792 [NTO ] = SHELL_ALIGN(sizeof(struct nfile)),
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01008793#if BASH_REDIR_OUTPUT
Denis Vlasenko340299a2008-11-21 10:36:36 +00008794 [NTO2 ] = SHELL_ALIGN(sizeof(struct nfile)),
Denis Vlasenkocc5feab2008-11-22 01:32:40 +00008795#endif
Denis Vlasenko340299a2008-11-21 10:36:36 +00008796 [NCLOBBER ] = SHELL_ALIGN(sizeof(struct nfile)),
8797 [NFROM ] = SHELL_ALIGN(sizeof(struct nfile)),
8798 [NFROMTO ] = SHELL_ALIGN(sizeof(struct nfile)),
8799 [NAPPEND ] = SHELL_ALIGN(sizeof(struct nfile)),
8800 [NTOFD ] = SHELL_ALIGN(sizeof(struct ndup)),
8801 [NFROMFD ] = SHELL_ALIGN(sizeof(struct ndup)),
8802 [NHERE ] = SHELL_ALIGN(sizeof(struct nhere)),
8803 [NXHERE ] = SHELL_ALIGN(sizeof(struct nhere)),
8804 [NNOT ] = SHELL_ALIGN(sizeof(struct nnot)),
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008805};
8806
Denys Vlasenko4c438b52016-10-07 04:05:15 +02008807static int calcsize(int funcblocksize, union node *n);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008808
Denys Vlasenko4c438b52016-10-07 04:05:15 +02008809static int
8810sizenodelist(int funcblocksize, struct nodelist *lp)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008811{
8812 while (lp) {
8813 funcblocksize += SHELL_ALIGN(sizeof(struct nodelist));
Denys Vlasenko4c438b52016-10-07 04:05:15 +02008814 funcblocksize = calcsize(funcblocksize, lp->n);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008815 lp = lp->next;
8816 }
Denys Vlasenko4c438b52016-10-07 04:05:15 +02008817 return funcblocksize;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008818}
8819
Denys Vlasenko4c438b52016-10-07 04:05:15 +02008820static int
8821calcsize(int funcblocksize, union node *n)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008822{
8823 if (n == NULL)
Denys Vlasenko4c438b52016-10-07 04:05:15 +02008824 return funcblocksize;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008825 funcblocksize += nodesize[n->type];
8826 switch (n->type) {
8827 case NCMD:
Denys Vlasenko4c438b52016-10-07 04:05:15 +02008828 funcblocksize = calcsize(funcblocksize, n->ncmd.redirect);
8829 funcblocksize = calcsize(funcblocksize, n->ncmd.args);
8830 funcblocksize = calcsize(funcblocksize, n->ncmd.assign);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008831 break;
8832 case NPIPE:
Denys Vlasenko4c438b52016-10-07 04:05:15 +02008833 funcblocksize = sizenodelist(funcblocksize, n->npipe.cmdlist);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008834 break;
8835 case NREDIR:
8836 case NBACKGND:
8837 case NSUBSHELL:
Denys Vlasenko4c438b52016-10-07 04:05:15 +02008838 funcblocksize = calcsize(funcblocksize, n->nredir.redirect);
8839 funcblocksize = calcsize(funcblocksize, n->nredir.n);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008840 break;
8841 case NAND:
8842 case NOR:
8843 case NSEMI:
8844 case NWHILE:
8845 case NUNTIL:
Denys Vlasenko4c438b52016-10-07 04:05:15 +02008846 funcblocksize = calcsize(funcblocksize, n->nbinary.ch2);
8847 funcblocksize = calcsize(funcblocksize, n->nbinary.ch1);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008848 break;
8849 case NIF:
Denys Vlasenko4c438b52016-10-07 04:05:15 +02008850 funcblocksize = calcsize(funcblocksize, n->nif.elsepart);
8851 funcblocksize = calcsize(funcblocksize, n->nif.ifpart);
8852 funcblocksize = calcsize(funcblocksize, n->nif.test);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008853 break;
8854 case NFOR:
Denys Vlasenko561639a2016-10-07 04:28:33 +02008855 funcblocksize += SHELL_ALIGN(strlen(n->nfor.var) + 1); /* was funcstringsize += ... */
Denys Vlasenko4c438b52016-10-07 04:05:15 +02008856 funcblocksize = calcsize(funcblocksize, n->nfor.body);
8857 funcblocksize = calcsize(funcblocksize, n->nfor.args);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008858 break;
8859 case NCASE:
Denys Vlasenko4c438b52016-10-07 04:05:15 +02008860 funcblocksize = calcsize(funcblocksize, n->ncase.cases);
8861 funcblocksize = calcsize(funcblocksize, n->ncase.expr);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008862 break;
8863 case NCLIST:
Denys Vlasenko4c438b52016-10-07 04:05:15 +02008864 funcblocksize = calcsize(funcblocksize, n->nclist.body);
8865 funcblocksize = calcsize(funcblocksize, n->nclist.pattern);
8866 funcblocksize = calcsize(funcblocksize, n->nclist.next);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008867 break;
8868 case NDEFUN:
Denys Vlasenko675d24a2018-01-27 22:02:05 +01008869 funcblocksize = calcsize(funcblocksize, n->ndefun.body);
8870 funcblocksize += SHELL_ALIGN(strlen(n->ndefun.text) + 1);
8871 break;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008872 case NARG:
Denys Vlasenko4c438b52016-10-07 04:05:15 +02008873 funcblocksize = sizenodelist(funcblocksize, n->narg.backquote);
Denys Vlasenko561639a2016-10-07 04:28:33 +02008874 funcblocksize += SHELL_ALIGN(strlen(n->narg.text) + 1); /* was funcstringsize += ... */
Denys Vlasenko4c438b52016-10-07 04:05:15 +02008875 funcblocksize = calcsize(funcblocksize, n->narg.next);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008876 break;
8877 case NTO:
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01008878#if BASH_REDIR_OUTPUT
Denis Vlasenko559691a2008-10-05 18:39:31 +00008879 case NTO2:
8880#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008881 case NCLOBBER:
8882 case NFROM:
8883 case NFROMTO:
8884 case NAPPEND:
Denys Vlasenko4c438b52016-10-07 04:05:15 +02008885 funcblocksize = calcsize(funcblocksize, n->nfile.fname);
8886 funcblocksize = calcsize(funcblocksize, n->nfile.next);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008887 break;
8888 case NTOFD:
8889 case NFROMFD:
Denys Vlasenko4c438b52016-10-07 04:05:15 +02008890 funcblocksize = calcsize(funcblocksize, n->ndup.vname);
8891 funcblocksize = calcsize(funcblocksize, n->ndup.next);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008892 break;
8893 case NHERE:
8894 case NXHERE:
Denys Vlasenko4c438b52016-10-07 04:05:15 +02008895 funcblocksize = calcsize(funcblocksize, n->nhere.doc);
8896 funcblocksize = calcsize(funcblocksize, n->nhere.next);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008897 break;
8898 case NNOT:
Denys Vlasenko4c438b52016-10-07 04:05:15 +02008899 funcblocksize = calcsize(funcblocksize, n->nnot.com);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008900 break;
8901 };
Denys Vlasenko4c438b52016-10-07 04:05:15 +02008902 return funcblocksize;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008903}
8904
8905static char *
8906nodeckstrdup(char *s)
8907{
Denys Vlasenko561639a2016-10-07 04:28:33 +02008908 funcstring_end -= SHELL_ALIGN(strlen(s) + 1);
Denys Vlasenko4c438b52016-10-07 04:05:15 +02008909 return strcpy(funcstring_end, s);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008910}
8911
8912static union node *copynode(union node *);
8913
8914static struct nodelist *
8915copynodelist(struct nodelist *lp)
8916{
8917 struct nodelist *start;
8918 struct nodelist **lpp;
8919
8920 lpp = &start;
8921 while (lp) {
8922 *lpp = funcblock;
8923 funcblock = (char *) funcblock + SHELL_ALIGN(sizeof(struct nodelist));
8924 (*lpp)->n = copynode(lp->n);
8925 lp = lp->next;
8926 lpp = &(*lpp)->next;
8927 }
8928 *lpp = NULL;
8929 return start;
8930}
8931
8932static union node *
8933copynode(union node *n)
8934{
8935 union node *new;
8936
8937 if (n == NULL)
8938 return NULL;
8939 new = funcblock;
8940 funcblock = (char *) funcblock + nodesize[n->type];
8941
8942 switch (n->type) {
8943 case NCMD:
8944 new->ncmd.redirect = copynode(n->ncmd.redirect);
8945 new->ncmd.args = copynode(n->ncmd.args);
8946 new->ncmd.assign = copynode(n->ncmd.assign);
Denys Vlasenko675d24a2018-01-27 22:02:05 +01008947 new->ncmd.linno = n->ncmd.linno;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008948 break;
8949 case NPIPE:
8950 new->npipe.cmdlist = copynodelist(n->npipe.cmdlist);
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00008951 new->npipe.pipe_backgnd = n->npipe.pipe_backgnd;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008952 break;
8953 case NREDIR:
8954 case NBACKGND:
8955 case NSUBSHELL:
8956 new->nredir.redirect = copynode(n->nredir.redirect);
8957 new->nredir.n = copynode(n->nredir.n);
Denys Vlasenko675d24a2018-01-27 22:02:05 +01008958 new->nredir.linno = n->nredir.linno;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008959 break;
8960 case NAND:
8961 case NOR:
8962 case NSEMI:
8963 case NWHILE:
8964 case NUNTIL:
8965 new->nbinary.ch2 = copynode(n->nbinary.ch2);
8966 new->nbinary.ch1 = copynode(n->nbinary.ch1);
8967 break;
8968 case NIF:
8969 new->nif.elsepart = copynode(n->nif.elsepart);
8970 new->nif.ifpart = copynode(n->nif.ifpart);
8971 new->nif.test = copynode(n->nif.test);
8972 break;
8973 case NFOR:
8974 new->nfor.var = nodeckstrdup(n->nfor.var);
8975 new->nfor.body = copynode(n->nfor.body);
8976 new->nfor.args = copynode(n->nfor.args);
Denys Vlasenko675d24a2018-01-27 22:02:05 +01008977 new->nfor.linno = n->nfor.linno;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008978 break;
8979 case NCASE:
8980 new->ncase.cases = copynode(n->ncase.cases);
8981 new->ncase.expr = copynode(n->ncase.expr);
Denys Vlasenko675d24a2018-01-27 22:02:05 +01008982 new->ncase.linno = n->ncase.linno;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008983 break;
8984 case NCLIST:
8985 new->nclist.body = copynode(n->nclist.body);
8986 new->nclist.pattern = copynode(n->nclist.pattern);
8987 new->nclist.next = copynode(n->nclist.next);
8988 break;
8989 case NDEFUN:
Denys Vlasenko675d24a2018-01-27 22:02:05 +01008990 new->ndefun.body = copynode(n->ndefun.body);
8991 new->ndefun.text = nodeckstrdup(n->ndefun.text);
8992 new->ndefun.linno = n->ndefun.linno;
8993 break;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008994 case NARG:
8995 new->narg.backquote = copynodelist(n->narg.backquote);
8996 new->narg.text = nodeckstrdup(n->narg.text);
8997 new->narg.next = copynode(n->narg.next);
8998 break;
8999 case NTO:
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01009000#if BASH_REDIR_OUTPUT
Denis Vlasenko559691a2008-10-05 18:39:31 +00009001 case NTO2:
9002#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009003 case NCLOBBER:
9004 case NFROM:
9005 case NFROMTO:
9006 case NAPPEND:
9007 new->nfile.fname = copynode(n->nfile.fname);
9008 new->nfile.fd = n->nfile.fd;
9009 new->nfile.next = copynode(n->nfile.next);
9010 break;
9011 case NTOFD:
9012 case NFROMFD:
9013 new->ndup.vname = copynode(n->ndup.vname);
9014 new->ndup.dupfd = n->ndup.dupfd;
9015 new->ndup.fd = n->ndup.fd;
9016 new->ndup.next = copynode(n->ndup.next);
9017 break;
9018 case NHERE:
9019 case NXHERE:
9020 new->nhere.doc = copynode(n->nhere.doc);
9021 new->nhere.fd = n->nhere.fd;
9022 new->nhere.next = copynode(n->nhere.next);
9023 break;
9024 case NNOT:
9025 new->nnot.com = copynode(n->nnot.com);
9026 break;
9027 };
9028 new->type = n->type;
9029 return new;
9030}
9031
9032/*
9033 * Make a copy of a parse tree.
9034 */
9035static struct funcnode *
9036copyfunc(union node *n)
9037{
9038 struct funcnode *f;
9039 size_t blocksize;
9040
Denys Vlasenko4c438b52016-10-07 04:05:15 +02009041 /*funcstringsize = 0;*/
9042 blocksize = offsetof(struct funcnode, n) + calcsize(0, n);
9043 f = ckzalloc(blocksize /* + funcstringsize */);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009044 funcblock = (char *) f + offsetof(struct funcnode, n);
Denys Vlasenko4c438b52016-10-07 04:05:15 +02009045 funcstring_end = (char *) f + blocksize;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009046 copynode(n);
Denys Vlasenko4c438b52016-10-07 04:05:15 +02009047 /* f->count = 0; - ckzalloc did it */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009048 return f;
9049}
9050
9051/*
9052 * Define a shell function.
9053 */
9054static void
Denys Vlasenko7aec8682016-10-25 20:26:02 +02009055defun(union node *func)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009056{
9057 struct cmdentry entry;
9058
9059 INT_OFF;
9060 entry.cmdtype = CMDFUNCTION;
9061 entry.u.func = copyfunc(func);
Denys Vlasenko675d24a2018-01-27 22:02:05 +01009062 addcmdentry(func->ndefun.text, &entry);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009063 INT_ON;
9064}
9065
Denis Vlasenko4b875702009-03-19 13:30:04 +00009066/* Reasons for skipping commands (see comment on breakcmd routine) */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009067#define SKIPBREAK (1 << 0)
9068#define SKIPCONT (1 << 1)
9069#define SKIPFUNC (1 << 2)
Denys Vlasenkocd24a502020-02-20 16:47:01 +01009070#define SKIPFUNCDEF (1 << 3)
Denis Vlasenko4b875702009-03-19 13:30:04 +00009071static smallint evalskip; /* set to SKIPxxx if we are skipping commands */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009072static int skipcount; /* number of levels to skip */
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00009073static int loopnest; /* current loop nesting level */
Denys Vlasenko675d24a2018-01-27 22:02:05 +01009074static int funcline; /* starting line number of current function, or 0 if not in a function */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009075
Denis Vlasenko4b875702009-03-19 13:30:04 +00009076/* Forward decl way out to parsing code - dotrap needs it */
Denys Vlasenko7b3fa1e2016-10-01 15:10:16 +02009077static int evalstring(char *s, int flags);
Denis Vlasenkofc06f292007-02-23 21:09:35 +00009078
Denis Vlasenko4b875702009-03-19 13:30:04 +00009079/* Called to execute a trap.
9080 * Single callsite - at the end of evaltree().
Denys Vlasenkob563f622010-09-25 17:15:13 +02009081 * If we return non-zero, evaltree raises EXEXIT exception.
Denis Vlasenko4b875702009-03-19 13:30:04 +00009082 *
9083 * Perhaps we should avoid entering new trap handlers
9084 * while we are executing a trap handler. [is it a TODO?]
Denis Vlasenkofc06f292007-02-23 21:09:35 +00009085 */
Denys Vlasenkob98b4c12016-10-01 23:25:12 +02009086static void
Denis Vlasenkofc06f292007-02-23 21:09:35 +00009087dotrap(void)
9088{
Denis Vlasenko4b875702009-03-19 13:30:04 +00009089 uint8_t *g;
9090 int sig;
Denys Vlasenko4ccddc82020-02-14 17:27:18 +01009091 int status, last_status;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00009092
Denys Vlasenkob98b4c12016-10-01 23:25:12 +02009093 if (!pending_sig)
9094 return;
9095
Denys Vlasenko4ccddc82020-02-14 17:27:18 +01009096 status = savestatus;
9097 last_status = status;
9098 if (status < 0) {
9099 status = exitstatus;
9100 savestatus = status;
9101 }
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02009102 pending_sig = 0;
Denys Vlasenkode892052016-10-02 01:49:13 +02009103 barrier();
Denis Vlasenkofc06f292007-02-23 21:09:35 +00009104
Denis Vlasenko653d8e72009-03-19 21:59:35 +00009105 TRACE(("dotrap entered\n"));
Denis Vlasenko4b875702009-03-19 13:30:04 +00009106 for (sig = 1, g = gotsig; sig < NSIG; sig++, g++) {
Denys Vlasenkob98b4c12016-10-01 23:25:12 +02009107 char *p;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00009108
Denys Vlasenkob98b4c12016-10-01 23:25:12 +02009109 if (!*g)
Denis Vlasenkofc06f292007-02-23 21:09:35 +00009110 continue;
Denys Vlasenkob98b4c12016-10-01 23:25:12 +02009111
9112 if (evalskip) {
9113 pending_sig = sig;
9114 break;
9115 }
9116
9117 p = trap[sig];
Denis Vlasenko4b875702009-03-19 13:30:04 +00009118 /* non-trapped SIGINT is handled separately by raise_interrupt,
9119 * don't upset it by resetting gotsig[SIGINT-1] */
Denys Vlasenkob98b4c12016-10-01 23:25:12 +02009120 if (sig == SIGINT && !p)
Denis Vlasenko4b875702009-03-19 13:30:04 +00009121 continue;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00009122
Denys Vlasenkob98b4c12016-10-01 23:25:12 +02009123 TRACE(("sig %d is active, will run handler '%s'\n", sig, p));
Denis Vlasenko4b875702009-03-19 13:30:04 +00009124 *g = 0;
Denys Vlasenkob98b4c12016-10-01 23:25:12 +02009125 if (!p)
Denis Vlasenko4b875702009-03-19 13:30:04 +00009126 continue;
Denys Vlasenkob98b4c12016-10-01 23:25:12 +02009127 evalstring(p, 0);
Denys Vlasenkocd24a502020-02-20 16:47:01 +01009128 if (evalskip != SKIPFUNC)
9129 exitstatus = status;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00009130 }
Denys Vlasenko4ccddc82020-02-14 17:27:18 +01009131
9132 savestatus = last_status;
Denys Vlasenkob98b4c12016-10-01 23:25:12 +02009133 TRACE(("dotrap returns\n"));
Denis Vlasenkofc06f292007-02-23 21:09:35 +00009134}
9135
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009136/* forward declarations - evaluation is fairly recursive business... */
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009137static int evalloop(union node *, int);
9138static int evalfor(union node *, int);
9139static int evalcase(union node *, int);
9140static int evalsubshell(union node *, int);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009141static void expredir(union node *);
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009142static int evalpipe(union node *, int);
9143static int evalcommand(union node *, int);
Denys Vlasenko7b3fa1e2016-10-01 15:10:16 +02009144static int evalbltin(const struct builtincmd *, int, char **, int);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009145static void prehash(union node *);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009146
Eric Andersen62483552001-07-10 06:09:16 +00009147/*
Eric Andersenc470f442003-07-28 09:56:35 +00009148 * Evaluate a parse tree. The value is left in the global variable
9149 * exitstatus.
Eric Andersen62483552001-07-10 06:09:16 +00009150 */
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009151static int
Eric Andersenc470f442003-07-28 09:56:35 +00009152evaltree(union node *n, int flags)
Eric Andersen62483552001-07-10 06:09:16 +00009153{
Eric Andersenc470f442003-07-28 09:56:35 +00009154 int checkexit = 0;
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009155 int (*evalfn)(union node *, int);
Ron Yorstonf55161a2019-02-25 08:29:38 +00009156 struct stackmark smark;
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009157 int status = 0;
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00009158
Ron Yorstonf55161a2019-02-25 08:29:38 +00009159 setstackmark(&smark);
9160
Eric Andersenc470f442003-07-28 09:56:35 +00009161 if (n == NULL) {
9162 TRACE(("evaltree(NULL) called\n"));
Denys Vlasenkoc0663c72016-10-27 21:09:01 +02009163 goto out;
Eric Andersen62483552001-07-10 06:09:16 +00009164 }
Denis Vlasenko653d8e72009-03-19 21:59:35 +00009165 TRACE(("evaltree(%p: %d, %d) called\n", n, n->type, flags));
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00009166
Denys Vlasenkob98b4c12016-10-01 23:25:12 +02009167 dotrap();
9168
Eric Andersenc470f442003-07-28 09:56:35 +00009169 switch (n->type) {
9170 default:
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00009171#if DEBUG
Eric Andersenc470f442003-07-28 09:56:35 +00009172 out1fmt("Node type = %d\n", n->type);
Denys Vlasenko8131eea2009-11-02 14:19:51 +01009173 fflush_all();
Eric Andersenc470f442003-07-28 09:56:35 +00009174 break;
9175#endif
9176 case NNOT:
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009177 status = !evaltree(n->nnot.com, EV_TESTED);
Eric Andersenc470f442003-07-28 09:56:35 +00009178 goto setstatus;
9179 case NREDIR:
Denys Vlasenko675d24a2018-01-27 22:02:05 +01009180 errlinno = lineno = n->nredir.linno;
9181 if (funcline)
9182 lineno -= funcline - 1;
Eric Andersenc470f442003-07-28 09:56:35 +00009183 expredir(n->nredir.redirect);
Denys Vlasenko1c79aeb2017-07-29 22:51:52 +02009184 pushredir(n->nredir.redirect);
Eric Andersenc470f442003-07-28 09:56:35 +00009185 status = redirectsafe(n->nredir.redirect, REDIR_PUSH);
9186 if (!status) {
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009187 status = evaltree(n->nredir.n, flags & EV_TESTED);
Eric Andersenc470f442003-07-28 09:56:35 +00009188 }
Denys Vlasenkoeaf94362016-10-25 21:46:03 +02009189 if (n->nredir.redirect)
Denys Vlasenko035486c2017-07-31 04:09:19 +02009190 popredir(/*drop:*/ 0);
Eric Andersenc470f442003-07-28 09:56:35 +00009191 goto setstatus;
9192 case NCMD:
9193 evalfn = evalcommand;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009194 checkexit:
Eric Andersenc470f442003-07-28 09:56:35 +00009195 if (eflag && !(flags & EV_TESTED))
9196 checkexit = ~0;
9197 goto calleval;
9198 case NFOR:
9199 evalfn = evalfor;
9200 goto calleval;
9201 case NWHILE:
9202 case NUNTIL:
9203 evalfn = evalloop;
9204 goto calleval;
9205 case NSUBSHELL:
9206 case NBACKGND:
9207 evalfn = evalsubshell;
Denys Vlasenkocf98b0c2016-10-25 18:19:39 +02009208 goto checkexit;
Eric Andersenc470f442003-07-28 09:56:35 +00009209 case NPIPE:
9210 evalfn = evalpipe;
9211 goto checkexit;
9212 case NCASE:
9213 evalfn = evalcase;
9214 goto calleval;
9215 case NAND:
9216 case NOR:
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00009217 case NSEMI: {
9218
Eric Andersenc470f442003-07-28 09:56:35 +00009219#if NAND + 1 != NOR
9220#error NAND + 1 != NOR
9221#endif
9222#if NOR + 1 != NSEMI
9223#error NOR + 1 != NSEMI
9224#endif
Denis Vlasenko87d5fd92008-07-26 13:48:35 +00009225 unsigned is_or = n->type - NAND;
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009226 status = evaltree(
Eric Andersenc470f442003-07-28 09:56:35 +00009227 n->nbinary.ch1,
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00009228 (flags | ((is_or >> 1) - 1)) & EV_TESTED
Eric Andersenc470f442003-07-28 09:56:35 +00009229 );
Denys Vlasenkobc1a0082016-10-02 15:31:33 +02009230 if ((!status) == is_or || evalskip)
Eric Andersenc470f442003-07-28 09:56:35 +00009231 break;
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009232 n = n->nbinary.ch2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009233 evaln:
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009234 evalfn = evaltree;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009235 calleval:
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009236 status = evalfn(n, flags);
9237 goto setstatus;
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00009238 }
Eric Andersenc470f442003-07-28 09:56:35 +00009239 case NIF:
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009240 status = evaltree(n->nif.test, EV_TESTED);
Eric Andersenc470f442003-07-28 09:56:35 +00009241 if (evalskip)
9242 break;
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009243 if (!status) {
Eric Andersenc470f442003-07-28 09:56:35 +00009244 n = n->nif.ifpart;
9245 goto evaln;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00009246 }
9247 if (n->nif.elsepart) {
Eric Andersenc470f442003-07-28 09:56:35 +00009248 n = n->nif.elsepart;
9249 goto evaln;
9250 }
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009251 status = 0;
9252 goto setstatus;
Eric Andersenc470f442003-07-28 09:56:35 +00009253 case NDEFUN:
Denys Vlasenko7aec8682016-10-25 20:26:02 +02009254 defun(n);
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009255 /* Not necessary. To test it:
9256 * "false; f() { qwerty; }; echo $?" should print 0.
9257 */
9258 /* status = 0; */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009259 setstatus:
Eric Andersenc470f442003-07-28 09:56:35 +00009260 exitstatus = status;
9261 break;
9262 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009263 out:
Denys Vlasenkob563f622010-09-25 17:15:13 +02009264 /* Order of checks below is important:
9265 * signal handlers trigger before exit caused by "set -e".
9266 */
Denys Vlasenkob98b4c12016-10-01 23:25:12 +02009267 dotrap();
9268
9269 if (checkexit & status)
Denys Vlasenkof977e002020-02-20 16:54:29 +01009270 raise_exception(EXEND);
Denys Vlasenkob98b4c12016-10-01 23:25:12 +02009271 if (flags & EV_EXIT)
Denys Vlasenkof977e002020-02-20 16:54:29 +01009272 raise_exception(EXEND);
Denis Vlasenko653d8e72009-03-19 21:59:35 +00009273
Ron Yorstonf55161a2019-02-25 08:29:38 +00009274 popstackmark(&smark);
Denis Vlasenko653d8e72009-03-19 21:59:35 +00009275 TRACE(("leaving evaltree (no interrupts)\n"));
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009276 return exitstatus;
Eric Andersen62483552001-07-10 06:09:16 +00009277}
9278
Denys Vlasenko37dc08b2016-10-02 04:38:07 +02009279static int
9280skiploop(void)
Denys Vlasenko35ec8182016-10-01 19:56:52 +02009281{
9282 int skip = evalskip;
9283
9284 switch (skip) {
9285 case 0:
9286 break;
9287 case SKIPBREAK:
9288 case SKIPCONT:
9289 if (--skipcount <= 0) {
9290 evalskip = 0;
9291 break;
9292 }
9293 skip = SKIPBREAK;
9294 break;
9295 }
9296 return skip;
9297}
9298
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009299static int
Eric Andersenc470f442003-07-28 09:56:35 +00009300evalloop(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00009301{
Denys Vlasenko35ec8182016-10-01 19:56:52 +02009302 int skip;
Eric Andersencb57d552001-06-28 07:25:16 +00009303 int status;
9304
9305 loopnest++;
9306 status = 0;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009307 flags &= EV_TESTED;
Denys Vlasenko35ec8182016-10-01 19:56:52 +02009308 do {
Eric Andersenc470f442003-07-28 09:56:35 +00009309 int i;
9310
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009311 i = evaltree(n->nbinary.ch1, EV_TESTED);
Denys Vlasenko35ec8182016-10-01 19:56:52 +02009312 skip = skiploop();
9313 if (skip == SKIPFUNC)
9314 status = i;
9315 if (skip)
9316 continue;
Eric Andersenc470f442003-07-28 09:56:35 +00009317 if (n->type != NWHILE)
9318 i = !i;
9319 if (i != 0)
9320 break;
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009321 status = evaltree(n->nbinary.ch2, flags);
Denys Vlasenko35ec8182016-10-01 19:56:52 +02009322 skip = skiploop();
9323 } while (!(skip & ~SKIPCONT));
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009324 loopnest--;
9325
9326 return status;
Eric Andersencb57d552001-06-28 07:25:16 +00009327}
9328
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009329static int
Eric Andersenc470f442003-07-28 09:56:35 +00009330evalfor(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00009331{
9332 struct arglist arglist;
9333 union node *argp;
9334 struct strlist *sp;
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009335 int status = 0;
Eric Andersencb57d552001-06-28 07:25:16 +00009336
Denys Vlasenko675d24a2018-01-27 22:02:05 +01009337 errlinno = lineno = n->ncase.linno;
9338 if (funcline)
9339 lineno -= funcline - 1;
9340
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00009341 arglist.list = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00009342 arglist.lastp = &arglist.list;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009343 for (argp = n->nfor.args; argp; argp = argp->narg.next) {
Ron Yorston549deab2015-05-18 09:57:51 +02009344 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
Eric Andersencb57d552001-06-28 07:25:16 +00009345 }
9346 *arglist.lastp = NULL;
9347
Eric Andersencb57d552001-06-28 07:25:16 +00009348 loopnest++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009349 flags &= EV_TESTED;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009350 for (sp = arglist.list; sp; sp = sp->next) {
Denys Vlasenko0a0acb52015-04-18 19:36:38 +02009351 setvar0(n->nfor.var, sp->text);
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009352 status = evaltree(n->nfor.body, flags);
Denys Vlasenko35ec8182016-10-01 19:56:52 +02009353 if (skiploop() & ~SKIPCONT)
Eric Andersencb57d552001-06-28 07:25:16 +00009354 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009355 }
9356 loopnest--;
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009357
9358 return status;
Eric Andersencb57d552001-06-28 07:25:16 +00009359}
9360
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009361static int
Eric Andersenc470f442003-07-28 09:56:35 +00009362evalcase(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00009363{
9364 union node *cp;
9365 union node *patp;
9366 struct arglist arglist;
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009367 int status = 0;
Eric Andersencb57d552001-06-28 07:25:16 +00009368
Denys Vlasenko675d24a2018-01-27 22:02:05 +01009369 errlinno = lineno = n->ncase.linno;
9370 if (funcline)
9371 lineno -= funcline - 1;
9372
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00009373 arglist.list = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00009374 arglist.lastp = &arglist.list;
Eric Andersencb57d552001-06-28 07:25:16 +00009375 expandarg(n->ncase.expr, &arglist, EXP_TILDE);
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009376 for (cp = n->ncase.cases; cp && evalskip == 0; cp = cp->nclist.next) {
9377 for (patp = cp->nclist.pattern; patp; patp = patp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00009378 if (casematch(patp, arglist.list->text)) {
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009379 /* Ensure body is non-empty as otherwise
9380 * EV_EXIT may prevent us from setting the
9381 * exit status.
9382 */
9383 if (evalskip == 0 && cp->nclist.body) {
9384 status = evaltree(cp->nclist.body, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00009385 }
9386 goto out;
9387 }
9388 }
9389 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009390 out:
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009391 return status;
Eric Andersencb57d552001-06-28 07:25:16 +00009392}
9393
Eric Andersenc470f442003-07-28 09:56:35 +00009394/*
9395 * Kick off a subshell to evaluate a tree.
9396 */
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009397static int
Eric Andersenc470f442003-07-28 09:56:35 +00009398evalsubshell(union node *n, int flags)
9399{
9400 struct job *jp;
Denys Vlasenko098b7132017-01-11 19:59:03 +01009401 int backgnd = (n->type == NBACKGND); /* FORK_BG(1) if yes, else FORK_FG(0) */
Eric Andersenc470f442003-07-28 09:56:35 +00009402 int status;
9403
Denys Vlasenko675d24a2018-01-27 22:02:05 +01009404 errlinno = lineno = n->nredir.linno;
9405 if (funcline)
9406 lineno -= funcline - 1;
9407
Eric Andersenc470f442003-07-28 09:56:35 +00009408 expredir(n->nredir.redirect);
Denys Vlasenko238bf182010-05-18 15:49:07 +02009409 if (!backgnd && (flags & EV_EXIT) && !may_have_traps)
Eric Andersenc470f442003-07-28 09:56:35 +00009410 goto nofork;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009411 INT_OFF;
Denys Vlasenko098b7132017-01-11 19:59:03 +01009412 if (backgnd == FORK_FG)
9413 get_tty_state();
Denis Vlasenko68404f12008-03-17 09:00:54 +00009414 jp = makejob(/*n,*/ 1);
Eric Andersenc470f442003-07-28 09:56:35 +00009415 if (forkshell(jp, n, backgnd) == 0) {
Denys Vlasenko238bf182010-05-18 15:49:07 +02009416 /* child */
Denis Vlasenkob012b102007-02-19 22:43:01 +00009417 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00009418 flags |= EV_EXIT;
9419 if (backgnd)
Denys Vlasenko238bf182010-05-18 15:49:07 +02009420 flags &= ~EV_TESTED;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00009421 nofork:
Eric Andersenc470f442003-07-28 09:56:35 +00009422 redirect(n->nredir.redirect, 0);
9423 evaltreenr(n->nredir.n, flags);
9424 /* never returns */
9425 }
Denys Vlasenko70392332016-10-27 02:31:55 +02009426 /* parent */
Eric Andersenc470f442003-07-28 09:56:35 +00009427 status = 0;
Denys Vlasenko098b7132017-01-11 19:59:03 +01009428 if (backgnd == FORK_FG)
Eric Andersenc470f442003-07-28 09:56:35 +00009429 status = waitforjob(jp);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009430 INT_ON;
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009431 return status;
Eric Andersenc470f442003-07-28 09:56:35 +00009432}
9433
Eric Andersenc470f442003-07-28 09:56:35 +00009434/*
9435 * Compute the names of the files in a redirection list.
9436 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009437static void fixredir(union node *, const char *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00009438static void
9439expredir(union node *n)
9440{
9441 union node *redir;
9442
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009443 for (redir = n; redir; redir = redir->nfile.next) {
Eric Andersenc470f442003-07-28 09:56:35 +00009444 struct arglist fn;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009445
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00009446 fn.list = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00009447 fn.lastp = &fn.list;
9448 switch (redir->type) {
9449 case NFROMTO:
9450 case NFROM:
9451 case NTO:
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01009452#if BASH_REDIR_OUTPUT
Denis Vlasenko559691a2008-10-05 18:39:31 +00009453 case NTO2:
9454#endif
Eric Andersenc470f442003-07-28 09:56:35 +00009455 case NCLOBBER:
9456 case NAPPEND:
9457 expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
Denys Vlasenkof451b2c2012-06-09 02:06:57 +02009458 TRACE(("expredir expanded to '%s'\n", fn.list->text));
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01009459#if BASH_REDIR_OUTPUT
Denis Vlasenko559691a2008-10-05 18:39:31 +00009460 store_expfname:
9461#endif
Denys Vlasenko7c4b13e2013-01-17 13:02:27 +01009462#if 0
9463// By the design of stack allocator, the loop of this kind:
9464// while true; do while true; do break; done </dev/null; done
9465// will look like a memory leak: ash plans to free expfname's
9466// of "/dev/null" as soon as it finishes running the loop
9467// (in this case, never).
9468// This "fix" is wrong:
Jon Tollefson4ba6c5d2012-11-13 19:26:53 +01009469 if (redir->nfile.expfname)
9470 stunalloc(redir->nfile.expfname);
Denys Vlasenko7c4b13e2013-01-17 13:02:27 +01009471// It results in corrupted state of stacked allocations.
9472#endif
Eric Andersenc470f442003-07-28 09:56:35 +00009473 redir->nfile.expfname = fn.list->text;
9474 break;
9475 case NFROMFD:
Denis Vlasenko559691a2008-10-05 18:39:31 +00009476 case NTOFD: /* >& */
Eric Andersenc470f442003-07-28 09:56:35 +00009477 if (redir->ndup.vname) {
Denys Vlasenkoe368d852020-02-16 19:02:22 +01009478 expandarg(redir->ndup.vname, &fn, EXP_TILDE | EXP_REDIR);
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009479 if (fn.list == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +00009480 ash_msg_and_raise_error("redir error");
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01009481#if BASH_REDIR_OUTPUT
Denis Vlasenko559691a2008-10-05 18:39:31 +00009482 if (!isdigit_str9(fn.list->text)) {
9483 /* >&file, not >&fd */
9484 if (redir->nfile.fd != 1) /* 123>&file - BAD */
9485 ash_msg_and_raise_error("redir error");
9486 redir->type = NTO2;
9487 goto store_expfname;
9488 }
9489#endif
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009490 fixredir(redir, fn.list->text, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00009491 }
9492 break;
9493 }
9494 }
9495}
9496
Eric Andersencb57d552001-06-28 07:25:16 +00009497/*
Eric Andersencb57d552001-06-28 07:25:16 +00009498 * Evaluate a pipeline. All the processes in the pipeline are children
9499 * of the process creating the pipeline. (This differs from some versions
9500 * of the shell, which make the last process in a pipeline the parent
9501 * of all the rest.)
9502 */
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009503static int
Eric Andersenc470f442003-07-28 09:56:35 +00009504evalpipe(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00009505{
9506 struct job *jp;
9507 struct nodelist *lp;
9508 int pipelen;
9509 int prevfd;
9510 int pip[2];
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009511 int status = 0;
Eric Andersencb57d552001-06-28 07:25:16 +00009512
Eric Andersenc470f442003-07-28 09:56:35 +00009513 TRACE(("evalpipe(0x%lx) called\n", (long)n));
Eric Andersencb57d552001-06-28 07:25:16 +00009514 pipelen = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009515 for (lp = n->npipe.cmdlist; lp; lp = lp->next)
Eric Andersencb57d552001-06-28 07:25:16 +00009516 pipelen++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009517 flags |= EV_EXIT;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009518 INT_OFF;
Denys Vlasenko098b7132017-01-11 19:59:03 +01009519 if (n->npipe.pipe_backgnd == 0)
9520 get_tty_state();
Denis Vlasenko68404f12008-03-17 09:00:54 +00009521 jp = makejob(/*n,*/ pipelen);
Eric Andersencb57d552001-06-28 07:25:16 +00009522 prevfd = -1;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009523 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009524 prehash(lp->n);
Eric Andersencb57d552001-06-28 07:25:16 +00009525 pip[1] = -1;
9526 if (lp->next) {
9527 if (pipe(pip) < 0) {
9528 close(prevfd);
Denys Vlasenko12ffefb2017-08-23 14:52:56 +02009529 ash_msg_and_raise_perror("can't create pipe");
Eric Andersencb57d552001-06-28 07:25:16 +00009530 }
9531 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00009532 if (forkshell(jp, lp->n, n->npipe.pipe_backgnd) == 0) {
Denys Vlasenko70392332016-10-27 02:31:55 +02009533 /* child */
Denis Vlasenkob012b102007-02-19 22:43:01 +00009534 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009535 if (pip[1] >= 0) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009536 close(pip[0]);
Eric Andersencb57d552001-06-28 07:25:16 +00009537 }
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009538 if (prevfd > 0) {
9539 dup2(prevfd, 0);
9540 close(prevfd);
9541 }
9542 if (pip[1] > 1) {
9543 dup2(pip[1], 1);
9544 close(pip[1]);
9545 }
Eric Andersenc470f442003-07-28 09:56:35 +00009546 evaltreenr(lp->n, flags);
9547 /* never returns */
Eric Andersencb57d552001-06-28 07:25:16 +00009548 }
Denys Vlasenko70392332016-10-27 02:31:55 +02009549 /* parent */
Eric Andersencb57d552001-06-28 07:25:16 +00009550 if (prevfd >= 0)
9551 close(prevfd);
9552 prevfd = pip[0];
Denis Vlasenkob9e70dd2009-03-20 01:24:08 +00009553 /* Don't want to trigger debugging */
9554 if (pip[1] != -1)
9555 close(pip[1]);
Eric Andersencb57d552001-06-28 07:25:16 +00009556 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00009557 if (n->npipe.pipe_backgnd == 0) {
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009558 status = waitforjob(jp);
9559 TRACE(("evalpipe: job done exit status %d\n", status));
Eric Andersencb57d552001-06-28 07:25:16 +00009560 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00009561 INT_ON;
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009562
9563 return status;
Eric Andersencb57d552001-06-28 07:25:16 +00009564}
9565
Ron Yorston9e2a5662020-01-21 16:01:58 +00009566/* setinteractive needs this forward reference */
9567#if EDITING_HAS_get_exe_name
9568static const char *get_builtin_name(int i) FAST_FUNC;
9569#endif
9570
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009571/*
9572 * Controls whether the shell is interactive or not.
9573 */
9574static void
9575setinteractive(int on)
9576{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009577 static smallint is_interactive;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009578
9579 if (++on == is_interactive)
9580 return;
9581 is_interactive = on;
9582 setsignal(SIGINT);
9583 setsignal(SIGQUIT);
9584 setsignal(SIGTERM);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009585 if (is_interactive > 1) {
Denys Vlasenko897475a2019-06-01 16:35:09 +02009586#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009587 /* Looks like they want an interactive shell */
Denis Vlasenkoca525b42007-06-13 12:27:17 +00009588 static smallint did_banner;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009589
Denis Vlasenkoca525b42007-06-13 12:27:17 +00009590 if (!did_banner) {
Denys Vlasenkoc34c0332009-09-29 12:25:30 +02009591 /* note: ash and hush share this string */
9592 out1fmt("\n\n%s %s\n"
Denys Vlasenko2ec34962014-09-08 16:52:39 +02009593 IF_ASH_HELP("Enter 'help' for a list of built-in commands.\n")
9594 "\n",
Denys Vlasenkoc34c0332009-09-29 12:25:30 +02009595 bb_banner,
9596 "built-in shell (ash)"
9597 );
Denis Vlasenkoca525b42007-06-13 12:27:17 +00009598 did_banner = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009599 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009600#endif
Denys Vlasenko897475a2019-06-01 16:35:09 +02009601#if ENABLE_FEATURE_EDITING
Ron Yorston9e2a5662020-01-21 16:01:58 +00009602 if (!line_input_state) {
Denys Vlasenko897475a2019-06-01 16:35:09 +02009603 line_input_state = new_line_input_t(FOR_SHELL | WITH_PATH_LOOKUP);
Ron Yorston9e2a5662020-01-21 16:01:58 +00009604# if EDITING_HAS_get_exe_name
9605 line_input_state->get_exe_name = get_builtin_name;
9606# endif
9607 }
Denys Vlasenko897475a2019-06-01 16:35:09 +02009608#endif
9609 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009610}
9611
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009612static void
9613optschanged(void)
9614{
9615#if DEBUG
9616 opentrace();
9617#endif
9618 setinteractive(iflag);
9619 setjobctl(mflag);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009620#if ENABLE_FEATURE_EDITING_VI
Denys Vlasenko897475a2019-06-01 16:35:09 +02009621 if (line_input_state) {
9622 if (viflag)
9623 line_input_state->flags |= VI_MODE;
9624 else
9625 line_input_state->flags &= ~VI_MODE;
9626 }
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009627#else
9628 viflag = 0; /* forcibly keep the option off */
9629#endif
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009630}
9631
Denys Vlasenkob8ab27b2017-07-26 19:22:34 +02009632struct localvar_list {
9633 struct localvar_list *next;
9634 struct localvar *lv;
9635};
9636
9637static struct localvar_list *localvar_stack;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009638
9639/*
9640 * Called after a function returns.
9641 * Interrupts must be off.
9642 */
9643static void
Denys Vlasenko981a0562017-07-26 19:53:11 +02009644poplocalvars(int keep)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009645{
Denys Vlasenkob8ab27b2017-07-26 19:22:34 +02009646 struct localvar_list *ll;
9647 struct localvar *lvp, *next;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009648 struct var *vp;
9649
Denys Vlasenkob8ab27b2017-07-26 19:22:34 +02009650 INT_OFF;
9651 ll = localvar_stack;
9652 localvar_stack = ll->next;
9653
9654 next = ll->lv;
9655 free(ll);
9656
9657 while ((lvp = next) != NULL) {
9658 next = lvp->next;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009659 vp = lvp->vp;
Denys Vlasenkob563f622010-09-25 17:15:13 +02009660 TRACE(("poplocalvar %s\n", vp ? vp->var_text : "-"));
Denys Vlasenko981a0562017-07-26 19:53:11 +02009661 if (keep) {
9662 int bits = VSTRFIXED;
9663
9664 if (lvp->flags != VUNSET) {
9665 if (vp->var_text == lvp->text)
9666 bits |= VTEXTFIXED;
9667 else if (!(lvp->flags & (VTEXTFIXED|VSTACK)))
9668 free((char*)lvp->text);
9669 }
9670
9671 vp->flags &= ~bits;
9672 vp->flags |= (lvp->flags & bits);
9673
9674 if ((vp->flags &
9675 (VEXPORT|VREADONLY|VSTRFIXED|VUNSET)) == VUNSET)
9676 unsetvar(vp->var_text);
9677 } else if (vp == NULL) { /* $- saved */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009678 memcpy(optlist, lvp->text, sizeof(optlist));
9679 free((char*)lvp->text);
9680 optschanged();
Denys Vlasenkod5b500c2017-07-26 19:25:40 +02009681 } else if (lvp->flags == VUNSET) {
9682 vp->flags &= ~(VSTRFIXED|VREADONLY);
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02009683 unsetvar(vp->var_text);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009684 } else {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02009685 if (vp->var_func)
9686 vp->var_func(var_end(lvp->text));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009687 if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02009688 free((char*)vp->var_text);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009689 vp->flags = lvp->flags;
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02009690 vp->var_text = lvp->text;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009691 }
9692 free(lvp);
9693 }
Denys Vlasenkob8ab27b2017-07-26 19:22:34 +02009694 INT_ON;
9695}
9696
9697/*
9698 * Create a new localvar environment.
9699 */
Denys Vlasenko484fc202017-07-26 19:55:31 +02009700static struct localvar_list *
Denys Vlasenkoe2dd2af2020-02-20 10:33:38 +01009701pushlocalvars(int push)
Denys Vlasenkob8ab27b2017-07-26 19:22:34 +02009702{
9703 struct localvar_list *ll;
Denys Vlasenkoe2dd2af2020-02-20 10:33:38 +01009704 struct localvar_list *top;
9705
9706 top = localvar_stack;
9707 if (!push)
9708 goto out;
Denys Vlasenkob8ab27b2017-07-26 19:22:34 +02009709
9710 INT_OFF;
9711 ll = ckzalloc(sizeof(*ll));
9712 /*ll->lv = NULL; - zalloc did it */
Denys Vlasenkoe2dd2af2020-02-20 10:33:38 +01009713 ll->next = top;
Denys Vlasenkob8ab27b2017-07-26 19:22:34 +02009714 localvar_stack = ll;
9715 INT_ON;
Denys Vlasenkoe2dd2af2020-02-20 10:33:38 +01009716 out:
9717 return top;
Denys Vlasenko484fc202017-07-26 19:55:31 +02009718}
9719
9720static void
9721unwindlocalvars(struct localvar_list *stop)
9722{
9723 while (localvar_stack != stop)
9724 poplocalvars(0);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009725}
9726
9727static int
9728evalfun(struct funcnode *func, int argc, char **argv, int flags)
9729{
9730 volatile struct shparam saveparam;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009731 struct jmploc *volatile savehandler;
9732 struct jmploc jmploc;
9733 int e;
Denys Vlasenko675d24a2018-01-27 22:02:05 +01009734 int savefuncline;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009735
9736 saveparam = shellparam;
Denys Vlasenko675d24a2018-01-27 22:02:05 +01009737 savefuncline = funcline;
Denys Vlasenkoa2d121c2016-09-30 11:30:11 +02009738 savehandler = exception_handler;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009739 e = setjmp(jmploc.loc);
9740 if (e) {
9741 goto funcdone;
9742 }
9743 INT_OFF;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009744 exception_handler = &jmploc;
Denis Vlasenko01631112007-12-16 17:20:38 +00009745 shellparam.malloced = 0;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009746 func->count++;
Denys Vlasenko675d24a2018-01-27 22:02:05 +01009747 funcline = func->n.ndefun.linno;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009748 INT_ON;
9749 shellparam.nparam = argc - 1;
9750 shellparam.p = argv + 1;
9751#if ENABLE_ASH_GETOPTS
9752 shellparam.optind = 1;
9753 shellparam.optoff = -1;
9754#endif
Denys Vlasenko675d24a2018-01-27 22:02:05 +01009755 evaltree(func->n.ndefun.body, flags & EV_TESTED);
Denis Vlasenko01631112007-12-16 17:20:38 +00009756 funcdone:
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009757 INT_OFF;
Denys Vlasenko675d24a2018-01-27 22:02:05 +01009758 funcline = savefuncline;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009759 freefunc(func);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009760 freeparam(&shellparam);
9761 shellparam = saveparam;
9762 exception_handler = savehandler;
9763 INT_ON;
Denys Vlasenkocd24a502020-02-20 16:47:01 +01009764 evalskip &= ~(SKIPFUNC | SKIPFUNCDEF);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009765 return e;
9766}
9767
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009768/*
9769 * Make a variable a local variable. When a variable is made local, it's
9770 * value and flags are saved in a localvar structure. The saved values
9771 * will be restored when the shell function returns. We handle the name
Denys Vlasenkoe0a4e102015-05-13 02:20:14 +02009772 * "-" as a special case: it makes changes to "set +-options" local
9773 * (options will be restored on return from the function).
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009774 */
9775static void
Denys Vlasenko3e729102020-02-19 17:33:44 +01009776mklocal(char *name, int flags)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009777{
9778 struct localvar *lvp;
9779 struct var **vpp;
9780 struct var *vp;
Denys Vlasenko0a0acb52015-04-18 19:36:38 +02009781 char *eq = strchr(name, '=');
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009782
9783 INT_OFF;
Denys Vlasenko0a0acb52015-04-18 19:36:38 +02009784 /* Cater for duplicate "local". Examples:
9785 * x=0; f() { local x=1; echo $x; local x; echo $x; }; f; echo $x
9786 * x=0; f() { local x=1; echo $x; local x=2; echo $x; }; f; echo $x
9787 */
Denys Vlasenkob8ab27b2017-07-26 19:22:34 +02009788 lvp = localvar_stack->lv;
Denys Vlasenko0a0acb52015-04-18 19:36:38 +02009789 while (lvp) {
Eugene Rudoy1285aa62015-04-26 23:32:00 +02009790 if (lvp->vp && varcmp(lvp->vp->var_text, name) == 0) {
Denys Vlasenko0a0acb52015-04-18 19:36:38 +02009791 if (eq)
9792 setvareq(name, 0);
9793 /* else:
9794 * it's a duplicate "local VAR" declaration, do nothing
9795 */
Denys Vlasenko06b11492016-11-04 16:43:18 +01009796 goto ret;
Denys Vlasenko0a0acb52015-04-18 19:36:38 +02009797 }
9798 lvp = lvp->next;
9799 }
9800
9801 lvp = ckzalloc(sizeof(*lvp));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009802 if (LONE_DASH(name)) {
9803 char *p;
9804 p = ckmalloc(sizeof(optlist));
9805 lvp->text = memcpy(p, optlist, sizeof(optlist));
9806 vp = NULL;
9807 } else {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009808 vpp = hashvar(name);
9809 vp = *findvar(vpp, name);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009810 if (vp == NULL) {
Denys Vlasenko0a0acb52015-04-18 19:36:38 +02009811 /* variable did not exist yet */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009812 if (eq)
Denys Vlasenko3e729102020-02-19 17:33:44 +01009813 vp = setvareq(name, VSTRFIXED | flags);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009814 else
Denys Vlasenko3e729102020-02-19 17:33:44 +01009815 vp = setvar(name, NULL, VSTRFIXED | flags);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009816 lvp->flags = VUNSET;
9817 } else {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02009818 lvp->text = vp->var_text;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009819 lvp->flags = vp->flags;
Denys Vlasenko0a0acb52015-04-18 19:36:38 +02009820 /* make sure neither "struct var" nor string gets freed
9821 * during (un)setting:
9822 */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009823 vp->flags |= VSTRFIXED|VTEXTFIXED;
9824 if (eq)
Denys Vlasenko3e729102020-02-19 17:33:44 +01009825 setvareq(name, flags);
Denys Vlasenko109ee5d2014-03-16 18:41:11 +01009826 else
9827 /* "local VAR" unsets VAR: */
Denys Vlasenko0a0acb52015-04-18 19:36:38 +02009828 setvar0(name, NULL);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009829 }
9830 }
9831 lvp->vp = vp;
Denys Vlasenkob8ab27b2017-07-26 19:22:34 +02009832 lvp->next = localvar_stack->lv;
9833 localvar_stack->lv = lvp;
Denys Vlasenko06b11492016-11-04 16:43:18 +01009834 ret:
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009835 INT_ON;
9836}
9837
9838/*
9839 * The "local" command.
9840 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009841static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009842localcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009843{
9844 char *name;
9845
Denys Vlasenkob8ab27b2017-07-26 19:22:34 +02009846 if (!localvar_stack)
Ron Yorstonef2386b2015-10-29 16:19:14 +00009847 ash_msg_and_raise_error("not in a function");
9848
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009849 argv = argptr;
9850 while ((name = *argv++) != NULL) {
Denys Vlasenko3e729102020-02-19 17:33:44 +01009851 mklocal(name, 0);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009852 }
9853 return 0;
9854}
9855
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009856static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009857falsecmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00009858{
9859 return 1;
9860}
9861
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009862static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009863truecmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00009864{
9865 return 0;
9866}
9867
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009868static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009869execcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00009870{
Denys Vlasenko6c149f42017-04-12 21:31:32 +02009871 optionarg = NULL;
9872 while (nextopt("a:") != '\0')
9873 /* nextopt() sets optionarg to "-a ARGV0" */;
9874
9875 argv = argptr;
9876 if (argv[0]) {
9877 char *prog;
9878
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00009879 iflag = 0; /* exit on error */
9880 mflag = 0;
9881 optschanged();
Denys Vlasenkoe5814a52016-07-16 18:33:55 +02009882 /* We should set up signals for "exec CMD"
9883 * the same way as for "CMD" without "exec".
9884 * But optschanged->setinteractive->setsignal
9885 * still thought we are a root shell. Therefore, for example,
9886 * SIGQUIT is still set to IGN. Fix it:
9887 */
9888 shlvl++;
9889 setsignal(SIGQUIT);
9890 /*setsignal(SIGTERM); - unnecessary because of iflag=0 */
9891 /*setsignal(SIGTSTP); - unnecessary because of mflag=0 */
9892 /*setsignal(SIGTTOU); - unnecessary because of mflag=0 */
9893
Denys Vlasenko6c149f42017-04-12 21:31:32 +02009894 prog = argv[0];
9895 if (optionarg)
9896 argv[0] = optionarg;
9897 shellexec(prog, argv, pathval(), 0);
Denys Vlasenkoe5814a52016-07-16 18:33:55 +02009898 /* NOTREACHED */
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00009899 }
9900 return 0;
9901}
9902
9903/*
9904 * The return command.
9905 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009906static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00009907returncmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00009908{
Denys Vlasenkocd24a502020-02-20 16:47:01 +01009909 int skip;
9910 int status;
9911
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00009912 /*
9913 * If called outside a function, do what ksh does;
9914 * skip the rest of the file.
9915 */
Denys Vlasenkocd24a502020-02-20 16:47:01 +01009916 if (argv[1]) {
9917 skip = SKIPFUNC;
9918 status = number(argv[1]);
9919 } else {
9920 skip = SKIPFUNCDEF;
9921 status = exitstatus;
9922 }
9923 evalskip = skip;
9924
9925 return status;
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +00009926}
9927
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009928/* Forward declarations for builtintab[] */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009929static int breakcmd(int, char **) FAST_FUNC;
9930static int dotcmd(int, char **) FAST_FUNC;
Denys Vlasenko7b3fa1e2016-10-01 15:10:16 +02009931static int evalcmd(int, char **, int) FAST_FUNC;
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009932static int exitcmd(int, char **) FAST_FUNC;
9933static int exportcmd(int, char **) FAST_FUNC;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009934#if ENABLE_ASH_GETOPTS
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009935static int getoptscmd(int, char **) FAST_FUNC;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009936#endif
Denys Vlasenko2ec34962014-09-08 16:52:39 +02009937#if ENABLE_ASH_HELP
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009938static int helpcmd(int, char **) FAST_FUNC;
Denis Vlasenko52764022007-02-24 13:42:56 +00009939#endif
Flemming Madsend96ffda2013-04-07 18:47:24 +02009940#if MAX_HISTORY
9941static int historycmd(int, char **) FAST_FUNC;
9942#endif
Denys Vlasenko0b883582016-12-23 16:49:07 +01009943#if ENABLE_FEATURE_SH_MATH
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009944static int letcmd(int, char **) FAST_FUNC;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009945#endif
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009946static int readcmd(int, char **) FAST_FUNC;
9947static int setcmd(int, char **) FAST_FUNC;
9948static int shiftcmd(int, char **) FAST_FUNC;
9949static int timescmd(int, char **) FAST_FUNC;
9950static int trapcmd(int, char **) FAST_FUNC;
9951static int umaskcmd(int, char **) FAST_FUNC;
9952static int unsetcmd(int, char **) FAST_FUNC;
9953static int ulimitcmd(int, char **) FAST_FUNC;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00009954
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009955#define BUILTIN_NOSPEC "0"
9956#define BUILTIN_SPECIAL "1"
9957#define BUILTIN_REGULAR "2"
9958#define BUILTIN_SPEC_REG "3"
9959#define BUILTIN_ASSIGN "4"
9960#define BUILTIN_SPEC_ASSG "5"
9961#define BUILTIN_REG_ASSG "6"
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009962#define BUILTIN_SPEC_REG_ASSG "7"
9963
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009964/* Stubs for calling non-FAST_FUNC's */
Denys Vlasenko265062d2017-01-10 15:13:30 +01009965#if ENABLE_ASH_ECHO
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009966static int FAST_FUNC echocmd(int argc, char **argv) { return echo_main(argc, argv); }
Denys Vlasenko2634bf32009-06-09 18:40:07 +02009967#endif
Denys Vlasenko265062d2017-01-10 15:13:30 +01009968#if ENABLE_ASH_PRINTF
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009969static int FAST_FUNC printfcmd(int argc, char **argv) { return printf_main(argc, argv); }
Denys Vlasenko2634bf32009-06-09 18:40:07 +02009970#endif
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01009971#if ENABLE_ASH_TEST || BASH_TEST2
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02009972static int FAST_FUNC testcmd(int argc, char **argv) { return test_main(argc, argv); }
Denys Vlasenko2634bf32009-06-09 18:40:07 +02009973#endif
Denis Vlasenko468aea22008-04-01 14:47:57 +00009974
Denis Vlasenkof7d56652008-03-25 05:51:41 +00009975/* Keep these in proper order since it is searched via bsearch() */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009976static const struct builtincmd builtintab[] = {
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009977 { BUILTIN_SPEC_REG "." , dotcmd },
9978 { BUILTIN_SPEC_REG ":" , truecmd },
Denys Vlasenko265062d2017-01-10 15:13:30 +01009979#if ENABLE_ASH_TEST
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009980 { BUILTIN_REGULAR "[" , testcmd },
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01009981#endif
9982#if BASH_TEST2
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009983 { BUILTIN_REGULAR "[[" , testcmd },
Denis Vlasenko80591b02008-03-25 07:49:43 +00009984#endif
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009985#if ENABLE_ASH_ALIAS
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009986 { BUILTIN_REG_ASSG "alias" , aliascmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009987#endif
9988#if JOBS
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009989 { BUILTIN_REGULAR "bg" , fg_bgcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009990#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009991 { BUILTIN_SPEC_REG "break" , breakcmd },
9992 { BUILTIN_REGULAR "cd" , cdcmd },
9993 { BUILTIN_NOSPEC "chdir" , cdcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009994#if ENABLE_ASH_CMDCMD
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009995 { BUILTIN_REGULAR "command" , commandcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +00009996#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009997 { BUILTIN_SPEC_REG "continue", breakcmd },
Denys Vlasenko265062d2017-01-10 15:13:30 +01009998#if ENABLE_ASH_ECHO
Denys Vlasenko023a08f2010-03-26 15:53:33 +01009999 { BUILTIN_REGULAR "echo" , echocmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +000010000#endif
Denys Vlasenko7b3fa1e2016-10-01 15:10:16 +020010001 { BUILTIN_SPEC_REG "eval" , NULL }, /*evalcmd() has a differing prototype*/
Denys Vlasenko023a08f2010-03-26 15:53:33 +010010002 { BUILTIN_SPEC_REG "exec" , execcmd },
10003 { BUILTIN_SPEC_REG "exit" , exitcmd },
10004 { BUILTIN_SPEC_REG_ASSG "export" , exportcmd },
10005 { BUILTIN_REGULAR "false" , falsecmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +000010006#if JOBS
Denys Vlasenko023a08f2010-03-26 15:53:33 +010010007 { BUILTIN_REGULAR "fg" , fg_bgcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +000010008#endif
10009#if ENABLE_ASH_GETOPTS
Denys Vlasenko023a08f2010-03-26 15:53:33 +010010010 { BUILTIN_REGULAR "getopts" , getoptscmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +000010011#endif
Denys Vlasenkoa7b97e32020-02-16 18:29:52 +010010012 { BUILTIN_REGULAR "hash" , hashcmd },
Denys Vlasenko2ec34962014-09-08 16:52:39 +020010013#if ENABLE_ASH_HELP
Denys Vlasenko023a08f2010-03-26 15:53:33 +010010014 { BUILTIN_NOSPEC "help" , helpcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +000010015#endif
Flemming Madsend96ffda2013-04-07 18:47:24 +020010016#if MAX_HISTORY
10017 { BUILTIN_NOSPEC "history" , historycmd },
10018#endif
Denis Vlasenko4fe15f32007-02-23 01:05:26 +000010019#if JOBS
Denys Vlasenko023a08f2010-03-26 15:53:33 +010010020 { BUILTIN_REGULAR "jobs" , jobscmd },
10021 { BUILTIN_REGULAR "kill" , killcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +000010022#endif
Denys Vlasenko0b883582016-12-23 16:49:07 +010010023#if ENABLE_FEATURE_SH_MATH
Denys Vlasenko023a08f2010-03-26 15:53:33 +010010024 { BUILTIN_NOSPEC "let" , letcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +000010025#endif
Denys Vlasenko85241c72017-07-26 20:00:08 +020010026 { BUILTIN_SPEC_REG_ASSG "local" , localcmd },
Denys Vlasenko265062d2017-01-10 15:13:30 +010010027#if ENABLE_ASH_PRINTF
Denys Vlasenko023a08f2010-03-26 15:53:33 +010010028 { BUILTIN_REGULAR "printf" , printfcmd },
Denis Vlasenkocd2663f2008-06-01 22:36:39 +000010029#endif
Denys Vlasenkoa7b97e32020-02-16 18:29:52 +010010030 { BUILTIN_REGULAR "pwd" , pwdcmd },
Denys Vlasenko023a08f2010-03-26 15:53:33 +010010031 { BUILTIN_REGULAR "read" , readcmd },
10032 { BUILTIN_SPEC_REG_ASSG "readonly", exportcmd },
10033 { BUILTIN_SPEC_REG "return" , returncmd },
10034 { BUILTIN_SPEC_REG "set" , setcmd },
10035 { BUILTIN_SPEC_REG "shift" , shiftcmd },
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010010036#if BASH_SOURCE
Denys Vlasenko023a08f2010-03-26 15:53:33 +010010037 { BUILTIN_SPEC_REG "source" , dotcmd },
Denys Vlasenko82731b42010-05-17 17:49:52 +020010038#endif
Denys Vlasenko265062d2017-01-10 15:13:30 +010010039#if ENABLE_ASH_TEST
Denys Vlasenko023a08f2010-03-26 15:53:33 +010010040 { BUILTIN_REGULAR "test" , testcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +000010041#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +010010042 { BUILTIN_SPEC_REG "times" , timescmd },
10043 { BUILTIN_SPEC_REG "trap" , trapcmd },
10044 { BUILTIN_REGULAR "true" , truecmd },
Denys Vlasenkoa7b97e32020-02-16 18:29:52 +010010045 { BUILTIN_REGULAR "type" , typecmd },
10046 { BUILTIN_REGULAR "ulimit" , ulimitcmd },
Denys Vlasenko023a08f2010-03-26 15:53:33 +010010047 { BUILTIN_REGULAR "umask" , umaskcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +000010048#if ENABLE_ASH_ALIAS
Denys Vlasenko023a08f2010-03-26 15:53:33 +010010049 { BUILTIN_REGULAR "unalias" , unaliascmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +000010050#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +010010051 { BUILTIN_SPEC_REG "unset" , unsetcmd },
10052 { BUILTIN_REGULAR "wait" , waitcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +000010053};
10054
Denis Vlasenko80591b02008-03-25 07:49:43 +000010055/* Should match the above table! */
10056#define COMMANDCMD (builtintab + \
Denys Vlasenko928e2a72016-09-29 00:30:31 +020010057 /* . : */ 2 + \
Denys Vlasenko265062d2017-01-10 15:13:30 +010010058 /* [ */ 1 * ENABLE_ASH_TEST + \
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010010059 /* [[ */ 1 * BASH_TEST2 + \
Denys Vlasenko928e2a72016-09-29 00:30:31 +020010060 /* alias */ 1 * ENABLE_ASH_ALIAS + \
10061 /* bg */ 1 * ENABLE_ASH_JOB_CONTROL + \
10062 /* break cd cddir */ 3)
10063#define EVALCMD (COMMANDCMD + \
10064 /* command */ 1 * ENABLE_ASH_CMDCMD + \
10065 /* continue */ 1 + \
Denys Vlasenko265062d2017-01-10 15:13:30 +010010066 /* echo */ 1 * ENABLE_ASH_ECHO + \
Denys Vlasenko928e2a72016-09-29 00:30:31 +020010067 0)
10068#define EXECCMD (EVALCMD + \
10069 /* eval */ 1)
Denis Vlasenko4fe15f32007-02-23 01:05:26 +000010070
10071/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000010072 * Search the table of builtin commands.
10073 */
Denys Vlasenko888527c2016-10-02 16:54:17 +020010074static int
10075pstrcmp1(const void *a, const void *b)
10076{
10077 return strcmp((char*)a, *(char**)b + 1);
10078}
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000010079static struct builtincmd *
10080find_builtin(const char *name)
10081{
10082 struct builtincmd *bp;
10083
10084 bp = bsearch(
Denis Vlasenko80b8b392007-06-25 10:55:35 +000010085 name, builtintab, ARRAY_SIZE(builtintab), sizeof(builtintab[0]),
Denys Vlasenko888527c2016-10-02 16:54:17 +020010086 pstrcmp1
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000010087 );
10088 return bp;
10089}
10090
Ron Yorston9e2a5662020-01-21 16:01:58 +000010091#if EDITING_HAS_get_exe_name
10092static const char * FAST_FUNC
10093get_builtin_name(int i)
10094{
10095 return /*i >= 0 &&*/ i < ARRAY_SIZE(builtintab) ? builtintab[i].name + 1 : NULL;
10096}
10097#endif
10098
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000010099/*
Denis Vlasenko4fe15f32007-02-23 01:05:26 +000010100 * Execute a simple command.
10101 */
Denys Vlasenko1c5eb882018-08-05 17:07:26 +020010102static void unwindfiles(struct parsefile *stop);
Denis Vlasenko4fe15f32007-02-23 01:05:26 +000010103static int
10104isassignment(const char *p)
Paul Foxc3850c82005-07-20 18:23:39 +000010105{
10106 const char *q = endofname(p);
10107 if (p == q)
10108 return 0;
10109 return *q == '=';
10110}
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010111static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000010112bltincmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko4fe15f32007-02-23 01:05:26 +000010113{
10114 /* Preserve exitstatus of a previous possible redirection
10115 * as POSIX mandates */
10116 return back_exitstatus;
10117}
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +020010118static int
Eric Andersenc470f442003-07-28 09:56:35 +000010119evalcommand(union node *cmd, int flags)
10120{
Denis Vlasenko0e6f6612008-02-15 15:02:15 +000010121 static const struct builtincmd null_bltin = {
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010010122 BUILTIN_REGULAR "", bltincmd
Denis Vlasenko4fe15f32007-02-23 01:05:26 +000010123 };
Denys Vlasenko484fc202017-07-26 19:55:31 +020010124 struct localvar_list *localvar_stop;
Denys Vlasenko1c5eb882018-08-05 17:07:26 +020010125 struct parsefile *file_stop;
Denys Vlasenko1c79aeb2017-07-29 22:51:52 +020010126 struct redirtab *redir_stop;
Eric Andersenc470f442003-07-28 09:56:35 +000010127 union node *argp;
10128 struct arglist arglist;
10129 struct arglist varlist;
10130 char **argv;
10131 int argc;
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010010132 struct strlist *osp;
Glenn L McGrath7b8765c2003-08-29 07:29:30 +000010133 const struct strlist *sp;
Eric Andersenc470f442003-07-28 09:56:35 +000010134 struct cmdentry cmdentry;
10135 struct job *jp;
10136 char *lastarg;
10137 const char *path;
10138 int spclbltin;
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010010139 int cmd_flag;
Eric Andersenc470f442003-07-28 09:56:35 +000010140 int status;
10141 char **nargv;
Denis Vlasenko34c73c42008-08-16 11:48:02 +000010142 smallint cmd_is_exec;
Denys Vlasenko3e729102020-02-19 17:33:44 +010010143 int vflags;
10144 int vlocal;
Eric Andersenc470f442003-07-28 09:56:35 +000010145
Denys Vlasenko675d24a2018-01-27 22:02:05 +010010146 errlinno = lineno = cmd->ncmd.linno;
10147 if (funcline)
10148 lineno -= funcline - 1;
10149
Eric Andersenc470f442003-07-28 09:56:35 +000010150 /* First expand the arguments. */
10151 TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags));
Denys Vlasenko1c5eb882018-08-05 17:07:26 +020010152 file_stop = g_parsefile;
Eric Andersenc470f442003-07-28 09:56:35 +000010153 back_exitstatus = 0;
10154
10155 cmdentry.cmdtype = CMDBUILTIN;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +000010156 cmdentry.u.cmd = &null_bltin;
Eric Andersenc470f442003-07-28 09:56:35 +000010157 varlist.lastp = &varlist.list;
10158 *varlist.lastp = NULL;
10159 arglist.lastp = &arglist.list;
10160 *arglist.lastp = NULL;
10161
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010010162 cmd_flag = 0;
10163 cmd_is_exec = 0;
10164 spclbltin = -1;
Denys Vlasenko3e729102020-02-19 17:33:44 +010010165 vflags = 0;
10166 vlocal = 0;
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010010167 path = NULL;
10168
Eric Andersenc470f442003-07-28 09:56:35 +000010169 argc = 0;
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010010170 argp = cmd->ncmd.args;
10171 osp = fill_arglist(&arglist, &argp);
10172 if (osp) {
10173 int pseudovarflag = 0;
Denys Vlasenkod07a15b2017-07-30 16:51:05 +020010174
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010010175 for (;;) {
10176 find_command(arglist.list->text, &cmdentry,
10177 cmd_flag | DO_REGBLTIN, pathval());
Paul Foxc3850c82005-07-20 18:23:39 +000010178
Denys Vlasenko3e729102020-02-19 17:33:44 +010010179 vlocal++;
10180
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010010181 /* implement bltin and command here */
10182 if (cmdentry.cmdtype != CMDBUILTIN)
10183 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010184
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010010185 pseudovarflag = IS_BUILTIN_ASSIGN(cmdentry.u.cmd);
10186 if (spclbltin < 0) {
10187 spclbltin = IS_BUILTIN_SPECIAL(cmdentry.u.cmd);
Denys Vlasenko3e729102020-02-19 17:33:44 +010010188 vlocal = !spclbltin;
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010010189 }
10190 cmd_is_exec = cmdentry.u.cmd == EXECCMD;
Ron Yorstonda7a6db2020-02-27 09:50:18 +000010191#if ENABLE_ASH_CMDCMD
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010010192 if (cmdentry.u.cmd != COMMANDCMD)
10193 break;
Paul Foxc3850c82005-07-20 18:23:39 +000010194
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010010195 cmd_flag = parse_command_args(&arglist, &argp, &path);
10196 if (!cmd_flag)
Ron Yorstonda7a6db2020-02-27 09:50:18 +000010197#endif
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010010198 break;
Denys Vlasenkod07a15b2017-07-30 16:51:05 +020010199 }
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010010200
10201 for (; argp; argp = argp->narg.next)
10202 expandarg(argp, &arglist,
10203 pseudovarflag &&
10204 isassignment(argp->narg.text) ?
10205 EXP_VARTILDE : EXP_FULL | EXP_TILDE);
10206
10207 for (sp = arglist.list; sp; sp = sp->next)
10208 argc++;
Denys Vlasenko3e729102020-02-19 17:33:44 +010010209
10210 if (cmd_is_exec && argc > 1)
10211 vflags = VEXPORT;
Eric Andersenc470f442003-07-28 09:56:35 +000010212 }
10213
Denys Vlasenkoe2dd2af2020-02-20 10:33:38 +010010214 localvar_stop = pushlocalvars(vlocal);
10215
Denys Vlasenko65a8b852016-10-26 22:29:11 +020010216 /* Reserve one extra spot at the front for shellexec. */
10217 nargv = stalloc(sizeof(char *) * (argc + 2));
10218 argv = ++nargv;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010219 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersenc470f442003-07-28 09:56:35 +000010220 TRACE(("evalcommand arg: %s\n", sp->text));
10221 *nargv++ = sp->text;
10222 }
10223 *nargv = NULL;
10224
10225 lastarg = NULL;
Denys Vlasenko675d24a2018-01-27 22:02:05 +010010226 if (iflag && funcline == 0 && argc > 0)
Eric Andersenc470f442003-07-28 09:56:35 +000010227 lastarg = nargv[-1];
10228
10229 expredir(cmd->ncmd.redirect);
Denys Vlasenko1c79aeb2017-07-29 22:51:52 +020010230 redir_stop = pushredir(cmd->ncmd.redirect);
Denys Vlasenkod07a15b2017-07-30 16:51:05 +020010231 preverrout_fd = 2;
Denys Vlasenkof8cdc7a2017-08-04 15:24:49 +020010232 if (BASH_XTRACEFD && xflag) {
10233 /* NB: bash closes fd == $BASH_XTRACEFD when it is changed.
10234 * we do not emulate this. We only use its value.
10235 */
10236 const char *xtracefd = lookupvar("BASH_XTRACEFD");
10237 if (xtracefd && is_number(xtracefd))
10238 preverrout_fd = atoi(xtracefd);
10239
10240 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010241 status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH | REDIR_SAVEFD2);
Eric Andersenc470f442003-07-28 09:56:35 +000010242
Denys Vlasenko54bef2a2020-02-19 15:30:20 +010010243 if (status) {
10244 bail:
10245 exitstatus = status;
10246
10247 /* We have a redirection error. */
10248 if (spclbltin > 0)
10249 raise_exception(EXERROR);
10250
10251 goto out;
10252 }
10253
Eric Andersenc470f442003-07-28 09:56:35 +000010254 for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) {
10255 struct strlist **spp;
Eric Andersenc470f442003-07-28 09:56:35 +000010256
10257 spp = varlist.lastp;
10258 expandarg(argp, &varlist, EXP_VARTILDE);
10259
Denys Vlasenko3e729102020-02-19 17:33:44 +010010260 if (vlocal)
10261 mklocal((*spp)->text, VEXPORT);
10262 else
10263 setvareq((*spp)->text, vflags);
Eric Andersenc470f442003-07-28 09:56:35 +000010264 }
10265
10266 /* Print the command if xflag is set. */
10267 if (xflag) {
Denys Vlasenko42ba7572017-07-21 13:20:14 +020010268 const char *pfx = "";
Eric Andersenc470f442003-07-28 09:56:35 +000010269
Denys Vlasenko46999802017-07-29 21:12:29 +020010270 fdprintf(preverrout_fd, "%s", expandstr(ps4val(), DQSYNTAX));
Denys Vlasenko42ba7572017-07-21 13:20:14 +020010271
Glenn L McGrath7b8765c2003-08-29 07:29:30 +000010272 sp = varlist.list;
Denys Vlasenko42ba7572017-07-21 13:20:14 +020010273 while (sp) {
10274 char *varval = sp->text;
10275 char *eq = strchrnul(varval, '=');
10276 if (*eq)
10277 eq++;
10278 fdprintf(preverrout_fd, "%s%.*s%s",
10279 pfx,
10280 (int)(eq - varval), varval,
10281 maybe_single_quote(eq)
10282 );
10283 sp = sp->next;
10284 pfx = " ";
10285 }
10286
10287 sp = arglist.list;
10288 while (sp) {
10289 fdprintf(preverrout_fd, "%s%s",
10290 pfx,
10291 /* always quote if matches reserved word: */
10292 findkwd(sp->text)
10293 ? single_quote(sp->text)
10294 : maybe_single_quote(sp->text)
10295 );
10296 sp = sp->next;
10297 pfx = " ";
Glenn L McGrath7b8765c2003-08-29 07:29:30 +000010298 }
Denis Vlasenko0e6f6612008-02-15 15:02:15 +000010299 safe_write(preverrout_fd, "\n", 1);
Eric Andersenc470f442003-07-28 09:56:35 +000010300 }
10301
Eric Andersenc470f442003-07-28 09:56:35 +000010302 /* Now locate the command. */
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010010303 if (cmdentry.cmdtype != CMDBUILTIN
10304 || !(IS_BUILTIN_REGULAR(cmdentry.u.cmd))
10305 ) {
Denys Vlasenko3e729102020-02-19 17:33:44 +010010306 path = path ? path : pathval();
10307 find_command(argv[0], &cmdentry, cmd_flag | DO_ERR, path);
Eric Andersenc470f442003-07-28 09:56:35 +000010308 }
10309
Denys Vlasenkod81af722020-02-18 14:28:30 +010010310 jp = NULL;
10311
Eric Andersenc470f442003-07-28 09:56:35 +000010312 /* Execute the command. */
10313 switch (cmdentry.cmdtype) {
Denys Vlasenko54bef2a2020-02-19 15:30:20 +010010314 case CMDUNKNOWN:
10315 status = 127;
10316 flush_stdout_stderr();
10317 goto bail;
10318
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +020010319 default: {
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +000010320
Denys Vlasenko1750d3a2018-01-15 00:41:04 +010010321#if ENABLE_FEATURE_SH_STANDALONE \
10322 && ENABLE_FEATURE_SH_NOFORK \
10323 && NUM_APPLETS > 1
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +020010324/* (1) BUG: if variables are set, we need to fork, or save/restore them
10325 * around run_nofork_applet() call.
10326 * (2) Should this check also be done in forkshell()?
10327 * (perhaps it should, so that "VAR=VAL nofork" at least avoids exec...)
10328 */
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000010329 /* find_command() encodes applet_no as (-2 - applet_no) */
10330 int applet_no = (- cmdentry.u.index - 2);
Denis Vlasenko9bc80d72008-04-12 20:07:53 +000010331 if (applet_no >= 0 && APPLET_IS_NOFORK(applet_no)) {
Denys Vlasenkoa5060b82017-11-03 14:16:25 +010010332 char **sv_environ;
10333
10334 INT_OFF;
10335 sv_environ = environ;
10336 environ = listvars(VEXPORT, VUNSET, varlist.list, /*end:*/ NULL);
Denys Vlasenkod329e342017-08-04 14:50:03 +020010337 /*
10338 * Run <applet>_main().
10339 * Signals (^C) can't interrupt here.
10340 * Otherwise we can mangle stdio or malloc internal state.
10341 * This makes applets which can run for a long time
10342 * and/or wait for user input ineligible for NOFORK:
10343 * for example, "yes" or "rm" (rm -i waits for input).
10344 */
Ron Yorstond5bfe262020-02-20 08:23:03 +000010345 exitstatus = run_nofork_applet(applet_no, argv);
Denys Vlasenkoa5060b82017-11-03 14:16:25 +010010346 environ = sv_environ;
Denys Vlasenkod329e342017-08-04 14:50:03 +020010347 /*
10348 * Try enabling NOFORK for "yes" applet.
10349 * ^C _will_ stop it (write returns EINTR),
10350 * but this causes stdout FILE to be stuck
10351 * and needing clearerr(). What if other applets
10352 * also can get EINTRs? Do we need to switch
10353 * our signals to SA_RESTART?
10354 */
10355 /*clearerr(stdout);*/
10356 INT_ON;
Denis Vlasenko9bc80d72008-04-12 20:07:53 +000010357 break;
10358 }
Denis Vlasenko9bc80d72008-04-12 20:07:53 +000010359#endif
Denys Vlasenkocfd392b2017-08-03 19:56:29 +020010360 /* Can we avoid forking? For example, very last command
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +020010361 * in a script or a subshell does not need forking,
10362 * we can just exec it.
10363 */
Denys Vlasenko238bf182010-05-18 15:49:07 +020010364 if (!(flags & EV_EXIT) || may_have_traps) {
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +020010365 /* No, forking off a child is necessary */
Denis Vlasenkob012b102007-02-19 22:43:01 +000010366 INT_OFF;
Denys Vlasenko098b7132017-01-11 19:59:03 +010010367 get_tty_state();
Denis Vlasenko68404f12008-03-17 09:00:54 +000010368 jp = makejob(/*cmd,*/ 1);
Eric Andersenc470f442003-07-28 09:56:35 +000010369 if (forkshell(jp, cmd, FORK_FG) != 0) {
Denys Vlasenko238bf182010-05-18 15:49:07 +020010370 /* parent */
Eric Andersenc470f442003-07-28 09:56:35 +000010371 break;
10372 }
Denys Vlasenko238bf182010-05-18 15:49:07 +020010373 /* child */
Denis Vlasenkob012b102007-02-19 22:43:01 +000010374 FORCE_INT_ON;
Denys Vlasenkoc7f95d22010-05-18 15:52:23 +020010375 /* fall through to exec'ing external program */
Eric Andersenc470f442003-07-28 09:56:35 +000010376 }
Denys Vlasenkoe139ae32017-04-12 21:02:33 +020010377 shellexec(argv[0], argv, path, cmdentry.u.index);
Eric Andersenc470f442003-07-28 09:56:35 +000010378 /* NOTREACHED */
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +020010379 } /* default */
Eric Andersenc470f442003-07-28 09:56:35 +000010380 case CMDBUILTIN:
Denys Vlasenko97edfc42020-02-18 14:37:56 +010010381 if (evalbltin(cmdentry.u.cmd, argc, argv, flags)
10382 && !(exception_type == EXERROR && spclbltin <= 0)
10383 ) {
Denys Vlasenkoc0663c72016-10-27 21:09:01 +020010384 raise:
10385 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +000010386 }
Denys Vlasenkod81af722020-02-18 14:28:30 +010010387 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010388
10389 case CMDFUNCTION:
Eric Andersenc470f442003-07-28 09:56:35 +000010390 if (evalfun(cmdentry.u.func, argc, argv, flags))
10391 goto raise;
10392 break;
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +020010393 } /* switch */
Eric Andersenc470f442003-07-28 09:56:35 +000010394
Denys Vlasenkod81af722020-02-18 14:28:30 +010010395 status = waitforjob(jp);
Ron Yorston6cda0b02020-02-21 16:16:56 +000010396 if (jp)
10397 TRACE(("forked child exited with %d\n", status));
Denys Vlasenko97edfc42020-02-18 14:37:56 +010010398 FORCE_INT_ON;
Denys Vlasenkod81af722020-02-18 14:28:30 +010010399
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010400 out:
Denys Vlasenkoeaf94362016-10-25 21:46:03 +020010401 if (cmd->ncmd.redirect)
Denys Vlasenko035486c2017-07-31 04:09:19 +020010402 popredir(/*drop:*/ cmd_is_exec);
Denys Vlasenko1c79aeb2017-07-29 22:51:52 +020010403 unwindredir(redir_stop);
Denys Vlasenko1c5eb882018-08-05 17:07:26 +020010404 unwindfiles(file_stop);
Denys Vlasenkoe2dd2af2020-02-20 10:33:38 +010010405 unwindlocalvars(localvar_stop);
Denis Vlasenko6514c5e2008-07-24 13:41:37 +000010406 if (lastarg) {
Eric Andersenc470f442003-07-28 09:56:35 +000010407 /* dsl: I think this is intended to be used to support
10408 * '_' in 'vi' command mode during line editing...
10409 * However I implemented that within libedit itself.
10410 */
Denys Vlasenko0a0acb52015-04-18 19:36:38 +020010411 setvar0("_", lastarg);
Denis Vlasenko6514c5e2008-07-24 13:41:37 +000010412 }
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +020010413
10414 return status;
Eric Andersenc470f442003-07-28 09:56:35 +000010415}
10416
10417static int
Denys Vlasenko7b3fa1e2016-10-01 15:10:16 +020010418evalbltin(const struct builtincmd *cmd, int argc, char **argv, int flags)
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010419{
Eric Andersenc470f442003-07-28 09:56:35 +000010420 char *volatile savecmdname;
10421 struct jmploc *volatile savehandler;
10422 struct jmploc jmploc;
Denys Vlasenko7b3fa1e2016-10-01 15:10:16 +020010423 int status;
Eric Andersenc470f442003-07-28 09:56:35 +000010424 int i;
10425
10426 savecmdname = commandname;
Denys Vlasenkoa2d121c2016-09-30 11:30:11 +020010427 savehandler = exception_handler;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010428 i = setjmp(jmploc.loc);
10429 if (i)
Eric Andersenc470f442003-07-28 09:56:35 +000010430 goto cmddone;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010431 exception_handler = &jmploc;
Eric Andersenc470f442003-07-28 09:56:35 +000010432 commandname = argv[0];
10433 argptr = argv + 1;
10434 optptr = NULL; /* initialize nextopt */
Denys Vlasenko7b3fa1e2016-10-01 15:10:16 +020010435 if (cmd == EVALCMD)
10436 status = evalcmd(argc, argv, flags);
10437 else
10438 status = (*cmd->builtin)(argc, argv);
Denis Vlasenkob012b102007-02-19 22:43:01 +000010439 flush_stdout_stderr();
Denys Vlasenko7b3fa1e2016-10-01 15:10:16 +020010440 status |= ferror(stdout);
10441 exitstatus = status;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010442 cmddone:
Rob Landleyf296f0b2006-07-06 01:09:21 +000010443 clearerr(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +000010444 commandname = savecmdname;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010445 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +000010446
10447 return i;
10448}
10449
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010450static int
10451goodname(const char *p)
Glenn L McGrath16e45d72004-02-04 08:24:39 +000010452{
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +020010453 return endofname(p)[0] == '\0';
Glenn L McGrath16e45d72004-02-04 08:24:39 +000010454}
10455
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010456
Glenn L McGrath50812ff2002-08-23 13:14:48 +000010457/*
10458 * Search for a command. This is called before we fork so that the
10459 * location of the command will be available in the parent as well as
Glenn L McGrath16e45d72004-02-04 08:24:39 +000010460 * the child. The check for "goodname" is an overly conservative
10461 * check that the name will not be subject to expansion.
Glenn L McGrath50812ff2002-08-23 13:14:48 +000010462 */
Eric Andersenc470f442003-07-28 09:56:35 +000010463static void
10464prehash(union node *n)
Glenn L McGrath50812ff2002-08-23 13:14:48 +000010465{
10466 struct cmdentry entry;
10467
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010468 if (n->type == NCMD && n->ncmd.args && goodname(n->ncmd.args->narg.text))
10469 find_command(n->ncmd.args->narg.text, &entry, 0, pathval());
Glenn L McGrath50812ff2002-08-23 13:14:48 +000010470}
10471
Eric Andersencb57d552001-06-28 07:25:16 +000010472
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000010473/* ============ Builtin commands
10474 *
10475 * Builtin commands whose functions are closely tied to evaluation
10476 * are implemented here.
Eric Andersencb57d552001-06-28 07:25:16 +000010477 */
10478
10479/*
Eric Andersencb57d552001-06-28 07:25:16 +000010480 * Handle break and continue commands. Break, continue, and return are
10481 * all handled by setting the evalskip flag. The evaluation routines
10482 * above all check this flag, and if it is set they start skipping
10483 * commands rather than executing them. The variable skipcount is
10484 * the number of loops to break/continue, or the number of function
10485 * levels to return. (The latter is always 1.) It should probably
10486 * be an error to break out of more loops than exist, but it isn't
10487 * in the standard shell so we don't make it one here.
10488 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010489static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000010490breakcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000010491{
Denis Vlasenko68404f12008-03-17 09:00:54 +000010492 int n = argv[1] ? number(argv[1]) : 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010493
Aaron Lehmann2aef3a62001-12-31 06:03:12 +000010494 if (n <= 0)
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +020010495 ash_msg_and_raise_error(msg_illnum, argv[1]);
Eric Andersencb57d552001-06-28 07:25:16 +000010496 if (n > loopnest)
10497 n = loopnest;
10498 if (n > 0) {
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000010499 evalskip = (**argv == 'c') ? SKIPCONT : SKIPBREAK;
Eric Andersencb57d552001-06-28 07:25:16 +000010500 skipcount = n;
10501 }
10502 return 0;
10503}
10504
Eric Andersenc470f442003-07-28 09:56:35 +000010505
Denys Vlasenko70392332016-10-27 02:31:55 +020010506/*
Eric Andersen90898442003-08-06 11:20:52 +000010507 * This implements the input routines used by the parser.
Eric Andersencb57d552001-06-28 07:25:16 +000010508 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +000010509
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010510enum {
10511 INPUT_PUSH_FILE = 1,
10512 INPUT_NOFILE_OK = 2,
10513};
Eric Andersencb57d552001-06-28 07:25:16 +000010514
Denis Vlasenkob07a4962008-06-22 13:16:23 +000010515static smallint checkkwd;
Denis Vlasenko99eb8502007-02-23 21:09:49 +000010516/* values of checkkwd variable */
10517#define CHKALIAS 0x1
10518#define CHKKWD 0x2
10519#define CHKNL 0x4
Denys Vlasenkoa7328982017-07-29 19:57:28 +020010520#define CHKEOFMARK 0x8
Denis Vlasenko99eb8502007-02-23 21:09:49 +000010521
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010522/*
10523 * Push a string back onto the input at this current parsefile level.
10524 * We handle aliases this way.
10525 */
10526#if !ENABLE_ASH_ALIAS
10527#define pushstring(s, ap) pushstring(s)
10528#endif
10529static void
10530pushstring(char *s, struct alias *ap)
10531{
10532 struct strpush *sp;
10533 int len;
10534
10535 len = strlen(s);
10536 INT_OFF;
10537 if (g_parsefile->strpush) {
10538 sp = ckzalloc(sizeof(*sp));
10539 sp->prev = g_parsefile->strpush;
10540 } else {
10541 sp = &(g_parsefile->basestrpush);
10542 }
10543 g_parsefile->strpush = sp;
10544 sp->prev_string = g_parsefile->next_to_pgetc;
10545 sp->prev_left_in_line = g_parsefile->left_in_line;
Denys Vlasenko3b4d04b2016-09-29 02:11:19 +020010546 sp->unget = g_parsefile->unget;
10547 memcpy(sp->lastc, g_parsefile->lastc, sizeof(sp->lastc));
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010548#if ENABLE_ASH_ALIAS
10549 sp->ap = ap;
10550 if (ap) {
10551 ap->flag |= ALIASINUSE;
10552 sp->string = s;
10553 }
10554#endif
10555 g_parsefile->next_to_pgetc = s;
10556 g_parsefile->left_in_line = len;
Denys Vlasenko3b4d04b2016-09-29 02:11:19 +020010557 g_parsefile->unget = 0;
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010558 INT_ON;
10559}
10560
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010561static void
10562popstring(void)
Eric Andersenc470f442003-07-28 09:56:35 +000010563{
Denis Vlasenkob07a4962008-06-22 13:16:23 +000010564 struct strpush *sp = g_parsefile->strpush;
Eric Andersenc470f442003-07-28 09:56:35 +000010565
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010566 INT_OFF;
Denis Vlasenko131ae172007-02-18 13:00:19 +000010567#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010568 if (sp->ap) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010569 if (g_parsefile->next_to_pgetc[-1] == ' '
10570 || g_parsefile->next_to_pgetc[-1] == '\t'
10571 ) {
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010572 checkkwd |= CHKALIAS;
Glenn L McGrath28939ad2004-07-21 10:20:19 +000010573 }
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010574 if (sp->string != sp->ap->val) {
10575 free(sp->string);
10576 }
10577 sp->ap->flag &= ~ALIASINUSE;
10578 if (sp->ap->flag & ALIASDEAD) {
10579 unalias(sp->ap->name);
10580 }
Glenn L McGrath28939ad2004-07-21 10:20:19 +000010581 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +000010582#endif
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010583 g_parsefile->next_to_pgetc = sp->prev_string;
10584 g_parsefile->left_in_line = sp->prev_left_in_line;
Denys Vlasenko3b4d04b2016-09-29 02:11:19 +020010585 g_parsefile->unget = sp->unget;
10586 memcpy(g_parsefile->lastc, sp->lastc, sizeof(sp->lastc));
Denis Vlasenkob07a4962008-06-22 13:16:23 +000010587 g_parsefile->strpush = sp->prev;
10588 if (sp != &(g_parsefile->basestrpush))
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010589 free(sp);
10590 INT_ON;
10591}
Denis Vlasenko8e1c7152007-01-22 07:21:38 +000010592
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010593static int
10594preadfd(void)
Eric Andersencb57d552001-06-28 07:25:16 +000010595{
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010596 int nr;
Denis Vlasenko6a0ad252008-07-25 13:34:05 +000010597 char *buf = g_parsefile->buf;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010598
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010599 g_parsefile->next_to_pgetc = buf;
Denis Vlasenko38f63192007-01-22 09:03:07 +000010600#if ENABLE_FEATURE_EDITING
Denys Vlasenko4ac35a32020-11-16 13:09:37 +010010601 /* retry: */
Denys Vlasenko79b3d422010-06-03 04:29:08 +020010602 if (!iflag || g_parsefile->pf_fd != STDIN_FILENO)
Ron Yorston61d6ae22015-04-19 10:50:25 +010010603 nr = nonblock_immune_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1);
Eric Andersenc470f442003-07-28 09:56:35 +000010604 else {
Denys Vlasenko66c5b122011-02-08 05:07:02 +010010605# if ENABLE_ASH_IDLE_TIMEOUT
Denys Vlasenko84ea60e2017-08-02 17:27:28 +020010606 int timeout = -1;
Denys Vlasenko897475a2019-06-01 16:35:09 +020010607 const char *tmout_var = lookupvar("TMOUT");
10608 if (tmout_var) {
10609 timeout = atoi(tmout_var) * 1000;
10610 if (timeout <= 0)
10611 timeout = -1;
Denys Vlasenko66c5b122011-02-08 05:07:02 +010010612 }
Denys Vlasenko84ea60e2017-08-02 17:27:28 +020010613 line_input_state->timeout = timeout;
Denys Vlasenko66c5b122011-02-08 05:07:02 +010010614# endif
Denys Vlasenko8c52f802011-02-04 17:36:21 +010010615# if ENABLE_FEATURE_TAB_COMPLETION
Denis Vlasenko8e1c7152007-01-22 07:21:38 +000010616 line_input_state->path_lookup = pathval();
Denys Vlasenko8c52f802011-02-04 17:36:21 +010010617# endif
Denys Vlasenkoe9ab07c2014-08-13 18:00:08 +020010618 reinit_unicode_for_ash();
Denys Vlasenko84ea60e2017-08-02 17:27:28 +020010619 nr = read_line_input(line_input_state, cmdedit_prompt, buf, IBUFSIZ);
Denis Vlasenko8e1c7152007-01-22 07:21:38 +000010620 if (nr == 0) {
Denys Vlasenko4b89d512016-11-25 03:41:03 +010010621 /* ^C pressed, "convert" to SIGINT */
10622 write(STDOUT_FILENO, "^C", 2);
Denys Vlasenko4ac35a32020-11-16 13:09:37 +010010623 raise(SIGINT);
Denis Vlasenko8e1c7152007-01-22 07:21:38 +000010624 if (trap[SIGINT]) {
Glenn L McGrath16e45d72004-02-04 08:24:39 +000010625 buf[0] = '\n';
Denis Vlasenko8e1c7152007-01-22 07:21:38 +000010626 buf[1] = '\0';
Glenn L McGrath16e45d72004-02-04 08:24:39 +000010627 return 1;
10628 }
Denys Vlasenko8660aeb2016-11-24 17:44:02 +010010629 exitstatus = 128 + SIGINT;
Denys Vlasenko4ac35a32020-11-16 13:09:37 +010010630 return -1;
Eric Andersenc470f442003-07-28 09:56:35 +000010631 }
Denys Vlasenko66c5b122011-02-08 05:07:02 +010010632 if (nr < 0) {
10633 if (errno == 0) {
10634 /* Ctrl+D pressed */
10635 nr = 0;
10636 }
10637# if ENABLE_ASH_IDLE_TIMEOUT
10638 else if (errno == EAGAIN && timeout > 0) {
Denys Vlasenkod60752f2015-10-07 22:42:45 +020010639 puts("\007timed out waiting for input: auto-logout");
Denys Vlasenko66c5b122011-02-08 05:07:02 +010010640 exitshell();
10641 }
10642# endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010643 }
Eric Andersencb57d552001-06-28 07:25:16 +000010644 }
10645#else
Ron Yorston61d6ae22015-04-19 10:50:25 +010010646 nr = nonblock_immune_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1);
Eric Andersencb57d552001-06-28 07:25:16 +000010647#endif
10648
Denys Vlasenko80c5b682011-05-08 21:21:10 +020010649#if 0 /* disabled: nonblock_immune_read() handles this problem */
Eric Andersencb57d552001-06-28 07:25:16 +000010650 if (nr < 0) {
Eric Andersencb57d552001-06-28 07:25:16 +000010651 if (parsefile->fd == 0 && errno == EWOULDBLOCK) {
Denis Vlasenkod37f2222007-08-19 13:42:08 +000010652 int flags = fcntl(0, F_GETFL);
Denis Vlasenko9cb220b2007-12-09 10:03:28 +000010653 if (flags >= 0 && (flags & O_NONBLOCK)) {
10654 flags &= ~O_NONBLOCK;
Eric Andersencb57d552001-06-28 07:25:16 +000010655 if (fcntl(0, F_SETFL, flags) >= 0) {
10656 out2str("sh: turning off NDELAY mode\n");
10657 goto retry;
10658 }
10659 }
10660 }
10661 }
Denis Vlasenkoe376d452008-02-20 22:23:24 +000010662#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010663 return nr;
10664}
10665
10666/*
10667 * Refill the input buffer and return the next input character:
10668 *
10669 * 1) If a string was pushed back on the input, pop it;
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010670 * 2) If an EOF was pushed back (g_parsefile->left_in_line < -BIGNUM)
10671 * or we are reading from a string so we can't refill the buffer,
10672 * return EOF.
Denys Vlasenko883cea42009-07-11 15:31:59 +020010673 * 3) If there is more stuff in this buffer, use it else call read to fill it.
Eric Andersencb57d552001-06-28 07:25:16 +000010674 * 4) Process input up to the next newline, deleting nul characters.
10675 */
Denis Vlasenko727752d2008-11-28 03:41:47 +000010676//#define pgetc_debug(...) bb_error_msg(__VA_ARGS__)
10677#define pgetc_debug(...) ((void)0)
Denys Vlasenko3b4d04b2016-09-29 02:11:19 +020010678static int pgetc(void);
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010679static int
Eric Andersenc470f442003-07-28 09:56:35 +000010680preadbuffer(void)
Eric Andersencb57d552001-06-28 07:25:16 +000010681{
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000010682 char *q;
Eric Andersencb57d552001-06-28 07:25:16 +000010683 int more;
Eric Andersencb57d552001-06-28 07:25:16 +000010684
Denys Vlasenko3b4d04b2016-09-29 02:11:19 +020010685 if (g_parsefile->strpush) {
Denis Vlasenko131ae172007-02-18 13:00:19 +000010686#if ENABLE_ASH_ALIAS
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010687 if (g_parsefile->left_in_line == -1
10688 && g_parsefile->strpush->ap
10689 && g_parsefile->next_to_pgetc[-1] != ' '
10690 && g_parsefile->next_to_pgetc[-1] != '\t'
Denis Vlasenko16898402008-11-25 01:34:52 +000010691 ) {
Denis Vlasenko727752d2008-11-28 03:41:47 +000010692 pgetc_debug("preadbuffer PEOA");
Eric Andersencb57d552001-06-28 07:25:16 +000010693 return PEOA;
10694 }
Eric Andersen2870d962001-07-02 17:27:21 +000010695#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010696 popstring();
Denys Vlasenko3b4d04b2016-09-29 02:11:19 +020010697 return pgetc();
Eric Andersencb57d552001-06-28 07:25:16 +000010698 }
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010699 /* on both branches above g_parsefile->left_in_line < 0.
Denis Vlasenko727752d2008-11-28 03:41:47 +000010700 * "pgetc" needs refilling.
10701 */
10702
Denis Vlasenkoe27dafd2008-11-28 04:01:03 +000010703 /* -90 is our -BIGNUM. Below we use -99 to mark "EOF on read",
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010704 * pungetc() may increment it a few times.
Denis Vlasenkoe27dafd2008-11-28 04:01:03 +000010705 * Assuming it won't increment it to less than -90.
Denis Vlasenko727752d2008-11-28 03:41:47 +000010706 */
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010707 if (g_parsefile->left_in_line < -90 || g_parsefile->buf == NULL) {
Denis Vlasenko727752d2008-11-28 03:41:47 +000010708 pgetc_debug("preadbuffer PEOF1");
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010709 /* even in failure keep left_in_line and next_to_pgetc
10710 * in lock step, for correct multi-layer pungetc.
10711 * left_in_line was decremented before preadbuffer(),
10712 * must inc next_to_pgetc: */
10713 g_parsefile->next_to_pgetc++;
Eric Andersencb57d552001-06-28 07:25:16 +000010714 return PEOF;
Denis Vlasenko727752d2008-11-28 03:41:47 +000010715 }
Eric Andersencb57d552001-06-28 07:25:16 +000010716
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010717 more = g_parsefile->left_in_buffer;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000010718 if (more <= 0) {
Denis Vlasenko727752d2008-11-28 03:41:47 +000010719 flush_stdout_stderr();
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010720 again:
10721 more = preadfd();
10722 if (more <= 0) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010723 /* don't try reading again */
10724 g_parsefile->left_in_line = -99;
Denis Vlasenko727752d2008-11-28 03:41:47 +000010725 pgetc_debug("preadbuffer PEOF2");
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010726 g_parsefile->next_to_pgetc++;
Eric Andersencb57d552001-06-28 07:25:16 +000010727 return PEOF;
10728 }
10729 }
10730
Denis Vlasenko727752d2008-11-28 03:41:47 +000010731 /* Find out where's the end of line.
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010732 * Set g_parsefile->left_in_line
10733 * and g_parsefile->left_in_buffer acordingly.
Denis Vlasenko727752d2008-11-28 03:41:47 +000010734 * NUL chars are deleted.
10735 */
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010736 q = g_parsefile->next_to_pgetc;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000010737 for (;;) {
Denis Vlasenko727752d2008-11-28 03:41:47 +000010738 char c;
Eric Andersencb57d552001-06-28 07:25:16 +000010739
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000010740 more--;
Eric Andersenc470f442003-07-28 09:56:35 +000010741
Denis Vlasenko727752d2008-11-28 03:41:47 +000010742 c = *q;
10743 if (c == '\0') {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000010744 memmove(q, q + 1, more);
Denis Vlasenko727752d2008-11-28 03:41:47 +000010745 } else {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000010746 q++;
10747 if (c == '\n') {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010748 g_parsefile->left_in_line = q - g_parsefile->next_to_pgetc - 1;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000010749 break;
10750 }
Eric Andersencb57d552001-06-28 07:25:16 +000010751 }
10752
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000010753 if (more <= 0) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010754 g_parsefile->left_in_line = q - g_parsefile->next_to_pgetc - 1;
10755 if (g_parsefile->left_in_line < 0)
Eric Andersencb57d552001-06-28 07:25:16 +000010756 goto again;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000010757 break;
Eric Andersencb57d552001-06-28 07:25:16 +000010758 }
10759 }
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010760 g_parsefile->left_in_buffer = more;
Eric Andersencb57d552001-06-28 07:25:16 +000010761
Eric Andersencb57d552001-06-28 07:25:16 +000010762 if (vflag) {
Denis Vlasenko727752d2008-11-28 03:41:47 +000010763 char save = *q;
10764 *q = '\0';
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010765 out2str(g_parsefile->next_to_pgetc);
Denis Vlasenko727752d2008-11-28 03:41:47 +000010766 *q = save;
Eric Andersencb57d552001-06-28 07:25:16 +000010767 }
10768
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010769 pgetc_debug("preadbuffer at %d:%p'%s'",
10770 g_parsefile->left_in_line,
10771 g_parsefile->next_to_pgetc,
10772 g_parsefile->next_to_pgetc);
Denys Vlasenkocd716832009-11-28 22:14:02 +010010773 return (unsigned char)*g_parsefile->next_to_pgetc++;
Eric Andersencb57d552001-06-28 07:25:16 +000010774}
10775
Denys Vlasenkoce332a22016-10-02 23:47:34 +020010776static void
10777nlprompt(void)
10778{
10779 g_parsefile->linno++;
10780 setprompt_if(doprompt, 2);
10781}
10782static void
10783nlnoprompt(void)
10784{
10785 g_parsefile->linno++;
10786 needprompt = doprompt;
10787}
10788
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010789static int
10790pgetc(void)
10791{
Denys Vlasenko3b4d04b2016-09-29 02:11:19 +020010792 int c;
10793
10794 pgetc_debug("pgetc at %d:%p'%s'",
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010795 g_parsefile->left_in_line,
10796 g_parsefile->next_to_pgetc,
10797 g_parsefile->next_to_pgetc);
Denys Vlasenko3b4d04b2016-09-29 02:11:19 +020010798 if (g_parsefile->unget)
10799 return g_parsefile->lastc[--g_parsefile->unget];
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +000010800
Denys Vlasenko3b4d04b2016-09-29 02:11:19 +020010801 if (--g_parsefile->left_in_line >= 0)
Denys Vlasenko2fe66b12016-12-12 17:39:12 +010010802 c = (unsigned char)*g_parsefile->next_to_pgetc++;
Denys Vlasenko3b4d04b2016-09-29 02:11:19 +020010803 else
10804 c = preadbuffer();
10805
10806 g_parsefile->lastc[1] = g_parsefile->lastc[0];
10807 g_parsefile->lastc[0] = c;
10808
10809 return c;
10810}
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010811
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010812#if ENABLE_ASH_ALIAS
10813static int
Denys Vlasenko2ce42e92009-11-29 02:18:13 +010010814pgetc_without_PEOA(void)
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010815{
10816 int c;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010817 do {
Denys Vlasenko3b4d04b2016-09-29 02:11:19 +020010818 pgetc_debug("pgetc at %d:%p'%s'",
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010819 g_parsefile->left_in_line,
10820 g_parsefile->next_to_pgetc,
10821 g_parsefile->next_to_pgetc);
Denys Vlasenko3b4d04b2016-09-29 02:11:19 +020010822 c = pgetc();
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010823 } while (c == PEOA);
10824 return c;
10825}
10826#else
Denys Vlasenko2ce42e92009-11-29 02:18:13 +010010827# define pgetc_without_PEOA() pgetc()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010828#endif
10829
10830/*
Denys Vlasenko3b4d04b2016-09-29 02:11:19 +020010831 * Undo a call to pgetc. Only two characters may be pushed back.
Eric Andersenc470f442003-07-28 09:56:35 +000010832 * PEOF may be pushed back.
10833 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010834static void
Eric Andersenc470f442003-07-28 09:56:35 +000010835pungetc(void)
10836{
Denys Vlasenko3b4d04b2016-09-29 02:11:19 +020010837 g_parsefile->unget++;
Eric Andersencb57d552001-06-28 07:25:16 +000010838}
10839
Denys Vlasenko73c3e072016-09-29 17:17:04 +020010840/* This one eats backslash+newline */
10841static int
10842pgetc_eatbnl(void)
10843{
10844 int c;
10845
10846 while ((c = pgetc()) == '\\') {
10847 if (pgetc() != '\n') {
10848 pungetc();
10849 break;
10850 }
10851
Denys Vlasenkoce332a22016-10-02 23:47:34 +020010852 nlprompt();
Denys Vlasenko73c3e072016-09-29 17:17:04 +020010853 }
10854
10855 return c;
10856}
10857
Denys Vlasenko216913c2018-04-02 12:35:04 +020010858struct synstack {
10859 smalluint syntax;
10860 uint8_t innerdq :1;
10861 uint8_t varpushed :1;
10862 uint8_t dblquote :1;
10863 int varnest; /* levels of variables expansion */
10864 int dqvarnest; /* levels of variables expansion within double quotes */
10865 int parenlevel; /* levels of parens in arithmetic */
10866 struct synstack *prev;
10867 struct synstack *next;
10868};
10869
Denys Vlasenkof7eea8c2020-02-14 16:16:34 +010010870static int
10871pgetc_top(struct synstack *stack)
10872{
10873 return stack->syntax == SQSYNTAX ? pgetc() : pgetc_eatbnl();
10874}
10875
Denys Vlasenko216913c2018-04-02 12:35:04 +020010876static void
10877synstack_push(struct synstack **stack, struct synstack *next, int syntax)
10878{
10879 memset(next, 0, sizeof(*next));
10880 next->syntax = syntax;
10881 next->next = *stack;
10882 (*stack)->prev = next;
10883 *stack = next;
10884}
10885
10886static ALWAYS_INLINE void
10887synstack_pop(struct synstack **stack)
10888{
10889 *stack = (*stack)->next;
10890}
10891
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010892/*
10893 * To handle the "." command, a stack of input files is used. Pushfile
10894 * adds a new entry to the stack and popfile restores the previous level.
10895 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010896static void
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010897pushfile(void)
Eric Andersenc470f442003-07-28 09:56:35 +000010898{
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010899 struct parsefile *pf;
10900
Denis Vlasenko597906c2008-02-20 16:38:54 +000010901 pf = ckzalloc(sizeof(*pf));
Denis Vlasenkob07a4962008-06-22 13:16:23 +000010902 pf->prev = g_parsefile;
Denys Vlasenko79b3d422010-06-03 04:29:08 +020010903 pf->pf_fd = -1;
Denis Vlasenko597906c2008-02-20 16:38:54 +000010904 /*pf->strpush = NULL; - ckzalloc did it */
10905 /*pf->basestrpush.prev = NULL;*/
Denys Vlasenko3b4d04b2016-09-29 02:11:19 +020010906 /*pf->unget = 0;*/
Denis Vlasenkob07a4962008-06-22 13:16:23 +000010907 g_parsefile = pf;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010908}
10909
10910static void
10911popfile(void)
10912{
Denis Vlasenkob07a4962008-06-22 13:16:23 +000010913 struct parsefile *pf = g_parsefile;
Eric Andersenc470f442003-07-28 09:56:35 +000010914
Denys Vlasenko493b9ca2016-10-30 18:27:14 +010010915 if (pf == &basepf)
10916 return;
10917
Denis Vlasenkob012b102007-02-19 22:43:01 +000010918 INT_OFF;
Denys Vlasenko79b3d422010-06-03 04:29:08 +020010919 if (pf->pf_fd >= 0)
10920 close(pf->pf_fd);
Denis Vlasenko60818682007-09-28 22:07:23 +000010921 free(pf->buf);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010922 while (pf->strpush)
10923 popstring();
Denis Vlasenkob07a4962008-06-22 13:16:23 +000010924 g_parsefile = pf->prev;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010925 free(pf);
Denis Vlasenkob012b102007-02-19 22:43:01 +000010926 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000010927}
10928
Denys Vlasenko1c5eb882018-08-05 17:07:26 +020010929static void
10930unwindfiles(struct parsefile *stop)
10931{
10932 while (g_parsefile != stop)
10933 popfile();
10934}
10935
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010936/*
10937 * Return to top level.
10938 */
10939static void
10940popallfiles(void)
10941{
Denys Vlasenko1c5eb882018-08-05 17:07:26 +020010942 unwindfiles(&basepf);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010943}
10944
10945/*
10946 * Close the file(s) that the shell is reading commands from. Called
10947 * after a fork is done.
10948 */
10949static void
10950closescript(void)
10951{
10952 popallfiles();
Denys Vlasenko79b3d422010-06-03 04:29:08 +020010953 if (g_parsefile->pf_fd > 0) {
10954 close(g_parsefile->pf_fd);
10955 g_parsefile->pf_fd = 0;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010956 }
10957}
10958
10959/*
10960 * Like setinputfile, but takes an open file descriptor. Call this with
10961 * interrupts off.
10962 */
10963static void
10964setinputfd(int fd, int push)
10965{
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010966 if (push) {
10967 pushfile();
Denis Vlasenko727752d2008-11-28 03:41:47 +000010968 g_parsefile->buf = NULL;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010969 }
Denys Vlasenko79b3d422010-06-03 04:29:08 +020010970 g_parsefile->pf_fd = fd;
Denis Vlasenkob07a4962008-06-22 13:16:23 +000010971 if (g_parsefile->buf == NULL)
10972 g_parsefile->buf = ckmalloc(IBUFSIZ);
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010973 g_parsefile->left_in_buffer = 0;
10974 g_parsefile->left_in_line = 0;
10975 g_parsefile->linno = 1;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010976}
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010977
Eric Andersenc470f442003-07-28 09:56:35 +000010978/*
10979 * Set the input to take input from a file. If push is set, push the
10980 * old input onto the stack first.
10981 */
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000010982static int
10983setinputfile(const char *fname, int flags)
Eric Andersenc470f442003-07-28 09:56:35 +000010984{
10985 int fd;
Eric Andersenc470f442003-07-28 09:56:35 +000010986
Denis Vlasenkob012b102007-02-19 22:43:01 +000010987 INT_OFF;
Denys Vlasenko60fb98e2018-03-30 22:15:14 +020010988 fd = open(fname, O_RDONLY | O_CLOEXEC);
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010989 if (fd < 0) {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000010990 if (flags & INPUT_NOFILE_OK)
10991 goto out;
Denys Vlasenkob7adf7a2016-10-25 17:00:13 +020010992 exitstatus = 127;
Johannes Schindelin20a63b22017-08-22 22:03:17 +020010993 ash_msg_and_raise_perror("can't open '%s'", fname);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000010994 }
Denys Vlasenko64774602016-10-26 15:24:30 +020010995 if (fd < 10)
10996 fd = savefd(fd);
Denys Vlasenko60fb98e2018-03-30 22:15:14 +020010997 else if (O_CLOEXEC == 0) /* old libc */
Denys Vlasenkoe19923f2016-10-26 15:38:44 +020010998 close_on_exec_on(fd);
Denys Vlasenko60fb98e2018-03-30 22:15:14 +020010999
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000011000 setinputfd(fd, flags & INPUT_PUSH_FILE);
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011001 out:
Denis Vlasenkob012b102007-02-19 22:43:01 +000011002 INT_ON;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000011003 return fd;
Eric Andersenc470f442003-07-28 09:56:35 +000011004}
11005
Eric Andersencb57d552001-06-28 07:25:16 +000011006/*
11007 * Like setinputfile, but takes input from a string.
11008 */
Eric Andersenc470f442003-07-28 09:56:35 +000011009static void
11010setinputstring(char *string)
Eric Andersen62483552001-07-10 06:09:16 +000011011{
Denis Vlasenkob012b102007-02-19 22:43:01 +000011012 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +000011013 pushfile();
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011014 g_parsefile->next_to_pgetc = string;
11015 g_parsefile->left_in_line = strlen(string);
Denis Vlasenkob07a4962008-06-22 13:16:23 +000011016 g_parsefile->buf = NULL;
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011017 g_parsefile->linno = 1;
Denis Vlasenkob012b102007-02-19 22:43:01 +000011018 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +000011019}
11020
11021
Denys Vlasenko70392332016-10-27 02:31:55 +020011022/*
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011023 * Routines to check for mail.
Eric Andersencb57d552001-06-28 07:25:16 +000011024 */
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +000011025
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011026#if ENABLE_ASH_MAIL
Eric Andersencb57d552001-06-28 07:25:16 +000011027
Denys Vlasenko23841622015-10-09 15:52:03 +020011028/* Hash of mtimes of mailboxes */
11029static unsigned mailtime_hash;
Eric Andersenc470f442003-07-28 09:56:35 +000011030/* Set if MAIL or MAILPATH is changed. */
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011031static smallint mail_var_path_changed;
Eric Andersencb57d552001-06-28 07:25:16 +000011032
Eric Andersencb57d552001-06-28 07:25:16 +000011033/*
Eric Andersenc470f442003-07-28 09:56:35 +000011034 * Print appropriate message(s) if mail has arrived.
11035 * If mail_var_path_changed is set,
11036 * then the value of MAIL has mail_var_path_changed,
11037 * so we just update the values.
Eric Andersencb57d552001-06-28 07:25:16 +000011038 */
Eric Andersenc470f442003-07-28 09:56:35 +000011039static void
11040chkmail(void)
Eric Andersencb57d552001-06-28 07:25:16 +000011041{
Eric Andersencb57d552001-06-28 07:25:16 +000011042 const char *mpath;
11043 char *p;
11044 char *q;
Denys Vlasenko23841622015-10-09 15:52:03 +020011045 unsigned new_hash;
Eric Andersencb57d552001-06-28 07:25:16 +000011046 struct stackmark smark;
11047 struct stat statb;
11048
Eric Andersencb57d552001-06-28 07:25:16 +000011049 setstackmark(&smark);
Eric Andersenc470f442003-07-28 09:56:35 +000011050 mpath = mpathset() ? mpathval() : mailval();
Denys Vlasenko23841622015-10-09 15:52:03 +020011051 new_hash = 0;
11052 for (;;) {
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +010011053 int len;
11054
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +010011055 len = padvance_magic(&mpath, nullstr, 2);
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +010011056 if (!len)
11057 break;
11058 p = stackblock();
Eric Andersencb57d552001-06-28 07:25:16 +000011059 break;
11060 if (*p == '\0')
11061 continue;
Denis Vlasenkof7d56652008-03-25 05:51:41 +000011062 for (q = p; *q; q++)
11063 continue;
Denis Vlasenkoa7189f02006-11-17 20:29:00 +000011064#if DEBUG
Eric Andersencb57d552001-06-28 07:25:16 +000011065 if (q[-1] != '/')
11066 abort();
11067#endif
Eric Andersenc470f442003-07-28 09:56:35 +000011068 q[-1] = '\0'; /* delete trailing '/' */
11069 if (stat(p, &statb) < 0) {
Eric Andersenc470f442003-07-28 09:56:35 +000011070 continue;
Eric Andersencb57d552001-06-28 07:25:16 +000011071 }
Denys Vlasenko23841622015-10-09 15:52:03 +020011072 /* Very simplistic "hash": just a sum of all mtimes */
11073 new_hash += (unsigned)statb.st_mtime;
11074 }
11075 if (!mail_var_path_changed && mailtime_hash != new_hash) {
Denys Vlasenko4cd99e72015-10-09 16:02:53 +020011076 if (mailtime_hash != 0)
11077 out2str("you have mail\n");
Denys Vlasenko23841622015-10-09 15:52:03 +020011078 mailtime_hash = new_hash;
Eric Andersencb57d552001-06-28 07:25:16 +000011079 }
Eric Andersenc470f442003-07-28 09:56:35 +000011080 mail_var_path_changed = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000011081 popstackmark(&smark);
11082}
Eric Andersencb57d552001-06-28 07:25:16 +000011083
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020011084static void FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000011085changemail(const char *val UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000011086{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011087 mail_var_path_changed = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000011088}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +000011089
Denis Vlasenko131ae172007-02-18 13:00:19 +000011090#endif /* ASH_MAIL */
Eric Andersenc470f442003-07-28 09:56:35 +000011091
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011092
11093/* ============ ??? */
11094
Eric Andersencb57d552001-06-28 07:25:16 +000011095/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000011096 * Set the shell parameters.
Eric Andersencb57d552001-06-28 07:25:16 +000011097 */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000011098static void
11099setparam(char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000011100{
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000011101 char **newparam;
11102 char **ap;
11103 int nparam;
Eric Andersencb57d552001-06-28 07:25:16 +000011104
Denis Vlasenkof7d56652008-03-25 05:51:41 +000011105 for (nparam = 0; argv[nparam]; nparam++)
11106 continue;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000011107 ap = newparam = ckmalloc((nparam + 1) * sizeof(*ap));
11108 while (*argv) {
11109 *ap++ = ckstrdup(*argv++);
Eric Andersencb57d552001-06-28 07:25:16 +000011110 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000011111 *ap = NULL;
11112 freeparam(&shellparam);
Denis Vlasenko01631112007-12-16 17:20:38 +000011113 shellparam.malloced = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000011114 shellparam.nparam = nparam;
11115 shellparam.p = newparam;
11116#if ENABLE_ASH_GETOPTS
11117 shellparam.optind = 1;
11118 shellparam.optoff = -1;
11119#endif
Eric Andersencb57d552001-06-28 07:25:16 +000011120}
11121
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011122/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000011123 * Process shell options. The global variable argptr contains a pointer
11124 * to the argument list; we advance it past the options.
Denis Vlasenko94e87bc2008-02-14 16:51:58 +000011125 *
11126 * SUSv3 section 2.8.1 "Consequences of Shell Errors" says:
11127 * For a non-interactive shell, an error condition encountered
11128 * by a special built-in ... shall cause the shell to write a diagnostic message
11129 * to standard error and exit as shown in the following table:
Denis Vlasenko56244732008-02-17 15:14:04 +000011130 * Error Special Built-In
Denis Vlasenko94e87bc2008-02-14 16:51:58 +000011131 * ...
11132 * Utility syntax error (option or operand error) Shall exit
11133 * ...
11134 * However, in bug 1142 (http://busybox.net/bugs/view.php?id=1142)
11135 * we see that bash does not do that (set "finishes" with error code 1 instead,
11136 * and shell continues), and people rely on this behavior!
11137 * Testcase:
11138 * set -o barfoo 2>/dev/null
11139 * echo $?
11140 *
11141 * Oh well. Let's mimic that.
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011142 */
Denis Vlasenko28bf6712008-02-14 15:01:47 +000011143static int
Denis Vlasenkodddfaff2008-05-06 15:30:27 +000011144plus_minus_o(char *name, int val)
Eric Andersen62483552001-07-10 06:09:16 +000011145{
11146 int i;
11147
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011148 if (name) {
11149 for (i = 0; i < NOPTS; i++) {
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000011150 if (strcmp(name, optnames(i)) == 0) {
Eric Andersenc470f442003-07-28 09:56:35 +000011151 optlist[i] = val;
Denis Vlasenko28bf6712008-02-14 15:01:47 +000011152 return 0;
Eric Andersen62483552001-07-10 06:09:16 +000011153 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011154 }
Denis Vlasenkodddfaff2008-05-06 15:30:27 +000011155 ash_msg("illegal option %co %s", val ? '-' : '+', name);
Denis Vlasenko28bf6712008-02-14 15:01:47 +000011156 return 1;
Eric Andersen62483552001-07-10 06:09:16 +000011157 }
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000011158 for (i = 0; i < NOPTS; i++) {
Denys Vlasenko2f9c1242019-08-02 16:43:36 +020011159 if (optnames(i)[0] == '\0')
11160 continue;
Denis Vlasenkodddfaff2008-05-06 15:30:27 +000011161 if (val) {
11162 out1fmt("%-16s%s\n", optnames(i), optlist[i] ? "on" : "off");
11163 } else {
11164 out1fmt("set %co %s\n", optlist[i] ? '-' : '+', optnames(i));
11165 }
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000011166 }
Denis Vlasenko28bf6712008-02-14 15:01:47 +000011167 return 0;
Eric Andersen62483552001-07-10 06:09:16 +000011168}
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011169static void
11170setoption(int flag, int val)
11171{
11172 int i;
11173
11174 for (i = 0; i < NOPTS; i++) {
Denys Vlasenkof3634582019-06-03 12:21:04 +020011175 if (optletters(i) == flag && optnames(i)[0] != '\0') {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011176 optlist[i] = val;
11177 return;
11178 }
11179 }
Denis Vlasenkodddfaff2008-05-06 15:30:27 +000011180 ash_msg_and_raise_error("illegal option %c%c", val ? '-' : '+', flag);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011181 /* NOTREACHED */
11182}
Denys Vlasenko897475a2019-06-01 16:35:09 +020011183/* If login_sh is not NULL, we are called to parse command line opts,
11184 * not "set -opts"
11185 */
Denis Vlasenko28bf6712008-02-14 15:01:47 +000011186static int
Denys Vlasenko897475a2019-06-01 16:35:09 +020011187options(int *login_sh)
Eric Andersencb57d552001-06-28 07:25:16 +000011188{
11189 char *p;
11190 int val;
11191 int c;
11192
Denys Vlasenko897475a2019-06-01 16:35:09 +020011193 if (login_sh)
Eric Andersencb57d552001-06-28 07:25:16 +000011194 minusc = NULL;
11195 while ((p = *argptr) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011196 c = *p++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +000011197 if (c != '-' && c != '+')
11198 break;
11199 argptr++;
11200 val = 0; /* val = 0 if c == '+' */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011201 if (c == '-') {
Eric Andersencb57d552001-06-28 07:25:16 +000011202 val = 1;
Denis Vlasenko9f739442006-12-16 23:49:13 +000011203 if (p[0] == '\0' || LONE_DASH(p)) {
Denys Vlasenko897475a2019-06-01 16:35:09 +020011204 if (!login_sh) {
Eric Andersen2870d962001-07-02 17:27:21 +000011205 /* "-" means turn off -x and -v */
11206 if (p[0] == '\0')
11207 xflag = vflag = 0;
11208 /* "--" means reset params */
11209 else if (*argptr == NULL)
Eric Andersencb57d552001-06-28 07:25:16 +000011210 setparam(argptr);
Eric Andersen2870d962001-07-02 17:27:21 +000011211 }
Denys Vlasenkob0b83432011-03-07 12:34:59 +010011212 break; /* "-" or "--" terminates options */
Eric Andersencb57d552001-06-28 07:25:16 +000011213 }
Eric Andersencb57d552001-06-28 07:25:16 +000011214 }
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +000011215 /* first char was + or - */
Eric Andersencb57d552001-06-28 07:25:16 +000011216 while ((c = *p++) != '\0') {
Denys Vlasenko897475a2019-06-01 16:35:09 +020011217 if (login_sh) {
11218 /* bash 3.2 indeed handles -c CMD and +c CMD the same */
11219 if (c == 'c') {
11220 minusc = p; /* command is after shell args */
Denys Vlasenkof3634582019-06-03 12:21:04 +020011221 cflag = 1;
11222 continue;
11223 }
11224 if (c == 's') { /* -s, +s */
11225 sflag = 1;
11226 continue;
11227 }
11228 if (c == 'i') { /* -i, +i */
11229 iflag = 1;
Denys Vlasenko897475a2019-06-01 16:35:09 +020011230 continue;
11231 }
11232 if (c == 'l') {
11233 *login_sh = 1; /* -l or +l == --login */
11234 continue;
11235 }
11236 /* bash does not accept +-login, we also won't */
11237 if (val && (c == '-')) { /* long options */
11238 if (strcmp(p, "login") == 0) {
11239 *login_sh = 1;
11240 }
11241 break;
11242 }
11243 }
11244 if (c == 'o') {
Denis Vlasenkodddfaff2008-05-06 15:30:27 +000011245 if (plus_minus_o(*argptr, val)) {
Denis Vlasenko28bf6712008-02-14 15:01:47 +000011246 /* it already printed err message */
11247 return 1; /* error */
11248 }
Eric Andersencb57d552001-06-28 07:25:16 +000011249 if (*argptr)
11250 argptr++;
11251 } else {
11252 setoption(c, val);
11253 }
11254 }
11255 }
Denis Vlasenko28bf6712008-02-14 15:01:47 +000011256 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +000011257}
11258
Eric Andersencb57d552001-06-28 07:25:16 +000011259/*
Eric Andersencb57d552001-06-28 07:25:16 +000011260 * The shift builtin command.
11261 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020011262static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000011263shiftcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000011264{
11265 int n;
11266 char **ap1, **ap2;
11267
11268 n = 1;
Denis Vlasenko68404f12008-03-17 09:00:54 +000011269 if (argv[1])
Eric Andersencb57d552001-06-28 07:25:16 +000011270 n = number(argv[1]);
11271 if (n > shellparam.nparam)
Ingo van Lil9c8e94b2018-01-05 15:04:23 +010011272 return 1;
Denis Vlasenkob012b102007-02-19 22:43:01 +000011273 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +000011274 shellparam.nparam -= n;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011275 for (ap1 = shellparam.p; --n >= 0; ap1++) {
Denis Vlasenko01631112007-12-16 17:20:38 +000011276 if (shellparam.malloced)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011277 free(*ap1);
Eric Andersencb57d552001-06-28 07:25:16 +000011278 }
11279 ap2 = shellparam.p;
Denis Vlasenkof7d56652008-03-25 05:51:41 +000011280 while ((*ap2++ = *ap1++) != NULL)
11281 continue;
Denis Vlasenko131ae172007-02-18 13:00:19 +000011282#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +000011283 shellparam.optind = 1;
11284 shellparam.optoff = -1;
Eric Andersenc470f442003-07-28 09:56:35 +000011285#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +000011286 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +000011287 return 0;
11288}
11289
Eric Andersencb57d552001-06-28 07:25:16 +000011290/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011291 * POSIX requires that 'set' (but not export or readonly) output the
11292 * variables in lexicographic order - by the locale's collating order (sigh).
11293 * Maybe we could keep them in an ordered balanced binary tree
11294 * instead of hashed lists.
11295 * For now just roll 'em through qsort for printing...
11296 */
11297static int
11298showvars(const char *sep_prefix, int on, int off)
11299{
11300 const char *sep;
11301 char **ep, **epend;
11302
Denys Vlasenkoa5060b82017-11-03 14:16:25 +010011303 ep = listvars(on, off, /*strlist:*/ NULL, &epend);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011304 qsort(ep, epend - ep, sizeof(char *), vpcmp);
11305
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +000011306 sep = *sep_prefix ? " " : sep_prefix;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011307
11308 for (; ep < epend; ep++) {
11309 const char *p;
11310 const char *q;
11311
Denys Vlasenko9c143ce2017-11-02 12:56:24 +010011312 p = endofname(*ep);
11313/* Used to have simple "p = strchrnul(*ep, '=')" here instead, but this
11314 * makes "export -p" to have output not suitable for "eval":
11315 * import os
11316 * os.environ["test-test"]="test"
11317 * if os.fork() == 0:
11318 * os.execv("ash", [ 'ash', '-c', 'eval $(export -p); echo OK' ]) # fixes this
11319 * os.execv("ash", [ 'ash', '-c', 'env | grep test-test' ])
11320 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011321 q = nullstr;
Denys Vlasenko9c143ce2017-11-02 12:56:24 +010011322 if (*p == '=')
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011323 q = single_quote(++p);
11324 out1fmt("%s%s%.*s%s\n", sep_prefix, sep, (int)(p - *ep), *ep, q);
11325 }
11326 return 0;
11327}
11328
11329/*
Eric Andersencb57d552001-06-28 07:25:16 +000011330 * The set command builtin.
11331 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020011332static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000011333setcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +000011334{
Denis Vlasenko28bf6712008-02-14 15:01:47 +000011335 int retval;
11336
Denis Vlasenko68404f12008-03-17 09:00:54 +000011337 if (!argv[1])
Eric Andersenc470f442003-07-28 09:56:35 +000011338 return showvars(nullstr, 0, VUNSET);
Denys Vlasenkob0b83432011-03-07 12:34:59 +010011339
Denis Vlasenkob012b102007-02-19 22:43:01 +000011340 INT_OFF;
Denys Vlasenko897475a2019-06-01 16:35:09 +020011341 retval = options(/*login_sh:*/ NULL);
Denys Vlasenkob0b83432011-03-07 12:34:59 +010011342 if (retval == 0) { /* if no parse error... */
Denis Vlasenko28bf6712008-02-14 15:01:47 +000011343 optschanged();
11344 if (*argptr != NULL) {
11345 setparam(argptr);
11346 }
Eric Andersencb57d552001-06-28 07:25:16 +000011347 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000011348 INT_ON;
Denis Vlasenko28bf6712008-02-14 15:01:47 +000011349 return retval;
Eric Andersencb57d552001-06-28 07:25:16 +000011350}
11351
Denis Vlasenko131ae172007-02-18 13:00:19 +000011352#if ENABLE_ASH_RANDOM_SUPPORT
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020011353static void FAST_FUNC
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000011354change_random(const char *value)
Eric Andersenef02f822004-03-11 13:34:24 +000011355{
Denys Vlasenko3ea2e822009-10-09 20:59:04 +020011356 uint32_t t;
11357
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011358 if (value == NULL) {
Eric Andersen16767e22004-03-16 05:14:10 +000011359 /* "get", generate */
Denys Vlasenko3ea2e822009-10-09 20:59:04 +020011360 t = next_random(&random_gen);
Eric Andersen16767e22004-03-16 05:14:10 +000011361 /* set without recursion */
Denys Vlasenko8837c5d2010-06-02 12:56:18 +020011362 setvar(vrandom.var_text, utoa(t), VNOFUNC);
Eric Andersen16767e22004-03-16 05:14:10 +000011363 vrandom.flags &= ~VNOFUNC;
11364 } else {
11365 /* set/reset */
Denys Vlasenko3ea2e822009-10-09 20:59:04 +020011366 t = strtoul(value, NULL, 10);
11367 INIT_RANDOM_T(&random_gen, (t ? t : 1), t);
Eric Andersen16767e22004-03-16 05:14:10 +000011368 }
Eric Andersenef02f822004-03-11 13:34:24 +000011369}
Eric Andersen16767e22004-03-16 05:14:10 +000011370#endif
11371
Ron Yorston1d371862019-04-15 10:52:05 +010011372#if BASH_EPOCH_VARS
11373static void FAST_FUNC
11374change_epoch(struct var *vepoch, const char *fmt)
11375{
11376 struct timeval tv;
Denys Vlasenko3c13da32020-12-30 23:48:01 +010011377 char buffer[sizeof("%llu.nnnnnn") + sizeof(long long)*3];
Ron Yorston1d371862019-04-15 10:52:05 +010011378
Denys Vlasenko3c13da32020-12-30 23:48:01 +010011379 xgettimeofday(&tv);
11380 sprintf(buffer, fmt, (unsigned long long)tv.tv_sec, (unsigned)tv.tv_usec);
Ron Yorston1d371862019-04-15 10:52:05 +010011381 setvar(vepoch->var_text, buffer, VNOFUNC);
11382 vepoch->flags &= ~VNOFUNC;
11383}
11384
11385static void FAST_FUNC
11386change_seconds(const char *value UNUSED_PARAM)
11387{
Denys Vlasenko3c13da32020-12-30 23:48:01 +010011388 change_epoch(&vepochs, "%llu");
Ron Yorston1d371862019-04-15 10:52:05 +010011389}
11390
11391static void FAST_FUNC
11392change_realtime(const char *value UNUSED_PARAM)
11393{
Denys Vlasenko3c13da32020-12-30 23:48:01 +010011394 change_epoch(&vepochr, "%llu.%06u");
Ron Yorston1d371862019-04-15 10:52:05 +010011395}
11396#endif
11397
Denis Vlasenko131ae172007-02-18 13:00:19 +000011398#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +000011399static int
Denys Vlasenko35c2a132016-10-26 17:34:26 +020011400getopts(char *optstr, char *optvar, char **optfirst)
Eric Andersencb57d552001-06-28 07:25:16 +000011401{
11402 char *p, *q;
11403 char c = '?';
11404 int done = 0;
Denys Vlasenko9c541002015-10-07 15:44:36 +020011405 char sbuf[2];
Eric Andersena48b0a32003-10-22 10:56:47 +000011406 char **optnext;
Denys Vlasenkodbef38a2016-10-26 17:54:32 +020011407 int ind = shellparam.optind;
11408 int off = shellparam.optoff;
Eric Andersencb57d552001-06-28 07:25:16 +000011409
Denys Vlasenko9c541002015-10-07 15:44:36 +020011410 sbuf[1] = '\0';
11411
Denys Vlasenkodbef38a2016-10-26 17:54:32 +020011412 shellparam.optind = -1;
11413 optnext = optfirst + ind - 1;
Eric Andersena48b0a32003-10-22 10:56:47 +000011414
Denys Vlasenkodbef38a2016-10-26 17:54:32 +020011415 if (ind <= 1 || off < 0 || (int)strlen(optnext[-1]) < off)
Eric Andersencb57d552001-06-28 07:25:16 +000011416 p = NULL;
Denys Vlasenkodbef38a2016-10-26 17:54:32 +020011417 else
11418 p = optnext[-1] + off;
Eric Andersencb57d552001-06-28 07:25:16 +000011419 if (p == NULL || *p == '\0') {
11420 /* Current word is done, advance */
Eric Andersencb57d552001-06-28 07:25:16 +000011421 p = *optnext;
11422 if (p == NULL || *p != '-' || *++p == '\0') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011423 atend:
Denys Vlasenko9832bba2017-08-15 15:44:41 +020011424 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +000011425 p = NULL;
11426 done = 1;
11427 goto out;
11428 }
11429 optnext++;
Denis Vlasenko9f739442006-12-16 23:49:13 +000011430 if (LONE_DASH(p)) /* check for "--" */
Eric Andersencb57d552001-06-28 07:25:16 +000011431 goto atend;
11432 }
11433
11434 c = *p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +000011435 for (q = optstr; *q != c;) {
Eric Andersencb57d552001-06-28 07:25:16 +000011436 if (*q == '\0') {
Denys Vlasenko9832bba2017-08-15 15:44:41 +020011437 /* OPTERR is a bashism */
11438 const char *cp = lookupvar("OPTERR");
11439 if ((cp && LONE_CHAR(cp, '0'))
11440 || (optstr[0] == ':')
11441 ) {
Denys Vlasenko9c541002015-10-07 15:44:36 +020011442 sbuf[0] = c;
11443 /*sbuf[1] = '\0'; - already is */
Denys Vlasenkodbef38a2016-10-26 17:54:32 +020011444 setvar0("OPTARG", sbuf);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011445 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000011446 fprintf(stderr, "Illegal option -%c\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011447 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +000011448 }
11449 c = '?';
Eric Andersenc470f442003-07-28 09:56:35 +000011450 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +000011451 }
11452 if (*++q == ':')
11453 q++;
11454 }
11455
11456 if (*++q == ':') {
11457 if (*p == '\0' && (p = *optnext) == NULL) {
Denys Vlasenko9832bba2017-08-15 15:44:41 +020011458 /* OPTERR is a bashism */
11459 const char *cp = lookupvar("OPTERR");
11460 if ((cp && LONE_CHAR(cp, '0'))
11461 || (optstr[0] == ':')
11462 ) {
Denys Vlasenko9c541002015-10-07 15:44:36 +020011463 sbuf[0] = c;
11464 /*sbuf[1] = '\0'; - already is */
Denys Vlasenkodbef38a2016-10-26 17:54:32 +020011465 setvar0("OPTARG", sbuf);
Eric Andersencb57d552001-06-28 07:25:16 +000011466 c = ':';
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011467 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000011468 fprintf(stderr, "No arg for -%c option\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011469 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +000011470 c = '?';
11471 }
Eric Andersenc470f442003-07-28 09:56:35 +000011472 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +000011473 }
11474
11475 if (p == *optnext)
11476 optnext++;
Denys Vlasenkodbef38a2016-10-26 17:54:32 +020011477 setvar0("OPTARG", p);
Eric Andersencb57d552001-06-28 07:25:16 +000011478 p = NULL;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011479 } else
Denys Vlasenkodbef38a2016-10-26 17:54:32 +020011480 setvar0("OPTARG", nullstr);
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011481 out:
Denys Vlasenkodbef38a2016-10-26 17:54:32 +020011482 ind = optnext - optfirst + 1;
11483 setvar("OPTIND", itoa(ind), VNOFUNC);
Denys Vlasenko9c541002015-10-07 15:44:36 +020011484 sbuf[0] = c;
11485 /*sbuf[1] = '\0'; - already is */
Denys Vlasenkodbef38a2016-10-26 17:54:32 +020011486 setvar0(optvar, sbuf);
11487
11488 shellparam.optoff = p ? p - *(optnext - 1) : -1;
11489 shellparam.optind = ind;
11490
Eric Andersencb57d552001-06-28 07:25:16 +000011491 return done;
11492}
Eric Andersenc470f442003-07-28 09:56:35 +000011493
11494/*
11495 * The getopts builtin. Shellparam.optnext points to the next argument
11496 * to be processed. Shellparam.optptr points to the next character to
11497 * be processed in the current argument. If shellparam.optnext is NULL,
11498 * then it's the first time getopts has been called.
11499 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020011500static int FAST_FUNC
Eric Andersenc470f442003-07-28 09:56:35 +000011501getoptscmd(int argc, char **argv)
11502{
11503 char **optbase;
11504
11505 if (argc < 3)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +000011506 ash_msg_and_raise_error("usage: getopts optstring var [arg]");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011507 if (argc == 3) {
Eric Andersenc470f442003-07-28 09:56:35 +000011508 optbase = shellparam.p;
Denys Vlasenkodbef38a2016-10-26 17:54:32 +020011509 if ((unsigned)shellparam.optind > shellparam.nparam + 1) {
Eric Andersenc470f442003-07-28 09:56:35 +000011510 shellparam.optind = 1;
11511 shellparam.optoff = -1;
11512 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011513 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000011514 optbase = &argv[3];
Denys Vlasenkodbef38a2016-10-26 17:54:32 +020011515 if ((unsigned)shellparam.optind > argc - 2) {
Eric Andersenc470f442003-07-28 09:56:35 +000011516 shellparam.optind = 1;
11517 shellparam.optoff = -1;
11518 }
11519 }
11520
Denys Vlasenko35c2a132016-10-26 17:34:26 +020011521 return getopts(argv[1], argv[2], optbase);
Eric Andersenc470f442003-07-28 09:56:35 +000011522}
Denis Vlasenko131ae172007-02-18 13:00:19 +000011523#endif /* ASH_GETOPTS */
Eric Andersencb57d552001-06-28 07:25:16 +000011524
Eric Andersencb57d552001-06-28 07:25:16 +000011525
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011526/* ============ Shell parser */
Eric Andersencb57d552001-06-28 07:25:16 +000011527
Denis Vlasenkob07a4962008-06-22 13:16:23 +000011528struct heredoc {
11529 struct heredoc *next; /* next here document in list */
11530 union node *here; /* redirection node */
11531 char *eofmark; /* string indicating end of input */
11532 smallint striptabs; /* if set, strip leading tabs */
11533};
11534
11535static smallint tokpushback; /* last token pushed back */
Denis Vlasenkob07a4962008-06-22 13:16:23 +000011536static smallint quoteflag; /* set if (part of) last token was quoted */
11537static token_id_t lasttoken; /* last token read (integer id Txxx) */
11538static struct heredoc *heredoclist; /* list of here documents to read */
11539static char *wordtext; /* text of last word returned by readtoken */
11540static struct nodelist *backquotelist;
11541static union node *redirnode;
11542static struct heredoc *heredoc;
Denis Vlasenko99eb8502007-02-23 21:09:49 +000011543
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020011544static const char *
11545tokname(char *buf, int tok)
11546{
11547 if (tok < TSEMI)
Denys Vlasenko888527c2016-10-02 16:54:17 +020011548 return tokname_array[tok];
11549 sprintf(buf, "\"%s\"", tokname_array[tok]);
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020011550 return buf;
11551}
11552
11553/* raise_error_unexpected_syntax:
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011554 * Called when an unexpected token is read during the parse. The argument
11555 * is the token that is expected, or -1 if more than one type of token can
11556 * occur at this point.
11557 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000011558static void raise_error_unexpected_syntax(int) NORETURN;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011559static void
11560raise_error_unexpected_syntax(int token)
11561{
11562 char msg[64];
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020011563 char buf[16];
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011564 int l;
11565
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020011566 l = sprintf(msg, "unexpected %s", tokname(buf, lasttoken));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011567 if (token >= 0)
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020011568 sprintf(msg + l, " (expecting %s)", tokname(buf, token));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011569 raise_error_syntax(msg);
11570 /* NOTREACHED */
11571}
Eric Andersencb57d552001-06-28 07:25:16 +000011572
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011573/* parsing is heavily cross-recursive, need these forward decls */
11574static union node *andor(void);
11575static union node *pipeline(void);
11576static union node *parse_command(void);
11577static void parseheredoc(void);
Ron Yorstonc0e00762015-10-29 11:30:55 +000011578static int peektoken(void);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011579static int readtoken(void);
Eric Andersencb57d552001-06-28 07:25:16 +000011580
Eric Andersenc470f442003-07-28 09:56:35 +000011581static union node *
11582list(int nlflag)
Eric Andersencb57d552001-06-28 07:25:16 +000011583{
11584 union node *n1, *n2, *n3;
11585 int tok;
11586
Eric Andersencb57d552001-06-28 07:25:16 +000011587 n1 = NULL;
11588 for (;;) {
Denys Vlasenkoc08993f2020-02-22 17:26:23 +010011589 switch (readtoken()) {
Ron Yorstonc0e00762015-10-29 11:30:55 +000011590 case TNL:
11591 if (!(nlflag & 1))
11592 break;
11593 parseheredoc();
11594 return n1;
11595
11596 case TEOF:
11597 if (!n1 && (nlflag & 1))
11598 n1 = NODE_EOF;
11599 parseheredoc();
Denys Vlasenkoc08993f2020-02-22 17:26:23 +010011600 tokpushback++;
11601 lasttoken = TEOF;
Ron Yorstonc0e00762015-10-29 11:30:55 +000011602 return n1;
11603 }
11604
Denys Vlasenkoc08993f2020-02-22 17:26:23 +010011605 tokpushback++;
Ron Yorstonc0e00762015-10-29 11:30:55 +000011606 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Denys Vlasenko888527c2016-10-02 16:54:17 +020011607 if (nlflag == 2 && ((1 << peektoken()) & tokendlist))
Ron Yorstonc0e00762015-10-29 11:30:55 +000011608 return n1;
11609 nlflag |= 2;
11610
Eric Andersencb57d552001-06-28 07:25:16 +000011611 n2 = andor();
11612 tok = readtoken();
11613 if (tok == TBACKGND) {
Eric Andersenc470f442003-07-28 09:56:35 +000011614 if (n2->type == NPIPE) {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000011615 n2->npipe.pipe_backgnd = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000011616 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000011617 if (n2->type != NREDIR) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000011618 n3 = stzalloc(sizeof(struct nredir));
Eric Andersenc470f442003-07-28 09:56:35 +000011619 n3->nredir.n = n2;
Denis Vlasenko597906c2008-02-20 16:38:54 +000011620 /*n3->nredir.redirect = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000011621 n2 = n3;
11622 }
11623 n2->type = NBACKGND;
Eric Andersencb57d552001-06-28 07:25:16 +000011624 }
11625 }
11626 if (n1 == NULL) {
11627 n1 = n2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011628 } else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011629 n3 = stzalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +000011630 n3->type = NSEMI;
11631 n3->nbinary.ch1 = n1;
11632 n3->nbinary.ch2 = n2;
11633 n1 = n3;
11634 }
11635 switch (tok) {
Ron Yorstonc0e00762015-10-29 11:30:55 +000011636 case TNL:
11637 case TEOF:
11638 tokpushback = 1;
11639 /* fall through */
Eric Andersencb57d552001-06-28 07:25:16 +000011640 case TBACKGND:
11641 case TSEMI:
Eric Andersencb57d552001-06-28 07:25:16 +000011642 break;
Eric Andersencb57d552001-06-28 07:25:16 +000011643 default:
Ron Yorstonc0e00762015-10-29 11:30:55 +000011644 if ((nlflag & 1))
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011645 raise_error_unexpected_syntax(-1);
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011646 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000011647 return n1;
11648 }
11649 }
11650}
11651
Eric Andersenc470f442003-07-28 09:56:35 +000011652static union node *
11653andor(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011654{
Eric Andersencb57d552001-06-28 07:25:16 +000011655 union node *n1, *n2, *n3;
11656 int t;
11657
Eric Andersencb57d552001-06-28 07:25:16 +000011658 n1 = pipeline();
11659 for (;;) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011660 t = readtoken();
11661 if (t == TAND) {
Eric Andersencb57d552001-06-28 07:25:16 +000011662 t = NAND;
11663 } else if (t == TOR) {
11664 t = NOR;
11665 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011666 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000011667 return n1;
11668 }
Eric Andersenc470f442003-07-28 09:56:35 +000011669 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000011670 n2 = pipeline();
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011671 n3 = stzalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +000011672 n3->type = t;
11673 n3->nbinary.ch1 = n1;
11674 n3->nbinary.ch2 = n2;
11675 n1 = n3;
11676 }
11677}
11678
Eric Andersenc470f442003-07-28 09:56:35 +000011679static union node *
11680pipeline(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011681{
Eric Andersencb57d552001-06-28 07:25:16 +000011682 union node *n1, *n2, *pipenode;
11683 struct nodelist *lp, *prev;
11684 int negate;
11685
11686 negate = 0;
11687 TRACE(("pipeline: entered\n"));
11688 if (readtoken() == TNOT) {
11689 negate = !negate;
Eric Andersenc470f442003-07-28 09:56:35 +000011690 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000011691 } else
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011692 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011693 n1 = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +000011694 if (readtoken() == TPIPE) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000011695 pipenode = stzalloc(sizeof(struct npipe));
Eric Andersencb57d552001-06-28 07:25:16 +000011696 pipenode->type = NPIPE;
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000011697 /*pipenode->npipe.pipe_backgnd = 0; - stzalloc did it */
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011698 lp = stzalloc(sizeof(struct nodelist));
Eric Andersencb57d552001-06-28 07:25:16 +000011699 pipenode->npipe.cmdlist = lp;
11700 lp->n = n1;
11701 do {
11702 prev = lp;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011703 lp = stzalloc(sizeof(struct nodelist));
Eric Andersenc470f442003-07-28 09:56:35 +000011704 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011705 lp->n = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +000011706 prev->next = lp;
11707 } while (readtoken() == TPIPE);
11708 lp->next = NULL;
11709 n1 = pipenode;
11710 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011711 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000011712 if (negate) {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011713 n2 = stzalloc(sizeof(struct nnot));
Eric Andersencb57d552001-06-28 07:25:16 +000011714 n2->type = NNOT;
11715 n2->nnot.com = n1;
11716 return n2;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011717 }
11718 return n1;
Eric Andersencb57d552001-06-28 07:25:16 +000011719}
11720
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011721static union node *
11722makename(void)
11723{
11724 union node *n;
11725
Denis Vlasenko597906c2008-02-20 16:38:54 +000011726 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011727 n->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000011728 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011729 n->narg.text = wordtext;
11730 n->narg.backquote = backquotelist;
11731 return n;
11732}
11733
11734static void
11735fixredir(union node *n, const char *text, int err)
11736{
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000011737 int fd;
11738
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011739 TRACE(("Fix redir %s %d\n", text, err));
11740 if (!err)
11741 n->ndup.vname = NULL;
11742
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000011743 fd = bb_strtou(text, NULL, 10);
11744 if (!errno && fd >= 0)
11745 n->ndup.dupfd = fd;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011746 else if (LONE_DASH(text))
11747 n->ndup.dupfd = -1;
11748 else {
11749 if (err)
Denis Vlasenko559691a2008-10-05 18:39:31 +000011750 raise_error_syntax("bad fd number");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011751 n->ndup.vname = makename();
11752 }
11753}
11754
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011755static void
11756parsefname(void)
11757{
11758 union node *n = redirnode;
11759
Denys Vlasenkoa7328982017-07-29 19:57:28 +020011760 if (n->type == NHERE)
11761 checkkwd = CHKEOFMARK;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011762 if (readtoken() != TWORD)
11763 raise_error_unexpected_syntax(-1);
11764 if (n->type == NHERE) {
11765 struct heredoc *here = heredoc;
11766 struct heredoc *p;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011767
11768 if (quoteflag == 0)
11769 n->type = NXHERE;
11770 TRACE(("Here document %d\n", n->type));
Denys Vlasenko740058b2018-01-09 17:01:00 +010011771 rmescapes(wordtext, 0, NULL);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011772 here->eofmark = wordtext;
11773 here->next = NULL;
11774 if (heredoclist == NULL)
11775 heredoclist = here;
11776 else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011777 for (p = heredoclist; p->next; p = p->next)
11778 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011779 p->next = here;
11780 }
11781 } else if (n->type == NTOFD || n->type == NFROMFD) {
11782 fixredir(n, wordtext, 0);
11783 } else {
11784 n->nfile.fname = makename();
11785 }
11786}
Eric Andersencb57d552001-06-28 07:25:16 +000011787
Eric Andersenc470f442003-07-28 09:56:35 +000011788static union node *
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011789simplecmd(void)
11790{
11791 union node *args, **app;
11792 union node *n = NULL;
11793 union node *vars, **vpp;
11794 union node **rpp, *redir;
11795 int savecheckkwd;
Denys Vlasenko675d24a2018-01-27 22:02:05 +010011796 int savelinno;
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010011797#if BASH_TEST2
Denis Vlasenko80591b02008-03-25 07:49:43 +000011798 smallint double_brackets_flag = 0;
11799#endif
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010011800 IF_BASH_FUNCTION(smallint function_flag = 0;)
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011801
11802 args = NULL;
11803 app = &args;
11804 vars = NULL;
11805 vpp = &vars;
11806 redir = NULL;
11807 rpp = &redir;
11808
11809 savecheckkwd = CHKALIAS;
Denys Vlasenko675d24a2018-01-27 22:02:05 +010011810 savelinno = g_parsefile->linno;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011811 for (;;) {
Denis Vlasenko80591b02008-03-25 07:49:43 +000011812 int t;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011813 checkkwd = savecheckkwd;
Denis Vlasenko80591b02008-03-25 07:49:43 +000011814 t = readtoken();
11815 switch (t) {
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010011816#if BASH_FUNCTION
Ron Yorston95ebcf72015-11-03 09:42:23 +000011817 case TFUNCTION:
11818 if (peektoken() != TWORD)
11819 raise_error_unexpected_syntax(TWORD);
11820 function_flag = 1;
11821 break;
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010011822#endif
11823#if BASH_TEST2
Denis Vlasenko80591b02008-03-25 07:49:43 +000011824 case TAND: /* "&&" */
11825 case TOR: /* "||" */
11826 if (!double_brackets_flag) {
11827 tokpushback = 1;
11828 goto out;
11829 }
Denys Vlasenkod2241f52020-10-31 03:34:07 +010011830 /* pass "&&" or "||" to [[ ]] as literal args */
11831 wordtext = (char *) (t == TAND ? "&&" : "||");
Denis Vlasenko80591b02008-03-25 07:49:43 +000011832#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011833 case TWORD:
Denis Vlasenko597906c2008-02-20 16:38:54 +000011834 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011835 n->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000011836 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011837 n->narg.text = wordtext;
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010011838#if BASH_TEST2
Denis Vlasenko80591b02008-03-25 07:49:43 +000011839 if (strcmp("[[", wordtext) == 0)
11840 double_brackets_flag = 1;
11841 else if (strcmp("]]", wordtext) == 0)
11842 double_brackets_flag = 0;
11843#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011844 n->narg.backquote = backquotelist;
11845 if (savecheckkwd && isassignment(wordtext)) {
11846 *vpp = n;
11847 vpp = &n->narg.next;
11848 } else {
11849 *app = n;
11850 app = &n->narg.next;
11851 savecheckkwd = 0;
11852 }
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010011853#if BASH_FUNCTION
Ron Yorston95ebcf72015-11-03 09:42:23 +000011854 if (function_flag) {
11855 checkkwd = CHKNL | CHKKWD;
11856 switch (peektoken()) {
11857 case TBEGIN:
11858 case TIF:
11859 case TCASE:
11860 case TUNTIL:
11861 case TWHILE:
11862 case TFOR:
11863 goto do_func;
11864 case TLP:
11865 function_flag = 0;
11866 break;
Denys Vlasenkoe93031e2018-04-10 01:23:19 +020011867# if BASH_TEST2
Ron Yorston95ebcf72015-11-03 09:42:23 +000011868 case TWORD:
11869 if (strcmp("[[", wordtext) == 0)
11870 goto do_func;
11871 /* fall through */
Denys Vlasenkoe93031e2018-04-10 01:23:19 +020011872# endif
Ron Yorston95ebcf72015-11-03 09:42:23 +000011873 default:
11874 raise_error_unexpected_syntax(-1);
11875 }
11876 }
11877#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011878 break;
11879 case TREDIR:
11880 *rpp = n = redirnode;
11881 rpp = &n->nfile.next;
11882 parsefname(); /* read name of redirection file */
11883 break;
11884 case TLP:
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010011885 IF_BASH_FUNCTION(do_func:)
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011886 if (args && app == &args->narg.next
11887 && !vars && !redir
11888 ) {
11889 struct builtincmd *bcmd;
11890 const char *name;
11891
11892 /* We have a function */
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010011893 if (IF_BASH_FUNCTION(!function_flag &&) readtoken() != TRP)
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011894 raise_error_unexpected_syntax(TRP);
11895 name = n->narg.text;
11896 if (!goodname(name)
11897 || ((bcmd = find_builtin(name)) && IS_BUILTIN_SPECIAL(bcmd))
11898 ) {
Denis Vlasenko559691a2008-10-05 18:39:31 +000011899 raise_error_syntax("bad function name");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011900 }
11901 n->type = NDEFUN;
11902 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Denys Vlasenko675d24a2018-01-27 22:02:05 +010011903 n->ndefun.text = n->narg.text;
11904 n->ndefun.linno = g_parsefile->linno;
11905 n->ndefun.body = parse_command();
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011906 return n;
11907 }
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010011908 IF_BASH_FUNCTION(function_flag = 0;)
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011909 /* fall through */
11910 default:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011911 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011912 goto out;
11913 }
11914 }
11915 out:
11916 *app = NULL;
11917 *vpp = NULL;
11918 *rpp = NULL;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011919 n = stzalloc(sizeof(struct ncmd));
Denys Vlasenko57b7efb2018-04-10 01:20:26 +020011920 if (NCMD != 0)
11921 n->type = NCMD;
Denys Vlasenko675d24a2018-01-27 22:02:05 +010011922 n->ncmd.linno = savelinno;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011923 n->ncmd.args = args;
11924 n->ncmd.assign = vars;
11925 n->ncmd.redirect = redir;
11926 return n;
11927}
11928
11929static union node *
11930parse_command(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011931{
Eric Andersencb57d552001-06-28 07:25:16 +000011932 union node *n1, *n2;
11933 union node *ap, **app;
11934 union node *cp, **cpp;
11935 union node *redir, **rpp;
Eric Andersenc470f442003-07-28 09:56:35 +000011936 union node **rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +000011937 int t;
Denys Vlasenko675d24a2018-01-27 22:02:05 +010011938 int savelinno;
Eric Andersencb57d552001-06-28 07:25:16 +000011939
11940 redir = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +000011941 rpp2 = &redir;
Eric Andersen88cec252001-09-06 17:35:20 +000011942
Denys Vlasenko675d24a2018-01-27 22:02:05 +010011943 savelinno = g_parsefile->linno;
11944
Eric Andersencb57d552001-06-28 07:25:16 +000011945 switch (readtoken()) {
Eric Andersenc470f442003-07-28 09:56:35 +000011946 default:
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011947 raise_error_unexpected_syntax(-1);
Eric Andersenc470f442003-07-28 09:56:35 +000011948 /* NOTREACHED */
Eric Andersencb57d552001-06-28 07:25:16 +000011949 case TIF:
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011950 n1 = stzalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +000011951 n1->type = NIF;
11952 n1->nif.test = list(0);
11953 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011954 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +000011955 n1->nif.ifpart = list(0);
11956 n2 = n1;
11957 while (readtoken() == TELIF) {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011958 n2->nif.elsepart = stzalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +000011959 n2 = n2->nif.elsepart;
11960 n2->type = NIF;
11961 n2->nif.test = list(0);
11962 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011963 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +000011964 n2->nif.ifpart = list(0);
11965 }
11966 if (lasttoken == TELSE)
11967 n2->nif.elsepart = list(0);
11968 else {
11969 n2->nif.elsepart = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011970 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000011971 }
Eric Andersenc470f442003-07-28 09:56:35 +000011972 t = TFI;
Eric Andersencb57d552001-06-28 07:25:16 +000011973 break;
11974 case TWHILE:
Eric Andersenc470f442003-07-28 09:56:35 +000011975 case TUNTIL: {
Eric Andersencb57d552001-06-28 07:25:16 +000011976 int got;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011977 n1 = stzalloc(sizeof(struct nbinary));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011978 n1->type = (lasttoken == TWHILE) ? NWHILE : NUNTIL;
Eric Andersencb57d552001-06-28 07:25:16 +000011979 n1->nbinary.ch1 = list(0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011980 got = readtoken();
11981 if (got != TDO) {
Denys Vlasenko888527c2016-10-02 16:54:17 +020011982 TRACE(("expecting DO got '%s' %s\n", tokname_array[got],
Denis Vlasenko131ae172007-02-18 13:00:19 +000011983 got == TWORD ? wordtext : ""));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011984 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +000011985 }
11986 n1->nbinary.ch2 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000011987 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +000011988 break;
11989 }
11990 case TFOR:
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000011991 if (readtoken() != TWORD || quoteflag || !goodname(wordtext))
Denis Vlasenko559691a2008-10-05 18:39:31 +000011992 raise_error_syntax("bad for loop variable");
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011993 n1 = stzalloc(sizeof(struct nfor));
Eric Andersencb57d552001-06-28 07:25:16 +000011994 n1->type = NFOR;
Denys Vlasenko675d24a2018-01-27 22:02:05 +010011995 n1->nfor.linno = savelinno;
Eric Andersencb57d552001-06-28 07:25:16 +000011996 n1->nfor.var = wordtext;
Ron Yorstonab80e012015-08-03 13:46:00 +010011997 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000011998 if (readtoken() == TIN) {
11999 app = &ap;
12000 while (readtoken() == TWORD) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000012001 n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000012002 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000012003 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000012004 n2->narg.text = wordtext;
12005 n2->narg.backquote = backquotelist;
12006 *app = n2;
12007 app = &n2->narg.next;
12008 }
12009 *app = NULL;
12010 n1->nfor.args = ap;
12011 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012012 raise_error_unexpected_syntax(-1);
Eric Andersencb57d552001-06-28 07:25:16 +000012013 } else {
Denis Vlasenko597906c2008-02-20 16:38:54 +000012014 n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000012015 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000012016 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000012017 n2->narg.text = (char *)dolatstr;
Denis Vlasenko597906c2008-02-20 16:38:54 +000012018 /*n2->narg.backquote = NULL;*/
Eric Andersencb57d552001-06-28 07:25:16 +000012019 n1->nfor.args = n2;
12020 /*
12021 * Newline or semicolon here is optional (but note
12022 * that the original Bourne shell only allowed NL).
12023 */
Ron Yorstonab80e012015-08-03 13:46:00 +010012024 if (lasttoken != TSEMI)
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000012025 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000012026 }
Eric Andersenc470f442003-07-28 09:56:35 +000012027 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000012028 if (readtoken() != TDO)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012029 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +000012030 n1->nfor.body = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000012031 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +000012032 break;
12033 case TCASE:
Denis Vlasenko838ffd52008-02-21 04:32:08 +000012034 n1 = stzalloc(sizeof(struct ncase));
Eric Andersencb57d552001-06-28 07:25:16 +000012035 n1->type = NCASE;
Denys Vlasenko675d24a2018-01-27 22:02:05 +010012036 n1->ncase.linno = savelinno;
Eric Andersencb57d552001-06-28 07:25:16 +000012037 if (readtoken() != TWORD)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012038 raise_error_unexpected_syntax(TWORD);
Denis Vlasenko597906c2008-02-20 16:38:54 +000012039 n1->ncase.expr = n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000012040 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000012041 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000012042 n2->narg.text = wordtext;
12043 n2->narg.backquote = backquotelist;
Ron Yorston383b8852015-08-03 13:46:25 +010012044 checkkwd = CHKNL | CHKKWD | CHKALIAS;
12045 if (readtoken() != TIN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012046 raise_error_unexpected_syntax(TIN);
Eric Andersencb57d552001-06-28 07:25:16 +000012047 cpp = &n1->ncase.cases;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012048 next_case:
Eric Andersenc470f442003-07-28 09:56:35 +000012049 checkkwd = CHKNL | CHKKWD;
12050 t = readtoken();
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012051 while (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +000012052 if (lasttoken == TLP)
12053 readtoken();
Denis Vlasenko838ffd52008-02-21 04:32:08 +000012054 *cpp = cp = stzalloc(sizeof(struct nclist));
Eric Andersencb57d552001-06-28 07:25:16 +000012055 cp->type = NCLIST;
12056 app = &cp->nclist.pattern;
12057 for (;;) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000012058 *app = ap = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000012059 ap->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000012060 /*ap->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000012061 ap->narg.text = wordtext;
12062 ap->narg.backquote = backquotelist;
Eric Andersenc470f442003-07-28 09:56:35 +000012063 if (readtoken() != TPIPE)
Eric Andersencb57d552001-06-28 07:25:16 +000012064 break;
12065 app = &ap->narg.next;
12066 readtoken();
12067 }
Denis Vlasenko597906c2008-02-20 16:38:54 +000012068 //ap->narg.next = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +000012069 if (lasttoken != TRP)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012070 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +000012071 cp->nclist.body = list(2);
Eric Andersencb57d552001-06-28 07:25:16 +000012072
Eric Andersenc470f442003-07-28 09:56:35 +000012073 cpp = &cp->nclist.next;
12074
12075 checkkwd = CHKNL | CHKKWD;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012076 t = readtoken();
12077 if (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +000012078 if (t != TENDCASE)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012079 raise_error_unexpected_syntax(TENDCASE);
12080 goto next_case;
Eric Andersencb57d552001-06-28 07:25:16 +000012081 }
Eric Andersenc470f442003-07-28 09:56:35 +000012082 }
Eric Andersencb57d552001-06-28 07:25:16 +000012083 *cpp = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +000012084 goto redir;
Eric Andersencb57d552001-06-28 07:25:16 +000012085 case TLP:
Denis Vlasenko597906c2008-02-20 16:38:54 +000012086 n1 = stzalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +000012087 n1->type = NSUBSHELL;
Denys Vlasenko675d24a2018-01-27 22:02:05 +010012088 n1->nredir.linno = savelinno;
Eric Andersencb57d552001-06-28 07:25:16 +000012089 n1->nredir.n = list(0);
Denis Vlasenko597906c2008-02-20 16:38:54 +000012090 /*n1->nredir.redirect = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000012091 t = TRP;
Eric Andersencb57d552001-06-28 07:25:16 +000012092 break;
12093 case TBEGIN:
12094 n1 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000012095 t = TEND;
Eric Andersencb57d552001-06-28 07:25:16 +000012096 break;
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010012097 IF_BASH_FUNCTION(case TFUNCTION:)
Eric Andersencb57d552001-06-28 07:25:16 +000012098 case TWORD:
Eric Andersenc470f442003-07-28 09:56:35 +000012099 case TREDIR:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000012100 tokpushback = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000012101 return simplecmd();
Eric Andersencb57d552001-06-28 07:25:16 +000012102 }
12103
Eric Andersenc470f442003-07-28 09:56:35 +000012104 if (readtoken() != t)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012105 raise_error_unexpected_syntax(t);
Eric Andersenc470f442003-07-28 09:56:35 +000012106
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012107 redir:
Eric Andersencb57d552001-06-28 07:25:16 +000012108 /* Now check for redirection which may follow command */
Eric Andersenc470f442003-07-28 09:56:35 +000012109 checkkwd = CHKKWD | CHKALIAS;
12110 rpp = rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +000012111 while (readtoken() == TREDIR) {
12112 *rpp = n2 = redirnode;
12113 rpp = &n2->nfile.next;
12114 parsefname();
12115 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000012116 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000012117 *rpp = NULL;
12118 if (redir) {
12119 if (n1->type != NSUBSHELL) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000012120 n2 = stzalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +000012121 n2->type = NREDIR;
Denys Vlasenko675d24a2018-01-27 22:02:05 +010012122 n2->nredir.linno = savelinno;
Eric Andersencb57d552001-06-28 07:25:16 +000012123 n2->nredir.n = n1;
12124 n1 = n2;
12125 }
12126 n1->nredir.redirect = redir;
12127 }
Eric Andersencb57d552001-06-28 07:25:16 +000012128 return n1;
12129}
12130
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010012131#if BASH_DOLLAR_SQUOTE
Denys Vlasenko37dc08b2016-10-02 04:38:07 +020012132static int
12133decode_dollar_squote(void)
Denis Vlasenkoef527f52008-06-23 01:52:30 +000012134{
12135 static const char C_escapes[] ALIGN1 = "nrbtfav""x\\01234567";
12136 int c, cnt;
12137 char *p;
12138 char buf[4];
12139
12140 c = pgetc();
12141 p = strchr(C_escapes, c);
12142 if (p) {
12143 buf[0] = c;
12144 p = buf;
12145 cnt = 3;
12146 if ((unsigned char)(c - '0') <= 7) { /* \ooo */
12147 do {
12148 c = pgetc();
12149 *++p = c;
12150 } while ((unsigned char)(c - '0') <= 7 && --cnt);
12151 pungetc();
12152 } else if (c == 'x') { /* \xHH */
12153 do {
12154 c = pgetc();
12155 *++p = c;
12156 } while (isxdigit(c) && --cnt);
12157 pungetc();
12158 if (cnt == 3) { /* \x but next char is "bad" */
12159 c = 'x';
12160 goto unrecognized;
12161 }
12162 } else { /* simple seq like \\ or \t */
12163 p++;
12164 }
12165 *p = '\0';
12166 p = buf;
12167 c = bb_process_escape_sequence((void*)&p);
12168 } else { /* unrecognized "\z": print both chars unless ' or " */
12169 if (c != '\'' && c != '"') {
12170 unrecognized:
12171 c |= 0x100; /* "please encode \, then me" */
12172 }
12173 }
12174 return c;
12175}
12176#endif
12177
Denys Vlasenko46999802017-07-29 21:12:29 +020012178/* Used by expandstr to get here-doc like behaviour. */
12179#define FAKEEOFMARK ((char*)(uintptr_t)1)
12180
12181static ALWAYS_INLINE int
12182realeofmark(const char *eofmark)
12183{
12184 return eofmark && eofmark != FAKEEOFMARK;
12185}
12186
Eric Andersencb57d552001-06-28 07:25:16 +000012187/*
12188 * If eofmark is NULL, read a word or a redirection symbol. If eofmark
12189 * is not NULL, read a here document. In the latter case, eofmark is the
12190 * word which marks the end of the document and striptabs is true if
Denys Vlasenkocd716832009-11-28 22:14:02 +010012191 * leading tabs should be stripped from the document. The argument c
Eric Andersencb57d552001-06-28 07:25:16 +000012192 * is the first character of the input token or document.
12193 *
12194 * Because C does not have internal subroutines, I have simulated them
12195 * using goto's to implement the subroutine linkage. The following macros
12196 * will run code that appears at the end of readtoken1.
12197 */
Eric Andersen2870d962001-07-02 17:27:21 +000012198#define CHECKEND() {goto checkend; checkend_return:;}
12199#define PARSEREDIR() {goto parseredir; parseredir_return:;}
12200#define PARSESUB() {goto parsesub; parsesub_return:;}
12201#define PARSEBACKQOLD() {oldstyle = 1; goto parsebackq; parsebackq_oldreturn:;}
12202#define PARSEBACKQNEW() {oldstyle = 0; goto parsebackq; parsebackq_newreturn:;}
12203#define PARSEARITH() {goto parsearith; parsearith_return:;}
Eric Andersencb57d552001-06-28 07:25:16 +000012204static int
Denys Vlasenkocd716832009-11-28 22:14:02 +010012205readtoken1(int c, int syntax, char *eofmark, int striptabs)
Manuel Novoa III 16815d42001-08-10 19:36:07 +000012206{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000012207 /* NB: syntax parameter fits into smallint */
Denys Vlasenkocd716832009-11-28 22:14:02 +010012208 /* c parameter is an unsigned char or PEOF or PEOA */
Eric Andersencb57d552001-06-28 07:25:16 +000012209 char *out;
Denys Vlasenko50e6d422016-09-30 11:35:54 +020012210 size_t len;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000012211 struct nodelist *bqlist;
12212 smallint quotef;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000012213 smallint oldstyle;
Denis Vlasenko46a53062007-09-24 18:30:02 +000012214 smallint pssyntax; /* we are expanding a prompt string */
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010012215 IF_BASH_DOLLAR_SQUOTE(smallint bash_dollar_squote = 0;)
Denys Vlasenko216913c2018-04-02 12:35:04 +020012216 /* syntax stack */
Denys Vlasenkoee1fd122018-04-04 13:59:53 +020012217 struct synstack synbase = { };
Denys Vlasenko216913c2018-04-02 12:35:04 +020012218 struct synstack *synstack = &synbase;
Denis Vlasenkoef527f52008-06-23 01:52:30 +000012219
Denys Vlasenko5f0a75f2017-07-29 22:58:44 +020012220#if ENABLE_ASH_EXPAND_PRMT
Denis Vlasenko46a53062007-09-24 18:30:02 +000012221 pssyntax = (syntax == PSSYNTAX);
12222 if (pssyntax)
12223 syntax = DQSYNTAX;
Denys Vlasenko5f0a75f2017-07-29 22:58:44 +020012224#else
12225 pssyntax = 0; /* constant */
12226#endif
Denys Vlasenkoee1fd122018-04-04 13:59:53 +020012227 synstack->syntax = syntax;
12228
Denys Vlasenko216913c2018-04-02 12:35:04 +020012229 if (syntax == DQSYNTAX)
12230 synstack->dblquote = 1;
12231 quotef = 0;
12232 bqlist = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +000012233
12234 STARTSTACKSTR(out);
Denis Vlasenko176d49d2008-10-06 09:51:47 +000012235 loop:
12236 /* For each line, until end of word */
Denys Vlasenko958581a2010-09-12 15:04:27 +020012237 CHECKEND(); /* set c to PEOF if at end of here document */
12238 for (;;) { /* until end of line or end of word */
12239 CHECKSTRSPACE(4, out); /* permit 4 calls to USTPUTC */
Denys Vlasenko216913c2018-04-02 12:35:04 +020012240 switch (SIT(c, synstack->syntax)) {
Denys Vlasenko958581a2010-09-12 15:04:27 +020012241 case CNL: /* '\n' */
Denys Vlasenko680c3012018-04-11 12:39:18 +020012242 if (synstack->syntax == BASESYNTAX
12243 && !synstack->varnest
12244 ) {
Denys Vlasenko958581a2010-09-12 15:04:27 +020012245 goto endword; /* exit outer loop */
Denys Vlasenko680c3012018-04-11 12:39:18 +020012246 }
Denys Vlasenko958581a2010-09-12 15:04:27 +020012247 USTPUTC(c, out);
Denys Vlasenkoce332a22016-10-02 23:47:34 +020012248 nlprompt();
Denys Vlasenkof7eea8c2020-02-14 16:16:34 +010012249 c = pgetc_top(synstack);
Denys Vlasenko958581a2010-09-12 15:04:27 +020012250 goto loop; /* continue outer loop */
12251 case CWORD:
12252 USTPUTC(c, out);
12253 break;
12254 case CCTL:
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010012255#if BASH_DOLLAR_SQUOTE
Denys Vlasenko958581a2010-09-12 15:04:27 +020012256 if (c == '\\' && bash_dollar_squote) {
12257 c = decode_dollar_squote();
Denys Vlasenko13f20912016-09-25 20:54:25 +020012258 if (c == '\0') {
12259 /* skip $'\000', $'\x00' (like bash) */
12260 break;
12261 }
Denys Vlasenko958581a2010-09-12 15:04:27 +020012262 if (c & 0x100) {
Denys Vlasenko13f20912016-09-25 20:54:25 +020012263 /* Unknown escape. Encode as '\z' */
Denys Vlasenko958581a2010-09-12 15:04:27 +020012264 c = (unsigned char)c;
Denys Vlasenko216913c2018-04-02 12:35:04 +020012265 if (eofmark == NULL || synstack->dblquote)
Denys Vlasenko13f20912016-09-25 20:54:25 +020012266 USTPUTC(CTLESC, out);
12267 USTPUTC('\\', out);
Denis Vlasenkoef527f52008-06-23 01:52:30 +000012268 }
Denys Vlasenko958581a2010-09-12 15:04:27 +020012269 }
Denis Vlasenkoef527f52008-06-23 01:52:30 +000012270#endif
Denys Vlasenkoc4c20122018-04-02 13:29:20 +020012271 if (!eofmark || synstack->dblquote || synstack->varnest)
Denys Vlasenko13f20912016-09-25 20:54:25 +020012272 USTPUTC(CTLESC, out);
Denys Vlasenko958581a2010-09-12 15:04:27 +020012273 USTPUTC(c, out);
12274 break;
12275 case CBACK: /* backslash */
12276 c = pgetc_without_PEOA();
12277 if (c == PEOF) {
12278 USTPUTC(CTLESC, out);
12279 USTPUTC('\\', out);
12280 pungetc();
Denys Vlasenko958581a2010-09-12 15:04:27 +020012281 } else {
Denys Vlasenko5f0a75f2017-07-29 22:58:44 +020012282 if (pssyntax && c == '$') {
Eric Andersenc470f442003-07-28 09:56:35 +000012283 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000012284 USTPUTC('\\', out);
Denys Vlasenko958581a2010-09-12 15:04:27 +020012285 }
Denys Vlasenko8de5b9f2018-02-13 14:43:29 +010012286 /* Backslash is retained if we are in "str"
12287 * and next char isn't dquote-special.
12288 */
Denys Vlasenko216913c2018-04-02 12:35:04 +020012289 if (synstack->dblquote
Denys Vlasenko958581a2010-09-12 15:04:27 +020012290 && c != '\\'
12291 && c != '`'
12292 && c != '$'
Denys Vlasenko216913c2018-04-02 12:35:04 +020012293 && (c != '"' || (eofmark != NULL && !synstack->varnest))
12294 && (c != '}' || !synstack->varnest)
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012295 ) {
Denys Vlasenko8de5b9f2018-02-13 14:43:29 +010012296 USTPUTC(CTLESC, out); /* protect '\' from glob */
Denys Vlasenko958581a2010-09-12 15:04:27 +020012297 USTPUTC('\\', out);
Eric Andersencb57d552001-06-28 07:25:16 +000012298 }
Ron Yorston549deab2015-05-18 09:57:51 +020012299 USTPUTC(CTLESC, out);
Denys Vlasenko0ff78a02010-08-30 15:20:07 +020012300 USTPUTC(c, out);
Denys Vlasenko958581a2010-09-12 15:04:27 +020012301 quotef = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000012302 }
Denys Vlasenko958581a2010-09-12 15:04:27 +020012303 break;
12304 case CSQUOTE:
Denys Vlasenko216913c2018-04-02 12:35:04 +020012305 synstack->syntax = SQSYNTAX;
Denys Vlasenko958581a2010-09-12 15:04:27 +020012306 quotemark:
12307 if (eofmark == NULL) {
12308 USTPUTC(CTLQUOTEMARK, out);
12309 }
12310 break;
12311 case CDQUOTE:
Denys Vlasenko216913c2018-04-02 12:35:04 +020012312 synstack->syntax = DQSYNTAX;
12313 synstack->dblquote = 1;
12314 toggledq:
12315 if (synstack->varnest)
12316 synstack->innerdq ^= 1;
Denys Vlasenko958581a2010-09-12 15:04:27 +020012317 goto quotemark;
12318 case CENDQUOTE:
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010012319 IF_BASH_DOLLAR_SQUOTE(bash_dollar_squote = 0;)
Denys Vlasenko216913c2018-04-02 12:35:04 +020012320 if (eofmark != NULL && synstack->varnest == 0) {
Denys Vlasenko958581a2010-09-12 15:04:27 +020012321 USTPUTC(c, out);
Denys Vlasenko216913c2018-04-02 12:35:04 +020012322 break;
Denys Vlasenko958581a2010-09-12 15:04:27 +020012323 }
Denys Vlasenko216913c2018-04-02 12:35:04 +020012324
12325 if (synstack->dqvarnest == 0) {
12326 synstack->syntax = BASESYNTAX;
12327 synstack->dblquote = 0;
12328 }
12329
12330 quotef = 1;
12331
12332 if (c == '"')
12333 goto toggledq;
12334
12335 goto quotemark;
Denys Vlasenko958581a2010-09-12 15:04:27 +020012336 case CVAR: /* '$' */
12337 PARSESUB(); /* parse substitution */
12338 break;
12339 case CENDVAR: /* '}' */
Denys Vlasenko216913c2018-04-02 12:35:04 +020012340 if (!synstack->innerdq && synstack->varnest > 0) {
12341 if (!--synstack->varnest && synstack->varpushed)
12342 synstack_pop(&synstack);
12343 else if (synstack->dqvarnest > 0)
12344 synstack->dqvarnest--;
Denys Vlasenko958581a2010-09-12 15:04:27 +020012345 c = CTLENDVAR;
12346 }
12347 USTPUTC(c, out);
12348 break;
Denys Vlasenko0b883582016-12-23 16:49:07 +010012349#if ENABLE_FEATURE_SH_MATH
Denys Vlasenko958581a2010-09-12 15:04:27 +020012350 case CLP: /* '(' in arithmetic */
Denys Vlasenko216913c2018-04-02 12:35:04 +020012351 synstack->parenlevel++;
Denys Vlasenko958581a2010-09-12 15:04:27 +020012352 USTPUTC(c, out);
12353 break;
12354 case CRP: /* ')' in arithmetic */
Denys Vlasenko216913c2018-04-02 12:35:04 +020012355 if (synstack->parenlevel > 0) {
12356 synstack->parenlevel--;
Denys Vlasenko958581a2010-09-12 15:04:27 +020012357 } else {
Denys Vlasenko459293b2016-09-29 17:58:58 +020012358 if (pgetc_eatbnl() == ')') {
Ron Yorstonad88bde2015-05-18 09:56:16 +020012359 c = CTLENDARI;
Denys Vlasenko216913c2018-04-02 12:35:04 +020012360 synstack_pop(&synstack);
Denys Vlasenko958581a2010-09-12 15:04:27 +020012361 } else {
12362 /*
12363 * unbalanced parens
12364 * (don't 2nd guess - no error)
12365 */
12366 pungetc();
12367 }
12368 }
12369 USTPUTC(c, out);
12370 break;
12371#endif
12372 case CBQUOTE: /* '`' */
Denys Vlasenko41fddb42018-04-01 16:38:32 +020012373 if (checkkwd & CHKEOFMARK) {
12374 quotef = 1;
12375 USTPUTC('`', out);
12376 break;
12377 }
12378
Denys Vlasenko958581a2010-09-12 15:04:27 +020012379 PARSEBACKQOLD();
12380 break;
12381 case CENDFILE:
12382 goto endword; /* exit outer loop */
12383 case CIGN:
12384 break;
12385 default:
Denys Vlasenko216913c2018-04-02 12:35:04 +020012386 if (synstack->varnest == 0) {
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010012387#if BASH_REDIR_OUTPUT
Denys Vlasenko958581a2010-09-12 15:04:27 +020012388 if (c == '&') {
Denys Vlasenko459293b2016-09-29 17:58:58 +020012389//Can't call pgetc_eatbnl() here, this requires three-deep pungetc()
Denys Vlasenko958581a2010-09-12 15:04:27 +020012390 if (pgetc() == '>')
12391 c = 0x100 + '>'; /* flag &> */
12392 pungetc();
12393 }
12394#endif
12395 goto endword; /* exit outer loop */
12396 }
12397 IF_ASH_ALIAS(if (c != PEOA))
12398 USTPUTC(c, out);
12399 }
Denys Vlasenkof7eea8c2020-02-14 16:16:34 +010012400 c = pgetc_top(synstack);
Denys Vlasenko958581a2010-09-12 15:04:27 +020012401 } /* for (;;) */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012402 endword:
Denys Vlasenko958581a2010-09-12 15:04:27 +020012403
Denys Vlasenko0b883582016-12-23 16:49:07 +010012404#if ENABLE_FEATURE_SH_MATH
Denys Vlasenko216913c2018-04-02 12:35:04 +020012405 if (synstack->syntax == ARISYNTAX)
Denis Vlasenko559691a2008-10-05 18:39:31 +000012406 raise_error_syntax("missing '))'");
Eric Andersenc470f442003-07-28 09:56:35 +000012407#endif
Denys Vlasenko216913c2018-04-02 12:35:04 +020012408 if (synstack->syntax != BASESYNTAX && eofmark == NULL)
Denis Vlasenko559691a2008-10-05 18:39:31 +000012409 raise_error_syntax("unterminated quoted string");
Denys Vlasenko216913c2018-04-02 12:35:04 +020012410 if (synstack->varnest != 0) {
Eric Andersenc470f442003-07-28 09:56:35 +000012411 /* { */
Denis Vlasenko559691a2008-10-05 18:39:31 +000012412 raise_error_syntax("missing '}'");
Eric Andersencb57d552001-06-28 07:25:16 +000012413 }
12414 USTPUTC('\0', out);
Eric Andersenc470f442003-07-28 09:56:35 +000012415 len = out - (char *)stackblock();
Eric Andersencb57d552001-06-28 07:25:16 +000012416 out = stackblock();
12417 if (eofmark == NULL) {
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010012418 if ((c == '>' || c == '<' IF_BASH_REDIR_OUTPUT( || c == 0x100 + '>'))
Denis Vlasenko834dee72008-10-07 09:18:30 +000012419 && quotef == 0
12420 ) {
Denis Vlasenko559691a2008-10-05 18:39:31 +000012421 if (isdigit_str9(out)) {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000012422 PARSEREDIR(); /* passed as params: out, c */
12423 lasttoken = TREDIR;
12424 return lasttoken;
12425 }
12426 /* else: non-number X seen, interpret it
12427 * as "NNNX>file" = "NNNX >file" */
Eric Andersencb57d552001-06-28 07:25:16 +000012428 }
Denis Vlasenkoef527f52008-06-23 01:52:30 +000012429 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000012430 }
12431 quoteflag = quotef;
12432 backquotelist = bqlist;
12433 grabstackblock(len);
12434 wordtext = out;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012435 lasttoken = TWORD;
12436 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000012437/* end of readtoken routine */
12438
Eric Andersencb57d552001-06-28 07:25:16 +000012439/*
12440 * Check to see whether we are at the end of the here document. When this
12441 * is called, c is set to the first character of the next input line. If
12442 * we are at the end of the here document, this routine sets the c to PEOF.
12443 */
Eric Andersenc470f442003-07-28 09:56:35 +000012444checkend: {
Denys Vlasenko46999802017-07-29 21:12:29 +020012445 if (realeofmark(eofmark)) {
Denys Vlasenkoa7328982017-07-29 19:57:28 +020012446 int markloc;
12447 char *p;
12448
Denis Vlasenko131ae172007-02-18 13:00:19 +000012449#if ENABLE_ASH_ALIAS
Denys Vlasenko2ce42e92009-11-29 02:18:13 +010012450 if (c == PEOA)
12451 c = pgetc_without_PEOA();
Eric Andersenc470f442003-07-28 09:56:35 +000012452#endif
12453 if (striptabs) {
12454 while (c == '\t') {
Denys Vlasenko2ce42e92009-11-29 02:18:13 +010012455 c = pgetc_without_PEOA();
Eric Andersencb57d552001-06-28 07:25:16 +000012456 }
Eric Andersenc470f442003-07-28 09:56:35 +000012457 }
Eric Andersencb57d552001-06-28 07:25:16 +000012458
Denys Vlasenkoa7328982017-07-29 19:57:28 +020012459 markloc = out - (char *)stackblock();
12460 for (p = eofmark; STPUTC(c, out), *p; p++) {
12461 if (c != *p)
12462 goto more_heredoc;
Denys Vlasenko35e349d2019-09-05 14:31:49 +020012463 /* FIXME: fails for backslash-newlined terminator:
12464 * cat <<EOF
12465 * ...
12466 * EO\
12467 * F
12468 * (see heredoc_bkslash_newline2.tests)
12469 */
Denys Vlasenkoa7328982017-07-29 19:57:28 +020012470 c = pgetc_without_PEOA();
12471 }
12472
12473 if (c == '\n' || c == PEOF) {
12474 c = PEOF;
12475 g_parsefile->linno++;
12476 needprompt = doprompt;
12477 } else {
12478 int len_here;
12479
12480 more_heredoc:
12481 p = (char *)stackblock() + markloc + 1;
12482 len_here = out - p;
12483
12484 if (len_here) {
12485 len_here -= (c >= PEOF);
12486 c = p[-1];
12487
12488 if (len_here) {
12489 char *str;
12490
12491 str = alloca(len_here + 1);
12492 *(char *)mempcpy(str, p, len_here) = '\0';
12493
12494 pushstring(str, NULL);
Eric Andersencb57d552001-06-28 07:25:16 +000012495 }
12496 }
12497 }
Denys Vlasenkoa7328982017-07-29 19:57:28 +020012498
12499 STADJUST((char *)stackblock() + markloc - out, out);
Eric Andersencb57d552001-06-28 07:25:16 +000012500 }
Eric Andersenc470f442003-07-28 09:56:35 +000012501 goto checkend_return;
12502}
Eric Andersencb57d552001-06-28 07:25:16 +000012503
Eric Andersencb57d552001-06-28 07:25:16 +000012504/*
12505 * Parse a redirection operator. The variable "out" points to a string
12506 * specifying the fd to be redirected. The variable "c" contains the
12507 * first character of the redirection operator.
12508 */
Eric Andersenc470f442003-07-28 09:56:35 +000012509parseredir: {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000012510 /* out is already checked to be a valid number or "" */
12511 int fd = (*out == '\0' ? -1 : atoi(out));
Eric Andersenc470f442003-07-28 09:56:35 +000012512 union node *np;
Eric Andersencb57d552001-06-28 07:25:16 +000012513
Denis Vlasenko597906c2008-02-20 16:38:54 +000012514 np = stzalloc(sizeof(struct nfile));
Eric Andersenc470f442003-07-28 09:56:35 +000012515 if (c == '>') {
12516 np->nfile.fd = 1;
Denys Vlasenko220be532018-03-31 19:21:31 +020012517 c = pgetc_eatbnl();
Eric Andersenc470f442003-07-28 09:56:35 +000012518 if (c == '>')
12519 np->type = NAPPEND;
12520 else if (c == '|')
12521 np->type = NCLOBBER;
12522 else if (c == '&')
12523 np->type = NTOFD;
Denis Vlasenko559691a2008-10-05 18:39:31 +000012524 /* it also can be NTO2 (>&file), but we can't figure it out yet */
Eric Andersenc470f442003-07-28 09:56:35 +000012525 else {
12526 np->type = NTO;
12527 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000012528 }
Denis Vlasenko834dee72008-10-07 09:18:30 +000012529 }
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010012530#if BASH_REDIR_OUTPUT
Denis Vlasenko834dee72008-10-07 09:18:30 +000012531 else if (c == 0x100 + '>') { /* this flags &> redirection */
12532 np->nfile.fd = 1;
12533 pgetc(); /* this is '>', no need to check */
12534 np->type = NTO2;
12535 }
12536#endif
12537 else { /* c == '<' */
Denis Vlasenko597906c2008-02-20 16:38:54 +000012538 /*np->nfile.fd = 0; - stzalloc did it */
Denys Vlasenko220be532018-03-31 19:21:31 +020012539 c = pgetc_eatbnl();
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012540 switch (c) {
Eric Andersenc470f442003-07-28 09:56:35 +000012541 case '<':
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012542 if (sizeof(struct nfile) != sizeof(struct nhere)) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000012543 np = stzalloc(sizeof(struct nhere));
12544 /*np->nfile.fd = 0; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000012545 }
12546 np->type = NHERE;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000012547 heredoc = stzalloc(sizeof(struct heredoc));
Eric Andersenc470f442003-07-28 09:56:35 +000012548 heredoc->here = np;
Denys Vlasenko220be532018-03-31 19:21:31 +020012549 c = pgetc_eatbnl();
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012550 if (c == '-') {
Eric Andersenc470f442003-07-28 09:56:35 +000012551 heredoc->striptabs = 1;
12552 } else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000012553 /*heredoc->striptabs = 0; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000012554 pungetc();
12555 }
12556 break;
12557
12558 case '&':
12559 np->type = NFROMFD;
12560 break;
12561
12562 case '>':
12563 np->type = NFROMTO;
12564 break;
12565
12566 default:
12567 np->type = NFROM;
12568 pungetc();
12569 break;
12570 }
Eric Andersencb57d552001-06-28 07:25:16 +000012571 }
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000012572 if (fd >= 0)
12573 np->nfile.fd = fd;
Eric Andersenc470f442003-07-28 09:56:35 +000012574 redirnode = np;
12575 goto parseredir_return;
12576}
Eric Andersencb57d552001-06-28 07:25:16 +000012577
Eric Andersencb57d552001-06-28 07:25:16 +000012578/*
12579 * Parse a substitution. At this point, we have read the dollar sign
12580 * and nothing else.
12581 */
Denis Vlasenkocc571512007-02-23 21:10:35 +000012582
12583/* is_special(c) evaluates to 1 for c in "!#$*-0123456789?@"; 0 otherwise
12584 * (assuming ascii char codes, as the original implementation did) */
12585#define is_special(c) \
Denis Vlasenkoef527f52008-06-23 01:52:30 +000012586 (((unsigned)(c) - 33 < 32) \
12587 && ((0xc1ff920dU >> ((unsigned)(c) - 33)) & 1))
Eric Andersenc470f442003-07-28 09:56:35 +000012588parsesub: {
Denys Vlasenkocd716832009-11-28 22:14:02 +010012589 unsigned char subtype;
Eric Andersenc470f442003-07-28 09:56:35 +000012590 int typeloc;
Eric Andersencb57d552001-06-28 07:25:16 +000012591
Denys Vlasenko73c3e072016-09-29 17:17:04 +020012592 c = pgetc_eatbnl();
Denys Vlasenkoa7328982017-07-29 19:57:28 +020012593 if ((checkkwd & CHKEOFMARK)
12594 || c > 255 /* PEOA or PEOF */
Denis Vlasenkoef527f52008-06-23 01:52:30 +000012595 || (c != '(' && c != '{' && !is_name(c) && !is_special(c))
Eric Andersenc470f442003-07-28 09:56:35 +000012596 ) {
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010012597#if BASH_DOLLAR_SQUOTE
Denys Vlasenko216913c2018-04-02 12:35:04 +020012598 if (synstack->syntax != DQSYNTAX && c == '\'')
Denis Vlasenkoef527f52008-06-23 01:52:30 +000012599 bash_dollar_squote = 1;
12600 else
12601#endif
12602 USTPUTC('$', out);
Eric Andersenc470f442003-07-28 09:56:35 +000012603 pungetc();
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020012604 } else if (c == '(') {
12605 /* $(command) or $((arith)) */
Denys Vlasenko73c3e072016-09-29 17:17:04 +020012606 if (pgetc_eatbnl() == '(') {
Denys Vlasenko0b883582016-12-23 16:49:07 +010012607#if ENABLE_FEATURE_SH_MATH
Eric Andersenc470f442003-07-28 09:56:35 +000012608 PARSEARITH();
12609#else
Denys Vlasenko4f8079d2017-07-17 17:11:48 +020012610 raise_error_syntax("support for $((arith)) is disabled");
Eric Andersenc470f442003-07-28 09:56:35 +000012611#endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000012612 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000012613 pungetc();
12614 PARSEBACKQNEW();
12615 }
12616 } else {
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020012617 /* $VAR, $<specialchar>, ${...}, or PEOA/PEOF */
Denys Vlasenko216913c2018-04-02 12:35:04 +020012618 smalluint newsyn = synstack->syntax;
12619
Eric Andersenc470f442003-07-28 09:56:35 +000012620 USTPUTC(CTLVAR, out);
12621 typeloc = out - (char *)stackblock();
Denys Vlasenko3df14102016-10-26 16:41:13 +020012622 STADJUST(1, out);
Eric Andersenc470f442003-07-28 09:56:35 +000012623 subtype = VSNORMAL;
12624 if (c == '{') {
Denys Vlasenko73c3e072016-09-29 17:17:04 +020012625 c = pgetc_eatbnl();
Denys Vlasenkof15aa572016-10-26 15:56:53 +020012626 subtype = 0;
Eric Andersenc470f442003-07-28 09:56:35 +000012627 }
Denys Vlasenkof15aa572016-10-26 15:56:53 +020012628 varname:
Denys Vlasenko3df14102016-10-26 16:41:13 +020012629 if (is_name(c)) {
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020012630 /* $[{[#]]NAME[}] */
Eric Andersenc470f442003-07-28 09:56:35 +000012631 do {
12632 STPUTC(c, out);
Denys Vlasenko73c3e072016-09-29 17:17:04 +020012633 c = pgetc_eatbnl();
Denys Vlasenko3df14102016-10-26 16:41:13 +020012634 } while (is_in_name(c));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012635 } else if (isdigit(c)) {
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020012636 /* $[{[#]]NUM[}] */
Eric Andersenc470f442003-07-28 09:56:35 +000012637 do {
12638 STPUTC(c, out);
Denys Vlasenko73c3e072016-09-29 17:17:04 +020012639 c = pgetc_eatbnl();
Denys Vlasenkoc2ce8882020-02-17 10:15:35 +010012640 } while (!subtype && isdigit(c));
Denys Vlasenko58eb8052018-08-05 15:58:13 +020012641 } else if (c != '}') {
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020012642 /* $[{[#]]<specialchar>[}] */
Denys Vlasenkof15aa572016-10-26 15:56:53 +020012643 int cc = c;
12644
Denys Vlasenko73c3e072016-09-29 17:17:04 +020012645 c = pgetc_eatbnl();
Denys Vlasenkof15aa572016-10-26 15:56:53 +020012646 if (!subtype && cc == '#') {
12647 subtype = VSLENGTH;
12648 if (c == '_' || isalnum(c))
12649 goto varname;
12650 cc = c;
12651 c = pgetc_eatbnl();
12652 if (cc == '}' || c != '}') {
12653 pungetc();
12654 subtype = 0;
12655 c = cc;
12656 cc = '#';
12657 }
12658 }
Denys Vlasenko452cc1d2017-08-14 14:23:45 +020012659
12660 if (!is_special(cc)) {
12661 if (subtype == VSLENGTH)
12662 subtype = 0;
12663 goto badsub;
12664 }
12665
Denys Vlasenkof15aa572016-10-26 15:56:53 +020012666 USTPUTC(cc, out);
Denys Vlasenko58eb8052018-08-05 15:58:13 +020012667 } else
12668 goto badsub;
Denys Vlasenko452cc1d2017-08-14 14:23:45 +020012669
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020012670 if (c != '}' && subtype == VSLENGTH) {
12671 /* ${#VAR didn't end with } */
Cristian Ionescu-Idbohrn301f5ec2009-10-05 02:07:23 +020012672 goto badsub;
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020012673 }
Eric Andersencb57d552001-06-28 07:25:16 +000012674
Eric Andersenc470f442003-07-28 09:56:35 +000012675 if (subtype == 0) {
Denys Vlasenkof8ddbe12016-07-25 03:56:00 +020012676 static const char types[] ALIGN1 = "}-+?=";
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020012677 /* ${VAR...} but not $VAR or ${#VAR} */
12678 /* c == first char after VAR */
Denys Vlasenko216913c2018-04-02 12:35:04 +020012679 int cc = c;
12680
Eric Andersenc470f442003-07-28 09:56:35 +000012681 switch (c) {
12682 case ':':
Denys Vlasenko73c3e072016-09-29 17:17:04 +020012683 c = pgetc_eatbnl();
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010012684#if BASH_SUBSTR
Denys Vlasenkof8ddbe12016-07-25 03:56:00 +020012685 /* This check is only needed to not misinterpret
12686 * ${VAR:-WORD}, ${VAR:+WORD}, ${VAR:=WORD}, ${VAR:?WORD}
12687 * constructs.
12688 */
12689 if (!strchr(types, c)) {
Denis Vlasenko92e13c22008-03-25 01:17:40 +000012690 subtype = VSSUBSTR;
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020012691 pungetc();
Denys Vlasenko88e15702016-10-26 01:55:56 +020012692 break; /* "goto badsub" is bigger (!) */
Denis Vlasenko92e13c22008-03-25 01:17:40 +000012693 }
12694#endif
Denys Vlasenko3df14102016-10-26 16:41:13 +020012695 subtype = VSNUL;
Eric Andersenc470f442003-07-28 09:56:35 +000012696 /*FALLTHROUGH*/
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020012697 default: {
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020012698 const char *p = strchr(types, c);
Eric Andersenc470f442003-07-28 09:56:35 +000012699 if (p == NULL)
Denys Vlasenko88e15702016-10-26 01:55:56 +020012700 break;
Denys Vlasenko3df14102016-10-26 16:41:13 +020012701 subtype |= p - types + VSNORMAL;
Eric Andersenc470f442003-07-28 09:56:35 +000012702 break;
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020012703 }
Eric Andersenc470f442003-07-28 09:56:35 +000012704 case '%':
Denys Vlasenko216913c2018-04-02 12:35:04 +020012705 case '#':
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020012706 subtype = (c == '#' ? VSTRIMLEFT : VSTRIMRIGHT);
Denys Vlasenko73c3e072016-09-29 17:17:04 +020012707 c = pgetc_eatbnl();
Denys Vlasenko216913c2018-04-02 12:35:04 +020012708 if (c == cc)
12709 subtype++;
12710 else
12711 pungetc();
12712
12713 newsyn = BASESYNTAX;
Denis Vlasenko92e13c22008-03-25 01:17:40 +000012714 break;
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010012715#if BASH_PATTERN_SUBST
Denis Vlasenko92e13c22008-03-25 01:17:40 +000012716 case '/':
Denys Vlasenko6040fe82010-09-12 15:03:16 +020012717 /* ${v/[/]pattern/repl} */
12718//TODO: encode pattern and repl separately.
Denys Vlasenko216913c2018-04-02 12:35:04 +020012719// Currently cases like: v=1;echo ${v/$((1/1))/ONE}
12720// are broken (should print "ONE")
Denis Vlasenko92e13c22008-03-25 01:17:40 +000012721 subtype = VSREPLACE;
Denys Vlasenko216913c2018-04-02 12:35:04 +020012722 newsyn = BASESYNTAX;
Denys Vlasenko73c3e072016-09-29 17:17:04 +020012723 c = pgetc_eatbnl();
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020012724 if (c != '/')
Denys Vlasenko88e15702016-10-26 01:55:56 +020012725 goto badsub;
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020012726 subtype++; /* VSREPLACEALL */
Denis Vlasenko92e13c22008-03-25 01:17:40 +000012727 break;
12728#endif
Eric Andersencb57d552001-06-28 07:25:16 +000012729 }
Eric Andersenc470f442003-07-28 09:56:35 +000012730 } else {
Denys Vlasenko88e15702016-10-26 01:55:56 +020012731 badsub:
Eric Andersenc470f442003-07-28 09:56:35 +000012732 pungetc();
12733 }
Denys Vlasenko216913c2018-04-02 12:35:04 +020012734
Denys Vlasenkof50e1462018-04-02 21:00:59 +020012735 if (newsyn == ARISYNTAX)
Denys Vlasenko216913c2018-04-02 12:35:04 +020012736 newsyn = DQSYNTAX;
12737
Denys Vlasenkof50e1462018-04-02 21:00:59 +020012738 if ((newsyn != synstack->syntax || synstack->innerdq)
12739 && subtype != VSNORMAL
12740 ) {
Denys Vlasenko216913c2018-04-02 12:35:04 +020012741 synstack_push(&synstack,
12742 synstack->prev ?: alloca(sizeof(*synstack)),
12743 newsyn);
12744
12745 synstack->varpushed = 1;
12746 synstack->dblquote = newsyn != BASESYNTAX;
12747 }
12748
Denys Vlasenko3df14102016-10-26 16:41:13 +020012749 ((unsigned char *)stackblock())[typeloc] = subtype;
Eric Andersenc470f442003-07-28 09:56:35 +000012750 if (subtype != VSNORMAL) {
Denys Vlasenko216913c2018-04-02 12:35:04 +020012751 synstack->varnest++;
12752 if (synstack->dblquote)
12753 synstack->dqvarnest++;
Eric Andersencb57d552001-06-28 07:25:16 +000012754 }
Denys Vlasenko88e15702016-10-26 01:55:56 +020012755 STPUTC('=', out);
Eric Andersencb57d552001-06-28 07:25:16 +000012756 }
Eric Andersenc470f442003-07-28 09:56:35 +000012757 goto parsesub_return;
12758}
Eric Andersencb57d552001-06-28 07:25:16 +000012759
Eric Andersencb57d552001-06-28 07:25:16 +000012760/*
12761 * Called to parse command substitutions. Newstyle is set if the command
12762 * is enclosed inside $(...); nlpp is a pointer to the head of the linked
12763 * list of commands (passed by reference), and savelen is the number of
12764 * characters on the top of the stack which must be preserved.
12765 */
Eric Andersenc470f442003-07-28 09:56:35 +000012766parsebackq: {
12767 struct nodelist **nlpp;
Eric Andersenc470f442003-07-28 09:56:35 +000012768 union node *n;
Ron Yorston072fc602015-07-01 16:46:18 +010012769 char *str;
Eric Andersenc470f442003-07-28 09:56:35 +000012770 size_t savelen;
Denys Vlasenko74aaf052020-02-17 12:11:26 +010012771 struct heredoc *saveheredoclist;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000012772 smallint saveprompt = 0;
12773
Eric Andersenc470f442003-07-28 09:56:35 +000012774 str = NULL;
12775 savelen = out - (char *)stackblock();
12776 if (savelen > 0) {
Denys Vlasenko7bc3d392016-09-17 20:53:47 +020012777 /*
12778 * FIXME: this can allocate very large block on stack and SEGV.
12779 * Example:
12780 * echo "..<100kbytes>..`true` $(true) `true` ..."
Denys Vlasenko73737592016-09-17 20:58:22 +020012781 * allocates 100kb for every command subst. With about
Denys Vlasenko7bc3d392016-09-17 20:53:47 +020012782 * a hundred command substitutions stack overflows.
12783 * With larger prepended string, SEGV happens sooner.
12784 */
Ron Yorston072fc602015-07-01 16:46:18 +010012785 str = alloca(savelen);
Eric Andersenc470f442003-07-28 09:56:35 +000012786 memcpy(str, stackblock(), savelen);
12787 }
Denys Vlasenko7bc3d392016-09-17 20:53:47 +020012788
Eric Andersenc470f442003-07-28 09:56:35 +000012789 if (oldstyle) {
12790 /* We must read until the closing backquote, giving special
Denys Vlasenko60cb48c2013-01-14 15:57:44 +010012791 * treatment to some slashes, and then push the string and
12792 * reread it as input, interpreting it normally.
12793 */
Eric Andersenc470f442003-07-28 09:56:35 +000012794 char *pout;
Eric Andersenc470f442003-07-28 09:56:35 +000012795 size_t psavelen;
12796 char *pstr;
12797
Eric Andersenc470f442003-07-28 09:56:35 +000012798 STARTSTACKSTR(pout);
12799 for (;;) {
Denys Vlasenko958581a2010-09-12 15:04:27 +020012800 int pc;
12801
12802 setprompt_if(needprompt, 2);
Denys Vlasenko220be532018-03-31 19:21:31 +020012803 pc = pgetc_eatbnl();
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012804 switch (pc) {
Eric Andersenc470f442003-07-28 09:56:35 +000012805 case '`':
12806 goto done;
12807
12808 case '\\':
Denys Vlasenko777a6352020-09-29 16:25:32 +020012809 pc = pgetc(); /* not pgetc_eatbnl! */
Eric Andersenc470f442003-07-28 09:56:35 +000012810 if (pc != '\\' && pc != '`' && pc != '$'
Denys Vlasenko216913c2018-04-02 12:35:04 +020012811 && (!synstack->dblquote || pc != '"')
Denys Vlasenko76bc2d62009-11-29 01:37:46 +010012812 ) {
Eric Andersenc470f442003-07-28 09:56:35 +000012813 STPUTC('\\', pout);
Denys Vlasenko76bc2d62009-11-29 01:37:46 +010012814 }
Denys Vlasenkocd716832009-11-28 22:14:02 +010012815 if (pc <= 255 /* not PEOA or PEOF */) {
Eric Andersenc470f442003-07-28 09:56:35 +000012816 break;
12817 }
12818 /* fall through */
12819
12820 case PEOF:
Denys Vlasenko2ce42e92009-11-29 02:18:13 +010012821 IF_ASH_ALIAS(case PEOA:)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012822 raise_error_syntax("EOF in backquote substitution");
Eric Andersenc470f442003-07-28 09:56:35 +000012823
12824 case '\n':
Denys Vlasenkoce332a22016-10-02 23:47:34 +020012825 nlnoprompt();
Eric Andersenc470f442003-07-28 09:56:35 +000012826 break;
12827
12828 default:
12829 break;
12830 }
12831 STPUTC(pc, pout);
12832 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012833 done:
Eric Andersenc470f442003-07-28 09:56:35 +000012834 STPUTC('\0', pout);
12835 psavelen = pout - (char *)stackblock();
12836 if (psavelen > 0) {
12837 pstr = grabstackstr(pout);
12838 setinputstring(pstr);
12839 }
12840 }
12841 nlpp = &bqlist;
12842 while (*nlpp)
12843 nlpp = &(*nlpp)->next;
Denis Vlasenko597906c2008-02-20 16:38:54 +000012844 *nlpp = stzalloc(sizeof(**nlpp));
12845 /* (*nlpp)->next = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000012846
Denys Vlasenko74aaf052020-02-17 12:11:26 +010012847 saveheredoclist = heredoclist;
12848 heredoclist = NULL;
12849
Eric Andersenc470f442003-07-28 09:56:35 +000012850 if (oldstyle) {
12851 saveprompt = doprompt;
12852 doprompt = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000012853 }
12854
Eric Andersenc470f442003-07-28 09:56:35 +000012855 n = list(2);
12856
12857 if (oldstyle)
12858 doprompt = saveprompt;
Denys Vlasenko74aaf052020-02-17 12:11:26 +010012859 else {
12860 if (readtoken() != TRP)
12861 raise_error_unexpected_syntax(TRP);
12862 setinputstring(nullstr);
Denys Vlasenko74aaf052020-02-17 12:11:26 +010012863 }
12864
Denys Vlasenko9a1a6592020-02-22 16:39:27 +010012865 parseheredoc();
Denys Vlasenko74aaf052020-02-17 12:11:26 +010012866 heredoclist = saveheredoclist;
Eric Andersenc470f442003-07-28 09:56:35 +000012867
12868 (*nlpp)->n = n;
Denys Vlasenko74aaf052020-02-17 12:11:26 +010012869 /* Start reading from old file again. */
12870 popfile();
12871 /* Ignore any pushed back tokens left from the backquote parsing. */
12872 if (oldstyle)
Eric Andersenc470f442003-07-28 09:56:35 +000012873 tokpushback = 0;
Denys Vlasenkoc55847f2020-02-17 15:59:08 +010012874 out = growstackto(savelen + 1);
Eric Andersenc470f442003-07-28 09:56:35 +000012875 if (str) {
12876 memcpy(out, str, savelen);
12877 STADJUST(savelen, out);
Eric Andersenc470f442003-07-28 09:56:35 +000012878 }
Ron Yorston549deab2015-05-18 09:57:51 +020012879 USTPUTC(CTLBACKQ, out);
Eric Andersenc470f442003-07-28 09:56:35 +000012880 if (oldstyle)
12881 goto parsebackq_oldreturn;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012882 goto parsebackq_newreturn;
Eric Andersenc470f442003-07-28 09:56:35 +000012883}
12884
Denys Vlasenko0b883582016-12-23 16:49:07 +010012885#if ENABLE_FEATURE_SH_MATH
Eric Andersencb57d552001-06-28 07:25:16 +000012886/*
12887 * Parse an arithmetic expansion (indicate start of one and set state)
12888 */
Eric Andersenc470f442003-07-28 09:56:35 +000012889parsearith: {
Denys Vlasenko216913c2018-04-02 12:35:04 +020012890
12891 synstack_push(&synstack,
12892 synstack->prev ?: alloca(sizeof(*synstack)),
12893 ARISYNTAX);
12894 synstack->dblquote = 1;
Ron Yorstonad88bde2015-05-18 09:56:16 +020012895 USTPUTC(CTLARI, out);
Eric Andersenc470f442003-07-28 09:56:35 +000012896 goto parsearith_return;
12897}
12898#endif
Eric Andersenc470f442003-07-28 09:56:35 +000012899} /* end of readtoken */
12900
Eric Andersencb57d552001-06-28 07:25:16 +000012901/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012902 * Read the next input token.
12903 * If the token is a word, we set backquotelist to the list of cmds in
12904 * backquotes. We set quoteflag to true if any part of the word was
12905 * quoted.
12906 * If the token is TREDIR, then we set redirnode to a structure containing
12907 * the redirection.
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012908 *
12909 * [Change comment: here documents and internal procedures]
12910 * [Readtoken shouldn't have any arguments. Perhaps we should make the
12911 * word parsing code into a separate routine. In this case, readtoken
12912 * doesn't need to have any internal procedures, but parseword does.
12913 * We could also make parseoperator in essence the main routine, and
12914 * have parseword (readtoken1?) handle both words and redirection.]
Eric Andersencb57d552001-06-28 07:25:16 +000012915 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012916#define NEW_xxreadtoken
12917#ifdef NEW_xxreadtoken
12918/* singles must be first! */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000012919static const char xxreadtoken_chars[7] ALIGN1 = {
Denis Vlasenko834dee72008-10-07 09:18:30 +000012920 '\n', '(', ')', /* singles */
12921 '&', '|', ';', /* doubles */
12922 0
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000012923};
Eric Andersencb57d552001-06-28 07:25:16 +000012924
Denis Vlasenko834dee72008-10-07 09:18:30 +000012925#define xxreadtoken_singles 3
12926#define xxreadtoken_doubles 3
12927
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000012928static const char xxreadtoken_tokens[] ALIGN1 = {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012929 TNL, TLP, TRP, /* only single occurrence allowed */
12930 TBACKGND, TPIPE, TSEMI, /* if single occurrence */
12931 TEOF, /* corresponds to trailing nul */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000012932 TAND, TOR, TENDCASE /* if double occurrence */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012933};
12934
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012935static int
12936xxreadtoken(void)
12937{
12938 int c;
12939
12940 if (tokpushback) {
12941 tokpushback = 0;
12942 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000012943 }
Denys Vlasenko958581a2010-09-12 15:04:27 +020012944 setprompt_if(needprompt, 2);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012945 for (;;) { /* until token or start of word found */
Denys Vlasenko220be532018-03-31 19:21:31 +020012946 c = pgetc_eatbnl();
Denis Vlasenko5e34ff22009-04-21 11:09:40 +000012947 if (c == ' ' || c == '\t' IF_ASH_ALIAS( || c == PEOA))
Denis Vlasenko176d49d2008-10-06 09:51:47 +000012948 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012949
Denis Vlasenko176d49d2008-10-06 09:51:47 +000012950 if (c == '#') {
12951 while ((c = pgetc()) != '\n' && c != PEOF)
12952 continue;
12953 pungetc();
12954 } else if (c == '\\') {
Denys Vlasenko220be532018-03-31 19:21:31 +020012955 break; /* return readtoken1(...) */
Denis Vlasenko176d49d2008-10-06 09:51:47 +000012956 } else {
12957 const char *p;
12958
12959 p = xxreadtoken_chars + sizeof(xxreadtoken_chars) - 1;
12960 if (c != PEOF) {
12961 if (c == '\n') {
Denys Vlasenkoce332a22016-10-02 23:47:34 +020012962 nlnoprompt();
Denis Vlasenko176d49d2008-10-06 09:51:47 +000012963 }
12964
12965 p = strchr(xxreadtoken_chars, c);
Denis Vlasenko834dee72008-10-07 09:18:30 +000012966 if (p == NULL)
12967 break; /* return readtoken1(...) */
Denis Vlasenko176d49d2008-10-06 09:51:47 +000012968
Denis Vlasenko834dee72008-10-07 09:18:30 +000012969 if ((int)(p - xxreadtoken_chars) >= xxreadtoken_singles) {
Denys Vlasenko1e5111b2018-04-01 03:04:55 +020012970 int cc = pgetc_eatbnl();
Denis Vlasenko834dee72008-10-07 09:18:30 +000012971 if (cc == c) { /* double occurrence? */
Denis Vlasenko176d49d2008-10-06 09:51:47 +000012972 p += xxreadtoken_doubles + 1;
12973 } else {
12974 pungetc();
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010012975#if BASH_REDIR_OUTPUT
Denis Vlasenko834dee72008-10-07 09:18:30 +000012976 if (c == '&' && cc == '>') /* &> */
12977 break; /* return readtoken1(...) */
12978#endif
Denis Vlasenko176d49d2008-10-06 09:51:47 +000012979 }
12980 }
12981 }
12982 lasttoken = xxreadtoken_tokens[p - xxreadtoken_chars];
12983 return lasttoken;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012984 }
Denis Vlasenko176d49d2008-10-06 09:51:47 +000012985 } /* for (;;) */
Denis Vlasenko834dee72008-10-07 09:18:30 +000012986
12987 return readtoken1(c, BASESYNTAX, (char *) NULL, 0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012988}
Denis Vlasenko176d49d2008-10-06 09:51:47 +000012989#else /* old xxreadtoken */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012990#define RETURN(token) return lasttoken = token
12991static int
12992xxreadtoken(void)
12993{
12994 int c;
12995
12996 if (tokpushback) {
12997 tokpushback = 0;
12998 return lasttoken;
12999 }
Denys Vlasenko958581a2010-09-12 15:04:27 +020013000 setprompt_if(needprompt, 2);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013001 for (;;) { /* until token or start of word found */
Denys Vlasenko220be532018-03-31 19:21:31 +020013002 c = pgetc_eatbnl();
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013003 switch (c) {
13004 case ' ': case '\t':
Denys Vlasenko2ce42e92009-11-29 02:18:13 +010013005 IF_ASH_ALIAS(case PEOA:)
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013006 continue;
13007 case '#':
Denis Vlasenkof7d56652008-03-25 05:51:41 +000013008 while ((c = pgetc()) != '\n' && c != PEOF)
13009 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013010 pungetc();
13011 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013012 case '\n':
Denys Vlasenkoce332a22016-10-02 23:47:34 +020013013 nlnoprompt();
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013014 RETURN(TNL);
13015 case PEOF:
13016 RETURN(TEOF);
13017 case '&':
Denys Vlasenko220be532018-03-31 19:21:31 +020013018 if (pgetc_eatbnl() == '&')
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013019 RETURN(TAND);
13020 pungetc();
13021 RETURN(TBACKGND);
13022 case '|':
Denys Vlasenko220be532018-03-31 19:21:31 +020013023 if (pgetc_eatbnl() == '|')
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013024 RETURN(TOR);
13025 pungetc();
13026 RETURN(TPIPE);
13027 case ';':
Denys Vlasenko220be532018-03-31 19:21:31 +020013028 if (pgetc_eatbnl() == ';')
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013029 RETURN(TENDCASE);
13030 pungetc();
13031 RETURN(TSEMI);
13032 case '(':
13033 RETURN(TLP);
13034 case ')':
13035 RETURN(TRP);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013036 }
Denys Vlasenko220be532018-03-31 19:21:31 +020013037 break;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013038 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013039 return readtoken1(c, BASESYNTAX, (char *)NULL, 0);
13040#undef RETURN
13041}
Denis Vlasenko176d49d2008-10-06 09:51:47 +000013042#endif /* old xxreadtoken */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013043
13044static int
13045readtoken(void)
13046{
13047 int t;
Ron Yorston713f07d2015-10-29 16:44:56 +000013048 int kwd = checkkwd;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013049#if DEBUG
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000013050 smallint alreadyseen = tokpushback;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013051#endif
13052
13053#if ENABLE_ASH_ALIAS
13054 top:
13055#endif
13056
13057 t = xxreadtoken();
13058
13059 /*
13060 * eat newlines
13061 */
Ron Yorston713f07d2015-10-29 16:44:56 +000013062 if (kwd & CHKNL) {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013063 while (t == TNL) {
13064 parseheredoc();
13065 t = xxreadtoken();
13066 }
13067 }
13068
13069 if (t != TWORD || quoteflag) {
13070 goto out;
13071 }
13072
13073 /*
13074 * check for keywords
13075 */
Ron Yorston713f07d2015-10-29 16:44:56 +000013076 if (kwd & CHKKWD) {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013077 const char *const *pp;
13078
13079 pp = findkwd(wordtext);
13080 if (pp) {
13081 lasttoken = t = pp - tokname_array;
Denys Vlasenko888527c2016-10-02 16:54:17 +020013082 TRACE(("keyword '%s' recognized\n", tokname_array[t]));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013083 goto out;
13084 }
13085 }
13086
13087 if (checkkwd & CHKALIAS) {
13088#if ENABLE_ASH_ALIAS
13089 struct alias *ap;
13090 ap = lookupalias(wordtext, 1);
13091 if (ap != NULL) {
13092 if (*ap->val) {
13093 pushstring(ap->val, ap);
13094 }
13095 goto top;
13096 }
13097#endif
13098 }
13099 out:
13100 checkkwd = 0;
13101#if DEBUG
13102 if (!alreadyseen)
Denys Vlasenko888527c2016-10-02 16:54:17 +020013103 TRACE(("token '%s' %s\n", tokname_array[t], t == TWORD ? wordtext : ""));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013104 else
Denys Vlasenko888527c2016-10-02 16:54:17 +020013105 TRACE(("reread token '%s' %s\n", tokname_array[t], t == TWORD ? wordtext : ""));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013106#endif
13107 return t;
Eric Andersencb57d552001-06-28 07:25:16 +000013108}
13109
Ron Yorstonc0e00762015-10-29 11:30:55 +000013110static int
Ron Yorston6bd2fab2015-10-29 11:30:22 +000013111peektoken(void)
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013112{
13113 int t;
13114
13115 t = readtoken();
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000013116 tokpushback = 1;
Ron Yorstonc0e00762015-10-29 11:30:55 +000013117 return t;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013118}
Eric Andersencb57d552001-06-28 07:25:16 +000013119
13120/*
Denys Vlasenko86e83ec2009-07-23 22:07:07 +020013121 * Read and parse a command. Returns NODE_EOF on end of file.
13122 * (NULL is a valid parse tree indicating a blank line.)
Eric Andersencb57d552001-06-28 07:25:16 +000013123 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013124static union node *
13125parsecmd(int interact)
Eric Andersen90898442003-08-06 11:20:52 +000013126{
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013127 tokpushback = 0;
Ron Yorstonc0e00762015-10-29 11:30:55 +000013128 checkkwd = 0;
13129 heredoclist = 0;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013130 doprompt = interact;
Denys Vlasenko958581a2010-09-12 15:04:27 +020013131 setprompt_if(doprompt, doprompt);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013132 needprompt = 0;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013133 return list(1);
13134}
13135
13136/*
13137 * Input any here documents.
13138 */
13139static void
13140parseheredoc(void)
13141{
13142 struct heredoc *here;
13143 union node *n;
13144
13145 here = heredoclist;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000013146 heredoclist = NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013147
13148 while (here) {
Christoph Schulz03ad7ae2018-11-20 17:45:52 +010013149 tokpushback = 0;
Denys Vlasenko958581a2010-09-12 15:04:27 +020013150 setprompt_if(needprompt, 2);
Denys Vlasenkoacf79f92020-02-14 16:12:06 +010013151 if (here->here->type == NHERE)
13152 readtoken1(pgetc(), SQSYNTAX, here->eofmark, here->striptabs);
13153 else
13154 readtoken1(pgetc_eatbnl(), DQSYNTAX, here->eofmark, here->striptabs);
Denis Vlasenko597906c2008-02-20 16:38:54 +000013155 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013156 n->narg.type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000013157 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013158 n->narg.text = wordtext;
13159 n->narg.backquote = backquotelist;
13160 here->here->nhere.doc = n;
13161 here = here->next;
Eric Andersencb57d552001-06-28 07:25:16 +000013162 }
Eric Andersencb57d552001-06-28 07:25:16 +000013163}
13164
13165
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000013166static const char *
Denys Vlasenko46999802017-07-29 21:12:29 +020013167expandstr(const char *ps, int syntax_type)
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000013168{
13169 union node n;
Denys Vlasenko2a6d29a2016-10-25 21:17:52 +020013170 int saveprompt;
Ron Yorston48645b82019-04-18 09:48:13 +010013171 struct parsefile *file_stop = g_parsefile;
Ron Yorstond1a2fa22019-04-18 09:49:13 +010013172 volatile int saveint;
13173 struct jmploc *volatile savehandler = exception_handler;
13174 struct jmploc jmploc;
Ron Yorstonb0c711e2020-01-23 11:26:08 +000013175 const char *volatile result;
13176 int err;
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000013177
Denys Vlasenko46999802017-07-29 21:12:29 +020013178 /* XXX Fix (char *) cast. */
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000013179 setinputstring((char *)ps);
Denys Vlasenko2a6d29a2016-10-25 21:17:52 +020013180
13181 saveprompt = doprompt;
13182 doprompt = 0;
Ron Yorstonb0c711e2020-01-23 11:26:08 +000013183 result = ps;
13184
13185 SAVE_INT(saveint);
13186 err = setjmp(jmploc.loc);
13187 if (err)
13188 goto out;
Denys Vlasenkoa2e32b32017-10-12 19:20:13 +020013189
13190 /* readtoken1() might die horribly.
Denys Vlasenko3c183a82017-10-12 19:35:42 +020013191 * Try a prompt with syntactically wrong command:
Denys Vlasenkoa2e32b32017-10-12 19:20:13 +020013192 * PS1='$(date "+%H:%M:%S) > '
13193 */
Ron Yorstonb0c711e2020-01-23 11:26:08 +000013194 exception_handler = &jmploc;
13195 readtoken1(pgetc(), syntax_type, FAKEEOFMARK, 0);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000013196
13197 n.narg.type = NARG;
13198 n.narg.next = NULL;
13199 n.narg.text = wordtext;
13200 n.narg.backquote = backquotelist;
13201
Ron Yorstond1a2fa22019-04-18 09:49:13 +010013202 /* expandarg() might fail too:
13203 * PS1='$((123+))'
13204 */
Ron Yorstonb0c711e2020-01-23 11:26:08 +000013205 expandarg(&n, NULL, EXP_QUOTED);
13206 result = stackblock();
13207
13208out:
Ron Yorstond1a2fa22019-04-18 09:49:13 +010013209 exception_handler = savehandler;
Ron Yorstonb0c711e2020-01-23 11:26:08 +000013210 if (err && exception_type != EXERROR)
13211 longjmp(exception_handler->loc, 1);
Ron Yorstond1a2fa22019-04-18 09:49:13 +010013212 RESTORE_INT(saveint);
13213
Ron Yorstonb0c711e2020-01-23 11:26:08 +000013214 doprompt = saveprompt;
13215 /* Try: PS1='`xxx(`' */
13216 unwindfiles(file_stop);
13217
13218 return result;
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000013219}
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000013220
Denys Vlasenko1e3e2cc2017-07-25 20:31:14 +020013221static inline int
13222parser_eof(void)
13223{
13224 return tokpushback && lasttoken == TEOF;
13225}
13226
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013227/*
13228 * Execute a command or commands contained in a string.
13229 */
13230static int
Denys Vlasenko7b3fa1e2016-10-01 15:10:16 +020013231evalstring(char *s, int flags)
Eric Andersenc470f442003-07-28 09:56:35 +000013232{
Denys Vlasenko493b9ca2016-10-30 18:27:14 +010013233 struct jmploc *volatile savehandler;
Denys Vlasenkod81e9f52016-10-28 15:43:50 +020013234 struct jmploc jmploc;
13235 int ex;
13236
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013237 union node *n;
13238 struct stackmark smark;
Denys Vlasenko928e2a72016-09-29 00:30:31 +020013239 int status;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013240
Denys Vlasenko8e2bc472016-09-28 23:02:57 +020013241 s = sstrdup(s);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013242 setinputstring(s);
13243 setstackmark(&smark);
13244
Denys Vlasenko928e2a72016-09-29 00:30:31 +020013245 status = 0;
Denys Vlasenkod81e9f52016-10-28 15:43:50 +020013246 /* On exception inside execution loop, we must popfile().
13247 * Try interactively:
13248 * readonly a=a
13249 * command eval "a=b" # throws "is read only" error
13250 * "command BLTIN" is not supposed to abort (even in non-interactive use).
13251 * But if we skip popfile(), we hit EOF in eval's string, and exit.
13252 */
13253 savehandler = exception_handler;
Denys Vlasenkod81e9f52016-10-28 15:43:50 +020013254 ex = setjmp(jmploc.loc);
13255 if (ex)
13256 goto out;
Denys Vlasenko493b9ca2016-10-30 18:27:14 +010013257 exception_handler = &jmploc;
Denys Vlasenkod81e9f52016-10-28 15:43:50 +020013258
Denys Vlasenko86e83ec2009-07-23 22:07:07 +020013259 while ((n = parsecmd(0)) != NODE_EOF) {
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +020013260 int i;
13261
Denys Vlasenko1e3e2cc2017-07-25 20:31:14 +020013262 i = evaltree(n, flags & ~(parser_eof() ? 0 : EV_EXIT));
Denys Vlasenko928e2a72016-09-29 00:30:31 +020013263 if (n)
13264 status = i;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013265 popstackmark(&smark);
Denys Vlasenko928e2a72016-09-29 00:30:31 +020013266 if (evalskip)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013267 break;
13268 }
Denys Vlasenkod81e9f52016-10-28 15:43:50 +020013269 out:
Denys Vlasenko8e2bc472016-09-28 23:02:57 +020013270 popstackmark(&smark);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013271 popfile();
Denys Vlasenko8e2bc472016-09-28 23:02:57 +020013272 stunalloc(s);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013273
Denys Vlasenkod81e9f52016-10-28 15:43:50 +020013274 exception_handler = savehandler;
13275 if (ex)
Denys Vlasenko14c85eb2017-10-12 19:40:47 +020013276 longjmp(exception_handler->loc, ex);
Denys Vlasenkod81e9f52016-10-28 15:43:50 +020013277
Denys Vlasenko928e2a72016-09-29 00:30:31 +020013278 return status;
Eric Andersenc470f442003-07-28 09:56:35 +000013279}
13280
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013281/*
13282 * The eval command.
13283 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020013284static int FAST_FUNC
Denys Vlasenko7b3fa1e2016-10-01 15:10:16 +020013285evalcmd(int argc UNUSED_PARAM, char **argv, int flags)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013286{
13287 char *p;
13288 char *concat;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013289
Denis Vlasenko68404f12008-03-17 09:00:54 +000013290 if (argv[1]) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013291 p = argv[1];
Denis Vlasenko68404f12008-03-17 09:00:54 +000013292 argv += 2;
13293 if (argv[0]) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013294 STARTSTACKSTR(concat);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013295 for (;;) {
13296 concat = stack_putstr(p, concat);
Denis Vlasenko68404f12008-03-17 09:00:54 +000013297 p = *argv++;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013298 if (p == NULL)
13299 break;
13300 STPUTC(' ', concat);
13301 }
13302 STPUTC('\0', concat);
13303 p = grabstackstr(concat);
13304 }
Denys Vlasenko7b3fa1e2016-10-01 15:10:16 +020013305 return evalstring(p, flags & EV_TESTED);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013306 }
Denys Vlasenko928e2a72016-09-29 00:30:31 +020013307 return 0;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013308}
13309
13310/*
Denys Vlasenko285ad152009-12-04 23:02:27 +010013311 * Read and execute commands.
13312 * "Top" is nonzero for the top level command loop;
13313 * it turns on prompting if the shell is interactive.
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013314 */
13315static int
13316cmdloop(int top)
13317{
13318 union node *n;
13319 struct stackmark smark;
13320 int inter;
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +020013321 int status = 0;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013322 int numeof = 0;
13323
13324 TRACE(("cmdloop(%d) called\n", top));
13325 for (;;) {
13326 int skip;
13327
13328 setstackmark(&smark);
13329#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +000013330 if (doing_jobctl)
Denys Vlasenko9c541002015-10-07 15:44:36 +020013331 showjobs(SHOW_CHANGED|SHOW_STDERR);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013332#endif
13333 inter = 0;
13334 if (iflag && top) {
13335 inter++;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013336 chkmail();
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013337 }
13338 n = parsecmd(inter);
Denys Vlasenko7cee00e2009-07-24 01:08:03 +020013339#if DEBUG
13340 if (DEBUG > 2 && debug && (n != NODE_EOF))
Denys Vlasenko883cea42009-07-11 15:31:59 +020013341 showtree(n);
Denis Vlasenko135cecb2009-04-12 00:00:57 +000013342#endif
Denys Vlasenko86e83ec2009-07-23 22:07:07 +020013343 if (n == NODE_EOF) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013344 if (!top || numeof >= 50)
13345 break;
13346 if (!stoppedjobs()) {
Denys Vlasenko226b8a12020-02-16 18:57:53 +010013347 if (!Iflag) {
13348 if (iflag) {
13349 newline_and_flush(stderr);
13350 }
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013351 break;
Denys Vlasenko226b8a12020-02-16 18:57:53 +010013352 }
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013353 out2str("\nUse \"exit\" to leave shell.\n");
13354 }
13355 numeof++;
13356 } else if (nflag == 0) {
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +020013357 int i;
13358
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +000013359 /* job_warning can only be 2,1,0. Here 2->1, 1/0->0 */
13360 job_warning >>= 1;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013361 numeof = 0;
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +020013362 i = evaltree(n, 0);
13363 if (n)
13364 status = i;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013365 }
13366 popstackmark(&smark);
13367 skip = evalskip;
13368
13369 if (skip) {
Denys Vlasenkocd24a502020-02-20 16:47:01 +010013370 evalskip &= ~(SKIPFUNC | SKIPFUNCDEF);
Denys Vlasenko0840c912016-10-01 15:27:44 +020013371 break;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013372 }
13373 }
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +020013374 return status;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013375}
13376
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000013377/*
13378 * Take commands from a file. To be compatible we should do a path
13379 * search for the file, which is necessary to find sub-commands.
13380 */
13381static char *
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +010013382find_dot_file(char *basename)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000013383{
13384 char *fullname;
13385 const char *path = pathval();
13386 struct stat statb;
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +010013387 int len;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000013388
13389 /* don't try this for absolute or relative paths */
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +010013390 if (strchr(basename, '/'))
13391 return basename;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000013392
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +010013393 while ((len = padvance(&path, basename)) >= 0) {
13394 fullname = stackblock();
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +010013395 if ((!pathopt || *pathopt == 'f')
13396 && !stat(fullname, &statb) && S_ISREG(statb.st_mode)
13397 ) {
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +010013398 /* This will be freed by the caller. */
13399 return stalloc(len);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000013400 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000013401 }
Denys Vlasenko01f7b9e2018-01-26 15:15:43 +010013402 /* not found in PATH */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000013403
Denys Vlasenko01f7b9e2018-01-26 15:15:43 +010013404#if ENABLE_ASH_BASH_SOURCE_CURDIR
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +010013405 return basename;
Denys Vlasenko01f7b9e2018-01-26 15:15:43 +010013406#else
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +010013407 ash_msg_and_raise_error("%s: not found", basename);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000013408 /* NOTREACHED */
Denys Vlasenko01f7b9e2018-01-26 15:15:43 +010013409#endif
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000013410}
13411
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020013412static int FAST_FUNC
Denys Vlasenko0cdb7ea2016-10-02 03:16:00 +020013413dotcmd(int argc_ UNUSED_PARAM, char **argv_ UNUSED_PARAM)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013414{
Denys Vlasenko0cdb7ea2016-10-02 03:16:00 +020013415 /* "false; . empty_file; echo $?" should print 0, not 1: */
13416 int status = 0;
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020013417 char *fullname;
Denys Vlasenko0cdb7ea2016-10-02 03:16:00 +020013418 char **argv;
Denys Vlasenkofb87d932017-01-09 08:22:06 +010013419 char *args_need_save;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013420 volatile struct shparam saveparam;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013421
Denys Vlasenko981a0562017-07-26 19:53:11 +020013422//???
13423// struct strlist *sp;
13424// for (sp = cmdenviron; sp; sp = sp->next)
13425// setvareq(ckstrdup(sp->text), VSTRFIXED | VTEXTFIXED);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013426
Denys Vlasenko0cdb7ea2016-10-02 03:16:00 +020013427 nextopt(nullstr); /* handle possible "--" */
13428 argv = argptr;
13429
13430 if (!argv[0]) {
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020013431 /* bash says: "bash: .: filename argument required" */
13432 return 2; /* bash compat */
13433 }
13434
Denys Vlasenko091f8312013-03-17 14:25:22 +010013435 /* This aborts if file isn't found, which is POSIXly correct.
13436 * bash returns exitcode 1 instead.
13437 */
Denys Vlasenko0cdb7ea2016-10-02 03:16:00 +020013438 fullname = find_dot_file(argv[0]);
13439 argv++;
Denys Vlasenkofb87d932017-01-09 08:22:06 +010013440 args_need_save = argv[0];
Denys Vlasenko5f7c82b2017-02-03 13:00:06 +010013441 if (args_need_save) { /* ". FILE ARGS", and ARGS are not empty */
Denys Vlasenko0cdb7ea2016-10-02 03:16:00 +020013442 int argc;
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020013443 saveparam = shellparam;
13444 shellparam.malloced = 0;
Denys Vlasenko0cdb7ea2016-10-02 03:16:00 +020013445 argc = 1;
13446 while (argv[argc])
13447 argc++;
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020013448 shellparam.nparam = argc;
13449 shellparam.p = argv;
13450 };
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013451
Denys Vlasenko091f8312013-03-17 14:25:22 +010013452 /* This aborts if file can't be opened, which is POSIXly correct.
13453 * bash returns exitcode 1 instead.
13454 */
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020013455 setinputfile(fullname, INPUT_PUSH_FILE);
13456 commandname = fullname;
Denys Vlasenko0cdb7ea2016-10-02 03:16:00 +020013457 status = cmdloop(0);
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020013458 popfile();
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013459
Denys Vlasenkofb87d932017-01-09 08:22:06 +010013460 if (args_need_save) {
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020013461 freeparam(&shellparam);
13462 shellparam = saveparam;
13463 };
13464
Denys Vlasenko0cdb7ea2016-10-02 03:16:00 +020013465 return status;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013466}
13467
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020013468static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000013469exitcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013470{
13471 if (stoppedjobs())
13472 return 0;
Denys Vlasenko4ccddc82020-02-14 17:27:18 +010013473
Denys Vlasenko970470e2020-02-14 17:32:22 +010013474 if (argv[1])
13475 savestatus = number(argv[1]);
Denys Vlasenko4ccddc82020-02-14 17:27:18 +010013476
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013477 raise_exception(EXEXIT);
13478 /* NOTREACHED */
13479}
13480
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013481/*
13482 * Read a file containing shell functions.
13483 */
13484static void
13485readcmdfile(char *name)
13486{
13487 setinputfile(name, INPUT_PUSH_FILE);
13488 cmdloop(0);
13489 popfile();
13490}
13491
13492
Denis Vlasenkocc571512007-02-23 21:10:35 +000013493/* ============ find_command inplementation */
13494
13495/*
13496 * Resolve a command name. If you change this routine, you may have to
13497 * change the shellexec routine as well.
13498 */
13499static void
13500find_command(char *name, struct cmdentry *entry, int act, const char *path)
13501{
13502 struct tblentry *cmdp;
13503 int idx;
13504 int prev;
13505 char *fullname;
13506 struct stat statb;
13507 int e;
13508 int updatetbl;
13509 struct builtincmd *bcmd;
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +010013510 int len;
Denis Vlasenkocc571512007-02-23 21:10:35 +000013511
13512 /* If name contains a slash, don't use PATH or hash table */
13513 if (strchr(name, '/') != NULL) {
13514 entry->u.index = -1;
13515 if (act & DO_ABS) {
13516 while (stat(name, &statb) < 0) {
13517#ifdef SYSV
13518 if (errno == EINTR)
13519 continue;
13520#endif
13521 entry->cmdtype = CMDUNKNOWN;
13522 return;
13523 }
13524 }
13525 entry->cmdtype = CMDNORMAL;
13526 return;
13527 }
13528
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000013529/* #if ENABLE_FEATURE_SH_STANDALONE... moved after builtin check */
Denis Vlasenkocc571512007-02-23 21:10:35 +000013530
13531 updatetbl = (path == pathval());
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010013532 if (!updatetbl)
Denis Vlasenkocc571512007-02-23 21:10:35 +000013533 act |= DO_ALTPATH;
Denis Vlasenkocc571512007-02-23 21:10:35 +000013534
13535 /* If name is in the table, check answer will be ok */
13536 cmdp = cmdlookup(name, 0);
13537 if (cmdp != NULL) {
13538 int bit;
13539
13540 switch (cmdp->cmdtype) {
13541 default:
13542#if DEBUG
13543 abort();
13544#endif
13545 case CMDNORMAL:
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010013546 bit = DO_ALTPATH | DO_REGBLTIN;
Denis Vlasenkocc571512007-02-23 21:10:35 +000013547 break;
13548 case CMDFUNCTION:
13549 bit = DO_NOFUNC;
13550 break;
13551 case CMDBUILTIN:
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010013552 bit = IS_BUILTIN_REGULAR(cmdp->param.cmd) ? 0 : DO_REGBLTIN;
Denis Vlasenkocc571512007-02-23 21:10:35 +000013553 break;
13554 }
13555 if (act & bit) {
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010013556 if (act & bit & DO_REGBLTIN)
13557 goto fail;
13558
Denis Vlasenkocc571512007-02-23 21:10:35 +000013559 updatetbl = 0;
13560 cmdp = NULL;
13561 } else if (cmdp->rehash == 0)
13562 /* if not invalidated by cd, we're done */
13563 goto success;
13564 }
13565
13566 /* If %builtin not in path, check for builtin next */
13567 bcmd = find_builtin(name);
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000013568 if (bcmd) {
13569 if (IS_BUILTIN_REGULAR(bcmd))
13570 goto builtin_success;
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010013571 if (act & DO_ALTPATH)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000013572 goto builtin_success;
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010013573 if (builtinloc <= 0)
13574 goto builtin_success;
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000013575 }
Denis Vlasenkocc571512007-02-23 21:10:35 +000013576
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010013577 if (act & DO_REGBLTIN)
13578 goto fail;
13579
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000013580#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000013581 {
13582 int applet_no = find_applet_by_name(name);
13583 if (applet_no >= 0) {
13584 entry->cmdtype = CMDNORMAL;
13585 entry->u.index = -2 - applet_no;
13586 return;
13587 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000013588 }
13589#endif
13590
Denis Vlasenkocc571512007-02-23 21:10:35 +000013591 /* We have to search path. */
13592 prev = -1; /* where to start */
13593 if (cmdp && cmdp->rehash) { /* doing a rehash */
13594 if (cmdp->cmdtype == CMDBUILTIN)
13595 prev = builtinloc;
13596 else
13597 prev = cmdp->param.index;
13598 }
13599
13600 e = ENOENT;
13601 idx = -1;
13602 loop:
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +010013603 while ((len = padvance(&path, name)) >= 0) {
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +010013604 const char *lpathopt = pathopt;
13605
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +010013606 fullname = stackblock();
Denis Vlasenkocc571512007-02-23 21:10:35 +000013607 idx++;
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +010013608 if (lpathopt) {
13609 if (*lpathopt == 'b') {
Denis Vlasenkocc571512007-02-23 21:10:35 +000013610 if (bcmd)
13611 goto builtin_success;
13612 continue;
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +010013613 } else if (!(act & DO_NOFUNC)) {
13614 /* handled below */
13615 } else {
13616 /* ignore unimplemented options */
Denis Vlasenkocc571512007-02-23 21:10:35 +000013617 continue;
13618 }
13619 }
13620 /* if rehash, don't redo absolute path names */
13621 if (fullname[0] == '/' && idx <= prev) {
13622 if (idx < prev)
13623 continue;
13624 TRACE(("searchexec \"%s\": no change\n", name));
13625 goto success;
13626 }
13627 while (stat(fullname, &statb) < 0) {
13628#ifdef SYSV
13629 if (errno == EINTR)
13630 continue;
13631#endif
13632 if (errno != ENOENT && errno != ENOTDIR)
13633 e = errno;
13634 goto loop;
13635 }
13636 e = EACCES; /* if we fail, this will be the error */
13637 if (!S_ISREG(statb.st_mode))
13638 continue;
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +010013639 if (lpathopt) { /* this is a %func directory */
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +010013640 stalloc(len);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000013641 /* NB: stalloc will return space pointed by fullname
13642 * (because we don't have any intervening allocations
13643 * between stunalloc above and this stalloc) */
Denis Vlasenkocc571512007-02-23 21:10:35 +000013644 readcmdfile(fullname);
13645 cmdp = cmdlookup(name, 0);
13646 if (cmdp == NULL || cmdp->cmdtype != CMDFUNCTION)
13647 ash_msg_and_raise_error("%s not defined in %s", name, fullname);
13648 stunalloc(fullname);
13649 goto success;
13650 }
13651 TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
13652 if (!updatetbl) {
13653 entry->cmdtype = CMDNORMAL;
13654 entry->u.index = idx;
13655 return;
13656 }
13657 INT_OFF;
13658 cmdp = cmdlookup(name, 1);
13659 cmdp->cmdtype = CMDNORMAL;
13660 cmdp->param.index = idx;
13661 INT_ON;
13662 goto success;
13663 }
13664
13665 /* We failed. If there was an entry for this command, delete it */
13666 if (cmdp && updatetbl)
13667 delete_cmd_entry();
William Pitcockd8fd88a2018-01-24 18:33:18 +010013668 if (act & DO_ERR) {
13669#if ENABLE_ASH_BASH_NOT_FOUND_HOOK
13670 struct tblentry *hookp = cmdlookup("command_not_found_handle", 0);
13671 if (hookp && hookp->cmdtype == CMDFUNCTION) {
13672 char *argv[3];
13673 argv[0] = (char*) "command_not_found_handle";
13674 argv[1] = name;
13675 argv[2] = NULL;
13676 evalfun(hookp->param.func, 2, argv, 0);
13677 entry->cmdtype = CMDUNKNOWN;
13678 return;
13679 }
13680#endif
Denis Vlasenkocc571512007-02-23 21:10:35 +000013681 ash_msg("%s: %s", name, errmsg(e, "not found"));
William Pitcockd8fd88a2018-01-24 18:33:18 +010013682 }
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010013683 fail:
Denis Vlasenkocc571512007-02-23 21:10:35 +000013684 entry->cmdtype = CMDUNKNOWN;
13685 return;
13686
13687 builtin_success:
13688 if (!updatetbl) {
13689 entry->cmdtype = CMDBUILTIN;
13690 entry->u.cmd = bcmd;
13691 return;
13692 }
13693 INT_OFF;
13694 cmdp = cmdlookup(name, 1);
13695 cmdp->cmdtype = CMDBUILTIN;
13696 cmdp->param.cmd = bcmd;
13697 INT_ON;
13698 success:
13699 cmdp->rehash = 0;
13700 entry->cmdtype = cmdp->cmdtype;
13701 entry->u = cmdp->param;
13702}
13703
13704
Eric Andersencb57d552001-06-28 07:25:16 +000013705/*
Eric Andersencb57d552001-06-28 07:25:16 +000013706 * The trap builtin.
13707 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020013708static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000013709trapcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +000013710{
13711 char *action;
13712 char **ap;
Denys Vlasenko496d5bf2010-03-26 15:52:24 +010013713 int signo, exitcode;
Eric Andersencb57d552001-06-28 07:25:16 +000013714
Eric Andersenc470f442003-07-28 09:56:35 +000013715 nextopt(nullstr);
13716 ap = argptr;
13717 if (!*ap) {
Denis Vlasenko2da584f2007-02-19 22:44:05 +000013718 for (signo = 0; signo < NSIG; signo++) {
Denys Vlasenko21d87d42009-09-25 00:06:51 +020013719 char *tr = trap_ptr[signo];
13720 if (tr) {
Denys Vlasenkoe74aaf92009-09-27 02:05:45 +020013721 /* note: bash adds "SIG", but only if invoked
13722 * as "bash". If called as "sh", or if set -o posix,
13723 * then it prints short signal names.
13724 * We are printing short names: */
13725 out1fmt("trap -- %s %s\n",
Denys Vlasenko21d87d42009-09-25 00:06:51 +020013726 single_quote(tr),
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +000013727 get_signame(signo));
Denys Vlasenko726e1a02009-09-25 02:58:20 +020013728 /* trap_ptr != trap only if we are in special-cased `trap` code.
13729 * In this case, we will exit very soon, no need to free(). */
Denys Vlasenkoe74aaf92009-09-27 02:05:45 +020013730 /* if (trap_ptr != trap && tp[0]) */
Denys Vlasenko726e1a02009-09-25 02:58:20 +020013731 /* free(tr); */
Eric Andersencb57d552001-06-28 07:25:16 +000013732 }
13733 }
Denys Vlasenko726e1a02009-09-25 02:58:20 +020013734 /*
Denys Vlasenko21d87d42009-09-25 00:06:51 +020013735 if (trap_ptr != trap) {
13736 free(trap_ptr);
13737 trap_ptr = trap;
13738 }
Denys Vlasenko726e1a02009-09-25 02:58:20 +020013739 */
Eric Andersencb57d552001-06-28 07:25:16 +000013740 return 0;
13741 }
Denys Vlasenko21d87d42009-09-25 00:06:51 +020013742
Denys Vlasenko86981e32017-07-25 20:06:17 +020013743 /* Why the second check?
13744 * "trap NUM [sig2]..." is the same as "trap - NUM [sig2]..."
13745 * In this case, NUM is signal no, not an action.
13746 */
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +000013747 action = NULL;
Denys Vlasenko86981e32017-07-25 20:06:17 +020013748 if (ap[1] && !is_number(ap[0]))
Eric Andersencb57d552001-06-28 07:25:16 +000013749 action = *ap++;
Denys Vlasenko86981e32017-07-25 20:06:17 +020013750
Denys Vlasenko496d5bf2010-03-26 15:52:24 +010013751 exitcode = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000013752 while (*ap) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000013753 signo = get_signum(*ap);
Denys Vlasenko86981e32017-07-25 20:06:17 +020013754 if (signo < 0) {
Denys Vlasenko496d5bf2010-03-26 15:52:24 +010013755 /* Mimic bash message exactly */
13756 ash_msg("%s: invalid signal specification", *ap);
13757 exitcode = 1;
13758 goto next;
13759 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000013760 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +000013761 if (action) {
Denis Vlasenko9f739442006-12-16 23:49:13 +000013762 if (LONE_DASH(action))
Eric Andersencb57d552001-06-28 07:25:16 +000013763 action = NULL;
Denys Vlasenkob4f51d32016-10-27 12:55:09 +020013764 else {
13765 if (action[0]) /* not NULL and not "" and not "-" */
13766 may_have_traps = 1;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013767 action = ckstrdup(action);
Denys Vlasenkob4f51d32016-10-27 12:55:09 +020013768 }
Eric Andersencb57d552001-06-28 07:25:16 +000013769 }
Denis Vlasenko60818682007-09-28 22:07:23 +000013770 free(trap[signo]);
Eric Andersencb57d552001-06-28 07:25:16 +000013771 trap[signo] = action;
13772 if (signo != 0)
13773 setsignal(signo);
Denis Vlasenkob012b102007-02-19 22:43:01 +000013774 INT_ON;
Denys Vlasenko496d5bf2010-03-26 15:52:24 +010013775 next:
Eric Andersencb57d552001-06-28 07:25:16 +000013776 ap++;
13777 }
Denys Vlasenko496d5bf2010-03-26 15:52:24 +010013778 return exitcode;
Eric Andersencb57d552001-06-28 07:25:16 +000013779}
13780
Eric Andersenc470f442003-07-28 09:56:35 +000013781
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000013782/* ============ Builtins */
Eric Andersenc470f442003-07-28 09:56:35 +000013783
Denys Vlasenko2ec34962014-09-08 16:52:39 +020013784#if ENABLE_ASH_HELP
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020013785static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000013786helpcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000013787{
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000013788 unsigned col;
13789 unsigned i;
Eric Andersenc470f442003-07-28 09:56:35 +000013790
Denys Vlasenkod6b05eb2009-06-06 20:59:55 +020013791 out1fmt(
Denis Vlasenko34d4d892009-04-04 20:24:37 +000013792 "Built-in commands:\n"
13793 "------------------\n");
Denis Vlasenkob71c6682007-07-21 15:08:09 +000013794 for (col = 0, i = 0; i < ARRAY_SIZE(builtintab); i++) {
Eric Andersenc470f442003-07-28 09:56:35 +000013795 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '),
Denis Vlasenko52764022007-02-24 13:42:56 +000013796 builtintab[i].name + 1);
Eric Andersenc470f442003-07-28 09:56:35 +000013797 if (col > 60) {
13798 out1fmt("\n");
13799 col = 0;
13800 }
13801 }
Denys Vlasenko2ec34962014-09-08 16:52:39 +020013802# if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko1aa7e472007-11-28 06:49:03 +000013803 {
13804 const char *a = applet_names;
13805 while (*a) {
13806 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '), a);
13807 if (col > 60) {
13808 out1fmt("\n");
13809 col = 0;
13810 }
Ron Yorston2b919582016-04-08 11:57:20 +010013811 while (*a++ != '\0')
13812 continue;
Eric Andersenc470f442003-07-28 09:56:35 +000013813 }
13814 }
Denys Vlasenko2ec34962014-09-08 16:52:39 +020013815# endif
Denys Vlasenkoebedb942016-10-02 18:45:09 +020013816 newline_and_flush(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +000013817 return EXIT_SUCCESS;
13818}
Denys Vlasenko2ec34962014-09-08 16:52:39 +020013819#endif
Eric Andersenc470f442003-07-28 09:56:35 +000013820
Flemming Madsend96ffda2013-04-07 18:47:24 +020013821#if MAX_HISTORY
13822static int FAST_FUNC
13823historycmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
13824{
Ron Yorston9f3b4102019-12-16 09:31:10 +000013825 show_history(line_input_state);
Flemming Madsend96ffda2013-04-07 18:47:24 +020013826 return EXIT_SUCCESS;
13827}
13828#endif
13829
Eric Andersencb57d552001-06-28 07:25:16 +000013830/*
Eric Andersencb57d552001-06-28 07:25:16 +000013831 * The export and readonly commands.
13832 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020013833static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000013834exportcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000013835{
13836 struct var *vp;
13837 char *name;
13838 const char *p;
Eric Andersenc470f442003-07-28 09:56:35 +000013839 char **aptr;
Denys Vlasenkod5275882012-10-01 13:41:17 +020013840 char opt;
13841 int flag;
13842 int flag_off;
Eric Andersencb57d552001-06-28 07:25:16 +000013843
Denys Vlasenkod5275882012-10-01 13:41:17 +020013844 /* "readonly" in bash accepts, but ignores -n.
13845 * We do the same: it saves a conditional in nextopt's param.
13846 */
13847 flag_off = 0;
13848 while ((opt = nextopt("np")) != '\0') {
13849 if (opt == 'n')
13850 flag_off = VEXPORT;
13851 }
13852 flag = VEXPORT;
13853 if (argv[0][0] == 'r') {
13854 flag = VREADONLY;
13855 flag_off = 0; /* readonly ignores -n */
13856 }
13857 flag_off = ~flag_off;
13858
Denys Vlasenko10ad6222017-04-17 16:13:32 +020013859 /*if (opt_p_not_specified) - bash doesn't check this. Try "export -p NAME" */
Denys Vlasenkod5275882012-10-01 13:41:17 +020013860 {
Denis Vlasenko2da584f2007-02-19 22:44:05 +000013861 aptr = argptr;
13862 name = *aptr;
13863 if (name) {
13864 do {
13865 p = strchr(name, '=');
13866 if (p != NULL) {
13867 p++;
13868 } else {
13869 vp = *findvar(hashvar(name), name);
13870 if (vp) {
Denys Vlasenkod5275882012-10-01 13:41:17 +020013871 vp->flags = ((vp->flags | flag) & flag_off);
Denis Vlasenko2da584f2007-02-19 22:44:05 +000013872 continue;
13873 }
Eric Andersencb57d552001-06-28 07:25:16 +000013874 }
Denys Vlasenkod5275882012-10-01 13:41:17 +020013875 setvar(name, p, (flag & flag_off));
Denis Vlasenko2da584f2007-02-19 22:44:05 +000013876 } while ((name = *++aptr) != NULL);
13877 return 0;
13878 }
Eric Andersencb57d552001-06-28 07:25:16 +000013879 }
Denys Vlasenkod5275882012-10-01 13:41:17 +020013880
13881 /* No arguments. Show the list of exported or readonly vars.
13882 * -n is ignored.
13883 */
Denis Vlasenko2da584f2007-02-19 22:44:05 +000013884 showvars(argv[0], flag, 0);
Eric Andersencb57d552001-06-28 07:25:16 +000013885 return 0;
13886}
13887
Eric Andersencb57d552001-06-28 07:25:16 +000013888/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000013889 * Delete a function if it exists.
Eric Andersencb57d552001-06-28 07:25:16 +000013890 */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000013891static void
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000013892unsetfunc(const char *name)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000013893{
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000013894 struct tblentry *cmdp;
Eric Andersencb57d552001-06-28 07:25:16 +000013895
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000013896 cmdp = cmdlookup(name, 0);
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +020013897 if (cmdp != NULL && cmdp->cmdtype == CMDFUNCTION)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000013898 delete_cmd_entry();
Eric Andersenc470f442003-07-28 09:56:35 +000013899}
13900
Eric Andersencb57d552001-06-28 07:25:16 +000013901/*
Eric Andersencb57d552001-06-28 07:25:16 +000013902 * The unset builtin command. We unset the function before we unset the
13903 * variable to allow a function to be unset when there is a readonly variable
13904 * with the same name.
13905 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020013906static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000013907unsetcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +000013908{
13909 char **ap;
13910 int i;
Eric Andersenc470f442003-07-28 09:56:35 +000013911 int flag = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000013912
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +020013913 while ((i = nextopt("vf")) != 0) {
Eric Andersenc470f442003-07-28 09:56:35 +000013914 flag = i;
Eric Andersencb57d552001-06-28 07:25:16 +000013915 }
Eric Andersencb57d552001-06-28 07:25:16 +000013916
Denis Vlasenko2da584f2007-02-19 22:44:05 +000013917 for (ap = argptr; *ap; ap++) {
Eric Andersenc470f442003-07-28 09:56:35 +000013918 if (flag != 'f') {
Denys Vlasenkob28d4c32017-07-25 16:29:36 +020013919 unsetvar(*ap);
13920 continue;
Eric Andersenc470f442003-07-28 09:56:35 +000013921 }
13922 if (flag != 'v')
Eric Andersencb57d552001-06-28 07:25:16 +000013923 unsetfunc(*ap);
Eric Andersencb57d552001-06-28 07:25:16 +000013924 }
Denys Vlasenkob28d4c32017-07-25 16:29:36 +020013925 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +000013926}
13927
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000013928static const unsigned char timescmd_str[] ALIGN1 = {
Manuel Novoa III 4456f252003-08-13 17:48:47 +000013929 ' ', offsetof(struct tms, tms_utime),
13930 '\n', offsetof(struct tms, tms_stime),
13931 ' ', offsetof(struct tms, tms_cutime),
13932 '\n', offsetof(struct tms, tms_cstime),
13933 0
13934};
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020013935static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000013936timescmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Manuel Novoa III 4456f252003-08-13 17:48:47 +000013937{
Denys Vlasenko11f2e992017-08-10 16:34:03 +020013938 unsigned clk_tck;
Manuel Novoa III 4456f252003-08-13 17:48:47 +000013939 const unsigned char *p;
13940 struct tms buf;
13941
Bartosz Golaszewski5d2e4092014-06-22 14:01:13 +020013942 clk_tck = bb_clk_tck();
Manuel Novoa III 4456f252003-08-13 17:48:47 +000013943
Denys Vlasenko11f2e992017-08-10 16:34:03 +020013944 times(&buf);
Manuel Novoa III 4456f252003-08-13 17:48:47 +000013945 p = timescmd_str;
13946 do {
Denys Vlasenko11f2e992017-08-10 16:34:03 +020013947 unsigned sec, frac;
13948 unsigned long t;
Manuel Novoa III 4456f252003-08-13 17:48:47 +000013949 t = *(clock_t *)(((char *) &buf) + p[1]);
Denys Vlasenko11f2e992017-08-10 16:34:03 +020013950 sec = t / clk_tck;
13951 frac = t % clk_tck;
13952 out1fmt("%um%u.%03us%c",
13953 sec / 60, sec % 60,
13954 (frac * 1000) / clk_tck,
Manuel Novoa III 4456f252003-08-13 17:48:47 +000013955 p[0]);
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +020013956 p += 2;
13957 } while (*p);
Manuel Novoa III 4456f252003-08-13 17:48:47 +000013958
Eric Andersencb57d552001-06-28 07:25:16 +000013959 return 0;
13960}
13961
Denys Vlasenko0b883582016-12-23 16:49:07 +010013962#if ENABLE_FEATURE_SH_MATH
Eric Andersenc470f442003-07-28 09:56:35 +000013963/*
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +020013964 * The let builtin. Partially stolen from GNU Bash, the Bourne Again SHell.
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +000013965 * Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc.
Eric Andersen90898442003-08-06 11:20:52 +000013966 *
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +000013967 * Copyright (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
Eric Andersenc470f442003-07-28 09:56:35 +000013968 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020013969static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000013970letcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000013971{
Denis Vlasenko68404f12008-03-17 09:00:54 +000013972 arith_t i;
Eric Andersenc470f442003-07-28 09:56:35 +000013973
Denis Vlasenko68404f12008-03-17 09:00:54 +000013974 argv++;
13975 if (!*argv)
Denis Vlasenkob012b102007-02-19 22:43:01 +000013976 ash_msg_and_raise_error("expression expected");
Denis Vlasenko68404f12008-03-17 09:00:54 +000013977 do {
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +000013978 i = ash_arith(*argv);
Denis Vlasenko68404f12008-03-17 09:00:54 +000013979 } while (*++argv);
Eric Andersenc470f442003-07-28 09:56:35 +000013980
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000013981 return !i;
Eric Andersenc470f442003-07-28 09:56:35 +000013982}
Eric Andersenc470f442003-07-28 09:56:35 +000013983#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000013984
Eric Andersenc470f442003-07-28 09:56:35 +000013985/*
Denis Vlasenko59f351c2008-03-25 00:07:12 +000013986 * The read builtin. Options:
13987 * -r Do not interpret '\' specially
13988 * -s Turn off echo (tty only)
13989 * -n NCHARS Read NCHARS max
13990 * -p PROMPT Display PROMPT on stderr (if input is from tty)
13991 * -t SECONDS Timeout after SECONDS (tty or pipe only)
13992 * -u FD Read from given FD instead of fd 0
Johannes Schindelin3bef5d82017-08-08 16:46:39 +020013993 * -d DELIM End on DELIM char, not newline
Eric Andersenc470f442003-07-28 09:56:35 +000013994 * This uses unbuffered input, which may be avoidable in some cases.
Denis Vlasenko59f351c2008-03-25 00:07:12 +000013995 * TODO: bash also has:
13996 * -a ARRAY Read into array[0],[1],etc
Denis Vlasenko59f351c2008-03-25 00:07:12 +000013997 * -e Use line editing (tty only)
Eric Andersenc470f442003-07-28 09:56:35 +000013998 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020013999static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000014000readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000014001{
Denys Vlasenko19358cc2018-08-05 15:42:29 +020014002 struct builtin_read_params params;
Denys Vlasenko73067272010-01-12 22:11:24 +010014003 const char *r;
Eric Andersenc470f442003-07-28 09:56:35 +000014004 int i;
14005
Denys Vlasenko19358cc2018-08-05 15:42:29 +020014006 memset(&params, 0, sizeof(params));
14007
Johannes Schindelin3bef5d82017-08-08 16:46:39 +020014008 while ((i = nextopt("p:u:rt:n:sd:")) != '\0') {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000014009 switch (i) {
Paul Fox02eb9342005-09-07 16:56:02 +000014010 case 'p':
Denys Vlasenko19358cc2018-08-05 15:42:29 +020014011 params.opt_p = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000014012 break;
Paul Fox02eb9342005-09-07 16:56:02 +000014013 case 'n':
Denys Vlasenko19358cc2018-08-05 15:42:29 +020014014 params.opt_n = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000014015 break;
14016 case 's':
Denys Vlasenko19358cc2018-08-05 15:42:29 +020014017 params.read_flags |= BUILTIN_READ_SILENT;
Paul Fox02eb9342005-09-07 16:56:02 +000014018 break;
Paul Fox02eb9342005-09-07 16:56:02 +000014019 case 't':
Denys Vlasenko19358cc2018-08-05 15:42:29 +020014020 params.opt_t = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000014021 break;
Paul Fox02eb9342005-09-07 16:56:02 +000014022 case 'r':
Denys Vlasenko19358cc2018-08-05 15:42:29 +020014023 params.read_flags |= BUILTIN_READ_RAW;
Paul Fox02eb9342005-09-07 16:56:02 +000014024 break;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000014025 case 'u':
Denys Vlasenko19358cc2018-08-05 15:42:29 +020014026 params.opt_u = optionarg;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000014027 break;
Johannes Schindelin3bef5d82017-08-08 16:46:39 +020014028#if BASH_READ_D
14029 case 'd':
Denys Vlasenko19358cc2018-08-05 15:42:29 +020014030 params.opt_d = optionarg;
Johannes Schindelin3bef5d82017-08-08 16:46:39 +020014031 break;
14032#endif
Paul Fox02eb9342005-09-07 16:56:02 +000014033 default:
14034 break;
14035 }
Eric Andersenc470f442003-07-28 09:56:35 +000014036 }
Paul Fox02eb9342005-09-07 16:56:02 +000014037
Denys Vlasenko19358cc2018-08-05 15:42:29 +020014038 params.argv = argptr;
14039 params.setvar = setvar0;
14040 params.ifs = bltinlookup("IFS"); /* can be NULL */
14041
Denys Vlasenko9e71e3c2012-09-06 13:28:10 +020014042 /* "read -s" needs to save/restore termios, can't allow ^C
14043 * to jump out of it.
14044 */
Denys Vlasenkof5470412017-05-22 19:34:45 +020014045 again:
Denys Vlasenko9e71e3c2012-09-06 13:28:10 +020014046 INT_OFF;
Denys Vlasenko19358cc2018-08-05 15:42:29 +020014047 r = shell_builtin_read(&params);
Denys Vlasenko9e71e3c2012-09-06 13:28:10 +020014048 INT_ON;
Denis Vlasenko46aeab92009-03-31 19:18:17 +000014049
Denys Vlasenkof5470412017-05-22 19:34:45 +020014050 if ((uintptr_t)r == 1 && errno == EINTR) {
Denys Vlasenko49e6bf22017-08-04 14:28:16 +020014051 /* To get SIGCHLD: sleep 1 & read x; echo $x
14052 * Correct behavior is to not exit "read"
14053 */
Denys Vlasenkof5470412017-05-22 19:34:45 +020014054 if (pending_sig == 0)
14055 goto again;
14056 }
14057
Denys Vlasenko73067272010-01-12 22:11:24 +010014058 if ((uintptr_t)r > 1)
14059 ash_msg_and_raise_error(r);
Denis Vlasenko037576d2007-10-20 18:30:38 +000014060
Denys Vlasenko73067272010-01-12 22:11:24 +010014061 return (uintptr_t)r;
Eric Andersenc470f442003-07-28 09:56:35 +000014062}
14063
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020014064static int FAST_FUNC
Denys Vlasenko6283f982015-10-07 16:56:20 +020014065umaskcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000014066{
Denys Vlasenkoc1e2e002015-10-07 17:32:56 +020014067 static const char permuser[3] ALIGN1 = "ogu";
Eric Andersenc470f442003-07-28 09:56:35 +000014068
Eric Andersenc470f442003-07-28 09:56:35 +000014069 mode_t mask;
Eric Andersenc470f442003-07-28 09:56:35 +000014070 int symbolic_mode = 0;
14071
14072 while (nextopt("S") != '\0') {
14073 symbolic_mode = 1;
14074 }
14075
Denis Vlasenkob012b102007-02-19 22:43:01 +000014076 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000014077 mask = umask(0);
14078 umask(mask);
Denis Vlasenkob012b102007-02-19 22:43:01 +000014079 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000014080
Denys Vlasenko6283f982015-10-07 16:56:20 +020014081 if (*argptr == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000014082 if (symbolic_mode) {
Denys Vlasenko005c4922015-10-10 20:17:12 +020014083 char buf[sizeof(",u=rwx,g=rwx,o=rwx")];
Eric Andersenc470f442003-07-28 09:56:35 +000014084 char *p = buf;
Denys Vlasenkoc1e2e002015-10-07 17:32:56 +020014085 int i;
Eric Andersenc470f442003-07-28 09:56:35 +000014086
Denys Vlasenkoc1e2e002015-10-07 17:32:56 +020014087 i = 2;
14088 for (;;) {
Denys Vlasenko005c4922015-10-10 20:17:12 +020014089 *p++ = ',';
Eric Andersenc470f442003-07-28 09:56:35 +000014090 *p++ = permuser[i];
14091 *p++ = '=';
Denys Vlasenkoc1e2e002015-10-07 17:32:56 +020014092 /* mask is 0..0uuugggooo. i=2 selects uuu bits */
Denys Vlasenko005c4922015-10-10 20:17:12 +020014093 if (!(mask & 0400)) *p++ = 'r';
14094 if (!(mask & 0200)) *p++ = 'w';
14095 if (!(mask & 0100)) *p++ = 'x';
14096 mask <<= 3;
Denys Vlasenkoc1e2e002015-10-07 17:32:56 +020014097 if (--i < 0)
14098 break;
Eric Andersenc470f442003-07-28 09:56:35 +000014099 }
Denys Vlasenkoc1e2e002015-10-07 17:32:56 +020014100 *p = '\0';
Denys Vlasenko005c4922015-10-10 20:17:12 +020014101 puts(buf + 1);
Eric Andersenc470f442003-07-28 09:56:35 +000014102 } else {
Denys Vlasenkoec046f72015-10-07 17:57:53 +020014103 out1fmt("%04o\n", mask);
Eric Andersenc470f442003-07-28 09:56:35 +000014104 }
14105 } else {
Denys Vlasenko6283f982015-10-07 16:56:20 +020014106 char *modestr = *argptr;
Denys Vlasenko14c85eb2017-10-12 19:40:47 +020014107 /* numeric umasks are taken as-is */
14108 /* symbolic umasks are inverted: "umask a=rx" calls umask(222) */
Denys Vlasenko6283f982015-10-07 16:56:20 +020014109 if (!isdigit(modestr[0]))
14110 mask ^= 0777;
Denys Vlasenko5711a2a2015-10-07 17:55:33 +020014111 mask = bb_parse_mode(modestr, mask);
14112 if ((unsigned)mask > 0777) {
Denys Vlasenko6283f982015-10-07 16:56:20 +020014113 ash_msg_and_raise_error("illegal mode: %s", modestr);
Eric Andersenc470f442003-07-28 09:56:35 +000014114 }
Denys Vlasenko6283f982015-10-07 16:56:20 +020014115 if (!isdigit(modestr[0]))
14116 mask ^= 0777;
14117 umask(mask);
Eric Andersenc470f442003-07-28 09:56:35 +000014118 }
14119 return 0;
14120}
14121
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020014122static int FAST_FUNC
Denys Vlasenkof3c742f2010-03-06 20:12:00 +010014123ulimitcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000014124{
Denys Vlasenkof3c742f2010-03-06 20:12:00 +010014125 return shell_builtin_ulimit(argv);
Eric Andersenc470f442003-07-28 09:56:35 +000014126}
14127
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000014128/* ============ main() and helpers */
14129
14130/*
Denys Vlasenkof977e002020-02-20 16:54:29 +010014131 * This routine is called when an error or an interrupt occurs in an
14132 * interactive shell and control is returned to the main command loop
14133 * but prior to exitshell.
14134 */
14135static void
14136exitreset(void)
14137{
14138 /* from eval.c: */
14139 if (savestatus >= 0) {
14140 if (exception_type == EXEXIT || evalskip == SKIPFUNCDEF)
14141 exitstatus = savestatus;
14142 savestatus = -1;
14143 }
14144 evalskip = 0;
14145 loopnest = 0;
14146
14147 /* from expand.c: */
14148 ifsfree();
14149
14150 /* from redir.c: */
14151 unwindredir(NULL);
14152}
14153
14154/*
14155 * This routine is called when an error or an interrupt occurs in an
14156 * interactive shell and control is returned to the main command loop.
14157 * (In dash, this function is auto-generated by build machinery).
14158 */
14159static void
14160reset(void)
14161{
14162 /* from input.c: */
14163 g_parsefile->left_in_buffer = 0;
14164 g_parsefile->left_in_line = 0; /* clear input buffer */
Denys Vlasenko51a471d2020-12-24 00:22:24 +010014165 g_parsefile->unget = 0;
Denys Vlasenkof977e002020-02-20 16:54:29 +010014166 popallfiles();
14167
14168 /* from var.c: */
14169 unwindlocalvars(NULL);
14170}
14171
14172/*
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000014173 * Called to exit the shell.
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014174 */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000014175static void
14176exitshell(void)
14177{
14178 struct jmploc loc;
14179 char *p;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000014180
Denys Vlasenkobede2152011-09-04 16:12:33 +020014181#if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT
Denys Vlasenko00eb23b2020-12-21 21:36:58 +010014182 save_history(line_input_state); /* may be NULL */
Denys Vlasenkobede2152011-09-04 16:12:33 +020014183#endif
Denys Vlasenko4ccddc82020-02-14 17:27:18 +010014184 savestatus = exitstatus;
14185 TRACE(("pid %d, exitshell(%d)\n", getpid(), savestatus));
14186 if (setjmp(loc.loc))
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000014187 goto out;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000014188 exception_handler = &loc;
14189 p = trap[0];
14190 if (p) {
14191 trap[0] = NULL;
Denys Vlasenko7b3fa1e2016-10-01 15:10:16 +020014192 evalskip = 0;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000014193 evalstring(p, 0);
Denys Vlasenkof977e002020-02-20 16:54:29 +010014194 evalskip = SKIPFUNCDEF;
Denys Vlasenkof37e1152016-10-07 03:17:28 +020014195 /*free(p); - we'll exit soon */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000014196 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000014197 out:
Denys Vlasenkof977e002020-02-20 16:54:29 +010014198 exitreset();
Denys Vlasenkof37e1152016-10-07 03:17:28 +020014199 /* dash wraps setjobctl(0) in "if (setjmp(loc.loc) == 0) {...}".
14200 * our setjobctl(0) does not panic if tcsetpgrp fails inside it.
14201 */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000014202 setjobctl(0);
Denys Vlasenkocaee80c2016-10-25 20:49:53 +020014203 flush_stdout_stderr();
Denys Vlasenkof977e002020-02-20 16:54:29 +010014204 _exit(exitstatus);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000014205 /* NOTREACHED */
14206}
14207
Denys Vlasenko49e6bf22017-08-04 14:28:16 +020014208/* Don't inline: conserve stack of caller from having our locals too */
14209static NOINLINE void
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000014210init(void)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014211{
Denys Vlasenko82dd14a2010-05-17 10:10:01 +020014212 /* we will never free this */
14213 basepf.next_to_pgetc = basepf.buf = ckmalloc(IBUFSIZ);
Denys Vlasenko0485b672017-08-14 19:46:56 +020014214 basepf.linno = 1;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014215
Denys Vlasenko49e6bf22017-08-04 14:28:16 +020014216 sigmode[SIGCHLD - 1] = S_DFL; /* ensure we install handler even if it is SIG_IGNed */
Denys Vlasenko458c1f22016-10-27 23:51:19 +020014217 setsignal(SIGCHLD);
14218
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014219 {
14220 char **envp;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014221 const char *p;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014222
14223 initvar();
14224 for (envp = environ; envp && *envp; envp++) {
Denys Vlasenko9c143ce2017-11-02 12:56:24 +010014225/* Used to have
14226 * p = endofname(*envp);
14227 * if (p != *envp && *p == '=') {
14228 * here to weed out badly-named variables, but this breaks
14229 * scenarios where people do want them passed to children:
14230 * import os
14231 * os.environ["test-test"]="test"
14232 * if os.fork() == 0:
14233 * os.execv("ash", [ 'ash', '-c', 'eval $(export -p); echo OK' ]) # fixes this
14234 * os.execv("ash", [ 'ash', '-c', 'env | grep test-test' ]) # breaks this
14235 */
14236 if (strchr(*envp, '=')) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014237 setvareq(*envp, VEXPORT|VTEXTFIXED);
14238 }
14239 }
14240
Denys Vlasenko67dae152018-08-05 13:59:35 +020014241 setvareq((char*)defifsvar, VTEXTFIXED);
Denys Vlasenkoe627ac92016-09-30 14:36:59 +020014242 setvareq((char*)defoptindvar, VTEXTFIXED);
14243
Denys Vlasenko0a0acb52015-04-18 19:36:38 +020014244 setvar0("PPID", utoa(getppid()));
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010014245#if BASH_SHLVL_VAR
Bernhard Reutner-Fischer80f8cdf2013-11-08 14:25:24 +010014246 p = lookupvar("SHLVL");
Denys Vlasenko5680e982014-01-07 16:12:48 +010014247 setvar("SHLVL", utoa((p ? atoi(p) : 0) + 1), VEXPORT);
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010014248#endif
14249#if BASH_HOSTNAME_VAR
Denys Vlasenko3fa97af2014-04-15 11:43:29 +020014250 if (!lookupvar("HOSTNAME")) {
14251 struct utsname uts;
14252 uname(&uts);
Denys Vlasenko0a0acb52015-04-18 19:36:38 +020014253 setvar0("HOSTNAME", uts.nodename);
Denys Vlasenko3fa97af2014-04-15 11:43:29 +020014254 }
Bernhard Reutner-Fischer80f8cdf2013-11-08 14:25:24 +010014255#endif
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014256 p = lookupvar("PWD");
Denys Vlasenkob0b83432011-03-07 12:34:59 +010014257 if (p) {
Denys Vlasenko49e6bf22017-08-04 14:28:16 +020014258 struct stat st1, st2;
Denys Vlasenkoef159702016-09-01 11:16:22 +020014259 if (p[0] != '/' || stat(p, &st1) || stat(".", &st2)
Denys Vlasenkob0b83432011-03-07 12:34:59 +010014260 || st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino
14261 ) {
Denys Vlasenkoef159702016-09-01 11:16:22 +020014262 p = NULL;
Denys Vlasenkob0b83432011-03-07 12:34:59 +010014263 }
14264 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014265 setpwd(p, 0);
14266 }
14267}
14268
Denys Vlasenkob0b83432011-03-07 12:34:59 +010014269
14270//usage:#define ash_trivial_usage
Denys Vlasenko4e039ba2021-01-04 03:50:38 +010014271//usage: "[-il] [-/+Cabefmnuvx] [-/+o OPT]... [-c 'SCRIPT' [ARG0 [ARGS]] / FILE [ARGS] / -s [ARGS]]"
14272//////// comes from ^^^^^^^^^^optletters
Denys Vlasenkob0b83432011-03-07 12:34:59 +010014273//usage:#define ash_full_usage "\n\n"
14274//usage: "Unix shell interpreter"
14275
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014276/*
14277 * Process the shell command line arguments.
14278 */
Denys Vlasenkoec05df12017-07-31 19:43:47 +020014279static int
Denis Vlasenko68404f12008-03-17 09:00:54 +000014280procargs(char **argv)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014281{
14282 int i;
14283 const char *xminusc;
14284 char **xargv;
Denys Vlasenkoec05df12017-07-31 19:43:47 +020014285 int login_sh;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014286
14287 xargv = argv;
Ron Yorston8767c122018-11-05 13:13:08 +000014288 login_sh = xargv[0] && xargv[0][0] == '-';
Denys Vlasenko4f2ef4a2018-11-01 09:53:25 +010014289#if NUM_SCRIPTS > 0
14290 if (minusc)
14291 goto setarg0;
14292#endif
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014293 arg0 = xargv[0];
Denis Vlasenko68404f12008-03-17 09:00:54 +000014294 /* if (xargv[0]) - mmm, this is always true! */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014295 xargv++;
Denys Vlasenko4f2ef4a2018-11-01 09:53:25 +010014296 argptr = xargv;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014297 for (i = 0; i < NOPTS; i++)
14298 optlist[i] = 2;
Denys Vlasenko897475a2019-06-01 16:35:09 +020014299 if (options(&login_sh)) {
Denis Vlasenko28bf6712008-02-14 15:01:47 +000014300 /* it already printed err message */
14301 raise_exception(EXERROR);
14302 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014303 xargv = argptr;
14304 xminusc = minusc;
14305 if (*xargv == NULL) {
14306 if (xminusc)
14307 ash_msg_and_raise_error(bb_msg_requires_arg, "-c");
14308 sflag = 1;
14309 }
Denys Vlasenkof3634582019-06-03 12:21:04 +020014310 if (iflag == 2 /* no explicit -i given */
14311 && sflag == 1 /* -s given (or implied) */
14312 && !minusc /* bash compat: ash -sc 'echo $-' is not interactive (dash is) */
14313 && isatty(0) && isatty(1) /* we are on tty */
14314 ) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014315 iflag = 1;
Denys Vlasenkof3634582019-06-03 12:21:04 +020014316 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014317 if (mflag == 2)
14318 mflag = iflag;
Denys Vlasenko85158b62021-01-03 12:14:58 +010014319 /* Unset options which weren't explicitly set or unset */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014320 for (i = 0; i < NOPTS; i++)
Denys Vlasenko85158b62021-01-03 12:14:58 +010014321 optlist[i] &= 1; /* same effect as "if (optlist[i] == 2) optlist[i] = 0;" */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014322#if DEBUG == 2
14323 debug = 1;
14324#endif
Denys Vlasenko5f7c82b2017-02-03 13:00:06 +010014325 /* POSIX 1003.2: first arg after "-c CMD" is $0, remainder $1... */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014326 if (xminusc) {
14327 minusc = *xargv++;
14328 if (*xargv)
14329 goto setarg0;
14330 } else if (!sflag) {
14331 setinputfile(*xargv, 0);
14332 setarg0:
14333 arg0 = *xargv++;
14334 commandname = arg0;
14335 }
14336
14337 shellparam.p = xargv;
14338#if ENABLE_ASH_GETOPTS
14339 shellparam.optind = 1;
14340 shellparam.optoff = -1;
14341#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000014342 /* assert(shellparam.malloced == 0 && shellparam.nparam == 0); */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014343 while (*xargv) {
14344 shellparam.nparam++;
14345 xargv++;
14346 }
Denys Vlasenko31df5a32020-12-13 16:36:28 +010014347
14348 /* Interactive bash re-enables SIGHUP which is SIG_IGNed on entry.
14349 * Try:
14350 * trap '' hup; bash; echo RET # type "kill -hup $$", see SIGHUP having effect
14351 * trap '' hup; bash -c 'kill -hup $$; echo ALIVE' # here SIGHUP is SIG_IGNed
14352 * NB: must do it before setting up signals (in optschanged())
14353 * and reading .profile etc (after we return from here):
14354 */
14355 if (iflag)
14356 signal(SIGHUP, SIG_DFL);
14357
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014358 optschanged();
Denys Vlasenkoec05df12017-07-31 19:43:47 +020014359
14360 return login_sh;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014361}
14362
14363/*
Denys Vlasenko2eb0a7e2016-10-27 11:28:59 +020014364 * Read /etc/profile, ~/.profile, $ENV.
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014365 */
14366static void
14367read_profile(const char *name)
14368{
Denys Vlasenko46999802017-07-29 21:12:29 +020014369 name = expandstr(name, DQSYNTAX);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014370 if (setinputfile(name, INPUT_PUSH_FILE | INPUT_NOFILE_OK) < 0)
14371 return;
Denys Vlasenko0840c912016-10-01 15:27:44 +020014372 cmdloop(0);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014373 popfile();
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014374}
14375
14376#if PROFILE
14377static short profile_buf[16384];
14378extern int etext();
14379#endif
14380
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000014381/*
14382 * Main routine. We initialize things, parse the arguments, execute
14383 * profiles if we're a login shell, and then call cmdloop to execute
14384 * commands. The setjmp call sets up the location to jump to when an
14385 * exception occurs. When an exception occurs the variable "state"
14386 * is used to figure out how far we had gotten.
14387 */
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +000014388int ash_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Ron Yorston8767c122018-11-05 13:13:08 +000014389#if NUM_SCRIPTS > 0
14390int ash_main(int argc, char **argv)
14391#else
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000014392int ash_main(int argc UNUSED_PARAM, char **argv)
Ron Yorston8767c122018-11-05 13:13:08 +000014393#endif
Denys Vlasenko4f2ef4a2018-11-01 09:53:25 +010014394/* note: 'argc' is used only if embedded scripts are enabled */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014395{
Denis Vlasenko4e12b1a2008-12-23 23:36:47 +000014396 volatile smallint state;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014397 struct jmploc jmploc;
14398 struct stackmark smark;
Denys Vlasenkoec05df12017-07-31 19:43:47 +020014399 int login_sh;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014400
Denis Vlasenko01631112007-12-16 17:20:38 +000014401 /* Initialize global data */
14402 INIT_G_misc();
14403 INIT_G_memstack();
14404 INIT_G_var();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000014405#if ENABLE_ASH_ALIAS
Denis Vlasenko01631112007-12-16 17:20:38 +000014406 INIT_G_alias();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000014407#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000014408 INIT_G_cmdtable();
14409
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014410#if PROFILE
14411 monitor(4, etext, profile_buf, sizeof(profile_buf), 50);
14412#endif
14413
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014414 state = 0;
14415 if (setjmp(jmploc.loc)) {
Denis Vlasenko7f88e342009-03-19 03:36:18 +000014416 smallint e;
Denis Vlasenko4e12b1a2008-12-23 23:36:47 +000014417 smallint s;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014418
Denys Vlasenkoafc91fa2020-02-17 11:22:59 +010014419 exitreset();
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014420
Denis Vlasenko7f88e342009-03-19 03:36:18 +000014421 e = exception_type;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014422 s = state;
Denys Vlasenkof977e002020-02-20 16:54:29 +010014423 if (e == EXEND || e == EXEXIT || s == 0 || iflag == 0 || shlvl) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014424 exitshell();
Denys Vlasenkob563f622010-09-25 17:15:13 +020014425 }
Denys Vlasenkoafc91fa2020-02-17 11:22:59 +010014426
14427 reset();
14428
Denys Vlasenkob563f622010-09-25 17:15:13 +020014429 if (e == EXINT) {
Denys Vlasenko9c541002015-10-07 15:44:36 +020014430 newline_and_flush(stderr);
Denys Vlasenkob563f622010-09-25 17:15:13 +020014431 }
Denis Vlasenko7f88e342009-03-19 03:36:18 +000014432
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014433 popstackmark(&smark);
14434 FORCE_INT_ON; /* enable interrupts */
14435 if (s == 1)
14436 goto state1;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000014437 if (s == 2)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014438 goto state2;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000014439 if (s == 3)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014440 goto state3;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000014441 goto state4;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014442 }
14443 exception_handler = &jmploc;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014444 rootpid = getpid();
14445
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014446 init();
14447 setstackmark(&smark);
Denys Vlasenko4f2ef4a2018-11-01 09:53:25 +010014448
14449#if NUM_SCRIPTS > 0
14450 if (argc < 0)
14451 /* Non-NULL minusc tells procargs that an embedded script is being run */
14452 minusc = get_script_content(-argc - 1);
14453#endif
Denys Vlasenkoec05df12017-07-31 19:43:47 +020014454 login_sh = procargs(argv);
Denys Vlasenko474ed062016-10-30 18:30:29 +010014455#if DEBUG
14456 TRACE(("Shell args: "));
14457 trace_puts_args(argv);
14458#endif
Denis Vlasenko68404f12008-03-17 09:00:54 +000014459
Denys Vlasenkoec05df12017-07-31 19:43:47 +020014460 if (login_sh) {
Stefan Hellermann4ef14392013-03-15 02:45:50 +010014461 const char *hp;
14462
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014463 state = 1;
14464 read_profile("/etc/profile");
14465 state1:
14466 state = 2;
Stefan Hellermann4ef14392013-03-15 02:45:50 +010014467 hp = lookupvar("HOME");
Denys Vlasenko2eb0a7e2016-10-27 11:28:59 +020014468 if (hp)
14469 read_profile("$HOME/.profile");
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014470 }
14471 state2:
14472 state = 3;
14473 if (
14474#ifndef linux
Denis Vlasenko0c032a42007-02-23 01:03:40 +000014475 getuid() == geteuid() && getgid() == getegid() &&
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014476#endif
Denis Vlasenko0c032a42007-02-23 01:03:40 +000014477 iflag
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014478 ) {
Denys Vlasenko2eb0a7e2016-10-27 11:28:59 +020014479 const char *shinit = lookupvar("ENV");
14480 if (shinit != NULL && *shinit != '\0')
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014481 read_profile(shinit);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014482 }
Denys Vlasenko2eb0a7e2016-10-27 11:28:59 +020014483 popstackmark(&smark);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014484 state3:
14485 state = 4;
Denis Vlasenko5c2b8142009-03-19 01:59:59 +000014486 if (minusc) {
14487 /* evalstring pushes parsefile stack.
14488 * Ensure we don't falsely claim that 0 (stdin)
Denis Vlasenko5368ad52009-03-20 10:20:08 +000014489 * is one of stacked source fds.
14490 * Testcase: ash -c 'exec 1>&0' must not complain. */
Denys Vlasenkof3634582019-06-03 12:21:04 +020014491
Denys Vlasenko79b3d422010-06-03 04:29:08 +020014492 // if (!sflag) g_parsefile->pf_fd = -1;
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +020014493 // ^^ not necessary since now we special-case fd 0
Denys Vlasenko035486c2017-07-31 04:09:19 +020014494 // in save_fd_on_redirect()
Denys Vlasenkof3634582019-06-03 12:21:04 +020014495
14496 // dash: evalstring(minusc, sflag ? 0 : EV_EXIT);
14497 // The above makes
14498 // ash -sc 'echo $-'
14499 // continue reading input from stdin after running 'echo'.
14500 // bash does not do this: it prints "hBcs" and exits.
14501 evalstring(minusc, EV_EXIT);
Denis Vlasenko5c2b8142009-03-19 01:59:59 +000014502 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014503
14504 if (sflag || minusc == NULL) {
Denys Vlasenko4840ae82011-09-04 15:28:03 +020014505#if MAX_HISTORY > 0 && ENABLE_FEATURE_EDITING_SAVEHISTORY
Denys Vlasenko3f8ec002021-01-03 10:55:39 +010014506 if (line_input_state) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014507 const char *hp = lookupvar("HISTFILE");
Stefan Hellermannaeb717a2013-03-03 15:29:32 +010014508 if (!hp) {
14509 hp = lookupvar("HOME");
Stefan Hellermann4ef14392013-03-15 02:45:50 +010014510 if (hp) {
Denys Vlasenko5f7c82b2017-02-03 13:00:06 +010014511 INT_OFF;
Stefan Hellermannaeb717a2013-03-03 15:29:32 +010014512 hp = concat_path_file(hp, ".ash_history");
Denys Vlasenko0a0acb52015-04-18 19:36:38 +020014513 setvar0("HISTFILE", hp);
Stefan Hellermannaeb717a2013-03-03 15:29:32 +010014514 free((char*)hp);
Denys Vlasenko5f7c82b2017-02-03 13:00:06 +010014515 INT_ON;
Stefan Hellermannaeb717a2013-03-03 15:29:32 +010014516 hp = lookupvar("HISTFILE");
14517 }
14518 }
Denis Vlasenko5c2b8142009-03-19 01:59:59 +000014519 if (hp)
Denys Vlasenko3f8ec002021-01-03 10:55:39 +010014520 line_input_state->hist_file = xstrdup(hp);
Denys Vlasenko2c4de5b2011-03-31 13:16:52 +020014521# if ENABLE_FEATURE_SH_HISTFILESIZE
14522 hp = lookupvar("HISTFILESIZE");
14523 line_input_state->max_history = size_from_HISTFILESIZE(hp);
14524# endif
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014525 }
14526#endif
14527 state4: /* XXX ??? - why isn't this before the "if" statement */
14528 cmdloop(1);
14529 }
14530#if PROFILE
14531 monitor(0);
14532#endif
14533#ifdef GPROF
14534 {
14535 extern void _mcleanup(void);
14536 _mcleanup();
14537 }
14538#endif
Denys Vlasenkob563f622010-09-25 17:15:13 +020014539 TRACE(("End of main reached\n"));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014540 exitshell();
14541 /* NOTREACHED */
14542}
14543
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014544
Eric Andersendf82f612001-06-28 07:46:40 +000014545/*-
14546 * Copyright (c) 1989, 1991, 1993, 1994
Eric Andersen2870d962001-07-02 17:27:21 +000014547 * The Regents of the University of California. All rights reserved.
Eric Andersendf82f612001-06-28 07:46:40 +000014548 *
14549 * This code is derived from software contributed to Berkeley by
14550 * Kenneth Almquist.
14551 *
14552 * Redistribution and use in source and binary forms, with or without
14553 * modification, are permitted provided that the following conditions
14554 * are met:
14555 * 1. Redistributions of source code must retain the above copyright
14556 * notice, this list of conditions and the following disclaimer.
14557 * 2. Redistributions in binary form must reproduce the above copyright
14558 * notice, this list of conditions and the following disclaimer in the
14559 * documentation and/or other materials provided with the distribution.
"Vladimir N. Oleynik"ddc280e2005-12-15 12:01:49 +000014560 * 3. Neither the name of the University nor the names of its contributors
Eric Andersendf82f612001-06-28 07:46:40 +000014561 * may be used to endorse or promote products derived from this software
14562 * without specific prior written permission.
14563 *
Denys Vlasenko95f79532017-08-02 14:26:33 +020014564 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND
Eric Andersendf82f612001-06-28 07:46:40 +000014565 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
14566 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
14567 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
14568 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
14569 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
14570 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
14571 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
14572 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
14573 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
14574 * SUCH DAMAGE.
14575 */