blob: c65f09782580b7233c70328d12c59d175a30ee48 [file] [log] [blame]
Eric Andersendf82f612001-06-28 07:46:40 +00001/* vi: set sw=4 ts=4: */
2/*
3 * ash shell port for busybox
4 *
Denys Vlasenko73067272010-01-12 22:11:24 +01005 * This code is derived from software contributed to Berkeley by
6 * Kenneth Almquist.
7 *
8 * Original BSD copyright notice is retained at the end of this file.
9 *
Eric Andersendf82f612001-06-28 07:46:40 +000010 * Copyright (c) 1989, 1991, 1993, 1994
Eric Andersen2870d962001-07-02 17:27:21 +000011 * The Regents of the University of California. All rights reserved.
Eric Andersencb57d552001-06-28 07:25:16 +000012 *
"Vladimir N. Oleynik"ddc280e2005-12-15 12:01:49 +000013 * Copyright (c) 1997-2005 Herbert Xu <herbert@gondor.apana.org.au>
Eric Andersen81fe1232003-07-29 06:38:40 +000014 * was re-ported from NetBSD and debianized.
15 *
Denys Vlasenko0ef64bd2010-08-16 20:14:46 +020016 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
Eric Andersencb57d552001-06-28 07:25:16 +000017 */
Denys Vlasenko67e15292020-06-24 13:39:13 +020018//config:config SHELL_ASH
19//config: bool #hidden option
20//config: depends on !NOMMU
21//config:
Denys Vlasenko771f1992010-07-16 14:31:34 +020022//config:config ASH
Denys Vlasenkob097a842018-12-28 03:20:17 +010023//config: bool "ash (78 kb)"
Denys Vlasenko771f1992010-07-16 14:31:34 +020024//config: default y
25//config: depends on !NOMMU
Denys Vlasenko67e15292020-06-24 13:39:13 +020026//config: select SHELL_ASH
Denys Vlasenko771f1992010-07-16 14:31:34 +020027//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +020028//config: The most complete and most pedantically correct shell included with
29//config: busybox. This shell is actually a derivative of the Debian 'dash'
30//config: shell (by Herbert Xu), which was created by porting the 'ash' shell
31//config: (written by Kenneth Almquist) from NetBSD.
Denys Vlasenko771f1992010-07-16 14:31:34 +020032//config:
Kang-Che Sung6cd02942017-01-06 17:02:03 +010033//config:# ash options
34//config:# note: Don't remove !NOMMU part in the next line; it would break
35//config:# menuconfig's indenting.
Denys Vlasenko67e15292020-06-24 13:39:13 +020036//config:if !NOMMU && (SHELL_ASH || ASH || SH_IS_ASH || BASH_IS_ASH)
Kang-Che Sung6cd02942017-01-06 17:02:03 +010037//config:
Denys Vlasenko514b51d2016-10-01 14:33:08 +020038//config:config ASH_OPTIMIZE_FOR_SIZE
39//config: bool "Optimize for size instead of speed"
40//config: default y
Denys Vlasenko67e15292020-06-24 13:39:13 +020041//config: depends on SHELL_ASH
Denys Vlasenko514b51d2016-10-01 14:33:08 +020042//config:
43//config:config ASH_INTERNAL_GLOB
44//config: bool "Use internal glob() implementation"
Denys Vlasenko326edc32016-12-22 14:36:49 +010045//config: default y # Y is bigger, but because of uclibc glob() bug, let Y be default for now
Denys Vlasenko67e15292020-06-24 13:39:13 +020046//config: depends on SHELL_ASH
Denys Vlasenko514b51d2016-10-01 14:33:08 +020047//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +020048//config: Do not use glob() function from libc, use internal implementation.
49//config: Use this if you are getting "glob.h: No such file or directory"
50//config: or similar build errors.
51//config: Note that as of now (2017-01), uclibc and musl glob() both have bugs
52//config: which would break ash if you select N here.
Denys Vlasenkof5604222017-01-10 14:58:54 +010053//config:
54//config:config ASH_BASH_COMPAT
55//config: bool "bash-compatible extensions"
56//config: default y
Denys Vlasenko67e15292020-06-24 13:39:13 +020057//config: depends on SHELL_ASH
Denys Vlasenkof5604222017-01-10 14:58:54 +010058//config:
Denys Vlasenko01f7b9e2018-01-26 15:15:43 +010059//config:config ASH_BASH_SOURCE_CURDIR
60//config: bool "'source' and '.' builtins search current directory after $PATH"
61//config: default n # do not encourage non-standard behavior
Denys Vlasenko54c21112018-01-27 20:46:45 +010062//config: depends on ASH_BASH_COMPAT
Denys Vlasenko01f7b9e2018-01-26 15:15:43 +010063//config: help
64//config: This is not compliant with standards. Avoid if possible.
65//config:
William Pitcockd8fd88a2018-01-24 18:33:18 +010066//config:config ASH_BASH_NOT_FOUND_HOOK
67//config: bool "command_not_found_handle hook support"
68//config: default y
Denys Vlasenko54c21112018-01-27 20:46:45 +010069//config: depends on ASH_BASH_COMPAT
William Pitcockd8fd88a2018-01-24 18:33:18 +010070//config: help
71//config: Enable support for the 'command_not_found_handle' hook function,
72//config: from GNU bash, which allows for alternative command not found
73//config: handling.
74//config:
Denys Vlasenkof5604222017-01-10 14:58:54 +010075//config:config ASH_JOB_CONTROL
76//config: bool "Job control"
77//config: default y
Denys Vlasenko67e15292020-06-24 13:39:13 +020078//config: depends on SHELL_ASH
Denys Vlasenkof5604222017-01-10 14:58:54 +010079//config:
80//config:config ASH_ALIAS
81//config: bool "Alias support"
82//config: default y
Denys Vlasenko67e15292020-06-24 13:39:13 +020083//config: depends on SHELL_ASH
Denys Vlasenko514b51d2016-10-01 14:33:08 +020084//config:
85//config:config ASH_RANDOM_SUPPORT
86//config: bool "Pseudorandom generator and $RANDOM variable"
87//config: default y
Denys Vlasenko67e15292020-06-24 13:39:13 +020088//config: depends on SHELL_ASH
Denys Vlasenko514b51d2016-10-01 14:33:08 +020089//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +020090//config: Enable pseudorandom generator and dynamic variable "$RANDOM".
91//config: Each read of "$RANDOM" will generate a new pseudorandom value.
92//config: You can reset the generator by using a specified start value.
93//config: After "unset RANDOM" the generator will switch off and this
94//config: variable will no longer have special treatment.
Denys Vlasenko514b51d2016-10-01 14:33:08 +020095//config:
96//config:config ASH_EXPAND_PRMT
97//config: bool "Expand prompt string"
98//config: default y
Denys Vlasenko67e15292020-06-24 13:39:13 +020099//config: depends on SHELL_ASH
Denys Vlasenko514b51d2016-10-01 14:33:08 +0200100//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +0200101//config: $PS# may contain volatile content, such as backquote commands.
102//config: This option recreates the prompt string from the environment
103//config: variable each time it is displayed.
Denys Vlasenko514b51d2016-10-01 14:33:08 +0200104//config:
Denys Vlasenko046341e2011-02-04 17:53:59 +0100105//config:config ASH_IDLE_TIMEOUT
Denys Vlasenkof5604222017-01-10 14:58:54 +0100106//config: bool "Idle timeout variable $TMOUT"
Denys Vlasenko771f1992010-07-16 14:31:34 +0200107//config: default y
Denys Vlasenko67e15292020-06-24 13:39:13 +0200108//config: depends on SHELL_ASH
Denys Vlasenko771f1992010-07-16 14:31:34 +0200109//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +0200110//config: Enable bash-like auto-logout after $TMOUT seconds of idle time.
Denys Vlasenko771f1992010-07-16 14:31:34 +0200111//config:
Denys Vlasenkof5604222017-01-10 14:58:54 +0100112//config:config ASH_MAIL
113//config: bool "Check for new mail in interactive shell"
Denys Vlasenko771f1992010-07-16 14:31:34 +0200114//config: default y
Denys Vlasenko67e15292020-06-24 13:39:13 +0200115//config: depends on SHELL_ASH
Denys Vlasenko771f1992010-07-16 14:31:34 +0200116//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +0200117//config: Enable "check for new mail" function:
118//config: if set, $MAIL file and $MAILPATH list of files
119//config: are checked for mtime changes, and "you have mail"
120//config: message is printed if change is detected.
Denys Vlasenko771f1992010-07-16 14:31:34 +0200121//config:
Denys Vlasenko265062d2017-01-10 15:13:30 +0100122//config:config ASH_ECHO
Denys Vlasenkof5604222017-01-10 14:58:54 +0100123//config: bool "echo builtin"
Denys Vlasenko771f1992010-07-16 14:31:34 +0200124//config: default y
Denys Vlasenko67e15292020-06-24 13:39:13 +0200125//config: depends on SHELL_ASH
Denys Vlasenko771f1992010-07-16 14:31:34 +0200126//config:
Denys Vlasenko265062d2017-01-10 15:13:30 +0100127//config:config ASH_PRINTF
Denys Vlasenkof5604222017-01-10 14:58:54 +0100128//config: bool "printf builtin"
Denys Vlasenko771f1992010-07-16 14:31:34 +0200129//config: default y
Denys Vlasenko67e15292020-06-24 13:39:13 +0200130//config: depends on SHELL_ASH
Denys Vlasenko771f1992010-07-16 14:31:34 +0200131//config:
Denys Vlasenko265062d2017-01-10 15:13:30 +0100132//config:config ASH_TEST
Denys Vlasenkof5604222017-01-10 14:58:54 +0100133//config: bool "test builtin"
Denys Vlasenko771f1992010-07-16 14:31:34 +0200134//config: default y
Denys Vlasenko67e15292020-06-24 13:39:13 +0200135//config: depends on SHELL_ASH
Denys Vlasenko771f1992010-07-16 14:31:34 +0200136//config:
Denys Vlasenko2ec34962014-09-08 16:52:39 +0200137//config:config ASH_HELP
138//config: bool "help builtin"
139//config: default y
Denys Vlasenko67e15292020-06-24 13:39:13 +0200140//config: depends on SHELL_ASH
Denys Vlasenkof5604222017-01-10 14:58:54 +0100141//config:
142//config:config ASH_GETOPTS
143//config: bool "getopts builtin"
144//config: default y
Denys Vlasenko67e15292020-06-24 13:39:13 +0200145//config: depends on SHELL_ASH
Denys Vlasenko2ec34962014-09-08 16:52:39 +0200146//config:
Denys Vlasenko771f1992010-07-16 14:31:34 +0200147//config:config ASH_CMDCMD
Denys Vlasenkof5604222017-01-10 14:58:54 +0100148//config: bool "command builtin"
Denys Vlasenko771f1992010-07-16 14:31:34 +0200149//config: default y
Denys Vlasenko67e15292020-06-24 13:39:13 +0200150//config: depends on SHELL_ASH
Denys Vlasenko771f1992010-07-16 14:31:34 +0200151//config: help
Denys Vlasenko72089cf2017-07-21 09:50:55 +0200152//config: Enable support for the 'command' builtin, which allows
153//config: you to run the specified command or builtin,
154//config: even when there is a function with the same name.
Kang-Che Sung6cd02942017-01-06 17:02:03 +0100155//config:
156//config:endif # ash options
Denys Vlasenko771f1992010-07-16 14:31:34 +0200157
Denys Vlasenko20704f02011-03-23 17:59:27 +0100158//applet:IF_ASH(APPLET(ash, BB_DIR_BIN, BB_SUID_DROP))
Denys Vlasenko205d48e2017-01-29 14:57:33 +0100159// APPLET_ODDNAME:name main location suid_type help
160//applet:IF_SH_IS_ASH( APPLET_ODDNAME(sh, ash, BB_DIR_BIN, BB_SUID_DROP, ash))
Denys Vlasenko0b883582016-12-23 16:49:07 +0100161//applet:IF_BASH_IS_ASH(APPLET_ODDNAME(bash, ash, BB_DIR_BIN, BB_SUID_DROP, ash))
Denys Vlasenko20704f02011-03-23 17:59:27 +0100162
Denys Vlasenko67e15292020-06-24 13:39:13 +0200163//kbuild:lib-$(CONFIG_SHELL_ASH) += ash.o ash_ptr_hack.o shell_common.o
Denys Vlasenko20704f02011-03-23 17:59:27 +0100164//kbuild:lib-$(CONFIG_ASH_RANDOM_SUPPORT) += random.o
165
Denys Vlasenko67047462016-12-22 15:21:58 +0100166/*
Denys Vlasenko7d4aec02017-01-11 14:00:38 +0100167 * DEBUG=1 to compile in debugging ('set -o debug' turns on)
168 * DEBUG=2 to compile in and turn on debugging.
169 * When debugging is on ("set -o debug" was executed, or DEBUG=2),
170 * debugging info is written to ./trace, quit signal generates core dump.
Denys Vlasenko67047462016-12-22 15:21:58 +0100171 */
172#define DEBUG 0
173/* Tweak debug output verbosity here */
174#define DEBUG_TIME 0
175#define DEBUG_PID 1
176#define DEBUG_SIG 1
177#define DEBUG_INTONOFF 0
178
179#define PROFILE 0
180
181#define JOBS ENABLE_ASH_JOB_CONTROL
182
Denys Vlasenko67047462016-12-22 15:21:58 +0100183#include <fnmatch.h>
184#include <sys/times.h>
185#include <sys/utsname.h> /* for setting $HOSTNAME */
Denys Vlasenko67047462016-12-22 15:21:58 +0100186#include "busybox.h" /* for applet_names */
Ron Yorston71df2d32018-11-27 14:34:25 +0000187#if ENABLE_FEATURE_SH_EMBEDDED_SCRIPTS
Denys Vlasenko4f2ef4a2018-11-01 09:53:25 +0100188# include "embedded_scripts.h"
189#else
190# define NUM_SCRIPTS 0
191#endif
Denys Vlasenko67047462016-12-22 15:21:58 +0100192
Denys Vlasenko7d4aec02017-01-11 14:00:38 +0100193/* So far, all bash compat is controlled by one config option */
194/* Separate defines document which part of code implements what */
195/* function keyword */
196#define BASH_FUNCTION ENABLE_ASH_BASH_COMPAT
197#define IF_BASH_FUNCTION IF_ASH_BASH_COMPAT
198/* &>file */
199#define BASH_REDIR_OUTPUT ENABLE_ASH_BASH_COMPAT
200#define IF_BASH_REDIR_OUTPUT IF_ASH_BASH_COMPAT
201/* $'...' */
202#define BASH_DOLLAR_SQUOTE ENABLE_ASH_BASH_COMPAT
203#define IF_BASH_DOLLAR_SQUOTE IF_ASH_BASH_COMPAT
204#define BASH_PATTERN_SUBST ENABLE_ASH_BASH_COMPAT
205#define IF_BASH_PATTERN_SUBST IF_ASH_BASH_COMPAT
206#define BASH_SUBSTR ENABLE_ASH_BASH_COMPAT
207#define IF_BASH_SUBSTR IF_ASH_BASH_COMPAT
Denys Vlasenko3632cb12018-04-10 15:25:41 +0200208/* BASH_TEST2: [[ EXPR ]]
209 * Status of [[ support:
Denys Vlasenkod2241f52020-10-31 03:34:07 +0100210 * && and || work as they should
211 * = is glob match operator, not equality operator: STR = GLOB
Denys Vlasenkod2241f52020-10-31 03:34:07 +0100212 * == same as =
Denys Vlasenkoa7c06532020-10-31 04:32:34 +0100213 * =~ is regex match operator: STR =~ REGEX
Denys Vlasenko3632cb12018-04-10 15:25:41 +0200214 * TODO:
215 * singleword+noglob expansion:
216 * v='a b'; [[ $v = 'a b' ]]; echo 0:$?
Denys Vlasenko89e9d552018-04-11 01:15:33 +0200217 * [[ /bin/n* ]]; echo 0:$?
Denys Vlasenko3632cb12018-04-10 15:25:41 +0200218 * quoting needs to be considered (-f is an operator, "-f" and ""-f are not; etc)
Denys Vlasenkoa7c06532020-10-31 04:32:34 +0100219 * ( ) < > should not have special meaning (IOW: should not require quoting)
220 * in word = GLOB, quoting should be significant on char-by-char basis: a*cd"*"
Denys Vlasenko3632cb12018-04-10 15:25:41 +0200221 */
Denys Vlasenko7d4aec02017-01-11 14:00:38 +0100222#define BASH_TEST2 (ENABLE_ASH_BASH_COMPAT * ENABLE_ASH_TEST)
223#define BASH_SOURCE ENABLE_ASH_BASH_COMPAT
224#define BASH_PIPEFAIL ENABLE_ASH_BASH_COMPAT
225#define BASH_HOSTNAME_VAR ENABLE_ASH_BASH_COMPAT
Ron Yorston1d371862019-04-15 10:52:05 +0100226#define BASH_EPOCH_VARS ENABLE_ASH_BASH_COMPAT
Denys Vlasenko7d4aec02017-01-11 14:00:38 +0100227#define BASH_SHLVL_VAR ENABLE_ASH_BASH_COMPAT
Denys Vlasenkof8cdc7a2017-08-04 15:24:49 +0200228#define BASH_XTRACEFD ENABLE_ASH_BASH_COMPAT
Johannes Schindelin3bef5d82017-08-08 16:46:39 +0200229#define BASH_READ_D ENABLE_ASH_BASH_COMPAT
230#define IF_BASH_READ_D IF_ASH_BASH_COMPAT
Ron Yorstone48559e2019-03-31 09:27:09 +0100231#define BASH_WAIT_N ENABLE_ASH_BASH_COMPAT
Ron Yorstona1b0d382020-07-23 08:32:27 +0100232/* <(...) and >(...) */
233#if HAVE_DEV_FD
234# define BASH_PROCESS_SUBST ENABLE_ASH_BASH_COMPAT
235# define IF_BASH_PROCESS_SUBST IF_ASH_BASH_COMPAT
236#else
237# define BASH_PROCESS_SUBST 0
238# define IF_BASH_PROCESS_SUBST(...)
239#endif
Denys Vlasenko7d4aec02017-01-11 14:00:38 +0100240
Denys Vlasenko67047462016-12-22 15:21:58 +0100241#if defined(__ANDROID_API__) && __ANDROID_API__ <= 24
242/* Bionic at least up to version 24 has no glob() */
243# undef ENABLE_ASH_INTERNAL_GLOB
244# define ENABLE_ASH_INTERNAL_GLOB 1
245#endif
246
247#if !ENABLE_ASH_INTERNAL_GLOB && defined(__UCLIBC__)
248# error uClibc glob() is buggy, use ASH_INTERNAL_GLOB.
249# error The bug is: for "$PWD"/<pattern> ash will escape e.g. dashes in "$PWD"
250# error with backslash, even ones which do not need to be: "/a-b" -> "/a\-b"
251# error glob() should unbackslash them and match. uClibc does not unbackslash,
252# error fails to match dirname, subsequently not expanding <pattern> in it.
253// Testcase:
254// if (glob("/etc/polkit\\-1", 0, NULL, &pglob)) - this returns 0 on uclibc, no bug
255// if (glob("/etc/polkit\\-1/*", 0, NULL, &pglob)) printf("uclibc bug!\n");
256#endif
257
258#if !ENABLE_ASH_INTERNAL_GLOB
259# include <glob.h>
260#endif
261
262#include "unicode.h"
263#include "shell_common.h"
Denys Vlasenko0b883582016-12-23 16:49:07 +0100264#if ENABLE_FEATURE_SH_MATH
Denys Vlasenko67047462016-12-22 15:21:58 +0100265# include "math.h"
Denys Vlasenkocf3a7962017-07-26 14:38:19 +0200266#else
267typedef long arith_t;
268# define ARITH_FMT "%ld"
Denys Vlasenko67047462016-12-22 15:21:58 +0100269#endif
270#if ENABLE_ASH_RANDOM_SUPPORT
271# include "random.h"
272#else
273# define CLEAR_RANDOM_T(rnd) ((void)0)
274#endif
275
276#include "NUM_APPLETS.h"
277#if NUM_APPLETS == 1
278/* STANDALONE does not make sense, and won't compile */
279# undef CONFIG_FEATURE_SH_STANDALONE
280# undef ENABLE_FEATURE_SH_STANDALONE
281# undef IF_FEATURE_SH_STANDALONE
282# undef IF_NOT_FEATURE_SH_STANDALONE
283# define ENABLE_FEATURE_SH_STANDALONE 0
284# define IF_FEATURE_SH_STANDALONE(...)
285# define IF_NOT_FEATURE_SH_STANDALONE(...) __VA_ARGS__
286#endif
287
Denys Vlasenko9acd63c2018-03-28 18:35:07 +0200288#ifndef F_DUPFD_CLOEXEC
289# define F_DUPFD_CLOEXEC F_DUPFD
290#endif
Denys Vlasenko60fb98e2018-03-30 22:15:14 +0200291#ifndef O_CLOEXEC
292# define O_CLOEXEC 0
293#endif
Denys Vlasenko67047462016-12-22 15:21:58 +0100294#ifndef PIPE_BUF
295# define PIPE_BUF 4096 /* amount of buffering in a pipe */
296#endif
297
298#if !BB_MMU
299# error "Do not even bother, ash will not run on NOMMU machine"
300#endif
301
Denys Vlasenko6f9442f2018-01-28 20:41:23 +0100302/* We use a trick to have more optimized code (fewer pointer reloads):
303 * ash.c: extern struct globals *const ash_ptr_to_globals;
304 * ash_ptr_hack.c: struct globals *ash_ptr_to_globals;
305 * This way, compiler in ash.c knows the pointer can not change.
306 *
307 * However, this may break on weird arches or toolchains. In this case,
308 * set "-DBB_GLOBAL_CONST=''" in CONFIG_EXTRA_CFLAGS to disable
309 * this optimization.
310 */
311#ifndef BB_GLOBAL_CONST
312# define BB_GLOBAL_CONST const
313#endif
314
Denis Vlasenkob012b102007-02-19 22:43:01 +0000315
Denis Vlasenko01631112007-12-16 17:20:38 +0000316/* ============ Hash table sizes. Configurable. */
317
318#define VTABSIZE 39
319#define ATABSIZE 39
320#define CMDTABLESIZE 31 /* should be prime */
321
322
Denis Vlasenkob012b102007-02-19 22:43:01 +0000323/* ============ Shell options */
324
Denys Vlasenko4e039ba2021-01-04 03:50:38 +0100325/* If you add/change options hare, update --help text too */
Denis Vlasenkob012b102007-02-19 22:43:01 +0000326static const char *const optletters_optnames[] = {
327 "e" "errexit",
328 "f" "noglob",
Denys Vlasenko4e039ba2021-01-04 03:50:38 +0100329/* bash has '-o ignoreeof', but no short synonym -I for it */
330/* (in bash, set -I disables invisible variables (what's that?)) */
Denis Vlasenkob012b102007-02-19 22:43:01 +0000331 "I" "ignoreeof",
Denys Vlasenkof3634582019-06-03 12:21:04 +0200332/* The below allowed this invocation:
Denys Vlasenko897475a2019-06-01 16:35:09 +0200333 * ash -c 'set -i; echo $-; sleep 5; echo $-'
334 * to be ^C-ed and get to interactive ash prompt.
Denys Vlasenkof3634582019-06-03 12:21:04 +0200335 * bash does not support such "set -i".
336 * In our code, this is denoted by empty long name:
Denys Vlasenko897475a2019-06-01 16:35:09 +0200337 */
Denys Vlasenkof3634582019-06-03 12:21:04 +0200338 "i" "",
Denys Vlasenko4e039ba2021-01-04 03:50:38 +0100339/* (removing "i" altogether would remove it from "$-", not good) */
Denis Vlasenkob012b102007-02-19 22:43:01 +0000340 "m" "monitor",
341 "n" "noexec",
Denys Vlasenko4e039ba2021-01-04 03:50:38 +0100342/* Ditto: bash has no "set -s", "set -c" */
Denys Vlasenkof3634582019-06-03 12:21:04 +0200343 "s" "",
344 "c" "",
Denis Vlasenkob012b102007-02-19 22:43:01 +0000345 "x" "xtrace",
346 "v" "verbose",
347 "C" "noclobber",
348 "a" "allexport",
349 "b" "notify",
350 "u" "nounset",
Roberto A. Fogliettae0bf3df2021-09-07 01:19:31 +0200351 "E" "errtrace",
Denys Vlasenkoe9ac32a2009-12-05 02:01:25 +0100352 "\0" "vi"
Denys Vlasenko7d4aec02017-01-11 14:00:38 +0100353#if BASH_PIPEFAIL
Denys Vlasenkoe9ac32a2009-12-05 02:01:25 +0100354 ,"\0" "pipefail"
Michael Abbott359da5e2009-12-04 23:03:29 +0100355#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +0000356#if DEBUG
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000357 ,"\0" "nolog"
358 ,"\0" "debug"
Denis Vlasenkob012b102007-02-19 22:43:01 +0000359#endif
360};
Denys Vlasenko897475a2019-06-01 16:35:09 +0200361//bash 4.4.23 also has these opts (with these defaults):
362//braceexpand on
363//emacs on
364//errtrace off
365//functrace off
366//hashall on
367//histexpand off
368//history on
369//interactive-comments on
370//keyword off
371//onecmd off
372//physical off
373//posix off
374//privileged off
Denis Vlasenkob012b102007-02-19 22:43:01 +0000375
Denys Vlasenko285ad152009-12-04 23:02:27 +0100376#define optletters(n) optletters_optnames[n][0]
377#define optnames(n) (optletters_optnames[n] + 1)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000378
Denis Vlasenko80b8b392007-06-25 10:55:35 +0000379enum { NOPTS = ARRAY_SIZE(optletters_optnames) };
Denis Vlasenkob012b102007-02-19 22:43:01 +0000380
Eric Andersenc470f442003-07-28 09:56:35 +0000381
Denis Vlasenkob012b102007-02-19 22:43:01 +0000382/* ============ Misc data */
Eric Andersenc470f442003-07-28 09:56:35 +0000383
Denys Vlasenkoea8b2522010-06-02 12:57:26 +0200384#define msg_illnum "Illegal number: %s"
Denis Vlasenkoaa744452007-02-23 01:04:22 +0000385
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +0000386/*
Eric Andersenc470f442003-07-28 09:56:35 +0000387 * We enclose jmp_buf in a structure so that we can declare pointers to
388 * jump locations. The global variable handler contains the location to
Denis Vlasenkof1733952009-03-19 23:21:55 +0000389 * jump to when an exception occurs, and the global variable exception_type
Eric Andersenaff114c2004-04-14 17:51:38 +0000390 * contains a code identifying the exception. To implement nested
Eric Andersenc470f442003-07-28 09:56:35 +0000391 * exception handlers, the user should save the value of handler on entry
392 * to an inner scope, set handler to point to a jmploc structure for the
393 * inner scope, and restore handler on exit from the scope.
394 */
Eric Andersenc470f442003-07-28 09:56:35 +0000395struct jmploc {
396 jmp_buf loc;
397};
Denis Vlasenko01631112007-12-16 17:20:38 +0000398
399struct globals_misc {
Denys Vlasenko4d12e942016-10-01 16:03:11 +0200400 uint8_t exitstatus; /* exit status of last command */
401 uint8_t back_exitstatus;/* exit status of backquoted command */
402 smallint job_warning; /* user was warned about stopped jobs (can be 2, 1 or 0). */
Denys Vlasenko4ccddc82020-02-14 17:27:18 +0100403 int savestatus; /* exit status of last command outside traps */
Denys Vlasenko4d12e942016-10-01 16:03:11 +0200404 int rootpid; /* pid of main shell */
Denis Vlasenko01631112007-12-16 17:20:38 +0000405 /* shell level: 0 for the main shell, 1 for its children, and so on */
406 int shlvl;
407#define rootshell (!shlvl)
Denys Vlasenko675d24a2018-01-27 22:02:05 +0100408 int errlinno;
409
Denis Vlasenko01631112007-12-16 17:20:38 +0000410 char *minusc; /* argument to -c option */
411
412 char *curdir; // = nullstr; /* current working directory */
413 char *physdir; // = nullstr; /* physical working directory */
414
415 char *arg0; /* value of $0 */
416
417 struct jmploc *exception_handler;
Denis Vlasenko991a1da2008-02-10 19:02:53 +0000418
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200419 volatile int suppress_int; /* counter */
420 volatile /*sig_atomic_t*/ smallint pending_int; /* 1 = got SIGINT */
Denys Vlasenko458c1f22016-10-27 23:51:19 +0200421 volatile /*sig_atomic_t*/ smallint got_sigchld; /* 1 = got SIGCHLD */
Denys Vlasenko8f7b0242016-10-28 17:16:11 +0200422 volatile /*sig_atomic_t*/ smallint pending_sig; /* last pending signal */
Denys Vlasenkof977e002020-02-20 16:54:29 +0100423 smallint exception_type; /* kind of exception: */
Eric Andersenc470f442003-07-28 09:56:35 +0000424#define EXINT 0 /* SIGINT received */
425#define EXERROR 1 /* a generic error */
Denys Vlasenkof977e002020-02-20 16:54:29 +0100426#define EXEND 3 /* exit the shell */
427#define EXEXIT 4 /* exit the shell via exitcmd */
Eric Andersen2870d962001-07-02 17:27:21 +0000428
Denis Vlasenkob07a4962008-06-22 13:16:23 +0000429 char nullstr[1]; /* zero length string */
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000430
431 char optlist[NOPTS];
432#define eflag optlist[0]
433#define fflag optlist[1]
434#define Iflag optlist[2]
435#define iflag optlist[3]
436#define mflag optlist[4]
437#define nflag optlist[5]
438#define sflag optlist[6]
Denys Vlasenkof3634582019-06-03 12:21:04 +0200439#define cflag optlist[7]
440#define xflag optlist[8]
441#define vflag optlist[9]
442#define Cflag optlist[10]
443#define aflag optlist[11]
444#define bflag optlist[12]
445#define uflag optlist[13]
Roberto A. Fogliettae0bf3df2021-09-07 01:19:31 +0200446#define Eflag optlist[14]
447#define viflag optlist[15]
Denys Vlasenko7d4aec02017-01-11 14:00:38 +0100448#if BASH_PIPEFAIL
Roberto A. Fogliettae0bf3df2021-09-07 01:19:31 +0200449# define pipefail optlist[16]
Michael Abbott359da5e2009-12-04 23:03:29 +0100450#else
451# define pipefail 0
452#endif
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000453#if DEBUG
Roberto A. Fogliettae0bf3df2021-09-07 01:19:31 +0200454# define nolog optlist[16 + BASH_PIPEFAIL]
455# define debug optlist[17 + BASH_PIPEFAIL]
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000456#endif
457
458 /* trap handler commands */
Denis Vlasenko01631112007-12-16 17:20:38 +0000459 /*
460 * Sigmode records the current value of the signal handlers for the various
461 * modes. A value of zero means that the current handler is not known.
Denis Vlasenkof8535cc2008-12-03 10:36:26 +0000462 * S_HARD_IGN indicates that the signal was ignored on entry to the shell.
Denis Vlasenko01631112007-12-16 17:20:38 +0000463 */
464 char sigmode[NSIG - 1];
Denis Vlasenkof8535cc2008-12-03 10:36:26 +0000465#define S_DFL 1 /* default signal handling (SIG_DFL) */
466#define S_CATCH 2 /* signal is caught */
467#define S_IGN 3 /* signal is ignored (SIG_IGN) */
Denys Vlasenko0f14f412017-08-06 20:06:19 +0200468#define S_HARD_IGN 4 /* signal is ignored permanently (it was SIG_IGN on entry to shell) */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000469
Denis Vlasenko01631112007-12-16 17:20:38 +0000470 /* indicates specified signal received */
Denis Vlasenko4b875702009-03-19 13:30:04 +0000471 uint8_t gotsig[NSIG - 1]; /* offset by 1: "signal" 0 is meaningless */
Denys Vlasenko238bf182010-05-18 15:49:07 +0200472 uint8_t may_have_traps; /* 0: definitely no traps are set, 1: some traps may be set */
Roberto A. Fogliettae0bf3df2021-09-07 01:19:31 +0200473 char *trap[NSIG + 1];
474/* trap[0] is EXIT trap, trap[NTRAP_ERR] is ERR trap, other trap[i] are signal traps */
475#define NTRAP_ERR NSIG
476#define NTRAP_LAST NSIG
477
Denys Vlasenko21d87d42009-09-25 00:06:51 +0200478 char **trap_ptr; /* used only by "trap hack" */
Denis Vlasenko448d30e2008-06-27 00:24:11 +0000479
480 /* Rarely referenced stuff */
481#if ENABLE_ASH_RANDOM_SUPPORT
Denys Vlasenko3ea2e822009-10-09 20:59:04 +0200482 random_t random_gen;
Denis Vlasenko448d30e2008-06-27 00:24:11 +0000483#endif
484 pid_t backgndpid; /* pid of last background process */
Denis Vlasenko01631112007-12-16 17:20:38 +0000485};
Denys Vlasenko6f9442f2018-01-28 20:41:23 +0100486extern struct globals_misc *BB_GLOBAL_CONST ash_ptr_to_globals_misc;
Denis Vlasenko574f2f42008-02-27 18:41:59 +0000487#define G_misc (*ash_ptr_to_globals_misc)
Denys Vlasenko4d12e942016-10-01 16:03:11 +0200488#define exitstatus (G_misc.exitstatus )
489#define back_exitstatus (G_misc.back_exitstatus )
490#define job_warning (G_misc.job_warning)
Denys Vlasenko4ccddc82020-02-14 17:27:18 +0100491#define savestatus (G_misc.savestatus )
Denis Vlasenko26bc57d2008-06-27 00:29:34 +0000492#define rootpid (G_misc.rootpid )
493#define shlvl (G_misc.shlvl )
Denys Vlasenko675d24a2018-01-27 22:02:05 +0100494#define errlinno (G_misc.errlinno )
Denis Vlasenko26bc57d2008-06-27 00:29:34 +0000495#define minusc (G_misc.minusc )
496#define curdir (G_misc.curdir )
497#define physdir (G_misc.physdir )
498#define arg0 (G_misc.arg0 )
Denis Vlasenko01631112007-12-16 17:20:38 +0000499#define exception_handler (G_misc.exception_handler)
Denis Vlasenko7f88e342009-03-19 03:36:18 +0000500#define exception_type (G_misc.exception_type )
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200501#define suppress_int (G_misc.suppress_int )
502#define pending_int (G_misc.pending_int )
Denys Vlasenko458c1f22016-10-27 23:51:19 +0200503#define got_sigchld (G_misc.got_sigchld )
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200504#define pending_sig (G_misc.pending_sig )
Denis Vlasenko26bc57d2008-06-27 00:29:34 +0000505#define nullstr (G_misc.nullstr )
506#define optlist (G_misc.optlist )
507#define sigmode (G_misc.sigmode )
508#define gotsig (G_misc.gotsig )
Denys Vlasenko238bf182010-05-18 15:49:07 +0200509#define may_have_traps (G_misc.may_have_traps )
Denis Vlasenko26bc57d2008-06-27 00:29:34 +0000510#define trap (G_misc.trap )
Denys Vlasenko21d87d42009-09-25 00:06:51 +0200511#define trap_ptr (G_misc.trap_ptr )
Denys Vlasenko3ea2e822009-10-09 20:59:04 +0200512#define random_gen (G_misc.random_gen )
Denis Vlasenko448d30e2008-06-27 00:24:11 +0000513#define backgndpid (G_misc.backgndpid )
Denis Vlasenko01631112007-12-16 17:20:38 +0000514#define INIT_G_misc() do { \
Denys Vlasenkoaf7169b2019-10-25 12:12:22 +0200515 (*(struct globals_misc**)not_const_pp(&ash_ptr_to_globals_misc)) = xzalloc(sizeof(G_misc)); \
Denis Vlasenko574f2f42008-02-27 18:41:59 +0000516 barrier(); \
Denys Vlasenko4ccddc82020-02-14 17:27:18 +0100517 savestatus = -1; \
Denis Vlasenko01631112007-12-16 17:20:38 +0000518 curdir = nullstr; \
519 physdir = nullstr; \
Denys Vlasenko21d87d42009-09-25 00:06:51 +0200520 trap_ptr = trap; \
Denis Vlasenko01631112007-12-16 17:20:38 +0000521} while (0)
522
523
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000524/* ============ DEBUG */
525#if DEBUG
526static void trace_printf(const char *fmt, ...);
527static void trace_vprintf(const char *fmt, va_list va);
528# define TRACE(param) trace_printf param
529# define TRACEV(param) trace_vprintf param
Denis Vlasenko1bb3d7e2009-03-20 07:45:36 +0000530# define close(fd) do { \
531 int dfd = (fd); \
Denis Vlasenkob9e70dd2009-03-20 01:24:08 +0000532 if (close(dfd) < 0) \
Denys Vlasenko883cea42009-07-11 15:31:59 +0200533 bb_error_msg("bug on %d: closing %d(0x%x)", \
Denis Vlasenko1bb3d7e2009-03-20 07:45:36 +0000534 __LINE__, dfd, dfd); \
Denis Vlasenkob9e70dd2009-03-20 01:24:08 +0000535} while (0)
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000536#else
537# define TRACE(param)
538# define TRACEV(param)
539#endif
540
541
Denis Vlasenko559691a2008-10-05 18:39:31 +0000542/* ============ Utility functions */
Denys Vlasenko1961aea2013-02-26 00:36:53 +0100543#define is_name(c) ((c) == '_' || isalpha((unsigned char)(c)))
544#define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c)))
545
Denys Vlasenko37dc08b2016-10-02 04:38:07 +0200546static int
547isdigit_str9(const char *str)
Denis Vlasenko559691a2008-10-05 18:39:31 +0000548{
549 int maxlen = 9 + 1; /* max 9 digits: 999999999 */
550 while (--maxlen && isdigit(*str))
551 str++;
552 return (*str == '\0');
553}
Denis Vlasenko01631112007-12-16 17:20:38 +0000554
Denys Vlasenko37dc08b2016-10-02 04:38:07 +0200555static const char *
556var_end(const char *var)
Denys Vlasenko8837c5d2010-06-02 12:56:18 +0200557{
558 while (*var)
559 if (*var++ == '=')
560 break;
561 return var;
562}
563
Denis Vlasenko559691a2008-10-05 18:39:31 +0000564
Denys Vlasenko2124c0e2020-12-19 14:33:02 +0100565/* ============ Parser data */
566
567/*
568 * ash_vmsg() needs parsefile->fd, hence parsefile definition is moved up.
569 */
570struct strlist {
571 struct strlist *next;
572 char *text;
573};
574
575struct alias;
576
577struct strpush {
578 struct strpush *prev; /* preceding string on stack */
579 char *prev_string;
580 int prev_left_in_line;
581#if ENABLE_ASH_ALIAS
582 struct alias *ap; /* if push was associated with an alias */
583#endif
584 char *string; /* remember the string since it may change */
585
586 /* Remember last two characters for pungetc. */
587 int lastc[2];
588
589 /* Number of outstanding calls to pungetc. */
590 int unget;
591};
592
593/*
594 * The parsefile structure pointed to by the global variable parsefile
595 * contains information about the current file being read.
596 */
597struct parsefile {
598 struct parsefile *prev; /* preceding file on stack */
599 int linno; /* current line */
600 int pf_fd; /* file descriptor (or -1 if string) */
601 int left_in_line; /* number of chars left in this line */
602 int left_in_buffer; /* number of chars left in this buffer past the line */
603 char *next_to_pgetc; /* next char in buffer */
604 char *buf; /* input buffer */
605 struct strpush *strpush; /* for pushing strings at this level */
606 struct strpush basestrpush; /* so pushing one is fast */
607
608 /* Remember last two characters for pungetc. */
609 int lastc[2];
610
611 /* Number of outstanding calls to pungetc. */
612 int unget;
613};
614
615static struct parsefile basepf; /* top level input file */
616static struct parsefile *g_parsefile = &basepf; /* current input file */
617static char *commandname; /* currently executing command */
618
619
Denis Vlasenko559691a2008-10-05 18:39:31 +0000620/* ============ Interrupts / exceptions */
Denys Vlasenko66c5b122011-02-08 05:07:02 +0100621
622static void exitshell(void) NORETURN;
623
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +0000624/*
Eric Andersen2870d962001-07-02 17:27:21 +0000625 * These macros allow the user to suspend the handling of interrupt signals
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +0000626 * over a period of time. This is similar to SIGHOLD or to sigblock, but
Eric Andersen2870d962001-07-02 17:27:21 +0000627 * much more efficient and portable. (But hacking the kernel is so much
628 * more fun than worrying about efficiency and portability. :-))
629 */
Denys Vlasenko06b11492016-11-04 16:43:18 +0100630#if DEBUG_INTONOFF
631# define INT_OFF do { \
632 TRACE(("%s:%d INT_OFF(%d)\n", __func__, __LINE__, suppress_int)); \
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200633 suppress_int++; \
Denys Vlasenkode892052016-10-02 01:49:13 +0200634 barrier(); \
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000635} while (0)
Denys Vlasenko06b11492016-11-04 16:43:18 +0100636#else
637# define INT_OFF do { \
638 suppress_int++; \
639 barrier(); \
640} while (0)
641#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +0000642
643/*
644 * Called to raise an exception. Since C doesn't include exceptions, we
645 * just do a longjmp to the exception handler. The type of exception is
Denis Vlasenko4b875702009-03-19 13:30:04 +0000646 * stored in the global variable "exception_type".
Denis Vlasenkob012b102007-02-19 22:43:01 +0000647 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000648static void raise_exception(int) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000649static void
650raise_exception(int e)
651{
652#if DEBUG
Denis Vlasenko2da584f2007-02-19 22:44:05 +0000653 if (exception_handler == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000654 abort();
655#endif
656 INT_OFF;
Denis Vlasenko7f88e342009-03-19 03:36:18 +0000657 exception_type = e;
Denis Vlasenko2da584f2007-02-19 22:44:05 +0000658 longjmp(exception_handler->loc, 1);
Denis Vlasenkob012b102007-02-19 22:43:01 +0000659}
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000660#if DEBUG
661#define raise_exception(e) do { \
662 TRACE(("raising exception %d on line %d\n", (e), __LINE__)); \
663 raise_exception(e); \
664} while (0)
665#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +0000666
667/*
Denys Vlasenkof37e1152016-10-07 03:17:28 +0200668 * Called when a SIGINT is received. (If the user specifies
Denis Vlasenkob012b102007-02-19 22:43:01 +0000669 * that SIGINT is to be trapped or ignored using the trap builtin, then
670 * this routine is not called.) Suppressint is nonzero when interrupts
671 * are held using the INT_OFF macro. (The test for iflag is just
672 * defensive programming.)
673 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +0000674static void raise_interrupt(void) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000675static void
676raise_interrupt(void)
677{
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200678 pending_int = 0;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +0000679 /* Signal is not automatically unmasked after it is raised,
680 * do it ourself - unmask all signals */
Denis Vlasenko3f165fa2008-03-17 08:29:08 +0000681 sigprocmask_allsigs(SIG_UNBLOCK);
Denys Vlasenko238bf182010-05-18 15:49:07 +0200682 /* pending_sig = 0; - now done in signal_handler() */
Denis Vlasenko7c139b42007-03-21 20:17:27 +0000683
Denys Vlasenkoc0663c72016-10-27 21:09:01 +0200684 if (!(rootshell && iflag)) {
685 /* Kill ourself with SIGINT */
686 signal(SIGINT, SIG_DFL);
687 raise(SIGINT);
Denis Vlasenkob012b102007-02-19 22:43:01 +0000688 }
Denys Vlasenko4d12e942016-10-01 16:03:11 +0200689 /* bash: ^C even on empty command line sets $? */
690 exitstatus = SIGINT + 128;
Denys Vlasenkoc0663c72016-10-27 21:09:01 +0200691 raise_exception(EXINT);
Denis Vlasenkob012b102007-02-19 22:43:01 +0000692 /* NOTREACHED */
693}
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000694#if DEBUG
695#define raise_interrupt() do { \
696 TRACE(("raising interrupt on line %d\n", __LINE__)); \
697 raise_interrupt(); \
698} while (0)
699#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +0000700
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000701static IF_ASH_OPTIMIZE_FOR_SIZE(inline) void
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000702int_on(void)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000703{
Denys Vlasenkode892052016-10-02 01:49:13 +0200704 barrier();
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200705 if (--suppress_int == 0 && pending_int) {
Denis Vlasenkob012b102007-02-19 22:43:01 +0000706 raise_interrupt();
707 }
708}
Denys Vlasenko06b11492016-11-04 16:43:18 +0100709#if DEBUG_INTONOFF
710# define INT_ON do { \
711 TRACE(("%s:%d INT_ON(%d)\n", __func__, __LINE__, suppress_int-1)); \
712 int_on(); \
713} while (0)
714#else
715# define INT_ON int_on()
716#endif
Denis Vlasenko5e34ff22009-04-21 11:09:40 +0000717static IF_ASH_OPTIMIZE_FOR_SIZE(inline) void
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000718force_int_on(void)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000719{
Denys Vlasenkode892052016-10-02 01:49:13 +0200720 barrier();
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200721 suppress_int = 0;
722 if (pending_int)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000723 raise_interrupt();
724}
725#define FORCE_INT_ON force_int_on()
Denis Vlasenko653d8e72009-03-19 21:59:35 +0000726
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200727#define SAVE_INT(v) ((v) = suppress_int)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000728
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000729#define RESTORE_INT(v) do { \
Denys Vlasenkode892052016-10-02 01:49:13 +0200730 barrier(); \
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +0200731 suppress_int = (v); \
732 if (suppress_int == 0 && pending_int) \
Denis Vlasenko843cbd52008-06-27 00:23:18 +0000733 raise_interrupt(); \
734} while (0)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000735
Glenn L McGrath9fef17d2002-08-22 18:41:20 +0000736
Denis Vlasenkobc54cff2007-02-23 01:05:52 +0000737/* ============ Stdout/stderr output */
Eric Andersenc470f442003-07-28 09:56:35 +0000738
Eric Andersenc470f442003-07-28 09:56:35 +0000739static void
Denis Vlasenkob012b102007-02-19 22:43:01 +0000740outstr(const char *p, FILE *file)
Denis Vlasenkoe5570da2007-02-19 22:41:55 +0000741{
Denis Vlasenkob012b102007-02-19 22:43:01 +0000742 INT_OFF;
743 fputs(p, file);
744 INT_ON;
745}
746
747static void
748flush_stdout_stderr(void)
749{
750 INT_OFF;
Denys Vlasenko8131eea2009-11-02 14:19:51 +0100751 fflush_all();
Denis Vlasenkob012b102007-02-19 22:43:01 +0000752 INT_ON;
753}
754
Denys Vlasenko9c541002015-10-07 15:44:36 +0200755/* Was called outcslow(c,FILE*), but c was always '\n' */
Denis Vlasenkob012b102007-02-19 22:43:01 +0000756static void
Denys Vlasenko9c541002015-10-07 15:44:36 +0200757newline_and_flush(FILE *dest)
Denis Vlasenkob012b102007-02-19 22:43:01 +0000758{
759 INT_OFF;
Denys Vlasenko9c541002015-10-07 15:44:36 +0200760 putc('\n', dest);
Denis Vlasenkob012b102007-02-19 22:43:01 +0000761 fflush(dest);
762 INT_ON;
763}
764
765static int out1fmt(const char *, ...) __attribute__((__format__(__printf__,1,2)));
766static int
767out1fmt(const char *fmt, ...)
768{
769 va_list ap;
770 int r;
771
772 INT_OFF;
773 va_start(ap, fmt);
774 r = vprintf(fmt, ap);
775 va_end(ap);
776 INT_ON;
777 return r;
778}
779
780static int fmtstr(char *, size_t, const char *, ...) __attribute__((__format__(__printf__,3,4)));
781static int
782fmtstr(char *outbuf, size_t length, const char *fmt, ...)
783{
784 va_list ap;
785 int ret;
786
Denis Vlasenkob012b102007-02-19 22:43:01 +0000787 INT_OFF;
Denys Vlasenkocf3a7962017-07-26 14:38:19 +0200788 va_start(ap, fmt);
Denis Vlasenkob012b102007-02-19 22:43:01 +0000789 ret = vsnprintf(outbuf, length, fmt, ap);
790 va_end(ap);
791 INT_ON;
Denys Vlasenko3f7fb2c2020-02-16 18:06:20 +0100792 return ret > (int)length ? length : ret;
Denis Vlasenkob012b102007-02-19 22:43:01 +0000793}
794
795static void
796out1str(const char *p)
797{
798 outstr(p, stdout);
799}
800
801static void
802out2str(const char *p)
803{
804 outstr(p, stderr);
Denys Vlasenko8131eea2009-11-02 14:19:51 +0100805 flush_stdout_stderr();
Denis Vlasenkob012b102007-02-19 22:43:01 +0000806}
807
808
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000809/* ============ Parser structures */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +0000810
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000811/* control characters in argument strings */
Denys Vlasenko2ce42e92009-11-29 02:18:13 +0100812#define CTL_FIRST CTLESC
Denys Vlasenkob6c84342009-08-29 20:23:20 +0200813#define CTLESC ((unsigned char)'\201') /* escape next character */
814#define CTLVAR ((unsigned char)'\202') /* variable defn */
815#define CTLENDVAR ((unsigned char)'\203')
816#define CTLBACKQ ((unsigned char)'\204')
Denys Vlasenkob6c84342009-08-29 20:23:20 +0200817#define CTLARI ((unsigned char)'\206') /* arithmetic expression */
818#define CTLENDARI ((unsigned char)'\207')
819#define CTLQUOTEMARK ((unsigned char)'\210')
Denys Vlasenko2ce42e92009-11-29 02:18:13 +0100820#define CTL_LAST CTLQUOTEMARK
Ron Yorstona1b0d382020-07-23 08:32:27 +0100821#if BASH_PROCESS_SUBST
822# define CTLTOPROC ((unsigned char)'\211')
823# define CTLFROMPROC ((unsigned char)'\212')
824# undef CTL_LAST
825# define CTL_LAST CTLFROMPROC
826#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000827
828/* variable substitution byte (follows CTLVAR) */
829#define VSTYPE 0x0f /* type of variable substitution */
830#define VSNUL 0x10 /* colon--treat the empty string as unset */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000831
832/* values of VSTYPE field */
Denis Vlasenko92e13c22008-03-25 01:17:40 +0000833#define VSNORMAL 0x1 /* normal variable: $var or ${var} */
834#define VSMINUS 0x2 /* ${var-text} */
835#define VSPLUS 0x3 /* ${var+text} */
836#define VSQUESTION 0x4 /* ${var?message} */
837#define VSASSIGN 0x5 /* ${var=text} */
838#define VSTRIMRIGHT 0x6 /* ${var%pattern} */
839#define VSTRIMRIGHTMAX 0x7 /* ${var%%pattern} */
840#define VSTRIMLEFT 0x8 /* ${var#pattern} */
841#define VSTRIMLEFTMAX 0x9 /* ${var##pattern} */
842#define VSLENGTH 0xa /* ${#var} */
Denys Vlasenko7d4aec02017-01-11 14:00:38 +0100843#if BASH_SUBSTR
Denis Vlasenko92e13c22008-03-25 01:17:40 +0000844#define VSSUBSTR 0xc /* ${var:position:length} */
Denys Vlasenko7d4aec02017-01-11 14:00:38 +0100845#endif
846#if BASH_PATTERN_SUBST
Denis Vlasenko92e13c22008-03-25 01:17:40 +0000847#define VSREPLACE 0xd /* ${var/pattern/replacement} */
848#define VSREPLACEALL 0xe /* ${var//pattern/replacement} */
849#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +0000850
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000851static const char dolatstr[] ALIGN1 = {
Ron Yorston549deab2015-05-18 09:57:51 +0200852 CTLQUOTEMARK, CTLVAR, VSNORMAL, '@', '=', CTLQUOTEMARK, '\0'
Denis Vlasenko6ca409e2007-08-12 20:58:27 +0000853};
Ron Yorston549deab2015-05-18 09:57:51 +0200854#define DOLATSTRLEN 6
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +0000855
Denis Vlasenko559691a2008-10-05 18:39:31 +0000856#define NCMD 0
857#define NPIPE 1
858#define NREDIR 2
859#define NBACKGND 3
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000860#define NSUBSHELL 4
Denis Vlasenko559691a2008-10-05 18:39:31 +0000861#define NAND 5
862#define NOR 6
863#define NSEMI 7
864#define NIF 8
865#define NWHILE 9
866#define NUNTIL 10
867#define NFOR 11
868#define NCASE 12
869#define NCLIST 13
870#define NDEFUN 14
871#define NARG 15
872#define NTO 16
Denys Vlasenko7d4aec02017-01-11 14:00:38 +0100873#if BASH_REDIR_OUTPUT
Denis Vlasenko559691a2008-10-05 18:39:31 +0000874#define NTO2 17
875#endif
876#define NCLOBBER 18
877#define NFROM 19
878#define NFROMTO 20
879#define NAPPEND 21
880#define NTOFD 22
881#define NFROMFD 23
882#define NHERE 24
883#define NXHERE 25
884#define NNOT 26
Denis Vlasenko340299a2008-11-21 10:36:36 +0000885#define N_NUMBER 27
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000886
887union node;
888
889struct ncmd {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000890 smallint type; /* Nxxxx */
Denys Vlasenko675d24a2018-01-27 22:02:05 +0100891 int linno;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000892 union node *assign;
893 union node *args;
894 union node *redirect;
895};
896
897struct npipe {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000898 smallint type;
899 smallint pipe_backgnd;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000900 struct nodelist *cmdlist;
901};
902
903struct nredir {
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 *n;
907 union node *redirect;
908};
909
910struct nbinary {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000911 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000912 union node *ch1;
913 union node *ch2;
914};
915
916struct nif {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000917 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000918 union node *test;
919 union node *ifpart;
920 union node *elsepart;
921};
922
923struct nfor {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000924 smallint type;
Denys Vlasenko675d24a2018-01-27 22:02:05 +0100925 int linno;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000926 union node *args;
927 union node *body;
928 char *var;
929};
930
931struct ncase {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000932 smallint type;
Denys Vlasenko675d24a2018-01-27 22:02:05 +0100933 int linno;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000934 union node *expr;
935 union node *cases;
936};
937
938struct nclist {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000939 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000940 union node *next;
941 union node *pattern;
942 union node *body;
943};
944
Denys Vlasenko675d24a2018-01-27 22:02:05 +0100945struct ndefun {
946 smallint type;
947 int linno;
948 char *text;
949 union node *body;
950};
951
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000952struct narg {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000953 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000954 union node *next;
955 char *text;
956 struct nodelist *backquote;
957};
958
Denis Vlasenko559691a2008-10-05 18:39:31 +0000959/* nfile and ndup layout must match!
960 * NTOFD (>&fdnum) uses ndup structure, but we may discover mid-flight
961 * that it is actually NTO2 (>&file), and change its type.
962 */
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000963struct nfile {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000964 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000965 union node *next;
966 int fd;
Denis Vlasenko559691a2008-10-05 18:39:31 +0000967 int _unused_dupfd;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000968 union node *fname;
969 char *expfname;
970};
971
972struct ndup {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000973 smallint type;
Denis Vlasenko559691a2008-10-05 18:39:31 +0000974 union node *next;
975 int fd;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000976 int dupfd;
977 union node *vname;
Denis Vlasenko559691a2008-10-05 18:39:31 +0000978 char *_unused_expfname;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000979};
980
981struct nhere {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000982 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000983 union node *next;
984 int fd;
985 union node *doc;
986};
987
988struct nnot {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000989 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000990 union node *com;
991};
992
993union node {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +0000994 smallint type;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +0000995 struct ncmd ncmd;
996 struct npipe npipe;
997 struct nredir nredir;
998 struct nbinary nbinary;
999 struct nif nif;
1000 struct nfor nfor;
1001 struct ncase ncase;
1002 struct nclist nclist;
Denys Vlasenko675d24a2018-01-27 22:02:05 +01001003 struct ndefun ndefun;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001004 struct narg narg;
1005 struct nfile nfile;
1006 struct ndup ndup;
1007 struct nhere nhere;
1008 struct nnot nnot;
1009};
1010
Denys Vlasenko86e83ec2009-07-23 22:07:07 +02001011/*
1012 * NODE_EOF is returned by parsecmd when it encounters an end of file.
1013 * It must be distinct from NULL.
1014 */
1015#define NODE_EOF ((union node *) -1L)
1016
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001017struct nodelist {
1018 struct nodelist *next;
1019 union node *n;
1020};
1021
1022struct funcnode {
1023 int count;
1024 union node n;
1025};
1026
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00001027/*
1028 * Free a parse tree.
1029 */
1030static void
1031freefunc(struct funcnode *f)
1032{
1033 if (f && --f->count < 0)
1034 free(f);
1035}
1036
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001037
1038/* ============ Debugging output */
1039
1040#if DEBUG
1041
1042static FILE *tracefile;
1043
1044static void
1045trace_printf(const char *fmt, ...)
1046{
1047 va_list va;
1048
1049 if (debug != 1)
1050 return;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00001051 if (DEBUG_TIME)
1052 fprintf(tracefile, "%u ", (int) time(NULL));
1053 if (DEBUG_PID)
1054 fprintf(tracefile, "[%u] ", (int) getpid());
1055 if (DEBUG_SIG)
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02001056 fprintf(tracefile, "pending s:%d i:%d(supp:%d) ", pending_sig, pending_int, suppress_int);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001057 va_start(va, fmt);
1058 vfprintf(tracefile, fmt, va);
1059 va_end(va);
1060}
1061
1062static void
1063trace_vprintf(const char *fmt, va_list va)
1064{
1065 if (debug != 1)
1066 return;
1067 vfprintf(tracefile, fmt, va);
Denys Vlasenko474ed062016-10-30 18:30:29 +01001068 fprintf(tracefile, "\n");
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001069}
1070
1071static void
1072trace_puts(const char *s)
1073{
1074 if (debug != 1)
1075 return;
1076 fputs(s, tracefile);
1077}
1078
1079static void
1080trace_puts_quoted(char *s)
1081{
1082 char *p;
1083 char c;
1084
1085 if (debug != 1)
1086 return;
1087 putc('"', tracefile);
1088 for (p = s; *p; p++) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01001089 switch ((unsigned char)*p) {
1090 case '\n': c = 'n'; goto backslash;
1091 case '\t': c = 't'; goto backslash;
1092 case '\r': c = 'r'; goto backslash;
1093 case '\"': c = '\"'; goto backslash;
1094 case '\\': c = '\\'; goto backslash;
1095 case CTLESC: c = 'e'; goto backslash;
1096 case CTLVAR: c = 'v'; goto backslash;
Denys Vlasenkocd716832009-11-28 22:14:02 +01001097 case CTLBACKQ: c = 'q'; goto backslash;
Ron Yorstona1b0d382020-07-23 08:32:27 +01001098#if BASH_PROCESS_SUBST
1099 case CTLTOPROC: c = 'p'; goto backslash;
1100 case CTLFROMPROC: c = 'P'; goto backslash;
1101#endif
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001102 backslash:
1103 putc('\\', tracefile);
1104 putc(c, tracefile);
1105 break;
1106 default:
1107 if (*p >= ' ' && *p <= '~')
1108 putc(*p, tracefile);
1109 else {
1110 putc('\\', tracefile);
Denys Vlasenkocd716832009-11-28 22:14:02 +01001111 putc((*p >> 6) & 03, tracefile);
1112 putc((*p >> 3) & 07, tracefile);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001113 putc(*p & 07, tracefile);
1114 }
1115 break;
1116 }
1117 }
1118 putc('"', tracefile);
1119}
1120
1121static void
1122trace_puts_args(char **ap)
1123{
1124 if (debug != 1)
1125 return;
1126 if (!*ap)
1127 return;
1128 while (1) {
1129 trace_puts_quoted(*ap);
1130 if (!*++ap) {
1131 putc('\n', tracefile);
1132 break;
1133 }
1134 putc(' ', tracefile);
1135 }
1136}
1137
1138static void
1139opentrace(void)
1140{
1141 char s[100];
1142#ifdef O_APPEND
1143 int flags;
1144#endif
1145
1146 if (debug != 1) {
1147 if (tracefile)
1148 fflush(tracefile);
1149 /* leave open because libedit might be using it */
1150 return;
1151 }
1152 strcpy(s, "./trace");
1153 if (tracefile) {
1154 if (!freopen(s, "a", tracefile)) {
1155 fprintf(stderr, "Can't re-open %s\n", s);
1156 debug = 0;
1157 return;
1158 }
1159 } else {
1160 tracefile = fopen(s, "a");
1161 if (tracefile == NULL) {
1162 fprintf(stderr, "Can't open %s\n", s);
1163 debug = 0;
1164 return;
1165 }
1166 }
1167#ifdef O_APPEND
Denis Vlasenkod37f2222007-08-19 13:42:08 +00001168 flags = fcntl(fileno(tracefile), F_GETFL);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001169 if (flags >= 0)
1170 fcntl(fileno(tracefile), F_SETFL, flags | O_APPEND);
1171#endif
1172 setlinebuf(tracefile);
1173 fputs("\nTracing started.\n", tracefile);
1174}
1175
1176static void
1177indent(int amount, char *pfx, FILE *fp)
1178{
1179 int i;
1180
1181 for (i = 0; i < amount; i++) {
1182 if (pfx && i == amount - 1)
1183 fputs(pfx, fp);
1184 putc('\t', fp);
1185 }
1186}
1187
1188/* little circular references here... */
1189static void shtree(union node *n, int ind, char *pfx, FILE *fp);
1190
1191static void
1192sharg(union node *arg, FILE *fp)
1193{
1194 char *p;
1195 struct nodelist *bqlist;
Denys Vlasenkocd716832009-11-28 22:14:02 +01001196 unsigned char subtype;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001197
1198 if (arg->type != NARG) {
1199 out1fmt("<node type %d>\n", arg->type);
1200 abort();
1201 }
1202 bqlist = arg->narg.backquote;
1203 for (p = arg->narg.text; *p; p++) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01001204 switch ((unsigned char)*p) {
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001205 case CTLESC:
Dan Fandrich77d48722010-09-07 23:38:28 -07001206 p++;
1207 putc(*p, fp);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001208 break;
1209 case CTLVAR:
1210 putc('$', fp);
1211 putc('{', fp);
1212 subtype = *++p;
1213 if (subtype == VSLENGTH)
1214 putc('#', fp);
1215
Dan Fandrich77d48722010-09-07 23:38:28 -07001216 while (*p != '=') {
1217 putc(*p, fp);
1218 p++;
1219 }
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001220
1221 if (subtype & VSNUL)
1222 putc(':', fp);
1223
1224 switch (subtype & VSTYPE) {
1225 case VSNORMAL:
1226 putc('}', fp);
1227 break;
1228 case VSMINUS:
1229 putc('-', fp);
1230 break;
1231 case VSPLUS:
1232 putc('+', fp);
1233 break;
1234 case VSQUESTION:
1235 putc('?', fp);
1236 break;
1237 case VSASSIGN:
1238 putc('=', fp);
1239 break;
1240 case VSTRIMLEFT:
1241 putc('#', fp);
1242 break;
1243 case VSTRIMLEFTMAX:
1244 putc('#', fp);
1245 putc('#', fp);
1246 break;
1247 case VSTRIMRIGHT:
1248 putc('%', fp);
1249 break;
1250 case VSTRIMRIGHTMAX:
1251 putc('%', fp);
1252 putc('%', fp);
1253 break;
1254 case VSLENGTH:
1255 break;
1256 default:
1257 out1fmt("<subtype %d>", subtype);
1258 }
1259 break;
1260 case CTLENDVAR:
1261 putc('}', fp);
1262 break;
Ron Yorstona1b0d382020-07-23 08:32:27 +01001263#if BASH_PROCESS_SUBST
1264 case CTLTOPROC:
1265 putc('>', fp);
1266 goto backq;
1267 case CTLFROMPROC:
1268 putc('<', fp);
1269 goto backq;
1270#endif
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001271 case CTLBACKQ:
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001272 putc('$', fp);
Ron Yorstona1b0d382020-07-23 08:32:27 +01001273 IF_BASH_PROCESS_SUBST(backq:)
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001274 putc('(', fp);
1275 shtree(bqlist->n, -1, NULL, fp);
1276 putc(')', fp);
1277 break;
1278 default:
1279 putc(*p, fp);
1280 break;
1281 }
1282 }
1283}
1284
Denys Vlasenko641dd7b2009-06-11 19:30:19 +02001285static void
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001286shcmd(union node *cmd, FILE *fp)
1287{
1288 union node *np;
1289 int first;
1290 const char *s;
1291 int dftfd;
1292
1293 first = 1;
1294 for (np = cmd->ncmd.args; np; np = np->narg.next) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +00001295 if (!first)
1296 putc(' ', fp);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001297 sharg(np, fp);
1298 first = 0;
1299 }
1300 for (np = cmd->ncmd.redirect; np; np = np->nfile.next) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +00001301 if (!first)
1302 putc(' ', fp);
1303 dftfd = 0;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001304 switch (np->nfile.type) {
Denis Vlasenko40ba9982007-07-14 00:48:29 +00001305 case NTO: s = ">>"+1; dftfd = 1; break;
1306 case NCLOBBER: s = ">|"; dftfd = 1; break;
1307 case NAPPEND: s = ">>"; dftfd = 1; break;
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01001308#if BASH_REDIR_OUTPUT
Denis Vlasenko559691a2008-10-05 18:39:31 +00001309 case NTO2:
1310#endif
Denis Vlasenko40ba9982007-07-14 00:48:29 +00001311 case NTOFD: s = ">&"; dftfd = 1; break;
Denis Vlasenko559691a2008-10-05 18:39:31 +00001312 case NFROM: s = "<"; break;
Denis Vlasenko40ba9982007-07-14 00:48:29 +00001313 case NFROMFD: s = "<&"; break;
1314 case NFROMTO: s = "<>"; break;
1315 default: s = "*error*"; break;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001316 }
1317 if (np->nfile.fd != dftfd)
1318 fprintf(fp, "%d", np->nfile.fd);
1319 fputs(s, fp);
1320 if (np->nfile.type == NTOFD || np->nfile.type == NFROMFD) {
1321 fprintf(fp, "%d", np->ndup.dupfd);
1322 } else {
1323 sharg(np->nfile.fname, fp);
1324 }
1325 first = 0;
1326 }
1327}
1328
1329static void
1330shtree(union node *n, int ind, char *pfx, FILE *fp)
1331{
1332 struct nodelist *lp;
1333 const char *s;
1334
1335 if (n == NULL)
1336 return;
1337
1338 indent(ind, pfx, fp);
Denys Vlasenko86e83ec2009-07-23 22:07:07 +02001339
1340 if (n == NODE_EOF) {
1341 fputs("<EOF>", fp);
1342 return;
1343 }
1344
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001345 switch (n->type) {
1346 case NSEMI:
1347 s = "; ";
1348 goto binop;
1349 case NAND:
1350 s = " && ";
1351 goto binop;
1352 case NOR:
1353 s = " || ";
1354 binop:
1355 shtree(n->nbinary.ch1, ind, NULL, fp);
1356 /* if (ind < 0) */
1357 fputs(s, fp);
1358 shtree(n->nbinary.ch2, ind, NULL, fp);
1359 break;
1360 case NCMD:
1361 shcmd(n, fp);
1362 if (ind >= 0)
1363 putc('\n', fp);
1364 break;
1365 case NPIPE:
1366 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
Denys Vlasenko7cee00e2009-07-24 01:08:03 +02001367 shtree(lp->n, 0, NULL, fp);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001368 if (lp->next)
1369 fputs(" | ", fp);
1370 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00001371 if (n->npipe.pipe_backgnd)
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001372 fputs(" &", fp);
1373 if (ind >= 0)
1374 putc('\n', fp);
1375 break;
1376 default:
1377 fprintf(fp, "<node type %d>", n->type);
1378 if (ind >= 0)
1379 putc('\n', fp);
1380 break;
1381 }
1382}
1383
1384static void
1385showtree(union node *n)
1386{
1387 trace_puts("showtree called\n");
Denys Vlasenko883cea42009-07-11 15:31:59 +02001388 shtree(n, 1, NULL, stderr);
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001389}
1390
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00001391#endif /* DEBUG */
1392
1393
Denis Vlasenkob012b102007-02-19 22:43:01 +00001394/* ============ Message printing */
1395
1396static void
1397ash_vmsg(const char *msg, va_list ap)
1398{
1399 fprintf(stderr, "%s: ", arg0);
1400 if (commandname) {
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00001401 if (strcmp(arg0, commandname))
1402 fprintf(stderr, "%s: ", commandname);
Denys Vlasenko79b3d422010-06-03 04:29:08 +02001403 if (!iflag || g_parsefile->pf_fd > 0)
Denys Vlasenko675d24a2018-01-27 22:02:05 +01001404 fprintf(stderr, "line %d: ", errlinno);
Eric Andersenc470f442003-07-28 09:56:35 +00001405 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00001406 vfprintf(stderr, msg, ap);
Denys Vlasenko9c541002015-10-07 15:44:36 +02001407 newline_and_flush(stderr);
Eric Andersenc470f442003-07-28 09:56:35 +00001408}
Denis Vlasenkob012b102007-02-19 22:43:01 +00001409
1410/*
1411 * Exverror is called to raise the error exception. If the second argument
1412 * is not NULL then error prints an error message using printf style
1413 * formatting. It then raises the error exception.
1414 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001415static void ash_vmsg_and_raise(int, const char *, va_list) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +00001416static void
1417ash_vmsg_and_raise(int cond, const char *msg, va_list ap)
Eric Andersenc470f442003-07-28 09:56:35 +00001418{
Denis Vlasenkob012b102007-02-19 22:43:01 +00001419#if DEBUG
1420 if (msg) {
Denys Vlasenko474ed062016-10-30 18:30:29 +01001421 TRACE(("ash_vmsg_and_raise(%d):", cond));
Denis Vlasenkob012b102007-02-19 22:43:01 +00001422 TRACEV((msg, ap));
Denis Vlasenkob012b102007-02-19 22:43:01 +00001423 } else
Denys Vlasenko474ed062016-10-30 18:30:29 +01001424 TRACE(("ash_vmsg_and_raise(%d):NULL\n", cond));
Denis Vlasenkob012b102007-02-19 22:43:01 +00001425 if (msg)
1426#endif
1427 ash_vmsg(msg, ap);
1428
1429 flush_stdout_stderr();
1430 raise_exception(cond);
1431 /* NOTREACHED */
Eric Andersenc470f442003-07-28 09:56:35 +00001432}
Denis Vlasenkob012b102007-02-19 22:43:01 +00001433
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001434static void ash_msg_and_raise_error(const char *, ...) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +00001435static void
1436ash_msg_and_raise_error(const char *msg, ...)
1437{
1438 va_list ap;
1439
Ron Yorstonea7d2f62017-01-03 11:18:23 +01001440 exitstatus = 2;
1441
Denis Vlasenkob012b102007-02-19 22:43:01 +00001442 va_start(ap, msg);
1443 ash_vmsg_and_raise(EXERROR, msg, ap);
1444 /* NOTREACHED */
1445 va_end(ap);
1446}
1447
Ron Yorstonbe366e52017-07-27 13:53:39 +01001448/*
Ron Yorstonbe366e52017-07-27 13:53:39 +01001449 * 'fmt' must be a string literal.
1450 */
Denys Vlasenko6f97b302017-09-29 18:17:25 +02001451#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 +01001452
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00001453static void raise_error_syntax(const char *) NORETURN;
1454static void
1455raise_error_syntax(const char *msg)
1456{
Denys Vlasenko675d24a2018-01-27 22:02:05 +01001457 errlinno = g_parsefile->linno;
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00001458 ash_msg_and_raise_error("syntax error: %s", msg);
1459 /* NOTREACHED */
1460}
1461
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00001462static void ash_msg_and_raise(int, const char *, ...) NORETURN;
Denis Vlasenkob012b102007-02-19 22:43:01 +00001463static void
1464ash_msg_and_raise(int cond, const char *msg, ...)
1465{
1466 va_list ap;
1467
1468 va_start(ap, msg);
1469 ash_vmsg_and_raise(cond, msg, ap);
1470 /* NOTREACHED */
1471 va_end(ap);
1472}
1473
1474/*
1475 * error/warning routines for external builtins
1476 */
1477static void
1478ash_msg(const char *fmt, ...)
1479{
1480 va_list ap;
1481
1482 va_start(ap, fmt);
1483 ash_vmsg(fmt, ap);
1484 va_end(ap);
1485}
1486
1487/*
1488 * Return a string describing an error. The returned string may be a
1489 * pointer to a static buffer that will be overwritten on the next call.
1490 * Action describes the operation that got the error.
1491 */
1492static const char *
1493errmsg(int e, const char *em)
1494{
1495 if (e == ENOENT || e == ENOTDIR) {
1496 return em;
1497 }
1498 return strerror(e);
1499}
1500
1501
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001502/* ============ Memory allocation */
1503
Denys Vlasenkoe7670ff2009-10-11 00:45:25 +02001504#if 0
1505/* I consider these wrappers nearly useless:
1506 * ok, they return you to nearest exception handler, but
1507 * how much memory do you leak in the process, making
1508 * memory starvation worse?
1509 */
1510static void *
1511ckrealloc(void * p, size_t nbytes)
1512{
1513 p = realloc(p, nbytes);
1514 if (!p)
1515 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1516 return p;
1517}
1518
1519static void *
1520ckmalloc(size_t nbytes)
1521{
1522 return ckrealloc(NULL, nbytes);
1523}
1524
1525static void *
1526ckzalloc(size_t nbytes)
1527{
1528 return memset(ckmalloc(nbytes), 0, nbytes);
1529}
1530
1531static char *
1532ckstrdup(const char *s)
1533{
1534 char *p = strdup(s);
1535 if (!p)
1536 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1537 return p;
1538}
1539#else
1540/* Using bbox equivalents. They exit if out of memory */
1541# define ckrealloc xrealloc
1542# define ckmalloc xmalloc
1543# define ckzalloc xzalloc
1544# define ckstrdup xstrdup
1545#endif
1546
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001547/*
1548 * It appears that grabstackstr() will barf with such alignments
1549 * because stalloc() will return a string allocated in a new stackblock.
1550 */
1551#define SHELL_ALIGN(nbytes) (((nbytes) + SHELL_SIZE) & ~SHELL_SIZE)
1552enum {
1553 /* Most machines require the value returned from malloc to be aligned
1554 * in some way. The following macro will get this right
1555 * on many machines. */
Denys Vlasenko0e5e4ea2009-10-11 00:36:20 +02001556 SHELL_SIZE = sizeof(union { int i; char *cp; double d; }) - 1,
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001557 /* Minimum size of a block */
Denis Vlasenko01631112007-12-16 17:20:38 +00001558 MINSIZE = SHELL_ALIGN(504),
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001559};
1560
1561struct stack_block {
1562 struct stack_block *prev;
1563 char space[MINSIZE];
1564};
1565
1566struct stackmark {
1567 struct stack_block *stackp;
1568 char *stacknxt;
1569 size_t stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001570};
1571
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001572
Denis Vlasenko01631112007-12-16 17:20:38 +00001573struct globals_memstack {
1574 struct stack_block *g_stackp; // = &stackbase;
Denis Vlasenko01631112007-12-16 17:20:38 +00001575 char *g_stacknxt; // = stackbase.space;
1576 char *sstrend; // = stackbase.space + MINSIZE;
1577 size_t g_stacknleft; // = MINSIZE;
Denis Vlasenko01631112007-12-16 17:20:38 +00001578 struct stack_block stackbase;
1579};
Denys Vlasenko6f9442f2018-01-28 20:41:23 +01001580extern struct globals_memstack *BB_GLOBAL_CONST ash_ptr_to_globals_memstack;
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001581#define G_memstack (*ash_ptr_to_globals_memstack)
Denis Vlasenko01631112007-12-16 17:20:38 +00001582#define g_stackp (G_memstack.g_stackp )
Denis Vlasenko01631112007-12-16 17:20:38 +00001583#define g_stacknxt (G_memstack.g_stacknxt )
1584#define sstrend (G_memstack.sstrend )
1585#define g_stacknleft (G_memstack.g_stacknleft)
Denis Vlasenko01631112007-12-16 17:20:38 +00001586#define stackbase (G_memstack.stackbase )
1587#define INIT_G_memstack() do { \
Denys Vlasenkoaf7169b2019-10-25 12:12:22 +02001588 (*(struct globals_memstack**)not_const_pp(&ash_ptr_to_globals_memstack)) = xzalloc(sizeof(G_memstack)); \
Denis Vlasenko574f2f42008-02-27 18:41:59 +00001589 barrier(); \
Denis Vlasenko01631112007-12-16 17:20:38 +00001590 g_stackp = &stackbase; \
1591 g_stacknxt = stackbase.space; \
1592 g_stacknleft = MINSIZE; \
1593 sstrend = stackbase.space + MINSIZE; \
Denis Vlasenko01631112007-12-16 17:20:38 +00001594} while (0)
1595
Denys Vlasenkoe7670ff2009-10-11 00:45:25 +02001596
Denis Vlasenko01631112007-12-16 17:20:38 +00001597#define stackblock() ((void *)g_stacknxt)
1598#define stackblocksize() g_stacknleft
1599
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001600/*
1601 * Parse trees for commands are allocated in lifo order, so we use a stack
1602 * to make this more efficient, and also to avoid all sorts of exception
1603 * handling code to handle interrupts in the middle of a parse.
1604 *
1605 * The size 504 was chosen because the Ultrix malloc handles that size
1606 * well.
1607 */
1608static void *
1609stalloc(size_t nbytes)
1610{
1611 char *p;
1612 size_t aligned;
1613
1614 aligned = SHELL_ALIGN(nbytes);
Denis Vlasenko01631112007-12-16 17:20:38 +00001615 if (aligned > g_stacknleft) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001616 size_t len;
1617 size_t blocksize;
1618 struct stack_block *sp;
1619
1620 blocksize = aligned;
1621 if (blocksize < MINSIZE)
1622 blocksize = MINSIZE;
1623 len = sizeof(struct stack_block) - MINSIZE + blocksize;
1624 if (len < blocksize)
1625 ash_msg_and_raise_error(bb_msg_memory_exhausted);
1626 INT_OFF;
1627 sp = ckmalloc(len);
Denis Vlasenko01631112007-12-16 17:20:38 +00001628 sp->prev = g_stackp;
1629 g_stacknxt = sp->space;
1630 g_stacknleft = blocksize;
1631 sstrend = g_stacknxt + blocksize;
1632 g_stackp = sp;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001633 INT_ON;
1634 }
Denis Vlasenko01631112007-12-16 17:20:38 +00001635 p = g_stacknxt;
1636 g_stacknxt += aligned;
1637 g_stacknleft -= aligned;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001638 return p;
1639}
1640
Denis Vlasenko597906c2008-02-20 16:38:54 +00001641static void *
1642stzalloc(size_t nbytes)
1643{
1644 return memset(stalloc(nbytes), 0, nbytes);
1645}
1646
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001647static void
1648stunalloc(void *p)
1649{
1650#if DEBUG
Denis Vlasenko01631112007-12-16 17:20:38 +00001651 if (!p || (g_stacknxt < (char *)p) || ((char *)p < g_stackp->space)) {
Bernhard Reutner-Fischer5e25ddb2008-05-19 09:48:17 +00001652 write(STDERR_FILENO, "stunalloc\n", 10);
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001653 abort();
1654 }
1655#endif
Denis Vlasenko01631112007-12-16 17:20:38 +00001656 g_stacknleft += g_stacknxt - (char *)p;
1657 g_stacknxt = p;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001658}
1659
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001660/*
1661 * Like strdup but works with the ash stack.
1662 */
1663static char *
Denys Vlasenko8e2bc472016-09-28 23:02:57 +02001664sstrdup(const char *p)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001665{
1666 size_t len = strlen(p) + 1;
1667 return memcpy(stalloc(len), p, len);
1668}
1669
Denys Vlasenko03c36e02018-01-10 15:18:35 +01001670static ALWAYS_INLINE void
Denys Vlasenko60ca8342016-09-30 11:21:21 +02001671grabstackblock(size_t len)
1672{
Denys Vlasenkoa318bba2016-10-26 18:26:27 +02001673 stalloc(len);
Denys Vlasenko60ca8342016-09-30 11:21:21 +02001674}
1675
1676static void
1677pushstackmark(struct stackmark *mark, size_t len)
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001678{
Denis Vlasenko01631112007-12-16 17:20:38 +00001679 mark->stackp = g_stackp;
1680 mark->stacknxt = g_stacknxt;
1681 mark->stacknleft = g_stacknleft;
Denys Vlasenko60ca8342016-09-30 11:21:21 +02001682 grabstackblock(len);
1683}
1684
1685static void
1686setstackmark(struct stackmark *mark)
1687{
1688 pushstackmark(mark, g_stacknxt == g_stackp->space && g_stackp != &stackbase);
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001689}
1690
1691static void
1692popstackmark(struct stackmark *mark)
1693{
1694 struct stack_block *sp;
1695
Denis Vlasenko93ebd4f2007-03-13 20:55:36 +00001696 if (!mark->stackp)
1697 return;
1698
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001699 INT_OFF;
Denis Vlasenko01631112007-12-16 17:20:38 +00001700 while (g_stackp != mark->stackp) {
1701 sp = g_stackp;
1702 g_stackp = sp->prev;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001703 free(sp);
1704 }
Denis Vlasenko01631112007-12-16 17:20:38 +00001705 g_stacknxt = mark->stacknxt;
1706 g_stacknleft = mark->stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001707 sstrend = mark->stacknxt + mark->stacknleft;
1708 INT_ON;
1709}
1710
1711/*
1712 * When the parser reads in a string, it wants to stick the string on the
1713 * stack and only adjust the stack pointer when it knows how big the
1714 * string is. Stackblock (defined in stack.h) returns a pointer to a block
1715 * of space on top of the stack and stackblocklen returns the length of
1716 * this block. Growstackblock will grow this space by at least one byte,
1717 * possibly moving it (like realloc). Grabstackblock actually allocates the
1718 * part of the block that has been used.
1719 */
1720static void
Denys Vlasenkoda2e46d2020-02-21 15:25:37 +01001721growstackblock(size_t min)
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001722{
1723 size_t newlen;
1724
Denis Vlasenko01631112007-12-16 17:20:38 +00001725 newlen = g_stacknleft * 2;
1726 if (newlen < g_stacknleft)
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001727 ash_msg_and_raise_error(bb_msg_memory_exhausted);
Denys Vlasenkoda2e46d2020-02-21 15:25:37 +01001728 min = SHELL_ALIGN(min | 128);
1729 if (newlen < min)
1730 newlen += min;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001731
Denis Vlasenko01631112007-12-16 17:20:38 +00001732 if (g_stacknxt == g_stackp->space && g_stackp != &stackbase) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001733 struct stack_block *sp;
1734 struct stack_block *prevstackp;
1735 size_t grosslen;
1736
1737 INT_OFF;
Denis Vlasenko01631112007-12-16 17:20:38 +00001738 sp = g_stackp;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001739 prevstackp = sp->prev;
1740 grosslen = newlen + sizeof(struct stack_block) - MINSIZE;
1741 sp = ckrealloc(sp, grosslen);
1742 sp->prev = prevstackp;
Denis Vlasenko01631112007-12-16 17:20:38 +00001743 g_stackp = sp;
1744 g_stacknxt = sp->space;
1745 g_stacknleft = newlen;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001746 sstrend = sp->space + newlen;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001747 INT_ON;
1748 } else {
Denis Vlasenko01631112007-12-16 17:20:38 +00001749 char *oldspace = g_stacknxt;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001750 size_t oldlen = g_stacknleft;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001751 char *p = stalloc(newlen);
1752
1753 /* free the space we just allocated */
Denis Vlasenko01631112007-12-16 17:20:38 +00001754 g_stacknxt = memcpy(p, oldspace, oldlen);
1755 g_stacknleft += newlen;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001756 }
1757}
1758
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001759/*
1760 * The following routines are somewhat easier to use than the above.
1761 * The user declares a variable of type STACKSTR, which may be declared
1762 * to be a register. The macro STARTSTACKSTR initializes things. Then
1763 * the user uses the macro STPUTC to add characters to the string. In
1764 * effect, STPUTC(c, p) is the same as *p++ = c except that the stack is
1765 * grown as necessary. When the user is done, she can just leave the
1766 * string there and refer to it using stackblock(). Or she can allocate
1767 * the space for it using grabstackstr(). If it is necessary to allow
1768 * someone else to use the stack temporarily and then continue to grow
1769 * the string, the user should use grabstack to allocate the space, and
1770 * then call ungrabstr(p) to return to the previous mode of operation.
1771 *
1772 * USTPUTC is like STPUTC except that it doesn't check for overflow.
1773 * CHECKSTACKSPACE can be called before USTPUTC to ensure that there
1774 * is space for at least one character.
1775 */
1776static void *
1777growstackstr(void)
1778{
1779 size_t len = stackblocksize();
Denys Vlasenkoda2e46d2020-02-21 15:25:37 +01001780 growstackblock(0);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00001781 return (char *)stackblock() + len;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001782}
1783
Denys Vlasenkoc55847f2020-02-17 15:59:08 +01001784static char *
1785growstackto(size_t len)
1786{
Denys Vlasenkoda2e46d2020-02-21 15:25:37 +01001787 if (stackblocksize() < len)
1788 growstackblock(len);
Denys Vlasenkoc55847f2020-02-17 15:59:08 +01001789 return stackblock();
1790}
1791
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001792/*
1793 * Called from CHECKSTRSPACE.
1794 */
1795static char *
1796makestrspace(size_t newlen, char *p)
1797{
Denis Vlasenko01631112007-12-16 17:20:38 +00001798 size_t len = p - g_stacknxt;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001799
Denys Vlasenkoc55847f2020-02-17 15:59:08 +01001800 return growstackto(len + newlen) + len;
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001801}
1802
1803static char *
Denys Vlasenko538ee412020-02-22 19:11:41 +01001804stnputs(const char *s, size_t n, char *p)
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001805{
1806 p = makestrspace(n, p);
Denys Vlasenko5ace96a2017-07-23 21:46:02 +02001807 p = (char *)mempcpy(p, s, n);
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001808 return p;
1809}
1810
1811static char *
1812stack_putstr(const char *s, char *p)
1813{
Denys Vlasenko538ee412020-02-22 19:11:41 +01001814 return stnputs(s, strlen(s), p);
Denis Vlasenko0c032a42007-02-23 01:03:40 +00001815}
1816
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001817static char *
1818_STPUTC(int c, char *p)
1819{
1820 if (p == sstrend)
1821 p = growstackstr();
1822 *p++ = c;
1823 return p;
1824}
1825
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00001826#define STARTSTACKSTR(p) ((p) = stackblock())
1827#define STPUTC(c, p) ((p) = _STPUTC((c), (p)))
Denis Vlasenko843cbd52008-06-27 00:23:18 +00001828#define CHECKSTRSPACE(n, p) do { \
1829 char *q = (p); \
1830 size_t l = (n); \
1831 size_t m = sstrend - q; \
1832 if (l > m) \
1833 (p) = makestrspace(l, q); \
1834} while (0)
Denis Vlasenkoef527f52008-06-23 01:52:30 +00001835#define USTPUTC(c, p) (*(p)++ = (c))
Denis Vlasenko843cbd52008-06-27 00:23:18 +00001836#define STACKSTRNUL(p) do { \
1837 if ((p) == sstrend) \
1838 (p) = growstackstr(); \
1839 *(p) = '\0'; \
1840} while (0)
Denis Vlasenkoef527f52008-06-23 01:52:30 +00001841#define STUNPUTC(p) (--(p))
1842#define STTOPC(p) ((p)[-1])
1843#define STADJUST(amount, p) ((p) += (amount))
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001844
1845#define grabstackstr(p) stalloc((char *)(p) - (char *)stackblock())
Denis Vlasenkoef527f52008-06-23 01:52:30 +00001846#define ungrabstackstr(s, p) stunalloc(s)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001847#define stackstrend() ((void *)sstrend)
1848
1849
1850/* ============ String helpers */
1851
1852/*
1853 * prefix -- see if pfx is a prefix of string.
1854 */
1855static char *
1856prefix(const char *string, const char *pfx)
1857{
1858 while (*pfx) {
1859 if (*pfx++ != *string++)
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00001860 return NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001861 }
1862 return (char *) string;
1863}
1864
1865/*
1866 * Check for a valid number. This should be elsewhere.
1867 */
1868static int
1869is_number(const char *p)
1870{
1871 do {
1872 if (!isdigit(*p))
1873 return 0;
1874 } while (*++p != '\0');
1875 return 1;
1876}
1877
1878/*
1879 * Convert a string of digits to an integer, printing an error message on
1880 * failure.
1881 */
1882static int
1883number(const char *s)
1884{
1885 if (!is_number(s))
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02001886 ash_msg_and_raise_error(msg_illnum, s);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001887 return atoi(s);
1888}
1889
1890/*
Denys Vlasenko42ba7572017-07-21 13:20:14 +02001891 * Produce a single quoted string suitable as input to the shell.
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001892 * The return string is allocated on the stack.
1893 */
1894static char *
1895single_quote(const char *s)
1896{
1897 char *p;
1898
1899 STARTSTACKSTR(p);
1900
1901 do {
1902 char *q;
1903 size_t len;
1904
1905 len = strchrnul(s, '\'') - s;
1906
1907 q = p = makestrspace(len + 3, p);
1908
1909 *q++ = '\'';
Denys Vlasenko94af83e2017-07-23 21:55:40 +02001910 q = (char *)mempcpy(q, s, len);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001911 *q++ = '\'';
1912 s += len;
1913
1914 STADJUST(q - p, p);
1915
Denys Vlasenkocd716832009-11-28 22:14:02 +01001916 if (*s != '\'')
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001917 break;
Denys Vlasenkocd716832009-11-28 22:14:02 +01001918 len = 0;
1919 do len++; while (*++s == '\'');
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001920
1921 q = p = makestrspace(len + 3, p);
1922
1923 *q++ = '"';
Denys Vlasenko5ace96a2017-07-23 21:46:02 +02001924 q = (char *)mempcpy(q, s - len, len);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001925 *q++ = '"';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001926
1927 STADJUST(q - p, p);
1928 } while (*s);
1929
Denys Vlasenkocd716832009-11-28 22:14:02 +01001930 USTPUTC('\0', p);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001931
1932 return stackblock();
1933}
1934
Denys Vlasenko42ba7572017-07-21 13:20:14 +02001935/*
1936 * Produce a possibly single quoted string suitable as input to the shell.
Denys Vlasenko42ba7572017-07-21 13:20:14 +02001937 * If quoting was done, the return string is allocated on the stack,
1938 * otherwise a pointer to the original string is returned.
1939 */
1940static const char *
1941maybe_single_quote(const char *s)
1942{
1943 const char *p = s;
1944
1945 while (*p) {
1946 /* Assuming ACSII */
1947 /* quote ctrl_chars space !"#$%&'()* */
1948 if (*p < '+')
1949 goto need_quoting;
1950 /* quote ;<=>? */
1951 if (*p >= ';' && *p <= '?')
1952 goto need_quoting;
1953 /* quote `[\ */
1954 if (*p == '`')
1955 goto need_quoting;
1956 if (*p == '[')
1957 goto need_quoting;
1958 if (*p == '\\')
1959 goto need_quoting;
1960 /* quote {|}~ DEL and high bytes */
1961 if (*p > 'z')
1962 goto need_quoting;
1963 /* Not quoting these: +,-./ 0-9 :@ A-Z ]^_ a-z */
1964 /* TODO: maybe avoid quoting % */
1965 p++;
1966 }
1967 return s;
1968
1969 need_quoting:
1970 return single_quote(s);
1971}
1972
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001973
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00001974/* ============ nextopt */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001975
1976static char **argptr; /* argument list for builtin commands */
1977static char *optionarg; /* set by nextopt (like getopt) */
1978static char *optptr; /* used by nextopt */
1979
1980/*
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001981 * XXX - should get rid of. Have all builtins use getopt(3).
1982 * The library getopt must have the BSD extension static variable
1983 * "optreset", otherwise it can't be used within the shell safely.
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001984 *
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001985 * Standard option processing (a la getopt) for builtin routines.
1986 * The only argument that is passed to nextopt is the option string;
1987 * the other arguments are unnecessary. It returns the character,
1988 * or '\0' on end of input.
Denis Vlasenkoaa744452007-02-23 01:04:22 +00001989 */
1990static int
1991nextopt(const char *optstring)
1992{
1993 char *p;
1994 const char *q;
1995 char c;
1996
1997 p = optptr;
1998 if (p == NULL || *p == '\0') {
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00001999 /* We ate entire "-param", take next one */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002000 p = *argptr;
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00002001 if (p == NULL)
2002 return '\0';
2003 if (*p != '-')
2004 return '\0';
2005 if (*++p == '\0') /* just "-" ? */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002006 return '\0';
2007 argptr++;
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00002008 if (LONE_DASH(p)) /* "--" ? */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002009 return '\0';
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00002010 /* p => next "-param" */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002011 }
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00002012 /* p => some option char in the middle of a "-param" */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002013 c = *p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00002014 for (q = optstring; *q != c;) {
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002015 if (*q == '\0')
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +00002016 ash_msg_and_raise_error("illegal option -%c", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002017 if (*++q == ':')
2018 q++;
2019 }
2020 if (*++q == ':') {
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00002021 if (*p == '\0') {
2022 p = *argptr++;
2023 if (p == NULL)
2024 ash_msg_and_raise_error("no arg for -%c option", c);
2025 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002026 optionarg = p;
2027 p = NULL;
2028 }
2029 optptr = p;
2030 return c;
2031}
2032
2033
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00002034/* ============ Shell variables */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002035
Denis Vlasenko01631112007-12-16 17:20:38 +00002036struct shparam {
2037 int nparam; /* # of positional parameters (without $0) */
2038#if ENABLE_ASH_GETOPTS
2039 int optind; /* next parameter to be processed by getopts */
2040 int optoff; /* used by getopts */
2041#endif
2042 unsigned char malloced; /* if parameter list dynamically allocated */
2043 char **p; /* parameter list */
2044};
2045
2046/*
2047 * Free the list of positional parameters.
2048 */
2049static void
2050freeparam(volatile struct shparam *param)
2051{
Denis Vlasenko01631112007-12-16 17:20:38 +00002052 if (param->malloced) {
Denis Vlasenko3177ba02008-07-13 20:39:23 +00002053 char **ap, **ap1;
2054 ap = ap1 = param->p;
2055 while (*ap)
2056 free(*ap++);
2057 free(ap1);
Denis Vlasenko01631112007-12-16 17:20:38 +00002058 }
2059}
2060
2061#if ENABLE_ASH_GETOPTS
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02002062static void FAST_FUNC getoptsreset(const char *value);
Denis Vlasenko01631112007-12-16 17:20:38 +00002063#endif
2064
2065struct var {
2066 struct var *next; /* next entry in hash list */
2067 int flags; /* flags are defined above */
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002068 const char *var_text; /* name=value */
2069 void (*var_func)(const char *) FAST_FUNC; /* function to be called when */
Denis Vlasenko01631112007-12-16 17:20:38 +00002070 /* the variable gets set/unset */
2071};
2072
2073struct localvar {
2074 struct localvar *next; /* next local variable in list */
2075 struct var *vp; /* the variable that was made local */
2076 int flags; /* saved flags */
2077 const char *text; /* saved text */
2078};
2079
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002080/* flags */
2081#define VEXPORT 0x01 /* variable is exported */
2082#define VREADONLY 0x02 /* variable cannot be modified */
2083#define VSTRFIXED 0x04 /* variable struct is statically allocated */
2084#define VTEXTFIXED 0x08 /* text is statically allocated */
2085#define VSTACK 0x10 /* text is allocated on the stack */
2086#define VUNSET 0x20 /* the variable is not set */
2087#define VNOFUNC 0x40 /* don't call the callback function */
2088#define VNOSET 0x80 /* do not set variable - just readonly test */
2089#define VNOSAVE 0x100 /* when text is on the heap before setvareq */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00002090#if ENABLE_ASH_RANDOM_SUPPORT
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00002091# define VDYNAMIC 0x200 /* dynamic variable */
2092#else
2093# define VDYNAMIC 0
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002094#endif
2095
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00002096
Denis Vlasenko01631112007-12-16 17:20:38 +00002097/* Need to be before varinit_data[] */
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00002098#if ENABLE_LOCALE_SUPPORT
Denys Vlasenko2634bf32009-06-09 18:40:07 +02002099static void FAST_FUNC
Denis Vlasenkoa8915072007-02-23 21:10:06 +00002100change_lc_all(const char *value)
2101{
2102 if (value && *value != '\0')
2103 setlocale(LC_ALL, value);
2104}
Denys Vlasenko2634bf32009-06-09 18:40:07 +02002105static void FAST_FUNC
Denis Vlasenkoa8915072007-02-23 21:10:06 +00002106change_lc_ctype(const char *value)
2107{
2108 if (value && *value != '\0')
2109 setlocale(LC_CTYPE, value);
2110}
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00002111#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002112#if ENABLE_ASH_MAIL
2113static void chkmail(void);
Denys Vlasenko8c52f802011-02-04 17:36:21 +01002114static void changemail(const char *var_value) FAST_FUNC;
2115#else
2116# define chkmail() ((void)0)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002117#endif
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02002118static void changepath(const char *) FAST_FUNC;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002119#if ENABLE_ASH_RANDOM_SUPPORT
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02002120static void change_random(const char *) FAST_FUNC;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002121#endif
Ron Yorston1d371862019-04-15 10:52:05 +01002122#if BASH_EPOCH_VARS
2123static void change_seconds(const char *) FAST_FUNC;
2124static void change_realtime(const char *) FAST_FUNC;
2125#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002126
Denis Vlasenko01631112007-12-16 17:20:38 +00002127static const struct {
2128 int flags;
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002129 const char *var_text;
2130 void (*var_func)(const char *) FAST_FUNC;
Denys Vlasenko965b7952020-11-30 13:03:03 +01002131} varinit_data[] ALIGN_PTR = {
Denys Vlasenko566a3132012-07-07 21:40:35 +02002132 /*
2133 * Note: VEXPORT would not work correctly here for NOFORK applets:
2134 * some environment strings may be constant.
2135 */
Denis Vlasenko01631112007-12-16 17:20:38 +00002136 { VSTRFIXED|VTEXTFIXED , defifsvar , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002137#if ENABLE_ASH_MAIL
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002138 { VSTRFIXED|VTEXTFIXED|VUNSET, "MAIL" , changemail },
2139 { VSTRFIXED|VTEXTFIXED|VUNSET, "MAILPATH" , changemail },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002140#endif
Denis Vlasenko01631112007-12-16 17:20:38 +00002141 { VSTRFIXED|VTEXTFIXED , bb_PATH_root_path, changepath },
2142 { VSTRFIXED|VTEXTFIXED , "PS1=$ " , NULL },
2143 { VSTRFIXED|VTEXTFIXED , "PS2=> " , NULL },
2144 { VSTRFIXED|VTEXTFIXED , "PS4=+ " , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002145#if ENABLE_ASH_GETOPTS
Denys Vlasenkoe627ac92016-09-30 14:36:59 +02002146 { VSTRFIXED|VTEXTFIXED , defoptindvar, getoptsreset },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002147#endif
Denys Vlasenko675d24a2018-01-27 22:02:05 +01002148 { VSTRFIXED|VTEXTFIXED , NULL /* inited to linenovar */, NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002149#if ENABLE_ASH_RANDOM_SUPPORT
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002150 { VSTRFIXED|VTEXTFIXED|VUNSET|VDYNAMIC, "RANDOM", change_random },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002151#endif
Ron Yorston1d371862019-04-15 10:52:05 +01002152#if BASH_EPOCH_VARS
2153 { VSTRFIXED|VTEXTFIXED|VUNSET|VDYNAMIC, "EPOCHSECONDS", change_seconds },
2154 { VSTRFIXED|VTEXTFIXED|VUNSET|VDYNAMIC, "EPOCHREALTIME", change_realtime },
2155#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002156#if ENABLE_LOCALE_SUPPORT
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002157 { VSTRFIXED|VTEXTFIXED|VUNSET, "LC_ALL" , change_lc_all },
2158 { VSTRFIXED|VTEXTFIXED|VUNSET, "LC_CTYPE" , change_lc_ctype },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002159#endif
2160#if ENABLE_FEATURE_EDITING_SAVEHISTORY
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002161 { VSTRFIXED|VTEXTFIXED|VUNSET, "HISTFILE" , NULL },
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002162#endif
2163};
2164
Denis Vlasenko0b769642008-07-24 07:54:57 +00002165struct redirtab;
Denis Vlasenko01631112007-12-16 17:20:38 +00002166
2167struct globals_var {
2168 struct shparam shellparam; /* $@ current positional parameters */
2169 struct redirtab *redirlist;
Denys Vlasenkod07a15b2017-07-30 16:51:05 +02002170 int preverrout_fd; /* stderr fd: usually 2, unless redirect moved it */
Denis Vlasenko01631112007-12-16 17:20:38 +00002171 struct var *vartab[VTABSIZE];
2172 struct var varinit[ARRAY_SIZE(varinit_data)];
Denys Vlasenko675d24a2018-01-27 22:02:05 +01002173 int lineno;
2174 char linenovar[sizeof("LINENO=") + sizeof(int)*3];
Roberto A. Fogliettae0bf3df2021-09-07 01:19:31 +02002175 unsigned trap_depth;
2176 bool in_trap_ERR; /* ERR cannot recurse, no need to be a counter */
Denis Vlasenko01631112007-12-16 17:20:38 +00002177};
Denys Vlasenko6f9442f2018-01-28 20:41:23 +01002178extern struct globals_var *BB_GLOBAL_CONST ash_ptr_to_globals_var;
Denis Vlasenko574f2f42008-02-27 18:41:59 +00002179#define G_var (*ash_ptr_to_globals_var)
Denis Vlasenko01631112007-12-16 17:20:38 +00002180#define shellparam (G_var.shellparam )
Denis Vlasenko0b769642008-07-24 07:54:57 +00002181//#define redirlist (G_var.redirlist )
Denis Vlasenko01631112007-12-16 17:20:38 +00002182#define preverrout_fd (G_var.preverrout_fd)
2183#define vartab (G_var.vartab )
2184#define varinit (G_var.varinit )
Denys Vlasenko675d24a2018-01-27 22:02:05 +01002185#define lineno (G_var.lineno )
2186#define linenovar (G_var.linenovar )
Roberto A. Fogliettae0bf3df2021-09-07 01:19:31 +02002187#define trap_depth (G_var.trap_depth )
2188#define in_trap_ERR (G_var.in_trap_ERR )
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00002189#define vifs varinit[0]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002190#if ENABLE_ASH_MAIL
Ron Yorston1d371862019-04-15 10:52:05 +01002191# define vmail varinit[1]
2192# define vmpath varinit[2]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002193#endif
Ron Yorston1d371862019-04-15 10:52:05 +01002194#define VAR_OFFSET1 (ENABLE_ASH_MAIL*2)
2195#define vpath varinit[VAR_OFFSET1 + 1]
2196#define vps1 varinit[VAR_OFFSET1 + 2]
2197#define vps2 varinit[VAR_OFFSET1 + 3]
2198#define vps4 varinit[VAR_OFFSET1 + 4]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002199#if ENABLE_ASH_GETOPTS
Ron Yorston1d371862019-04-15 10:52:05 +01002200# define voptind varinit[VAR_OFFSET1 + 5]
2201#endif
2202#define VAR_OFFSET2 (VAR_OFFSET1 + ENABLE_ASH_GETOPTS)
2203#define vlineno varinit[VAR_OFFSET2 + 5]
2204#if ENABLE_ASH_RANDOM_SUPPORT
2205# define vrandom varinit[VAR_OFFSET2 + 6]
2206#endif
2207#define VAR_OFFSET3 (VAR_OFFSET2 + ENABLE_ASH_RANDOM_SUPPORT)
2208#if BASH_EPOCH_VARS
2209# define vepochs varinit[VAR_OFFSET3 + 6]
2210# define vepochr varinit[VAR_OFFSET3 + 7]
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002211#endif
Denys Vlasenko675d24a2018-01-27 22:02:05 +01002212#define INIT_G_var() do { \
2213 unsigned i; \
Denys Vlasenkoaf7169b2019-10-25 12:12:22 +02002214 (*(struct globals_var**)not_const_pp(&ash_ptr_to_globals_var)) = xzalloc(sizeof(G_var)); \
Denys Vlasenko675d24a2018-01-27 22:02:05 +01002215 barrier(); \
2216 for (i = 0; i < ARRAY_SIZE(varinit_data); i++) { \
2217 varinit[i].flags = varinit_data[i].flags; \
2218 varinit[i].var_text = varinit_data[i].var_text; \
2219 varinit[i].var_func = varinit_data[i].var_func; \
2220 } \
2221 strcpy(linenovar, "LINENO="); \
2222 vlineno.var_text = linenovar; \
2223} while (0)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002224
2225/*
2226 * The following macros access the values of the above variables.
2227 * They have to skip over the name. They return the null string
2228 * for unset variables.
2229 */
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002230#define ifsval() (vifs.var_text + 4)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002231#define ifsset() ((vifs.flags & VUNSET) == 0)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00002232#if ENABLE_ASH_MAIL
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002233# define mailval() (vmail.var_text + 5)
2234# define mpathval() (vmpath.var_text + 9)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00002235# define mpathset() ((vmpath.flags & VUNSET) == 0)
2236#endif
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002237#define pathval() (vpath.var_text + 5)
2238#define ps1val() (vps1.var_text + 4)
2239#define ps2val() (vps2.var_text + 4)
2240#define ps4val() (vps4.var_text + 4)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00002241#if ENABLE_ASH_GETOPTS
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002242# define optindval() (voptind.var_text + 7)
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00002243#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002244
Denis Vlasenko01631112007-12-16 17:20:38 +00002245#if ENABLE_ASH_GETOPTS
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02002246static void FAST_FUNC
Denis Vlasenko01631112007-12-16 17:20:38 +00002247getoptsreset(const char *value)
2248{
Denys Vlasenko46289452017-08-11 00:59:36 +02002249 shellparam.optind = 1;
2250 if (is_number(value))
2251 shellparam.optind = number(value) ?: 1;
Denis Vlasenko01631112007-12-16 17:20:38 +00002252 shellparam.optoff = -1;
2253}
2254#endif
2255
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002256/*
2257 * Compares two strings up to the first = or '\0'. The first
2258 * string must be terminated by '='; the second may be terminated by
2259 * either '=' or '\0'.
2260 */
2261static int
2262varcmp(const char *p, const char *q)
2263{
2264 int c, d;
2265
2266 while ((c = *p) == (d = *q)) {
Denys Vlasenko0a0acb52015-04-18 19:36:38 +02002267 if (c == '\0' || c == '=')
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002268 goto out;
2269 p++;
2270 q++;
2271 }
2272 if (c == '=')
Denis Vlasenko9650f362007-02-23 01:04:37 +00002273 c = '\0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002274 if (d == '=')
Denis Vlasenko9650f362007-02-23 01:04:37 +00002275 d = '\0';
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002276 out:
2277 return c - d;
2278}
2279
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002280/*
2281 * Find the appropriate entry in the hash table from the name.
2282 */
2283static struct var **
2284hashvar(const char *p)
2285{
2286 unsigned hashval;
2287
2288 hashval = ((unsigned char) *p) << 4;
2289 while (*p && *p != '=')
2290 hashval += (unsigned char) *p++;
2291 return &vartab[hashval % VTABSIZE];
2292}
2293
2294static int
2295vpcmp(const void *a, const void *b)
2296{
2297 return varcmp(*(const char **)a, *(const char **)b);
2298}
2299
2300/*
2301 * This routine initializes the builtin variables.
2302 */
2303static void
2304initvar(void)
2305{
2306 struct var *vp;
2307 struct var *end;
2308 struct var **vpp;
2309
2310 /*
2311 * PS1 depends on uid
2312 */
2313#if ENABLE_FEATURE_EDITING && ENABLE_FEATURE_EDITING_FANCY_PROMPT
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002314 vps1.var_text = "PS1=\\w \\$ ";
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002315#else
2316 if (!geteuid())
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002317 vps1.var_text = "PS1=# ";
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002318#endif
2319 vp = varinit;
Denis Vlasenko80b8b392007-06-25 10:55:35 +00002320 end = vp + ARRAY_SIZE(varinit);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002321 do {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002322 vpp = hashvar(vp->var_text);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002323 vp->next = *vpp;
2324 *vpp = vp;
2325 } while (++vp < end);
2326}
2327
2328static struct var **
2329findvar(struct var **vpp, const char *name)
2330{
2331 for (; *vpp; vpp = &(*vpp)->next) {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002332 if (varcmp((*vpp)->var_text, name) == 0) {
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002333 break;
2334 }
2335 }
2336 return vpp;
2337}
2338
2339/*
2340 * Find the value of a variable. Returns NULL if not set.
2341 */
Denys Vlasenko03dad222010-01-12 23:29:57 +01002342static const char* FAST_FUNC
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002343lookupvar(const char *name)
2344{
2345 struct var *v;
2346
2347 v = *findvar(hashvar(name), name);
2348 if (v) {
Ron Yorston1d371862019-04-15 10:52:05 +01002349#if ENABLE_ASH_RANDOM_SUPPORT || BASH_EPOCH_VARS
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002350 /*
2351 * Dynamic variables are implemented roughly the same way they are
2352 * in bash. Namely, they're "special" so long as they aren't unset.
2353 * As soon as they're unset, they're no longer dynamic, and dynamic
2354 * lookup will no longer happen at that point. -- PFM.
2355 */
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002356 if (v->flags & VDYNAMIC)
2357 v->var_func(NULL);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002358#endif
Denys Vlasenko675d24a2018-01-27 22:02:05 +01002359 if (!(v->flags & VUNSET)) {
2360 if (v == &vlineno && v->var_text == linenovar) {
2361 fmtstr(linenovar+7, sizeof(linenovar)-7, "%d", lineno);
2362 }
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002363 return var_end(v->var_text);
Denys Vlasenko675d24a2018-01-27 22:02:05 +01002364 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002365 }
2366 return NULL;
2367}
2368
Denys Vlasenko0b883582016-12-23 16:49:07 +01002369#if ENABLE_UNICODE_SUPPORT
Denys Vlasenko37dc08b2016-10-02 04:38:07 +02002370static void
2371reinit_unicode_for_ash(void)
Denys Vlasenkoe9ab07c2014-08-13 18:00:08 +02002372{
2373 /* Unicode support should be activated even if LANG is set
2374 * _during_ shell execution, not only if it was set when
2375 * shell was started. Therefore, re-check LANG every time:
2376 */
2377 if (ENABLE_FEATURE_CHECK_UNICODE_IN_ENV
2378 || ENABLE_UNICODE_USING_LOCALE
2379 ) {
2380 const char *s = lookupvar("LC_ALL");
2381 if (!s) s = lookupvar("LC_CTYPE");
2382 if (!s) s = lookupvar("LANG");
2383 reinit_unicode(s);
2384 }
2385}
Denys Vlasenko0b883582016-12-23 16:49:07 +01002386#else
2387# define reinit_unicode_for_ash() ((void)0)
2388#endif
Denys Vlasenkoe9ab07c2014-08-13 18:00:08 +02002389
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002390/*
2391 * Search the environment of a builtin command.
2392 */
Denys Vlasenko488e6092017-07-26 23:08:36 +02002393static ALWAYS_INLINE const char *
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002394bltinlookup(const char *name)
2395{
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002396 return lookupvar(name);
2397}
2398
2399/*
2400 * Same as setvar except that the variable and value are passed in
2401 * the first argument as name=value. Since the first argument will
2402 * be actually stored in the table, it should not be a string that
2403 * will go away.
2404 * Called with interrupts off.
2405 */
Denys Vlasenkod04fc712017-07-26 20:06:48 +02002406static struct var *
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002407setvareq(char *s, int flags)
2408{
2409 struct var *vp, **vpp;
2410
2411 vpp = hashvar(s);
2412 flags |= (VEXPORT & (((unsigned) (1 - aflag)) - 1));
Denys Vlasenkod04fc712017-07-26 20:06:48 +02002413 vpp = findvar(vpp, s);
2414 vp = *vpp;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002415 if (vp) {
2416 if ((vp->flags & (VREADONLY|VDYNAMIC)) == VREADONLY) {
2417 const char *n;
2418
2419 if (flags & VNOSAVE)
2420 free(s);
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002421 n = vp->var_text;
Denys Vlasenkod81e9f52016-10-28 15:43:50 +02002422 exitstatus = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002423 ash_msg_and_raise_error("%.*s: is read only", strchrnul(n, '=') - n, n);
2424 }
2425
2426 if (flags & VNOSET)
Denys Vlasenkod04fc712017-07-26 20:06:48 +02002427 goto out;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002428
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002429 if (vp->var_func && !(flags & VNOFUNC))
2430 vp->var_func(var_end(s));
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002431
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002432 if (!(vp->flags & (VTEXTFIXED|VSTACK)))
2433 free((char*)vp->var_text);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002434
Denys Vlasenkob28d4c32017-07-25 16:29:36 +02002435 if (((flags & (VEXPORT|VREADONLY|VSTRFIXED|VUNSET)) | (vp->flags & VSTRFIXED)) == VUNSET) {
2436 *vpp = vp->next;
2437 free(vp);
2438 out_free:
2439 if ((flags & (VTEXTFIXED|VSTACK|VNOSAVE)) == VNOSAVE)
2440 free(s);
Denys Vlasenkod04fc712017-07-26 20:06:48 +02002441 goto out;
Denys Vlasenkob28d4c32017-07-25 16:29:36 +02002442 }
2443
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002444 flags |= vp->flags & ~(VTEXTFIXED|VSTACK|VNOSAVE|VUNSET);
Ron Yorston1d371862019-04-15 10:52:05 +01002445#if ENABLE_ASH_RANDOM_SUPPORT || BASH_EPOCH_VARS
Ron Yorstond96c69d2019-04-15 10:49:35 +01002446 if (flags & VUNSET)
2447 flags &= ~VDYNAMIC;
2448#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002449 } else {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002450 /* variable s is not found */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002451 if (flags & VNOSET)
Denys Vlasenkod04fc712017-07-26 20:06:48 +02002452 goto out;
Denys Vlasenkob28d4c32017-07-25 16:29:36 +02002453 if ((flags & (VEXPORT|VREADONLY|VSTRFIXED|VUNSET)) == VUNSET)
2454 goto out_free;
Denis Vlasenko597906c2008-02-20 16:38:54 +00002455 vp = ckzalloc(sizeof(*vp));
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002456 vp->next = *vpp;
Denis Vlasenko597906c2008-02-20 16:38:54 +00002457 /*vp->func = NULL; - ckzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002458 *vpp = vp;
2459 }
2460 if (!(flags & (VTEXTFIXED|VSTACK|VNOSAVE)))
2461 s = ckstrdup(s);
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002462 vp->var_text = s;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002463 vp->flags = flags;
Denys Vlasenkod04fc712017-07-26 20:06:48 +02002464
2465 out:
2466 return vp;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002467}
2468
2469/*
2470 * Set the value of a variable. The flags argument is ored with the
2471 * flags of the variable. If val is NULL, the variable is unset.
2472 */
Denys Vlasenkod04fc712017-07-26 20:06:48 +02002473static struct var *
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002474setvar(const char *name, const char *val, int flags)
2475{
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002476 const char *q;
2477 char *p;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002478 char *nameeq;
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002479 size_t namelen;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002480 size_t vallen;
Denys Vlasenkod04fc712017-07-26 20:06:48 +02002481 struct var *vp;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002482
2483 q = endofname(name);
2484 p = strchrnul(q, '=');
2485 namelen = p - name;
2486 if (!namelen || p != q)
2487 ash_msg_and_raise_error("%.*s: bad variable name", namelen, name);
2488 vallen = 0;
2489 if (val == NULL) {
2490 flags |= VUNSET;
2491 } else {
2492 vallen = strlen(val);
2493 }
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +02002494
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002495 INT_OFF;
Ron Yorstone6a63bf2018-11-12 21:10:54 +00002496 nameeq = ckzalloc(namelen + vallen + 2);
Denys Vlasenkoda2244f2017-07-21 18:51:29 +02002497 p = mempcpy(nameeq, name, namelen);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002498 if (val) {
2499 *p++ = '=';
Ron Yorstone6a63bf2018-11-12 21:10:54 +00002500 memcpy(p, val, vallen);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002501 }
Denys Vlasenkod04fc712017-07-26 20:06:48 +02002502 vp = setvareq(nameeq, flags | VNOSAVE);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002503 INT_ON;
Denys Vlasenkod04fc712017-07-26 20:06:48 +02002504
2505 return vp;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002506}
2507
Denys Vlasenko03dad222010-01-12 23:29:57 +01002508static void FAST_FUNC
Denys Vlasenko0a0acb52015-04-18 19:36:38 +02002509setvar0(const char *name, const char *val)
Denys Vlasenko03dad222010-01-12 23:29:57 +01002510{
2511 setvar(name, val, 0);
2512}
2513
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002514/*
2515 * Unset the specified variable.
2516 */
Denys Vlasenkob28d4c32017-07-25 16:29:36 +02002517static void
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002518unsetvar(const char *s)
2519{
Denys Vlasenkocf3a7962017-07-26 14:38:19 +02002520 setvar(s, NULL, 0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002521}
2522
2523/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002524 * Generate a list of variables satisfying the given conditions.
2525 */
Denys Vlasenkoa5060b82017-11-03 14:16:25 +01002526#if !ENABLE_FEATURE_SH_NOFORK
2527# define listvars(on, off, lp, end) listvars(on, off, end)
2528#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002529static char **
Denys Vlasenkoa5060b82017-11-03 14:16:25 +01002530listvars(int on, int off, struct strlist *lp, char ***end)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002531{
2532 struct var **vpp;
2533 struct var *vp;
2534 char **ep;
2535 int mask;
2536
2537 STARTSTACKSTR(ep);
2538 vpp = vartab;
2539 mask = on | off;
2540 do {
2541 for (vp = *vpp; vp; vp = vp->next) {
2542 if ((vp->flags & mask) == on) {
Denys Vlasenkoa5060b82017-11-03 14:16:25 +01002543#if ENABLE_FEATURE_SH_NOFORK
2544 /* If variable with the same name is both
2545 * exported and temporarily set for a command:
2546 * export ZVAR=5
2547 * ZVAR=6 printenv
2548 * then "ZVAR=6" will be both in vartab and
2549 * lp lists. Do not pass it twice to printenv.
2550 */
2551 struct strlist *lp1 = lp;
2552 while (lp1) {
2553 if (strcmp(lp1->text, vp->var_text) == 0)
2554 goto skip;
2555 lp1 = lp1->next;
2556 }
2557#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002558 if (ep == stackstrend())
2559 ep = growstackstr();
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02002560 *ep++ = (char*)vp->var_text;
Denys Vlasenkoa5060b82017-11-03 14:16:25 +01002561#if ENABLE_FEATURE_SH_NOFORK
2562 skip: ;
2563#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002564 }
2565 }
2566 } while (++vpp < vartab + VTABSIZE);
Denys Vlasenkoa5060b82017-11-03 14:16:25 +01002567
2568#if ENABLE_FEATURE_SH_NOFORK
2569 while (lp) {
2570 if (ep == stackstrend())
2571 ep = growstackstr();
2572 *ep++ = lp->text;
2573 lp = lp->next;
2574 }
2575#endif
2576
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002577 if (ep == stackstrend())
2578 ep = growstackstr();
2579 if (end)
2580 *end = ep;
2581 *ep++ = NULL;
2582 return grabstackstr(ep);
2583}
2584
2585
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +01002586/* ============ Path search helper */
2587static const char *
2588legal_pathopt(const char *opt, const char *term, int magic)
2589{
2590 switch (magic) {
2591 case 0:
2592 opt = NULL;
2593 break;
2594
2595 case 1:
2596 opt = prefix(opt, "builtin") ?: prefix(opt, "func");
2597 break;
2598
2599 default:
2600 opt += strcspn(opt, term);
2601 break;
2602 }
2603
2604 if (opt && *opt == '%')
2605 opt++;
2606
2607 return opt;
2608}
2609
2610/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002611 * The variable path (passed by reference) should be set to the start
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +01002612 * of the path before the first call; padvance will update
2613 * this value as it proceeds. Successive calls to padvance will return
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002614 * the possible path expansions in sequence. If an option (indicated by
2615 * a percent sign) appears in the path entry then the global variable
2616 * pathopt will be set to point to it; otherwise pathopt will be set to
2617 * NULL.
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +01002618 *
2619 * If magic is 0 then pathopt recognition will be disabled. If magic is
2620 * 1 we shall recognise %builtin/%func. Otherwise we shall accept any
2621 * pathopt.
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002622 */
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +01002623static const char *pathopt; /* set by padvance */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002624
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +01002625static int
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +01002626padvance_magic(const char **path, const char *name, int magic)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002627{
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +01002628 const char *term = "%:";
2629 const char *lpathopt;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002630 const char *p;
2631 char *q;
2632 const char *start;
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +01002633 size_t qlen;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002634 size_t len;
2635
2636 if (*path == NULL)
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +01002637 return -1;
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +01002638
2639 lpathopt = NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002640 start = *path;
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +01002641
2642 if (*start == '%' && (p = legal_pathopt(start + 1, term, magic))) {
2643 lpathopt = start + 1;
2644 start = p;
2645 term = ":";
2646 }
2647
2648 len = strcspn(start, term);
2649 p = start + len;
2650
2651 if (*p == '%') {
2652 size_t extra = strchrnul(p, ':') - p;
2653
2654 if (legal_pathopt(p + 1, term, magic))
2655 lpathopt = p + 1;
2656 else
2657 len += extra;
2658
2659 p += extra;
2660 }
2661
2662 pathopt = lpathopt;
2663 *path = *p == ':' ? p + 1 : NULL;
2664
2665 /* "2" is for '/' and '\0' */
2666 qlen = len + strlen(name) + 2;
2667 q = growstackto(qlen);
2668
2669 if (len) {
2670 q = mempcpy(q, start, len);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002671 *q++ = '/';
2672 }
2673 strcpy(q, name);
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +01002674
2675 return qlen;
2676}
2677
2678static int
2679padvance(const char **path, const char *name)
2680{
2681 return padvance_magic(path, name, 1);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002682}
2683
2684
2685/* ============ Prompt */
2686
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +00002687static smallint doprompt; /* if set, prompt the user */
2688static smallint needprompt; /* true if interactive and at start of line */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002689
2690#if ENABLE_FEATURE_EDITING
2691static line_input_t *line_input_state;
2692static const char *cmdedit_prompt;
2693static void
2694putprompt(const char *s)
2695{
2696 if (ENABLE_ASH_EXPAND_PRMT) {
2697 free((char*)cmdedit_prompt);
Denis Vlasenko4222ae42007-02-25 02:37:49 +00002698 cmdedit_prompt = ckstrdup(s);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002699 return;
2700 }
2701 cmdedit_prompt = s;
2702}
2703#else
2704static void
2705putprompt(const char *s)
2706{
2707 out2str(s);
2708}
2709#endif
2710
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002711/* expandstr() needs parsing machinery, so it is far away ahead... */
Denys Vlasenko46999802017-07-29 21:12:29 +02002712static const char *expandstr(const char *ps, int syntax_type);
2713/* Values for syntax param */
2714#define BASESYNTAX 0 /* not in quotes */
2715#define DQSYNTAX 1 /* in double quotes */
2716#define SQSYNTAX 2 /* in single quotes */
2717#define ARISYNTAX 3 /* in arithmetic */
Denys Vlasenko5f0a75f2017-07-29 22:58:44 +02002718#if ENABLE_ASH_EXPAND_PRMT
2719# define PSSYNTAX 4 /* prompt. never passed to SIT() */
2720#endif
Denys Vlasenko46999802017-07-29 21:12:29 +02002721/* PSSYNTAX expansion is identical to DQSYNTAX, except keeping '\$' as '\$' */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002722
Denys Vlasenko46999802017-07-29 21:12:29 +02002723/*
2724 * called by editline -- any expansions to the prompt should be added here.
2725 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002726static void
Denys Vlasenko958581a2010-09-12 15:04:27 +02002727setprompt_if(smallint do_set, int whichprompt)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002728{
2729 const char *prompt;
Denys Vlasenko958581a2010-09-12 15:04:27 +02002730 IF_ASH_EXPAND_PRMT(struct stackmark smark;)
2731
2732 if (!do_set)
2733 return;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002734
2735 needprompt = 0;
2736
2737 switch (whichprompt) {
2738 case 1:
2739 prompt = ps1val();
2740 break;
2741 case 2:
2742 prompt = ps2val();
2743 break;
2744 default: /* 0 */
2745 prompt = nullstr;
2746 }
2747#if ENABLE_ASH_EXPAND_PRMT
Denys Vlasenko60ca8342016-09-30 11:21:21 +02002748 pushstackmark(&smark, stackblocksize());
Denys Vlasenko46999802017-07-29 21:12:29 +02002749 putprompt(expandstr(prompt, PSSYNTAX));
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002750 popstackmark(&smark);
Denys Vlasenko48c803a2017-07-01 23:24:48 +02002751#else
2752 putprompt(prompt);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002753#endif
2754}
2755
2756
2757/* ============ The cd and pwd commands */
2758
2759#define CD_PHYSICAL 1
2760#define CD_PRINT 2
2761
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002762static int
2763cdopt(void)
2764{
2765 int flags = 0;
2766 int i, j;
2767
2768 j = 'L';
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02002769 while ((i = nextopt("LP")) != '\0') {
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002770 if (i != j) {
2771 flags ^= CD_PHYSICAL;
2772 j = i;
2773 }
2774 }
2775
2776 return flags;
2777}
2778
2779/*
2780 * Update curdir (the name of the current directory) in response to a
2781 * cd command.
2782 */
2783static const char *
2784updatepwd(const char *dir)
2785{
2786 char *new;
2787 char *p;
2788 char *cdcomppath;
2789 const char *lim;
2790
Denys Vlasenko8e2bc472016-09-28 23:02:57 +02002791 cdcomppath = sstrdup(dir);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002792 STARTSTACKSTR(new);
2793 if (*dir != '/') {
2794 if (curdir == nullstr)
2795 return 0;
2796 new = stack_putstr(curdir, new);
2797 }
2798 new = makestrspace(strlen(dir) + 2, new);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00002799 lim = (char *)stackblock() + 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002800 if (*dir != '/') {
2801 if (new[-1] != '/')
2802 USTPUTC('/', new);
2803 if (new > lim && *lim == '/')
2804 lim++;
2805 } else {
2806 USTPUTC('/', new);
2807 cdcomppath++;
2808 if (dir[1] == '/' && dir[2] != '/') {
2809 USTPUTC('/', new);
2810 cdcomppath++;
2811 lim++;
2812 }
2813 }
Denys Vlasenko24966162020-10-06 02:36:47 +02002814 p = strtok_r(cdcomppath, "/", &cdcomppath);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002815 while (p) {
2816 switch (*p) {
2817 case '.':
2818 if (p[1] == '.' && p[2] == '\0') {
2819 while (new > lim) {
2820 STUNPUTC(new);
2821 if (new[-1] == '/')
2822 break;
2823 }
2824 break;
Denis Vlasenko16abcd92007-04-13 23:59:52 +00002825 }
2826 if (p[1] == '\0')
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002827 break;
2828 /* fall through */
2829 default:
2830 new = stack_putstr(p, new);
2831 USTPUTC('/', new);
2832 }
Denys Vlasenko24966162020-10-06 02:36:47 +02002833 p = strtok_r(NULL, "/", &cdcomppath);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002834 }
2835 if (new > lim)
2836 STUNPUTC(new);
2837 *new = 0;
2838 return stackblock();
2839}
2840
2841/*
2842 * Find out what the current directory is. If we already know the current
2843 * directory, this routine returns immediately.
2844 */
2845static char *
2846getpwd(void)
2847{
Denis Vlasenko01631112007-12-16 17:20:38 +00002848 char *dir = getcwd(NULL, 0); /* huh, using glibc extension? */
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002849 return dir ? dir : nullstr;
2850}
2851
2852static void
2853setpwd(const char *val, int setold)
2854{
2855 char *oldcur, *dir;
2856
2857 oldcur = dir = curdir;
2858
2859 if (setold) {
2860 setvar("OLDPWD", oldcur, VEXPORT);
2861 }
2862 INT_OFF;
2863 if (physdir != nullstr) {
2864 if (physdir != oldcur)
2865 free(physdir);
2866 physdir = nullstr;
2867 }
2868 if (oldcur == val || !val) {
2869 char *s = getpwd();
2870 physdir = s;
2871 if (!val)
2872 dir = s;
2873 } else
2874 dir = ckstrdup(val);
2875 if (oldcur != dir && oldcur != nullstr) {
2876 free(oldcur);
2877 }
2878 curdir = dir;
2879 INT_ON;
2880 setvar("PWD", dir, VEXPORT);
2881}
2882
2883static void hashcd(void);
2884
2885/*
Denys Vlasenko70392332016-10-27 02:31:55 +02002886 * Actually do the chdir. We also call hashcd to let other routines
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002887 * know that the current directory has changed.
2888 */
2889static int
2890docd(const char *dest, int flags)
2891{
Denys Vlasenko4b1100e2010-03-05 14:10:54 +01002892 const char *dir = NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002893 int err;
2894
2895 TRACE(("docd(\"%s\", %d) called\n", dest, flags));
2896
2897 INT_OFF;
2898 if (!(flags & CD_PHYSICAL)) {
2899 dir = updatepwd(dest);
2900 if (dir)
2901 dest = dir;
2902 }
2903 err = chdir(dest);
2904 if (err)
2905 goto out;
2906 setpwd(dir, 1);
2907 hashcd();
2908 out:
2909 INT_ON;
2910 return err;
2911}
2912
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02002913static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00002914cdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002915{
2916 const char *dest;
2917 const char *path;
2918 const char *p;
2919 char c;
2920 struct stat statb;
2921 int flags;
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +01002922 int len;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002923
2924 flags = cdopt();
2925 dest = *argptr;
2926 if (!dest)
Denys Vlasenkoea8b2522010-06-02 12:57:26 +02002927 dest = bltinlookup("HOME");
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002928 else if (LONE_DASH(dest)) {
2929 dest = bltinlookup("OLDPWD");
2930 flags |= CD_PRINT;
2931 }
2932 if (!dest)
2933 dest = nullstr;
2934 if (*dest == '/')
Denys Vlasenko0e081d02016-10-26 19:56:05 +02002935 goto step6;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002936 if (*dest == '.') {
2937 c = dest[1];
2938 dotdot:
2939 switch (c) {
2940 case '\0':
2941 case '/':
2942 goto step6;
2943 case '.':
2944 c = dest[2];
2945 if (c != '.')
2946 goto dotdot;
2947 }
2948 }
2949 if (!*dest)
2950 dest = ".";
2951 path = bltinlookup("CDPATH");
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +01002952 while (p = path, (len = padvance(&path, dest)) >= 0) {
2953 c = *p;
2954 p = stalloc(len);
2955
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002956 if (stat(p, &statb) >= 0 && S_ISDIR(statb.st_mode)) {
2957 if (c && c != ':')
2958 flags |= CD_PRINT;
2959 docd:
2960 if (!docd(p, flags))
2961 goto out;
Denys Vlasenko0e081d02016-10-26 19:56:05 +02002962 goto err;
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002963 }
Denys Vlasenko0e081d02016-10-26 19:56:05 +02002964 }
2965
2966 step6:
2967 p = dest;
2968 goto docd;
2969
2970 err:
Johannes Schindelin687aac02017-08-22 22:03:22 +02002971 ash_msg_and_raise_perror("can't cd to %s", dest);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002972 /* NOTREACHED */
2973 out:
2974 if (flags & CD_PRINT)
Denys Vlasenkoea8b2522010-06-02 12:57:26 +02002975 out1fmt("%s\n", curdir);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002976 return 0;
2977}
2978
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02002979static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00002980pwdcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002981{
2982 int flags;
2983 const char *dir = curdir;
2984
2985 flags = cdopt();
2986 if (flags) {
2987 if (physdir == nullstr)
2988 setpwd(dir, 0);
2989 dir = physdir;
2990 }
Denys Vlasenkoea8b2522010-06-02 12:57:26 +02002991 out1fmt("%s\n", dir);
Denis Vlasenkoaa744452007-02-23 01:04:22 +00002992 return 0;
2993}
2994
Denis Vlasenko0c032a42007-02-23 01:03:40 +00002995
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00002996/* ============ ... */
Eric Andersenc470f442003-07-28 09:56:35 +00002997
Denis Vlasenko834dee72008-10-07 09:18:30 +00002998
Denys Vlasenko82dd14a2010-05-17 10:10:01 +02002999#define IBUFSIZ (ENABLE_FEATURE_EDITING ? CONFIG_FEATURE_EDITING_MAX_LEN : 1024)
Eric Andersenc470f442003-07-28 09:56:35 +00003000
Eric Andersenc470f442003-07-28 09:56:35 +00003001/* Syntax classes */
Denis Vlasenko834dee72008-10-07 09:18:30 +00003002#define CWORD 0 /* character is nothing special */
3003#define CNL 1 /* newline character */
3004#define CBACK 2 /* a backslash character */
3005#define CSQUOTE 3 /* single quote */
3006#define CDQUOTE 4 /* double quote */
Eric Andersenc470f442003-07-28 09:56:35 +00003007#define CENDQUOTE 5 /* a terminating quote */
Denis Vlasenko834dee72008-10-07 09:18:30 +00003008#define CBQUOTE 6 /* backwards single quote */
3009#define CVAR 7 /* a dollar sign */
3010#define CENDVAR 8 /* a '}' character */
3011#define CLP 9 /* a left paren in arithmetic */
3012#define CRP 10 /* a right paren in arithmetic */
Eric Andersenc470f442003-07-28 09:56:35 +00003013#define CENDFILE 11 /* end of file */
Denis Vlasenko834dee72008-10-07 09:18:30 +00003014#define CCTL 12 /* like CWORD, except it must be escaped */
3015#define CSPCL 13 /* these terminate a word */
3016#define CIGN 14 /* character should be ignored */
Eric Andersenc470f442003-07-28 09:56:35 +00003017
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01003018#define PEOF 256
Denis Vlasenko131ae172007-02-18 13:00:19 +00003019#if ENABLE_ASH_ALIAS
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01003020# define PEOA 257
Eric Andersenc470f442003-07-28 09:56:35 +00003021#endif
3022
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01003023#define USE_SIT_FUNCTION ENABLE_ASH_OPTIMIZE_FOR_SIZE
Manuel Novoa III 16815d42001-08-10 19:36:07 +00003024
Denys Vlasenko0b883582016-12-23 16:49:07 +01003025#if ENABLE_FEATURE_SH_MATH
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01003026# define SIT_ITEM(a,b,c,d) (a | (b << 4) | (c << 8) | (d << 12))
Eric Andersenc470f442003-07-28 09:56:35 +00003027#else
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01003028# define SIT_ITEM(a,b,c,d) (a | (b << 4) | (c << 8))
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01003029#endif
Denys Vlasenko3e134eb2016-04-22 18:09:21 +02003030static const uint16_t S_I_T[] ALIGN2 = {
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01003031#if ENABLE_ASH_ALIAS
3032 SIT_ITEM(CSPCL , CIGN , CIGN , CIGN ), /* 0, PEOA */
3033#endif
3034 SIT_ITEM(CSPCL , CWORD , CWORD, CWORD ), /* 1, ' ' */
3035 SIT_ITEM(CNL , CNL , CNL , CNL ), /* 2, \n */
3036 SIT_ITEM(CWORD , CCTL , CCTL , CWORD ), /* 3, !*-/:=?[]~ */
3037 SIT_ITEM(CDQUOTE , CENDQUOTE, CWORD, CWORD ), /* 4, '"' */
3038 SIT_ITEM(CVAR , CVAR , CWORD, CVAR ), /* 5, $ */
3039 SIT_ITEM(CSQUOTE , CWORD , CENDQUOTE, CWORD), /* 6, "'" */
3040 SIT_ITEM(CSPCL , CWORD , CWORD, CLP ), /* 7, ( */
3041 SIT_ITEM(CSPCL , CWORD , CWORD, CRP ), /* 8, ) */
3042 SIT_ITEM(CBACK , CBACK , CCTL , CBACK ), /* 9, \ */
3043 SIT_ITEM(CBQUOTE , CBQUOTE , CWORD, CBQUOTE), /* 10, ` */
3044 SIT_ITEM(CENDVAR , CENDVAR , CWORD, CENDVAR), /* 11, } */
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01003045#if !USE_SIT_FUNCTION
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01003046 SIT_ITEM(CENDFILE, CENDFILE , CENDFILE, CENDFILE),/* 12, PEOF */
3047 SIT_ITEM(CWORD , CWORD , CWORD, CWORD ), /* 13, 0-9A-Za-z */
3048 SIT_ITEM(CCTL , CCTL , CCTL , CCTL ) /* 14, CTLESC ... */
3049#endif
3050#undef SIT_ITEM
Eric Andersenc470f442003-07-28 09:56:35 +00003051};
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01003052/* Constants below must match table above */
3053enum {
3054#if ENABLE_ASH_ALIAS
3055 CSPCL_CIGN_CIGN_CIGN , /* 0 */
3056#endif
3057 CSPCL_CWORD_CWORD_CWORD , /* 1 */
3058 CNL_CNL_CNL_CNL , /* 2 */
3059 CWORD_CCTL_CCTL_CWORD , /* 3 */
3060 CDQUOTE_CENDQUOTE_CWORD_CWORD , /* 4 */
3061 CVAR_CVAR_CWORD_CVAR , /* 5 */
3062 CSQUOTE_CWORD_CENDQUOTE_CWORD , /* 6 */
3063 CSPCL_CWORD_CWORD_CLP , /* 7 */
3064 CSPCL_CWORD_CWORD_CRP , /* 8 */
3065 CBACK_CBACK_CCTL_CBACK , /* 9 */
3066 CBQUOTE_CBQUOTE_CWORD_CBQUOTE , /* 10 */
3067 CENDVAR_CENDVAR_CWORD_CENDVAR , /* 11 */
3068 CENDFILE_CENDFILE_CENDFILE_CENDFILE, /* 12 */
3069 CWORD_CWORD_CWORD_CWORD , /* 13 */
3070 CCTL_CCTL_CCTL_CCTL , /* 14 */
3071};
Eric Andersen2870d962001-07-02 17:27:21 +00003072
Denys Vlasenkocd716832009-11-28 22:14:02 +01003073/* c in SIT(c, syntax) must be an *unsigned char* or PEOA or PEOF,
3074 * caller must ensure proper cast on it if c is *char_ptr!
3075 */
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01003076#if USE_SIT_FUNCTION
Manuel Novoa III 16815d42001-08-10 19:36:07 +00003077
Denis Vlasenko0c032a42007-02-23 01:03:40 +00003078static int
3079SIT(int c, int syntax)
Manuel Novoa III 16815d42001-08-10 19:36:07 +00003080{
Denys Vlasenkob3f29b42016-09-21 16:25:58 +02003081 /* Used to also have '/' in this string: "\t\n !\"$&'()*-/:;<=>?[\\]`|}~" */
3082 static const char spec_symbls[] ALIGN1 = "\t\n !\"$&'()*-:;<=>?[\\]`|}~";
3083 /*
3084 * This causes '/' to be prepended with CTLESC in dquoted string,
3085 * making "./file"* treated incorrectly because we feed
3086 * ".\/file*" string to glob(), confusing it (see expandmeta func).
3087 * The "homegrown" glob implementation is okay with that,
3088 * but glibc one isn't. With '/' always treated as CWORD,
3089 * both work fine.
3090 */
Denys Vlasenkocd716832009-11-28 22:14:02 +01003091# if ENABLE_ASH_ALIAS
3092 static const uint8_t syntax_index_table[] ALIGN1 = {
Eric Andersenc470f442003-07-28 09:56:35 +00003093 1, 2, 1, 3, 4, 5, 1, 6, /* "\t\n !\"$&'" */
Denys Vlasenkob3f29b42016-09-21 16:25:58 +02003094 7, 8, 3, 3,/*3,*/3, 1, 1, /* "()*-/:;<" */
Eric Andersenc470f442003-07-28 09:56:35 +00003095 3, 1, 3, 3, 9, 3, 10, 1, /* "=>?[\\]`|" */
3096 11, 3 /* "}~" */
3097 };
Denys Vlasenkocd716832009-11-28 22:14:02 +01003098# else
3099 static const uint8_t syntax_index_table[] ALIGN1 = {
Eric Andersenc470f442003-07-28 09:56:35 +00003100 0, 1, 0, 2, 3, 4, 0, 5, /* "\t\n !\"$&'" */
Denys Vlasenkob3f29b42016-09-21 16:25:58 +02003101 6, 7, 2, 2,/*2,*/2, 0, 0, /* "()*-/:;<" */
Eric Andersenc470f442003-07-28 09:56:35 +00003102 2, 0, 2, 2, 8, 2, 9, 0, /* "=>?[\\]`|" */
3103 10, 2 /* "}~" */
3104 };
Denys Vlasenkocd716832009-11-28 22:14:02 +01003105# endif
Manuel Novoa III 16815d42001-08-10 19:36:07 +00003106 const char *s;
3107 int indx;
3108
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01003109 if (c == PEOF)
Manuel Novoa III 16815d42001-08-10 19:36:07 +00003110 return CENDFILE;
Denys Vlasenkocd716832009-11-28 22:14:02 +01003111# if ENABLE_ASH_ALIAS
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01003112 if (c == PEOA)
Denis Vlasenko0dfe1d22009-04-02 12:57:38 +00003113 indx = 0;
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01003114 else
Denys Vlasenkocd716832009-11-28 22:14:02 +01003115# endif
Denis Vlasenko0dfe1d22009-04-02 12:57:38 +00003116 {
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01003117 /* Cast is purely for paranoia here,
3118 * just in case someone passed signed char to us */
3119 if ((unsigned char)c >= CTL_FIRST
3120 && (unsigned char)c <= CTL_LAST
Denis Vlasenko0dfe1d22009-04-02 12:57:38 +00003121 ) {
3122 return CCTL;
3123 }
3124 s = strchrnul(spec_symbls, c);
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01003125 if (*s == '\0')
Denis Vlasenko0dfe1d22009-04-02 12:57:38 +00003126 return CWORD;
Denis Vlasenko0dfe1d22009-04-02 12:57:38 +00003127 indx = syntax_index_table[s - spec_symbls];
3128 }
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01003129 return (S_I_T[indx] >> (syntax*4)) & 0xf;
Manuel Novoa III 16815d42001-08-10 19:36:07 +00003130}
3131
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00003132#else /* !USE_SIT_FUNCTION */
Manuel Novoa III 16815d42001-08-10 19:36:07 +00003133
Denys Vlasenko3e134eb2016-04-22 18:09:21 +02003134static const uint8_t syntax_index_table[] ALIGN1 = {
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003135 /* BASESYNTAX_DQSYNTAX_SQSYNTAX_ARISYNTAX */
Denys Vlasenkocd716832009-11-28 22:14:02 +01003136 /* 0 */ CWORD_CWORD_CWORD_CWORD,
3137 /* 1 */ CWORD_CWORD_CWORD_CWORD,
3138 /* 2 */ CWORD_CWORD_CWORD_CWORD,
3139 /* 3 */ CWORD_CWORD_CWORD_CWORD,
3140 /* 4 */ CWORD_CWORD_CWORD_CWORD,
3141 /* 5 */ CWORD_CWORD_CWORD_CWORD,
3142 /* 6 */ CWORD_CWORD_CWORD_CWORD,
3143 /* 7 */ CWORD_CWORD_CWORD_CWORD,
3144 /* 8 */ CWORD_CWORD_CWORD_CWORD,
3145 /* 9 "\t" */ CSPCL_CWORD_CWORD_CWORD,
3146 /* 10 "\n" */ CNL_CNL_CNL_CNL,
3147 /* 11 */ CWORD_CWORD_CWORD_CWORD,
3148 /* 12 */ CWORD_CWORD_CWORD_CWORD,
3149 /* 13 */ CWORD_CWORD_CWORD_CWORD,
3150 /* 14 */ CWORD_CWORD_CWORD_CWORD,
3151 /* 15 */ CWORD_CWORD_CWORD_CWORD,
3152 /* 16 */ CWORD_CWORD_CWORD_CWORD,
3153 /* 17 */ CWORD_CWORD_CWORD_CWORD,
3154 /* 18 */ CWORD_CWORD_CWORD_CWORD,
3155 /* 19 */ CWORD_CWORD_CWORD_CWORD,
3156 /* 20 */ CWORD_CWORD_CWORD_CWORD,
3157 /* 21 */ CWORD_CWORD_CWORD_CWORD,
3158 /* 22 */ CWORD_CWORD_CWORD_CWORD,
3159 /* 23 */ CWORD_CWORD_CWORD_CWORD,
3160 /* 24 */ CWORD_CWORD_CWORD_CWORD,
3161 /* 25 */ CWORD_CWORD_CWORD_CWORD,
3162 /* 26 */ CWORD_CWORD_CWORD_CWORD,
3163 /* 27 */ CWORD_CWORD_CWORD_CWORD,
3164 /* 28 */ CWORD_CWORD_CWORD_CWORD,
3165 /* 29 */ CWORD_CWORD_CWORD_CWORD,
3166 /* 30 */ CWORD_CWORD_CWORD_CWORD,
3167 /* 31 */ CWORD_CWORD_CWORD_CWORD,
3168 /* 32 " " */ CSPCL_CWORD_CWORD_CWORD,
3169 /* 33 "!" */ CWORD_CCTL_CCTL_CWORD,
3170 /* 34 """ */ CDQUOTE_CENDQUOTE_CWORD_CWORD,
3171 /* 35 "#" */ CWORD_CWORD_CWORD_CWORD,
3172 /* 36 "$" */ CVAR_CVAR_CWORD_CVAR,
3173 /* 37 "%" */ CWORD_CWORD_CWORD_CWORD,
3174 /* 38 "&" */ CSPCL_CWORD_CWORD_CWORD,
3175 /* 39 "'" */ CSQUOTE_CWORD_CENDQUOTE_CWORD,
3176 /* 40 "(" */ CSPCL_CWORD_CWORD_CLP,
3177 /* 41 ")" */ CSPCL_CWORD_CWORD_CRP,
3178 /* 42 "*" */ CWORD_CCTL_CCTL_CWORD,
3179 /* 43 "+" */ CWORD_CWORD_CWORD_CWORD,
3180 /* 44 "," */ CWORD_CWORD_CWORD_CWORD,
3181 /* 45 "-" */ CWORD_CCTL_CCTL_CWORD,
3182 /* 46 "." */ CWORD_CWORD_CWORD_CWORD,
Denys Vlasenkob3f29b42016-09-21 16:25:58 +02003183/* "/" was CWORD_CCTL_CCTL_CWORD, see comment in SIT() function why this is changed: */
3184 /* 47 "/" */ CWORD_CWORD_CWORD_CWORD,
Denys Vlasenkocd716832009-11-28 22:14:02 +01003185 /* 48 "0" */ CWORD_CWORD_CWORD_CWORD,
3186 /* 49 "1" */ CWORD_CWORD_CWORD_CWORD,
3187 /* 50 "2" */ CWORD_CWORD_CWORD_CWORD,
3188 /* 51 "3" */ CWORD_CWORD_CWORD_CWORD,
3189 /* 52 "4" */ CWORD_CWORD_CWORD_CWORD,
3190 /* 53 "5" */ CWORD_CWORD_CWORD_CWORD,
3191 /* 54 "6" */ CWORD_CWORD_CWORD_CWORD,
3192 /* 55 "7" */ CWORD_CWORD_CWORD_CWORD,
3193 /* 56 "8" */ CWORD_CWORD_CWORD_CWORD,
3194 /* 57 "9" */ CWORD_CWORD_CWORD_CWORD,
3195 /* 58 ":" */ CWORD_CCTL_CCTL_CWORD,
3196 /* 59 ";" */ CSPCL_CWORD_CWORD_CWORD,
3197 /* 60 "<" */ CSPCL_CWORD_CWORD_CWORD,
3198 /* 61 "=" */ CWORD_CCTL_CCTL_CWORD,
3199 /* 62 ">" */ CSPCL_CWORD_CWORD_CWORD,
3200 /* 63 "?" */ CWORD_CCTL_CCTL_CWORD,
3201 /* 64 "@" */ CWORD_CWORD_CWORD_CWORD,
3202 /* 65 "A" */ CWORD_CWORD_CWORD_CWORD,
3203 /* 66 "B" */ CWORD_CWORD_CWORD_CWORD,
3204 /* 67 "C" */ CWORD_CWORD_CWORD_CWORD,
3205 /* 68 "D" */ CWORD_CWORD_CWORD_CWORD,
3206 /* 69 "E" */ CWORD_CWORD_CWORD_CWORD,
3207 /* 70 "F" */ CWORD_CWORD_CWORD_CWORD,
3208 /* 71 "G" */ CWORD_CWORD_CWORD_CWORD,
3209 /* 72 "H" */ CWORD_CWORD_CWORD_CWORD,
3210 /* 73 "I" */ CWORD_CWORD_CWORD_CWORD,
3211 /* 74 "J" */ CWORD_CWORD_CWORD_CWORD,
3212 /* 75 "K" */ CWORD_CWORD_CWORD_CWORD,
3213 /* 76 "L" */ CWORD_CWORD_CWORD_CWORD,
3214 /* 77 "M" */ CWORD_CWORD_CWORD_CWORD,
3215 /* 78 "N" */ CWORD_CWORD_CWORD_CWORD,
3216 /* 79 "O" */ CWORD_CWORD_CWORD_CWORD,
3217 /* 80 "P" */ CWORD_CWORD_CWORD_CWORD,
3218 /* 81 "Q" */ CWORD_CWORD_CWORD_CWORD,
3219 /* 82 "R" */ CWORD_CWORD_CWORD_CWORD,
3220 /* 83 "S" */ CWORD_CWORD_CWORD_CWORD,
3221 /* 84 "T" */ CWORD_CWORD_CWORD_CWORD,
3222 /* 85 "U" */ CWORD_CWORD_CWORD_CWORD,
3223 /* 86 "V" */ CWORD_CWORD_CWORD_CWORD,
3224 /* 87 "W" */ CWORD_CWORD_CWORD_CWORD,
3225 /* 88 "X" */ CWORD_CWORD_CWORD_CWORD,
3226 /* 89 "Y" */ CWORD_CWORD_CWORD_CWORD,
3227 /* 90 "Z" */ CWORD_CWORD_CWORD_CWORD,
3228 /* 91 "[" */ CWORD_CCTL_CCTL_CWORD,
3229 /* 92 "\" */ CBACK_CBACK_CCTL_CBACK,
3230 /* 93 "]" */ CWORD_CCTL_CCTL_CWORD,
3231 /* 94 "^" */ CWORD_CWORD_CWORD_CWORD,
3232 /* 95 "_" */ CWORD_CWORD_CWORD_CWORD,
3233 /* 96 "`" */ CBQUOTE_CBQUOTE_CWORD_CBQUOTE,
3234 /* 97 "a" */ CWORD_CWORD_CWORD_CWORD,
3235 /* 98 "b" */ CWORD_CWORD_CWORD_CWORD,
3236 /* 99 "c" */ CWORD_CWORD_CWORD_CWORD,
3237 /* 100 "d" */ CWORD_CWORD_CWORD_CWORD,
3238 /* 101 "e" */ CWORD_CWORD_CWORD_CWORD,
3239 /* 102 "f" */ CWORD_CWORD_CWORD_CWORD,
3240 /* 103 "g" */ CWORD_CWORD_CWORD_CWORD,
3241 /* 104 "h" */ CWORD_CWORD_CWORD_CWORD,
3242 /* 105 "i" */ CWORD_CWORD_CWORD_CWORD,
3243 /* 106 "j" */ CWORD_CWORD_CWORD_CWORD,
3244 /* 107 "k" */ CWORD_CWORD_CWORD_CWORD,
3245 /* 108 "l" */ CWORD_CWORD_CWORD_CWORD,
3246 /* 109 "m" */ CWORD_CWORD_CWORD_CWORD,
3247 /* 110 "n" */ CWORD_CWORD_CWORD_CWORD,
3248 /* 111 "o" */ CWORD_CWORD_CWORD_CWORD,
3249 /* 112 "p" */ CWORD_CWORD_CWORD_CWORD,
3250 /* 113 "q" */ CWORD_CWORD_CWORD_CWORD,
3251 /* 114 "r" */ CWORD_CWORD_CWORD_CWORD,
3252 /* 115 "s" */ CWORD_CWORD_CWORD_CWORD,
3253 /* 116 "t" */ CWORD_CWORD_CWORD_CWORD,
3254 /* 117 "u" */ CWORD_CWORD_CWORD_CWORD,
3255 /* 118 "v" */ CWORD_CWORD_CWORD_CWORD,
3256 /* 119 "w" */ CWORD_CWORD_CWORD_CWORD,
3257 /* 120 "x" */ CWORD_CWORD_CWORD_CWORD,
3258 /* 121 "y" */ CWORD_CWORD_CWORD_CWORD,
3259 /* 122 "z" */ CWORD_CWORD_CWORD_CWORD,
3260 /* 123 "{" */ CWORD_CWORD_CWORD_CWORD,
3261 /* 124 "|" */ CSPCL_CWORD_CWORD_CWORD,
3262 /* 125 "}" */ CENDVAR_CENDVAR_CWORD_CENDVAR,
3263 /* 126 "~" */ CWORD_CCTL_CCTL_CWORD,
3264 /* 127 del */ CWORD_CWORD_CWORD_CWORD,
3265 /* 128 0x80 */ CWORD_CWORD_CWORD_CWORD,
3266 /* 129 CTLESC */ CCTL_CCTL_CCTL_CCTL,
3267 /* 130 CTLVAR */ CCTL_CCTL_CCTL_CCTL,
3268 /* 131 CTLENDVAR */ CCTL_CCTL_CCTL_CCTL,
3269 /* 132 CTLBACKQ */ CCTL_CCTL_CCTL_CCTL,
3270 /* 133 CTLQUOTE */ CCTL_CCTL_CCTL_CCTL,
3271 /* 134 CTLARI */ CCTL_CCTL_CCTL_CCTL,
3272 /* 135 CTLENDARI */ CCTL_CCTL_CCTL_CCTL,
3273 /* 136 CTLQUOTEMARK */ CCTL_CCTL_CCTL_CCTL,
Ron Yorstona1b0d382020-07-23 08:32:27 +01003274#if BASH_PROCESS_SUBST
3275 /* 137 CTLTOPROC */ CCTL_CCTL_CCTL_CCTL,
3276 /* 138 CTLFROMPROC */ CCTL_CCTL_CCTL_CCTL,
3277#else
Denys Vlasenkocd716832009-11-28 22:14:02 +01003278 /* 137 */ CWORD_CWORD_CWORD_CWORD,
3279 /* 138 */ CWORD_CWORD_CWORD_CWORD,
Ron Yorstona1b0d382020-07-23 08:32:27 +01003280#endif
Denys Vlasenkocd716832009-11-28 22:14:02 +01003281 /* 139 */ CWORD_CWORD_CWORD_CWORD,
3282 /* 140 */ CWORD_CWORD_CWORD_CWORD,
3283 /* 141 */ CWORD_CWORD_CWORD_CWORD,
3284 /* 142 */ CWORD_CWORD_CWORD_CWORD,
3285 /* 143 */ CWORD_CWORD_CWORD_CWORD,
3286 /* 144 */ CWORD_CWORD_CWORD_CWORD,
3287 /* 145 */ CWORD_CWORD_CWORD_CWORD,
3288 /* 146 */ CWORD_CWORD_CWORD_CWORD,
3289 /* 147 */ CWORD_CWORD_CWORD_CWORD,
3290 /* 148 */ CWORD_CWORD_CWORD_CWORD,
3291 /* 149 */ CWORD_CWORD_CWORD_CWORD,
3292 /* 150 */ CWORD_CWORD_CWORD_CWORD,
3293 /* 151 */ CWORD_CWORD_CWORD_CWORD,
3294 /* 152 */ CWORD_CWORD_CWORD_CWORD,
3295 /* 153 */ CWORD_CWORD_CWORD_CWORD,
3296 /* 154 */ CWORD_CWORD_CWORD_CWORD,
3297 /* 155 */ CWORD_CWORD_CWORD_CWORD,
3298 /* 156 */ CWORD_CWORD_CWORD_CWORD,
3299 /* 157 */ CWORD_CWORD_CWORD_CWORD,
3300 /* 158 */ CWORD_CWORD_CWORD_CWORD,
3301 /* 159 */ CWORD_CWORD_CWORD_CWORD,
3302 /* 160 */ CWORD_CWORD_CWORD_CWORD,
3303 /* 161 */ CWORD_CWORD_CWORD_CWORD,
3304 /* 162 */ CWORD_CWORD_CWORD_CWORD,
3305 /* 163 */ CWORD_CWORD_CWORD_CWORD,
3306 /* 164 */ CWORD_CWORD_CWORD_CWORD,
3307 /* 165 */ CWORD_CWORD_CWORD_CWORD,
3308 /* 166 */ CWORD_CWORD_CWORD_CWORD,
3309 /* 167 */ CWORD_CWORD_CWORD_CWORD,
3310 /* 168 */ CWORD_CWORD_CWORD_CWORD,
3311 /* 169 */ CWORD_CWORD_CWORD_CWORD,
3312 /* 170 */ CWORD_CWORD_CWORD_CWORD,
3313 /* 171 */ CWORD_CWORD_CWORD_CWORD,
3314 /* 172 */ CWORD_CWORD_CWORD_CWORD,
3315 /* 173 */ CWORD_CWORD_CWORD_CWORD,
3316 /* 174 */ CWORD_CWORD_CWORD_CWORD,
3317 /* 175 */ CWORD_CWORD_CWORD_CWORD,
3318 /* 176 */ CWORD_CWORD_CWORD_CWORD,
3319 /* 177 */ CWORD_CWORD_CWORD_CWORD,
3320 /* 178 */ CWORD_CWORD_CWORD_CWORD,
3321 /* 179 */ CWORD_CWORD_CWORD_CWORD,
3322 /* 180 */ CWORD_CWORD_CWORD_CWORD,
3323 /* 181 */ CWORD_CWORD_CWORD_CWORD,
3324 /* 182 */ CWORD_CWORD_CWORD_CWORD,
3325 /* 183 */ CWORD_CWORD_CWORD_CWORD,
3326 /* 184 */ CWORD_CWORD_CWORD_CWORD,
3327 /* 185 */ CWORD_CWORD_CWORD_CWORD,
3328 /* 186 */ CWORD_CWORD_CWORD_CWORD,
3329 /* 187 */ CWORD_CWORD_CWORD_CWORD,
3330 /* 188 */ CWORD_CWORD_CWORD_CWORD,
3331 /* 189 */ CWORD_CWORD_CWORD_CWORD,
3332 /* 190 */ CWORD_CWORD_CWORD_CWORD,
3333 /* 191 */ CWORD_CWORD_CWORD_CWORD,
3334 /* 192 */ CWORD_CWORD_CWORD_CWORD,
3335 /* 193 */ CWORD_CWORD_CWORD_CWORD,
3336 /* 194 */ CWORD_CWORD_CWORD_CWORD,
3337 /* 195 */ CWORD_CWORD_CWORD_CWORD,
3338 /* 196 */ CWORD_CWORD_CWORD_CWORD,
3339 /* 197 */ CWORD_CWORD_CWORD_CWORD,
3340 /* 198 */ CWORD_CWORD_CWORD_CWORD,
3341 /* 199 */ CWORD_CWORD_CWORD_CWORD,
3342 /* 200 */ CWORD_CWORD_CWORD_CWORD,
3343 /* 201 */ CWORD_CWORD_CWORD_CWORD,
3344 /* 202 */ CWORD_CWORD_CWORD_CWORD,
3345 /* 203 */ CWORD_CWORD_CWORD_CWORD,
3346 /* 204 */ CWORD_CWORD_CWORD_CWORD,
3347 /* 205 */ CWORD_CWORD_CWORD_CWORD,
3348 /* 206 */ CWORD_CWORD_CWORD_CWORD,
3349 /* 207 */ CWORD_CWORD_CWORD_CWORD,
3350 /* 208 */ CWORD_CWORD_CWORD_CWORD,
3351 /* 209 */ CWORD_CWORD_CWORD_CWORD,
3352 /* 210 */ CWORD_CWORD_CWORD_CWORD,
3353 /* 211 */ CWORD_CWORD_CWORD_CWORD,
3354 /* 212 */ CWORD_CWORD_CWORD_CWORD,
3355 /* 213 */ CWORD_CWORD_CWORD_CWORD,
3356 /* 214 */ CWORD_CWORD_CWORD_CWORD,
3357 /* 215 */ CWORD_CWORD_CWORD_CWORD,
3358 /* 216 */ CWORD_CWORD_CWORD_CWORD,
3359 /* 217 */ CWORD_CWORD_CWORD_CWORD,
3360 /* 218 */ CWORD_CWORD_CWORD_CWORD,
3361 /* 219 */ CWORD_CWORD_CWORD_CWORD,
3362 /* 220 */ CWORD_CWORD_CWORD_CWORD,
3363 /* 221 */ CWORD_CWORD_CWORD_CWORD,
3364 /* 222 */ CWORD_CWORD_CWORD_CWORD,
3365 /* 223 */ CWORD_CWORD_CWORD_CWORD,
3366 /* 224 */ CWORD_CWORD_CWORD_CWORD,
3367 /* 225 */ CWORD_CWORD_CWORD_CWORD,
3368 /* 226 */ CWORD_CWORD_CWORD_CWORD,
3369 /* 227 */ CWORD_CWORD_CWORD_CWORD,
3370 /* 228 */ CWORD_CWORD_CWORD_CWORD,
3371 /* 229 */ CWORD_CWORD_CWORD_CWORD,
3372 /* 230 */ CWORD_CWORD_CWORD_CWORD,
3373 /* 231 */ CWORD_CWORD_CWORD_CWORD,
3374 /* 232 */ CWORD_CWORD_CWORD_CWORD,
3375 /* 233 */ CWORD_CWORD_CWORD_CWORD,
3376 /* 234 */ CWORD_CWORD_CWORD_CWORD,
3377 /* 235 */ CWORD_CWORD_CWORD_CWORD,
3378 /* 236 */ CWORD_CWORD_CWORD_CWORD,
3379 /* 237 */ CWORD_CWORD_CWORD_CWORD,
3380 /* 238 */ CWORD_CWORD_CWORD_CWORD,
3381 /* 239 */ CWORD_CWORD_CWORD_CWORD,
3382 /* 230 */ CWORD_CWORD_CWORD_CWORD,
3383 /* 241 */ CWORD_CWORD_CWORD_CWORD,
3384 /* 242 */ CWORD_CWORD_CWORD_CWORD,
3385 /* 243 */ CWORD_CWORD_CWORD_CWORD,
3386 /* 244 */ CWORD_CWORD_CWORD_CWORD,
3387 /* 245 */ CWORD_CWORD_CWORD_CWORD,
3388 /* 246 */ CWORD_CWORD_CWORD_CWORD,
3389 /* 247 */ CWORD_CWORD_CWORD_CWORD,
3390 /* 248 */ CWORD_CWORD_CWORD_CWORD,
3391 /* 249 */ CWORD_CWORD_CWORD_CWORD,
3392 /* 250 */ CWORD_CWORD_CWORD_CWORD,
3393 /* 251 */ CWORD_CWORD_CWORD_CWORD,
3394 /* 252 */ CWORD_CWORD_CWORD_CWORD,
3395 /* 253 */ CWORD_CWORD_CWORD_CWORD,
3396 /* 254 */ CWORD_CWORD_CWORD_CWORD,
3397 /* 255 */ CWORD_CWORD_CWORD_CWORD,
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01003398 /* PEOF */ CENDFILE_CENDFILE_CENDFILE_CENDFILE,
Denys Vlasenkocd716832009-11-28 22:14:02 +01003399# if ENABLE_ASH_ALIAS
3400 /* PEOA */ CSPCL_CIGN_CIGN_CIGN,
3401# endif
Eric Andersen2870d962001-07-02 17:27:21 +00003402};
3403
Denys Vlasenko2fe66b12016-12-12 17:39:12 +01003404#if 1
Denys Vlasenko2ce42e92009-11-29 02:18:13 +01003405# define SIT(c, syntax) ((S_I_T[syntax_index_table[c]] >> ((syntax)*4)) & 0xf)
Denys Vlasenko2fe66b12016-12-12 17:39:12 +01003406#else /* debug version, caught one signed char bug */
3407# define SIT(c, syntax) \
3408 ({ \
3409 if ((c) < 0 || (c) > (PEOF + ENABLE_ASH_ALIAS)) \
3410 bb_error_msg_and_die("line:%d c:%d", __LINE__, (c)); \
Denys Vlasenko0b883582016-12-23 16:49:07 +01003411 if ((syntax) < 0 || (syntax) > (2 + ENABLE_FEATURE_SH_MATH)) \
Denys Vlasenko2fe66b12016-12-12 17:39:12 +01003412 bb_error_msg_and_die("line:%d c:%d", __LINE__, (c)); \
3413 ((S_I_T[syntax_index_table[c]] >> ((syntax)*4)) & 0xf); \
3414 })
3415#endif
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00003416
Denys Vlasenko76bc2d62009-11-29 01:37:46 +01003417#endif /* !USE_SIT_FUNCTION */
Eric Andersenc470f442003-07-28 09:56:35 +00003418
Eric Andersen2870d962001-07-02 17:27:21 +00003419
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003420/* ============ Alias handling */
Denis Vlasenkofc06f292007-02-23 21:09:35 +00003421
Denis Vlasenko131ae172007-02-18 13:00:19 +00003422#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003423
3424#define ALIASINUSE 1
3425#define ALIASDEAD 2
3426
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00003427struct alias {
3428 struct alias *next;
3429 char *name;
3430 char *val;
3431 int flag;
3432};
3433
Denis Vlasenko01631112007-12-16 17:20:38 +00003434
3435static struct alias **atab; // [ATABSIZE];
3436#define INIT_G_alias() do { \
3437 atab = xzalloc(ATABSIZE * sizeof(atab[0])); \
3438} while (0)
3439
Eric Andersen2870d962001-07-02 17:27:21 +00003440
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00003441static struct alias **
Denys Vlasenko37dc08b2016-10-02 04:38:07 +02003442__lookupalias(const char *name)
3443{
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00003444 unsigned int hashval;
3445 struct alias **app;
3446 const char *p;
3447 unsigned int ch;
3448
3449 p = name;
3450
3451 ch = (unsigned char)*p;
3452 hashval = ch << 4;
3453 while (ch) {
3454 hashval += ch;
3455 ch = (unsigned char)*++p;
3456 }
3457 app = &atab[hashval % ATABSIZE];
3458
3459 for (; *app; app = &(*app)->next) {
3460 if (strcmp(name, (*app)->name) == 0) {
3461 break;
3462 }
3463 }
3464
3465 return app;
3466}
3467
3468static struct alias *
3469lookupalias(const char *name, int check)
3470{
3471 struct alias *ap = *__lookupalias(name);
3472
3473 if (check && ap && (ap->flag & ALIASINUSE))
3474 return NULL;
3475 return ap;
3476}
3477
3478static struct alias *
3479freealias(struct alias *ap)
3480{
3481 struct alias *next;
3482
3483 if (ap->flag & ALIASINUSE) {
3484 ap->flag |= ALIASDEAD;
3485 return ap;
3486 }
3487
3488 next = ap->next;
3489 free(ap->name);
3490 free(ap->val);
3491 free(ap);
3492 return next;
3493}
Eric Andersencb57d552001-06-28 07:25:16 +00003494
Eric Andersenc470f442003-07-28 09:56:35 +00003495static void
3496setalias(const char *name, const char *val)
Eric Andersencb57d552001-06-28 07:25:16 +00003497{
3498 struct alias *ap, **app;
3499
3500 app = __lookupalias(name);
3501 ap = *app;
Denis Vlasenkob012b102007-02-19 22:43:01 +00003502 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003503 if (ap) {
3504 if (!(ap->flag & ALIASINUSE)) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00003505 free(ap->val);
Eric Andersencb57d552001-06-28 07:25:16 +00003506 }
Denis Vlasenko0c032a42007-02-23 01:03:40 +00003507 ap->val = ckstrdup(val);
Eric Andersencb57d552001-06-28 07:25:16 +00003508 ap->flag &= ~ALIASDEAD;
3509 } else {
3510 /* not found */
Denis Vlasenko597906c2008-02-20 16:38:54 +00003511 ap = ckzalloc(sizeof(struct alias));
Denis Vlasenko0c032a42007-02-23 01:03:40 +00003512 ap->name = ckstrdup(name);
3513 ap->val = ckstrdup(val);
Denis Vlasenko597906c2008-02-20 16:38:54 +00003514 /*ap->flag = 0; - ckzalloc did it */
3515 /*ap->next = NULL;*/
Eric Andersencb57d552001-06-28 07:25:16 +00003516 *app = ap;
3517 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00003518 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00003519}
3520
Eric Andersenc470f442003-07-28 09:56:35 +00003521static int
3522unalias(const char *name)
Eric Andersen2870d962001-07-02 17:27:21 +00003523{
Eric Andersencb57d552001-06-28 07:25:16 +00003524 struct alias **app;
3525
3526 app = __lookupalias(name);
3527
3528 if (*app) {
Denis Vlasenkob012b102007-02-19 22:43:01 +00003529 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003530 *app = freealias(*app);
Denis Vlasenkob012b102007-02-19 22:43:01 +00003531 INT_ON;
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003532 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003533 }
3534
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003535 return 1;
Eric Andersencb57d552001-06-28 07:25:16 +00003536}
3537
Eric Andersenc470f442003-07-28 09:56:35 +00003538static void
3539rmaliases(void)
Eric Andersen2870d962001-07-02 17:27:21 +00003540{
Eric Andersencb57d552001-06-28 07:25:16 +00003541 struct alias *ap, **app;
3542 int i;
3543
Denis Vlasenkob012b102007-02-19 22:43:01 +00003544 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +00003545 for (i = 0; i < ATABSIZE; i++) {
3546 app = &atab[i];
3547 for (ap = *app; ap; ap = *app) {
3548 *app = freealias(*app);
3549 if (ap == *app) {
3550 app = &ap->next;
3551 }
3552 }
3553 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00003554 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00003555}
3556
Denis Vlasenko4d2183b2007-02-23 01:05:38 +00003557static void
3558printalias(const struct alias *ap)
3559{
3560 out1fmt("%s=%s\n", ap->name, single_quote(ap->val));
3561}
3562
Eric Andersencb57d552001-06-28 07:25:16 +00003563/*
3564 * TODO - sort output
3565 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02003566static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00003567aliascmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +00003568{
3569 char *n, *v;
3570 int ret = 0;
3571 struct alias *ap;
3572
Denis Vlasenko68404f12008-03-17 09:00:54 +00003573 if (!argv[1]) {
Eric Andersencb57d552001-06-28 07:25:16 +00003574 int i;
3575
Denis Vlasenko68404f12008-03-17 09:00:54 +00003576 for (i = 0; i < ATABSIZE; i++) {
Eric Andersencb57d552001-06-28 07:25:16 +00003577 for (ap = atab[i]; ap; ap = ap->next) {
3578 printalias(ap);
3579 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00003580 }
Denis Vlasenko079f8af2006-11-27 16:49:31 +00003581 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003582 }
3583 while ((n = *++argv) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +00003584 v = strchr(n+1, '=');
3585 if (v == NULL) { /* n+1: funny ksh stuff */
3586 ap = *__lookupalias(n);
3587 if (ap == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +00003588 fprintf(stderr, "%s: %s not found\n", "alias", n);
Eric Andersencb57d552001-06-28 07:25:16 +00003589 ret = 1;
3590 } else
3591 printalias(ap);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00003592 } else {
Eric Andersencb57d552001-06-28 07:25:16 +00003593 *v++ = '\0';
3594 setalias(n, v);
3595 }
3596 }
3597
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003598 return ret;
Eric Andersencb57d552001-06-28 07:25:16 +00003599}
3600
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02003601static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00003602unaliascmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +00003603{
3604 int i;
3605
Denys Vlasenko6c149f42017-04-12 21:31:32 +02003606 while (nextopt("a") != '\0') {
3607 rmaliases();
3608 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +00003609 }
3610 for (i = 0; *argptr; argptr++) {
3611 if (unalias(*argptr)) {
Eric Andersenc470f442003-07-28 09:56:35 +00003612 fprintf(stderr, "%s: %s not found\n", "unalias", *argptr);
Eric Andersencb57d552001-06-28 07:25:16 +00003613 i = 1;
3614 }
3615 }
3616
Denis Vlasenkod9e15f22006-11-27 16:49:55 +00003617 return i;
Eric Andersencb57d552001-06-28 07:25:16 +00003618}
Denis Vlasenkofc06f292007-02-23 21:09:35 +00003619
Denis Vlasenko131ae172007-02-18 13:00:19 +00003620#endif /* ASH_ALIAS */
Eric Andersencb57d552001-06-28 07:25:16 +00003621
Eric Andersenc470f442003-07-28 09:56:35 +00003622
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003623/* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */
Denys Vlasenko285ad152009-12-04 23:02:27 +01003624#define FORK_FG 0
3625#define FORK_BG 1
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003626#define FORK_NOJOB 2
3627
3628/* mode flags for showjob(s) */
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02003629#define SHOW_ONLY_PGID 0x01 /* show only pgid (jobs -p) */
3630#define SHOW_PIDS 0x02 /* show individual pids, not just one line per job */
3631#define SHOW_CHANGED 0x04 /* only jobs whose state has changed */
Denys Vlasenko9c541002015-10-07 15:44:36 +02003632#define SHOW_STDERR 0x08 /* print to stderr (else stdout) */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003633
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003634/*
3635 * A job structure contains information about a job. A job is either a
3636 * single process or a set of processes contained in a pipeline. In the
3637 * latter case, pidlist will be non-NULL, and will point to a -1 terminated
3638 * array of pids.
3639 */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003640struct procstat {
Denys Vlasenko285ad152009-12-04 23:02:27 +01003641 pid_t ps_pid; /* process id */
3642 int ps_status; /* last process status from wait() */
3643 char *ps_cmd; /* text of command being run */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003644};
3645
3646struct job {
3647 struct procstat ps0; /* status of process */
Denys Vlasenko966f0872019-03-27 15:51:42 +01003648 struct procstat *ps; /* status of processes when more than one */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003649#if JOBS
3650 int stopstatus; /* status of a stopped job */
3651#endif
Denys Vlasenko4c179372017-01-11 18:44:15 +01003652 unsigned nprocs; /* number of processes */
3653
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003654#define JOBRUNNING 0 /* at least one proc running */
3655#define JOBSTOPPED 1 /* all procs are stopped */
3656#define JOBDONE 2 /* all procs are completed */
Denys Vlasenko4c179372017-01-11 18:44:15 +01003657 unsigned
3658 state: 8,
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003659#if JOBS
3660 sigint: 1, /* job was killed by SIGINT */
3661 jobctl: 1, /* job running under job control */
3662#endif
3663 waited: 1, /* true if this entry has been waited for */
3664 used: 1, /* true if this entry is in used */
3665 changed: 1; /* true if status has changed */
3666 struct job *prev_job; /* previous job */
3667};
3668
Denis Vlasenko68404f12008-03-17 09:00:54 +00003669static struct job *makejob(/*union node *,*/ int);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003670static int forkshell(struct job *, union node *, int);
3671static int waitforjob(struct job *);
3672
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003673#if !JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00003674enum { doing_jobctl = 0 };
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00003675#define setjobctl(on) do {} while (0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003676#else
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003677static smallint doing_jobctl; //references:8
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003678static void setjobctl(int);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003679#endif
3680
3681/*
Denis Vlasenko4b875702009-03-19 13:30:04 +00003682 * Ignore a signal.
3683 */
3684static void
3685ignoresig(int signo)
3686{
3687 /* Avoid unnecessary system calls. Is it already SIG_IGNed? */
3688 if (sigmode[signo - 1] != S_IGN && sigmode[signo - 1] != S_HARD_IGN) {
3689 /* No, need to do it */
3690 signal(signo, SIG_IGN);
3691 }
3692 sigmode[signo - 1] = S_HARD_IGN;
3693}
3694
3695/*
Denys Vlasenko238bf182010-05-18 15:49:07 +02003696 * Only one usage site - in setsignal()
Denis Vlasenko4b875702009-03-19 13:30:04 +00003697 */
3698static void
Denys Vlasenko238bf182010-05-18 15:49:07 +02003699signal_handler(int signo)
Denis Vlasenko4b875702009-03-19 13:30:04 +00003700{
Denys Vlasenko458c1f22016-10-27 23:51:19 +02003701 if (signo == SIGCHLD) {
3702 got_sigchld = 1;
3703 if (!trap[SIGCHLD])
3704 return;
3705 }
3706
Denis Vlasenko4b875702009-03-19 13:30:04 +00003707 gotsig[signo - 1] = 1;
Denys Vlasenko458c1f22016-10-27 23:51:19 +02003708 pending_sig = signo;
Denis Vlasenko4b875702009-03-19 13:30:04 +00003709
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02003710 if (signo == SIGINT && !trap[SIGINT]) {
3711 if (!suppress_int) {
3712 pending_sig = 0;
Denis Vlasenko4b875702009-03-19 13:30:04 +00003713 raise_interrupt(); /* does not return */
3714 }
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02003715 pending_int = 1;
Denis Vlasenko4b875702009-03-19 13:30:04 +00003716 }
3717}
3718
3719/*
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003720 * Set the signal handler for the specified signal. The routine figures
3721 * out what it should be set to.
3722 */
3723static void
3724setsignal(int signo)
3725{
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003726 char *t;
3727 char cur_act, new_act;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003728 struct sigaction act;
3729
3730 t = trap[signo];
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003731 new_act = S_DFL;
3732 if (t != NULL) { /* trap for this sig is set */
3733 new_act = S_CATCH;
3734 if (t[0] == '\0') /* trap is "": ignore this sig */
3735 new_act = S_IGN;
3736 }
3737
3738 if (rootshell && new_act == S_DFL) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003739 switch (signo) {
3740 case SIGINT:
3741 if (iflag || minusc || sflag == 0)
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003742 new_act = S_CATCH;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003743 break;
3744 case SIGQUIT:
3745#if DEBUG
3746 if (debug)
3747 break;
3748#endif
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003749 /* man bash:
3750 * "In all cases, bash ignores SIGQUIT. Non-builtin
3751 * commands run by bash have signal handlers
3752 * set to the values inherited by the shell
3753 * from its parent". */
3754 new_act = S_IGN;
3755 break;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003756 case SIGTERM:
3757 if (iflag)
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003758 new_act = S_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003759 break;
3760#if JOBS
3761 case SIGTSTP:
3762 case SIGTTOU:
3763 if (mflag)
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003764 new_act = S_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003765 break;
3766#endif
3767 }
3768 }
Denys Vlasenko49e6bf22017-08-04 14:28:16 +02003769 /* if !rootshell, we reset SIGQUIT to DFL,
3770 * whereas we have to restore it to what shell got on entry.
3771 * This is handled by the fact that if signal was IGNored on entry,
3772 * then cur_act is S_HARD_IGN and we never change its sigaction
3773 * (see code below).
3774 */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003775
Denys Vlasenko458c1f22016-10-27 23:51:19 +02003776 if (signo == SIGCHLD)
3777 new_act = S_CATCH;
3778
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003779 t = &sigmode[signo - 1];
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003780 cur_act = *t;
3781 if (cur_act == 0) {
3782 /* current setting is not yet known */
3783 if (sigaction(signo, NULL, &act)) {
3784 /* pretend it worked; maybe we should give a warning,
3785 * but other shells don't. We don't alter sigmode,
3786 * so we retry every time.
3787 * btw, in Linux it never fails. --vda */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003788 return;
3789 }
3790 if (act.sa_handler == SIG_IGN) {
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003791 cur_act = S_HARD_IGN;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003792 if (mflag
3793 && (signo == SIGTSTP || signo == SIGTTIN || signo == SIGTTOU)
3794 ) {
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003795 cur_act = S_IGN; /* don't hard ignore these */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003796 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003797 }
Denys Vlasenko0f14f412017-08-06 20:06:19 +02003798 if (act.sa_handler == SIG_DFL && new_act == S_DFL) {
3799 /* installing SIG_DFL over SIG_DFL is a no-op */
3800 /* saves one sigaction call in each "sh -c SCRIPT" invocation */
3801 *t = S_DFL;
3802 return;
3803 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003804 }
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003805 if (cur_act == S_HARD_IGN || cur_act == new_act)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003806 return;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003807
Denys Vlasenko49e6bf22017-08-04 14:28:16 +02003808 *t = new_act;
3809
Denis Vlasenko991a1da2008-02-10 19:02:53 +00003810 act.sa_handler = SIG_DFL;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00003811 switch (new_act) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003812 case S_CATCH:
Denys Vlasenko238bf182010-05-18 15:49:07 +02003813 act.sa_handler = signal_handler;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003814 break;
3815 case S_IGN:
3816 act.sa_handler = SIG_IGN;
3817 break;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003818 }
Ian Wienand89b3cba2011-04-16 20:05:14 +02003819 /* flags and mask matter only if !DFL and !IGN, but we do it
3820 * for all cases for more deterministic behavior:
3821 */
Denys Vlasenko49e6bf22017-08-04 14:28:16 +02003822 act.sa_flags = 0; //TODO: why not SA_RESTART?
Ian Wienand89b3cba2011-04-16 20:05:14 +02003823 sigfillset(&act.sa_mask);
3824
Denis Vlasenko8e2cfec2008-03-12 23:19:35 +00003825 sigaction_set(signo, &act);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003826}
3827
3828/* mode flags for set_curjob */
3829#define CUR_DELETE 2
3830#define CUR_RUNNING 1
3831#define CUR_STOPPED 0
3832
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003833#if JOBS
3834/* pgrp of shell on invocation */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003835static int initialpgrp; //references:2
3836static int ttyfd = -1; //5
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003837#endif
3838/* array of jobs */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003839static struct job *jobtab; //5
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003840/* size of array */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003841static unsigned njobs; //4
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003842/* current job */
Denis Vlasenko448d30e2008-06-27 00:24:11 +00003843static struct job *curjob; //lots
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003844
Denys Vlasenko098b7132017-01-11 19:59:03 +01003845#if 0
3846/* Bash has a feature: it restores termios after a successful wait for
3847 * a foreground job which had at least one stopped or sigkilled member.
3848 * The probable rationale is that SIGSTOP and SIGKILL can preclude task from
3849 * properly restoring tty state. Should we do this too?
3850 * A reproducer: ^Z an interactive python:
3851 *
3852 * # python
3853 * Python 2.7.12 (...)
3854 * >>> ^Z
3855 * { python leaves tty in -icanon -echo state. We do survive that... }
3856 * [1]+ Stopped python
3857 * { ...however, next program (python #2) does not survive it well: }
3858 * # python
3859 * Python 2.7.12 (...)
3860 * >>> Traceback (most recent call last):
3861 * { above, I typed "qwerty<CR>", but -echo state is still in effect }
3862 * File "<stdin>", line 1, in <module>
3863 * NameError: name 'qwerty' is not defined
3864 *
3865 * The implementation below is modeled on bash code and seems to work.
3866 * However, I'm not sure we should do this. For one: what if I'd fg
3867 * the stopped python instead? It'll be confused by "restored" tty state.
3868 */
3869static struct termios shell_tty_info;
3870static void
3871get_tty_state(void)
3872{
3873 if (rootshell && ttyfd >= 0)
3874 tcgetattr(ttyfd, &shell_tty_info);
3875}
3876static void
3877set_tty_state(void)
3878{
3879 /* if (rootshell) - caller ensures this */
3880 if (ttyfd >= 0)
3881 tcsetattr(ttyfd, TCSADRAIN, &shell_tty_info);
3882}
3883static int
3884job_signal_status(struct job *jp)
3885{
3886 int status;
3887 unsigned i;
3888 struct procstat *ps = jp->ps;
3889 for (i = 0; i < jp->nprocs; i++) {
3890 status = ps[i].ps_status;
3891 if (WIFSIGNALED(status) || WIFSTOPPED(status))
3892 return status;
3893 }
3894 return 0;
3895}
3896static void
3897restore_tty_if_stopped_or_signaled(struct job *jp)
3898{
3899//TODO: check what happens if we come from waitforjob() in expbackq()
3900 if (rootshell) {
3901 int s = job_signal_status(jp);
3902 if (s) /* WIFSIGNALED(s) || WIFSTOPPED(s) */
3903 set_tty_state();
3904 }
3905}
3906#else
3907# define get_tty_state() ((void)0)
3908# define restore_tty_if_stopped_or_signaled(jp) ((void)0)
3909#endif
3910
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003911static void
3912set_curjob(struct job *jp, unsigned mode)
3913{
3914 struct job *jp1;
3915 struct job **jpp, **curp;
3916
3917 /* first remove from list */
3918 jpp = curp = &curjob;
Denys Vlasenko940c7202011-03-02 04:07:14 +01003919 while (1) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003920 jp1 = *jpp;
3921 if (jp1 == jp)
3922 break;
3923 jpp = &jp1->prev_job;
Denys Vlasenko940c7202011-03-02 04:07:14 +01003924 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003925 *jpp = jp1->prev_job;
3926
3927 /* Then re-insert in correct position */
3928 jpp = curp;
3929 switch (mode) {
3930 default:
3931#if DEBUG
3932 abort();
3933#endif
3934 case CUR_DELETE:
3935 /* job being deleted */
3936 break;
3937 case CUR_RUNNING:
3938 /* newly created job or backgrounded job,
Denys Vlasenko60cb48c2013-01-14 15:57:44 +01003939 * put after all stopped jobs.
3940 */
Denys Vlasenko940c7202011-03-02 04:07:14 +01003941 while (1) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003942 jp1 = *jpp;
3943#if JOBS
3944 if (!jp1 || jp1->state != JOBSTOPPED)
3945#endif
3946 break;
3947 jpp = &jp1->prev_job;
Denys Vlasenko940c7202011-03-02 04:07:14 +01003948 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003949 /* FALLTHROUGH */
3950#if JOBS
3951 case CUR_STOPPED:
3952#endif
3953 /* newly stopped job - becomes curjob */
3954 jp->prev_job = *jpp;
3955 *jpp = jp;
3956 break;
3957 }
3958}
3959
3960#if JOBS || DEBUG
3961static int
3962jobno(const struct job *jp)
3963{
3964 return jp - jobtab + 1;
3965}
3966#endif
3967
3968/*
3969 * Convert a job name to a job structure.
3970 */
Denis Vlasenko85c24712008-03-17 09:04:04 +00003971#if !JOBS
3972#define getjob(name, getctl) getjob(name)
3973#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003974static struct job *
3975getjob(const char *name, int getctl)
3976{
3977 struct job *jp;
3978 struct job *found;
Denys Vlasenkoffc39202009-08-17 02:12:20 +02003979 const char *err_msg = "%s: no such job";
Denis Vlasenkoa8915072007-02-23 21:10:06 +00003980 unsigned num;
3981 int c;
3982 const char *p;
3983 char *(*match)(const char *, const char *);
3984
3985 jp = curjob;
3986 p = name;
3987 if (!p)
3988 goto currentjob;
3989
3990 if (*p != '%')
3991 goto err;
3992
3993 c = *++p;
3994 if (!c)
3995 goto currentjob;
3996
3997 if (!p[1]) {
3998 if (c == '+' || c == '%') {
3999 currentjob:
4000 err_msg = "No current job";
4001 goto check;
4002 }
4003 if (c == '-') {
4004 if (jp)
4005 jp = jp->prev_job;
4006 err_msg = "No previous job";
4007 check:
4008 if (!jp)
4009 goto err;
4010 goto gotit;
4011 }
4012 }
4013
4014 if (is_number(p)) {
4015 num = atoi(p);
Denys Vlasenko46a45ce2016-09-29 01:10:08 +02004016 if (num > 0 && num <= njobs) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004017 jp = jobtab + num - 1;
4018 if (jp->used)
4019 goto gotit;
4020 goto err;
4021 }
4022 }
4023
4024 match = prefix;
4025 if (*p == '?') {
4026 match = strstr;
4027 p++;
4028 }
4029
Denys Vlasenkoffc39202009-08-17 02:12:20 +02004030 found = NULL;
4031 while (jp) {
Denys Vlasenko285ad152009-12-04 23:02:27 +01004032 if (match(jp->ps[0].ps_cmd, p)) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004033 if (found)
4034 goto err;
4035 found = jp;
4036 err_msg = "%s: ambiguous";
4037 }
4038 jp = jp->prev_job;
4039 }
Denys Vlasenkoffc39202009-08-17 02:12:20 +02004040 if (!found)
4041 goto err;
4042 jp = found;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004043
4044 gotit:
4045#if JOBS
4046 err_msg = "job %s not created under job control";
4047 if (getctl && jp->jobctl == 0)
4048 goto err;
4049#endif
4050 return jp;
4051 err:
4052 ash_msg_and_raise_error(err_msg, name);
4053}
4054
4055/*
4056 * Mark a job structure as unused.
4057 */
4058static void
4059freejob(struct job *jp)
4060{
4061 struct procstat *ps;
4062 int i;
4063
4064 INT_OFF;
4065 for (i = jp->nprocs, ps = jp->ps; --i >= 0; ps++) {
Denys Vlasenko285ad152009-12-04 23:02:27 +01004066 if (ps->ps_cmd != nullstr)
4067 free(ps->ps_cmd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004068 }
4069 if (jp->ps != &jp->ps0)
4070 free(jp->ps);
4071 jp->used = 0;
4072 set_curjob(jp, CUR_DELETE);
4073 INT_ON;
4074}
4075
4076#if JOBS
4077static void
4078xtcsetpgrp(int fd, pid_t pgrp)
4079{
4080 if (tcsetpgrp(fd, pgrp))
Ron Yorstonbe366e52017-07-27 13:53:39 +01004081 ash_msg_and_raise_perror("can't set tty process group");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004082}
4083
4084/*
4085 * Turn job control on and off.
4086 *
4087 * Note: This code assumes that the third arg to ioctl is a character
4088 * pointer, which is true on Berkeley systems but not System V. Since
4089 * System V doesn't have job control yet, this isn't a problem now.
4090 *
4091 * Called with interrupts off.
4092 */
4093static void
4094setjobctl(int on)
4095{
4096 int fd;
4097 int pgrp;
4098
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004099 if (on == doing_jobctl || rootshell == 0)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004100 return;
4101 if (on) {
4102 int ofd;
4103 ofd = fd = open(_PATH_TTY, O_RDWR);
4104 if (fd < 0) {
4105 /* BTW, bash will try to open(ttyname(0)) if open("/dev/tty") fails.
4106 * That sometimes helps to acquire controlling tty.
4107 * Obviously, a workaround for bugs when someone
4108 * failed to provide a controlling tty to bash! :) */
Denis Vlasenkoed270a52007-11-26 05:37:07 +00004109 fd = 2;
4110 while (!isatty(fd))
4111 if (--fd < 0)
4112 goto out;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004113 }
Denys Vlasenko64774602016-10-26 15:24:30 +02004114 /* fd is a tty at this point */
Denys Vlasenko60fb98e2018-03-30 22:15:14 +02004115 fd = fcntl(fd, F_DUPFD_CLOEXEC, 10);
Denys Vlasenko10ad6222017-04-17 16:13:32 +02004116 if (ofd >= 0) /* if it is "/dev/tty", close. If 0/1/2, don't */
Denis Vlasenkoed270a52007-11-26 05:37:07 +00004117 close(ofd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004118 if (fd < 0)
Denys Vlasenko64774602016-10-26 15:24:30 +02004119 goto out; /* F_DUPFD failed */
Denys Vlasenko60fb98e2018-03-30 22:15:14 +02004120 if (F_DUPFD_CLOEXEC == F_DUPFD) /* if old libc (w/o F_DUPFD_CLOEXEC) */
4121 close_on_exec_on(fd);
Denys Vlasenko940c7202011-03-02 04:07:14 +01004122 while (1) { /* while we are in the background */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004123 pgrp = tcgetpgrp(fd);
4124 if (pgrp < 0) {
4125 out:
4126 ash_msg("can't access tty; job control turned off");
4127 mflag = on = 0;
4128 goto close;
4129 }
4130 if (pgrp == getpgrp())
4131 break;
4132 killpg(0, SIGTTIN);
Denys Vlasenko940c7202011-03-02 04:07:14 +01004133 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004134 initialpgrp = pgrp;
4135
4136 setsignal(SIGTSTP);
4137 setsignal(SIGTTOU);
4138 setsignal(SIGTTIN);
4139 pgrp = rootpid;
4140 setpgid(0, pgrp);
4141 xtcsetpgrp(fd, pgrp);
4142 } else {
4143 /* turning job control off */
4144 fd = ttyfd;
4145 pgrp = initialpgrp;
Denis Vlasenko08c8c1d2007-04-28 22:39:02 +00004146 /* was xtcsetpgrp, but this can make exiting ash
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004147 * loop forever if pty is already deleted */
Denis Vlasenko08c8c1d2007-04-28 22:39:02 +00004148 tcsetpgrp(fd, pgrp);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004149 setpgid(0, pgrp);
4150 setsignal(SIGTSTP);
4151 setsignal(SIGTTOU);
4152 setsignal(SIGTTIN);
4153 close:
Denis Vlasenkoed270a52007-11-26 05:37:07 +00004154 if (fd >= 0)
4155 close(fd);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004156 fd = -1;
4157 }
4158 ttyfd = fd;
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004159 doing_jobctl = on;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004160}
4161
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02004162static int FAST_FUNC
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004163killcmd(int argc, char **argv)
4164{
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00004165 if (argv[1] && strcmp(argv[1], "-l") != 0) {
Denys Vlasenkob12553f2011-02-21 03:22:20 +01004166 int i = 1;
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00004167 do {
4168 if (argv[i][0] == '%') {
Denys Vlasenkob12553f2011-02-21 03:22:20 +01004169 /*
4170 * "kill %N" - job kill
4171 * Converting to pgrp / pid kill
4172 */
4173 struct job *jp;
4174 char *dst;
4175 int j, n;
4176
4177 jp = getjob(argv[i], 0);
4178 /*
4179 * In jobs started under job control, we signal
4180 * entire process group by kill -PGRP_ID.
4181 * This happens, f.e., in interactive shell.
4182 *
4183 * Otherwise, we signal each child via
4184 * kill PID1 PID2 PID3.
4185 * Testcases:
4186 * sh -c 'sleep 1|sleep 1 & kill %1'
4187 * sh -c 'true|sleep 2 & sleep 1; kill %1'
4188 * sh -c 'true|sleep 1 & sleep 2; kill %1'
4189 */
4190 n = jp->nprocs; /* can't be 0 (I hope) */
4191 if (jp->jobctl)
4192 n = 1;
4193 dst = alloca(n * sizeof(int)*4);
4194 argv[i] = dst;
4195 for (j = 0; j < n; j++) {
4196 struct procstat *ps = &jp->ps[j];
4197 /* Skip non-running and not-stopped members
4198 * (i.e. dead members) of the job
4199 */
4200 if (ps->ps_status != -1 && !WIFSTOPPED(ps->ps_status))
4201 continue;
4202 /*
4203 * kill_main has matching code to expect
4204 * leading space. Needed to not confuse
4205 * negative pids with "kill -SIGNAL_NO" syntax
4206 */
4207 dst += sprintf(dst, jp->jobctl ? " -%u" : " %u", (int)ps->ps_pid);
4208 }
4209 *dst = '\0';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004210 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00004211 } while (argv[++i]);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004212 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00004213 return kill_main(argc, argv);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004214}
4215
4216static void
Denys Vlasenko285ad152009-12-04 23:02:27 +01004217showpipe(struct job *jp /*, FILE *out*/)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004218{
Denys Vlasenko285ad152009-12-04 23:02:27 +01004219 struct procstat *ps;
4220 struct procstat *psend;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004221
Denys Vlasenko285ad152009-12-04 23:02:27 +01004222 psend = jp->ps + jp->nprocs;
4223 for (ps = jp->ps + 1; ps < psend; ps++)
4224 printf(" | %s", ps->ps_cmd);
Denys Vlasenko9c541002015-10-07 15:44:36 +02004225 newline_and_flush(stdout);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004226 flush_stdout_stderr();
4227}
4228
4229
4230static int
4231restartjob(struct job *jp, int mode)
4232{
4233 struct procstat *ps;
4234 int i;
4235 int status;
4236 pid_t pgid;
4237
4238 INT_OFF;
4239 if (jp->state == JOBDONE)
4240 goto out;
4241 jp->state = JOBRUNNING;
Denys Vlasenko285ad152009-12-04 23:02:27 +01004242 pgid = jp->ps[0].ps_pid;
Denys Vlasenko098b7132017-01-11 19:59:03 +01004243 if (mode == FORK_FG) {
4244 get_tty_state();
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004245 xtcsetpgrp(ttyfd, pgid);
Denys Vlasenko098b7132017-01-11 19:59:03 +01004246 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004247 killpg(pgid, SIGCONT);
4248 ps = jp->ps;
4249 i = jp->nprocs;
4250 do {
Denys Vlasenko285ad152009-12-04 23:02:27 +01004251 if (WIFSTOPPED(ps->ps_status)) {
4252 ps->ps_status = -1;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004253 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00004254 ps++;
4255 } while (--i);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004256 out:
4257 status = (mode == FORK_FG) ? waitforjob(jp) : 0;
4258 INT_ON;
4259 return status;
4260}
4261
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02004262static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00004263fg_bgcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004264{
4265 struct job *jp;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004266 int mode;
4267 int retval;
4268
4269 mode = (**argv == 'f') ? FORK_FG : FORK_BG;
4270 nextopt(nullstr);
4271 argv = argptr;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004272 do {
4273 jp = getjob(*argv, 1);
4274 if (mode == FORK_BG) {
4275 set_curjob(jp, CUR_RUNNING);
Denys Vlasenko285ad152009-12-04 23:02:27 +01004276 printf("[%d] ", jobno(jp));
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004277 }
Denys Vlasenko285ad152009-12-04 23:02:27 +01004278 out1str(jp->ps[0].ps_cmd);
4279 showpipe(jp /*, stdout*/);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004280 retval = restartjob(jp, mode);
4281 } while (*argv && *++argv);
4282 return retval;
4283}
4284#endif
4285
4286static int
Denys Vlasenko2bad3a32020-02-16 18:23:43 +01004287sprint_status48(char *os, int status, int sigonly)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004288{
Denys Vlasenko2bad3a32020-02-16 18:23:43 +01004289 char *s = os;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004290 int st;
4291
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004292 if (!WIFEXITED(status)) {
Johannes Schindelin9d4dc842017-07-14 22:25:58 +02004293#if JOBS
4294 if (WIFSTOPPED(status))
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004295 st = WSTOPSIG(status);
4296 else
Johannes Schindelin9d4dc842017-07-14 22:25:58 +02004297#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004298 st = WTERMSIG(status);
4299 if (sigonly) {
4300 if (st == SIGINT || st == SIGPIPE)
4301 goto out;
Johannes Schindelin9d4dc842017-07-14 22:25:58 +02004302#if JOBS
4303 if (WIFSTOPPED(status))
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004304 goto out;
Johannes Schindelin9d4dc842017-07-14 22:25:58 +02004305#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004306 }
4307 st &= 0x7f;
Denys Vlasenko33745b12021-02-18 13:44:27 +01004308 s = stpncpy(s, strsignal(st), 32);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004309 if (WCOREDUMP(status)) {
Denys Vlasenko2bad3a32020-02-16 18:23:43 +01004310 s = stpcpy(s, " (core dumped)");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004311 }
4312 } else if (!sigonly) {
4313 st = WEXITSTATUS(status);
Denys Vlasenko2bad3a32020-02-16 18:23:43 +01004314 s += fmtstr(s, 16, (st ? "Done(%d)" : "Done"), st);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004315 }
4316 out:
Denys Vlasenko2bad3a32020-02-16 18:23:43 +01004317 return s - os;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004318}
4319
Denys Vlasenko8f7b0242016-10-28 17:16:11 +02004320#define DOWAIT_NONBLOCK 0
4321#define DOWAIT_BLOCK 1
4322#define DOWAIT_BLOCK_OR_SIG 2
Ron Yorstone48559e2019-03-31 09:27:09 +01004323#if BASH_WAIT_N
Denys Vlasenko966f0872019-03-27 15:51:42 +01004324# define DOWAIT_JOBSTATUS 0x10 /* OR this to get job's exitstatus instead of pid */
4325#endif
Denys Vlasenko458c1f22016-10-27 23:51:19 +02004326
4327static int
Denys Vlasenko91e11eb2020-09-29 20:35:55 +02004328waitproc(int block, int *status)
4329{
4330 sigset_t oldmask;
4331 int flags = block == DOWAIT_BLOCK ? 0 : WNOHANG;
4332 int err;
4333
4334#if JOBS
4335 if (doing_jobctl)
4336 flags |= WUNTRACED;
4337#endif
4338
4339 do {
4340 got_sigchld = 0;
4341 do
4342 err = waitpid(-1, status, flags);
4343 while (err < 0 && errno == EINTR);
4344
4345 if (err || (err = -!block))
4346 break;
4347
4348 sigfillset(&oldmask);
4349 sigprocmask2(SIG_SETMASK, &oldmask); /* mask is updated */
4350 while (!got_sigchld && !pending_sig)
4351 sigsuspend(&oldmask);
4352 sigprocmask(SIG_SETMASK, &oldmask, NULL);
4353 //simpler, but unsafe: a signal can set pending_sig after check, but before pause():
4354 //while (!got_sigchld && !pending_sig)
4355 // pause();
4356
4357 } while (got_sigchld);
4358
4359 return err;
4360}
4361
4362static int
Denys Vlasenko47eb9792020-02-18 15:37:43 +01004363waitone(int block, struct job *job)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004364{
4365 int pid;
4366 int status;
4367 struct job *jp;
Denys Vlasenko91e11eb2020-09-29 20:35:55 +02004368 struct job *thisjob = NULL;
Ron Yorstone48559e2019-03-31 09:27:09 +01004369#if BASH_WAIT_N
Denys Vlasenko966f0872019-03-27 15:51:42 +01004370 bool want_jobexitstatus = (block & DOWAIT_JOBSTATUS);
4371 block = (block & ~DOWAIT_JOBSTATUS);
4372#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004373
Denys Vlasenkob543bda2016-10-27 20:08:28 +02004374 TRACE(("dowait(0x%x) called\n", block));
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +00004375
Denys Vlasenko458c1f22016-10-27 23:51:19 +02004376 /* It's wrong to call waitpid() outside of INT_OFF region:
4377 * signal can arrive just after syscall return and handler can
4378 * longjmp away, losing stop/exit notification processing.
4379 * Thus, for "jobs" builtin, and for waiting for a fg job,
4380 * we call waitpid() (blocking or non-blocking) inside INT_OFF.
4381 *
4382 * However, for "wait" builtin it is wrong to simply call waitpid()
4383 * in INT_OFF region: "wait" needs to wait for any running job
4384 * to change state, but should exit on any trap too.
4385 * In INT_OFF region, a signal just before syscall entry can set
Denys Vlasenko8f7b0242016-10-28 17:16:11 +02004386 * pending_sig variables, but we can't check them, and we would
Denys Vlasenko458c1f22016-10-27 23:51:19 +02004387 * either enter a sleeping waitpid() (BUG), or need to busy-loop.
Denys Vlasenko8f7b0242016-10-28 17:16:11 +02004388 *
Denys Vlasenko458c1f22016-10-27 23:51:19 +02004389 * Because of this, we run inside INT_OFF, but use a special routine
Denys Vlasenko1ab7c2f2016-11-03 20:17:23 +01004390 * which combines waitpid() and sigsuspend().
Denys Vlasenko8f7b0242016-10-28 17:16:11 +02004391 * This is the reason why we need to have a handler for SIGCHLD:
Denys Vlasenko1ab7c2f2016-11-03 20:17:23 +01004392 * SIG_DFL handler does not wake sigsuspend().
Denys Vlasenko458c1f22016-10-27 23:51:19 +02004393 */
4394 INT_OFF;
Denys Vlasenko91e11eb2020-09-29 20:35:55 +02004395 pid = waitproc(block, &status);
4396 TRACE(("wait returns pid %d, status=%d\n", pid, status));
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004397 if (pid <= 0)
Denys Vlasenko458c1f22016-10-27 23:51:19 +02004398 goto out;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004399
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004400 for (jp = curjob; jp; jp = jp->prev_job) {
Denys Vlasenko4700fb52015-10-09 15:40:13 +02004401 int jobstate;
Denys Vlasenko285ad152009-12-04 23:02:27 +01004402 struct procstat *ps;
4403 struct procstat *psend;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004404 if (jp->state == JOBDONE)
4405 continue;
Denys Vlasenko4700fb52015-10-09 15:40:13 +02004406 jobstate = JOBDONE;
Denys Vlasenko285ad152009-12-04 23:02:27 +01004407 ps = jp->ps;
4408 psend = ps + jp->nprocs;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004409 do {
Denys Vlasenko285ad152009-12-04 23:02:27 +01004410 if (ps->ps_pid == pid) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004411 TRACE(("Job %d: changing status of proc %d "
4412 "from 0x%x to 0x%x\n",
Denys Vlasenko285ad152009-12-04 23:02:27 +01004413 jobno(jp), pid, ps->ps_status, status));
4414 ps->ps_status = status;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004415 thisjob = jp;
4416 }
Denys Vlasenko285ad152009-12-04 23:02:27 +01004417 if (ps->ps_status == -1)
Denys Vlasenko4700fb52015-10-09 15:40:13 +02004418 jobstate = JOBRUNNING;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004419#if JOBS
Denys Vlasenko4700fb52015-10-09 15:40:13 +02004420 if (jobstate == JOBRUNNING)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004421 continue;
Denys Vlasenko285ad152009-12-04 23:02:27 +01004422 if (WIFSTOPPED(ps->ps_status)) {
4423 jp->stopstatus = ps->ps_status;
Denys Vlasenko4700fb52015-10-09 15:40:13 +02004424 jobstate = JOBSTOPPED;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004425 }
4426#endif
Denys Vlasenko285ad152009-12-04 23:02:27 +01004427 } while (++ps < psend);
Denys Vlasenko4700fb52015-10-09 15:40:13 +02004428 if (!thisjob)
4429 continue;
4430
4431 /* Found the job where one of its processes changed its state.
4432 * Is there at least one live and running process in this job? */
4433 if (jobstate != JOBRUNNING) {
4434 /* No. All live processes in the job are stopped
4435 * (JOBSTOPPED) or there are no live processes (JOBDONE)
4436 */
4437 thisjob->changed = 1;
4438 if (thisjob->state != jobstate) {
4439 TRACE(("Job %d: changing state from %d to %d\n",
4440 jobno(thisjob), thisjob->state, jobstate));
4441 thisjob->state = jobstate;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004442#if JOBS
Denys Vlasenko4700fb52015-10-09 15:40:13 +02004443 if (jobstate == JOBSTOPPED)
4444 set_curjob(thisjob, CUR_STOPPED);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004445#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004446 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004447 }
Denys Vlasenko4700fb52015-10-09 15:40:13 +02004448 goto out;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004449 }
Denys Vlasenko4700fb52015-10-09 15:40:13 +02004450 /* The process wasn't found in job list */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004451 out:
4452 INT_ON;
4453
Ron Yorstone48559e2019-03-31 09:27:09 +01004454#if BASH_WAIT_N
Denys Vlasenko966f0872019-03-27 15:51:42 +01004455 if (want_jobexitstatus) {
4456 pid = -1;
4457 if (thisjob && thisjob->state == JOBDONE)
4458 pid = thisjob->ps[thisjob->nprocs - 1].ps_status;
4459 }
4460#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004461 if (thisjob && thisjob == job) {
4462 char s[48 + 1];
4463 int len;
4464
Denys Vlasenko9c541002015-10-07 15:44:36 +02004465 len = sprint_status48(s, status, 1);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004466 if (len) {
4467 s[len] = '\n';
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004468 s[len + 1] = '\0';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004469 out2str(s);
4470 }
4471 }
4472 return pid;
4473}
4474
Denys Vlasenko47eb9792020-02-18 15:37:43 +01004475static int
4476dowait(int block, struct job *jp)
4477{
Denys Vlasenko8d5f4652020-09-29 16:44:46 +02004478 smallint gotchld = *(volatile smallint *)&got_sigchld;
4479 int rpid;
4480 int pid;
Denys Vlasenko47eb9792020-02-18 15:37:43 +01004481
Denys Vlasenko8d5f4652020-09-29 16:44:46 +02004482 if (jp && jp->state != JOBRUNNING)
4483 block = DOWAIT_NONBLOCK;
4484
4485 if (block == DOWAIT_NONBLOCK && !gotchld)
4486 return 1;
4487
4488 rpid = 1;
4489
4490 do {
Denys Vlasenko47eb9792020-02-18 15:37:43 +01004491 pid = waitone(block, jp);
Denys Vlasenko8d5f4652020-09-29 16:44:46 +02004492 rpid &= !!pid;
Denys Vlasenko47eb9792020-02-18 15:37:43 +01004493
Denys Vlasenko8d5f4652020-09-29 16:44:46 +02004494 if (!pid || (jp && jp->state != JOBRUNNING))
4495 block = DOWAIT_NONBLOCK;
4496 } while (pid >= 0);
4497
4498 return rpid;
Denys Vlasenko47eb9792020-02-18 15:37:43 +01004499}
4500
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004501#if JOBS
4502static void
Denys Vlasenko9c541002015-10-07 15:44:36 +02004503showjob(struct job *jp, int mode)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004504{
4505 struct procstat *ps;
4506 struct procstat *psend;
4507 int col;
Denis Vlasenko40ba9982007-07-14 00:48:29 +00004508 int indent_col;
Denys Vlasenko9c541002015-10-07 15:44:36 +02004509 char s[16 + 16 + 48];
4510 FILE *out = (mode & SHOW_STDERR ? stderr : stdout);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004511
4512 ps = jp->ps;
4513
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004514 if (mode & SHOW_ONLY_PGID) { /* jobs -p */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004515 /* just output process (group) id of pipeline */
Denys Vlasenko285ad152009-12-04 23:02:27 +01004516 fprintf(out, "%d\n", ps->ps_pid);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004517 return;
4518 }
4519
4520 col = fmtstr(s, 16, "[%d] ", jobno(jp));
Denis Vlasenko40ba9982007-07-14 00:48:29 +00004521 indent_col = col;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004522
4523 if (jp == curjob)
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004524 s[col - 3] = '+';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004525 else if (curjob && jp == curjob->prev_job)
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004526 s[col - 3] = '-';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004527
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004528 if (mode & SHOW_PIDS)
Denys Vlasenko285ad152009-12-04 23:02:27 +01004529 col += fmtstr(s + col, 16, "%d ", ps->ps_pid);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004530
4531 psend = ps + jp->nprocs;
4532
4533 if (jp->state == JOBRUNNING) {
4534 strcpy(s + col, "Running");
4535 col += sizeof("Running") - 1;
4536 } else {
Denys Vlasenko285ad152009-12-04 23:02:27 +01004537 int status = psend[-1].ps_status;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004538 if (jp->state == JOBSTOPPED)
4539 status = jp->stopstatus;
Denys Vlasenko9c541002015-10-07 15:44:36 +02004540 col += sprint_status48(s + col, status, 0);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004541 }
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004542 /* By now, "[JOBID]* [maybe PID] STATUS" is printed */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004543
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004544 /* This loop either prints "<cmd1> | <cmd2> | <cmd3>" line
4545 * or prints several "PID | <cmdN>" lines,
4546 * depending on SHOW_PIDS bit.
4547 * We do not print status of individual processes
4548 * between PID and <cmdN>. bash does it, but not very well:
4549 * first line shows overall job status, not process status,
4550 * making it impossible to know 1st process status.
4551 */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004552 goto start;
Denys Vlasenko285ad152009-12-04 23:02:27 +01004553 do {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004554 /* for each process */
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004555 s[0] = '\0';
4556 col = 33;
4557 if (mode & SHOW_PIDS)
Denys Vlasenko285ad152009-12-04 23:02:27 +01004558 col = fmtstr(s, 48, "\n%*c%d ", indent_col, ' ', ps->ps_pid) - 1;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004559 start:
Denys Vlasenko285ad152009-12-04 23:02:27 +01004560 fprintf(out, "%s%*c%s%s",
4561 s,
4562 33 - col >= 0 ? 33 - col : 0, ' ',
4563 ps == jp->ps ? "" : "| ",
4564 ps->ps_cmd
4565 );
4566 } while (++ps != psend);
Denys Vlasenko9c541002015-10-07 15:44:36 +02004567 newline_and_flush(out);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004568
4569 jp->changed = 0;
4570
4571 if (jp->state == JOBDONE) {
4572 TRACE(("showjob: freeing job %d\n", jobno(jp)));
4573 freejob(jp);
4574 }
4575}
4576
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004577/*
4578 * Print a list of jobs. If "change" is nonzero, only print jobs whose
4579 * statuses have changed since the last call to showjobs.
4580 */
4581static void
Denys Vlasenko9c541002015-10-07 15:44:36 +02004582showjobs(int mode)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004583{
4584 struct job *jp;
4585
Denys Vlasenko883cea42009-07-11 15:31:59 +02004586 TRACE(("showjobs(0x%x) called\n", mode));
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004587
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004588 /* Handle all finished jobs */
Denys Vlasenko47eb9792020-02-18 15:37:43 +01004589 dowait(DOWAIT_NONBLOCK, NULL);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004590
4591 for (jp = curjob; jp; jp = jp->prev_job) {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004592 if (!(mode & SHOW_CHANGED) || jp->changed) {
Denys Vlasenko9c541002015-10-07 15:44:36 +02004593 showjob(jp, mode);
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004594 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004595 }
4596}
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004597
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02004598static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00004599jobscmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004600{
4601 int mode, m;
4602
4603 mode = 0;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02004604 while ((m = nextopt("lp")) != '\0') {
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004605 if (m == 'l')
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004606 mode |= SHOW_PIDS;
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004607 else
Denys Vlasenkoa12af2d2009-08-23 22:10:04 +02004608 mode |= SHOW_ONLY_PGID;
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004609 }
4610
4611 argv = argptr;
4612 if (*argv) {
4613 do
Denys Vlasenko9c541002015-10-07 15:44:36 +02004614 showjob(getjob(*argv, 0), mode);
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004615 while (*++argv);
Denys Vlasenko285ad152009-12-04 23:02:27 +01004616 } else {
Denys Vlasenko9c541002015-10-07 15:44:36 +02004617 showjobs(mode);
Denys Vlasenko285ad152009-12-04 23:02:27 +01004618 }
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004619
4620 return 0;
4621}
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004622#endif /* JOBS */
4623
Michael Abbott359da5e2009-12-04 23:03:29 +01004624/* Called only on finished or stopped jobs (no members are running) */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004625static int
4626getstatus(struct job *job)
4627{
4628 int status;
4629 int retval;
Michael Abbott359da5e2009-12-04 23:03:29 +01004630 struct procstat *ps;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004631
Michael Abbott359da5e2009-12-04 23:03:29 +01004632 /* Fetch last member's status */
4633 ps = job->ps + job->nprocs - 1;
4634 status = ps->ps_status;
4635 if (pipefail) {
4636 /* "set -o pipefail" mode: use last _nonzero_ status */
4637 while (status == 0 && --ps >= job->ps)
4638 status = ps->ps_status;
4639 }
4640
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004641 retval = WEXITSTATUS(status);
4642 if (!WIFEXITED(status)) {
4643#if JOBS
4644 retval = WSTOPSIG(status);
4645 if (!WIFSTOPPED(status))
4646#endif
4647 {
4648 /* XXX: limits number of signals */
4649 retval = WTERMSIG(status);
4650#if JOBS
4651 if (retval == SIGINT)
4652 job->sigint = 1;
4653#endif
4654 }
Denys Vlasenko93e2a222020-12-23 12:23:21 +01004655 retval |= 128;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004656 }
Denys Vlasenko883cea42009-07-11 15:31:59 +02004657 TRACE(("getstatus: job %d, nproc %d, status 0x%x, retval 0x%x\n",
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004658 jobno(job), job->nprocs, status, retval));
4659 return retval;
4660}
4661
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02004662static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00004663waitcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004664{
4665 struct job *job;
4666 int retval;
4667 struct job *jp;
Ron Yorstone48559e2019-03-31 09:27:09 +01004668#if BASH_WAIT_N
Denys Vlasenko966f0872019-03-27 15:51:42 +01004669 int status;
4670 char one = nextopt("n");
4671#else
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004672 nextopt(nullstr);
Denys Vlasenko966f0872019-03-27 15:51:42 +01004673#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004674 retval = 0;
4675
4676 argv = argptr;
Denys Vlasenko966f0872019-03-27 15:51:42 +01004677 if (!argv[0]) {
4678 /* wait for all jobs / one job if -n */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004679 for (;;) {
4680 jp = curjob;
Ron Yorstone48559e2019-03-31 09:27:09 +01004681#if BASH_WAIT_N
Denys Vlasenko966f0872019-03-27 15:51:42 +01004682 if (one && !jp)
4683 /* exitcode of "wait -n" with nothing to wait for is 127, not 0 */
4684 retval = 127;
4685#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004686 while (1) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004687 if (!jp) /* no running procs */
4688 goto ret;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004689 if (jp->state == JOBRUNNING)
4690 break;
4691 jp->waited = 1;
4692 jp = jp->prev_job;
4693 }
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004694 /* man bash:
4695 * "When bash is waiting for an asynchronous command via
4696 * the wait builtin, the reception of a signal for which a trap
4697 * has been set will cause the wait builtin to return immediately
4698 * with an exit status greater than 128, immediately after which
4699 * the trap is executed."
Denys Vlasenko69188112016-10-27 20:18:18 +02004700 */
Ron Yorstone48559e2019-03-31 09:27:09 +01004701#if BASH_WAIT_N
Denys Vlasenko966f0872019-03-27 15:51:42 +01004702 status = dowait(DOWAIT_BLOCK_OR_SIG | DOWAIT_JOBSTATUS, NULL);
4703#else
Denys Vlasenko458c1f22016-10-27 23:51:19 +02004704 dowait(DOWAIT_BLOCK_OR_SIG, NULL);
Denys Vlasenko966f0872019-03-27 15:51:42 +01004705#endif
Denys Vlasenko47eb9792020-02-18 15:37:43 +01004706 /* if child sends us a signal *and immediately exits*,
4707 * dowait() returns pid > 0. Check this case,
4708 * not "if (dowait() < 0)"!
4709 */
Denys Vlasenko7c1ed9f2010-05-17 04:42:40 +02004710 if (pending_sig)
Denys Vlasenkoc0663c72016-10-27 21:09:01 +02004711 goto sigout;
Ron Yorstone48559e2019-03-31 09:27:09 +01004712#if BASH_WAIT_N
Denys Vlasenko966f0872019-03-27 15:51:42 +01004713 if (one) {
4714 /* wait -n waits for one _job_, not one _process_.
4715 * date; sleep 3 & sleep 2 | sleep 1 & wait -n; date
4716 * should wait for 2 seconds. Not 1 or 3.
4717 */
4718 if (status != -1 && !WIFSTOPPED(status)) {
4719 retval = WEXITSTATUS(status);
4720 if (WIFSIGNALED(status))
Denys Vlasenko93e2a222020-12-23 12:23:21 +01004721 retval = 128 | WTERMSIG(status);
Denys Vlasenko966f0872019-03-27 15:51:42 +01004722 goto ret;
4723 }
4724 }
4725#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004726 }
4727 }
4728
4729 retval = 127;
4730 do {
4731 if (**argv != '%') {
4732 pid_t pid = number(*argv);
4733 job = curjob;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004734 while (1) {
4735 if (!job)
4736 goto repeat;
Denys Vlasenko285ad152009-12-04 23:02:27 +01004737 if (job->ps[job->nprocs - 1].ps_pid == pid)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004738 break;
4739 job = job->prev_job;
Denis Vlasenko36fc3cd2008-01-29 09:23:49 +00004740 }
Denys Vlasenkob12553f2011-02-21 03:22:20 +01004741 } else {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004742 job = getjob(*argv, 0);
Denys Vlasenkob12553f2011-02-21 03:22:20 +01004743 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004744 /* loop until process terminated or stopped */
Denys Vlasenko91e11eb2020-09-29 20:35:55 +02004745 dowait(DOWAIT_BLOCK_OR_SIG, job);
Denys Vlasenko47eb9792020-02-18 15:37:43 +01004746 if (pending_sig)
4747 goto sigout;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004748 job->waited = 1;
4749 retval = getstatus(job);
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00004750 repeat: ;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004751 } while (*++argv);
4752
Denis Vlasenko991a1da2008-02-10 19:02:53 +00004753 ret:
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004754 return retval;
Denys Vlasenkoc0663c72016-10-27 21:09:01 +02004755 sigout:
Denys Vlasenko93e2a222020-12-23 12:23:21 +01004756 retval = 128 | pending_sig;
Denys Vlasenkoc0663c72016-10-27 21:09:01 +02004757 return retval;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004758}
4759
4760static struct job *
4761growjobtab(void)
4762{
4763 size_t len;
4764 ptrdiff_t offset;
4765 struct job *jp, *jq;
4766
4767 len = njobs * sizeof(*jp);
4768 jq = jobtab;
4769 jp = ckrealloc(jq, len + 4 * sizeof(*jp));
4770
4771 offset = (char *)jp - (char *)jq;
4772 if (offset) {
4773 /* Relocate pointers */
4774 size_t l = len;
4775
4776 jq = (struct job *)((char *)jq + l);
4777 while (l) {
4778 l -= sizeof(*jp);
4779 jq--;
4780#define joff(p) ((struct job *)((char *)(p) + l))
4781#define jmove(p) (p) = (void *)((char *)(p) + offset)
4782 if (joff(jp)->ps == &jq->ps0)
4783 jmove(joff(jp)->ps);
4784 if (joff(jp)->prev_job)
4785 jmove(joff(jp)->prev_job);
4786 }
4787 if (curjob)
4788 jmove(curjob);
4789#undef joff
4790#undef jmove
4791 }
4792
4793 njobs += 4;
4794 jobtab = jp;
4795 jp = (struct job *)((char *)jp + len);
4796 jq = jp + 3;
4797 do {
4798 jq->used = 0;
4799 } while (--jq >= jp);
4800 return jp;
4801}
4802
4803/*
4804 * Return a new job structure.
4805 * Called with interrupts off.
4806 */
4807static struct job *
Denis Vlasenko68404f12008-03-17 09:00:54 +00004808makejob(/*union node *node,*/ int nprocs)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004809{
4810 int i;
4811 struct job *jp;
4812
4813 for (i = njobs, jp = jobtab; ; jp++) {
4814 if (--i < 0) {
4815 jp = growjobtab();
4816 break;
4817 }
4818 if (jp->used == 0)
4819 break;
4820 if (jp->state != JOBDONE || !jp->waited)
4821 continue;
4822#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004823 if (doing_jobctl)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004824 continue;
4825#endif
4826 freejob(jp);
4827 break;
4828 }
4829 memset(jp, 0, sizeof(*jp));
4830#if JOBS
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +00004831 /* jp->jobctl is a bitfield.
Denys Vlasenko098b7132017-01-11 19:59:03 +01004832 * "jp->jobctl |= doing_jobctl" likely to give awful code */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00004833 if (doing_jobctl)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004834 jp->jobctl = 1;
4835#endif
4836 jp->prev_job = curjob;
4837 curjob = jp;
4838 jp->used = 1;
4839 jp->ps = &jp->ps0;
4840 if (nprocs > 1) {
4841 jp->ps = ckmalloc(nprocs * sizeof(struct procstat));
4842 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00004843 TRACE(("makejob(%d) returns %%%d\n", nprocs,
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004844 jobno(jp)));
4845 return jp;
4846}
4847
4848#if JOBS
4849/*
4850 * Return a string identifying a command (to be printed by the
4851 * jobs command).
4852 */
4853static char *cmdnextc;
4854
4855static void
4856cmdputs(const char *s)
4857{
Denys Vlasenko965b7952020-11-30 13:03:03 +01004858 static const char vstype[VSTYPE + 1][3] ALIGN1 = {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00004859 "", "}", "-", "+", "?", "=",
4860 "%", "%%", "#", "##"
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01004861 IF_BASH_SUBSTR(, ":")
4862 IF_BASH_PATTERN_SUBST(, "/", "//")
Denis Vlasenko92e13c22008-03-25 01:17:40 +00004863 };
4864
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004865 const char *p, *str;
Denys Vlasenko46a14772009-12-10 21:27:13 +01004866 char cc[2];
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004867 char *nextc;
Denys Vlasenkocd716832009-11-28 22:14:02 +01004868 unsigned char c;
4869 unsigned char subtype = 0;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004870 int quoted = 0;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004871
Denys Vlasenko46a14772009-12-10 21:27:13 +01004872 cc[1] = '\0';
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004873 nextc = makestrspace((strlen(s) + 1) * 8, cmdnextc);
4874 p = s;
Denys Vlasenko46a14772009-12-10 21:27:13 +01004875 while ((c = *p++) != '\0') {
Denis Vlasenkoef527f52008-06-23 01:52:30 +00004876 str = NULL;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004877 switch (c) {
4878 case CTLESC:
4879 c = *p++;
4880 break;
4881 case CTLVAR:
4882 subtype = *p++;
4883 if ((subtype & VSTYPE) == VSLENGTH)
4884 str = "${#";
4885 else
4886 str = "${";
Ron Yorston549deab2015-05-18 09:57:51 +02004887 goto dostr;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004888 case CTLENDVAR:
Denys Vlasenko3f4847b2020-02-16 19:06:42 +01004889 str = "\"}";
4890 str += !(quoted & 1);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004891 quoted >>= 1;
4892 subtype = 0;
4893 goto dostr;
Ron Yorstona1b0d382020-07-23 08:32:27 +01004894#if BASH_PROCESS_SUBST
4895 case CTLBACKQ:
4896 c = '$';
4897 str = "(...)";
4898 break;
4899 case CTLTOPROC:
4900 c = '>';
4901 str = "(...)";
4902 break;
4903 case CTLFROMPROC:
4904 c = '<';
4905 str = "(...)";
4906 break;
4907#else
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004908 case CTLBACKQ:
4909 str = "$(...)";
4910 goto dostr;
Ron Yorstona1b0d382020-07-23 08:32:27 +01004911#endif
Denys Vlasenko0b883582016-12-23 16:49:07 +01004912#if ENABLE_FEATURE_SH_MATH
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004913 case CTLARI:
4914 str = "$((";
4915 goto dostr;
4916 case CTLENDARI:
4917 str = "))";
4918 goto dostr;
4919#endif
4920 case CTLQUOTEMARK:
4921 quoted ^= 1;
4922 c = '"';
4923 break;
4924 case '=':
4925 if (subtype == 0)
4926 break;
4927 if ((subtype & VSTYPE) != VSNORMAL)
4928 quoted <<= 1;
4929 str = vstype[subtype & VSTYPE];
4930 if (subtype & VSNUL)
4931 c = ':';
4932 else
4933 goto checkstr;
4934 break;
4935 case '\'':
4936 case '\\':
4937 case '"':
4938 case '$':
4939 /* These can only happen inside quotes */
4940 cc[0] = c;
4941 str = cc;
Denys Vlasenkod0fff912017-07-31 14:32:18 +02004942//FIXME:
4943// $ true $$ &
4944// $ <cr>
4945// [1]+ Done true ${\$} <<=== BUG: ${\$} is not a valid way to write $$ (${$} would be ok)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004946 c = '\\';
4947 break;
4948 default:
4949 break;
4950 }
4951 USTPUTC(c, nextc);
4952 checkstr:
4953 if (!str)
4954 continue;
4955 dostr:
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02004956 while ((c = *str++) != '\0') {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004957 USTPUTC(c, nextc);
4958 }
Denys Vlasenko46a14772009-12-10 21:27:13 +01004959 } /* while *p++ not NUL */
4960
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004961 if (quoted & 1) {
4962 USTPUTC('"', nextc);
4963 }
4964 *nextc = 0;
4965 cmdnextc = nextc;
4966}
4967
4968/* cmdtxt() and cmdlist() call each other */
4969static void cmdtxt(union node *n);
4970
4971static void
4972cmdlist(union node *np, int sep)
4973{
4974 for (; np; np = np->narg.next) {
4975 if (!sep)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004976 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004977 cmdtxt(np);
4978 if (sep && np->narg.next)
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +00004979 cmdputs(" ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004980 }
4981}
4982
4983static void
4984cmdtxt(union node *n)
4985{
4986 union node *np;
4987 struct nodelist *lp;
4988 const char *p;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00004989
4990 if (!n)
4991 return;
4992 switch (n->type) {
4993 default:
4994#if DEBUG
4995 abort();
4996#endif
4997 case NPIPE:
4998 lp = n->npipe.cmdlist;
4999 for (;;) {
5000 cmdtxt(lp->n);
5001 lp = lp->next;
5002 if (!lp)
5003 break;
5004 cmdputs(" | ");
5005 }
5006 break;
5007 case NSEMI:
5008 p = "; ";
5009 goto binop;
5010 case NAND:
5011 p = " && ";
5012 goto binop;
5013 case NOR:
5014 p = " || ";
5015 binop:
5016 cmdtxt(n->nbinary.ch1);
5017 cmdputs(p);
5018 n = n->nbinary.ch2;
5019 goto donode;
5020 case NREDIR:
5021 case NBACKGND:
5022 n = n->nredir.n;
5023 goto donode;
5024 case NNOT:
5025 cmdputs("!");
5026 n = n->nnot.com;
5027 donode:
5028 cmdtxt(n);
5029 break;
5030 case NIF:
5031 cmdputs("if ");
5032 cmdtxt(n->nif.test);
5033 cmdputs("; then ");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005034 if (n->nif.elsepart) {
Denys Vlasenko7cee00e2009-07-24 01:08:03 +02005035 cmdtxt(n->nif.ifpart);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005036 cmdputs("; else ");
5037 n = n->nif.elsepart;
Denys Vlasenko7cee00e2009-07-24 01:08:03 +02005038 } else {
5039 n = n->nif.ifpart;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005040 }
5041 p = "; fi";
5042 goto dotail;
5043 case NSUBSHELL:
5044 cmdputs("(");
5045 n = n->nredir.n;
5046 p = ")";
5047 goto dotail;
5048 case NWHILE:
5049 p = "while ";
5050 goto until;
5051 case NUNTIL:
5052 p = "until ";
5053 until:
5054 cmdputs(p);
5055 cmdtxt(n->nbinary.ch1);
5056 n = n->nbinary.ch2;
5057 p = "; done";
5058 dodo:
5059 cmdputs("; do ");
5060 dotail:
5061 cmdtxt(n);
5062 goto dotail2;
5063 case NFOR:
5064 cmdputs("for ");
5065 cmdputs(n->nfor.var);
5066 cmdputs(" in ");
5067 cmdlist(n->nfor.args, 1);
5068 n = n->nfor.body;
5069 p = "; done";
5070 goto dodo;
5071 case NDEFUN:
Denys Vlasenko675d24a2018-01-27 22:02:05 +01005072 cmdputs(n->ndefun.text);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005073 p = "() { ... }";
5074 goto dotail2;
5075 case NCMD:
5076 cmdlist(n->ncmd.args, 1);
5077 cmdlist(n->ncmd.redirect, 0);
5078 break;
5079 case NARG:
5080 p = n->narg.text;
5081 dotail2:
5082 cmdputs(p);
5083 break;
5084 case NHERE:
5085 case NXHERE:
5086 p = "<<...";
5087 goto dotail2;
5088 case NCASE:
5089 cmdputs("case ");
5090 cmdputs(n->ncase.expr->narg.text);
5091 cmdputs(" in ");
5092 for (np = n->ncase.cases; np; np = np->nclist.next) {
5093 cmdtxt(np->nclist.pattern);
5094 cmdputs(") ");
5095 cmdtxt(np->nclist.body);
5096 cmdputs(";; ");
5097 }
5098 p = "esac";
5099 goto dotail2;
5100 case NTO:
5101 p = ">";
5102 goto redir;
5103 case NCLOBBER:
5104 p = ">|";
5105 goto redir;
5106 case NAPPEND:
5107 p = ">>";
5108 goto redir;
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01005109#if BASH_REDIR_OUTPUT
Denis Vlasenko559691a2008-10-05 18:39:31 +00005110 case NTO2:
5111#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005112 case NTOFD:
5113 p = ">&";
5114 goto redir;
5115 case NFROM:
5116 p = "<";
5117 goto redir;
5118 case NFROMFD:
5119 p = "<&";
5120 goto redir;
5121 case NFROMTO:
5122 p = "<>";
5123 redir:
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005124 cmdputs(utoa(n->nfile.fd));
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005125 cmdputs(p);
5126 if (n->type == NTOFD || n->type == NFROMFD) {
Denys Vlasenkod0fff912017-07-31 14:32:18 +02005127 if (n->ndup.dupfd >= 0)
5128 cmdputs(utoa(n->ndup.dupfd));
5129 else
5130 cmdputs("-");
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005131 break;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005132 }
5133 n = n->nfile.fname;
5134 goto donode;
5135 }
5136}
5137
5138static char *
5139commandtext(union node *n)
5140{
5141 char *name;
5142
5143 STARTSTACKSTR(cmdnextc);
5144 cmdtxt(n);
5145 name = stackblock();
Denys Vlasenko6a94cee2016-10-25 17:40:25 +02005146 TRACE(("commandtext: name %p, end %p\n", name, cmdnextc));
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005147 return ckstrdup(name);
5148}
5149#endif /* JOBS */
5150
5151/*
5152 * Fork off a subshell. If we are doing job control, give the subshell its
5153 * own process group. Jp is a job structure that the job is to be added to.
5154 * N is the command that will be evaluated by the child. Both jp and n may
5155 * be NULL. The mode parameter can be one of the following:
5156 * FORK_FG - Fork off a foreground process.
5157 * FORK_BG - Fork off a background process.
5158 * FORK_NOJOB - Like FORK_FG, but don't give the process its own
5159 * process group even if job control is on.
5160 *
5161 * When job control is turned off, background processes have their standard
5162 * input redirected to /dev/null (except for the second and later processes
5163 * in a pipeline).
5164 *
5165 * Called with interrupts off.
5166 */
5167/*
5168 * Clear traps on a fork.
5169 */
5170static void
5171clear_traps(void)
5172{
5173 char **tp;
5174
Denys Vlasenkob4f51d32016-10-27 12:55:09 +02005175 INT_OFF;
Roberto A. Fogliettae0bf3df2021-09-07 01:19:31 +02005176 for (tp = trap; tp <= &trap[NTRAP_LAST]; tp++) {
Denis Vlasenko991a1da2008-02-10 19:02:53 +00005177 if (*tp && **tp) { /* trap not NULL or "" (SIG_IGN) */
Denys Vlasenkoe305c282009-09-25 02:12:27 +02005178 if (trap_ptr == trap)
5179 free(*tp);
5180 /* else: it "belongs" to trap_ptr vector, don't free */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005181 *tp = NULL;
Roberto A. Fogliettae0bf3df2021-09-07 01:19:31 +02005182 if ((tp - trap) != 0 && (tp - trap) < NSIG)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005183 setsignal(tp - trap);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005184 }
5185 }
Alexander Shishkinccb97712010-07-25 13:07:39 +02005186 may_have_traps = 0;
Denys Vlasenkob4f51d32016-10-27 12:55:09 +02005187 INT_ON;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005188}
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00005189
5190/* Lives far away from here, needed for forkchild */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005191static void closescript(void);
Denis Vlasenko41770222007-10-07 18:02:52 +00005192
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00005193/* Called after fork(), in child */
Denys Vlasenko70392332016-10-27 02:31:55 +02005194/* jp and n are NULL when called by openhere() for heredoc support */
Denys Vlasenko21d87d42009-09-25 00:06:51 +02005195static NOINLINE void
Denys Vlasenkoe56f22a2009-07-24 00:16:59 +02005196forkchild(struct job *jp, union node *n, int mode)
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005197{
5198 int oldlvl;
5199
5200 TRACE(("Child shell %d\n", getpid()));
5201 oldlvl = shlvl;
5202 shlvl++;
5203
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00005204 /* man bash: "Non-builtin commands run by bash have signal handlers
5205 * set to the values inherited by the shell from its parent".
5206 * Do we do it correctly? */
5207
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005208 closescript();
Denys Vlasenko844f9902009-09-23 03:25:52 +02005209
5210 if (mode == FORK_NOJOB /* is it `xxx` ? */
5211 && n && n->type == NCMD /* is it single cmd? */
5212 /* && n->ncmd.args->type == NARG - always true? */
Denys Vlasenko74269202010-02-21 01:26:42 +01005213 && n->ncmd.args && strcmp(n->ncmd.args->narg.text, "trap") == 0
Denys Vlasenko844f9902009-09-23 03:25:52 +02005214 && n->ncmd.args->narg.next == NULL /* "trap" with no arguments */
5215 /* && n->ncmd.args->narg.backquote == NULL - do we need to check this? */
5216 ) {
5217 TRACE(("Trap hack\n"));
5218 /* Awful hack for `trap` or $(trap).
5219 *
5220 * http://www.opengroup.org/onlinepubs/009695399/utilities/trap.html
5221 * contains an example where "trap" is executed in a subshell:
5222 *
5223 * save_traps=$(trap)
5224 * ...
5225 * eval "$save_traps"
5226 *
5227 * Standard does not say that "trap" in subshell shall print
5228 * parent shell's traps. It only says that its output
5229 * must have suitable form, but then, in the above example
5230 * (which is not supposed to be normative), it implies that.
5231 *
5232 * bash (and probably other shell) does implement it
5233 * (traps are reset to defaults, but "trap" still shows them),
5234 * but as a result, "trap" logic is hopelessly messed up:
5235 *
5236 * # trap
5237 * trap -- 'echo Ho' SIGWINCH <--- we have a handler
5238 * # (trap) <--- trap is in subshell - no output (correct, traps are reset)
5239 * # true | trap <--- trap is in subshell - no output (ditto)
5240 * # echo `true | trap` <--- in subshell - output (but traps are reset!)
5241 * trap -- 'echo Ho' SIGWINCH
5242 * # echo `(trap)` <--- in subshell in subshell - output
5243 * trap -- 'echo Ho' SIGWINCH
5244 * # echo `true | (trap)` <--- in subshell in subshell in subshell - output!
5245 * trap -- 'echo Ho' SIGWINCH
5246 *
5247 * The rules when to forget and when to not forget traps
5248 * get really complex and nonsensical.
5249 *
5250 * Our solution: ONLY bare $(trap) or `trap` is special.
5251 */
Denys Vlasenko8f88d852009-09-25 12:12:53 +02005252 /* Save trap handler strings for trap builtin to print */
Ron Yorstond840c5d2015-07-19 23:05:20 +02005253 trap_ptr = xmemdup(trap, sizeof(trap));
Denys Vlasenko8f88d852009-09-25 12:12:53 +02005254 /* Fall through into clearing traps */
Denys Vlasenko844f9902009-09-23 03:25:52 +02005255 }
Denys Vlasenkoe305c282009-09-25 02:12:27 +02005256 clear_traps();
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005257#if JOBS
5258 /* do job control only in root shell */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00005259 doing_jobctl = 0;
Denys Vlasenkob12553f2011-02-21 03:22:20 +01005260 if (mode != FORK_NOJOB && jp->jobctl && oldlvl == 0) {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005261 pid_t pgrp;
5262
5263 if (jp->nprocs == 0)
5264 pgrp = getpid();
5265 else
Denys Vlasenko285ad152009-12-04 23:02:27 +01005266 pgrp = jp->ps[0].ps_pid;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00005267 /* this can fail because we are doing it in the parent also */
5268 setpgid(0, pgrp);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005269 if (mode == FORK_FG)
5270 xtcsetpgrp(ttyfd, pgrp);
5271 setsignal(SIGTSTP);
5272 setsignal(SIGTTOU);
5273 } else
5274#endif
5275 if (mode == FORK_BG) {
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00005276 /* man bash: "When job control is not in effect,
5277 * asynchronous commands ignore SIGINT and SIGQUIT" */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005278 ignoresig(SIGINT);
5279 ignoresig(SIGQUIT);
5280 if (jp->nprocs == 0) {
5281 close(0);
5282 if (open(bb_dev_null, O_RDONLY) != 0)
Denys Vlasenko12ffefb2017-08-23 14:52:56 +02005283 ash_msg_and_raise_perror("can't open '%s'", bb_dev_null);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005284 }
5285 }
Denys Vlasenkob12553f2011-02-21 03:22:20 +01005286 if (oldlvl == 0) {
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00005287 if (iflag) { /* why if iflag only? */
5288 setsignal(SIGINT);
5289 setsignal(SIGTERM);
5290 }
5291 /* man bash:
5292 * "In all cases, bash ignores SIGQUIT. Non-builtin
5293 * commands run by bash have signal handlers
5294 * set to the values inherited by the shell
5295 * from its parent".
5296 * Take care of the second rule: */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005297 setsignal(SIGQUIT);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005298 }
Denys Vlasenkoe56f22a2009-07-24 00:16:59 +02005299#if JOBS
Denys Vlasenko844f9902009-09-23 03:25:52 +02005300 if (n && n->type == NCMD
Denys Vlasenko74269202010-02-21 01:26:42 +01005301 && n->ncmd.args && strcmp(n->ncmd.args->narg.text, "jobs") == 0
Denys Vlasenko844f9902009-09-23 03:25:52 +02005302 ) {
Denys Vlasenkoe56f22a2009-07-24 00:16:59 +02005303 TRACE(("Job hack\n"));
Denys Vlasenko844f9902009-09-23 03:25:52 +02005304 /* "jobs": we do not want to clear job list for it,
5305 * instead we remove only _its_ own_ job from job list.
5306 * This makes "jobs .... | cat" more useful.
5307 */
Denys Vlasenkoe56f22a2009-07-24 00:16:59 +02005308 freejob(curjob);
5309 return;
5310 }
5311#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005312 for (jp = curjob; jp; jp = jp->prev_job)
5313 freejob(jp);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005314}
5315
Denis Vlasenkobdc406d2007-07-15 01:13:25 +00005316/* Called after fork(), in parent */
Denis Vlasenko85c24712008-03-17 09:04:04 +00005317#if !JOBS
5318#define forkparent(jp, n, mode, pid) forkparent(jp, mode, pid)
5319#endif
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005320static void
5321forkparent(struct job *jp, union node *n, int mode, pid_t pid)
5322{
5323 TRACE(("In parent shell: child = %d\n", pid));
Denys Vlasenko47eb9792020-02-18 15:37:43 +01005324 if (!jp) /* jp is NULL when called by openhere() for heredoc support */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005325 return;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005326#if JOBS
5327 if (mode != FORK_NOJOB && jp->jobctl) {
5328 int pgrp;
5329
5330 if (jp->nprocs == 0)
5331 pgrp = pid;
5332 else
Denys Vlasenko285ad152009-12-04 23:02:27 +01005333 pgrp = jp->ps[0].ps_pid;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005334 /* This can fail because we are doing it in the child also */
5335 setpgid(pid, pgrp);
5336 }
5337#endif
5338 if (mode == FORK_BG) {
5339 backgndpid = pid; /* set $! */
5340 set_curjob(jp, CUR_RUNNING);
5341 }
5342 if (jp) {
5343 struct procstat *ps = &jp->ps[jp->nprocs++];
Denys Vlasenko285ad152009-12-04 23:02:27 +01005344 ps->ps_pid = pid;
5345 ps->ps_status = -1;
5346 ps->ps_cmd = nullstr;
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005347#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +00005348 if (doing_jobctl && n)
Denys Vlasenko285ad152009-12-04 23:02:27 +01005349 ps->ps_cmd = commandtext(n);
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005350#endif
5351 }
5352}
5353
Denys Vlasenko70392332016-10-27 02:31:55 +02005354/* jp and n are NULL when called by openhere() for heredoc support */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005355static int
5356forkshell(struct job *jp, union node *n, int mode)
5357{
5358 int pid;
5359
5360 TRACE(("forkshell(%%%d, %p, %d) called\n", jobno(jp), n, mode));
5361 pid = fork();
5362 if (pid < 0) {
5363 TRACE(("Fork failed, errno=%d", errno));
5364 if (jp)
5365 freejob(jp);
Denys Vlasenko12ffefb2017-08-23 14:52:56 +02005366 ash_msg_and_raise_perror("can't fork");
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005367 }
Denys Vlasenko76ace252009-10-12 15:25:01 +02005368 if (pid == 0) {
5369 CLEAR_RANDOM_T(&random_gen); /* or else $RANDOM repeats in child */
Denys Vlasenkoe56f22a2009-07-24 00:16:59 +02005370 forkchild(jp, n, mode);
Denys Vlasenko76ace252009-10-12 15:25:01 +02005371 } else {
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005372 forkparent(jp, n, mode, pid);
Denys Vlasenko76ace252009-10-12 15:25:01 +02005373 }
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005374 return pid;
5375}
5376
5377/*
5378 * Wait for job to finish.
5379 *
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00005380 * Under job control we have the problem that while a child process
5381 * is running interrupts generated by the user are sent to the child
5382 * but not to the shell. This means that an infinite loop started by
5383 * an interactive user may be hard to kill. With job control turned off,
5384 * an interactive user may place an interactive program inside a loop.
5385 * If the interactive program catches interrupts, the user doesn't want
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005386 * these interrupts to also abort the loop. The approach we take here
5387 * is to have the shell ignore interrupt signals while waiting for a
5388 * foreground process to terminate, and then send itself an interrupt
5389 * signal if the child process was terminated by an interrupt signal.
5390 * Unfortunately, some programs want to do a bit of cleanup and then
5391 * exit on interrupt; unless these processes terminate themselves by
5392 * sending a signal to themselves (instead of calling exit) they will
5393 * confuse this approach.
5394 *
5395 * Called with interrupts off.
5396 */
5397static int
5398waitforjob(struct job *jp)
5399{
5400 int st;
5401
Denys Vlasenkod81af722020-02-18 14:28:30 +01005402 TRACE(("waitforjob(%%%d) called\n", jp ? jobno(jp) : 0));
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00005403
Denys Vlasenko47eb9792020-02-18 15:37:43 +01005404 /* In non-interactive shells, we _can_ get
5405 * a keyboard signal here and be EINTRed, but we just loop
5406 * inside dowait(), waiting for command to complete.
5407 *
5408 * man bash:
5409 * "If bash is waiting for a command to complete and receives
5410 * a signal for which a trap has been set, the trap
5411 * will not be executed until the command completes."
5412 *
5413 * Reality is that even if trap is not set, bash
5414 * will not act on the signal until command completes.
5415 * Try this. sleep5intoff.c:
5416 * #include <signal.h>
5417 * #include <unistd.h>
5418 * int main() {
5419 * sigset_t set;
5420 * sigemptyset(&set);
5421 * sigaddset(&set, SIGINT);
5422 * sigaddset(&set, SIGQUIT);
5423 * sigprocmask(SIG_BLOCK, &set, NULL);
5424 * sleep(5);
5425 * return 0;
5426 * }
5427 * $ bash -c './sleep5intoff; echo hi'
5428 * ^C^C^C^C <--- pressing ^C once a second
5429 * $ _
5430 * $ bash -c './sleep5intoff; echo hi'
5431 * ^\^\^\^\hi <--- pressing ^\ (SIGQUIT)
5432 * $ _
5433 */
5434 dowait(jp ? DOWAIT_BLOCK : DOWAIT_NONBLOCK, jp);
5435 if (!jp)
Denys Vlasenko97edfc42020-02-18 14:37:56 +01005436 return exitstatus;
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00005437
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005438 st = getstatus(jp);
5439#if JOBS
5440 if (jp->jobctl) {
5441 xtcsetpgrp(ttyfd, rootpid);
Denys Vlasenko098b7132017-01-11 19:59:03 +01005442 restore_tty_if_stopped_or_signaled(jp);
5443
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005444 /*
5445 * This is truly gross.
5446 * If we're doing job control, then we did a TIOCSPGRP which
5447 * caused us (the shell) to no longer be in the controlling
5448 * session -- so we wouldn't have seen any ^C/SIGINT. So, we
5449 * intuit from the subprocess exit status whether a SIGINT
5450 * occurred, and if so interrupt ourselves. Yuck. - mycroft
5451 */
Denis Vlasenko991a1da2008-02-10 19:02:53 +00005452 if (jp->sigint) /* TODO: do the same with all signals */
5453 raise(SIGINT); /* ... by raise(jp->sig) instead? */
Denis Vlasenkoa8915072007-02-23 21:10:06 +00005454 }
5455 if (jp->state == JOBDONE)
5456#endif
5457 freejob(jp);
5458 return st;
5459}
5460
5461/*
5462 * return 1 if there are stopped jobs, otherwise 0
5463 */
5464static int
5465stoppedjobs(void)
5466{
5467 struct job *jp;
5468 int retval;
5469
5470 retval = 0;
5471 if (job_warning)
5472 goto out;
5473 jp = curjob;
5474 if (jp && jp->state == JOBSTOPPED) {
5475 out2str("You have stopped jobs.\n");
5476 job_warning = 2;
5477 retval++;
5478 }
5479 out:
5480 return retval;
5481}
5482
5483
Denys Vlasenko70392332016-10-27 02:31:55 +02005484/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005485 * Code for dealing with input/output redirection.
5486 */
5487
Denys Vlasenko8d0e0cd2011-01-25 23:21:46 +01005488#undef EMPTY
5489#undef CLOSED
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005490#define EMPTY -2 /* marks an unused slot in redirtab */
Denys Vlasenko035486c2017-07-31 04:09:19 +02005491#define CLOSED -1 /* marks a slot of previously-closed fd */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005492
5493/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005494 * Handle here documents. Normally we fork off a process to write the
5495 * data to a pipe. If the document is short, we can stuff the data in
5496 * the pipe without forking.
5497 */
5498/* openhere needs this forward reference */
Denys Vlasenkoc2058ec2020-02-22 20:25:03 +01005499static void expandhere(union node *arg);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005500static int
5501openhere(union node *redir)
5502{
Denys Vlasenkoc2058ec2020-02-22 20:25:03 +01005503 char *p;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005504 int pip[2];
5505 size_t len = 0;
5506
5507 if (pipe(pip) < 0)
Denys Vlasenko12ffefb2017-08-23 14:52:56 +02005508 ash_msg_and_raise_perror("can't create pipe");
Denys Vlasenkoc2058ec2020-02-22 20:25:03 +01005509
5510 p = redir->nhere.doc->narg.text;
5511 if (redir->type == NXHERE) {
5512 expandhere(redir->nhere.doc);
5513 p = stackblock();
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005514 }
Denys Vlasenkoc2058ec2020-02-22 20:25:03 +01005515
5516 len = strlen(p);
5517 if (len <= PIPE_BUF) {
5518 xwrite(pip[1], p, len);
5519 goto out;
5520 }
5521
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005522 if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) {
Denis Vlasenko0b769642008-07-24 07:54:57 +00005523 /* child */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005524 close(pip[0]);
Denis Vlasenkof8535cc2008-12-03 10:36:26 +00005525 ignoresig(SIGINT); //signal(SIGINT, SIG_IGN);
5526 ignoresig(SIGQUIT); //signal(SIGQUIT, SIG_IGN);
5527 ignoresig(SIGHUP); //signal(SIGHUP, SIG_IGN);
5528 ignoresig(SIGTSTP); //signal(SIGTSTP, SIG_IGN);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005529 signal(SIGPIPE, SIG_DFL);
Denys Vlasenkoc2058ec2020-02-22 20:25:03 +01005530 xwrite(pip[1], p, len);
Bernhard Reutner-Fischer636a1f82008-05-19 09:29:47 +00005531 _exit(EXIT_SUCCESS);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005532 }
5533 out:
5534 close(pip[1]);
5535 return pip[0];
5536}
5537
5538static int
5539openredirect(union node *redir)
5540{
Denys Vlasenkof1a5cb02017-07-25 17:47:48 +02005541 struct stat sb;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005542 char *fname;
5543 int f;
5544
5545 switch (redir->nfile.type) {
Denys Vlasenko557482c2016-09-25 21:24:04 +02005546/* Can't happen, our single caller does this itself */
5547// case NTOFD:
5548// case NFROMFD:
5549// return -1;
5550 case NHERE:
5551 case NXHERE:
5552 return openhere(redir);
5553 }
5554
5555 /* For N[X]HERE, reading redir->nfile.expfname would touch beyond
5556 * allocated space. Do it only when we know it is safe.
5557 */
5558 fname = redir->nfile.expfname;
5559
5560 switch (redir->nfile.type) {
5561 default:
5562#if DEBUG
5563 abort();
5564#endif
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005565 case NFROM:
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005566 f = open(fname, O_RDONLY);
5567 if (f < 0)
5568 goto eopen;
5569 break;
5570 case NFROMTO:
Andreas Bühmannda75f442010-06-24 04:32:37 +02005571 f = open(fname, O_RDWR|O_CREAT, 0666);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005572 if (f < 0)
5573 goto ecreate;
5574 break;
5575 case NTO:
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01005576#if BASH_REDIR_OUTPUT
Denis Vlasenko559691a2008-10-05 18:39:31 +00005577 case NTO2:
5578#endif
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005579 /* Take care of noclobber mode. */
5580 if (Cflag) {
Denys Vlasenkof1a5cb02017-07-25 17:47:48 +02005581 if (stat(fname, &sb) < 0) {
5582 f = open(fname, O_WRONLY|O_CREAT|O_EXCL, 0666);
5583 if (f < 0)
5584 goto ecreate;
5585 } else if (!S_ISREG(sb.st_mode)) {
5586 f = open(fname, O_WRONLY, 0666);
5587 if (f < 0)
5588 goto ecreate;
Denys Vlasenko355ec352018-04-02 13:34:57 +02005589 if (!fstat(f, &sb) && S_ISREG(sb.st_mode)) {
Denys Vlasenkof1a5cb02017-07-25 17:47:48 +02005590 close(f);
5591 errno = EEXIST;
5592 goto ecreate;
5593 }
5594 } else {
5595 errno = EEXIST;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005596 goto ecreate;
Denys Vlasenkof1a5cb02017-07-25 17:47:48 +02005597 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005598 break;
5599 }
5600 /* FALLTHROUGH */
5601 case NCLOBBER:
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005602 f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666);
5603 if (f < 0)
5604 goto ecreate;
5605 break;
5606 case NAPPEND:
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005607 f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666);
5608 if (f < 0)
5609 goto ecreate;
5610 break;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005611 }
5612
5613 return f;
5614 ecreate:
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +00005615 ash_msg_and_raise_error("can't create %s: %s", fname, errmsg(errno, "nonexistent directory"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005616 eopen:
Bernhard Reutner-Fischera53de7f2008-07-21 13:46:54 +00005617 ash_msg_and_raise_error("can't open %s: %s", fname, errmsg(errno, "no such file"));
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005618}
5619
5620/*
Denys Vlasenko64774602016-10-26 15:24:30 +02005621 * Copy a file descriptor to be >= 10. Throws exception on error.
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005622 */
5623static int
Denys Vlasenko64774602016-10-26 15:24:30 +02005624savefd(int from)
5625{
5626 int newfd;
5627 int err;
5628
Denys Vlasenko60fb98e2018-03-30 22:15:14 +02005629 newfd = fcntl(from, F_DUPFD_CLOEXEC, 10);
Denys Vlasenko64774602016-10-26 15:24:30 +02005630 err = newfd < 0 ? errno : 0;
5631 if (err != EBADF) {
5632 if (err)
Ron Yorstonbe366e52017-07-27 13:53:39 +01005633 ash_msg_and_raise_perror("%d", from);
Denys Vlasenko64774602016-10-26 15:24:30 +02005634 close(from);
Denys Vlasenko60fb98e2018-03-30 22:15:14 +02005635 if (F_DUPFD_CLOEXEC == F_DUPFD)
5636 close_on_exec_on(newfd);
Denys Vlasenko64774602016-10-26 15:24:30 +02005637 }
5638
5639 return newfd;
5640}
5641static int
5642dup2_or_raise(int from, int to)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005643{
5644 int newfd;
5645
Denys Vlasenko64774602016-10-26 15:24:30 +02005646 newfd = (from != to) ? dup2(from, to) : to;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005647 if (newfd < 0) {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005648 /* Happens when source fd is not open: try "echo >&99" */
Ron Yorstonbe366e52017-07-27 13:53:39 +01005649 ash_msg_and_raise_perror("%d", from);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005650 }
5651 return newfd;
5652}
Denys Vlasenko035486c2017-07-31 04:09:19 +02005653static int
Denys Vlasenko9acd63c2018-03-28 18:35:07 +02005654dup_CLOEXEC(int fd, int avoid_fd)
Denys Vlasenko035486c2017-07-31 04:09:19 +02005655{
5656 int newfd;
5657 repeat:
Denys Vlasenko9acd63c2018-03-28 18:35:07 +02005658 newfd = fcntl(fd, F_DUPFD_CLOEXEC, avoid_fd + 1);
5659 if (newfd >= 0) {
5660 if (F_DUPFD_CLOEXEC == F_DUPFD) /* if old libc (w/o F_DUPFD_CLOEXEC) */
Denys Vlasenko60fb98e2018-03-30 22:15:14 +02005661 close_on_exec_on(newfd);
Denys Vlasenko9acd63c2018-03-28 18:35:07 +02005662 } else { /* newfd < 0 */
Denys Vlasenko035486c2017-07-31 04:09:19 +02005663 if (errno == EBUSY)
5664 goto repeat;
5665 if (errno == EINTR)
5666 goto repeat;
5667 }
5668 return newfd;
5669}
5670static int
5671xdup_CLOEXEC_and_close(int fd, int avoid_fd)
5672{
5673 int newfd;
5674 repeat:
Denys Vlasenko60fb98e2018-03-30 22:15:14 +02005675 newfd = fcntl(fd, F_DUPFD_CLOEXEC, avoid_fd + 1);
Denys Vlasenko035486c2017-07-31 04:09:19 +02005676 if (newfd < 0) {
5677 if (errno == EBUSY)
5678 goto repeat;
5679 if (errno == EINTR)
5680 goto repeat;
5681 /* fd was not open? */
5682 if (errno == EBADF)
5683 return fd;
5684 ash_msg_and_raise_perror("%d", newfd);
5685 }
Denys Vlasenko60fb98e2018-03-30 22:15:14 +02005686 if (F_DUPFD_CLOEXEC == F_DUPFD)
5687 close_on_exec_on(newfd);
Denys Vlasenko035486c2017-07-31 04:09:19 +02005688 close(fd);
5689 return newfd;
5690}
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005691
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005692/* Struct def and variable are moved down to the first usage site */
Denys Vlasenko035486c2017-07-31 04:09:19 +02005693struct squirrel {
5694 int orig_fd;
5695 int moved_to;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005696};
Denis Vlasenko0b769642008-07-24 07:54:57 +00005697struct redirtab {
5698 struct redirtab *next;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005699 int pair_count;
Denys Vlasenko035486c2017-07-31 04:09:19 +02005700 struct squirrel two_fd[];
Denis Vlasenko0b769642008-07-24 07:54:57 +00005701};
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005702#define redirlist (G_var.redirlist)
Denis Vlasenko0b769642008-07-24 07:54:57 +00005703
Denys Vlasenko035486c2017-07-31 04:09:19 +02005704static void
5705add_squirrel_closed(struct redirtab *sq, int fd)
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005706{
5707 int i;
5708
Denys Vlasenko035486c2017-07-31 04:09:19 +02005709 if (!sq)
5710 return;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005711
Denys Vlasenko035486c2017-07-31 04:09:19 +02005712 for (i = 0; sq->two_fd[i].orig_fd != EMPTY; i++) {
5713 /* If we collide with an already moved fd... */
5714 if (fd == sq->two_fd[i].orig_fd) {
5715 /* Examples:
5716 * "echo 3>FILE 3>&- 3>FILE"
5717 * "echo 3>&- 3>FILE"
5718 * No need for last redirect to insert
5719 * another "need to close 3" indicator.
5720 */
5721 TRACE(("redirect_fd %d: already moved or closed\n", fd));
5722 return;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005723 }
5724 }
Denys Vlasenko035486c2017-07-31 04:09:19 +02005725 TRACE(("redirect_fd %d: previous fd was closed\n", fd));
5726 sq->two_fd[i].orig_fd = fd;
5727 sq->two_fd[i].moved_to = CLOSED;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00005728}
5729
Denys Vlasenko37dc08b2016-10-02 04:38:07 +02005730static int
Denys Vlasenko035486c2017-07-31 04:09:19 +02005731save_fd_on_redirect(int fd, int avoid_fd, struct redirtab *sq)
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005732{
Denys Vlasenko035486c2017-07-31 04:09:19 +02005733 int i, new_fd;
Denis Vlasenko34c73c42008-08-16 11:48:02 +00005734
Denys Vlasenko035486c2017-07-31 04:09:19 +02005735 if (avoid_fd < 9) /* the important case here is that it can be -1 */
5736 avoid_fd = 9;
5737
5738#if JOBS
5739 if (fd == ttyfd) {
5740 /* Testcase: "ls -l /proc/$$/fd 10>&-" should work */
5741 ttyfd = xdup_CLOEXEC_and_close(ttyfd, avoid_fd);
5742 TRACE(("redirect_fd %d: matches ttyfd, moving it to %d\n", fd, ttyfd));
5743 return 1; /* "we closed fd" */
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005744 }
Denys Vlasenko035486c2017-07-31 04:09:19 +02005745#endif
5746 /* Are we called from redirect(0)? E.g. redirect
5747 * in a forked child. No need to save fds,
5748 * we aren't going to use them anymore, ok to trash.
5749 */
5750 if (!sq)
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005751 return 0;
Denys Vlasenko035486c2017-07-31 04:09:19 +02005752
5753 /* If this one of script's fds? */
5754 if (fd != 0) {
5755 struct parsefile *pf = g_parsefile;
5756 while (pf) {
5757 /* We skip fd == 0 case because of the following:
5758 * $ ash # running ash interactively
5759 * $ . ./script.sh
5760 * and in script.sh: "exec 9>&0".
5761 * Even though top-level pf_fd _is_ 0,
5762 * it's still ok to use it: "read" builtin uses it,
5763 * why should we cripple "exec" builtin?
5764 */
5765 if (fd == pf->pf_fd) {
5766 pf->pf_fd = xdup_CLOEXEC_and_close(fd, avoid_fd);
5767 return 1; /* "we closed fd" */
5768 }
5769 pf = pf->prev;
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005770 }
5771 }
Denys Vlasenko035486c2017-07-31 04:09:19 +02005772
5773 /* Check whether it collides with any open fds (e.g. stdio), save fds as needed */
5774
5775 /* First: do we collide with some already moved fds? */
5776 for (i = 0; sq->two_fd[i].orig_fd != EMPTY; i++) {
5777 /* If we collide with an already moved fd... */
5778 if (fd == sq->two_fd[i].moved_to) {
Denys Vlasenko9acd63c2018-03-28 18:35:07 +02005779 new_fd = dup_CLOEXEC(fd, avoid_fd);
Denys Vlasenko035486c2017-07-31 04:09:19 +02005780 sq->two_fd[i].moved_to = new_fd;
5781 TRACE(("redirect_fd %d: already busy, moving to %d\n", fd, new_fd));
5782 if (new_fd < 0) /* what? */
5783 xfunc_die();
5784 return 0; /* "we did not close fd" */
5785 }
5786 if (fd == sq->two_fd[i].orig_fd) {
5787 /* Example: echo Hello >/dev/null 1>&2 */
5788 TRACE(("redirect_fd %d: already moved\n", fd));
5789 return 0; /* "we did not close fd" */
5790 }
5791 }
5792
5793 /* 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 +02005794 new_fd = dup_CLOEXEC(fd, avoid_fd);
Denys Vlasenko035486c2017-07-31 04:09:19 +02005795 TRACE(("redirect_fd %d: previous fd is moved to %d (-1 if it was closed)\n", fd, new_fd));
5796 if (new_fd < 0) {
5797 if (errno != EBADF)
5798 xfunc_die();
5799 /* new_fd = CLOSED; - already is -1 */
5800 }
5801 sq->two_fd[i].moved_to = new_fd;
5802 sq->two_fd[i].orig_fd = fd;
5803
5804 /* if we move stderr, let "set -x" code know */
5805 if (fd == preverrout_fd)
5806 preverrout_fd = new_fd;
5807
5808 return 0; /* "we did not close fd" */
Denis Vlasenko6a0ad252008-07-25 13:34:05 +00005809}
5810
Denys Vlasenko32fdf2f2017-07-31 04:32:06 +02005811static int
5812internally_opened_fd(int fd, struct redirtab *sq)
5813{
5814 int i;
5815#if JOBS
5816 if (fd == ttyfd)
5817 return 1;
5818#endif
5819 /* If this one of script's fds? */
5820 if (fd != 0) {
5821 struct parsefile *pf = g_parsefile;
5822 while (pf) {
5823 if (fd == pf->pf_fd)
5824 return 1;
5825 pf = pf->prev;
5826 }
5827 }
5828
5829 if (sq) for (i = 0; i < sq->pair_count && sq->two_fd[i].orig_fd != EMPTY; i++) {
5830 if (fd == sq->two_fd[i].moved_to)
5831 return 1;
5832 }
5833 return 0;
5834}
5835
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005836/*
5837 * Process a list of redirection commands. If the REDIR_PUSH flag is set,
5838 * old file descriptors are stashed away so that the redirection can be
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +02005839 * undone by calling popredir.
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005840 */
5841/* flags passed to redirect */
5842#define REDIR_PUSH 01 /* save previous values of file descriptors */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005843static void
5844redirect(union node *redir, int flags)
5845{
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005846 struct redirtab *sv;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005847
Denys Vlasenko1c79aeb2017-07-29 22:51:52 +02005848 if (!redir)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005849 return;
Denys Vlasenko035486c2017-07-31 04:09:19 +02005850
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005851 sv = NULL;
5852 INT_OFF;
Denys Vlasenko1c79aeb2017-07-29 22:51:52 +02005853 if (flags & REDIR_PUSH)
5854 sv = redirlist;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005855 do {
Denys Vlasenko035486c2017-07-31 04:09:19 +02005856 int fd;
5857 int newfd;
5858 int close_fd;
5859 int closed;
5860
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00005861 fd = redir->nfile.fd;
Denis Vlasenko0b769642008-07-24 07:54:57 +00005862 if (redir->nfile.type == NTOFD || redir->nfile.type == NFROMFD) {
Denys Vlasenko035486c2017-07-31 04:09:19 +02005863 //bb_error_msg("doing %d > %d", fd, newfd);
5864 newfd = redir->ndup.dupfd;
5865 close_fd = -1;
Denis Vlasenko0b769642008-07-24 07:54:57 +00005866 } else {
5867 newfd = openredirect(redir); /* always >= 0 */
5868 if (fd == newfd) {
Denys Vlasenko035486c2017-07-31 04:09:19 +02005869 /* open() gave us precisely the fd we wanted.
5870 * This means that this fd was not busy
5871 * (not opened to anywhere).
5872 * Remember to close it on restore:
5873 */
5874 add_squirrel_closed(sv, fd);
Denis Vlasenko0b769642008-07-24 07:54:57 +00005875 continue;
5876 }
Denys Vlasenko035486c2017-07-31 04:09:19 +02005877 close_fd = newfd;
Denis Vlasenko7d75a962007-11-22 08:16:57 +00005878 }
Denys Vlasenko035486c2017-07-31 04:09:19 +02005879
5880 if (fd == newfd)
5881 continue;
5882
5883 /* if "N>FILE": move newfd to fd */
5884 /* if "N>&M": dup newfd to fd */
5885 /* if "N>&-": close fd (newfd is -1) */
5886
5887 IF_BASH_REDIR_OUTPUT(redirect_more:)
5888
5889 closed = save_fd_on_redirect(fd, /*avoid:*/ newfd, sv);
5890 if (newfd == -1) {
5891 /* "N>&-" means "close me" */
5892 if (!closed) {
5893 /* ^^^ optimization: saving may already
5894 * have closed it. If not... */
5895 close(fd);
Denis Vlasenko22f74142008-07-24 22:34:43 +00005896 }
Denys Vlasenko035486c2017-07-31 04:09:19 +02005897 } else {
Denys Vlasenko32fdf2f2017-07-31 04:32:06 +02005898 /* if newfd is a script fd or saved fd, simulate EBADF */
5899 if (internally_opened_fd(newfd, sv)) {
5900 errno = EBADF;
5901 ash_msg_and_raise_perror("%d", newfd);
5902 }
Denys Vlasenko64774602016-10-26 15:24:30 +02005903 dup2_or_raise(newfd, fd);
Denys Vlasenko035486c2017-07-31 04:09:19 +02005904 if (close_fd >= 0) /* "N>FILE" or ">&FILE" or heredoc? */
5905 close(close_fd);
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01005906#if BASH_REDIR_OUTPUT
Denys Vlasenko035486c2017-07-31 04:09:19 +02005907 if (redir->nfile.type == NTO2 && fd == 1) {
5908 /* ">&FILE". we already redirected to 1, now copy 1 to 2 */
5909 fd = 2;
5910 newfd = 1;
5911 close_fd = -1;
5912 goto redirect_more;
5913 }
Denis Vlasenko559691a2008-10-05 18:39:31 +00005914#endif
Denis Vlasenko8d924ec2008-07-24 11:34:27 +00005915 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00005916 } while ((redir = redir->nfile.next) != NULL);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005917 INT_ON;
Denys Vlasenkod07a15b2017-07-30 16:51:05 +02005918
5919//dash:#define REDIR_SAVEFD2 03 /* set preverrout */
5920#define REDIR_SAVEFD2 0
5921 // dash has a bug: since REDIR_SAVEFD2=3 and REDIR_PUSH=1, this test
5922 // triggers for pure REDIR_PUSH too. Thus, this is done almost always,
5923 // not only for calls with flags containing REDIR_SAVEFD2.
Denys Vlasenko035486c2017-07-31 04:09:19 +02005924 // We do this unconditionally (see save_fd_on_redirect()).
Denys Vlasenkod07a15b2017-07-30 16:51:05 +02005925 //if ((flags & REDIR_SAVEFD2) && copied_fd2 >= 0)
5926 // preverrout_fd = copied_fd2;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005927}
5928
Denys Vlasenko170f93e2017-07-29 18:54:53 +02005929static int
5930redirectsafe(union node *redir, int flags)
5931{
5932 int err;
5933 volatile int saveint;
5934 struct jmploc *volatile savehandler = exception_handler;
5935 struct jmploc jmploc;
5936
5937 SAVE_INT(saveint);
5938 /* "echo 9>/dev/null; echo >&9; echo result: $?" - result should be 1, not 2! */
Denys Vlasenko035486c2017-07-31 04:09:19 +02005939 err = setjmp(jmploc.loc); /* was = setjmp(jmploc.loc) * 2; */
Denys Vlasenko170f93e2017-07-29 18:54:53 +02005940 if (!err) {
5941 exception_handler = &jmploc;
5942 redirect(redir, flags);
5943 }
5944 exception_handler = savehandler;
5945 if (err && exception_type != EXERROR)
5946 longjmp(exception_handler->loc, 1);
5947 RESTORE_INT(saveint);
5948 return err;
5949}
5950
Ron Yorstona1b0d382020-07-23 08:32:27 +01005951#if BASH_PROCESS_SUBST
5952static void
5953pushfd(int fd)
5954{
5955 struct redirtab *sv;
5956
5957 sv = ckzalloc(sizeof(*sv) + sizeof(sv->two_fd[0]));
5958 sv->pair_count = 1;
5959 sv->two_fd[0].orig_fd = fd;
5960 sv->two_fd[0].moved_to = CLOSED;
5961 sv->next = redirlist;
5962 redirlist = sv;
5963}
5964#endif
5965
Denys Vlasenko1c79aeb2017-07-29 22:51:52 +02005966static struct redirtab*
5967pushredir(union node *redir)
5968{
5969 struct redirtab *sv;
5970 int i;
5971
5972 if (!redir)
5973 return redirlist;
5974
5975 i = 0;
5976 do {
5977 i++;
5978#if BASH_REDIR_OUTPUT
5979 if (redir->nfile.type == NTO2)
5980 i++;
5981#endif
5982 redir = redir->nfile.next;
5983 } while (redir);
5984
5985 sv = ckzalloc(sizeof(*sv) + i * sizeof(sv->two_fd[0]));
5986 sv->pair_count = i;
5987 while (--i >= 0)
Denys Vlasenko035486c2017-07-31 04:09:19 +02005988 sv->two_fd[i].orig_fd = sv->two_fd[i].moved_to = EMPTY;
Denys Vlasenko1c79aeb2017-07-29 22:51:52 +02005989 sv->next = redirlist;
5990 redirlist = sv;
5991 return sv->next;
5992}
5993
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005994/*
5995 * Undo the effects of the last redirection.
5996 */
5997static void
Denys Vlasenko035486c2017-07-31 04:09:19 +02005998popredir(int drop)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00005999{
6000 struct redirtab *rp;
6001 int i;
6002
Denys Vlasenkoeaf94362016-10-25 21:46:03 +02006003 if (redirlist == NULL)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00006004 return;
6005 INT_OFF;
6006 rp = redirlist;
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00006007 for (i = 0; i < rp->pair_count; i++) {
Denys Vlasenko035486c2017-07-31 04:09:19 +02006008 int fd = rp->two_fd[i].orig_fd;
6009 int copy = rp->two_fd[i].moved_to;
Denis Vlasenko22f74142008-07-24 22:34:43 +00006010 if (copy == CLOSED) {
Denis Vlasenko7d75a962007-11-22 08:16:57 +00006011 if (!drop)
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +00006012 close(fd);
Denis Vlasenko7d75a962007-11-22 08:16:57 +00006013 continue;
6014 }
Denis Vlasenko22f74142008-07-24 22:34:43 +00006015 if (copy != EMPTY) {
Denys Vlasenko035486c2017-07-31 04:09:19 +02006016 if (!drop) {
Denis Vlasenko5a867312008-07-24 19:46:38 +00006017 /*close(fd);*/
Denys Vlasenko64774602016-10-26 15:24:30 +02006018 dup2_or_raise(copy, fd);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00006019 }
Denys Vlasenko035486c2017-07-31 04:09:19 +02006020 close(copy);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00006021 }
6022 }
6023 redirlist = rp->next;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00006024 free(rp);
6025 INT_ON;
6026}
6027
Denys Vlasenko1c79aeb2017-07-29 22:51:52 +02006028static void
6029unwindredir(struct redirtab *stop)
6030{
6031 while (redirlist != stop)
Denys Vlasenko035486c2017-07-31 04:09:19 +02006032 popredir(/*drop:*/ 0);
Denys Vlasenko1c79aeb2017-07-29 22:51:52 +02006033}
6034
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00006035
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006036/* ============ Routines to expand arguments to commands
6037 *
6038 * We have to deal with backquotes, shell variables, and file metacharacters.
6039 */
6040
Denys Vlasenko0b883582016-12-23 16:49:07 +01006041#if ENABLE_FEATURE_SH_MATH
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00006042static arith_t
6043ash_arith(const char *s)
6044{
Denys Vlasenko06d44d72010-09-13 12:49:03 +02006045 arith_state_t math_state;
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00006046 arith_t result;
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00006047
Denys Vlasenko06d44d72010-09-13 12:49:03 +02006048 math_state.lookupvar = lookupvar;
Denys Vlasenko0a0acb52015-04-18 19:36:38 +02006049 math_state.setvar = setvar0;
Denys Vlasenko06d44d72010-09-13 12:49:03 +02006050 //math_state.endofname = endofname;
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00006051
6052 INT_OFF;
Denys Vlasenko06d44d72010-09-13 12:49:03 +02006053 result = arith(&math_state, s);
Denys Vlasenko063847d2010-09-15 13:33:02 +02006054 if (math_state.errmsg)
6055 ash_msg_and_raise_error(math_state.errmsg);
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +00006056 INT_ON;
6057
6058 return result;
6059}
Denis Vlasenko448d30e2008-06-27 00:24:11 +00006060#endif
Denys Vlasenkobaa41c72018-01-10 13:22:25 +01006061#if BASH_SUBSTR
6062# if ENABLE_FEATURE_SH_MATH
6063static int substr_atoi(const char *s)
6064{
6065 arith_t t = ash_arith(s);
6066 if (sizeof(t) > sizeof(int)) {
6067 /* clamp very large or very large negative nums for ${v:N:M}:
6068 * else "${v:0:0x100000001}" would work as "${v:0:1}"
6069 */
6070 if (t > INT_MAX)
6071 t = INT_MAX;
6072 if (t < INT_MIN)
6073 t = INT_MIN;
6074 }
6075 return t;
6076}
6077# else
6078# define substr_atoi(s) number(s)
6079# endif
6080#endif
Denis Vlasenko448d30e2008-06-27 00:24:11 +00006081
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006082/*
6083 * expandarg flags
6084 */
6085#define EXP_FULL 0x1 /* perform word splitting & file globbing */
6086#define EXP_TILDE 0x2 /* do normal tilde expansion */
6087#define EXP_VARTILDE 0x4 /* expand tildes in an assignment */
6088#define EXP_REDIR 0x8 /* file glob for a redirection (1 match only) */
Denys Vlasenkodb74c6c2016-10-24 21:12:33 +02006089/* ^^^^^^^^^^^^^^ this is meant to support constructs such as "cmd >file*.txt"
6090 * POSIX says for this case:
6091 * Pathname expansion shall not be performed on the word by a
6092 * non-interactive shell; an interactive shell may perform it, but shall
6093 * do so only when the expansion would result in one word.
6094 * Currently, our code complies to the above rule by never globbing
6095 * redirection filenames.
6096 * Bash performs globbing, unless it is non-interactive and in POSIX mode.
6097 * (this means that on a typical Linux distro, bash almost always
6098 * performs globbing, and thus diverges from what we do).
6099 */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006100#define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */
Denys Vlasenko216913c2018-04-02 12:35:04 +02006101#define EXP_VARTILDE2 0x20 /* expand tildes after colons only */
6102#define EXP_WORD 0x40 /* expand word in parameter expansion */
Denys Vlasenko440da972018-08-05 14:29:58 +02006103#define EXP_QUOTED 0x100 /* expand word in double quotes */
Denys Vlasenkoecc85832020-02-20 10:06:20 +01006104#define EXP_KEEPNUL 0x200 /* do not skip NUL characters */
Denys Vlasenko82331882020-02-24 10:02:50 +01006105#define EXP_DISCARD 0x400 /* discard result of expansion */
Denys Vlasenkoecc85832020-02-20 10:06:20 +01006106
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006107/*
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006108 * rmescape() flags
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006109 */
6110#define RMESCAPE_ALLOC 0x1 /* Allocate a new string */
6111#define RMESCAPE_GLOB 0x2 /* Add backslashes for glob */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006112#define RMESCAPE_GROW 0x8 /* Grow strings instead of stalloc */
6113#define RMESCAPE_HEAP 0x10 /* Malloc strings instead of stalloc */
6114
Ron Yorstond68d1fb2015-05-18 09:49:28 +02006115/* Add CTLESC when necessary. */
Denys Vlasenko216913c2018-04-02 12:35:04 +02006116#define QUOTES_ESC (EXP_FULL | EXP_CASE)
Ron Yorstond68d1fb2015-05-18 09:49:28 +02006117
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006118/*
6119 * Structure specifying which parts of the string should be searched
6120 * for IFS characters.
6121 */
6122struct ifsregion {
6123 struct ifsregion *next; /* next region in list */
6124 int begoff; /* offset of start of region */
6125 int endoff; /* offset of end of region */
6126 int nulonly; /* search for nul bytes only */
6127};
6128
6129struct arglist {
6130 struct strlist *list;
6131 struct strlist **lastp;
6132};
6133
6134/* output of current string */
6135static char *expdest;
6136/* list of back quote expressions */
6137static struct nodelist *argbackq;
6138/* first struct in list of ifs regions */
6139static struct ifsregion ifsfirst;
6140/* last struct in list */
6141static struct ifsregion *ifslastp;
6142/* holds expanded arg list */
6143static struct arglist exparg;
6144
6145/*
Denys Vlasenko455e4222016-10-27 14:45:13 +02006146 * Break the argument string into pieces based upon IFS and add the
6147 * strings to the argument list. The regions of the string to be
6148 * searched for IFS characters have been stored by recordregion.
6149 */
6150static void
6151ifsbreakup(char *string, struct arglist *arglist)
6152{
6153 struct ifsregion *ifsp;
6154 struct strlist *sp;
6155 char *start;
6156 char *p;
6157 char *q;
6158 const char *ifs, *realifs;
6159 int ifsspc;
6160 int nulonly;
6161
6162 start = string;
6163 if (ifslastp != NULL) {
6164 ifsspc = 0;
6165 nulonly = 0;
6166 realifs = ifsset() ? ifsval() : defifs;
6167 ifsp = &ifsfirst;
6168 do {
Denys Vlasenko9a95df92018-04-02 14:27:50 +02006169 int afternul;
6170
Denys Vlasenko455e4222016-10-27 14:45:13 +02006171 p = string + ifsp->begoff;
Denys Vlasenko9a95df92018-04-02 14:27:50 +02006172 afternul = nulonly;
Denys Vlasenko455e4222016-10-27 14:45:13 +02006173 nulonly = ifsp->nulonly;
6174 ifs = nulonly ? nullstr : realifs;
6175 ifsspc = 0;
6176 while (p < string + ifsp->endoff) {
6177 q = p;
6178 if ((unsigned char)*p == CTLESC)
6179 p++;
6180 if (!strchr(ifs, *p)) {
6181 p++;
6182 continue;
6183 }
Denys Vlasenko9a95df92018-04-02 14:27:50 +02006184 if (!(afternul || nulonly))
Denys Vlasenko455e4222016-10-27 14:45:13 +02006185 ifsspc = (strchr(defifs, *p) != NULL);
6186 /* Ignore IFS whitespace at start */
6187 if (q == start && ifsspc) {
6188 p++;
6189 start = p;
6190 continue;
6191 }
6192 *q = '\0';
6193 sp = stzalloc(sizeof(*sp));
6194 sp->text = start;
6195 *arglist->lastp = sp;
6196 arglist->lastp = &sp->next;
6197 p++;
6198 if (!nulonly) {
6199 for (;;) {
6200 if (p >= string + ifsp->endoff) {
6201 break;
6202 }
6203 q = p;
6204 if ((unsigned char)*p == CTLESC)
6205 p++;
6206 if (strchr(ifs, *p) == NULL) {
6207 p = q;
6208 break;
6209 }
6210 if (strchr(defifs, *p) == NULL) {
6211 if (ifsspc) {
6212 p++;
6213 ifsspc = 0;
6214 } else {
6215 p = q;
6216 break;
6217 }
6218 } else
6219 p++;
6220 }
6221 }
6222 start = p;
6223 } /* while */
6224 ifsp = ifsp->next;
6225 } while (ifsp != NULL);
6226 if (nulonly)
6227 goto add;
6228 }
6229
6230 if (!*start)
6231 return;
6232
6233 add:
6234 sp = stzalloc(sizeof(*sp));
6235 sp->text = start;
6236 *arglist->lastp = sp;
6237 arglist->lastp = &sp->next;
6238}
6239
6240static void
6241ifsfree(void)
6242{
Denys Vlasenko5ac04f22016-10-27 14:46:50 +02006243 struct ifsregion *p = ifsfirst.next;
6244
6245 if (!p)
6246 goto out;
Denys Vlasenko455e4222016-10-27 14:45:13 +02006247
6248 INT_OFF;
Denys Vlasenko455e4222016-10-27 14:45:13 +02006249 do {
6250 struct ifsregion *ifsp;
6251 ifsp = p->next;
6252 free(p);
6253 p = ifsp;
6254 } while (p);
Denys Vlasenko455e4222016-10-27 14:45:13 +02006255 ifsfirst.next = NULL;
6256 INT_ON;
Denys Vlasenko5ac04f22016-10-27 14:46:50 +02006257 out:
6258 ifslastp = NULL;
Denys Vlasenko455e4222016-10-27 14:45:13 +02006259}
6260
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006261static size_t
6262esclen(const char *start, const char *p)
6263{
6264 size_t esc = 0;
6265
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006266 while (p > start && (unsigned char)*--p == CTLESC) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006267 esc++;
6268 }
6269 return esc;
6270}
6271
6272/*
6273 * Remove any CTLESC characters from a string.
6274 */
Denys Vlasenko740058b2018-01-09 17:01:00 +01006275#if !BASH_PATTERN_SUBST
6276#define rmescapes(str, flag, slash_position) \
6277 rmescapes(str, flag)
6278#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006279static char *
Denys Vlasenko740058b2018-01-09 17:01:00 +01006280rmescapes(char *str, int flag, int *slash_position)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006281{
Ron Yorston417622c2015-05-18 09:59:14 +02006282 static const char qchars[] ALIGN1 = {
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01006283 IF_BASH_PATTERN_SUBST('/',) CTLESC, CTLQUOTEMARK, '\0' };
Denis Vlasenkof20de5b2007-04-29 23:42:54 +00006284
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006285 char *p, *q, *r;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006286 unsigned protect_against_glob;
6287 unsigned globbing;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006288
Denys Vlasenko740058b2018-01-09 17:01:00 +01006289 p = strpbrk(str, qchars IF_BASH_PATTERN_SUBST(+ !slash_position));
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006290 if (!p)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006291 return str;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006292
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006293 q = p;
6294 r = str;
6295 if (flag & RMESCAPE_ALLOC) {
6296 size_t len = p - str;
6297 size_t fulllen = len + strlen(p) + 1;
6298
6299 if (flag & RMESCAPE_GROW) {
Colin Watson3963d942010-04-26 14:21:27 +02006300 int strloc = str - (char *)stackblock();
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006301 r = makestrspace(fulllen, expdest);
Colin Watson3963d942010-04-26 14:21:27 +02006302 /* p and str may be invalidated by makestrspace */
6303 str = (char *)stackblock() + strloc;
6304 p = str + len;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006305 } else if (flag & RMESCAPE_HEAP) {
6306 r = ckmalloc(fulllen);
6307 } else {
6308 r = stalloc(fulllen);
6309 }
6310 q = r;
6311 if (len > 0) {
Denys Vlasenko5ace96a2017-07-23 21:46:02 +02006312 q = (char *)mempcpy(q, str, len);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006313 }
6314 }
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006315
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006316 globbing = flag & RMESCAPE_GLOB;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006317 protect_against_glob = globbing;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006318 while (*p) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006319 if ((unsigned char)*p == CTLQUOTEMARK) {
Denys Vlasenko216913c2018-04-02 12:35:04 +02006320// Note: protect_against_glob only affect whether
Denys Vlasenkofda9faf2017-07-05 19:10:21 +02006321// CTLESC,<ch> gets converted to <ch> or to \<ch>
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006322 p++;
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006323 protect_against_glob = globbing;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006324 continue;
6325 }
Denys Vlasenko216913c2018-04-02 12:35:04 +02006326 if (*p == '\\') {
6327 /* naked back slash */
6328 protect_against_glob = 0;
6329 goto copy;
6330 }
Ron Yorston549deab2015-05-18 09:57:51 +02006331 if ((unsigned char)*p == CTLESC) {
6332 p++;
Denys Vlasenko13f20912016-09-25 20:54:25 +02006333#if DEBUG
6334 if (*p == '\0')
6335 ash_msg_and_raise_error("CTLESC at EOL (shouldn't happen)");
6336#endif
Ron Yorston549deab2015-05-18 09:57:51 +02006337 if (protect_against_glob) {
Denys Vlasenkofda9faf2017-07-05 19:10:21 +02006338 /*
6339 * We used to trust glob() and fnmatch() to eat
6340 * superfluous escapes (\z where z has no
6341 * special meaning anyway). But this causes
6342 * bugs such as string of one greek letter rho
Denys Vlasenkoed79a632017-07-05 19:20:43 +02006343 * (unicode-encoded as two bytes "cf,81")
Denys Vlasenkofda9faf2017-07-05 19:10:21 +02006344 * getting encoded as "cf,CTLESC,81"
6345 * and here, converted to "cf,\,81" -
6346 * which does not go well with some flavors
Denys Vlasenko92b8d9c2017-07-05 19:13:44 +02006347 * of fnmatch() in unicode locales
6348 * (for example, glibc <= 2.22).
Denys Vlasenkofda9faf2017-07-05 19:10:21 +02006349 *
6350 * Lets add "\" only on the chars which need it.
Denys Vlasenko4142f012017-07-05 22:19:28 +02006351 * Testcases for less obvious chars are shown.
Denys Vlasenkofda9faf2017-07-05 19:10:21 +02006352 */
6353 if (*p == '*'
6354 || *p == '?'
6355 || *p == '['
Denys Vlasenko8de5b9f2018-02-13 14:43:29 +01006356 || *p == '\\' /* case '\' in \\ ) echo ok;; *) echo WRONG;; esac */
6357 || *p == ']' /* case ']' in [a\]] ) echo ok;; *) echo WRONG;; esac */
6358 || *p == '-' /* case '-' in [a\-c]) echo ok;; *) echo WRONG;; esac */
6359 || *p == '!' /* case '!' in [\!] ) echo ok;; *) echo WRONG;; esac */
Denys Vlasenko4142f012017-07-05 22:19:28 +02006360 /* Some libc support [^negate], that's why "^" also needs love */
Denys Vlasenko8de5b9f2018-02-13 14:43:29 +01006361 || *p == '^' /* case '^' in [\^] ) echo ok;; *) echo WRONG;; esac */
Denys Vlasenkofda9faf2017-07-05 19:10:21 +02006362 ) {
6363 *q++ = '\\';
6364 }
Ron Yorston549deab2015-05-18 09:57:51 +02006365 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006366 }
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01006367#if BASH_PATTERN_SUBST
Denys Vlasenko740058b2018-01-09 17:01:00 +01006368 else if (slash_position && p == str + *slash_position) {
6369 /* stop handling globbing */
6370 globbing = 0;
6371 *slash_position = q - r;
6372 slash_position = NULL;
Ron Yorston417622c2015-05-18 09:59:14 +02006373 }
6374#endif
Denys Vlasenkob0d63382009-09-16 16:18:32 +02006375 protect_against_glob = globbing;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006376 copy:
6377 *q++ = *p++;
6378 }
6379 *q = '\0';
6380 if (flag & RMESCAPE_GROW) {
6381 expdest = r;
6382 STADJUST(q - r + 1, expdest);
6383 }
6384 return r;
6385}
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006386#define pmatch(a, b) !fnmatch((a), (b), 0)
6387
6388/*
6389 * Prepare a pattern for a expmeta (internal glob(3)) call.
6390 *
6391 * Returns an stalloced string.
6392 */
6393static char *
Ron Yorston549deab2015-05-18 09:57:51 +02006394preglob(const char *pattern, int flag)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006395{
Denys Vlasenko740058b2018-01-09 17:01:00 +01006396 return rmescapes((char *)pattern, flag | RMESCAPE_GLOB, NULL);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006397}
6398
6399/*
6400 * Put a string on the stack.
6401 */
Denys Vlasenko45dd87a2020-02-21 16:30:44 +01006402static size_t
Denys Vlasenkoecc85832020-02-20 10:06:20 +01006403memtodest(const char *p, size_t len, int flags)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006404{
Denys Vlasenkoecc85832020-02-20 10:06:20 +01006405 int syntax = flags & EXP_QUOTED ? DQSYNTAX : BASESYNTAX;
Ron Yorstond68d1fb2015-05-18 09:49:28 +02006406 char *q;
Denys Vlasenko45dd87a2020-02-21 16:30:44 +01006407 char *s;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006408
Ron Yorstond68d1fb2015-05-18 09:49:28 +02006409 if (!len)
Denys Vlasenko45dd87a2020-02-21 16:30:44 +01006410 return 0;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006411
Denys Vlasenkoecc85832020-02-20 10:06:20 +01006412 q = makestrspace(len * 2, expdest);
Denys Vlasenko45dd87a2020-02-21 16:30:44 +01006413 s = q;
Ron Yorstond68d1fb2015-05-18 09:49:28 +02006414
6415 do {
Denys Vlasenkocd716832009-11-28 22:14:02 +01006416 unsigned char c = *p++;
Ron Yorstond68d1fb2015-05-18 09:49:28 +02006417 if (c) {
Denys Vlasenkoecc85832020-02-20 10:06:20 +01006418 if (flags & QUOTES_ESC) {
Denys Vlasenko2fe66b12016-12-12 17:39:12 +01006419 int n = SIT(c, syntax);
6420 if (n == CCTL
Denys Vlasenkoecc85832020-02-20 10:06:20 +01006421 || ((flags & EXP_QUOTED) && n == CBACK)
Denys Vlasenko2fe66b12016-12-12 17:39:12 +01006422 ) {
6423 USTPUTC(CTLESC, q);
6424 }
Denys Vlasenkob3f29b42016-09-21 16:25:58 +02006425 }
Denys Vlasenkoecc85832020-02-20 10:06:20 +01006426 } else if (!(flags & EXP_KEEPNUL))
Ron Yorstond68d1fb2015-05-18 09:49:28 +02006427 continue;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006428 USTPUTC(c, q);
Ron Yorstond68d1fb2015-05-18 09:49:28 +02006429 } while (--len);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006430
6431 expdest = q;
Denys Vlasenko45dd87a2020-02-21 16:30:44 +01006432 return q - s;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006433}
6434
Ron Yorstond68d1fb2015-05-18 09:49:28 +02006435static size_t
Denys Vlasenkoecc85832020-02-20 10:06:20 +01006436strtodest(const char *p, int flags)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006437{
Ron Yorstond68d1fb2015-05-18 09:49:28 +02006438 size_t len = strlen(p);
Denys Vlasenkoecc85832020-02-20 10:06:20 +01006439 memtodest(p, len, flags);
Ron Yorstond68d1fb2015-05-18 09:49:28 +02006440 return len;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006441}
6442
6443/*
Denys Vlasenko45dd87a2020-02-21 16:30:44 +01006444 * Our own itoa().
6445 * cvtnum() is used even if math support is off (to prepare $? values and such).
6446 */
6447static int
6448cvtnum(arith_t num, int flags)
6449{
6450 /* 32-bit and wider ints require buffer size of bytes*3 (or less) */
6451 /* If narrower: worst case, 1-byte ints: need 5 bytes: "-127<NUL>" */
6452 int len = (sizeof(arith_t) >= 4) ? sizeof(arith_t) * 3 : sizeof(arith_t) * 3 + 2;
6453 char buf[len];
6454
6455 len = fmtstr(buf, len, ARITH_FMT, num);
6456 return memtodest(buf, len, flags);
6457}
6458
6459/*
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006460 * Record the fact that we have to scan this region of the
6461 * string for IFS characters.
6462 */
6463static void
6464recordregion(int start, int end, int nulonly)
6465{
6466 struct ifsregion *ifsp;
6467
6468 if (ifslastp == NULL) {
6469 ifsp = &ifsfirst;
6470 } else {
6471 INT_OFF;
Denis Vlasenko597906c2008-02-20 16:38:54 +00006472 ifsp = ckzalloc(sizeof(*ifsp));
6473 /*ifsp->next = NULL; - ckzalloc did it */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006474 ifslastp->next = ifsp;
6475 INT_ON;
6476 }
6477 ifslastp = ifsp;
6478 ifslastp->begoff = start;
6479 ifslastp->endoff = end;
6480 ifslastp->nulonly = nulonly;
6481}
6482
6483static void
6484removerecordregions(int endoff)
6485{
6486 if (ifslastp == NULL)
6487 return;
6488
6489 if (ifsfirst.endoff > endoff) {
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006490 while (ifsfirst.next) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006491 struct ifsregion *ifsp;
6492 INT_OFF;
6493 ifsp = ifsfirst.next->next;
6494 free(ifsfirst.next);
6495 ifsfirst.next = ifsp;
6496 INT_ON;
6497 }
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006498 if (ifsfirst.begoff > endoff) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006499 ifslastp = NULL;
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006500 } else {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006501 ifslastp = &ifsfirst;
6502 ifsfirst.endoff = endoff;
6503 }
6504 return;
6505 }
6506
6507 ifslastp = &ifsfirst;
6508 while (ifslastp->next && ifslastp->next->begoff < endoff)
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006509 ifslastp = ifslastp->next;
6510 while (ifslastp->next) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006511 struct ifsregion *ifsp;
6512 INT_OFF;
6513 ifsp = ifslastp->next->next;
6514 free(ifslastp->next);
6515 ifslastp->next = ifsp;
6516 INT_ON;
6517 }
6518 if (ifslastp->endoff > endoff)
6519 ifslastp->endoff = endoff;
6520}
6521
6522static char *
Denys Vlasenko82331882020-02-24 10:02:50 +01006523exptilde(char *startp, int flag)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006524{
Denys Vlasenkocd716832009-11-28 22:14:02 +01006525 unsigned char c;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006526 char *name;
6527 struct passwd *pw;
6528 const char *home;
Denys Vlasenko82331882020-02-24 10:02:50 +01006529 char *p;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006530
Denys Vlasenko82331882020-02-24 10:02:50 +01006531 p = startp;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006532 name = p + 1;
6533
6534 while ((c = *++p) != '\0') {
6535 switch (c) {
6536 case CTLESC:
6537 return startp;
6538 case CTLQUOTEMARK:
6539 return startp;
6540 case ':':
Denys Vlasenkoecc85832020-02-20 10:06:20 +01006541 if (flag & EXP_VARTILDE)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006542 goto done;
6543 break;
6544 case '/':
6545 case CTLENDVAR:
6546 goto done;
6547 }
6548 }
6549 done:
Denys Vlasenko82331882020-02-24 10:02:50 +01006550 if (flag & EXP_DISCARD)
6551 goto out;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006552 *p = '\0';
6553 if (*name == '\0') {
Denys Vlasenkoea8b2522010-06-02 12:57:26 +02006554 home = lookupvar("HOME");
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006555 } else {
6556 pw = getpwnam(name);
6557 if (pw == NULL)
6558 goto lose;
6559 home = pw->pw_dir;
6560 }
Denys Vlasenko82331882020-02-24 10:02:50 +01006561 *p = c;
Denys Vlasenkoe880b1f2020-02-16 18:31:05 +01006562 if (!home)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006563 goto lose;
Denys Vlasenkoecc85832020-02-20 10:06:20 +01006564 strtodest(home, flag | EXP_QUOTED);
Denys Vlasenko82331882020-02-24 10:02:50 +01006565 out:
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006566 return p;
6567 lose:
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006568 return startp;
6569}
6570
6571/*
6572 * Execute a command inside back quotes. If it's a builtin command, we
6573 * want to save its output in a block obtained from malloc. Otherwise
6574 * we fork off a subprocess and get the output of the command via a pipe.
6575 * Should be called with interrupts off.
6576 */
6577struct backcmd { /* result of evalbackcmd */
6578 int fd; /* file descriptor to read from */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006579 int nleft; /* number of chars in buffer */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00006580 char *buf; /* buffer */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006581 struct job *jp; /* job structure for command */
6582};
6583
6584/* These forward decls are needed to use "eval" code for backticks handling: */
Denys Vlasenko1e3e2cc2017-07-25 20:31:14 +02006585/* flags in argument to evaltree */
6586#define EV_EXIT 01 /* exit after evaluating tree */
6587#define EV_TESTED 02 /* exit status is checked; ignore -e flag */
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02006588static int evaltree(union node *, int);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006589
Denys Vlasenko619d9b52017-07-28 15:28:33 +02006590/* An evaltree() which is known to never return.
6591 * Used to use an alias:
6592 * static int evaltreenr(union node *, int) __attribute__((alias("evaltree"),__noreturn__));
6593 * but clang was reported to "transfer" noreturn-ness to evaltree() as well.
6594 */
6595static ALWAYS_INLINE NORETURN void
6596evaltreenr(union node *n, int flags)
6597{
6598 evaltree(n, flags);
6599 bb_unreachable(abort());
6600 /* NOTREACHED */
6601}
6602
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02006603static void FAST_FUNC
Ron Yorstona1b0d382020-07-23 08:32:27 +01006604evalbackcmd(union node *n, struct backcmd *result
6605 IF_BASH_PROCESS_SUBST(, int ctl))
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006606{
Denys Vlasenko579ad102016-10-25 21:10:20 +02006607 int pip[2];
6608 struct job *jp;
Ron Yorstona1b0d382020-07-23 08:32:27 +01006609#if BASH_PROCESS_SUBST
6610 /* determine end of pipe used by parent (ip) and child (ic) */
6611 const int ip = (ctl == CTLTOPROC);
6612 const int ic = !(ctl == CTLTOPROC);
6613#else
6614 const int ctl = CTLBACKQ;
6615 const int ip = 0;
6616 const int ic = 1;
6617#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006618
6619 result->fd = -1;
6620 result->buf = NULL;
6621 result->nleft = 0;
6622 result->jp = NULL;
Denys Vlasenko579ad102016-10-25 21:10:20 +02006623 if (n == NULL) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006624 goto out;
Denys Vlasenko579ad102016-10-25 21:10:20 +02006625 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006626
Denys Vlasenko579ad102016-10-25 21:10:20 +02006627 if (pipe(pip) < 0)
Denys Vlasenko12ffefb2017-08-23 14:52:56 +02006628 ash_msg_and_raise_perror("can't create pipe");
Ron Yorstona1b0d382020-07-23 08:32:27 +01006629 /* process substitution uses NULL job/node, like openhere() */
6630 jp = (ctl == CTLBACKQ) ? makejob(/*n,*/ 1) : NULL;
6631 if (forkshell(jp, (ctl == CTLBACKQ) ? n : NULL, FORK_NOJOB) == 0) {
Denys Vlasenko70392332016-10-27 02:31:55 +02006632 /* child */
Denys Vlasenko579ad102016-10-25 21:10:20 +02006633 FORCE_INT_ON;
Ron Yorstona1b0d382020-07-23 08:32:27 +01006634 close(pip[ip]);
6635 /* ic is index of child end of pipe *and* fd to connect it to */
6636 if (pip[ic] != ic) {
6637 /*close(ic);*/
6638 dup2_or_raise(pip[ic], ic);
6639 close(pip[ic]);
Denys Vlasenko579ad102016-10-25 21:10:20 +02006640 }
Denys Vlasenko960ca382016-10-25 18:12:15 +02006641/* TODO: eflag clearing makes the following not abort:
6642 * ash -c 'set -e; z=$(false;echo foo); echo $z'
6643 * which is what bash does (unless it is in POSIX mode).
6644 * dash deleted "eflag = 0" line in the commit
6645 * Date: Mon, 28 Jun 2010 17:11:58 +1000
6646 * [EVAL] Don't clear eflag in evalbackcmd
6647 * For now, preserve bash-like behavior, it seems to be somewhat more useful:
6648 */
Denys Vlasenko579ad102016-10-25 21:10:20 +02006649 eflag = 0;
Denys Vlasenko5ac04f22016-10-27 14:46:50 +02006650 ifsfree();
Denys Vlasenko619d9b52017-07-28 15:28:33 +02006651 evaltreenr(n, EV_EXIT);
Denys Vlasenko579ad102016-10-25 21:10:20 +02006652 /* NOTREACHED */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006653 }
Denys Vlasenko70392332016-10-27 02:31:55 +02006654 /* parent */
Ron Yorstona1b0d382020-07-23 08:32:27 +01006655#if BASH_PROCESS_SUBST
6656 if (ctl != CTLBACKQ) {
6657 int fd = fcntl(pip[ip], F_DUPFD, 64);
6658 if (fd > 0) {
6659 close(pip[ip]);
6660 pip[ip] = fd;
6661 }
6662 pushfd(pip[ip]);
6663 }
6664#endif
6665 close(pip[ic]);
6666 result->fd = pip[ip];
Denys Vlasenko579ad102016-10-25 21:10:20 +02006667 result->jp = jp;
6668
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006669 out:
6670 TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n",
6671 result->fd, result->buf, result->nleft, result->jp));
6672}
6673
6674/*
6675 * Expand stuff in backwards quotes.
6676 */
6677static void
Ron Yorstona1b0d382020-07-23 08:32:27 +01006678expbackq(union node *cmd, int flag IF_BASH_PROCESS_SUBST(, int ctl))
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006679{
Ron Yorstona1b0d382020-07-23 08:32:27 +01006680#if !BASH_PROCESS_SUBST
6681 const int ctl = CTLBACKQ;
6682#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006683 struct backcmd in;
6684 int i;
6685 char buf[128];
6686 char *p;
6687 char *dest;
6688 int startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006689 struct stackmark smark;
6690
Denys Vlasenko82331882020-02-24 10:02:50 +01006691 if (flag & EXP_DISCARD)
6692 goto out;
6693
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006694 INT_OFF;
Denys Vlasenko60ca8342016-09-30 11:21:21 +02006695 startloc = expdest - (char *)stackblock();
6696 pushstackmark(&smark, startloc);
Ron Yorstona1b0d382020-07-23 08:32:27 +01006697 evalbackcmd(cmd, &in IF_BASH_PROCESS_SUBST(, ctl));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006698 popstackmark(&smark);
6699
Ron Yorstona1b0d382020-07-23 08:32:27 +01006700 if (ctl != CTLBACKQ) {
6701 sprintf(buf, DEV_FD_PREFIX"%d", in.fd);
6702 strtodest(buf, BASESYNTAX);
6703 goto done;
6704 }
6705
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006706 p = in.buf;
6707 i = in.nleft;
6708 if (i == 0)
6709 goto read;
6710 for (;;) {
Denys Vlasenkoecc85832020-02-20 10:06:20 +01006711 memtodest(p, i, flag);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006712 read:
6713 if (in.fd < 0)
6714 break;
Ron Yorston61d6ae22015-04-19 10:50:25 +01006715 i = nonblock_immune_read(in.fd, buf, sizeof(buf));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006716 TRACE(("expbackq: read returns %d\n", i));
6717 if (i <= 0)
6718 break;
6719 p = buf;
6720 }
6721
Denis Vlasenko60818682007-09-28 22:07:23 +00006722 free(in.buf);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006723 if (in.fd >= 0) {
6724 close(in.fd);
6725 back_exitstatus = waitforjob(in.jp);
6726 }
Ron Yorstona1b0d382020-07-23 08:32:27 +01006727 done:
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006728 INT_ON;
6729
6730 /* Eat all trailing newlines */
6731 dest = expdest;
Denys Vlasenko9ee58922020-02-17 10:24:32 +01006732 for (; dest > ((char *)stackblock() + startloc) && dest[-1] == '\n';)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006733 STUNPUTC(dest);
6734 expdest = dest;
6735
Ron Yorston549deab2015-05-18 09:57:51 +02006736 if (!(flag & EXP_QUOTED))
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006737 recordregion(startloc, dest - (char *)stackblock(), 0);
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006738 TRACE(("evalbackq: size:%d:'%.*s'\n",
6739 (int)((dest - (char *)stackblock()) - startloc),
6740 (int)((dest - (char *)stackblock()) - startloc),
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006741 stackblock() + startloc));
Denys Vlasenko82331882020-02-24 10:02:50 +01006742
6743 out:
6744 argbackq = argbackq->next;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006745}
6746
Denys Vlasenko82331882020-02-24 10:02:50 +01006747/* expari needs it */
6748static char *argstr(char *p, int flag);
6749
Denys Vlasenko0b883582016-12-23 16:49:07 +01006750#if ENABLE_FEATURE_SH_MATH
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006751/*
6752 * Expand arithmetic expression. Backup to start of expression,
6753 * evaluate, place result in (backed up) result, adjust string position.
6754 */
Denys Vlasenko82331882020-02-24 10:02:50 +01006755static char *
6756expari(char *start, int flag)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006757{
Denys Vlasenko82331882020-02-24 10:02:50 +01006758 struct stackmark sm;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006759 int begoff;
Denys Vlasenko82331882020-02-24 10:02:50 +01006760 int endoff;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006761 int len;
Denys Vlasenko82331882020-02-24 10:02:50 +01006762 arith_t result;
6763 char *p;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006764
Denys Vlasenko82331882020-02-24 10:02:50 +01006765 p = stackblock();
6766 begoff = expdest - p;
6767 p = argstr(start, flag & EXP_DISCARD);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006768
Denys Vlasenko82331882020-02-24 10:02:50 +01006769 if (flag & EXP_DISCARD)
6770 goto out;
6771
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006772 start = stackblock();
Denys Vlasenko82331882020-02-24 10:02:50 +01006773 endoff = expdest - start;
6774 start += begoff;
6775 STADJUST(start - expdest, expdest);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006776
6777 removerecordregions(begoff);
6778
Ron Yorston549deab2015-05-18 09:57:51 +02006779 if (flag & QUOTES_ESC)
Denys Vlasenko82331882020-02-24 10:02:50 +01006780 rmescapes(start, 0, NULL);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006781
Denys Vlasenko82331882020-02-24 10:02:50 +01006782 pushstackmark(&sm, endoff);
6783 result = ash_arith(start);
6784 popstackmark(&sm);
6785
6786 len = cvtnum(result, flag);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006787
Ron Yorston549deab2015-05-18 09:57:51 +02006788 if (!(flag & EXP_QUOTED))
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006789 recordregion(begoff, begoff + len, 0);
Denys Vlasenko82331882020-02-24 10:02:50 +01006790
6791 out:
6792 return p;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006793}
6794#endif
6795
6796/* argstr needs it */
Denys Vlasenkob8c0bc12017-07-26 23:03:21 +02006797static char *evalvar(char *p, int flags);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006798
6799/*
6800 * Perform variable and command substitution. If EXP_FULL is set, output CTLESC
6801 * characters to allow for further processing. Otherwise treat
6802 * $@ like $* since no splitting will be performed.
6803 */
Denys Vlasenko82331882020-02-24 10:02:50 +01006804static char *
Denys Vlasenko7f198482020-02-24 09:57:08 +01006805argstr(char *p, int flag)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006806{
Denis Vlasenko6ca409e2007-08-12 20:58:27 +00006807 static const char spclchars[] ALIGN1 = {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006808 '=',
6809 ':',
6810 CTLQUOTEMARK,
6811 CTLENDVAR,
6812 CTLESC,
6813 CTLVAR,
6814 CTLBACKQ,
Ron Yorstona1b0d382020-07-23 08:32:27 +01006815#if BASH_PROCESS_SUBST
6816 CTLTOPROC,
6817 CTLFROMPROC,
6818#endif
Denys Vlasenko0b883582016-12-23 16:49:07 +01006819#if ENABLE_FEATURE_SH_MATH
Denys Vlasenko82331882020-02-24 10:02:50 +01006820 CTLARI,
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006821 CTLENDARI,
6822#endif
Denys Vlasenkocd716832009-11-28 22:14:02 +01006823 '\0'
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006824 };
6825 const char *reject = spclchars;
Denys Vlasenko7f198482020-02-24 09:57:08 +01006826 int breakall = (flag & (EXP_WORD | EXP_QUOTED)) == EXP_WORD;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006827 int inquotes;
6828 size_t length;
6829 int startloc;
6830
Denys Vlasenko82331882020-02-24 10:02:50 +01006831 reject += !!(flag & EXP_VARTILDE2);
6832 reject += flag & EXP_VARTILDE ? 0 : 2;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006833 inquotes = 0;
6834 length = 0;
Denys Vlasenko7f198482020-02-24 09:57:08 +01006835 if (flag & EXP_TILDE) {
Denys Vlasenko7f198482020-02-24 09:57:08 +01006836 flag &= ~EXP_TILDE;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006837 tilde:
Denys Vlasenko82331882020-02-24 10:02:50 +01006838 if (*p == '~')
6839 p = exptilde(p, flag);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006840 }
6841 start:
6842 startloc = expdest - (char *)stackblock();
6843 for (;;) {
Denys Vlasenko82331882020-02-24 10:02:50 +01006844 int end;
Denys Vlasenkocd716832009-11-28 22:14:02 +01006845 unsigned char c;
6846
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006847 length += strcspn(p + length, reject);
Denys Vlasenko82331882020-02-24 10:02:50 +01006848 end = 0;
Denys Vlasenkocd716832009-11-28 22:14:02 +01006849 c = p[length];
Denys Vlasenko82331882020-02-24 10:02:50 +01006850 if (!(c & 0x80)
6851 IF_FEATURE_SH_MATH(|| c == CTLENDARI)
6852 || c == CTLENDVAR
6853 ) {
6854 /*
6855 * c == '=' || c == ':' || c == '\0' ||
6856 * c == CTLENDARI || c == CTLENDVAR
6857 */
6858 length++;
6859 /* c == '\0' || c == CTLENDARI || c == CTLENDVAR */
6860 end = !!((c - 1) & 0x80);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006861 }
Denys Vlasenko82331882020-02-24 10:02:50 +01006862 if (length > 0 && !(flag & EXP_DISCARD)) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006863 int newloc;
Denys Vlasenko82331882020-02-24 10:02:50 +01006864 char *q;
6865
6866 q = stnputs(p, length, expdest);
6867 q[-1] &= end - 1;
6868 expdest = q - (flag & EXP_WORD ? end : 0);
6869 newloc = q - (char *)stackblock() - end;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006870 if (breakall && !inquotes && newloc > startloc) {
6871 recordregion(startloc, newloc, 0);
6872 }
6873 startloc = newloc;
6874 }
6875 p += length + 1;
6876 length = 0;
6877
Denys Vlasenko82331882020-02-24 10:02:50 +01006878 if (end)
6879 break;
6880
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006881 switch (c) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006882 case '=':
Denys Vlasenko7f198482020-02-24 09:57:08 +01006883 flag |= EXP_VARTILDE2;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006884 reject++;
6885 /* fall through */
6886 case ':':
6887 /*
6888 * sort of a hack - expand tildes in variable
6889 * assignments (after the first '=' and after ':'s).
6890 */
6891 if (*--p == '~') {
6892 goto tilde;
6893 }
6894 continue;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006895 case CTLQUOTEMARK:
6896 /* "$@" syntax adherence hack */
Denys Vlasenko9a95df92018-04-02 14:27:50 +02006897 if (!inquotes && !memcmp(p, dolatstr + 1, DOLATSTRLEN - 1)) {
Denys Vlasenko7f198482020-02-24 09:57:08 +01006898 p = evalvar(p + 1, flag | EXP_QUOTED) + 1;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006899 goto start;
6900 }
Denys Vlasenko9a95df92018-04-02 14:27:50 +02006901 inquotes ^= EXP_QUOTED;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006902 addquote:
Denys Vlasenko7f198482020-02-24 09:57:08 +01006903 if (flag & QUOTES_ESC) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006904 p--;
6905 length++;
6906 startloc++;
6907 }
6908 break;
6909 case CTLESC:
6910 startloc++;
6911 length++;
6912 goto addquote;
6913 case CTLVAR:
Denys Vlasenkof451b2c2012-06-09 02:06:57 +02006914 TRACE(("argstr: evalvar('%s')\n", p));
Denys Vlasenko7f198482020-02-24 09:57:08 +01006915 p = evalvar(p, flag | inquotes);
Denys Vlasenkof451b2c2012-06-09 02:06:57 +02006916 TRACE(("argstr: evalvar:'%s'\n", (char *)stackblock()));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006917 goto start;
Ron Yorstona1b0d382020-07-23 08:32:27 +01006918#if BASH_PROCESS_SUBST
6919 case CTLTOPROC:
6920 case CTLFROMPROC:
6921#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006922 case CTLBACKQ:
Ron Yorstona1b0d382020-07-23 08:32:27 +01006923 expbackq(argbackq->n, flag | inquotes IF_BASH_PROCESS_SUBST(, c));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006924 goto start;
Denys Vlasenko0b883582016-12-23 16:49:07 +01006925#if ENABLE_FEATURE_SH_MATH
Denys Vlasenko82331882020-02-24 10:02:50 +01006926 case CTLARI:
6927 p = expari(p, flag | inquotes);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006928 goto start;
6929#endif
6930 }
6931 }
Denys Vlasenko82331882020-02-24 10:02:50 +01006932 return p - 1;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006933}
6934
6935static char *
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006936scanleft(char *startp, char *rmesc, char *rmescend UNUSED_PARAM,
6937 char *pattern, int quotes, int zero)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006938{
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006939 char *loc, *loc2;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006940 char c;
6941
6942 loc = startp;
6943 loc2 = rmesc;
6944 do {
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006945 int match;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006946 const char *s = loc2;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006947
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006948 c = *loc2;
6949 if (zero) {
6950 *loc2 = '\0';
6951 s = rmesc;
6952 }
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006953 match = pmatch(pattern, s);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00006954
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006955 *loc2 = c;
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006956 if (match)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006957 return loc;
Denys Vlasenkocd716832009-11-28 22:14:02 +01006958 if (quotes && (unsigned char)*loc == CTLESC)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006959 loc++;
6960 loc++;
6961 loc2++;
6962 } while (c);
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006963 return NULL;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006964}
6965
6966static char *
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02006967scanright(char *startp, char *rmesc, char *rmescend,
6968 char *pattern, int quotes, int match_at_start)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006969{
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006970#if !ENABLE_ASH_OPTIMIZE_FOR_SIZE
6971 int try2optimize = match_at_start;
6972#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006973 int esc = 0;
6974 char *loc;
6975 char *loc2;
6976
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006977 /* If we called by "${v/pattern/repl}" or "${v//pattern/repl}":
6978 * startp="escaped_value_of_v" rmesc="raw_value_of_v"
6979 * rmescend=""(ptr to NUL in rmesc) pattern="pattern" quotes=match_at_start=1
6980 * Logic:
6981 * loc starts at NUL at the end of startp, loc2 starts at the end of rmesc,
6982 * and on each iteration they go back two/one char until they reach the beginning.
Denys Vlasenko1310d7b2021-07-22 18:15:59 +02006983 * We try to match "raw_value_of_v", "raw_value_of_", "raw_value_of" etc.
6984 * If one of these matches, return pointer past last matched char in startp.
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006985 */
6986 /* TODO: document in what other circumstances we are called. */
6987
6988 for (loc = pattern - 1, loc2 = rmescend; loc >= startp; loc2--) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006989 int match;
6990 char c = *loc2;
6991 const char *s = loc2;
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006992 if (match_at_start) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006993 *loc2 = '\0';
6994 s = rmesc;
6995 }
Denys Vlasenkob76356b2010-03-13 16:19:04 +01006996 match = pmatch(pattern, s);
6997 //bb_error_msg("pmatch(pattern:'%s',s:'%s'):%d", pattern, s, match);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00006998 *loc2 = c;
6999 if (match)
7000 return loc;
Denys Vlasenkob76356b2010-03-13 16:19:04 +01007001#if !ENABLE_ASH_OPTIMIZE_FOR_SIZE
7002 if (try2optimize) {
7003 /* Maybe we can optimize this:
7004 * if pattern ends with unescaped *, we can avoid checking
Denys Vlasenko10ad6222017-04-17 16:13:32 +02007005 * shorter strings: if "foo*" doesn't match "raw_value_of_v",
7006 * it won't match truncated "raw_value_of_" strings too.
Denys Vlasenkob76356b2010-03-13 16:19:04 +01007007 */
7008 unsigned plen = strlen(pattern);
7009 /* Does it end with "*"? */
7010 if (plen != 0 && pattern[--plen] == '*') {
7011 /* "xxxx*" is not escaped */
7012 /* "xxx\*" is escaped */
7013 /* "xx\\*" is not escaped */
7014 /* "x\\\*" is escaped */
7015 int slashes = 0;
7016 while (plen != 0 && pattern[--plen] == '\\')
7017 slashes++;
7018 if (!(slashes & 1))
7019 break; /* ends with unescaped "*" */
7020 }
7021 try2optimize = 0;
7022 }
7023#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007024 loc--;
7025 if (quotes) {
7026 if (--esc < 0) {
7027 esc = esclen(startp, loc);
7028 }
7029 if (esc % 2) {
7030 esc--;
7031 loc--;
7032 }
7033 }
7034 }
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02007035 return NULL;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007036}
7037
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00007038static void varunset(const char *, const char *, const char *, int) NORETURN;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007039static void
7040varunset(const char *end, const char *var, const char *umsg, int varflags)
7041{
7042 const char *msg;
7043 const char *tail;
7044
7045 tail = nullstr;
7046 msg = "parameter not set";
7047 if (umsg) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01007048 if ((unsigned char)*end == CTLENDVAR) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007049 if (varflags & VSNUL)
7050 tail = " or null";
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00007051 } else {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007052 msg = umsg;
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00007053 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007054 }
Denys Vlasenko09dd6ec2010-08-07 02:44:33 +02007055 ash_msg_and_raise_error("%.*s: %s%s", (int)(end - var - 1), var, msg, tail);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007056}
7057
Denys Vlasenko82331882020-02-24 10:02:50 +01007058static char *
7059subevalvar(char *start, char *str, int strloc,
Denys Vlasenkob8c0bc12017-07-26 23:03:21 +02007060 int startloc, int varflags, int flag)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007061{
Denys Vlasenko82331882020-02-24 10:02:50 +01007062 int subtype = varflags & VSTYPE;
Ron Yorston549deab2015-05-18 09:57:51 +02007063 int quotes = flag & QUOTES_ESC;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007064 char *startp;
7065 char *loc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007066 char *rmesc, *rmescend;
Denys Vlasenko82331882020-02-24 10:02:50 +01007067 long amount;
7068 int resetloc;
Denys Vlasenko740058b2018-01-09 17:01:00 +01007069 int argstr_flags;
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01007070 IF_BASH_PATTERN_SUBST(int workloc;)
Denys Vlasenko740058b2018-01-09 17:01:00 +01007071 IF_BASH_PATTERN_SUBST(int slash_pos;)
7072 IF_BASH_PATTERN_SUBST(char *repl;)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007073 int zero;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007074 char *(*scan)(char*, char*, char*, char*, int, int);
Denys Vlasenko82331882020-02-24 10:02:50 +01007075 char *p;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007076
Denys Vlasenko82331882020-02-24 10:02:50 +01007077 //bb_error_msg("subevalvar(start:'%s',str:'%s',strloc:%d,startloc:%d,varflags:%x,quotes:%d)",
7078 // start, str, strloc, startloc, varflags, quotes);
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02007079
Denys Vlasenko740058b2018-01-09 17:01:00 +01007080#if BASH_PATTERN_SUBST
Denys Vlasenkod1df1a72018-01-09 17:25:58 +01007081 /* For "${v/pattern/repl}", we must find the delimiter _before_
7082 * argstr() call expands possible variable references in pattern:
7083 * think about "v=a; a=a/; echo ${v/$a/r}" case.
7084 */
Denys Vlasenko740058b2018-01-09 17:01:00 +01007085 repl = NULL;
7086 if (subtype == VSREPLACE || subtype == VSREPLACEALL) {
7087 /* Find '/' and replace with NUL */
Denys Vlasenko82331882020-02-24 10:02:50 +01007088 repl = start;
Denys Vlasenkoc2aa2182018-08-04 22:25:28 +02007089 /* The pattern can't be empty.
7090 * IOW: if the first char after "${v//" is a slash,
7091 * it does not terminate the pattern - it's the first char of the pattern:
7092 * v=/dev/ram; echo ${v////-} prints -dev-ram (pattern is "/")
7093 * v=/dev/ram; echo ${v///r/-} prints /dev-am (pattern is "/r")
7094 */
7095 if (*repl == '/')
7096 repl++;
Denys Vlasenko740058b2018-01-09 17:01:00 +01007097 for (;;) {
Denys Vlasenko740058b2018-01-09 17:01:00 +01007098 if (*repl == '\0') {
7099 repl = NULL;
7100 break;
7101 }
7102 if (*repl == '/') {
7103 *repl = '\0';
7104 break;
7105 }
Denys Vlasenkoc2aa2182018-08-04 22:25:28 +02007106 /* Handle escaped slashes, e.g. "${v/\//_}" (they are CTLESC'ed by this point) */
Denys Vlasenkod1df1a72018-01-09 17:25:58 +01007107 if ((unsigned char)*repl == CTLESC && repl[1])
Denys Vlasenko740058b2018-01-09 17:01:00 +01007108 repl++;
Denys Vlasenko740058b2018-01-09 17:01:00 +01007109 repl++;
7110 }
7111 }
7112#endif
Denys Vlasenko82331882020-02-24 10:02:50 +01007113 argstr_flags = (flag & EXP_DISCARD) | EXP_TILDE;
7114 if (!str
Denys Vlasenko216913c2018-04-02 12:35:04 +02007115#if BASH_SUBSTR
7116 && subtype != VSSUBSTR
7117#endif
7118 ) {
7119 /* EXP_CASE keeps CTLESC's */
Denys Vlasenko82331882020-02-24 10:02:50 +01007120 argstr_flags |= EXP_CASE;
Denys Vlasenko216913c2018-04-02 12:35:04 +02007121 }
Denys Vlasenko82331882020-02-24 10:02:50 +01007122 p = argstr(start, argstr_flags);
7123
Denys Vlasenko216913c2018-04-02 12:35:04 +02007124 //bb_error_msg("str0:'%s'", (char *)stackblock() + strloc);
Denys Vlasenko740058b2018-01-09 17:01:00 +01007125#if BASH_PATTERN_SUBST
7126 slash_pos = -1;
7127 if (repl) {
7128 slash_pos = expdest - ((char *)stackblock() + strloc);
Denys Vlasenko883cdb72021-01-09 08:27:37 +01007129 if (!(flag & EXP_DISCARD))
7130 STPUTC('/', expdest);
Denys Vlasenko216913c2018-04-02 12:35:04 +02007131 //bb_error_msg("repl+1:'%s'", repl + 1);
Denys Vlasenko82331882020-02-24 10:02:50 +01007132 p = argstr(repl + 1, (flag & EXP_DISCARD) | EXP_TILDE); /* EXP_TILDE: echo "${v/x/~}" expands ~ ! */
Denys Vlasenko740058b2018-01-09 17:01:00 +01007133 *repl = '/';
7134 }
7135#endif
Denys Vlasenko82331882020-02-24 10:02:50 +01007136 if (flag & EXP_DISCARD)
7137 return p;
7138
Denis Vlasenko29eb3592008-05-18 14:06:08 +00007139 startp = (char *)stackblock() + startloc;
Denys Vlasenko740058b2018-01-09 17:01:00 +01007140 //bb_error_msg("str1:'%s'", (char *)stackblock() + strloc);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007141
7142 switch (subtype) {
7143 case VSASSIGN:
Denys Vlasenko7f198482020-02-24 09:57:08 +01007144 setvar0(str, startp);
Denys Vlasenko82331882020-02-24 10:02:50 +01007145
7146 loc = startp;
7147 goto out;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007148
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02007149 case VSQUESTION:
Denys Vlasenko82331882020-02-24 10:02:50 +01007150 varunset(start, str, startp, varflags);
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02007151 /* NOTREACHED */
7152
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01007153#if BASH_SUBSTR
Denys Vlasenko826360f2017-07-17 17:49:11 +02007154 case VSSUBSTR: {
7155 int pos, len, orig_len;
7156 char *colon;
Denys Vlasenko7f198482020-02-24 09:57:08 +01007157 char *vstr;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007158
Denys Vlasenko7f198482020-02-24 09:57:08 +01007159 loc = vstr = stackblock() + strloc;
Denys Vlasenko826360f2017-07-17 17:49:11 +02007160
Denys Vlasenko826360f2017-07-17 17:49:11 +02007161 /* Read POS in ${var:POS:LEN} */
7162 colon = strchr(loc, ':');
7163 if (colon) *colon = '\0';
Denys Vlasenkobaa41c72018-01-10 13:22:25 +01007164 pos = substr_atoi(loc);
Denys Vlasenko826360f2017-07-17 17:49:11 +02007165 if (colon) *colon = ':';
7166
7167 /* Read LEN in ${var:POS:LEN} */
Denys Vlasenko7f198482020-02-24 09:57:08 +01007168 len = vstr - startp - 1;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007169 /* *loc != '\0', guaranteed by parser */
7170 if (quotes) {
7171 char *ptr;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02007172 /* Adjust the length by the number of escapes */
Denys Vlasenko7f198482020-02-24 09:57:08 +01007173 for (ptr = startp; ptr < (vstr - 1); ptr++) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01007174 if ((unsigned char)*ptr == CTLESC) {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007175 len--;
7176 ptr++;
7177 }
7178 }
7179 }
7180 orig_len = len;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007181 if (*loc++ == ':') {
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02007182 /* ${var::LEN} */
Denys Vlasenkobaa41c72018-01-10 13:22:25 +01007183 len = substr_atoi(loc);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007184 } else {
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02007185 /* Skip POS in ${var:POS:LEN} */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007186 len = orig_len;
Denys Vlasenkobaa41c72018-01-10 13:22:25 +01007187 while (*loc && *loc != ':')
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007188 loc++;
Denys Vlasenkobaa41c72018-01-10 13:22:25 +01007189 if (*loc++ == ':')
7190 len = substr_atoi(loc);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007191 }
Denys Vlasenko08a5dab2014-11-17 20:27:18 +01007192 if (pos < 0) {
7193 /* ${VAR:$((-n)):l} starts n chars from the end */
7194 pos = orig_len + pos;
7195 }
7196 if ((unsigned)pos >= orig_len) {
7197 /* apart from obvious ${VAR:999999:l},
7198 * covers ${VAR:$((-9999999)):l} - result is ""
Denys Vlasenko826360f2017-07-17 17:49:11 +02007199 * (bash compat)
Denys Vlasenko08a5dab2014-11-17 20:27:18 +01007200 */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007201 pos = 0;
7202 len = 0;
7203 }
Denys Vlasenko826360f2017-07-17 17:49:11 +02007204 if (len < 0) {
7205 /* ${VAR:N:-M} sets LEN to strlen()-M */
7206 len = (orig_len - pos) + len;
7207 }
7208 if ((unsigned)len > (orig_len - pos))
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007209 len = orig_len - pos;
7210
Denys Vlasenko7f198482020-02-24 09:57:08 +01007211 for (vstr = startp; pos; vstr++, pos--) {
7212 if (quotes && (unsigned char)*vstr == CTLESC)
7213 vstr++;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007214 }
7215 for (loc = startp; len; len--) {
Denys Vlasenko7f198482020-02-24 09:57:08 +01007216 if (quotes && (unsigned char)*vstr == CTLESC)
7217 *loc++ = *vstr++;
7218 *loc++ = *vstr++;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007219 }
7220 *loc = '\0';
Denys Vlasenko82331882020-02-24 10:02:50 +01007221 goto out;
Denys Vlasenko826360f2017-07-17 17:49:11 +02007222 }
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01007223#endif /* BASH_SUBSTR */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007224 }
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02007225
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007226 resetloc = expdest - (char *)stackblock();
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007227
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01007228#if BASH_PATTERN_SUBST
Denys Vlasenko740058b2018-01-09 17:01:00 +01007229 repl = NULL;
7230
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007231 /* We'll comeback here if we grow the stack while handling
7232 * a VSREPLACE or VSREPLACEALL, since our pointers into the
7233 * stack will need rebasing, and we'll need to remove our work
7234 * areas each time
7235 */
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01007236 restart:
7237#endif
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007238
7239 amount = expdest - ((char *)stackblock() + resetloc);
7240 STADJUST(-amount, expdest);
Denis Vlasenko29eb3592008-05-18 14:06:08 +00007241 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007242
7243 rmesc = startp;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00007244 rmescend = (char *)stackblock() + strloc;
Denys Vlasenko740058b2018-01-09 17:01:00 +01007245 //bb_error_msg("str7:'%s'", rmescend);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007246 if (quotes) {
Denys Vlasenko740058b2018-01-09 17:01:00 +01007247//TODO: how to handle slash_pos here if string changes (shortens?)
7248 rmesc = rmescapes(startp, RMESCAPE_ALLOC | RMESCAPE_GROW, NULL);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007249 if (rmesc != startp) {
7250 rmescend = expdest;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00007251 startp = (char *)stackblock() + startloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007252 }
7253 }
7254 rmescend--;
Denis Vlasenko29eb3592008-05-18 14:06:08 +00007255 str = (char *)stackblock() + strloc;
Ron Yorston417622c2015-05-18 09:59:14 +02007256 /*
7257 * Example: v='a\bc'; echo ${v/\\b/_\\_\z_}
7258 * The result is a_\_z_c (not a\_\_z_c)!
7259 *
7260 * The search pattern and replace string treat backslashes differently!
Denys Vlasenko740058b2018-01-09 17:01:00 +01007261 * "&slash_pos" causes rmescapes() to work differently on the pattern
Ron Yorston417622c2015-05-18 09:59:14 +02007262 * and string. It's only used on the first call.
7263 */
Denys Vlasenko740058b2018-01-09 17:01:00 +01007264 //bb_error_msg("str8:'%s' slash_pos:%d", str, slash_pos);
7265 rmescapes(str, RMESCAPE_GLOB,
7266 repl ? NULL : (slash_pos < 0 ? NULL : &slash_pos)
7267 );
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007268
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01007269#if BASH_PATTERN_SUBST
Denys Vlasenko0b4980c2012-09-25 12:49:29 +02007270 workloc = expdest - (char *)stackblock();
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007271 if (subtype == VSREPLACE || subtype == VSREPLACEALL) {
Denys Vlasenko1310d7b2021-07-22 18:15:59 +02007272 size_t no_meta_len;
Denys Vlasenko826360f2017-07-17 17:49:11 +02007273 int len;
Denys Vlasenkob76356b2010-03-13 16:19:04 +01007274 char *idx, *end;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007275
Denis Vlasenkod6855d12008-09-27 14:03:25 +00007276 if (!repl) {
Denys Vlasenko740058b2018-01-09 17:01:00 +01007277 //bb_error_msg("str9:'%s' slash_pos:%d", str, slash_pos);
Denys Vlasenkod1df1a72018-01-09 17:25:58 +01007278 repl = nullstr;
Denys Vlasenko740058b2018-01-09 17:01:00 +01007279 if (slash_pos >= 0) {
7280 repl = str + slash_pos;
Ron Yorston417622c2015-05-18 09:59:14 +02007281 *repl++ = '\0';
Denys Vlasenko740058b2018-01-09 17:01:00 +01007282 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007283 }
Ron Yorston417622c2015-05-18 09:59:14 +02007284 //bb_error_msg("str:'%s' repl:'%s'", str, repl);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007285
7286 /* If there's no pattern to match, return the expansion unmolested */
Denys Vlasenkob76356b2010-03-13 16:19:04 +01007287 if (str[0] == '\0')
Denys Vlasenko82331882020-02-24 10:02:50 +01007288 goto out1;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007289
Denys Vlasenko1310d7b2021-07-22 18:15:59 +02007290 no_meta_len = (ENABLE_ASH_OPTIMIZE_FOR_SIZE || strpbrk(str, "*?[\\")) ? 0 : strlen(str);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007291 len = 0;
7292 idx = startp;
7293 end = str - 1;
Denys Vlasenko2b7c1aa2021-01-09 08:46:54 +01007294 while (idx <= end) {
Denys Vlasenkob76356b2010-03-13 16:19:04 +01007295 try_to_match:
Denys Vlasenko1310d7b2021-07-22 18:15:59 +02007296 if (no_meta_len == 0) {
Denys Vlasenko53d45c92021-07-25 21:54:14 +02007297 /* pattern has meta chars, have to glob; or ENABLE_ASH_OPTIMIZE_FOR_SIZE */
Denys Vlasenko1310d7b2021-07-22 18:15:59 +02007298 loc = scanright(idx, rmesc, rmescend, str, quotes, /*match_at_start:*/ 1);
7299 } else {
7300 /* Testcase for very slow replace (performs about 22k replaces):
7301 * x=::::::::::::::::::::::
7302 * x=$x$x;x=$x$x;x=$x$x;x=$x$x;x=$x$x;x=$x$x;x=$x$x;x=$x$x;x=$x$x;x=$x$x;echo ${#x}
7303 * echo "${x//:/|}"
7304 */
Denys Vlasenko1310d7b2021-07-22 18:15:59 +02007305 if (strncmp(rmesc, str, no_meta_len) != 0)
7306 goto no_match;
Denys Vlasenko1310d7b2021-07-22 18:15:59 +02007307 loc = idx;
Denys Vlasenko53d45c92021-07-25 21:54:14 +02007308 if (!quotes) {
7309 loc += no_meta_len;
7310 } else {
7311 size_t n = no_meta_len;
7312 do {
7313 if ((unsigned char)*loc == CTLESC)
7314 loc++;
Denys Vlasenko1310d7b2021-07-22 18:15:59 +02007315 loc++;
Denys Vlasenko53d45c92021-07-25 21:54:14 +02007316 } while (--n != 0);
7317 }
Denys Vlasenko1310d7b2021-07-22 18:15:59 +02007318 }
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02007319 //bb_error_msg("scanright('%s'):'%s'", str, loc);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007320 if (!loc) {
Denys Vlasenko1310d7b2021-07-22 18:15:59 +02007321 char *restart_detect;
7322 no_match:
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007323 /* No match, advance */
Denys Vlasenko1310d7b2021-07-22 18:15:59 +02007324 restart_detect = stackblock();
Denys Vlasenkob76356b2010-03-13 16:19:04 +01007325 skip_matching:
Denys Vlasenko2b7c1aa2021-01-09 08:46:54 +01007326 if (idx >= end)
7327 break;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007328 STPUTC(*idx, expdest);
Denys Vlasenkocd716832009-11-28 22:14:02 +01007329 if (quotes && (unsigned char)*idx == CTLESC) {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007330 idx++;
7331 len++;
7332 STPUTC(*idx, expdest);
7333 }
7334 if (stackblock() != restart_detect)
7335 goto restart;
7336 idx++;
7337 len++;
7338 rmesc++;
Denys Vlasenkob76356b2010-03-13 16:19:04 +01007339 /* continue; - prone to quadratic behavior, smarter code: */
Denys Vlasenkob76356b2010-03-13 16:19:04 +01007340 if (str[0] == '*') {
7341 /* Pattern is "*foo". If "*foo" does not match "long_string",
7342 * it would never match "ong_string" etc, no point in trying.
7343 */
7344 goto skip_matching;
7345 }
7346 goto try_to_match;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007347 }
7348
7349 if (subtype == VSREPLACEALL) {
7350 while (idx < loc) {
Denys Vlasenkocd716832009-11-28 22:14:02 +01007351 if (quotes && (unsigned char)*idx == CTLESC)
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007352 idx++;
7353 idx++;
7354 rmesc++;
7355 }
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00007356 } else {
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007357 idx = loc;
Denis Vlasenko81c3a1d2008-12-03 11:59:12 +00007358 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007359
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02007360 //bb_error_msg("repl:'%s'", repl);
Denys Vlasenkofd33e172010-06-26 22:55:44 +02007361 for (loc = (char*)repl; *loc; loc++) {
Denys Vlasenkob76356b2010-03-13 16:19:04 +01007362 char *restart_detect = stackblock();
Denys Vlasenkofd33e172010-06-26 22:55:44 +02007363 if (quotes && *loc == '\\') {
7364 STPUTC(CTLESC, expdest);
7365 len++;
7366 }
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007367 STPUTC(*loc, expdest);
7368 if (stackblock() != restart_detect)
7369 goto restart;
7370 len++;
7371 }
7372
7373 if (subtype == VSREPLACE) {
Denys Vlasenkof02c82f2010-08-06 19:14:47 +02007374 //bb_error_msg("tail:'%s', quotes:%x", idx, quotes);
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007375 while (*idx) {
Denys Vlasenkob76356b2010-03-13 16:19:04 +01007376 char *restart_detect = stackblock();
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007377 STPUTC(*idx, expdest);
7378 if (stackblock() != restart_detect)
7379 goto restart;
7380 len++;
7381 idx++;
7382 }
7383 break;
7384 }
7385 }
7386
7387 /* We've put the replaced text into a buffer at workloc, now
7388 * move it to the right place and adjust the stack.
7389 */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007390 STPUTC('\0', expdest);
Denys Vlasenkofd33e172010-06-26 22:55:44 +02007391 startp = (char *)stackblock() + startloc;
7392 memmove(startp, (char *)stackblock() + workloc, len + 1);
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +02007393 //bb_error_msg("startp:'%s'", startp);
Denys Vlasenko82331882020-02-24 10:02:50 +01007394 loc = startp + len;
7395 goto out;
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007396 }
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01007397#endif /* BASH_PATTERN_SUBST */
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007398
7399 subtype -= VSTRIMRIGHT;
7400#if DEBUG
7401 if (subtype < 0 || subtype > 7)
7402 abort();
7403#endif
Denys Vlasenkob76356b2010-03-13 16:19:04 +01007404 /* zero = (subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX) */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007405 zero = subtype >> 1;
7406 /* VSTRIMLEFT/VSTRIMRIGHTMAX -> scanleft */
7407 scan = (subtype & 1) ^ zero ? scanleft : scanright;
7408
7409 loc = scan(startp, rmesc, rmescend, str, quotes, zero);
7410 if (loc) {
7411 if (zero) {
7412 memmove(startp, loc, str - loc);
7413 loc = startp + (str - loc) - 1;
7414 }
7415 *loc = '\0';
Denys Vlasenko82331882020-02-24 10:02:50 +01007416 } else
7417 loc = str - 1;
7418
7419 out:
7420 amount = loc - expdest;
7421 STADJUST(amount, expdest);
Cristian Ionescu-Idbohrnddfdf682020-11-18 10:41:14 +01007422#if BASH_PATTERN_SUBST
Denys Vlasenko82331882020-02-24 10:02:50 +01007423 out1:
Cristian Ionescu-Idbohrnddfdf682020-11-18 10:41:14 +01007424#endif
Denys Vlasenko82331882020-02-24 10:02:50 +01007425 /* Remove any recorded regions beyond start of variable */
7426 removerecordregions(startloc);
7427
7428 return p;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007429}
7430
7431/*
7432 * Add the value of a specialized variable to the stack string.
Denys Vlasenko4d8873f2009-10-04 03:14:41 +02007433 * name parameter (examples):
7434 * ash -c 'echo $1' name:'1='
7435 * ash -c 'echo $qwe' name:'qwe='
7436 * ash -c 'echo $$' name:'$='
7437 * ash -c 'echo ${$}' name:'$='
7438 * ash -c 'echo ${$##q}' name:'$=q'
7439 * ash -c 'echo ${#$}' name:'$='
7440 * note: examples with bad shell syntax:
7441 * ash -c 'echo ${#$1}' name:'$=1'
7442 * ash -c 'echo ${#1#}' name:'1=#'
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007443 */
Denys Vlasenkoadf922e2009-10-08 14:35:37 +02007444static NOINLINE ssize_t
Denys Vlasenko440da972018-08-05 14:29:58 +02007445varvalue(char *name, int varflags, int flags, int quoted)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007446{
Mike Frysinger98c52642009-04-02 10:02:37 +00007447 const char *p;
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01007448 int num;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007449 int i;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007450 ssize_t len = 0;
Ron Yorstond68d1fb2015-05-18 09:49:28 +02007451 int sep;
Ron Yorstond68d1fb2015-05-18 09:49:28 +02007452 int subtype = varflags & VSTYPE;
Denys Vlasenko82331882020-02-24 10:02:50 +01007453 int discard = (subtype == VSPLUS || subtype == VSLENGTH) | (flags & EXP_DISCARD);
7454
7455 if (!subtype) {
7456 if (discard)
7457 return -1;
7458
7459 raise_error_syntax("bad substitution");
7460 }
Denys Vlasenko0aaaa502016-10-02 02:46:56 +02007461
Denys Vlasenkoecc85832020-02-20 10:06:20 +01007462 flags |= EXP_KEEPNUL;
7463 flags &= discard ? ~QUOTES_ESC : ~0;
Denys Vlasenko0aaaa502016-10-02 02:46:56 +02007464 sep = (flags & EXP_FULL) << CHAR_BIT;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007465
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007466 switch (*name) {
7467 case '$':
7468 num = rootpid;
7469 goto numvar;
7470 case '?':
7471 num = exitstatus;
7472 goto numvar;
7473 case '#':
7474 num = shellparam.nparam;
7475 goto numvar;
7476 case '!':
7477 num = backgndpid;
7478 if (num == 0)
7479 return -1;
7480 numvar:
Denys Vlasenko45dd87a2020-02-21 16:30:44 +01007481 len = cvtnum(num, flags);
Denys Vlasenko4d8873f2009-10-04 03:14:41 +02007482 goto check_1char_name;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007483 case '-':
Mike Frysinger98c52642009-04-02 10:02:37 +00007484 expdest = makestrspace(NOPTS, expdest);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007485 for (i = NOPTS - 1; i >= 0; i--) {
Martijn Dekkerad4e9612018-03-31 18:15:59 +02007486 if (optlist[i] && optletters(i)) {
Mike Frysinger98c52642009-04-02 10:02:37 +00007487 USTPUTC(optletters(i), expdest);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007488 len++;
7489 }
7490 }
Denys Vlasenko4d8873f2009-10-04 03:14:41 +02007491 check_1char_name:
7492#if 0
7493 /* handles cases similar to ${#$1} */
7494 if (name[2] != '\0')
7495 raise_error_syntax("bad substitution");
7496#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007497 break;
Denys Vlasenko0aaaa502016-10-02 02:46:56 +02007498 case '@':
7499 if (quoted && sep)
7500 goto param;
7501 /* fall through */
7502 case '*': {
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01007503 char **ap;
Ron Yorstond68d1fb2015-05-18 09:49:28 +02007504 char sepc;
Denys Vlasenko440da972018-08-05 14:29:58 +02007505 char c;
Denys Vlasenko8eda4a92009-11-30 12:16:17 +01007506
Denys Vlasenko440da972018-08-05 14:29:58 +02007507 /* We will set c to 0 or ~0 depending on whether
7508 * we're doing field splitting. We won't do field
7509 * splitting if either we're quoted or sep is zero.
7510 *
7511 * Instead of testing (quoted || !sep) the following
7512 * trick optimises away any branches by using the
7513 * fact that EXP_QUOTED (which is the only bit that
7514 * can be set in quoted) is the same as EXP_FULL <<
7515 * CHAR_BIT (which is the only bit that can be set
7516 * in sep).
7517 */
7518#if EXP_QUOTED >> CHAR_BIT != EXP_FULL
7519#error The following two lines expect EXP_QUOTED == EXP_FULL << CHAR_BIT
7520#endif
7521 c = !((quoted | ~sep) & EXP_QUOTED) - 1;
7522 sep &= ~quoted;
7523 sep |= ifsset() ? (unsigned char)(c & ifsval()[0]) : ' ';
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007524 param:
Ron Yorstond68d1fb2015-05-18 09:49:28 +02007525 sepc = sep;
Denys Vlasenko0dd8e452016-10-01 21:02:06 +02007526 ap = shellparam.p;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007527 if (!ap)
7528 return -1;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02007529 while ((p = *ap++) != NULL) {
Denys Vlasenkoecc85832020-02-20 10:06:20 +01007530 len += strtodest(p, flags);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007531
7532 if (*ap && sep) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007533 len++;
Denys Vlasenkoecc85832020-02-20 10:06:20 +01007534 memtodest(&sepc, 1, flags);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007535 }
7536 }
Ron Yorstond68d1fb2015-05-18 09:49:28 +02007537 break;
Denys Vlasenko0aaaa502016-10-02 02:46:56 +02007538 } /* case '*' */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007539 case '0':
7540 case '1':
7541 case '2':
7542 case '3':
7543 case '4':
7544 case '5':
7545 case '6':
7546 case '7':
7547 case '8':
7548 case '9':
Denys Vlasenkoa00329c2009-08-30 20:05:10 +02007549 num = atoi(name); /* number(name) fails on ${N#str} etc */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007550 if (num < 0 || num > shellparam.nparam)
7551 return -1;
7552 p = num ? shellparam.p[num - 1] : arg0;
7553 goto value;
7554 default:
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00007555 /* NB: name has form "VAR=..." */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007556 p = lookupvar(name);
7557 value:
7558 if (!p)
7559 return -1;
7560
Denys Vlasenkoecc85832020-02-20 10:06:20 +01007561 len = strtodest(p, flags);
Denys Vlasenkoc76236f2014-12-29 00:04:18 +01007562#if ENABLE_UNICODE_SUPPORT
7563 if (subtype == VSLENGTH && len > 0) {
7564 reinit_unicode_for_ash();
7565 if (unicode_status == UNICODE_ON) {
Ron Yorston3e3bfb82016-03-18 11:29:19 +00007566 STADJUST(-len, expdest);
7567 discard = 0;
Denys Vlasenkoc76236f2014-12-29 00:04:18 +01007568 len = unicode_strlen(p);
7569 }
7570 }
7571#endif
Ron Yorstond68d1fb2015-05-18 09:49:28 +02007572 break;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007573 }
7574
Ron Yorstond68d1fb2015-05-18 09:49:28 +02007575 if (discard)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007576 STADJUST(-len, expdest);
Denys Vlasenko82331882020-02-24 10:02:50 +01007577
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007578 return len;
7579}
7580
7581/*
7582 * Expand a variable, and return a pointer to the next character in the
7583 * input string.
7584 */
7585static char *
Denys Vlasenkob8c0bc12017-07-26 23:03:21 +02007586evalvar(char *p, int flag)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007587{
Denis Vlasenko0e6f6612008-02-15 15:02:15 +00007588 char varflags;
7589 char subtype;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007590 char *var;
7591 int patloc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007592 int startloc;
7593 ssize_t varlen;
Denys Vlasenko15558952020-02-22 19:38:40 +01007594 int discard;
Denys Vlasenko82331882020-02-24 10:02:50 +01007595 int quoted;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007596
Denys Vlasenkob0d63382009-09-16 16:18:32 +02007597 varflags = (unsigned char) *p++;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007598 subtype = varflags & VSTYPE;
Denys Vlasenko88e15702016-10-26 01:55:56 +02007599
Denys Vlasenko88ac97d2016-10-01 20:55:02 +02007600 quoted = flag & EXP_QUOTED;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007601 var = p;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007602 startloc = expdest - (char *)stackblock();
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02007603 p = strchr(p, '=') + 1; //TODO: use var_end(p)?
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007604
7605 again:
Denys Vlasenko440da972018-08-05 14:29:58 +02007606 varlen = varvalue(var, varflags, flag, quoted);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007607 if (varflags & VSNUL)
7608 varlen--;
7609
Denys Vlasenko15558952020-02-22 19:38:40 +01007610 discard = varlen < 0 ? EXP_DISCARD : 0;
7611
Denys Vlasenko82331882020-02-24 10:02:50 +01007612 switch (subtype) {
7613 case VSPLUS:
Denys Vlasenko15558952020-02-22 19:38:40 +01007614 discard ^= EXP_DISCARD;
Denys Vlasenko82331882020-02-24 10:02:50 +01007615 /* fall through */
7616 case 0:
7617 case VSMINUS:
Denys Vlasenko15558952020-02-22 19:38:40 +01007618 p = argstr(p, flag | EXP_TILDE | EXP_WORD | (discard ^ EXP_DISCARD));
Denys Vlasenko88ac97d2016-10-01 20:55:02 +02007619 goto record;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007620
Denys Vlasenko82331882020-02-24 10:02:50 +01007621 case VSASSIGN:
7622 case VSQUESTION:
Denys Vlasenko15558952020-02-22 19:38:40 +01007623 p = subevalvar(p, var, 0, startloc, varflags,
7624 (flag & ~QUOTES_ESC) | (discard ^ EXP_DISCARD));
7625
7626 if ((flag | ~discard) & EXP_DISCARD)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007627 goto record;
Denys Vlasenko88ac97d2016-10-01 20:55:02 +02007628
Denys Vlasenko88ac97d2016-10-01 20:55:02 +02007629 varflags &= ~VSNUL;
Denys Vlasenko15558952020-02-22 19:38:40 +01007630 subtype = VSNORMAL;
Denys Vlasenko88ac97d2016-10-01 20:55:02 +02007631 goto again;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007632 }
7633
Denys Vlasenko15558952020-02-22 19:38:40 +01007634 if ((discard & ~flag) && uflag)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007635 varunset(p, var, 0, 0);
7636
7637 if (subtype == VSLENGTH) {
Denys Vlasenko82331882020-02-24 10:02:50 +01007638 p++;
7639 if (flag & EXP_DISCARD)
7640 return p;
Denys Vlasenko45dd87a2020-02-21 16:30:44 +01007641 cvtnum(varlen > 0 ? varlen : 0, flag);
Denys Vlasenko15558952020-02-22 19:38:40 +01007642 goto really_record;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007643 }
7644
Denys Vlasenko82331882020-02-24 10:02:50 +01007645 if (subtype == VSNORMAL)
7646 goto record;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007647
7648#if DEBUG
7649 switch (subtype) {
7650 case VSTRIMLEFT:
7651 case VSTRIMLEFTMAX:
7652 case VSTRIMRIGHT:
7653 case VSTRIMRIGHTMAX:
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01007654#if BASH_SUBSTR
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007655 case VSSUBSTR:
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01007656#endif
7657#if BASH_PATTERN_SUBST
Denis Vlasenko92e13c22008-03-25 01:17:40 +00007658 case VSREPLACE:
7659 case VSREPLACEALL:
7660#endif
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007661 break;
7662 default:
7663 abort();
7664 }
7665#endif
7666
Denys Vlasenko15558952020-02-22 19:38:40 +01007667 flag |= discard;
Denys Vlasenko82331882020-02-24 10:02:50 +01007668 if (!(flag & EXP_DISCARD)) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007669 /*
7670 * Terminate the string and start recording the pattern
7671 * right after it
7672 */
7673 STPUTC('\0', expdest);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007674 }
7675
Denys Vlasenko82331882020-02-24 10:02:50 +01007676 patloc = expdest - (char *)stackblock();
7677 p = subevalvar(p, NULL, patloc, startloc, varflags, flag);
Denys Vlasenko4ace3852020-02-16 18:42:50 +01007678
Denys Vlasenko82331882020-02-24 10:02:50 +01007679 record:
Denys Vlasenko15558952020-02-22 19:38:40 +01007680 if ((flag | discard) & EXP_DISCARD)
Denys Vlasenko82331882020-02-24 10:02:50 +01007681 return p;
7682
Denys Vlasenko15558952020-02-22 19:38:40 +01007683 really_record:
Denys Vlasenko82331882020-02-24 10:02:50 +01007684 if (quoted) {
7685 quoted = *var == '@' && shellparam.nparam;
7686 if (!quoted)
7687 return p;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007688 }
Denys Vlasenko82331882020-02-24 10:02:50 +01007689 recordregion(startloc, expdest - (char *)stackblock(), quoted);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007690 return p;
7691}
7692
7693/*
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007694 * Add a file name to the list.
7695 */
7696static void
7697addfname(const char *name)
7698{
7699 struct strlist *sp;
7700
Denis Vlasenko597906c2008-02-20 16:38:54 +00007701 sp = stzalloc(sizeof(*sp));
Denys Vlasenko8e2bc472016-09-28 23:02:57 +02007702 sp->text = sstrdup(name);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007703 *exparg.lastp = sp;
7704 exparg.lastp = &sp->next;
7705}
7706
Felix Fietkaub5b21122017-01-31 21:58:55 +01007707/* Avoid glob() (and thus, stat() et al) for words like "echo" */
7708static int
7709hasmeta(const char *p)
7710{
7711 static const char chars[] ALIGN1 = {
7712 '*', '?', '[', '\\', CTLQUOTEMARK, CTLESC, 0
7713 };
7714
7715 for (;;) {
7716 p = strpbrk(p, chars);
7717 if (!p)
7718 break;
Denys Vlasenkoac61f442018-03-30 23:03:29 +02007719 switch ((unsigned char)*p) {
Felix Fietkaub5b21122017-01-31 21:58:55 +01007720 case CTLQUOTEMARK:
7721 for (;;) {
7722 p++;
Denys Vlasenkoac61f442018-03-30 23:03:29 +02007723 if ((unsigned char)*p == CTLQUOTEMARK)
Felix Fietkaub5b21122017-01-31 21:58:55 +01007724 break;
Denys Vlasenkoac61f442018-03-30 23:03:29 +02007725 if ((unsigned char)*p == CTLESC)
Felix Fietkaub5b21122017-01-31 21:58:55 +01007726 p++;
7727 if (*p == '\0') /* huh? */
7728 return 0;
7729 }
7730 break;
7731 case '\\':
7732 case CTLESC:
7733 p++;
7734 if (*p == '\0')
7735 return 0;
7736 break;
7737 case '[':
7738 if (!strchr(p + 1, ']')) {
7739 /* It's not a properly closed [] pattern,
7740 * but other metas may follow. Continue checking.
7741 * my[file* _is_ globbed by bash
7742 * and matches filenames like "my[file1".
7743 */
7744 break;
7745 }
7746 /* fallthrough */
7747 default:
7748 /* case '*': */
7749 /* case '?': */
7750 return 1;
7751 }
7752 p++;
7753 }
7754
7755 return 0;
7756}
7757
Denys Vlasenkob3f29b42016-09-21 16:25:58 +02007758/* If we want to use glob() from libc... */
Denys Vlasenko514b51d2016-10-01 14:33:08 +02007759#if !ENABLE_ASH_INTERNAL_GLOB
Denys Vlasenkob3f29b42016-09-21 16:25:58 +02007760
7761/* Add the result of glob() to the list */
7762static void
7763addglob(const glob_t *pglob)
7764{
7765 char **p = pglob->gl_pathv;
7766
7767 do {
7768 addfname(*p);
7769 } while (*++p);
7770}
7771static void
7772expandmeta(struct strlist *str /*, int flag*/)
7773{
7774 /* TODO - EXP_REDIR */
7775
7776 while (str) {
7777 char *p;
7778 glob_t pglob;
7779 int i;
7780
7781 if (fflag)
7782 goto nometa;
Denys Vlasenkod4f3db92016-10-30 18:41:01 +01007783
Felix Fietkaub5b21122017-01-31 21:58:55 +01007784 if (!hasmeta(str->text))
7785 goto nometa;
Denys Vlasenkod4f3db92016-10-30 18:41:01 +01007786
Denys Vlasenkob3f29b42016-09-21 16:25:58 +02007787 INT_OFF;
7788 p = preglob(str->text, RMESCAPE_ALLOC | RMESCAPE_HEAP);
Denys Vlasenko8e2c9cc2016-10-02 15:17:15 +02007789// GLOB_NOMAGIC (GNU): if no *?[ chars in pattern, return it even if no match
7790// GLOB_NOCHECK: if no match, return unchanged pattern (sans \* escapes?)
7791//
7792// glibc 2.24.90 glob(GLOB_NOMAGIC) does not remove backslashes used for escaping:
7793// if you pass it "file\?", it returns "file\?", not "file?", if no match.
7794// Which means you need to unescape the string, right? Not so fast:
7795// if there _is_ a file named "file\?" (with backslash), it is returned
7796// as "file\?" too (whichever pattern you used to find it, say, "file*").
Denys Vlasenko10ad6222017-04-17 16:13:32 +02007797// You DON'T KNOW by looking at the result whether you need to unescape it.
Denys Vlasenko8e2c9cc2016-10-02 15:17:15 +02007798//
7799// Worse, globbing of "file\?" in a directory with two files, "file?" and "file\?",
7800// returns "file\?" - which is WRONG: "file\?" pattern matches "file?" file.
7801// Without GLOB_NOMAGIC, this works correctly ("file?" is returned as a match).
7802// With GLOB_NOMAGIC | GLOB_NOCHECK, this also works correctly.
7803// i = glob(p, GLOB_NOMAGIC | GLOB_NOCHECK, NULL, &pglob);
7804// i = glob(p, GLOB_NOMAGIC, NULL, &pglob);
7805 i = glob(p, 0, NULL, &pglob);
7806 //bb_error_msg("glob('%s'):%d '%s'...", p, i, pglob.gl_pathv ? pglob.gl_pathv[0] : "-");
Denys Vlasenkob3f29b42016-09-21 16:25:58 +02007807 if (p != str->text)
7808 free(p);
7809 switch (i) {
7810 case 0:
Denys Vlasenko8e2c9cc2016-10-02 15:17:15 +02007811#if 0 // glibc 2.24.90 bug? Patterns like "*/file", when match, don't set GLOB_MAGCHAR
Denys Vlasenkob3f29b42016-09-21 16:25:58 +02007812 /* GLOB_MAGCHAR is set if *?[ chars were seen (GNU) */
7813 if (!(pglob.gl_flags & GLOB_MAGCHAR))
7814 goto nometa2;
Denys Vlasenko8e2c9cc2016-10-02 15:17:15 +02007815#endif
Denys Vlasenkob3f29b42016-09-21 16:25:58 +02007816 addglob(&pglob);
7817 globfree(&pglob);
7818 INT_ON;
7819 break;
7820 case GLOB_NOMATCH:
Denys Vlasenko8e2c9cc2016-10-02 15:17:15 +02007821 //nometa2:
Denys Vlasenkob3f29b42016-09-21 16:25:58 +02007822 globfree(&pglob);
7823 INT_ON;
Denys Vlasenko8e2c9cc2016-10-02 15:17:15 +02007824 nometa:
Denys Vlasenkob3f29b42016-09-21 16:25:58 +02007825 *exparg.lastp = str;
Denys Vlasenko740058b2018-01-09 17:01:00 +01007826 rmescapes(str->text, 0, NULL);
Denys Vlasenkob3f29b42016-09-21 16:25:58 +02007827 exparg.lastp = &str->next;
7828 break;
7829 default: /* GLOB_NOSPACE */
7830 globfree(&pglob);
7831 INT_ON;
7832 ash_msg_and_raise_error(bb_msg_memory_exhausted);
7833 }
7834 str = str->next;
7835 }
7836}
7837
7838#else
Denys Vlasenko514b51d2016-10-01 14:33:08 +02007839/* ENABLE_ASH_INTERNAL_GLOB: Homegrown globbing code. (dash also has both, uses homegrown one.) */
Denys Vlasenkob3f29b42016-09-21 16:25:58 +02007840
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007841/*
7842 * Do metacharacter (i.e. *, ?, [...]) expansion.
7843 */
Denys Vlasenkod5f50452018-04-14 14:50:47 +02007844typedef struct exp_t {
7845 char *dir;
7846 unsigned dir_max;
7847} exp_t;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007848static void
Denys Vlasenkod5f50452018-04-14 14:50:47 +02007849expmeta(exp_t *exp, char *name, unsigned name_len, unsigned expdir_len)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007850{
Denys Vlasenkod5f50452018-04-14 14:50:47 +02007851#define expdir exp->dir
7852#define expdir_max exp->dir_max
7853 char *enddir = expdir + expdir_len;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007854 char *p;
7855 const char *cp;
7856 char *start;
7857 char *endname;
7858 int metaflag;
7859 struct stat statb;
7860 DIR *dirp;
7861 struct dirent *dp;
7862 int atend;
7863 int matchdot;
Ron Yorstonca25af92015-09-04 10:32:41 +01007864 int esc;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007865
7866 metaflag = 0;
7867 start = name;
Ron Yorstonca25af92015-09-04 10:32:41 +01007868 for (p = name; esc = 0, *p; p += esc + 1) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007869 if (*p == '*' || *p == '?')
7870 metaflag = 1;
7871 else if (*p == '[') {
7872 char *q = p + 1;
7873 if (*q == '!')
7874 q++;
7875 for (;;) {
7876 if (*q == '\\')
7877 q++;
7878 if (*q == '/' || *q == '\0')
7879 break;
7880 if (*++q == ']') {
7881 metaflag = 1;
7882 break;
7883 }
7884 }
Ron Yorstonca25af92015-09-04 10:32:41 +01007885 } else {
Denys Vlasenkoeb54ca82018-08-07 18:54:52 +02007886 if (*p == '\\' && p[1])
Ron Yorstonca25af92015-09-04 10:32:41 +01007887 esc++;
7888 if (p[esc] == '/') {
7889 if (metaflag)
7890 break;
7891 start = p + esc + 1;
7892 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007893 }
7894 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007895 if (metaflag == 0) { /* we've reached the end of the file name */
Denys Vlasenkod5f50452018-04-14 14:50:47 +02007896 if (!expdir_len)
7897 return;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007898 p = name;
7899 do {
Denys Vlasenkoeb54ca82018-08-07 18:54:52 +02007900 if (*p == '\\' && p[1])
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007901 p++;
7902 *enddir++ = *p;
7903 } while (*p++);
Denys Vlasenkod5f50452018-04-14 14:50:47 +02007904 if (lstat(expdir, &statb) == 0)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007905 addfname(expdir);
7906 return;
7907 }
7908 endname = p;
7909 if (name < start) {
7910 p = name;
7911 do {
Denys Vlasenkoeb54ca82018-08-07 18:54:52 +02007912 if (*p == '\\' && p[1])
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007913 p++;
7914 *enddir++ = *p++;
7915 } while (p < start);
7916 }
Denys Vlasenkod5f50452018-04-14 14:50:47 +02007917 *enddir = '\0';
7918 cp = expdir;
7919 expdir_len = enddir - cp;
7920 if (!expdir_len)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007921 cp = ".";
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007922 dirp = opendir(cp);
7923 if (dirp == NULL)
7924 return;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007925 if (*endname == 0) {
7926 atend = 1;
7927 } else {
7928 atend = 0;
Ron Yorstonca25af92015-09-04 10:32:41 +01007929 *endname = '\0';
7930 endname += esc + 1;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007931 }
Denys Vlasenkod5f50452018-04-14 14:50:47 +02007932 name_len -= endname - name;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007933 matchdot = 0;
7934 p = start;
7935 if (*p == '\\')
7936 p++;
7937 if (*p == '.')
7938 matchdot++;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02007939 while (!pending_int && (dp = readdir(dirp)) != NULL) {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00007940 if (dp->d_name[0] == '.' && !matchdot)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007941 continue;
7942 if (pmatch(start, dp->d_name)) {
7943 if (atend) {
7944 strcpy(enddir, dp->d_name);
7945 addfname(expdir);
7946 } else {
Denys Vlasenkod5f50452018-04-14 14:50:47 +02007947 unsigned offset;
7948 unsigned len;
7949
7950 p = stpcpy(enddir, dp->d_name);
7951 *p = '/';
7952
7953 offset = p - expdir + 1;
7954 len = offset + name_len + NAME_MAX;
7955 if (len > expdir_max) {
7956 len += PATH_MAX;
7957 expdir = ckrealloc(expdir, len);
7958 expdir_max = len;
7959 }
7960
7961 expmeta(exp, endname, name_len, offset);
7962 enddir = expdir + expdir_len;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007963 }
7964 }
7965 }
7966 closedir(dirp);
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00007967 if (!atend)
Ron Yorstonca25af92015-09-04 10:32:41 +01007968 endname[-esc - 1] = esc ? '\\' : '/';
Denys Vlasenkod5f50452018-04-14 14:50:47 +02007969#undef expdir
7970#undef expdir_max
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007971}
7972
7973static struct strlist *
7974msort(struct strlist *list, int len)
7975{
7976 struct strlist *p, *q = NULL;
7977 struct strlist **lpp;
7978 int half;
7979 int n;
7980
7981 if (len <= 1)
7982 return list;
7983 half = len >> 1;
7984 p = list;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00007985 for (n = half; --n >= 0;) {
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00007986 q = p;
7987 p = p->next;
7988 }
7989 q->next = NULL; /* terminate first half of list */
7990 q = msort(list, half); /* sort first half of list */
7991 p = msort(p, len - half); /* sort second half */
7992 lpp = &list;
7993 for (;;) {
7994#if ENABLE_LOCALE_SUPPORT
7995 if (strcoll(p->text, q->text) < 0)
7996#else
7997 if (strcmp(p->text, q->text) < 0)
7998#endif
7999 {
8000 *lpp = p;
8001 lpp = &p->next;
8002 p = *lpp;
8003 if (p == NULL) {
8004 *lpp = q;
8005 break;
8006 }
8007 } else {
8008 *lpp = q;
8009 lpp = &q->next;
8010 q = *lpp;
8011 if (q == NULL) {
8012 *lpp = p;
8013 break;
8014 }
8015 }
8016 }
8017 return list;
8018}
8019
8020/*
8021 * Sort the results of file name expansion. It calculates the number of
8022 * strings to sort and then calls msort (short for merge sort) to do the
8023 * work.
8024 */
8025static struct strlist *
8026expsort(struct strlist *str)
8027{
8028 int len;
8029 struct strlist *sp;
8030
8031 len = 0;
8032 for (sp = str; sp; sp = sp->next)
8033 len++;
8034 return msort(str, len);
8035}
8036
8037static void
Denis Vlasenko68404f12008-03-17 09:00:54 +00008038expandmeta(struct strlist *str /*, int flag*/)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008039{
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008040 /* TODO - EXP_REDIR */
8041
8042 while (str) {
Denys Vlasenkod5f50452018-04-14 14:50:47 +02008043 exp_t exp;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008044 struct strlist **savelastp;
8045 struct strlist *sp;
8046 char *p;
Denys Vlasenkod5f50452018-04-14 14:50:47 +02008047 unsigned len;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008048
8049 if (fflag)
8050 goto nometa;
Felix Fietkaub5b21122017-01-31 21:58:55 +01008051 if (!hasmeta(str->text))
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008052 goto nometa;
8053 savelastp = exparg.lastp;
8054
8055 INT_OFF;
Ron Yorston549deab2015-05-18 09:57:51 +02008056 p = preglob(str->text, RMESCAPE_ALLOC | RMESCAPE_HEAP);
Denys Vlasenkod5f50452018-04-14 14:50:47 +02008057 len = strlen(p);
8058 exp.dir_max = len + PATH_MAX;
8059 exp.dir = ckmalloc(exp.dir_max);
8060
8061 expmeta(&exp, p, len, 0);
8062 free(exp.dir);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008063 if (p != str->text)
8064 free(p);
8065 INT_ON;
8066 if (exparg.lastp == savelastp) {
8067 /*
8068 * no matches
8069 */
8070 nometa:
8071 *exparg.lastp = str;
Denys Vlasenko740058b2018-01-09 17:01:00 +01008072 rmescapes(str->text, 0, NULL);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008073 exparg.lastp = &str->next;
8074 } else {
8075 *exparg.lastp = NULL;
8076 *savelastp = sp = expsort(*savelastp);
8077 while (sp->next != NULL)
8078 sp = sp->next;
8079 exparg.lastp = &sp->next;
8080 }
8081 str = str->next;
8082 }
8083}
Denys Vlasenko514b51d2016-10-01 14:33:08 +02008084#endif /* ENABLE_ASH_INTERNAL_GLOB */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008085
8086/*
8087 * Perform variable substitution and command substitution on an argument,
8088 * placing the resulting list of arguments in arglist. If EXP_FULL is true,
8089 * perform splitting and file name expansion. When arglist is NULL, perform
8090 * here document expansion.
8091 */
8092static void
8093expandarg(union node *arg, struct arglist *arglist, int flag)
8094{
8095 struct strlist *sp;
8096 char *p;
8097
8098 argbackq = arg->narg.backquote;
8099 STARTSTACKSTR(expdest);
Denys Vlasenkof451b2c2012-06-09 02:06:57 +02008100 TRACE(("expandarg: argstr('%s',flags:%x)\n", arg->narg.text, flag));
Denys Vlasenkob8c0bc12017-07-26 23:03:21 +02008101 argstr(arg->narg.text, flag);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008102 if (arglist == NULL) {
Denys Vlasenko5ac04f22016-10-27 14:46:50 +02008103 /* here document expanded */
8104 goto out;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008105 }
Denys Vlasenko82331882020-02-24 10:02:50 +01008106 p = grabstackstr(expdest);
Denys Vlasenkof451b2c2012-06-09 02:06:57 +02008107 TRACE(("expandarg: p:'%s'\n", p));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008108 exparg.lastp = &exparg.list;
8109 /*
8110 * TODO - EXP_REDIR
8111 */
8112 if (flag & EXP_FULL) {
8113 ifsbreakup(p, &exparg);
8114 *exparg.lastp = NULL;
8115 exparg.lastp = &exparg.list;
Denis Vlasenko68404f12008-03-17 09:00:54 +00008116 expandmeta(exparg.list /*, flag*/);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008117 } else {
Denis Vlasenko597906c2008-02-20 16:38:54 +00008118 sp = stzalloc(sizeof(*sp));
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008119 sp->text = p;
8120 *exparg.lastp = sp;
8121 exparg.lastp = &sp->next;
8122 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008123 *exparg.lastp = NULL;
8124 if (exparg.list) {
8125 *arglist->lastp = exparg.list;
8126 arglist->lastp = exparg.lastp;
8127 }
Denys Vlasenko5ac04f22016-10-27 14:46:50 +02008128
8129 out:
8130 ifsfree();
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008131}
8132
8133/*
8134 * Expand shell variables and backquotes inside a here document.
8135 */
8136static void
Denys Vlasenkoc2058ec2020-02-22 20:25:03 +01008137expandhere(union node *arg)
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008138{
Ron Yorston549deab2015-05-18 09:57:51 +02008139 expandarg(arg, (struct arglist *)NULL, EXP_QUOTED);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008140}
8141
8142/*
8143 * Returns true if the pattern matches the string.
8144 */
8145static int
8146patmatch(char *pattern, const char *string)
8147{
Denys Vlasenkobd43c672017-07-05 23:12:15 +02008148 char *p = preglob(pattern, 0);
Denys Vlasenko4476c702017-08-15 15:27:41 +02008149 int r = pmatch(p, string);
8150 //bb_error_msg("!fnmatch(pattern:'%s',str:'%s',0):%d", p, string, r);
8151 return r;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008152}
8153
8154/*
8155 * See if a pattern matches in a case statement.
8156 */
8157static int
8158casematch(union node *pattern, char *val)
8159{
8160 struct stackmark smark;
8161 int result;
8162
8163 setstackmark(&smark);
8164 argbackq = pattern->narg.backquote;
8165 STARTSTACKSTR(expdest);
Denys Vlasenkob8c0bc12017-07-26 23:03:21 +02008166 argstr(pattern->narg.text, EXP_TILDE | EXP_CASE);
Denys Vlasenko5ac04f22016-10-27 14:46:50 +02008167 ifsfree();
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00008168 result = patmatch(stackblock(), val);
8169 popstackmark(&smark);
8170 return result;
8171}
8172
8173
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008174/* ============ find_command */
8175
8176struct builtincmd {
8177 const char *name;
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008178 int (*builtin)(int, char **) FAST_FUNC;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008179 /* unsigned flags; */
8180};
8181#define IS_BUILTIN_SPECIAL(b) ((b)->name[0] & 1)
Denis Vlasenkoe26b2782008-02-12 07:40:29 +00008182/* "regular" builtins always take precedence over commands,
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00008183 * regardless of PATH=....%builtin... position */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008184#define IS_BUILTIN_REGULAR(b) ((b)->name[0] & 2)
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00008185#define IS_BUILTIN_ASSIGN(b) ((b)->name[0] & 4)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008186
8187struct cmdentry {
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00008188 smallint cmdtype; /* CMDxxx */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008189 union param {
8190 int index;
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00008191 /* index >= 0 for commands without path (slashes) */
8192 /* (TODO: what exactly does the value mean? PATH position?) */
8193 /* index == -1 for commands with slashes */
8194 /* index == (-2 - applet_no) for NOFORK applets */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008195 const struct builtincmd *cmd;
8196 struct funcnode *func;
8197 } u;
8198};
8199/* values of cmdtype */
8200#define CMDUNKNOWN -1 /* no entry in table for command */
8201#define CMDNORMAL 0 /* command is an executable program */
8202#define CMDFUNCTION 1 /* command is a shell function */
8203#define CMDBUILTIN 2 /* command is a shell builtin */
8204
8205/* action to find_command() */
8206#define DO_ERR 0x01 /* prints errors */
8207#define DO_ABS 0x02 /* checks absolute paths */
8208#define DO_NOFUNC 0x04 /* don't return shell functions, for command */
8209#define DO_ALTPATH 0x08 /* using alternate path */
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +01008210#define DO_REGBLTIN 0x10 /* regular built-ins and functions only */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008211
8212static void find_command(char *, struct cmdentry *, int, const char *);
8213
8214
8215/* ============ Hashing commands */
8216
8217/*
8218 * When commands are first encountered, they are entered in a hash table.
8219 * This ensures that a full path search will not have to be done for them
8220 * on each invocation.
8221 *
8222 * We should investigate converting to a linear search, even though that
8223 * would make the command name "hash" a misnomer.
8224 */
8225
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008226struct tblentry {
8227 struct tblentry *next; /* next entry in hash chain */
8228 union param param; /* definition of builtin function */
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00008229 smallint cmdtype; /* CMDxxx */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008230 char rehash; /* if set, cd done since entry created */
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008231 char cmdname[1]; /* name of command */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008232};
8233
Denis Vlasenko01631112007-12-16 17:20:38 +00008234static struct tblentry **cmdtable;
8235#define INIT_G_cmdtable() do { \
8236 cmdtable = xzalloc(CMDTABLESIZE * sizeof(cmdtable[0])); \
8237} while (0)
8238
8239static int builtinloc = -1; /* index in path of %builtin, or -1 */
8240
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008241
8242static void
Denys Vlasenko00a1dbd2017-07-29 01:20:53 +02008243tryexec(IF_FEATURE_SH_STANDALONE(int applet_no,) const char *cmd, char **argv, char **envp)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008244{
Denis Vlasenko80d14be2007-04-10 23:03:30 +00008245#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00008246 if (applet_no >= 0) {
Denis Vlasenkob7304742008-10-20 08:15:51 +00008247 if (APPLET_IS_NOEXEC(applet_no)) {
Denys Vlasenko7df28bb2010-06-18 14:23:47 +02008248 clearenv();
Denis Vlasenkob7304742008-10-20 08:15:51 +00008249 while (*envp)
8250 putenv(*envp++);
Denys Vlasenko035486c2017-07-31 04:09:19 +02008251 popredir(/*drop:*/ 1);
Denys Vlasenko80e8e3c2017-08-07 19:24:57 +02008252 run_noexec_applet_and_exit(applet_no, cmd, argv);
Denis Vlasenkob7304742008-10-20 08:15:51 +00008253 }
Denis Vlasenko4a9ca132008-04-12 20:07:08 +00008254 /* re-exec ourselves with the new arguments */
8255 execve(bb_busybox_exec_path, argv, envp);
8256 /* If they called chroot or otherwise made the binary no longer
8257 * executable, fall through */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008258 }
8259#endif
8260
8261 repeat:
8262#ifdef SYSV
8263 do {
8264 execve(cmd, argv, envp);
8265 } while (errno == EINTR);
8266#else
8267 execve(cmd, argv, envp);
8268#endif
Ron Yorstonca82b532018-11-01 11:45:03 +01008269
Denys Vlasenko00a1dbd2017-07-29 01:20:53 +02008270 if (cmd != bb_busybox_exec_path && errno == ENOEXEC) {
Denys Vlasenkoaefe1c22011-03-07 12:02:40 +01008271 /* Run "cmd" as a shell script:
8272 * http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html
8273 * "If the execve() function fails with ENOEXEC, the shell
8274 * shall execute a command equivalent to having a shell invoked
8275 * with the command name as its first operand,
8276 * with any remaining arguments passed to the new shell"
8277 *
8278 * That is, do not use $SHELL, user's shell, or /bin/sh;
8279 * just call ourselves.
Denys Vlasenko2bef5262011-12-16 00:25:17 +01008280 *
8281 * Note that bash reads ~80 chars of the file, and if it sees
8282 * a zero byte before it sees newline, it doesn't try to
8283 * interpret it, but fails with "cannot execute binary file"
Denys Vlasenkocda6ea92011-12-16 00:44:36 +01008284 * message and exit code 126. For one, this prevents attempts
8285 * to interpret foreign ELF binaries as shell scripts.
Denys Vlasenkoaefe1c22011-03-07 12:02:40 +01008286 */
Denys Vlasenko00a1dbd2017-07-29 01:20:53 +02008287 argv[0] = (char*) cmd;
8288 cmd = bb_busybox_exec_path;
Denys Vlasenko65a8b852016-10-26 22:29:11 +02008289 /* NB: this is only possible because all callers of shellexec()
8290 * ensure that the argv[-1] slot exists!
8291 */
8292 argv--;
8293 argv[0] = (char*) "ash";
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008294 goto repeat;
8295 }
8296}
8297
8298/*
8299 * Exec a program. Never returns. If you change this routine, you may
8300 * have to change the find_command routine as well.
Denys Vlasenko65a8b852016-10-26 22:29:11 +02008301 * argv[-1] must exist and be writable! See tryexec() for why.
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008302 */
Denys Vlasenkoe139ae32017-04-12 21:02:33 +02008303static void shellexec(char *prog, char **argv, const char *path, int idx) NORETURN;
8304static void shellexec(char *prog, char **argv, const char *path, int idx)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008305{
8306 char *cmdname;
8307 int e;
8308 char **envp;
8309 int exerrno;
Denys Vlasenko83f103b2011-12-20 06:10:35 +01008310 int applet_no = -1; /* used only by FEATURE_SH_STANDALONE */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008311
Denys Vlasenkoa5060b82017-11-03 14:16:25 +01008312 envp = listvars(VEXPORT, VUNSET, /*strlist:*/ NULL, /*end:*/ NULL);
Denys Vlasenkoe139ae32017-04-12 21:02:33 +02008313 if (strchr(prog, '/') != NULL
Denis Vlasenko80d14be2007-04-10 23:03:30 +00008314#if ENABLE_FEATURE_SH_STANDALONE
Denys Vlasenkoe139ae32017-04-12 21:02:33 +02008315 || (applet_no = find_applet_by_name(prog)) >= 0
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008316#endif
8317 ) {
Denys Vlasenkoe139ae32017-04-12 21:02:33 +02008318 tryexec(IF_FEATURE_SH_STANDALONE(applet_no,) prog, argv, envp);
Denys Vlasenko83f103b2011-12-20 06:10:35 +01008319 if (applet_no >= 0) {
8320 /* We tried execing ourself, but it didn't work.
8321 * Maybe /proc/self/exe doesn't exist?
8322 * Try $PATH search.
8323 */
8324 goto try_PATH;
8325 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008326 e = errno;
8327 } else {
Denys Vlasenko83f103b2011-12-20 06:10:35 +01008328 try_PATH:
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008329 e = ENOENT;
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +01008330 while (padvance(&path, argv[0]) >= 0) {
8331 cmdname = stackblock();
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008332 if (--idx < 0 && pathopt == NULL) {
Denis Vlasenko5e34ff22009-04-21 11:09:40 +00008333 tryexec(IF_FEATURE_SH_STANDALONE(-1,) cmdname, argv, envp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008334 if (errno != ENOENT && errno != ENOTDIR)
8335 e = errno;
8336 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008337 }
8338 }
8339
8340 /* Map to POSIX errors */
8341 switch (e) {
Denys Vlasenko2596f412018-08-05 18:04:09 +02008342 default:
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008343 exerrno = 126;
8344 break;
Denys Vlasenko2596f412018-08-05 18:04:09 +02008345 case ELOOP:
8346 case ENAMETOOLONG:
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008347 case ENOENT:
Denys Vlasenko2596f412018-08-05 18:04:09 +02008348 case ENOTDIR:
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008349 exerrno = 127;
8350 break;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008351 }
8352 exitstatus = exerrno;
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02008353 TRACE(("shellexec failed for %s, errno %d, suppress_int %d\n",
Denys Vlasenkoe139ae32017-04-12 21:02:33 +02008354 prog, e, suppress_int));
Denys Vlasenkof977e002020-02-20 16:54:29 +01008355 ash_msg_and_raise(EXEND, "%s: %s", prog, errmsg(e, "not found"));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008356 /* NOTREACHED */
8357}
8358
8359static void
8360printentry(struct tblentry *cmdp)
8361{
8362 int idx;
8363 const char *path;
8364 char *name;
8365
8366 idx = cmdp->param.index;
8367 path = pathval();
8368 do {
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +01008369 padvance(&path, cmdp->cmdname);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008370 } while (--idx >= 0);
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +01008371 name = stackblock();
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008372 out1fmt("%s%s\n", name, (cmdp->rehash ? "*" : nullstr));
8373}
8374
8375/*
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +01008376 * Clear out command entries.
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008377 */
8378static void
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +01008379clearcmdentry(void)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008380{
8381 struct tblentry **tblp;
8382 struct tblentry **pp;
8383 struct tblentry *cmdp;
8384
8385 INT_OFF;
8386 for (tblp = cmdtable; tblp < &cmdtable[CMDTABLESIZE]; tblp++) {
8387 pp = tblp;
8388 while ((cmdp = *pp) != NULL) {
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +01008389 if (cmdp->cmdtype == CMDNORMAL
Denys Vlasenko22c75922020-02-17 16:20:05 +01008390 || (cmdp->cmdtype == CMDBUILTIN
8391 && !IS_BUILTIN_REGULAR(cmdp->param.cmd)
8392 && builtinloc > 0
8393 )
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008394 ) {
8395 *pp = cmdp->next;
8396 free(cmdp);
8397 } else {
8398 pp = &cmdp->next;
8399 }
8400 }
8401 }
8402 INT_ON;
8403}
8404
8405/*
8406 * Locate a command in the command hash table. If "add" is nonzero,
8407 * add the command to the table if it is not already present. The
8408 * variable "lastcmdentry" is set to point to the address of the link
8409 * pointing to the entry, so that delete_cmd_entry can delete the
8410 * entry.
8411 *
8412 * Interrupts must be off if called with add != 0.
8413 */
8414static struct tblentry **lastcmdentry;
8415
8416static struct tblentry *
8417cmdlookup(const char *name, int add)
8418{
8419 unsigned int hashval;
8420 const char *p;
8421 struct tblentry *cmdp;
8422 struct tblentry **pp;
8423
8424 p = name;
8425 hashval = (unsigned char)*p << 4;
8426 while (*p)
8427 hashval += (unsigned char)*p++;
8428 hashval &= 0x7FFF;
8429 pp = &cmdtable[hashval % CMDTABLESIZE];
8430 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
8431 if (strcmp(cmdp->cmdname, name) == 0)
8432 break;
8433 pp = &cmdp->next;
8434 }
8435 if (add && cmdp == NULL) {
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008436 cmdp = *pp = ckzalloc(sizeof(struct tblentry)
8437 + strlen(name)
8438 /* + 1 - already done because
8439 * tblentry::cmdname is char[1] */);
Denis Vlasenko597906c2008-02-20 16:38:54 +00008440 /*cmdp->next = NULL; - ckzalloc did it */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008441 cmdp->cmdtype = CMDUNKNOWN;
8442 strcpy(cmdp->cmdname, name);
8443 }
8444 lastcmdentry = pp;
8445 return cmdp;
8446}
8447
8448/*
8449 * Delete the command entry returned on the last lookup.
8450 */
8451static void
8452delete_cmd_entry(void)
8453{
8454 struct tblentry *cmdp;
8455
8456 INT_OFF;
8457 cmdp = *lastcmdentry;
8458 *lastcmdentry = cmdp->next;
8459 if (cmdp->cmdtype == CMDFUNCTION)
8460 freefunc(cmdp->param.func);
8461 free(cmdp);
8462 INT_ON;
8463}
8464
8465/*
8466 * Add a new command entry, replacing any existing command entry for
8467 * the same name - except special builtins.
8468 */
8469static void
8470addcmdentry(char *name, struct cmdentry *entry)
8471{
8472 struct tblentry *cmdp;
8473
8474 cmdp = cmdlookup(name, 1);
8475 if (cmdp->cmdtype == CMDFUNCTION) {
8476 freefunc(cmdp->param.func);
8477 }
8478 cmdp->cmdtype = entry->cmdtype;
8479 cmdp->param = entry->u;
8480 cmdp->rehash = 0;
8481}
8482
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008483static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008484hashcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008485{
8486 struct tblentry **pp;
8487 struct tblentry *cmdp;
8488 int c;
8489 struct cmdentry entry;
8490 char *name;
8491
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00008492 if (nextopt("r") != '\0') {
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +01008493 clearcmdentry();
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008494 return 0;
8495 }
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00008496
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008497 if (*argptr == NULL) {
8498 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
8499 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
8500 if (cmdp->cmdtype == CMDNORMAL)
8501 printentry(cmdp);
8502 }
8503 }
8504 return 0;
8505 }
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00008506
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008507 c = 0;
8508 while ((name = *argptr) != NULL) {
8509 cmdp = cmdlookup(name, 0);
8510 if (cmdp != NULL
8511 && (cmdp->cmdtype == CMDNORMAL
Denys Vlasenko22c75922020-02-17 16:20:05 +01008512 || (cmdp->cmdtype == CMDBUILTIN
8513 && !IS_BUILTIN_REGULAR(cmdp->param.cmd)
8514 && builtinloc > 0
8515 )
8516 )
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00008517 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008518 delete_cmd_entry();
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00008519 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008520 find_command(name, &entry, DO_ERR, pathval());
8521 if (entry.cmdtype == CMDUNKNOWN)
8522 c = 1;
8523 argptr++;
8524 }
8525 return c;
8526}
8527
8528/*
8529 * Called when a cd is done. Marks all commands so the next time they
8530 * are executed they will be rehashed.
8531 */
8532static void
8533hashcd(void)
8534{
8535 struct tblentry **pp;
8536 struct tblentry *cmdp;
8537
8538 for (pp = cmdtable; pp < &cmdtable[CMDTABLESIZE]; pp++) {
8539 for (cmdp = *pp; cmdp; cmdp = cmdp->next) {
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00008540 if (cmdp->cmdtype == CMDNORMAL
8541 || (cmdp->cmdtype == CMDBUILTIN
Denys Vlasenkoe4dcba12010-10-28 18:57:19 +02008542 && !IS_BUILTIN_REGULAR(cmdp->param.cmd)
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00008543 && builtinloc > 0)
8544 ) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008545 cmdp->rehash = 1;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00008546 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008547 }
8548 }
8549}
8550
8551/*
8552 * Fix command hash table when PATH changed.
8553 * Called before PATH is changed. The argument is the new value of PATH;
8554 * pathval() still returns the old value at this point.
8555 * Called with interrupts off.
8556 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008557static void FAST_FUNC
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +01008558changepath(const char *newval)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008559{
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +01008560 const char *new;
Denis Vlasenko5c3d2b32008-02-03 22:01:08 +00008561 int idx;
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +01008562 int bltin;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008563
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +01008564 new = newval;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008565 idx = 0;
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +01008566 bltin = -1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008567 for (;;) {
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +01008568 if (*new == '%' && prefix(new + 1, "builtin")) {
8569 bltin = idx;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008570 break;
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +01008571 }
8572 new = strchr(new, ':');
8573 if (!new)
8574 break;
8575 idx++;
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +02008576 new++;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008577 }
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +01008578 builtinloc = bltin;
8579 clearcmdentry();
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008580}
Ron Yorston95ebcf72015-11-03 09:42:23 +00008581enum {
8582 TEOF,
8583 TNL,
8584 TREDIR,
8585 TWORD,
8586 TSEMI,
8587 TBACKGND,
8588 TAND,
8589 TOR,
8590 TPIPE,
8591 TLP,
8592 TRP,
8593 TENDCASE,
8594 TENDBQUOTE,
8595 TNOT,
8596 TCASE,
8597 TDO,
8598 TDONE,
8599 TELIF,
8600 TELSE,
8601 TESAC,
8602 TFI,
8603 TFOR,
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01008604#if BASH_FUNCTION
Ron Yorston95ebcf72015-11-03 09:42:23 +00008605 TFUNCTION,
8606#endif
8607 TIF,
8608 TIN,
8609 TTHEN,
8610 TUNTIL,
8611 TWHILE,
8612 TBEGIN,
8613 TEND
8614};
Denis Vlasenkob07a4962008-06-22 13:16:23 +00008615typedef smallint token_id_t;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008616
Denys Vlasenko888527c2016-10-02 16:54:17 +02008617/* Nth bit indicates if token marks the end of a list */
8618enum {
8619 tokendlist = 0
8620 /* 0 */ | (1u << TEOF)
8621 /* 1 */ | (0u << TNL)
8622 /* 2 */ | (0u << TREDIR)
8623 /* 3 */ | (0u << TWORD)
8624 /* 4 */ | (0u << TSEMI)
8625 /* 5 */ | (0u << TBACKGND)
8626 /* 6 */ | (0u << TAND)
8627 /* 7 */ | (0u << TOR)
8628 /* 8 */ | (0u << TPIPE)
8629 /* 9 */ | (0u << TLP)
8630 /* 10 */ | (1u << TRP)
8631 /* 11 */ | (1u << TENDCASE)
8632 /* 12 */ | (1u << TENDBQUOTE)
8633 /* 13 */ | (0u << TNOT)
8634 /* 14 */ | (0u << TCASE)
8635 /* 15 */ | (1u << TDO)
8636 /* 16 */ | (1u << TDONE)
8637 /* 17 */ | (1u << TELIF)
8638 /* 18 */ | (1u << TELSE)
8639 /* 19 */ | (1u << TESAC)
8640 /* 20 */ | (1u << TFI)
8641 /* 21 */ | (0u << TFOR)
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01008642#if BASH_FUNCTION
Denys Vlasenko888527c2016-10-02 16:54:17 +02008643 /* 22 */ | (0u << TFUNCTION)
Denys Vlasenko80729a42016-10-02 22:33:15 +02008644#endif
Denys Vlasenko888527c2016-10-02 16:54:17 +02008645 /* 23 */ | (0u << TIF)
8646 /* 24 */ | (0u << TIN)
8647 /* 25 */ | (1u << TTHEN)
8648 /* 26 */ | (0u << TUNTIL)
8649 /* 27 */ | (0u << TWHILE)
8650 /* 28 */ | (0u << TBEGIN)
8651 /* 29 */ | (1u << TEND)
8652 , /* thus far 29 bits used */
8653};
8654
Denys Vlasenko965b7952020-11-30 13:03:03 +01008655static const char *const tokname_array[] ALIGN_PTR = {
Denys Vlasenko888527c2016-10-02 16:54:17 +02008656 "end of file",
8657 "newline",
8658 "redirection",
8659 "word",
8660 ";",
8661 "&",
8662 "&&",
8663 "||",
8664 "|",
8665 "(",
8666 ")",
8667 ";;",
8668 "`",
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008669#define KWDOFFSET 13
8670 /* the following are keywords */
Denys Vlasenko888527c2016-10-02 16:54:17 +02008671 "!",
8672 "case",
8673 "do",
8674 "done",
8675 "elif",
8676 "else",
8677 "esac",
8678 "fi",
8679 "for",
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01008680#if BASH_FUNCTION
Denys Vlasenko888527c2016-10-02 16:54:17 +02008681 "function",
Ron Yorston95ebcf72015-11-03 09:42:23 +00008682#endif
Denys Vlasenko888527c2016-10-02 16:54:17 +02008683 "if",
8684 "in",
8685 "then",
8686 "until",
8687 "while",
8688 "{",
8689 "}",
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008690};
8691
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008692/* Wrapper around strcmp for qsort/bsearch/... */
8693static int
8694pstrcmp(const void *a, const void *b)
8695{
Denys Vlasenko888527c2016-10-02 16:54:17 +02008696 return strcmp((char*)a, *(char**)b);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008697}
8698
8699static const char *const *
8700findkwd(const char *s)
8701{
8702 return bsearch(s, tokname_array + KWDOFFSET,
Denis Vlasenko80b8b392007-06-25 10:55:35 +00008703 ARRAY_SIZE(tokname_array) - KWDOFFSET,
8704 sizeof(tokname_array[0]), pstrcmp);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008705}
8706
8707/*
8708 * Locate and print what a word is...
8709 */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008710static int
Ron Yorston3f221112015-08-03 13:47:33 +01008711describe_command(char *command, const char *path, int describe_command_verbose)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008712{
8713 struct cmdentry entry;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008714#if ENABLE_ASH_ALIAS
8715 const struct alias *ap;
8716#endif
Ron Yorston3f221112015-08-03 13:47:33 +01008717
8718 path = path ? path : pathval();
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008719
8720 if (describe_command_verbose) {
8721 out1str(command);
8722 }
8723
8724 /* First look at the keywords */
8725 if (findkwd(command)) {
8726 out1str(describe_command_verbose ? " is a shell keyword" : command);
8727 goto out;
8728 }
8729
8730#if ENABLE_ASH_ALIAS
8731 /* Then look at the aliases */
8732 ap = lookupalias(command, 0);
8733 if (ap != NULL) {
Denis Vlasenko46846e22007-05-20 13:08:31 +00008734 if (!describe_command_verbose) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008735 out1str("alias ");
8736 printalias(ap);
8737 return 0;
8738 }
Denis Vlasenko46846e22007-05-20 13:08:31 +00008739 out1fmt(" is an alias for %s", ap->val);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008740 goto out;
8741 }
8742#endif
Youfu Zhang6683d1c2017-05-26 15:31:29 +08008743 /* Brute force */
8744 find_command(command, &entry, DO_ABS, path);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008745
8746 switch (entry.cmdtype) {
8747 case CMDNORMAL: {
8748 int j = entry.u.index;
8749 char *p;
Denis Vlasenko7465dbc2008-04-13 02:25:53 +00008750 if (j < 0) {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008751 p = command;
8752 } else {
8753 do {
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +01008754 padvance(&path, command);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008755 } while (--j >= 0);
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +01008756 p = stackblock();
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008757 }
8758 if (describe_command_verbose) {
Youfu Zhang6683d1c2017-05-26 15:31:29 +08008759 out1fmt(" is %s", p);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008760 } else {
8761 out1str(p);
8762 }
8763 break;
8764 }
8765
8766 case CMDFUNCTION:
8767 if (describe_command_verbose) {
Denys Vlasenko63c42af2018-07-24 17:08:04 +02008768 /*out1str(" is a shell function");*/
8769 out1str(" is a function"); /* bash says this */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008770 } else {
8771 out1str(command);
8772 }
8773 break;
8774
8775 case CMDBUILTIN:
8776 if (describe_command_verbose) {
8777 out1fmt(" is a %sshell builtin",
8778 IS_BUILTIN_SPECIAL(entry.u.cmd) ?
8779 "special " : nullstr
8780 );
8781 } else {
8782 out1str(command);
8783 }
8784 break;
8785
8786 default:
8787 if (describe_command_verbose) {
8788 out1str(": not found\n");
8789 }
8790 return 127;
8791 }
8792 out:
Denys Vlasenko285ad152009-12-04 23:02:27 +01008793 out1str("\n");
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008794 return 0;
8795}
8796
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008797static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008798typecmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008799{
Denis Vlasenko46846e22007-05-20 13:08:31 +00008800 int i = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008801 int err = 0;
Denis Vlasenko46846e22007-05-20 13:08:31 +00008802 int verbose = 1;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008803
Denis Vlasenko46846e22007-05-20 13:08:31 +00008804 /* type -p ... ? (we don't bother checking for 'p') */
Denis Vlasenko1fc62382007-06-25 22:55:34 +00008805 if (argv[1] && argv[1][0] == '-') {
Denis Vlasenko46846e22007-05-20 13:08:31 +00008806 i++;
8807 verbose = 0;
8808 }
Denis Vlasenko68404f12008-03-17 09:00:54 +00008809 while (argv[i]) {
Ron Yorston3f221112015-08-03 13:47:33 +01008810 err |= describe_command(argv[i++], NULL, verbose);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008811 }
8812 return err;
8813}
8814
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +01008815static struct strlist *
8816fill_arglist(struct arglist *arglist, union node **argpp)
Denys Vlasenkocac4d002016-10-01 03:02:25 +02008817{
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +01008818 struct strlist **lastp = arglist->lastp;
8819 union node *argp;
8820
8821 while ((argp = *argpp) != NULL) {
8822 expandarg(argp, arglist, EXP_FULL | EXP_TILDE);
8823 *argpp = argp->narg.next;
8824 if (*lastp)
8825 break;
8826 }
8827
8828 return *lastp;
8829}
8830
Ron Yorstonda7a6db2020-02-27 09:50:18 +00008831#if ENABLE_ASH_CMDCMD
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +01008832/* Is it "command [-p] PROG ARGS" bltin, no other opts? Return ptr to "PROG" if yes */
8833static int
8834parse_command_args(struct arglist *arglist, union node **argpp, const char **path)
8835{
8836 struct strlist *sp = arglist->list;
Denys Vlasenkocac4d002016-10-01 03:02:25 +02008837 char *cp, c;
8838
8839 for (;;) {
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +01008840 sp = sp->next ? sp->next : fill_arglist(arglist, argpp);
8841 if (!sp)
8842 return 0;
8843 cp = sp->text;
Denys Vlasenkocac4d002016-10-01 03:02:25 +02008844 if (*cp++ != '-')
8845 break;
8846 c = *cp++;
8847 if (!c)
8848 break;
8849 if (c == '-' && !*cp) {
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +01008850 if (!sp->next && !fill_arglist(arglist, argpp))
8851 return 0;
8852 sp = sp->next;
Denys Vlasenkocac4d002016-10-01 03:02:25 +02008853 break;
8854 }
8855 do {
8856 switch (c) {
8857 case 'p':
8858 *path = bb_default_path;
8859 break;
8860 default:
8861 /* run 'typecmd' for other options */
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +01008862 return 0;
Denys Vlasenkocac4d002016-10-01 03:02:25 +02008863 }
8864 c = *cp++;
8865 } while (c);
8866 }
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +01008867
8868 arglist->list = sp;
8869 return DO_NOFUNC;
Denys Vlasenkocac4d002016-10-01 03:02:25 +02008870}
8871
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +02008872static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +00008873commandcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008874{
Denys Vlasenkocac4d002016-10-01 03:02:25 +02008875 char *cmd;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008876 int c;
8877 enum {
8878 VERIFY_BRIEF = 1,
8879 VERIFY_VERBOSE = 2,
8880 } verify = 0;
Ron Yorston3f221112015-08-03 13:47:33 +01008881 const char *path = NULL;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008882
Denys Vlasenkocac4d002016-10-01 03:02:25 +02008883 /* "command [-p] PROG ARGS" (that is, without -V or -v)
8884 * never reaches this function.
8885 */
8886
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008887 while ((c = nextopt("pvV")) != '\0')
8888 if (c == 'V')
8889 verify |= VERIFY_VERBOSE;
8890 else if (c == 'v')
Denys Vlasenkocac4d002016-10-01 03:02:25 +02008891 /*verify |= VERIFY_BRIEF*/;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008892#if DEBUG
8893 else if (c != 'p')
8894 abort();
8895#endif
Ron Yorston3f221112015-08-03 13:47:33 +01008896 else
8897 path = bb_default_path;
Denys Vlasenkocac4d002016-10-01 03:02:25 +02008898
Denis Vlasenkoe7067e32008-07-11 23:09:34 +00008899 /* Mimic bash: just "command -v" doesn't complain, it's a nop */
Denys Vlasenkocac4d002016-10-01 03:02:25 +02008900 cmd = *argptr;
8901 if (/*verify && */ cmd)
8902 return describe_command(cmd, path, verify /* - VERIFY_BRIEF*/);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008903
8904 return 0;
8905}
8906#endif
8907
8908
Denys Vlasenko4c438b52016-10-07 04:05:15 +02008909/*static int funcblocksize; // size of structures in function */
8910/*static int funcstringsize; // size of strings in node */
Denis Vlasenko340299a2008-11-21 10:36:36 +00008911static void *funcblock; /* block to allocate function from */
Denys Vlasenko4c438b52016-10-07 04:05:15 +02008912static char *funcstring_end; /* end of block to allocate strings from */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008913
Denys Vlasenko3e134eb2016-04-22 18:09:21 +02008914static const uint8_t nodesize[N_NUMBER] ALIGN1 = {
Denis Vlasenko340299a2008-11-21 10:36:36 +00008915 [NCMD ] = SHELL_ALIGN(sizeof(struct ncmd)),
8916 [NPIPE ] = SHELL_ALIGN(sizeof(struct npipe)),
8917 [NREDIR ] = SHELL_ALIGN(sizeof(struct nredir)),
8918 [NBACKGND ] = SHELL_ALIGN(sizeof(struct nredir)),
8919 [NSUBSHELL] = SHELL_ALIGN(sizeof(struct nredir)),
8920 [NAND ] = SHELL_ALIGN(sizeof(struct nbinary)),
8921 [NOR ] = SHELL_ALIGN(sizeof(struct nbinary)),
8922 [NSEMI ] = SHELL_ALIGN(sizeof(struct nbinary)),
8923 [NIF ] = SHELL_ALIGN(sizeof(struct nif)),
8924 [NWHILE ] = SHELL_ALIGN(sizeof(struct nbinary)),
8925 [NUNTIL ] = SHELL_ALIGN(sizeof(struct nbinary)),
8926 [NFOR ] = SHELL_ALIGN(sizeof(struct nfor)),
8927 [NCASE ] = SHELL_ALIGN(sizeof(struct ncase)),
8928 [NCLIST ] = SHELL_ALIGN(sizeof(struct nclist)),
8929 [NDEFUN ] = SHELL_ALIGN(sizeof(struct narg)),
8930 [NARG ] = SHELL_ALIGN(sizeof(struct narg)),
8931 [NTO ] = SHELL_ALIGN(sizeof(struct nfile)),
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01008932#if BASH_REDIR_OUTPUT
Denis Vlasenko340299a2008-11-21 10:36:36 +00008933 [NTO2 ] = SHELL_ALIGN(sizeof(struct nfile)),
Denis Vlasenkocc5feab2008-11-22 01:32:40 +00008934#endif
Denis Vlasenko340299a2008-11-21 10:36:36 +00008935 [NCLOBBER ] = SHELL_ALIGN(sizeof(struct nfile)),
8936 [NFROM ] = SHELL_ALIGN(sizeof(struct nfile)),
8937 [NFROMTO ] = SHELL_ALIGN(sizeof(struct nfile)),
8938 [NAPPEND ] = SHELL_ALIGN(sizeof(struct nfile)),
8939 [NTOFD ] = SHELL_ALIGN(sizeof(struct ndup)),
8940 [NFROMFD ] = SHELL_ALIGN(sizeof(struct ndup)),
8941 [NHERE ] = SHELL_ALIGN(sizeof(struct nhere)),
8942 [NXHERE ] = SHELL_ALIGN(sizeof(struct nhere)),
8943 [NNOT ] = SHELL_ALIGN(sizeof(struct nnot)),
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008944};
8945
Denys Vlasenko4c438b52016-10-07 04:05:15 +02008946static int calcsize(int funcblocksize, union node *n);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008947
Denys Vlasenko4c438b52016-10-07 04:05:15 +02008948static int
8949sizenodelist(int funcblocksize, struct nodelist *lp)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008950{
8951 while (lp) {
8952 funcblocksize += SHELL_ALIGN(sizeof(struct nodelist));
Denys Vlasenko4c438b52016-10-07 04:05:15 +02008953 funcblocksize = calcsize(funcblocksize, lp->n);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008954 lp = lp->next;
8955 }
Denys Vlasenko4c438b52016-10-07 04:05:15 +02008956 return funcblocksize;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008957}
8958
Denys Vlasenko4c438b52016-10-07 04:05:15 +02008959static int
8960calcsize(int funcblocksize, union node *n)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008961{
8962 if (n == NULL)
Denys Vlasenko4c438b52016-10-07 04:05:15 +02008963 return funcblocksize;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008964 funcblocksize += nodesize[n->type];
8965 switch (n->type) {
8966 case NCMD:
Denys Vlasenko4c438b52016-10-07 04:05:15 +02008967 funcblocksize = calcsize(funcblocksize, n->ncmd.redirect);
8968 funcblocksize = calcsize(funcblocksize, n->ncmd.args);
8969 funcblocksize = calcsize(funcblocksize, n->ncmd.assign);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008970 break;
8971 case NPIPE:
Denys Vlasenko4c438b52016-10-07 04:05:15 +02008972 funcblocksize = sizenodelist(funcblocksize, n->npipe.cmdlist);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008973 break;
8974 case NREDIR:
8975 case NBACKGND:
8976 case NSUBSHELL:
Denys Vlasenko4c438b52016-10-07 04:05:15 +02008977 funcblocksize = calcsize(funcblocksize, n->nredir.redirect);
8978 funcblocksize = calcsize(funcblocksize, n->nredir.n);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008979 break;
8980 case NAND:
8981 case NOR:
8982 case NSEMI:
8983 case NWHILE:
8984 case NUNTIL:
Denys Vlasenko4c438b52016-10-07 04:05:15 +02008985 funcblocksize = calcsize(funcblocksize, n->nbinary.ch2);
8986 funcblocksize = calcsize(funcblocksize, n->nbinary.ch1);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008987 break;
8988 case NIF:
Denys Vlasenko4c438b52016-10-07 04:05:15 +02008989 funcblocksize = calcsize(funcblocksize, n->nif.elsepart);
8990 funcblocksize = calcsize(funcblocksize, n->nif.ifpart);
8991 funcblocksize = calcsize(funcblocksize, n->nif.test);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008992 break;
8993 case NFOR:
Denys Vlasenko561639a2016-10-07 04:28:33 +02008994 funcblocksize += SHELL_ALIGN(strlen(n->nfor.var) + 1); /* was funcstringsize += ... */
Denys Vlasenko4c438b52016-10-07 04:05:15 +02008995 funcblocksize = calcsize(funcblocksize, n->nfor.body);
8996 funcblocksize = calcsize(funcblocksize, n->nfor.args);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00008997 break;
8998 case NCASE:
Denys Vlasenko4c438b52016-10-07 04:05:15 +02008999 funcblocksize = calcsize(funcblocksize, n->ncase.cases);
9000 funcblocksize = calcsize(funcblocksize, n->ncase.expr);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009001 break;
9002 case NCLIST:
Denys Vlasenko4c438b52016-10-07 04:05:15 +02009003 funcblocksize = calcsize(funcblocksize, n->nclist.body);
9004 funcblocksize = calcsize(funcblocksize, n->nclist.pattern);
9005 funcblocksize = calcsize(funcblocksize, n->nclist.next);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009006 break;
9007 case NDEFUN:
Denys Vlasenko675d24a2018-01-27 22:02:05 +01009008 funcblocksize = calcsize(funcblocksize, n->ndefun.body);
9009 funcblocksize += SHELL_ALIGN(strlen(n->ndefun.text) + 1);
9010 break;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009011 case NARG:
Denys Vlasenko4c438b52016-10-07 04:05:15 +02009012 funcblocksize = sizenodelist(funcblocksize, n->narg.backquote);
Denys Vlasenko561639a2016-10-07 04:28:33 +02009013 funcblocksize += SHELL_ALIGN(strlen(n->narg.text) + 1); /* was funcstringsize += ... */
Denys Vlasenko4c438b52016-10-07 04:05:15 +02009014 funcblocksize = calcsize(funcblocksize, n->narg.next);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009015 break;
9016 case NTO:
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01009017#if BASH_REDIR_OUTPUT
Denis Vlasenko559691a2008-10-05 18:39:31 +00009018 case NTO2:
9019#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009020 case NCLOBBER:
9021 case NFROM:
9022 case NFROMTO:
9023 case NAPPEND:
Denys Vlasenko4c438b52016-10-07 04:05:15 +02009024 funcblocksize = calcsize(funcblocksize, n->nfile.fname);
9025 funcblocksize = calcsize(funcblocksize, n->nfile.next);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009026 break;
9027 case NTOFD:
9028 case NFROMFD:
Denys Vlasenko4c438b52016-10-07 04:05:15 +02009029 funcblocksize = calcsize(funcblocksize, n->ndup.vname);
9030 funcblocksize = calcsize(funcblocksize, n->ndup.next);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009031 break;
9032 case NHERE:
9033 case NXHERE:
Denys Vlasenko4c438b52016-10-07 04:05:15 +02009034 funcblocksize = calcsize(funcblocksize, n->nhere.doc);
9035 funcblocksize = calcsize(funcblocksize, n->nhere.next);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009036 break;
9037 case NNOT:
Denys Vlasenko4c438b52016-10-07 04:05:15 +02009038 funcblocksize = calcsize(funcblocksize, n->nnot.com);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009039 break;
9040 };
Denys Vlasenko4c438b52016-10-07 04:05:15 +02009041 return funcblocksize;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009042}
9043
9044static char *
9045nodeckstrdup(char *s)
9046{
Denys Vlasenko561639a2016-10-07 04:28:33 +02009047 funcstring_end -= SHELL_ALIGN(strlen(s) + 1);
Denys Vlasenko4c438b52016-10-07 04:05:15 +02009048 return strcpy(funcstring_end, s);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009049}
9050
9051static union node *copynode(union node *);
9052
9053static struct nodelist *
9054copynodelist(struct nodelist *lp)
9055{
9056 struct nodelist *start;
9057 struct nodelist **lpp;
9058
9059 lpp = &start;
9060 while (lp) {
9061 *lpp = funcblock;
9062 funcblock = (char *) funcblock + SHELL_ALIGN(sizeof(struct nodelist));
9063 (*lpp)->n = copynode(lp->n);
9064 lp = lp->next;
9065 lpp = &(*lpp)->next;
9066 }
9067 *lpp = NULL;
9068 return start;
9069}
9070
9071static union node *
9072copynode(union node *n)
9073{
9074 union node *new;
9075
9076 if (n == NULL)
9077 return NULL;
9078 new = funcblock;
9079 funcblock = (char *) funcblock + nodesize[n->type];
9080
9081 switch (n->type) {
9082 case NCMD:
9083 new->ncmd.redirect = copynode(n->ncmd.redirect);
9084 new->ncmd.args = copynode(n->ncmd.args);
9085 new->ncmd.assign = copynode(n->ncmd.assign);
Denys Vlasenko675d24a2018-01-27 22:02:05 +01009086 new->ncmd.linno = n->ncmd.linno;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009087 break;
9088 case NPIPE:
9089 new->npipe.cmdlist = copynodelist(n->npipe.cmdlist);
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00009090 new->npipe.pipe_backgnd = n->npipe.pipe_backgnd;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009091 break;
9092 case NREDIR:
9093 case NBACKGND:
9094 case NSUBSHELL:
9095 new->nredir.redirect = copynode(n->nredir.redirect);
9096 new->nredir.n = copynode(n->nredir.n);
Denys Vlasenko675d24a2018-01-27 22:02:05 +01009097 new->nredir.linno = n->nredir.linno;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009098 break;
9099 case NAND:
9100 case NOR:
9101 case NSEMI:
9102 case NWHILE:
9103 case NUNTIL:
9104 new->nbinary.ch2 = copynode(n->nbinary.ch2);
9105 new->nbinary.ch1 = copynode(n->nbinary.ch1);
9106 break;
9107 case NIF:
9108 new->nif.elsepart = copynode(n->nif.elsepart);
9109 new->nif.ifpart = copynode(n->nif.ifpart);
9110 new->nif.test = copynode(n->nif.test);
9111 break;
9112 case NFOR:
9113 new->nfor.var = nodeckstrdup(n->nfor.var);
9114 new->nfor.body = copynode(n->nfor.body);
9115 new->nfor.args = copynode(n->nfor.args);
Denys Vlasenko675d24a2018-01-27 22:02:05 +01009116 new->nfor.linno = n->nfor.linno;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009117 break;
9118 case NCASE:
9119 new->ncase.cases = copynode(n->ncase.cases);
9120 new->ncase.expr = copynode(n->ncase.expr);
Denys Vlasenko675d24a2018-01-27 22:02:05 +01009121 new->ncase.linno = n->ncase.linno;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009122 break;
9123 case NCLIST:
9124 new->nclist.body = copynode(n->nclist.body);
9125 new->nclist.pattern = copynode(n->nclist.pattern);
9126 new->nclist.next = copynode(n->nclist.next);
9127 break;
9128 case NDEFUN:
Denys Vlasenko675d24a2018-01-27 22:02:05 +01009129 new->ndefun.body = copynode(n->ndefun.body);
9130 new->ndefun.text = nodeckstrdup(n->ndefun.text);
9131 new->ndefun.linno = n->ndefun.linno;
9132 break;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009133 case NARG:
9134 new->narg.backquote = copynodelist(n->narg.backquote);
9135 new->narg.text = nodeckstrdup(n->narg.text);
9136 new->narg.next = copynode(n->narg.next);
9137 break;
9138 case NTO:
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01009139#if BASH_REDIR_OUTPUT
Denis Vlasenko559691a2008-10-05 18:39:31 +00009140 case NTO2:
9141#endif
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009142 case NCLOBBER:
9143 case NFROM:
9144 case NFROMTO:
9145 case NAPPEND:
9146 new->nfile.fname = copynode(n->nfile.fname);
9147 new->nfile.fd = n->nfile.fd;
9148 new->nfile.next = copynode(n->nfile.next);
9149 break;
9150 case NTOFD:
9151 case NFROMFD:
9152 new->ndup.vname = copynode(n->ndup.vname);
9153 new->ndup.dupfd = n->ndup.dupfd;
9154 new->ndup.fd = n->ndup.fd;
9155 new->ndup.next = copynode(n->ndup.next);
9156 break;
9157 case NHERE:
9158 case NXHERE:
9159 new->nhere.doc = copynode(n->nhere.doc);
9160 new->nhere.fd = n->nhere.fd;
9161 new->nhere.next = copynode(n->nhere.next);
9162 break;
9163 case NNOT:
9164 new->nnot.com = copynode(n->nnot.com);
9165 break;
9166 };
9167 new->type = n->type;
9168 return new;
9169}
9170
9171/*
9172 * Make a copy of a parse tree.
9173 */
9174static struct funcnode *
9175copyfunc(union node *n)
9176{
9177 struct funcnode *f;
9178 size_t blocksize;
9179
Denys Vlasenko4c438b52016-10-07 04:05:15 +02009180 /*funcstringsize = 0;*/
9181 blocksize = offsetof(struct funcnode, n) + calcsize(0, n);
9182 f = ckzalloc(blocksize /* + funcstringsize */);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009183 funcblock = (char *) f + offsetof(struct funcnode, n);
Denys Vlasenko4c438b52016-10-07 04:05:15 +02009184 funcstring_end = (char *) f + blocksize;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009185 copynode(n);
Denys Vlasenko4c438b52016-10-07 04:05:15 +02009186 /* f->count = 0; - ckzalloc did it */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009187 return f;
9188}
9189
9190/*
9191 * Define a shell function.
9192 */
9193static void
Denys Vlasenko7aec8682016-10-25 20:26:02 +02009194defun(union node *func)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009195{
9196 struct cmdentry entry;
9197
9198 INT_OFF;
9199 entry.cmdtype = CMDFUNCTION;
9200 entry.u.func = copyfunc(func);
Denys Vlasenko675d24a2018-01-27 22:02:05 +01009201 addcmdentry(func->ndefun.text, &entry);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009202 INT_ON;
9203}
9204
Denis Vlasenko4b875702009-03-19 13:30:04 +00009205/* Reasons for skipping commands (see comment on breakcmd routine) */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009206#define SKIPBREAK (1 << 0)
9207#define SKIPCONT (1 << 1)
9208#define SKIPFUNC (1 << 2)
Denys Vlasenkocd24a502020-02-20 16:47:01 +01009209#define SKIPFUNCDEF (1 << 3)
Denis Vlasenko4b875702009-03-19 13:30:04 +00009210static smallint evalskip; /* set to SKIPxxx if we are skipping commands */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009211static int skipcount; /* number of levels to skip */
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +00009212static int loopnest; /* current loop nesting level */
Denys Vlasenko675d24a2018-01-27 22:02:05 +01009213static int funcline; /* starting line number of current function, or 0 if not in a function */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009214
Denis Vlasenko4b875702009-03-19 13:30:04 +00009215/* Forward decl way out to parsing code - dotrap needs it */
Denys Vlasenko7b3fa1e2016-10-01 15:10:16 +02009216static int evalstring(char *s, int flags);
Denis Vlasenkofc06f292007-02-23 21:09:35 +00009217
Denis Vlasenko4b875702009-03-19 13:30:04 +00009218/* Called to execute a trap.
9219 * Single callsite - at the end of evaltree().
Denys Vlasenkob563f622010-09-25 17:15:13 +02009220 * If we return non-zero, evaltree raises EXEXIT exception.
Denis Vlasenko4b875702009-03-19 13:30:04 +00009221 *
9222 * Perhaps we should avoid entering new trap handlers
9223 * while we are executing a trap handler. [is it a TODO?]
Denis Vlasenkofc06f292007-02-23 21:09:35 +00009224 */
Denys Vlasenkob98b4c12016-10-01 23:25:12 +02009225static void
Denis Vlasenkofc06f292007-02-23 21:09:35 +00009226dotrap(void)
9227{
Denis Vlasenko4b875702009-03-19 13:30:04 +00009228 uint8_t *g;
9229 int sig;
Denys Vlasenko4ccddc82020-02-14 17:27:18 +01009230 int status, last_status;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00009231
Denys Vlasenkob98b4c12016-10-01 23:25:12 +02009232 if (!pending_sig)
9233 return;
9234
Denys Vlasenko4ccddc82020-02-14 17:27:18 +01009235 status = savestatus;
9236 last_status = status;
9237 if (status < 0) {
9238 status = exitstatus;
9239 savestatus = status;
9240 }
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +02009241 pending_sig = 0;
Denys Vlasenkode892052016-10-02 01:49:13 +02009242 barrier();
Denis Vlasenkofc06f292007-02-23 21:09:35 +00009243
Denis Vlasenko653d8e72009-03-19 21:59:35 +00009244 TRACE(("dotrap entered\n"));
Denis Vlasenko4b875702009-03-19 13:30:04 +00009245 for (sig = 1, g = gotsig; sig < NSIG; sig++, g++) {
Denys Vlasenkob98b4c12016-10-01 23:25:12 +02009246 char *p;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00009247
Denys Vlasenkob98b4c12016-10-01 23:25:12 +02009248 if (!*g)
Denis Vlasenkofc06f292007-02-23 21:09:35 +00009249 continue;
Denys Vlasenkob98b4c12016-10-01 23:25:12 +02009250
9251 if (evalskip) {
9252 pending_sig = sig;
9253 break;
9254 }
9255
9256 p = trap[sig];
Denis Vlasenko4b875702009-03-19 13:30:04 +00009257 /* non-trapped SIGINT is handled separately by raise_interrupt,
9258 * don't upset it by resetting gotsig[SIGINT-1] */
Denys Vlasenkob98b4c12016-10-01 23:25:12 +02009259 if (sig == SIGINT && !p)
Denis Vlasenko4b875702009-03-19 13:30:04 +00009260 continue;
Denis Vlasenko653d8e72009-03-19 21:59:35 +00009261
Denys Vlasenkob98b4c12016-10-01 23:25:12 +02009262 TRACE(("sig %d is active, will run handler '%s'\n", sig, p));
Denis Vlasenko4b875702009-03-19 13:30:04 +00009263 *g = 0;
Denys Vlasenkob98b4c12016-10-01 23:25:12 +02009264 if (!p)
Denis Vlasenko4b875702009-03-19 13:30:04 +00009265 continue;
Roberto A. Fogliettae0bf3df2021-09-07 01:19:31 +02009266 trap_depth++;
Denys Vlasenkob98b4c12016-10-01 23:25:12 +02009267 evalstring(p, 0);
Roberto A. Fogliettae0bf3df2021-09-07 01:19:31 +02009268 trap_depth--;
Denys Vlasenkocd24a502020-02-20 16:47:01 +01009269 if (evalskip != SKIPFUNC)
9270 exitstatus = status;
Denis Vlasenkofc06f292007-02-23 21:09:35 +00009271 }
Denys Vlasenko4ccddc82020-02-14 17:27:18 +01009272
9273 savestatus = last_status;
Denys Vlasenkob98b4c12016-10-01 23:25:12 +02009274 TRACE(("dotrap returns\n"));
Denis Vlasenkofc06f292007-02-23 21:09:35 +00009275}
9276
Denis Vlasenkobc54cff2007-02-23 01:05:52 +00009277/* forward declarations - evaluation is fairly recursive business... */
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009278static int evalloop(union node *, int);
9279static int evalfor(union node *, int);
9280static int evalcase(union node *, int);
9281static int evalsubshell(union node *, int);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009282static void expredir(union node *);
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009283static int evalpipe(union node *, int);
9284static int evalcommand(union node *, int);
Denys Vlasenko7b3fa1e2016-10-01 15:10:16 +02009285static int evalbltin(const struct builtincmd *, int, char **, int);
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009286static void prehash(union node *);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +00009287
Eric Andersen62483552001-07-10 06:09:16 +00009288/*
Eric Andersenc470f442003-07-28 09:56:35 +00009289 * Evaluate a parse tree. The value is left in the global variable
9290 * exitstatus.
Eric Andersen62483552001-07-10 06:09:16 +00009291 */
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009292static int
Eric Andersenc470f442003-07-28 09:56:35 +00009293evaltree(union node *n, int flags)
Eric Andersen62483552001-07-10 06:09:16 +00009294{
Eric Andersenc470f442003-07-28 09:56:35 +00009295 int checkexit = 0;
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009296 int (*evalfn)(union node *, int);
Ron Yorstonf55161a2019-02-25 08:29:38 +00009297 struct stackmark smark;
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009298 int status = 0;
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00009299
Ron Yorstonf55161a2019-02-25 08:29:38 +00009300 setstackmark(&smark);
9301
Denys Vlasenko41beb532021-09-07 01:52:21 +02009302 if (nflag)
9303 goto out;
9304
Eric Andersenc470f442003-07-28 09:56:35 +00009305 if (n == NULL) {
9306 TRACE(("evaltree(NULL) called\n"));
Denys Vlasenkoc0663c72016-10-27 21:09:01 +02009307 goto out;
Eric Andersen62483552001-07-10 06:09:16 +00009308 }
Denis Vlasenko653d8e72009-03-19 21:59:35 +00009309 TRACE(("evaltree(%p: %d, %d) called\n", n, n->type, flags));
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00009310
Denys Vlasenkob98b4c12016-10-01 23:25:12 +02009311 dotrap();
9312
Eric Andersenc470f442003-07-28 09:56:35 +00009313 switch (n->type) {
9314 default:
Denis Vlasenkoa7189f02006-11-17 20:29:00 +00009315#if DEBUG
Eric Andersenc470f442003-07-28 09:56:35 +00009316 out1fmt("Node type = %d\n", n->type);
Denys Vlasenko8131eea2009-11-02 14:19:51 +01009317 fflush_all();
Eric Andersenc470f442003-07-28 09:56:35 +00009318 break;
9319#endif
9320 case NNOT:
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009321 status = !evaltree(n->nnot.com, EV_TESTED);
Eric Andersenc470f442003-07-28 09:56:35 +00009322 goto setstatus;
9323 case NREDIR:
Denys Vlasenko675d24a2018-01-27 22:02:05 +01009324 errlinno = lineno = n->nredir.linno;
9325 if (funcline)
9326 lineno -= funcline - 1;
Eric Andersenc470f442003-07-28 09:56:35 +00009327 expredir(n->nredir.redirect);
Denys Vlasenko1c79aeb2017-07-29 22:51:52 +02009328 pushredir(n->nredir.redirect);
Eric Andersenc470f442003-07-28 09:56:35 +00009329 status = redirectsafe(n->nredir.redirect, REDIR_PUSH);
9330 if (!status) {
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009331 status = evaltree(n->nredir.n, flags & EV_TESTED);
Eric Andersenc470f442003-07-28 09:56:35 +00009332 }
Denys Vlasenkoeaf94362016-10-25 21:46:03 +02009333 if (n->nredir.redirect)
Denys Vlasenko035486c2017-07-31 04:09:19 +02009334 popredir(/*drop:*/ 0);
Eric Andersenc470f442003-07-28 09:56:35 +00009335 goto setstatus;
9336 case NCMD:
9337 evalfn = evalcommand;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009338 checkexit:
Denys Vlasenkof415e212021-09-07 01:54:23 +02009339 checkexit = ~flags & EV_TESTED;
Eric Andersenc470f442003-07-28 09:56:35 +00009340 goto calleval;
9341 case NFOR:
9342 evalfn = evalfor;
9343 goto calleval;
9344 case NWHILE:
9345 case NUNTIL:
9346 evalfn = evalloop;
9347 goto calleval;
9348 case NSUBSHELL:
9349 case NBACKGND:
9350 evalfn = evalsubshell;
Denys Vlasenkocf98b0c2016-10-25 18:19:39 +02009351 goto checkexit;
Eric Andersenc470f442003-07-28 09:56:35 +00009352 case NPIPE:
9353 evalfn = evalpipe;
9354 goto checkexit;
9355 case NCASE:
9356 evalfn = evalcase;
9357 goto calleval;
9358 case NAND:
9359 case NOR:
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00009360 case NSEMI: {
Eric Andersenc470f442003-07-28 09:56:35 +00009361#if NAND + 1 != NOR
9362#error NAND + 1 != NOR
9363#endif
9364#if NOR + 1 != NSEMI
9365#error NOR + 1 != NSEMI
9366#endif
Denis Vlasenko87d5fd92008-07-26 13:48:35 +00009367 unsigned is_or = n->type - NAND;
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009368 status = evaltree(
Eric Andersenc470f442003-07-28 09:56:35 +00009369 n->nbinary.ch1,
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00009370 (flags | ((is_or >> 1) - 1)) & EV_TESTED
Eric Andersenc470f442003-07-28 09:56:35 +00009371 );
Denys Vlasenkobc1a0082016-10-02 15:31:33 +02009372 if ((!status) == is_or || evalskip)
Eric Andersenc470f442003-07-28 09:56:35 +00009373 break;
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009374 n = n->nbinary.ch2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009375 evaln:
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009376 evalfn = evaltree;
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009377 calleval:
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009378 status = evalfn(n, flags);
9379 goto setstatus;
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +00009380 }
Eric Andersenc470f442003-07-28 09:56:35 +00009381 case NIF:
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009382 status = evaltree(n->nif.test, EV_TESTED);
Eric Andersenc470f442003-07-28 09:56:35 +00009383 if (evalskip)
9384 break;
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009385 if (!status) {
Eric Andersenc470f442003-07-28 09:56:35 +00009386 n = n->nif.ifpart;
9387 goto evaln;
Denys Vlasenkof415e212021-09-07 01:54:23 +02009388 } else if (n->nif.elsepart) {
Eric Andersenc470f442003-07-28 09:56:35 +00009389 n = n->nif.elsepart;
9390 goto evaln;
9391 }
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009392 status = 0;
9393 goto setstatus;
Eric Andersenc470f442003-07-28 09:56:35 +00009394 case NDEFUN:
Denys Vlasenko7aec8682016-10-25 20:26:02 +02009395 defun(n);
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009396 /* Not necessary. To test it:
9397 * "false; f() { qwerty; }; echo $?" should print 0.
9398 */
9399 /* status = 0; */
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009400 setstatus:
Eric Andersenc470f442003-07-28 09:56:35 +00009401 exitstatus = status;
9402 break;
9403 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009404 out:
Denys Vlasenkob563f622010-09-25 17:15:13 +02009405 /* Order of checks below is important:
9406 * signal handlers trigger before exit caused by "set -e".
9407 */
Denys Vlasenkob98b4c12016-10-01 23:25:12 +02009408 dotrap();
9409
Denys Vlasenkof415e212021-09-07 01:54:23 +02009410 if (checkexit && status) {
Roberto A. Fogliettae0bf3df2021-09-07 01:19:31 +02009411 if (trap[NTRAP_ERR] && !in_trap_ERR) {
9412 int err;
9413 struct jmploc *volatile savehandler = exception_handler;
9414 struct jmploc jmploc;
9415
9416 in_trap_ERR = 1;
9417 trap_depth++;
9418 err = setjmp(jmploc.loc);
9419 if (!err) {
9420 exception_handler = &jmploc;
9421 savestatus = exitstatus;
9422 evalstring(trap[NTRAP_ERR], 0);
9423 }
9424 trap_depth--;
9425 in_trap_ERR = 0;
9426
9427 exception_handler = savehandler;
9428 if (err && exception_type != EXERROR)
9429 longjmp(exception_handler->loc, 1);
9430
9431 exitstatus = savestatus;
9432 }
9433 if (eflag)
Denys Vlasenkof415e212021-09-07 01:54:23 +02009434 goto exexit;
Roberto A. Fogliettae0bf3df2021-09-07 01:19:31 +02009435 }
Denys Vlasenkof415e212021-09-07 01:54:23 +02009436 if (flags & EV_EXIT) {
9437 exexit:
Denys Vlasenkof977e002020-02-20 16:54:29 +01009438 raise_exception(EXEND);
Denys Vlasenkof415e212021-09-07 01:54:23 +02009439 }
Denis Vlasenko653d8e72009-03-19 21:59:35 +00009440
Ron Yorstonf55161a2019-02-25 08:29:38 +00009441 popstackmark(&smark);
Denis Vlasenko653d8e72009-03-19 21:59:35 +00009442 TRACE(("leaving evaltree (no interrupts)\n"));
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009443 return exitstatus;
Eric Andersen62483552001-07-10 06:09:16 +00009444}
9445
Denys Vlasenko37dc08b2016-10-02 04:38:07 +02009446static int
9447skiploop(void)
Denys Vlasenko35ec8182016-10-01 19:56:52 +02009448{
9449 int skip = evalskip;
9450
9451 switch (skip) {
9452 case 0:
9453 break;
9454 case SKIPBREAK:
9455 case SKIPCONT:
9456 if (--skipcount <= 0) {
9457 evalskip = 0;
9458 break;
9459 }
9460 skip = SKIPBREAK;
9461 break;
9462 }
9463 return skip;
9464}
9465
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009466static int
Eric Andersenc470f442003-07-28 09:56:35 +00009467evalloop(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00009468{
Denys Vlasenko35ec8182016-10-01 19:56:52 +02009469 int skip;
Eric Andersencb57d552001-06-28 07:25:16 +00009470 int status;
9471
9472 loopnest++;
9473 status = 0;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009474 flags &= EV_TESTED;
Denys Vlasenko35ec8182016-10-01 19:56:52 +02009475 do {
Eric Andersenc470f442003-07-28 09:56:35 +00009476 int i;
9477
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009478 i = evaltree(n->nbinary.ch1, EV_TESTED);
Denys Vlasenko35ec8182016-10-01 19:56:52 +02009479 skip = skiploop();
9480 if (skip == SKIPFUNC)
9481 status = i;
9482 if (skip)
9483 continue;
Eric Andersenc470f442003-07-28 09:56:35 +00009484 if (n->type != NWHILE)
9485 i = !i;
9486 if (i != 0)
9487 break;
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009488 status = evaltree(n->nbinary.ch2, flags);
Denys Vlasenko35ec8182016-10-01 19:56:52 +02009489 skip = skiploop();
9490 } while (!(skip & ~SKIPCONT));
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009491 loopnest--;
9492
9493 return status;
Eric Andersencb57d552001-06-28 07:25:16 +00009494}
9495
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009496static int
Eric Andersenc470f442003-07-28 09:56:35 +00009497evalfor(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00009498{
9499 struct arglist arglist;
9500 union node *argp;
9501 struct strlist *sp;
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009502 int status = 0;
Eric Andersencb57d552001-06-28 07:25:16 +00009503
Denys Vlasenko675d24a2018-01-27 22:02:05 +01009504 errlinno = lineno = n->ncase.linno;
9505 if (funcline)
9506 lineno -= funcline - 1;
9507
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00009508 arglist.list = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00009509 arglist.lastp = &arglist.list;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009510 for (argp = n->nfor.args; argp; argp = argp->narg.next) {
Ron Yorston549deab2015-05-18 09:57:51 +02009511 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
Eric Andersencb57d552001-06-28 07:25:16 +00009512 }
9513 *arglist.lastp = NULL;
9514
Eric Andersencb57d552001-06-28 07:25:16 +00009515 loopnest++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009516 flags &= EV_TESTED;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009517 for (sp = arglist.list; sp; sp = sp->next) {
Denys Vlasenko0a0acb52015-04-18 19:36:38 +02009518 setvar0(n->nfor.var, sp->text);
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009519 status = evaltree(n->nfor.body, flags);
Denys Vlasenko35ec8182016-10-01 19:56:52 +02009520 if (skiploop() & ~SKIPCONT)
Eric Andersencb57d552001-06-28 07:25:16 +00009521 break;
Eric Andersencb57d552001-06-28 07:25:16 +00009522 }
9523 loopnest--;
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009524
9525 return status;
Eric Andersencb57d552001-06-28 07:25:16 +00009526}
9527
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009528static int
Eric Andersenc470f442003-07-28 09:56:35 +00009529evalcase(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00009530{
9531 union node *cp;
9532 union node *patp;
9533 struct arglist arglist;
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009534 int status = 0;
Eric Andersencb57d552001-06-28 07:25:16 +00009535
Denys Vlasenko675d24a2018-01-27 22:02:05 +01009536 errlinno = lineno = n->ncase.linno;
9537 if (funcline)
9538 lineno -= funcline - 1;
9539
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00009540 arglist.list = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +00009541 arglist.lastp = &arglist.list;
Eric Andersencb57d552001-06-28 07:25:16 +00009542 expandarg(n->ncase.expr, &arglist, EXP_TILDE);
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009543 for (cp = n->ncase.cases; cp && evalskip == 0; cp = cp->nclist.next) {
9544 for (patp = cp->nclist.pattern; patp; patp = patp->narg.next) {
Eric Andersencb57d552001-06-28 07:25:16 +00009545 if (casematch(patp, arglist.list->text)) {
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009546 /* Ensure body is non-empty as otherwise
9547 * EV_EXIT may prevent us from setting the
9548 * exit status.
9549 */
9550 if (evalskip == 0 && cp->nclist.body) {
9551 status = evaltree(cp->nclist.body, flags);
Eric Andersencb57d552001-06-28 07:25:16 +00009552 }
9553 goto out;
9554 }
9555 }
9556 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +00009557 out:
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009558 return status;
Eric Andersencb57d552001-06-28 07:25:16 +00009559}
9560
Eric Andersenc470f442003-07-28 09:56:35 +00009561/*
9562 * Kick off a subshell to evaluate a tree.
9563 */
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009564static int
Eric Andersenc470f442003-07-28 09:56:35 +00009565evalsubshell(union node *n, int flags)
9566{
9567 struct job *jp;
Denys Vlasenko098b7132017-01-11 19:59:03 +01009568 int backgnd = (n->type == NBACKGND); /* FORK_BG(1) if yes, else FORK_FG(0) */
Eric Andersenc470f442003-07-28 09:56:35 +00009569 int status;
9570
Denys Vlasenko675d24a2018-01-27 22:02:05 +01009571 errlinno = lineno = n->nredir.linno;
9572 if (funcline)
9573 lineno -= funcline - 1;
9574
Eric Andersenc470f442003-07-28 09:56:35 +00009575 expredir(n->nredir.redirect);
Denys Vlasenko238bf182010-05-18 15:49:07 +02009576 if (!backgnd && (flags & EV_EXIT) && !may_have_traps)
Eric Andersenc470f442003-07-28 09:56:35 +00009577 goto nofork;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009578 INT_OFF;
Denys Vlasenko098b7132017-01-11 19:59:03 +01009579 if (backgnd == FORK_FG)
9580 get_tty_state();
Denis Vlasenko68404f12008-03-17 09:00:54 +00009581 jp = makejob(/*n,*/ 1);
Eric Andersenc470f442003-07-28 09:56:35 +00009582 if (forkshell(jp, n, backgnd) == 0) {
Denys Vlasenko238bf182010-05-18 15:49:07 +02009583 /* child */
Denis Vlasenkob012b102007-02-19 22:43:01 +00009584 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +00009585 flags |= EV_EXIT;
9586 if (backgnd)
Denys Vlasenko238bf182010-05-18 15:49:07 +02009587 flags &= ~EV_TESTED;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +00009588 nofork:
Eric Andersenc470f442003-07-28 09:56:35 +00009589 redirect(n->nredir.redirect, 0);
9590 evaltreenr(n->nredir.n, flags);
9591 /* never returns */
9592 }
Denys Vlasenko70392332016-10-27 02:31:55 +02009593 /* parent */
Eric Andersenc470f442003-07-28 09:56:35 +00009594 status = 0;
Denys Vlasenko098b7132017-01-11 19:59:03 +01009595 if (backgnd == FORK_FG)
Eric Andersenc470f442003-07-28 09:56:35 +00009596 status = waitforjob(jp);
Denis Vlasenkob012b102007-02-19 22:43:01 +00009597 INT_ON;
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009598 return status;
Eric Andersenc470f442003-07-28 09:56:35 +00009599}
9600
Eric Andersenc470f442003-07-28 09:56:35 +00009601/*
9602 * Compute the names of the files in a redirection list.
9603 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +00009604static void fixredir(union node *, const char *, int);
Eric Andersenc470f442003-07-28 09:56:35 +00009605static void
9606expredir(union node *n)
9607{
9608 union node *redir;
9609
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009610 for (redir = n; redir; redir = redir->nfile.next) {
Eric Andersenc470f442003-07-28 09:56:35 +00009611 struct arglist fn;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009612
Denis Vlasenkoc12d51e2008-02-19 23:31:05 +00009613 fn.list = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +00009614 fn.lastp = &fn.list;
9615 switch (redir->type) {
9616 case NFROMTO:
9617 case NFROM:
9618 case NTO:
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01009619#if BASH_REDIR_OUTPUT
Denis Vlasenko559691a2008-10-05 18:39:31 +00009620 case NTO2:
9621#endif
Eric Andersenc470f442003-07-28 09:56:35 +00009622 case NCLOBBER:
9623 case NAPPEND:
9624 expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
Denys Vlasenkof451b2c2012-06-09 02:06:57 +02009625 TRACE(("expredir expanded to '%s'\n", fn.list->text));
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01009626#if BASH_REDIR_OUTPUT
Denis Vlasenko559691a2008-10-05 18:39:31 +00009627 store_expfname:
9628#endif
Denys Vlasenko7c4b13e2013-01-17 13:02:27 +01009629#if 0
9630// By the design of stack allocator, the loop of this kind:
9631// while true; do while true; do break; done </dev/null; done
9632// will look like a memory leak: ash plans to free expfname's
9633// of "/dev/null" as soon as it finishes running the loop
9634// (in this case, never).
9635// This "fix" is wrong:
Jon Tollefson4ba6c5d2012-11-13 19:26:53 +01009636 if (redir->nfile.expfname)
9637 stunalloc(redir->nfile.expfname);
Denys Vlasenko7c4b13e2013-01-17 13:02:27 +01009638// It results in corrupted state of stacked allocations.
9639#endif
Eric Andersenc470f442003-07-28 09:56:35 +00009640 redir->nfile.expfname = fn.list->text;
9641 break;
9642 case NFROMFD:
Denis Vlasenko559691a2008-10-05 18:39:31 +00009643 case NTOFD: /* >& */
Eric Andersenc470f442003-07-28 09:56:35 +00009644 if (redir->ndup.vname) {
Denys Vlasenkoe368d852020-02-16 19:02:22 +01009645 expandarg(redir->ndup.vname, &fn, EXP_TILDE | EXP_REDIR);
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009646 if (fn.list == NULL)
Denis Vlasenkob012b102007-02-19 22:43:01 +00009647 ash_msg_and_raise_error("redir error");
Denys Vlasenko7d4aec02017-01-11 14:00:38 +01009648#if BASH_REDIR_OUTPUT
Denis Vlasenko559691a2008-10-05 18:39:31 +00009649 if (!isdigit_str9(fn.list->text)) {
9650 /* >&file, not >&fd */
9651 if (redir->nfile.fd != 1) /* 123>&file - BAD */
9652 ash_msg_and_raise_error("redir error");
9653 redir->type = NTO2;
9654 goto store_expfname;
9655 }
9656#endif
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009657 fixredir(redir, fn.list->text, 1);
Eric Andersenc470f442003-07-28 09:56:35 +00009658 }
9659 break;
9660 }
9661 }
9662}
9663
Eric Andersencb57d552001-06-28 07:25:16 +00009664/*
Eric Andersencb57d552001-06-28 07:25:16 +00009665 * Evaluate a pipeline. All the processes in the pipeline are children
9666 * of the process creating the pipeline. (This differs from some versions
9667 * of the shell, which make the last process in a pipeline the parent
9668 * of all the rest.)
9669 */
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009670static int
Eric Andersenc470f442003-07-28 09:56:35 +00009671evalpipe(union node *n, int flags)
Eric Andersencb57d552001-06-28 07:25:16 +00009672{
9673 struct job *jp;
9674 struct nodelist *lp;
9675 int pipelen;
9676 int prevfd;
9677 int pip[2];
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009678 int status = 0;
Eric Andersencb57d552001-06-28 07:25:16 +00009679
Eric Andersenc470f442003-07-28 09:56:35 +00009680 TRACE(("evalpipe(0x%lx) called\n", (long)n));
Eric Andersencb57d552001-06-28 07:25:16 +00009681 pipelen = 0;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009682 for (lp = n->npipe.cmdlist; lp; lp = lp->next)
Eric Andersencb57d552001-06-28 07:25:16 +00009683 pipelen++;
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009684 flags |= EV_EXIT;
Denis Vlasenkob012b102007-02-19 22:43:01 +00009685 INT_OFF;
Denys Vlasenko098b7132017-01-11 19:59:03 +01009686 if (n->npipe.pipe_backgnd == 0)
9687 get_tty_state();
Denis Vlasenko68404f12008-03-17 09:00:54 +00009688 jp = makejob(/*n,*/ pipelen);
Eric Andersencb57d552001-06-28 07:25:16 +00009689 prevfd = -1;
Denis Vlasenko2da584f2007-02-19 22:44:05 +00009690 for (lp = n->npipe.cmdlist; lp; lp = lp->next) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009691 prehash(lp->n);
Eric Andersencb57d552001-06-28 07:25:16 +00009692 pip[1] = -1;
9693 if (lp->next) {
9694 if (pipe(pip) < 0) {
9695 close(prevfd);
Denys Vlasenko12ffefb2017-08-23 14:52:56 +02009696 ash_msg_and_raise_perror("can't create pipe");
Eric Andersencb57d552001-06-28 07:25:16 +00009697 }
9698 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00009699 if (forkshell(jp, lp->n, n->npipe.pipe_backgnd) == 0) {
Denys Vlasenko70392332016-10-27 02:31:55 +02009700 /* child */
Denis Vlasenkob012b102007-02-19 22:43:01 +00009701 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +00009702 if (pip[1] >= 0) {
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009703 close(pip[0]);
Eric Andersencb57d552001-06-28 07:25:16 +00009704 }
Glenn L McGrath50812ff2002-08-23 13:14:48 +00009705 if (prevfd > 0) {
9706 dup2(prevfd, 0);
9707 close(prevfd);
9708 }
9709 if (pip[1] > 1) {
9710 dup2(pip[1], 1);
9711 close(pip[1]);
9712 }
Eric Andersenc470f442003-07-28 09:56:35 +00009713 evaltreenr(lp->n, flags);
9714 /* never returns */
Eric Andersencb57d552001-06-28 07:25:16 +00009715 }
Denys Vlasenko70392332016-10-27 02:31:55 +02009716 /* parent */
Eric Andersencb57d552001-06-28 07:25:16 +00009717 if (prevfd >= 0)
9718 close(prevfd);
9719 prevfd = pip[0];
Denis Vlasenkob9e70dd2009-03-20 01:24:08 +00009720 /* Don't want to trigger debugging */
9721 if (pip[1] != -1)
9722 close(pip[1]);
Eric Andersencb57d552001-06-28 07:25:16 +00009723 }
Denis Vlasenko2dc240c2008-07-24 06:07:50 +00009724 if (n->npipe.pipe_backgnd == 0) {
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009725 status = waitforjob(jp);
9726 TRACE(("evalpipe: job done exit status %d\n", status));
Eric Andersencb57d552001-06-28 07:25:16 +00009727 }
Denis Vlasenkob012b102007-02-19 22:43:01 +00009728 INT_ON;
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +02009729
9730 return status;
Eric Andersencb57d552001-06-28 07:25:16 +00009731}
9732
Ron Yorston9e2a5662020-01-21 16:01:58 +00009733/* setinteractive needs this forward reference */
9734#if EDITING_HAS_get_exe_name
9735static const char *get_builtin_name(int i) FAST_FUNC;
9736#endif
9737
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009738/*
9739 * Controls whether the shell is interactive or not.
9740 */
9741static void
9742setinteractive(int on)
9743{
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009744 static smallint is_interactive;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009745
9746 if (++on == is_interactive)
9747 return;
9748 is_interactive = on;
9749 setsignal(SIGINT);
9750 setsignal(SIGQUIT);
9751 setsignal(SIGTERM);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009752 if (is_interactive > 1) {
Denys Vlasenko897475a2019-06-01 16:35:09 +02009753#if !ENABLE_FEATURE_SH_EXTRA_QUIET
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009754 /* Looks like they want an interactive shell */
Denis Vlasenkoca525b42007-06-13 12:27:17 +00009755 static smallint did_banner;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009756
Denis Vlasenkoca525b42007-06-13 12:27:17 +00009757 if (!did_banner) {
Denys Vlasenkoc34c0332009-09-29 12:25:30 +02009758 /* note: ash and hush share this string */
9759 out1fmt("\n\n%s %s\n"
Denys Vlasenko2ec34962014-09-08 16:52:39 +02009760 IF_ASH_HELP("Enter 'help' for a list of built-in commands.\n")
9761 "\n",
Denys Vlasenkoc34c0332009-09-29 12:25:30 +02009762 bb_banner,
9763 "built-in shell (ash)"
9764 );
Denis Vlasenkoca525b42007-06-13 12:27:17 +00009765 did_banner = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009766 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009767#endif
Denys Vlasenko897475a2019-06-01 16:35:09 +02009768#if ENABLE_FEATURE_EDITING
Ron Yorston9e2a5662020-01-21 16:01:58 +00009769 if (!line_input_state) {
Denys Vlasenko897475a2019-06-01 16:35:09 +02009770 line_input_state = new_line_input_t(FOR_SHELL | WITH_PATH_LOOKUP);
Ron Yorston9e2a5662020-01-21 16:01:58 +00009771# if EDITING_HAS_get_exe_name
9772 line_input_state->get_exe_name = get_builtin_name;
9773# endif
9774 }
Denys Vlasenko897475a2019-06-01 16:35:09 +02009775#endif
9776 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009777}
9778
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009779static void
9780optschanged(void)
9781{
9782#if DEBUG
9783 opentrace();
9784#endif
9785 setinteractive(iflag);
9786 setjobctl(mflag);
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009787#if ENABLE_FEATURE_EDITING_VI
Denys Vlasenko897475a2019-06-01 16:35:09 +02009788 if (line_input_state) {
9789 if (viflag)
9790 line_input_state->flags |= VI_MODE;
9791 else
9792 line_input_state->flags &= ~VI_MODE;
9793 }
Denis Vlasenkob07a4962008-06-22 13:16:23 +00009794#else
9795 viflag = 0; /* forcibly keep the option off */
9796#endif
Denis Vlasenko0dec6de2007-02-23 21:10:47 +00009797}
9798
Denys Vlasenkob8ab27b2017-07-26 19:22:34 +02009799struct localvar_list {
9800 struct localvar_list *next;
9801 struct localvar *lv;
9802};
9803
9804static struct localvar_list *localvar_stack;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009805
9806/*
9807 * Called after a function returns.
9808 * Interrupts must be off.
9809 */
9810static void
Denys Vlasenko981a0562017-07-26 19:53:11 +02009811poplocalvars(int keep)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009812{
Denys Vlasenkob8ab27b2017-07-26 19:22:34 +02009813 struct localvar_list *ll;
9814 struct localvar *lvp, *next;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009815 struct var *vp;
9816
Denys Vlasenkob8ab27b2017-07-26 19:22:34 +02009817 INT_OFF;
9818 ll = localvar_stack;
9819 localvar_stack = ll->next;
9820
9821 next = ll->lv;
9822 free(ll);
9823
9824 while ((lvp = next) != NULL) {
9825 next = lvp->next;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009826 vp = lvp->vp;
Denys Vlasenkob563f622010-09-25 17:15:13 +02009827 TRACE(("poplocalvar %s\n", vp ? vp->var_text : "-"));
Denys Vlasenko981a0562017-07-26 19:53:11 +02009828 if (keep) {
9829 int bits = VSTRFIXED;
9830
9831 if (lvp->flags != VUNSET) {
9832 if (vp->var_text == lvp->text)
9833 bits |= VTEXTFIXED;
9834 else if (!(lvp->flags & (VTEXTFIXED|VSTACK)))
9835 free((char*)lvp->text);
9836 }
9837
9838 vp->flags &= ~bits;
9839 vp->flags |= (lvp->flags & bits);
9840
9841 if ((vp->flags &
9842 (VEXPORT|VREADONLY|VSTRFIXED|VUNSET)) == VUNSET)
9843 unsetvar(vp->var_text);
9844 } else if (vp == NULL) { /* $- saved */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009845 memcpy(optlist, lvp->text, sizeof(optlist));
9846 free((char*)lvp->text);
9847 optschanged();
Denys Vlasenkod5b500c2017-07-26 19:25:40 +02009848 } else if (lvp->flags == VUNSET) {
9849 vp->flags &= ~(VSTRFIXED|VREADONLY);
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02009850 unsetvar(vp->var_text);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009851 } else {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02009852 if (vp->var_func)
9853 vp->var_func(var_end(lvp->text));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009854 if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0)
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02009855 free((char*)vp->var_text);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009856 vp->flags = lvp->flags;
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02009857 vp->var_text = lvp->text;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009858 }
9859 free(lvp);
9860 }
Denys Vlasenkob8ab27b2017-07-26 19:22:34 +02009861 INT_ON;
9862}
9863
9864/*
9865 * Create a new localvar environment.
9866 */
Denys Vlasenko484fc202017-07-26 19:55:31 +02009867static struct localvar_list *
Denys Vlasenkoe2dd2af2020-02-20 10:33:38 +01009868pushlocalvars(int push)
Denys Vlasenkob8ab27b2017-07-26 19:22:34 +02009869{
9870 struct localvar_list *ll;
Denys Vlasenkoe2dd2af2020-02-20 10:33:38 +01009871 struct localvar_list *top;
9872
9873 top = localvar_stack;
9874 if (!push)
9875 goto out;
Denys Vlasenkob8ab27b2017-07-26 19:22:34 +02009876
9877 INT_OFF;
9878 ll = ckzalloc(sizeof(*ll));
9879 /*ll->lv = NULL; - zalloc did it */
Denys Vlasenkoe2dd2af2020-02-20 10:33:38 +01009880 ll->next = top;
Denys Vlasenkob8ab27b2017-07-26 19:22:34 +02009881 localvar_stack = ll;
9882 INT_ON;
Denys Vlasenkoe2dd2af2020-02-20 10:33:38 +01009883 out:
9884 return top;
Denys Vlasenko484fc202017-07-26 19:55:31 +02009885}
9886
9887static void
9888unwindlocalvars(struct localvar_list *stop)
9889{
9890 while (localvar_stack != stop)
9891 poplocalvars(0);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009892}
9893
9894static int
9895evalfun(struct funcnode *func, int argc, char **argv, int flags)
9896{
9897 volatile struct shparam saveparam;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009898 struct jmploc *volatile savehandler;
9899 struct jmploc jmploc;
9900 int e;
Denys Vlasenko675d24a2018-01-27 22:02:05 +01009901 int savefuncline;
Roberto A. Fogliettae0bf3df2021-09-07 01:19:31 +02009902 char *savetrap = NULL;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009903
Roberto A. Fogliettae0bf3df2021-09-07 01:19:31 +02009904 if (!Eflag) {
9905 savetrap = trap[NTRAP_ERR];
9906 trap[NTRAP_ERR] = NULL;
9907 }
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009908 saveparam = shellparam;
Denys Vlasenko675d24a2018-01-27 22:02:05 +01009909 savefuncline = funcline;
Denys Vlasenkoa2d121c2016-09-30 11:30:11 +02009910 savehandler = exception_handler;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009911 e = setjmp(jmploc.loc);
9912 if (e) {
9913 goto funcdone;
9914 }
9915 INT_OFF;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009916 exception_handler = &jmploc;
Denis Vlasenko01631112007-12-16 17:20:38 +00009917 shellparam.malloced = 0;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009918 func->count++;
Denys Vlasenko675d24a2018-01-27 22:02:05 +01009919 funcline = func->n.ndefun.linno;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009920 INT_ON;
9921 shellparam.nparam = argc - 1;
9922 shellparam.p = argv + 1;
9923#if ENABLE_ASH_GETOPTS
9924 shellparam.optind = 1;
9925 shellparam.optoff = -1;
9926#endif
Denys Vlasenko675d24a2018-01-27 22:02:05 +01009927 evaltree(func->n.ndefun.body, flags & EV_TESTED);
Denis Vlasenko01631112007-12-16 17:20:38 +00009928 funcdone:
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009929 INT_OFF;
Roberto A. Fogliettae0bf3df2021-09-07 01:19:31 +02009930 if (savetrap) {
9931 if (!trap[NTRAP_ERR])
9932 trap[NTRAP_ERR] = savetrap;
9933 else
9934 free(savetrap);
9935 }
Denys Vlasenko675d24a2018-01-27 22:02:05 +01009936 funcline = savefuncline;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009937 freefunc(func);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009938 freeparam(&shellparam);
9939 shellparam = saveparam;
9940 exception_handler = savehandler;
9941 INT_ON;
Denys Vlasenkocd24a502020-02-20 16:47:01 +01009942 evalskip &= ~(SKIPFUNC | SKIPFUNCDEF);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009943 return e;
9944}
9945
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009946/*
9947 * Make a variable a local variable. When a variable is made local, it's
9948 * value and flags are saved in a localvar structure. The saved values
9949 * will be restored when the shell function returns. We handle the name
Denys Vlasenkoe0a4e102015-05-13 02:20:14 +02009950 * "-" as a special case: it makes changes to "set +-options" local
9951 * (options will be restored on return from the function).
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009952 */
9953static void
Denys Vlasenko3e729102020-02-19 17:33:44 +01009954mklocal(char *name, int flags)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009955{
9956 struct localvar *lvp;
9957 struct var **vpp;
9958 struct var *vp;
Denys Vlasenko0a0acb52015-04-18 19:36:38 +02009959 char *eq = strchr(name, '=');
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009960
9961 INT_OFF;
Denys Vlasenko0a0acb52015-04-18 19:36:38 +02009962 /* Cater for duplicate "local". Examples:
9963 * x=0; f() { local x=1; echo $x; local x; echo $x; }; f; echo $x
9964 * x=0; f() { local x=1; echo $x; local x=2; echo $x; }; f; echo $x
9965 */
Denys Vlasenkob8ab27b2017-07-26 19:22:34 +02009966 lvp = localvar_stack->lv;
Denys Vlasenko0a0acb52015-04-18 19:36:38 +02009967 while (lvp) {
Eugene Rudoy1285aa62015-04-26 23:32:00 +02009968 if (lvp->vp && varcmp(lvp->vp->var_text, name) == 0) {
Denys Vlasenko0a0acb52015-04-18 19:36:38 +02009969 if (eq)
9970 setvareq(name, 0);
9971 /* else:
9972 * it's a duplicate "local VAR" declaration, do nothing
9973 */
Denys Vlasenko06b11492016-11-04 16:43:18 +01009974 goto ret;
Denys Vlasenko0a0acb52015-04-18 19:36:38 +02009975 }
9976 lvp = lvp->next;
9977 }
9978
9979 lvp = ckzalloc(sizeof(*lvp));
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009980 if (LONE_DASH(name)) {
9981 char *p;
9982 p = ckmalloc(sizeof(optlist));
9983 lvp->text = memcpy(p, optlist, sizeof(optlist));
9984 vp = NULL;
9985 } else {
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009986 vpp = hashvar(name);
9987 vp = *findvar(vpp, name);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009988 if (vp == NULL) {
Denys Vlasenko0a0acb52015-04-18 19:36:38 +02009989 /* variable did not exist yet */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009990 if (eq)
Denys Vlasenko3e729102020-02-19 17:33:44 +01009991 vp = setvareq(name, VSTRFIXED | flags);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009992 else
Denys Vlasenko3e729102020-02-19 17:33:44 +01009993 vp = setvar(name, NULL, VSTRFIXED | flags);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009994 lvp->flags = VUNSET;
9995 } else {
Denys Vlasenko8837c5d2010-06-02 12:56:18 +02009996 lvp->text = vp->var_text;
Denis Vlasenko5651bfc2007-02-23 21:08:58 +00009997 lvp->flags = vp->flags;
Denys Vlasenko0a0acb52015-04-18 19:36:38 +02009998 /* make sure neither "struct var" nor string gets freed
9999 * during (un)setting:
10000 */
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000010001 vp->flags |= VSTRFIXED|VTEXTFIXED;
10002 if (eq)
Denys Vlasenko3e729102020-02-19 17:33:44 +010010003 setvareq(name, flags);
Denys Vlasenko109ee5d2014-03-16 18:41:11 +010010004 else
10005 /* "local VAR" unsets VAR: */
Denys Vlasenko0a0acb52015-04-18 19:36:38 +020010006 setvar0(name, NULL);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000010007 }
10008 }
10009 lvp->vp = vp;
Denys Vlasenkob8ab27b2017-07-26 19:22:34 +020010010 lvp->next = localvar_stack->lv;
10011 localvar_stack->lv = lvp;
Denys Vlasenko06b11492016-11-04 16:43:18 +010010012 ret:
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000010013 INT_ON;
10014}
10015
10016/*
10017 * The "local" command.
10018 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010019static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000010020localcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000010021{
10022 char *name;
10023
Denys Vlasenkob8ab27b2017-07-26 19:22:34 +020010024 if (!localvar_stack)
Ron Yorstonef2386b2015-10-29 16:19:14 +000010025 ash_msg_and_raise_error("not in a function");
10026
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000010027 argv = argptr;
10028 while ((name = *argv++) != NULL) {
Denys Vlasenko3e729102020-02-19 17:33:44 +010010029 mklocal(name, 0);
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000010030 }
10031 return 0;
10032}
10033
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010034static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000010035falsecmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000010036{
10037 return 1;
10038}
10039
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010040static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000010041truecmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000010042{
10043 return 0;
10044}
10045
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010046static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000010047execcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000010048{
Denys Vlasenko6c149f42017-04-12 21:31:32 +020010049 optionarg = NULL;
10050 while (nextopt("a:") != '\0')
10051 /* nextopt() sets optionarg to "-a ARGV0" */;
10052
10053 argv = argptr;
10054 if (argv[0]) {
10055 char *prog;
10056
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000010057 iflag = 0; /* exit on error */
10058 mflag = 0;
10059 optschanged();
Denys Vlasenkoe5814a52016-07-16 18:33:55 +020010060 /* We should set up signals for "exec CMD"
10061 * the same way as for "CMD" without "exec".
10062 * But optschanged->setinteractive->setsignal
10063 * still thought we are a root shell. Therefore, for example,
10064 * SIGQUIT is still set to IGN. Fix it:
10065 */
10066 shlvl++;
10067 setsignal(SIGQUIT);
10068 /*setsignal(SIGTERM); - unnecessary because of iflag=0 */
10069 /*setsignal(SIGTSTP); - unnecessary because of mflag=0 */
10070 /*setsignal(SIGTTOU); - unnecessary because of mflag=0 */
10071
Denys Vlasenko6c149f42017-04-12 21:31:32 +020010072 prog = argv[0];
10073 if (optionarg)
10074 argv[0] = optionarg;
10075 shellexec(prog, argv, pathval(), 0);
Denys Vlasenkoe5814a52016-07-16 18:33:55 +020010076 /* NOTREACHED */
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000010077 }
10078 return 0;
10079}
10080
10081/*
10082 * The return command.
10083 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010084static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000010085returncmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000010086{
Denys Vlasenkocd24a502020-02-20 16:47:01 +010010087 int skip;
10088 int status;
10089
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000010090 /*
10091 * If called outside a function, do what ksh does;
10092 * skip the rest of the file.
10093 */
Denys Vlasenkocd24a502020-02-20 16:47:01 +010010094 if (argv[1]) {
10095 skip = SKIPFUNC;
10096 status = number(argv[1]);
10097 } else {
10098 skip = SKIPFUNCDEF;
10099 status = exitstatus;
10100 }
10101 evalskip = skip;
10102
10103 return status;
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000010104}
10105
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010106/* Forward declarations for builtintab[] */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010107static int breakcmd(int, char **) FAST_FUNC;
10108static int dotcmd(int, char **) FAST_FUNC;
Denys Vlasenko7b3fa1e2016-10-01 15:10:16 +020010109static int evalcmd(int, char **, int) FAST_FUNC;
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010110static int exitcmd(int, char **) FAST_FUNC;
10111static int exportcmd(int, char **) FAST_FUNC;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010112#if ENABLE_ASH_GETOPTS
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010113static int getoptscmd(int, char **) FAST_FUNC;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010114#endif
Denys Vlasenko2ec34962014-09-08 16:52:39 +020010115#if ENABLE_ASH_HELP
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010116static int helpcmd(int, char **) FAST_FUNC;
Denis Vlasenko52764022007-02-24 13:42:56 +000010117#endif
Flemming Madsend96ffda2013-04-07 18:47:24 +020010118#if MAX_HISTORY
10119static int historycmd(int, char **) FAST_FUNC;
10120#endif
Denys Vlasenko0b883582016-12-23 16:49:07 +010010121#if ENABLE_FEATURE_SH_MATH
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010122static int letcmd(int, char **) FAST_FUNC;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010123#endif
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010124static int readcmd(int, char **) FAST_FUNC;
10125static int setcmd(int, char **) FAST_FUNC;
10126static int shiftcmd(int, char **) FAST_FUNC;
10127static int timescmd(int, char **) FAST_FUNC;
10128static int trapcmd(int, char **) FAST_FUNC;
10129static int umaskcmd(int, char **) FAST_FUNC;
10130static int unsetcmd(int, char **) FAST_FUNC;
10131static int ulimitcmd(int, char **) FAST_FUNC;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010132
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000010133#define BUILTIN_NOSPEC "0"
10134#define BUILTIN_SPECIAL "1"
10135#define BUILTIN_REGULAR "2"
10136#define BUILTIN_SPEC_REG "3"
10137#define BUILTIN_ASSIGN "4"
10138#define BUILTIN_SPEC_ASSG "5"
10139#define BUILTIN_REG_ASSG "6"
Denis Vlasenko4fe15f32007-02-23 01:05:26 +000010140#define BUILTIN_SPEC_REG_ASSG "7"
10141
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010142/* Stubs for calling non-FAST_FUNC's */
Denys Vlasenko265062d2017-01-10 15:13:30 +010010143#if ENABLE_ASH_ECHO
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010144static int FAST_FUNC echocmd(int argc, char **argv) { return echo_main(argc, argv); }
Denys Vlasenko2634bf32009-06-09 18:40:07 +020010145#endif
Denys Vlasenko265062d2017-01-10 15:13:30 +010010146#if ENABLE_ASH_PRINTF
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010147static int FAST_FUNC printfcmd(int argc, char **argv) { return printf_main(argc, argv); }
Denys Vlasenko2634bf32009-06-09 18:40:07 +020010148#endif
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010010149#if ENABLE_ASH_TEST || BASH_TEST2
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010150static int FAST_FUNC testcmd(int argc, char **argv) { return test_main(argc, argv); }
Denys Vlasenko2634bf32009-06-09 18:40:07 +020010151#endif
Denis Vlasenko468aea22008-04-01 14:47:57 +000010152
Denis Vlasenkof7d56652008-03-25 05:51:41 +000010153/* Keep these in proper order since it is searched via bsearch() */
Denis Vlasenko4fe15f32007-02-23 01:05:26 +000010154static const struct builtincmd builtintab[] = {
Denys Vlasenko023a08f2010-03-26 15:53:33 +010010155 { BUILTIN_SPEC_REG "." , dotcmd },
10156 { BUILTIN_SPEC_REG ":" , truecmd },
Denys Vlasenko265062d2017-01-10 15:13:30 +010010157#if ENABLE_ASH_TEST
Denys Vlasenko023a08f2010-03-26 15:53:33 +010010158 { BUILTIN_REGULAR "[" , testcmd },
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010010159#endif
10160#if BASH_TEST2
Denys Vlasenko023a08f2010-03-26 15:53:33 +010010161 { BUILTIN_REGULAR "[[" , testcmd },
Denis Vlasenko80591b02008-03-25 07:49:43 +000010162#endif
Denis Vlasenko4fe15f32007-02-23 01:05:26 +000010163#if ENABLE_ASH_ALIAS
Denys Vlasenko023a08f2010-03-26 15:53:33 +010010164 { BUILTIN_REG_ASSG "alias" , aliascmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +000010165#endif
10166#if JOBS
Denys Vlasenko023a08f2010-03-26 15:53:33 +010010167 { BUILTIN_REGULAR "bg" , fg_bgcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +000010168#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +010010169 { BUILTIN_SPEC_REG "break" , breakcmd },
10170 { BUILTIN_REGULAR "cd" , cdcmd },
10171 { BUILTIN_NOSPEC "chdir" , cdcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +000010172#if ENABLE_ASH_CMDCMD
Denys Vlasenko023a08f2010-03-26 15:53:33 +010010173 { BUILTIN_REGULAR "command" , commandcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +000010174#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +010010175 { BUILTIN_SPEC_REG "continue", breakcmd },
Denys Vlasenko265062d2017-01-10 15:13:30 +010010176#if ENABLE_ASH_ECHO
Denys Vlasenko023a08f2010-03-26 15:53:33 +010010177 { BUILTIN_REGULAR "echo" , echocmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +000010178#endif
Denys Vlasenko7b3fa1e2016-10-01 15:10:16 +020010179 { BUILTIN_SPEC_REG "eval" , NULL }, /*evalcmd() has a differing prototype*/
Denys Vlasenko023a08f2010-03-26 15:53:33 +010010180 { BUILTIN_SPEC_REG "exec" , execcmd },
10181 { BUILTIN_SPEC_REG "exit" , exitcmd },
10182 { BUILTIN_SPEC_REG_ASSG "export" , exportcmd },
10183 { BUILTIN_REGULAR "false" , falsecmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +000010184#if JOBS
Denys Vlasenko023a08f2010-03-26 15:53:33 +010010185 { BUILTIN_REGULAR "fg" , fg_bgcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +000010186#endif
10187#if ENABLE_ASH_GETOPTS
Denys Vlasenko023a08f2010-03-26 15:53:33 +010010188 { BUILTIN_REGULAR "getopts" , getoptscmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +000010189#endif
Denys Vlasenkoa7b97e32020-02-16 18:29:52 +010010190 { BUILTIN_REGULAR "hash" , hashcmd },
Denys Vlasenko2ec34962014-09-08 16:52:39 +020010191#if ENABLE_ASH_HELP
Denys Vlasenko023a08f2010-03-26 15:53:33 +010010192 { BUILTIN_NOSPEC "help" , helpcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +000010193#endif
Flemming Madsend96ffda2013-04-07 18:47:24 +020010194#if MAX_HISTORY
10195 { BUILTIN_NOSPEC "history" , historycmd },
10196#endif
Denis Vlasenko4fe15f32007-02-23 01:05:26 +000010197#if JOBS
Denys Vlasenko023a08f2010-03-26 15:53:33 +010010198 { BUILTIN_REGULAR "jobs" , jobscmd },
10199 { BUILTIN_REGULAR "kill" , killcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +000010200#endif
Denys Vlasenko0b883582016-12-23 16:49:07 +010010201#if ENABLE_FEATURE_SH_MATH
Denys Vlasenko023a08f2010-03-26 15:53:33 +010010202 { BUILTIN_NOSPEC "let" , letcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +000010203#endif
Denys Vlasenko85241c72017-07-26 20:00:08 +020010204 { BUILTIN_SPEC_REG_ASSG "local" , localcmd },
Denys Vlasenko265062d2017-01-10 15:13:30 +010010205#if ENABLE_ASH_PRINTF
Denys Vlasenko023a08f2010-03-26 15:53:33 +010010206 { BUILTIN_REGULAR "printf" , printfcmd },
Denis Vlasenkocd2663f2008-06-01 22:36:39 +000010207#endif
Denys Vlasenkoa7b97e32020-02-16 18:29:52 +010010208 { BUILTIN_REGULAR "pwd" , pwdcmd },
Denys Vlasenko023a08f2010-03-26 15:53:33 +010010209 { BUILTIN_REGULAR "read" , readcmd },
10210 { BUILTIN_SPEC_REG_ASSG "readonly", exportcmd },
10211 { BUILTIN_SPEC_REG "return" , returncmd },
10212 { BUILTIN_SPEC_REG "set" , setcmd },
10213 { BUILTIN_SPEC_REG "shift" , shiftcmd },
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010010214#if BASH_SOURCE
Denys Vlasenko023a08f2010-03-26 15:53:33 +010010215 { BUILTIN_SPEC_REG "source" , dotcmd },
Denys Vlasenko82731b42010-05-17 17:49:52 +020010216#endif
Denys Vlasenko265062d2017-01-10 15:13:30 +010010217#if ENABLE_ASH_TEST
Denys Vlasenko023a08f2010-03-26 15:53:33 +010010218 { BUILTIN_REGULAR "test" , testcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +000010219#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +010010220 { BUILTIN_SPEC_REG "times" , timescmd },
10221 { BUILTIN_SPEC_REG "trap" , trapcmd },
10222 { BUILTIN_REGULAR "true" , truecmd },
Denys Vlasenkoa7b97e32020-02-16 18:29:52 +010010223 { BUILTIN_REGULAR "type" , typecmd },
10224 { BUILTIN_REGULAR "ulimit" , ulimitcmd },
Denys Vlasenko023a08f2010-03-26 15:53:33 +010010225 { BUILTIN_REGULAR "umask" , umaskcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +000010226#if ENABLE_ASH_ALIAS
Denys Vlasenko023a08f2010-03-26 15:53:33 +010010227 { BUILTIN_REGULAR "unalias" , unaliascmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +000010228#endif
Denys Vlasenko023a08f2010-03-26 15:53:33 +010010229 { BUILTIN_SPEC_REG "unset" , unsetcmd },
10230 { BUILTIN_REGULAR "wait" , waitcmd },
Denis Vlasenko4fe15f32007-02-23 01:05:26 +000010231};
10232
Denis Vlasenko80591b02008-03-25 07:49:43 +000010233/* Should match the above table! */
10234#define COMMANDCMD (builtintab + \
Denys Vlasenko928e2a72016-09-29 00:30:31 +020010235 /* . : */ 2 + \
Denys Vlasenko265062d2017-01-10 15:13:30 +010010236 /* [ */ 1 * ENABLE_ASH_TEST + \
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010010237 /* [[ */ 1 * BASH_TEST2 + \
Denys Vlasenko928e2a72016-09-29 00:30:31 +020010238 /* alias */ 1 * ENABLE_ASH_ALIAS + \
10239 /* bg */ 1 * ENABLE_ASH_JOB_CONTROL + \
10240 /* break cd cddir */ 3)
10241#define EVALCMD (COMMANDCMD + \
10242 /* command */ 1 * ENABLE_ASH_CMDCMD + \
10243 /* continue */ 1 + \
Denys Vlasenko265062d2017-01-10 15:13:30 +010010244 /* echo */ 1 * ENABLE_ASH_ECHO + \
Denys Vlasenko928e2a72016-09-29 00:30:31 +020010245 0)
10246#define EXECCMD (EVALCMD + \
10247 /* eval */ 1)
Denis Vlasenko4fe15f32007-02-23 01:05:26 +000010248
10249/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000010250 * Search the table of builtin commands.
10251 */
Denys Vlasenko888527c2016-10-02 16:54:17 +020010252static int
10253pstrcmp1(const void *a, const void *b)
10254{
10255 return strcmp((char*)a, *(char**)b + 1);
10256}
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000010257static struct builtincmd *
10258find_builtin(const char *name)
10259{
10260 struct builtincmd *bp;
10261
10262 bp = bsearch(
Denis Vlasenko80b8b392007-06-25 10:55:35 +000010263 name, builtintab, ARRAY_SIZE(builtintab), sizeof(builtintab[0]),
Denys Vlasenko888527c2016-10-02 16:54:17 +020010264 pstrcmp1
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000010265 );
10266 return bp;
10267}
10268
Ron Yorston9e2a5662020-01-21 16:01:58 +000010269#if EDITING_HAS_get_exe_name
10270static const char * FAST_FUNC
10271get_builtin_name(int i)
10272{
10273 return /*i >= 0 &&*/ i < ARRAY_SIZE(builtintab) ? builtintab[i].name + 1 : NULL;
10274}
10275#endif
10276
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000010277/*
Denis Vlasenko4fe15f32007-02-23 01:05:26 +000010278 * Execute a simple command.
10279 */
Denys Vlasenko1c5eb882018-08-05 17:07:26 +020010280static void unwindfiles(struct parsefile *stop);
Denis Vlasenko4fe15f32007-02-23 01:05:26 +000010281static int
10282isassignment(const char *p)
Paul Foxc3850c82005-07-20 18:23:39 +000010283{
10284 const char *q = endofname(p);
10285 if (p == q)
10286 return 0;
10287 return *q == '=';
10288}
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010289static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000010290bltincmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Denis Vlasenko4fe15f32007-02-23 01:05:26 +000010291{
10292 /* Preserve exitstatus of a previous possible redirection
10293 * as POSIX mandates */
10294 return back_exitstatus;
10295}
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +020010296static int
Eric Andersenc470f442003-07-28 09:56:35 +000010297evalcommand(union node *cmd, int flags)
10298{
Denis Vlasenko0e6f6612008-02-15 15:02:15 +000010299 static const struct builtincmd null_bltin = {
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010010300 BUILTIN_REGULAR "", bltincmd
Denis Vlasenko4fe15f32007-02-23 01:05:26 +000010301 };
Denys Vlasenko484fc202017-07-26 19:55:31 +020010302 struct localvar_list *localvar_stop;
Denys Vlasenko1c5eb882018-08-05 17:07:26 +020010303 struct parsefile *file_stop;
Denys Vlasenko1c79aeb2017-07-29 22:51:52 +020010304 struct redirtab *redir_stop;
Eric Andersenc470f442003-07-28 09:56:35 +000010305 union node *argp;
10306 struct arglist arglist;
10307 struct arglist varlist;
10308 char **argv;
10309 int argc;
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010010310 struct strlist *osp;
Glenn L McGrath7b8765c2003-08-29 07:29:30 +000010311 const struct strlist *sp;
Eric Andersenc470f442003-07-28 09:56:35 +000010312 struct cmdentry cmdentry;
10313 struct job *jp;
10314 char *lastarg;
10315 const char *path;
10316 int spclbltin;
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010010317 int cmd_flag;
Eric Andersenc470f442003-07-28 09:56:35 +000010318 int status;
10319 char **nargv;
Denis Vlasenko34c73c42008-08-16 11:48:02 +000010320 smallint cmd_is_exec;
Denys Vlasenko3e729102020-02-19 17:33:44 +010010321 int vflags;
10322 int vlocal;
Eric Andersenc470f442003-07-28 09:56:35 +000010323
Denys Vlasenko675d24a2018-01-27 22:02:05 +010010324 errlinno = lineno = cmd->ncmd.linno;
10325 if (funcline)
10326 lineno -= funcline - 1;
10327
Eric Andersenc470f442003-07-28 09:56:35 +000010328 /* First expand the arguments. */
10329 TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags));
Ron Yorston4a36ef12021-08-30 20:31:42 +010010330#if BASH_PROCESS_SUBST
10331 redir_stop = redirlist;
10332#endif
Denys Vlasenko1c5eb882018-08-05 17:07:26 +020010333 file_stop = g_parsefile;
Eric Andersenc470f442003-07-28 09:56:35 +000010334 back_exitstatus = 0;
10335
10336 cmdentry.cmdtype = CMDBUILTIN;
Denis Vlasenko0e6f6612008-02-15 15:02:15 +000010337 cmdentry.u.cmd = &null_bltin;
Eric Andersenc470f442003-07-28 09:56:35 +000010338 varlist.lastp = &varlist.list;
10339 *varlist.lastp = NULL;
10340 arglist.lastp = &arglist.list;
10341 *arglist.lastp = NULL;
10342
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010010343 cmd_flag = 0;
10344 cmd_is_exec = 0;
10345 spclbltin = -1;
Denys Vlasenko3e729102020-02-19 17:33:44 +010010346 vflags = 0;
10347 vlocal = 0;
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010010348 path = NULL;
10349
Eric Andersenc470f442003-07-28 09:56:35 +000010350 argc = 0;
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010010351 argp = cmd->ncmd.args;
10352 osp = fill_arglist(&arglist, &argp);
10353 if (osp) {
10354 int pseudovarflag = 0;
Denys Vlasenkod07a15b2017-07-30 16:51:05 +020010355
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010010356 for (;;) {
10357 find_command(arglist.list->text, &cmdentry,
10358 cmd_flag | DO_REGBLTIN, pathval());
Paul Foxc3850c82005-07-20 18:23:39 +000010359
Denys Vlasenko3e729102020-02-19 17:33:44 +010010360 vlocal++;
10361
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010010362 /* implement bltin and command here */
10363 if (cmdentry.cmdtype != CMDBUILTIN)
10364 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010365
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010010366 pseudovarflag = IS_BUILTIN_ASSIGN(cmdentry.u.cmd);
10367 if (spclbltin < 0) {
10368 spclbltin = IS_BUILTIN_SPECIAL(cmdentry.u.cmd);
Denys Vlasenko3e729102020-02-19 17:33:44 +010010369 vlocal = !spclbltin;
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010010370 }
10371 cmd_is_exec = cmdentry.u.cmd == EXECCMD;
Ron Yorstonda7a6db2020-02-27 09:50:18 +000010372#if ENABLE_ASH_CMDCMD
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010010373 if (cmdentry.u.cmd != COMMANDCMD)
10374 break;
Paul Foxc3850c82005-07-20 18:23:39 +000010375
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010010376 cmd_flag = parse_command_args(&arglist, &argp, &path);
10377 if (!cmd_flag)
Ron Yorstonda7a6db2020-02-27 09:50:18 +000010378#endif
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010010379 break;
Denys Vlasenkod07a15b2017-07-30 16:51:05 +020010380 }
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010010381
10382 for (; argp; argp = argp->narg.next)
10383 expandarg(argp, &arglist,
10384 pseudovarflag &&
10385 isassignment(argp->narg.text) ?
10386 EXP_VARTILDE : EXP_FULL | EXP_TILDE);
10387
10388 for (sp = arglist.list; sp; sp = sp->next)
10389 argc++;
Denys Vlasenko3e729102020-02-19 17:33:44 +010010390
10391 if (cmd_is_exec && argc > 1)
10392 vflags = VEXPORT;
Eric Andersenc470f442003-07-28 09:56:35 +000010393 }
10394
Denys Vlasenkoe2dd2af2020-02-20 10:33:38 +010010395 localvar_stop = pushlocalvars(vlocal);
10396
Denys Vlasenko65a8b852016-10-26 22:29:11 +020010397 /* Reserve one extra spot at the front for shellexec. */
10398 nargv = stalloc(sizeof(char *) * (argc + 2));
10399 argv = ++nargv;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010400 for (sp = arglist.list; sp; sp = sp->next) {
Eric Andersenc470f442003-07-28 09:56:35 +000010401 TRACE(("evalcommand arg: %s\n", sp->text));
10402 *nargv++ = sp->text;
10403 }
10404 *nargv = NULL;
10405
10406 lastarg = NULL;
Denys Vlasenko675d24a2018-01-27 22:02:05 +010010407 if (iflag && funcline == 0 && argc > 0)
Eric Andersenc470f442003-07-28 09:56:35 +000010408 lastarg = nargv[-1];
10409
10410 expredir(cmd->ncmd.redirect);
Ron Yorston4a36ef12021-08-30 20:31:42 +010010411#if !BASH_PROCESS_SUBST
Denys Vlasenko1c79aeb2017-07-29 22:51:52 +020010412 redir_stop = pushredir(cmd->ncmd.redirect);
Ron Yorston4a36ef12021-08-30 20:31:42 +010010413#else
10414 pushredir(cmd->ncmd.redirect);
10415#endif
Denys Vlasenkod07a15b2017-07-30 16:51:05 +020010416 preverrout_fd = 2;
Denys Vlasenkof8cdc7a2017-08-04 15:24:49 +020010417 if (BASH_XTRACEFD && xflag) {
10418 /* NB: bash closes fd == $BASH_XTRACEFD when it is changed.
10419 * we do not emulate this. We only use its value.
10420 */
10421 const char *xtracefd = lookupvar("BASH_XTRACEFD");
10422 if (xtracefd && is_number(xtracefd))
10423 preverrout_fd = atoi(xtracefd);
10424
10425 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000010426 status = redirectsafe(cmd->ncmd.redirect, REDIR_PUSH | REDIR_SAVEFD2);
Eric Andersenc470f442003-07-28 09:56:35 +000010427
Denys Vlasenko54bef2a2020-02-19 15:30:20 +010010428 if (status) {
10429 bail:
10430 exitstatus = status;
10431
10432 /* We have a redirection error. */
10433 if (spclbltin > 0)
10434 raise_exception(EXERROR);
10435
10436 goto out;
10437 }
10438
Eric Andersenc470f442003-07-28 09:56:35 +000010439 for (argp = cmd->ncmd.assign; argp; argp = argp->narg.next) {
10440 struct strlist **spp;
Eric Andersenc470f442003-07-28 09:56:35 +000010441
10442 spp = varlist.lastp;
10443 expandarg(argp, &varlist, EXP_VARTILDE);
10444
Denys Vlasenko3e729102020-02-19 17:33:44 +010010445 if (vlocal)
10446 mklocal((*spp)->text, VEXPORT);
10447 else
10448 setvareq((*spp)->text, vflags);
Eric Andersenc470f442003-07-28 09:56:35 +000010449 }
10450
10451 /* Print the command if xflag is set. */
10452 if (xflag) {
Denys Vlasenko42ba7572017-07-21 13:20:14 +020010453 const char *pfx = "";
Eric Andersenc470f442003-07-28 09:56:35 +000010454
Denys Vlasenko46999802017-07-29 21:12:29 +020010455 fdprintf(preverrout_fd, "%s", expandstr(ps4val(), DQSYNTAX));
Denys Vlasenko42ba7572017-07-21 13:20:14 +020010456
Glenn L McGrath7b8765c2003-08-29 07:29:30 +000010457 sp = varlist.list;
Denys Vlasenko42ba7572017-07-21 13:20:14 +020010458 while (sp) {
10459 char *varval = sp->text;
10460 char *eq = strchrnul(varval, '=');
10461 if (*eq)
10462 eq++;
10463 fdprintf(preverrout_fd, "%s%.*s%s",
10464 pfx,
10465 (int)(eq - varval), varval,
10466 maybe_single_quote(eq)
10467 );
10468 sp = sp->next;
10469 pfx = " ";
10470 }
10471
10472 sp = arglist.list;
10473 while (sp) {
10474 fdprintf(preverrout_fd, "%s%s",
10475 pfx,
10476 /* always quote if matches reserved word: */
10477 findkwd(sp->text)
10478 ? single_quote(sp->text)
10479 : maybe_single_quote(sp->text)
10480 );
10481 sp = sp->next;
10482 pfx = " ";
Glenn L McGrath7b8765c2003-08-29 07:29:30 +000010483 }
Denis Vlasenko0e6f6612008-02-15 15:02:15 +000010484 safe_write(preverrout_fd, "\n", 1);
Eric Andersenc470f442003-07-28 09:56:35 +000010485 }
10486
Eric Andersenc470f442003-07-28 09:56:35 +000010487 /* Now locate the command. */
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010010488 if (cmdentry.cmdtype != CMDBUILTIN
10489 || !(IS_BUILTIN_REGULAR(cmdentry.u.cmd))
10490 ) {
Denys Vlasenko3e729102020-02-19 17:33:44 +010010491 path = path ? path : pathval();
10492 find_command(argv[0], &cmdentry, cmd_flag | DO_ERR, path);
Eric Andersenc470f442003-07-28 09:56:35 +000010493 }
10494
Denys Vlasenkod81af722020-02-18 14:28:30 +010010495 jp = NULL;
10496
Eric Andersenc470f442003-07-28 09:56:35 +000010497 /* Execute the command. */
10498 switch (cmdentry.cmdtype) {
Denys Vlasenko54bef2a2020-02-19 15:30:20 +010010499 case CMDUNKNOWN:
10500 status = 127;
10501 flush_stdout_stderr();
10502 goto bail;
10503
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +020010504 default: {
Denis Vlasenkobe54d6b2008-10-27 14:25:52 +000010505
Denys Vlasenko1750d3a2018-01-15 00:41:04 +010010506#if ENABLE_FEATURE_SH_STANDALONE \
10507 && ENABLE_FEATURE_SH_NOFORK \
10508 && NUM_APPLETS > 1
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +020010509/* (1) BUG: if variables are set, we need to fork, or save/restore them
10510 * around run_nofork_applet() call.
10511 * (2) Should this check also be done in forkshell()?
10512 * (perhaps it should, so that "VAR=VAL nofork" at least avoids exec...)
10513 */
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000010514 /* find_command() encodes applet_no as (-2 - applet_no) */
10515 int applet_no = (- cmdentry.u.index - 2);
Denis Vlasenko9bc80d72008-04-12 20:07:53 +000010516 if (applet_no >= 0 && APPLET_IS_NOFORK(applet_no)) {
Denys Vlasenkoa5060b82017-11-03 14:16:25 +010010517 char **sv_environ;
10518
10519 INT_OFF;
10520 sv_environ = environ;
10521 environ = listvars(VEXPORT, VUNSET, varlist.list, /*end:*/ NULL);
Denys Vlasenkod329e342017-08-04 14:50:03 +020010522 /*
10523 * Run <applet>_main().
10524 * Signals (^C) can't interrupt here.
10525 * Otherwise we can mangle stdio or malloc internal state.
10526 * This makes applets which can run for a long time
10527 * and/or wait for user input ineligible for NOFORK:
10528 * for example, "yes" or "rm" (rm -i waits for input).
10529 */
Ron Yorstond5bfe262020-02-20 08:23:03 +000010530 exitstatus = run_nofork_applet(applet_no, argv);
Denys Vlasenkoa5060b82017-11-03 14:16:25 +010010531 environ = sv_environ;
Denys Vlasenkod329e342017-08-04 14:50:03 +020010532 /*
10533 * Try enabling NOFORK for "yes" applet.
10534 * ^C _will_ stop it (write returns EINTR),
10535 * but this causes stdout FILE to be stuck
10536 * and needing clearerr(). What if other applets
10537 * also can get EINTRs? Do we need to switch
10538 * our signals to SA_RESTART?
10539 */
10540 /*clearerr(stdout);*/
10541 INT_ON;
Denis Vlasenko9bc80d72008-04-12 20:07:53 +000010542 break;
10543 }
Denis Vlasenko9bc80d72008-04-12 20:07:53 +000010544#endif
Denys Vlasenkocfd392b2017-08-03 19:56:29 +020010545 /* Can we avoid forking? For example, very last command
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +020010546 * in a script or a subshell does not need forking,
10547 * we can just exec it.
10548 */
Denys Vlasenko238bf182010-05-18 15:49:07 +020010549 if (!(flags & EV_EXIT) || may_have_traps) {
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +020010550 /* No, forking off a child is necessary */
Denis Vlasenkob012b102007-02-19 22:43:01 +000010551 INT_OFF;
Denys Vlasenko098b7132017-01-11 19:59:03 +010010552 get_tty_state();
Denis Vlasenko68404f12008-03-17 09:00:54 +000010553 jp = makejob(/*cmd,*/ 1);
Eric Andersenc470f442003-07-28 09:56:35 +000010554 if (forkshell(jp, cmd, FORK_FG) != 0) {
Denys Vlasenko238bf182010-05-18 15:49:07 +020010555 /* parent */
Eric Andersenc470f442003-07-28 09:56:35 +000010556 break;
10557 }
Denys Vlasenko238bf182010-05-18 15:49:07 +020010558 /* child */
Denis Vlasenkob012b102007-02-19 22:43:01 +000010559 FORCE_INT_ON;
Denys Vlasenkoc7f95d22010-05-18 15:52:23 +020010560 /* fall through to exec'ing external program */
Eric Andersenc470f442003-07-28 09:56:35 +000010561 }
Denys Vlasenkoe139ae32017-04-12 21:02:33 +020010562 shellexec(argv[0], argv, path, cmdentry.u.index);
Eric Andersenc470f442003-07-28 09:56:35 +000010563 /* NOTREACHED */
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +020010564 } /* default */
Eric Andersenc470f442003-07-28 09:56:35 +000010565 case CMDBUILTIN:
Denys Vlasenko97edfc42020-02-18 14:37:56 +010010566 if (evalbltin(cmdentry.u.cmd, argc, argv, flags)
10567 && !(exception_type == EXERROR && spclbltin <= 0)
10568 ) {
Denys Vlasenkoc0663c72016-10-27 21:09:01 +020010569 raise:
10570 longjmp(exception_handler->loc, 1);
Eric Andersenc470f442003-07-28 09:56:35 +000010571 }
Denys Vlasenkod81af722020-02-18 14:28:30 +010010572 break;
Eric Andersenc470f442003-07-28 09:56:35 +000010573
10574 case CMDFUNCTION:
Eric Andersenc470f442003-07-28 09:56:35 +000010575 if (evalfun(cmdentry.u.func, argc, argv, flags))
10576 goto raise;
10577 break;
Denys Vlasenko42c4b2e2010-05-18 16:13:56 +020010578 } /* switch */
Eric Andersenc470f442003-07-28 09:56:35 +000010579
Denys Vlasenkod81af722020-02-18 14:28:30 +010010580 status = waitforjob(jp);
Ron Yorston6cda0b02020-02-21 16:16:56 +000010581 if (jp)
10582 TRACE(("forked child exited with %d\n", status));
Denys Vlasenko97edfc42020-02-18 14:37:56 +010010583 FORCE_INT_ON;
Denys Vlasenkod81af722020-02-18 14:28:30 +010010584
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010585 out:
Denys Vlasenkoeaf94362016-10-25 21:46:03 +020010586 if (cmd->ncmd.redirect)
Denys Vlasenko035486c2017-07-31 04:09:19 +020010587 popredir(/*drop:*/ cmd_is_exec);
Denys Vlasenko1c79aeb2017-07-29 22:51:52 +020010588 unwindredir(redir_stop);
Denys Vlasenko1c5eb882018-08-05 17:07:26 +020010589 unwindfiles(file_stop);
Denys Vlasenkoe2dd2af2020-02-20 10:33:38 +010010590 unwindlocalvars(localvar_stop);
Denis Vlasenko6514c5e2008-07-24 13:41:37 +000010591 if (lastarg) {
Eric Andersenc470f442003-07-28 09:56:35 +000010592 /* dsl: I think this is intended to be used to support
10593 * '_' in 'vi' command mode during line editing...
10594 * However I implemented that within libedit itself.
10595 */
Denys Vlasenko0a0acb52015-04-18 19:36:38 +020010596 setvar0("_", lastarg);
Denis Vlasenko6514c5e2008-07-24 13:41:37 +000010597 }
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +020010598
10599 return status;
Eric Andersenc470f442003-07-28 09:56:35 +000010600}
10601
10602static int
Denys Vlasenko7b3fa1e2016-10-01 15:10:16 +020010603evalbltin(const struct builtincmd *cmd, int argc, char **argv, int flags)
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010604{
Eric Andersenc470f442003-07-28 09:56:35 +000010605 char *volatile savecmdname;
10606 struct jmploc *volatile savehandler;
10607 struct jmploc jmploc;
Denys Vlasenko7b3fa1e2016-10-01 15:10:16 +020010608 int status;
Eric Andersenc470f442003-07-28 09:56:35 +000010609 int i;
10610
10611 savecmdname = commandname;
Denys Vlasenkoa2d121c2016-09-30 11:30:11 +020010612 savehandler = exception_handler;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010613 i = setjmp(jmploc.loc);
10614 if (i)
Eric Andersenc470f442003-07-28 09:56:35 +000010615 goto cmddone;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010616 exception_handler = &jmploc;
Eric Andersenc470f442003-07-28 09:56:35 +000010617 commandname = argv[0];
10618 argptr = argv + 1;
10619 optptr = NULL; /* initialize nextopt */
Denys Vlasenko7b3fa1e2016-10-01 15:10:16 +020010620 if (cmd == EVALCMD)
10621 status = evalcmd(argc, argv, flags);
10622 else
10623 status = (*cmd->builtin)(argc, argv);
Denis Vlasenkob012b102007-02-19 22:43:01 +000010624 flush_stdout_stderr();
Denys Vlasenko7b3fa1e2016-10-01 15:10:16 +020010625 status |= ferror(stdout);
10626 exitstatus = status;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010627 cmddone:
Rob Landleyf296f0b2006-07-06 01:09:21 +000010628 clearerr(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +000010629 commandname = savecmdname;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000010630 exception_handler = savehandler;
Eric Andersenc470f442003-07-28 09:56:35 +000010631
10632 return i;
10633}
10634
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010635static int
10636goodname(const char *p)
Glenn L McGrath16e45d72004-02-04 08:24:39 +000010637{
Denys Vlasenko8b2f13d2010-09-07 12:19:33 +020010638 return endofname(p)[0] == '\0';
Glenn L McGrath16e45d72004-02-04 08:24:39 +000010639}
10640
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010641
Glenn L McGrath50812ff2002-08-23 13:14:48 +000010642/*
10643 * Search for a command. This is called before we fork so that the
10644 * location of the command will be available in the parent as well as
Glenn L McGrath16e45d72004-02-04 08:24:39 +000010645 * the child. The check for "goodname" is an overly conservative
10646 * check that the name will not be subject to expansion.
Glenn L McGrath50812ff2002-08-23 13:14:48 +000010647 */
Eric Andersenc470f442003-07-28 09:56:35 +000010648static void
10649prehash(union node *n)
Glenn L McGrath50812ff2002-08-23 13:14:48 +000010650{
10651 struct cmdentry entry;
10652
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000010653 if (n->type == NCMD && n->ncmd.args && goodname(n->ncmd.args->narg.text))
10654 find_command(n->ncmd.args->narg.text, &entry, 0, pathval());
Glenn L McGrath50812ff2002-08-23 13:14:48 +000010655}
10656
Eric Andersencb57d552001-06-28 07:25:16 +000010657
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000010658/* ============ Builtin commands
10659 *
10660 * Builtin commands whose functions are closely tied to evaluation
10661 * are implemented here.
Eric Andersencb57d552001-06-28 07:25:16 +000010662 */
10663
10664/*
Eric Andersencb57d552001-06-28 07:25:16 +000010665 * Handle break and continue commands. Break, continue, and return are
10666 * all handled by setting the evalskip flag. The evaluation routines
10667 * above all check this flag, and if it is set they start skipping
10668 * commands rather than executing them. The variable skipcount is
10669 * the number of loops to break/continue, or the number of function
10670 * levels to return. (The latter is always 1.) It should probably
10671 * be an error to break out of more loops than exist, but it isn't
10672 * in the standard shell so we don't make it one here.
10673 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020010674static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000010675breakcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000010676{
Denis Vlasenko68404f12008-03-17 09:00:54 +000010677 int n = argv[1] ? number(argv[1]) : 1;
Eric Andersencb57d552001-06-28 07:25:16 +000010678
Aaron Lehmann2aef3a62001-12-31 06:03:12 +000010679 if (n <= 0)
Denys Vlasenkoecc2a2e2009-08-29 22:53:41 +020010680 ash_msg_and_raise_error(msg_illnum, argv[1]);
Eric Andersencb57d552001-06-28 07:25:16 +000010681 if (n > loopnest)
10682 n = loopnest;
10683 if (n > 0) {
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000010684 evalskip = (**argv == 'c') ? SKIPCONT : SKIPBREAK;
Eric Andersencb57d552001-06-28 07:25:16 +000010685 skipcount = n;
10686 }
10687 return 0;
10688}
10689
Eric Andersenc470f442003-07-28 09:56:35 +000010690
Denys Vlasenko70392332016-10-27 02:31:55 +020010691/*
Eric Andersen90898442003-08-06 11:20:52 +000010692 * This implements the input routines used by the parser.
Eric Andersencb57d552001-06-28 07:25:16 +000010693 */
Denis Vlasenko99eb8502007-02-23 21:09:49 +000010694
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010695enum {
10696 INPUT_PUSH_FILE = 1,
10697 INPUT_NOFILE_OK = 2,
10698};
Eric Andersencb57d552001-06-28 07:25:16 +000010699
Denis Vlasenkob07a4962008-06-22 13:16:23 +000010700static smallint checkkwd;
Denis Vlasenko99eb8502007-02-23 21:09:49 +000010701/* values of checkkwd variable */
10702#define CHKALIAS 0x1
10703#define CHKKWD 0x2
10704#define CHKNL 0x4
Denys Vlasenkoa7328982017-07-29 19:57:28 +020010705#define CHKEOFMARK 0x8
Denis Vlasenko99eb8502007-02-23 21:09:49 +000010706
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010707/*
10708 * Push a string back onto the input at this current parsefile level.
10709 * We handle aliases this way.
10710 */
10711#if !ENABLE_ASH_ALIAS
10712#define pushstring(s, ap) pushstring(s)
10713#endif
10714static void
10715pushstring(char *s, struct alias *ap)
10716{
10717 struct strpush *sp;
10718 int len;
10719
10720 len = strlen(s);
10721 INT_OFF;
10722 if (g_parsefile->strpush) {
10723 sp = ckzalloc(sizeof(*sp));
10724 sp->prev = g_parsefile->strpush;
10725 } else {
10726 sp = &(g_parsefile->basestrpush);
10727 }
10728 g_parsefile->strpush = sp;
10729 sp->prev_string = g_parsefile->next_to_pgetc;
10730 sp->prev_left_in_line = g_parsefile->left_in_line;
Denys Vlasenko3b4d04b2016-09-29 02:11:19 +020010731 sp->unget = g_parsefile->unget;
10732 memcpy(sp->lastc, g_parsefile->lastc, sizeof(sp->lastc));
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010733#if ENABLE_ASH_ALIAS
10734 sp->ap = ap;
10735 if (ap) {
10736 ap->flag |= ALIASINUSE;
10737 sp->string = s;
10738 }
10739#endif
10740 g_parsefile->next_to_pgetc = s;
10741 g_parsefile->left_in_line = len;
Denys Vlasenko3b4d04b2016-09-29 02:11:19 +020010742 g_parsefile->unget = 0;
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010743 INT_ON;
10744}
10745
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010746static void
10747popstring(void)
Eric Andersenc470f442003-07-28 09:56:35 +000010748{
Denis Vlasenkob07a4962008-06-22 13:16:23 +000010749 struct strpush *sp = g_parsefile->strpush;
Eric Andersenc470f442003-07-28 09:56:35 +000010750
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010751 INT_OFF;
Denis Vlasenko131ae172007-02-18 13:00:19 +000010752#if ENABLE_ASH_ALIAS
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010753 if (sp->ap) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010754 if (g_parsefile->next_to_pgetc[-1] == ' '
10755 || g_parsefile->next_to_pgetc[-1] == '\t'
10756 ) {
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010757 checkkwd |= CHKALIAS;
Glenn L McGrath28939ad2004-07-21 10:20:19 +000010758 }
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010759 if (sp->string != sp->ap->val) {
10760 free(sp->string);
10761 }
10762 sp->ap->flag &= ~ALIASINUSE;
10763 if (sp->ap->flag & ALIASDEAD) {
10764 unalias(sp->ap->name);
10765 }
Glenn L McGrath28939ad2004-07-21 10:20:19 +000010766 }
Denis Vlasenko8e1c7152007-01-22 07:21:38 +000010767#endif
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010768 g_parsefile->next_to_pgetc = sp->prev_string;
10769 g_parsefile->left_in_line = sp->prev_left_in_line;
Denys Vlasenko3b4d04b2016-09-29 02:11:19 +020010770 g_parsefile->unget = sp->unget;
10771 memcpy(g_parsefile->lastc, sp->lastc, sizeof(sp->lastc));
Denis Vlasenkob07a4962008-06-22 13:16:23 +000010772 g_parsefile->strpush = sp->prev;
10773 if (sp != &(g_parsefile->basestrpush))
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010774 free(sp);
10775 INT_ON;
10776}
Denis Vlasenko8e1c7152007-01-22 07:21:38 +000010777
Denis Vlasenkoaa744452007-02-23 01:04:22 +000010778static int
10779preadfd(void)
Eric Andersencb57d552001-06-28 07:25:16 +000010780{
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010781 int nr;
Denis Vlasenko6a0ad252008-07-25 13:34:05 +000010782 char *buf = g_parsefile->buf;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010783
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010784 g_parsefile->next_to_pgetc = buf;
Denis Vlasenko38f63192007-01-22 09:03:07 +000010785#if ENABLE_FEATURE_EDITING
Denys Vlasenko4ac35a32020-11-16 13:09:37 +010010786 /* retry: */
Denys Vlasenko79b3d422010-06-03 04:29:08 +020010787 if (!iflag || g_parsefile->pf_fd != STDIN_FILENO)
Ron Yorston61d6ae22015-04-19 10:50:25 +010010788 nr = nonblock_immune_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1);
Eric Andersenc470f442003-07-28 09:56:35 +000010789 else {
Denys Vlasenko66c5b122011-02-08 05:07:02 +010010790# if ENABLE_ASH_IDLE_TIMEOUT
Denys Vlasenko84ea60e2017-08-02 17:27:28 +020010791 int timeout = -1;
Denys Vlasenko897475a2019-06-01 16:35:09 +020010792 const char *tmout_var = lookupvar("TMOUT");
10793 if (tmout_var) {
10794 timeout = atoi(tmout_var) * 1000;
10795 if (timeout <= 0)
10796 timeout = -1;
Denys Vlasenko66c5b122011-02-08 05:07:02 +010010797 }
Denys Vlasenko84ea60e2017-08-02 17:27:28 +020010798 line_input_state->timeout = timeout;
Denys Vlasenko66c5b122011-02-08 05:07:02 +010010799# endif
Denys Vlasenko8c52f802011-02-04 17:36:21 +010010800# if ENABLE_FEATURE_TAB_COMPLETION
Denis Vlasenko8e1c7152007-01-22 07:21:38 +000010801 line_input_state->path_lookup = pathval();
Denys Vlasenko8c52f802011-02-04 17:36:21 +010010802# endif
Denys Vlasenkoe9ab07c2014-08-13 18:00:08 +020010803 reinit_unicode_for_ash();
Denys Vlasenko84ea60e2017-08-02 17:27:28 +020010804 nr = read_line_input(line_input_state, cmdedit_prompt, buf, IBUFSIZ);
Denis Vlasenko8e1c7152007-01-22 07:21:38 +000010805 if (nr == 0) {
Denys Vlasenko4b89d512016-11-25 03:41:03 +010010806 /* ^C pressed, "convert" to SIGINT */
10807 write(STDOUT_FILENO, "^C", 2);
Denys Vlasenko4ac35a32020-11-16 13:09:37 +010010808 raise(SIGINT);
Denis Vlasenko8e1c7152007-01-22 07:21:38 +000010809 if (trap[SIGINT]) {
Glenn L McGrath16e45d72004-02-04 08:24:39 +000010810 buf[0] = '\n';
Denis Vlasenko8e1c7152007-01-22 07:21:38 +000010811 buf[1] = '\0';
Glenn L McGrath16e45d72004-02-04 08:24:39 +000010812 return 1;
10813 }
Denys Vlasenko8660aeb2016-11-24 17:44:02 +010010814 exitstatus = 128 + SIGINT;
Denys Vlasenko4ac35a32020-11-16 13:09:37 +010010815 return -1;
Eric Andersenc470f442003-07-28 09:56:35 +000010816 }
Denys Vlasenko66c5b122011-02-08 05:07:02 +010010817 if (nr < 0) {
10818 if (errno == 0) {
10819 /* Ctrl+D pressed */
10820 nr = 0;
10821 }
10822# if ENABLE_ASH_IDLE_TIMEOUT
10823 else if (errno == EAGAIN && timeout > 0) {
Denys Vlasenkod60752f2015-10-07 22:42:45 +020010824 puts("\007timed out waiting for input: auto-logout");
Denys Vlasenko66c5b122011-02-08 05:07:02 +010010825 exitshell();
10826 }
10827# endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000010828 }
Eric Andersencb57d552001-06-28 07:25:16 +000010829 }
10830#else
Ron Yorston61d6ae22015-04-19 10:50:25 +010010831 nr = nonblock_immune_read(g_parsefile->pf_fd, buf, IBUFSIZ - 1);
Eric Andersencb57d552001-06-28 07:25:16 +000010832#endif
10833
Denys Vlasenko80c5b682011-05-08 21:21:10 +020010834#if 0 /* disabled: nonblock_immune_read() handles this problem */
Eric Andersencb57d552001-06-28 07:25:16 +000010835 if (nr < 0) {
Eric Andersencb57d552001-06-28 07:25:16 +000010836 if (parsefile->fd == 0 && errno == EWOULDBLOCK) {
Denis Vlasenkod37f2222007-08-19 13:42:08 +000010837 int flags = fcntl(0, F_GETFL);
Denis Vlasenko9cb220b2007-12-09 10:03:28 +000010838 if (flags >= 0 && (flags & O_NONBLOCK)) {
10839 flags &= ~O_NONBLOCK;
Eric Andersencb57d552001-06-28 07:25:16 +000010840 if (fcntl(0, F_SETFL, flags) >= 0) {
10841 out2str("sh: turning off NDELAY mode\n");
10842 goto retry;
10843 }
10844 }
10845 }
10846 }
Denis Vlasenkoe376d452008-02-20 22:23:24 +000010847#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010848 return nr;
10849}
10850
10851/*
10852 * Refill the input buffer and return the next input character:
10853 *
10854 * 1) If a string was pushed back on the input, pop it;
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010855 * 2) If an EOF was pushed back (g_parsefile->left_in_line < -BIGNUM)
10856 * or we are reading from a string so we can't refill the buffer,
10857 * return EOF.
Denys Vlasenko883cea42009-07-11 15:31:59 +020010858 * 3) If there is more stuff in this buffer, use it else call read to fill it.
Eric Andersencb57d552001-06-28 07:25:16 +000010859 * 4) Process input up to the next newline, deleting nul characters.
10860 */
Denis Vlasenko727752d2008-11-28 03:41:47 +000010861//#define pgetc_debug(...) bb_error_msg(__VA_ARGS__)
10862#define pgetc_debug(...) ((void)0)
Denys Vlasenko3b4d04b2016-09-29 02:11:19 +020010863static int pgetc(void);
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010864static int
Eric Andersenc470f442003-07-28 09:56:35 +000010865preadbuffer(void)
Eric Andersencb57d552001-06-28 07:25:16 +000010866{
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000010867 char *q;
Eric Andersencb57d552001-06-28 07:25:16 +000010868 int more;
Eric Andersencb57d552001-06-28 07:25:16 +000010869
Denys Vlasenko3b4d04b2016-09-29 02:11:19 +020010870 if (g_parsefile->strpush) {
Denis Vlasenko131ae172007-02-18 13:00:19 +000010871#if ENABLE_ASH_ALIAS
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010872 if (g_parsefile->left_in_line == -1
10873 && g_parsefile->strpush->ap
10874 && g_parsefile->next_to_pgetc[-1] != ' '
10875 && g_parsefile->next_to_pgetc[-1] != '\t'
Denis Vlasenko16898402008-11-25 01:34:52 +000010876 ) {
Denis Vlasenko727752d2008-11-28 03:41:47 +000010877 pgetc_debug("preadbuffer PEOA");
Eric Andersencb57d552001-06-28 07:25:16 +000010878 return PEOA;
10879 }
Eric Andersen2870d962001-07-02 17:27:21 +000010880#endif
Eric Andersencb57d552001-06-28 07:25:16 +000010881 popstring();
Denys Vlasenko3b4d04b2016-09-29 02:11:19 +020010882 return pgetc();
Eric Andersencb57d552001-06-28 07:25:16 +000010883 }
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010884 /* on both branches above g_parsefile->left_in_line < 0.
Denis Vlasenko727752d2008-11-28 03:41:47 +000010885 * "pgetc" needs refilling.
10886 */
10887
Denis Vlasenkoe27dafd2008-11-28 04:01:03 +000010888 /* -90 is our -BIGNUM. Below we use -99 to mark "EOF on read",
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010889 * pungetc() may increment it a few times.
Denis Vlasenkoe27dafd2008-11-28 04:01:03 +000010890 * Assuming it won't increment it to less than -90.
Denis Vlasenko727752d2008-11-28 03:41:47 +000010891 */
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010892 if (g_parsefile->left_in_line < -90 || g_parsefile->buf == NULL) {
Denis Vlasenko727752d2008-11-28 03:41:47 +000010893 pgetc_debug("preadbuffer PEOF1");
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010894 /* even in failure keep left_in_line and next_to_pgetc
10895 * in lock step, for correct multi-layer pungetc.
10896 * left_in_line was decremented before preadbuffer(),
10897 * must inc next_to_pgetc: */
10898 g_parsefile->next_to_pgetc++;
Eric Andersencb57d552001-06-28 07:25:16 +000010899 return PEOF;
Denis Vlasenko727752d2008-11-28 03:41:47 +000010900 }
Eric Andersencb57d552001-06-28 07:25:16 +000010901
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010902 more = g_parsefile->left_in_buffer;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000010903 if (more <= 0) {
Denis Vlasenko727752d2008-11-28 03:41:47 +000010904 flush_stdout_stderr();
Denis Vlasenko5cedb752007-02-18 19:56:41 +000010905 again:
10906 more = preadfd();
10907 if (more <= 0) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010908 /* don't try reading again */
10909 g_parsefile->left_in_line = -99;
Denis Vlasenko727752d2008-11-28 03:41:47 +000010910 pgetc_debug("preadbuffer PEOF2");
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010911 g_parsefile->next_to_pgetc++;
Eric Andersencb57d552001-06-28 07:25:16 +000010912 return PEOF;
10913 }
10914 }
10915
Denis Vlasenko727752d2008-11-28 03:41:47 +000010916 /* Find out where's the end of line.
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010917 * Set g_parsefile->left_in_line
10918 * and g_parsefile->left_in_buffer acordingly.
Denis Vlasenko727752d2008-11-28 03:41:47 +000010919 * NUL chars are deleted.
10920 */
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010921 q = g_parsefile->next_to_pgetc;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000010922 for (;;) {
Denis Vlasenko727752d2008-11-28 03:41:47 +000010923 char c;
Eric Andersencb57d552001-06-28 07:25:16 +000010924
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000010925 more--;
Eric Andersenc470f442003-07-28 09:56:35 +000010926
Denis Vlasenko727752d2008-11-28 03:41:47 +000010927 c = *q;
10928 if (c == '\0') {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000010929 memmove(q, q + 1, more);
Denis Vlasenko727752d2008-11-28 03:41:47 +000010930 } else {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000010931 q++;
10932 if (c == '\n') {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010933 g_parsefile->left_in_line = q - g_parsefile->next_to_pgetc - 1;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000010934 break;
10935 }
Eric Andersencb57d552001-06-28 07:25:16 +000010936 }
10937
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000010938 if (more <= 0) {
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010939 g_parsefile->left_in_line = q - g_parsefile->next_to_pgetc - 1;
10940 if (g_parsefile->left_in_line < 0)
Eric Andersencb57d552001-06-28 07:25:16 +000010941 goto again;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000010942 break;
Eric Andersencb57d552001-06-28 07:25:16 +000010943 }
10944 }
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010945 g_parsefile->left_in_buffer = more;
Eric Andersencb57d552001-06-28 07:25:16 +000010946
Eric Andersencb57d552001-06-28 07:25:16 +000010947 if (vflag) {
Denis Vlasenko727752d2008-11-28 03:41:47 +000010948 char save = *q;
10949 *q = '\0';
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010950 out2str(g_parsefile->next_to_pgetc);
Denis Vlasenko727752d2008-11-28 03:41:47 +000010951 *q = save;
Eric Andersencb57d552001-06-28 07:25:16 +000010952 }
10953
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010954 pgetc_debug("preadbuffer at %d:%p'%s'",
10955 g_parsefile->left_in_line,
10956 g_parsefile->next_to_pgetc,
10957 g_parsefile->next_to_pgetc);
Denys Vlasenkocd716832009-11-28 22:14:02 +010010958 return (unsigned char)*g_parsefile->next_to_pgetc++;
Eric Andersencb57d552001-06-28 07:25:16 +000010959}
10960
Denys Vlasenkoce332a22016-10-02 23:47:34 +020010961static void
10962nlprompt(void)
10963{
Roberto A. Fogliettae0bf3df2021-09-07 01:19:31 +020010964 if (trap_depth == 0)
10965 g_parsefile->linno++;
Denys Vlasenkoce332a22016-10-02 23:47:34 +020010966 setprompt_if(doprompt, 2);
10967}
10968static void
10969nlnoprompt(void)
10970{
Roberto A. Fogliettae0bf3df2021-09-07 01:19:31 +020010971 if (trap_depth == 0)
10972 g_parsefile->linno++;
Denys Vlasenkoce332a22016-10-02 23:47:34 +020010973 needprompt = doprompt;
10974}
10975
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010976static int
10977pgetc(void)
10978{
Denys Vlasenko3b4d04b2016-09-29 02:11:19 +020010979 int c;
10980
10981 pgetc_debug("pgetc at %d:%p'%s'",
Denis Vlasenko41eb3002008-11-28 03:42:31 +000010982 g_parsefile->left_in_line,
10983 g_parsefile->next_to_pgetc,
10984 g_parsefile->next_to_pgetc);
Denys Vlasenko3b4d04b2016-09-29 02:11:19 +020010985 if (g_parsefile->unget)
10986 return g_parsefile->lastc[--g_parsefile->unget];
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +000010987
Denys Vlasenko3b4d04b2016-09-29 02:11:19 +020010988 if (--g_parsefile->left_in_line >= 0)
Denys Vlasenko2fe66b12016-12-12 17:39:12 +010010989 c = (unsigned char)*g_parsefile->next_to_pgetc++;
Denys Vlasenko3b4d04b2016-09-29 02:11:19 +020010990 else
10991 c = preadbuffer();
10992
10993 g_parsefile->lastc[1] = g_parsefile->lastc[0];
10994 g_parsefile->lastc[0] = c;
10995
10996 return c;
10997}
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010998
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000010999#if ENABLE_ASH_ALIAS
11000static int
Denys Vlasenko2ce42e92009-11-29 02:18:13 +010011001pgetc_without_PEOA(void)
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000011002{
11003 int c;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000011004 do {
Denys Vlasenko3b4d04b2016-09-29 02:11:19 +020011005 pgetc_debug("pgetc at %d:%p'%s'",
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011006 g_parsefile->left_in_line,
11007 g_parsefile->next_to_pgetc,
11008 g_parsefile->next_to_pgetc);
Denys Vlasenko3b4d04b2016-09-29 02:11:19 +020011009 c = pgetc();
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000011010 } while (c == PEOA);
11011 return c;
11012}
11013#else
Denys Vlasenko2ce42e92009-11-29 02:18:13 +010011014# define pgetc_without_PEOA() pgetc()
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000011015#endif
11016
11017/*
Denys Vlasenko3b4d04b2016-09-29 02:11:19 +020011018 * Undo a call to pgetc. Only two characters may be pushed back.
Eric Andersenc470f442003-07-28 09:56:35 +000011019 * PEOF may be pushed back.
11020 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011021static void
Eric Andersenc470f442003-07-28 09:56:35 +000011022pungetc(void)
11023{
Denys Vlasenko3b4d04b2016-09-29 02:11:19 +020011024 g_parsefile->unget++;
Eric Andersencb57d552001-06-28 07:25:16 +000011025}
11026
Denys Vlasenko73c3e072016-09-29 17:17:04 +020011027/* This one eats backslash+newline */
11028static int
11029pgetc_eatbnl(void)
11030{
11031 int c;
11032
11033 while ((c = pgetc()) == '\\') {
11034 if (pgetc() != '\n') {
11035 pungetc();
11036 break;
11037 }
11038
Denys Vlasenkoce332a22016-10-02 23:47:34 +020011039 nlprompt();
Denys Vlasenko73c3e072016-09-29 17:17:04 +020011040 }
11041
11042 return c;
11043}
11044
Denys Vlasenko216913c2018-04-02 12:35:04 +020011045struct synstack {
11046 smalluint syntax;
11047 uint8_t innerdq :1;
11048 uint8_t varpushed :1;
11049 uint8_t dblquote :1;
11050 int varnest; /* levels of variables expansion */
11051 int dqvarnest; /* levels of variables expansion within double quotes */
11052 int parenlevel; /* levels of parens in arithmetic */
11053 struct synstack *prev;
11054 struct synstack *next;
11055};
11056
Denys Vlasenkof7eea8c2020-02-14 16:16:34 +010011057static int
11058pgetc_top(struct synstack *stack)
11059{
11060 return stack->syntax == SQSYNTAX ? pgetc() : pgetc_eatbnl();
11061}
11062
Denys Vlasenko216913c2018-04-02 12:35:04 +020011063static void
11064synstack_push(struct synstack **stack, struct synstack *next, int syntax)
11065{
11066 memset(next, 0, sizeof(*next));
11067 next->syntax = syntax;
11068 next->next = *stack;
11069 (*stack)->prev = next;
11070 *stack = next;
11071}
11072
11073static ALWAYS_INLINE void
11074synstack_pop(struct synstack **stack)
11075{
11076 *stack = (*stack)->next;
11077}
11078
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000011079/*
11080 * To handle the "." command, a stack of input files is used. Pushfile
11081 * adds a new entry to the stack and popfile restores the previous level.
11082 */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011083static void
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000011084pushfile(void)
Eric Andersenc470f442003-07-28 09:56:35 +000011085{
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000011086 struct parsefile *pf;
11087
Denis Vlasenko597906c2008-02-20 16:38:54 +000011088 pf = ckzalloc(sizeof(*pf));
Denis Vlasenkob07a4962008-06-22 13:16:23 +000011089 pf->prev = g_parsefile;
Denys Vlasenko79b3d422010-06-03 04:29:08 +020011090 pf->pf_fd = -1;
Denis Vlasenko597906c2008-02-20 16:38:54 +000011091 /*pf->strpush = NULL; - ckzalloc did it */
11092 /*pf->basestrpush.prev = NULL;*/
Denys Vlasenko3b4d04b2016-09-29 02:11:19 +020011093 /*pf->unget = 0;*/
Denis Vlasenkob07a4962008-06-22 13:16:23 +000011094 g_parsefile = pf;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000011095}
11096
11097static void
11098popfile(void)
11099{
Denis Vlasenkob07a4962008-06-22 13:16:23 +000011100 struct parsefile *pf = g_parsefile;
Eric Andersenc470f442003-07-28 09:56:35 +000011101
Denys Vlasenko493b9ca2016-10-30 18:27:14 +010011102 if (pf == &basepf)
11103 return;
11104
Denis Vlasenkob012b102007-02-19 22:43:01 +000011105 INT_OFF;
Denys Vlasenko79b3d422010-06-03 04:29:08 +020011106 if (pf->pf_fd >= 0)
11107 close(pf->pf_fd);
Denis Vlasenko60818682007-09-28 22:07:23 +000011108 free(pf->buf);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000011109 while (pf->strpush)
11110 popstring();
Denis Vlasenkob07a4962008-06-22 13:16:23 +000011111 g_parsefile = pf->prev;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000011112 free(pf);
Denis Vlasenkob012b102007-02-19 22:43:01 +000011113 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000011114}
11115
Denys Vlasenko1c5eb882018-08-05 17:07:26 +020011116static void
11117unwindfiles(struct parsefile *stop)
11118{
11119 while (g_parsefile != stop)
11120 popfile();
11121}
11122
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000011123/*
11124 * Return to top level.
11125 */
11126static void
11127popallfiles(void)
11128{
Denys Vlasenko1c5eb882018-08-05 17:07:26 +020011129 unwindfiles(&basepf);
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000011130}
11131
11132/*
11133 * Close the file(s) that the shell is reading commands from. Called
11134 * after a fork is done.
11135 */
11136static void
11137closescript(void)
11138{
11139 popallfiles();
Denys Vlasenko79b3d422010-06-03 04:29:08 +020011140 if (g_parsefile->pf_fd > 0) {
11141 close(g_parsefile->pf_fd);
11142 g_parsefile->pf_fd = 0;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000011143 }
11144}
11145
11146/*
11147 * Like setinputfile, but takes an open file descriptor. Call this with
11148 * interrupts off.
11149 */
11150static void
11151setinputfd(int fd, int push)
11152{
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000011153 if (push) {
11154 pushfile();
Denis Vlasenko727752d2008-11-28 03:41:47 +000011155 g_parsefile->buf = NULL;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000011156 }
Denys Vlasenko79b3d422010-06-03 04:29:08 +020011157 g_parsefile->pf_fd = fd;
Denis Vlasenkob07a4962008-06-22 13:16:23 +000011158 if (g_parsefile->buf == NULL)
11159 g_parsefile->buf = ckmalloc(IBUFSIZ);
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011160 g_parsefile->left_in_buffer = 0;
11161 g_parsefile->left_in_line = 0;
11162 g_parsefile->linno = 1;
Denis Vlasenko4d2183b2007-02-23 01:05:38 +000011163}
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011164
Eric Andersenc470f442003-07-28 09:56:35 +000011165/*
11166 * Set the input to take input from a file. If push is set, push the
11167 * old input onto the stack first.
11168 */
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000011169static int
11170setinputfile(const char *fname, int flags)
Eric Andersenc470f442003-07-28 09:56:35 +000011171{
11172 int fd;
Eric Andersenc470f442003-07-28 09:56:35 +000011173
Denis Vlasenkob012b102007-02-19 22:43:01 +000011174 INT_OFF;
Denys Vlasenko60fb98e2018-03-30 22:15:14 +020011175 fd = open(fname, O_RDONLY | O_CLOEXEC);
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011176 if (fd < 0) {
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000011177 if (flags & INPUT_NOFILE_OK)
11178 goto out;
Denys Vlasenkob7adf7a2016-10-25 17:00:13 +020011179 exitstatus = 127;
Johannes Schindelin20a63b22017-08-22 22:03:17 +020011180 ash_msg_and_raise_perror("can't open '%s'", fname);
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000011181 }
Denys Vlasenko64774602016-10-26 15:24:30 +020011182 if (fd < 10)
11183 fd = savefd(fd);
Denys Vlasenko60fb98e2018-03-30 22:15:14 +020011184 else if (O_CLOEXEC == 0) /* old libc */
Denys Vlasenkoe19923f2016-10-26 15:38:44 +020011185 close_on_exec_on(fd);
Denys Vlasenko60fb98e2018-03-30 22:15:14 +020011186
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000011187 setinputfd(fd, flags & INPUT_PUSH_FILE);
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011188 out:
Denis Vlasenkob012b102007-02-19 22:43:01 +000011189 INT_ON;
"Vladimir N. Oleynik"fb29b462006-01-15 14:21:01 +000011190 return fd;
Eric Andersenc470f442003-07-28 09:56:35 +000011191}
11192
Eric Andersencb57d552001-06-28 07:25:16 +000011193/*
11194 * Like setinputfile, but takes input from a string.
11195 */
Eric Andersenc470f442003-07-28 09:56:35 +000011196static void
11197setinputstring(char *string)
Eric Andersen62483552001-07-10 06:09:16 +000011198{
Denis Vlasenkob012b102007-02-19 22:43:01 +000011199 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +000011200 pushfile();
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011201 g_parsefile->next_to_pgetc = string;
11202 g_parsefile->left_in_line = strlen(string);
Denis Vlasenkob07a4962008-06-22 13:16:23 +000011203 g_parsefile->buf = NULL;
Denis Vlasenko41eb3002008-11-28 03:42:31 +000011204 g_parsefile->linno = 1;
Denis Vlasenkob012b102007-02-19 22:43:01 +000011205 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +000011206}
11207
11208
Denys Vlasenko70392332016-10-27 02:31:55 +020011209/*
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011210 * Routines to check for mail.
Eric Andersencb57d552001-06-28 07:25:16 +000011211 */
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +000011212
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011213#if ENABLE_ASH_MAIL
Eric Andersencb57d552001-06-28 07:25:16 +000011214
Denys Vlasenko23841622015-10-09 15:52:03 +020011215/* Hash of mtimes of mailboxes */
11216static unsigned mailtime_hash;
Eric Andersenc470f442003-07-28 09:56:35 +000011217/* Set if MAIL or MAILPATH is changed. */
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011218static smallint mail_var_path_changed;
Eric Andersencb57d552001-06-28 07:25:16 +000011219
Eric Andersencb57d552001-06-28 07:25:16 +000011220/*
Eric Andersenc470f442003-07-28 09:56:35 +000011221 * Print appropriate message(s) if mail has arrived.
11222 * If mail_var_path_changed is set,
11223 * then the value of MAIL has mail_var_path_changed,
11224 * so we just update the values.
Eric Andersencb57d552001-06-28 07:25:16 +000011225 */
Eric Andersenc470f442003-07-28 09:56:35 +000011226static void
11227chkmail(void)
Eric Andersencb57d552001-06-28 07:25:16 +000011228{
Eric Andersencb57d552001-06-28 07:25:16 +000011229 const char *mpath;
11230 char *p;
11231 char *q;
Denys Vlasenko23841622015-10-09 15:52:03 +020011232 unsigned new_hash;
Eric Andersencb57d552001-06-28 07:25:16 +000011233 struct stackmark smark;
11234 struct stat statb;
11235
Eric Andersencb57d552001-06-28 07:25:16 +000011236 setstackmark(&smark);
Eric Andersenc470f442003-07-28 09:56:35 +000011237 mpath = mpathset() ? mpathval() : mailval();
Denys Vlasenko23841622015-10-09 15:52:03 +020011238 new_hash = 0;
11239 for (;;) {
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +010011240 int len;
11241
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +010011242 len = padvance_magic(&mpath, nullstr, 2);
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +010011243 if (!len)
11244 break;
11245 p = stackblock();
Eric Andersencb57d552001-06-28 07:25:16 +000011246 break;
11247 if (*p == '\0')
11248 continue;
Denis Vlasenkof7d56652008-03-25 05:51:41 +000011249 for (q = p; *q; q++)
11250 continue;
Denis Vlasenkoa7189f02006-11-17 20:29:00 +000011251#if DEBUG
Eric Andersencb57d552001-06-28 07:25:16 +000011252 if (q[-1] != '/')
11253 abort();
11254#endif
Eric Andersenc470f442003-07-28 09:56:35 +000011255 q[-1] = '\0'; /* delete trailing '/' */
11256 if (stat(p, &statb) < 0) {
Eric Andersenc470f442003-07-28 09:56:35 +000011257 continue;
Eric Andersencb57d552001-06-28 07:25:16 +000011258 }
Denys Vlasenko23841622015-10-09 15:52:03 +020011259 /* Very simplistic "hash": just a sum of all mtimes */
11260 new_hash += (unsigned)statb.st_mtime;
11261 }
11262 if (!mail_var_path_changed && mailtime_hash != new_hash) {
Denys Vlasenko4cd99e72015-10-09 16:02:53 +020011263 if (mailtime_hash != 0)
11264 out2str("you have mail\n");
Denys Vlasenko23841622015-10-09 15:52:03 +020011265 mailtime_hash = new_hash;
Eric Andersencb57d552001-06-28 07:25:16 +000011266 }
Eric Andersenc470f442003-07-28 09:56:35 +000011267 mail_var_path_changed = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000011268 popstackmark(&smark);
11269}
Eric Andersencb57d552001-06-28 07:25:16 +000011270
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020011271static void FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000011272changemail(const char *val UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000011273{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011274 mail_var_path_changed = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000011275}
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +000011276
Denis Vlasenko131ae172007-02-18 13:00:19 +000011277#endif /* ASH_MAIL */
Eric Andersenc470f442003-07-28 09:56:35 +000011278
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011279
11280/* ============ ??? */
11281
Eric Andersencb57d552001-06-28 07:25:16 +000011282/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000011283 * Set the shell parameters.
Eric Andersencb57d552001-06-28 07:25:16 +000011284 */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000011285static void
11286setparam(char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000011287{
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000011288 char **newparam;
11289 char **ap;
11290 int nparam;
Eric Andersencb57d552001-06-28 07:25:16 +000011291
Denis Vlasenkof7d56652008-03-25 05:51:41 +000011292 for (nparam = 0; argv[nparam]; nparam++)
11293 continue;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000011294 ap = newparam = ckmalloc((nparam + 1) * sizeof(*ap));
11295 while (*argv) {
11296 *ap++ = ckstrdup(*argv++);
Eric Andersencb57d552001-06-28 07:25:16 +000011297 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000011298 *ap = NULL;
11299 freeparam(&shellparam);
Denis Vlasenko01631112007-12-16 17:20:38 +000011300 shellparam.malloced = 1;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000011301 shellparam.nparam = nparam;
11302 shellparam.p = newparam;
11303#if ENABLE_ASH_GETOPTS
11304 shellparam.optind = 1;
11305 shellparam.optoff = -1;
11306#endif
Eric Andersencb57d552001-06-28 07:25:16 +000011307}
11308
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011309/*
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000011310 * Process shell options. The global variable argptr contains a pointer
11311 * to the argument list; we advance it past the options.
Denis Vlasenko94e87bc2008-02-14 16:51:58 +000011312 *
11313 * SUSv3 section 2.8.1 "Consequences of Shell Errors" says:
11314 * For a non-interactive shell, an error condition encountered
11315 * by a special built-in ... shall cause the shell to write a diagnostic message
11316 * to standard error and exit as shown in the following table:
Denis Vlasenko56244732008-02-17 15:14:04 +000011317 * Error Special Built-In
Denis Vlasenko94e87bc2008-02-14 16:51:58 +000011318 * ...
11319 * Utility syntax error (option or operand error) Shall exit
11320 * ...
11321 * However, in bug 1142 (http://busybox.net/bugs/view.php?id=1142)
11322 * we see that bash does not do that (set "finishes" with error code 1 instead,
11323 * and shell continues), and people rely on this behavior!
11324 * Testcase:
11325 * set -o barfoo 2>/dev/null
11326 * echo $?
11327 *
11328 * Oh well. Let's mimic that.
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000011329 */
Denis Vlasenko28bf6712008-02-14 15:01:47 +000011330static int
Denis Vlasenkodddfaff2008-05-06 15:30:27 +000011331plus_minus_o(char *name, int val)
Eric Andersen62483552001-07-10 06:09:16 +000011332{
11333 int i;
11334
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011335 if (name) {
11336 for (i = 0; i < NOPTS; i++) {
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000011337 if (strcmp(name, optnames(i)) == 0) {
Eric Andersenc470f442003-07-28 09:56:35 +000011338 optlist[i] = val;
Denis Vlasenko28bf6712008-02-14 15:01:47 +000011339 return 0;
Eric Andersen62483552001-07-10 06:09:16 +000011340 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011341 }
Denis Vlasenkodddfaff2008-05-06 15:30:27 +000011342 ash_msg("illegal option %co %s", val ? '-' : '+', name);
Denis Vlasenko28bf6712008-02-14 15:01:47 +000011343 return 1;
Eric Andersen62483552001-07-10 06:09:16 +000011344 }
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000011345 for (i = 0; i < NOPTS; i++) {
Denys Vlasenko2f9c1242019-08-02 16:43:36 +020011346 if (optnames(i)[0] == '\0')
11347 continue;
Denis Vlasenkodddfaff2008-05-06 15:30:27 +000011348 if (val) {
11349 out1fmt("%-16s%s\n", optnames(i), optlist[i] ? "on" : "off");
11350 } else {
11351 out1fmt("set %co %s\n", optlist[i] ? '-' : '+', optnames(i));
11352 }
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000011353 }
Denis Vlasenko28bf6712008-02-14 15:01:47 +000011354 return 0;
Eric Andersen62483552001-07-10 06:09:16 +000011355}
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011356static void
11357setoption(int flag, int val)
11358{
11359 int i;
11360
11361 for (i = 0; i < NOPTS; i++) {
Denys Vlasenkof3634582019-06-03 12:21:04 +020011362 if (optletters(i) == flag && optnames(i)[0] != '\0') {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011363 optlist[i] = val;
11364 return;
11365 }
11366 }
Denis Vlasenkodddfaff2008-05-06 15:30:27 +000011367 ash_msg_and_raise_error("illegal option %c%c", val ? '-' : '+', flag);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011368 /* NOTREACHED */
11369}
Denys Vlasenko897475a2019-06-01 16:35:09 +020011370/* If login_sh is not NULL, we are called to parse command line opts,
11371 * not "set -opts"
11372 */
Denis Vlasenko28bf6712008-02-14 15:01:47 +000011373static int
Denys Vlasenko897475a2019-06-01 16:35:09 +020011374options(int *login_sh)
Eric Andersencb57d552001-06-28 07:25:16 +000011375{
11376 char *p;
11377 int val;
11378 int c;
11379
Denys Vlasenko897475a2019-06-01 16:35:09 +020011380 if (login_sh)
Eric Andersencb57d552001-06-28 07:25:16 +000011381 minusc = NULL;
11382 while ((p = *argptr) != NULL) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011383 c = *p++;
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +000011384 if (c != '-' && c != '+')
11385 break;
11386 argptr++;
11387 val = 0; /* val = 0 if c == '+' */
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011388 if (c == '-') {
Eric Andersencb57d552001-06-28 07:25:16 +000011389 val = 1;
Denis Vlasenko9f739442006-12-16 23:49:13 +000011390 if (p[0] == '\0' || LONE_DASH(p)) {
Denys Vlasenko897475a2019-06-01 16:35:09 +020011391 if (!login_sh) {
Eric Andersen2870d962001-07-02 17:27:21 +000011392 /* "-" means turn off -x and -v */
11393 if (p[0] == '\0')
11394 xflag = vflag = 0;
11395 /* "--" means reset params */
11396 else if (*argptr == NULL)
Eric Andersencb57d552001-06-28 07:25:16 +000011397 setparam(argptr);
Eric Andersen2870d962001-07-02 17:27:21 +000011398 }
Denys Vlasenkob0b83432011-03-07 12:34:59 +010011399 break; /* "-" or "--" terminates options */
Eric Andersencb57d552001-06-28 07:25:16 +000011400 }
Eric Andersencb57d552001-06-28 07:25:16 +000011401 }
Denis Vlasenko8fdc4b72007-07-14 11:33:10 +000011402 /* first char was + or - */
Eric Andersencb57d552001-06-28 07:25:16 +000011403 while ((c = *p++) != '\0') {
Denys Vlasenko897475a2019-06-01 16:35:09 +020011404 if (login_sh) {
11405 /* bash 3.2 indeed handles -c CMD and +c CMD the same */
11406 if (c == 'c') {
11407 minusc = p; /* command is after shell args */
Denys Vlasenkof3634582019-06-03 12:21:04 +020011408 cflag = 1;
11409 continue;
11410 }
11411 if (c == 's') { /* -s, +s */
11412 sflag = 1;
11413 continue;
11414 }
11415 if (c == 'i') { /* -i, +i */
11416 iflag = 1;
Denys Vlasenko897475a2019-06-01 16:35:09 +020011417 continue;
11418 }
11419 if (c == 'l') {
11420 *login_sh = 1; /* -l or +l == --login */
11421 continue;
11422 }
11423 /* bash does not accept +-login, we also won't */
11424 if (val && (c == '-')) { /* long options */
11425 if (strcmp(p, "login") == 0) {
11426 *login_sh = 1;
11427 }
11428 break;
11429 }
11430 }
11431 if (c == 'o') {
Denis Vlasenkodddfaff2008-05-06 15:30:27 +000011432 if (plus_minus_o(*argptr, val)) {
Denis Vlasenko28bf6712008-02-14 15:01:47 +000011433 /* it already printed err message */
11434 return 1; /* error */
11435 }
Eric Andersencb57d552001-06-28 07:25:16 +000011436 if (*argptr)
11437 argptr++;
11438 } else {
11439 setoption(c, val);
11440 }
11441 }
11442 }
Denis Vlasenko28bf6712008-02-14 15:01:47 +000011443 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +000011444}
11445
Eric Andersencb57d552001-06-28 07:25:16 +000011446/*
Eric Andersencb57d552001-06-28 07:25:16 +000011447 * The shift builtin command.
11448 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020011449static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000011450shiftcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000011451{
11452 int n;
11453 char **ap1, **ap2;
11454
11455 n = 1;
Denis Vlasenko68404f12008-03-17 09:00:54 +000011456 if (argv[1])
Eric Andersencb57d552001-06-28 07:25:16 +000011457 n = number(argv[1]);
11458 if (n > shellparam.nparam)
Ingo van Lil9c8e94b2018-01-05 15:04:23 +010011459 return 1;
Denis Vlasenkob012b102007-02-19 22:43:01 +000011460 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +000011461 shellparam.nparam -= n;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011462 for (ap1 = shellparam.p; --n >= 0; ap1++) {
Denis Vlasenko01631112007-12-16 17:20:38 +000011463 if (shellparam.malloced)
Denis Vlasenkob012b102007-02-19 22:43:01 +000011464 free(*ap1);
Eric Andersencb57d552001-06-28 07:25:16 +000011465 }
11466 ap2 = shellparam.p;
Denis Vlasenkof7d56652008-03-25 05:51:41 +000011467 while ((*ap2++ = *ap1++) != NULL)
11468 continue;
Denis Vlasenko131ae172007-02-18 13:00:19 +000011469#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +000011470 shellparam.optind = 1;
11471 shellparam.optoff = -1;
Eric Andersenc470f442003-07-28 09:56:35 +000011472#endif
Denis Vlasenkob012b102007-02-19 22:43:01 +000011473 INT_ON;
Eric Andersencb57d552001-06-28 07:25:16 +000011474 return 0;
11475}
11476
Eric Andersencb57d552001-06-28 07:25:16 +000011477/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011478 * POSIX requires that 'set' (but not export or readonly) output the
11479 * variables in lexicographic order - by the locale's collating order (sigh).
11480 * Maybe we could keep them in an ordered balanced binary tree
11481 * instead of hashed lists.
11482 * For now just roll 'em through qsort for printing...
11483 */
11484static int
11485showvars(const char *sep_prefix, int on, int off)
11486{
11487 const char *sep;
11488 char **ep, **epend;
11489
Denys Vlasenkoa5060b82017-11-03 14:16:25 +010011490 ep = listvars(on, off, /*strlist:*/ NULL, &epend);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011491 qsort(ep, epend - ep, sizeof(char *), vpcmp);
11492
Denis Vlasenko2de3d9f2007-02-23 21:10:23 +000011493 sep = *sep_prefix ? " " : sep_prefix;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011494
11495 for (; ep < epend; ep++) {
11496 const char *p;
11497 const char *q;
11498
Denys Vlasenko9c143ce2017-11-02 12:56:24 +010011499 p = endofname(*ep);
11500/* Used to have simple "p = strchrnul(*ep, '=')" here instead, but this
11501 * makes "export -p" to have output not suitable for "eval":
11502 * import os
11503 * os.environ["test-test"]="test"
11504 * if os.fork() == 0:
11505 * os.execv("ash", [ 'ash', '-c', 'eval $(export -p); echo OK' ]) # fixes this
11506 * os.execv("ash", [ 'ash', '-c', 'env | grep test-test' ])
11507 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011508 q = nullstr;
Denys Vlasenko9c143ce2017-11-02 12:56:24 +010011509 if (*p == '=')
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011510 q = single_quote(++p);
11511 out1fmt("%s%s%.*s%s\n", sep_prefix, sep, (int)(p - *ep), *ep, q);
11512 }
11513 return 0;
11514}
11515
11516/*
Eric Andersencb57d552001-06-28 07:25:16 +000011517 * The set command builtin.
11518 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020011519static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000011520setcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +000011521{
Denis Vlasenko28bf6712008-02-14 15:01:47 +000011522 int retval;
11523
Denis Vlasenko68404f12008-03-17 09:00:54 +000011524 if (!argv[1])
Eric Andersenc470f442003-07-28 09:56:35 +000011525 return showvars(nullstr, 0, VUNSET);
Denys Vlasenkob0b83432011-03-07 12:34:59 +010011526
Denis Vlasenkob012b102007-02-19 22:43:01 +000011527 INT_OFF;
Denys Vlasenko897475a2019-06-01 16:35:09 +020011528 retval = options(/*login_sh:*/ NULL);
Denys Vlasenkob0b83432011-03-07 12:34:59 +010011529 if (retval == 0) { /* if no parse error... */
Denis Vlasenko28bf6712008-02-14 15:01:47 +000011530 optschanged();
11531 if (*argptr != NULL) {
11532 setparam(argptr);
11533 }
Eric Andersencb57d552001-06-28 07:25:16 +000011534 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000011535 INT_ON;
Denis Vlasenko28bf6712008-02-14 15:01:47 +000011536 return retval;
Eric Andersencb57d552001-06-28 07:25:16 +000011537}
11538
Denis Vlasenko131ae172007-02-18 13:00:19 +000011539#if ENABLE_ASH_RANDOM_SUPPORT
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020011540static void FAST_FUNC
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000011541change_random(const char *value)
Eric Andersenef02f822004-03-11 13:34:24 +000011542{
Denys Vlasenko3ea2e822009-10-09 20:59:04 +020011543 uint32_t t;
11544
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000011545 if (value == NULL) {
Eric Andersen16767e22004-03-16 05:14:10 +000011546 /* "get", generate */
Denys Vlasenko3ea2e822009-10-09 20:59:04 +020011547 t = next_random(&random_gen);
Eric Andersen16767e22004-03-16 05:14:10 +000011548 /* set without recursion */
Denys Vlasenko8837c5d2010-06-02 12:56:18 +020011549 setvar(vrandom.var_text, utoa(t), VNOFUNC);
Eric Andersen16767e22004-03-16 05:14:10 +000011550 vrandom.flags &= ~VNOFUNC;
11551 } else {
11552 /* set/reset */
Denys Vlasenko3ea2e822009-10-09 20:59:04 +020011553 t = strtoul(value, NULL, 10);
11554 INIT_RANDOM_T(&random_gen, (t ? t : 1), t);
Eric Andersen16767e22004-03-16 05:14:10 +000011555 }
Eric Andersenef02f822004-03-11 13:34:24 +000011556}
Eric Andersen16767e22004-03-16 05:14:10 +000011557#endif
11558
Ron Yorston1d371862019-04-15 10:52:05 +010011559#if BASH_EPOCH_VARS
11560static void FAST_FUNC
11561change_epoch(struct var *vepoch, const char *fmt)
11562{
11563 struct timeval tv;
Denys Vlasenko3c13da32020-12-30 23:48:01 +010011564 char buffer[sizeof("%llu.nnnnnn") + sizeof(long long)*3];
Ron Yorston1d371862019-04-15 10:52:05 +010011565
Denys Vlasenko3c13da32020-12-30 23:48:01 +010011566 xgettimeofday(&tv);
11567 sprintf(buffer, fmt, (unsigned long long)tv.tv_sec, (unsigned)tv.tv_usec);
Ron Yorston1d371862019-04-15 10:52:05 +010011568 setvar(vepoch->var_text, buffer, VNOFUNC);
11569 vepoch->flags &= ~VNOFUNC;
11570}
11571
11572static void FAST_FUNC
11573change_seconds(const char *value UNUSED_PARAM)
11574{
Denys Vlasenko3c13da32020-12-30 23:48:01 +010011575 change_epoch(&vepochs, "%llu");
Ron Yorston1d371862019-04-15 10:52:05 +010011576}
11577
11578static void FAST_FUNC
11579change_realtime(const char *value UNUSED_PARAM)
11580{
Denys Vlasenko3c13da32020-12-30 23:48:01 +010011581 change_epoch(&vepochr, "%llu.%06u");
Ron Yorston1d371862019-04-15 10:52:05 +010011582}
11583#endif
11584
Denis Vlasenko131ae172007-02-18 13:00:19 +000011585#if ENABLE_ASH_GETOPTS
Eric Andersencb57d552001-06-28 07:25:16 +000011586static int
Denys Vlasenko35c2a132016-10-26 17:34:26 +020011587getopts(char *optstr, char *optvar, char **optfirst)
Eric Andersencb57d552001-06-28 07:25:16 +000011588{
11589 char *p, *q;
11590 char c = '?';
11591 int done = 0;
Denys Vlasenko9c541002015-10-07 15:44:36 +020011592 char sbuf[2];
Eric Andersena48b0a32003-10-22 10:56:47 +000011593 char **optnext;
Denys Vlasenkodbef38a2016-10-26 17:54:32 +020011594 int ind = shellparam.optind;
11595 int off = shellparam.optoff;
Eric Andersencb57d552001-06-28 07:25:16 +000011596
Denys Vlasenko9c541002015-10-07 15:44:36 +020011597 sbuf[1] = '\0';
11598
Denys Vlasenkodbef38a2016-10-26 17:54:32 +020011599 shellparam.optind = -1;
11600 optnext = optfirst + ind - 1;
Eric Andersena48b0a32003-10-22 10:56:47 +000011601
Denys Vlasenkodbef38a2016-10-26 17:54:32 +020011602 if (ind <= 1 || off < 0 || (int)strlen(optnext[-1]) < off)
Eric Andersencb57d552001-06-28 07:25:16 +000011603 p = NULL;
Denys Vlasenkodbef38a2016-10-26 17:54:32 +020011604 else
11605 p = optnext[-1] + off;
Eric Andersencb57d552001-06-28 07:25:16 +000011606 if (p == NULL || *p == '\0') {
11607 /* Current word is done, advance */
Eric Andersencb57d552001-06-28 07:25:16 +000011608 p = *optnext;
11609 if (p == NULL || *p != '-' || *++p == '\0') {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011610 atend:
Denys Vlasenko9832bba2017-08-15 15:44:41 +020011611 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +000011612 p = NULL;
11613 done = 1;
11614 goto out;
11615 }
11616 optnext++;
Denis Vlasenko9f739442006-12-16 23:49:13 +000011617 if (LONE_DASH(p)) /* check for "--" */
Eric Andersencb57d552001-06-28 07:25:16 +000011618 goto atend;
11619 }
11620
11621 c = *p++;
Denis Vlasenko2f5d0cd2008-06-23 13:24:19 +000011622 for (q = optstr; *q != c;) {
Eric Andersencb57d552001-06-28 07:25:16 +000011623 if (*q == '\0') {
Denys Vlasenko9832bba2017-08-15 15:44:41 +020011624 /* OPTERR is a bashism */
11625 const char *cp = lookupvar("OPTERR");
11626 if ((cp && LONE_CHAR(cp, '0'))
11627 || (optstr[0] == ':')
11628 ) {
Denys Vlasenko9c541002015-10-07 15:44:36 +020011629 sbuf[0] = c;
11630 /*sbuf[1] = '\0'; - already is */
Denys Vlasenkodbef38a2016-10-26 17:54:32 +020011631 setvar0("OPTARG", sbuf);
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011632 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000011633 fprintf(stderr, "Illegal option -%c\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011634 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +000011635 }
11636 c = '?';
Eric Andersenc470f442003-07-28 09:56:35 +000011637 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +000011638 }
11639 if (*++q == ':')
11640 q++;
11641 }
11642
11643 if (*++q == ':') {
11644 if (*p == '\0' && (p = *optnext) == NULL) {
Denys Vlasenko9832bba2017-08-15 15:44:41 +020011645 /* OPTERR is a bashism */
11646 const char *cp = lookupvar("OPTERR");
11647 if ((cp && LONE_CHAR(cp, '0'))
11648 || (optstr[0] == ':')
11649 ) {
Denys Vlasenko9c541002015-10-07 15:44:36 +020011650 sbuf[0] = c;
11651 /*sbuf[1] = '\0'; - already is */
Denys Vlasenkodbef38a2016-10-26 17:54:32 +020011652 setvar0("OPTARG", sbuf);
Eric Andersencb57d552001-06-28 07:25:16 +000011653 c = ':';
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011654 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000011655 fprintf(stderr, "No arg for -%c option\n", c);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011656 unsetvar("OPTARG");
Eric Andersencb57d552001-06-28 07:25:16 +000011657 c = '?';
11658 }
Eric Andersenc470f442003-07-28 09:56:35 +000011659 goto out;
Eric Andersencb57d552001-06-28 07:25:16 +000011660 }
11661
11662 if (p == *optnext)
11663 optnext++;
Denys Vlasenkodbef38a2016-10-26 17:54:32 +020011664 setvar0("OPTARG", p);
Eric Andersencb57d552001-06-28 07:25:16 +000011665 p = NULL;
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011666 } else
Denys Vlasenkodbef38a2016-10-26 17:54:32 +020011667 setvar0("OPTARG", nullstr);
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011668 out:
Denys Vlasenkodbef38a2016-10-26 17:54:32 +020011669 ind = optnext - optfirst + 1;
11670 setvar("OPTIND", itoa(ind), VNOFUNC);
Denys Vlasenko9c541002015-10-07 15:44:36 +020011671 sbuf[0] = c;
11672 /*sbuf[1] = '\0'; - already is */
Denys Vlasenkodbef38a2016-10-26 17:54:32 +020011673 setvar0(optvar, sbuf);
11674
11675 shellparam.optoff = p ? p - *(optnext - 1) : -1;
11676 shellparam.optind = ind;
11677
Eric Andersencb57d552001-06-28 07:25:16 +000011678 return done;
11679}
Eric Andersenc470f442003-07-28 09:56:35 +000011680
11681/*
11682 * The getopts builtin. Shellparam.optnext points to the next argument
11683 * to be processed. Shellparam.optptr points to the next character to
11684 * be processed in the current argument. If shellparam.optnext is NULL,
11685 * then it's the first time getopts has been called.
11686 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020011687static int FAST_FUNC
Eric Andersenc470f442003-07-28 09:56:35 +000011688getoptscmd(int argc, char **argv)
11689{
11690 char **optbase;
11691
11692 if (argc < 3)
Denis Vlasenko3af3e5b2007-03-05 00:24:52 +000011693 ash_msg_and_raise_error("usage: getopts optstring var [arg]");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011694 if (argc == 3) {
Eric Andersenc470f442003-07-28 09:56:35 +000011695 optbase = shellparam.p;
Denys Vlasenkodbef38a2016-10-26 17:54:32 +020011696 if ((unsigned)shellparam.optind > shellparam.nparam + 1) {
Eric Andersenc470f442003-07-28 09:56:35 +000011697 shellparam.optind = 1;
11698 shellparam.optoff = -1;
11699 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011700 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000011701 optbase = &argv[3];
Denys Vlasenkodbef38a2016-10-26 17:54:32 +020011702 if ((unsigned)shellparam.optind > argc - 2) {
Eric Andersenc470f442003-07-28 09:56:35 +000011703 shellparam.optind = 1;
11704 shellparam.optoff = -1;
11705 }
11706 }
11707
Denys Vlasenko35c2a132016-10-26 17:34:26 +020011708 return getopts(argv[1], argv[2], optbase);
Eric Andersenc470f442003-07-28 09:56:35 +000011709}
Denis Vlasenko131ae172007-02-18 13:00:19 +000011710#endif /* ASH_GETOPTS */
Eric Andersencb57d552001-06-28 07:25:16 +000011711
Eric Andersencb57d552001-06-28 07:25:16 +000011712
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011713/* ============ Shell parser */
Eric Andersencb57d552001-06-28 07:25:16 +000011714
Denis Vlasenkob07a4962008-06-22 13:16:23 +000011715struct heredoc {
11716 struct heredoc *next; /* next here document in list */
11717 union node *here; /* redirection node */
11718 char *eofmark; /* string indicating end of input */
11719 smallint striptabs; /* if set, strip leading tabs */
11720};
11721
11722static smallint tokpushback; /* last token pushed back */
Denis Vlasenkob07a4962008-06-22 13:16:23 +000011723static smallint quoteflag; /* set if (part of) last token was quoted */
11724static token_id_t lasttoken; /* last token read (integer id Txxx) */
11725static struct heredoc *heredoclist; /* list of here documents to read */
11726static char *wordtext; /* text of last word returned by readtoken */
11727static struct nodelist *backquotelist;
11728static union node *redirnode;
11729static struct heredoc *heredoc;
Denis Vlasenko99eb8502007-02-23 21:09:49 +000011730
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020011731static const char *
11732tokname(char *buf, int tok)
11733{
11734 if (tok < TSEMI)
Denys Vlasenko888527c2016-10-02 16:54:17 +020011735 return tokname_array[tok];
11736 sprintf(buf, "\"%s\"", tokname_array[tok]);
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020011737 return buf;
11738}
11739
11740/* raise_error_unexpected_syntax:
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011741 * Called when an unexpected token is read during the parse. The argument
11742 * is the token that is expected, or -1 if more than one type of token can
11743 * occur at this point.
11744 */
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000011745static void raise_error_unexpected_syntax(int) NORETURN;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011746static void
11747raise_error_unexpected_syntax(int token)
11748{
11749 char msg[64];
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020011750 char buf[16];
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011751 int l;
11752
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020011753 l = sprintf(msg, "unexpected %s", tokname(buf, lasttoken));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011754 if (token >= 0)
Denys Vlasenkoa0ec4f52010-05-20 12:50:42 +020011755 sprintf(msg + l, " (expecting %s)", tokname(buf, token));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011756 raise_error_syntax(msg);
11757 /* NOTREACHED */
11758}
Eric Andersencb57d552001-06-28 07:25:16 +000011759
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011760/* parsing is heavily cross-recursive, need these forward decls */
11761static union node *andor(void);
11762static union node *pipeline(void);
11763static union node *parse_command(void);
11764static void parseheredoc(void);
Ron Yorstonc0e00762015-10-29 11:30:55 +000011765static int peektoken(void);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011766static int readtoken(void);
Eric Andersencb57d552001-06-28 07:25:16 +000011767
Eric Andersenc470f442003-07-28 09:56:35 +000011768static union node *
11769list(int nlflag)
Eric Andersencb57d552001-06-28 07:25:16 +000011770{
11771 union node *n1, *n2, *n3;
11772 int tok;
11773
Eric Andersencb57d552001-06-28 07:25:16 +000011774 n1 = NULL;
11775 for (;;) {
Denys Vlasenkoc08993f2020-02-22 17:26:23 +010011776 switch (readtoken()) {
Ron Yorstonc0e00762015-10-29 11:30:55 +000011777 case TNL:
11778 if (!(nlflag & 1))
11779 break;
11780 parseheredoc();
11781 return n1;
11782
11783 case TEOF:
11784 if (!n1 && (nlflag & 1))
11785 n1 = NODE_EOF;
11786 parseheredoc();
Denys Vlasenkoc08993f2020-02-22 17:26:23 +010011787 tokpushback++;
11788 lasttoken = TEOF;
Ron Yorstonc0e00762015-10-29 11:30:55 +000011789 return n1;
11790 }
11791
Denys Vlasenkoc08993f2020-02-22 17:26:23 +010011792 tokpushback++;
Ron Yorstonc0e00762015-10-29 11:30:55 +000011793 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Denys Vlasenko888527c2016-10-02 16:54:17 +020011794 if (nlflag == 2 && ((1 << peektoken()) & tokendlist))
Ron Yorstonc0e00762015-10-29 11:30:55 +000011795 return n1;
11796 nlflag |= 2;
11797
Eric Andersencb57d552001-06-28 07:25:16 +000011798 n2 = andor();
11799 tok = readtoken();
11800 if (tok == TBACKGND) {
Eric Andersenc470f442003-07-28 09:56:35 +000011801 if (n2->type == NPIPE) {
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000011802 n2->npipe.pipe_backgnd = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000011803 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000011804 if (n2->type != NREDIR) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000011805 n3 = stzalloc(sizeof(struct nredir));
Eric Andersenc470f442003-07-28 09:56:35 +000011806 n3->nredir.n = n2;
Denis Vlasenko597906c2008-02-20 16:38:54 +000011807 /*n3->nredir.redirect = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000011808 n2 = n3;
11809 }
11810 n2->type = NBACKGND;
Eric Andersencb57d552001-06-28 07:25:16 +000011811 }
11812 }
11813 if (n1 == NULL) {
11814 n1 = n2;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011815 } else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011816 n3 = stzalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +000011817 n3->type = NSEMI;
11818 n3->nbinary.ch1 = n1;
11819 n3->nbinary.ch2 = n2;
11820 n1 = n3;
11821 }
11822 switch (tok) {
Ron Yorstonc0e00762015-10-29 11:30:55 +000011823 case TNL:
11824 case TEOF:
11825 tokpushback = 1;
11826 /* fall through */
Eric Andersencb57d552001-06-28 07:25:16 +000011827 case TBACKGND:
11828 case TSEMI:
Eric Andersencb57d552001-06-28 07:25:16 +000011829 break;
Eric Andersencb57d552001-06-28 07:25:16 +000011830 default:
Ron Yorstonc0e00762015-10-29 11:30:55 +000011831 if ((nlflag & 1))
Denis Vlasenkoa624c112007-02-19 22:45:43 +000011832 raise_error_unexpected_syntax(-1);
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011833 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000011834 return n1;
11835 }
11836 }
11837}
11838
Eric Andersenc470f442003-07-28 09:56:35 +000011839static union node *
11840andor(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011841{
Eric Andersencb57d552001-06-28 07:25:16 +000011842 union node *n1, *n2, *n3;
11843 int t;
11844
Eric Andersencb57d552001-06-28 07:25:16 +000011845 n1 = pipeline();
11846 for (;;) {
Denis Vlasenko5cedb752007-02-18 19:56:41 +000011847 t = readtoken();
11848 if (t == TAND) {
Eric Andersencb57d552001-06-28 07:25:16 +000011849 t = NAND;
11850 } else if (t == TOR) {
11851 t = NOR;
11852 } else {
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011853 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000011854 return n1;
11855 }
Eric Andersenc470f442003-07-28 09:56:35 +000011856 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000011857 n2 = pipeline();
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011858 n3 = stzalloc(sizeof(struct nbinary));
Eric Andersencb57d552001-06-28 07:25:16 +000011859 n3->type = t;
11860 n3->nbinary.ch1 = n1;
11861 n3->nbinary.ch2 = n2;
11862 n1 = n3;
11863 }
11864}
11865
Eric Andersenc470f442003-07-28 09:56:35 +000011866static union node *
11867pipeline(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000011868{
Eric Andersencb57d552001-06-28 07:25:16 +000011869 union node *n1, *n2, *pipenode;
11870 struct nodelist *lp, *prev;
11871 int negate;
11872
11873 negate = 0;
11874 TRACE(("pipeline: entered\n"));
11875 if (readtoken() == TNOT) {
11876 negate = !negate;
Eric Andersenc470f442003-07-28 09:56:35 +000011877 checkkwd = CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000011878 } else
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011879 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011880 n1 = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +000011881 if (readtoken() == TPIPE) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000011882 pipenode = stzalloc(sizeof(struct npipe));
Eric Andersencb57d552001-06-28 07:25:16 +000011883 pipenode->type = NPIPE;
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000011884 /*pipenode->npipe.pipe_backgnd = 0; - stzalloc did it */
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011885 lp = stzalloc(sizeof(struct nodelist));
Eric Andersencb57d552001-06-28 07:25:16 +000011886 pipenode->npipe.cmdlist = lp;
11887 lp->n = n1;
11888 do {
11889 prev = lp;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011890 lp = stzalloc(sizeof(struct nodelist));
Eric Andersenc470f442003-07-28 09:56:35 +000011891 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011892 lp->n = parse_command();
Eric Andersencb57d552001-06-28 07:25:16 +000011893 prev->next = lp;
11894 } while (readtoken() == TPIPE);
11895 lp->next = NULL;
11896 n1 = pipenode;
11897 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000011898 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000011899 if (negate) {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011900 n2 = stzalloc(sizeof(struct nnot));
Eric Andersencb57d552001-06-28 07:25:16 +000011901 n2->type = NNOT;
11902 n2->nnot.com = n1;
11903 return n2;
Denis Vlasenko2da584f2007-02-19 22:44:05 +000011904 }
11905 return n1;
Eric Andersencb57d552001-06-28 07:25:16 +000011906}
11907
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011908static union node *
11909makename(void)
11910{
11911 union node *n;
11912
Denis Vlasenko597906c2008-02-20 16:38:54 +000011913 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011914 n->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000011915 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011916 n->narg.text = wordtext;
11917 n->narg.backquote = backquotelist;
11918 return n;
11919}
11920
11921static void
11922fixredir(union node *n, const char *text, int err)
11923{
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000011924 int fd;
11925
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011926 TRACE(("Fix redir %s %d\n", text, err));
11927 if (!err)
11928 n->ndup.vname = NULL;
11929
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000011930 fd = bb_strtou(text, NULL, 10);
11931 if (!errno && fd >= 0)
11932 n->ndup.dupfd = fd;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011933 else if (LONE_DASH(text))
11934 n->ndup.dupfd = -1;
11935 else {
11936 if (err)
Denis Vlasenko559691a2008-10-05 18:39:31 +000011937 raise_error_syntax("bad fd number");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011938 n->ndup.vname = makename();
11939 }
11940}
11941
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011942static void
11943parsefname(void)
11944{
11945 union node *n = redirnode;
11946
Denys Vlasenkoa7328982017-07-29 19:57:28 +020011947 if (n->type == NHERE)
11948 checkkwd = CHKEOFMARK;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011949 if (readtoken() != TWORD)
11950 raise_error_unexpected_syntax(-1);
11951 if (n->type == NHERE) {
11952 struct heredoc *here = heredoc;
11953 struct heredoc *p;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011954
11955 if (quoteflag == 0)
11956 n->type = NXHERE;
11957 TRACE(("Here document %d\n", n->type));
Denys Vlasenko740058b2018-01-09 17:01:00 +010011958 rmescapes(wordtext, 0, NULL);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011959 here->eofmark = wordtext;
11960 here->next = NULL;
11961 if (heredoclist == NULL)
11962 heredoclist = here;
11963 else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000011964 for (p = heredoclist; p->next; p = p->next)
11965 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011966 p->next = here;
11967 }
11968 } else if (n->type == NTOFD || n->type == NFROMFD) {
11969 fixredir(n, wordtext, 0);
11970 } else {
11971 n->nfile.fname = makename();
11972 }
11973}
Eric Andersencb57d552001-06-28 07:25:16 +000011974
Eric Andersenc470f442003-07-28 09:56:35 +000011975static union node *
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011976simplecmd(void)
11977{
11978 union node *args, **app;
11979 union node *n = NULL;
11980 union node *vars, **vpp;
11981 union node **rpp, *redir;
11982 int savecheckkwd;
Denys Vlasenko675d24a2018-01-27 22:02:05 +010011983 int savelinno;
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010011984#if BASH_TEST2
Denis Vlasenko80591b02008-03-25 07:49:43 +000011985 smallint double_brackets_flag = 0;
11986#endif
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010011987 IF_BASH_FUNCTION(smallint function_flag = 0;)
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011988
11989 args = NULL;
11990 app = &args;
11991 vars = NULL;
11992 vpp = &vars;
11993 redir = NULL;
11994 rpp = &redir;
11995
11996 savecheckkwd = CHKALIAS;
Denys Vlasenko675d24a2018-01-27 22:02:05 +010011997 savelinno = g_parsefile->linno;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000011998 for (;;) {
Denis Vlasenko80591b02008-03-25 07:49:43 +000011999 int t;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012000 checkkwd = savecheckkwd;
Denis Vlasenko80591b02008-03-25 07:49:43 +000012001 t = readtoken();
12002 switch (t) {
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010012003#if BASH_FUNCTION
Ron Yorston95ebcf72015-11-03 09:42:23 +000012004 case TFUNCTION:
12005 if (peektoken() != TWORD)
12006 raise_error_unexpected_syntax(TWORD);
12007 function_flag = 1;
12008 break;
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010012009#endif
12010#if BASH_TEST2
Denis Vlasenko80591b02008-03-25 07:49:43 +000012011 case TAND: /* "&&" */
12012 case TOR: /* "||" */
12013 if (!double_brackets_flag) {
12014 tokpushback = 1;
12015 goto out;
12016 }
Denys Vlasenkod2241f52020-10-31 03:34:07 +010012017 /* pass "&&" or "||" to [[ ]] as literal args */
12018 wordtext = (char *) (t == TAND ? "&&" : "||");
Denis Vlasenko80591b02008-03-25 07:49:43 +000012019#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012020 case TWORD:
Denis Vlasenko597906c2008-02-20 16:38:54 +000012021 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012022 n->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000012023 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012024 n->narg.text = wordtext;
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010012025#if BASH_TEST2
Denis Vlasenko80591b02008-03-25 07:49:43 +000012026 if (strcmp("[[", wordtext) == 0)
12027 double_brackets_flag = 1;
12028 else if (strcmp("]]", wordtext) == 0)
12029 double_brackets_flag = 0;
12030#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012031 n->narg.backquote = backquotelist;
12032 if (savecheckkwd && isassignment(wordtext)) {
12033 *vpp = n;
12034 vpp = &n->narg.next;
12035 } else {
12036 *app = n;
12037 app = &n->narg.next;
12038 savecheckkwd = 0;
12039 }
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010012040#if BASH_FUNCTION
Ron Yorston95ebcf72015-11-03 09:42:23 +000012041 if (function_flag) {
12042 checkkwd = CHKNL | CHKKWD;
12043 switch (peektoken()) {
12044 case TBEGIN:
12045 case TIF:
12046 case TCASE:
12047 case TUNTIL:
12048 case TWHILE:
12049 case TFOR:
12050 goto do_func;
12051 case TLP:
12052 function_flag = 0;
12053 break;
Denys Vlasenkoe93031e2018-04-10 01:23:19 +020012054# if BASH_TEST2
Ron Yorston95ebcf72015-11-03 09:42:23 +000012055 case TWORD:
12056 if (strcmp("[[", wordtext) == 0)
12057 goto do_func;
12058 /* fall through */
Denys Vlasenkoe93031e2018-04-10 01:23:19 +020012059# endif
Ron Yorston95ebcf72015-11-03 09:42:23 +000012060 default:
12061 raise_error_unexpected_syntax(-1);
12062 }
12063 }
12064#endif
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012065 break;
12066 case TREDIR:
12067 *rpp = n = redirnode;
12068 rpp = &n->nfile.next;
12069 parsefname(); /* read name of redirection file */
12070 break;
12071 case TLP:
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010012072 IF_BASH_FUNCTION(do_func:)
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012073 if (args && app == &args->narg.next
12074 && !vars && !redir
12075 ) {
12076 struct builtincmd *bcmd;
12077 const char *name;
12078
12079 /* We have a function */
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010012080 if (IF_BASH_FUNCTION(!function_flag &&) readtoken() != TRP)
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012081 raise_error_unexpected_syntax(TRP);
12082 name = n->narg.text;
12083 if (!goodname(name)
12084 || ((bcmd = find_builtin(name)) && IS_BUILTIN_SPECIAL(bcmd))
12085 ) {
Denis Vlasenko559691a2008-10-05 18:39:31 +000012086 raise_error_syntax("bad function name");
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012087 }
12088 n->type = NDEFUN;
12089 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Denys Vlasenko675d24a2018-01-27 22:02:05 +010012090 n->ndefun.text = n->narg.text;
12091 n->ndefun.linno = g_parsefile->linno;
12092 n->ndefun.body = parse_command();
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012093 return n;
12094 }
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010012095 IF_BASH_FUNCTION(function_flag = 0;)
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012096 /* fall through */
12097 default:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000012098 tokpushback = 1;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012099 goto out;
12100 }
12101 }
12102 out:
12103 *app = NULL;
12104 *vpp = NULL;
12105 *rpp = NULL;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000012106 n = stzalloc(sizeof(struct ncmd));
Denys Vlasenko57b7efb2018-04-10 01:20:26 +020012107 if (NCMD != 0)
12108 n->type = NCMD;
Denys Vlasenko675d24a2018-01-27 22:02:05 +010012109 n->ncmd.linno = savelinno;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012110 n->ncmd.args = args;
12111 n->ncmd.assign = vars;
12112 n->ncmd.redirect = redir;
12113 return n;
12114}
12115
12116static union node *
12117parse_command(void)
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000012118{
Eric Andersencb57d552001-06-28 07:25:16 +000012119 union node *n1, *n2;
12120 union node *ap, **app;
12121 union node *cp, **cpp;
12122 union node *redir, **rpp;
Eric Andersenc470f442003-07-28 09:56:35 +000012123 union node **rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +000012124 int t;
Denys Vlasenko675d24a2018-01-27 22:02:05 +010012125 int savelinno;
Eric Andersencb57d552001-06-28 07:25:16 +000012126
12127 redir = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +000012128 rpp2 = &redir;
Eric Andersen88cec252001-09-06 17:35:20 +000012129
Denys Vlasenko675d24a2018-01-27 22:02:05 +010012130 savelinno = g_parsefile->linno;
12131
Eric Andersencb57d552001-06-28 07:25:16 +000012132 switch (readtoken()) {
Eric Andersenc470f442003-07-28 09:56:35 +000012133 default:
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012134 raise_error_unexpected_syntax(-1);
Eric Andersenc470f442003-07-28 09:56:35 +000012135 /* NOTREACHED */
Eric Andersencb57d552001-06-28 07:25:16 +000012136 case TIF:
Denis Vlasenko838ffd52008-02-21 04:32:08 +000012137 n1 = stzalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +000012138 n1->type = NIF;
12139 n1->nif.test = list(0);
12140 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012141 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +000012142 n1->nif.ifpart = list(0);
12143 n2 = n1;
12144 while (readtoken() == TELIF) {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000012145 n2->nif.elsepart = stzalloc(sizeof(struct nif));
Eric Andersencb57d552001-06-28 07:25:16 +000012146 n2 = n2->nif.elsepart;
12147 n2->type = NIF;
12148 n2->nif.test = list(0);
12149 if (readtoken() != TTHEN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012150 raise_error_unexpected_syntax(TTHEN);
Eric Andersencb57d552001-06-28 07:25:16 +000012151 n2->nif.ifpart = list(0);
12152 }
12153 if (lasttoken == TELSE)
12154 n2->nif.elsepart = list(0);
12155 else {
12156 n2->nif.elsepart = NULL;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000012157 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000012158 }
Eric Andersenc470f442003-07-28 09:56:35 +000012159 t = TFI;
Eric Andersencb57d552001-06-28 07:25:16 +000012160 break;
12161 case TWHILE:
Eric Andersenc470f442003-07-28 09:56:35 +000012162 case TUNTIL: {
Eric Andersencb57d552001-06-28 07:25:16 +000012163 int got;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000012164 n1 = stzalloc(sizeof(struct nbinary));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012165 n1->type = (lasttoken == TWHILE) ? NWHILE : NUNTIL;
Eric Andersencb57d552001-06-28 07:25:16 +000012166 n1->nbinary.ch1 = list(0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012167 got = readtoken();
12168 if (got != TDO) {
Denys Vlasenko888527c2016-10-02 16:54:17 +020012169 TRACE(("expecting DO got '%s' %s\n", tokname_array[got],
Denis Vlasenko131ae172007-02-18 13:00:19 +000012170 got == TWORD ? wordtext : ""));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012171 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +000012172 }
12173 n1->nbinary.ch2 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000012174 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +000012175 break;
12176 }
12177 case TFOR:
Denis Vlasenko2dc240c2008-07-24 06:07:50 +000012178 if (readtoken() != TWORD || quoteflag || !goodname(wordtext))
Denis Vlasenko559691a2008-10-05 18:39:31 +000012179 raise_error_syntax("bad for loop variable");
Denis Vlasenko838ffd52008-02-21 04:32:08 +000012180 n1 = stzalloc(sizeof(struct nfor));
Eric Andersencb57d552001-06-28 07:25:16 +000012181 n1->type = NFOR;
Denys Vlasenko675d24a2018-01-27 22:02:05 +010012182 n1->nfor.linno = savelinno;
Eric Andersencb57d552001-06-28 07:25:16 +000012183 n1->nfor.var = wordtext;
Ron Yorstonab80e012015-08-03 13:46:00 +010012184 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000012185 if (readtoken() == TIN) {
12186 app = &ap;
12187 while (readtoken() == TWORD) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000012188 n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000012189 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000012190 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000012191 n2->narg.text = wordtext;
12192 n2->narg.backquote = backquotelist;
12193 *app = n2;
12194 app = &n2->narg.next;
12195 }
12196 *app = NULL;
12197 n1->nfor.args = ap;
12198 if (lasttoken != TNL && lasttoken != TSEMI)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012199 raise_error_unexpected_syntax(-1);
Eric Andersencb57d552001-06-28 07:25:16 +000012200 } else {
Denis Vlasenko597906c2008-02-20 16:38:54 +000012201 n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000012202 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000012203 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000012204 n2->narg.text = (char *)dolatstr;
Denis Vlasenko597906c2008-02-20 16:38:54 +000012205 /*n2->narg.backquote = NULL;*/
Eric Andersencb57d552001-06-28 07:25:16 +000012206 n1->nfor.args = n2;
12207 /*
12208 * Newline or semicolon here is optional (but note
12209 * that the original Bourne shell only allowed NL).
12210 */
Ron Yorstonab80e012015-08-03 13:46:00 +010012211 if (lasttoken != TSEMI)
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000012212 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000012213 }
Eric Andersenc470f442003-07-28 09:56:35 +000012214 checkkwd = CHKNL | CHKKWD | CHKALIAS;
Eric Andersencb57d552001-06-28 07:25:16 +000012215 if (readtoken() != TDO)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012216 raise_error_unexpected_syntax(TDO);
Eric Andersencb57d552001-06-28 07:25:16 +000012217 n1->nfor.body = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000012218 t = TDONE;
Eric Andersencb57d552001-06-28 07:25:16 +000012219 break;
12220 case TCASE:
Denis Vlasenko838ffd52008-02-21 04:32:08 +000012221 n1 = stzalloc(sizeof(struct ncase));
Eric Andersencb57d552001-06-28 07:25:16 +000012222 n1->type = NCASE;
Denys Vlasenko675d24a2018-01-27 22:02:05 +010012223 n1->ncase.linno = savelinno;
Eric Andersencb57d552001-06-28 07:25:16 +000012224 if (readtoken() != TWORD)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012225 raise_error_unexpected_syntax(TWORD);
Denis Vlasenko597906c2008-02-20 16:38:54 +000012226 n1->ncase.expr = n2 = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000012227 n2->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000012228 /*n2->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000012229 n2->narg.text = wordtext;
12230 n2->narg.backquote = backquotelist;
Ron Yorston383b8852015-08-03 13:46:25 +010012231 checkkwd = CHKNL | CHKKWD | CHKALIAS;
12232 if (readtoken() != TIN)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012233 raise_error_unexpected_syntax(TIN);
Eric Andersencb57d552001-06-28 07:25:16 +000012234 cpp = &n1->ncase.cases;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012235 next_case:
Eric Andersenc470f442003-07-28 09:56:35 +000012236 checkkwd = CHKNL | CHKKWD;
12237 t = readtoken();
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012238 while (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +000012239 if (lasttoken == TLP)
12240 readtoken();
Denis Vlasenko838ffd52008-02-21 04:32:08 +000012241 *cpp = cp = stzalloc(sizeof(struct nclist));
Eric Andersencb57d552001-06-28 07:25:16 +000012242 cp->type = NCLIST;
12243 app = &cp->nclist.pattern;
12244 for (;;) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000012245 *app = ap = stzalloc(sizeof(struct narg));
Eric Andersencb57d552001-06-28 07:25:16 +000012246 ap->type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000012247 /*ap->narg.next = NULL; - stzalloc did it */
Eric Andersencb57d552001-06-28 07:25:16 +000012248 ap->narg.text = wordtext;
12249 ap->narg.backquote = backquotelist;
Eric Andersenc470f442003-07-28 09:56:35 +000012250 if (readtoken() != TPIPE)
Eric Andersencb57d552001-06-28 07:25:16 +000012251 break;
12252 app = &ap->narg.next;
12253 readtoken();
12254 }
Denis Vlasenko597906c2008-02-20 16:38:54 +000012255 //ap->narg.next = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +000012256 if (lasttoken != TRP)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012257 raise_error_unexpected_syntax(TRP);
Eric Andersenc470f442003-07-28 09:56:35 +000012258 cp->nclist.body = list(2);
Eric Andersencb57d552001-06-28 07:25:16 +000012259
Eric Andersenc470f442003-07-28 09:56:35 +000012260 cpp = &cp->nclist.next;
12261
12262 checkkwd = CHKNL | CHKKWD;
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012263 t = readtoken();
12264 if (t != TESAC) {
Eric Andersencb57d552001-06-28 07:25:16 +000012265 if (t != TENDCASE)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012266 raise_error_unexpected_syntax(TENDCASE);
12267 goto next_case;
Eric Andersencb57d552001-06-28 07:25:16 +000012268 }
Eric Andersenc470f442003-07-28 09:56:35 +000012269 }
Eric Andersencb57d552001-06-28 07:25:16 +000012270 *cpp = NULL;
Eric Andersenc470f442003-07-28 09:56:35 +000012271 goto redir;
Eric Andersencb57d552001-06-28 07:25:16 +000012272 case TLP:
Denis Vlasenko597906c2008-02-20 16:38:54 +000012273 n1 = stzalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +000012274 n1->type = NSUBSHELL;
Denys Vlasenko675d24a2018-01-27 22:02:05 +010012275 n1->nredir.linno = savelinno;
Eric Andersencb57d552001-06-28 07:25:16 +000012276 n1->nredir.n = list(0);
Denis Vlasenko597906c2008-02-20 16:38:54 +000012277 /*n1->nredir.redirect = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000012278 t = TRP;
Eric Andersencb57d552001-06-28 07:25:16 +000012279 break;
12280 case TBEGIN:
12281 n1 = list(0);
Eric Andersenc470f442003-07-28 09:56:35 +000012282 t = TEND;
Eric Andersencb57d552001-06-28 07:25:16 +000012283 break;
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010012284 IF_BASH_FUNCTION(case TFUNCTION:)
Eric Andersencb57d552001-06-28 07:25:16 +000012285 case TWORD:
Eric Andersenc470f442003-07-28 09:56:35 +000012286 case TREDIR:
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000012287 tokpushback = 1;
Eric Andersenc470f442003-07-28 09:56:35 +000012288 return simplecmd();
Eric Andersencb57d552001-06-28 07:25:16 +000012289 }
12290
Eric Andersenc470f442003-07-28 09:56:35 +000012291 if (readtoken() != t)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000012292 raise_error_unexpected_syntax(t);
Eric Andersenc470f442003-07-28 09:56:35 +000012293
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012294 redir:
Eric Andersencb57d552001-06-28 07:25:16 +000012295 /* Now check for redirection which may follow command */
Eric Andersenc470f442003-07-28 09:56:35 +000012296 checkkwd = CHKKWD | CHKALIAS;
12297 rpp = rpp2;
Eric Andersencb57d552001-06-28 07:25:16 +000012298 while (readtoken() == TREDIR) {
12299 *rpp = n2 = redirnode;
12300 rpp = &n2->nfile.next;
12301 parsefname();
12302 }
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000012303 tokpushback = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000012304 *rpp = NULL;
12305 if (redir) {
12306 if (n1->type != NSUBSHELL) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000012307 n2 = stzalloc(sizeof(struct nredir));
Eric Andersencb57d552001-06-28 07:25:16 +000012308 n2->type = NREDIR;
Denys Vlasenko675d24a2018-01-27 22:02:05 +010012309 n2->nredir.linno = savelinno;
Eric Andersencb57d552001-06-28 07:25:16 +000012310 n2->nredir.n = n1;
12311 n1 = n2;
12312 }
12313 n1->nredir.redirect = redir;
12314 }
Eric Andersencb57d552001-06-28 07:25:16 +000012315 return n1;
12316}
12317
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010012318#if BASH_DOLLAR_SQUOTE
Denys Vlasenko37dc08b2016-10-02 04:38:07 +020012319static int
12320decode_dollar_squote(void)
Denis Vlasenkoef527f52008-06-23 01:52:30 +000012321{
12322 static const char C_escapes[] ALIGN1 = "nrbtfav""x\\01234567";
12323 int c, cnt;
12324 char *p;
12325 char buf[4];
12326
12327 c = pgetc();
12328 p = strchr(C_escapes, c);
12329 if (p) {
12330 buf[0] = c;
12331 p = buf;
12332 cnt = 3;
12333 if ((unsigned char)(c - '0') <= 7) { /* \ooo */
12334 do {
12335 c = pgetc();
12336 *++p = c;
12337 } while ((unsigned char)(c - '0') <= 7 && --cnt);
12338 pungetc();
12339 } else if (c == 'x') { /* \xHH */
12340 do {
12341 c = pgetc();
12342 *++p = c;
12343 } while (isxdigit(c) && --cnt);
12344 pungetc();
12345 if (cnt == 3) { /* \x but next char is "bad" */
12346 c = 'x';
12347 goto unrecognized;
12348 }
12349 } else { /* simple seq like \\ or \t */
12350 p++;
12351 }
12352 *p = '\0';
12353 p = buf;
12354 c = bb_process_escape_sequence((void*)&p);
12355 } else { /* unrecognized "\z": print both chars unless ' or " */
12356 if (c != '\'' && c != '"') {
12357 unrecognized:
12358 c |= 0x100; /* "please encode \, then me" */
12359 }
12360 }
12361 return c;
12362}
12363#endif
12364
Denys Vlasenko46999802017-07-29 21:12:29 +020012365/* Used by expandstr to get here-doc like behaviour. */
12366#define FAKEEOFMARK ((char*)(uintptr_t)1)
12367
12368static ALWAYS_INLINE int
12369realeofmark(const char *eofmark)
12370{
12371 return eofmark && eofmark != FAKEEOFMARK;
12372}
12373
Eric Andersencb57d552001-06-28 07:25:16 +000012374/*
12375 * If eofmark is NULL, read a word or a redirection symbol. If eofmark
12376 * is not NULL, read a here document. In the latter case, eofmark is the
12377 * word which marks the end of the document and striptabs is true if
Denys Vlasenkocd716832009-11-28 22:14:02 +010012378 * leading tabs should be stripped from the document. The argument c
Eric Andersencb57d552001-06-28 07:25:16 +000012379 * is the first character of the input token or document.
12380 *
12381 * Because C does not have internal subroutines, I have simulated them
12382 * using goto's to implement the subroutine linkage. The following macros
12383 * will run code that appears at the end of readtoken1.
12384 */
Eric Andersen2870d962001-07-02 17:27:21 +000012385#define CHECKEND() {goto checkend; checkend_return:;}
12386#define PARSEREDIR() {goto parseredir; parseredir_return:;}
12387#define PARSESUB() {goto parsesub; parsesub_return:;}
Ron Yorstona1b0d382020-07-23 08:32:27 +010012388#define PARSEBACKQOLD() {style = OLD; goto parsebackq; parsebackq_oldreturn:;}
12389#define PARSEBACKQNEW() {style = NEW; goto parsebackq; parsebackq_newreturn:;}
12390#define PARSEPROCSUB() {style = PSUB; goto parsebackq; parsebackq_psreturn:;}
Eric Andersen2870d962001-07-02 17:27:21 +000012391#define PARSEARITH() {goto parsearith; parsearith_return:;}
Eric Andersencb57d552001-06-28 07:25:16 +000012392static int
Denys Vlasenkocd716832009-11-28 22:14:02 +010012393readtoken1(int c, int syntax, char *eofmark, int striptabs)
Manuel Novoa III 16815d42001-08-10 19:36:07 +000012394{
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000012395 /* NB: syntax parameter fits into smallint */
Denys Vlasenkocd716832009-11-28 22:14:02 +010012396 /* c parameter is an unsigned char or PEOF or PEOA */
Eric Andersencb57d552001-06-28 07:25:16 +000012397 char *out;
Denys Vlasenko50e6d422016-09-30 11:35:54 +020012398 size_t len;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000012399 struct nodelist *bqlist;
12400 smallint quotef;
Ron Yorstona1b0d382020-07-23 08:32:27 +010012401 smallint style;
12402 enum { OLD, NEW, PSUB };
12403#define oldstyle (style == OLD)
Denis Vlasenko46a53062007-09-24 18:30:02 +000012404 smallint pssyntax; /* we are expanding a prompt string */
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010012405 IF_BASH_DOLLAR_SQUOTE(smallint bash_dollar_squote = 0;)
Denys Vlasenko216913c2018-04-02 12:35:04 +020012406 /* syntax stack */
Denys Vlasenkoee1fd122018-04-04 13:59:53 +020012407 struct synstack synbase = { };
Denys Vlasenko216913c2018-04-02 12:35:04 +020012408 struct synstack *synstack = &synbase;
Denis Vlasenkoef527f52008-06-23 01:52:30 +000012409
Denys Vlasenko5f0a75f2017-07-29 22:58:44 +020012410#if ENABLE_ASH_EXPAND_PRMT
Denis Vlasenko46a53062007-09-24 18:30:02 +000012411 pssyntax = (syntax == PSSYNTAX);
12412 if (pssyntax)
12413 syntax = DQSYNTAX;
Denys Vlasenko5f0a75f2017-07-29 22:58:44 +020012414#else
12415 pssyntax = 0; /* constant */
12416#endif
Denys Vlasenkoee1fd122018-04-04 13:59:53 +020012417 synstack->syntax = syntax;
12418
Denys Vlasenko216913c2018-04-02 12:35:04 +020012419 if (syntax == DQSYNTAX)
12420 synstack->dblquote = 1;
12421 quotef = 0;
12422 bqlist = NULL;
Eric Andersencb57d552001-06-28 07:25:16 +000012423
12424 STARTSTACKSTR(out);
Denis Vlasenko176d49d2008-10-06 09:51:47 +000012425 loop:
12426 /* For each line, until end of word */
Denys Vlasenko958581a2010-09-12 15:04:27 +020012427 CHECKEND(); /* set c to PEOF if at end of here document */
12428 for (;;) { /* until end of line or end of word */
12429 CHECKSTRSPACE(4, out); /* permit 4 calls to USTPUTC */
Denys Vlasenko216913c2018-04-02 12:35:04 +020012430 switch (SIT(c, synstack->syntax)) {
Denys Vlasenko958581a2010-09-12 15:04:27 +020012431 case CNL: /* '\n' */
Denys Vlasenko680c3012018-04-11 12:39:18 +020012432 if (synstack->syntax == BASESYNTAX
12433 && !synstack->varnest
12434 ) {
Denys Vlasenko958581a2010-09-12 15:04:27 +020012435 goto endword; /* exit outer loop */
Denys Vlasenko680c3012018-04-11 12:39:18 +020012436 }
Denys Vlasenko958581a2010-09-12 15:04:27 +020012437 USTPUTC(c, out);
Denys Vlasenkoce332a22016-10-02 23:47:34 +020012438 nlprompt();
Denys Vlasenkof7eea8c2020-02-14 16:16:34 +010012439 c = pgetc_top(synstack);
Denys Vlasenko958581a2010-09-12 15:04:27 +020012440 goto loop; /* continue outer loop */
12441 case CWORD:
12442 USTPUTC(c, out);
12443 break;
12444 case CCTL:
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010012445#if BASH_DOLLAR_SQUOTE
Denys Vlasenko958581a2010-09-12 15:04:27 +020012446 if (c == '\\' && bash_dollar_squote) {
12447 c = decode_dollar_squote();
Denys Vlasenko13f20912016-09-25 20:54:25 +020012448 if (c == '\0') {
12449 /* skip $'\000', $'\x00' (like bash) */
12450 break;
12451 }
Denys Vlasenko958581a2010-09-12 15:04:27 +020012452 if (c & 0x100) {
Denys Vlasenko13f20912016-09-25 20:54:25 +020012453 /* Unknown escape. Encode as '\z' */
Denys Vlasenko958581a2010-09-12 15:04:27 +020012454 c = (unsigned char)c;
Denys Vlasenko216913c2018-04-02 12:35:04 +020012455 if (eofmark == NULL || synstack->dblquote)
Denys Vlasenko13f20912016-09-25 20:54:25 +020012456 USTPUTC(CTLESC, out);
12457 USTPUTC('\\', out);
Denis Vlasenkoef527f52008-06-23 01:52:30 +000012458 }
Denys Vlasenko958581a2010-09-12 15:04:27 +020012459 }
Denis Vlasenkoef527f52008-06-23 01:52:30 +000012460#endif
Denys Vlasenkoc4c20122018-04-02 13:29:20 +020012461 if (!eofmark || synstack->dblquote || synstack->varnest)
Denys Vlasenko13f20912016-09-25 20:54:25 +020012462 USTPUTC(CTLESC, out);
Denys Vlasenko958581a2010-09-12 15:04:27 +020012463 USTPUTC(c, out);
12464 break;
12465 case CBACK: /* backslash */
12466 c = pgetc_without_PEOA();
12467 if (c == PEOF) {
12468 USTPUTC(CTLESC, out);
12469 USTPUTC('\\', out);
12470 pungetc();
Denys Vlasenko958581a2010-09-12 15:04:27 +020012471 } else {
Denys Vlasenko5f0a75f2017-07-29 22:58:44 +020012472 if (pssyntax && c == '$') {
Eric Andersenc470f442003-07-28 09:56:35 +000012473 USTPUTC(CTLESC, out);
Eric Andersencb57d552001-06-28 07:25:16 +000012474 USTPUTC('\\', out);
Denys Vlasenko958581a2010-09-12 15:04:27 +020012475 }
Denys Vlasenko8de5b9f2018-02-13 14:43:29 +010012476 /* Backslash is retained if we are in "str"
12477 * and next char isn't dquote-special.
12478 */
Denys Vlasenko216913c2018-04-02 12:35:04 +020012479 if (synstack->dblquote
Denys Vlasenko958581a2010-09-12 15:04:27 +020012480 && c != '\\'
12481 && c != '`'
12482 && c != '$'
Denys Vlasenko216913c2018-04-02 12:35:04 +020012483 && (c != '"' || (eofmark != NULL && !synstack->varnest))
12484 && (c != '}' || !synstack->varnest)
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012485 ) {
Denys Vlasenko8de5b9f2018-02-13 14:43:29 +010012486 USTPUTC(CTLESC, out); /* protect '\' from glob */
Denys Vlasenko958581a2010-09-12 15:04:27 +020012487 USTPUTC('\\', out);
Eric Andersencb57d552001-06-28 07:25:16 +000012488 }
Ron Yorston549deab2015-05-18 09:57:51 +020012489 USTPUTC(CTLESC, out);
Denys Vlasenko0ff78a02010-08-30 15:20:07 +020012490 USTPUTC(c, out);
Denys Vlasenko958581a2010-09-12 15:04:27 +020012491 quotef = 1;
Eric Andersencb57d552001-06-28 07:25:16 +000012492 }
Denys Vlasenko958581a2010-09-12 15:04:27 +020012493 break;
12494 case CSQUOTE:
Denys Vlasenko216913c2018-04-02 12:35:04 +020012495 synstack->syntax = SQSYNTAX;
Denys Vlasenko958581a2010-09-12 15:04:27 +020012496 quotemark:
12497 if (eofmark == NULL) {
12498 USTPUTC(CTLQUOTEMARK, out);
12499 }
12500 break;
12501 case CDQUOTE:
Denys Vlasenko216913c2018-04-02 12:35:04 +020012502 synstack->syntax = DQSYNTAX;
12503 synstack->dblquote = 1;
12504 toggledq:
12505 if (synstack->varnest)
12506 synstack->innerdq ^= 1;
Denys Vlasenko958581a2010-09-12 15:04:27 +020012507 goto quotemark;
12508 case CENDQUOTE:
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010012509 IF_BASH_DOLLAR_SQUOTE(bash_dollar_squote = 0;)
Denys Vlasenko216913c2018-04-02 12:35:04 +020012510 if (eofmark != NULL && synstack->varnest == 0) {
Denys Vlasenko958581a2010-09-12 15:04:27 +020012511 USTPUTC(c, out);
Denys Vlasenko216913c2018-04-02 12:35:04 +020012512 break;
Denys Vlasenko958581a2010-09-12 15:04:27 +020012513 }
Denys Vlasenko216913c2018-04-02 12:35:04 +020012514
12515 if (synstack->dqvarnest == 0) {
12516 synstack->syntax = BASESYNTAX;
12517 synstack->dblquote = 0;
12518 }
12519
12520 quotef = 1;
12521
12522 if (c == '"')
12523 goto toggledq;
12524
12525 goto quotemark;
Denys Vlasenko958581a2010-09-12 15:04:27 +020012526 case CVAR: /* '$' */
12527 PARSESUB(); /* parse substitution */
12528 break;
12529 case CENDVAR: /* '}' */
Denys Vlasenko216913c2018-04-02 12:35:04 +020012530 if (!synstack->innerdq && synstack->varnest > 0) {
12531 if (!--synstack->varnest && synstack->varpushed)
12532 synstack_pop(&synstack);
12533 else if (synstack->dqvarnest > 0)
12534 synstack->dqvarnest--;
Denys Vlasenko958581a2010-09-12 15:04:27 +020012535 c = CTLENDVAR;
12536 }
12537 USTPUTC(c, out);
12538 break;
Denys Vlasenko0b883582016-12-23 16:49:07 +010012539#if ENABLE_FEATURE_SH_MATH
Denys Vlasenko958581a2010-09-12 15:04:27 +020012540 case CLP: /* '(' in arithmetic */
Denys Vlasenko216913c2018-04-02 12:35:04 +020012541 synstack->parenlevel++;
Denys Vlasenko958581a2010-09-12 15:04:27 +020012542 USTPUTC(c, out);
12543 break;
12544 case CRP: /* ')' in arithmetic */
Denys Vlasenko216913c2018-04-02 12:35:04 +020012545 if (synstack->parenlevel > 0) {
12546 synstack->parenlevel--;
Denys Vlasenko958581a2010-09-12 15:04:27 +020012547 } else {
Denys Vlasenko459293b2016-09-29 17:58:58 +020012548 if (pgetc_eatbnl() == ')') {
Ron Yorstonad88bde2015-05-18 09:56:16 +020012549 c = CTLENDARI;
Denys Vlasenko216913c2018-04-02 12:35:04 +020012550 synstack_pop(&synstack);
Denys Vlasenko958581a2010-09-12 15:04:27 +020012551 } else {
12552 /*
12553 * unbalanced parens
12554 * (don't 2nd guess - no error)
12555 */
12556 pungetc();
12557 }
12558 }
12559 USTPUTC(c, out);
12560 break;
12561#endif
12562 case CBQUOTE: /* '`' */
Denys Vlasenko41fddb42018-04-01 16:38:32 +020012563 if (checkkwd & CHKEOFMARK) {
12564 quotef = 1;
12565 USTPUTC('`', out);
12566 break;
12567 }
12568
Denys Vlasenko958581a2010-09-12 15:04:27 +020012569 PARSEBACKQOLD();
12570 break;
12571 case CENDFILE:
12572 goto endword; /* exit outer loop */
12573 case CIGN:
12574 break;
12575 default:
Denys Vlasenko216913c2018-04-02 12:35:04 +020012576 if (synstack->varnest == 0) {
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010012577#if BASH_REDIR_OUTPUT
Denys Vlasenko958581a2010-09-12 15:04:27 +020012578 if (c == '&') {
Denys Vlasenko459293b2016-09-29 17:58:58 +020012579//Can't call pgetc_eatbnl() here, this requires three-deep pungetc()
Denys Vlasenko958581a2010-09-12 15:04:27 +020012580 if (pgetc() == '>')
12581 c = 0x100 + '>'; /* flag &> */
12582 pungetc();
12583 }
12584#endif
Ron Yorstona1b0d382020-07-23 08:32:27 +010012585#if BASH_PROCESS_SUBST
12586 if (c == '<' || c == '>') {
12587 if (pgetc() == '(') {
12588 PARSEPROCSUB();
12589 break;
12590 }
12591 pungetc();
12592 }
12593#endif
Denys Vlasenko958581a2010-09-12 15:04:27 +020012594 goto endword; /* exit outer loop */
12595 }
12596 IF_ASH_ALIAS(if (c != PEOA))
12597 USTPUTC(c, out);
12598 }
Denys Vlasenkof7eea8c2020-02-14 16:16:34 +010012599 c = pgetc_top(synstack);
Denys Vlasenko958581a2010-09-12 15:04:27 +020012600 } /* for (;;) */
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012601 endword:
Denys Vlasenko958581a2010-09-12 15:04:27 +020012602
Denys Vlasenko0b883582016-12-23 16:49:07 +010012603#if ENABLE_FEATURE_SH_MATH
Denys Vlasenko216913c2018-04-02 12:35:04 +020012604 if (synstack->syntax == ARISYNTAX)
Denis Vlasenko559691a2008-10-05 18:39:31 +000012605 raise_error_syntax("missing '))'");
Eric Andersenc470f442003-07-28 09:56:35 +000012606#endif
Denys Vlasenko216913c2018-04-02 12:35:04 +020012607 if (synstack->syntax != BASESYNTAX && eofmark == NULL)
Denis Vlasenko559691a2008-10-05 18:39:31 +000012608 raise_error_syntax("unterminated quoted string");
Denys Vlasenko216913c2018-04-02 12:35:04 +020012609 if (synstack->varnest != 0) {
Eric Andersenc470f442003-07-28 09:56:35 +000012610 /* { */
Denis Vlasenko559691a2008-10-05 18:39:31 +000012611 raise_error_syntax("missing '}'");
Eric Andersencb57d552001-06-28 07:25:16 +000012612 }
12613 USTPUTC('\0', out);
Eric Andersenc470f442003-07-28 09:56:35 +000012614 len = out - (char *)stackblock();
Eric Andersencb57d552001-06-28 07:25:16 +000012615 out = stackblock();
12616 if (eofmark == NULL) {
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010012617 if ((c == '>' || c == '<' IF_BASH_REDIR_OUTPUT( || c == 0x100 + '>'))
Denis Vlasenko834dee72008-10-07 09:18:30 +000012618 && quotef == 0
12619 ) {
Denis Vlasenko559691a2008-10-05 18:39:31 +000012620 if (isdigit_str9(out)) {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000012621 PARSEREDIR(); /* passed as params: out, c */
12622 lasttoken = TREDIR;
12623 return lasttoken;
12624 }
12625 /* else: non-number X seen, interpret it
12626 * as "NNNX>file" = "NNNX >file" */
Eric Andersencb57d552001-06-28 07:25:16 +000012627 }
Denis Vlasenkoef527f52008-06-23 01:52:30 +000012628 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000012629 }
12630 quoteflag = quotef;
12631 backquotelist = bqlist;
12632 grabstackblock(len);
12633 wordtext = out;
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012634 lasttoken = TWORD;
12635 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000012636/* end of readtoken routine */
12637
Eric Andersencb57d552001-06-28 07:25:16 +000012638/*
12639 * Check to see whether we are at the end of the here document. When this
12640 * is called, c is set to the first character of the next input line. If
12641 * we are at the end of the here document, this routine sets the c to PEOF.
12642 */
Eric Andersenc470f442003-07-28 09:56:35 +000012643checkend: {
Denys Vlasenko46999802017-07-29 21:12:29 +020012644 if (realeofmark(eofmark)) {
Denys Vlasenkoa7328982017-07-29 19:57:28 +020012645 int markloc;
12646 char *p;
12647
Denis Vlasenko131ae172007-02-18 13:00:19 +000012648#if ENABLE_ASH_ALIAS
Denys Vlasenko2ce42e92009-11-29 02:18:13 +010012649 if (c == PEOA)
12650 c = pgetc_without_PEOA();
Eric Andersenc470f442003-07-28 09:56:35 +000012651#endif
12652 if (striptabs) {
12653 while (c == '\t') {
Denys Vlasenko2ce42e92009-11-29 02:18:13 +010012654 c = pgetc_without_PEOA();
Eric Andersencb57d552001-06-28 07:25:16 +000012655 }
Eric Andersenc470f442003-07-28 09:56:35 +000012656 }
Eric Andersencb57d552001-06-28 07:25:16 +000012657
Denys Vlasenkoa7328982017-07-29 19:57:28 +020012658 markloc = out - (char *)stackblock();
12659 for (p = eofmark; STPUTC(c, out), *p; p++) {
12660 if (c != *p)
12661 goto more_heredoc;
Denys Vlasenko35e349d2019-09-05 14:31:49 +020012662 /* FIXME: fails for backslash-newlined terminator:
12663 * cat <<EOF
12664 * ...
12665 * EO\
12666 * F
12667 * (see heredoc_bkslash_newline2.tests)
12668 */
Denys Vlasenkoa7328982017-07-29 19:57:28 +020012669 c = pgetc_without_PEOA();
12670 }
12671
12672 if (c == '\n' || c == PEOF) {
12673 c = PEOF;
Roberto A. Fogliettae0bf3df2021-09-07 01:19:31 +020012674 if (trap_depth == 0)
12675 g_parsefile->linno++;
Denys Vlasenkoa7328982017-07-29 19:57:28 +020012676 needprompt = doprompt;
12677 } else {
12678 int len_here;
12679
12680 more_heredoc:
12681 p = (char *)stackblock() + markloc + 1;
12682 len_here = out - p;
12683
12684 if (len_here) {
12685 len_here -= (c >= PEOF);
12686 c = p[-1];
12687
12688 if (len_here) {
12689 char *str;
12690
12691 str = alloca(len_here + 1);
12692 *(char *)mempcpy(str, p, len_here) = '\0';
12693
12694 pushstring(str, NULL);
Eric Andersencb57d552001-06-28 07:25:16 +000012695 }
12696 }
12697 }
Denys Vlasenkoa7328982017-07-29 19:57:28 +020012698
12699 STADJUST((char *)stackblock() + markloc - out, out);
Eric Andersencb57d552001-06-28 07:25:16 +000012700 }
Eric Andersenc470f442003-07-28 09:56:35 +000012701 goto checkend_return;
12702}
Eric Andersencb57d552001-06-28 07:25:16 +000012703
Eric Andersencb57d552001-06-28 07:25:16 +000012704/*
12705 * Parse a redirection operator. The variable "out" points to a string
12706 * specifying the fd to be redirected. The variable "c" contains the
12707 * first character of the redirection operator.
12708 */
Eric Andersenc470f442003-07-28 09:56:35 +000012709parseredir: {
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000012710 /* out is already checked to be a valid number or "" */
12711 int fd = (*out == '\0' ? -1 : atoi(out));
Eric Andersenc470f442003-07-28 09:56:35 +000012712 union node *np;
Eric Andersencb57d552001-06-28 07:25:16 +000012713
Denis Vlasenko597906c2008-02-20 16:38:54 +000012714 np = stzalloc(sizeof(struct nfile));
Eric Andersenc470f442003-07-28 09:56:35 +000012715 if (c == '>') {
12716 np->nfile.fd = 1;
Denys Vlasenko220be532018-03-31 19:21:31 +020012717 c = pgetc_eatbnl();
Eric Andersenc470f442003-07-28 09:56:35 +000012718 if (c == '>')
12719 np->type = NAPPEND;
12720 else if (c == '|')
12721 np->type = NCLOBBER;
12722 else if (c == '&')
12723 np->type = NTOFD;
Denis Vlasenko559691a2008-10-05 18:39:31 +000012724 /* it also can be NTO2 (>&file), but we can't figure it out yet */
Eric Andersenc470f442003-07-28 09:56:35 +000012725 else {
12726 np->type = NTO;
12727 pungetc();
Eric Andersencb57d552001-06-28 07:25:16 +000012728 }
Denis Vlasenko834dee72008-10-07 09:18:30 +000012729 }
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010012730#if BASH_REDIR_OUTPUT
Denis Vlasenko834dee72008-10-07 09:18:30 +000012731 else if (c == 0x100 + '>') { /* this flags &> redirection */
12732 np->nfile.fd = 1;
12733 pgetc(); /* this is '>', no need to check */
12734 np->type = NTO2;
12735 }
12736#endif
12737 else { /* c == '<' */
Denis Vlasenko597906c2008-02-20 16:38:54 +000012738 /*np->nfile.fd = 0; - stzalloc did it */
Denys Vlasenko220be532018-03-31 19:21:31 +020012739 c = pgetc_eatbnl();
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012740 switch (c) {
Eric Andersenc470f442003-07-28 09:56:35 +000012741 case '<':
Denis Vlasenkoa0f82e92007-02-18 12:35:30 +000012742 if (sizeof(struct nfile) != sizeof(struct nhere)) {
Denis Vlasenko597906c2008-02-20 16:38:54 +000012743 np = stzalloc(sizeof(struct nhere));
12744 /*np->nfile.fd = 0; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000012745 }
12746 np->type = NHERE;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000012747 heredoc = stzalloc(sizeof(struct heredoc));
Eric Andersenc470f442003-07-28 09:56:35 +000012748 heredoc->here = np;
Denys Vlasenko220be532018-03-31 19:21:31 +020012749 c = pgetc_eatbnl();
Denis Vlasenko5cedb752007-02-18 19:56:41 +000012750 if (c == '-') {
Eric Andersenc470f442003-07-28 09:56:35 +000012751 heredoc->striptabs = 1;
12752 } else {
Denis Vlasenko838ffd52008-02-21 04:32:08 +000012753 /*heredoc->striptabs = 0; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000012754 pungetc();
12755 }
12756 break;
12757
12758 case '&':
12759 np->type = NFROMFD;
12760 break;
12761
12762 case '>':
12763 np->type = NFROMTO;
12764 break;
12765
12766 default:
12767 np->type = NFROM;
12768 pungetc();
12769 break;
12770 }
Eric Andersencb57d552001-06-28 07:25:16 +000012771 }
Denis Vlasenko6fbb43b2008-07-24 19:44:41 +000012772 if (fd >= 0)
12773 np->nfile.fd = fd;
Eric Andersenc470f442003-07-28 09:56:35 +000012774 redirnode = np;
12775 goto parseredir_return;
12776}
Eric Andersencb57d552001-06-28 07:25:16 +000012777
Eric Andersencb57d552001-06-28 07:25:16 +000012778/*
12779 * Parse a substitution. At this point, we have read the dollar sign
12780 * and nothing else.
12781 */
Denis Vlasenkocc571512007-02-23 21:10:35 +000012782
12783/* is_special(c) evaluates to 1 for c in "!#$*-0123456789?@"; 0 otherwise
12784 * (assuming ascii char codes, as the original implementation did) */
12785#define is_special(c) \
Denis Vlasenkoef527f52008-06-23 01:52:30 +000012786 (((unsigned)(c) - 33 < 32) \
12787 && ((0xc1ff920dU >> ((unsigned)(c) - 33)) & 1))
Eric Andersenc470f442003-07-28 09:56:35 +000012788parsesub: {
Denys Vlasenkocd716832009-11-28 22:14:02 +010012789 unsigned char subtype;
Eric Andersenc470f442003-07-28 09:56:35 +000012790 int typeloc;
Eric Andersencb57d552001-06-28 07:25:16 +000012791
Denys Vlasenko73c3e072016-09-29 17:17:04 +020012792 c = pgetc_eatbnl();
Denys Vlasenkoa7328982017-07-29 19:57:28 +020012793 if ((checkkwd & CHKEOFMARK)
12794 || c > 255 /* PEOA or PEOF */
Denis Vlasenkoef527f52008-06-23 01:52:30 +000012795 || (c != '(' && c != '{' && !is_name(c) && !is_special(c))
Eric Andersenc470f442003-07-28 09:56:35 +000012796 ) {
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010012797#if BASH_DOLLAR_SQUOTE
Denys Vlasenko216913c2018-04-02 12:35:04 +020012798 if (synstack->syntax != DQSYNTAX && c == '\'')
Denis Vlasenkoef527f52008-06-23 01:52:30 +000012799 bash_dollar_squote = 1;
12800 else
12801#endif
12802 USTPUTC('$', out);
Eric Andersenc470f442003-07-28 09:56:35 +000012803 pungetc();
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020012804 } else if (c == '(') {
12805 /* $(command) or $((arith)) */
Denys Vlasenko73c3e072016-09-29 17:17:04 +020012806 if (pgetc_eatbnl() == '(') {
Denys Vlasenko0b883582016-12-23 16:49:07 +010012807#if ENABLE_FEATURE_SH_MATH
Eric Andersenc470f442003-07-28 09:56:35 +000012808 PARSEARITH();
12809#else
Denys Vlasenko4f8079d2017-07-17 17:11:48 +020012810 raise_error_syntax("support for $((arith)) is disabled");
Eric Andersenc470f442003-07-28 09:56:35 +000012811#endif
Glenn L McGrath9fef17d2002-08-22 18:41:20 +000012812 } else {
Eric Andersenc470f442003-07-28 09:56:35 +000012813 pungetc();
12814 PARSEBACKQNEW();
12815 }
12816 } else {
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020012817 /* $VAR, $<specialchar>, ${...}, or PEOA/PEOF */
Denys Vlasenko216913c2018-04-02 12:35:04 +020012818 smalluint newsyn = synstack->syntax;
12819
Eric Andersenc470f442003-07-28 09:56:35 +000012820 USTPUTC(CTLVAR, out);
12821 typeloc = out - (char *)stackblock();
Denys Vlasenko3df14102016-10-26 16:41:13 +020012822 STADJUST(1, out);
Eric Andersenc470f442003-07-28 09:56:35 +000012823 subtype = VSNORMAL;
12824 if (c == '{') {
Denys Vlasenko73c3e072016-09-29 17:17:04 +020012825 c = pgetc_eatbnl();
Denys Vlasenkof15aa572016-10-26 15:56:53 +020012826 subtype = 0;
Eric Andersenc470f442003-07-28 09:56:35 +000012827 }
Denys Vlasenkof15aa572016-10-26 15:56:53 +020012828 varname:
Denys Vlasenko3df14102016-10-26 16:41:13 +020012829 if (is_name(c)) {
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020012830 /* $[{[#]]NAME[}] */
Eric Andersenc470f442003-07-28 09:56:35 +000012831 do {
12832 STPUTC(c, out);
Denys Vlasenko73c3e072016-09-29 17:17:04 +020012833 c = pgetc_eatbnl();
Denys Vlasenko3df14102016-10-26 16:41:13 +020012834 } while (is_in_name(c));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000012835 } else if (isdigit(c)) {
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020012836 /* $[{[#]]NUM[}] */
Eric Andersenc470f442003-07-28 09:56:35 +000012837 do {
12838 STPUTC(c, out);
Denys Vlasenko73c3e072016-09-29 17:17:04 +020012839 c = pgetc_eatbnl();
Denys Vlasenko53a7a9c2021-06-25 02:09:41 +020012840 } while ((subtype == 0 || subtype == VSLENGTH) && isdigit(c));
Denys Vlasenko58eb8052018-08-05 15:58:13 +020012841 } else if (c != '}') {
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020012842 /* $[{[#]]<specialchar>[}] */
Denys Vlasenkof15aa572016-10-26 15:56:53 +020012843 int cc = c;
12844
Denys Vlasenko73c3e072016-09-29 17:17:04 +020012845 c = pgetc_eatbnl();
Denys Vlasenkof15aa572016-10-26 15:56:53 +020012846 if (!subtype && cc == '#') {
12847 subtype = VSLENGTH;
12848 if (c == '_' || isalnum(c))
12849 goto varname;
12850 cc = c;
12851 c = pgetc_eatbnl();
12852 if (cc == '}' || c != '}') {
12853 pungetc();
12854 subtype = 0;
12855 c = cc;
12856 cc = '#';
12857 }
12858 }
Denys Vlasenko452cc1d2017-08-14 14:23:45 +020012859
12860 if (!is_special(cc)) {
12861 if (subtype == VSLENGTH)
12862 subtype = 0;
12863 goto badsub;
12864 }
12865
Denys Vlasenkof15aa572016-10-26 15:56:53 +020012866 USTPUTC(cc, out);
Denys Vlasenko58eb8052018-08-05 15:58:13 +020012867 } else
12868 goto badsub;
Denys Vlasenko452cc1d2017-08-14 14:23:45 +020012869
Eric Andersenc470f442003-07-28 09:56:35 +000012870 if (subtype == 0) {
Denys Vlasenkof8ddbe12016-07-25 03:56:00 +020012871 static const char types[] ALIGN1 = "}-+?=";
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020012872 /* ${VAR...} but not $VAR or ${#VAR} */
12873 /* c == first char after VAR */
Denys Vlasenko216913c2018-04-02 12:35:04 +020012874 int cc = c;
12875
Eric Andersenc470f442003-07-28 09:56:35 +000012876 switch (c) {
12877 case ':':
Denys Vlasenko73c3e072016-09-29 17:17:04 +020012878 c = pgetc_eatbnl();
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010012879#if BASH_SUBSTR
Denys Vlasenkof8ddbe12016-07-25 03:56:00 +020012880 /* This check is only needed to not misinterpret
12881 * ${VAR:-WORD}, ${VAR:+WORD}, ${VAR:=WORD}, ${VAR:?WORD}
12882 * constructs.
12883 */
12884 if (!strchr(types, c)) {
Denis Vlasenko92e13c22008-03-25 01:17:40 +000012885 subtype = VSSUBSTR;
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020012886 pungetc();
Denys Vlasenko88e15702016-10-26 01:55:56 +020012887 break; /* "goto badsub" is bigger (!) */
Denis Vlasenko92e13c22008-03-25 01:17:40 +000012888 }
12889#endif
Denys Vlasenko3df14102016-10-26 16:41:13 +020012890 subtype = VSNUL;
Eric Andersenc470f442003-07-28 09:56:35 +000012891 /*FALLTHROUGH*/
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020012892 default: {
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020012893 const char *p = strchr(types, c);
Eric Andersenc470f442003-07-28 09:56:35 +000012894 if (p == NULL)
Denys Vlasenko88e15702016-10-26 01:55:56 +020012895 break;
Denys Vlasenko3df14102016-10-26 16:41:13 +020012896 subtype |= p - types + VSNORMAL;
Eric Andersenc470f442003-07-28 09:56:35 +000012897 break;
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020012898 }
Eric Andersenc470f442003-07-28 09:56:35 +000012899 case '%':
Denys Vlasenko216913c2018-04-02 12:35:04 +020012900 case '#':
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020012901 subtype = (c == '#' ? VSTRIMLEFT : VSTRIMRIGHT);
Denys Vlasenko73c3e072016-09-29 17:17:04 +020012902 c = pgetc_eatbnl();
Denys Vlasenko216913c2018-04-02 12:35:04 +020012903 if (c == cc)
12904 subtype++;
12905 else
12906 pungetc();
12907
12908 newsyn = BASESYNTAX;
Denis Vlasenko92e13c22008-03-25 01:17:40 +000012909 break;
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010012910#if BASH_PATTERN_SUBST
Denis Vlasenko92e13c22008-03-25 01:17:40 +000012911 case '/':
Denys Vlasenko6040fe82010-09-12 15:03:16 +020012912 /* ${v/[/]pattern/repl} */
12913//TODO: encode pattern and repl separately.
Denys Vlasenko216913c2018-04-02 12:35:04 +020012914// Currently cases like: v=1;echo ${v/$((1/1))/ONE}
12915// are broken (should print "ONE")
Denis Vlasenko92e13c22008-03-25 01:17:40 +000012916 subtype = VSREPLACE;
Denys Vlasenko216913c2018-04-02 12:35:04 +020012917 newsyn = BASESYNTAX;
Denys Vlasenko73c3e072016-09-29 17:17:04 +020012918 c = pgetc_eatbnl();
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020012919 if (c != '/')
Denys Vlasenko88e15702016-10-26 01:55:56 +020012920 goto badsub;
Denys Vlasenkob0fbe4b2010-08-05 17:19:27 +020012921 subtype++; /* VSREPLACEALL */
Denis Vlasenko92e13c22008-03-25 01:17:40 +000012922 break;
12923#endif
Eric Andersencb57d552001-06-28 07:25:16 +000012924 }
Eric Andersenc470f442003-07-28 09:56:35 +000012925 } else {
Denys Vlasenko53a7a9c2021-06-25 02:09:41 +020012926 if (subtype == VSLENGTH && c != '}')
12927 subtype = 0;
Denys Vlasenko88e15702016-10-26 01:55:56 +020012928 badsub:
Eric Andersenc470f442003-07-28 09:56:35 +000012929 pungetc();
12930 }
Denys Vlasenko216913c2018-04-02 12:35:04 +020012931
Denys Vlasenkof50e1462018-04-02 21:00:59 +020012932 if (newsyn == ARISYNTAX)
Denys Vlasenko216913c2018-04-02 12:35:04 +020012933 newsyn = DQSYNTAX;
12934
Denys Vlasenkof50e1462018-04-02 21:00:59 +020012935 if ((newsyn != synstack->syntax || synstack->innerdq)
12936 && subtype != VSNORMAL
12937 ) {
Denys Vlasenko216913c2018-04-02 12:35:04 +020012938 synstack_push(&synstack,
12939 synstack->prev ?: alloca(sizeof(*synstack)),
12940 newsyn);
12941
12942 synstack->varpushed = 1;
12943 synstack->dblquote = newsyn != BASESYNTAX;
12944 }
12945
Denys Vlasenko3df14102016-10-26 16:41:13 +020012946 ((unsigned char *)stackblock())[typeloc] = subtype;
Eric Andersenc470f442003-07-28 09:56:35 +000012947 if (subtype != VSNORMAL) {
Denys Vlasenko216913c2018-04-02 12:35:04 +020012948 synstack->varnest++;
12949 if (synstack->dblquote)
12950 synstack->dqvarnest++;
Eric Andersencb57d552001-06-28 07:25:16 +000012951 }
Denys Vlasenko88e15702016-10-26 01:55:56 +020012952 STPUTC('=', out);
Eric Andersencb57d552001-06-28 07:25:16 +000012953 }
Eric Andersenc470f442003-07-28 09:56:35 +000012954 goto parsesub_return;
12955}
Eric Andersencb57d552001-06-28 07:25:16 +000012956
Eric Andersencb57d552001-06-28 07:25:16 +000012957/*
12958 * Called to parse command substitutions. Newstyle is set if the command
12959 * is enclosed inside $(...); nlpp is a pointer to the head of the linked
12960 * list of commands (passed by reference), and savelen is the number of
12961 * characters on the top of the stack which must be preserved.
12962 */
Eric Andersenc470f442003-07-28 09:56:35 +000012963parsebackq: {
12964 struct nodelist **nlpp;
Eric Andersenc470f442003-07-28 09:56:35 +000012965 union node *n;
Ron Yorston072fc602015-07-01 16:46:18 +010012966 char *str;
Eric Andersenc470f442003-07-28 09:56:35 +000012967 size_t savelen;
Denys Vlasenko74aaf052020-02-17 12:11:26 +010012968 struct heredoc *saveheredoclist;
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000012969 smallint saveprompt = 0;
12970
Eric Andersenc470f442003-07-28 09:56:35 +000012971 str = NULL;
12972 savelen = out - (char *)stackblock();
12973 if (savelen > 0) {
Denys Vlasenko7bc3d392016-09-17 20:53:47 +020012974 /*
12975 * FIXME: this can allocate very large block on stack and SEGV.
12976 * Example:
12977 * echo "..<100kbytes>..`true` $(true) `true` ..."
Denys Vlasenko73737592016-09-17 20:58:22 +020012978 * allocates 100kb for every command subst. With about
Denys Vlasenko7bc3d392016-09-17 20:53:47 +020012979 * a hundred command substitutions stack overflows.
12980 * With larger prepended string, SEGV happens sooner.
12981 */
Ron Yorston072fc602015-07-01 16:46:18 +010012982 str = alloca(savelen);
Eric Andersenc470f442003-07-28 09:56:35 +000012983 memcpy(str, stackblock(), savelen);
12984 }
Denys Vlasenko7bc3d392016-09-17 20:53:47 +020012985
Eric Andersenc470f442003-07-28 09:56:35 +000012986 if (oldstyle) {
12987 /* We must read until the closing backquote, giving special
Denys Vlasenko60cb48c2013-01-14 15:57:44 +010012988 * treatment to some slashes, and then push the string and
12989 * reread it as input, interpreting it normally.
12990 */
Eric Andersenc470f442003-07-28 09:56:35 +000012991 char *pout;
Eric Andersenc470f442003-07-28 09:56:35 +000012992 size_t psavelen;
12993 char *pstr;
12994
Eric Andersenc470f442003-07-28 09:56:35 +000012995 STARTSTACKSTR(pout);
12996 for (;;) {
Denys Vlasenko958581a2010-09-12 15:04:27 +020012997 int pc;
12998
12999 setprompt_if(needprompt, 2);
Denys Vlasenko220be532018-03-31 19:21:31 +020013000 pc = pgetc_eatbnl();
Denis Vlasenko5cedb752007-02-18 19:56:41 +000013001 switch (pc) {
Eric Andersenc470f442003-07-28 09:56:35 +000013002 case '`':
13003 goto done;
13004
13005 case '\\':
Denys Vlasenko777a6352020-09-29 16:25:32 +020013006 pc = pgetc(); /* not pgetc_eatbnl! */
Eric Andersenc470f442003-07-28 09:56:35 +000013007 if (pc != '\\' && pc != '`' && pc != '$'
Denys Vlasenko216913c2018-04-02 12:35:04 +020013008 && (!synstack->dblquote || pc != '"')
Denys Vlasenko76bc2d62009-11-29 01:37:46 +010013009 ) {
Eric Andersenc470f442003-07-28 09:56:35 +000013010 STPUTC('\\', pout);
Denys Vlasenko76bc2d62009-11-29 01:37:46 +010013011 }
Denys Vlasenkocd716832009-11-28 22:14:02 +010013012 if (pc <= 255 /* not PEOA or PEOF */) {
Eric Andersenc470f442003-07-28 09:56:35 +000013013 break;
13014 }
13015 /* fall through */
13016
13017 case PEOF:
Denys Vlasenko2ce42e92009-11-29 02:18:13 +010013018 IF_ASH_ALIAS(case PEOA:)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013019 raise_error_syntax("EOF in backquote substitution");
Eric Andersenc470f442003-07-28 09:56:35 +000013020
13021 case '\n':
Denys Vlasenkoce332a22016-10-02 23:47:34 +020013022 nlnoprompt();
Eric Andersenc470f442003-07-28 09:56:35 +000013023 break;
13024
13025 default:
13026 break;
13027 }
13028 STPUTC(pc, pout);
13029 }
Denis Vlasenko5cedb752007-02-18 19:56:41 +000013030 done:
Eric Andersenc470f442003-07-28 09:56:35 +000013031 STPUTC('\0', pout);
13032 psavelen = pout - (char *)stackblock();
13033 if (psavelen > 0) {
13034 pstr = grabstackstr(pout);
13035 setinputstring(pstr);
13036 }
13037 }
13038 nlpp = &bqlist;
13039 while (*nlpp)
13040 nlpp = &(*nlpp)->next;
Denis Vlasenko597906c2008-02-20 16:38:54 +000013041 *nlpp = stzalloc(sizeof(**nlpp));
13042 /* (*nlpp)->next = NULL; - stzalloc did it */
Eric Andersenc470f442003-07-28 09:56:35 +000013043
Denys Vlasenko74aaf052020-02-17 12:11:26 +010013044 saveheredoclist = heredoclist;
13045 heredoclist = NULL;
13046
Eric Andersenc470f442003-07-28 09:56:35 +000013047 if (oldstyle) {
13048 saveprompt = doprompt;
13049 doprompt = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000013050 }
13051
Eric Andersenc470f442003-07-28 09:56:35 +000013052 n = list(2);
13053
13054 if (oldstyle)
13055 doprompt = saveprompt;
Denys Vlasenko74aaf052020-02-17 12:11:26 +010013056 else {
13057 if (readtoken() != TRP)
13058 raise_error_unexpected_syntax(TRP);
13059 setinputstring(nullstr);
Denys Vlasenko74aaf052020-02-17 12:11:26 +010013060 }
13061
Denys Vlasenko9a1a6592020-02-22 16:39:27 +010013062 parseheredoc();
Denys Vlasenko74aaf052020-02-17 12:11:26 +010013063 heredoclist = saveheredoclist;
Eric Andersenc470f442003-07-28 09:56:35 +000013064
13065 (*nlpp)->n = n;
Denys Vlasenko74aaf052020-02-17 12:11:26 +010013066 /* Start reading from old file again. */
13067 popfile();
13068 /* Ignore any pushed back tokens left from the backquote parsing. */
13069 if (oldstyle)
Eric Andersenc470f442003-07-28 09:56:35 +000013070 tokpushback = 0;
Denys Vlasenkoc55847f2020-02-17 15:59:08 +010013071 out = growstackto(savelen + 1);
Eric Andersenc470f442003-07-28 09:56:35 +000013072 if (str) {
13073 memcpy(out, str, savelen);
13074 STADJUST(savelen, out);
Eric Andersenc470f442003-07-28 09:56:35 +000013075 }
Ron Yorstona1b0d382020-07-23 08:32:27 +010013076#if BASH_PROCESS_SUBST
13077 if (style == PSUB)
13078 USTPUTC(c == '<' ? CTLFROMPROC : CTLTOPROC, out);
13079 else
13080#endif
13081 USTPUTC(CTLBACKQ, out);
Eric Andersenc470f442003-07-28 09:56:35 +000013082 if (oldstyle)
13083 goto parsebackq_oldreturn;
Ron Yorstona1b0d382020-07-23 08:32:27 +010013084#if BASH_PROCESS_SUBST
13085 else if (style == PSUB)
13086 goto parsebackq_psreturn;
13087#endif
Denis Vlasenkoa624c112007-02-19 22:45:43 +000013088 goto parsebackq_newreturn;
Eric Andersenc470f442003-07-28 09:56:35 +000013089}
13090
Denys Vlasenko0b883582016-12-23 16:49:07 +010013091#if ENABLE_FEATURE_SH_MATH
Eric Andersencb57d552001-06-28 07:25:16 +000013092/*
13093 * Parse an arithmetic expansion (indicate start of one and set state)
13094 */
Eric Andersenc470f442003-07-28 09:56:35 +000013095parsearith: {
Denys Vlasenko216913c2018-04-02 12:35:04 +020013096
13097 synstack_push(&synstack,
13098 synstack->prev ?: alloca(sizeof(*synstack)),
13099 ARISYNTAX);
13100 synstack->dblquote = 1;
Ron Yorstonad88bde2015-05-18 09:56:16 +020013101 USTPUTC(CTLARI, out);
Eric Andersenc470f442003-07-28 09:56:35 +000013102 goto parsearith_return;
13103}
13104#endif
Eric Andersenc470f442003-07-28 09:56:35 +000013105} /* end of readtoken */
13106
Eric Andersencb57d552001-06-28 07:25:16 +000013107/*
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013108 * Read the next input token.
13109 * If the token is a word, we set backquotelist to the list of cmds in
13110 * backquotes. We set quoteflag to true if any part of the word was
13111 * quoted.
13112 * If the token is TREDIR, then we set redirnode to a structure containing
13113 * the redirection.
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013114 *
13115 * [Change comment: here documents and internal procedures]
13116 * [Readtoken shouldn't have any arguments. Perhaps we should make the
13117 * word parsing code into a separate routine. In this case, readtoken
13118 * doesn't need to have any internal procedures, but parseword does.
13119 * We could also make parseoperator in essence the main routine, and
13120 * have parseword (readtoken1?) handle both words and redirection.]
Eric Andersencb57d552001-06-28 07:25:16 +000013121 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013122#define NEW_xxreadtoken
13123#ifdef NEW_xxreadtoken
13124/* singles must be first! */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000013125static const char xxreadtoken_chars[7] ALIGN1 = {
Denis Vlasenko834dee72008-10-07 09:18:30 +000013126 '\n', '(', ')', /* singles */
13127 '&', '|', ';', /* doubles */
13128 0
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000013129};
Eric Andersencb57d552001-06-28 07:25:16 +000013130
Denis Vlasenko834dee72008-10-07 09:18:30 +000013131#define xxreadtoken_singles 3
13132#define xxreadtoken_doubles 3
13133
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000013134static const char xxreadtoken_tokens[] ALIGN1 = {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013135 TNL, TLP, TRP, /* only single occurrence allowed */
13136 TBACKGND, TPIPE, TSEMI, /* if single occurrence */
13137 TEOF, /* corresponds to trailing nul */
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000013138 TAND, TOR, TENDCASE /* if double occurrence */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013139};
13140
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013141static int
13142xxreadtoken(void)
13143{
13144 int c;
13145
13146 if (tokpushback) {
13147 tokpushback = 0;
13148 return lasttoken;
Eric Andersencb57d552001-06-28 07:25:16 +000013149 }
Denys Vlasenko958581a2010-09-12 15:04:27 +020013150 setprompt_if(needprompt, 2);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013151 for (;;) { /* until token or start of word found */
Denys Vlasenko220be532018-03-31 19:21:31 +020013152 c = pgetc_eatbnl();
Denis Vlasenko5e34ff22009-04-21 11:09:40 +000013153 if (c == ' ' || c == '\t' IF_ASH_ALIAS( || c == PEOA))
Denis Vlasenko176d49d2008-10-06 09:51:47 +000013154 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013155
Denis Vlasenko176d49d2008-10-06 09:51:47 +000013156 if (c == '#') {
13157 while ((c = pgetc()) != '\n' && c != PEOF)
13158 continue;
13159 pungetc();
13160 } else if (c == '\\') {
Denys Vlasenko220be532018-03-31 19:21:31 +020013161 break; /* return readtoken1(...) */
Denis Vlasenko176d49d2008-10-06 09:51:47 +000013162 } else {
13163 const char *p;
13164
13165 p = xxreadtoken_chars + sizeof(xxreadtoken_chars) - 1;
13166 if (c != PEOF) {
13167 if (c == '\n') {
Denys Vlasenkoce332a22016-10-02 23:47:34 +020013168 nlnoprompt();
Denis Vlasenko176d49d2008-10-06 09:51:47 +000013169 }
13170
13171 p = strchr(xxreadtoken_chars, c);
Denis Vlasenko834dee72008-10-07 09:18:30 +000013172 if (p == NULL)
13173 break; /* return readtoken1(...) */
Denis Vlasenko176d49d2008-10-06 09:51:47 +000013174
Denis Vlasenko834dee72008-10-07 09:18:30 +000013175 if ((int)(p - xxreadtoken_chars) >= xxreadtoken_singles) {
Denys Vlasenko1e5111b2018-04-01 03:04:55 +020013176 int cc = pgetc_eatbnl();
Denis Vlasenko834dee72008-10-07 09:18:30 +000013177 if (cc == c) { /* double occurrence? */
Denis Vlasenko176d49d2008-10-06 09:51:47 +000013178 p += xxreadtoken_doubles + 1;
13179 } else {
13180 pungetc();
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010013181#if BASH_REDIR_OUTPUT
Denis Vlasenko834dee72008-10-07 09:18:30 +000013182 if (c == '&' && cc == '>') /* &> */
13183 break; /* return readtoken1(...) */
13184#endif
Denis Vlasenko176d49d2008-10-06 09:51:47 +000013185 }
13186 }
13187 }
13188 lasttoken = xxreadtoken_tokens[p - xxreadtoken_chars];
13189 return lasttoken;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013190 }
Denis Vlasenko176d49d2008-10-06 09:51:47 +000013191 } /* for (;;) */
Denis Vlasenko834dee72008-10-07 09:18:30 +000013192
13193 return readtoken1(c, BASESYNTAX, (char *) NULL, 0);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013194}
Denis Vlasenko176d49d2008-10-06 09:51:47 +000013195#else /* old xxreadtoken */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013196#define RETURN(token) return lasttoken = token
13197static int
13198xxreadtoken(void)
13199{
13200 int c;
13201
13202 if (tokpushback) {
13203 tokpushback = 0;
13204 return lasttoken;
13205 }
Denys Vlasenko958581a2010-09-12 15:04:27 +020013206 setprompt_if(needprompt, 2);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013207 for (;;) { /* until token or start of word found */
Denys Vlasenko220be532018-03-31 19:21:31 +020013208 c = pgetc_eatbnl();
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013209 switch (c) {
13210 case ' ': case '\t':
Denys Vlasenko2ce42e92009-11-29 02:18:13 +010013211 IF_ASH_ALIAS(case PEOA:)
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013212 continue;
13213 case '#':
Denis Vlasenkof7d56652008-03-25 05:51:41 +000013214 while ((c = pgetc()) != '\n' && c != PEOF)
13215 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013216 pungetc();
13217 continue;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013218 case '\n':
Denys Vlasenkoce332a22016-10-02 23:47:34 +020013219 nlnoprompt();
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013220 RETURN(TNL);
13221 case PEOF:
13222 RETURN(TEOF);
13223 case '&':
Denys Vlasenko220be532018-03-31 19:21:31 +020013224 if (pgetc_eatbnl() == '&')
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013225 RETURN(TAND);
13226 pungetc();
13227 RETURN(TBACKGND);
13228 case '|':
Denys Vlasenko220be532018-03-31 19:21:31 +020013229 if (pgetc_eatbnl() == '|')
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013230 RETURN(TOR);
13231 pungetc();
13232 RETURN(TPIPE);
13233 case ';':
Denys Vlasenko220be532018-03-31 19:21:31 +020013234 if (pgetc_eatbnl() == ';')
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013235 RETURN(TENDCASE);
13236 pungetc();
13237 RETURN(TSEMI);
13238 case '(':
13239 RETURN(TLP);
13240 case ')':
13241 RETURN(TRP);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013242 }
Denys Vlasenko220be532018-03-31 19:21:31 +020013243 break;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013244 }
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013245 return readtoken1(c, BASESYNTAX, (char *)NULL, 0);
13246#undef RETURN
13247}
Denis Vlasenko176d49d2008-10-06 09:51:47 +000013248#endif /* old xxreadtoken */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013249
13250static int
13251readtoken(void)
13252{
13253 int t;
Ron Yorston713f07d2015-10-29 16:44:56 +000013254 int kwd = checkkwd;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013255#if DEBUG
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000013256 smallint alreadyseen = tokpushback;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013257#endif
13258
13259#if ENABLE_ASH_ALIAS
13260 top:
13261#endif
13262
13263 t = xxreadtoken();
13264
13265 /*
13266 * eat newlines
13267 */
Ron Yorston713f07d2015-10-29 16:44:56 +000013268 if (kwd & CHKNL) {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013269 while (t == TNL) {
13270 parseheredoc();
13271 t = xxreadtoken();
13272 }
13273 }
13274
13275 if (t != TWORD || quoteflag) {
13276 goto out;
13277 }
13278
13279 /*
13280 * check for keywords
13281 */
Ron Yorston713f07d2015-10-29 16:44:56 +000013282 if (kwd & CHKKWD) {
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013283 const char *const *pp;
13284
13285 pp = findkwd(wordtext);
13286 if (pp) {
13287 lasttoken = t = pp - tokname_array;
Denys Vlasenko888527c2016-10-02 16:54:17 +020013288 TRACE(("keyword '%s' recognized\n", tokname_array[t]));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013289 goto out;
13290 }
13291 }
13292
13293 if (checkkwd & CHKALIAS) {
13294#if ENABLE_ASH_ALIAS
13295 struct alias *ap;
13296 ap = lookupalias(wordtext, 1);
13297 if (ap != NULL) {
13298 if (*ap->val) {
13299 pushstring(ap->val, ap);
13300 }
13301 goto top;
13302 }
13303#endif
13304 }
13305 out:
13306 checkkwd = 0;
13307#if DEBUG
13308 if (!alreadyseen)
Denys Vlasenko888527c2016-10-02 16:54:17 +020013309 TRACE(("token '%s' %s\n", tokname_array[t], t == TWORD ? wordtext : ""));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013310 else
Denys Vlasenko888527c2016-10-02 16:54:17 +020013311 TRACE(("reread token '%s' %s\n", tokname_array[t], t == TWORD ? wordtext : ""));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013312#endif
13313 return t;
Eric Andersencb57d552001-06-28 07:25:16 +000013314}
13315
Ron Yorstonc0e00762015-10-29 11:30:55 +000013316static int
Ron Yorston6bd2fab2015-10-29 11:30:22 +000013317peektoken(void)
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013318{
13319 int t;
13320
13321 t = readtoken();
Denis Vlasenkobcceb0c2007-09-21 18:06:20 +000013322 tokpushback = 1;
Ron Yorstonc0e00762015-10-29 11:30:55 +000013323 return t;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013324}
Eric Andersencb57d552001-06-28 07:25:16 +000013325
13326/*
Denys Vlasenko86e83ec2009-07-23 22:07:07 +020013327 * Read and parse a command. Returns NODE_EOF on end of file.
13328 * (NULL is a valid parse tree indicating a blank line.)
Eric Andersencb57d552001-06-28 07:25:16 +000013329 */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013330static union node *
13331parsecmd(int interact)
Eric Andersen90898442003-08-06 11:20:52 +000013332{
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013333 tokpushback = 0;
Ron Yorstonc0e00762015-10-29 11:30:55 +000013334 checkkwd = 0;
13335 heredoclist = 0;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013336 doprompt = interact;
Denys Vlasenko958581a2010-09-12 15:04:27 +020013337 setprompt_if(doprompt, doprompt);
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013338 needprompt = 0;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013339 return list(1);
13340}
13341
13342/*
13343 * Input any here documents.
13344 */
13345static void
13346parseheredoc(void)
13347{
13348 struct heredoc *here;
13349 union node *n;
13350
13351 here = heredoclist;
Denis Vlasenko838ffd52008-02-21 04:32:08 +000013352 heredoclist = NULL;
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013353
13354 while (here) {
Christoph Schulz03ad7ae2018-11-20 17:45:52 +010013355 tokpushback = 0;
Denys Vlasenko958581a2010-09-12 15:04:27 +020013356 setprompt_if(needprompt, 2);
Denys Vlasenkoacf79f92020-02-14 16:12:06 +010013357 if (here->here->type == NHERE)
13358 readtoken1(pgetc(), SQSYNTAX, here->eofmark, here->striptabs);
13359 else
13360 readtoken1(pgetc_eatbnl(), DQSYNTAX, here->eofmark, here->striptabs);
Denis Vlasenko597906c2008-02-20 16:38:54 +000013361 n = stzalloc(sizeof(struct narg));
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013362 n->narg.type = NARG;
Denis Vlasenko597906c2008-02-20 16:38:54 +000013363 /*n->narg.next = NULL; - stzalloc did it */
Denis Vlasenkoaa744452007-02-23 01:04:22 +000013364 n->narg.text = wordtext;
13365 n->narg.backquote = backquotelist;
13366 here->here->nhere.doc = n;
13367 here = here->next;
Eric Andersencb57d552001-06-28 07:25:16 +000013368 }
Eric Andersencb57d552001-06-28 07:25:16 +000013369}
13370
13371
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000013372static const char *
Denys Vlasenko46999802017-07-29 21:12:29 +020013373expandstr(const char *ps, int syntax_type)
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000013374{
13375 union node n;
Denys Vlasenko2a6d29a2016-10-25 21:17:52 +020013376 int saveprompt;
Ron Yorston48645b82019-04-18 09:48:13 +010013377 struct parsefile *file_stop = g_parsefile;
Ron Yorstond1a2fa22019-04-18 09:49:13 +010013378 volatile int saveint;
13379 struct jmploc *volatile savehandler = exception_handler;
13380 struct jmploc jmploc;
Ron Yorstonb0c711e2020-01-23 11:26:08 +000013381 const char *volatile result;
13382 int err;
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000013383
Denys Vlasenko46999802017-07-29 21:12:29 +020013384 /* XXX Fix (char *) cast. */
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000013385 setinputstring((char *)ps);
Denys Vlasenko2a6d29a2016-10-25 21:17:52 +020013386
13387 saveprompt = doprompt;
13388 doprompt = 0;
Ron Yorstonb0c711e2020-01-23 11:26:08 +000013389 result = ps;
13390
13391 SAVE_INT(saveint);
13392 err = setjmp(jmploc.loc);
13393 if (err)
13394 goto out;
Denys Vlasenkoa2e32b32017-10-12 19:20:13 +020013395
13396 /* readtoken1() might die horribly.
Denys Vlasenko3c183a82017-10-12 19:35:42 +020013397 * Try a prompt with syntactically wrong command:
Denys Vlasenkoa2e32b32017-10-12 19:20:13 +020013398 * PS1='$(date "+%H:%M:%S) > '
13399 */
Ron Yorstonb0c711e2020-01-23 11:26:08 +000013400 exception_handler = &jmploc;
13401 readtoken1(pgetc(), syntax_type, FAKEEOFMARK, 0);
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000013402
13403 n.narg.type = NARG;
13404 n.narg.next = NULL;
13405 n.narg.text = wordtext;
13406 n.narg.backquote = backquotelist;
13407
Ron Yorstond1a2fa22019-04-18 09:49:13 +010013408 /* expandarg() might fail too:
13409 * PS1='$((123+))'
13410 */
Ron Yorstonb0c711e2020-01-23 11:26:08 +000013411 expandarg(&n, NULL, EXP_QUOTED);
13412 result = stackblock();
13413
13414out:
Ron Yorstond1a2fa22019-04-18 09:49:13 +010013415 exception_handler = savehandler;
Ron Yorstonb0c711e2020-01-23 11:26:08 +000013416 if (err && exception_type != EXERROR)
13417 longjmp(exception_handler->loc, 1);
Ron Yorstond1a2fa22019-04-18 09:49:13 +010013418 RESTORE_INT(saveint);
13419
Ron Yorstonb0c711e2020-01-23 11:26:08 +000013420 doprompt = saveprompt;
13421 /* Try: PS1='`xxx(`' */
13422 unwindfiles(file_stop);
13423
13424 return result;
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000013425}
"Vladimir N. Oleynik"bef14d72005-09-05 13:25:11 +000013426
Denys Vlasenko1e3e2cc2017-07-25 20:31:14 +020013427static inline int
13428parser_eof(void)
13429{
13430 return tokpushback && lasttoken == TEOF;
13431}
13432
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013433/*
13434 * Execute a command or commands contained in a string.
13435 */
13436static int
Denys Vlasenko7b3fa1e2016-10-01 15:10:16 +020013437evalstring(char *s, int flags)
Eric Andersenc470f442003-07-28 09:56:35 +000013438{
Denys Vlasenko493b9ca2016-10-30 18:27:14 +010013439 struct jmploc *volatile savehandler;
Denys Vlasenkod81e9f52016-10-28 15:43:50 +020013440 struct jmploc jmploc;
13441 int ex;
13442
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013443 union node *n;
13444 struct stackmark smark;
Denys Vlasenko928e2a72016-09-29 00:30:31 +020013445 int status;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013446
Denys Vlasenko8e2bc472016-09-28 23:02:57 +020013447 s = sstrdup(s);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013448 setinputstring(s);
13449 setstackmark(&smark);
13450
Denys Vlasenko928e2a72016-09-29 00:30:31 +020013451 status = 0;
Denys Vlasenkod81e9f52016-10-28 15:43:50 +020013452 /* On exception inside execution loop, we must popfile().
13453 * Try interactively:
13454 * readonly a=a
13455 * command eval "a=b" # throws "is read only" error
13456 * "command BLTIN" is not supposed to abort (even in non-interactive use).
13457 * But if we skip popfile(), we hit EOF in eval's string, and exit.
13458 */
13459 savehandler = exception_handler;
Denys Vlasenkod81e9f52016-10-28 15:43:50 +020013460 ex = setjmp(jmploc.loc);
13461 if (ex)
13462 goto out;
Denys Vlasenko493b9ca2016-10-30 18:27:14 +010013463 exception_handler = &jmploc;
Denys Vlasenkod81e9f52016-10-28 15:43:50 +020013464
Denys Vlasenko86e83ec2009-07-23 22:07:07 +020013465 while ((n = parsecmd(0)) != NODE_EOF) {
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +020013466 int i;
13467
Denys Vlasenko1e3e2cc2017-07-25 20:31:14 +020013468 i = evaltree(n, flags & ~(parser_eof() ? 0 : EV_EXIT));
Denys Vlasenko928e2a72016-09-29 00:30:31 +020013469 if (n)
13470 status = i;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013471 popstackmark(&smark);
Denys Vlasenko928e2a72016-09-29 00:30:31 +020013472 if (evalskip)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013473 break;
13474 }
Denys Vlasenkod81e9f52016-10-28 15:43:50 +020013475 out:
Denys Vlasenko8e2bc472016-09-28 23:02:57 +020013476 popstackmark(&smark);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013477 popfile();
Denys Vlasenko8e2bc472016-09-28 23:02:57 +020013478 stunalloc(s);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013479
Denys Vlasenkod81e9f52016-10-28 15:43:50 +020013480 exception_handler = savehandler;
13481 if (ex)
Denys Vlasenko14c85eb2017-10-12 19:40:47 +020013482 longjmp(exception_handler->loc, ex);
Denys Vlasenkod81e9f52016-10-28 15:43:50 +020013483
Denys Vlasenko928e2a72016-09-29 00:30:31 +020013484 return status;
Eric Andersenc470f442003-07-28 09:56:35 +000013485}
13486
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013487/*
13488 * The eval command.
13489 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020013490static int FAST_FUNC
Denys Vlasenko7b3fa1e2016-10-01 15:10:16 +020013491evalcmd(int argc UNUSED_PARAM, char **argv, int flags)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013492{
13493 char *p;
13494 char *concat;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013495
Denis Vlasenko68404f12008-03-17 09:00:54 +000013496 if (argv[1]) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013497 p = argv[1];
Denis Vlasenko68404f12008-03-17 09:00:54 +000013498 argv += 2;
13499 if (argv[0]) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013500 STARTSTACKSTR(concat);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013501 for (;;) {
13502 concat = stack_putstr(p, concat);
Denis Vlasenko68404f12008-03-17 09:00:54 +000013503 p = *argv++;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013504 if (p == NULL)
13505 break;
13506 STPUTC(' ', concat);
13507 }
13508 STPUTC('\0', concat);
13509 p = grabstackstr(concat);
13510 }
Denys Vlasenko7b3fa1e2016-10-01 15:10:16 +020013511 return evalstring(p, flags & EV_TESTED);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013512 }
Denys Vlasenko928e2a72016-09-29 00:30:31 +020013513 return 0;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013514}
13515
13516/*
Denys Vlasenko285ad152009-12-04 23:02:27 +010013517 * Read and execute commands.
13518 * "Top" is nonzero for the top level command loop;
13519 * it turns on prompting if the shell is interactive.
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013520 */
13521static int
13522cmdloop(int top)
13523{
13524 union node *n;
13525 struct stackmark smark;
13526 int inter;
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +020013527 int status = 0;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013528 int numeof = 0;
13529
13530 TRACE(("cmdloop(%d) called\n", top));
13531 for (;;) {
13532 int skip;
13533
13534 setstackmark(&smark);
13535#if JOBS
Denis Vlasenkob07a4962008-06-22 13:16:23 +000013536 if (doing_jobctl)
Denys Vlasenko9c541002015-10-07 15:44:36 +020013537 showjobs(SHOW_CHANGED|SHOW_STDERR);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013538#endif
13539 inter = 0;
13540 if (iflag && top) {
13541 inter++;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013542 chkmail();
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013543 }
13544 n = parsecmd(inter);
Denys Vlasenko7cee00e2009-07-24 01:08:03 +020013545#if DEBUG
13546 if (DEBUG > 2 && debug && (n != NODE_EOF))
Denys Vlasenko883cea42009-07-11 15:31:59 +020013547 showtree(n);
Denis Vlasenko135cecb2009-04-12 00:00:57 +000013548#endif
Denys Vlasenko86e83ec2009-07-23 22:07:07 +020013549 if (n == NODE_EOF) {
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013550 if (!top || numeof >= 50)
13551 break;
13552 if (!stoppedjobs()) {
Denys Vlasenko226b8a12020-02-16 18:57:53 +010013553 if (!Iflag) {
13554 if (iflag) {
13555 newline_and_flush(stderr);
13556 }
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013557 break;
Denys Vlasenko226b8a12020-02-16 18:57:53 +010013558 }
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013559 out2str("\nUse \"exit\" to leave shell.\n");
13560 }
13561 numeof++;
Denys Vlasenko41beb532021-09-07 01:52:21 +020013562 } else {
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +020013563 int i;
13564
Denis Vlasenkofcfaf2e2007-07-14 18:45:37 +000013565 /* job_warning can only be 2,1,0. Here 2->1, 1/0->0 */
13566 job_warning >>= 1;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013567 numeof = 0;
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +020013568 i = evaltree(n, 0);
13569 if (n)
13570 status = i;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013571 }
13572 popstackmark(&smark);
13573 skip = evalskip;
13574
13575 if (skip) {
Denys Vlasenkocd24a502020-02-20 16:47:01 +010013576 evalskip &= ~(SKIPFUNC | SKIPFUNCDEF);
Denys Vlasenko0840c912016-10-01 15:27:44 +020013577 break;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013578 }
13579 }
Denys Vlasenkoeb17b6f2016-09-28 19:41:57 +020013580 return status;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013581}
13582
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000013583/*
13584 * Take commands from a file. To be compatible we should do a path
13585 * search for the file, which is necessary to find sub-commands.
13586 */
13587static char *
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +010013588find_dot_file(char *basename)
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000013589{
13590 char *fullname;
13591 const char *path = pathval();
13592 struct stat statb;
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +010013593 int len;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000013594
13595 /* don't try this for absolute or relative paths */
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +010013596 if (strchr(basename, '/'))
13597 return basename;
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000013598
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +010013599 while ((len = padvance(&path, basename)) >= 0) {
13600 fullname = stackblock();
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +010013601 if ((!pathopt || *pathopt == 'f')
13602 && !stat(fullname, &statb) && S_ISREG(statb.st_mode)
13603 ) {
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +010013604 /* This will be freed by the caller. */
13605 return stalloc(len);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000013606 }
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000013607 }
Denys Vlasenko01f7b9e2018-01-26 15:15:43 +010013608 /* not found in PATH */
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000013609
Denys Vlasenko01f7b9e2018-01-26 15:15:43 +010013610#if ENABLE_ASH_BASH_SOURCE_CURDIR
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +010013611 return basename;
Denys Vlasenko01f7b9e2018-01-26 15:15:43 +010013612#else
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +010013613 ash_msg_and_raise_error("%s: not found", basename);
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000013614 /* NOTREACHED */
Denys Vlasenko01f7b9e2018-01-26 15:15:43 +010013615#endif
Denis Vlasenko0dec6de2007-02-23 21:10:47 +000013616}
13617
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020013618static int FAST_FUNC
Denys Vlasenko0cdb7ea2016-10-02 03:16:00 +020013619dotcmd(int argc_ UNUSED_PARAM, char **argv_ UNUSED_PARAM)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013620{
Denys Vlasenko0cdb7ea2016-10-02 03:16:00 +020013621 /* "false; . empty_file; echo $?" should print 0, not 1: */
13622 int status = 0;
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020013623 char *fullname;
Denys Vlasenko0cdb7ea2016-10-02 03:16:00 +020013624 char **argv;
Denys Vlasenkofb87d932017-01-09 08:22:06 +010013625 char *args_need_save;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013626 volatile struct shparam saveparam;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013627
Denys Vlasenko981a0562017-07-26 19:53:11 +020013628//???
13629// struct strlist *sp;
13630// for (sp = cmdenviron; sp; sp = sp->next)
13631// setvareq(ckstrdup(sp->text), VSTRFIXED | VTEXTFIXED);
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013632
Denys Vlasenko0cdb7ea2016-10-02 03:16:00 +020013633 nextopt(nullstr); /* handle possible "--" */
13634 argv = argptr;
13635
13636 if (!argv[0]) {
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020013637 /* bash says: "bash: .: filename argument required" */
13638 return 2; /* bash compat */
13639 }
13640
Denys Vlasenko091f8312013-03-17 14:25:22 +010013641 /* This aborts if file isn't found, which is POSIXly correct.
13642 * bash returns exitcode 1 instead.
13643 */
Denys Vlasenko0cdb7ea2016-10-02 03:16:00 +020013644 fullname = find_dot_file(argv[0]);
13645 argv++;
Denys Vlasenkofb87d932017-01-09 08:22:06 +010013646 args_need_save = argv[0];
Denys Vlasenko5f7c82b2017-02-03 13:00:06 +010013647 if (args_need_save) { /* ". FILE ARGS", and ARGS are not empty */
Denys Vlasenko0cdb7ea2016-10-02 03:16:00 +020013648 int argc;
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020013649 saveparam = shellparam;
13650 shellparam.malloced = 0;
Denys Vlasenko0cdb7ea2016-10-02 03:16:00 +020013651 argc = 1;
13652 while (argv[argc])
13653 argc++;
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020013654 shellparam.nparam = argc;
13655 shellparam.p = argv;
13656 };
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013657
Denys Vlasenko091f8312013-03-17 14:25:22 +010013658 /* This aborts if file can't be opened, which is POSIXly correct.
13659 * bash returns exitcode 1 instead.
13660 */
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020013661 setinputfile(fullname, INPUT_PUSH_FILE);
13662 commandname = fullname;
Denys Vlasenko0cdb7ea2016-10-02 03:16:00 +020013663 status = cmdloop(0);
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020013664 popfile();
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013665
Denys Vlasenkofb87d932017-01-09 08:22:06 +010013666 if (args_need_save) {
Denys Vlasenkoe66cf822010-05-18 09:12:53 +020013667 freeparam(&shellparam);
13668 shellparam = saveparam;
13669 };
13670
Denys Vlasenko0cdb7ea2016-10-02 03:16:00 +020013671 return status;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013672}
13673
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020013674static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000013675exitcmd(int argc UNUSED_PARAM, char **argv)
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013676{
13677 if (stoppedjobs())
13678 return 0;
Denys Vlasenko4ccddc82020-02-14 17:27:18 +010013679
Denys Vlasenko970470e2020-02-14 17:32:22 +010013680 if (argv[1])
13681 savestatus = number(argv[1]);
Denys Vlasenko4ccddc82020-02-14 17:27:18 +010013682
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013683 raise_exception(EXEXIT);
13684 /* NOTREACHED */
13685}
13686
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013687/*
13688 * Read a file containing shell functions.
13689 */
13690static void
13691readcmdfile(char *name)
13692{
13693 setinputfile(name, INPUT_PUSH_FILE);
13694 cmdloop(0);
13695 popfile();
13696}
13697
13698
Denis Vlasenkocc571512007-02-23 21:10:35 +000013699/* ============ find_command inplementation */
13700
13701/*
13702 * Resolve a command name. If you change this routine, you may have to
13703 * change the shellexec routine as well.
13704 */
13705static void
13706find_command(char *name, struct cmdentry *entry, int act, const char *path)
13707{
13708 struct tblentry *cmdp;
13709 int idx;
13710 int prev;
13711 char *fullname;
13712 struct stat statb;
13713 int e;
13714 int updatetbl;
13715 struct builtincmd *bcmd;
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +010013716 int len;
Denis Vlasenkocc571512007-02-23 21:10:35 +000013717
13718 /* If name contains a slash, don't use PATH or hash table */
13719 if (strchr(name, '/') != NULL) {
13720 entry->u.index = -1;
13721 if (act & DO_ABS) {
13722 while (stat(name, &statb) < 0) {
13723#ifdef SYSV
13724 if (errno == EINTR)
13725 continue;
13726#endif
13727 entry->cmdtype = CMDUNKNOWN;
13728 return;
13729 }
13730 }
13731 entry->cmdtype = CMDNORMAL;
13732 return;
13733 }
13734
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000013735/* #if ENABLE_FEATURE_SH_STANDALONE... moved after builtin check */
Denis Vlasenkocc571512007-02-23 21:10:35 +000013736
13737 updatetbl = (path == pathval());
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010013738 if (!updatetbl)
Denis Vlasenkocc571512007-02-23 21:10:35 +000013739 act |= DO_ALTPATH;
Denis Vlasenkocc571512007-02-23 21:10:35 +000013740
13741 /* If name is in the table, check answer will be ok */
13742 cmdp = cmdlookup(name, 0);
13743 if (cmdp != NULL) {
13744 int bit;
13745
13746 switch (cmdp->cmdtype) {
13747 default:
13748#if DEBUG
13749 abort();
13750#endif
13751 case CMDNORMAL:
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010013752 bit = DO_ALTPATH | DO_REGBLTIN;
Denis Vlasenkocc571512007-02-23 21:10:35 +000013753 break;
13754 case CMDFUNCTION:
13755 bit = DO_NOFUNC;
13756 break;
13757 case CMDBUILTIN:
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010013758 bit = IS_BUILTIN_REGULAR(cmdp->param.cmd) ? 0 : DO_REGBLTIN;
Denis Vlasenkocc571512007-02-23 21:10:35 +000013759 break;
13760 }
13761 if (act & bit) {
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010013762 if (act & bit & DO_REGBLTIN)
13763 goto fail;
13764
Denis Vlasenkocc571512007-02-23 21:10:35 +000013765 updatetbl = 0;
13766 cmdp = NULL;
13767 } else if (cmdp->rehash == 0)
13768 /* if not invalidated by cd, we're done */
13769 goto success;
13770 }
13771
13772 /* If %builtin not in path, check for builtin next */
13773 bcmd = find_builtin(name);
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000013774 if (bcmd) {
13775 if (IS_BUILTIN_REGULAR(bcmd))
13776 goto builtin_success;
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010013777 if (act & DO_ALTPATH)
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000013778 goto builtin_success;
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010013779 if (builtinloc <= 0)
13780 goto builtin_success;
Denis Vlasenkof98dc4d2007-02-23 21:11:02 +000013781 }
Denis Vlasenkocc571512007-02-23 21:10:35 +000013782
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010013783 if (act & DO_REGBLTIN)
13784 goto fail;
13785
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000013786#if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko7465dbc2008-04-13 02:25:53 +000013787 {
13788 int applet_no = find_applet_by_name(name);
13789 if (applet_no >= 0) {
13790 entry->cmdtype = CMDNORMAL;
13791 entry->u.index = -2 - applet_no;
13792 return;
13793 }
Denis Vlasenkof20de5b2007-04-29 23:42:54 +000013794 }
13795#endif
13796
Denis Vlasenkocc571512007-02-23 21:10:35 +000013797 /* We have to search path. */
13798 prev = -1; /* where to start */
13799 if (cmdp && cmdp->rehash) { /* doing a rehash */
13800 if (cmdp->cmdtype == CMDBUILTIN)
13801 prev = builtinloc;
13802 else
13803 prev = cmdp->param.index;
13804 }
13805
13806 e = ENOENT;
13807 idx = -1;
13808 loop:
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +010013809 while ((len = padvance(&path, name)) >= 0) {
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +010013810 const char *lpathopt = pathopt;
13811
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +010013812 fullname = stackblock();
Denis Vlasenkocc571512007-02-23 21:10:35 +000013813 idx++;
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +010013814 if (lpathopt) {
13815 if (*lpathopt == 'b') {
Denis Vlasenkocc571512007-02-23 21:10:35 +000013816 if (bcmd)
13817 goto builtin_success;
13818 continue;
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +010013819 } else if (!(act & DO_NOFUNC)) {
13820 /* handled below */
13821 } else {
13822 /* ignore unimplemented options */
Denis Vlasenkocc571512007-02-23 21:10:35 +000013823 continue;
13824 }
13825 }
13826 /* if rehash, don't redo absolute path names */
13827 if (fullname[0] == '/' && idx <= prev) {
13828 if (idx < prev)
13829 continue;
13830 TRACE(("searchexec \"%s\": no change\n", name));
13831 goto success;
13832 }
13833 while (stat(fullname, &statb) < 0) {
13834#ifdef SYSV
13835 if (errno == EINTR)
13836 continue;
13837#endif
13838 if (errno != ENOENT && errno != ENOTDIR)
13839 e = errno;
13840 goto loop;
13841 }
13842 e = EACCES; /* if we fail, this will be the error */
13843 if (!S_ISREG(statb.st_mode))
13844 continue;
Denys Vlasenko6c4f87e2020-02-17 16:02:40 +010013845 if (lpathopt) { /* this is a %func directory */
Denys Vlasenkob0d2dc72020-02-17 15:27:41 +010013846 stalloc(len);
Denis Vlasenkodee82b62007-07-29 14:05:27 +000013847 /* NB: stalloc will return space pointed by fullname
13848 * (because we don't have any intervening allocations
13849 * between stunalloc above and this stalloc) */
Denis Vlasenkocc571512007-02-23 21:10:35 +000013850 readcmdfile(fullname);
13851 cmdp = cmdlookup(name, 0);
13852 if (cmdp == NULL || cmdp->cmdtype != CMDFUNCTION)
13853 ash_msg_and_raise_error("%s not defined in %s", name, fullname);
13854 stunalloc(fullname);
13855 goto success;
13856 }
13857 TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname));
13858 if (!updatetbl) {
13859 entry->cmdtype = CMDNORMAL;
13860 entry->u.index = idx;
13861 return;
13862 }
13863 INT_OFF;
13864 cmdp = cmdlookup(name, 1);
13865 cmdp->cmdtype = CMDNORMAL;
13866 cmdp->param.index = idx;
13867 INT_ON;
13868 goto success;
13869 }
13870
13871 /* We failed. If there was an entry for this command, delete it */
13872 if (cmdp && updatetbl)
13873 delete_cmd_entry();
William Pitcockd8fd88a2018-01-24 18:33:18 +010013874 if (act & DO_ERR) {
13875#if ENABLE_ASH_BASH_NOT_FOUND_HOOK
13876 struct tblentry *hookp = cmdlookup("command_not_found_handle", 0);
13877 if (hookp && hookp->cmdtype == CMDFUNCTION) {
13878 char *argv[3];
13879 argv[0] = (char*) "command_not_found_handle";
13880 argv[1] = name;
13881 argv[2] = NULL;
13882 evalfun(hookp->param.func, 2, argv, 0);
13883 entry->cmdtype = CMDUNKNOWN;
13884 return;
13885 }
13886#endif
Denis Vlasenkocc571512007-02-23 21:10:35 +000013887 ash_msg("%s: %s", name, errmsg(e, "not found"));
William Pitcockd8fd88a2018-01-24 18:33:18 +010013888 }
Denys Vlasenko7eb8eec2020-02-19 15:15:13 +010013889 fail:
Denis Vlasenkocc571512007-02-23 21:10:35 +000013890 entry->cmdtype = CMDUNKNOWN;
13891 return;
13892
13893 builtin_success:
13894 if (!updatetbl) {
13895 entry->cmdtype = CMDBUILTIN;
13896 entry->u.cmd = bcmd;
13897 return;
13898 }
13899 INT_OFF;
13900 cmdp = cmdlookup(name, 1);
13901 cmdp->cmdtype = CMDBUILTIN;
13902 cmdp->param.cmd = bcmd;
13903 INT_ON;
13904 success:
13905 cmdp->rehash = 0;
13906 entry->cmdtype = cmdp->cmdtype;
13907 entry->u = cmdp->param;
13908}
13909
13910
Eric Andersencb57d552001-06-28 07:25:16 +000013911/*
Eric Andersencb57d552001-06-28 07:25:16 +000013912 * The trap builtin.
13913 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020013914static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000013915trapcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +000013916{
13917 char *action;
13918 char **ap;
Denys Vlasenko496d5bf2010-03-26 15:52:24 +010013919 int signo, exitcode;
Eric Andersencb57d552001-06-28 07:25:16 +000013920
Eric Andersenc470f442003-07-28 09:56:35 +000013921 nextopt(nullstr);
13922 ap = argptr;
13923 if (!*ap) {
Roberto A. Fogliettae0bf3df2021-09-07 01:19:31 +020013924 for (signo = 0; signo <= NTRAP_LAST; signo++) {
Denys Vlasenko21d87d42009-09-25 00:06:51 +020013925 char *tr = trap_ptr[signo];
13926 if (tr) {
Denys Vlasenkoe74aaf92009-09-27 02:05:45 +020013927 /* note: bash adds "SIG", but only if invoked
13928 * as "bash". If called as "sh", or if set -o posix,
13929 * then it prints short signal names.
13930 * We are printing short names: */
13931 out1fmt("trap -- %s %s\n",
Denys Vlasenko21d87d42009-09-25 00:06:51 +020013932 single_quote(tr),
Roberto A. Fogliettae0bf3df2021-09-07 01:19:31 +020013933 (signo == NTRAP_ERR) ? "ERR" : get_signame(signo));
Denys Vlasenko726e1a02009-09-25 02:58:20 +020013934 /* trap_ptr != trap only if we are in special-cased `trap` code.
13935 * In this case, we will exit very soon, no need to free(). */
Denys Vlasenkoe74aaf92009-09-27 02:05:45 +020013936 /* if (trap_ptr != trap && tp[0]) */
Denys Vlasenko726e1a02009-09-25 02:58:20 +020013937 /* free(tr); */
Eric Andersencb57d552001-06-28 07:25:16 +000013938 }
13939 }
Denys Vlasenko726e1a02009-09-25 02:58:20 +020013940 /*
Denys Vlasenko21d87d42009-09-25 00:06:51 +020013941 if (trap_ptr != trap) {
13942 free(trap_ptr);
13943 trap_ptr = trap;
13944 }
Denys Vlasenko726e1a02009-09-25 02:58:20 +020013945 */
Eric Andersencb57d552001-06-28 07:25:16 +000013946 return 0;
13947 }
Denys Vlasenko21d87d42009-09-25 00:06:51 +020013948
Denys Vlasenko86981e32017-07-25 20:06:17 +020013949 /* Why the second check?
13950 * "trap NUM [sig2]..." is the same as "trap - NUM [sig2]..."
13951 * In this case, NUM is signal no, not an action.
13952 */
Denis Vlasenko4e19a9c2008-07-26 13:45:57 +000013953 action = NULL;
Denys Vlasenko86981e32017-07-25 20:06:17 +020013954 if (ap[1] && !is_number(ap[0]))
Eric Andersencb57d552001-06-28 07:25:16 +000013955 action = *ap++;
Denys Vlasenko86981e32017-07-25 20:06:17 +020013956
Denys Vlasenko496d5bf2010-03-26 15:52:24 +010013957 exitcode = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000013958 while (*ap) {
Roberto A. Fogliettae0bf3df2021-09-07 01:19:31 +020013959 signo = strcmp(*ap, "ERR") == 0 ? NTRAP_ERR : get_signum(*ap);
Denys Vlasenko86981e32017-07-25 20:06:17 +020013960 if (signo < 0) {
Denys Vlasenko496d5bf2010-03-26 15:52:24 +010013961 /* Mimic bash message exactly */
13962 ash_msg("%s: invalid signal specification", *ap);
13963 exitcode = 1;
13964 goto next;
13965 }
Denis Vlasenkob012b102007-02-19 22:43:01 +000013966 INT_OFF;
Eric Andersencb57d552001-06-28 07:25:16 +000013967 if (action) {
Denis Vlasenko9f739442006-12-16 23:49:13 +000013968 if (LONE_DASH(action))
Eric Andersencb57d552001-06-28 07:25:16 +000013969 action = NULL;
Denys Vlasenkob4f51d32016-10-27 12:55:09 +020013970 else {
13971 if (action[0]) /* not NULL and not "" and not "-" */
13972 may_have_traps = 1;
Denis Vlasenko0c032a42007-02-23 01:03:40 +000013973 action = ckstrdup(action);
Denys Vlasenkob4f51d32016-10-27 12:55:09 +020013974 }
Eric Andersencb57d552001-06-28 07:25:16 +000013975 }
Denis Vlasenko60818682007-09-28 22:07:23 +000013976 free(trap[signo]);
Eric Andersencb57d552001-06-28 07:25:16 +000013977 trap[signo] = action;
Roberto A. Fogliettae0bf3df2021-09-07 01:19:31 +020013978 if (signo != 0 && signo < NSIG)
Eric Andersencb57d552001-06-28 07:25:16 +000013979 setsignal(signo);
Denis Vlasenkob012b102007-02-19 22:43:01 +000013980 INT_ON;
Denys Vlasenko496d5bf2010-03-26 15:52:24 +010013981 next:
Eric Andersencb57d552001-06-28 07:25:16 +000013982 ap++;
13983 }
Denys Vlasenko496d5bf2010-03-26 15:52:24 +010013984 return exitcode;
Eric Andersencb57d552001-06-28 07:25:16 +000013985}
13986
Eric Andersenc470f442003-07-28 09:56:35 +000013987
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000013988/* ============ Builtins */
Eric Andersenc470f442003-07-28 09:56:35 +000013989
Denys Vlasenko2ec34962014-09-08 16:52:39 +020013990#if ENABLE_ASH_HELP
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020013991static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000013992helpcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000013993{
Denis Vlasenko6b06cb82008-05-15 21:30:45 +000013994 unsigned col;
13995 unsigned i;
Eric Andersenc470f442003-07-28 09:56:35 +000013996
Denys Vlasenkod6b05eb2009-06-06 20:59:55 +020013997 out1fmt(
Denis Vlasenko34d4d892009-04-04 20:24:37 +000013998 "Built-in commands:\n"
13999 "------------------\n");
Denis Vlasenkob71c6682007-07-21 15:08:09 +000014000 for (col = 0, i = 0; i < ARRAY_SIZE(builtintab); i++) {
Eric Andersenc470f442003-07-28 09:56:35 +000014001 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '),
Denis Vlasenko52764022007-02-24 13:42:56 +000014002 builtintab[i].name + 1);
Eric Andersenc470f442003-07-28 09:56:35 +000014003 if (col > 60) {
14004 out1fmt("\n");
14005 col = 0;
14006 }
14007 }
Denys Vlasenko2ec34962014-09-08 16:52:39 +020014008# if ENABLE_FEATURE_SH_STANDALONE
Denis Vlasenko1aa7e472007-11-28 06:49:03 +000014009 {
14010 const char *a = applet_names;
14011 while (*a) {
14012 col += out1fmt("%c%s", ((col == 0) ? '\t' : ' '), a);
14013 if (col > 60) {
14014 out1fmt("\n");
14015 col = 0;
14016 }
Ron Yorston2b919582016-04-08 11:57:20 +010014017 while (*a++ != '\0')
14018 continue;
Eric Andersenc470f442003-07-28 09:56:35 +000014019 }
14020 }
Denys Vlasenko2ec34962014-09-08 16:52:39 +020014021# endif
Denys Vlasenkoebedb942016-10-02 18:45:09 +020014022 newline_and_flush(stdout);
Eric Andersenc470f442003-07-28 09:56:35 +000014023 return EXIT_SUCCESS;
14024}
Denys Vlasenko2ec34962014-09-08 16:52:39 +020014025#endif
Eric Andersenc470f442003-07-28 09:56:35 +000014026
Flemming Madsend96ffda2013-04-07 18:47:24 +020014027#if MAX_HISTORY
14028static int FAST_FUNC
14029historycmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
14030{
Ron Yorston9f3b4102019-12-16 09:31:10 +000014031 show_history(line_input_state);
Flemming Madsend96ffda2013-04-07 18:47:24 +020014032 return EXIT_SUCCESS;
14033}
14034#endif
14035
Eric Andersencb57d552001-06-28 07:25:16 +000014036/*
Eric Andersencb57d552001-06-28 07:25:16 +000014037 * The export and readonly commands.
14038 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020014039static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000014040exportcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersencb57d552001-06-28 07:25:16 +000014041{
14042 struct var *vp;
14043 char *name;
14044 const char *p;
Eric Andersenc470f442003-07-28 09:56:35 +000014045 char **aptr;
Denys Vlasenkod5275882012-10-01 13:41:17 +020014046 char opt;
14047 int flag;
14048 int flag_off;
Eric Andersencb57d552001-06-28 07:25:16 +000014049
Denys Vlasenkod5275882012-10-01 13:41:17 +020014050 /* "readonly" in bash accepts, but ignores -n.
14051 * We do the same: it saves a conditional in nextopt's param.
14052 */
14053 flag_off = 0;
14054 while ((opt = nextopt("np")) != '\0') {
14055 if (opt == 'n')
14056 flag_off = VEXPORT;
14057 }
14058 flag = VEXPORT;
14059 if (argv[0][0] == 'r') {
14060 flag = VREADONLY;
14061 flag_off = 0; /* readonly ignores -n */
14062 }
14063 flag_off = ~flag_off;
14064
Denys Vlasenko10ad6222017-04-17 16:13:32 +020014065 /*if (opt_p_not_specified) - bash doesn't check this. Try "export -p NAME" */
Denys Vlasenkod5275882012-10-01 13:41:17 +020014066 {
Denis Vlasenko2da584f2007-02-19 22:44:05 +000014067 aptr = argptr;
14068 name = *aptr;
14069 if (name) {
14070 do {
14071 p = strchr(name, '=');
14072 if (p != NULL) {
14073 p++;
14074 } else {
14075 vp = *findvar(hashvar(name), name);
14076 if (vp) {
Denys Vlasenkod5275882012-10-01 13:41:17 +020014077 vp->flags = ((vp->flags | flag) & flag_off);
Denis Vlasenko2da584f2007-02-19 22:44:05 +000014078 continue;
14079 }
Eric Andersencb57d552001-06-28 07:25:16 +000014080 }
Denys Vlasenkod5275882012-10-01 13:41:17 +020014081 setvar(name, p, (flag & flag_off));
Denis Vlasenko2da584f2007-02-19 22:44:05 +000014082 } while ((name = *++aptr) != NULL);
14083 return 0;
14084 }
Eric Andersencb57d552001-06-28 07:25:16 +000014085 }
Denys Vlasenkod5275882012-10-01 13:41:17 +020014086
14087 /* No arguments. Show the list of exported or readonly vars.
14088 * -n is ignored.
14089 */
Denis Vlasenko2da584f2007-02-19 22:44:05 +000014090 showvars(argv[0], flag, 0);
Eric Andersencb57d552001-06-28 07:25:16 +000014091 return 0;
14092}
14093
Eric Andersencb57d552001-06-28 07:25:16 +000014094/*
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000014095 * Delete a function if it exists.
Eric Andersencb57d552001-06-28 07:25:16 +000014096 */
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000014097static void
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000014098unsetfunc(const char *name)
Aaron Lehmannb6ecbdc2001-12-06 03:37:38 +000014099{
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000014100 struct tblentry *cmdp;
Eric Andersencb57d552001-06-28 07:25:16 +000014101
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000014102 cmdp = cmdlookup(name, 0);
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +020014103 if (cmdp != NULL && cmdp->cmdtype == CMDFUNCTION)
Denis Vlasenko5651bfc2007-02-23 21:08:58 +000014104 delete_cmd_entry();
Eric Andersenc470f442003-07-28 09:56:35 +000014105}
14106
Eric Andersencb57d552001-06-28 07:25:16 +000014107/*
Eric Andersencb57d552001-06-28 07:25:16 +000014108 * The unset builtin command. We unset the function before we unset the
14109 * variable to allow a function to be unset when there is a readonly variable
14110 * with the same name.
14111 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020014112static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000014113unsetcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersencb57d552001-06-28 07:25:16 +000014114{
14115 char **ap;
14116 int i;
Eric Andersenc470f442003-07-28 09:56:35 +000014117 int flag = 0;
Eric Andersencb57d552001-06-28 07:25:16 +000014118
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +020014119 while ((i = nextopt("vf")) != 0) {
Eric Andersenc470f442003-07-28 09:56:35 +000014120 flag = i;
Eric Andersencb57d552001-06-28 07:25:16 +000014121 }
Eric Andersencb57d552001-06-28 07:25:16 +000014122
Denis Vlasenko2da584f2007-02-19 22:44:05 +000014123 for (ap = argptr; *ap; ap++) {
Eric Andersenc470f442003-07-28 09:56:35 +000014124 if (flag != 'f') {
Denys Vlasenkob28d4c32017-07-25 16:29:36 +020014125 unsetvar(*ap);
14126 continue;
Eric Andersenc470f442003-07-28 09:56:35 +000014127 }
14128 if (flag != 'v')
Eric Andersencb57d552001-06-28 07:25:16 +000014129 unsetfunc(*ap);
Eric Andersencb57d552001-06-28 07:25:16 +000014130 }
Denys Vlasenkob28d4c32017-07-25 16:29:36 +020014131 return 0;
Eric Andersencb57d552001-06-28 07:25:16 +000014132}
14133
Denis Vlasenko6ca409e2007-08-12 20:58:27 +000014134static const unsigned char timescmd_str[] ALIGN1 = {
Manuel Novoa III 4456f252003-08-13 17:48:47 +000014135 ' ', offsetof(struct tms, tms_utime),
14136 '\n', offsetof(struct tms, tms_stime),
14137 ' ', offsetof(struct tms, tms_cutime),
14138 '\n', offsetof(struct tms, tms_cstime),
14139 0
14140};
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020014141static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000014142timescmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Manuel Novoa III 4456f252003-08-13 17:48:47 +000014143{
Denys Vlasenko11f2e992017-08-10 16:34:03 +020014144 unsigned clk_tck;
Manuel Novoa III 4456f252003-08-13 17:48:47 +000014145 const unsigned char *p;
14146 struct tms buf;
14147
Bartosz Golaszewski5d2e4092014-06-22 14:01:13 +020014148 clk_tck = bb_clk_tck();
Manuel Novoa III 4456f252003-08-13 17:48:47 +000014149
Denys Vlasenko11f2e992017-08-10 16:34:03 +020014150 times(&buf);
Manuel Novoa III 4456f252003-08-13 17:48:47 +000014151 p = timescmd_str;
14152 do {
Denys Vlasenko11f2e992017-08-10 16:34:03 +020014153 unsigned sec, frac;
14154 unsigned long t;
Manuel Novoa III 4456f252003-08-13 17:48:47 +000014155 t = *(clock_t *)(((char *) &buf) + p[1]);
Denys Vlasenko11f2e992017-08-10 16:34:03 +020014156 sec = t / clk_tck;
14157 frac = t % clk_tck;
14158 out1fmt("%um%u.%03us%c",
14159 sec / 60, sec % 60,
14160 (frac * 1000) / clk_tck,
Manuel Novoa III 4456f252003-08-13 17:48:47 +000014161 p[0]);
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +020014162 p += 2;
14163 } while (*p);
Manuel Novoa III 4456f252003-08-13 17:48:47 +000014164
Eric Andersencb57d552001-06-28 07:25:16 +000014165 return 0;
14166}
14167
Denys Vlasenko0b883582016-12-23 16:49:07 +010014168#if ENABLE_FEATURE_SH_MATH
Eric Andersenc470f442003-07-28 09:56:35 +000014169/*
Denys Vlasenko1ed2fb42010-06-18 14:09:48 +020014170 * The let builtin. Partially stolen from GNU Bash, the Bourne Again SHell.
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +000014171 * Copyright (C) 1987, 1989, 1991 Free Software Foundation, Inc.
Eric Andersen90898442003-08-06 11:20:52 +000014172 *
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +000014173 * Copyright (C) 2003 Vladimir Oleynik <dzo@simtreas.ru>
Eric Andersenc470f442003-07-28 09:56:35 +000014174 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020014175static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000014176letcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000014177{
Denis Vlasenko68404f12008-03-17 09:00:54 +000014178 arith_t i;
Eric Andersenc470f442003-07-28 09:56:35 +000014179
Denis Vlasenko68404f12008-03-17 09:00:54 +000014180 argv++;
14181 if (!*argv)
Denis Vlasenkob012b102007-02-19 22:43:01 +000014182 ash_msg_and_raise_error("expression expected");
Denis Vlasenko68404f12008-03-17 09:00:54 +000014183 do {
Denis Vlasenkob29eb6e2009-04-02 13:46:27 +000014184 i = ash_arith(*argv);
Denis Vlasenko68404f12008-03-17 09:00:54 +000014185 } while (*++argv);
Eric Andersenc470f442003-07-28 09:56:35 +000014186
Denis Vlasenkod9e15f22006-11-27 16:49:55 +000014187 return !i;
Eric Andersenc470f442003-07-28 09:56:35 +000014188}
Eric Andersenc470f442003-07-28 09:56:35 +000014189#endif
Eric Andersen74bcd162001-07-30 21:41:37 +000014190
Eric Andersenc470f442003-07-28 09:56:35 +000014191/*
Denis Vlasenko59f351c2008-03-25 00:07:12 +000014192 * The read builtin. Options:
14193 * -r Do not interpret '\' specially
14194 * -s Turn off echo (tty only)
14195 * -n NCHARS Read NCHARS max
14196 * -p PROMPT Display PROMPT on stderr (if input is from tty)
14197 * -t SECONDS Timeout after SECONDS (tty or pipe only)
14198 * -u FD Read from given FD instead of fd 0
Johannes Schindelin3bef5d82017-08-08 16:46:39 +020014199 * -d DELIM End on DELIM char, not newline
Eric Andersenc470f442003-07-28 09:56:35 +000014200 * This uses unbuffered input, which may be avoidable in some cases.
Denis Vlasenko59f351c2008-03-25 00:07:12 +000014201 * TODO: bash also has:
14202 * -a ARRAY Read into array[0],[1],etc
Denis Vlasenko59f351c2008-03-25 00:07:12 +000014203 * -e Use line editing (tty only)
Eric Andersenc470f442003-07-28 09:56:35 +000014204 */
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020014205static int FAST_FUNC
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000014206readcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000014207{
Denys Vlasenko19358cc2018-08-05 15:42:29 +020014208 struct builtin_read_params params;
Denys Vlasenko73067272010-01-12 22:11:24 +010014209 const char *r;
Eric Andersenc470f442003-07-28 09:56:35 +000014210 int i;
14211
Denys Vlasenko19358cc2018-08-05 15:42:29 +020014212 memset(&params, 0, sizeof(params));
14213
Johannes Schindelin3bef5d82017-08-08 16:46:39 +020014214 while ((i = nextopt("p:u:rt:n:sd:")) != '\0') {
Denis Vlasenkobf0a2012006-12-26 10:42:51 +000014215 switch (i) {
Paul Fox02eb9342005-09-07 16:56:02 +000014216 case 'p':
Denys Vlasenko19358cc2018-08-05 15:42:29 +020014217 params.opt_p = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000014218 break;
Paul Fox02eb9342005-09-07 16:56:02 +000014219 case 'n':
Denys Vlasenko19358cc2018-08-05 15:42:29 +020014220 params.opt_n = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000014221 break;
14222 case 's':
Denys Vlasenko19358cc2018-08-05 15:42:29 +020014223 params.read_flags |= BUILTIN_READ_SILENT;
Paul Fox02eb9342005-09-07 16:56:02 +000014224 break;
Paul Fox02eb9342005-09-07 16:56:02 +000014225 case 't':
Denys Vlasenko19358cc2018-08-05 15:42:29 +020014226 params.opt_t = optionarg;
Paul Fox02eb9342005-09-07 16:56:02 +000014227 break;
Paul Fox02eb9342005-09-07 16:56:02 +000014228 case 'r':
Denys Vlasenko19358cc2018-08-05 15:42:29 +020014229 params.read_flags |= BUILTIN_READ_RAW;
Paul Fox02eb9342005-09-07 16:56:02 +000014230 break;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000014231 case 'u':
Denys Vlasenko19358cc2018-08-05 15:42:29 +020014232 params.opt_u = optionarg;
Denis Vlasenko59f351c2008-03-25 00:07:12 +000014233 break;
Johannes Schindelin3bef5d82017-08-08 16:46:39 +020014234#if BASH_READ_D
14235 case 'd':
Denys Vlasenko19358cc2018-08-05 15:42:29 +020014236 params.opt_d = optionarg;
Johannes Schindelin3bef5d82017-08-08 16:46:39 +020014237 break;
14238#endif
Paul Fox02eb9342005-09-07 16:56:02 +000014239 default:
14240 break;
14241 }
Eric Andersenc470f442003-07-28 09:56:35 +000014242 }
Paul Fox02eb9342005-09-07 16:56:02 +000014243
Denys Vlasenko457825f2021-06-06 12:07:11 +020014244 if (!ENABLE_ASH_BASH_COMPAT && !argptr) {
14245 bb_simple_error_msg("read: need variable name");
14246 return 1;
14247 }
Denys Vlasenko19358cc2018-08-05 15:42:29 +020014248 params.argv = argptr;
14249 params.setvar = setvar0;
14250 params.ifs = bltinlookup("IFS"); /* can be NULL */
14251
Denys Vlasenko9e71e3c2012-09-06 13:28:10 +020014252 /* "read -s" needs to save/restore termios, can't allow ^C
14253 * to jump out of it.
14254 */
Denys Vlasenkof5470412017-05-22 19:34:45 +020014255 again:
Denys Vlasenko9e71e3c2012-09-06 13:28:10 +020014256 INT_OFF;
Denys Vlasenko19358cc2018-08-05 15:42:29 +020014257 r = shell_builtin_read(&params);
Denys Vlasenko9e71e3c2012-09-06 13:28:10 +020014258 INT_ON;
Denis Vlasenko46aeab92009-03-31 19:18:17 +000014259
Denys Vlasenkof5470412017-05-22 19:34:45 +020014260 if ((uintptr_t)r == 1 && errno == EINTR) {
Denys Vlasenko49e6bf22017-08-04 14:28:16 +020014261 /* To get SIGCHLD: sleep 1 & read x; echo $x
14262 * Correct behavior is to not exit "read"
14263 */
Denys Vlasenkof5470412017-05-22 19:34:45 +020014264 if (pending_sig == 0)
14265 goto again;
14266 }
14267
Denys Vlasenko73067272010-01-12 22:11:24 +010014268 if ((uintptr_t)r > 1)
14269 ash_msg_and_raise_error(r);
Denis Vlasenko037576d2007-10-20 18:30:38 +000014270
Denys Vlasenko73067272010-01-12 22:11:24 +010014271 return (uintptr_t)r;
Eric Andersenc470f442003-07-28 09:56:35 +000014272}
14273
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020014274static int FAST_FUNC
Denys Vlasenko6283f982015-10-07 16:56:20 +020014275umaskcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
Eric Andersenc470f442003-07-28 09:56:35 +000014276{
Denys Vlasenkoc1e2e002015-10-07 17:32:56 +020014277 static const char permuser[3] ALIGN1 = "ogu";
Eric Andersenc470f442003-07-28 09:56:35 +000014278
Eric Andersenc470f442003-07-28 09:56:35 +000014279 mode_t mask;
Eric Andersenc470f442003-07-28 09:56:35 +000014280 int symbolic_mode = 0;
14281
14282 while (nextopt("S") != '\0') {
14283 symbolic_mode = 1;
14284 }
14285
Denis Vlasenkob012b102007-02-19 22:43:01 +000014286 INT_OFF;
Eric Andersenc470f442003-07-28 09:56:35 +000014287 mask = umask(0);
14288 umask(mask);
Denis Vlasenkob012b102007-02-19 22:43:01 +000014289 INT_ON;
Eric Andersenc470f442003-07-28 09:56:35 +000014290
Denys Vlasenko6283f982015-10-07 16:56:20 +020014291 if (*argptr == NULL) {
Eric Andersenc470f442003-07-28 09:56:35 +000014292 if (symbolic_mode) {
Denys Vlasenko005c4922015-10-10 20:17:12 +020014293 char buf[sizeof(",u=rwx,g=rwx,o=rwx")];
Eric Andersenc470f442003-07-28 09:56:35 +000014294 char *p = buf;
Denys Vlasenkoc1e2e002015-10-07 17:32:56 +020014295 int i;
Eric Andersenc470f442003-07-28 09:56:35 +000014296
Denys Vlasenkoc1e2e002015-10-07 17:32:56 +020014297 i = 2;
14298 for (;;) {
Denys Vlasenko005c4922015-10-10 20:17:12 +020014299 *p++ = ',';
Eric Andersenc470f442003-07-28 09:56:35 +000014300 *p++ = permuser[i];
14301 *p++ = '=';
Denys Vlasenkoc1e2e002015-10-07 17:32:56 +020014302 /* mask is 0..0uuugggooo. i=2 selects uuu bits */
Denys Vlasenko005c4922015-10-10 20:17:12 +020014303 if (!(mask & 0400)) *p++ = 'r';
14304 if (!(mask & 0200)) *p++ = 'w';
14305 if (!(mask & 0100)) *p++ = 'x';
14306 mask <<= 3;
Denys Vlasenkoc1e2e002015-10-07 17:32:56 +020014307 if (--i < 0)
14308 break;
Eric Andersenc470f442003-07-28 09:56:35 +000014309 }
Denys Vlasenkoc1e2e002015-10-07 17:32:56 +020014310 *p = '\0';
Denys Vlasenko005c4922015-10-10 20:17:12 +020014311 puts(buf + 1);
Eric Andersenc470f442003-07-28 09:56:35 +000014312 } else {
Denys Vlasenkoec046f72015-10-07 17:57:53 +020014313 out1fmt("%04o\n", mask);
Eric Andersenc470f442003-07-28 09:56:35 +000014314 }
14315 } else {
Denys Vlasenko6283f982015-10-07 16:56:20 +020014316 char *modestr = *argptr;
Denys Vlasenko14c85eb2017-10-12 19:40:47 +020014317 /* numeric umasks are taken as-is */
14318 /* symbolic umasks are inverted: "umask a=rx" calls umask(222) */
Denys Vlasenko6283f982015-10-07 16:56:20 +020014319 if (!isdigit(modestr[0]))
14320 mask ^= 0777;
Denys Vlasenko5711a2a2015-10-07 17:55:33 +020014321 mask = bb_parse_mode(modestr, mask);
14322 if ((unsigned)mask > 0777) {
Denys Vlasenko6283f982015-10-07 16:56:20 +020014323 ash_msg_and_raise_error("illegal mode: %s", modestr);
Eric Andersenc470f442003-07-28 09:56:35 +000014324 }
Denys Vlasenko6283f982015-10-07 16:56:20 +020014325 if (!isdigit(modestr[0]))
14326 mask ^= 0777;
14327 umask(mask);
Eric Andersenc470f442003-07-28 09:56:35 +000014328 }
14329 return 0;
14330}
14331
Denys Vlasenkod5f1b1b2009-06-05 12:06:05 +020014332static int FAST_FUNC
Denys Vlasenkof3c742f2010-03-06 20:12:00 +010014333ulimitcmd(int argc UNUSED_PARAM, char **argv)
Eric Andersenc470f442003-07-28 09:56:35 +000014334{
Denys Vlasenkof3c742f2010-03-06 20:12:00 +010014335 return shell_builtin_ulimit(argv);
Eric Andersenc470f442003-07-28 09:56:35 +000014336}
14337
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000014338/* ============ main() and helpers */
14339
14340/*
Denys Vlasenkof977e002020-02-20 16:54:29 +010014341 * This routine is called when an error or an interrupt occurs in an
14342 * interactive shell and control is returned to the main command loop
14343 * but prior to exitshell.
14344 */
14345static void
14346exitreset(void)
14347{
14348 /* from eval.c: */
14349 if (savestatus >= 0) {
14350 if (exception_type == EXEXIT || evalskip == SKIPFUNCDEF)
14351 exitstatus = savestatus;
14352 savestatus = -1;
14353 }
14354 evalskip = 0;
14355 loopnest = 0;
14356
14357 /* from expand.c: */
14358 ifsfree();
14359
14360 /* from redir.c: */
14361 unwindredir(NULL);
14362}
14363
14364/*
14365 * This routine is called when an error or an interrupt occurs in an
14366 * interactive shell and control is returned to the main command loop.
14367 * (In dash, this function is auto-generated by build machinery).
14368 */
14369static void
14370reset(void)
14371{
14372 /* from input.c: */
14373 g_parsefile->left_in_buffer = 0;
14374 g_parsefile->left_in_line = 0; /* clear input buffer */
Denys Vlasenko51a471d2020-12-24 00:22:24 +010014375 g_parsefile->unget = 0;
Denys Vlasenkof977e002020-02-20 16:54:29 +010014376 popallfiles();
14377
14378 /* from var.c: */
14379 unwindlocalvars(NULL);
14380}
14381
14382/*
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000014383 * Called to exit the shell.
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014384 */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000014385static void
14386exitshell(void)
14387{
14388 struct jmploc loc;
14389 char *p;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000014390
Denys Vlasenkobede2152011-09-04 16:12:33 +020014391#if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT
Denys Vlasenko00eb23b2020-12-21 21:36:58 +010014392 save_history(line_input_state); /* may be NULL */
Denys Vlasenkobede2152011-09-04 16:12:33 +020014393#endif
Denys Vlasenko4ccddc82020-02-14 17:27:18 +010014394 savestatus = exitstatus;
14395 TRACE(("pid %d, exitshell(%d)\n", getpid(), savestatus));
14396 if (setjmp(loc.loc))
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000014397 goto out;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000014398 exception_handler = &loc;
14399 p = trap[0];
14400 if (p) {
14401 trap[0] = NULL;
Denys Vlasenko7b3fa1e2016-10-01 15:10:16 +020014402 evalskip = 0;
Roberto A. Fogliettae0bf3df2021-09-07 01:19:31 +020014403 trap_depth++;
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000014404 evalstring(p, 0);
Roberto A. Fogliettae0bf3df2021-09-07 01:19:31 +020014405 trap_depth--;
Denys Vlasenkof977e002020-02-20 16:54:29 +010014406 evalskip = SKIPFUNCDEF;
Denys Vlasenkof37e1152016-10-07 03:17:28 +020014407 /*free(p); - we'll exit soon */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000014408 }
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000014409 out:
Denys Vlasenkof977e002020-02-20 16:54:29 +010014410 exitreset();
Denys Vlasenkof37e1152016-10-07 03:17:28 +020014411 /* dash wraps setjobctl(0) in "if (setjmp(loc.loc) == 0) {...}".
14412 * our setjobctl(0) does not panic if tcsetpgrp fails inside it.
14413 */
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000014414 setjobctl(0);
Denys Vlasenkocaee80c2016-10-25 20:49:53 +020014415 flush_stdout_stderr();
Denys Vlasenkof977e002020-02-20 16:54:29 +010014416 _exit(exitstatus);
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000014417 /* NOTREACHED */
14418}
14419
Denys Vlasenko49e6bf22017-08-04 14:28:16 +020014420/* Don't inline: conserve stack of caller from having our locals too */
14421static NOINLINE void
Denis Vlasenko5c67e3e2007-02-23 01:05:03 +000014422init(void)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014423{
Denys Vlasenko82dd14a2010-05-17 10:10:01 +020014424 /* we will never free this */
14425 basepf.next_to_pgetc = basepf.buf = ckmalloc(IBUFSIZ);
Denys Vlasenko0485b672017-08-14 19:46:56 +020014426 basepf.linno = 1;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014427
Denys Vlasenko49e6bf22017-08-04 14:28:16 +020014428 sigmode[SIGCHLD - 1] = S_DFL; /* ensure we install handler even if it is SIG_IGNed */
Denys Vlasenko458c1f22016-10-27 23:51:19 +020014429 setsignal(SIGCHLD);
14430
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014431 {
14432 char **envp;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014433 const char *p;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014434
14435 initvar();
14436 for (envp = environ; envp && *envp; envp++) {
Denys Vlasenko9c143ce2017-11-02 12:56:24 +010014437/* Used to have
14438 * p = endofname(*envp);
14439 * if (p != *envp && *p == '=') {
14440 * here to weed out badly-named variables, but this breaks
14441 * scenarios where people do want them passed to children:
14442 * import os
14443 * os.environ["test-test"]="test"
14444 * if os.fork() == 0:
14445 * os.execv("ash", [ 'ash', '-c', 'eval $(export -p); echo OK' ]) # fixes this
14446 * os.execv("ash", [ 'ash', '-c', 'env | grep test-test' ]) # breaks this
14447 */
14448 if (strchr(*envp, '=')) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014449 setvareq(*envp, VEXPORT|VTEXTFIXED);
14450 }
14451 }
14452
Denys Vlasenko67dae152018-08-05 13:59:35 +020014453 setvareq((char*)defifsvar, VTEXTFIXED);
Denys Vlasenkoe627ac92016-09-30 14:36:59 +020014454 setvareq((char*)defoptindvar, VTEXTFIXED);
14455
Denys Vlasenko0a0acb52015-04-18 19:36:38 +020014456 setvar0("PPID", utoa(getppid()));
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010014457#if BASH_SHLVL_VAR
Bernhard Reutner-Fischer80f8cdf2013-11-08 14:25:24 +010014458 p = lookupvar("SHLVL");
Denys Vlasenko5680e982014-01-07 16:12:48 +010014459 setvar("SHLVL", utoa((p ? atoi(p) : 0) + 1), VEXPORT);
Denys Vlasenko7d4aec02017-01-11 14:00:38 +010014460#endif
14461#if BASH_HOSTNAME_VAR
Denys Vlasenko3fa97af2014-04-15 11:43:29 +020014462 if (!lookupvar("HOSTNAME")) {
14463 struct utsname uts;
14464 uname(&uts);
Denys Vlasenko0a0acb52015-04-18 19:36:38 +020014465 setvar0("HOSTNAME", uts.nodename);
Denys Vlasenko3fa97af2014-04-15 11:43:29 +020014466 }
Bernhard Reutner-Fischer80f8cdf2013-11-08 14:25:24 +010014467#endif
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014468 p = lookupvar("PWD");
Denys Vlasenkob0b83432011-03-07 12:34:59 +010014469 if (p) {
Denys Vlasenko49e6bf22017-08-04 14:28:16 +020014470 struct stat st1, st2;
Denys Vlasenkoef159702016-09-01 11:16:22 +020014471 if (p[0] != '/' || stat(p, &st1) || stat(".", &st2)
Denys Vlasenkob0b83432011-03-07 12:34:59 +010014472 || st1.st_dev != st2.st_dev || st1.st_ino != st2.st_ino
14473 ) {
Denys Vlasenkoef159702016-09-01 11:16:22 +020014474 p = NULL;
Denys Vlasenkob0b83432011-03-07 12:34:59 +010014475 }
14476 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014477 setpwd(p, 0);
14478 }
14479}
14480
Denys Vlasenkob0b83432011-03-07 12:34:59 +010014481
14482//usage:#define ash_trivial_usage
Denys Vlasenko1f60d882021-06-15 10:00:18 +020014483//usage: "[-il] [-|+Cabefmnuvx] [-|+o OPT]... [-c 'SCRIPT' [ARG0 ARGS] | FILE [ARGS] | -s [ARGS]]"
Denys Vlasenko4e039ba2021-01-04 03:50:38 +010014484//////// comes from ^^^^^^^^^^optletters
Denys Vlasenkob0b83432011-03-07 12:34:59 +010014485//usage:#define ash_full_usage "\n\n"
14486//usage: "Unix shell interpreter"
14487
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014488/*
14489 * Process the shell command line arguments.
14490 */
Denys Vlasenkoec05df12017-07-31 19:43:47 +020014491static int
Denis Vlasenko68404f12008-03-17 09:00:54 +000014492procargs(char **argv)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014493{
14494 int i;
14495 const char *xminusc;
14496 char **xargv;
Denys Vlasenkoec05df12017-07-31 19:43:47 +020014497 int login_sh;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014498
14499 xargv = argv;
Ron Yorston8767c122018-11-05 13:13:08 +000014500 login_sh = xargv[0] && xargv[0][0] == '-';
Denys Vlasenko4f2ef4a2018-11-01 09:53:25 +010014501#if NUM_SCRIPTS > 0
14502 if (minusc)
14503 goto setarg0;
14504#endif
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014505 arg0 = xargv[0];
Denis Vlasenko68404f12008-03-17 09:00:54 +000014506 /* if (xargv[0]) - mmm, this is always true! */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014507 xargv++;
Denys Vlasenko4f2ef4a2018-11-01 09:53:25 +010014508 argptr = xargv;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014509 for (i = 0; i < NOPTS; i++)
14510 optlist[i] = 2;
Denys Vlasenko897475a2019-06-01 16:35:09 +020014511 if (options(&login_sh)) {
Denis Vlasenko28bf6712008-02-14 15:01:47 +000014512 /* it already printed err message */
14513 raise_exception(EXERROR);
14514 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014515 xargv = argptr;
14516 xminusc = minusc;
14517 if (*xargv == NULL) {
14518 if (xminusc)
14519 ash_msg_and_raise_error(bb_msg_requires_arg, "-c");
14520 sflag = 1;
14521 }
Denys Vlasenkof3634582019-06-03 12:21:04 +020014522 if (iflag == 2 /* no explicit -i given */
14523 && sflag == 1 /* -s given (or implied) */
14524 && !minusc /* bash compat: ash -sc 'echo $-' is not interactive (dash is) */
14525 && isatty(0) && isatty(1) /* we are on tty */
14526 ) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014527 iflag = 1;
Denys Vlasenkof3634582019-06-03 12:21:04 +020014528 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014529 if (mflag == 2)
14530 mflag = iflag;
Denys Vlasenko85158b62021-01-03 12:14:58 +010014531 /* Unset options which weren't explicitly set or unset */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014532 for (i = 0; i < NOPTS; i++)
Denys Vlasenko85158b62021-01-03 12:14:58 +010014533 optlist[i] &= 1; /* same effect as "if (optlist[i] == 2) optlist[i] = 0;" */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014534#if DEBUG == 2
14535 debug = 1;
14536#endif
Denys Vlasenko5f7c82b2017-02-03 13:00:06 +010014537 /* POSIX 1003.2: first arg after "-c CMD" is $0, remainder $1... */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014538 if (xminusc) {
14539 minusc = *xargv++;
14540 if (*xargv)
14541 goto setarg0;
14542 } else if (!sflag) {
14543 setinputfile(*xargv, 0);
14544 setarg0:
14545 arg0 = *xargv++;
14546 commandname = arg0;
14547 }
14548
14549 shellparam.p = xargv;
14550#if ENABLE_ASH_GETOPTS
14551 shellparam.optind = 1;
14552 shellparam.optoff = -1;
14553#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000014554 /* assert(shellparam.malloced == 0 && shellparam.nparam == 0); */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014555 while (*xargv) {
14556 shellparam.nparam++;
14557 xargv++;
14558 }
Denys Vlasenko31df5a32020-12-13 16:36:28 +010014559
14560 /* Interactive bash re-enables SIGHUP which is SIG_IGNed on entry.
14561 * Try:
14562 * trap '' hup; bash; echo RET # type "kill -hup $$", see SIGHUP having effect
14563 * trap '' hup; bash -c 'kill -hup $$; echo ALIVE' # here SIGHUP is SIG_IGNed
14564 * NB: must do it before setting up signals (in optschanged())
14565 * and reading .profile etc (after we return from here):
14566 */
14567 if (iflag)
14568 signal(SIGHUP, SIG_DFL);
14569
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014570 optschanged();
Denys Vlasenkoec05df12017-07-31 19:43:47 +020014571
14572 return login_sh;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014573}
14574
14575/*
Denys Vlasenko2eb0a7e2016-10-27 11:28:59 +020014576 * Read /etc/profile, ~/.profile, $ENV.
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014577 */
14578static void
14579read_profile(const char *name)
14580{
Denys Vlasenko46999802017-07-29 21:12:29 +020014581 name = expandstr(name, DQSYNTAX);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014582 if (setinputfile(name, INPUT_PUSH_FILE | INPUT_NOFILE_OK) < 0)
14583 return;
Denys Vlasenko0840c912016-10-01 15:27:44 +020014584 cmdloop(0);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014585 popfile();
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014586}
14587
14588#if PROFILE
14589static short profile_buf[16384];
14590extern int etext();
14591#endif
14592
Denis Vlasenkobc54cff2007-02-23 01:05:52 +000014593/*
14594 * Main routine. We initialize things, parse the arguments, execute
14595 * profiles if we're a login shell, and then call cmdloop to execute
14596 * commands. The setjmp call sets up the location to jump to when an
14597 * exception occurs. When an exception occurs the variable "state"
14598 * is used to figure out how far we had gotten.
14599 */
Denis Vlasenko9b49a5e2007-10-11 10:05:36 +000014600int ash_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
Ron Yorston8767c122018-11-05 13:13:08 +000014601#if NUM_SCRIPTS > 0
14602int ash_main(int argc, char **argv)
14603#else
Denis Vlasenkoa60f84e2008-07-05 09:18:54 +000014604int ash_main(int argc UNUSED_PARAM, char **argv)
Ron Yorston8767c122018-11-05 13:13:08 +000014605#endif
Denys Vlasenko4f2ef4a2018-11-01 09:53:25 +010014606/* note: 'argc' is used only if embedded scripts are enabled */
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014607{
Denis Vlasenko4e12b1a2008-12-23 23:36:47 +000014608 volatile smallint state;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014609 struct jmploc jmploc;
14610 struct stackmark smark;
Denys Vlasenkoec05df12017-07-31 19:43:47 +020014611 int login_sh;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014612
Denis Vlasenko01631112007-12-16 17:20:38 +000014613 /* Initialize global data */
14614 INIT_G_misc();
14615 INIT_G_memstack();
14616 INIT_G_var();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000014617#if ENABLE_ASH_ALIAS
Denis Vlasenko01631112007-12-16 17:20:38 +000014618 INIT_G_alias();
Denis Vlasenkoee87ebf2007-12-21 22:18:16 +000014619#endif
Denis Vlasenko01631112007-12-16 17:20:38 +000014620 INIT_G_cmdtable();
14621
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014622#if PROFILE
14623 monitor(4, etext, profile_buf, sizeof(profile_buf), 50);
14624#endif
14625
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014626 state = 0;
14627 if (setjmp(jmploc.loc)) {
Denis Vlasenko7f88e342009-03-19 03:36:18 +000014628 smallint e;
Denis Vlasenko4e12b1a2008-12-23 23:36:47 +000014629 smallint s;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014630
Denys Vlasenkoafc91fa2020-02-17 11:22:59 +010014631 exitreset();
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014632
Denis Vlasenko7f88e342009-03-19 03:36:18 +000014633 e = exception_type;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014634 s = state;
Denys Vlasenkof977e002020-02-20 16:54:29 +010014635 if (e == EXEND || e == EXEXIT || s == 0 || iflag == 0 || shlvl) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014636 exitshell();
Denys Vlasenkob563f622010-09-25 17:15:13 +020014637 }
Denys Vlasenkoafc91fa2020-02-17 11:22:59 +010014638
14639 reset();
14640
Denys Vlasenkob563f622010-09-25 17:15:13 +020014641 if (e == EXINT) {
Denys Vlasenko9c541002015-10-07 15:44:36 +020014642 newline_and_flush(stderr);
Denys Vlasenkob563f622010-09-25 17:15:13 +020014643 }
Denis Vlasenko7f88e342009-03-19 03:36:18 +000014644
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014645 popstackmark(&smark);
14646 FORCE_INT_ON; /* enable interrupts */
14647 if (s == 1)
14648 goto state1;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000014649 if (s == 2)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014650 goto state2;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000014651 if (s == 3)
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014652 goto state3;
Denis Vlasenkofe1f00a2007-02-23 01:04:50 +000014653 goto state4;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014654 }
14655 exception_handler = &jmploc;
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014656 rootpid = getpid();
14657
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014658 init();
14659 setstackmark(&smark);
Denys Vlasenko4f2ef4a2018-11-01 09:53:25 +010014660
14661#if NUM_SCRIPTS > 0
14662 if (argc < 0)
14663 /* Non-NULL minusc tells procargs that an embedded script is being run */
14664 minusc = get_script_content(-argc - 1);
14665#endif
Denys Vlasenkoec05df12017-07-31 19:43:47 +020014666 login_sh = procargs(argv);
Denys Vlasenko474ed062016-10-30 18:30:29 +010014667#if DEBUG
14668 TRACE(("Shell args: "));
14669 trace_puts_args(argv);
14670#endif
Denis Vlasenko68404f12008-03-17 09:00:54 +000014671
Denys Vlasenkoec05df12017-07-31 19:43:47 +020014672 if (login_sh) {
Stefan Hellermann4ef14392013-03-15 02:45:50 +010014673 const char *hp;
14674
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014675 state = 1;
14676 read_profile("/etc/profile");
14677 state1:
14678 state = 2;
Stefan Hellermann4ef14392013-03-15 02:45:50 +010014679 hp = lookupvar("HOME");
Denys Vlasenko2eb0a7e2016-10-27 11:28:59 +020014680 if (hp)
14681 read_profile("$HOME/.profile");
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014682 }
14683 state2:
14684 state = 3;
14685 if (
14686#ifndef linux
Denis Vlasenko0c032a42007-02-23 01:03:40 +000014687 getuid() == geteuid() && getgid() == getegid() &&
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014688#endif
Denis Vlasenko0c032a42007-02-23 01:03:40 +000014689 iflag
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014690 ) {
Denys Vlasenko2eb0a7e2016-10-27 11:28:59 +020014691 const char *shinit = lookupvar("ENV");
14692 if (shinit != NULL && *shinit != '\0')
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014693 read_profile(shinit);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014694 }
Denys Vlasenko2eb0a7e2016-10-27 11:28:59 +020014695 popstackmark(&smark);
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014696 state3:
14697 state = 4;
Denis Vlasenko5c2b8142009-03-19 01:59:59 +000014698 if (minusc) {
14699 /* evalstring pushes parsefile stack.
14700 * Ensure we don't falsely claim that 0 (stdin)
Denis Vlasenko5368ad52009-03-20 10:20:08 +000014701 * is one of stacked source fds.
14702 * Testcase: ash -c 'exec 1>&0' must not complain. */
Denys Vlasenkof3634582019-06-03 12:21:04 +020014703
Denys Vlasenko79b3d422010-06-03 04:29:08 +020014704 // if (!sflag) g_parsefile->pf_fd = -1;
Denys Vlasenko08d8b3c2010-06-03 04:28:28 +020014705 // ^^ not necessary since now we special-case fd 0
Denys Vlasenko035486c2017-07-31 04:09:19 +020014706 // in save_fd_on_redirect()
Denys Vlasenkof3634582019-06-03 12:21:04 +020014707
14708 // dash: evalstring(minusc, sflag ? 0 : EV_EXIT);
14709 // The above makes
14710 // ash -sc 'echo $-'
14711 // continue reading input from stdin after running 'echo'.
14712 // bash does not do this: it prints "hBcs" and exits.
14713 evalstring(minusc, EV_EXIT);
Denis Vlasenko5c2b8142009-03-19 01:59:59 +000014714 }
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014715
14716 if (sflag || minusc == NULL) {
Denys Vlasenko4840ae82011-09-04 15:28:03 +020014717#if MAX_HISTORY > 0 && ENABLE_FEATURE_EDITING_SAVEHISTORY
Denys Vlasenko3f8ec002021-01-03 10:55:39 +010014718 if (line_input_state) {
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014719 const char *hp = lookupvar("HISTFILE");
Stefan Hellermannaeb717a2013-03-03 15:29:32 +010014720 if (!hp) {
14721 hp = lookupvar("HOME");
Stefan Hellermann4ef14392013-03-15 02:45:50 +010014722 if (hp) {
Denys Vlasenko5f7c82b2017-02-03 13:00:06 +010014723 INT_OFF;
Stefan Hellermannaeb717a2013-03-03 15:29:32 +010014724 hp = concat_path_file(hp, ".ash_history");
Denys Vlasenko0a0acb52015-04-18 19:36:38 +020014725 setvar0("HISTFILE", hp);
Stefan Hellermannaeb717a2013-03-03 15:29:32 +010014726 free((char*)hp);
Denys Vlasenko5f7c82b2017-02-03 13:00:06 +010014727 INT_ON;
Stefan Hellermannaeb717a2013-03-03 15:29:32 +010014728 hp = lookupvar("HISTFILE");
14729 }
14730 }
Denis Vlasenko5c2b8142009-03-19 01:59:59 +000014731 if (hp)
Denys Vlasenko3f8ec002021-01-03 10:55:39 +010014732 line_input_state->hist_file = xstrdup(hp);
Denys Vlasenko2c4de5b2011-03-31 13:16:52 +020014733# if ENABLE_FEATURE_SH_HISTFILESIZE
14734 hp = lookupvar("HISTFILESIZE");
14735 line_input_state->max_history = size_from_HISTFILESIZE(hp);
14736# endif
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014737 }
14738#endif
14739 state4: /* XXX ??? - why isn't this before the "if" statement */
14740 cmdloop(1);
14741 }
14742#if PROFILE
14743 monitor(0);
14744#endif
14745#ifdef GPROF
14746 {
14747 extern void _mcleanup(void);
14748 _mcleanup();
14749 }
14750#endif
Denys Vlasenkob563f622010-09-25 17:15:13 +020014751 TRACE(("End of main reached\n"));
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014752 exitshell();
14753 /* NOTREACHED */
14754}
14755
Denis Vlasenkoa624c112007-02-19 22:45:43 +000014756
Eric Andersendf82f612001-06-28 07:46:40 +000014757/*-
14758 * Copyright (c) 1989, 1991, 1993, 1994
Eric Andersen2870d962001-07-02 17:27:21 +000014759 * The Regents of the University of California. All rights reserved.
Eric Andersendf82f612001-06-28 07:46:40 +000014760 *
14761 * This code is derived from software contributed to Berkeley by
14762 * Kenneth Almquist.
14763 *
14764 * Redistribution and use in source and binary forms, with or without
14765 * modification, are permitted provided that the following conditions
14766 * are met:
14767 * 1. Redistributions of source code must retain the above copyright
14768 * notice, this list of conditions and the following disclaimer.
14769 * 2. Redistributions in binary form must reproduce the above copyright
14770 * notice, this list of conditions and the following disclaimer in the
14771 * documentation and/or other materials provided with the distribution.
"Vladimir N. Oleynik"ddc280e2005-12-15 12:01:49 +000014772 * 3. Neither the name of the University nor the names of its contributors
Eric Andersendf82f612001-06-28 07:46:40 +000014773 * may be used to endorse or promote products derived from this software
14774 * without specific prior written permission.
14775 *
Denys Vlasenko95f79532017-08-02 14:26:33 +020014776 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND
Eric Andersendf82f612001-06-28 07:46:40 +000014777 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
14778 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
14779 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
14780 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
14781 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
14782 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
14783 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
14784 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
14785 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
14786 * SUCH DAMAGE.
14787 */