blob: ca40bbb2cd8ae3c7aa8c4a3b60418ebb87d658e3 [file] [log] [blame]
Francis Laniel110b7692023-12-22 22:02:27 +01001/* vi: set sw=4 ts=4: */
2/*
3 * A prototype Bourne shell grammar parser.
4 * Intended to follow the original Thompson and Ritchie
5 * "small and simple is beautiful" philosophy, which
6 * incidentally is a good match to today's BusyBox.
7 *
8 * Copyright (C) 2000,2001 Larry Doolittle <larry@doolittle.boa.org>
9 * Copyright (C) 2008,2009 Denys Vlasenko <vda.linux@googlemail.com>
10 *
11 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
12 *
13 * Credits:
14 * The parser routines proper are all original material, first
15 * written Dec 2000 and Jan 2001 by Larry Doolittle. The
16 * execution engine, the builtins, and much of the underlying
17 * support has been adapted from busybox-0.49pre's lash, which is
18 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
19 * written by Erik Andersen <andersen@codepoet.org>. That, in turn,
20 * is based in part on ladsh.c, by Michael K. Johnson and Erik W.
21 * Troan, which they placed in the public domain. I don't know
22 * how much of the Johnson/Troan code has survived the repeated
23 * rewrites.
24 *
25 * Other credits:
26 * o_addchr derived from similar w_addchar function in glibc-2.2.
27 * parse_redirect, redirect_opt_num, and big chunks of main
28 * and many builtins derived from contributions by Erik Andersen.
29 * Miscellaneous bugfixes from Matt Kraai.
30 *
31 * There are two big (and related) architecture differences between
32 * this parser and the lash parser. One is that this version is
33 * actually designed from the ground up to understand nearly all
34 * of the Bourne grammar. The second, consequential change is that
35 * the parser and input reader have been turned inside out. Now,
36 * the parser is in control, and asks for input as needed. The old
37 * way had the input reader in control, and it asked for parsing to
38 * take place as needed. The new way makes it much easier to properly
39 * handle the recursion implicit in the various substitutions, especially
40 * across continuation lines.
41 *
42 * TODOs:
43 * grep for "TODO" and fix (some of them are easy)
44 * make complex ${var%...} constructs support optional
45 * make here documents optional
46 * special variables (done: PWD, PPID, RANDOM)
47 * follow IFS rules more precisely, including update semantics
48 * tilde expansion
49 * aliases
50 * "command" missing features:
51 * command -p CMD: run CMD using default $PATH
52 * (can use this to override standalone shell as well?)
53 * command BLTIN: disables special-ness (e.g. errors do not abort)
54 * command -V CMD1 CMD2 CMD3 (multiple args) (not in standard)
55 * builtins mandated by standards we don't support:
56 * [un]alias, fc:
57 * fc -l[nr] [BEG] [END]: list range of commands in history
58 * fc [-e EDITOR] [BEG] [END]: edit/rerun range of commands
59 * fc -s [PAT=REP] [CMD]: rerun CMD, replacing PAT with REP
60 *
61 * Bash compat TODO:
62 * redirection of stdout+stderr: &> and >&
63 * reserved words: function select
64 * advanced test: [[ ]]
65 * process substitution: <(list) and >(list)
66 * let EXPR [EXPR...]
67 * Each EXPR is an arithmetic expression (ARITHMETIC EVALUATION)
68 * If the last arg evaluates to 0, let returns 1; 0 otherwise.
69 * NB: let `echo 'a=a + 1'` - error (IOW: multi-word expansion is used)
70 * ((EXPR))
71 * The EXPR is evaluated according to ARITHMETIC EVALUATION.
72 * This is exactly equivalent to let "EXPR".
73 * $[EXPR]: synonym for $((EXPR))
74 * indirect expansion: ${!VAR}
75 * substring op on @: ${@:n:m}
76 *
77 * Won't do:
78 * Some builtins mandated by standards:
79 * newgrp [GRP]: not a builtin in bash but a suid binary
80 * which spawns a new shell with new group ID
81 *
82 * Status of [[ support:
83 * [[ args ]] are CMD_SINGLEWORD_NOGLOB:
84 * v='a b'; [[ $v = 'a b' ]]; echo 0:$?
85 * [[ /bin/n* ]]; echo 0:$?
86 * = is glob match operator, not equality operator: STR = GLOB
87 * == same as =
88 * =~ is regex match operator: STR =~ REGEX
89 * TODO:
90 * quoting needs to be considered (-f is an operator, "-f" and ""-f are not; etc)
91 * in word = GLOB, quoting should be significant on char-by-char basis: a*cd"*"
92 */
93//config:config HUSH
Francis Laniele7ca3a32023-12-22 22:02:42 +010094//config: bool "hush (70 kb)"
Francis Laniel110b7692023-12-22 22:02:27 +010095//config: default y
96//config: select SHELL_HUSH
97//config: help
98//config: hush is a small shell. It handles the normal flow control
99//config: constructs such as if/then/elif/else/fi, for/in/do/done, while loops,
100//config: case/esac. Redirections, here documents, $((arithmetic))
101//config: and functions are supported.
102//config:
103//config: It will compile and work on no-mmu systems.
104//config:
105//config: It does not handle select, aliases, tilde expansion,
106//config: &>file and >&file redirection of stdout+stderr.
107//config:
108// This option is visible (has a description) to make it possible to select
109// a "scripted" applet (such as NOLOGIN) but avoid selecting any shells:
110//config:config SHELL_HUSH
111//config: bool "Internal shell for embedded script support"
112//config: default n
113//config:
114//config:# hush options
115//config:# It's only needed to get "nice" menuconfig indenting.
116//config:if SHELL_HUSH || HUSH || SH_IS_HUSH || BASH_IS_HUSH
117//config:
118//config:config HUSH_BASH_COMPAT
119//config: bool "bash-compatible extensions"
120//config: default y
121//config: depends on SHELL_HUSH
122//config:
123//config:config HUSH_BRACE_EXPANSION
124//config: bool "Brace expansion"
125//config: default y
126//config: depends on HUSH_BASH_COMPAT
127//config: help
128//config: Enable {abc,def} extension.
129//config:
130//config:config HUSH_BASH_SOURCE_CURDIR
131//config: bool "'source' and '.' builtins search current directory after $PATH"
132//config: default n # do not encourage non-standard behavior
133//config: depends on HUSH_BASH_COMPAT
134//config: help
135//config: This is not compliant with standards. Avoid if possible.
136//config:
137//config:config HUSH_LINENO_VAR
138//config: bool "$LINENO variable (bashism)"
139//config: default y
140//config: depends on SHELL_HUSH
141//config:
142//config:config HUSH_INTERACTIVE
143//config: bool "Interactive mode"
144//config: default y
145//config: depends on SHELL_HUSH
146//config: help
147//config: Enable interactive mode (prompt and command editing).
148//config: Without this, hush simply reads and executes commands
149//config: from stdin just like a shell script from a file.
150//config: No prompt, no PS1/PS2 magic shell variables.
151//config:
152//config:config HUSH_SAVEHISTORY
153//config: bool "Save command history to .hush_history"
154//config: default y
155//config: depends on HUSH_INTERACTIVE && FEATURE_EDITING_SAVEHISTORY
156//config:
157//config:config HUSH_JOB
158//config: bool "Job control"
159//config: default y
160//config: depends on HUSH_INTERACTIVE
161//config: help
162//config: Enable job control: Ctrl-Z backgrounds, Ctrl-C interrupts current
163//config: command (not entire shell), fg/bg builtins work. Without this option,
164//config: "cmd &" still works by simply spawning a process and immediately
165//config: prompting for next command (or executing next command in a script),
166//config: but no separate process group is formed.
167//config:
168//config:config HUSH_TICK
169//config: bool "Support command substitution"
170//config: default y
171//config: depends on SHELL_HUSH
172//config: help
173//config: Enable `command` and $(command).
174//config:
175//config:config HUSH_IF
176//config: bool "Support if/then/elif/else/fi"
177//config: default y
178//config: depends on SHELL_HUSH
179//config:
180//config:config HUSH_LOOPS
181//config: bool "Support for, while and until loops"
182//config: default y
183//config: depends on SHELL_HUSH
184//config:
185//config:config HUSH_CASE
186//config: bool "Support case ... esac statement"
187//config: default y
188//config: depends on SHELL_HUSH
189//config: help
190//config: Enable case ... esac statement. +400 bytes.
191//config:
192//config:config HUSH_FUNCTIONS
193//config: bool "Support funcname() { commands; } syntax"
194//config: default y
195//config: depends on SHELL_HUSH
196//config: help
197//config: Enable support for shell functions. +800 bytes.
198//config:
199//config:config HUSH_LOCAL
200//config: bool "local builtin"
201//config: default y
202//config: depends on HUSH_FUNCTIONS
203//config: help
204//config: Enable support for local variables in functions.
205//config:
206//config:config HUSH_RANDOM_SUPPORT
207//config: bool "Pseudorandom generator and $RANDOM variable"
208//config: default y
209//config: depends on SHELL_HUSH
210//config: help
211//config: Enable pseudorandom generator and dynamic variable "$RANDOM".
212//config: Each read of "$RANDOM" will generate a new pseudorandom value.
213//config:
214//config:config HUSH_MODE_X
215//config: bool "Support 'hush -x' option and 'set -x' command"
216//config: default y
217//config: depends on SHELL_HUSH
218//config: help
219//config: This instructs hush to print commands before execution.
220//config: Adds ~300 bytes.
221//config:
222//config:config HUSH_ECHO
223//config: bool "echo builtin"
224//config: default y
225//config: depends on SHELL_HUSH
226//config:
227//config:config HUSH_PRINTF
228//config: bool "printf builtin"
229//config: default y
230//config: depends on SHELL_HUSH
231//config:
232//config:config HUSH_TEST
233//config: bool "test builtin"
234//config: default y
235//config: depends on SHELL_HUSH
236//config:
237//config:config HUSH_HELP
238//config: bool "help builtin"
239//config: default y
240//config: depends on SHELL_HUSH
241//config:
242//config:config HUSH_EXPORT
243//config: bool "export builtin"
244//config: default y
245//config: depends on SHELL_HUSH
246//config:
247//config:config HUSH_EXPORT_N
248//config: bool "Support 'export -n' option"
249//config: default y
250//config: depends on HUSH_EXPORT
251//config: help
252//config: export -n unexports variables. It is a bash extension.
253//config:
254//config:config HUSH_READONLY
255//config: bool "readonly builtin"
256//config: default y
257//config: depends on SHELL_HUSH
258//config: help
259//config: Enable support for read-only variables.
260//config:
261//config:config HUSH_KILL
262//config: bool "kill builtin (supports kill %jobspec)"
263//config: default y
264//config: depends on SHELL_HUSH
265//config:
266//config:config HUSH_WAIT
267//config: bool "wait builtin"
268//config: default y
269//config: depends on SHELL_HUSH
270//config:
271//config:config HUSH_COMMAND
272//config: bool "command builtin"
273//config: default y
274//config: depends on SHELL_HUSH
275//config:
276//config:config HUSH_TRAP
277//config: bool "trap builtin"
278//config: default y
279//config: depends on SHELL_HUSH
280//config:
281//config:config HUSH_TYPE
282//config: bool "type builtin"
283//config: default y
284//config: depends on SHELL_HUSH
285//config:
286//config:config HUSH_TIMES
287//config: bool "times builtin"
288//config: default y
289//config: depends on SHELL_HUSH
290//config:
291//config:config HUSH_READ
292//config: bool "read builtin"
293//config: default y
294//config: depends on SHELL_HUSH
295//config:
296//config:config HUSH_SET
297//config: bool "set builtin"
298//config: default y
299//config: depends on SHELL_HUSH
300//config:
301//config:config HUSH_UNSET
302//config: bool "unset builtin"
303//config: default y
304//config: depends on SHELL_HUSH
305//config:
306//config:config HUSH_ULIMIT
307//config: bool "ulimit builtin"
308//config: default y
309//config: depends on SHELL_HUSH
310//config:
311//config:config HUSH_UMASK
312//config: bool "umask builtin"
313//config: default y
314//config: depends on SHELL_HUSH
315//config:
316//config:config HUSH_GETOPTS
317//config: bool "getopts builtin"
318//config: default y
319//config: depends on SHELL_HUSH
320//config:
321//config:config HUSH_MEMLEAK
322//config: bool "memleak builtin (debugging)"
323//config: default n
324//config: depends on SHELL_HUSH
325//config:
326//config:endif # hush options
327
328//applet:IF_HUSH(APPLET(hush, BB_DIR_BIN, BB_SUID_DROP))
329// APPLET_ODDNAME:name main location suid_type help
330//applet:IF_SH_IS_HUSH( APPLET_ODDNAME(sh, hush, BB_DIR_BIN, BB_SUID_DROP, hush))
331//applet:IF_BASH_IS_HUSH(APPLET_ODDNAME(bash, hush, BB_DIR_BIN, BB_SUID_DROP, hush))
332
333//kbuild:lib-$(CONFIG_SHELL_HUSH) += hush.o match.o shell_common.o
334//kbuild:lib-$(CONFIG_HUSH_RANDOM_SUPPORT) += random.o
335
336/* -i (interactive) is also accepted,
337 * but does nothing, therefore not shown in help.
338 * NOMMU-specific options are not meant to be used by users,
339 * therefore we don't show them either.
340 */
341//usage:#define hush_trivial_usage
Francis Laniele7ca3a32023-12-22 22:02:42 +0100342//usage: "[-enxl] [-c 'SCRIPT' [ARG0 ARGS] | FILE ARGS | -s ARGS]"
Francis Laniel110b7692023-12-22 22:02:27 +0100343//usage:#define hush_full_usage "\n\n"
344//usage: "Unix shell interpreter"
345
Francis Laniel36836fc2023-12-22 22:02:28 +0100346#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +0100347#if !(defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) \
348 || defined(__APPLE__) \
349 )
350# include <malloc.h> /* for malloc_trim */
351#endif
352#include <glob.h>
353/* #include <dmalloc.h> */
354#if ENABLE_HUSH_CASE
355# include <fnmatch.h>
356#endif
357#include <sys/times.h>
358#include <sys/utsname.h> /* for setting $HOSTNAME */
359
360#include "busybox.h" /* for APPLET_IS_NOFORK/NOEXEC */
361#include "unicode.h"
362#include "shell_common.h"
363#include "math.h"
364#include "match.h"
365#if ENABLE_HUSH_RANDOM_SUPPORT
366# include "random.h"
367#else
368# define CLEAR_RANDOM_T(rnd) ((void)0)
369#endif
370#ifndef O_CLOEXEC
371# define O_CLOEXEC 0
372#endif
373#ifndef F_DUPFD_CLOEXEC
374# define F_DUPFD_CLOEXEC F_DUPFD
375#endif
376
Francis Laniele7ca3a32023-12-22 22:02:42 +0100377#if ENABLE_FEATURE_SH_EMBEDDED_SCRIPTS && !ENABLE_SHELL_ASH
Francis Laniel110b7692023-12-22 22:02:27 +0100378# include "embedded_scripts.h"
379#else
380# define NUM_SCRIPTS 0
381#endif
Francis Laniel36836fc2023-12-22 22:02:28 +0100382#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +0100383
384/* So far, all bash compat is controlled by one config option */
385/* Separate defines document which part of code implements what */
386#define BASH_PATTERN_SUBST ENABLE_HUSH_BASH_COMPAT
387#define BASH_SUBSTR ENABLE_HUSH_BASH_COMPAT
388#define BASH_SOURCE ENABLE_HUSH_BASH_COMPAT
389#define BASH_DOLLAR_SQUOTE ENABLE_HUSH_BASH_COMPAT
390#define BASH_HOSTNAME_VAR ENABLE_HUSH_BASH_COMPAT
391#define BASH_EPOCH_VARS ENABLE_HUSH_BASH_COMPAT
392#define BASH_TEST2 (ENABLE_HUSH_BASH_COMPAT && ENABLE_HUSH_TEST)
393#define BASH_READ_D ENABLE_HUSH_BASH_COMPAT
394
395
396/* Build knobs */
397#define LEAK_HUNTING 0
398#define BUILD_AS_NOMMU 0
399/* Enable/disable sanity checks. Ok to enable in production,
400 * only adds a bit of bloat. Set to >1 to get non-production level verbosity.
401 * Keeping 1 for now even in released versions.
402 */
403#define HUSH_DEBUG 1
404/* Slightly bigger (+200 bytes), but faster hush.
405 * So far it only enables a trick with counting SIGCHLDs and forks,
406 * which allows us to do fewer waitpid's.
407 * (we can detect a case where neither forks were done nor SIGCHLDs happened
408 * and therefore waitpid will return the same result as last time)
409 */
410#define ENABLE_HUSH_FAST 0
411/* TODO: implement simplified code for users which do not need ${var%...} ops
412 * So far ${var%...} ops are always enabled:
413 */
414#define ENABLE_HUSH_DOLLAR_OPS 1
415
416
417#if BUILD_AS_NOMMU
418# undef BB_MMU
419# undef USE_FOR_NOMMU
420# undef USE_FOR_MMU
421# define BB_MMU 0
422# define USE_FOR_NOMMU(...) __VA_ARGS__
423# define USE_FOR_MMU(...)
424#endif
425
Francis Laniel36836fc2023-12-22 22:02:28 +0100426#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +0100427#include "NUM_APPLETS.h"
428#if NUM_APPLETS == 1
429/* STANDALONE does not make sense, and won't compile */
Francis Laniel110b7692023-12-22 22:02:27 +0100430# undef ENABLE_FEATURE_SH_STANDALONE
431# undef IF_FEATURE_SH_STANDALONE
432# undef IF_NOT_FEATURE_SH_STANDALONE
433# define ENABLE_FEATURE_SH_STANDALONE 0
434# define IF_FEATURE_SH_STANDALONE(...)
435# define IF_NOT_FEATURE_SH_STANDALONE(...) __VA_ARGS__
436#endif
Francis Laniel36836fc2023-12-22 22:02:28 +0100437#endif /* __U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +0100438
439#if !ENABLE_HUSH_INTERACTIVE
440# undef ENABLE_FEATURE_EDITING
441# define ENABLE_FEATURE_EDITING 0
442# undef ENABLE_FEATURE_EDITING_FANCY_PROMPT
443# define ENABLE_FEATURE_EDITING_FANCY_PROMPT 0
444# undef ENABLE_FEATURE_EDITING_SAVE_ON_EXIT
445# define ENABLE_FEATURE_EDITING_SAVE_ON_EXIT 0
446#endif
447
448/* Do we support ANY keywords? */
449#if ENABLE_HUSH_IF || ENABLE_HUSH_LOOPS || ENABLE_HUSH_CASE
450# define HAS_KEYWORDS 1
451# define IF_HAS_KEYWORDS(...) __VA_ARGS__
452# define IF_HAS_NO_KEYWORDS(...)
453#else
454# define HAS_KEYWORDS 0
455# define IF_HAS_KEYWORDS(...)
456# define IF_HAS_NO_KEYWORDS(...) __VA_ARGS__
457#endif
458
459/* If you comment out one of these below, it will be #defined later
460 * to perform debug printfs to stderr: */
461#define debug_printf(...) do {} while (0)
462/* Finer-grained debug switches */
463#define debug_printf_parse(...) do {} while (0)
464#define debug_printf_heredoc(...) do {} while (0)
465#define debug_print_tree(a, b) do {} while (0)
466#define debug_printf_exec(...) do {} while (0)
467#define debug_printf_env(...) do {} while (0)
468#define debug_printf_jobs(...) do {} while (0)
469#define debug_printf_expand(...) do {} while (0)
470#define debug_printf_varexp(...) do {} while (0)
471#define debug_printf_glob(...) do {} while (0)
472#define debug_printf_redir(...) do {} while (0)
473#define debug_printf_list(...) do {} while (0)
474#define debug_printf_subst(...) do {} while (0)
475#define debug_printf_prompt(...) do {} while (0)
476#define debug_printf_clean(...) do {} while (0)
477
478#define ERR_PTR ((void*)(long)1)
479
480#define JOB_STATUS_FORMAT "[%u] %-22s %.40s\n"
481
482#define _SPECIAL_VARS_STR "_*@$!?#-"
Francis Laniel36836fc2023-12-22 22:02:28 +0100483#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +0100484#define SPECIAL_VARS_STR ("_*@$!?#-" + 1)
485#define NUMERIC_SPECVARS_STR ("_*@$!?#-" + 3)
Francis Laniel36836fc2023-12-22 22:02:28 +0100486#else /* __U_BOOT__ */
487#define SPECIAL_VARS_STR "*@$!?#-"
488#define NUMERIC_SPECVARS_STR "$!?#-"
489#endif /* __U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +0100490#if BASH_PATTERN_SUBST
491/* Support / and // replace ops */
492/* Note that // is stored as \ in "encoded" string representation */
493# define VAR_ENCODED_SUBST_OPS "\\/%#:-=+?"
494# define VAR_SUBST_OPS ("\\/%#:-=+?" + 1)
495# define MINUS_PLUS_EQUAL_QUESTION ("\\/%#:-=+?" + 5)
496#else
497# define VAR_ENCODED_SUBST_OPS "%#:-=+?"
498# define VAR_SUBST_OPS "%#:-=+?"
499# define MINUS_PLUS_EQUAL_QUESTION ("%#:-=+?" + 3)
500#endif
501
502#define SPECIAL_VAR_SYMBOL_STR "\3"
503#define SPECIAL_VAR_SYMBOL 3
504/* The "variable" with name "\1" emits string "\3". Testcase: "echo ^C" */
505#define SPECIAL_VAR_QUOTED_SVS 1
506
507struct variable;
508
509static const char hush_version_str[] ALIGN1 = "HUSH_VERSION="BB_VER;
510
511/* This supports saving pointers malloced in vfork child,
512 * to be freed in the parent.
513 */
514#if !BB_MMU
515typedef struct nommu_save_t {
516 struct variable *old_vars;
517 char **argv;
518 char **argv_from_re_execing;
519} nommu_save_t;
520#endif
521
Francis Laniel36836fc2023-12-22 22:02:28 +0100522
Francis Laniel110b7692023-12-22 22:02:27 +0100523enum {
524 RES_NONE = 0,
525#if ENABLE_HUSH_IF
526 RES_IF ,
527 RES_THEN ,
528 RES_ELIF ,
529 RES_ELSE ,
530 RES_FI ,
531#endif
532#if ENABLE_HUSH_LOOPS
533 RES_FOR ,
534 RES_WHILE ,
535 RES_UNTIL ,
536 RES_DO ,
537 RES_DONE ,
538#endif
539#if ENABLE_HUSH_LOOPS || ENABLE_HUSH_CASE
540 RES_IN ,
541#endif
542#if ENABLE_HUSH_CASE
543 RES_CASE ,
544 /* three pseudo-keywords support contrived "case" syntax: */
545 RES_CASE_IN, /* "case ... IN", turns into RES_MATCH when IN is observed */
546 RES_MATCH , /* "word)" */
547 RES_CASE_BODY, /* "this command is inside CASE" */
548 RES_ESAC ,
549#endif
550 RES_XXXX ,
551 RES_SNTX
552};
553
554typedef struct o_string {
555 char *data;
556 int length; /* position where data is appended */
557 int maxlen;
558 int o_expflags;
559 /* At least some part of the string was inside '' or "",
560 * possibly empty one: word"", wo''rd etc. */
561 smallint has_quoted_part;
562 smallint has_empty_slot;
563 smallint ended_in_ifs;
564} o_string;
565enum {
566 EXP_FLAG_SINGLEWORD = 0x80, /* must be 0x80 */
567 EXP_FLAG_GLOB = 0x2,
568 /* Protect newly added chars against globbing
569 * by prepending \ to *, ?, [, \ */
570 EXP_FLAG_ESC_GLOB_CHARS = 0x1,
571};
572/* Used for initialization: o_string foo = NULL_O_STRING; */
573#define NULL_O_STRING { NULL }
574
575#ifndef debug_printf_parse
Francis Laniele7ca3a32023-12-22 22:02:42 +0100576static const char *const assignment_flag[] ALIGN_PTR = {
Francis Laniel110b7692023-12-22 22:02:27 +0100577 "MAYBE_ASSIGNMENT",
578 "DEFINITELY_ASSIGNMENT",
579 "NOT_ASSIGNMENT",
580 "WORD_IS_KEYWORD",
581};
582#endif
583
584/* We almost can use standard FILE api, but we need an ability to move
585 * its fd when redirects coincide with it. No api exists for that
586 * (RFE for it at https://sourceware.org/bugzilla/show_bug.cgi?id=21902).
587 * HFILE is our internal alternative. Only supports reading.
588 * Since we now can, we incorporate linked list of all opened HFILEs
589 * into the struct (used to be a separate mini-list).
590 */
591typedef struct HFILE {
592 char *cur;
593 char *end;
594 struct HFILE *next_hfile;
595 int fd;
596 char buf[1024];
597} HFILE;
598
599typedef struct in_str {
600 const char *p;
601 int peek_buf[2];
602 int last_char;
603 HFILE *file;
604} in_str;
605
Francis Laniel36836fc2023-12-22 22:02:28 +0100606#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +0100607/* The descrip member of this structure is only used to make
608 * debugging output pretty */
609static const struct {
610 int32_t mode;
611 signed char default_fd;
612 char descrip[3];
613} redir_table[] ALIGN4 = {
614 { O_RDONLY, 0, "<" },
615 { O_CREAT|O_TRUNC|O_WRONLY, 1, ">" },
616 { O_CREAT|O_APPEND|O_WRONLY, 1, ">>" },
617 { O_CREAT|O_RDWR, 1, "<>" },
618 { O_RDONLY, 0, "<<" },
619/* Should not be needed. Bogus default_fd helps in debugging */
620/* { O_RDONLY, 77, "<<" }, */
621};
622
623struct redir_struct {
624 struct redir_struct *next;
625 char *rd_filename; /* filename */
626 int rd_fd; /* fd to redirect */
627 /* fd to redirect to, or -3 if rd_fd is to be closed (n>&-) */
628 int rd_dup;
629 smallint rd_type; /* (enum redir_type) */
630 /* note: for heredocs, rd_filename contains heredoc delimiter,
631 * and subsequently heredoc itself; and rd_dup is a bitmask:
632 * bit 0: do we need to trim leading tabs?
633 * bit 1: is heredoc quoted (<<'delim' syntax) ?
634 */
635};
636typedef enum redir_type {
637 REDIRECT_INPUT = 0,
638 REDIRECT_OVERWRITE = 1,
639 REDIRECT_APPEND = 2,
640 REDIRECT_IO = 3,
641 REDIRECT_HEREDOC = 4,
642 REDIRECT_HEREDOC2 = 5, /* REDIRECT_HEREDOC after heredoc is loaded */
643
644 REDIRFD_CLOSE = -3,
645 REDIRFD_SYNTAX_ERR = -2,
646 REDIRFD_TO_FILE = -1,
647 /* otherwise, rd_fd is redirected to rd_dup */
648
649 HEREDOC_SKIPTABS = 1,
650 HEREDOC_QUOTED = 2,
651} redir_type;
652
Francis Laniel36836fc2023-12-22 22:02:28 +0100653#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +0100654
655struct command {
Francis Laniel36836fc2023-12-22 22:02:28 +0100656#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +0100657 pid_t pid; /* 0 if exited */
Francis Laniel36836fc2023-12-22 22:02:28 +0100658#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +0100659 unsigned assignment_cnt; /* how many argv[i] are assignments? */
660#if ENABLE_HUSH_LINENO_VAR
661 unsigned lineno;
662#endif
663 smallint cmd_type; /* CMD_xxx */
664#define CMD_NORMAL 0
665#define CMD_SUBSHELL 1
666#if BASH_TEST2
667/* used for "[[ EXPR ]]" */
668# define CMD_TEST2_SINGLEWORD_NOGLOB 2
669#endif
670#if BASH_TEST2 || ENABLE_HUSH_LOCAL || ENABLE_HUSH_EXPORT || ENABLE_HUSH_READONLY
671/* used to prevent word splitting and globbing in "export v=t*" */
672# define CMD_SINGLEWORD_NOGLOB 3
673#endif
674#if ENABLE_HUSH_FUNCTIONS
675# define CMD_FUNCDEF 4
676#endif
677
678 smalluint cmd_exitcode;
679 /* if non-NULL, this "command" is { list }, ( list ), or a compound statement */
680 struct pipe *group;
681#if !BB_MMU
682 char *group_as_string;
683#endif
684#if ENABLE_HUSH_FUNCTIONS
685 struct function *child_func;
686/* This field is used to prevent a bug here:
687 * while...do f1() {a;}; f1; f1() {b;}; f1; done
688 * When we execute "f1() {a;}" cmd, we create new function and clear
689 * cmd->group, cmd->group_as_string, cmd->argv[0].
690 * When we execute "f1() {b;}", we notice that f1 exists,
691 * and that its "parent cmd" struct is still "alive",
692 * we put those fields back into cmd->xxx
693 * (struct function has ->parent_cmd ptr to facilitate that).
694 * When we loop back, we can execute "f1() {a;}" again and set f1 correctly.
695 * Without this trick, loop would execute a;b;b;b;...
696 * instead of correct sequence a;b;a;b;...
697 * When command is freed, it severs the link
698 * (sets ->child_func->parent_cmd to NULL).
699 */
700#endif
Francis Laniel36836fc2023-12-22 22:02:28 +0100701#ifdef __U_BOOT__
702 int argc; /* number of program arguments */
703#endif
Francis Laniel110b7692023-12-22 22:02:27 +0100704 char **argv; /* command name and arguments */
705/* argv vector may contain variable references (^Cvar^C, ^C0^C etc)
706 * and on execution these are substituted with their values.
707 * Substitution can make _several_ words out of one argv[n]!
708 * Example: argv[0]=='.^C*^C.' here: echo .$*.
709 * References of the form ^C`cmd arg^C are `cmd arg` substitutions.
710 */
Francis Laniel36836fc2023-12-22 22:02:28 +0100711#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +0100712 struct redir_struct *redirects; /* I/O redirections */
Francis Laniel36836fc2023-12-22 22:02:28 +0100713#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +0100714};
715/* Is there anything in this command at all? */
Francis Laniel36836fc2023-12-22 22:02:28 +0100716#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +0100717#define IS_NULL_CMD(cmd) \
718 (!(cmd)->group && !(cmd)->argv && !(cmd)->redirects)
719
Francis Laniel36836fc2023-12-22 22:02:28 +0100720#else /* __U_BOOT__ */
721#define IS_NULL_CMD(cmd) \
722 (!(cmd)->group && !(cmd)->argv)
723#endif /* __U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +0100724struct pipe {
725 struct pipe *next;
726 int num_cmds; /* total number of commands in pipe */
Francis Laniel36836fc2023-12-22 22:02:28 +0100727#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +0100728 int alive_cmds; /* number of commands running (not exited) */
729 int stopped_cmds; /* number of commands alive, but stopped */
730#if ENABLE_HUSH_JOB
731 unsigned jobid; /* job number */
732 pid_t pgrp; /* process group ID for the job */
733 char *cmdtext; /* name of job */
734#endif
Francis Laniel36836fc2023-12-22 22:02:28 +0100735#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +0100736 struct command *cmds; /* array of commands in pipe */
737 smallint followup; /* PIPE_BG, PIPE_SEQ, PIPE_OR, PIPE_AND */
738 IF_HAS_KEYWORDS(smallint pi_inverted;) /* "! cmd | cmd" */
739 IF_HAS_KEYWORDS(smallint res_word;) /* needed for if, for, while, until... */
740};
741typedef enum pipe_style {
742 PIPE_SEQ = 0,
743 PIPE_AND = 1,
744 PIPE_OR = 2,
745 PIPE_BG = 3,
746} pipe_style;
747/* Is there anything in this pipe at all? */
748#define IS_NULL_PIPE(pi) \
749 ((pi)->num_cmds == 0 IF_HAS_KEYWORDS( && (pi)->res_word == RES_NONE))
750
751/* This holds pointers to the various results of parsing */
752struct parse_context {
753 /* linked list of pipes */
754 struct pipe *list_head;
755 /* last pipe (being constructed right now) */
756 struct pipe *pipe;
757 /* last command in pipe (being constructed right now) */
758 struct command *command;
Francis Laniel36836fc2023-12-22 22:02:28 +0100759#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +0100760 /* last redirect in command->redirects list */
761 struct redir_struct *pending_redirect;
Francis Laniel36836fc2023-12-22 22:02:28 +0100762#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +0100763 o_string word;
764#if !BB_MMU
765 o_string as_string;
766#endif
767 smallint is_assignment; /* 0:maybe, 1:yes, 2:no, 3:keyword */
768#if HAS_KEYWORDS
769 smallint ctx_res_w;
770 smallint ctx_inverted; /* "! cmd | cmd" */
771#if ENABLE_HUSH_CASE
772 smallint ctx_dsemicolon; /* ";;" seen */
773#endif
774 /* bitmask of FLAG_xxx, for figuring out valid reserved words */
775 int old_flag;
776 /* group we are enclosed in:
777 * example: "if pipe1; pipe2; then pipe3; fi"
778 * when we see "if" or "then", we malloc and copy current context,
779 * and make ->stack point to it. then we parse pipeN.
780 * when closing "then" / fi" / whatever is found,
781 * we move list_head into ->stack->command->group,
782 * copy ->stack into current context, and delete ->stack.
783 * (parsing of { list } and ( list ) doesn't use this method)
784 */
785 struct parse_context *stack;
786#endif
787};
788enum {
789 MAYBE_ASSIGNMENT = 0,
790 DEFINITELY_ASSIGNMENT = 1,
791 NOT_ASSIGNMENT = 2,
792 /* Not an assignment, but next word may be: "if v=xyz cmd;" */
793 WORD_IS_KEYWORD = 3,
794};
795
Francis Laniel36836fc2023-12-22 22:02:28 +0100796#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +0100797/* On program start, environ points to initial environment.
798 * putenv adds new pointers into it, unsetenv removes them.
799 * Neither of these (de)allocates the strings.
800 * setenv allocates new strings in malloc space and does putenv,
801 * and thus setenv is unusable (leaky) for shell's purposes */
802#define setenv(...) setenv_is_leaky_dont_use()
Francis Laniel36836fc2023-12-22 22:02:28 +0100803#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +0100804struct variable {
805 struct variable *next;
806 char *varstr; /* points to "name=" portion */
807 int max_len; /* if > 0, name is part of initial env; else name is malloced */
Francis Laniel36836fc2023-12-22 22:02:28 +0100808#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +0100809 uint16_t var_nest_level;
810 smallint flg_export; /* putenv should be done on this var */
811 smallint flg_read_only;
Francis Laniel36836fc2023-12-22 22:02:28 +0100812#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +0100813};
814
815enum {
816 BC_BREAK = 1,
817 BC_CONTINUE = 2,
818};
819
820#if ENABLE_HUSH_FUNCTIONS
821struct function {
822 struct function *next;
823 char *name;
824 struct command *parent_cmd;
825 struct pipe *body;
826# if !BB_MMU
827 char *body_as_string;
828# endif
829};
830#endif
831
832
833/* set -/+o OPT support. (TODO: make it optional)
834 * bash supports the following opts:
835 * allexport off
836 * braceexpand on
837 * emacs on
838 * errexit off
839 * errtrace off
840 * functrace off
841 * hashall on
842 * histexpand off
843 * history on
844 * ignoreeof off
845 * interactive-comments on
846 * keyword off
847 * monitor on
848 * noclobber off
849 * noexec off
850 * noglob off
851 * nolog off
852 * notify off
853 * nounset off
854 * onecmd off
855 * physical off
856 * pipefail off
857 * posix off
858 * privileged off
859 * verbose off
860 * vi off
861 * xtrace off
862 */
863static const char o_opt_strings[] ALIGN1 =
864 "pipefail\0"
865 "noexec\0"
866 "errexit\0"
867#if ENABLE_HUSH_MODE_X
868 "xtrace\0"
869#endif
870 ;
871enum {
872 OPT_O_PIPEFAIL,
873 OPT_O_NOEXEC,
874 OPT_O_ERREXIT,
875#if ENABLE_HUSH_MODE_X
876 OPT_O_XTRACE,
877#endif
878 NUM_OPT_O
879};
880
881/* "Globals" within this file */
882/* Sorted roughly by size (smaller offsets == smaller code) */
883struct globals {
Francis Laniel36836fc2023-12-22 22:02:28 +0100884#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +0100885 /* interactive_fd != 0 means we are an interactive shell.
886 * If we are, then saved_tty_pgrp can also be != 0, meaning
887 * that controlling tty is available. With saved_tty_pgrp == 0,
888 * job control still works, but terminal signals
889 * (^C, ^Z, ^Y, ^\) won't work at all, and background
890 * process groups can only be created with "cmd &".
891 * With saved_tty_pgrp != 0, hush will use tcsetpgrp()
892 * to give tty to the foreground process group,
893 * and will take it back when the group is stopped (^Z)
894 * or killed (^C).
895 */
896#if ENABLE_HUSH_INTERACTIVE
897 /* 'interactive_fd' is a fd# open to ctty, if we have one
898 * _AND_ if we decided to act interactively */
899 int interactive_fd;
900 IF_NOT_FEATURE_EDITING_FANCY_PROMPT(char *PS1;)
901# define G_interactive_fd (G.interactive_fd)
902#else
903# define G_interactive_fd 0
904#endif
Francis Laniel36836fc2023-12-22 22:02:28 +0100905#else /* __U_BOOT__ */
906# define G_interactive_fd 0
907#endif /* __U_BOOT__ */
908#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +0100909#if ENABLE_FEATURE_EDITING
910 line_input_t *line_input_state;
911#endif
912 pid_t root_pid;
913 pid_t root_ppid;
914 pid_t last_bg_pid;
915#if ENABLE_HUSH_RANDOM_SUPPORT
916 random_t random_gen;
917#endif
918#if ENABLE_HUSH_JOB
919 int run_list_level;
920 unsigned last_jobid;
921 pid_t saved_tty_pgrp;
922 struct pipe *job_list;
923# define G_saved_tty_pgrp (G.saved_tty_pgrp)
924#else
925# define G_saved_tty_pgrp 0
926#endif
Francis Laniel36836fc2023-12-22 22:02:28 +0100927#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +0100928 /* How deeply are we in context where "set -e" is ignored */
929 int errexit_depth;
Francis Laniel36836fc2023-12-22 22:02:28 +0100930#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +0100931 /* "set -e" rules (do we follow them correctly?):
932 * Exit if pipe, list, or compound command exits with a non-zero status.
933 * Shell does not exit if failed command is part of condition in
934 * if/while, part of && or || list except the last command, any command
935 * in a pipe but the last, or if the command's return value is being
936 * inverted with !. If a compound command other than a subshell returns a
937 * non-zero status because a command failed while -e was being ignored, the
938 * shell does not exit. A trap on ERR, if set, is executed before the shell
939 * exits [ERR is a bashism].
940 *
941 * If a compound command or function executes in a context where -e is
942 * ignored, none of the commands executed within are affected by the -e
943 * setting. If a compound command or function sets -e while executing in a
944 * context where -e is ignored, that setting does not have any effect until
945 * the compound command or the command containing the function call completes.
946 */
947
948 char o_opt[NUM_OPT_O];
949#if ENABLE_HUSH_MODE_X
950# define G_x_mode (G.o_opt[OPT_O_XTRACE])
951#else
952# define G_x_mode 0
953#endif
954 char opt_s;
955 char opt_c;
Francis Laniel36836fc2023-12-22 22:02:28 +0100956#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +0100957#if ENABLE_HUSH_INTERACTIVE
958 smallint promptmode; /* 0: PS1, 1: PS2 */
959#endif
Francis Laniele7ca3a32023-12-22 22:02:42 +0100960 /* set by signal handler if SIGINT is received _and_ its trap is not set */
Francis Laniel110b7692023-12-22 22:02:27 +0100961 smallint flag_SIGINT;
Francis Laniel36836fc2023-12-22 22:02:28 +0100962#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +0100963#if ENABLE_HUSH_LOOPS
964 smallint flag_break_continue;
965#endif
Francis Laniel36836fc2023-12-22 22:02:28 +0100966#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +0100967#if ENABLE_HUSH_FUNCTIONS
968 /* 0: outside of a function (or sourced file)
969 * -1: inside of a function, ok to use return builtin
970 * 1: return is invoked, skip all till end of func
971 */
972 smallint flag_return_in_progress;
973# define G_flag_return_in_progress (G.flag_return_in_progress)
974#else
975# define G_flag_return_in_progress 0
976#endif
977 smallint exiting; /* used to prevent EXIT trap recursion */
978 /* These support $? */
979 smalluint last_exitcode;
980 smalluint expand_exitcode;
981 smalluint last_bg_pid_exitcode;
Francis Laniel36836fc2023-12-22 22:02:28 +0100982#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +0100983#if ENABLE_HUSH_SET
984 /* are global_argv and global_argv[1..n] malloced? (note: not [0]) */
985 smalluint global_args_malloced;
986# define G_global_args_malloced (G.global_args_malloced)
987#else
988# define G_global_args_malloced 0
989#endif
990#if ENABLE_HUSH_BASH_COMPAT
991 int dead_job_exitcode; /* for "wait -n" */
992#endif
Francis Laniel36836fc2023-12-22 22:02:28 +0100993#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +0100994 /* how many non-NULL argv's we have. NB: $# + 1 */
995 int global_argc;
996 char **global_argv;
997#if !BB_MMU
998 char *argv0_for_re_execing;
999#endif
1000#if ENABLE_HUSH_LOOPS
Francis Laniel36836fc2023-12-22 22:02:28 +01001001#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01001002 unsigned depth_break_continue;
Francis Laniel36836fc2023-12-22 22:02:28 +01001003#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01001004 unsigned depth_of_loop;
1005#endif
Francis Laniel36836fc2023-12-22 22:02:28 +01001006#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01001007#if ENABLE_HUSH_GETOPTS
1008 unsigned getopt_count;
1009#endif
Francis Laniel36836fc2023-12-22 22:02:28 +01001010#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01001011 const char *ifs;
Francis Laniel36836fc2023-12-22 22:02:28 +01001012#ifdef __U_BOOT__
1013 int flag_repeat;
1014 int do_repeat;
Francis Laniel26cafe12023-12-22 22:02:34 +01001015 int run_command_flags;
Francis Laniel36836fc2023-12-22 22:02:28 +01001016#endif /* __U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01001017 char *ifs_whitespace; /* = G.ifs or malloced */
Francis Laniel36836fc2023-12-22 22:02:28 +01001018#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01001019 const char *cwd;
Francis Laniel36836fc2023-12-22 22:02:28 +01001020#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01001021 struct variable *top_var;
1022 char **expanded_assignments;
1023 struct variable **shadowed_vars_pp;
1024 unsigned var_nest_level;
Francis Laniel36836fc2023-12-22 22:02:28 +01001025#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01001026#if ENABLE_HUSH_FUNCTIONS
1027# if ENABLE_HUSH_LOCAL
1028 unsigned func_nest_level; /* solely to prevent "local v" in non-functions */
1029# endif
1030 struct function *top_func;
1031#endif
1032 /* Signal and trap handling */
1033#if ENABLE_HUSH_FAST
1034 unsigned count_SIGCHLD;
1035 unsigned handled_SIGCHLD;
1036 smallint we_have_children;
1037#endif
1038#if ENABLE_HUSH_LINENO_VAR
1039 unsigned parse_lineno;
1040 unsigned execute_lineno;
1041#endif
1042 HFILE *HFILE_list;
1043 HFILE *HFILE_stdin;
1044 /* Which signals have non-DFL handler (even with no traps set)?
1045 * Set at the start to:
1046 * (SIGQUIT + maybe SPECIAL_INTERACTIVE_SIGS + maybe SPECIAL_JOBSTOP_SIGS)
1047 * SPECIAL_INTERACTIVE_SIGS are cleared after fork.
1048 * The rest is cleared right before execv syscalls.
1049 * Other than these two times, never modified.
1050 */
1051 unsigned special_sig_mask;
1052#if ENABLE_HUSH_JOB
1053 unsigned fatal_sig_mask;
1054# define G_fatal_sig_mask (G.fatal_sig_mask)
1055#else
1056# define G_fatal_sig_mask 0
1057#endif
1058#if ENABLE_HUSH_TRAP
1059 int pre_trap_exitcode;
1060# if ENABLE_HUSH_FUNCTIONS
1061 int return_exitcode;
1062# endif
1063 char **traps; /* char *traps[NSIG] */
1064# define G_traps G.traps
1065#else
1066# define G_traps ((char**)NULL)
1067#endif
1068 sigset_t pending_set;
1069#if ENABLE_HUSH_MEMLEAK
1070 unsigned long memleak_value;
1071#endif
1072#if ENABLE_HUSH_MODE_X
1073 unsigned x_mode_depth;
1074 /* "set -x" output should not be redirectable with subsequent 2>FILE.
1075 * We dup fd#2 to x_mode_fd when "set -x" is executed, and use it
1076 * for all subsequent output.
1077 */
1078 int x_mode_fd;
1079 o_string x_mode_buf;
1080#endif
Francis Laniel36836fc2023-12-22 22:02:28 +01001081#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01001082#if HUSH_DEBUG >= 2
1083 int debug_indent;
1084#endif
Francis Laniel36836fc2023-12-22 22:02:28 +01001085#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01001086 struct sigaction sa;
1087 char optstring_buf[sizeof("eixcs")];
1088#if BASH_EPOCH_VARS
1089 char epoch_buf[sizeof("%llu.nnnnnn") + sizeof(long long)*3];
1090#endif
1091#if ENABLE_FEATURE_EDITING
1092 char user_input_buf[CONFIG_FEATURE_EDITING_MAX_LEN];
1093#endif
Francis Laniel36836fc2023-12-22 22:02:28 +01001094#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01001095};
Francis Laniel36836fc2023-12-22 22:02:28 +01001096#ifdef __U_BOOT__
1097struct globals *ptr_to_globals;
1098#endif /* __U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01001099#define G (*ptr_to_globals)
1100/* Not #defining name to G.name - this quickly gets unwieldy
1101 * (too many defines). Also, I actually prefer to see when a variable
1102 * is global, thus "G." prefix is a useful hint */
Francis Laniel36836fc2023-12-22 22:02:28 +01001103#ifdef __U_BOOT__
1104#define SET_PTR_TO_GLOBALS(x) do { \
1105 (*(struct globals**)&ptr_to_globals) = (void*)(x); \
1106 barrier(); \
1107} while (0)
1108#define INIT_G() do { \
1109 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
1110 G.promptmode = 1; \
1111} while (0)
1112#else /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01001113#define INIT_G() do { \
1114 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
1115 /* memset(&G.sa, 0, sizeof(G.sa)); */ \
1116 sigfillset(&G.sa.sa_mask); \
1117 G.sa.sa_flags = SA_RESTART; \
1118} while (0)
Francis Laniel36836fc2023-12-22 22:02:28 +01001119#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01001120
1121
Francis Laniel36836fc2023-12-22 22:02:28 +01001122#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01001123/* Function prototypes for builtins */
1124static int builtin_cd(char **argv) FAST_FUNC;
1125#if ENABLE_HUSH_ECHO
1126static int builtin_echo(char **argv) FAST_FUNC;
1127#endif
1128static int builtin_eval(char **argv) FAST_FUNC;
1129static int builtin_exec(char **argv) FAST_FUNC;
1130static int builtin_exit(char **argv) FAST_FUNC;
1131#if ENABLE_HUSH_EXPORT
1132static int builtin_export(char **argv) FAST_FUNC;
1133#endif
1134#if ENABLE_HUSH_READONLY
1135static int builtin_readonly(char **argv) FAST_FUNC;
1136#endif
Francis Laniele7ca3a32023-12-22 22:02:42 +01001137static int builtin_false(char **argv) FAST_FUNC;
Francis Laniel110b7692023-12-22 22:02:27 +01001138#if ENABLE_HUSH_JOB
1139static int builtin_fg_bg(char **argv) FAST_FUNC;
1140static int builtin_jobs(char **argv) FAST_FUNC;
1141#endif
1142#if ENABLE_HUSH_GETOPTS
1143static int builtin_getopts(char **argv) FAST_FUNC;
1144#endif
1145#if ENABLE_HUSH_HELP
1146static int builtin_help(char **argv) FAST_FUNC;
1147#endif
1148#if MAX_HISTORY && ENABLE_FEATURE_EDITING
1149static int builtin_history(char **argv) FAST_FUNC;
1150#endif
1151#if ENABLE_HUSH_LOCAL
1152static int builtin_local(char **argv) FAST_FUNC;
1153#endif
1154#if ENABLE_HUSH_MEMLEAK
1155static int builtin_memleak(char **argv) FAST_FUNC;
1156#endif
1157#if ENABLE_HUSH_PRINTF
1158static int builtin_printf(char **argv) FAST_FUNC;
1159#endif
1160static int builtin_pwd(char **argv) FAST_FUNC;
1161#if ENABLE_HUSH_READ
1162static int builtin_read(char **argv) FAST_FUNC;
1163#endif
1164#if ENABLE_HUSH_SET
1165static int builtin_set(char **argv) FAST_FUNC;
1166#endif
1167static int builtin_shift(char **argv) FAST_FUNC;
1168static int builtin_source(char **argv) FAST_FUNC;
1169#if ENABLE_HUSH_TEST || BASH_TEST2
1170static int builtin_test(char **argv) FAST_FUNC;
1171#endif
1172#if ENABLE_HUSH_TRAP
1173static int builtin_trap(char **argv) FAST_FUNC;
1174#endif
1175#if ENABLE_HUSH_TYPE
1176static int builtin_type(char **argv) FAST_FUNC;
1177#endif
1178#if ENABLE_HUSH_TIMES
1179static int builtin_times(char **argv) FAST_FUNC;
1180#endif
1181static int builtin_true(char **argv) FAST_FUNC;
1182#if ENABLE_HUSH_UMASK
1183static int builtin_umask(char **argv) FAST_FUNC;
1184#endif
1185#if ENABLE_HUSH_UNSET
1186static int builtin_unset(char **argv) FAST_FUNC;
1187#endif
1188#if ENABLE_HUSH_KILL
1189static int builtin_kill(char **argv) FAST_FUNC;
1190#endif
1191#if ENABLE_HUSH_WAIT
1192static int builtin_wait(char **argv) FAST_FUNC;
1193#endif
1194#if ENABLE_HUSH_LOOPS
1195static int builtin_break(char **argv) FAST_FUNC;
1196static int builtin_continue(char **argv) FAST_FUNC;
1197#endif
1198#if ENABLE_HUSH_FUNCTIONS
1199static int builtin_return(char **argv) FAST_FUNC;
1200#endif
1201
1202/* Table of built-in functions. They can be forked or not, depending on
1203 * context: within pipes, they fork. As simple commands, they do not.
1204 * When used in non-forking context, they can change global variables
1205 * in the parent shell process. If forked, of course they cannot.
1206 * For example, 'unset foo | whatever' will parse and run, but foo will
1207 * still be set at the end. */
1208struct built_in_command {
1209 const char *b_cmd;
1210 int (*b_function)(char **argv) FAST_FUNC;
1211#if ENABLE_HUSH_HELP
1212 const char *b_descr;
1213# define BLTIN(cmd, func, help) { cmd, func, help }
1214#else
1215# define BLTIN(cmd, func, help) { cmd, func }
1216#endif
1217};
1218
1219static const struct built_in_command bltins1[] ALIGN_PTR = {
1220 BLTIN("." , builtin_source , "Run commands in file"),
1221 BLTIN(":" , builtin_true , NULL),
1222#if ENABLE_HUSH_JOB
1223 BLTIN("bg" , builtin_fg_bg , "Resume job in background"),
1224#endif
1225#if ENABLE_HUSH_LOOPS
1226 BLTIN("break" , builtin_break , "Exit loop"),
1227#endif
1228 BLTIN("cd" , builtin_cd , "Change directory"),
1229#if ENABLE_HUSH_LOOPS
1230 BLTIN("continue" , builtin_continue, "Start new loop iteration"),
1231#endif
1232 BLTIN("eval" , builtin_eval , "Construct and run shell command"),
1233 BLTIN("exec" , builtin_exec , "Execute command, don't return to shell"),
1234 BLTIN("exit" , builtin_exit , NULL),
1235#if ENABLE_HUSH_EXPORT
1236 BLTIN("export" , builtin_export , "Set environment variables"),
1237#endif
Francis Laniele7ca3a32023-12-22 22:02:42 +01001238 BLTIN("false" , builtin_false , NULL),
Francis Laniel110b7692023-12-22 22:02:27 +01001239#if ENABLE_HUSH_JOB
1240 BLTIN("fg" , builtin_fg_bg , "Bring job to foreground"),
1241#endif
1242#if ENABLE_HUSH_GETOPTS
1243 BLTIN("getopts" , builtin_getopts , NULL),
1244#endif
1245#if ENABLE_HUSH_HELP
1246 BLTIN("help" , builtin_help , NULL),
1247#endif
1248#if MAX_HISTORY && ENABLE_FEATURE_EDITING
1249 BLTIN("history" , builtin_history , "Show history"),
1250#endif
1251#if ENABLE_HUSH_JOB
1252 BLTIN("jobs" , builtin_jobs , "List jobs"),
1253#endif
1254#if ENABLE_HUSH_KILL
1255 BLTIN("kill" , builtin_kill , "Send signals to processes"),
1256#endif
1257#if ENABLE_HUSH_LOCAL
1258 BLTIN("local" , builtin_local , "Set local variables"),
1259#endif
1260#if ENABLE_HUSH_MEMLEAK
1261 BLTIN("memleak" , builtin_memleak , NULL),
1262#endif
1263#if ENABLE_HUSH_READ
1264 BLTIN("read" , builtin_read , "Input into variable"),
1265#endif
1266#if ENABLE_HUSH_READONLY
1267 BLTIN("readonly" , builtin_readonly, "Make variables read-only"),
1268#endif
1269#if ENABLE_HUSH_FUNCTIONS
1270 BLTIN("return" , builtin_return , "Return from function"),
1271#endif
1272#if ENABLE_HUSH_SET
1273 BLTIN("set" , builtin_set , "Set positional parameters"),
1274#endif
1275 BLTIN("shift" , builtin_shift , "Shift positional parameters"),
1276#if BASH_SOURCE
1277 BLTIN("source" , builtin_source , NULL),
1278#endif
1279#if ENABLE_HUSH_TIMES
1280 BLTIN("times" , builtin_times , NULL),
1281#endif
1282#if ENABLE_HUSH_TRAP
1283 BLTIN("trap" , builtin_trap , "Trap signals"),
1284#endif
1285 BLTIN("true" , builtin_true , NULL),
1286#if ENABLE_HUSH_TYPE
1287 BLTIN("type" , builtin_type , "Show command type"),
1288#endif
1289#if ENABLE_HUSH_ULIMIT
1290 BLTIN("ulimit" , shell_builtin_ulimit, "Control resource limits"),
1291#endif
1292#if ENABLE_HUSH_UMASK
1293 BLTIN("umask" , builtin_umask , "Set file creation mask"),
1294#endif
1295#if ENABLE_HUSH_UNSET
1296 BLTIN("unset" , builtin_unset , "Unset variables"),
1297#endif
1298#if ENABLE_HUSH_WAIT
1299 BLTIN("wait" , builtin_wait , "Wait for process to finish"),
1300#endif
1301};
1302/* These builtins won't be used if we are on NOMMU and need to re-exec
1303 * (it's cheaper to run an external program in this case):
1304 */
1305static const struct built_in_command bltins2[] ALIGN_PTR = {
1306#if ENABLE_HUSH_TEST
1307 BLTIN("[" , builtin_test , NULL),
1308#endif
1309#if BASH_TEST2
1310 BLTIN("[[" , builtin_test , NULL),
1311#endif
1312#if ENABLE_HUSH_ECHO
1313 BLTIN("echo" , builtin_echo , NULL),
1314#endif
1315#if ENABLE_HUSH_PRINTF
1316 BLTIN("printf" , builtin_printf , NULL),
1317#endif
1318 BLTIN("pwd" , builtin_pwd , NULL),
1319#if ENABLE_HUSH_TEST
1320 BLTIN("test" , builtin_test , NULL),
1321#endif
1322};
1323
Francis Laniel36836fc2023-12-22 22:02:28 +01001324#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01001325
1326/* Debug printouts.
1327 */
1328#if HUSH_DEBUG >= 2
1329/* prevent disasters with G.debug_indent < 0 */
1330# define indent() fdprintf(2, "%*s", (G.debug_indent * 2) & 0xff, "")
1331# define debug_enter() (G.debug_indent++)
1332# define debug_leave() (G.debug_indent--)
1333#else
1334# define indent() ((void)0)
1335# define debug_enter() ((void)0)
1336# define debug_leave() ((void)0)
1337#endif
1338
1339#ifndef debug_printf
1340# define debug_printf(...) (indent(), fdprintf(2, __VA_ARGS__))
1341#endif
1342
1343#ifndef debug_printf_parse
1344# define debug_printf_parse(...) (indent(), fdprintf(2, __VA_ARGS__))
1345#endif
1346
1347#ifndef debug_printf_heredoc
1348# define debug_printf_heredoc(...) (indent(), fdprintf(2, __VA_ARGS__))
1349#endif
1350
1351#ifndef debug_printf_exec
1352#define debug_printf_exec(...) (indent(), fdprintf(2, __VA_ARGS__))
1353#endif
1354
1355#ifndef debug_printf_env
1356# define debug_printf_env(...) (indent(), fdprintf(2, __VA_ARGS__))
1357#endif
1358
1359#ifndef debug_printf_jobs
1360# define debug_printf_jobs(...) (indent(), fdprintf(2, __VA_ARGS__))
1361# define DEBUG_JOBS 1
1362#else
1363# define DEBUG_JOBS 0
1364#endif
1365
1366#ifndef debug_printf_expand
1367# define debug_printf_expand(...) (indent(), fdprintf(2, __VA_ARGS__))
1368# define DEBUG_EXPAND 1
1369#else
1370# define DEBUG_EXPAND 0
1371#endif
1372
1373#ifndef debug_printf_varexp
1374# define debug_printf_varexp(...) (indent(), fdprintf(2, __VA_ARGS__))
1375#endif
1376
1377#ifndef debug_printf_glob
1378# define debug_printf_glob(...) (indent(), fdprintf(2, __VA_ARGS__))
1379# define DEBUG_GLOB 1
1380#else
1381# define DEBUG_GLOB 0
1382#endif
1383
1384#ifndef debug_printf_redir
1385# define debug_printf_redir(...) (indent(), fdprintf(2, __VA_ARGS__))
1386#endif
1387
1388#ifndef debug_printf_list
1389# define debug_printf_list(...) (indent(), fdprintf(2, __VA_ARGS__))
1390#endif
1391
1392#ifndef debug_printf_subst
1393# define debug_printf_subst(...) (indent(), fdprintf(2, __VA_ARGS__))
1394#endif
1395
1396#ifndef debug_printf_prompt
1397# define debug_printf_prompt(...) (indent(), fdprintf(2, __VA_ARGS__))
1398#endif
1399
1400#ifndef debug_printf_clean
1401# define debug_printf_clean(...) (indent(), fdprintf(2, __VA_ARGS__))
1402# define DEBUG_CLEAN 1
1403#else
1404# define DEBUG_CLEAN 0
1405#endif
1406
1407#if DEBUG_EXPAND
1408static void debug_print_strings(const char *prefix, char **vv)
1409{
1410 indent();
1411 fdprintf(2, "%s:\n", prefix);
1412 while (*vv)
1413 fdprintf(2, " '%s'\n", *vv++);
1414}
1415#else
1416# define debug_print_strings(prefix, vv) ((void)0)
1417#endif
1418
1419
1420/* Leak hunting. Use hush_leaktool.sh for post-processing.
1421 */
1422#if LEAK_HUNTING
1423static void *xxmalloc(int lineno, size_t size)
1424{
1425 void *ptr = xmalloc((size + 0xff) & ~0xff);
1426 fdprintf(2, "line %d: malloc %p\n", lineno, ptr);
1427 return ptr;
1428}
1429static void *xxrealloc(int lineno, void *ptr, size_t size)
1430{
1431 ptr = xrealloc(ptr, (size + 0xff) & ~0xff);
1432 fdprintf(2, "line %d: realloc %p\n", lineno, ptr);
1433 return ptr;
1434}
1435static char *xxstrdup(int lineno, const char *str)
1436{
1437 char *ptr = xstrdup(str);
1438 fdprintf(2, "line %d: strdup %p\n", lineno, ptr);
1439 return ptr;
1440}
1441static void xxfree(void *ptr)
1442{
1443 fdprintf(2, "free %p\n", ptr);
1444 free(ptr);
1445}
1446# define xmalloc(s) xxmalloc(__LINE__, s)
1447# define xrealloc(p, s) xxrealloc(__LINE__, p, s)
1448# define xstrdup(s) xxstrdup(__LINE__, s)
1449# define free(p) xxfree(p)
1450#endif
1451
1452
1453/* Syntax and runtime errors. They always abort scripts.
1454 * In interactive use they usually discard unparsed and/or unexecuted commands
1455 * and return to the prompt.
1456 * HUSH_DEBUG >= 2 prints line number in this file where it was detected.
1457 */
1458#if HUSH_DEBUG < 2
1459# define msg_and_die_if_script(lineno, ...) msg_and_die_if_script(__VA_ARGS__)
1460# define syntax_error(lineno, msg) syntax_error(msg)
1461# define syntax_error_at(lineno, msg) syntax_error_at(msg)
1462# define syntax_error_unterm_ch(lineno, ch) syntax_error_unterm_ch(ch)
1463# define syntax_error_unterm_str(lineno, s) syntax_error_unterm_str(s)
1464# define syntax_error_unexpected_ch(lineno, ch) syntax_error_unexpected_ch(ch)
1465#endif
1466
1467static void die_if_script(void)
1468{
1469 if (!G_interactive_fd) {
1470 if (G.last_exitcode) /* sometines it's 2, not 1 (bash compat) */
1471 xfunc_error_retval = G.last_exitcode;
1472 xfunc_die();
1473 }
1474}
1475
Francis Laniel36836fc2023-12-22 22:02:28 +01001476#ifdef __U_BOOT__
1477static void __maybe_unused msg_and_die_if_script(unsigned lineno, const char *fmt, ...)
1478#else /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01001479static void msg_and_die_if_script(unsigned lineno, const char *fmt, ...)
Francis Laniel36836fc2023-12-22 22:02:28 +01001480#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01001481{
1482 va_list p;
1483
1484#if HUSH_DEBUG >= 2
1485 bb_error_msg("hush.c:%u", lineno);
1486#endif
1487 va_start(p, fmt);
1488 bb_verror_msg(fmt, p, NULL);
1489 va_end(p);
1490 die_if_script();
1491}
1492
1493static void syntax_error(unsigned lineno UNUSED_PARAM, const char *msg)
1494{
1495 if (msg)
1496 bb_error_msg("syntax error: %s", msg);
1497 else
1498 bb_simple_error_msg("syntax error");
1499 die_if_script();
1500}
1501
1502static void syntax_error_at(unsigned lineno UNUSED_PARAM, const char *msg)
1503{
1504 bb_error_msg("syntax error at '%s'", msg);
1505 die_if_script();
1506}
1507
1508static void syntax_error_unterm_str(unsigned lineno UNUSED_PARAM, const char *s)
1509{
1510 bb_error_msg("syntax error: unterminated %s", s);
1511//? source4.tests fails: in bash, echo ${^} in script does not terminate the script
Francis Laniele7ca3a32023-12-22 22:02:42 +01001512// (but bash --posix, or if bash is run as "sh", does terminate in script, so maybe uncomment this?)
Francis Laniel110b7692023-12-22 22:02:27 +01001513// die_if_script();
1514}
1515
1516static void syntax_error_unterm_ch(unsigned lineno, char ch)
1517{
1518 char msg[2] = { ch, '\0' };
1519 syntax_error_unterm_str(lineno, msg);
1520}
1521
1522static void syntax_error_unexpected_ch(unsigned lineno UNUSED_PARAM, int ch)
1523{
1524 char msg[2];
1525 msg[0] = ch;
1526 msg[1] = '\0';
1527#if HUSH_DEBUG >= 2
1528 bb_error_msg("hush.c:%u", lineno);
1529#endif
1530 bb_error_msg("syntax error: unexpected %s", ch == EOF ? "EOF" : msg);
1531 die_if_script();
1532}
1533
1534#if HUSH_DEBUG < 2
1535# undef msg_and_die_if_script
1536# undef syntax_error
1537# undef syntax_error_at
1538# undef syntax_error_unterm_ch
1539# undef syntax_error_unterm_str
1540# undef syntax_error_unexpected_ch
1541#else
1542# define msg_and_die_if_script(...) msg_and_die_if_script(__LINE__, __VA_ARGS__)
1543# define syntax_error(msg) syntax_error(__LINE__, msg)
1544# define syntax_error_at(msg) syntax_error_at(__LINE__, msg)
1545# define syntax_error_unterm_ch(ch) syntax_error_unterm_ch(__LINE__, ch)
1546# define syntax_error_unterm_str(s) syntax_error_unterm_str(__LINE__, s)
1547# define syntax_error_unexpected_ch(ch) syntax_error_unexpected_ch(__LINE__, ch)
1548#endif
1549
Francis Laniel110b7692023-12-22 22:02:27 +01001550/* Utility functions
1551 */
1552/* Replace each \x with x in place, return ptr past NUL. */
1553static char *unbackslash(char *src)
1554{
Francis Laniel36836fc2023-12-22 22:02:28 +01001555#ifdef __U_BOOT__
1556 char *dst = src = (char *)strchrnul(src, '\\');
1557#else /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01001558 char *dst = src = strchrnul(src, '\\');
Francis Laniel36836fc2023-12-22 22:02:28 +01001559#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01001560 while (1) {
1561 if (*src == '\\') {
1562 src++;
1563 if (*src != '\0') {
1564 /* \x -> x */
1565 *dst++ = *src++;
1566 continue;
1567 }
1568 /* else: "\<nul>". Do not delete this backslash.
1569 * Testcase: eval 'echo ok\'
1570 */
1571 *dst++ = '\\';
1572 /* fallthrough */
1573 }
1574 if ((*dst++ = *src++) == '\0')
1575 break;
1576 }
1577 return dst;
1578}
1579
1580static char **add_strings_to_strings(char **strings, char **add, int need_to_dup)
1581{
1582 int i;
1583 unsigned count1;
1584 unsigned count2;
1585 char **v;
1586
1587 v = strings;
1588 count1 = 0;
1589 if (v) {
1590 while (*v) {
1591 count1++;
1592 v++;
1593 }
1594 }
1595 count2 = 0;
1596 v = add;
1597 while (*v) {
1598 count2++;
1599 v++;
1600 }
1601 v = xrealloc(strings, (count1 + count2 + 1) * sizeof(char*));
1602 v[count1 + count2] = NULL;
1603 i = count2;
1604 while (--i >= 0)
1605 v[count1 + i] = (need_to_dup ? xstrdup(add[i]) : add[i]);
1606 return v;
1607}
1608#if LEAK_HUNTING
1609static char **xx_add_strings_to_strings(int lineno, char **strings, char **add, int need_to_dup)
1610{
1611 char **ptr = add_strings_to_strings(strings, add, need_to_dup);
1612 fdprintf(2, "line %d: add_strings_to_strings %p\n", lineno, ptr);
1613 return ptr;
1614}
1615#define add_strings_to_strings(strings, add, need_to_dup) \
1616 xx_add_strings_to_strings(__LINE__, strings, add, need_to_dup)
1617#endif
1618
1619/* Note: takes ownership of "add" ptr (it is not strdup'ed) */
1620static char **add_string_to_strings(char **strings, char *add)
1621{
1622 char *v[2];
1623 v[0] = add;
1624 v[1] = NULL;
1625 return add_strings_to_strings(strings, v, /*dup:*/ 0);
1626}
Francis Laniel36836fc2023-12-22 22:02:28 +01001627
Francis Laniel110b7692023-12-22 22:02:27 +01001628#if LEAK_HUNTING
1629static char **xx_add_string_to_strings(int lineno, char **strings, char *add)
1630{
1631 char **ptr = add_string_to_strings(strings, add);
1632 fdprintf(2, "line %d: add_string_to_strings %p\n", lineno, ptr);
1633 return ptr;
1634}
1635#define add_string_to_strings(strings, add) \
1636 xx_add_string_to_strings(__LINE__, strings, add)
1637#endif
1638
1639static void free_strings(char **strings)
1640{
1641 char **v;
1642
1643 if (!strings)
1644 return;
1645 v = strings;
1646 while (*v) {
1647 free(*v);
1648 v++;
1649 }
1650 free(strings);
1651}
1652
Francis Laniel36836fc2023-12-22 22:02:28 +01001653#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01001654static int dup_CLOEXEC(int fd, int avoid_fd)
1655{
1656 int newfd;
1657 repeat:
1658 newfd = fcntl(fd, F_DUPFD_CLOEXEC, avoid_fd + 1);
1659 if (newfd >= 0) {
1660 if (F_DUPFD_CLOEXEC == F_DUPFD) /* if old libc (w/o F_DUPFD_CLOEXEC) */
1661 fcntl(newfd, F_SETFD, FD_CLOEXEC);
1662 } else { /* newfd < 0 */
1663 if (errno == EBUSY)
1664 goto repeat;
1665 if (errno == EINTR)
1666 goto repeat;
1667 }
1668 return newfd;
1669}
1670
1671static int xdup_CLOEXEC_and_close(int fd, int avoid_fd)
1672{
1673 int newfd;
1674 repeat:
1675 newfd = fcntl(fd, F_DUPFD_CLOEXEC, avoid_fd + 1);
1676 if (newfd < 0) {
1677 if (errno == EBUSY)
1678 goto repeat;
1679 if (errno == EINTR)
1680 goto repeat;
1681 /* fd was not open? */
1682 if (errno == EBADF)
1683 return fd;
1684 xfunc_die();
1685 }
1686 if (F_DUPFD_CLOEXEC == F_DUPFD) /* if old libc (w/o F_DUPFD_CLOEXEC) */
1687 fcntl(newfd, F_SETFD, FD_CLOEXEC);
1688 close(fd);
1689 return newfd;
1690}
1691
1692
1693/* Manipulating HFILEs */
1694static HFILE *hfopen(const char *name)
1695{
1696 HFILE *fp;
1697 int fd;
1698
1699 fd = STDIN_FILENO;
1700 if (name) {
1701 fd = open(name, O_RDONLY | O_CLOEXEC);
1702 if (fd < 0)
1703 return NULL;
1704 if (O_CLOEXEC == 0) /* ancient libc */
1705 close_on_exec_on(fd);
1706 }
1707
1708 fp = xmalloc(sizeof(*fp));
1709 if (name == NULL)
1710 G.HFILE_stdin = fp;
1711 fp->fd = fd;
1712 fp->cur = fp->end = fp->buf;
1713 fp->next_hfile = G.HFILE_list;
1714 G.HFILE_list = fp;
1715 return fp;
1716}
1717static void hfclose(HFILE *fp)
1718{
1719 HFILE **pp = &G.HFILE_list;
1720 while (*pp) {
1721 HFILE *cur = *pp;
1722 if (cur == fp) {
1723 *pp = cur->next_hfile;
1724 break;
1725 }
1726 pp = &cur->next_hfile;
1727 }
1728 if (fp->fd >= 0)
1729 close(fp->fd);
1730 free(fp);
1731}
1732static int refill_HFILE_and_getc(HFILE *fp)
1733{
1734 int n;
1735
1736 if (fp->fd < 0) {
1737 /* Already saw EOF */
1738 return EOF;
1739 }
1740#if ENABLE_HUSH_INTERACTIVE && !ENABLE_FEATURE_EDITING
1741 /* If user presses ^C, read() restarts after SIGINT (we use SA_RESTART).
1742 * IOW: ^C will not immediately stop line input.
1743 * But poll() is different: it does NOT restart after signals.
1744 */
1745 if (fp == G.HFILE_stdin) {
1746 struct pollfd pfd[1];
1747 pfd[0].fd = fp->fd;
1748 pfd[0].events = POLLIN;
1749 n = poll(pfd, 1, -1);
1750 if (n < 0
1751 /*&& errno == EINTR - assumed true */
1752 && sigismember(&G.pending_set, SIGINT)
1753 ) {
1754 return '\0';
1755 }
1756 }
1757#else
1758/* if FEATURE_EDITING=y, we do not use this routine for interactive input */
1759#endif
1760 /* Try to buffer more input */
1761 n = safe_read(fp->fd, fp->buf, sizeof(fp->buf));
1762 if (n < 0) {
1763 bb_simple_perror_msg("read error");
1764 n = 0;
1765 }
1766 fp->cur = fp->buf;
1767 fp->end = fp->buf + n;
1768 if (n == 0) {
1769 /* EOF/error */
1770 close(fp->fd);
1771 fp->fd = -1;
1772 return EOF;
1773 }
1774 return (unsigned char)(*fp->cur++);
1775}
1776/* Inlined for common case of non-empty buffer.
1777 */
1778static ALWAYS_INLINE int hfgetc(HFILE *fp)
1779{
1780 if (fp->cur < fp->end)
1781 return (unsigned char)(*fp->cur++);
1782 /* Buffer empty */
1783 return refill_HFILE_and_getc(fp);
1784}
1785static int move_HFILEs_on_redirect(int fd, int avoid_fd)
1786{
1787 HFILE *fl = G.HFILE_list;
1788 while (fl) {
1789 if (fd == fl->fd) {
1790 /* We use it only on script files, they are all CLOEXEC */
1791 fl->fd = xdup_CLOEXEC_and_close(fd, avoid_fd);
1792 debug_printf_redir("redirect_fd %d: matches a script fd, moving it to %d\n", fd, fl->fd);
1793 return 1; /* "found and moved" */
1794 }
1795 fl = fl->next_hfile;
1796 }
1797#if ENABLE_HUSH_MODE_X
1798 if (G.x_mode_fd > 0 && fd == G.x_mode_fd) {
1799 G.x_mode_fd = xdup_CLOEXEC_and_close(fd, avoid_fd);
1800 return 1; /* "found and moved" */
1801 }
1802#endif
1803 return 0; /* "not in the list" */
1804}
1805#if ENABLE_FEATURE_SH_STANDALONE && BB_MMU
1806static void close_all_HFILE_list(void)
1807{
1808 HFILE *fl = G.HFILE_list;
1809 while (fl) {
1810 /* hfclose would also free HFILE object.
1811 * It is disastrous if we share memory with a vforked parent.
1812 * I'm not sure we never come here after vfork.
1813 * Therefore just close fd, nothing more.
1814 *
1815 * ">" instead of ">=": we don't close fd#0,
1816 * interactive shell uses hfopen(NULL) as stdin input
1817 * which has fl->fd == 0, but fd#0 gets redirected in pipes.
1818 * If we'd close it here, then e.g. interactive "set | sort"
1819 * with NOFORKed sort, would have sort's input fd closed.
1820 */
1821 if (fl->fd > 0)
1822 /*hfclose(fl); - unsafe */
1823 close(fl->fd);
1824 fl = fl->next_hfile;
1825 }
1826}
1827#endif
1828static int fd_in_HFILEs(int fd)
1829{
1830 HFILE *fl = G.HFILE_list;
1831 while (fl) {
1832 if (fl->fd == fd)
1833 return 1;
1834 fl = fl->next_hfile;
1835 }
1836 return 0;
1837}
1838
Francis Laniel36836fc2023-12-22 22:02:28 +01001839#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01001840
1841/* Helpers for setting new $n and restoring them back
1842 */
1843typedef struct save_arg_t {
1844 char *sv_argv0;
1845 char **sv_g_argv;
1846 int sv_g_argc;
Francis Laniel36836fc2023-12-22 22:02:28 +01001847#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01001848 IF_HUSH_SET(smallint sv_g_malloced;)
Francis Laniel36836fc2023-12-22 22:02:28 +01001849#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01001850} save_arg_t;
1851
Francis Laniel36836fc2023-12-22 22:02:28 +01001852#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01001853static void save_and_replace_G_args(save_arg_t *sv, char **argv)
1854{
1855 sv->sv_argv0 = argv[0];
1856 sv->sv_g_argv = G.global_argv;
1857 sv->sv_g_argc = G.global_argc;
1858 IF_HUSH_SET(sv->sv_g_malloced = G.global_args_malloced;)
1859
1860 argv[0] = G.global_argv[0]; /* retain $0 */
1861 G.global_argv = argv;
1862 IF_HUSH_SET(G.global_args_malloced = 0;)
1863
1864 G.global_argc = 1 + string_array_len(argv + 1);
1865}
1866
1867static void restore_G_args(save_arg_t *sv, char **argv)
1868{
1869#if ENABLE_HUSH_SET
1870 if (G.global_args_malloced) {
1871 /* someone ran "set -- arg1 arg2 ...", undo */
1872 char **pp = G.global_argv;
1873 while (*++pp) /* note: does not free $0 */
1874 free(*pp);
1875 free(G.global_argv);
1876 }
1877#endif
1878 argv[0] = sv->sv_argv0;
1879 G.global_argv = sv->sv_g_argv;
1880 G.global_argc = sv->sv_g_argc;
1881 IF_HUSH_SET(G.global_args_malloced = sv->sv_g_malloced;)
1882}
Francis Laniel36836fc2023-12-22 22:02:28 +01001883#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01001884
1885
Francis Laniel36836fc2023-12-22 22:02:28 +01001886#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01001887/* Basic theory of signal handling in shell
1888 * ========================================
1889 * This does not describe what hush does, rather, it is current understanding
1890 * what it _should_ do. If it doesn't, it's a bug.
1891 * http://www.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#trap
1892 *
1893 * Signals are handled only after each pipe ("cmd | cmd | cmd" thing)
1894 * is finished or backgrounded. It is the same in interactive and
1895 * non-interactive shells, and is the same regardless of whether
1896 * a user trap handler is installed or a shell special one is in effect.
1897 * ^C or ^Z from keyboard seems to execute "at once" because it usually
1898 * backgrounds (i.e. stops) or kills all members of currently running
1899 * pipe.
1900 *
1901 * Wait builtin is interruptible by signals for which user trap is set
1902 * or by SIGINT in interactive shell.
1903 *
1904 * Trap handlers will execute even within trap handlers. (right?)
1905 *
1906 * User trap handlers are forgotten when subshell ("(cmd)") is entered,
1907 * except for handlers set to '' (empty string).
1908 *
1909 * If job control is off, backgrounded commands ("cmd &")
1910 * have SIGINT, SIGQUIT set to SIG_IGN.
1911 *
1912 * Commands which are run in command substitution ("`cmd`")
1913 * have SIGTTIN, SIGTTOU, SIGTSTP set to SIG_IGN.
1914 *
1915 * Ordinary commands have signals set to SIG_IGN/DFL as inherited
1916 * by the shell from its parent.
1917 *
1918 * Signals which differ from SIG_DFL action
1919 * (note: child (i.e., [v]forked) shell is not an interactive shell):
1920 *
1921 * SIGQUIT: ignore
1922 * SIGTERM (interactive): ignore
1923 * SIGHUP (interactive):
Francis Laniele7ca3a32023-12-22 22:02:42 +01001924 * Send SIGCONT to stopped jobs, send SIGHUP to all jobs and exit.
1925 * Kernel would do this for us ("orphaned process group" handling
1926 * according to POSIX) if we are a session leader and thus our death
1927 * frees the controlling tty, but to be bash-compatible, we also do it
1928 * for every interactive shell's death by SIGHUP.
1929 * (Also, we need to restore tty pgrp, otherwise e.g. Midnight Commander
1930 * backgrounds when hush started from it gets killed by SIGHUP).
Francis Laniel110b7692023-12-22 22:02:27 +01001931 * SIGTTIN, SIGTTOU, SIGTSTP (if job control is on): ignore
1932 * Note that ^Z is handled not by trapping SIGTSTP, but by seeing
1933 * that all pipe members are stopped. Try this in bash:
1934 * while :; do :; done - ^Z does not background it
1935 * (while :; do :; done) - ^Z backgrounds it
1936 * SIGINT (interactive): wait for last pipe, ignore the rest
1937 * of the command line, show prompt. NB: ^C does not send SIGINT
1938 * to interactive shell while shell is waiting for a pipe,
1939 * since shell is bg'ed (is not in foreground process group).
1940 * Example 1: this waits 5 sec, but does not execute ls:
1941 * "echo $$; sleep 5; ls -l" + "kill -INT <pid>"
1942 * Example 2: this does not wait and does not execute ls:
1943 * "echo $$; sleep 5 & wait; ls -l" + "kill -INT <pid>"
1944 * Example 3: this does not wait 5 sec, but executes ls:
1945 * "sleep 5; ls -l" + press ^C
1946 * Example 4: this does not wait and does not execute ls:
1947 * "sleep 5 & wait; ls -l" + press ^C
1948 *
1949 * (What happens to signals which are IGN on shell start?)
1950 * (What happens with signal mask on shell start?)
1951 *
1952 * Old implementation
1953 * ==================
1954 * We use in-kernel pending signal mask to determine which signals were sent.
1955 * We block all signals which we don't want to take action immediately,
1956 * i.e. we block all signals which need to have special handling as described
1957 * above, and all signals which have traps set.
1958 * After each pipe execution, we extract any pending signals via sigtimedwait()
1959 * and act on them.
1960 *
1961 * unsigned special_sig_mask: a mask of such "special" signals
1962 * sigset_t blocked_set: current blocked signal set
1963 *
1964 * "trap - SIGxxx":
1965 * clear bit in blocked_set unless it is also in special_sig_mask
1966 * "trap 'cmd' SIGxxx":
1967 * set bit in blocked_set (even if 'cmd' is '')
1968 * after [v]fork, if we plan to be a shell:
1969 * unblock signals with special interactive handling
1970 * (child shell is not interactive),
1971 * unset all traps except '' (note: regardless of child shell's type - {}, (), etc)
1972 * after [v]fork, if we plan to exec:
1973 * POSIX says fork clears pending signal mask in child - no need to clear it.
1974 * Restore blocked signal set to one inherited by shell just prior to exec.
1975 *
1976 * Note: as a result, we do not use signal handlers much. The only uses
1977 * are to count SIGCHLDs
1978 * and to restore tty pgrp on signal-induced exit.
1979 *
1980 * Note 2 (compat):
1981 * Standard says "When a subshell is entered, traps that are not being ignored
1982 * are set to the default actions". bash interprets it so that traps which
1983 * are set to '' (ignore) are NOT reset to defaults. We do the same.
1984 *
1985 * Problem: the above approach makes it unwieldy to catch signals while
1986 * we are in read builtin, or while we read commands from stdin:
1987 * masked signals are not visible!
1988 *
1989 * New implementation
1990 * ==================
1991 * We record each signal we are interested in by installing signal handler
1992 * for them - a bit like emulating kernel pending signal mask in userspace.
1993 * We are interested in: signals which need to have special handling
1994 * as described above, and all signals which have traps set.
1995 * Signals are recorded in pending_set.
1996 * After each pipe execution, we extract any pending signals
1997 * and act on them.
1998 *
1999 * unsigned special_sig_mask: a mask of shell-special signals.
2000 * unsigned fatal_sig_mask: a mask of signals on which we restore tty pgrp.
2001 * char *traps[sig] if trap for sig is set (even if it's '').
2002 * sigset_t pending_set: set of sigs we received.
2003 *
2004 * "trap - SIGxxx":
2005 * if sig is in special_sig_mask, set handler back to:
2006 * record_pending_signo, or to IGN if it's a tty stop signal
2007 * if sig is in fatal_sig_mask, set handler back to sigexit.
2008 * else: set handler back to SIG_DFL
2009 * "trap 'cmd' SIGxxx":
2010 * set handler to record_pending_signo.
2011 * "trap '' SIGxxx":
2012 * set handler to SIG_IGN.
2013 * after [v]fork, if we plan to be a shell:
2014 * set signals with special interactive handling to SIG_DFL
2015 * (because child shell is not interactive),
2016 * unset all traps except '' (note: regardless of child shell's type - {}, (), etc)
2017 * after [v]fork, if we plan to exec:
2018 * POSIX says fork clears pending signal mask in child - no need to clear it.
2019 *
2020 * To make wait builtin interruptible, we handle SIGCHLD as special signal,
2021 * otherwise (if we leave it SIG_DFL) sigsuspend in wait builtin will not wake up on it.
2022 *
2023 * Note (compat):
2024 * Standard says "When a subshell is entered, traps that are not being ignored
2025 * are set to the default actions". bash interprets it so that traps which
2026 * are set to '' (ignore) are NOT reset to defaults. We do the same.
2027 */
2028enum {
2029 SPECIAL_INTERACTIVE_SIGS = 0
2030 | (1 << SIGTERM)
2031 | (1 << SIGINT)
2032 | (1 << SIGHUP)
2033 ,
2034 SPECIAL_JOBSTOP_SIGS = 0
2035#if ENABLE_HUSH_JOB
2036 | (1 << SIGTTIN)
2037 | (1 << SIGTTOU)
2038 | (1 << SIGTSTP)
2039#endif
2040 ,
2041};
2042
2043static void record_pending_signo(int sig)
2044{
2045 sigaddset(&G.pending_set, sig);
Francis Laniele7ca3a32023-12-22 22:02:42 +01002046#if ENABLE_FEATURE_EDITING
2047 if (sig != SIGCHLD
2048 || (G_traps && G_traps[SIGCHLD] && G_traps[SIGCHLD][0])
2049 /* ^^^ if SIGCHLD, interrupt line reading only if it has a trap */
2050 ) {
2051 bb_got_signal = sig; /* for read_line_input: "we got a signal" */
2052 }
2053#endif
Francis Laniel110b7692023-12-22 22:02:27 +01002054#if ENABLE_HUSH_FAST
2055 if (sig == SIGCHLD) {
2056 G.count_SIGCHLD++;
2057//bb_error_msg("[%d] SIGCHLD_handler: G.count_SIGCHLD:%d G.handled_SIGCHLD:%d", getpid(), G.count_SIGCHLD, G.handled_SIGCHLD);
2058 }
2059#endif
2060}
2061
2062static sighandler_t install_sighandler(int sig, sighandler_t handler)
2063{
2064 struct sigaction old_sa;
2065
2066 /* We could use signal() to install handlers... almost:
2067 * except that we need to mask ALL signals while handlers run.
2068 * I saw signal nesting in strace, race window isn't small.
2069 * SA_RESTART is also needed, but in Linux, signal()
2070 * sets SA_RESTART too.
2071 */
2072 /* memset(&G.sa, 0, sizeof(G.sa)); - already done */
2073 /* sigfillset(&G.sa.sa_mask); - already done */
2074 /* G.sa.sa_flags = SA_RESTART; - already done */
2075 G.sa.sa_handler = handler;
2076 sigaction(sig, &G.sa, &old_sa);
2077 return old_sa.sa_handler;
2078}
Francis Laniel36836fc2023-12-22 22:02:28 +01002079#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01002080
Francis Laniel36836fc2023-12-22 22:02:28 +01002081#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01002082static void hush_exit(int exitcode) NORETURN;
2083
2084static void restore_ttypgrp_and__exit(void) NORETURN;
2085static void restore_ttypgrp_and__exit(void)
2086{
2087 /* xfunc has failed! die die die */
2088 /* no EXIT traps, this is an escape hatch! */
2089 G.exiting = 1;
2090 hush_exit(xfunc_error_retval);
2091}
2092
2093#if ENABLE_HUSH_JOB
2094
2095/* Needed only on some libc:
2096 * It was observed that on exit(), fgetc'ed buffered data
2097 * gets "unwound" via lseek(fd, -NUM, SEEK_CUR).
2098 * With the net effect that even after fork(), not vfork(),
2099 * exit() in NOEXECed applet in "sh SCRIPT":
2100 * noexec_applet_here
2101 * echo END_OF_SCRIPT
2102 * lseeks fd in input FILE object from EOF to "e" in "echo END_OF_SCRIPT".
2103 * This makes "echo END_OF_SCRIPT" executed twice.
2104 * Similar problems can be seen with msg_and_die_if_script() -> xfunc_die()
2105 * and in `cmd` handling.
2106 * If set as die_func(), this makes xfunc_die() exit via _exit(), not exit():
2107 */
2108static void fflush_and__exit(void) NORETURN;
2109static void fflush_and__exit(void)
2110{
2111 fflush_all();
2112 _exit(xfunc_error_retval);
2113}
2114
2115/* After [v]fork, in child: do not restore tty pgrp on xfunc death */
2116# define disable_restore_tty_pgrp_on_exit() (die_func = fflush_and__exit)
2117/* After [v]fork, in parent: restore tty pgrp on xfunc death */
2118# define enable_restore_tty_pgrp_on_exit() (die_func = restore_ttypgrp_and__exit)
2119
2120/* Restores tty foreground process group, and exits.
2121 * May be called as signal handler for fatal signal
2122 * (will resend signal to itself, producing correct exit state)
2123 * or called directly with -EXITCODE.
2124 * We also call it if xfunc is exiting.
2125 */
2126static void sigexit(int sig) NORETURN;
2127static void sigexit(int sig)
2128{
2129 /* Careful: we can end up here after [v]fork. Do not restore
2130 * tty pgrp then, only top-level shell process does that */
2131 if (G_saved_tty_pgrp && getpid() == G.root_pid) {
2132 /* Disable all signals: job control, SIGPIPE, etc.
2133 * Mostly paranoid measure, to prevent infinite SIGTTOU.
2134 */
2135 sigprocmask_allsigs(SIG_BLOCK);
2136 tcsetpgrp(G_interactive_fd, G_saved_tty_pgrp);
2137 }
2138
2139 /* Not a signal, just exit */
2140 if (sig <= 0)
2141 _exit(- sig);
2142
2143 kill_myself_with_sig(sig); /* does not return */
2144}
2145#else
2146
2147# define disable_restore_tty_pgrp_on_exit() ((void)0)
2148# define enable_restore_tty_pgrp_on_exit() ((void)0)
2149
2150#endif
Francis Laniel36836fc2023-12-22 22:02:28 +01002151#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01002152
Francis Laniel36836fc2023-12-22 22:02:28 +01002153#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01002154static sighandler_t pick_sighandler(unsigned sig)
2155{
2156 sighandler_t handler = SIG_DFL;
2157 if (sig < sizeof(unsigned)*8) {
2158 unsigned sigmask = (1 << sig);
2159
2160#if ENABLE_HUSH_JOB
2161 /* is sig fatal? */
2162 if (G_fatal_sig_mask & sigmask)
2163 handler = sigexit;
2164 else
2165#endif
2166 /* sig has special handling? */
2167 if (G.special_sig_mask & sigmask) {
2168 handler = record_pending_signo;
2169 /* TTIN/TTOU/TSTP can't be set to record_pending_signo
2170 * in order to ignore them: they will be raised
2171 * in an endless loop when we try to do some
2172 * terminal ioctls! We do have to _ignore_ these.
2173 */
2174 if (SPECIAL_JOBSTOP_SIGS & sigmask)
2175 handler = SIG_IGN;
2176 }
2177 }
2178 return handler;
2179}
Francis Laniel36836fc2023-12-22 22:02:28 +01002180#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01002181
Francis Laniel36836fc2023-12-22 22:02:28 +01002182#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01002183/* Restores tty foreground process group, and exits. */
2184static void hush_exit(int exitcode)
2185{
2186#if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT
2187 save_history(G.line_input_state); /* may be NULL */
2188#endif
2189
2190 fflush_all();
2191 if (G.exiting <= 0 && G_traps && G_traps[0] && G_traps[0][0]) {
2192 char *argv[3];
2193 /* argv[0] is unused */
2194 argv[1] = xstrdup(G_traps[0]); /* copy, since EXIT trap handler may modify G_traps[0] */
2195 argv[2] = NULL;
2196 G.exiting = 1; /* prevent EXIT trap recursion */
2197 /* Note: G_traps[0] is not cleared!
2198 * "trap" will still show it, if executed
2199 * in the handler */
2200 builtin_eval(argv);
2201 }
2202
2203#if ENABLE_FEATURE_CLEAN_UP
2204 {
2205 struct variable *cur_var;
2206 if (G.cwd != bb_msg_unknown)
2207 free((char*)G.cwd);
2208 cur_var = G.top_var;
2209 while (cur_var) {
2210 struct variable *tmp = cur_var;
2211 if (!cur_var->max_len)
2212 free(cur_var->varstr);
2213 cur_var = cur_var->next;
2214 free(tmp);
2215 }
2216 }
2217#endif
2218
2219 fflush_all();
2220#if ENABLE_HUSH_JOB
2221 sigexit(- (exitcode & 0xff));
2222#else
2223 _exit(exitcode);
2224#endif
2225}
2226
2227//TODO: return a mask of ALL handled sigs?
2228static int check_and_run_traps(void)
2229{
2230 int last_sig = 0;
2231
2232 while (1) {
2233 int sig;
2234
2235 if (sigisemptyset(&G.pending_set))
2236 break;
2237 sig = 0;
2238 do {
2239 sig++;
2240 if (sigismember(&G.pending_set, sig)) {
2241 sigdelset(&G.pending_set, sig);
2242 goto got_sig;
2243 }
2244 } while (sig < NSIG);
2245 break;
2246 got_sig:
2247#if ENABLE_HUSH_TRAP
2248 if (G_traps && G_traps[sig]) {
2249 debug_printf_exec("%s: sig:%d handler:'%s'\n", __func__, sig, G.traps[sig]);
2250 if (G_traps[sig][0]) {
2251 /* We have user-defined handler */
2252 smalluint save_rcode;
2253 int save_pre;
2254 char *argv[3];
2255 /* argv[0] is unused */
2256 argv[1] = xstrdup(G_traps[sig]);
2257 /* why strdup? trap can modify itself: trap 'trap "echo oops" INT' INT */
2258 argv[2] = NULL;
2259 save_pre = G.pre_trap_exitcode;
2260 G.pre_trap_exitcode = save_rcode = G.last_exitcode;
2261 builtin_eval(argv);
2262 free(argv[1]);
2263 G.pre_trap_exitcode = save_pre;
2264 G.last_exitcode = save_rcode;
2265# if ENABLE_HUSH_FUNCTIONS
2266 if (G.return_exitcode >= 0) {
2267 debug_printf_exec("trap exitcode:%d\n", G.return_exitcode);
2268 G.last_exitcode = G.return_exitcode;
2269 }
2270# endif
2271 last_sig = sig;
2272 } /* else: "" trap, ignoring signal */
2273 continue;
2274 }
2275#endif
2276 /* not a trap: special action */
2277 switch (sig) {
2278 case SIGINT:
2279 debug_printf_exec("%s: sig:%d default SIGINT handler\n", __func__, sig);
2280 G.flag_SIGINT = 1;
2281 last_sig = sig;
2282 break;
2283#if ENABLE_HUSH_JOB
2284 case SIGHUP: {
Francis Laniele7ca3a32023-12-22 22:02:42 +01002285 /* if (G_interactive_fd) - no need to check, the handler
2286 * is only installed if we *are* interactive */
2287 {
2288 /* bash compat: "Before exiting, an interactive
2289 * shell resends the SIGHUP to all jobs, running
2290 * or stopped. Stopped jobs are sent SIGCONT
2291 * to ensure that they receive the SIGHUP."
2292 */
2293 struct pipe *job;
2294 debug_printf_exec("%s: sig:%d default SIGHUP handler\n", __func__, sig);
2295 /* bash is observed to signal whole process groups,
2296 * not individual processes */
2297 for (job = G.job_list; job; job = job->next) {
2298 if (job->pgrp <= 0)
2299 continue;
2300 debug_printf_exec("HUPing pgrp %d\n", job->pgrp);
2301 if (kill(- job->pgrp, SIGHUP) == 0)
2302 kill(- job->pgrp, SIGCONT);
2303 }
Francis Laniel110b7692023-12-22 22:02:27 +01002304 }
Francis Laniele7ca3a32023-12-22 22:02:42 +01002305 /* this restores tty pgrp, then kills us with SIGHUP */
Francis Laniel110b7692023-12-22 22:02:27 +01002306 sigexit(SIGHUP);
2307 }
2308#endif
2309#if ENABLE_HUSH_FAST
2310 case SIGCHLD:
2311 debug_printf_exec("%s: sig:%d default SIGCHLD handler\n", __func__, sig);
2312 G.count_SIGCHLD++;
2313//bb_error_msg("[%d] check_and_run_traps: G.count_SIGCHLD:%d G.handled_SIGCHLD:%d", getpid(), G.count_SIGCHLD, G.handled_SIGCHLD);
2314 /* Note:
2315 * We don't do 'last_sig = sig' here -> NOT returning this sig.
2316 * This simplifies wait builtin a bit.
2317 */
2318 break;
2319#endif
2320 default: /* ignored: */
2321 debug_printf_exec("%s: sig:%d default handling is to ignore\n", __func__, sig);
2322 /* SIGTERM, SIGQUIT, SIGTTIN, SIGTTOU, SIGTSTP */
2323 /* Note:
2324 * We don't do 'last_sig = sig' here -> NOT returning this sig.
2325 * Example: wait is not interrupted by TERM
2326 * in interactive shell, because TERM is ignored.
2327 */
2328 break;
2329 }
2330 }
2331 return last_sig;
2332}
2333
2334
2335static const char *get_cwd(int force)
2336{
2337 if (force || G.cwd == NULL) {
2338 /* xrealloc_getcwd_or_warn(arg) calls free(arg),
2339 * we must not try to free(bb_msg_unknown) */
2340 if (G.cwd == bb_msg_unknown)
2341 G.cwd = NULL;
2342 G.cwd = xrealloc_getcwd_or_warn((char *)G.cwd);
2343 if (!G.cwd)
2344 G.cwd = bb_msg_unknown;
2345 }
2346 return G.cwd;
2347}
2348
Francis Laniel36836fc2023-12-22 22:02:28 +01002349#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01002350
2351/*
2352 * Shell and environment variable support
2353 */
Francis Laniele7ca3a32023-12-22 22:02:42 +01002354static struct variable **get_ptr_to_local_var(const char *name)
Francis Laniel110b7692023-12-22 22:02:27 +01002355{
2356 struct variable **pp;
2357 struct variable *cur;
2358
2359 pp = &G.top_var;
2360 while ((cur = *pp) != NULL) {
Francis Laniele7ca3a32023-12-22 22:02:42 +01002361 if (varcmp(cur->varstr, name) == 0)
Francis Laniel110b7692023-12-22 22:02:27 +01002362 return pp;
2363 pp = &cur->next;
2364 }
2365 return NULL;
2366}
2367
2368static const char* FAST_FUNC get_local_var_value(const char *name)
2369{
2370 struct variable **vpp;
Francis Laniel110b7692023-12-22 22:02:27 +01002371
2372 if (G.expanded_assignments) {
2373 char **cpp = G.expanded_assignments;
2374 while (*cpp) {
2375 char *cp = *cpp;
Francis Laniele7ca3a32023-12-22 22:02:42 +01002376 if (varcmp(cp, name) == 0)
2377 return strchr(cp, '=') + 1;
Francis Laniel110b7692023-12-22 22:02:27 +01002378 cpp++;
2379 }
2380 }
2381
Francis Laniele7ca3a32023-12-22 22:02:42 +01002382 vpp = get_ptr_to_local_var(name);
Francis Laniel110b7692023-12-22 22:02:27 +01002383 if (vpp)
Francis Laniele7ca3a32023-12-22 22:02:42 +01002384 return strchr((*vpp)->varstr, '=') + 1;
Francis Laniel110b7692023-12-22 22:02:27 +01002385
Francis Laniel36836fc2023-12-22 22:02:28 +01002386#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01002387 if (strcmp(name, "PPID") == 0)
2388 return utoa(G.root_ppid);
Francis Laniel36836fc2023-12-22 22:02:28 +01002389#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01002390 // bash compat: UID? EUID?
2391#if ENABLE_HUSH_RANDOM_SUPPORT
2392 if (strcmp(name, "RANDOM") == 0)
2393 return utoa(next_random(&G.random_gen));
2394#endif
2395#if ENABLE_HUSH_LINENO_VAR
2396 if (strcmp(name, "LINENO") == 0)
2397 return utoa(G.execute_lineno);
2398#endif
2399#if BASH_EPOCH_VARS
2400 {
2401 const char *fmt = NULL;
2402 if (strcmp(name, "EPOCHSECONDS") == 0)
2403 fmt = "%llu";
2404 else if (strcmp(name, "EPOCHREALTIME") == 0)
2405 fmt = "%llu.%06u";
2406 if (fmt) {
2407 struct timeval tv;
2408 xgettimeofday(&tv);
2409 sprintf(G.epoch_buf, fmt, (unsigned long long)tv.tv_sec,
2410 (unsigned)tv.tv_usec);
2411 return G.epoch_buf;
2412 }
2413 }
2414#endif
2415 return NULL;
2416}
2417
Francis Laniel36836fc2023-12-22 22:02:28 +01002418#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01002419#if ENABLE_HUSH_GETOPTS
Francis Laniele7ca3a32023-12-22 22:02:42 +01002420static void handle_changed_special_names(const char *name)
Francis Laniel110b7692023-12-22 22:02:27 +01002421{
Francis Laniele7ca3a32023-12-22 22:02:42 +01002422 if (varcmp(name, "OPTIND") == 0) {
2423 G.getopt_count = 0;
2424 return;
Francis Laniel110b7692023-12-22 22:02:27 +01002425 }
2426}
2427#else
2428/* Do not even bother evaluating arguments */
2429# define handle_changed_special_names(...) ((void)0)
2430#endif
Francis Laniel36836fc2023-12-22 22:02:28 +01002431#else /* __U_BOOT__ */
2432/* Do not even bother evaluating arguments */
2433# define handle_changed_special_names(...) ((void)0)
2434#endif /* __U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01002435
2436/* str holds "NAME=VAL" and is expected to be malloced.
2437 * We take ownership of it.
2438 */
Francis Laniel36836fc2023-12-22 22:02:28 +01002439#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01002440#define SETFLAG_EXPORT (1 << 0)
2441#define SETFLAG_UNEXPORT (1 << 1)
2442#define SETFLAG_MAKE_RO (1 << 2)
Francis Laniel36836fc2023-12-22 22:02:28 +01002443#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01002444#define SETFLAG_VARLVL_SHIFT 3
Francis Laniel36836fc2023-12-22 22:02:28 +01002445#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01002446static int set_local_var(char *str, unsigned flags)
Francis Laniel36836fc2023-12-22 22:02:28 +01002447#else /* __U_BOOT__ */
2448int set_local_var_modern(char *str, int flags)
2449#endif /* __U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01002450{
2451 struct variable **cur_pp;
2452 struct variable *cur;
2453 char *free_me = NULL;
2454 char *eq_sign;
2455 int name_len;
2456 int retval;
Francis Laniel36836fc2023-12-22 22:02:28 +01002457#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01002458 unsigned local_lvl = (flags >> SETFLAG_VARLVL_SHIFT);
Francis Laniel36836fc2023-12-22 22:02:28 +01002459#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01002460
2461 eq_sign = strchr(str, '=');
2462 if (HUSH_DEBUG && !eq_sign)
2463 bb_simple_error_msg_and_die("BUG in setvar");
2464
2465 name_len = eq_sign - str + 1; /* including '=' */
2466 cur_pp = &G.top_var;
2467 while ((cur = *cur_pp) != NULL) {
2468 if (strncmp(cur->varstr, str, name_len) != 0) {
2469 cur_pp = &cur->next;
2470 continue;
2471 }
2472
Francis Laniel36836fc2023-12-22 22:02:28 +01002473#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01002474 /* We found an existing var with this name */
2475 if (cur->flg_read_only) {
2476 bb_error_msg("%s: readonly variable", str);
2477 free(str);
2478//NOTE: in bash, assignment in "export READONLY_VAR=Z" fails, and sets $?=1,
2479//but export per se succeeds (does put the var in env). We don't mimic that.
2480 return -1;
2481 }
2482 if (flags & SETFLAG_UNEXPORT) { // && cur->flg_export ?
2483 debug_printf_env("%s: unsetenv '%s'\n", __func__, str);
2484 *eq_sign = '\0';
2485 unsetenv(str);
2486 *eq_sign = '=';
2487 }
2488 if (cur->var_nest_level < local_lvl) {
2489 /* bash 3.2.33(1) and exported vars:
2490 * # export z=z
2491 * # f() { local z=a; env | grep ^z; }
2492 * # f
2493 * z=a
2494 * # env | grep ^z
2495 * z=z
2496 */
2497 if (cur->flg_export)
2498 flags |= SETFLAG_EXPORT;
2499 /* New variable is local ("local VAR=VAL" or
2500 * "VAR=VAL cmd")
2501 * and existing one is global, or local
2502 * on a lower level that new one.
2503 * Remove it from global variable list:
2504 */
2505 *cur_pp = cur->next;
2506 if (G.shadowed_vars_pp) {
2507 /* Save in "shadowed" list */
2508 debug_printf_env("shadowing %s'%s'/%u by '%s'/%u\n",
2509 cur->flg_export ? "exported " : "",
2510 cur->varstr, cur->var_nest_level, str, local_lvl
2511 );
2512 cur->next = *G.shadowed_vars_pp;
2513 *G.shadowed_vars_pp = cur;
2514 } else {
2515 /* Came from pseudo_exec_argv(), no need to save: delete it */
2516 debug_printf_env("shadow-deleting %s'%s'/%u by '%s'/%u\n",
2517 cur->flg_export ? "exported " : "",
2518 cur->varstr, cur->var_nest_level, str, local_lvl
2519 );
2520 if (cur->max_len == 0) /* allocated "VAR=VAL"? */
2521 free_me = cur->varstr; /* then free it later */
2522 free(cur);
2523 }
2524 break;
2525 }
Francis Laniel36836fc2023-12-22 22:02:28 +01002526#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01002527
2528 if (strcmp(cur->varstr + name_len, eq_sign + 1) == 0) {
2529 debug_printf_env("assignement '%s' does not change anything\n", str);
2530 free_and_exp:
2531 free(str);
2532 goto exp;
2533 }
2534
2535 /* Replace the value in the found "struct variable" */
2536 if (cur->max_len != 0) {
2537 if (cur->max_len >= strnlen(str, cur->max_len + 1)) {
2538 /* This one is from startup env, reuse space */
2539 debug_printf_env("reusing startup env for '%s'\n", str);
2540 strcpy(cur->varstr, str);
2541 goto free_and_exp;
2542 }
2543 /* Can't reuse */
2544 cur->max_len = 0;
2545 goto set_str_and_exp;
2546 }
2547 /* max_len == 0 signifies "malloced" var, which we can
2548 * (and have to) free. But we can't free(cur->varstr) here:
2549 * if cur->flg_export is 1, it is in the environment.
2550 * We should either unsetenv+free, or wait until putenv,
2551 * then putenv(new)+free(old).
2552 */
2553 free_me = cur->varstr;
2554 goto set_str_and_exp;
2555 }
2556
2557 /* Not found or shadowed - create new variable struct */
Francis Laniel36836fc2023-12-22 22:02:28 +01002558#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01002559 debug_printf_env("%s: alloc new var '%s'/%u\n", __func__, str, local_lvl);
Francis Laniel36836fc2023-12-22 22:02:28 +01002560#else /* __U_BOOT__ */
2561 debug_printf_env("%s: alloc new var '%s'\n", __func__, str);
2562#endif /* __U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01002563 cur = xzalloc(sizeof(*cur));
Francis Laniel36836fc2023-12-22 22:02:28 +01002564#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01002565 cur->var_nest_level = local_lvl;
Francis Laniel36836fc2023-12-22 22:02:28 +01002566#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01002567 cur->next = *cur_pp;
2568 *cur_pp = cur;
2569
2570 set_str_and_exp:
2571 cur->varstr = str;
2572 exp:
Francis Laniel36836fc2023-12-22 22:02:28 +01002573#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01002574#if !BB_MMU || ENABLE_HUSH_READONLY
2575 if (flags & SETFLAG_MAKE_RO) {
2576 cur->flg_read_only = 1;
2577 }
2578#endif
2579 if (flags & SETFLAG_EXPORT)
2580 cur->flg_export = 1;
Francis Laniel36836fc2023-12-22 22:02:28 +01002581#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01002582 retval = 0;
Francis Laniel36836fc2023-12-22 22:02:28 +01002583#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01002584 if (cur->flg_export) {
2585 if (flags & SETFLAG_UNEXPORT) {
2586 cur->flg_export = 0;
2587 /* unsetenv was already done */
2588 } else {
2589 debug_printf_env("%s: putenv '%s'/%u\n", __func__, cur->varstr, cur->var_nest_level);
2590 retval = putenv(cur->varstr);
2591 /* fall through to "free(free_me)" -
2592 * only now we can free old exported malloced string
2593 */
2594 }
2595 }
Francis Laniel36836fc2023-12-22 22:02:28 +01002596#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01002597 free(free_me);
2598
Francis Laniele7ca3a32023-12-22 22:02:42 +01002599 handle_changed_special_names(cur->varstr);
Francis Laniel110b7692023-12-22 22:02:27 +01002600
2601 return retval;
2602}
2603
Francis Laniel36836fc2023-12-22 22:02:28 +01002604#ifndef __U_BOOT__
Francis Laniele7ca3a32023-12-22 22:02:42 +01002605static int set_local_var0(char *str)
2606{
2607 return set_local_var(str, 0);
2608}
2609
Francis Laniel110b7692023-12-22 22:02:27 +01002610static void FAST_FUNC set_local_var_from_halves(const char *name, const char *val)
2611{
2612 char *var = xasprintf("%s=%s", name, val);
Francis Laniele7ca3a32023-12-22 22:02:42 +01002613 set_local_var0(var);
Francis Laniel110b7692023-12-22 22:02:27 +01002614}
2615
2616/* Used at startup and after each cd */
2617static void set_pwd_var(unsigned flag)
2618{
2619 set_local_var(xasprintf("PWD=%s", get_cwd(/*force:*/ 1)), flag);
2620}
Francis Laniel36836fc2023-12-22 22:02:28 +01002621#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01002622
2623#if ENABLE_HUSH_UNSET || ENABLE_HUSH_GETOPTS
Francis Laniele7ca3a32023-12-22 22:02:42 +01002624static int unset_local_var(const char *name)
Francis Laniel110b7692023-12-22 22:02:27 +01002625{
2626 struct variable *cur;
2627 struct variable **cur_pp;
2628
2629 cur_pp = &G.top_var;
2630 while ((cur = *cur_pp) != NULL) {
Francis Laniele7ca3a32023-12-22 22:02:42 +01002631 if (varcmp(cur->varstr, name) == 0) {
Francis Laniel110b7692023-12-22 22:02:27 +01002632 if (cur->flg_read_only) {
2633 bb_error_msg("%s: readonly variable", name);
2634 return EXIT_FAILURE;
2635 }
2636
2637 *cur_pp = cur->next;
2638 debug_printf_env("%s: unsetenv '%s'\n", __func__, cur->varstr);
2639 bb_unsetenv(cur->varstr);
2640 if (!cur->max_len)
2641 free(cur->varstr);
2642 free(cur);
2643
2644 break;
2645 }
2646 cur_pp = &cur->next;
2647 }
2648
2649 /* Handle "unset LINENO" et al even if did not find the variable to unset */
Francis Laniele7ca3a32023-12-22 22:02:42 +01002650 handle_changed_special_names(name);
Francis Laniel110b7692023-12-22 22:02:27 +01002651
2652 return EXIT_SUCCESS;
2653}
Francis Laniel110b7692023-12-22 22:02:27 +01002654#endif
2655
2656
Francis Laniel36836fc2023-12-22 22:02:28 +01002657#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01002658/*
2659 * Helpers for "var1=val1 var2=val2 cmd" feature
2660 */
2661static void add_vars(struct variable *var)
2662{
2663 struct variable *next;
2664
2665 while (var) {
2666 next = var->next;
2667 var->next = G.top_var;
2668 G.top_var = var;
2669 if (var->flg_export) {
2670 debug_printf_env("%s: restoring exported '%s'/%u\n", __func__, var->varstr, var->var_nest_level);
2671 putenv(var->varstr);
2672 } else {
2673 debug_printf_env("%s: restoring variable '%s'/%u\n", __func__, var->varstr, var->var_nest_level);
2674 }
2675 var = next;
2676 }
2677}
2678
2679/* We put strings[i] into variable table and possibly putenv them.
2680 * If variable is read only, we can free the strings[i]
2681 * which attempts to overwrite it.
2682 * The strings[] vector itself is freed.
2683 */
2684static void set_vars_and_save_old(char **strings)
2685{
2686 char **s;
2687
2688 if (!strings)
2689 return;
2690
2691 s = strings;
2692 while (*s) {
2693 struct variable *var_p;
2694 struct variable **var_pp;
2695 char *eq;
2696
2697 eq = strchr(*s, '=');
2698 if (HUSH_DEBUG && !eq)
2699 bb_simple_error_msg_and_die("BUG in varexp4");
Francis Laniele7ca3a32023-12-22 22:02:42 +01002700 var_pp = get_ptr_to_local_var(*s);
Francis Laniel110b7692023-12-22 22:02:27 +01002701 if (var_pp) {
2702 var_p = *var_pp;
2703 if (var_p->flg_read_only) {
2704 char **p;
2705 bb_error_msg("%s: readonly variable", *s);
2706 /*
2707 * "VAR=V BLTIN" unsets VARs after BLTIN completes.
2708 * If VAR is readonly, leaving it in the list
2709 * after asssignment error (msg above)
2710 * causes doubled error message later, on unset.
2711 */
2712 debug_printf_env("removing/freeing '%s' element\n", *s);
2713 free(*s);
2714 p = s;
2715 do { *p = p[1]; p++; } while (*p);
2716 goto next;
2717 }
2718 /* below, set_local_var() with nest level will
2719 * "shadow" (remove) this variable from
2720 * global linked list.
2721 */
2722 }
2723 debug_printf_env("%s: env override '%s'/%u\n", __func__, *s, G.var_nest_level);
2724 set_local_var(*s, (G.var_nest_level << SETFLAG_VARLVL_SHIFT) | SETFLAG_EXPORT);
2725 s++;
2726 next: ;
2727 }
2728 free(strings);
2729}
2730
2731
2732/*
2733 * Unicode helper
2734 */
2735static void reinit_unicode_for_hush(void)
2736{
2737 /* Unicode support should be activated even if LANG is set
2738 * _during_ shell execution, not only if it was set when
2739 * shell was started. Therefore, re-check LANG every time:
2740 */
2741 if (ENABLE_FEATURE_CHECK_UNICODE_IN_ENV
2742 || ENABLE_UNICODE_USING_LOCALE
2743 ) {
2744 const char *s = get_local_var_value("LC_ALL");
2745 if (!s) s = get_local_var_value("LC_CTYPE");
2746 if (!s) s = get_local_var_value("LANG");
2747 reinit_unicode(s);
2748 }
2749}
2750
Francis Laniel36836fc2023-12-22 22:02:28 +01002751#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01002752/*
2753 * in_str support (strings, and "strings" read from files).
2754 */
2755
2756#if ENABLE_HUSH_INTERACTIVE
Francis Laniel36836fc2023-12-22 22:02:28 +01002757#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01002758/* To test correct lineedit/interactive behavior, type from command line:
2759 * echo $P\
2760 * \
2761 * AT\
2762 * H\
2763 * \
2764 * It exercises a lot of corner cases.
2765 */
2766static const char *setup_prompt_string(void)
2767{
2768 const char *prompt_str;
2769
2770 debug_printf_prompt("%s promptmode:%d\n", __func__, G.promptmode);
2771
2772# if ENABLE_FEATURE_EDITING_FANCY_PROMPT
2773 prompt_str = get_local_var_value(G.promptmode == 0 ? "PS1" : "PS2");
2774 if (!prompt_str)
2775 prompt_str = "";
2776# else
2777 prompt_str = "> "; /* if PS2, else... */
2778 if (G.promptmode == 0) { /* PS1 */
2779 /* No fancy prompts supported, (re)generate "CURDIR $ " by hand */
2780 free(G.PS1);
2781 /* bash uses $PWD value, even if it is set by user.
2782 * It uses current dir only if PWD is unset.
2783 * We always use current dir. */
2784 prompt_str = G.PS1 = xasprintf("%s %c ", get_cwd(0), (geteuid() != 0) ? '$' : '#');
2785 }
2786# endif
2787 debug_printf("prompt_str '%s'\n", prompt_str);
2788 return prompt_str;
2789}
Francis Laniel36836fc2023-12-22 22:02:28 +01002790#endif /* !__U_BOOT__ */
2791
2792#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01002793static int get_user_input(struct in_str *i)
Francis Laniel36836fc2023-12-22 22:02:28 +01002794#else /* __U_BOOT__ */
2795static void get_user_input(struct in_str *i)
2796#endif /* __U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01002797{
Francis Laniel36836fc2023-12-22 22:02:28 +01002798#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01002799# if ENABLE_FEATURE_EDITING
2800 /* In EDITING case, this function reads next input line,
2801 * saves it in i->p, then returns 1st char of it.
2802 */
2803 int r;
2804 const char *prompt_str;
2805
2806 prompt_str = setup_prompt_string();
2807 for (;;) {
2808 reinit_unicode_for_hush();
2809 G.flag_SIGINT = 0;
Francis Laniele7ca3a32023-12-22 22:02:42 +01002810
2811 bb_got_signal = 0;
2812 if (!sigisemptyset(&G.pending_set)) {
2813 /* Whoops, already got a signal, do not call read_line_input */
2814 bb_got_signal = r = -1;
2815 } else {
2816 /* For shell, LI_INTERRUPTIBLE is set:
2817 * read_line_input will abort on either
2818 * getting EINTR in poll() and bb_got_signal became != 0,
2819 * or if it sees bb_got_signal != 0
2820 * (IOW: if signal arrives before poll() is reached).
2821 * Interactive testcases:
2822 * (while kill -INT $$; do sleep 1; done) &
2823 * #^^^ prints ^C, prints prompt, repeats
2824 * trap 'echo I' int; (while kill -INT $$; do sleep 1; done) &
2825 * #^^^ prints ^C, prints "I", prints prompt, repeats
2826 * trap 'echo T' term; (while kill $$; do sleep 1; done) &
2827 * #^^^ prints "T", prints prompt, repeats
2828 * #(bash 5.0.17 exits after first "T", looks like a bug)
2829 */
2830 r = read_line_input(G.line_input_state, prompt_str,
Francis Laniel110b7692023-12-22 22:02:27 +01002831 G.user_input_buf, CONFIG_FEATURE_EDITING_MAX_LEN-1
Francis Laniele7ca3a32023-12-22 22:02:42 +01002832 );
2833 /* read_line_input intercepts ^C, "convert" it to SIGINT */
2834 if (r == 0)
2835 raise(SIGINT);
2836 }
2837 /* bash prints ^C (before running a trap, if any)
2838 * both on keyboard ^C and on real SIGINT (non-kbd generated).
2839 */
2840 if (sigismember(&G.pending_set, SIGINT)) {
2841 write(STDOUT_FILENO, "^C\n", 3);
2842 G.last_exitcode = 128 | SIGINT;
Francis Laniel110b7692023-12-22 22:02:27 +01002843 }
2844 check_and_run_traps();
Francis Laniele7ca3a32023-12-22 22:02:42 +01002845 if (r == 0) /* keyboard ^C? */
2846 continue; /* go back, read another input line */
2847 if (r > 0) /* normal input? (no ^C, no ^D, no signals) */
Francis Laniel110b7692023-12-22 22:02:27 +01002848 break;
Francis Laniele7ca3a32023-12-22 22:02:42 +01002849 if (!bb_got_signal) {
2850 /* r < 0: ^D/EOF/error detected (but not signal) */
2851 /* ^D on interactive input goes to next line before exiting: */
2852 write(STDOUT_FILENO, "\n", 1);
2853 i->p = NULL;
2854 i->peek_buf[0] = r = EOF;
2855 return r;
2856 }
2857 /* it was a signal: go back, read another input line */
Francis Laniel110b7692023-12-22 22:02:27 +01002858 }
2859 i->p = G.user_input_buf;
2860 return (unsigned char)*i->p++;
2861# else
2862 /* In !EDITING case, this function gets called for every char.
2863 * Buffering happens deeper in the call chain, in hfgetc(i->file).
2864 */
2865 int r;
2866
2867 for (;;) {
2868 G.flag_SIGINT = 0;
2869 if (i->last_char == '\0' || i->last_char == '\n') {
2870 const char *prompt_str = setup_prompt_string();
2871 /* Why check_and_run_traps here? Try this interactively:
2872 * $ trap 'echo INT' INT; (sleep 2; kill -INT $$) &
2873 * $ <[enter], repeatedly...>
2874 * Without check_and_run_traps, handler never runs.
2875 */
2876 check_and_run_traps();
2877 fputs_stdout(prompt_str);
2878 fflush_all();
2879 }
2880 r = hfgetc(i->file);
2881 /* In !ENABLE_FEATURE_EDITING we don't use read_line_input,
2882 * no ^C masking happens during fgetc, no special code for ^C:
2883 * it generates SIGINT as usual.
2884 */
2885 check_and_run_traps();
2886 if (r != '\0' && !G.flag_SIGINT)
2887 break;
2888 if (G.flag_SIGINT) {
2889 /* ^C or SIGINT: repeat */
2890 /* bash prints ^C even on real SIGINT (non-kbd generated) */
2891 /* kernel prints "^C" itself, just print newline: */
2892 write(STDOUT_FILENO, "\n", 1);
2893 G.last_exitcode = 128 | SIGINT;
2894 }
2895 }
2896 return r;
2897# endif
Francis Laniel36836fc2023-12-22 22:02:28 +01002898#else /* __U_BOOT__ */
2899 int n;
2900 int promptme;
2901 static char the_command[CONFIG_SYS_CBSIZE + 1];
2902
2903 bootretry_reset_cmd_timeout();
2904 promptme = 1;
2905 n = u_boot_cli_readline(i);
2906
2907# ifdef CONFIG_BOOT_RETRY_TIME
2908 if (n == -2) {
2909 puts("\nTimeout waiting for command\n");
2910# ifdef CONFIG_RESET_TO_RETRY
2911 do_reset(NULL, 0, 0, NULL);
2912# else
2913# error "This currently only works with CONFIG_RESET_TO_RETRY enabled"
2914# endif
2915 }
2916# endif
2917 if (n == -1 ) {
2918 G.flag_repeat = 0;
2919 promptme = 0;
2920 }
2921 n = strlen(console_buffer);
2922 console_buffer[n] = '\n';
2923 console_buffer[n+1]= '\0';
2924 if (had_ctrlc())
2925 G.flag_repeat = 0;
2926 clear_ctrlc();
2927 G.do_repeat = 0;
2928#ifndef __U_BOOT__
2929 if (G.promptmode == 1) {
2930#else /* __U_BOOT__ */
2931 if (!G.promptmode) {
2932#endif /* __U_BOOT__ */
2933 if (console_buffer[0] == '\n'&& G.flag_repeat == 0) {
2934 strcpy(the_command, console_buffer);
2935 }
2936 else {
2937 if (console_buffer[0] != '\n') {
2938 strcpy(the_command, console_buffer);
2939 G.flag_repeat = 1;
2940 }
2941 else {
2942 G.do_repeat = 1;
2943 }
2944 }
2945 i->p = the_command;
2946 }
2947 else {
2948 if (console_buffer[0] != '\n') {
2949 if (strlen(the_command) + strlen(console_buffer)
2950 < CONFIG_SYS_CBSIZE) {
2951 n = strlen(the_command);
2952#ifdef __U_BOOT__
2953 /*
2954 * To avoid writing to bad places, we check if
2955 * n is greater than 0.
2956 * This bug was found by Harald Seiler.
2957 */
2958 if (n > 0)
2959 the_command[n-1] = ' ';
2960 strcpy(&the_command[n], console_buffer);
2961#else /* !__U_BOOT__ */
2962 the_command[n-1] = ' ';
2963 strcpy(&the_command[n], console_buffer);
2964#endif /* !__U_BOOT__ */
2965 }
2966 else {
2967 the_command[0] = '\n';
2968 the_command[1] = '\0';
2969 G.flag_repeat = 0;
2970 }
2971 }
2972 if (promptme == 0) {
2973 the_command[0] = '\n';
2974 the_command[1] = '\0';
2975 }
2976 i->p = console_buffer;
2977 }
2978#endif /* __U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01002979}
2980/* This is the magic location that prints prompts
2981 * and gets data back from the user */
2982static int fgetc_interactive(struct in_str *i)
2983{
2984 int ch;
Francis Laniel36836fc2023-12-22 22:02:28 +01002985#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01002986 /* If it's interactive stdin, get new line. */
2987 if (G_interactive_fd && i->file == G.HFILE_stdin) {
Francis Laniel36836fc2023-12-22 22:02:28 +01002988#endif /* !__U_BOOT__ */
2989#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01002990 /* Returns first char (or EOF), the rest is in i->p[] */
2991 ch = get_user_input(i);
Francis Laniel36836fc2023-12-22 22:02:28 +01002992#else /* __U_BOOT__ */
2993 /* Avoid garbage value and make clang happy. */
2994 ch = 0;
2995 /*
2996 * get_user_input() does not return anything when used in
2997 * U-Boot.
2998 * So, we need to take the read character from i->p[].
2999 */
3000 get_user_input(i);
3001 if (i->p && *i->p) {
3002 ch = *i->p++;
3003 }
3004#endif /* __U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01003005 G.promptmode = 1; /* PS2 */
3006 debug_printf_prompt("%s promptmode=%d\n", __func__, G.promptmode);
Francis Laniel36836fc2023-12-22 22:02:28 +01003007#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01003008 } else {
3009 /* Not stdin: script file, sourced file, etc */
3010 do ch = hfgetc(i->file); while (ch == '\0');
3011 }
Francis Laniel36836fc2023-12-22 22:02:28 +01003012#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01003013 return ch;
3014}
3015#else /* !INTERACTIVE */
Francis Laniel36836fc2023-12-22 22:02:28 +01003016#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01003017static ALWAYS_INLINE int fgetc_interactive(struct in_str *i)
3018{
3019 int ch;
3020 do ch = hfgetc(i->file); while (ch == '\0');
3021 return ch;
3022}
Francis Laniel36836fc2023-12-22 22:02:28 +01003023#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01003024#endif /* !INTERACTIVE */
3025
3026static int i_getch(struct in_str *i)
3027{
3028 int ch;
3029
Francis Laniel36836fc2023-12-22 22:02:28 +01003030#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01003031 if (!i->file) {
3032 /* string-based in_str */
3033 ch = (unsigned char)*i->p;
3034 if (ch != '\0') {
3035 i->p++;
3036 i->last_char = ch;
Francis Laniele7ca3a32023-12-22 22:02:42 +01003037#if ENABLE_HUSH_LINENO_VAR
3038 if (ch == '\n') {
3039 G.parse_lineno++;
3040 debug_printf_parse("G.parse_lineno++ = %u\n", G.parse_lineno);
3041 }
3042#endif
Francis Laniel110b7692023-12-22 22:02:27 +01003043 return ch;
3044 }
3045 return EOF;
3046 }
3047
Francis Laniel36836fc2023-12-22 22:02:28 +01003048#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01003049 /* FILE-based in_str */
3050
3051#if ENABLE_FEATURE_EDITING
3052 /* This can be stdin, check line editing char[] buffer */
3053 if (i->p && *i->p != '\0') {
3054 ch = (unsigned char)*i->p++;
3055 goto out;
Francis Laniel26cafe12023-12-22 22:02:34 +01003056#ifndef __U_BOOT__
3057 }
3058#else /* __U_BOOT__ */
3059 /*
3060 * There are two ways for command to be called:
3061 * 1. The first one is when they are typed by the user.
3062 * 2. The second one is through run_command() (NOTE command run
3063 * internally calls run_command()).
3064 *
3065 * In the second case, we do not get input from the user, so once we
3066 * get a '\0', it means we need to stop.
3067 * NOTE G.run_command_flags is only set on run_command call stack, so
3068 * we use this to know if we come from user input or run_command().
3069 */
3070 } else if (i->p && *i->p == '\0' && G.run_command_flags){
3071 return EOF;
Francis Laniel110b7692023-12-22 22:02:27 +01003072 }
Francis Laniel26cafe12023-12-22 22:02:34 +01003073#endif /* __U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01003074#endif
Francis Laniel36836fc2023-12-22 22:02:28 +01003075#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01003076 /* peek_buf[] is an int array, not char. Can contain EOF. */
3077 ch = i->peek_buf[0];
3078 if (ch != 0) {
3079 int ch2 = i->peek_buf[1];
3080 i->peek_buf[0] = ch2;
3081 if (ch2 == 0) /* very likely, avoid redundant write */
3082 goto out;
3083 i->peek_buf[1] = 0;
3084 goto out;
3085 }
3086
Francis Laniel36836fc2023-12-22 22:02:28 +01003087#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01003088 ch = fgetc_interactive(i);
3089 out:
3090 debug_printf("file_get: got '%c' %d\n", ch, ch);
3091 i->last_char = ch;
3092#if ENABLE_HUSH_LINENO_VAR
3093 if (ch == '\n') {
3094 G.parse_lineno++;
3095 debug_printf_parse("G.parse_lineno++ = %u\n", G.parse_lineno);
3096 }
3097#endif
3098 return ch;
3099}
3100
3101static int i_peek(struct in_str *i)
3102{
Francis Laniel36836fc2023-12-22 22:02:28 +01003103#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01003104 int ch;
3105
3106 if (!i->file) {
3107 /* string-based in_str */
3108 /* Doesn't report EOF on NUL. None of the callers care. */
3109 return (unsigned char)*i->p;
3110 }
3111
3112 /* FILE-based in_str */
3113
3114#if ENABLE_FEATURE_EDITING && ENABLE_HUSH_INTERACTIVE
3115 /* This can be stdin, check line editing char[] buffer */
3116 if (i->p && *i->p != '\0')
3117 return (unsigned char)*i->p;
3118#endif
3119 /* peek_buf[] is an int array, not char. Can contain EOF. */
3120 ch = i->peek_buf[0];
3121 if (ch != 0)
3122 return ch;
3123
3124 /* Need to get a new char */
3125 ch = fgetc_interactive(i);
3126 debug_printf("file_peek: got '%c' %d\n", ch, ch);
3127
3128 /* Save it by either rolling back line editing buffer, or in i->peek_buf[0] */
3129#if ENABLE_FEATURE_EDITING && ENABLE_HUSH_INTERACTIVE
3130 if (i->p) {
3131 i->p -= 1;
3132 return ch;
3133 }
3134#endif
3135 i->peek_buf[0] = ch;
3136 /*i->peek_buf[1] = 0; - already is */
3137 return ch;
Francis Laniel36836fc2023-12-22 22:02:28 +01003138#else /* __U_BOOT__ */
3139 /* string-based in_str */
3140 /* Doesn't report EOF on NUL. None of the callers care. */
3141 return (unsigned char)*i->p;
3142#endif /* __U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01003143}
3144
3145/* Only ever called if i_peek() was called, and did not return EOF.
3146 * IOW: we know the previous peek saw an ordinary char, not EOF, not NUL,
3147 * not end-of-line. Therefore we never need to read a new editing line here.
3148 */
3149static int i_peek2(struct in_str *i)
3150{
Francis Laniel36836fc2023-12-22 22:02:28 +01003151#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01003152 int ch;
Francis Laniel36836fc2023-12-22 22:02:28 +01003153#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01003154
3155 /* There are two cases when i->p[] buffer exists.
3156 * (1) it's a string in_str.
3157 * (2) It's a file, and we have a saved line editing buffer.
3158 * In both cases, we know that i->p[0] exists and not NUL, and
3159 * the peek2 result is in i->p[1].
3160 */
3161 if (i->p)
3162 return (unsigned char)i->p[1];
3163
Francis Laniel36836fc2023-12-22 22:02:28 +01003164#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01003165 /* Now we know it is a file-based in_str. */
3166
3167 /* peek_buf[] is an int array, not char. Can contain EOF. */
3168 /* Is there 2nd char? */
3169 ch = i->peek_buf[1];
3170 if (ch == 0) {
3171 /* We did not read it yet, get it now */
3172 do ch = hfgetc(i->file); while (ch == '\0');
3173 i->peek_buf[1] = ch;
3174 }
3175
3176 debug_printf("file_peek2: got '%c' %d\n", ch, ch);
3177 return ch;
Francis Laniel36836fc2023-12-22 22:02:28 +01003178#else
3179 return 0;
3180#endif /* __U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01003181}
3182
3183static int i_getch_and_eat_bkslash_nl(struct in_str *input)
3184{
3185 for (;;) {
3186 int ch, ch2;
3187
3188 ch = i_getch(input);
3189 if (ch != '\\')
3190 return ch;
3191 ch2 = i_peek(input);
3192 if (ch2 != '\n')
3193 return ch;
3194 /* backslash+newline, skip it */
3195 i_getch(input);
3196 }
3197}
3198
3199/* Note: this function _eats_ \<newline> pairs, safe to use plain
3200 * i_getch() after it instead of i_getch_and_eat_bkslash_nl().
3201 */
3202static int i_peek_and_eat_bkslash_nl(struct in_str *input)
3203{
3204 for (;;) {
3205 int ch, ch2;
3206
3207 ch = i_peek(input);
3208 if (ch != '\\')
3209 return ch;
3210 ch2 = i_peek2(input);
3211 if (ch2 != '\n')
3212 return ch;
3213 /* backslash+newline, skip it */
3214 i_getch(input);
3215 i_getch(input);
3216 }
3217}
3218
Francis Laniel36836fc2023-12-22 22:02:28 +01003219#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01003220static void setup_file_in_str(struct in_str *i, HFILE *fp)
Francis Laniel36836fc2023-12-22 22:02:28 +01003221#else /* __U_BOOT__ */
3222static void setup_file_in_str(struct in_str *i)
3223#endif /* __U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01003224{
3225 memset(i, 0, sizeof(*i));
Francis Laniel36836fc2023-12-22 22:02:28 +01003226#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01003227 i->file = fp;
3228 /* i->p = NULL; */
Francis Laniel36836fc2023-12-22 22:02:28 +01003229#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01003230}
3231
3232static void setup_string_in_str(struct in_str *i, const char *s)
3233{
3234 memset(i, 0, sizeof(*i));
3235 /*i->file = NULL */;
3236 i->p = s;
3237}
3238
3239
3240/*
3241 * o_string support
3242 */
3243#define B_CHUNK (32 * sizeof(char*))
3244
3245static void o_reset_to_empty_unquoted(o_string *o)
3246{
3247 o->length = 0;
3248 o->has_quoted_part = 0;
3249 if (o->data)
3250 o->data[0] = '\0';
3251}
3252
3253static void o_free_and_set_NULL(o_string *o)
3254{
3255 free(o->data);
3256 memset(o, 0, sizeof(*o));
3257}
3258
3259static ALWAYS_INLINE void o_free(o_string *o)
3260{
3261 free(o->data);
3262}
3263
3264static void o_grow_by(o_string *o, int len)
3265{
3266 if (o->length + len > o->maxlen) {
3267 o->maxlen += (2 * len) | (B_CHUNK-1);
3268 o->data = xrealloc(o->data, 1 + o->maxlen);
3269 }
3270}
3271
3272static void o_addchr(o_string *o, int ch)
3273{
3274 debug_printf("o_addchr: '%c' o->length=%d o=%p\n", ch, o->length, o);
3275 if (o->length < o->maxlen) {
3276 /* likely. avoid o_grow_by() call */
3277 add:
3278 o->data[o->length] = ch;
3279 o->length++;
3280 o->data[o->length] = '\0';
3281 return;
3282 }
3283 o_grow_by(o, 1);
3284 goto add;
3285}
3286
3287#if 0
3288/* Valid only if we know o_string is not empty */
3289static void o_delchr(o_string *o)
3290{
3291 o->length--;
3292 o->data[o->length] = '\0';
3293}
3294#endif
3295
3296static void o_addblock(o_string *o, const char *str, int len)
3297{
3298 o_grow_by(o, len);
3299 ((char*)mempcpy(&o->data[o->length], str, len))[0] = '\0';
3300 o->length += len;
3301}
3302
3303static void o_addstr(o_string *o, const char *str)
3304{
3305 o_addblock(o, str, strlen(str));
3306}
3307
Francis Laniel36836fc2023-12-22 22:02:28 +01003308#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01003309static void o_addstr_with_NUL(o_string *o, const char *str)
3310{
3311 o_addblock(o, str, strlen(str) + 1);
3312}
Francis Laniel36836fc2023-12-22 22:02:28 +01003313#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01003314
3315#if !BB_MMU
3316static void nommu_addchr(o_string *o, int ch)
3317{
3318 if (o)
3319 o_addchr(o, ch);
3320}
3321#else
3322# define nommu_addchr(o, str) ((void)0)
3323#endif
3324
Francis Laniel36836fc2023-12-22 22:02:28 +01003325#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01003326#if ENABLE_HUSH_MODE_X
3327static void x_mode_addchr(int ch)
3328{
3329 o_addchr(&G.x_mode_buf, ch);
3330}
3331static void x_mode_addstr(const char *str)
3332{
3333 o_addstr(&G.x_mode_buf, str);
3334}
3335static void x_mode_addblock(const char *str, int len)
3336{
3337 o_addblock(&G.x_mode_buf, str, len);
3338}
3339static void x_mode_prefix(void)
3340{
3341 int n = G.x_mode_depth;
3342 do x_mode_addchr('+'); while (--n >= 0);
3343}
3344static void x_mode_flush(void)
3345{
3346 int len = G.x_mode_buf.length;
3347 if (len <= 0)
3348 return;
3349 if (G.x_mode_fd > 0) {
3350 G.x_mode_buf.data[len] = '\n';
3351 full_write(G.x_mode_fd, G.x_mode_buf.data, len + 1);
3352 }
3353 G.x_mode_buf.length = 0;
3354}
3355#endif
Francis Laniel36836fc2023-12-22 22:02:28 +01003356#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01003357
3358/*
3359 * HUSH_BRACE_EXPANSION code needs corresponding quoting on variable expansion side.
3360 * Currently, "v='{q,w}'; echo $v" erroneously expands braces in $v.
3361 * Apparently, on unquoted $v bash still does globbing
3362 * ("v='*.txt'; echo $v" prints all .txt files),
3363 * but NOT brace expansion! Thus, there should be TWO independent
3364 * quoting mechanisms on $v expansion side: one protects
3365 * $v from brace expansion, and other additionally protects "$v" against globbing.
3366 * We have only second one.
3367 */
3368
3369#if ENABLE_HUSH_BRACE_EXPANSION
3370# define MAYBE_BRACES "{}"
3371#else
3372# define MAYBE_BRACES ""
3373#endif
3374
3375/* My analysis of quoting semantics tells me that state information
3376 * is associated with a destination, not a source.
3377 */
3378static void o_addqchr(o_string *o, int ch)
3379{
3380 int sz = 1;
3381 /* '-' is included because of this case:
3382 * >filename0 >filename1 >filename9; v='-'; echo filename[0"$v"9]
3383 */
3384 char *found = strchr("*?[-\\" MAYBE_BRACES, ch);
3385 if (found)
3386 sz++;
3387 o_grow_by(o, sz);
3388 if (found) {
3389 o->data[o->length] = '\\';
3390 o->length++;
3391 }
3392 o->data[o->length] = ch;
3393 o->length++;
3394 o->data[o->length] = '\0';
3395}
3396
3397static void o_addQchr(o_string *o, int ch)
3398{
3399 int sz = 1;
3400 if ((o->o_expflags & EXP_FLAG_ESC_GLOB_CHARS)
3401 && strchr("*?[-\\" MAYBE_BRACES, ch)
3402 ) {
3403 sz++;
3404 o->data[o->length] = '\\';
3405 o->length++;
3406 }
3407 o_grow_by(o, sz);
3408 o->data[o->length] = ch;
3409 o->length++;
3410 o->data[o->length] = '\0';
3411}
3412
3413static void o_addqblock(o_string *o, const char *str, int len)
3414{
3415 while (len) {
3416 char ch;
3417 int sz;
3418 int ordinary_cnt = strcspn(str, "*?[-\\" MAYBE_BRACES);
3419 if (ordinary_cnt > len) /* paranoia */
3420 ordinary_cnt = len;
3421 o_addblock(o, str, ordinary_cnt);
3422 if (ordinary_cnt == len)
3423 return; /* NUL is already added by o_addblock */
3424 str += ordinary_cnt;
3425 len -= ordinary_cnt + 1; /* we are processing + 1 char below */
3426
3427 ch = *str++;
3428 sz = 1;
3429 if (ch) { /* it is necessarily one of "*?[-\\" MAYBE_BRACES */
3430 sz++;
3431 o->data[o->length] = '\\';
3432 o->length++;
3433 }
3434 o_grow_by(o, sz);
3435 o->data[o->length] = ch;
3436 o->length++;
3437 }
3438 o->data[o->length] = '\0';
3439}
3440
3441static void o_addQblock(o_string *o, const char *str, int len)
3442{
3443 if (!(o->o_expflags & EXP_FLAG_ESC_GLOB_CHARS)) {
3444 o_addblock(o, str, len);
3445 return;
3446 }
3447 o_addqblock(o, str, len);
3448}
3449
3450static void o_addQstr(o_string *o, const char *str)
3451{
3452 o_addQblock(o, str, strlen(str));
3453}
3454
3455/* A special kind of o_string for $VAR and `cmd` expansion.
3456 * It contains char* list[] at the beginning, which is grown in 16 element
3457 * increments. Actual string data starts at the next multiple of 16 * (char*).
3458 * list[i] contains an INDEX (int!) into this string data.
3459 * It means that if list[] needs to grow, data needs to be moved higher up
3460 * but list[i]'s need not be modified.
3461 * NB: remembering how many list[i]'s you have there is crucial.
3462 * o_finalize_list() operation post-processes this structure - calculates
3463 * and stores actual char* ptrs in list[]. Oh, it NULL terminates it as well.
3464 */
3465#if DEBUG_EXPAND || DEBUG_GLOB
3466static void debug_print_list(const char *prefix, o_string *o, int n)
3467{
3468 char **list = (char**)o->data;
3469 int string_start = ((n + 0xf) & ~0xf) * sizeof(list[0]);
3470 int i = 0;
3471
3472 indent();
3473 fdprintf(2, "%s: list:%p n:%d string_start:%d length:%d maxlen:%d glob:%d quoted:%d escape:%d\n",
3474 prefix, list, n, string_start, o->length, o->maxlen,
3475 !!(o->o_expflags & EXP_FLAG_GLOB),
3476 o->has_quoted_part,
3477 !!(o->o_expflags & EXP_FLAG_ESC_GLOB_CHARS));
3478 while (i < n) {
3479 indent();
3480 fdprintf(2, " list[%d]=%d '%s' %p\n", i, (int)(uintptr_t)list[i],
3481 o->data + (int)(uintptr_t)list[i] + string_start,
3482 o->data + (int)(uintptr_t)list[i] + string_start);
3483 i++;
3484 }
3485 if (n) {
3486 const char *p = o->data + (int)(uintptr_t)list[n - 1] + string_start;
3487 indent();
Francis Laniel36836fc2023-12-22 22:02:28 +01003488#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01003489 fdprintf(2, " total_sz:%ld\n", (long)((p + strlen(p) + 1) - o->data));
Francis Laniel36836fc2023-12-22 22:02:28 +01003490#else /* __U_BOOT__ */
3491 printf(" total_sz:%ld\n", (long)((p + strlen(p) + 1) - o->data));
3492#endif /* __U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01003493 }
3494}
3495#else
3496# define debug_print_list(prefix, o, n) ((void)0)
3497#endif
3498
3499/* n = o_save_ptr_helper(str, n) "starts new string" by storing an index value
3500 * in list[n] so that it points past last stored byte so far.
3501 * It returns n+1. */
3502static int o_save_ptr_helper(o_string *o, int n)
3503{
3504 char **list = (char**)o->data;
3505 int string_start;
3506 int string_len;
3507
3508 if (!o->has_empty_slot) {
3509 string_start = ((n + 0xf) & ~0xf) * sizeof(list[0]);
3510 string_len = o->length - string_start;
3511 if (!(n & 0xf)) { /* 0, 0x10, 0x20...? */
3512 debug_printf_list("list[%d]=%d string_start=%d (growing)\n", n, string_len, string_start);
3513 /* list[n] points to string_start, make space for 16 more pointers */
3514 o->maxlen += 0x10 * sizeof(list[0]);
3515 o->data = xrealloc(o->data, o->maxlen + 1);
3516 list = (char**)o->data;
3517 memmove(list + n + 0x10, list + n, string_len);
3518 /*
3519 * expand_on_ifs() has a "previous argv[] ends in IFS?"
3520 * check. (grep for -prev-ifs-check-).
3521 * Ensure that argv[-1][last] is not garbage
3522 * but zero bytes, to save index check there.
3523 */
3524 list[n + 0x10 - 1] = 0;
3525 o->length += 0x10 * sizeof(list[0]);
3526 } else {
3527 debug_printf_list("list[%d]=%d string_start=%d\n",
3528 n, string_len, string_start);
3529 }
3530 } else {
3531 /* We have empty slot at list[n], reuse without growth */
3532 string_start = ((n+1 + 0xf) & ~0xf) * sizeof(list[0]); /* NB: n+1! */
3533 string_len = o->length - string_start;
3534 debug_printf_list("list[%d]=%d string_start=%d (empty slot)\n",
3535 n, string_len, string_start);
3536 o->has_empty_slot = 0;
3537 }
3538 o->has_quoted_part = 0;
3539 list[n] = (char*)(uintptr_t)string_len;
3540 return n + 1;
3541}
3542
3543/* "What was our last o_save_ptr'ed position (byte offset relative o->data)?" */
3544static int o_get_last_ptr(o_string *o, int n)
3545{
3546 char **list = (char**)o->data;
3547 int string_start = ((n + 0xf) & ~0xf) * sizeof(list[0]);
3548
3549 return ((int)(uintptr_t)list[n-1]) + string_start;
3550}
3551
3552/*
3553 * Globbing routines.
3554 *
3555 * Most words in commands need to be globbed, even ones which are
3556 * (single or double) quoted. This stems from the possiblity of
3557 * constructs like "abc"* and 'abc'* - these should be globbed.
3558 * Having a different code path for fully-quoted strings ("abc",
3559 * 'abc') would only help performance-wise, but we still need
3560 * code for partially-quoted strings.
3561 *
3562 * Unfortunately, if we want to match bash and ash behavior in all cases,
3563 * the logic can't be "shell-syntax argument is first transformed
3564 * to a string, then globbed, and if globbing does not match anything,
3565 * it is used verbatim". Here are two examples where it fails:
3566 *
3567 * echo 'b\*'?
3568 *
3569 * The globbing can't be avoided (because of '?' at the end).
3570 * The glob pattern is: b\\\*? - IOW, both \ and * are literals
3571 * and are glob-escaped. If this does not match, bash/ash print b\*?
3572 * - IOW: they "unbackslash" the glob pattern.
3573 * Now, look at this:
3574 *
3575 * v='\\\*'; echo b$v?
3576 *
3577 * The glob pattern is the same here: b\\\*? - the unquoted $v expansion
3578 * should be used as glob pattern with no changes. However, if glob
3579 * does not match, bash/ash print b\\\*? - NOT THE SAME as first example!
3580 *
3581 * ash implements this by having an encoded representation of the word
3582 * to glob, which IS NOT THE SAME as the glob pattern - it has more data.
3583 * Glob pattern is derived from it. If glob fails, the decision what result
3584 * should be is made using that encoded representation. Not glob pattern.
3585 */
3586
3587#if ENABLE_HUSH_BRACE_EXPANSION
3588/* There in a GNU extension, GLOB_BRACE, but it is not usable:
3589 * first, it processes even {a} (no commas), second,
3590 * I didn't manage to make it return strings when they don't match
3591 * existing files. Need to re-implement it.
3592 */
3593
3594/* Helper */
3595static int glob_needed(const char *s)
3596{
3597 while (*s) {
3598 if (*s == '\\') {
3599 if (!s[1])
3600 return 0;
3601 s += 2;
3602 continue;
3603 }
3604 if (*s == '*' || *s == '[' || *s == '?' || *s == '{')
3605 return 1;
3606 s++;
3607 }
3608 return 0;
3609}
3610/* Return pointer to next closing brace or to comma */
3611static const char *next_brace_sub(const char *cp)
3612{
3613 unsigned depth = 0;
3614 cp++;
3615 while (*cp != '\0') {
3616 if (*cp == '\\') {
3617 if (*++cp == '\0')
3618 break;
3619 cp++;
3620 continue;
3621 }
3622 if ((*cp == '}' && depth-- == 0) || (*cp == ',' && depth == 0))
3623 break;
3624 if (*cp++ == '{')
3625 depth++;
3626 }
3627
3628 return *cp != '\0' ? cp : NULL;
3629}
3630/* Recursive brace globber. Note: may garble pattern[]. */
3631static int glob_brace(char *pattern, o_string *o, int n)
3632{
3633 char *new_pattern_buf;
3634 const char *begin;
3635 const char *next;
3636 const char *rest;
3637 const char *p;
3638 size_t rest_len;
3639
3640 debug_printf_glob("glob_brace('%s')\n", pattern);
3641
3642 begin = pattern;
3643 while (1) {
3644 if (*begin == '\0')
3645 goto simple_glob;
3646 if (*begin == '{') {
3647 /* Find the first sub-pattern and at the same time
3648 * find the rest after the closing brace */
3649 next = next_brace_sub(begin);
3650 if (next == NULL) {
3651 /* An illegal expression */
3652 goto simple_glob;
3653 }
3654 if (*next == '}') {
3655 /* "{abc}" with no commas - illegal
3656 * brace expr, disregard and skip it */
3657 begin = next + 1;
3658 continue;
3659 }
3660 break;
3661 }
3662 if (*begin == '\\' && begin[1] != '\0')
3663 begin++;
3664 begin++;
3665 }
3666 debug_printf_glob("begin:%s\n", begin);
3667 debug_printf_glob("next:%s\n", next);
3668
3669 /* Now find the end of the whole brace expression */
3670 rest = next;
3671 while (*rest != '}') {
3672 rest = next_brace_sub(rest);
3673 if (rest == NULL) {
3674 /* An illegal expression */
3675 goto simple_glob;
3676 }
3677 debug_printf_glob("rest:%s\n", rest);
3678 }
3679 rest_len = strlen(++rest) + 1;
3680
3681 /* We are sure the brace expression is well-formed */
3682
3683 /* Allocate working buffer large enough for our work */
3684 new_pattern_buf = xmalloc(strlen(pattern));
3685
3686 /* We have a brace expression. BEGIN points to the opening {,
3687 * NEXT points past the terminator of the first element, and REST
3688 * points past the final }. We will accumulate result names from
3689 * recursive runs for each brace alternative in the buffer using
Francis Laniele7ca3a32023-12-22 22:02:42 +01003690 * GLOB_APPEND. */
Francis Laniel110b7692023-12-22 22:02:27 +01003691
3692 p = begin + 1;
3693 while (1) {
3694 /* Construct the new glob expression */
3695 memcpy(
3696 mempcpy(
3697 mempcpy(new_pattern_buf,
3698 /* We know the prefix for all sub-patterns */
3699 pattern, begin - pattern),
3700 p, next - p),
3701 rest, rest_len);
3702
3703 /* Note: glob_brace() may garble new_pattern_buf[].
3704 * That's why we re-copy prefix every time (1st memcpy above).
3705 */
3706 n = glob_brace(new_pattern_buf, o, n);
3707 if (*next == '}') {
3708 /* We saw the last entry */
3709 break;
3710 }
3711 p = next + 1;
3712 next = next_brace_sub(next);
3713 }
3714 free(new_pattern_buf);
3715 return n;
3716
3717 simple_glob:
3718 {
3719 int gr;
3720 glob_t globdata;
3721
3722 memset(&globdata, 0, sizeof(globdata));
3723 gr = glob(pattern, 0, NULL, &globdata);
3724 debug_printf_glob("glob('%s'):%d\n", pattern, gr);
3725 if (gr != 0) {
3726 if (gr == GLOB_NOMATCH) {
3727 globfree(&globdata);
3728 /* NB: garbles parameter */
3729 unbackslash(pattern);
3730 o_addstr_with_NUL(o, pattern);
3731 debug_printf_glob("glob pattern '%s' is literal\n", pattern);
3732 return o_save_ptr_helper(o, n);
3733 }
3734 if (gr == GLOB_NOSPACE)
3735 bb_die_memory_exhausted();
3736 /* GLOB_ABORTED? Only happens with GLOB_ERR flag,
3737 * but we didn't specify it. Paranoia again. */
3738 bb_error_msg_and_die("glob error %d on '%s'", gr, pattern);
3739 }
3740 if (globdata.gl_pathv && globdata.gl_pathv[0]) {
3741 char **argv = globdata.gl_pathv;
3742 while (1) {
3743 o_addstr_with_NUL(o, *argv);
3744 n = o_save_ptr_helper(o, n);
3745 argv++;
3746 if (!*argv)
3747 break;
3748 }
3749 }
3750 globfree(&globdata);
3751 }
3752 return n;
3753}
3754/* Performs globbing on last list[],
3755 * saving each result as a new list[].
3756 */
3757static int perform_glob(o_string *o, int n)
3758{
3759 char *pattern, *copy;
3760
3761 debug_printf_glob("start perform_glob: n:%d o->data:%p\n", n, o->data);
3762 if (!o->data)
3763 return o_save_ptr_helper(o, n);
3764 pattern = o->data + o_get_last_ptr(o, n);
3765 debug_printf_glob("glob pattern '%s'\n", pattern);
3766 if (!glob_needed(pattern)) {
3767 /* unbackslash last string in o in place, fix length */
3768 o->length = unbackslash(pattern) - o->data;
3769 debug_printf_glob("glob pattern '%s' is literal\n", pattern);
3770 return o_save_ptr_helper(o, n);
3771 }
3772
3773 copy = xstrdup(pattern);
3774 /* "forget" pattern in o */
3775 o->length = pattern - o->data;
3776 n = glob_brace(copy, o, n);
3777 free(copy);
3778 if (DEBUG_GLOB)
3779 debug_print_list("perform_glob returning", o, n);
3780 return n;
3781}
3782
3783#else /* !HUSH_BRACE_EXPANSION */
3784
3785/* Helper */
3786static int glob_needed(const char *s)
3787{
3788 while (*s) {
3789 if (*s == '\\') {
3790 if (!s[1])
3791 return 0;
3792 s += 2;
3793 continue;
3794 }
3795 if (*s == '*' || *s == '[' || *s == '?')
3796 return 1;
3797 s++;
3798 }
3799 return 0;
3800}
3801/* Performs globbing on last list[],
3802 * saving each result as a new list[].
3803 */
3804static int perform_glob(o_string *o, int n)
3805{
Francis Lanielbfc406a2023-12-22 22:02:33 +01003806#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01003807 glob_t globdata;
3808 int gr;
Francis Lanielbfc406a2023-12-22 22:02:33 +01003809#endif /* __U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01003810 char *pattern;
3811
3812 debug_printf_glob("start perform_glob: n:%d o->data:%p\n", n, o->data);
3813 if (!o->data)
3814 return o_save_ptr_helper(o, n);
3815 pattern = o->data + o_get_last_ptr(o, n);
3816 debug_printf_glob("glob pattern '%s'\n", pattern);
3817 if (!glob_needed(pattern)) {
Francis Lanielbfc406a2023-12-22 22:02:33 +01003818#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01003819 literal:
Francis Lanielbfc406a2023-12-22 22:02:33 +01003820#endif /* __U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01003821 /* unbackslash last string in o in place, fix length */
3822 o->length = unbackslash(pattern) - o->data;
3823 debug_printf_glob("glob pattern '%s' is literal\n", pattern);
3824 return o_save_ptr_helper(o, n);
3825 }
3826
Francis Lanielbfc406a2023-12-22 22:02:33 +01003827#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01003828 memset(&globdata, 0, sizeof(globdata));
3829 /* Can't use GLOB_NOCHECK: it does not unescape the string.
3830 * If we glob "*.\*" and don't find anything, we need
3831 * to fall back to using literal "*.*", but GLOB_NOCHECK
3832 * will return "*.\*"!
3833 */
3834 gr = glob(pattern, 0, NULL, &globdata);
3835 debug_printf_glob("glob('%s'):%d\n", pattern, gr);
3836 if (gr != 0) {
3837 if (gr == GLOB_NOMATCH) {
3838 globfree(&globdata);
3839 goto literal;
3840 }
3841 if (gr == GLOB_NOSPACE)
3842 bb_die_memory_exhausted();
3843 /* GLOB_ABORTED? Only happens with GLOB_ERR flag,
3844 * but we didn't specify it. Paranoia again. */
3845 bb_error_msg_and_die("glob error %d on '%s'", gr, pattern);
3846 }
3847 if (globdata.gl_pathv && globdata.gl_pathv[0]) {
3848 char **argv = globdata.gl_pathv;
3849 /* "forget" pattern in o */
3850 o->length = pattern - o->data;
3851 while (1) {
3852 o_addstr_with_NUL(o, *argv);
3853 n = o_save_ptr_helper(o, n);
3854 argv++;
3855 if (!*argv)
3856 break;
3857 }
3858 }
3859 globfree(&globdata);
3860 if (DEBUG_GLOB)
3861 debug_print_list("perform_glob returning", o, n);
3862 return n;
Francis Lanielbfc406a2023-12-22 22:02:33 +01003863#else /* __U_BOOT__ */
3864 /*
3865 * NOTE We only use perform glob to call unbackslash to remove backslash
3866 * from string once expanded.
3867 * So, it seems OK to return this if no previous return was done.
3868 */
3869 return o_save_ptr_helper(o, n);
3870#endif /* __U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01003871}
3872
3873#endif /* !HUSH_BRACE_EXPANSION */
3874
3875/* If o->o_expflags & EXP_FLAG_GLOB, glob the string so far remembered.
3876 * Otherwise, just finish current list[] and start new */
3877static int o_save_ptr(o_string *o, int n)
3878{
3879 if (o->o_expflags & EXP_FLAG_GLOB) {
3880 /* If o->has_empty_slot, list[n] was already globbed
3881 * (if it was requested back then when it was filled)
3882 * so don't do that again! */
3883 if (!o->has_empty_slot)
3884 return perform_glob(o, n); /* o_save_ptr_helper is inside */
3885 }
3886 return o_save_ptr_helper(o, n);
3887}
3888
3889/* "Please convert list[n] to real char* ptrs, and NULL terminate it." */
3890static char **o_finalize_list(o_string *o, int n)
3891{
3892 char **list;
3893 int string_start;
3894
3895 if (DEBUG_EXPAND)
3896 debug_print_list("finalized", o, n);
3897 debug_printf_expand("finalized n:%d\n", n);
3898 list = (char**)o->data;
3899 string_start = ((n + 0xf) & ~0xf) * sizeof(list[0]);
3900 list[--n] = NULL;
3901 while (n) {
3902 n--;
3903 list[n] = o->data + (int)(uintptr_t)list[n] + string_start;
3904 }
3905 return list;
3906}
3907
3908static void free_pipe_list(struct pipe *pi);
3909
3910/* Returns pi->next - next pipe in the list */
3911static struct pipe *free_pipe(struct pipe *pi)
3912{
3913 struct pipe *next;
3914 int i;
3915
Francis Laniel36836fc2023-12-22 22:02:28 +01003916#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01003917 debug_printf_clean("free_pipe (pid %d)\n", getpid());
Francis Laniel36836fc2023-12-22 22:02:28 +01003918#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01003919 for (i = 0; i < pi->num_cmds; i++) {
3920 struct command *command;
Francis Laniel36836fc2023-12-22 22:02:28 +01003921#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01003922 struct redir_struct *r, *rnext;
Francis Laniel36836fc2023-12-22 22:02:28 +01003923#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01003924
3925 command = &pi->cmds[i];
3926 debug_printf_clean(" command %d:\n", i);
3927 if (command->argv) {
3928 if (DEBUG_CLEAN) {
3929 int a;
3930 char **p;
3931 for (a = 0, p = command->argv; *p; a++, p++) {
3932 debug_printf_clean(" argv[%d] = %s\n", a, *p);
3933 }
3934 }
3935 free_strings(command->argv);
3936 //command->argv = NULL;
3937 }
3938 /* not "else if": on syntax error, we may have both! */
3939 if (command->group) {
3940 debug_printf_clean(" begin group (cmd_type:%d)\n",
3941 command->cmd_type);
3942 free_pipe_list(command->group);
3943 debug_printf_clean(" end group\n");
3944 //command->group = NULL;
3945 }
3946 /* else is crucial here.
3947 * If group != NULL, child_func is meaningless */
3948#if ENABLE_HUSH_FUNCTIONS
3949 else if (command->child_func) {
3950 debug_printf_exec("cmd %p releases child func at %p\n", command, command->child_func);
3951 command->child_func->parent_cmd = NULL;
3952 }
3953#endif
3954#if !BB_MMU
3955 free(command->group_as_string);
3956 //command->group_as_string = NULL;
3957#endif
Francis Laniel36836fc2023-12-22 22:02:28 +01003958#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01003959 for (r = command->redirects; r; r = rnext) {
3960 debug_printf_clean(" redirect %d%s",
3961 r->rd_fd, redir_table[r->rd_type].descrip);
3962 /* guard against the case >$FOO, where foo is unset or blank */
3963 if (r->rd_filename) {
3964 debug_printf_clean(" fname:'%s'\n", r->rd_filename);
3965 free(r->rd_filename);
3966 //r->rd_filename = NULL;
3967 }
3968 debug_printf_clean(" rd_dup:%d\n", r->rd_dup);
3969 rnext = r->next;
3970 free(r);
3971 }
3972 //command->redirects = NULL;
Francis Laniel36836fc2023-12-22 22:02:28 +01003973#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01003974 }
3975 free(pi->cmds); /* children are an array, they get freed all at once */
3976 //pi->cmds = NULL;
Francis Laniel36836fc2023-12-22 22:02:28 +01003977#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01003978#if ENABLE_HUSH_JOB
3979 free(pi->cmdtext);
3980 //pi->cmdtext = NULL;
3981#endif
Francis Laniel36836fc2023-12-22 22:02:28 +01003982#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01003983
3984 next = pi->next;
3985 free(pi);
3986 return next;
3987}
3988
3989static void free_pipe_list(struct pipe *pi)
3990{
3991 while (pi) {
3992#if HAS_KEYWORDS
3993 debug_printf_clean("pipe reserved word %d\n", pi->res_word);
3994#endif
3995 debug_printf_clean("pipe followup code %d\n", pi->followup);
3996 pi = free_pipe(pi);
3997 }
3998}
3999
4000
4001/*** Parsing routines ***/
4002
4003#ifndef debug_print_tree
4004static void debug_print_tree(struct pipe *pi, int lvl)
4005{
Francis Laniele7ca3a32023-12-22 22:02:42 +01004006 static const char *const PIPE[] ALIGN_PTR = {
Francis Laniel110b7692023-12-22 22:02:27 +01004007 [PIPE_SEQ] = "SEQ",
4008 [PIPE_AND] = "AND",
4009 [PIPE_OR ] = "OR" ,
4010 [PIPE_BG ] = "BG" ,
4011 };
4012 static const char *RES[] = {
4013 [RES_NONE ] = "NONE" ,
4014# if ENABLE_HUSH_IF
4015 [RES_IF ] = "IF" ,
4016 [RES_THEN ] = "THEN" ,
4017 [RES_ELIF ] = "ELIF" ,
4018 [RES_ELSE ] = "ELSE" ,
4019 [RES_FI ] = "FI" ,
4020# endif
4021# if ENABLE_HUSH_LOOPS
4022 [RES_FOR ] = "FOR" ,
4023 [RES_WHILE] = "WHILE",
4024 [RES_UNTIL] = "UNTIL",
4025 [RES_DO ] = "DO" ,
4026 [RES_DONE ] = "DONE" ,
4027# endif
4028# if ENABLE_HUSH_LOOPS || ENABLE_HUSH_CASE
4029 [RES_IN ] = "IN" ,
4030# endif
4031# if ENABLE_HUSH_CASE
4032 [RES_CASE ] = "CASE" ,
4033 [RES_CASE_IN ] = "CASE_IN" ,
4034 [RES_MATCH] = "MATCH",
4035 [RES_CASE_BODY] = "CASE_BODY",
4036 [RES_ESAC ] = "ESAC" ,
4037# endif
4038 [RES_XXXX ] = "XXXX" ,
4039 [RES_SNTX ] = "SNTX" ,
4040 };
Francis Laniele7ca3a32023-12-22 22:02:42 +01004041 static const char *const CMDTYPE[] ALIGN_PTR = {
Francis Laniel110b7692023-12-22 22:02:27 +01004042 "{}",
4043 "()",
4044 "[noglob]",
4045# if ENABLE_HUSH_FUNCTIONS
4046 "func()",
4047# endif
4048 };
4049
4050 int pin, prn;
4051
4052 pin = 0;
4053 while (pi) {
4054 fdprintf(2, "%*spipe %d #cmds:%d %sres_word=%s followup=%d %s\n",
4055 lvl*2, "",
4056 pin,
4057 pi->num_cmds,
4058 (IF_HAS_KEYWORDS(pi->pi_inverted ? "! " :) ""),
4059 RES[pi->res_word],
4060 pi->followup, PIPE[pi->followup]
4061 );
4062 prn = 0;
4063 while (prn < pi->num_cmds) {
4064 struct command *command = &pi->cmds[prn];
4065 char **argv = command->argv;
4066
4067 fdprintf(2, "%*s cmd %d assignment_cnt:%d",
4068 lvl*2, "", prn,
4069 command->assignment_cnt);
4070# if ENABLE_HUSH_LINENO_VAR
4071 fdprintf(2, " LINENO:%u", command->lineno);
4072# endif
4073 if (command->group) {
4074 fdprintf(2, " group %s: (argv=%p)%s%s\n",
4075 CMDTYPE[command->cmd_type],
4076 argv
4077# if !BB_MMU
4078 , " group_as_string:", command->group_as_string
4079# else
4080 , "", ""
4081# endif
4082 );
4083 debug_print_tree(command->group, lvl+1);
4084 prn++;
4085 continue;
4086 }
4087 if (argv) while (*argv) {
4088 fdprintf(2, " '%s'", *argv);
4089 argv++;
4090 }
Francis Laniel36836fc2023-12-22 22:02:28 +01004091#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01004092 if (command->redirects)
4093 fdprintf(2, " {redir}");
Francis Laniel36836fc2023-12-22 22:02:28 +01004094#endif /* __U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01004095 fdprintf(2, "\n");
4096 prn++;
4097 }
4098 pi = pi->next;
4099 pin++;
4100 }
4101}
4102#endif /* debug_print_tree */
4103
4104static struct pipe *new_pipe(void)
4105{
4106 struct pipe *pi;
4107 pi = xzalloc(sizeof(struct pipe));
4108 /*pi->res_word = RES_NONE; - RES_NONE is 0 anyway */
4109 return pi;
4110}
4111
4112/* Command (member of a pipe) is complete, or we start a new pipe
4113 * if ctx->command is NULL.
4114 * No errors possible here.
4115 */
4116static int done_command(struct parse_context *ctx)
4117{
4118 /* The command is really already in the pipe structure, so
4119 * advance the pipe counter and make a new, null command. */
4120 struct pipe *pi = ctx->pipe;
4121 struct command *command = ctx->command;
4122
4123#if 0 /* Instead we emit error message at run time */
4124 if (ctx->pending_redirect) {
4125 /* For example, "cmd >" (no filename to redirect to) */
4126 syntax_error("invalid redirect");
4127 ctx->pending_redirect = NULL;
4128 }
4129#endif
4130
4131 if (command) {
4132 if (IS_NULL_CMD(command)) {
4133 debug_printf_parse("done_command: skipping null cmd, num_cmds=%d\n", pi->num_cmds);
4134 goto clear_and_ret;
4135 }
4136 pi->num_cmds++;
4137 debug_printf_parse("done_command: ++num_cmds=%d\n", pi->num_cmds);
4138 //debug_print_tree(ctx->list_head, 20);
4139 } else {
4140 debug_printf_parse("done_command: initializing, num_cmds=%d\n", pi->num_cmds);
4141 }
4142
4143 /* Only real trickiness here is that the uncommitted
4144 * command structure is not counted in pi->num_cmds. */
4145 pi->cmds = xrealloc(pi->cmds, sizeof(*pi->cmds) * (pi->num_cmds+1));
4146 ctx->command = command = &pi->cmds[pi->num_cmds];
4147 clear_and_ret:
4148 memset(command, 0, sizeof(*command));
4149#if ENABLE_HUSH_LINENO_VAR
4150 command->lineno = G.parse_lineno;
4151 debug_printf_parse("command->lineno = G.parse_lineno (%u)\n", G.parse_lineno);
4152#endif
4153 return pi->num_cmds; /* used only for 0/nonzero check */
4154}
4155
4156static void done_pipe(struct parse_context *ctx, pipe_style type)
4157{
4158 int not_null;
4159
4160 debug_printf_parse("done_pipe entered, followup %d\n", type);
4161 /* Close previous command */
4162 not_null = done_command(ctx);
4163#if HAS_KEYWORDS
4164 ctx->pipe->pi_inverted = ctx->ctx_inverted;
4165 ctx->ctx_inverted = 0;
4166 ctx->pipe->res_word = ctx->ctx_res_w;
4167#endif
4168 if (type == PIPE_BG && ctx->list_head != ctx->pipe) {
4169 /* Necessary since && and || have precedence over &:
4170 * "cmd1 && cmd2 &" must spawn both cmds, not only cmd2,
4171 * in a backgrounded subshell.
4172 */
4173 struct pipe *pi;
4174 struct command *command;
4175
4176 /* Is this actually this construct, all pipes end with && or ||? */
4177 pi = ctx->list_head;
4178 while (pi != ctx->pipe) {
4179 if (pi->followup != PIPE_AND && pi->followup != PIPE_OR)
4180 goto no_conv;
4181 pi = pi->next;
4182 }
4183
4184 debug_printf_parse("BG with more than one pipe, converting to { p1 &&...pN; } &\n");
4185 pi->followup = PIPE_SEQ; /* close pN _not_ with "&"! */
4186 pi = xzalloc(sizeof(*pi));
4187 pi->followup = PIPE_BG;
4188 pi->num_cmds = 1;
4189 pi->cmds = xzalloc(sizeof(pi->cmds[0]));
4190 command = &pi->cmds[0];
4191 if (CMD_NORMAL != 0) /* "if xzalloc didn't do that already" */
4192 command->cmd_type = CMD_NORMAL;
4193 command->group = ctx->list_head;
4194#if !BB_MMU
4195 command->group_as_string = xstrndup(
4196 ctx->as_string.data,
4197 ctx->as_string.length - 1 /* do not copy last char, "&" */
4198 );
4199#endif
4200 /* Replace all pipes in ctx with one newly created */
4201 ctx->list_head = ctx->pipe = pi;
4202 /* for cases like "cmd && &", do not be tricked by last command
4203 * being null - the entire {...} & is NOT null! */
4204 not_null = 1;
4205 } else {
4206 no_conv:
4207 ctx->pipe->followup = type;
4208 }
4209
4210 /* Without this check, even just <enter> on command line generates
4211 * tree of three NOPs (!). Which is harmless but annoying.
4212 * IOW: it is safe to do it unconditionally. */
4213 if (not_null
4214#if ENABLE_HUSH_IF
4215 || ctx->ctx_res_w == RES_FI
4216#endif
4217#if ENABLE_HUSH_LOOPS
4218 || ctx->ctx_res_w == RES_DONE
4219 || ctx->ctx_res_w == RES_FOR
4220 || ctx->ctx_res_w == RES_IN
4221#endif
4222#if ENABLE_HUSH_CASE
4223 || ctx->ctx_res_w == RES_ESAC
4224#endif
4225 ) {
4226 struct pipe *new_p;
4227 debug_printf_parse("done_pipe: adding new pipe: "
Francis Laniel36836fc2023-12-22 22:02:28 +01004228#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01004229 "not_null:%d ctx->ctx_res_w:%d\n",
4230 not_null, ctx->ctx_res_w);
Francis Laniel36836fc2023-12-22 22:02:28 +01004231#else /* __U_BOOT__ */
4232 "not_null:%d\n",
4233 not_null);
4234#endif /* __U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01004235 new_p = new_pipe();
4236 ctx->pipe->next = new_p;
4237 ctx->pipe = new_p;
4238 /* RES_THEN, RES_DO etc are "sticky" -
4239 * they remain set for pipes inside if/while.
4240 * This is used to control execution.
4241 * RES_FOR and RES_IN are NOT sticky (needed to support
4242 * cases where variable or value happens to match a keyword):
4243 */
4244#if ENABLE_HUSH_LOOPS
4245 if (ctx->ctx_res_w == RES_FOR
4246 || ctx->ctx_res_w == RES_IN)
4247 ctx->ctx_res_w = RES_NONE;
4248#endif
4249#if ENABLE_HUSH_CASE
4250 if (ctx->ctx_res_w == RES_MATCH)
4251 ctx->ctx_res_w = RES_CASE_BODY;
4252 if (ctx->ctx_res_w == RES_CASE)
4253 ctx->ctx_res_w = RES_CASE_IN;
4254#endif
4255 ctx->command = NULL; /* trick done_command below */
4256 /* Create the memory for command, roughly:
4257 * ctx->pipe->cmds = new struct command;
4258 * ctx->command = &ctx->pipe->cmds[0];
4259 */
4260 done_command(ctx);
4261 //debug_print_tree(ctx->list_head, 10);
4262 }
4263 debug_printf_parse("done_pipe return\n");
4264}
4265
4266static void initialize_context(struct parse_context *ctx)
4267{
4268 memset(ctx, 0, sizeof(*ctx));
4269 if (MAYBE_ASSIGNMENT != 0)
4270 ctx->is_assignment = MAYBE_ASSIGNMENT;
4271 ctx->pipe = ctx->list_head = new_pipe();
4272 /* Create the memory for command, roughly:
4273 * ctx->pipe->cmds = new struct command;
4274 * ctx->command = &ctx->pipe->cmds[0];
4275 */
4276 done_command(ctx);
4277}
4278
4279/* If a reserved word is found and processed, parse context is modified
4280 * and 1 is returned.
4281 */
4282#if HAS_KEYWORDS
4283struct reserved_combo {
4284 char literal[6];
4285 unsigned char res;
4286 unsigned char assignment_flag;
4287 uint32_t flag;
4288};
4289enum {
4290 FLAG_END = (1 << RES_NONE ),
4291# if ENABLE_HUSH_IF
4292 FLAG_IF = (1 << RES_IF ),
4293 FLAG_THEN = (1 << RES_THEN ),
4294 FLAG_ELIF = (1 << RES_ELIF ),
4295 FLAG_ELSE = (1 << RES_ELSE ),
4296 FLAG_FI = (1 << RES_FI ),
4297# endif
4298# if ENABLE_HUSH_LOOPS
4299 FLAG_FOR = (1 << RES_FOR ),
4300 FLAG_WHILE = (1 << RES_WHILE),
4301 FLAG_UNTIL = (1 << RES_UNTIL),
4302 FLAG_DO = (1 << RES_DO ),
4303 FLAG_DONE = (1 << RES_DONE ),
4304 FLAG_IN = (1 << RES_IN ),
4305# endif
4306# if ENABLE_HUSH_CASE
4307 FLAG_MATCH = (1 << RES_MATCH),
4308 FLAG_ESAC = (1 << RES_ESAC ),
4309# endif
4310 FLAG_START = (1 << RES_XXXX ),
4311};
4312
4313static const struct reserved_combo* match_reserved_word(o_string *word)
4314{
4315 /* Mostly a list of accepted follow-up reserved words.
4316 * FLAG_END means we are done with the sequence, and are ready
4317 * to turn the compound list into a command.
4318 * FLAG_START means the word must start a new compound list.
4319 */
4320 static const struct reserved_combo reserved_list[] ALIGN4 = {
4321# if ENABLE_HUSH_IF
4322 { "!", RES_NONE, NOT_ASSIGNMENT , 0 },
4323 { "if", RES_IF, MAYBE_ASSIGNMENT, FLAG_THEN | FLAG_START },
4324 { "then", RES_THEN, MAYBE_ASSIGNMENT, FLAG_ELIF | FLAG_ELSE | FLAG_FI },
4325 { "elif", RES_ELIF, MAYBE_ASSIGNMENT, FLAG_THEN },
4326 { "else", RES_ELSE, MAYBE_ASSIGNMENT, FLAG_FI },
4327 { "fi", RES_FI, NOT_ASSIGNMENT , FLAG_END },
4328# endif
4329# if ENABLE_HUSH_LOOPS
4330 { "for", RES_FOR, NOT_ASSIGNMENT , FLAG_IN | FLAG_DO | FLAG_START },
4331 { "while", RES_WHILE, MAYBE_ASSIGNMENT, FLAG_DO | FLAG_START },
4332 { "until", RES_UNTIL, MAYBE_ASSIGNMENT, FLAG_DO | FLAG_START },
4333 { "in", RES_IN, NOT_ASSIGNMENT , FLAG_DO },
4334 { "do", RES_DO, MAYBE_ASSIGNMENT, FLAG_DONE },
4335 { "done", RES_DONE, NOT_ASSIGNMENT , FLAG_END },
4336# endif
4337# if ENABLE_HUSH_CASE
4338 { "case", RES_CASE, NOT_ASSIGNMENT , FLAG_MATCH | FLAG_START },
4339 { "esac", RES_ESAC, NOT_ASSIGNMENT , FLAG_END },
4340# endif
4341 };
4342 const struct reserved_combo *r;
4343
4344 for (r = reserved_list; r < reserved_list + ARRAY_SIZE(reserved_list); r++) {
4345 if (strcmp(word->data, r->literal) == 0)
4346 return r;
4347 }
4348 return NULL;
4349}
4350/* Return NULL: not a keyword, else: keyword
4351 */
4352static const struct reserved_combo* reserved_word(struct parse_context *ctx)
4353{
4354# if ENABLE_HUSH_CASE
4355 static const struct reserved_combo reserved_match = {
4356 "", RES_MATCH, NOT_ASSIGNMENT , FLAG_MATCH | FLAG_ESAC
4357 };
4358# endif
4359 const struct reserved_combo *r;
4360
4361 if (ctx->word.has_quoted_part)
4362 return 0;
4363 r = match_reserved_word(&ctx->word);
4364 if (!r)
4365 return r; /* NULL */
4366
4367 debug_printf("found reserved word %s, res %d\n", r->literal, r->res);
4368# if ENABLE_HUSH_CASE
4369 if (r->res == RES_IN && ctx->ctx_res_w == RES_CASE_IN) {
4370 /* "case word IN ..." - IN part starts first MATCH part */
4371 r = &reserved_match;
4372 } else
4373# endif
4374 if (r->flag == 0) { /* '!' */
4375 if (ctx->ctx_inverted) { /* bash doesn't accept '! ! true' */
4376 syntax_error("! ! command");
4377 ctx->ctx_res_w = RES_SNTX;
4378 }
4379 ctx->ctx_inverted = 1;
4380 return r;
4381 }
4382 if (r->flag & FLAG_START) {
4383 struct parse_context *old;
4384
4385 old = xmemdup(ctx, sizeof(*ctx));
4386 debug_printf_parse("push stack %p\n", old);
4387 initialize_context(ctx);
4388 ctx->stack = old;
4389 } else if (/*ctx->ctx_res_w == RES_NONE ||*/ !(ctx->old_flag & (1 << r->res))) {
4390 syntax_error_at(ctx->word.data);
4391 ctx->ctx_res_w = RES_SNTX;
4392 return r;
4393 } else {
4394 /* "{...} fi" is ok. "{...} if" is not
4395 * Example:
4396 * if { echo foo; } then { echo bar; } fi */
4397 if (ctx->command->group)
4398 done_pipe(ctx, PIPE_SEQ);
4399 }
4400
4401 ctx->ctx_res_w = r->res;
4402 ctx->old_flag = r->flag;
4403 ctx->is_assignment = r->assignment_flag;
4404 debug_printf_parse("ctx->is_assignment='%s'\n", assignment_flag[ctx->is_assignment]);
4405
4406 if (ctx->old_flag & FLAG_END) {
4407 struct parse_context *old;
4408
4409 done_pipe(ctx, PIPE_SEQ);
4410 debug_printf_parse("pop stack %p\n", ctx->stack);
4411 old = ctx->stack;
4412 old->command->group = ctx->list_head;
4413 old->command->cmd_type = CMD_NORMAL;
4414# if !BB_MMU
4415 /* At this point, the compound command's string is in
4416 * ctx->as_string... except for the leading keyword!
4417 * Consider this example: "echo a | if true; then echo a; fi"
4418 * ctx->as_string will contain "true; then echo a; fi",
4419 * with "if " remaining in old->as_string!
4420 */
4421 {
4422 char *str;
4423 int len = old->as_string.length;
4424 /* Concatenate halves */
4425 o_addstr(&old->as_string, ctx->as_string.data);
4426 o_free(&ctx->as_string);
4427 /* Find where leading keyword starts in first half */
4428 str = old->as_string.data + len;
4429 if (str > old->as_string.data)
4430 str--; /* skip whitespace after keyword */
4431 while (str > old->as_string.data && isalpha(str[-1]))
4432 str--;
4433 /* Ugh, we're done with this horrid hack */
4434 old->command->group_as_string = xstrdup(str);
4435 debug_printf_parse("pop, remembering as:'%s'\n",
4436 old->command->group_as_string);
4437 }
4438# endif
4439 *ctx = *old; /* physical copy */
4440 free(old);
4441 }
4442 return r;
4443}
4444#endif /* HAS_KEYWORDS */
4445
4446/* Word is complete, look at it and update parsing context.
4447 * Normal return is 0. Syntax errors return 1.
4448 * Note: on return, word is reset, but not o_free'd!
4449 */
4450static int done_word(struct parse_context *ctx)
4451{
4452 struct command *command = ctx->command;
4453
4454 debug_printf_parse("done_word entered: '%s' %p\n", ctx->word.data, command);
4455 if (ctx->word.length == 0 && !ctx->word.has_quoted_part) {
4456 debug_printf_parse("done_word return 0: true null, ignored\n");
4457 return 0;
4458 }
4459
Francis Laniel36836fc2023-12-22 22:02:28 +01004460#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01004461 if (ctx->pending_redirect) {
4462 /* We do not glob in e.g. >*.tmp case. bash seems to glob here
4463 * only if run as "bash", not "sh" */
4464 /* http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html
4465 * "2.7 Redirection
4466 * If the redirection operator is "<<" or "<<-", the word
4467 * that follows the redirection operator shall be
4468 * subjected to quote removal; it is unspecified whether
4469 * any of the other expansions occur. For the other
4470 * redirection operators, the word that follows the
4471 * redirection operator shall be subjected to tilde
4472 * expansion, parameter expansion, command substitution,
4473 * arithmetic expansion, and quote removal.
4474 * Pathname expansion shall not be performed
4475 * on the word by a non-interactive shell; an interactive
4476 * shell may perform it, but shall do so only when
4477 * the expansion would result in one word."
4478 */
4479//bash does not do parameter/command substitution or arithmetic expansion
4480//for _heredoc_ redirection word: these constructs look for exact eof marker
4481// as written:
4482// <<EOF$t
4483// <<EOF$((1))
4484// <<EOF`true` [this case also makes heredoc "quoted", a-la <<"EOF". Probably bash-4.3.43 bug]
4485
4486 ctx->pending_redirect->rd_filename = xstrdup(ctx->word.data);
4487 /* Cater for >\file case:
4488 * >\a creates file a; >\\a, >"\a", >"\\a" create file \a
4489 * Same with heredocs:
4490 * for <<\H delim is H; <<\\H, <<"\H", <<"\\H" - \H
4491 */
4492 if (ctx->pending_redirect->rd_type == REDIRECT_HEREDOC) {
4493 unbackslash(ctx->pending_redirect->rd_filename);
4494 /* Is it <<"HEREDOC"? */
4495 if (ctx->word.has_quoted_part) {
4496 ctx->pending_redirect->rd_dup |= HEREDOC_QUOTED;
4497 }
4498 }
4499 debug_printf_parse("word stored in rd_filename: '%s'\n", ctx->word.data);
4500 ctx->pending_redirect = NULL;
4501 } else {
Francis Laniel36836fc2023-12-22 22:02:28 +01004502#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01004503#if HAS_KEYWORDS
4504# if ENABLE_HUSH_CASE
4505 if (ctx->ctx_dsemicolon
4506 && strcmp(ctx->word.data, "esac") != 0 /* not "... pattern) cmd;; esac" */
4507 ) {
4508 /* already done when ctx_dsemicolon was set to 1: */
4509 /* ctx->ctx_res_w = RES_MATCH; */
4510 ctx->ctx_dsemicolon = 0;
4511 } else
4512# endif
4513# if defined(CMD_TEST2_SINGLEWORD_NOGLOB)
4514 if (command->cmd_type == CMD_TEST2_SINGLEWORD_NOGLOB
4515 && strcmp(ctx->word.data, "]]") == 0
4516 ) {
4517 /* allow "[[ ]] >file" etc */
4518 command->cmd_type = CMD_SINGLEWORD_NOGLOB;
4519 } else
4520# endif
4521 if (!command->argv /* if it's the first word... */
4522# if ENABLE_HUSH_LOOPS
4523 && ctx->ctx_res_w != RES_FOR /* ...not after FOR or IN */
4524 && ctx->ctx_res_w != RES_IN
4525# endif
4526# if ENABLE_HUSH_CASE
4527 && ctx->ctx_res_w != RES_CASE
4528# endif
4529 ) {
4530 const struct reserved_combo *reserved;
4531 reserved = reserved_word(ctx);
4532 debug_printf_parse("checking for reserved-ness: %d\n", !!reserved);
4533 if (reserved) {
4534# if ENABLE_HUSH_LINENO_VAR
4535/* Case:
4536 * "while ...; do
4537 * cmd ..."
4538 * If we don't close the pipe _now_, immediately after "do", lineno logic
4539 * sees "cmd" as starting at "do" - i.e., at the previous line.
4540 */
4541 if (0
4542 IF_HUSH_IF(|| reserved->res == RES_THEN)
4543 IF_HUSH_IF(|| reserved->res == RES_ELIF)
4544 IF_HUSH_IF(|| reserved->res == RES_ELSE)
4545 IF_HUSH_LOOPS(|| reserved->res == RES_DO)
4546 ) {
4547 done_pipe(ctx, PIPE_SEQ);
4548 }
4549# endif
4550 o_reset_to_empty_unquoted(&ctx->word);
4551 debug_printf_parse("done_word return %d\n",
4552 (ctx->ctx_res_w == RES_SNTX));
4553 return (ctx->ctx_res_w == RES_SNTX);
4554 }
4555# if defined(CMD_TEST2_SINGLEWORD_NOGLOB)
4556 if (strcmp(ctx->word.data, "[[") == 0) {
4557 command->cmd_type = CMD_TEST2_SINGLEWORD_NOGLOB;
4558 } else
4559# endif
4560# if defined(CMD_SINGLEWORD_NOGLOB)
4561 if (0
4562 /* In bash, local/export/readonly are special, args
4563 * are assignments and therefore expansion of them
4564 * should be "one-word" expansion:
4565 * $ export i=`echo 'a b'` # one arg: "i=a b"
4566 * compare with:
4567 * $ ls i=`echo 'a b'` # two args: "i=a" and "b"
4568 * ls: cannot access i=a: No such file or directory
4569 * ls: cannot access b: No such file or directory
4570 * Note: bash 3.2.33(1) does this only if export word
4571 * itself is not quoted:
4572 * $ export i=`echo 'aaa bbb'`; echo "$i"
4573 * aaa bbb
4574 * $ "export" i=`echo 'aaa bbb'`; echo "$i"
4575 * aaa
4576 */
4577 IF_HUSH_LOCAL( || strcmp(ctx->word.data, "local") == 0)
4578 IF_HUSH_EXPORT( || strcmp(ctx->word.data, "export") == 0)
4579 IF_HUSH_READONLY(|| strcmp(ctx->word.data, "readonly") == 0)
4580 ) {
4581 command->cmd_type = CMD_SINGLEWORD_NOGLOB;
4582 }
4583# else
4584 { /* empty block to pair "if ... else" */ }
4585# endif
4586 }
4587#endif /* HAS_KEYWORDS */
4588
4589 if (command->group) {
4590 /* "{ echo foo; } echo bar" - bad */
4591 syntax_error_at(ctx->word.data);
4592 debug_printf_parse("done_word return 1: syntax error, "
4593 "groups and arglists don't mix\n");
4594 return 1;
4595 }
4596
4597 /* If this word wasn't an assignment, next ones definitely
4598 * can't be assignments. Even if they look like ones. */
4599 if (ctx->is_assignment != DEFINITELY_ASSIGNMENT
4600 && ctx->is_assignment != WORD_IS_KEYWORD
4601 ) {
4602 ctx->is_assignment = NOT_ASSIGNMENT;
4603 } else {
4604 if (ctx->is_assignment == DEFINITELY_ASSIGNMENT) {
4605 command->assignment_cnt++;
4606 debug_printf_parse("++assignment_cnt=%d\n", command->assignment_cnt);
4607 }
4608 debug_printf_parse("ctx->is_assignment was:'%s'\n", assignment_flag[ctx->is_assignment]);
4609 ctx->is_assignment = MAYBE_ASSIGNMENT;
4610 }
4611 debug_printf_parse("ctx->is_assignment='%s'\n", assignment_flag[ctx->is_assignment]);
4612 command->argv = add_string_to_strings(command->argv, xstrdup(ctx->word.data));
Francis Laniel36836fc2023-12-22 22:02:28 +01004613#ifdef __U_BOOT__
4614 command->argc++;
4615#endif /* __U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01004616 debug_print_strings("word appended to argv", command->argv);
Francis Laniel36836fc2023-12-22 22:02:28 +01004617
4618#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01004619 }
Francis Laniel36836fc2023-12-22 22:02:28 +01004620#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01004621
4622#if ENABLE_HUSH_LOOPS
4623 if (ctx->ctx_res_w == RES_FOR) {
4624 if (ctx->word.has_quoted_part
4625 || endofname(command->argv[0])[0] != '\0'
4626 ) {
4627 /* bash says just "not a valid identifier" */
Francis Laniele7ca3a32023-12-22 22:02:42 +01004628 syntax_error("bad for loop variable");
Francis Laniel110b7692023-12-22 22:02:27 +01004629 return 1;
4630 }
4631 /* Force FOR to have just one word (variable name) */
4632 /* NB: basically, this makes hush see "for v in ..."
4633 * syntax as if it is "for v; in ...". FOR and IN become
4634 * two pipe structs in parse tree. */
4635 done_pipe(ctx, PIPE_SEQ);
4636 }
4637#endif
4638#if ENABLE_HUSH_CASE
4639 /* Force CASE to have just one word */
4640 if (ctx->ctx_res_w == RES_CASE) {
4641 done_pipe(ctx, PIPE_SEQ);
4642 }
4643#endif
4644
4645 o_reset_to_empty_unquoted(&ctx->word);
4646
4647 debug_printf_parse("done_word return 0\n");
4648 return 0;
4649}
4650
4651
Francis Laniel36836fc2023-12-22 22:02:28 +01004652#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01004653/* Peek ahead in the input to find out if we have a "&n" construct,
4654 * as in "2>&1", that represents duplicating a file descriptor.
4655 * Return:
4656 * REDIRFD_CLOSE if >&- "close fd" construct is seen,
4657 * REDIRFD_SYNTAX_ERR if syntax error,
4658 * REDIRFD_TO_FILE if no & was seen,
4659 * or the number found.
4660 */
4661#if BB_MMU
4662#define parse_redir_right_fd(as_string, input) \
4663 parse_redir_right_fd(input)
4664#endif
4665static int parse_redir_right_fd(o_string *as_string, struct in_str *input)
4666{
4667 int ch, d, ok;
4668
4669 ch = i_peek(input);
4670 if (ch != '&')
4671 return REDIRFD_TO_FILE;
4672
4673 ch = i_getch(input); /* get the & */
4674 nommu_addchr(as_string, ch);
4675 ch = i_peek(input);
4676 if (ch == '-') {
4677 ch = i_getch(input);
4678 nommu_addchr(as_string, ch);
4679 return REDIRFD_CLOSE;
4680 }
4681 d = 0;
4682 ok = 0;
4683 while (ch != EOF && isdigit(ch)) {
4684 d = d*10 + (ch-'0');
4685 ok = 1;
4686 ch = i_getch(input);
4687 nommu_addchr(as_string, ch);
4688 ch = i_peek(input);
4689 }
4690 if (ok) return d;
4691
4692//TODO: this is the place to catch ">&file" bashism (redirect both fd 1 and 2)
4693
4694 bb_simple_error_msg("ambiguous redirect");
4695 return REDIRFD_SYNTAX_ERR;
4696}
4697
4698/* Return code is 0 normal, 1 if a syntax error is detected
4699 */
4700static int parse_redirect(struct parse_context *ctx,
4701 int fd,
4702 redir_type style,
4703 struct in_str *input)
4704{
4705 struct command *command = ctx->command;
4706 struct redir_struct *redir;
4707 struct redir_struct **redirp;
4708 int dup_num;
4709
4710 dup_num = REDIRFD_TO_FILE;
4711 if (style != REDIRECT_HEREDOC) {
4712 /* Check for a '>&1' type redirect */
4713 dup_num = parse_redir_right_fd(&ctx->as_string, input);
4714 if (dup_num == REDIRFD_SYNTAX_ERR)
4715 return 1;
4716 } else {
4717 int ch = i_peek_and_eat_bkslash_nl(input);
4718 dup_num = (ch == '-'); /* HEREDOC_SKIPTABS bit is 1 */
4719 if (dup_num) { /* <<-... */
4720 ch = i_getch(input);
4721 nommu_addchr(&ctx->as_string, ch);
4722 ch = i_peek(input);
4723 }
4724 }
4725
4726 if (style == REDIRECT_OVERWRITE && dup_num == REDIRFD_TO_FILE) {
4727 int ch = i_peek_and_eat_bkslash_nl(input);
4728 if (ch == '|') {
4729 /* >|FILE redirect ("clobbering" >).
4730 * Since we do not support "set -o noclobber" yet,
4731 * >| and > are the same for now. Just eat |.
4732 */
4733 ch = i_getch(input);
4734 nommu_addchr(&ctx->as_string, ch);
4735 }
4736 }
4737
4738 /* Create a new redir_struct and append it to the linked list */
4739 redirp = &command->redirects;
4740 while ((redir = *redirp) != NULL) {
4741 redirp = &(redir->next);
4742 }
4743 *redirp = redir = xzalloc(sizeof(*redir));
4744 /* redir->next = NULL; */
4745 /* redir->rd_filename = NULL; */
4746 redir->rd_type = style;
4747 redir->rd_fd = (fd == -1) ? redir_table[style].default_fd : fd;
4748
4749 debug_printf_parse("redirect type %d %s\n", redir->rd_fd,
4750 redir_table[style].descrip);
4751
4752 redir->rd_dup = dup_num;
4753 if (style != REDIRECT_HEREDOC && dup_num != REDIRFD_TO_FILE) {
4754 /* Erik had a check here that the file descriptor in question
4755 * is legit; I postpone that to "run time"
4756 * A "-" representation of "close me" shows up as a -3 here */
4757 debug_printf_parse("duplicating redirect '%d>&%d'\n",
4758 redir->rd_fd, redir->rd_dup);
4759 } else {
4760#if 0 /* Instead we emit error message at run time */
4761 if (ctx->pending_redirect) {
4762 /* For example, "cmd > <file" */
4763 syntax_error("invalid redirect");
4764 }
4765#endif
4766 /* Set ctx->pending_redirect, so we know what to do at the
4767 * end of the next parsed word. */
4768 ctx->pending_redirect = redir;
4769 }
4770 return 0;
4771}
4772
4773/* If a redirect is immediately preceded by a number, that number is
4774 * supposed to tell which file descriptor to redirect. This routine
4775 * looks for such preceding numbers. In an ideal world this routine
4776 * needs to handle all the following classes of redirects...
4777 * echo 2>foo # redirects fd 2 to file "foo", nothing passed to echo
4778 * echo 49>foo # redirects fd 49 to file "foo", nothing passed to echo
4779 * echo -2>foo # redirects fd 1 to file "foo", "-2" passed to echo
4780 * echo 49x>foo # redirects fd 1 to file "foo", "49x" passed to echo
4781 *
4782 * http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html
4783 * "2.7 Redirection
4784 * ... If n is quoted, the number shall not be recognized as part of
4785 * the redirection expression. For example:
4786 * echo \2>a
4787 * writes the character 2 into file a"
4788 * We are getting it right by setting ->has_quoted_part on any \<char>
4789 *
4790 * A -1 return means no valid number was found,
4791 * the caller should use the appropriate default for this redirection.
4792 */
4793static int redirect_opt_num(o_string *o)
4794{
4795 int num;
4796
4797 if (o->data == NULL)
4798 return -1;
4799 num = bb_strtou(o->data, NULL, 10);
4800 if (errno || num < 0)
4801 return -1;
4802 o_reset_to_empty_unquoted(o);
4803 return num;
4804}
4805
4806#if BB_MMU
4807#define fetch_till_str(as_string, input, word, skip_tabs) \
4808 fetch_till_str(input, word, skip_tabs)
4809#endif
4810static char *fetch_till_str(o_string *as_string,
4811 struct in_str *input,
4812 const char *word,
4813 int heredoc_flags)
4814{
4815 o_string heredoc = NULL_O_STRING;
4816 unsigned past_EOL;
4817 int prev = 0; /* not \ */
4818 int ch;
4819
4820 /* Starting with "" is necessary for this case:
4821 * cat <<EOF
4822 *
4823 * xxx
4824 * EOF
4825 */
4826 heredoc.data = xzalloc(1); /* start as "", not as NULL */
4827
4828 goto jump_in;
4829
4830 while (1) {
4831 ch = i_getch(input);
4832 if (ch != EOF)
4833 nommu_addchr(as_string, ch);
4834 if (ch == '\n' || ch == EOF) {
4835 check_heredoc_end:
Francis Laniel36836fc2023-12-22 22:02:28 +01004836#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01004837 if ((heredoc_flags & HEREDOC_QUOTED) || prev != '\\') {
Francis Laniel36836fc2023-12-22 22:02:28 +01004838#else /* __U_BOOT__ */
4839 if (prev != '\\') {
4840#endif
Francis Laniel110b7692023-12-22 22:02:27 +01004841 /* End-of-line, and not a line continuation */
4842 if (strcmp(heredoc.data + past_EOL, word) == 0) {
4843 heredoc.data[past_EOL] = '\0';
4844 debug_printf_heredoc("parsed '%s' heredoc '%s'\n", word, heredoc.data);
4845 return heredoc.data;
4846 }
4847 if (ch == '\n') {
4848 /* This is a new line.
4849 * Remember position and backslash-escaping status.
4850 */
4851 o_addchr(&heredoc, ch);
4852 prev = ch;
4853 jump_in:
4854 past_EOL = heredoc.length;
4855 /* Get 1st char of next line, possibly skipping leading tabs */
4856 do {
4857 ch = i_getch(input);
4858 if (ch != EOF)
4859 nommu_addchr(as_string, ch);
Francis Laniel36836fc2023-12-22 22:02:28 +01004860#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01004861 } while ((heredoc_flags & HEREDOC_SKIPTABS) && ch == '\t');
Francis Laniel36836fc2023-12-22 22:02:28 +01004862#else /* __U_BOOT__ */
4863 } while (ch == '\t');
4864#endif
Francis Laniel110b7692023-12-22 22:02:27 +01004865 /* If this immediately ended the line,
4866 * go back to end-of-line checks.
4867 */
4868 if (ch == '\n')
4869 goto check_heredoc_end;
4870 }
4871 } else {
4872 /* Backslash-line continuation in an unquoted
4873 * heredoc. This does not need special handling
4874 * for heredoc body (unquoted heredocs are
4875 * expanded on "execution" and that would take
4876 * care of this case too), but not the case
4877 * of line continuation *in terminator*:
4878 * cat <<EOF
4879 * Ok1
4880 * EO\
4881 * F
4882 */
4883 heredoc.data[--heredoc.length] = '\0';
4884 prev = 0; /* not '\' */
4885 continue;
4886 }
4887 }
4888 if (ch == EOF) {
4889 o_free(&heredoc);
4890 return NULL; /* error */
4891 }
4892 o_addchr(&heredoc, ch);
4893 nommu_addchr(as_string, ch);
4894 if (prev == '\\' && ch == '\\')
4895 /* Correctly handle foo\\<eol> (not a line cont.) */
4896 prev = 0; /* not '\' */
4897 else
4898 prev = ch;
4899 }
4900}
Francis Laniel36836fc2023-12-22 22:02:28 +01004901#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01004902
4903/* Look at entire parse tree for not-yet-loaded REDIRECT_HEREDOCs
4904 * and load them all. There should be exactly heredoc_cnt of them.
4905 */
4906#if BB_MMU
4907#define fetch_heredocs(as_string, pi, heredoc_cnt, input) \
4908 fetch_heredocs(pi, heredoc_cnt, input)
4909#endif
4910static int fetch_heredocs(o_string *as_string, struct pipe *pi, int heredoc_cnt, struct in_str *input)
4911{
4912 while (pi && heredoc_cnt) {
4913 int i;
4914 struct command *cmd = pi->cmds;
4915
4916 debug_printf_heredoc("fetch_heredocs: num_cmds:%d cmd argv0:'%s'\n",
4917 pi->num_cmds,
4918 cmd->argv ? cmd->argv[0] : "NONE"
4919 );
4920 for (i = 0; i < pi->num_cmds; i++) {
Francis Laniel36836fc2023-12-22 22:02:28 +01004921#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01004922 struct redir_struct *redir = cmd->redirects;
4923
Francis Laniel36836fc2023-12-22 22:02:28 +01004924#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01004925 debug_printf_heredoc("fetch_heredocs: %d cmd argv0:'%s'\n",
4926 i, cmd->argv ? cmd->argv[0] : "NONE");
Francis Laniel36836fc2023-12-22 22:02:28 +01004927#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01004928 while (redir) {
4929 if (redir->rd_type == REDIRECT_HEREDOC) {
4930 char *p;
4931
4932 redir->rd_type = REDIRECT_HEREDOC2;
4933 /* redir->rd_dup is (ab)used to indicate <<- */
4934 p = fetch_till_str(as_string, input,
4935 redir->rd_filename, redir->rd_dup);
4936 if (!p) {
4937 syntax_error("unexpected EOF in here document");
4938 return -1;
4939 }
4940 free(redir->rd_filename);
4941 redir->rd_filename = p;
4942 heredoc_cnt--;
4943 }
4944 redir = redir->next;
4945 }
Francis Laniel36836fc2023-12-22 22:02:28 +01004946#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01004947 if (cmd->group) {
4948 //bb_error_msg("%s:%u heredoc_cnt:%d", __func__, __LINE__, heredoc_cnt);
4949 heredoc_cnt = fetch_heredocs(as_string, cmd->group, heredoc_cnt, input);
4950 //bb_error_msg("%s:%u heredoc_cnt:%d", __func__, __LINE__, heredoc_cnt);
4951 if (heredoc_cnt < 0)
4952 return heredoc_cnt; /* error */
4953 }
4954 cmd++;
4955 }
4956 pi = pi->next;
4957 }
4958 return heredoc_cnt;
4959}
4960
4961
4962static int run_list(struct pipe *pi);
4963#if BB_MMU
4964#define parse_stream(pstring, heredoc_cnt_ptr, input, end_trigger) \
4965 parse_stream(heredoc_cnt_ptr, input, end_trigger)
4966#endif
4967static struct pipe *parse_stream(char **pstring,
4968 int *heredoc_cnt_ptr,
4969 struct in_str *input,
4970 int end_trigger);
4971
4972/* Returns number of heredocs not yet consumed,
4973 * or -1 on error.
4974 */
4975static int parse_group(struct parse_context *ctx,
4976 struct in_str *input, int ch)
4977{
4978 /* ctx->word contains characters seen prior to ( or {.
4979 * Typically it's empty, but for function defs,
4980 * it contains function name (without '()'). */
4981#if BB_MMU
4982# define as_string NULL
4983#else
4984 char *as_string = NULL;
4985#endif
4986 struct pipe *pipe_list;
4987 int heredoc_cnt = 0;
4988 int endch;
4989 struct command *command = ctx->command;
4990
4991 debug_printf_parse("parse_group entered\n");
4992#if ENABLE_HUSH_FUNCTIONS
4993 if (ch == '(' && !ctx->word.has_quoted_part) {
4994 if (ctx->word.length)
4995 if (done_word(ctx))
4996 return -1;
4997 if (!command->argv)
4998 goto skip; /* (... */
4999 if (command->argv[1]) { /* word word ... (... */
5000 syntax_error_unexpected_ch('(');
5001 return -1;
5002 }
5003 /* it is "word(..." or "word (..." */
5004 do
5005 ch = i_getch(input);
5006 while (ch == ' ' || ch == '\t');
5007 if (ch != ')') {
5008 syntax_error_unexpected_ch(ch);
5009 return -1;
5010 }
5011 nommu_addchr(&ctx->as_string, ch);
5012 do
5013 ch = i_getch(input);
5014 while (ch == ' ' || ch == '\t' || ch == '\n');
5015 if (ch != '{' && ch != '(') {
5016 syntax_error_unexpected_ch(ch);
5017 return -1;
5018 }
Francis Laniele7ca3a32023-12-22 22:02:42 +01005019//bash allows functions named "123", "..", "return"!
5020// if (endofname(command->argv[0])[0] != '\0') {
5021// syntax_error("bad function name");
5022// return -1;
5023// }
Francis Laniel110b7692023-12-22 22:02:27 +01005024 nommu_addchr(&ctx->as_string, ch);
5025 command->cmd_type = CMD_FUNCDEF;
5026 goto skip;
5027 }
5028#endif
5029
5030#if 0 /* Prevented by caller */
5031 if (command->argv /* word [word]{... */
5032 || ctx->word.length /* word{... */
5033 || ctx->word.has_quoted_part /* ""{... */
5034 ) {
5035 syntax_error(NULL);
5036 debug_printf_parse("parse_group return -1: "
5037 "syntax error, groups and arglists don't mix\n");
5038 return -1;
5039 }
5040#endif
5041
Francis Laniel36836fc2023-12-22 22:02:28 +01005042#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01005043 IF_HUSH_FUNCTIONS(skip:)
Francis Laniel36836fc2023-12-22 22:02:28 +01005044#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01005045
5046 endch = '}';
5047 if (ch == '(') {
5048 endch = ')';
Francis Laniel36836fc2023-12-22 22:02:28 +01005049#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01005050 IF_HUSH_FUNCTIONS(if (command->cmd_type != CMD_FUNCDEF))
5051 command->cmd_type = CMD_SUBSHELL;
Francis Laniel36836fc2023-12-22 22:02:28 +01005052#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01005053 } else {
5054 /* bash does not allow "{echo...", requires whitespace */
5055 ch = i_peek(input);
5056 if (ch != ' ' && ch != '\t' && ch != '\n'
5057 && ch != '(' /* but "{(..." is allowed (without whitespace) */
5058 ) {
5059 syntax_error_unexpected_ch(ch);
5060 return -1;
5061 }
5062 if (ch != '(') {
5063 ch = i_getch(input);
5064 nommu_addchr(&ctx->as_string, ch);
5065 }
5066 }
5067
5068 debug_printf_heredoc("calling parse_stream, heredoc_cnt:%d\n", heredoc_cnt);
5069 pipe_list = parse_stream(&as_string, &heredoc_cnt, input, endch);
5070 debug_printf_heredoc("parse_stream returned: heredoc_cnt:%d\n", heredoc_cnt);
5071#if !BB_MMU
5072 if (as_string)
5073 o_addstr(&ctx->as_string, as_string);
5074#endif
5075
5076 /* empty ()/{} or parse error? */
5077 if (!pipe_list || pipe_list == ERR_PTR) {
5078 /* parse_stream already emitted error msg */
5079 if (!BB_MMU)
5080 free(as_string);
5081 debug_printf_parse("parse_group return -1: "
5082 "parse_stream returned %p\n", pipe_list);
5083 return -1;
5084 }
5085#if !BB_MMU
5086 as_string[strlen(as_string) - 1] = '\0'; /* plink ')' or '}' */
5087 command->group_as_string = as_string;
5088 debug_printf_parse("end of group, remembering as:'%s'\n",
5089 command->group_as_string);
5090#endif
5091
5092#if ENABLE_HUSH_FUNCTIONS
5093 /* Convert "f() (cmds)" to "f() {(cmds)}" */
5094 if (command->cmd_type == CMD_FUNCDEF && endch == ')') {
5095 struct command *cmd2;
5096
5097 cmd2 = xzalloc(sizeof(*cmd2));
5098 cmd2->cmd_type = CMD_SUBSHELL;
5099 cmd2->group = pipe_list;
5100# if !BB_MMU
5101//UNTESTED!
5102 cmd2->group_as_string = command->group_as_string;
5103 command->group_as_string = xasprintf("(%s)", command->group_as_string);
5104# endif
5105
5106 pipe_list = new_pipe();
5107 pipe_list->cmds = cmd2;
5108 pipe_list->num_cmds = 1;
5109 }
5110#endif
5111
5112 command->group = pipe_list;
5113
5114 debug_printf_parse("parse_group return %d\n", heredoc_cnt);
5115 return heredoc_cnt;
5116 /* command remains "open", available for possible redirects */
5117#undef as_string
5118}
5119
Francis Laniel36836fc2023-12-22 22:02:28 +01005120#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01005121#if ENABLE_HUSH_TICK || ENABLE_FEATURE_SH_MATH || ENABLE_HUSH_DOLLAR_OPS
5122/* Subroutines for copying $(...) and `...` things */
5123/* '...' */
5124static int add_till_single_quote(o_string *dest, struct in_str *input)
5125{
5126 while (1) {
5127 int ch = i_getch(input);
5128 if (ch == EOF) {
5129 syntax_error_unterm_ch('\'');
5130 return 0;
5131 }
5132 if (ch == '\'')
5133 return 1;
5134 o_addchr(dest, ch);
5135 }
5136}
5137static int add_till_single_quote_dquoted(o_string *dest, struct in_str *input)
5138{
5139 while (1) {
5140 int ch = i_getch(input);
5141 if (ch == EOF) {
5142 syntax_error_unterm_ch('\'');
5143 return 0;
5144 }
5145 if (ch == '\'')
5146 return 1;
5147 o_addqchr(dest, ch);
5148 }
5149}
Francis Laniel36836fc2023-12-22 22:02:28 +01005150
Francis Laniel110b7692023-12-22 22:02:27 +01005151/* "...\"...`..`...." - do we need to handle "...$(..)..." too? */
5152static int add_till_backquote(o_string *dest, struct in_str *input, int in_dquote);
5153static int add_till_double_quote(o_string *dest, struct in_str *input)
5154{
5155 while (1) {
5156 int ch = i_getch(input);
5157 if (ch == EOF) {
5158 syntax_error_unterm_ch('"');
5159 return 0;
5160 }
5161 if (ch == '"')
5162 return 1;
5163 if (ch == '\\') { /* \x. Copy both chars. */
5164 o_addchr(dest, ch);
5165 ch = i_getch(input);
5166 }
5167 o_addchr(dest, ch);
5168 if (ch == '`') {
5169 if (!add_till_backquote(dest, input, /*in_dquote:*/ 1))
5170 return 0;
5171 o_addchr(dest, ch);
5172 continue;
5173 }
5174 //if (ch == '$') ...
5175 }
5176}
Francis Laniel36836fc2023-12-22 22:02:28 +01005177
5178
Francis Laniel110b7692023-12-22 22:02:27 +01005179/* Process `cmd` - copy contents until "`" is seen. Complicated by
5180 * \` quoting.
5181 * "Within the backquoted style of command substitution, backslash
5182 * shall retain its literal meaning, except when followed by: '$', '`', or '\'.
5183 * The search for the matching backquote shall be satisfied by the first
5184 * backquote found without a preceding backslash; during this search,
5185 * if a non-escaped backquote is encountered within a shell comment,
5186 * a here-document, an embedded command substitution of the $(command)
5187 * form, or a quoted string, undefined results occur. A single-quoted
5188 * or double-quoted string that begins, but does not end, within the
5189 * "`...`" sequence produces undefined results."
5190 * Example Output
5191 * echo `echo '\'TEST\`echo ZZ\`BEST` \TESTZZBEST
5192 */
5193static int add_till_backquote(o_string *dest, struct in_str *input, int in_dquote)
5194{
5195 while (1) {
5196 int ch = i_getch(input);
5197 if (ch == '`')
5198 return 1;
5199 if (ch == '\\') {
5200 /* \x. Copy both unless it is \`, \$, \\ and maybe \" */
5201 ch = i_getch(input);
5202 if (ch != '`'
5203 && ch != '$'
5204 && ch != '\\'
5205 && (!in_dquote || ch != '"')
5206 ) {
5207 o_addchr(dest, '\\');
5208 }
5209 }
5210 if (ch == EOF) {
5211 syntax_error_unterm_ch('`');
5212 return 0;
5213 }
5214 o_addchr(dest, ch);
5215 }
5216}
5217/* Process $(cmd) - copy contents until ")" is seen. Complicated by
5218 * quoting and nested ()s.
5219 * "With the $(command) style of command substitution, all characters
5220 * following the open parenthesis to the matching closing parenthesis
5221 * constitute the command. Any valid shell script can be used for command,
5222 * except a script consisting solely of redirections which produces
5223 * unspecified results."
5224 * Example Output
5225 * echo $(echo '(TEST)' BEST) (TEST) BEST
5226 * echo $(echo 'TEST)' BEST) TEST) BEST
5227 * echo $(echo \(\(TEST\) BEST) ((TEST) BEST
5228 *
5229 * Also adapted to eat ${var%...} and $((...)) constructs, since ... part
5230 * can contain arbitrary constructs, just like $(cmd).
5231 * In bash compat mode, it needs to also be able to stop on ':' or '/'
5232 * for ${var:N[:M]} and ${var/P[/R]} parsing.
5233 */
5234#define DOUBLE_CLOSE_CHAR_FLAG 0x80
5235static int add_till_closing_bracket(o_string *dest, struct in_str *input, unsigned end_ch)
5236{
5237 int ch;
5238 char dbl = end_ch & DOUBLE_CLOSE_CHAR_FLAG;
5239# if BASH_SUBSTR || BASH_PATTERN_SUBST
5240 char end_char2 = end_ch >> 8;
5241# endif
5242 end_ch &= (DOUBLE_CLOSE_CHAR_FLAG - 1);
5243
5244# if ENABLE_HUSH_INTERACTIVE
5245 G.promptmode = 1; /* PS2 */
5246# endif
5247 debug_printf_prompt("%s promptmode=%d\n", __func__, G.promptmode);
5248
5249 while (1) {
5250 ch = i_getch(input);
5251 if (ch == EOF) {
5252 syntax_error_unterm_ch(end_ch);
5253 return 0;
5254 }
5255 if (ch == end_ch
5256# if BASH_SUBSTR || BASH_PATTERN_SUBST
5257 || ch == end_char2
5258# endif
5259 ) {
5260 if (!dbl)
5261 break;
5262 /* we look for closing )) of $((EXPR)) */
5263 if (i_peek_and_eat_bkslash_nl(input) == end_ch) {
5264 i_getch(input); /* eat second ')' */
5265 break;
5266 }
5267 }
5268 o_addchr(dest, ch);
5269 //bb_error_msg("%s:o_addchr('%c')", __func__, ch);
5270 if (ch == '(' || ch == '{') {
5271 ch = (ch == '(' ? ')' : '}');
5272 if (!add_till_closing_bracket(dest, input, ch))
5273 return 0;
5274 o_addchr(dest, ch);
5275 continue;
5276 }
5277 if (ch == '\'') {
5278 if (!add_till_single_quote(dest, input))
5279 return 0;
5280 o_addchr(dest, ch);
5281 continue;
5282 }
5283 if (ch == '"') {
5284 if (!add_till_double_quote(dest, input))
5285 return 0;
5286 o_addchr(dest, ch);
5287 continue;
5288 }
5289 if (ch == '`') {
5290 if (!add_till_backquote(dest, input, /*in_dquote:*/ 0))
5291 return 0;
5292 o_addchr(dest, ch);
5293 continue;
5294 }
5295 if (ch == '\\') {
5296 /* \x. Copy verbatim. Important for \(, \) */
5297 ch = i_getch(input);
5298 if (ch == EOF) {
5299 syntax_error_unterm_ch(end_ch);
5300 return 0;
5301 }
5302# if 0
5303 if (ch == '\n') {
5304 /* "backslash+newline", ignore both */
5305 o_delchr(dest); /* undo insertion of '\' */
5306 continue;
5307 }
5308# endif
5309 o_addchr(dest, ch);
5310 //bb_error_msg("%s:o_addchr('%c') after '\\'", __func__, ch);
5311 continue;
5312 }
5313 }
5314 debug_printf_parse("%s return '%s' ch:'%c'\n", __func__, dest->data, ch);
5315 return ch;
5316}
5317#endif /* ENABLE_HUSH_TICK || ENABLE_FEATURE_SH_MATH || ENABLE_HUSH_DOLLAR_OPS */
5318
5319#if BASH_DOLLAR_SQUOTE
5320/* Return code: 1 for "found and parsed", 0 for "seen something else" */
5321# if BB_MMU
5322#define parse_dollar_squote(as_string, dest, input) \
5323 parse_dollar_squote(dest, input)
5324#define as_string NULL
5325# endif
5326static int parse_dollar_squote(o_string *as_string, o_string *dest, struct in_str *input)
5327{
5328 int start;
5329 int ch = i_peek_and_eat_bkslash_nl(input); /* first character after the $ */
5330 debug_printf_parse("parse_dollar_squote entered: ch='%c'\n", ch);
5331 if (ch != '\'')
5332 return 0;
5333
5334 dest->has_quoted_part = 1;
5335 start = dest->length;
5336
5337 ch = i_getch(input); /* eat ' */
5338 nommu_addchr(as_string, ch);
5339 while (1) {
5340 ch = i_getch(input);
5341 nommu_addchr(as_string, ch);
5342 if (ch == EOF) {
5343 syntax_error_unterm_ch('\'');
5344 return 0;
5345 }
5346 if (ch == '\'')
5347 break;
5348 if (ch == SPECIAL_VAR_SYMBOL) {
5349 /* Convert raw ^C to corresponding special variable reference */
5350 o_addchr(dest, SPECIAL_VAR_SYMBOL);
5351 o_addchr(dest, SPECIAL_VAR_QUOTED_SVS);
5352 /* will addchr() another SPECIAL_VAR_SYMBOL (see after the if() block) */
5353 } else if (ch == '\\') {
5354 static const char C_escapes[] ALIGN1 = "nrbtfav""x\\01234567";
5355
5356 ch = i_getch(input);
5357 nommu_addchr(as_string, ch);
5358 if (strchr(C_escapes, ch)) {
5359 char buf[4];
5360 char *p = buf;
5361 int cnt = 2;
5362
5363 buf[0] = ch;
5364 if ((unsigned char)(ch - '0') <= 7) { /* \ooo */
5365 do {
5366 ch = i_peek(input);
5367 if ((unsigned char)(ch - '0') > 7)
5368 break;
5369 *++p = ch = i_getch(input);
5370 nommu_addchr(as_string, ch);
5371 } while (--cnt != 0);
5372 } else if (ch == 'x') { /* \xHH */
5373 do {
5374 ch = i_peek(input);
5375 if (!isxdigit(ch))
5376 break;
5377 *++p = ch = i_getch(input);
5378 nommu_addchr(as_string, ch);
5379 } while (--cnt != 0);
5380 if (cnt == 2) { /* \x but next char is "bad" */
5381 ch = 'x';
5382 goto unrecognized;
5383 }
5384 } /* else simple seq like \\ or \t */
5385 *++p = '\0';
5386 p = buf;
5387 ch = bb_process_escape_sequence((void*)&p);
5388 //bb_error_msg("buf:'%s' ch:%x", buf, ch);
5389 if (ch == '\0')
5390 continue; /* bash compat: $'...\0...' emits nothing */
5391 } else { /* unrecognized "\z": encode both chars unless ' or " */
5392 if (ch != '\'' && ch != '"') {
5393 unrecognized:
5394 o_addqchr(dest, '\\');
5395 }
5396 }
5397 } /* if (\...) */
5398 o_addqchr(dest, ch);
5399 }
5400
5401 if (dest->length == start) {
5402 /* $'', $'\0', $'\000\x00' and the like */
5403 o_addchr(dest, SPECIAL_VAR_SYMBOL);
5404 o_addchr(dest, SPECIAL_VAR_SYMBOL);
5405 }
5406
5407 return 1;
5408# undef as_string
5409}
5410#else
Francis Laniele7ca3a32023-12-22 22:02:42 +01005411# define parse_dollar_squote(as_string, dest, input) 0
Francis Laniel110b7692023-12-22 22:02:27 +01005412#endif /* BASH_DOLLAR_SQUOTE */
Francis Laniel36836fc2023-12-22 22:02:28 +01005413#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01005414
5415/* Return code: 0 for OK, 1 for syntax error */
5416#if BB_MMU
5417#define parse_dollar(as_string, dest, input, quote_mask) \
5418 parse_dollar(dest, input, quote_mask)
5419#define as_string NULL
5420#endif
5421static int parse_dollar(o_string *as_string,
5422 o_string *dest,
5423 struct in_str *input, unsigned char quote_mask)
5424{
5425 int ch = i_peek_and_eat_bkslash_nl(input); /* first character after the $ */
5426
5427 debug_printf_parse("parse_dollar entered: ch='%c' quote_mask:0x%x\n", ch, quote_mask);
5428 if (isalpha(ch)) {
5429 make_var:
5430 ch = i_getch(input);
5431 nommu_addchr(as_string, ch);
5432 /*make_var1:*/
5433 o_addchr(dest, SPECIAL_VAR_SYMBOL);
5434 while (1) {
5435 debug_printf_parse(": '%c'\n", ch);
5436 o_addchr(dest, ch | quote_mask);
5437 quote_mask = 0;
5438 ch = i_peek_and_eat_bkslash_nl(input);
5439 if (!isalnum(ch) && ch != '_') {
5440 /* End of variable name reached */
5441 break;
5442 }
5443 ch = i_getch(input);
5444 nommu_addchr(as_string, ch);
5445 }
5446 o_addchr(dest, SPECIAL_VAR_SYMBOL);
5447 } else if (isdigit(ch)) {
5448 make_one_char_var:
5449 ch = i_getch(input);
5450 nommu_addchr(as_string, ch);
5451 o_addchr(dest, SPECIAL_VAR_SYMBOL);
5452 debug_printf_parse(": '%c'\n", ch);
5453 o_addchr(dest, ch | quote_mask);
5454 o_addchr(dest, SPECIAL_VAR_SYMBOL);
5455 } else switch (ch) {
Francis Laniel36836fc2023-12-22 22:02:28 +01005456#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01005457 case '$': /* pid */
5458 case '!': /* last bg pid */
Francis Laniel36836fc2023-12-22 22:02:28 +01005459#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01005460 case '?': /* last exit code */
5461 case '#': /* number of args */
5462 case '*': /* args */
5463 case '@': /* args */
5464 case '-': /* $- option flags set by set builtin or shell options (-i etc) */
5465 goto make_one_char_var;
5466 case '{': {
5467 char len_single_ch;
5468
5469 o_addchr(dest, SPECIAL_VAR_SYMBOL);
5470
5471 ch = i_getch(input); /* eat '{' */
5472 nommu_addchr(as_string, ch);
5473
5474 ch = i_getch_and_eat_bkslash_nl(input); /* first char after '{' */
5475 /* It should be ${?}, or ${#var},
5476 * or even ${?+subst} - operator acting on a special variable,
5477 * or the beginning of variable name.
5478 */
5479 if (ch == EOF
5480 || (!strchr(_SPECIAL_VARS_STR, ch) && !isalnum(ch)) /* not one of those */
5481 ) {
5482 bad_dollar_syntax:
5483 syntax_error_unterm_str("${name}");
5484 debug_printf_parse("parse_dollar return 0: unterminated ${name}\n");
5485 return 0;
5486 }
5487 nommu_addchr(as_string, ch);
5488 len_single_ch = ch;
5489 ch |= quote_mask;
5490
5491 /* It's possible to just call add_till_closing_bracket() at this point.
5492 * However, this regresses some of our testsuite cases
5493 * which check invalid constructs like ${%}.
5494 * Oh well... let's check that the var name part is fine... */
5495
5496 if (isdigit(len_single_ch)
5497 || (len_single_ch == '#' && isdigit(i_peek_and_eat_bkslash_nl(input)))
5498 ) {
5499 /* Execution engine uses plain xatoi_positive()
5500 * to interpret ${NNN} and {#NNN},
5501 * check syntax here in the parser.
5502 * (bash does not support expressions in ${#NN},
5503 * e.g. ${#$var} and {#1:+WORD} are not supported).
5504 */
5505 unsigned cnt = 9; /* max 9 digits for ${NN} and 8 for {#NN} */
5506 while (1) {
5507 o_addchr(dest, ch);
5508 debug_printf_parse(": '%c'\n", ch);
5509 ch = i_getch_and_eat_bkslash_nl(input);
5510 nommu_addchr(as_string, ch);
5511 if (ch == '}')
5512 break;
5513 if (--cnt == 0)
5514 goto bad_dollar_syntax;
5515 if (len_single_ch != '#' && strchr(VAR_SUBST_OPS, ch))
5516 /* ${NN<op>...} is valid */
5517 goto eat_until_closing;
5518 if (!isdigit(ch))
5519 goto bad_dollar_syntax;
5520 }
5521 } else
5522 while (1) {
5523 unsigned pos;
5524
5525 o_addchr(dest, ch);
5526 debug_printf_parse(": '%c'\n", ch);
5527
5528 ch = i_getch(input);
5529 nommu_addchr(as_string, ch);
5530 if (ch == '}')
5531 break;
Francis Lanielbfc406a2023-12-22 22:02:33 +01005532#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01005533 if (!isalnum(ch) && ch != '_') {
Francis Lanielbfc406a2023-12-22 22:02:33 +01005534#else /* __U_BOOT__ */
5535 /*
5536 * In several places in U-Boot, we use variable like
5537 * foo# (e.g. serial#), particularly in env.
5538 * So, we need to authorize # to appear inside
5539 * variable name and then expand this variable.
5540 * NOTE Having # in variable name is not permitted in
5541 * upstream hush but expansion will be done (even though
5542 * the result will be empty).
5543 */
5544 if (!isalnum(ch) && ch != '_' && ch != '#') {
5545#endif /* __U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01005546 unsigned end_ch;
Francis Laniel36836fc2023-12-22 22:02:28 +01005547#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01005548 unsigned char last_ch;
Francis Laniel36836fc2023-12-22 22:02:28 +01005549#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01005550 /* handle parameter expansions
5551 * http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_06_02
5552 */
5553 if (!strchr(VAR_SUBST_OPS, ch)) { /* ${var<bad_char>... */
5554 if (len_single_ch != '#'
5555 /*|| !strchr(SPECIAL_VARS_STR, ch) - disallow errors like ${#+} ? */
5556 || i_peek(input) != '}'
5557 ) {
5558 goto bad_dollar_syntax;
5559 }
5560 /* else: it's "length of C" ${#C} op,
5561 * where C is a single char
5562 * special var name, e.g. ${#!}.
5563 */
5564 }
5565 eat_until_closing:
5566 /* Eat everything until closing '}' (or ':') */
5567 end_ch = '}';
Francis Laniel36836fc2023-12-22 22:02:28 +01005568#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01005569 if (BASH_SUBSTR
5570 && ch == ':'
5571 && !strchr(MINUS_PLUS_EQUAL_QUESTION, i_peek(input))
5572 ) {
5573 /* It's ${var:N[:M]} thing */
5574 end_ch = '}' * 0x100 + ':';
5575 }
5576 if (BASH_PATTERN_SUBST
5577 && ch == '/'
5578 ) {
5579 /* It's ${var/[/]pattern[/repl]} thing */
5580 if (i_peek(input) == '/') { /* ${var//pattern[/repl]}? */
5581 i_getch(input);
5582 nommu_addchr(as_string, '/');
5583 ch = '\\';
5584 }
5585 end_ch = '}' * 0x100 + '/';
5586 }
Francis Laniel36836fc2023-12-22 22:02:28 +01005587#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01005588 o_addchr(dest, ch);
5589 /* The pattern can't be empty.
5590 * IOW: if the first char after "${v//" is a slash,
5591 * it does not terminate the pattern - it's the first char of the pattern:
5592 * v=/dev/ram; echo ${v////-} prints -dev-ram (pattern is "/")
5593 * v=/dev/ram; echo ${v///r/-} prints /dev-am (pattern is "/r")
5594 */
5595 if (i_peek(input) == '/') {
5596 o_addchr(dest, i_getch(input));
5597 }
Francis Laniel36836fc2023-12-22 22:02:28 +01005598#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01005599 again:
Francis Laniel36836fc2023-12-22 22:02:28 +01005600#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01005601 if (!BB_MMU)
5602 pos = dest->length;
5603#if ENABLE_HUSH_DOLLAR_OPS
Francis Laniel36836fc2023-12-22 22:02:28 +01005604#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01005605 last_ch = add_till_closing_bracket(dest, input, end_ch);
5606 if (last_ch == 0) /* error? */
5607 return 0;
Francis Laniel36836fc2023-12-22 22:02:28 +01005608#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01005609#else
5610# error Simple code to only allow ${var} is not implemented
5611#endif
5612 if (as_string) {
5613 o_addstr(as_string, dest->data + pos);
Francis Laniel36836fc2023-12-22 22:02:28 +01005614#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01005615 o_addchr(as_string, last_ch);
Francis Laniel36836fc2023-12-22 22:02:28 +01005616#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01005617 }
5618
Francis Laniel36836fc2023-12-22 22:02:28 +01005619#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01005620 if ((BASH_SUBSTR || BASH_PATTERN_SUBST)
5621 && (end_ch & 0xff00)
5622 ) {
5623 /* close the first block: */
5624 o_addchr(dest, SPECIAL_VAR_SYMBOL);
5625 /* while parsing N from ${var:N[:M]}
5626 * or pattern from ${var/[/]pattern[/repl]} */
5627 if ((end_ch & 0xff) == last_ch) {
5628 /* got ':' or '/'- parse the rest */
5629 end_ch = '}';
5630 goto again;
5631 }
5632 /* got '}' */
5633 if (BASH_SUBSTR && end_ch == '}' * 0x100 + ':') {
5634 /* it's ${var:N} - emulate :999999999 */
5635 o_addstr(dest, "999999999");
5636 } /* else: it's ${var/[/]pattern} */
5637 }
Francis Laniel36836fc2023-12-22 22:02:28 +01005638#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01005639 break;
5640 }
5641 len_single_ch = 0; /* it can't be ${#C} op */
5642 }
5643 o_addchr(dest, SPECIAL_VAR_SYMBOL);
5644 break;
5645 }
5646#if ENABLE_FEATURE_SH_MATH || ENABLE_HUSH_TICK
5647 case '(': {
5648 unsigned pos;
5649
5650 ch = i_getch(input);
5651 nommu_addchr(as_string, ch);
5652# if ENABLE_FEATURE_SH_MATH
5653 if (i_peek_and_eat_bkslash_nl(input) == '(') {
5654 ch = i_getch(input);
5655 nommu_addchr(as_string, ch);
5656 o_addchr(dest, SPECIAL_VAR_SYMBOL);
5657 o_addchr(dest, quote_mask | '+');
5658 if (!BB_MMU)
5659 pos = dest->length;
5660 if (!add_till_closing_bracket(dest, input, ')' | DOUBLE_CLOSE_CHAR_FLAG))
5661 return 0; /* error */
5662 if (as_string) {
5663 o_addstr(as_string, dest->data + pos);
5664 o_addchr(as_string, ')');
5665 o_addchr(as_string, ')');
5666 }
5667 o_addchr(dest, SPECIAL_VAR_SYMBOL);
5668 break;
5669 }
5670# endif
5671# if ENABLE_HUSH_TICK
5672 o_addchr(dest, SPECIAL_VAR_SYMBOL);
5673 o_addchr(dest, quote_mask | '`');
5674 if (!BB_MMU)
5675 pos = dest->length;
5676 if (!add_till_closing_bracket(dest, input, ')'))
5677 return 0; /* error */
5678 if (as_string) {
5679 o_addstr(as_string, dest->data + pos);
5680 o_addchr(as_string, ')');
5681 }
5682 o_addchr(dest, SPECIAL_VAR_SYMBOL);
5683# endif
5684 break;
5685 }
5686#endif
5687 case '_':
5688 goto make_var;
5689#if 0
5690 /* TODO: $_: */
5691 /* $_ Shell or shell script name; or last argument of last command
5692 * (if last command wasn't a pipe; if it was, bash sets $_ to "");
5693 * but in command's env, set to full pathname used to invoke it */
5694 ch = i_getch(input);
5695 nommu_addchr(as_string, ch);
5696 ch = i_peek_and_eat_bkslash_nl(input);
5697 if (isalnum(ch)) { /* it's $_name or $_123 */
5698 ch = '_';
5699 goto make_var1;
5700 }
5701 /* else: it's $_ */
5702#endif
5703 default:
5704 o_addQchr(dest, '$');
5705 }
5706 debug_printf_parse("parse_dollar return 1 (ok)\n");
5707 return 1;
5708#undef as_string
5709}
5710
5711#if BB_MMU
5712#define encode_string(as_string, dest, input, dquote_end) \
5713 encode_string(dest, input, dquote_end)
5714#define as_string NULL
5715#endif
5716static int encode_string(o_string *as_string,
5717 o_string *dest,
5718 struct in_str *input,
5719 int dquote_end)
5720{
5721 int ch;
5722 int next;
5723
5724 again:
5725 ch = i_getch(input);
5726 if (ch != EOF)
5727 nommu_addchr(as_string, ch);
5728 if (ch == dquote_end) { /* may be only '"' or EOF */
5729 debug_printf_parse("encode_string return 1 (ok)\n");
5730 return 1;
5731 }
5732 /* note: can't move it above ch == dquote_end check! */
5733 if (ch == EOF) {
5734 syntax_error_unterm_ch('"');
5735 return 0; /* error */
5736 }
5737 next = '\0';
5738 if (ch != '\n') {
5739 next = i_peek(input);
5740 }
5741 debug_printf_parse("\" ch=%c (%d) escape=%d\n",
5742 ch, ch, !!(dest->o_expflags & EXP_FLAG_ESC_GLOB_CHARS));
5743 if (ch == '\\') {
5744 if (next == EOF) {
5745 /* Testcase: in interactive shell a file with
5746 * echo "unterminated string\<eof>
5747 * is sourced.
5748 */
5749 syntax_error_unterm_ch('"');
5750 return 0; /* error */
5751 }
5752 /* bash:
5753 * "The backslash retains its special meaning [in "..."]
5754 * only when followed by one of the following characters:
5755 * $, `, ", \, or <newline>. A double quote may be quoted
5756 * within double quotes by preceding it with a backslash."
5757 * NB: in (unquoted) heredoc, above does not apply to ",
5758 * therefore we check for it by "next == dquote_end" cond.
5759 */
5760 if (next == dquote_end || strchr("$`\\\n", next)) {
5761 ch = i_getch(input); /* eat next */
5762 if (ch == '\n')
5763 goto again; /* skip \<newline> */
5764 } /* else: ch remains == '\\', and we double it below: */
5765 o_addqchr(dest, ch); /* \c if c is a glob char, else just c */
5766 nommu_addchr(as_string, ch);
5767 goto again;
5768 }
5769 if (ch == '$') {
5770 //if (parse_dollar_squote(as_string, dest, input))
5771 // goto again;
5772 if (!parse_dollar(as_string, dest, input, /*quote_mask:*/ 0x80)) {
5773 debug_printf_parse("encode_string return 0: "
5774 "parse_dollar returned 0 (error)\n");
5775 return 0;
5776 }
5777 goto again;
5778 }
5779#if ENABLE_HUSH_TICK
5780 if (ch == '`') {
5781 //unsigned pos = dest->length;
5782 o_addchr(dest, SPECIAL_VAR_SYMBOL);
5783 o_addchr(dest, 0x80 | '`');
5784 if (!add_till_backquote(dest, input, /*in_dquote:*/ dquote_end == '"'))
5785 return 0; /* error */
5786 o_addchr(dest, SPECIAL_VAR_SYMBOL);
5787 //debug_printf_subst("SUBST RES3 '%s'\n", dest->data + pos);
5788 goto again;
5789 }
5790#endif
5791 o_addQchr(dest, ch);
5792 if (ch == SPECIAL_VAR_SYMBOL) {
5793 /* Convert "^C" to corresponding special variable reference */
5794 o_addchr(dest, SPECIAL_VAR_QUOTED_SVS);
5795 o_addchr(dest, SPECIAL_VAR_SYMBOL);
5796 }
5797 goto again;
5798#undef as_string
5799}
5800
5801/*
5802 * Scan input until EOF or end_trigger char.
5803 * Return a list of pipes to execute, or NULL on EOF
5804 * or if end_trigger character is met.
5805 * On syntax error, exit if shell is not interactive,
5806 * reset parsing machinery and start parsing anew,
5807 * or return ERR_PTR.
5808 */
5809static struct pipe *parse_stream(char **pstring,
5810 int *heredoc_cnt_ptr,
5811 struct in_str *input,
5812 int end_trigger)
5813{
5814 struct parse_context ctx;
5815 int heredoc_cnt;
5816
5817 /* Single-quote triggers a bypass of the main loop until its mate is
5818 * found. When recursing, quote state is passed in via ctx.word.o_expflags.
5819 */
5820 debug_printf_parse("parse_stream entered, end_trigger='%c'\n",
5821 end_trigger ? end_trigger : 'X');
5822 debug_enter();
5823
5824 initialize_context(&ctx);
5825
5826 /* If very first arg is "" or '', ctx.word.data may end up NULL.
5827 * Preventing this:
5828 */
5829 ctx.word.data = xzalloc(1); /* start as "", not as NULL */
5830
5831 /* We used to separate words on $IFS here. This was wrong.
5832 * $IFS is used only for word splitting when $var is expanded,
5833 * here we should use blank chars as separators, not $IFS
5834 */
5835
5836 heredoc_cnt = 0;
5837 while (1) {
5838 const char *is_blank;
5839 const char *is_special;
5840 int ch;
5841 int next;
Francis Laniel36836fc2023-12-22 22:02:28 +01005842#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01005843 int redir_fd;
5844 redir_type redir_style;
Francis Laniel36836fc2023-12-22 22:02:28 +01005845#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01005846
5847 ch = i_getch(input);
5848 debug_printf_parse(": ch=%c (%d) escape=%d\n",
5849 ch, ch, !!(ctx.word.o_expflags & EXP_FLAG_ESC_GLOB_CHARS));
5850 if (ch == EOF) {
5851 struct pipe *pi;
5852
5853 if (heredoc_cnt) {
5854 syntax_error_unterm_str("here document");
5855 goto parse_error_exitcode1;
5856 }
5857 if (end_trigger == ')') {
5858 syntax_error_unterm_ch('(');
5859 goto parse_error_exitcode1;
5860 }
5861 if (end_trigger == '}') {
5862 syntax_error_unterm_ch('{');
5863 goto parse_error_exitcode1;
5864 }
5865
5866 if (done_word(&ctx)) {
5867 goto parse_error_exitcode1;
5868 }
5869 o_free_and_set_NULL(&ctx.word);
5870 done_pipe(&ctx, PIPE_SEQ);
5871 pi = ctx.list_head;
5872 /* If we got nothing... */
5873 /* (this makes bare "&" cmd a no-op.
5874 * bash says: "syntax error near unexpected token '&'") */
5875 if (pi->num_cmds == 0
5876 IF_HAS_KEYWORDS(&& pi->res_word == RES_NONE)
5877 ) {
5878 free_pipe_list(pi);
5879 pi = NULL;
5880 }
5881#if !BB_MMU
5882 debug_printf_parse("as_string1 '%s'\n", ctx.as_string.data);
5883 if (pstring)
5884 *pstring = ctx.as_string.data;
5885 else
5886 o_free(&ctx.as_string);
5887#endif
5888 // heredoc_cnt must be 0 here anyway
5889 //if (heredoc_cnt_ptr)
5890 // *heredoc_cnt_ptr = heredoc_cnt;
5891 debug_leave();
5892 debug_printf_heredoc("parse_stream return heredoc_cnt:%d\n", heredoc_cnt);
5893 debug_printf_parse("parse_stream return %p\n", pi);
5894 return pi;
5895 }
5896
5897 /* Handle "'" and "\" first, as they won't play nice with
5898 * i_peek_and_eat_bkslash_nl() anyway:
5899 * echo z\\
5900 * and
5901 * echo '\
5902 * '
5903 * would break.
5904 */
5905 if (ch == '\\') {
5906 ch = i_getch(input);
5907 if (ch == '\n')
5908 continue; /* drop \<newline>, get next char */
5909 nommu_addchr(&ctx.as_string, '\\');
5910 if (ch == SPECIAL_VAR_SYMBOL) {
5911 nommu_addchr(&ctx.as_string, ch);
5912 /* Convert \^C to corresponding special variable reference */
5913 goto case_SPECIAL_VAR_SYMBOL;
5914 }
5915 o_addchr(&ctx.word, '\\');
5916 if (ch == EOF) {
5917 /* Testcase: eval 'echo Ok\' */
5918 /* bash-4.3.43 was removing backslash,
5919 * but 4.4.19 retains it, most other shells too
5920 */
5921 continue; /* get next char */
5922 }
5923 /* Example: echo Hello \2>file
5924 * we need to know that word 2 is quoted
5925 */
5926 ctx.word.has_quoted_part = 1;
5927 nommu_addchr(&ctx.as_string, ch);
5928 o_addchr(&ctx.word, ch);
5929 continue; /* get next char */
5930 }
5931 nommu_addchr(&ctx.as_string, ch);
5932 if (ch == '\'') {
5933 ctx.word.has_quoted_part = 1;
5934 next = i_getch(input);
Francis Laniel36836fc2023-12-22 22:02:28 +01005935#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01005936 if (next == '\'' && !ctx.pending_redirect)
5937 goto insert_empty_quoted_str_marker;
Francis Laniel36836fc2023-12-22 22:02:28 +01005938#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01005939
5940 ch = next;
5941 while (1) {
5942 if (ch == EOF) {
5943 syntax_error_unterm_ch('\'');
5944 goto parse_error_exitcode1;
5945 }
5946 nommu_addchr(&ctx.as_string, ch);
5947 if (ch == '\'')
5948 break;
5949 if (ch == SPECIAL_VAR_SYMBOL) {
5950 /* Convert raw ^C to corresponding special variable reference */
5951 o_addchr(&ctx.word, SPECIAL_VAR_SYMBOL);
5952 o_addchr(&ctx.word, SPECIAL_VAR_QUOTED_SVS);
5953 }
5954 o_addqchr(&ctx.word, ch);
5955 ch = i_getch(input);
5956 }
5957 continue; /* get next char */
5958 }
5959
5960 next = '\0';
5961 if (ch != '\n')
5962 next = i_peek_and_eat_bkslash_nl(input);
5963
5964 is_special = "{}<>&|();#" /* special outside of "str" */
Francis Laniel36836fc2023-12-22 22:02:28 +01005965#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01005966 "$\"" IF_HUSH_TICK("`") /* always special */
Francis Laniel36836fc2023-12-22 22:02:28 +01005967#else /* __U_BOOT__ */
5968 "$\""
5969#endif /* __U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01005970 SPECIAL_VAR_SYMBOL_STR;
5971#if defined(CMD_TEST2_SINGLEWORD_NOGLOB)
5972 if (ctx.command->cmd_type == CMD_TEST2_SINGLEWORD_NOGLOB) {
5973 /* In [[ ]], {}<>&|() are not special */
5974 is_special += 8;
5975 } else
5976#endif
5977 /* Are { and } special here? */
5978 if (ctx.command->argv /* word [word]{... - non-special */
5979 || ctx.word.length /* word{... - non-special */
5980 || ctx.word.has_quoted_part /* ""{... - non-special */
5981 || (next != ';' /* }; - special */
5982 && next != ')' /* }) - special */
5983 && next != '(' /* {( - special */
5984 && next != '&' /* }& and }&& ... - special */
5985 && next != '|' /* }|| ... - special */
5986 && !strchr(defifs, next) /* {word - non-special */
5987 )
5988 ) {
5989 /* They are not special, skip "{}" */
5990 is_special += 2;
5991 }
5992 is_special = strchr(is_special, ch);
5993 is_blank = strchr(defifs, ch);
5994
5995 if (!is_special && !is_blank) { /* ordinary char */
5996 ordinary_char:
5997 o_addQchr(&ctx.word, ch);
5998 if ((ctx.is_assignment == MAYBE_ASSIGNMENT
5999 || ctx.is_assignment == WORD_IS_KEYWORD)
6000 && ch == '='
6001 && endofname(ctx.word.data)[0] == '='
6002 ) {
6003 ctx.is_assignment = DEFINITELY_ASSIGNMENT;
6004 debug_printf_parse("ctx.is_assignment='%s'\n", assignment_flag[ctx.is_assignment]);
6005 }
6006 continue;
6007 }
6008
6009 if (is_blank) {
6010#if ENABLE_HUSH_LINENO_VAR
6011/* Case:
6012 * "while ...; do<whitespace><newline>
6013 * cmd ..."
6014 * would think that "cmd" starts in <whitespace> -
6015 * i.e., at the previous line.
6016 * We need to skip all whitespace before newlines.
6017 */
6018 while (ch != '\n') {
6019 next = i_peek(input);
6020 if (next != ' ' && next != '\t' && next != '\n')
6021 break; /* next char is not ws */
6022 ch = i_getch(input);
6023 }
6024 /* ch == last eaten whitespace char */
6025#endif
6026 if (done_word(&ctx)) {
6027 goto parse_error_exitcode1;
6028 }
6029 if (ch == '\n') {
6030 /* Is this a case when newline is simply ignored?
6031 * Some examples:
6032 * "cmd | <newline> cmd ..."
6033 * "case ... in <newline> word) ..."
6034 */
6035 if (IS_NULL_CMD(ctx.command)
6036 && ctx.word.length == 0
6037 && !ctx.word.has_quoted_part
6038 && heredoc_cnt == 0
6039 ) {
6040 /* This newline can be ignored. But...
6041 * Without check #1, interactive shell
6042 * ignores even bare <newline>,
6043 * and shows the continuation prompt:
6044 * ps1_prompt$ <enter>
6045 * ps2> _ <=== wrong, should be ps1
6046 * Without check #2, "cmd & <newline>"
6047 * is similarly mistreated.
6048 * (BTW, this makes "cmd & cmd"
6049 * and "cmd && cmd" non-orthogonal.
6050 * Really, ask yourself, why
6051 * "cmd && <newline>" doesn't start
6052 * cmd but waits for more input?
6053 * The only reason is that it might be
6054 * a "cmd1 && <nl> cmd2 &" construct,
6055 * cmd1 may need to run in BG).
6056 */
6057 struct pipe *pi = ctx.list_head;
6058 if (pi->num_cmds != 0 /* check #1 */
6059 && pi->followup != PIPE_BG /* check #2 */
6060 ) {
6061 continue;
6062 }
6063 }
6064 /* Treat newline as a command separator. */
6065 done_pipe(&ctx, PIPE_SEQ);
6066 debug_printf_heredoc("heredoc_cnt:%d\n", heredoc_cnt);
6067 if (heredoc_cnt) {
6068 heredoc_cnt = fetch_heredocs(&ctx.as_string, ctx.list_head, heredoc_cnt, input);
6069 if (heredoc_cnt != 0)
6070 goto parse_error_exitcode1;
6071 }
6072 ctx.is_assignment = MAYBE_ASSIGNMENT;
6073 debug_printf_parse("ctx.is_assignment='%s'\n", assignment_flag[ctx.is_assignment]);
6074 ch = ';';
6075 /* note: if (is_blank) continue;
6076 * will still trigger for us */
6077 }
6078 }
6079
6080 /* "cmd}" or "cmd }..." without semicolon or &:
6081 * } is an ordinary char in this case, even inside { cmd; }
6082 * Pathological example: { ""}; } should exec "}" cmd
6083 */
Francis Laniel36836fc2023-12-22 22:02:28 +01006084#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01006085 if (ch == '}') {
Francis Laniel36836fc2023-12-22 22:02:28 +01006086#else /* __U_BOOT__ */
6087 if (ch == '}' || ch == ')') {
6088#endif /* __U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01006089 if (ctx.word.length != 0 /* word} */
6090 || ctx.word.has_quoted_part /* ""} */
6091 ) {
6092 goto ordinary_char;
6093 }
6094 if (!IS_NULL_CMD(ctx.command)) { /* cmd } */
6095 /* Generally, there should be semicolon: "cmd; }"
6096 * However, bash allows to omit it if "cmd" is
6097 * a group. Examples:
6098 * { { echo 1; } }
6099 * {(echo 1)}
6100 * { echo 0 >&2 | { echo 1; } }
6101 * { while false; do :; done }
6102 * { case a in b) ;; esac }
6103 */
6104 if (ctx.command->group)
6105 goto term_group;
6106 goto ordinary_char;
6107 }
6108 if (!IS_NULL_PIPE(ctx.pipe)) /* cmd | } */
6109 /* Can't be an end of {cmd}, skip the check */
6110 goto skip_end_trigger;
6111 /* else: } does terminate a group */
6112 }
6113 term_group:
6114 if (end_trigger && end_trigger == ch
6115 && (ch != ';' || heredoc_cnt == 0)
6116#if ENABLE_HUSH_CASE
6117 && (ch != ')'
6118 || ctx.ctx_res_w != RES_MATCH
6119 || (!ctx.word.has_quoted_part && strcmp(ctx.word.data, "esac") == 0)
6120 )
6121#endif
6122 ) {
6123 if (done_word(&ctx)) {
6124 goto parse_error_exitcode1;
6125 }
6126 done_pipe(&ctx, PIPE_SEQ);
6127 ctx.is_assignment = MAYBE_ASSIGNMENT;
6128 debug_printf_parse("ctx.is_assignment='%s'\n", assignment_flag[ctx.is_assignment]);
6129 /* Do we sit outside of any if's, loops or case's? */
6130 if (!HAS_KEYWORDS
6131 IF_HAS_KEYWORDS(|| (ctx.ctx_res_w == RES_NONE && ctx.old_flag == 0))
6132 ) {
6133 o_free_and_set_NULL(&ctx.word);
6134#if !BB_MMU
6135 debug_printf_parse("as_string2 '%s'\n", ctx.as_string.data);
6136 if (pstring)
6137 *pstring = ctx.as_string.data;
6138 else
6139 o_free(&ctx.as_string);
6140#endif
6141 if (ch != ';' && IS_NULL_PIPE(ctx.list_head)) {
6142 /* Example: bare "{ }", "()" */
6143 G.last_exitcode = 2; /* bash compat */
6144 syntax_error_unexpected_ch(ch);
6145 goto parse_error;
6146 }
6147 if (heredoc_cnt_ptr)
6148 *heredoc_cnt_ptr = heredoc_cnt;
6149 debug_printf_heredoc("parse_stream return heredoc_cnt:%d\n", heredoc_cnt);
6150 debug_printf_parse("parse_stream return %p: "
6151 "end_trigger char found\n",
6152 ctx.list_head);
6153 debug_leave();
6154 return ctx.list_head;
6155 }
6156 }
6157
6158 if (is_blank)
6159 continue;
6160
6161 /* Catch <, > before deciding whether this word is
6162 * an assignment. a=1 2>z b=2: b=2 is still assignment */
6163 switch (ch) {
Francis Laniel36836fc2023-12-22 22:02:28 +01006164#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01006165 case '>':
6166 redir_fd = redirect_opt_num(&ctx.word);
6167 if (done_word(&ctx)) {
6168 goto parse_error_exitcode1;
6169 }
6170 redir_style = REDIRECT_OVERWRITE;
6171 if (next == '>') {
6172 redir_style = REDIRECT_APPEND;
6173 ch = i_getch(input);
6174 nommu_addchr(&ctx.as_string, ch);
6175 }
6176#if 0
6177 else if (next == '(') {
6178 syntax_error(">(process) not supported");
6179 goto parse_error_exitcode1;
6180 }
6181#endif
6182 if (parse_redirect(&ctx, redir_fd, redir_style, input))
6183 goto parse_error_exitcode1;
6184 continue; /* get next char */
6185 case '<':
6186 redir_fd = redirect_opt_num(&ctx.word);
6187 if (done_word(&ctx)) {
6188 goto parse_error_exitcode1;
6189 }
6190 redir_style = REDIRECT_INPUT;
6191 if (next == '<') {
6192 redir_style = REDIRECT_HEREDOC;
6193 heredoc_cnt++;
6194 debug_printf_heredoc("++heredoc_cnt=%d\n", heredoc_cnt);
6195 ch = i_getch(input);
6196 nommu_addchr(&ctx.as_string, ch);
6197 } else if (next == '>') {
6198 redir_style = REDIRECT_IO;
6199 ch = i_getch(input);
6200 nommu_addchr(&ctx.as_string, ch);
6201 }
6202#if 0
6203 else if (next == '(') {
6204 syntax_error("<(process) not supported");
6205 goto parse_error_exitcode1;
6206 }
6207#endif
6208 if (parse_redirect(&ctx, redir_fd, redir_style, input))
6209 goto parse_error_exitcode1;
6210 continue; /* get next char */
Francis Lanielaa44c262023-12-22 22:02:38 +01006211#else /* __U_BOOT__ */
6212 /*
6213 * In U-Boot, '<' and '>' can be used in test command to test if
6214 * a string is, alphabetically, before or after another.
6215 * In 2021 Busybox hush, we will keep the same behavior and so not treat
6216 * them as redirection operator.
6217 *
6218 * Indeed, in U-Boot, tests are handled by the test command and not by the
6219 * shell code.
6220 * So, better to give this character as input to test command.
6221 *
6222 * NOTE In my opinion, when you use '<' or '>' I am almost sure
6223 * you wanted to use "-gt" or "-lt" in place, so thinking to
6224 * escape these will make you should check your code (sh syntax
6225 * at this level is, for me, error prone).
6226 */
6227 case '>':
6228 fallthrough;
6229 case '<':
6230 o_addQchr(&ctx.word, ch);
6231 continue;
6232#endif /* __U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01006233 case '#':
6234 if (ctx.word.length == 0 && !ctx.word.has_quoted_part) {
6235 /* skip "#comment" */
6236 /* note: we do not add it to &ctx.as_string */
6237/* TODO: in bash:
6238 * comment inside $() goes to the next \n, even inside quoted string (!):
6239 * cmd "$(cmd2 #comment)" - syntax error
6240 * cmd "`cmd2 #comment`" - ok
6241 * We accept both (comment ends where command subst ends, in both cases).
6242 */
6243 while (1) {
6244 ch = i_peek(input);
6245 if (ch == '\n') {
6246 nommu_addchr(&ctx.as_string, '\n');
6247 break;
6248 }
6249 ch = i_getch(input);
6250 if (ch == EOF)
6251 break;
6252 }
6253 continue; /* get next char */
6254 }
6255 break;
6256 }
6257 skip_end_trigger:
6258
6259 if (ctx.is_assignment == MAYBE_ASSIGNMENT
Francis Laniel36836fc2023-12-22 22:02:28 +01006260#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01006261 /* check that we are not in word in "a=1 2>word b=1": */
6262 && !ctx.pending_redirect
Francis Laniel36836fc2023-12-22 22:02:28 +01006263#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01006264 ) {
6265 /* ch is a special char and thus this word
6266 * cannot be an assignment */
6267 ctx.is_assignment = NOT_ASSIGNMENT;
6268 debug_printf_parse("ctx.is_assignment='%s'\n", assignment_flag[ctx.is_assignment]);
6269 }
6270
6271 /* Note: nommu_addchr(&ctx.as_string, ch) is already done */
6272
6273 switch (ch) {
6274 case_SPECIAL_VAR_SYMBOL:
6275 case SPECIAL_VAR_SYMBOL:
6276 /* Convert raw ^C to corresponding special variable reference */
6277 o_addchr(&ctx.word, SPECIAL_VAR_SYMBOL);
6278 o_addchr(&ctx.word, SPECIAL_VAR_QUOTED_SVS);
6279 /* fall through */
6280 case '#':
6281 /* non-comment #: "echo a#b" etc */
6282 o_addchr(&ctx.word, ch);
6283 continue; /* get next char */
6284 case '$':
Francis Laniel36836fc2023-12-22 22:02:28 +01006285#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01006286 if (parse_dollar_squote(&ctx.as_string, &ctx.word, input))
6287 continue; /* get next char */
Francis Laniel36836fc2023-12-22 22:02:28 +01006288#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01006289 if (!parse_dollar(&ctx.as_string, &ctx.word, input, /*quote_mask:*/ 0)) {
6290 debug_printf_parse("parse_stream parse error: "
6291 "parse_dollar returned 0 (error)\n");
6292 goto parse_error_exitcode1;
6293 }
6294 continue; /* get next char */
6295 case '"':
6296 ctx.word.has_quoted_part = 1;
Francis Laniel36836fc2023-12-22 22:02:28 +01006297#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01006298 if (next == '"' && !ctx.pending_redirect) {
Francis Laniel36836fc2023-12-22 22:02:28 +01006299#else /* __U_BOOT__ */
6300 if (next == '"') {
6301#endif /* __U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01006302 i_getch(input); /* eat second " */
Francis Laniel36836fc2023-12-22 22:02:28 +01006303#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01006304 insert_empty_quoted_str_marker:
Francis Laniel36836fc2023-12-22 22:02:28 +01006305#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01006306 nommu_addchr(&ctx.as_string, next);
6307 o_addchr(&ctx.word, SPECIAL_VAR_SYMBOL);
6308 o_addchr(&ctx.word, SPECIAL_VAR_SYMBOL);
6309 continue; /* get next char */
6310 }
6311 if (ctx.is_assignment == NOT_ASSIGNMENT)
6312 ctx.word.o_expflags |= EXP_FLAG_ESC_GLOB_CHARS;
6313 if (!encode_string(&ctx.as_string, &ctx.word, input, '"'))
6314 goto parse_error_exitcode1;
6315 ctx.word.o_expflags &= ~EXP_FLAG_ESC_GLOB_CHARS;
6316 continue; /* get next char */
6317#if ENABLE_HUSH_TICK
6318 case '`': {
6319 USE_FOR_NOMMU(unsigned pos;)
6320
6321 o_addchr(&ctx.word, SPECIAL_VAR_SYMBOL);
6322 o_addchr(&ctx.word, '`');
6323 USE_FOR_NOMMU(pos = ctx.word.length;)
6324 if (!add_till_backquote(&ctx.word, input, /*in_dquote:*/ 0))
6325 goto parse_error_exitcode1;
6326# if !BB_MMU
6327 o_addstr(&ctx.as_string, ctx.word.data + pos);
6328 o_addchr(&ctx.as_string, '`');
6329# endif
6330 o_addchr(&ctx.word, SPECIAL_VAR_SYMBOL);
6331 //debug_printf_subst("SUBST RES3 '%s'\n", ctx.word.data + pos);
6332 continue; /* get next char */
6333 }
6334#endif
6335 case ';':
6336#if ENABLE_HUSH_CASE
6337 case_semi:
6338#endif
6339 if (done_word(&ctx)) {
6340 goto parse_error_exitcode1;
6341 }
6342 done_pipe(&ctx, PIPE_SEQ);
6343#if ENABLE_HUSH_CASE
6344 /* Eat multiple semicolons, detect
6345 * whether it means something special */
6346 while (1) {
6347 ch = i_peek_and_eat_bkslash_nl(input);
6348 if (ch != ';')
6349 break;
6350 ch = i_getch(input);
6351 nommu_addchr(&ctx.as_string, ch);
6352 if (ctx.ctx_res_w == RES_CASE_BODY) {
6353 ctx.ctx_dsemicolon = 1;
6354 ctx.ctx_res_w = RES_MATCH;
6355 break;
6356 }
6357 }
6358#endif
6359 new_cmd:
6360 /* We just finished a cmd. New one may start
6361 * with an assignment */
6362 ctx.is_assignment = MAYBE_ASSIGNMENT;
6363 debug_printf_parse("ctx.is_assignment='%s'\n", assignment_flag[ctx.is_assignment]);
6364 continue; /* get next char */
6365 case '&':
6366 if (done_word(&ctx)) {
6367 goto parse_error_exitcode1;
6368 }
6369 if (next == '&') {
6370 ch = i_getch(input);
6371 nommu_addchr(&ctx.as_string, ch);
6372 done_pipe(&ctx, PIPE_AND);
6373 } else {
6374 done_pipe(&ctx, PIPE_BG);
6375 }
6376 goto new_cmd;
6377 case '|':
6378 if (done_word(&ctx)) {
6379 goto parse_error_exitcode1;
6380 }
6381#if ENABLE_HUSH_CASE
6382 if (ctx.ctx_res_w == RES_MATCH)
6383 break; /* we are in case's "word | word)" */
6384#endif
6385 if (next == '|') { /* || */
6386 ch = i_getch(input);
6387 nommu_addchr(&ctx.as_string, ch);
6388 done_pipe(&ctx, PIPE_OR);
6389 } else {
6390 /* we could pick up a file descriptor choice here
6391 * with redirect_opt_num(), but bash doesn't do it.
6392 * "echo foo 2| cat" yields "foo 2". */
6393 done_command(&ctx);
6394 }
6395 goto new_cmd;
6396 case '(':
6397#if ENABLE_HUSH_CASE
6398 /* "case... in [(]word)..." - skip '(' */
6399 if (ctx.ctx_res_w == RES_MATCH
6400 && ctx.command->argv == NULL /* not (word|(... */
6401 && ctx.word.length == 0 /* not word(... */
6402 && ctx.word.has_quoted_part == 0 /* not ""(... */
6403 ) {
6404 continue; /* get next char */
6405 }
6406#endif
6407 /* fall through */
6408 case '{': {
6409 int n = parse_group(&ctx, input, ch);
6410 if (n < 0) {
6411 goto parse_error_exitcode1;
6412 }
6413 debug_printf_heredoc("parse_group done, needs heredocs:%d\n", n);
6414 heredoc_cnt += n;
6415 goto new_cmd;
6416 }
6417 case ')':
6418#if ENABLE_HUSH_CASE
6419 if (ctx.ctx_res_w == RES_MATCH)
6420 goto case_semi;
6421#endif
6422 case '}':
6423 /* proper use of this character is caught by end_trigger:
6424 * if we see {, we call parse_group(..., end_trigger='}')
6425 * and it will match } earlier (not here). */
6426 G.last_exitcode = 2;
6427 syntax_error_unexpected_ch(ch);
6428 goto parse_error;
6429 default:
6430 if (HUSH_DEBUG)
6431 bb_error_msg_and_die("BUG: unexpected %c", ch);
6432 }
6433 } /* while (1) */
6434
6435 parse_error_exitcode1:
6436 G.last_exitcode = 1;
6437 parse_error:
6438 {
6439 struct parse_context *pctx;
6440 IF_HAS_KEYWORDS(struct parse_context *p2;)
6441
6442 /* Clean up allocated tree.
6443 * Sample for finding leaks on syntax error recovery path.
6444 * Run it from interactive shell, watch pmap `pidof hush`.
6445 * while if false; then false; fi; do break; fi
6446 * Samples to catch leaks at execution:
6447 * while if (true | { true;}); then echo ok; fi; do break; done
6448 * while if (true | { true;}); then echo ok; fi; do (if echo ok; break; then :; fi) | cat; break; done
6449 */
6450 pctx = &ctx;
6451 do {
6452 /* Update pipe/command counts,
6453 * otherwise freeing may miss some */
6454 done_pipe(pctx, PIPE_SEQ);
6455 debug_printf_clean("freeing list %p from ctx %p\n",
6456 pctx->list_head, pctx);
6457 debug_print_tree(pctx->list_head, 0);
6458 free_pipe_list(pctx->list_head);
6459 debug_printf_clean("freed list %p\n", pctx->list_head);
6460#if !BB_MMU
6461 o_free(&pctx->as_string);
6462#endif
6463 IF_HAS_KEYWORDS(p2 = pctx->stack;)
6464 if (pctx != &ctx) {
6465 free(pctx);
6466 }
6467 IF_HAS_KEYWORDS(pctx = p2;)
6468 } while (HAS_KEYWORDS && pctx);
6469
6470 o_free(&ctx.word);
6471#if !BB_MMU
6472 if (pstring)
6473 *pstring = NULL;
6474#endif
6475 debug_leave();
6476 return ERR_PTR;
6477 }
6478}
6479
6480
6481/*** Execution routines ***/
6482
6483/* Expansion can recurse, need forward decls: */
6484#if !BASH_PATTERN_SUBST && !ENABLE_HUSH_CASE
6485#define expand_string_to_string(str, EXP_flags, do_unbackslash) \
6486 expand_string_to_string(str)
6487#endif
6488static char *expand_string_to_string(const char *str, int EXP_flags, int do_unbackslash);
6489#if ENABLE_HUSH_TICK
6490static int process_command_subs(o_string *dest, const char *s);
6491#endif
6492static int expand_vars_to_list(o_string *output, int n, char *arg);
6493
6494/* expand_strvec_to_strvec() takes a list of strings, expands
6495 * all variable references within and returns a pointer to
6496 * a list of expanded strings, possibly with larger number
6497 * of strings. (Think VAR="a b"; echo $VAR).
6498 * This new list is allocated as a single malloc block.
6499 * NULL-terminated list of char* pointers is at the beginning of it,
6500 * followed by strings themselves.
6501 * Caller can deallocate entire list by single free(list). */
6502
6503/* A horde of its helpers come first: */
6504
6505static void o_addblock_duplicate_backslash(o_string *o, const char *str, int len)
6506{
6507 while (--len >= 0) {
6508 char c = *str++;
6509
6510#if ENABLE_HUSH_BRACE_EXPANSION
6511 if (c == '{' || c == '}') {
6512 /* { -> \{, } -> \} */
6513 o_addchr(o, '\\');
6514 /* And now we want to add { or } and continue:
6515 * o_addchr(o, c);
6516 * continue;
6517 * luckily, just falling through achieves this.
6518 */
6519 }
6520#endif
6521 o_addchr(o, c);
6522 if (c == '\\') {
6523 /* \z -> \\\z; \<eol> -> \\<eol> */
6524 o_addchr(o, '\\');
6525 if (len) {
6526 len--;
6527 o_addchr(o, '\\');
6528 o_addchr(o, *str++);
6529 }
6530 }
6531 }
6532}
6533
6534/* Store given string, finalizing the word and starting new one whenever
6535 * we encounter IFS char(s). This is used for expanding variable values.
6536 * End-of-string does NOT finalize word: think about 'echo -$VAR-'.
6537 * Return in output->ended_in_ifs:
6538 * 1 - ended with IFS char, else 0 (this includes case of empty str).
6539 */
6540static int expand_on_ifs(o_string *output, int n, const char *str)
6541{
6542 int last_is_ifs = 0;
6543
6544 while (1) {
6545 int word_len;
6546
6547 if (!*str) /* EOL - do not finalize word */
6548 break;
6549 word_len = strcspn(str, G.ifs);
6550 if (word_len) {
6551 /* We have WORD_LEN leading non-IFS chars */
6552 if (!(output->o_expflags & EXP_FLAG_GLOB)) {
6553 o_addblock(output, str, word_len);
6554 } else {
6555 /* Protect backslashes against globbing up :)
6556 * Example: "v='\*'; echo b$v" prints "b\*"
6557 * (and does not try to glob on "*")
6558 */
6559 o_addblock_duplicate_backslash(output, str, word_len);
6560 /*/ Why can't we do it easier? */
6561 /*o_addblock(output, str, word_len); - WRONG: "v='\*'; echo Z$v" prints "Z*" instead of "Z\*" */
6562 /*o_addqblock(output, str, word_len); - WRONG: "v='*'; echo Z$v" prints "Z*" instead of Z* files */
6563 }
6564 last_is_ifs = 0;
6565 str += word_len;
6566 if (!*str) /* EOL - do not finalize word */
6567 break;
6568 }
6569
6570 /* We know str here points to at least one IFS char */
6571 last_is_ifs = 1;
6572 str += strspn(str, G.ifs_whitespace); /* skip IFS whitespace chars */
6573 if (!*str) /* EOL - do not finalize word */
6574 break;
6575
6576 if (G.ifs_whitespace != G.ifs /* usually false ($IFS is usually all whitespace), */
6577 && strchr(G.ifs, *str) /* the second check would fail */
6578 ) {
6579 /* This is a non-whitespace $IFS char */
6580 /* Skip it and IFS whitespace chars, start new word */
6581 str++;
6582 str += strspn(str, G.ifs_whitespace);
6583 goto new_word;
6584 }
6585
6586 /* Start new word... but not always! */
6587 /* Case "v=' a'; echo ''$v": we do need to finalize empty word: */
6588 if (output->has_quoted_part
6589 /*
6590 * Case "v=' a'; echo $v":
6591 * here nothing precedes the space in $v expansion,
6592 * therefore we should not finish the word
6593 * (IOW: if there *is* word to finalize, only then do it):
6594 * It's okay if this accesses the byte before first argv[]:
6595 * past call to o_save_ptr() cleared it to zero byte
6596 * (grep for -prev-ifs-check-).
6597 */
6598 || output->data[output->length - 1]
6599 ) {
6600 new_word:
6601 o_addchr(output, '\0');
6602 debug_print_list("expand_on_ifs", output, n);
6603 n = o_save_ptr(output, n);
6604 }
6605 }
6606
6607 output->ended_in_ifs = last_is_ifs;
6608 debug_print_list("expand_on_ifs[1]", output, n);
6609 return n;
6610}
6611
Francis Laniel36836fc2023-12-22 22:02:28 +01006612#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01006613/* Helper to expand $((...)) and heredoc body. These act as if
6614 * they are in double quotes, with the exception that they are not :).
6615 * Just the rules are similar: "expand only $var and `cmd`"
6616 *
6617 * Returns malloced string.
6618 * As an optimization, we return NULL if expansion is not needed.
6619 */
6620static char *encode_then_expand_string(const char *str)
6621{
6622 char *exp_str;
6623 struct in_str input;
6624 o_string dest = NULL_O_STRING;
6625 const char *cp;
6626
6627 cp = str;
6628 for (;;) {
6629 if (!*cp) return NULL; /* string has no special chars */
6630 if (*cp == '$') break;
6631 if (*cp == '\\') break;
6632#if ENABLE_HUSH_TICK
6633 if (*cp == '`') break;
6634#endif
6635 cp++;
6636 }
6637
6638 /* We need to expand. Example:
6639 * echo $(($a + `echo 1`)) $((1 + $((2)) ))
6640 */
6641 setup_string_in_str(&input, str);
6642 encode_string(NULL, &dest, &input, EOF);
6643//TODO: error check (encode_string returns 0 on error)?
6644 //bb_error_msg("'%s' -> '%s'", str, dest.data);
6645 exp_str = expand_string_to_string(dest.data,
6646 EXP_FLAG_ESC_GLOB_CHARS,
6647 /*unbackslash:*/ 1
6648 );
6649 //bb_error_msg("'%s' -> '%s'", dest.data, exp_str);
6650 o_free(&dest);
6651 return exp_str;
6652}
6653
6654static const char *first_special_char_in_vararg(const char *cp)
6655{
6656 for (;;) {
6657 if (!*cp) return NULL; /* string has no special chars */
6658 if (*cp == '$') return cp;
6659 if (*cp == '\\') return cp;
6660 if (*cp == '\'') return cp;
6661 if (*cp == '"') return cp;
6662#if ENABLE_HUSH_TICK
6663 if (*cp == '`') return cp;
6664#endif
6665 /* dquoted "${x:+ARG}" should not glob, therefore
6666 * '*' et al require some non-literal processing: */
6667 if (*cp == '*') return cp;
6668 if (*cp == '?') return cp;
6669 if (*cp == '[') return cp;
6670 cp++;
6671 }
6672}
6673
6674/* Expanding ARG in ${var#ARG}, ${var%ARG}, or ${var/ARG/ARG}.
6675 * These can contain single- and double-quoted strings,
6676 * and treated as if the ARG string is initially unquoted. IOW:
6677 * ${var#ARG} and "${var#ARG}" treat ARG the same (ARG can even be
6678 * a dquoted string: "${var#"zz"}"), the difference only comes later
6679 * (word splitting and globbing of the ${var...} result).
6680 */
6681#if !BASH_PATTERN_SUBST
6682#define encode_then_expand_vararg(str, handle_squotes, do_unbackslash) \
6683 encode_then_expand_vararg(str, handle_squotes)
6684#endif
6685static char *encode_then_expand_vararg(const char *str, int handle_squotes, int do_unbackslash)
6686{
6687#if !BASH_PATTERN_SUBST && ENABLE_HUSH_CASE
6688 const int do_unbackslash = 0;
6689#endif
6690 char *exp_str;
6691 struct in_str input;
6692 o_string dest = NULL_O_STRING;
6693
6694 if (!first_special_char_in_vararg(str)) {
6695 /* string has no special chars */
6696 return NULL;
6697 }
6698
6699 setup_string_in_str(&input, str);
6700 dest.data = xzalloc(1); /* start as "", not as NULL */
6701 exp_str = NULL;
6702
6703 for (;;) {
6704 int ch;
6705
6706 ch = i_getch(&input);
6707 debug_printf_parse("%s: ch=%c (%d) escape=%d\n",
6708 __func__, ch, ch, !!dest.o_expflags);
6709
6710 if (!dest.o_expflags) {
6711 if (ch == EOF)
6712 break;
6713 if (handle_squotes && ch == '\'') {
6714 if (!add_till_single_quote_dquoted(&dest, &input))
6715 goto ret; /* error */
6716 continue;
6717 }
6718 }
6719 if (ch == EOF) {
6720 syntax_error_unterm_ch('"');
6721 goto ret; /* error */
6722 }
6723 if (ch == '"') {
6724 dest.o_expflags ^= EXP_FLAG_ESC_GLOB_CHARS;
6725 continue;
6726 }
6727 if (ch == '\\') {
6728 ch = i_getch(&input);
6729 if (ch == EOF) {
6730//example? error message? syntax_error_unterm_ch('"');
6731 debug_printf_parse("%s: error: \\<eof>\n", __func__);
6732 goto ret;
6733 }
6734 o_addqchr(&dest, ch);
6735 continue;
6736 }
6737 if (ch == '$') {
6738 if (parse_dollar_squote(NULL, &dest, &input))
6739 continue;
6740 if (!parse_dollar(NULL, &dest, &input, /*quote_mask:*/ 0x80)) {
6741 debug_printf_parse("%s: error: parse_dollar returned 0 (error)\n", __func__);
6742 goto ret;
6743 }
6744 continue;
6745 }
6746#if ENABLE_HUSH_TICK
6747 if (ch == '`') {
6748 //unsigned pos = dest->length;
6749 o_addchr(&dest, SPECIAL_VAR_SYMBOL);
6750 o_addchr(&dest, 0x80 | '`');
6751 if (!add_till_backquote(&dest, &input,
6752 /*in_dquote:*/ dest.o_expflags /* nonzero if EXP_FLAG_ESC_GLOB_CHARS set */
6753 )
6754 ) {
6755 goto ret; /* error */
6756 }
6757 o_addchr(&dest, SPECIAL_VAR_SYMBOL);
6758 //debug_printf_subst("SUBST RES3 '%s'\n", dest->data + pos);
6759 continue;
6760 }
6761#endif
6762 o_addQchr(&dest, ch);
6763 } /* for (;;) */
6764
6765 debug_printf_parse("encode: '%s' -> '%s'\n", str, dest.data);
6766 exp_str = expand_string_to_string(dest.data,
6767 do_unbackslash ? EXP_FLAG_ESC_GLOB_CHARS : 0,
6768 do_unbackslash
6769 );
6770 ret:
6771 debug_printf_parse("expand: '%s' -> '%s'\n", dest.data, exp_str);
6772 o_free(&dest);
6773 return exp_str;
6774}
6775
6776/* Expanding ARG in ${var+ARG}, ${var-ARG}
6777 */
Francis Laniele7ca3a32023-12-22 22:02:42 +01006778static NOINLINE int encode_then_append_var_plusminus(o_string *output, int n,
Francis Laniel110b7692023-12-22 22:02:27 +01006779 char *str, int dquoted)
6780{
6781 struct in_str input;
6782 o_string dest = NULL_O_STRING;
6783
6784 if (!first_special_char_in_vararg(str)
6785 && '\0' == str[strcspn(str, G.ifs)]
6786 ) {
6787 /* string has no special chars
6788 * && string has no $IFS chars
6789 */
6790 if (dquoted) {
6791 /* Prints 1 (quoted expansion is a "" word, not nothing):
6792 * set -- "${notexist-}"; echo $#
6793 */
6794 output->has_quoted_part = 1;
6795 }
6796 return expand_vars_to_list(output, n, str);
6797 }
6798
6799 setup_string_in_str(&input, str);
6800
6801 for (;;) {
6802 int ch;
6803
6804 ch = i_getch(&input);
6805 debug_printf_parse("%s: ch=%c (%d) escape=%x\n",
6806 __func__, ch, ch, dest.o_expflags);
6807
6808 if (!dest.o_expflags) {
6809 if (ch == EOF)
6810 break;
Francis Laniele7ca3a32023-12-22 22:02:42 +01006811 if (!dquoted && !(output->o_expflags & EXP_FLAG_SINGLEWORD) && strchr(G.ifs, ch)) {
Francis Laniel110b7692023-12-22 22:02:27 +01006812 /* PREFIX${x:d${e}f ...} and we met space: expand "d${e}f" and start new word.
6813 * do not assume we are at the start of the word (PREFIX above).
6814 */
6815 if (dest.data) {
6816 n = expand_vars_to_list(output, n, dest.data);
6817 o_free_and_set_NULL(&dest);
6818 o_addchr(output, '\0');
6819 n = o_save_ptr(output, n); /* create next word */
6820 } else
6821 if (output->length != o_get_last_ptr(output, n)
6822 || output->has_quoted_part
6823 ) {
6824 /* For these cases:
6825 * f() { for i; do echo "|$i|"; done; }; x=x
6826 * f a${x:+ }b # 1st condition
6827 * |a|
6828 * |b|
6829 * f ""${x:+ }b # 2nd condition
6830 * ||
6831 * |b|
6832 */
6833 o_addchr(output, '\0');
6834 n = o_save_ptr(output, n); /* create next word */
6835 }
6836 continue;
6837 }
6838 if (!dquoted && ch == '\'') {
6839 if (!add_till_single_quote_dquoted(&dest, &input))
6840 goto ret; /* error */
6841 o_addchr(&dest, SPECIAL_VAR_SYMBOL);
6842 o_addchr(&dest, SPECIAL_VAR_SYMBOL);
6843 continue;
6844 }
6845 }
6846 if (ch == EOF) {
6847 syntax_error_unterm_ch('"');
6848 goto ret; /* error */
6849 }
6850 if (ch == '"') {
6851 dest.o_expflags ^= EXP_FLAG_ESC_GLOB_CHARS;
6852 if (dest.o_expflags) {
6853 o_addchr(&dest, SPECIAL_VAR_SYMBOL);
6854 o_addchr(&dest, SPECIAL_VAR_SYMBOL);
6855 }
6856 continue;
6857 }
6858 if (ch == '\\') {
6859 ch = i_getch(&input);
6860 if (ch == EOF) {
6861//example? error message? syntax_error_unterm_ch('"');
6862 debug_printf_parse("%s: error: \\<eof>\n", __func__);
6863 goto ret;
6864 }
6865 o_addqchr(&dest, ch);
6866 continue;
6867 }
6868 if (ch == '$') {
6869 if (!parse_dollar(NULL, &dest, &input, /*quote_mask:*/ (dest.o_expflags || dquoted) ? 0x80 : 0)) {
6870 debug_printf_parse("%s: error: parse_dollar returned 0 (error)\n", __func__);
6871 goto ret;
6872 }
6873 continue;
6874 }
6875#if ENABLE_HUSH_TICK
6876 if (ch == '`') {
6877 //unsigned pos = dest->length;
6878 o_addchr(&dest, SPECIAL_VAR_SYMBOL);
6879 o_addchr(&dest, (dest.o_expflags || dquoted) ? 0x80 | '`' : '`');
6880 if (!add_till_backquote(&dest, &input,
6881 /*in_dquote:*/ dest.o_expflags /* nonzero if EXP_FLAG_ESC_GLOB_CHARS set */
6882 )
6883 ) {
6884 goto ret; /* error */
6885 }
6886 o_addchr(&dest, SPECIAL_VAR_SYMBOL);
6887 //debug_printf_subst("SUBST RES3 '%s'\n", dest->data + pos);
6888 continue;
6889 }
6890#endif
6891 if (dquoted) {
6892 /* Always glob-protect if in dquotes:
6893 * x=x; echo "${x:+/bin/c*}" - prints: /bin/c*
6894 * x=x; echo "${x:+"/bin/c*"}" - prints: /bin/c*
6895 */
6896 o_addqchr(&dest, ch);
6897 } else {
6898 /* Glob-protect only if char is quoted:
6899 * x=x; echo ${x:+/bin/c*} - prints many filenames
6900 * x=x; echo ${x:+"/bin/c*"} - prints: /bin/c*
6901 */
6902 o_addQchr(&dest, ch);
6903 }
6904 } /* for (;;) */
6905
6906 if (dest.data) {
6907 n = expand_vars_to_list(output, n, dest.data);
6908 }
6909 ret:
6910 o_free(&dest);
6911 return n;
6912}
Francis Laniel36836fc2023-12-22 22:02:28 +01006913#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01006914
Francis Laniel36836fc2023-12-22 22:02:28 +01006915#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01006916#if ENABLE_FEATURE_SH_MATH
6917static arith_t expand_and_evaluate_arith(const char *arg, const char **errmsg_p)
6918{
6919 arith_state_t math_state;
6920 arith_t res;
6921 char *exp_str;
6922
6923 math_state.lookupvar = get_local_var_value;
6924 math_state.setvar = set_local_var_from_halves;
6925 //math_state.endofname = endofname;
6926 exp_str = encode_then_expand_string(arg);
6927 res = arith(&math_state, exp_str ? exp_str : arg);
6928 free(exp_str);
6929 if (errmsg_p)
6930 *errmsg_p = math_state.errmsg;
6931 if (math_state.errmsg)
6932 msg_and_die_if_script(math_state.errmsg);
6933 return res;
6934}
6935#endif
Francis Laniel36836fc2023-12-22 22:02:28 +01006936#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01006937
Francis Laniel36836fc2023-12-22 22:02:28 +01006938#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01006939#if BASH_PATTERN_SUBST
6940/* ${var/[/]pattern[/repl]} helpers */
6941static char *strstr_pattern(char *val, const char *pattern, int *size)
6942{
Francis Laniele7ca3a32023-12-22 22:02:42 +01006943 int first_escaped = (pattern[0] == '\\' && pattern[1]);
6944 /* "first_escaped" trick allows to treat e.g. "\*no_glob_chars"
6945 * as literal too (as it is semi-common, and easy to accomodate
6946 * by just using str + 1).
6947 */
6948 int sz = strcspn(pattern + first_escaped * 2, "*?[\\");
6949 if ((pattern + first_escaped * 2)[sz] == '\0') {
Francis Laniel110b7692023-12-22 22:02:27 +01006950 /* Optimization for trivial patterns.
6951 * Testcase for very slow replace (performs about 22k replaces):
6952 * x=::::::::::::::::::::::
6953 * 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}
6954 * echo "${x//:/|}"
6955 */
Francis Laniele7ca3a32023-12-22 22:02:42 +01006956 *size = sz + first_escaped;
6957 return strstr(val, pattern + first_escaped);
Francis Laniel110b7692023-12-22 22:02:27 +01006958 }
6959
6960 while (1) {
6961 char *end = scan_and_match(val, pattern, SCAN_MOVE_FROM_RIGHT + SCAN_MATCH_LEFT_HALF);
6962 debug_printf_varexp("val:'%s' pattern:'%s' end:'%s'\n", val, pattern, end);
6963 if (end) {
6964 *size = end - val;
6965 return val;
6966 }
6967 if (*val == '\0')
6968 return NULL;
6969 /* Optimization: if "*pat" did not match the start of "string",
6970 * we know that "tring", "ring" etc will not match too:
6971 */
6972 if (pattern[0] == '*')
6973 return NULL;
6974 val++;
6975 }
6976}
6977static char *replace_pattern(char *val, const char *pattern, const char *repl, char exp_op)
6978{
6979 char *result = NULL;
6980 unsigned res_len = 0;
6981 unsigned repl_len = strlen(repl);
6982
6983 /* Null pattern never matches, including if "var" is empty */
6984 if (!pattern[0])
6985 return result; /* NULL, no replaces happened */
6986
6987 while (1) {
6988 int size;
6989 char *s = strstr_pattern(val, pattern, &size);
6990 if (!s)
6991 break;
6992
6993 result = xrealloc(result, res_len + (s - val) + repl_len + 1);
6994 strcpy(mempcpy(result + res_len, val, s - val), repl);
6995 res_len += (s - val) + repl_len;
6996 debug_printf_varexp("val:'%s' s:'%s' result:'%s'\n", val, s, result);
6997
6998 val = s + size;
6999 if (exp_op == '/')
7000 break;
7001 }
7002 if (*val && result) {
7003 result = xrealloc(result, res_len + strlen(val) + 1);
7004 strcpy(result + res_len, val);
7005 debug_printf_varexp("val:'%s' result:'%s'\n", val, result);
7006 }
7007 debug_printf_varexp("result:'%s'\n", result);
7008 return result;
7009}
7010#endif /* BASH_PATTERN_SUBST */
Francis Laniel36836fc2023-12-22 22:02:28 +01007011#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01007012
7013static int append_str_maybe_ifs_split(o_string *output, int n,
7014 int first_ch, const char *val)
7015{
7016 if (!(first_ch & 0x80)) { /* unquoted $VAR */
7017 debug_printf_expand("unquoted '%s', output->o_escape:%d\n", val,
7018 !!(output->o_expflags & EXP_FLAG_ESC_GLOB_CHARS));
7019 if (val && val[0])
7020 n = expand_on_ifs(output, n, val);
7021 } else { /* quoted "$VAR" */
7022 output->has_quoted_part = 1;
7023 debug_printf_expand("quoted '%s', output->o_escape:%d\n", val,
7024 !!(output->o_expflags & EXP_FLAG_ESC_GLOB_CHARS));
7025 if (val && val[0])
7026 o_addQstr(output, val);
7027 }
7028 return n;
7029}
7030
7031/* Handle <SPECIAL_VAR_SYMBOL>varname...<SPECIAL_VAR_SYMBOL> construct.
7032 */
7033static NOINLINE int expand_one_var(o_string *output, int n,
7034 int first_ch, char *arg, char **pp)
7035{
7036 const char *val;
7037 char *to_be_freed;
7038 char *p;
7039 char *var;
7040 char exp_op;
7041 char exp_save = exp_save; /* for compiler */
7042 char *exp_saveptr; /* points to expansion operator */
7043 char *exp_word = exp_word; /* for compiler */
7044 char arg0;
7045
7046 val = NULL;
7047 to_be_freed = NULL;
7048 p = *pp;
7049 *p = '\0'; /* replace trailing SPECIAL_VAR_SYMBOL */
7050 var = arg;
7051 exp_saveptr = arg[1] ? strchr(VAR_ENCODED_SUBST_OPS, arg[1]) : NULL;
7052 arg0 = arg[0];
7053 arg[0] = (arg0 & 0x7f);
7054 exp_op = 0;
7055
7056 if (arg[0] == '#' && arg[1] /* ${#...} but not ${#} */
7057 && (!exp_saveptr /* and ( not(${#<op_char>...}) */
7058 || (arg[2] == '\0' && strchr(SPECIAL_VARS_STR, arg[1])) /* or ${#C} "len of $C" ) */
7059 ) /* NB: skipping ^^^specvar check mishandles ${#::2} */
7060 ) {
7061 /* It must be length operator: ${#var} */
7062 var++;
7063 exp_op = 'L';
7064 } else {
7065 /* Maybe handle parameter expansion */
7066 if (exp_saveptr /* if 2nd char is one of expansion operators */
7067 && strchr(NUMERIC_SPECVARS_STR, arg[0]) /* 1st char is special variable */
7068 ) {
7069 /* ${?:0}, ${#[:]%0} etc */
7070 exp_saveptr = var + 1;
7071 } else {
7072 /* ${?}, ${var}, ${var:0}, ${var[:]%0} etc */
7073 exp_saveptr = var+1 + strcspn(var+1, VAR_ENCODED_SUBST_OPS);
7074 }
7075 exp_op = exp_save = *exp_saveptr;
Francis Laniel36836fc2023-12-22 22:02:28 +01007076#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01007077 if (exp_op) {
7078 exp_word = exp_saveptr + 1;
7079 if (exp_op == ':') {
7080 exp_op = *exp_word++;
7081//TODO: try ${var:} and ${var:bogus} in non-bash config
7082 if (BASH_SUBSTR
7083 && (!exp_op || !strchr(MINUS_PLUS_EQUAL_QUESTION, exp_op))
7084 ) {
7085 /* oops... it's ${var:N[:M]}, not ${var:?xxx} or some such */
7086 exp_op = ':';
7087 exp_word--;
7088 }
7089 }
7090 *exp_saveptr = '\0';
7091 } /* else: it's not an expansion op, but bare ${var} */
Francis Laniel36836fc2023-12-22 22:02:28 +01007092#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01007093 }
7094
7095 /* Look up the variable in question */
7096 if (isdigit(var[0])) {
7097 /* parse_dollar should have vetted var for us */
Francis Laniel36836fc2023-12-22 22:02:28 +01007098#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01007099 int nn = xatoi_positive(var);
Francis Laniel36836fc2023-12-22 22:02:28 +01007100#else /* __U_BOOT__ */
7101 int nn = simple_strtoul(var, NULL, 10);
7102#endif /* __U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01007103 if (nn < G.global_argc)
7104 val = G.global_argv[nn];
7105 /* else val remains NULL: $N with too big N */
7106 } else {
7107 switch (var[0]) {
Francis Laniel36836fc2023-12-22 22:02:28 +01007108#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01007109 case '$': /* pid */
7110 val = utoa(G.root_pid);
7111 break;
7112 case '!': /* bg pid */
7113 val = G.last_bg_pid ? utoa(G.last_bg_pid) : "";
7114 break;
Francis Laniel36836fc2023-12-22 22:02:28 +01007115#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01007116 case '?': /* exitcode */
7117 val = utoa(G.last_exitcode);
7118 break;
7119 case '#': /* argc */
7120 val = utoa(G.global_argc ? G.global_argc-1 : 0);
7121 break;
Francis Laniel36836fc2023-12-22 22:02:28 +01007122#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01007123 case '-': { /* active options */
7124 /* Check set_mode() to see what option chars we support */
7125 char *cp;
7126 val = cp = G.optstring_buf;
7127 if (G.o_opt[OPT_O_ERREXIT])
7128 *cp++ = 'e';
7129 if (G_interactive_fd)
7130 *cp++ = 'i';
7131 if (G_x_mode)
7132 *cp++ = 'x';
7133 /* If G.o_opt[OPT_O_NOEXEC] is true,
7134 * commands read but are not executed,
7135 * so $- can not execute too, 'n' is never seen in $-.
7136 */
7137 if (G.opt_c)
7138 *cp++ = 'c';
7139 if (G.opt_s)
7140 *cp++ = 's';
7141 *cp = '\0';
7142 break;
7143 }
Francis Laniel36836fc2023-12-22 22:02:28 +01007144#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01007145 default:
Francis Lanielbfc406a2023-12-22 22:02:33 +01007146#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01007147 val = get_local_var_value(var);
Francis Lanielbfc406a2023-12-22 22:02:33 +01007148#else /* __U_BOOT__ */
7149 /*
7150 * Environment variable set with setenv* have to be
7151 * expanded.
7152 * So, we first search if the variable exists in
7153 * environment, if this is not the case, we default to
7154 * local value.
7155 */
7156 val = env_get(var);
7157 if (!val)
7158 val = get_local_var_value(var);
7159#endif /* __U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01007160 }
7161 }
7162
Francis Laniel36836fc2023-12-22 22:02:28 +01007163#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01007164 /* Handle any expansions */
7165 if (exp_op == 'L') {
7166 reinit_unicode_for_hush();
7167 debug_printf_expand("expand: length(%s)=", val);
7168 val = utoa(val ? unicode_strlen(val) : 0);
7169 debug_printf_expand("%s\n", val);
7170 } else if (exp_op) {
7171 if (exp_op == '%' || exp_op == '#') {
7172 /* Standard-mandated substring removal ops:
7173 * ${parameter%word} - remove smallest suffix pattern
7174 * ${parameter%%word} - remove largest suffix pattern
7175 * ${parameter#word} - remove smallest prefix pattern
7176 * ${parameter##word} - remove largest prefix pattern
7177 *
7178 * Word is expanded to produce a glob pattern.
7179 * Then var's value is matched to it and matching part removed.
7180 */
7181 /* bash compat: if x is "" and no shrinking of it is possible,
7182 * inner ${...} is not evaluated. Example:
7183 * unset b; : ${a%${b=B}}; echo $b
7184 * assignment b=B only happens if $a is not "".
7185 */
7186 if (val && val[0]) {
7187 char *t;
7188 char *exp_exp_word;
7189 char *loc;
7190 unsigned scan_flags = pick_scan(exp_op, *exp_word);
7191 if (exp_op == *exp_word) /* ## or %% */
7192 exp_word++;
7193 debug_printf_expand("expand: exp_word:'%s'\n", exp_word);
7194 exp_exp_word = encode_then_expand_vararg(exp_word, /*handle_squotes:*/ 1, /*unbackslash:*/ 0);
7195 if (exp_exp_word)
7196 exp_word = exp_exp_word;
7197 debug_printf_expand("expand: exp_word:'%s'\n", exp_word);
7198 /*
7199 * HACK ALERT. We depend here on the fact that
7200 * G.global_argv and results of utoa and get_local_var_value
7201 * are actually in writable memory:
7202 * scan_and_match momentarily stores NULs there.
7203 */
7204 t = (char*)val;
7205 loc = scan_and_match(t, exp_word, scan_flags);
7206 debug_printf_expand("op:%c str:'%s' pat:'%s' res:'%s'\n", exp_op, t, exp_word, loc);
7207 free(exp_exp_word);
7208 if (loc) { /* match was found */
7209 if (scan_flags & SCAN_MATCH_LEFT_HALF) /* #[#] */
7210 val = loc; /* take right part */
7211 else /* %[%] */
7212 val = to_be_freed = xstrndup(val, loc - val); /* left */
7213 }
7214 }
7215 }
7216#if BASH_PATTERN_SUBST
7217 else if (exp_op == '/' || exp_op == '\\') {
7218 /* It's ${var/[/]pattern[/repl]} thing.
7219 * Note that in encoded form it has TWO parts:
7220 * var/pattern<SPECIAL_VAR_SYMBOL>repl<SPECIAL_VAR_SYMBOL>
7221 * and if // is used, it is encoded as \:
7222 * var\pattern<SPECIAL_VAR_SYMBOL>repl<SPECIAL_VAR_SYMBOL>
7223 */
7224 /* bash compat: if var is "", both pattern and repl
7225 * are still evaluated, if it is unset, then not:
7226 * unset b; a=; : ${a/z/${b=3}}; echo $b # b=3
7227 * unset b; unset a; : ${a/z/${b=3}}; echo $b # b not set
7228 */
7229 if (val /*&& val[0]*/) {
7230 /* pattern uses non-standard expansion.
7231 * repl should be unbackslashed and globbed
7232 * by the usual expansion rules:
7233 * >az >bz
7234 * v='a bz'; echo "${v/a*z/a*z}" #prints "a*z"
7235 * v='a bz'; echo "${v/a*z/\z}" #prints "z"
7236 * v='a bz'; echo ${v/a*z/a*z} #prints "az"
7237 * v='a bz'; echo ${v/a*z/\z} #prints "z"
7238 * (note that a*z _pattern_ is never globbed!)
7239 */
7240 char *pattern, *repl, *t;
7241 pattern = encode_then_expand_vararg(exp_word, /*handle_squotes:*/ 1, /*unbackslash:*/ 0);
7242 if (!pattern)
7243 pattern = xstrdup(exp_word);
7244 debug_printf_varexp("pattern:'%s'->'%s'\n", exp_word, pattern);
7245 *p++ = SPECIAL_VAR_SYMBOL;
7246 exp_word = p;
7247 p = strchr(p, SPECIAL_VAR_SYMBOL);
7248 *p = '\0';
7249 repl = encode_then_expand_vararg(exp_word, /*handle_squotes:*/ 1, /*unbackslash:*/ 1);
7250 debug_printf_varexp("repl:'%s'->'%s'\n", exp_word, repl);
7251 /* HACK ALERT. We depend here on the fact that
7252 * G.global_argv and results of utoa and get_local_var_value
7253 * are actually in writable memory:
7254 * replace_pattern momentarily stores NULs there. */
7255 t = (char*)val;
7256 to_be_freed = replace_pattern(t,
7257 pattern,
7258 (repl ? repl : exp_word),
7259 exp_op);
7260 if (to_be_freed) /* at least one replace happened */
7261 val = to_be_freed;
7262 free(pattern);
7263 free(repl);
7264 } else {
7265 /* Unset variable always gives nothing */
7266 // a=; echo ${a/*/w} # "w"
7267 // unset a; echo ${a/*/w} # ""
7268 /* Just skip "replace" part */
7269 *p++ = SPECIAL_VAR_SYMBOL;
7270 p = strchr(p, SPECIAL_VAR_SYMBOL);
7271 *p = '\0';
7272 }
7273 }
7274#endif /* BASH_PATTERN_SUBST */
7275 else if (exp_op == ':') {
7276#if BASH_SUBSTR && ENABLE_FEATURE_SH_MATH
7277 /* It's ${var:N[:M]} bashism.
7278 * Note that in encoded form it has TWO parts:
7279 * var:N<SPECIAL_VAR_SYMBOL>M<SPECIAL_VAR_SYMBOL>
7280 */
7281 arith_t beg, len;
7282 unsigned vallen;
7283 const char *errmsg;
7284
7285 beg = expand_and_evaluate_arith(exp_word, &errmsg);
7286 if (errmsg)
7287 goto empty_result;
7288 debug_printf_varexp("beg:'%s'=%lld\n", exp_word, (long long)beg);
7289 *p++ = SPECIAL_VAR_SYMBOL;
7290 exp_word = p;
7291 p = strchr(p, SPECIAL_VAR_SYMBOL);
7292 *p = '\0';
7293 vallen = val ? strlen(val) : 0;
7294 if (beg < 0) {
7295 /* negative beg counts from the end */
7296 beg = (arith_t)vallen + beg;
7297 }
7298 /* If expansion will be empty, do not even evaluate len */
7299 if (!val || beg < 0 || beg > vallen) {
7300 /* Why > vallen, not >=? bash:
7301 * unset b; a=ab; : ${a:2:${b=3}}; echo $b # "", b=3 (!!!)
7302 * unset b; a=a; : ${a:2:${b=3}}; echo $b # "", b not set
7303 */
7304 goto empty_result;
7305 }
7306 len = expand_and_evaluate_arith(exp_word, &errmsg);
7307 if (errmsg)
7308 goto empty_result;
7309 debug_printf_varexp("len:'%s'=%lld\n", exp_word, (long long)len);
7310 debug_printf_varexp("from val:'%s'\n", val);
7311 if (len < 0) {
7312 /* in bash, len=-n means strlen()-n */
7313 len = (arith_t)vallen - beg + len;
7314 if (len < 0) /* bash compat */
7315 msg_and_die_if_script("%s: substring expression < 0", var);
7316 }
7317 if (len <= 0 || !val /*|| beg >= vallen*/) {
7318 empty_result:
7319 val = NULL;
7320 } else {
7321 /* Paranoia. What if user entered 9999999999999
7322 * which fits in arith_t but not int? */
7323 if (len > INT_MAX)
7324 len = INT_MAX;
7325 val = to_be_freed = xstrndup(val + beg, len);
7326 }
7327 debug_printf_varexp("val:'%s'\n", val);
7328#else /* not (HUSH_SUBSTR_EXPANSION && FEATURE_SH_MATH) */
7329 msg_and_die_if_script("malformed ${%s:...}", var);
7330 val = NULL;
7331#endif
7332 } else { /* one of "-=+?" */
7333 /* Standard-mandated substitution ops:
7334 * ${var?word} - indicate error if unset
7335 * If var is unset, word (or a message indicating it is unset
7336 * if word is null) is written to standard error
7337 * and the shell exits with a non-zero exit status.
7338 * Otherwise, the value of var is substituted.
7339 * ${var-word} - use default value
7340 * If var is unset, word is substituted.
7341 * ${var=word} - assign and use default value
7342 * If var is unset, word is assigned to var.
7343 * In all cases, final value of var is substituted.
7344 * ${var+word} - use alternative value
7345 * If var is unset, null is substituted.
7346 * Otherwise, word is substituted.
7347 *
7348 * Word is subjected to tilde expansion, parameter expansion,
7349 * command substitution, and arithmetic expansion.
7350 * If word is not needed, it is not expanded.
7351 *
7352 * Colon forms (${var:-word}, ${var:=word} etc) do the same,
7353 * but also treat null var as if it is unset.
7354 *
7355 * Word-splitting and single quote behavior:
7356 *
7357 * $ f() { for i; do echo "|$i|"; done; }
7358 *
7359 * $ x=; f ${x:?'x y' z}; echo $?
7360 * bash: x: x y z # neither f nor "echo $?" executes
7361 * (if interactive, bash does not exit, but merely aborts to prompt. $? is set to 1)
7362 * $ x=; f "${x:?'x y' z}"
7363 * bash: x: x y z # dash prints: dash: x: 'x y' z
7364 *
7365 * $ x=; f ${x:='x y' z}
7366 * |x|
7367 * |y|
7368 * |z|
7369 * $ x=; f "${x:='x y' z}"
7370 * |'x y' z|
7371 *
7372 * $ x=x; f ${x:+'x y' z}
7373 * |x y|
7374 * |z|
7375 * $ x=x; f "${x:+'x y' z}"
7376 * |'x y' z|
7377 *
7378 * $ x=; f ${x:-'x y' z}
7379 * |x y|
7380 * |z|
7381 * $ x=; f "${x:-'x y' z}"
7382 * |'x y' z|
7383 */
7384 int use_word = (!val || ((exp_save == ':') && !val[0]));
7385 if (exp_op == '+')
7386 use_word = !use_word;
7387 debug_printf_expand("expand: op:%c (null:%s) test:%i\n", exp_op,
7388 (exp_save == ':') ? "true" : "false", use_word);
7389 if (use_word) {
7390 if (exp_op == '+' || exp_op == '-') {
7391 /* ${var+word} - use alternative value */
7392 /* ${var-word} - use default value */
7393 n = encode_then_append_var_plusminus(output, n, exp_word,
7394 /*dquoted:*/ (arg0 & 0x80)
7395 );
7396 val = NULL;
7397 } else {
7398 /* ${var?word} - indicate error if unset */
7399 /* ${var=word} - assign and use default value */
7400 to_be_freed = encode_then_expand_vararg(exp_word,
7401 /*handle_squotes:*/ !(arg0 & 0x80),
7402 /*unbackslash:*/ 0
7403 );
7404 if (to_be_freed)
7405 exp_word = to_be_freed;
7406 if (exp_op == '?') {
7407 /* mimic bash message */
7408 msg_and_die_if_script("%s: %s",
7409 var,
7410 exp_word[0]
7411 ? exp_word
7412 : "parameter null or not set"
7413 /* ash has more specific messages, a-la: */
7414 /*: (exp_save == ':' ? "parameter null or not set" : "parameter not set")*/
7415 );
7416//TODO: how interactive bash aborts expansion mid-command?
7417//It aborts the entire line, returns to prompt:
7418// $ f() { for i; do echo "|$i|"; done; }; x=; f "${x:?'x y' z}"; echo YO
7419// bash: x: x y z
7420// $
7421// ("echo YO" is not executed, neither the f function call)
7422 } else {
7423 val = exp_word;
7424 }
7425 if (exp_op == '=') {
7426 /* ${var=[word]} or ${var:=[word]} */
7427 if (isdigit(var[0]) || var[0] == '#') {
7428 /* mimic bash message */
7429 msg_and_die_if_script("$%s: cannot assign in this way", var);
7430 val = NULL;
7431 } else {
7432 char *new_var = xasprintf("%s=%s", var, val);
Francis Laniele7ca3a32023-12-22 22:02:42 +01007433 set_local_var0(new_var);
Francis Laniel110b7692023-12-22 22:02:27 +01007434 }
7435 }
7436 }
7437 }
7438 } /* one of "-=+?" */
7439
7440 *exp_saveptr = exp_save;
7441 } /* if (exp_op) */
7442
Francis Laniel36836fc2023-12-22 22:02:28 +01007443#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01007444 arg[0] = arg0;
7445 *pp = p;
7446
7447 n = append_str_maybe_ifs_split(output, n, first_ch, val);
7448
7449 free(to_be_freed);
7450 return n;
7451}
7452
7453/* Expand all variable references in given string, adding words to list[]
7454 * at n, n+1,... positions. Return updated n (so that list[n] is next one
7455 * to be filled). This routine is extremely tricky: has to deal with
7456 * variables/parameters with whitespace, $* and $@, and constructs like
7457 * 'echo -$*-'. If you play here, you must run testsuite afterwards! */
7458static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg)
7459{
7460 /* output->o_expflags & EXP_FLAG_SINGLEWORD (0x80) if we are in
7461 * expansion of right-hand side of assignment == 1-element expand.
7462 */
7463 char cant_be_null = 0; /* only bit 0x80 matters */
7464 char *p;
7465
7466 debug_printf_expand("expand_vars_to_list: arg:'%s' singleword:%x\n", arg,
7467 !!(output->o_expflags & EXP_FLAG_SINGLEWORD));
7468 debug_print_list("expand_vars_to_list[0]", output, n);
7469
7470 while ((p = strchr(arg, SPECIAL_VAR_SYMBOL)) != NULL) {
7471 char first_ch;
7472#if ENABLE_FEATURE_SH_MATH
7473 char arith_buf[sizeof(arith_t)*3 + 2];
7474#endif
7475
7476 if (output->ended_in_ifs) {
7477 o_addchr(output, '\0');
7478 n = o_save_ptr(output, n);
7479 output->ended_in_ifs = 0;
7480 }
7481
7482 o_addblock(output, arg, p - arg);
7483 debug_print_list("expand_vars_to_list[1]", output, n);
7484 arg = ++p;
7485 p = strchr(p, SPECIAL_VAR_SYMBOL);
7486
7487 /* Fetch special var name (if it is indeed one of them)
7488 * and quote bit, force the bit on if singleword expansion -
7489 * important for not getting v=$@ expand to many words. */
7490 first_ch = arg[0] | (output->o_expflags & EXP_FLAG_SINGLEWORD);
7491
7492 /* Is this variable quoted and thus expansion can't be null?
7493 * "$@" is special. Even if quoted, it can still
7494 * expand to nothing (not even an empty string),
7495 * thus it is excluded. */
7496 if ((first_ch & 0x7f) != '@')
7497 cant_be_null |= first_ch;
7498
7499 switch (first_ch & 0x7f) {
7500 /* Highest bit in first_ch indicates that var is double-quoted */
7501 case '*':
7502 case '@': {
7503 int i;
Francis Lanielbfc406a2023-12-22 22:02:33 +01007504#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01007505 if (!G.global_argv[1])
Francis Lanielbfc406a2023-12-22 22:02:33 +01007506#else /* __U_BOOT__ */
7507 if (!G.global_argv || !G.global_argv[1])
7508#endif /* __U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01007509 break;
7510 i = 1;
7511 cant_be_null |= first_ch; /* do it for "$@" _now_, when we know it's not empty */
7512 if (!(first_ch & 0x80)) { /* unquoted $* or $@ */
7513 while (G.global_argv[i]) {
7514 n = expand_on_ifs(output, n, G.global_argv[i]);
7515 debug_printf_expand("expand_vars_to_list: argv %d (last %d)\n", i, G.global_argc - 1);
7516 if (G.global_argv[i++][0] && G.global_argv[i]) {
7517 /* this argv[] is not empty and not last:
7518 * put terminating NUL, start new word */
7519 o_addchr(output, '\0');
7520 debug_print_list("expand_vars_to_list[2]", output, n);
7521 n = o_save_ptr(output, n);
7522 debug_print_list("expand_vars_to_list[3]", output, n);
7523 }
7524 }
7525 } else
7526 /* If EXP_FLAG_SINGLEWORD, we handle assignment 'a=....$@.....'
7527 * and in this case should treat it like '$*' - see 'else...' below */
7528 if (first_ch == (char)('@'|0x80) /* quoted $@ */
7529 && !(output->o_expflags & EXP_FLAG_SINGLEWORD) /* not v="$@" case */
7530 ) {
7531 while (1) {
7532 o_addQstr(output, G.global_argv[i]);
7533 if (++i >= G.global_argc)
7534 break;
7535 o_addchr(output, '\0');
7536 debug_print_list("expand_vars_to_list[4]", output, n);
7537 n = o_save_ptr(output, n);
7538 }
7539 } else { /* quoted $* (or v="$@" case): add as one word */
7540 while (1) {
7541 o_addQstr(output, G.global_argv[i]);
7542 if (!G.global_argv[++i])
7543 break;
7544 if (G.ifs[0])
7545 o_addchr(output, G.ifs[0]);
7546 }
7547 output->has_quoted_part = 1;
7548 }
7549 break;
7550 }
7551 case SPECIAL_VAR_SYMBOL: {
7552 /* <SPECIAL_VAR_SYMBOL><SPECIAL_VAR_SYMBOL> */
7553 /* "Empty variable", used to make "" etc to not disappear */
7554 output->has_quoted_part = 1;
7555 cant_be_null = 0x80;
7556 arg++;
7557 break;
7558 }
7559 case SPECIAL_VAR_QUOTED_SVS:
7560 /* <SPECIAL_VAR_SYMBOL><SPECIAL_VAR_QUOTED_SVS><SPECIAL_VAR_SYMBOL> */
7561 /* "^C variable", represents literal ^C char (possible in scripts) */
7562 o_addchr(output, SPECIAL_VAR_SYMBOL);
7563 arg++;
7564 break;
7565#if ENABLE_HUSH_TICK
7566 case '`': {
7567 /* <SPECIAL_VAR_SYMBOL>`cmd<SPECIAL_VAR_SYMBOL> */
7568 o_string subst_result = NULL_O_STRING;
7569
7570 *p = '\0'; /* replace trailing <SPECIAL_VAR_SYMBOL> */
7571 arg++;
7572 /* Can't just stuff it into output o_string,
7573 * expanded result may need to be globbed
7574 * and $IFS-split */
7575 debug_printf_subst("SUBST '%s' first_ch %x\n", arg, first_ch);
7576 G.last_exitcode = process_command_subs(&subst_result, arg);
7577 G.expand_exitcode = G.last_exitcode;
7578 debug_printf_subst("SUBST RES:%d '%s'\n", G.last_exitcode, subst_result.data);
7579 n = append_str_maybe_ifs_split(output, n, first_ch, subst_result.data);
7580 o_free(&subst_result);
7581 break;
7582 }
7583#endif
7584#if ENABLE_FEATURE_SH_MATH
7585 case '+': {
7586 /* <SPECIAL_VAR_SYMBOL>+arith<SPECIAL_VAR_SYMBOL> */
7587 arith_t res;
7588
7589 arg++; /* skip '+' */
7590 *p = '\0'; /* replace trailing <SPECIAL_VAR_SYMBOL> */
7591 debug_printf_subst("ARITH '%s' first_ch %x\n", arg, first_ch);
7592 res = expand_and_evaluate_arith(arg, NULL);
7593 debug_printf_subst("ARITH RES '"ARITH_FMT"'\n", res);
7594 sprintf(arith_buf, ARITH_FMT, res);
7595 if (res < 0
7596 && first_ch == (char)('+'|0x80)
7597 /* && (output->o_expflags & EXP_FLAG_ESC_GLOB_CHARS) */
7598 ) {
7599 /* Quoted negative ariths, like filename[0"$((-9))"],
7600 * should not be interpreted as glob ranges.
7601 * Convert leading '-' to '\-':
7602 */
7603 o_grow_by(output, 1);
7604 output->data[output->length++] = '\\';
7605 }
7606 o_addstr(output, arith_buf);
7607 break;
7608 }
7609#endif
7610 default:
7611 /* <SPECIAL_VAR_SYMBOL>varname[ops]<SPECIAL_VAR_SYMBOL> */
7612 n = expand_one_var(output, n, first_ch, arg, &p);
7613 break;
7614 } /* switch (char after <SPECIAL_VAR_SYMBOL>) */
7615
7616 /* Restore NULL'ed SPECIAL_VAR_SYMBOL.
7617 * Do the check to avoid writing to a const string. */
7618 if (*p != SPECIAL_VAR_SYMBOL)
7619 *p = SPECIAL_VAR_SYMBOL;
7620 arg = ++p;
7621 } /* end of "while (SPECIAL_VAR_SYMBOL is found) ..." */
7622
7623 if (*arg) {
7624 /* handle trailing string */
7625 if (output->ended_in_ifs) {
7626 o_addchr(output, '\0');
7627 n = o_save_ptr(output, n);
7628 }
7629 debug_print_list("expand_vars_to_list[a]", output, n);
7630 /* this part is literal, and it was already pre-quoted
7631 * if needed (much earlier), do not use o_addQstr here!
7632 */
7633 o_addstr(output, arg);
7634 debug_print_list("expand_vars_to_list[b]", output, n);
7635 } else
7636 if (output->length == o_get_last_ptr(output, n) /* expansion is empty */
7637 && !(cant_be_null & 0x80) /* and all vars were not quoted */
7638 && !output->has_quoted_part
7639 ) {
7640 n--;
7641 /* allow to reuse list[n] later without re-growth */
7642 output->has_empty_slot = 1;
7643 }
7644
7645 return n;
7646}
7647
7648static char **expand_variables(char **argv, unsigned expflags)
7649{
7650 int n;
7651 char **list;
7652 o_string output = NULL_O_STRING;
7653
7654 output.o_expflags = expflags;
7655
7656 n = 0;
7657 for (;;) {
7658 /* go to next list[n] */
7659 output.ended_in_ifs = 0;
7660 n = o_save_ptr(&output, n);
7661
7662 if (!*argv)
7663 break;
7664
7665 /* expand argv[i] */
7666 n = expand_vars_to_list(&output, n, *argv++);
7667 /* if (!output->has_empty_slot) -- need this?? */
7668 o_addchr(&output, '\0');
7669 }
7670 debug_print_list("expand_variables", &output, n);
7671
7672 /* output.data (malloced in one block) gets returned in "list" */
7673 list = o_finalize_list(&output, n);
7674 debug_print_strings("expand_variables[1]", list);
7675 return list;
7676}
7677
7678static char **expand_strvec_to_strvec(char **argv)
7679{
7680 return expand_variables(argv, EXP_FLAG_GLOB | EXP_FLAG_ESC_GLOB_CHARS);
7681}
7682
7683#if defined(CMD_SINGLEWORD_NOGLOB) || defined(CMD_TEST2_SINGLEWORD_NOGLOB)
7684static char **expand_strvec_to_strvec_singleword_noglob(char **argv)
7685{
7686 return expand_variables(argv, EXP_FLAG_SINGLEWORD);
7687}
7688#endif
7689
7690/* Used for expansion of right hand of assignments,
7691 * $((...)), heredocs, variable expansion parts.
7692 *
7693 * NB: should NOT do globbing!
7694 * "export v=/bin/c*; env | grep ^v=" outputs "v=/bin/c*"
7695 */
7696static char *expand_string_to_string(const char *str, int EXP_flags, int do_unbackslash)
7697{
7698#if !BASH_PATTERN_SUBST && !ENABLE_HUSH_CASE
7699 const int do_unbackslash = 1;
7700 const int EXP_flags = EXP_FLAG_ESC_GLOB_CHARS;
7701#endif
7702 char *argv[2], **list;
7703
7704 debug_printf_expand("string_to_string<='%s'\n", str);
7705 /* This is generally an optimization, but it also
7706 * handles "", which otherwise trips over !list[0] check below.
7707 * (is this ever happens that we actually get str="" here?)
7708 */
7709 if (!strchr(str, SPECIAL_VAR_SYMBOL) && !strchr(str, '\\')) {
7710 //TODO: Can use on strings with \ too, just unbackslash() them?
7711 debug_printf_expand("string_to_string(fast)=>'%s'\n", str);
7712 return xstrdup(str);
7713 }
7714
7715 argv[0] = (char*)str;
7716 argv[1] = NULL;
7717 list = expand_variables(argv, EXP_flags | EXP_FLAG_SINGLEWORD);
7718 if (!list[0]) {
7719 /* Example where it happens:
7720 * x=; echo ${x:-"$@"}
7721 */
7722 ((char*)list)[0] = '\0';
7723 } else {
7724 if (HUSH_DEBUG)
7725 if (list[1])
7726 bb_simple_error_msg_and_die("BUG in varexp2");
7727 /* actually, just move string 2*sizeof(char*) bytes back */
7728 overlapping_strcpy((char*)list, list[0]);
7729 if (do_unbackslash)
7730 unbackslash((char*)list);
7731 }
7732 debug_printf_expand("string_to_string=>'%s'\n", (char*)list);
7733 return (char*)list;
7734}
7735
7736#if 0
7737static char* expand_strvec_to_string(char **argv)
7738{
7739 char **list;
7740
7741 list = expand_variables(argv, EXP_FLAG_SINGLEWORD);
7742 /* Convert all NULs to spaces */
7743 if (list[0]) {
7744 int n = 1;
7745 while (list[n]) {
7746 if (HUSH_DEBUG)
7747 if (list[n-1] + strlen(list[n-1]) + 1 != list[n])
7748 bb_error_msg_and_die("BUG in varexp3");
7749 /* bash uses ' ' regardless of $IFS contents */
7750 list[n][-1] = ' ';
7751 n++;
7752 }
7753 }
7754 overlapping_strcpy((char*)list, list[0] ? list[0] : "");
7755 debug_printf_expand("strvec_to_string='%s'\n", (char*)list);
7756 return (char*)list;
7757}
7758#endif
7759
Francis Laniel36836fc2023-12-22 22:02:28 +01007760#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01007761static char **expand_assignments(char **argv, int count)
7762{
7763 int i;
7764 char **p;
7765
7766 G.expanded_assignments = p = NULL;
7767 /* Expand assignments into one string each */
7768 for (i = 0; i < count; i++) {
7769 p = add_string_to_strings(p,
7770 expand_string_to_string(argv[i],
7771 EXP_FLAG_ESC_GLOB_CHARS,
7772 /*unbackslash:*/ 1
7773 )
7774 );
7775 G.expanded_assignments = p;
7776 }
7777 G.expanded_assignments = NULL;
7778 return p;
7779}
7780
7781
7782static void switch_off_special_sigs(unsigned mask)
7783{
7784 unsigned sig = 0;
7785 while ((mask >>= 1) != 0) {
7786 sig++;
7787 if (!(mask & 1))
7788 continue;
7789#if ENABLE_HUSH_TRAP
7790 if (G_traps) {
7791 if (G_traps[sig] && !G_traps[sig][0])
7792 /* trap is '', has to remain SIG_IGN */
7793 continue;
7794 free(G_traps[sig]);
7795 G_traps[sig] = NULL;
7796 }
7797#endif
7798 /* We are here only if no trap or trap was not '' */
7799 install_sighandler(sig, SIG_DFL);
7800 }
7801}
Francis Laniel36836fc2023-12-22 22:02:28 +01007802#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01007803
Francis Laniel36836fc2023-12-22 22:02:28 +01007804#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01007805#if BB_MMU
7806/* never called */
7807void re_execute_shell(char ***to_free, const char *s,
7808 char *g_argv0, char **g_argv,
7809 char **builtin_argv) NORETURN;
7810
7811static void reset_traps_to_defaults(void)
7812{
7813 /* This function is always called in a child shell
7814 * after fork (not vfork, NOMMU doesn't use this function).
7815 */
7816 IF_HUSH_TRAP(unsigned sig;)
7817 unsigned mask;
7818
7819 /* Child shells are not interactive.
7820 * SIGTTIN/SIGTTOU/SIGTSTP should not have special handling.
7821 * Testcase: (while :; do :; done) + ^Z should background.
7822 * Same goes for SIGTERM, SIGHUP, SIGINT.
7823 */
7824 mask = (G.special_sig_mask & SPECIAL_INTERACTIVE_SIGS) | G_fatal_sig_mask;
7825 if (!G_traps && !mask)
7826 return; /* already no traps and no special sigs */
7827
7828 /* Switch off special sigs */
7829 switch_off_special_sigs(mask);
7830# if ENABLE_HUSH_JOB
7831 G_fatal_sig_mask = 0;
7832# endif
7833 G.special_sig_mask &= ~SPECIAL_INTERACTIVE_SIGS;
7834 /* SIGQUIT,SIGCHLD and maybe SPECIAL_JOBSTOP_SIGS
7835 * remain set in G.special_sig_mask */
7836
7837# if ENABLE_HUSH_TRAP
7838 if (!G_traps)
7839 return;
7840
7841 /* Reset all sigs to default except ones with empty traps */
7842 for (sig = 0; sig < NSIG; sig++) {
7843 if (!G_traps[sig])
7844 continue; /* no trap: nothing to do */
7845 if (!G_traps[sig][0])
7846 continue; /* empty trap: has to remain SIG_IGN */
7847 /* sig has non-empty trap, reset it: */
7848 free(G_traps[sig]);
7849 G_traps[sig] = NULL;
7850 /* There is no signal for trap 0 (EXIT) */
7851 if (sig == 0)
7852 continue;
7853 install_sighandler(sig, pick_sighandler(sig));
7854 }
7855# endif
7856}
7857
7858#else /* !BB_MMU */
7859
7860static void re_execute_shell(char ***to_free, const char *s,
7861 char *g_argv0, char **g_argv,
7862 char **builtin_argv) NORETURN;
7863static void re_execute_shell(char ***to_free, const char *s,
7864 char *g_argv0, char **g_argv,
7865 char **builtin_argv)
7866{
7867# define NOMMU_HACK_FMT ("-$%x:%x:%x:%x:%x:%llx" IF_HUSH_LOOPS(":%x"))
7868 /* delims + 2 * (number of bytes in printed hex numbers) */
7869 char param_buf[sizeof(NOMMU_HACK_FMT) + 2 * (sizeof(int)*6 + sizeof(long long)*1)];
7870 char *heredoc_argv[4];
7871 struct variable *cur;
7872# if ENABLE_HUSH_FUNCTIONS
7873 struct function *funcp;
7874# endif
7875 char **argv, **pp;
7876 unsigned cnt;
7877 unsigned long long empty_trap_mask;
7878
7879 if (!g_argv0) { /* heredoc */
7880 argv = heredoc_argv;
7881 argv[0] = (char *) G.argv0_for_re_execing;
7882 argv[1] = (char *) "-<";
7883 argv[2] = (char *) s;
7884 argv[3] = NULL;
7885 pp = &argv[3]; /* used as pointer to empty environment */
7886 goto do_exec;
7887 }
7888
7889 cnt = 0;
7890 pp = builtin_argv;
7891 if (pp) while (*pp++)
7892 cnt++;
7893
7894 empty_trap_mask = 0;
7895 if (G_traps) {
7896 int sig;
7897 for (sig = 1; sig < NSIG; sig++) {
7898 if (G_traps[sig] && !G_traps[sig][0])
7899 empty_trap_mask |= 1LL << sig;
7900 }
7901 }
7902
7903 sprintf(param_buf, NOMMU_HACK_FMT
7904 , (unsigned) G.root_pid
7905 , (unsigned) G.root_ppid
7906 , (unsigned) G.last_bg_pid
7907 , (unsigned) G.last_exitcode
7908 , cnt
7909 , empty_trap_mask
7910 IF_HUSH_LOOPS(, G.depth_of_loop)
7911 );
7912# undef NOMMU_HACK_FMT
7913 /* 1:hush 2:-$<pid>:<pid>:<exitcode>:<etc...> <vars...> <funcs...>
7914 * 3:-c 4:<cmd> 5:<arg0> <argN...> 6:NULL
7915 */
7916 cnt += 6;
7917 for (cur = G.top_var; cur; cur = cur->next) {
7918 if (!cur->flg_export || cur->flg_read_only)
7919 cnt += 2;
7920 }
Francis Laniele7ca3a32023-12-22 22:02:42 +01007921# if ENABLE_HUSH_LINENO_VAR
7922 cnt += 2;
7923# endif
Francis Laniel110b7692023-12-22 22:02:27 +01007924# if ENABLE_HUSH_FUNCTIONS
7925 for (funcp = G.top_func; funcp; funcp = funcp->next)
7926 cnt += 3;
7927# endif
7928 pp = g_argv;
7929 while (*pp++)
7930 cnt++;
7931 *to_free = argv = pp = xzalloc(sizeof(argv[0]) * cnt);
7932 *pp++ = (char *) G.argv0_for_re_execing;
7933 *pp++ = param_buf;
7934 for (cur = G.top_var; cur; cur = cur->next) {
7935 if (strcmp(cur->varstr, hush_version_str) == 0)
7936 continue;
7937 if (cur->flg_read_only) {
7938 *pp++ = (char *) "-R";
7939 *pp++ = cur->varstr;
7940 } else if (!cur->flg_export) {
7941 *pp++ = (char *) "-V";
7942 *pp++ = cur->varstr;
7943 }
7944 }
Francis Laniele7ca3a32023-12-22 22:02:42 +01007945# if ENABLE_HUSH_LINENO_VAR
7946 *pp++ = (char *) "-L";
7947 *pp++ = utoa(G.execute_lineno);
7948# endif
Francis Laniel110b7692023-12-22 22:02:27 +01007949# if ENABLE_HUSH_FUNCTIONS
7950 for (funcp = G.top_func; funcp; funcp = funcp->next) {
7951 *pp++ = (char *) "-F";
7952 *pp++ = funcp->name;
7953 *pp++ = funcp->body_as_string;
7954 }
7955# endif
7956 /* We can pass activated traps here. Say, -Tnn:trap_string
7957 *
7958 * However, POSIX says that subshells reset signals with traps
7959 * to SIG_DFL.
7960 * I tested bash-3.2 and it not only does that with true subshells
7961 * of the form ( list ), but with any forked children shells.
7962 * I set trap "echo W" WINCH; and then tried:
7963 *
7964 * { echo 1; sleep 20; echo 2; } &
7965 * while true; do echo 1; sleep 20; echo 2; break; done &
7966 * true | { echo 1; sleep 20; echo 2; } | cat
7967 *
7968 * In all these cases sending SIGWINCH to the child shell
7969 * did not run the trap. If I add trap "echo V" WINCH;
7970 * _inside_ group (just before echo 1), it works.
7971 *
7972 * I conclude it means we don't need to pass active traps here.
7973 */
7974 *pp++ = (char *) "-c";
7975 *pp++ = (char *) s;
7976 if (builtin_argv) {
7977 while (*++builtin_argv)
7978 *pp++ = *builtin_argv;
7979 *pp++ = (char *) "";
7980 }
7981 *pp++ = g_argv0;
7982 while (*g_argv)
7983 *pp++ = *g_argv++;
7984 /* *pp = NULL; - is already there */
7985 pp = environ;
7986
7987 do_exec:
7988 debug_printf_exec("re_execute_shell pid:%d cmd:'%s'\n", getpid(), s);
7989 /* Don't propagate SIG_IGN to the child */
7990 if (SPECIAL_JOBSTOP_SIGS != 0)
7991 switch_off_special_sigs(G.special_sig_mask & SPECIAL_JOBSTOP_SIGS);
7992 execve(bb_busybox_exec_path, argv, pp);
7993 /* Fallback. Useful for init=/bin/hush usage etc */
7994 if (argv[0][0] == '/')
7995 execve(argv[0], argv, pp);
7996 xfunc_error_retval = 127;
7997 bb_simple_error_msg_and_die("can't re-execute the shell");
7998}
7999#endif /* !BB_MMU */
8000
Francis Laniel36836fc2023-12-22 22:02:28 +01008001#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01008002
8003static int run_and_free_list(struct pipe *pi);
8004
8005/* Executing from string: eval, sh -c '...'
8006 * or from file: /etc/profile, . file, sh <script>, sh (intereactive)
8007 * end_trigger controls how often we stop parsing
8008 * NUL: parse all, execute, return
8009 * ';': parse till ';' or newline, execute, repeat till EOF
8010 */
Francis Laniel26cafe12023-12-22 22:02:34 +01008011#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01008012static void parse_and_run_stream(struct in_str *inp, int end_trigger)
Francis Laniel26cafe12023-12-22 22:02:34 +01008013#else /* __U_BOOT__ */
8014static int parse_and_run_stream(struct in_str *inp, int end_trigger)
8015#endif /* __U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01008016{
8017 /* Why we need empty flag?
8018 * An obscure corner case "false; ``; echo $?":
8019 * empty command in `` should still set $? to 0.
8020 * But we can't just set $? to 0 at the start,
8021 * this breaks "false; echo `echo $?`" case.
8022 */
8023 bool empty = 1;
Francis Laniel26cafe12023-12-22 22:02:34 +01008024#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01008025 while (1) {
Francis Laniel26cafe12023-12-22 22:02:34 +01008026#else /* __U_BOOT__ */
8027 do {
8028#endif /* __U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01008029 struct pipe *pipe_list;
8030
8031#if ENABLE_HUSH_INTERACTIVE
8032 if (end_trigger == ';') {
8033 G.promptmode = 0; /* PS1 */
8034 debug_printf_prompt("%s promptmode=%d\n", __func__, G.promptmode);
8035 }
8036#endif
8037 pipe_list = parse_stream(NULL, NULL, inp, end_trigger);
8038 if (!pipe_list || pipe_list == ERR_PTR) { /* EOF/error */
8039 /* If we are in "big" script
8040 * (not in `cmd` or something similar)...
8041 */
8042 if (pipe_list == ERR_PTR && end_trigger == ';') {
8043 /* Discard cached input (rest of line) */
8044 int ch = inp->last_char;
8045 while (ch != EOF && ch != '\n') {
8046 //bb_error_msg("Discarded:'%c'", ch);
8047 ch = i_getch(inp);
8048 }
8049 /* Force prompt */
8050 inp->p = NULL;
8051 /* This stream isn't empty */
8052 empty = 0;
8053 continue;
8054 }
8055 if (!pipe_list && empty)
8056 G.last_exitcode = 0;
8057 break;
8058 }
8059 debug_print_tree(pipe_list, 0);
8060 debug_printf_exec("parse_and_run_stream: run_and_free_list\n");
Francis Laniel3b66e572023-12-22 22:02:32 +01008061#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01008062 run_and_free_list(pipe_list);
Francis Laniel3b66e572023-12-22 22:02:32 +01008063#else /* __U_BOOT__ */
8064 int rcode = run_and_free_list(pipe_list);
8065 /*
8066 * We reset input string to not run the following command, so running
8067 * 'exit; echo foo' does not print foo.
8068 */
8069 if (rcode <= EXIT_RET_CODE)
8070 setup_file_in_str(inp);
8071#endif /* __U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01008072 empty = 0;
8073 if (G_flag_return_in_progress == 1)
8074 break;
Francis Laniel26cafe12023-12-22 22:02:34 +01008075#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01008076 }
Francis Laniel26cafe12023-12-22 22:02:34 +01008077#else /* __U_BOOT__ */
8078 /*
8079 * This do/while is needed by run_command to avoid looping on a command
8080 * with syntax error.
8081 */
8082 } while (!(G.run_command_flags & FLAG_EXIT_FROM_LOOP));
8083
8084 return G.last_exitcode;
8085#endif /* __U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01008086}
8087
Francis Laniel36836fc2023-12-22 22:02:28 +01008088#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01008089static void parse_and_run_string(const char *s)
Francis Laniel26cafe12023-12-22 22:02:34 +01008090#else /* __U_BOOT__ */
8091static int parse_and_run_string(const char *s)
8092#endif /* __U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01008093{
8094 struct in_str input;
Francis Laniele7ca3a32023-12-22 22:02:42 +01008095#ifndef __U_BOOT__
8096 IF_HUSH_LINENO_VAR(unsigned sv = G.parse_lineno;)
8097#else /* __U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01008098 //IF_HUSH_LINENO_VAR(unsigned sv = G.parse_lineno;)
Francis Laniele7ca3a32023-12-22 22:02:42 +01008099#endif /* __U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01008100
8101 setup_string_in_str(&input, s);
Francis Laniel26cafe12023-12-22 22:02:34 +01008102#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01008103 parse_and_run_stream(&input, '\0');
Francis Laniel26cafe12023-12-22 22:02:34 +01008104#else /* __U_BOOT__ */
8105 return parse_and_run_stream(&input, '\0');
8106#endif /* __U_BOOT__ */
Francis Laniele7ca3a32023-12-22 22:02:42 +01008107#ifndef __U_BOOT__
8108 IF_HUSH_LINENO_VAR(unsigned sv = G.parse_lineno;)
8109#else /* __U_BOOT__ */
8110 //IF_HUSH_LINENO_VAR(unsigned sv = G.parse_lineno;)
8111#endif /* __U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01008112}
8113
Francis Laniel26cafe12023-12-22 22:02:34 +01008114#ifdef __U_BOOT__
Francis Laniel5b64c452023-12-22 22:02:35 +01008115int parse_string_outer_modern(const char *cmd, int flags)
Francis Laniel26cafe12023-12-22 22:02:34 +01008116{
8117 int ret;
8118 int old_flags;
8119
8120 /*
8121 * Keep old values of run_command to be able to restore them once
8122 * command was executed.
8123 */
8124 old_flags = G.run_command_flags;
8125 G.run_command_flags = flags;
8126
8127 ret = parse_and_run_string(cmd);
8128
8129 G.run_command_flags = old_flags;
8130
8131 return ret;
8132}
8133#endif /* __U_BOOT__ */
Francis Laniel36836fc2023-12-22 22:02:28 +01008134#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01008135static void parse_and_run_file(HFILE *fp)
Francis Laniel36836fc2023-12-22 22:02:28 +01008136#else /* __U_BOOT__ */
8137void parse_and_run_file(void)
8138#endif /* __U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01008139{
8140 struct in_str input;
Francis Laniel36836fc2023-12-22 22:02:28 +01008141#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01008142 IF_HUSH_LINENO_VAR(unsigned sv = G.parse_lineno;)
8143
8144 IF_HUSH_LINENO_VAR(G.parse_lineno = 1;)
8145 setup_file_in_str(&input, fp);
Francis Laniel36836fc2023-12-22 22:02:28 +01008146#else /* __U_BOOT__ */
8147 setup_file_in_str(&input);
8148#endif /* __U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01008149 parse_and_run_stream(&input, ';');
Francis Laniel36836fc2023-12-22 22:02:28 +01008150#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01008151 IF_HUSH_LINENO_VAR(G.parse_lineno = sv;)
Francis Laniel36836fc2023-12-22 22:02:28 +01008152#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01008153}
8154
Francis Laniel36836fc2023-12-22 22:02:28 +01008155#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01008156#if ENABLE_HUSH_TICK
8157static int generate_stream_from_string(const char *s, pid_t *pid_p)
8158{
8159 pid_t pid;
8160 int channel[2];
8161# if !BB_MMU
8162 char **to_free = NULL;
8163# endif
8164
8165 xpipe(channel);
8166 pid = BB_MMU ? xfork() : xvfork();
8167 if (pid == 0) { /* child */
8168 disable_restore_tty_pgrp_on_exit();
8169 /* Process substitution is not considered to be usual
8170 * 'command execution'.
8171 * SUSv3 says ctrl-Z should be ignored, ctrl-C should not.
8172 */
8173 bb_signals(0
8174 + (1 << SIGTSTP)
8175 + (1 << SIGTTIN)
8176 + (1 << SIGTTOU)
8177 , SIG_IGN);
8178 close(channel[0]); /* NB: close _first_, then move fd! */
8179 xmove_fd(channel[1], 1);
8180# if ENABLE_HUSH_TRAP
8181 /* Awful hack for `trap` or $(trap).
8182 *
8183 * http://www.opengroup.org/onlinepubs/009695399/utilities/trap.html
8184 * contains an example where "trap" is executed in a subshell:
8185 *
8186 * save_traps=$(trap)
8187 * ...
8188 * eval "$save_traps"
8189 *
8190 * Standard does not say that "trap" in subshell shall print
8191 * parent shell's traps. It only says that its output
8192 * must have suitable form, but then, in the above example
8193 * (which is not supposed to be normative), it implies that.
8194 *
8195 * bash (and probably other shell) does implement it
8196 * (traps are reset to defaults, but "trap" still shows them),
8197 * but as a result, "trap" logic is hopelessly messed up:
8198 *
8199 * # trap
8200 * trap -- 'echo Ho' SIGWINCH <--- we have a handler
8201 * # (trap) <--- trap is in subshell - no output (correct, traps are reset)
8202 * # true | trap <--- trap is in subshell - no output (ditto)
8203 * # echo `true | trap` <--- in subshell - output (but traps are reset!)
8204 * trap -- 'echo Ho' SIGWINCH
8205 * # echo `(trap)` <--- in subshell in subshell - output
8206 * trap -- 'echo Ho' SIGWINCH
8207 * # echo `true | (trap)` <--- in subshell in subshell in subshell - output!
8208 * trap -- 'echo Ho' SIGWINCH
8209 *
8210 * The rules when to forget and when to not forget traps
8211 * get really complex and nonsensical.
8212 *
8213 * Our solution: ONLY bare $(trap) or `trap` is special.
8214 */
8215 s = skip_whitespace(s);
8216 if (is_prefixed_with(s, "trap")
8217 && skip_whitespace(s + 4)[0] == '\0'
8218 ) {
Francis Laniele7ca3a32023-12-22 22:02:42 +01008219 static const char *const argv[] ALIGN_PTR = { NULL, NULL };
Francis Laniel110b7692023-12-22 22:02:27 +01008220 builtin_trap((char**)argv);
8221 fflush_all(); /* important */
8222 _exit(0);
8223 }
8224# endif
8225# if BB_MMU
8226 /* Prevent it from trying to handle ctrl-z etc */
8227 IF_HUSH_JOB(G.run_list_level = 1;)
8228 CLEAR_RANDOM_T(&G.random_gen); /* or else $RANDOM repeats in child */
8229 reset_traps_to_defaults();
8230 IF_HUSH_MODE_X(G.x_mode_depth++;)
8231 //bb_error_msg("%s: ++x_mode_depth=%d", __func__, G.x_mode_depth);
8232 parse_and_run_string(s);
8233 _exit(G.last_exitcode);
8234# else
8235 /* We re-execute after vfork on NOMMU. This makes this script safe:
8236 * yes "0123456789012345678901234567890" | dd bs=32 count=64k >BIG
8237 * huge=`cat BIG` # was blocking here forever
8238 * echo OK
8239 */
8240 re_execute_shell(&to_free,
8241 s,
8242 G.global_argv[0],
8243 G.global_argv + 1,
8244 NULL);
8245# endif
8246 }
8247
8248 /* parent */
8249 *pid_p = pid;
8250# if ENABLE_HUSH_FAST
8251 G.count_SIGCHLD++;
8252//bb_error_msg("[%d] fork in generate_stream_from_string:"
8253// " G.count_SIGCHLD:%d G.handled_SIGCHLD:%d",
8254// getpid(), G.count_SIGCHLD, G.handled_SIGCHLD);
8255# endif
8256 enable_restore_tty_pgrp_on_exit();
8257# if !BB_MMU
8258 free(to_free);
8259# endif
8260 close(channel[1]);
8261 return channel[0];
8262}
8263
8264/* Return code is exit status of the process that is run. */
8265static int process_command_subs(o_string *dest, const char *s)
8266{
8267 FILE *fp;
8268 pid_t pid;
8269 int status, ch, eol_cnt;
8270
8271 fp = xfdopen_for_read(generate_stream_from_string(s, &pid));
8272
8273 /* Now send results of command back into original context */
8274 eol_cnt = 0;
8275 while ((ch = getc(fp)) != EOF) {
8276 if (ch == '\0')
8277 continue;
8278 if (ch == '\n') {
8279 eol_cnt++;
8280 continue;
8281 }
8282 while (eol_cnt) {
8283 o_addchr(dest, '\n');
8284 eol_cnt--;
8285 }
8286 o_addQchr(dest, ch);
8287 }
8288
8289 debug_printf("done reading from `cmd` pipe, closing it\n");
8290 fclose(fp);
8291 /* We need to extract exitcode. Test case
8292 * "true; echo `sleep 1; false` $?"
8293 * should print 1 */
8294 safe_waitpid(pid, &status, 0);
8295 debug_printf("child exited. returning its exitcode:%d\n", WEXITSTATUS(status));
8296 return WEXITSTATUS(status);
8297}
8298#endif /* ENABLE_HUSH_TICK */
8299
8300
8301static void setup_heredoc(struct redir_struct *redir)
8302{
8303 struct fd_pair pair;
8304 pid_t pid;
8305 int len, written;
8306 /* the _body_ of heredoc (misleading field name) */
8307 const char *heredoc = redir->rd_filename;
8308 char *expanded;
8309#if !BB_MMU
8310 char **to_free;
8311#endif
8312
8313 expanded = NULL;
8314 if (!(redir->rd_dup & HEREDOC_QUOTED)) {
8315 expanded = encode_then_expand_string(heredoc);
8316 if (expanded)
8317 heredoc = expanded;
8318 }
8319 len = strlen(heredoc);
8320
8321 close(redir->rd_fd); /* often saves dup2+close in xmove_fd */
8322 xpiped_pair(pair);
8323 xmove_fd(pair.rd, redir->rd_fd);
8324
8325 /* Try writing without forking. Newer kernels have
8326 * dynamically growing pipes. Must use non-blocking write! */
8327 ndelay_on(pair.wr);
8328 while (1) {
8329 written = write(pair.wr, heredoc, len);
8330 if (written <= 0)
8331 break;
8332 len -= written;
8333 if (len == 0) {
8334 close(pair.wr);
8335 free(expanded);
8336 return;
8337 }
8338 heredoc += written;
8339 }
8340 ndelay_off(pair.wr);
8341
8342 /* Okay, pipe buffer was not big enough */
8343 /* Note: we must not create a stray child (bastard? :)
8344 * for the unsuspecting parent process. Child creates a grandchild
8345 * and exits before parent execs the process which consumes heredoc
8346 * (that exec happens after we return from this function) */
8347#if !BB_MMU
8348 to_free = NULL;
8349#endif
8350 pid = xvfork();
8351 if (pid == 0) {
8352 /* child */
8353 disable_restore_tty_pgrp_on_exit();
8354 pid = BB_MMU ? xfork() : xvfork();
8355 if (pid != 0)
8356 _exit(0);
8357 /* grandchild */
8358 close(redir->rd_fd); /* read side of the pipe */
8359#if BB_MMU
8360 full_write(pair.wr, heredoc, len); /* may loop or block */
8361 _exit(0);
8362#else
8363 /* Delegate blocking writes to another process */
8364 xmove_fd(pair.wr, STDOUT_FILENO);
8365 re_execute_shell(&to_free, heredoc, NULL, NULL, NULL);
8366#endif
8367 }
8368 /* parent */
8369#if ENABLE_HUSH_FAST
8370 G.count_SIGCHLD++;
8371//bb_error_msg("[%d] fork in setup_heredoc: G.count_SIGCHLD:%d G.handled_SIGCHLD:%d", getpid(), G.count_SIGCHLD, G.handled_SIGCHLD);
8372#endif
8373 enable_restore_tty_pgrp_on_exit();
8374#if !BB_MMU
8375 free(to_free);
8376#endif
8377 close(pair.wr);
8378 free(expanded);
8379 wait(NULL); /* wait till child has died */
8380}
8381
8382struct squirrel {
8383 int orig_fd;
8384 int moved_to;
8385 /* moved_to = n: fd was moved to n; restore back to orig_fd after redir */
8386 /* moved_to = -1: fd was opened by redirect; close orig_fd after redir */
8387};
8388
8389static struct squirrel *append_squirrel(struct squirrel *sq, int i, int orig, int moved)
8390{
8391 sq = xrealloc(sq, (i + 2) * sizeof(sq[0]));
8392 sq[i].orig_fd = orig;
8393 sq[i].moved_to = moved;
8394 sq[i+1].orig_fd = -1; /* end marker */
8395 return sq;
8396}
8397
8398static struct squirrel *add_squirrel(struct squirrel *sq, int fd, int avoid_fd)
8399{
8400 int moved_to;
8401 int i;
8402
8403 i = 0;
8404 if (sq) for (; sq[i].orig_fd >= 0; i++) {
8405 /* If we collide with an already moved fd... */
8406 if (fd == sq[i].moved_to) {
8407 sq[i].moved_to = dup_CLOEXEC(sq[i].moved_to, avoid_fd);
8408 debug_printf_redir("redirect_fd %d: already busy, moving to %d\n", fd, sq[i].moved_to);
8409 if (sq[i].moved_to < 0) /* what? */
8410 xfunc_die();
8411 return sq;
8412 }
8413 if (fd == sq[i].orig_fd) {
8414 /* Example: echo Hello >/dev/null 1>&2 */
8415 debug_printf_redir("redirect_fd %d: already moved\n", fd);
8416 return sq;
8417 }
8418 }
8419
8420 /* If this fd is open, we move and remember it; if it's closed, moved_to = -1 */
8421 moved_to = dup_CLOEXEC(fd, avoid_fd);
8422 debug_printf_redir("redirect_fd %d: previous fd is moved to %d (-1 if it was closed)\n", fd, moved_to);
8423 if (moved_to < 0 && errno != EBADF)
8424 xfunc_die();
8425 return append_squirrel(sq, i, fd, moved_to);
8426}
8427
8428static struct squirrel *add_squirrel_closed(struct squirrel *sq, int fd)
8429{
8430 int i;
8431
8432 i = 0;
8433 if (sq) for (; sq[i].orig_fd >= 0; i++) {
8434 /* If we collide with an already moved fd... */
8435 if (fd == sq[i].orig_fd) {
8436 /* Examples:
8437 * "echo 3>FILE 3>&- 3>FILE"
8438 * "echo 3>&- 3>FILE"
8439 * No need for last redirect to insert
8440 * another "need to close 3" indicator.
8441 */
8442 debug_printf_redir("redirect_fd %d: already moved or closed\n", fd);
8443 return sq;
8444 }
8445 }
8446
8447 debug_printf_redir("redirect_fd %d: previous fd was closed\n", fd);
8448 return append_squirrel(sq, i, fd, -1);
8449}
8450
8451/* fd: redirect wants this fd to be used (e.g. 3>file).
8452 * Move all conflicting internally used fds,
8453 * and remember them so that we can restore them later.
8454 */
8455static int save_fd_on_redirect(int fd, int avoid_fd, struct squirrel **sqp)
8456{
8457 if (avoid_fd < 9) /* the important case here is that it can be -1 */
8458 avoid_fd = 9;
8459
8460#if ENABLE_HUSH_INTERACTIVE
8461 if (fd != 0 /* don't trigger for G_interactive_fd == 0 (that's "not interactive" flag) */
8462 && fd == G_interactive_fd
8463 ) {
8464 /* Testcase: "ls -l /proc/$$/fd 255>&-" should work */
8465 G_interactive_fd = xdup_CLOEXEC_and_close(G_interactive_fd, avoid_fd);
8466 debug_printf_redir("redirect_fd %d: matches interactive_fd, moving it to %d\n", fd, G_interactive_fd);
8467 return 1; /* "we closed fd" */
8468 }
8469#endif
8470 /* Are we called from setup_redirects(squirrel==NULL)
8471 * in redirect in a [v]forked child?
8472 */
8473 if (sqp == NULL) {
8474 /* No need to move script fds.
8475 * For NOMMU case, it's actively wrong: we'd change ->fd
8476 * fields in memory for the parent, but parent's fds
8477 * aren't moved, it would use wrong fd!
8478 * Reproducer: "cmd 3>FILE" in script.
8479 * If we would call move_HFILEs_on_redirect(), child would:
8480 * fcntl64(3, F_DUPFD_CLOEXEC, 10) = 10
8481 * close(3) = 0
8482 * and change ->fd to 10 if fd#3 is a script fd. WRONG.
8483 */
8484 //bb_error_msg("sqp == NULL: [v]forked child");
8485 return 0;
8486 }
8487
8488 /* If this one of script's fds? */
8489 if (move_HFILEs_on_redirect(fd, avoid_fd))
8490 return 1; /* yes. "we closed fd" (actually moved it) */
8491
8492 /* Are we called for "exec 3>FILE"? Came through
8493 * redirect_and_varexp_helper(squirrel=ERR_PTR) -> setup_redirects(ERR_PTR)
8494 * This case used to fail for this script:
8495 * exec 3>FILE
8496 * echo Ok
8497 * ...100000 more lines...
8498 * echo Ok
8499 * as follows:
8500 * read(3, "exec 3>FILE\necho Ok\necho Ok"..., 1024) = 1024
8501 * open("FILE", O_WRONLY|O_CREAT|O_TRUNC|O_LARGEFILE, 0666) = 4
8502 * dup2(4, 3) = 3
8503 * ^^^^^^^^ oops, we lost fd#3 opened to our script!
8504 * close(4) = 0
8505 * write(1, "Ok\n", 3) = 3
8506 * ... = 3
8507 * write(1, "Ok\n", 3) = 3
8508 * read(3, 0x94fbc08, 1024) = -1 EBADF (Bad file descriptor)
8509 * ^^^^^^^^ oops, wrong fd!!!
8510 * With this case separate from sqp == NULL and *after* move_HFILEs,
8511 * it now works:
8512 */
8513 if (sqp == ERR_PTR) {
8514 /* Don't preserve redirected fds: exec is _meant_ to change these */
8515 //bb_error_msg("sqp == ERR_PTR: exec >FILE");
8516 return 0;
8517 }
8518
8519 /* Check whether it collides with any open fds (e.g. stdio), save fds as needed */
8520 *sqp = add_squirrel(*sqp, fd, avoid_fd);
8521 return 0; /* "we did not close fd" */
8522}
8523
8524static void restore_redirects(struct squirrel *sq)
8525{
8526 if (sq) {
8527 int i;
8528 for (i = 0; sq[i].orig_fd >= 0; i++) {
8529 if (sq[i].moved_to >= 0) {
8530 /* We simply die on error */
8531 debug_printf_redir("restoring redirected fd from %d to %d\n", sq[i].moved_to, sq[i].orig_fd);
8532 xmove_fd(sq[i].moved_to, sq[i].orig_fd);
8533 } else {
8534 /* cmd1 9>FILE; cmd2_should_see_fd9_closed */
8535 debug_printf_redir("restoring redirected fd %d: closing it\n", sq[i].orig_fd);
8536 close(sq[i].orig_fd);
8537 }
8538 }
8539 free(sq);
8540 }
8541 if (G.HFILE_stdin
8542 && G.HFILE_stdin->fd > STDIN_FILENO
8543 /* we compare > STDIN, not == STDIN, since hfgetc()
8544 * closes fd and sets ->fd to -1 if EOF is reached.
8545 * Testcase: echo 'pwd' | hush
8546 */
8547 ) {
8548 /* Testcase: interactive "read r <FILE; echo $r; read r; echo $r".
8549 * Redirect moves ->fd to e.g. 10,
8550 * and it is not restored above (we do not restore script fds
8551 * after redirects, we just use new, "moved" fds).
8552 * However for stdin, get_user_input() -> read_line_input(),
8553 * and read builtin, depend on fd == STDIN_FILENO.
8554 */
8555 debug_printf_redir("restoring %d to stdin\n", G.HFILE_stdin->fd);
8556 xmove_fd(G.HFILE_stdin->fd, STDIN_FILENO);
8557 G.HFILE_stdin->fd = STDIN_FILENO;
8558 }
8559
8560 /* If moved, G_interactive_fd stays on new fd, not restoring it */
8561}
8562
8563#if ENABLE_FEATURE_SH_STANDALONE && BB_MMU
8564static void close_saved_fds_and_FILE_fds(void)
8565{
8566 if (G_interactive_fd)
8567 close(G_interactive_fd);
8568 close_all_HFILE_list();
8569}
8570#endif
8571
8572static int internally_opened_fd(int fd, struct squirrel *sq)
8573{
8574 int i;
8575
8576#if ENABLE_HUSH_INTERACTIVE
8577 if (fd == G_interactive_fd)
8578 return 1;
8579#endif
8580 /* If this one of script's fds? */
8581 if (fd_in_HFILEs(fd))
8582 return 1;
8583
8584 if (sq) for (i = 0; sq[i].orig_fd >= 0; i++) {
8585 if (fd == sq[i].moved_to)
8586 return 1;
8587 }
8588 return 0;
8589}
8590
8591/* squirrel != NULL means we squirrel away copies of stdin, stdout,
8592 * and stderr if they are redirected. */
8593static int setup_redirects(struct command *prog, struct squirrel **sqp)
8594{
8595 struct redir_struct *redir;
8596
8597 for (redir = prog->redirects; redir; redir = redir->next) {
8598 int newfd;
8599 int closed;
8600
8601 if (redir->rd_type == REDIRECT_HEREDOC2) {
8602 /* "rd_fd<<HERE" case */
8603 save_fd_on_redirect(redir->rd_fd, /*avoid:*/ 0, sqp);
8604 /* for REDIRECT_HEREDOC2, rd_filename holds _contents_
8605 * of the heredoc */
8606 debug_printf_redir("set heredoc '%s'\n",
8607 redir->rd_filename);
8608 setup_heredoc(redir);
8609 continue;
8610 }
8611
8612 if (redir->rd_dup == REDIRFD_TO_FILE) {
8613 /* "rd_fd<*>file" case (<*> is <,>,>>,<>) */
8614 char *p;
8615 int mode;
8616
8617 if (redir->rd_filename == NULL) {
8618 /* Examples:
8619 * "cmd >" (no filename)
8620 * "cmd > <file" (2nd redirect starts too early)
8621 */
8622 syntax_error("invalid redirect");
8623 continue;
8624 }
8625 mode = redir_table[redir->rd_type].mode;
8626 p = expand_string_to_string(redir->rd_filename,
8627 EXP_FLAG_ESC_GLOB_CHARS, /*unbackslash:*/ 1);
8628 newfd = open_or_warn(p, mode);
8629 free(p);
8630 if (newfd < 0) {
8631 /* Error message from open_or_warn can be lost
8632 * if stderr has been redirected, but bash
8633 * and ash both lose it as well
8634 * (though zsh doesn't!)
8635 */
8636 return 1;
8637 }
8638 if (newfd == redir->rd_fd && sqp) {
8639 /* open() gave us precisely the fd we wanted.
8640 * This means that this fd was not busy
8641 * (not opened to anywhere).
8642 * Remember to close it on restore:
8643 */
8644 *sqp = add_squirrel_closed(*sqp, newfd);
8645 debug_printf_redir("redir to previously closed fd %d\n", newfd);
8646 }
8647 } else {
8648 /* "rd_fd>&rd_dup" or "rd_fd>&-" case */
8649 newfd = redir->rd_dup;
8650 }
8651
8652 if (newfd == redir->rd_fd)
8653 continue;
8654
8655 /* if "N>FILE": move newfd to redir->rd_fd */
8656 /* if "N>&M": dup newfd to redir->rd_fd */
8657 /* if "N>&-": close redir->rd_fd (newfd is REDIRFD_CLOSE) */
8658
8659 closed = save_fd_on_redirect(redir->rd_fd, /*avoid:*/ newfd, sqp);
8660 if (newfd == REDIRFD_CLOSE) {
8661 /* "N>&-" means "close me" */
8662 if (!closed) {
8663 /* ^^^ optimization: saving may already
8664 * have closed it. If not... */
8665 close(redir->rd_fd);
8666 }
8667 /* Sometimes we do another close on restore, getting EBADF.
8668 * Consider "echo 3>FILE 3>&-"
8669 * first redirect remembers "need to close 3",
8670 * and second redirect closes 3! Restore code then closes 3 again.
8671 */
8672 } else {
8673 /* if newfd is a script fd or saved fd, simulate EBADF */
8674 if (internally_opened_fd(newfd, sqp && sqp != ERR_PTR ? *sqp : NULL)) {
8675 //errno = EBADF;
8676 //bb_perror_msg_and_die("can't duplicate file descriptor");
8677 newfd = -1; /* same effect as code above */
8678 }
8679 xdup2(newfd, redir->rd_fd);
8680 if (redir->rd_dup == REDIRFD_TO_FILE)
8681 /* "rd_fd > FILE" */
8682 close(newfd);
8683 /* else: "rd_fd > rd_dup" */
8684 }
8685 }
8686 return 0;
8687}
8688
8689static char *find_in_path(const char *arg)
8690{
8691 char *ret = NULL;
8692 const char *PATH = get_local_var_value("PATH");
8693
8694 if (!PATH)
8695 return NULL;
8696
8697 while (1) {
8698 const char *end = strchrnul(PATH, ':');
8699 int sz = end - PATH; /* must be int! */
8700
8701 free(ret);
8702 if (sz != 0) {
8703 ret = xasprintf("%.*s/%s", sz, PATH, arg);
8704 } else {
8705 /* We have xxx::yyyy in $PATH,
8706 * it means "use current dir" */
8707 ret = xstrdup(arg);
8708 }
8709 if (access(ret, F_OK) == 0)
8710 break;
8711
8712 if (*end == '\0') {
8713 free(ret);
8714 return NULL;
8715 }
8716 PATH = end + 1;
8717 }
8718
8719 return ret;
8720}
8721
8722static const struct built_in_command *find_builtin_helper(const char *name,
8723 const struct built_in_command *x,
8724 const struct built_in_command *end)
8725{
8726 while (x != end) {
8727 if (strcmp(name, x->b_cmd) != 0) {
8728 x++;
8729 continue;
8730 }
8731 debug_printf_exec("found builtin '%s'\n", name);
8732 return x;
8733 }
8734 return NULL;
8735}
8736static const struct built_in_command *find_builtin1(const char *name)
8737{
8738 return find_builtin_helper(name, bltins1, &bltins1[ARRAY_SIZE(bltins1)]);
8739}
8740static const struct built_in_command *find_builtin(const char *name)
8741{
8742 const struct built_in_command *x = find_builtin1(name);
8743 if (x)
8744 return x;
8745 return find_builtin_helper(name, bltins2, &bltins2[ARRAY_SIZE(bltins2)]);
8746}
8747
Francis Laniele7ca3a32023-12-22 22:02:42 +01008748#if ENABLE_HUSH_JOB && ENABLE_FEATURE_TAB_COMPLETION
8749static const char * FAST_FUNC hush_command_name(int i)
Francis Laniel110b7692023-12-22 22:02:27 +01008750{
8751 if (/*i >= 0 && */ i < ARRAY_SIZE(bltins1)) {
8752 return bltins1[i].b_cmd;
8753 }
8754 i -= ARRAY_SIZE(bltins1);
8755 if (i < ARRAY_SIZE(bltins2)) {
8756 return bltins2[i].b_cmd;
8757 }
Francis Laniele7ca3a32023-12-22 22:02:42 +01008758# if ENABLE_HUSH_FUNCTIONS
8759 {
8760 struct function *funcp;
8761 i -= ARRAY_SIZE(bltins2);
8762 for (funcp = G.top_func; funcp; funcp = funcp->next) {
8763 if (--i < 0)
8764 return funcp->name;
8765 }
8766 }
8767# endif
Francis Laniel110b7692023-12-22 22:02:27 +01008768 return NULL;
8769}
8770#endif
Francis Laniel36836fc2023-12-22 22:02:28 +01008771#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01008772
Francis Laniel36836fc2023-12-22 22:02:28 +01008773#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01008774static void remove_nested_vars(void)
8775{
8776 struct variable *cur;
8777 struct variable **cur_pp;
8778
8779 cur_pp = &G.top_var;
8780 while ((cur = *cur_pp) != NULL) {
8781 if (cur->var_nest_level <= G.var_nest_level) {
8782 cur_pp = &cur->next;
8783 continue;
8784 }
8785 /* Unexport */
8786 if (cur->flg_export) {
8787 debug_printf_env("unexporting nested '%s'/%u\n", cur->varstr, cur->var_nest_level);
8788 bb_unsetenv(cur->varstr);
8789 }
8790 /* Remove from global list */
8791 *cur_pp = cur->next;
8792 /* Free */
8793 if (!cur->max_len) {
8794 debug_printf_env("freeing nested '%s'/%u\n", cur->varstr, cur->var_nest_level);
8795 free(cur->varstr);
8796 }
8797 free(cur);
8798 }
8799}
8800
8801static void enter_var_nest_level(void)
8802{
8803 G.var_nest_level++;
8804 debug_printf_env("var_nest_level++ %u\n", G.var_nest_level);
8805
8806 /* Try: f() { echo -n .; f; }; f
8807 * struct variable::var_nest_level is uint16_t,
8808 * thus limiting recursion to < 2^16.
8809 * In any case, with 8 Mbyte stack SEGV happens
8810 * not too long after 2^16 recursions anyway.
8811 */
8812 if (G.var_nest_level > 0xff00)
8813 bb_error_msg_and_die("fatal recursion (depth %u)", G.var_nest_level);
8814}
8815
8816static void leave_var_nest_level(void)
8817{
8818 G.var_nest_level--;
8819 debug_printf_env("var_nest_level-- %u\n", G.var_nest_level);
8820 if (HUSH_DEBUG && (int)G.var_nest_level < 0)
8821 bb_simple_error_msg_and_die("BUG: nesting underflow");
8822
8823 remove_nested_vars();
8824}
Francis Laniel36836fc2023-12-22 22:02:28 +01008825#endif /* __U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01008826
8827#if ENABLE_HUSH_FUNCTIONS
8828static struct function **find_function_slot(const char *name)
8829{
8830 struct function *funcp;
8831 struct function **funcpp = &G.top_func;
8832
8833 while ((funcp = *funcpp) != NULL) {
8834 if (strcmp(name, funcp->name) == 0) {
8835 debug_printf_exec("found function '%s'\n", name);
8836 break;
8837 }
8838 funcpp = &funcp->next;
8839 }
8840 return funcpp;
8841}
8842
8843static ALWAYS_INLINE const struct function *find_function(const char *name)
8844{
8845 const struct function *funcp = *find_function_slot(name);
8846 return funcp;
8847}
8848
8849/* Note: takes ownership on name ptr */
8850static struct function *new_function(char *name)
8851{
8852 struct function **funcpp = find_function_slot(name);
8853 struct function *funcp = *funcpp;
8854
8855 if (funcp != NULL) {
8856 struct command *cmd = funcp->parent_cmd;
8857 debug_printf_exec("func %p parent_cmd %p\n", funcp, cmd);
8858 if (!cmd) {
8859 debug_printf_exec("freeing & replacing function '%s'\n", funcp->name);
8860 free(funcp->name);
8861 /* Note: if !funcp->body, do not free body_as_string!
8862 * This is a special case of "-F name body" function:
8863 * body_as_string was not malloced! */
8864 if (funcp->body) {
8865 free_pipe_list(funcp->body);
8866# if !BB_MMU
8867 free(funcp->body_as_string);
8868# endif
8869 }
8870 } else {
8871 debug_printf_exec("reinserting in tree & replacing function '%s'\n", funcp->name);
8872 cmd->argv[0] = funcp->name;
8873 cmd->group = funcp->body;
8874# if !BB_MMU
8875 cmd->group_as_string = funcp->body_as_string;
8876# endif
8877 }
8878 } else {
8879 debug_printf_exec("remembering new function '%s'\n", name);
8880 funcp = *funcpp = xzalloc(sizeof(*funcp));
8881 /*funcp->next = NULL;*/
8882 }
8883
8884 funcp->name = name;
8885 return funcp;
8886}
8887
8888# if ENABLE_HUSH_UNSET
8889static void unset_func(const char *name)
8890{
8891 struct function **funcpp = find_function_slot(name);
8892 struct function *funcp = *funcpp;
8893
8894 if (funcp != NULL) {
8895 debug_printf_exec("freeing function '%s'\n", funcp->name);
8896 *funcpp = funcp->next;
8897 /* funcp is unlinked now, deleting it.
8898 * Note: if !funcp->body, the function was created by
8899 * "-F name body", do not free ->body_as_string
8900 * and ->name as they were not malloced. */
8901 if (funcp->body) {
8902 free_pipe_list(funcp->body);
8903 free(funcp->name);
8904# if !BB_MMU
8905 free(funcp->body_as_string);
8906# endif
8907 }
8908 free(funcp);
8909 }
8910}
8911# endif
8912
8913# if BB_MMU
8914#define exec_function(to_free, funcp, argv) \
8915 exec_function(funcp, argv)
8916# endif
8917static void exec_function(char ***to_free,
8918 const struct function *funcp,
8919 char **argv) NORETURN;
8920static void exec_function(char ***to_free,
8921 const struct function *funcp,
8922 char **argv)
8923{
8924# if BB_MMU
8925 int n;
8926
8927 argv[0] = G.global_argv[0];
8928 G.global_argv = argv;
8929 G.global_argc = n = 1 + string_array_len(argv + 1);
8930
8931// Example when we are here: "cmd | func"
8932// func will run with saved-redirect fds open.
8933// $ f() { echo /proc/self/fd/*; }
8934// $ true | f
8935// /proc/self/fd/0 /proc/self/fd/1 /proc/self/fd/2 /proc/self/fd/255 /proc/self/fd/3
8936// stdio^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ G_interactive_fd^ DIR fd for glob
8937// Same in script:
8938// $ . ./SCRIPT
8939// /proc/self/fd/0 /proc/self/fd/1 /proc/self/fd/2 /proc/self/fd/255 /proc/self/fd/3 /proc/self/fd/4
8940// stdio^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ G_interactive_fd^ opened ./SCRIPT DIR fd for glob
8941// They are CLOEXEC so external programs won't see them, but
8942// for "more correctness" we might want to close those extra fds here:
8943//? close_saved_fds_and_FILE_fds();
8944
8945 /* "we are in a function, ok to use return" */
8946 G_flag_return_in_progress = -1;
8947 enter_var_nest_level();
8948 IF_HUSH_LOCAL(G.func_nest_level++;)
8949
8950 /* On MMU, funcp->body is always non-NULL */
8951 n = run_list(funcp->body);
8952 _exit(n);
8953# else
8954//? close_saved_fds_and_FILE_fds();
8955
8956//TODO: check whether "true | func_with_return" works
8957
8958 re_execute_shell(to_free,
8959 funcp->body_as_string,
8960 G.global_argv[0],
8961 argv + 1,
8962 NULL);
8963# endif
8964}
8965
8966static int run_function(const struct function *funcp, char **argv)
8967{
8968 int rc;
8969 save_arg_t sv;
8970 smallint sv_flg;
8971
8972 save_and_replace_G_args(&sv, argv);
8973
8974 /* "We are in function, ok to use return" */
8975 sv_flg = G_flag_return_in_progress;
8976 G_flag_return_in_progress = -1;
8977
8978 /* Make "local" variables properly shadow previous ones */
8979 IF_HUSH_LOCAL(enter_var_nest_level();)
8980 IF_HUSH_LOCAL(G.func_nest_level++;)
8981
8982 /* On MMU, funcp->body is always non-NULL */
8983# if !BB_MMU
8984 if (!funcp->body) {
8985 /* Function defined by -F */
8986 parse_and_run_string(funcp->body_as_string);
8987 rc = G.last_exitcode;
8988 } else
8989# endif
8990 {
8991 rc = run_list(funcp->body);
8992 }
8993
8994 IF_HUSH_LOCAL(G.func_nest_level--;)
8995 IF_HUSH_LOCAL(leave_var_nest_level();)
8996
8997 G_flag_return_in_progress = sv_flg;
8998# if ENABLE_HUSH_TRAP
8999 debug_printf_exec("G.return_exitcode=-1\n");
9000 G.return_exitcode = -1; /* invalidate stashed return value */
9001# endif
9002
9003 restore_G_args(&sv, argv);
9004
9005 return rc;
9006}
9007#endif /* ENABLE_HUSH_FUNCTIONS */
9008
9009
Francis Laniel36836fc2023-12-22 22:02:28 +01009010#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01009011#if BB_MMU
9012#define exec_builtin(to_free, x, argv) \
9013 exec_builtin(x, argv)
9014#else
9015#define exec_builtin(to_free, x, argv) \
9016 exec_builtin(to_free, argv)
9017#endif
9018static void exec_builtin(char ***to_free,
9019 const struct built_in_command *x,
9020 char **argv) NORETURN;
9021static void exec_builtin(char ***to_free,
9022 const struct built_in_command *x,
9023 char **argv)
9024{
9025#if BB_MMU
9026 int rcode;
9027//? close_saved_fds_and_FILE_fds();
9028 rcode = x->b_function(argv);
9029 fflush_all();
9030 _exit(rcode);
9031#else
9032 fflush_all();
9033 /* On NOMMU, we must never block!
9034 * Example: { sleep 99 | read line; } & echo Ok
9035 */
9036 re_execute_shell(to_free,
9037 argv[0],
9038 G.global_argv[0],
9039 G.global_argv + 1,
9040 argv);
9041#endif
9042}
Francis Laniel36836fc2023-12-22 22:02:28 +01009043#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01009044
9045
Francis Laniel36836fc2023-12-22 22:02:28 +01009046#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01009047static void execvp_or_die(char **argv) NORETURN;
9048static void execvp_or_die(char **argv)
9049{
9050 int e;
9051 debug_printf_exec("execing '%s'\n", argv[0]);
9052 /* Don't propagate SIG_IGN to the child */
9053 if (SPECIAL_JOBSTOP_SIGS != 0)
9054 switch_off_special_sigs(G.special_sig_mask & SPECIAL_JOBSTOP_SIGS);
9055 execvp(argv[0], argv);
9056 e = 2;
9057 if (errno == EACCES) e = 126;
9058 if (errno == ENOENT) e = 127;
9059 bb_perror_msg("can't execute '%s'", argv[0]);
9060 _exit(e);
9061}
9062
9063#if ENABLE_HUSH_MODE_X
9064static void x_mode_print_optionally_squoted(const char *str)
9065{
9066 unsigned len;
9067 const char *cp;
9068
9069 cp = str;
9070
9071 /* the set of chars which-cause-string-to-be-squoted mimics bash */
9072 /* test a char with: bash -c 'set -x; echo "CH"' */
9073 if (str[strcspn(str, "\\\"'`$(){}[]<>;#&|~*?!^"
9074 " " "\001\002\003\004\005\006\007"
9075 "\010\011\012\013\014\015\016\017"
9076 "\020\021\022\023\024\025\026\027"
9077 "\030\031\032\033\034\035\036\037"
9078 )
9079 ] == '\0'
9080 ) {
9081 /* string has no special chars */
9082 x_mode_addstr(str);
9083 return;
9084 }
9085
9086 cp = str;
9087 for (;;) {
9088 /* print '....' up to EOL or first squote */
9089 len = (int)(strchrnul(cp, '\'') - cp);
9090 if (len != 0) {
9091 x_mode_addchr('\'');
9092 x_mode_addblock(cp, len);
9093 x_mode_addchr('\'');
9094 cp += len;
9095 }
9096 if (*cp == '\0')
9097 break;
9098 /* string contains squote(s), print them as \' */
9099 x_mode_addchr('\\');
9100 x_mode_addchr('\'');
9101 cp++;
9102 }
9103}
9104static void dump_cmd_in_x_mode(char **argv)
9105{
9106 if (G_x_mode && argv) {
9107 unsigned n;
9108
9109 /* "+[+++...][ cmd...]\n\0" */
9110 x_mode_prefix();
9111 n = 0;
9112 while (argv[n]) {
9113 x_mode_addchr(' ');
9114 if (argv[n][0] == '\0') {
9115 x_mode_addchr('\'');
9116 x_mode_addchr('\'');
9117 } else {
9118 x_mode_print_optionally_squoted(argv[n]);
9119 }
9120 n++;
9121 }
9122 x_mode_flush();
9123 }
9124}
9125#else
9126# define dump_cmd_in_x_mode(argv) ((void)0)
9127#endif
Francis Laniel36836fc2023-12-22 22:02:28 +01009128#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01009129
Francis Laniel36836fc2023-12-22 22:02:28 +01009130#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01009131#if ENABLE_HUSH_COMMAND
9132static void if_command_vV_print_and_exit(char opt_vV, char *cmd, const char *explanation)
9133{
9134 char *to_free;
9135
9136 if (!opt_vV)
9137 return;
9138
9139 to_free = NULL;
9140 if (!explanation) {
9141 char *path = getenv("PATH");
9142 explanation = to_free = find_executable(cmd, &path); /* path == NULL is ok */
9143 if (!explanation)
9144 _exit(1); /* PROG was not found */
9145 if (opt_vV != 'V')
9146 cmd = to_free; /* -v PROG prints "/path/to/PROG" */
9147 }
9148 printf((opt_vV == 'V') ? "%s is %s\n" : "%s\n", cmd, explanation);
9149 free(to_free);
9150 fflush_all();
9151 _exit(0);
9152}
9153#else
9154# define if_command_vV_print_and_exit(a,b,c) ((void)0)
9155#endif
Francis Laniel36836fc2023-12-22 22:02:28 +01009156#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01009157
9158#if BB_MMU
9159#define pseudo_exec_argv(nommu_save, argv, assignment_cnt, argv_expanded) \
9160 pseudo_exec_argv(argv, assignment_cnt, argv_expanded)
9161#define pseudo_exec(nommu_save, command, argv_expanded) \
9162 pseudo_exec(command, argv_expanded)
9163#endif
9164
Francis Laniel36836fc2023-12-22 22:02:28 +01009165#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01009166/* Called after [v]fork() in run_pipe, or from builtin_exec.
9167 * Never returns.
9168 * Don't exit() here. If you don't exec, use _exit instead.
9169 * The at_exit handlers apparently confuse the calling process,
9170 * in particular stdin handling. Not sure why? -- because of vfork! (vda)
9171 */
9172static void pseudo_exec_argv(nommu_save_t *nommu_save,
9173 char **argv, int assignment_cnt,
9174 char **argv_expanded) NORETURN;
9175static NOINLINE void pseudo_exec_argv(nommu_save_t *nommu_save,
9176 char **argv, int assignment_cnt,
9177 char **argv_expanded)
9178{
9179 const struct built_in_command *x;
9180 struct variable **sv_shadowed;
9181 char **new_env;
9182 IF_HUSH_COMMAND(char opt_vV = 0;)
9183 IF_HUSH_FUNCTIONS(const struct function *funcp;)
9184
9185 new_env = expand_assignments(argv, assignment_cnt);
9186 dump_cmd_in_x_mode(new_env);
9187
9188 if (!argv[assignment_cnt]) {
9189 /* Case when we are here: ... | var=val | ...
9190 * (note that we do not exit early, i.e., do not optimize out
9191 * expand_assignments(): think about ... | var=`sleep 1` | ...
9192 */
9193 free_strings(new_env);
Francis Laniele7ca3a32023-12-22 22:02:42 +01009194 _exit_SUCCESS();
Francis Laniel110b7692023-12-22 22:02:27 +01009195 }
9196
9197 sv_shadowed = G.shadowed_vars_pp;
9198#if BB_MMU
9199 G.shadowed_vars_pp = NULL; /* "don't save, free them instead" */
9200#else
9201 G.shadowed_vars_pp = &nommu_save->old_vars;
9202 G.var_nest_level++;
9203#endif
9204 set_vars_and_save_old(new_env);
9205 G.shadowed_vars_pp = sv_shadowed;
9206
9207 if (argv_expanded) {
9208 argv = argv_expanded;
9209 } else {
9210 argv = expand_strvec_to_strvec(argv + assignment_cnt);
9211#if !BB_MMU
9212 nommu_save->argv = argv;
9213#endif
9214 }
9215 dump_cmd_in_x_mode(argv);
9216
9217#if ENABLE_FEATURE_SH_STANDALONE || BB_MMU
9218 if (strchr(argv[0], '/') != NULL)
9219 goto skip;
9220#endif
9221
9222#if ENABLE_HUSH_FUNCTIONS
9223 /* Check if the command matches any functions (this goes before bltins) */
9224 funcp = find_function(argv[0]);
9225 if (funcp)
9226 exec_function(&nommu_save->argv_from_re_execing, funcp, argv);
9227#endif
9228
9229#if ENABLE_HUSH_COMMAND
9230 /* "command BAR": run BAR without looking it up among functions
9231 * "command -v BAR": print "BAR" or "/path/to/BAR"; or exit 1
9232 * "command -V BAR": print "BAR is {a function,a shell builtin,/path/to/BAR}"
9233 */
9234 while (strcmp(argv[0], "command") == 0 && argv[1]) {
9235 char *p;
9236
9237 argv++;
9238 p = *argv;
9239 if (p[0] != '-' || !p[1])
9240 continue; /* bash allows "command command command [-OPT] BAR" */
9241
9242 for (;;) {
9243 p++;
9244 switch (*p) {
9245 case '\0':
9246 argv++;
9247 p = *argv;
9248 if (p[0] != '-' || !p[1])
9249 goto after_opts;
9250 continue; /* next arg is also -opts, process it too */
9251 case 'v':
9252 case 'V':
9253 opt_vV = *p;
9254 continue;
9255 default:
9256 bb_error_msg_and_die("%s: %s: invalid option", "command", argv[0]);
9257 }
9258 }
9259 }
9260 after_opts:
9261# if ENABLE_HUSH_FUNCTIONS
9262 if (opt_vV && find_function(argv[0]))
9263 if_command_vV_print_and_exit(opt_vV, argv[0], "a function");
9264# endif
9265#endif
9266
9267 /* Check if the command matches any of the builtins.
9268 * Depending on context, this might be redundant. But it's
9269 * easier to waste a few CPU cycles than it is to figure out
9270 * if this is one of those cases.
9271 */
9272 /* Why "BB_MMU ? :" difference in logic? -
9273 * On NOMMU, it is more expensive to re-execute shell
9274 * just in order to run echo or test builtin.
9275 * It's better to skip it here and run corresponding
9276 * non-builtin later. */
9277 x = BB_MMU ? find_builtin(argv[0]) : find_builtin1(argv[0]);
9278 if (x) {
9279 if_command_vV_print_and_exit(opt_vV, argv[0], "a shell builtin");
9280 exec_builtin(&nommu_save->argv_from_re_execing, x, argv);
9281 }
9282
9283#if ENABLE_FEATURE_SH_STANDALONE
9284 /* Check if the command matches any busybox applets */
9285 {
9286 int a = find_applet_by_name(argv[0]);
9287 if (a >= 0) {
9288 if_command_vV_print_and_exit(opt_vV, argv[0], "an applet");
9289# if BB_MMU /* see above why on NOMMU it is not allowed */
9290 if (APPLET_IS_NOEXEC(a)) {
9291 /* Do not leak open fds from opened script files etc.
9292 * Testcase: interactive "ls -l /proc/self/fd"
9293 * should not show tty fd open.
9294 */
9295 close_saved_fds_and_FILE_fds();
9296//FIXME: should also close saved redir fds
9297//This casuses test failures in
9298//redir_children_should_not_see_saved_fd_2.tests
9299//redir_children_should_not_see_saved_fd_3.tests
9300//if you replace "busybox find" with just "find" in them
9301 /* Without this, "rm -i FILE" can't be ^C'ed: */
9302 switch_off_special_sigs(G.special_sig_mask);
9303 debug_printf_exec("running applet '%s'\n", argv[0]);
9304 run_noexec_applet_and_exit(a, argv[0], argv);
9305 }
9306# endif
9307 /* Re-exec ourselves */
9308 debug_printf_exec("re-execing applet '%s'\n", argv[0]);
9309 /* Don't propagate SIG_IGN to the child */
9310 if (SPECIAL_JOBSTOP_SIGS != 0)
9311 switch_off_special_sigs(G.special_sig_mask & SPECIAL_JOBSTOP_SIGS);
9312 execv(bb_busybox_exec_path, argv);
9313 /* If they called chroot or otherwise made the binary no longer
9314 * executable, fall through */
9315 }
9316 }
9317#endif
9318
9319#if ENABLE_FEATURE_SH_STANDALONE || BB_MMU
9320 skip:
9321#endif
9322 if_command_vV_print_and_exit(opt_vV, argv[0], NULL);
9323 execvp_or_die(argv);
9324}
9325
9326/* Called after [v]fork() in run_pipe
9327 */
9328static void pseudo_exec(nommu_save_t *nommu_save,
9329 struct command *command,
9330 char **argv_expanded) NORETURN;
9331static void pseudo_exec(nommu_save_t *nommu_save,
9332 struct command *command,
9333 char **argv_expanded)
9334{
9335#if ENABLE_HUSH_FUNCTIONS
9336 if (command->cmd_type == CMD_FUNCDEF) {
9337 /* Ignore funcdefs in pipes:
9338 * true | f() { cmd }
9339 */
9340 _exit(0);
9341 }
9342#endif
9343
9344 if (command->argv) {
9345 pseudo_exec_argv(nommu_save, command->argv,
9346 command->assignment_cnt, argv_expanded);
9347 }
9348
9349 if (command->group) {
9350 /* Cases when we are here:
9351 * ( list )
9352 * { list } &
9353 * ... | ( list ) | ...
9354 * ... | { list } | ...
9355 */
9356#if BB_MMU
9357 int rcode;
9358 debug_printf_exec("pseudo_exec: run_list\n");
9359 reset_traps_to_defaults();
9360 rcode = run_list(command->group);
9361 /* OK to leak memory by not calling free_pipe_list,
9362 * since this process is about to exit */
9363 _exit(rcode);
9364#else
9365 re_execute_shell(&nommu_save->argv_from_re_execing,
9366 command->group_as_string,
9367 G.global_argv[0],
9368 G.global_argv + 1,
9369 NULL);
9370#endif
9371 }
9372
9373 /* Case when we are here: ... | >file */
9374 debug_printf_exec("pseudo_exec'ed null command\n");
Francis Laniele7ca3a32023-12-22 22:02:42 +01009375 _exit_SUCCESS();
Francis Laniel110b7692023-12-22 22:02:27 +01009376}
9377
9378#if ENABLE_HUSH_JOB
9379static const char *get_cmdtext(struct pipe *pi)
9380{
9381 char **argv;
9382 char *p;
9383 int len;
9384
9385 /* This is subtle. ->cmdtext is created only on first backgrounding.
9386 * (Think "cat, <ctrl-z>, fg, <ctrl-z>, fg, <ctrl-z>...." here...)
9387 * On subsequent bg argv is trashed, but we won't use it */
9388 if (pi->cmdtext)
9389 return pi->cmdtext;
9390
9391 argv = pi->cmds[0].argv;
9392 if (!argv) {
9393 pi->cmdtext = xzalloc(1);
9394 return pi->cmdtext;
9395 }
9396 len = 0;
9397 do {
9398 len += strlen(*argv) + 1;
9399 } while (*++argv);
9400 p = xmalloc(len);
9401 pi->cmdtext = p;
9402 argv = pi->cmds[0].argv;
9403 do {
9404 p = stpcpy(p, *argv);
9405 *p++ = ' ';
9406 } while (*++argv);
9407 p[-1] = '\0';
9408 return pi->cmdtext;
9409}
9410
9411static void remove_job_from_table(struct pipe *pi)
9412{
9413 struct pipe *prev_pipe;
9414
9415 if (pi == G.job_list) {
9416 G.job_list = pi->next;
9417 } else {
9418 prev_pipe = G.job_list;
9419 while (prev_pipe->next != pi)
9420 prev_pipe = prev_pipe->next;
9421 prev_pipe->next = pi->next;
9422 }
9423 G.last_jobid = 0;
9424 if (G.job_list)
9425 G.last_jobid = G.job_list->jobid;
9426}
9427
9428static void delete_finished_job(struct pipe *pi)
9429{
9430 remove_job_from_table(pi);
9431 free_pipe(pi);
9432}
9433
9434static void clean_up_last_dead_job(void)
9435{
9436 if (G.job_list && !G.job_list->alive_cmds)
9437 delete_finished_job(G.job_list);
9438}
9439
9440static void insert_job_into_table(struct pipe *pi)
9441{
9442 struct pipe *job, **jobp;
9443 int i;
9444
9445 clean_up_last_dead_job();
9446
9447 /* Find the end of the list, and find next job ID to use */
9448 i = 0;
9449 jobp = &G.job_list;
9450 while ((job = *jobp) != NULL) {
9451 if (job->jobid > i)
9452 i = job->jobid;
9453 jobp = &job->next;
9454 }
9455 pi->jobid = i + 1;
9456
9457 /* Create a new job struct at the end */
9458 job = *jobp = xmemdup(pi, sizeof(*pi));
9459 job->next = NULL;
9460 job->cmds = xzalloc(sizeof(pi->cmds[0]) * pi->num_cmds);
9461 /* Cannot copy entire pi->cmds[] vector! This causes double frees */
9462 for (i = 0; i < pi->num_cmds; i++) {
9463 job->cmds[i].pid = pi->cmds[i].pid;
9464 /* all other fields are not used and stay zero */
9465 }
9466 job->cmdtext = xstrdup(get_cmdtext(pi));
9467
9468 if (G_interactive_fd)
9469 printf("[%u] %u %s\n", job->jobid, (unsigned)job->cmds[0].pid, job->cmdtext);
9470 G.last_jobid = job->jobid;
9471}
9472#endif /* JOB */
9473
9474static int job_exited_or_stopped(struct pipe *pi)
9475{
9476 int rcode, i;
9477
9478 if (pi->alive_cmds != pi->stopped_cmds)
9479 return -1;
9480
9481 /* All processes in fg pipe have exited or stopped */
9482 rcode = 0;
9483 i = pi->num_cmds;
9484 while (--i >= 0) {
9485 rcode = pi->cmds[i].cmd_exitcode;
9486 /* usually last process gives overall exitstatus,
9487 * but with "set -o pipefail", last *failed* process does */
9488 if (G.o_opt[OPT_O_PIPEFAIL] == 0 || rcode != 0)
9489 break;
9490 }
9491 IF_HAS_KEYWORDS(if (pi->pi_inverted) rcode = !rcode;)
9492 return rcode;
9493}
9494
9495static int process_wait_result(struct pipe *fg_pipe, pid_t childpid, int status)
9496{
9497#if ENABLE_HUSH_JOB
9498 struct pipe *pi;
9499#endif
9500 int i, dead;
9501
9502 dead = WIFEXITED(status) || WIFSIGNALED(status);
9503
9504#if DEBUG_JOBS
9505 if (WIFSTOPPED(status))
9506 debug_printf_jobs("pid %d stopped by sig %d (exitcode %d)\n",
9507 childpid, WSTOPSIG(status), WEXITSTATUS(status));
9508 if (WIFSIGNALED(status))
9509 debug_printf_jobs("pid %d killed by sig %d (exitcode %d)\n",
9510 childpid, WTERMSIG(status), WEXITSTATUS(status));
9511 if (WIFEXITED(status))
9512 debug_printf_jobs("pid %d exited, exitcode %d\n",
9513 childpid, WEXITSTATUS(status));
9514#endif
9515 /* Were we asked to wait for a fg pipe? */
9516 if (fg_pipe) {
9517 i = fg_pipe->num_cmds;
9518
9519 while (--i >= 0) {
9520 int rcode;
9521
9522 debug_printf_jobs("check pid %d\n", fg_pipe->cmds[i].pid);
9523 if (fg_pipe->cmds[i].pid != childpid)
9524 continue;
9525 if (dead) {
9526 int ex;
9527 fg_pipe->cmds[i].pid = 0;
9528 fg_pipe->alive_cmds--;
9529 ex = WEXITSTATUS(status);
9530 /* bash prints killer signal's name for *last*
9531 * process in pipe (prints just newline for SIGINT/SIGPIPE).
9532 * Mimic this. Example: "sleep 5" + (^\ or kill -QUIT)
9533 */
9534 if (WIFSIGNALED(status)) {
9535 int sig = WTERMSIG(status);
9536#if ENABLE_HUSH_JOB
9537 if (G.run_list_level == 1
9538 /* ^^^^^ Do not print in nested contexts, example:
9539 * echo `sleep 1; sh -c 'kill -9 $$'` - prints "137", NOT "Killed 137"
9540 */
9541 && i == fg_pipe->num_cmds-1
9542 ) {
9543 /* strsignal() is for bash compat. ~600 bloat versus bbox's get_signame() */
9544 puts(sig == SIGINT || sig == SIGPIPE ? "" : strsignal(sig));
9545 }
9546#endif
9547 /* TODO: if (WCOREDUMP(status)) + " (core dumped)"; */
9548 /* MIPS has 128 sigs (1..128), if sig==128,
9549 * 128 + sig would result in exitcode 256 -> 0!
9550 */
9551 ex = 128 | sig;
9552 }
9553 fg_pipe->cmds[i].cmd_exitcode = ex;
9554 } else {
9555 fg_pipe->stopped_cmds++;
9556 }
9557 debug_printf_jobs("fg_pipe: alive_cmds %d stopped_cmds %d\n",
9558 fg_pipe->alive_cmds, fg_pipe->stopped_cmds);
9559 rcode = job_exited_or_stopped(fg_pipe);
9560 if (rcode >= 0) {
9561/* Note: *non-interactive* bash does not continue if all processes in fg pipe
9562 * are stopped. Testcase: "cat | cat" in a script (not on command line!)
9563 * and "killall -STOP cat" */
9564 if (G_interactive_fd) {
9565#if ENABLE_HUSH_JOB
9566 if (fg_pipe->alive_cmds != 0)
9567 insert_job_into_table(fg_pipe);
9568#endif
9569 return rcode;
9570 }
9571 if (fg_pipe->alive_cmds == 0)
9572 return rcode;
9573 }
9574 /* There are still running processes in the fg_pipe */
9575 return -1;
9576 }
9577 /* It wasn't in fg_pipe, look for process in bg pipes */
9578 }
9579
9580#if ENABLE_HUSH_JOB
9581 /* We were asked to wait for bg or orphaned children */
9582 /* No need to remember exitcode in this case */
9583 for (pi = G.job_list; pi; pi = pi->next) {
9584 for (i = 0; i < pi->num_cmds; i++) {
9585 if (pi->cmds[i].pid == childpid)
9586 goto found_pi_and_prognum;
9587 }
9588 }
9589 /* Happens when shell is used as init process (init=/bin/sh) */
9590 debug_printf("checkjobs: pid %d was not in our list!\n", childpid);
9591 return -1; /* this wasn't a process from fg_pipe */
9592
9593 found_pi_and_prognum:
9594 if (dead) {
9595 /* child exited */
9596 int rcode = WEXITSTATUS(status);
9597 if (WIFSIGNALED(status))
9598 /* NB: not 128 + sig, MIPS has sig 128 */
9599 rcode = 128 | WTERMSIG(status);
9600 pi->cmds[i].cmd_exitcode = rcode;
9601 if (G.last_bg_pid == pi->cmds[i].pid)
9602 G.last_bg_pid_exitcode = rcode;
9603 pi->cmds[i].pid = 0;
9604 pi->alive_cmds--;
9605 if (!pi->alive_cmds) {
9606# if ENABLE_HUSH_BASH_COMPAT
9607 G.dead_job_exitcode = job_exited_or_stopped(pi);
9608# endif
9609 if (G_interactive_fd) {
9610 printf(JOB_STATUS_FORMAT, pi->jobid,
9611 "Done", pi->cmdtext);
9612 delete_finished_job(pi);
9613 } else {
9614/*
9615 * bash deletes finished jobs from job table only in interactive mode,
9616 * after "jobs" cmd, or if pid of a new process matches one of the old ones
9617 * (see cleanup_dead_jobs(), delete_old_job(), J_NOTIFIED in bash source).
9618 * Testcase script: "(exit 3) & sleep 1; wait %1; echo $?" prints 3 in bash.
9619 * We only retain one "dead" job, if it's the single job on the list.
9620 * This covers most of real-world scenarios where this is useful.
9621 */
9622 if (pi != G.job_list)
9623 delete_finished_job(pi);
9624 }
9625 }
9626 } else {
9627 /* child stopped */
9628 pi->stopped_cmds++;
9629 }
9630#endif
9631 return -1; /* this wasn't a process from fg_pipe */
9632}
9633
9634/* Check to see if any processes have exited -- if they have,
9635 * figure out why and see if a job has completed.
9636 *
9637 * If non-NULL fg_pipe: wait for its completion or stop.
9638 * Return its exitcode or zero if stopped.
9639 *
9640 * Alternatively (fg_pipe == NULL, waitfor_pid != 0):
9641 * waitpid(WNOHANG), if waitfor_pid exits or stops, return exitcode+1,
9642 * else return <0 if waitpid errors out (e.g. ECHILD: nothing to wait for)
9643 * or 0 if no children changed status.
9644 *
9645 * Alternatively (fg_pipe == NULL, waitfor_pid == 0),
9646 * return <0 if waitpid errors out (e.g. ECHILD: nothing to wait for)
9647 * or 0 if no children changed status.
9648 */
9649static int checkjobs(struct pipe *fg_pipe, pid_t waitfor_pid)
9650{
9651 int attributes;
9652 int status;
9653 int rcode = 0;
9654
9655 debug_printf_jobs("checkjobs %p\n", fg_pipe);
9656
9657 attributes = WUNTRACED;
9658 if (fg_pipe == NULL)
9659 attributes |= WNOHANG;
9660
9661 errno = 0;
9662#if ENABLE_HUSH_FAST
9663 if (G.handled_SIGCHLD == G.count_SIGCHLD) {
9664//bb_error_msg("[%d] checkjobs: G.count_SIGCHLD:%d G.handled_SIGCHLD:%d children?:%d fg_pipe:%p",
9665//getpid(), G.count_SIGCHLD, G.handled_SIGCHLD, G.we_have_children, fg_pipe);
9666 /* There was neither fork nor SIGCHLD since last waitpid */
9667 /* Avoid doing waitpid syscall if possible */
9668 if (!G.we_have_children) {
9669 errno = ECHILD;
9670 return -1;
9671 }
9672 if (fg_pipe == NULL) { /* is WNOHANG set? */
9673 /* We have children, but they did not exit
9674 * or stop yet (we saw no SIGCHLD) */
9675 return 0;
9676 }
9677 /* else: !WNOHANG, waitpid will block, can't short-circuit */
9678 }
9679#endif
9680
9681/* Do we do this right?
9682 * bash-3.00# sleep 20 | false
9683 * <ctrl-Z pressed>
9684 * [3]+ Stopped sleep 20 | false
9685 * bash-3.00# echo $?
9686 * 1 <========== bg pipe is not fully done, but exitcode is already known!
9687 * [hush 1.14.0: yes we do it right]
9688 */
9689 while (1) {
9690 pid_t childpid;
9691#if ENABLE_HUSH_FAST
9692 int i;
9693 i = G.count_SIGCHLD;
9694#endif
9695 childpid = waitpid(-1, &status, attributes);
9696 if (childpid <= 0) {
9697 if (childpid && errno != ECHILD)
9698 bb_simple_perror_msg("waitpid");
9699#if ENABLE_HUSH_FAST
9700 else { /* Until next SIGCHLD, waitpid's are useless */
9701 G.we_have_children = (childpid == 0);
9702 G.handled_SIGCHLD = i;
9703//bb_error_msg("[%d] checkjobs: waitpid returned <= 0, G.count_SIGCHLD:%d G.handled_SIGCHLD:%d", getpid(), G.count_SIGCHLD, G.handled_SIGCHLD);
9704 }
9705#endif
9706 /* ECHILD (no children), or 0 (no change in children status) */
9707 rcode = childpid;
9708 break;
9709 }
9710 rcode = process_wait_result(fg_pipe, childpid, status);
9711 if (rcode >= 0) {
9712 /* fg_pipe exited or stopped */
9713 break;
9714 }
9715 if (childpid == waitfor_pid) { /* "wait PID" */
9716 debug_printf_exec("childpid==waitfor_pid:%d status:0x%08x\n", childpid, status);
9717 rcode = WEXITSTATUS(status);
9718 if (WIFSIGNALED(status))
9719 rcode = 128 | WTERMSIG(status);
9720 if (WIFSTOPPED(status))
9721 /* bash: "cmd & wait $!" and cmd stops: $? = 128 | stopsig */
9722 rcode = 128 | WSTOPSIG(status);
9723 rcode++;
9724 break; /* "wait PID" called us, give it exitcode+1 */
9725 }
9726#if ENABLE_HUSH_BASH_COMPAT
9727 if (-1 == waitfor_pid /* "wait -n" (wait for any one job) */
9728 && G.dead_job_exitcode >= 0 /* some job did finish */
9729 ) {
9730 debug_printf_exec("waitfor_pid:-1\n");
9731 rcode = G.dead_job_exitcode + 1;
9732 break;
9733 }
9734#endif
9735 /* This wasn't one of our processes, or */
9736 /* fg_pipe still has running processes, do waitpid again */
9737 } /* while (waitpid succeeds)... */
9738
9739 return rcode;
9740}
9741
9742#if ENABLE_HUSH_JOB
9743static int checkjobs_and_fg_shell(struct pipe *fg_pipe)
9744{
9745 pid_t p;
9746 int rcode = checkjobs(fg_pipe, 0 /*(no pid to wait for)*/);
9747 if (G_saved_tty_pgrp) {
9748 /* Job finished, move the shell to the foreground */
9749 p = getpgrp(); /* our process group id */
9750 debug_printf_jobs("fg'ing ourself: getpgrp()=%d\n", (int)p);
9751 tcsetpgrp(G_interactive_fd, p);
9752 }
9753 return rcode;
9754}
9755#endif
9756
9757/* Start all the jobs, but don't wait for anything to finish.
9758 * See checkjobs().
9759 *
9760 * Return code is normally -1, when the caller has to wait for children
9761 * to finish to determine the exit status of the pipe. If the pipe
9762 * is a simple builtin command, however, the action is done by the
9763 * time run_pipe returns, and the exit code is provided as the
9764 * return value.
9765 *
9766 * Returns -1 only if started some children. IOW: we have to
9767 * mask out retvals of builtins etc with 0xff!
9768 *
9769 * The only case when we do not need to [v]fork is when the pipe
9770 * is single, non-backgrounded, non-subshell command. Examples:
9771 * cmd ; ... { list } ; ...
9772 * cmd && ... { list } && ...
9773 * cmd || ... { list } || ...
9774 * If it is, then we can run cmd as a builtin, NOFORK,
9775 * or (if SH_STANDALONE) an applet, and we can run the { list }
9776 * with run_list. If it isn't one of these, we fork and exec cmd.
9777 *
9778 * Cases when we must fork:
9779 * non-single: cmd | cmd
9780 * backgrounded: cmd & { list } &
9781 * subshell: ( list ) [&]
9782 */
9783#if !ENABLE_HUSH_MODE_X
9784#define redirect_and_varexp_helper(command, sqp, argv_expanded) \
9785 redirect_and_varexp_helper(command, sqp)
9786#endif
9787static int redirect_and_varexp_helper(
9788 struct command *command,
9789 struct squirrel **sqp,
9790 char **argv_expanded)
9791{
9792 /* Assignments occur before redirects. Try:
9793 * a=`sleep 1` sleep 2 3>/qwe/rty
9794 */
9795
9796 char **new_env = expand_assignments(command->argv, command->assignment_cnt);
9797 dump_cmd_in_x_mode(new_env);
9798 dump_cmd_in_x_mode(argv_expanded);
9799 /* this takes ownership of new_env[i] elements, and frees new_env: */
9800 set_vars_and_save_old(new_env);
9801
9802 return setup_redirects(command, sqp);
9803}
Francis Laniel36836fc2023-12-22 22:02:28 +01009804#endif /* !__U_BOOT__ */
9805
Francis Laniel110b7692023-12-22 22:02:27 +01009806static NOINLINE int run_pipe(struct pipe *pi)
9807{
9808 static const char *const null_ptr = NULL;
9809
9810 int cmd_no;
Francis Laniel36836fc2023-12-22 22:02:28 +01009811#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01009812 int next_infd;
Francis Laniel36836fc2023-12-22 22:02:28 +01009813#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01009814 struct command *command;
9815 char **argv_expanded;
9816 char **argv;
Francis Laniel36836fc2023-12-22 22:02:28 +01009817#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01009818 struct squirrel *squirrel = NULL;
Francis Laniel36836fc2023-12-22 22:02:28 +01009819#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01009820 int rcode;
9821
Francis Laniel36836fc2023-12-22 22:02:28 +01009822#ifdef __U_BOOT__
9823 /*
9824 * Set rcode here to avoid returning a garbage value in the middle of
9825 * the function.
9826 * Also, if an error occurs, rcode value would be changed and last
9827 * return will signal the error.
9828 */
9829 rcode = 0;
9830#endif /* __U_BOOT__ */
9831
Francis Laniel110b7692023-12-22 22:02:27 +01009832 debug_printf_exec("run_pipe start: members:%d\n", pi->num_cmds);
9833 debug_enter();
9834
9835 /* Testcase: set -- q w e; (IFS='' echo "$*"; IFS=''; echo "$*"); echo "$*"
9836 * Result should be 3 lines: q w e, qwe, q w e
9837 */
9838 if (G.ifs_whitespace != G.ifs)
9839 free(G.ifs_whitespace);
9840 G.ifs = get_local_var_value("IFS");
9841 if (G.ifs) {
9842 char *p;
9843 G.ifs_whitespace = (char*)G.ifs;
9844 p = skip_whitespace(G.ifs);
9845 if (*p) {
9846 /* Not all $IFS is whitespace */
9847 char *d;
9848 int len = p - G.ifs;
9849 p = skip_non_whitespace(p);
9850 G.ifs_whitespace = xmalloc(len + strlen(p) + 1); /* can overestimate */
9851 d = mempcpy(G.ifs_whitespace, G.ifs, len);
9852 while (*p) {
9853 if (isspace(*p))
9854 *d++ = *p;
9855 p++;
9856 }
9857 *d = '\0';
9858 }
9859 } else {
9860 G.ifs = defifs;
9861 G.ifs_whitespace = (char*)G.ifs;
9862 }
9863
Francis Laniel36836fc2023-12-22 22:02:28 +01009864#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01009865 IF_HUSH_JOB(pi->pgrp = -1;)
9866 pi->stopped_cmds = 0;
Francis Laniel36836fc2023-12-22 22:02:28 +01009867#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01009868 command = &pi->cmds[0];
9869 argv_expanded = NULL;
9870
Francis Laniel36836fc2023-12-22 22:02:28 +01009871#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01009872 if (pi->num_cmds != 1
9873 || pi->followup == PIPE_BG
9874 || command->cmd_type == CMD_SUBSHELL
9875 ) {
9876 goto must_fork;
9877 }
9878
9879 pi->alive_cmds = 1;
Francis Laniel36836fc2023-12-22 22:02:28 +01009880#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01009881
9882 debug_printf_exec(": group:%p argv:'%s'\n",
9883 command->group, command->argv ? command->argv[0] : "NONE");
9884
9885 if (command->group) {
9886#if ENABLE_HUSH_FUNCTIONS
9887 if (command->cmd_type == CMD_FUNCDEF) {
9888 /* "executing" func () { list } */
9889 struct function *funcp;
9890
9891 funcp = new_function(command->argv[0]);
9892 /* funcp->name is already set to argv[0] */
9893 funcp->body = command->group;
9894# if !BB_MMU
9895 funcp->body_as_string = command->group_as_string;
9896 command->group_as_string = NULL;
9897# endif
9898 command->group = NULL;
9899 command->argv[0] = NULL;
9900 debug_printf_exec("cmd %p has child func at %p\n", command, funcp);
9901 funcp->parent_cmd = command;
9902 command->child_func = funcp;
9903
9904 debug_printf_exec("run_pipe: return EXIT_SUCCESS\n");
9905 debug_leave();
9906 return EXIT_SUCCESS;
9907 }
9908#endif
9909 /* { list } */
9910 debug_printf_exec("non-subshell group\n");
9911 rcode = 1; /* exitcode if redir failed */
Francis Laniel36836fc2023-12-22 22:02:28 +01009912#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01009913 if (setup_redirects(command, &squirrel) == 0) {
Francis Laniel9492c942023-12-22 22:02:39 +01009914#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01009915 debug_printf_exec(": run_list\n");
9916//FIXME: we need to pass squirrel down into run_list()
9917//for SH_STANDALONE case, or else this construct:
9918// { find /proc/self/fd; true; } >FILE; cmd2
9919//has no way of closing saved fd#1 for "find",
9920//and in SH_STANDALONE mode, "find" is not execed,
9921//therefore CLOEXEC on saved fd does not help.
9922 rcode = run_list(command->group) & 0xff;
Francis Laniel9492c942023-12-22 22:02:39 +01009923#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01009924 }
9925 restore_redirects(squirrel);
Francis Laniel36836fc2023-12-22 22:02:28 +01009926#endif /* !__U_BOOT__ */
Francis Laniel9492c942023-12-22 22:02:39 +01009927 IF_HAS_KEYWORDS(if (pi->pi_inverted) rcode = !rcode;)
Francis Laniel110b7692023-12-22 22:02:27 +01009928 debug_leave();
9929 debug_printf_exec("run_pipe: return %d\n", rcode);
9930 return rcode;
9931 }
9932
9933 argv = command->argv ? command->argv : (char **) &null_ptr;
9934 {
Francis Laniel36836fc2023-12-22 22:02:28 +01009935#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01009936 const struct built_in_command *x;
9937 IF_HUSH_FUNCTIONS(const struct function *funcp;)
9938 IF_NOT_HUSH_FUNCTIONS(enum { funcp = 0 };)
9939 struct variable **sv_shadowed;
Francis Laniel36836fc2023-12-22 22:02:28 +01009940#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01009941 struct variable *old_vars;
9942
9943#if ENABLE_HUSH_LINENO_VAR
9944 G.execute_lineno = command->lineno;
9945#endif
9946
9947 if (argv[command->assignment_cnt] == NULL) {
9948 /* Assignments, but no command.
9949 * Ensure redirects take effect (that is, create files).
9950 * Try "a=t >file"
9951 */
9952 unsigned i;
9953 G.expand_exitcode = 0;
9954 only_assignments:
Francis Laniel36836fc2023-12-22 22:02:28 +01009955#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01009956 rcode = setup_redirects(command, &squirrel);
9957 restore_redirects(squirrel);
Francis Laniel36836fc2023-12-22 22:02:28 +01009958#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01009959
9960 /* Set shell variables */
9961 i = 0;
9962 while (i < command->assignment_cnt) {
9963 char *p = expand_string_to_string(argv[i],
9964 EXP_FLAG_ESC_GLOB_CHARS,
9965 /*unbackslash:*/ 1
9966 );
9967#if ENABLE_HUSH_MODE_X
9968 if (G_x_mode) {
9969 char *eq;
9970 if (i == 0)
9971 x_mode_prefix();
9972 x_mode_addchr(' ');
9973 eq = strchrnul(p, '=');
9974 if (*eq) eq++;
9975 x_mode_addblock(p, (eq - p));
9976 x_mode_print_optionally_squoted(eq);
9977 x_mode_flush();
9978 }
9979#endif
9980 debug_printf_env("set shell var:'%s'->'%s'\n", *argv, p);
Francis Laniel36836fc2023-12-22 22:02:28 +01009981#ifndef __U_BOOT__
Francis Laniele7ca3a32023-12-22 22:02:42 +01009982 if (set_local_var0(p)) {
Francis Laniel36836fc2023-12-22 22:02:28 +01009983#else /* __U_BOOT__ */
9984 if (set_local_var_modern(p, /*flag:*/ 0)) {
9985#endif
Francis Laniel110b7692023-12-22 22:02:27 +01009986 /* assignment to readonly var / putenv error? */
9987 rcode = 1;
9988 }
9989 i++;
9990 }
9991 /* Redirect error sets $? to 1. Otherwise,
9992 * if evaluating assignment value set $?, retain it.
9993 * Else, clear $?:
9994 * false; q=`exit 2`; echo $? - should print 2
9995 * false; x=1; echo $? - should print 0
9996 * Because of the 2nd case, we can't just use G.last_exitcode.
9997 */
9998 if (rcode == 0)
9999 rcode = G.expand_exitcode;
10000 IF_HAS_KEYWORDS(if (pi->pi_inverted) rcode = !rcode;)
10001 debug_leave();
10002 debug_printf_exec("run_pipe: return %d\n", rcode);
10003 return rcode;
10004 }
10005
10006 /* Expand the rest into (possibly) many strings each */
10007#if defined(CMD_TEST2_SINGLEWORD_NOGLOB)
10008 if (command->cmd_type == CMD_TEST2_SINGLEWORD_NOGLOB)
10009 argv_expanded = expand_strvec_to_strvec_singleword_noglob(argv + command->assignment_cnt);
10010 else
10011#endif
10012#if defined(CMD_SINGLEWORD_NOGLOB)
10013 if (command->cmd_type == CMD_SINGLEWORD_NOGLOB)
10014 argv_expanded = expand_strvec_to_strvec_singleword_noglob(argv + command->assignment_cnt);
10015 else
10016#endif
10017 argv_expanded = expand_strvec_to_strvec(argv + command->assignment_cnt);
10018
10019 /* If someone gives us an empty string: `cmd with empty output` */
10020 if (!argv_expanded[0]) {
10021 free(argv_expanded);
10022 /* `false` still has to set exitcode 1 */
10023 G.expand_exitcode = G.last_exitcode;
10024 goto only_assignments;
10025 }
10026
10027 old_vars = NULL;
Francis Laniel36836fc2023-12-22 22:02:28 +010010028#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +010010029 sv_shadowed = G.shadowed_vars_pp;
10030
10031 /* Check if argv[0] matches any functions (this goes before bltins) */
10032 IF_HUSH_FUNCTIONS(funcp = find_function(argv_expanded[0]);)
10033 IF_HUSH_FUNCTIONS(x = NULL;)
10034 IF_HUSH_FUNCTIONS(if (!funcp))
10035 x = find_builtin(argv_expanded[0]);
10036 if (x || funcp) {
10037 if (x && x->b_function == builtin_exec && argv_expanded[1] == NULL) {
10038 debug_printf("exec with redirects only\n");
10039 /*
10040 * Variable assignments are executed, but then "forgotten":
10041 * a=`sleep 1;echo A` exec 3>&-; echo $a
10042 * sleeps, but prints nothing.
10043 */
10044 enter_var_nest_level();
10045 G.shadowed_vars_pp = &old_vars;
10046 rcode = redirect_and_varexp_helper(command,
10047 /*squirrel:*/ ERR_PTR,
10048 argv_expanded
10049 );
10050 G.shadowed_vars_pp = sv_shadowed;
10051 /* rcode=1 can be if redir file can't be opened */
10052
10053 goto clean_up_and_ret1;
10054 }
10055
10056 /* Bump var nesting, or this will leak exported $a:
10057 * a=b true; env | grep ^a=
10058 */
10059 enter_var_nest_level();
10060 /* Collect all variables "shadowed" by helper
10061 * (IOW: old vars overridden by "var1=val1 var2=val2 cmd..." syntax)
10062 * into old_vars list:
10063 */
10064 G.shadowed_vars_pp = &old_vars;
10065 rcode = redirect_and_varexp_helper(command, &squirrel, argv_expanded);
10066 if (rcode == 0) {
10067 if (!funcp) {
10068 /* Do not collect *to old_vars list* vars shadowed
10069 * by e.g. "local VAR" builtin (collect them
10070 * in the previously nested list instead):
10071 * don't want them to be restored immediately
10072 * after "local" completes.
10073 */
10074 G.shadowed_vars_pp = sv_shadowed;
10075
10076 debug_printf_exec(": builtin '%s' '%s'...\n",
10077 x->b_cmd, argv_expanded[1]);
10078 fflush_all();
10079 rcode = x->b_function(argv_expanded) & 0xff;
10080 fflush_all();
10081 }
10082#if ENABLE_HUSH_FUNCTIONS
10083 else {
10084 debug_printf_exec(": function '%s' '%s'...\n",
10085 funcp->name, argv_expanded[1]);
10086 rcode = run_function(funcp, argv_expanded) & 0xff;
10087 /*
10088 * But do collect *to old_vars list* vars shadowed
10089 * within function execution. To that end, restore
10090 * this pointer _after_ function run:
10091 */
10092 G.shadowed_vars_pp = sv_shadowed;
10093 }
10094#endif
10095 }
10096 } else
10097 if (ENABLE_FEATURE_SH_NOFORK && NUM_APPLETS > 1) {
10098 int n = find_applet_by_name(argv_expanded[0]);
10099 if (n < 0 || !APPLET_IS_NOFORK(n))
10100 goto must_fork;
10101
10102 enter_var_nest_level();
10103 /* Collect all variables "shadowed" by helper into old_vars list */
10104 G.shadowed_vars_pp = &old_vars;
10105 rcode = redirect_and_varexp_helper(command, &squirrel, argv_expanded);
10106 G.shadowed_vars_pp = sv_shadowed;
10107
10108 if (rcode == 0) {
10109 debug_printf_exec(": run_nofork_applet '%s' '%s'...\n",
10110 argv_expanded[0], argv_expanded[1]);
10111 /*
10112 * Note: signals (^C) can't interrupt here.
10113 * We remember them and they will be acted upon
10114 * after applet returns.
10115 * This makes applets which can run for a long time
10116 * and/or wait for user input ineligible for NOFORK:
10117 * for example, "yes" or "rm" (rm -i waits for input).
10118 */
10119 rcode = run_nofork_applet(n, argv_expanded);
10120 }
10121 } else
10122 goto must_fork;
10123
10124 restore_redirects(squirrel);
10125 clean_up_and_ret1:
10126 leave_var_nest_level();
10127 add_vars(old_vars);
10128
10129 /*
10130 * Try "usleep 99999999" + ^C + "echo $?"
10131 * with FEATURE_SH_NOFORK=y.
10132 */
10133 if (!funcp) {
10134 /* It was builtin or nofork.
10135 * if this would be a real fork/execed program,
10136 * it should have died if a fatal sig was received.
10137 * But OTOH, there was no separate process,
10138 * the sig was sent to _shell_, not to non-existing
10139 * child.
10140 * Let's just handle ^C only, this one is obvious:
10141 * we aren't ok with exitcode 0 when ^C was pressed
10142 * during builtin/nofork.
10143 */
10144 if (sigismember(&G.pending_set, SIGINT))
10145 rcode = 128 | SIGINT;
10146 }
10147 free(argv_expanded);
10148 IF_HAS_KEYWORDS(if (pi->pi_inverted) rcode = !rcode;)
10149 debug_leave();
10150 debug_printf_exec("run_pipe return %d\n", rcode);
10151 return rcode;
Francis Laniel36836fc2023-12-22 22:02:28 +010010152#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +010010153 }
10154
Francis Laniel36836fc2023-12-22 22:02:28 +010010155#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +010010156 must_fork:
10157 /* NB: argv_expanded may already be created, and that
10158 * might include `cmd` runs! Do not rerun it! We *must*
10159 * use argv_expanded if it's non-NULL */
10160
10161 /* Going to fork a child per each pipe member */
10162 pi->alive_cmds = 0;
10163 next_infd = 0;
Francis Laniel36836fc2023-12-22 22:02:28 +010010164#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +010010165
10166 cmd_no = 0;
10167 while (cmd_no < pi->num_cmds) {
Francis Laniel36836fc2023-12-22 22:02:28 +010010168#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +010010169 struct fd_pair pipefds;
10170#if !BB_MMU
10171 int sv_var_nest_level = G.var_nest_level;
10172 volatile nommu_save_t nommu_save;
10173 nommu_save.old_vars = NULL;
10174 nommu_save.argv = NULL;
10175 nommu_save.argv_from_re_execing = NULL;
10176#endif
Francis Laniel36836fc2023-12-22 22:02:28 +010010177#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +010010178 command = &pi->cmds[cmd_no];
10179 cmd_no++;
Francis Lanielbfc406a2023-12-22 22:02:33 +010010180
10181#ifdef __U_BOOT__
10182 /* Replace argv and argc by expanded if it exists. */
10183 if (argv_expanded) {
10184 /*
10185 * We need to save a pointer to argv, we will restore it
10186 * later, so it will be freed when pipe is freed.
10187 */
10188 argv = command->argv;
10189
10190 /*
10191 * After expansion, there can be more or less argument, so we need to
10192 * update argc, for example:
10193 * - More arguments:
10194 * foo='bar quuz'
10195 * echo $foo
10196 * - Less arguments:
10197 * echo $foo (if foo was never set)
10198 */
10199 command->argc = list_size(argv_expanded);
10200 command->argv = argv_expanded;
10201 }
10202#endif /* __U_BOOT__ */
10203 if (command->argv) {
Francis Laniel110b7692023-12-22 22:02:27 +010010204 debug_printf_exec(": pipe member '%s' '%s'...\n",
10205 command->argv[0], command->argv[1]);
10206 } else {
10207 debug_printf_exec(": pipe member with no argv\n");
10208 }
10209
Francis Laniel36836fc2023-12-22 22:02:28 +010010210#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +010010211 /* pipes are inserted between pairs of commands */
10212 pipefds.rd = 0;
10213 pipefds.wr = 1;
10214 if (cmd_no < pi->num_cmds)
10215 xpiped_pair(pipefds);
10216
10217#if ENABLE_HUSH_LINENO_VAR
10218 G.execute_lineno = command->lineno;
10219#endif
10220
10221 command->pid = BB_MMU ? fork() : vfork();
10222 if (!command->pid) { /* child */
10223#if ENABLE_HUSH_JOB
10224 disable_restore_tty_pgrp_on_exit();
10225 CLEAR_RANDOM_T(&G.random_gen); /* or else $RANDOM repeats in child */
10226
10227 /* Every child adds itself to new process group
10228 * with pgid == pid_of_first_child_in_pipe */
10229 if (G.run_list_level == 1 && G_interactive_fd) {
10230 pid_t pgrp;
10231 pgrp = pi->pgrp;
10232 if (pgrp < 0) /* true for 1st process only */
10233 pgrp = getpid();
10234 if (setpgid(0, pgrp) == 0
10235 && pi->followup != PIPE_BG
10236 && G_saved_tty_pgrp /* we have ctty */
10237 ) {
10238 /* We do it in *every* child, not just first,
10239 * to avoid races */
10240 tcsetpgrp(G_interactive_fd, pgrp);
10241 }
10242 }
10243#endif
10244 if (pi->alive_cmds == 0 && pi->followup == PIPE_BG) {
10245 /* 1st cmd in backgrounded pipe
10246 * should have its stdin /dev/null'ed */
10247 close(0);
10248 if (open(bb_dev_null, O_RDONLY))
10249 xopen("/", O_RDONLY);
10250 } else {
10251 xmove_fd(next_infd, 0);
10252 }
10253 xmove_fd(pipefds.wr, 1);
10254 if (pipefds.rd > 1)
10255 close(pipefds.rd);
10256 /* Like bash, explicit redirects override pipes,
10257 * and the pipe fd (fd#1) is available for dup'ing:
10258 * "cmd1 2>&1 | cmd2": fd#1 is duped to fd#2, thus stderr
10259 * of cmd1 goes into pipe.
10260 */
10261 if (setup_redirects(command, NULL)) {
10262 /* Happens when redir file can't be opened:
10263 * $ hush -c 'echo FOO >&2 | echo BAR 3>/qwe/rty; echo BAZ'
10264 * FOO
10265 * hush: can't open '/qwe/rty': No such file or directory
10266 * BAZ
10267 * (echo BAR is not executed, it hits _exit(1) below)
10268 */
10269 _exit(1);
10270 }
10271
10272 /* Stores to nommu_save list of env vars putenv'ed
10273 * (NOMMU, on MMU we don't need that) */
10274 /* cast away volatility... */
10275 pseudo_exec((nommu_save_t*) &nommu_save, command, argv_expanded);
10276 /* pseudo_exec() does not return */
10277 }
10278
10279 /* parent or error */
10280#if ENABLE_HUSH_FAST
10281 G.count_SIGCHLD++;
10282//bb_error_msg("[%d] fork in run_pipe: G.count_SIGCHLD:%d G.handled_SIGCHLD:%d", getpid(), G.count_SIGCHLD, G.handled_SIGCHLD);
10283#endif
10284 enable_restore_tty_pgrp_on_exit();
10285#if !BB_MMU
10286 /* Clean up after vforked child */
10287 free(nommu_save.argv);
10288 free(nommu_save.argv_from_re_execing);
10289 G.var_nest_level = sv_var_nest_level;
10290 remove_nested_vars();
10291 add_vars(nommu_save.old_vars);
10292#endif
10293 free(argv_expanded);
10294 argv_expanded = NULL;
10295 if (command->pid < 0) { /* [v]fork failed */
10296 /* Clearly indicate, was it fork or vfork */
10297 bb_simple_perror_msg(BB_MMU ? "vfork"+1 : "vfork");
10298 } else {
10299 pi->alive_cmds++;
10300#if ENABLE_HUSH_JOB
10301 /* Second and next children need to know pid of first one */
10302 if (pi->pgrp < 0)
10303 pi->pgrp = command->pid;
10304#endif
10305 }
10306
10307 if (cmd_no > 1)
10308 close(next_infd);
10309 if (cmd_no < pi->num_cmds)
10310 close(pipefds.wr);
10311 /* Pass read (output) pipe end to next iteration */
10312 next_infd = pipefds.rd;
Francis Laniel36836fc2023-12-22 22:02:28 +010010313#else /* __U_BOOT__ */
10314 /* Process the command */
10315 rcode = cmd_process(G.do_repeat ? CMD_FLAG_REPEAT : 0,
10316 command->argc, command->argv,
10317 &(G.flag_repeat), NULL);
Francis Lanielbfc406a2023-12-22 22:02:33 +010010318
10319 if (argv_expanded) {
10320 /*
10321 * expand_strvec_to_strvec() allocates memory to expand
10322 * argv, we need to free it.
10323 */
10324 free(argv_expanded);
10325
10326 /*
10327 * We also restore command->argv to its original value
10328 * so no memory leak happens.
10329 */
10330 command->argv = argv;
10331
10332 /*
10333 * NOTE argc exists only in U-Boot, so argv freeing does
10334 * not rely on it as this code exists in BusyBox.
10335 */
10336 }
Francis Laniel36836fc2023-12-22 22:02:28 +010010337#endif /* __U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +010010338 }
10339
Francis Laniel36836fc2023-12-22 22:02:28 +010010340#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +010010341 if (!pi->alive_cmds) {
10342 debug_leave();
10343 debug_printf_exec("run_pipe return 1 (all forks failed, no children)\n");
10344 return 1;
10345 }
Francis Laniel36836fc2023-12-22 22:02:28 +010010346#endif /* __U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +010010347
10348 debug_leave();
Francis Laniel36836fc2023-12-22 22:02:28 +010010349#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +010010350 debug_printf_exec("run_pipe return -1 (%u children started)\n", pi->alive_cmds);
10351 return -1;
Francis Laniel36836fc2023-12-22 22:02:28 +010010352#else /* __U_BOOT__ */
10353 debug_printf_exec("run_pipe return %d\n", rcode);
10354 return rcode;
10355#endif /* __U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +010010356}
10357
10358/* NB: called by pseudo_exec, and therefore must not modify any
10359 * global data until exec/_exit (we can be a child after vfork!) */
10360static int run_list(struct pipe *pi)
10361{
10362#if ENABLE_HUSH_CASE
10363 char *case_word = NULL;
10364#endif
10365#if ENABLE_HUSH_LOOPS
10366 struct pipe *loop_top = NULL;
10367 char **for_lcur = NULL;
10368 char **for_list = NULL;
10369#endif
10370 smallint last_followup;
10371 smalluint rcode;
10372#if ENABLE_HUSH_IF || ENABLE_HUSH_CASE
10373 smalluint cond_code = 0;
10374#else
10375 enum { cond_code = 0 };
10376#endif
10377#if HAS_KEYWORDS
10378 smallint rword; /* RES_foo */
10379 smallint last_rword; /* ditto */
10380#endif
10381
Francis Laniel36836fc2023-12-22 22:02:28 +010010382#ifndef __U_BOOT__
Francis Laniele7ca3a32023-12-22 22:02:42 +010010383 debug_printf_exec("run_list lvl %d start\n", G.run_list_level);
Francis Laniel110b7692023-12-22 22:02:27 +010010384 debug_enter();
Francis Laniel36836fc2023-12-22 22:02:28 +010010385#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +010010386
10387#if ENABLE_HUSH_LOOPS
10388 /* Check syntax for "for" */
10389 {
10390 struct pipe *cpipe;
10391 for (cpipe = pi; cpipe; cpipe = cpipe->next) {
10392 if (cpipe->res_word != RES_FOR && cpipe->res_word != RES_IN)
10393 continue;
10394 /* current word is FOR or IN (BOLD in comments below) */
10395 if (cpipe->next == NULL) {
10396 syntax_error("malformed for");
10397 debug_leave();
10398 debug_printf_exec("run_list lvl %d return 1\n", G.run_list_level);
10399 return 1;
10400 }
10401 /* "FOR v; do ..." and "for v IN a b; do..." are ok */
10402 if (cpipe->next->res_word == RES_DO)
10403 continue;
10404 /* next word is not "do". It must be "in" then ("FOR v in ...") */
10405 if (cpipe->res_word == RES_IN /* "for v IN a b; not_do..."? */
10406 || cpipe->next->res_word != RES_IN /* FOR v not_do_and_not_in..."? */
10407 ) {
10408 syntax_error("malformed for");
10409 debug_leave();
10410 debug_printf_exec("run_list lvl %d return 1\n", G.run_list_level);
10411 return 1;
10412 }
10413 }
10414 }
10415#endif
10416
10417 /* Past this point, all code paths should jump to ret: label
10418 * in order to return, no direct "return" statements please.
10419 * This helps to ensure that no memory is leaked. */
10420
10421#if ENABLE_HUSH_JOB
10422 G.run_list_level++;
10423#endif
10424
10425#if HAS_KEYWORDS
10426 rword = RES_NONE;
10427 last_rword = RES_XXXX;
10428#endif
10429 last_followup = PIPE_SEQ;
10430 rcode = G.last_exitcode;
10431
10432 /* Go through list of pipes, (maybe) executing them. */
Francis Laniel36836fc2023-12-22 22:02:28 +010010433#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +010010434 for (; pi; pi = IF_HUSH_LOOPS(rword == RES_DONE ? loop_top : ) pi->next) {
Francis Laniel36836fc2023-12-22 22:02:28 +010010435#else /* __U_BOOT__ */
Francis Lanield0003142023-12-22 22:02:40 +010010436 for (; pi; pi = rword == RES_DONE ? loop_top : pi->next) {
Francis Laniel36836fc2023-12-22 22:02:28 +010010437#endif /* __U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +010010438 int r;
10439 int sv_errexit_depth;
10440
Francis Laniel36836fc2023-12-22 22:02:28 +010010441#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +010010442 if (G.flag_SIGINT)
10443 break;
10444 if (G_flag_return_in_progress == 1)
10445 break;
Francis Laniel9492c942023-12-22 22:02:39 +010010446#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +010010447
10448 IF_HAS_KEYWORDS(rword = pi->res_word;)
Francis Laniele7ca3a32023-12-22 22:02:42 +010010449 debug_printf_exec(": rword:%d cond_code:%d last_rword:%d\n",
Francis Laniel110b7692023-12-22 22:02:27 +010010450 rword, cond_code, last_rword);
10451
10452 sv_errexit_depth = G.errexit_depth;
10453 if (
10454#if ENABLE_HUSH_IF
10455 rword == RES_IF || rword == RES_ELIF ||
10456#endif
10457 pi->followup != PIPE_SEQ
10458 ) {
10459 G.errexit_depth++;
10460 }
10461#if ENABLE_HUSH_LOOPS
10462 if ((rword == RES_WHILE || rword == RES_UNTIL || rword == RES_FOR)
10463 && loop_top == NULL /* avoid bumping G.depth_of_loop twice */
10464 ) {
10465 /* start of a loop: remember where loop starts */
10466 loop_top = pi;
10467 G.depth_of_loop++;
10468 }
10469#endif
10470 /* Still in the same "if...", "then..." or "do..." branch? */
10471 if (IF_HAS_KEYWORDS(rword == last_rword &&) 1) {
10472 if ((rcode == 0 && last_followup == PIPE_OR)
10473 || (rcode != 0 && last_followup == PIPE_AND)
10474 ) {
10475 /* It is "<true> || CMD" or "<false> && CMD"
10476 * and we should not execute CMD */
10477 debug_printf_exec("skipped cmd because of || or &&\n");
10478 last_followup = pi->followup;
10479 goto dont_check_jobs_but_continue;
10480 }
10481 }
10482 last_followup = pi->followup;
Francis Laniel110b7692023-12-22 22:02:27 +010010483#if ENABLE_HUSH_IF
Francis Laniele7ca3a32023-12-22 22:02:42 +010010484 if (cond_code != 0) {
Francis Laniel110b7692023-12-22 22:02:27 +010010485 if (rword == RES_THEN) {
10486 /* if false; then ... fi has exitcode 0! */
10487 G.last_exitcode = rcode = EXIT_SUCCESS;
10488 /* "if <false> THEN cmd": skip cmd */
Francis Laniele7ca3a32023-12-22 22:02:42 +010010489 debug_printf_exec("skipped THEN cmd because IF condition was false\n");
10490 last_rword = rword;
Francis Laniel110b7692023-12-22 22:02:27 +010010491 continue;
10492 }
10493 } else {
Francis Laniele7ca3a32023-12-22 22:02:42 +010010494 if (rword == RES_ELSE
10495 || (rword == RES_ELIF && last_rword != RES_ELIF)
10496 ) {
Francis Laniel110b7692023-12-22 22:02:27 +010010497 /* "if <true> then ... ELSE/ELIF cmd":
10498 * skip cmd and all following ones */
Francis Laniele7ca3a32023-12-22 22:02:42 +010010499 debug_printf_exec("skipped ELSE/ELIF branch because IF condition was true\n");
Francis Laniel110b7692023-12-22 22:02:27 +010010500 break;
10501 }
Francis Laniele7ca3a32023-12-22 22:02:42 +010010502 //if (rword == RES_THEN): "if <true> THEN cmd", run cmd (fall through)
Francis Laniel110b7692023-12-22 22:02:27 +010010503 }
10504#endif
Francis Laniele7ca3a32023-12-22 22:02:42 +010010505 IF_HAS_KEYWORDS(last_rword = rword;)
Francis Laniel110b7692023-12-22 22:02:27 +010010506#if ENABLE_HUSH_LOOPS
10507 if (rword == RES_FOR) { /* && pi->num_cmds - always == 1 */
10508 if (!for_lcur) {
10509 /* first loop through for */
10510
10511 static const char encoded_dollar_at[] ALIGN1 = {
10512 SPECIAL_VAR_SYMBOL, '@' | 0x80, SPECIAL_VAR_SYMBOL, '\0'
10513 }; /* encoded representation of "$@" */
Francis Laniele7ca3a32023-12-22 22:02:42 +010010514 static const char *const encoded_dollar_at_argv[] ALIGN_PTR = {
Francis Laniel110b7692023-12-22 22:02:27 +010010515 encoded_dollar_at, NULL
10516 }; /* argv list with one element: "$@" */
10517 char **vals;
10518
10519 G.last_exitcode = rcode = EXIT_SUCCESS;
10520 vals = (char**)encoded_dollar_at_argv;
10521 if (pi->next->res_word == RES_IN) {
10522 /* if no variable values after "in" we skip "for" */
10523 if (!pi->next->cmds[0].argv) {
10524 debug_printf_exec(": null FOR: exitcode EXIT_SUCCESS\n");
10525 break;
10526 }
10527 vals = pi->next->cmds[0].argv;
10528 } /* else: "for var; do..." -> assume "$@" list */
10529 /* create list of variable values */
10530 debug_print_strings("for_list made from", vals);
10531 for_list = expand_strvec_to_strvec(vals);
10532 for_lcur = for_list;
10533 debug_print_strings("for_list", for_list);
10534 }
10535 if (!*for_lcur) {
10536 /* "for" loop is over, clean up */
10537 free(for_list);
10538 for_list = NULL;
10539 for_lcur = NULL;
10540 break;
10541 }
10542 /* Insert next value from for_lcur */
10543 /* note: *for_lcur already has quotes removed, $var expanded, etc */
Francis Lanield0003142023-12-22 22:02:40 +010010544#ifndef __U_BOOT__
Francis Laniele7ca3a32023-12-22 22:02:42 +010010545 set_local_var_from_halves(pi->cmds[0].argv[0], *for_lcur++);
Francis Lanield0003142023-12-22 22:02:40 +010010546#else /* __U_BOOT__ */
10547 /* We cannot use xasprintf, so we emulate it. */
10548 char *full_var;
10549 char *var = pi->cmds[0].argv[0];
10550 char *val = *for_lcur++;
10551
10552 /* + 1 to take into account =. */
10553 full_var = xmalloc(strlen(var) + strlen(val) + 1);
10554 sprintf(full_var, "%s=%s", var, val);
10555
10556 set_local_var_modern(full_var, /*flag:*/ 0);
10557#endif /* __U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +010010558 continue;
10559 }
10560 if (rword == RES_IN) {
10561 continue; /* "for v IN list;..." - "in" has no cmds anyway */
10562 }
10563 if (rword == RES_DONE) {
10564 continue; /* "done" has no cmds too */
10565 }
10566#endif
10567#if ENABLE_HUSH_CASE
10568 if (rword == RES_CASE) {
10569 debug_printf_exec("CASE cond_code:%d\n", cond_code);
10570 case_word = expand_string_to_string(pi->cmds->argv[0],
10571 EXP_FLAG_ESC_GLOB_CHARS, /*unbackslash:*/ 1);
10572 debug_printf_exec("CASE word1:'%s'\n", case_word);
10573 //unbackslash(case_word);
10574 //debug_printf_exec("CASE word2:'%s'\n", case_word);
10575 continue;
10576 }
10577 if (rword == RES_MATCH) {
10578 char **argv;
10579
10580 debug_printf_exec("MATCH cond_code:%d\n", cond_code);
10581 if (!case_word) /* "case ... matched_word) ... WORD)": we executed selected branch, stop */
10582 break;
10583 /* all prev words didn't match, does this one match? */
10584 argv = pi->cmds->argv;
10585 while (*argv) {
10586 char *pattern;
10587 debug_printf_exec("expand_string_to_string('%s')\n", *argv);
10588 pattern = expand_string_to_string(*argv,
10589 EXP_FLAG_ESC_GLOB_CHARS,
10590 /*unbackslash:*/ 0
10591 );
10592 /* TODO: which FNM_xxx flags to use? */
10593 cond_code = (fnmatch(pattern, case_word, /*flags:*/ 0) != 0);
Francis Laniele7ca3a32023-12-22 22:02:42 +010010594 debug_printf_exec("cond_code=fnmatch(pattern:'%s',str:'%s'):%d\n",
Francis Laniel110b7692023-12-22 22:02:27 +010010595 pattern, case_word, cond_code);
10596 free(pattern);
10597 if (cond_code == 0) {
10598 /* match! we will execute this branch */
10599 free(case_word);
10600 case_word = NULL; /* make future "word)" stop */
10601 break;
10602 }
10603 argv++;
10604 }
10605 continue;
10606 }
10607 if (rword == RES_CASE_BODY) { /* inside of a case branch */
10608 debug_printf_exec("CASE_BODY cond_code:%d\n", cond_code);
10609 if (cond_code != 0)
10610 continue; /* not matched yet, skip this pipe */
10611 }
10612 if (rword == RES_ESAC) {
10613 debug_printf_exec("ESAC cond_code:%d\n", cond_code);
10614 if (case_word) {
10615 /* "case" did not match anything: still set $? (to 0) */
10616 G.last_exitcode = rcode = EXIT_SUCCESS;
10617 }
10618 }
10619#endif
10620 /* Just pressing <enter> in shell should check for jobs.
10621 * OTOH, in non-interactive shell this is useless
10622 * and only leads to extra job checks */
10623 if (pi->num_cmds == 0) {
Francis Laniel36836fc2023-12-22 22:02:28 +010010624#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +010010625 if (G_interactive_fd)
10626 goto check_jobs_and_continue;
Francis Laniel36836fc2023-12-22 22:02:28 +010010627#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +010010628 continue;
10629 }
10630
10631 /* After analyzing all keywords and conditions, we decided
10632 * to execute this pipe. NB: have to do checkjobs(NULL)
10633 * after run_pipe to collect any background children,
10634 * even if list execution is to be stopped. */
10635 debug_printf_exec(": run_pipe with %d members\n", pi->num_cmds);
Francis Laniel36836fc2023-12-22 22:02:28 +010010636#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +010010637#if ENABLE_HUSH_LOOPS
10638 G.flag_break_continue = 0;
10639#endif
Francis Laniel36836fc2023-12-22 22:02:28 +010010640#endif /* !__U_BOOT__ */
Francis Laniele7ca3a32023-12-22 22:02:42 +010010641#ifndef __U_BOOT__
10642 rcode = r = G.o_opt[OPT_O_NOEXEC] ? 0 : run_pipe(pi);
10643 /* NB: rcode is a smalluint, r is int */
10644#else /* __U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +010010645 rcode = r = run_pipe(pi); /* NB: rcode is a smalluint, r is int */
Francis Laniel3b66e572023-12-22 22:02:32 +010010646 if (r <= EXIT_RET_CODE) {
10647 int previous_rcode = G.last_exitcode;
10648 /*
10649 * This magic is to get the exit code given by the user.
10650 * Contrary to old shell code, we use + EXIT_RET_CODE as EXIT_RET_CODE
10651 * equals -2.
10652 */
10653 G.last_exitcode = -r + EXIT_RET_CODE;
Francis Laniel36836fc2023-12-22 22:02:28 +010010654
Francis Laniel3b66e572023-12-22 22:02:32 +010010655 /*
10656 * This case deals with the following:
10657 * => setenv inner 'echo entry inner; exit; echo inner done'
10658 * => setenv outer 'echo entry outer; run inner; echo outer done'
10659 * => run outer
10660 * So, if we are in inner, we need to break and not run the other
10661 * commands.
10662 * Otherwise, we just continue in outer.
10663 * As return code are propagated, we use the previous value to check if
10664 * exit was just called or was propagated.
10665 */
10666 if (previous_rcode != r) {
10667 /*
10668 * If run from run_command, run_command_flags will be set, so we check
10669 * this to know if we are in main input shell.
10670 */
10671 if (!G.run_command_flags)
10672 printf("exit not allowed from main input shell.\n");
10673
10674 break;
10675 }
10676 continue;
Francis Laniel36836fc2023-12-22 22:02:28 +010010677 }
Francis Laniel3b66e572023-12-22 22:02:32 +010010678#endif /* __U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +010010679 if (r != -1) {
10680 /* We ran a builtin, function, or group.
10681 * rcode is already known
10682 * and we don't need to wait for anything. */
10683 debug_printf_exec(": builtin/func exitcode %d\n", rcode);
10684 G.last_exitcode = rcode;
Francis Laniel36836fc2023-12-22 22:02:28 +010010685#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +010010686 check_and_run_traps();
Francis Laniel36836fc2023-12-22 22:02:28 +010010687#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +010010688#if ENABLE_HUSH_TRAP && ENABLE_HUSH_FUNCTIONS
10689 rcode = G.last_exitcode; /* "return" in trap can change it, read back */
10690#endif
Francis Laniel36836fc2023-12-22 22:02:28 +010010691#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +010010692#if ENABLE_HUSH_LOOPS
10693 /* Was it "break" or "continue"? */
10694 if (G.flag_break_continue) {
10695 smallint fbc = G.flag_break_continue;
10696 /* We might fall into outer *loop*,
10697 * don't want to break it too */
10698 if (loop_top) {
10699 G.depth_break_continue--;
10700 if (G.depth_break_continue == 0)
10701 G.flag_break_continue = 0;
10702 /* else: e.g. "continue 2" should *break* once, *then* continue */
10703 } /* else: "while... do... { we are here (innermost list is not a loop!) };...done" */
10704 if (G.depth_break_continue != 0 || fbc == BC_BREAK) {
10705 checkjobs(NULL, 0 /*(no pid to wait for)*/);
10706 break;
10707 }
10708 /* "continue": simulate end of loop */
10709 rword = RES_DONE;
10710 continue;
10711 }
10712#endif
10713 if (G_flag_return_in_progress == 1) {
10714 checkjobs(NULL, 0 /*(no pid to wait for)*/);
10715 break;
10716 }
Francis Laniel36836fc2023-12-22 22:02:28 +010010717
Francis Laniel110b7692023-12-22 22:02:27 +010010718 } else if (pi->followup == PIPE_BG) {
10719 /* What does bash do with attempts to background builtins? */
10720 /* even bash 3.2 doesn't do that well with nested bg:
10721 * try "{ { sleep 10; echo DEEP; } & echo HERE; } &".
10722 * I'm NOT treating inner &'s as jobs */
10723#if ENABLE_HUSH_JOB
10724 if (G.run_list_level == 1)
10725 insert_job_into_table(pi);
10726#endif
10727 /* Last command's pid goes to $! */
10728 G.last_bg_pid = pi->cmds[pi->num_cmds - 1].pid;
10729 G.last_bg_pid_exitcode = 0;
10730 debug_printf_exec(": cmd&: exitcode EXIT_SUCCESS\n");
10731/* Check pi->pi_inverted? "! sleep 1 & echo $?": bash says 1. dash and ash say 0 */
10732 rcode = EXIT_SUCCESS;
10733 goto check_traps;
10734 } else {
10735#if ENABLE_HUSH_JOB
10736 if (G.run_list_level == 1 && G_interactive_fd) {
10737 /* Waits for completion, then fg's main shell */
10738 rcode = checkjobs_and_fg_shell(pi);
10739 debug_printf_exec(": checkjobs_and_fg_shell exitcode %d\n", rcode);
10740 goto check_traps;
10741 }
10742#endif
10743 /* This one just waits for completion */
10744 rcode = checkjobs(pi, 0 /*(no pid to wait for)*/);
10745 debug_printf_exec(": checkjobs exitcode %d\n", rcode);
10746 check_traps:
10747 G.last_exitcode = rcode;
10748 check_and_run_traps();
10749#if ENABLE_HUSH_TRAP && ENABLE_HUSH_FUNCTIONS
10750 rcode = G.last_exitcode; /* "return" in trap can change it, read back */
10751#endif
10752 }
Francis Laniel36836fc2023-12-22 22:02:28 +010010753#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +010010754
Francis Laniel36836fc2023-12-22 22:02:28 +010010755#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +010010756 /* Handle "set -e" */
10757 if (rcode != 0 && G.o_opt[OPT_O_ERREXIT]) {
10758 debug_printf_exec("ERREXIT:1 errexit_depth:%d\n", G.errexit_depth);
10759 if (G.errexit_depth == 0)
10760 hush_exit(rcode);
10761 }
Francis Laniel36836fc2023-12-22 22:02:28 +010010762#else /* __U_BOOT__ */
10763 } /* if (r != -1) */
10764#endif /* __U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +010010765 G.errexit_depth = sv_errexit_depth;
10766
10767 /* Analyze how result affects subsequent commands */
10768#if ENABLE_HUSH_IF
Francis Laniele7ca3a32023-12-22 22:02:42 +010010769 if (rword == RES_IF || rword == RES_ELIF) {
10770 debug_printf_exec("cond_code=rcode:%d\n", rcode);
Francis Laniel110b7692023-12-22 22:02:27 +010010771 cond_code = rcode;
Francis Laniele7ca3a32023-12-22 22:02:42 +010010772 }
Francis Laniel110b7692023-12-22 22:02:27 +010010773#endif
Francis Laniel36836fc2023-12-22 22:02:28 +010010774#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +010010775 check_jobs_and_continue:
10776 checkjobs(NULL, 0 /*(no pid to wait for)*/);
Francis Laniel36836fc2023-12-22 22:02:28 +010010777#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +010010778 dont_check_jobs_but_continue: ;
10779#if ENABLE_HUSH_LOOPS
10780 /* Beware of "while false; true; do ..."! */
10781 if (pi->next
10782 && (pi->next->res_word == RES_DO || pi->next->res_word == RES_DONE)
10783 /* check for RES_DONE is needed for "while ...; do \n done" case */
10784 ) {
10785 if (rword == RES_WHILE) {
10786 if (rcode) {
10787 /* "while false; do...done" - exitcode 0 */
10788 G.last_exitcode = rcode = EXIT_SUCCESS;
10789 debug_printf_exec(": while expr is false: breaking (exitcode:EXIT_SUCCESS)\n");
10790 break;
10791 }
10792 }
10793 if (rword == RES_UNTIL) {
10794 if (!rcode) {
10795 debug_printf_exec(": until expr is true: breaking\n");
10796 break;
10797 }
10798 }
10799 }
10800#endif
10801 } /* for (pi) */
10802
10803#if ENABLE_HUSH_JOB
10804 G.run_list_level--;
10805#endif
10806#if ENABLE_HUSH_LOOPS
10807 if (loop_top)
10808 G.depth_of_loop--;
10809 free(for_list);
10810#endif
10811#if ENABLE_HUSH_CASE
10812 free(case_word);
10813#endif
Francis Laniel36836fc2023-12-22 22:02:28 +010010814#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +010010815 debug_leave();
Francis Laniele7ca3a32023-12-22 22:02:42 +010010816 debug_printf_exec("run_list lvl %d return %d\n", G.run_list_level, rcode);
Francis Laniel36836fc2023-12-22 22:02:28 +010010817#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +010010818 return rcode;
10819}
10820
10821/* Select which version we will use */
10822static int run_and_free_list(struct pipe *pi)
10823{
10824 int rcode = 0;
10825 debug_printf_exec("run_and_free_list entered\n");
Francis Laniel36836fc2023-12-22 22:02:28 +010010826#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +010010827 if (!G.o_opt[OPT_O_NOEXEC]) {
Francis Laniel36836fc2023-12-22 22:02:28 +010010828#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +010010829 debug_printf_exec(": run_list: 1st pipe with %d cmds\n", pi->num_cmds);
10830 rcode = run_list(pi);
Francis Laniel36836fc2023-12-22 22:02:28 +010010831#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +010010832 }
Francis Laniel36836fc2023-12-22 22:02:28 +010010833#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +010010834 /* free_pipe_list has the side effect of clearing memory.
10835 * In the long run that function can be merged with run_list,
10836 * but doing that now would hobble the debugging effort. */
10837 free_pipe_list(pi);
10838 debug_printf_exec("run_and_free_list return %d\n", rcode);
10839 return rcode;
10840}
10841
10842
Francis Laniel36836fc2023-12-22 22:02:28 +010010843#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +010010844static void install_sighandlers(unsigned mask)
10845{
10846 sighandler_t old_handler;
10847 unsigned sig = 0;
10848 while ((mask >>= 1) != 0) {
10849 sig++;
10850 if (!(mask & 1))
10851 continue;
10852 old_handler = install_sighandler(sig, pick_sighandler(sig));
10853 /* POSIX allows shell to re-enable SIGCHLD
10854 * even if it was SIG_IGN on entry.
10855 * Therefore we skip IGN check for it:
10856 */
10857 if (sig == SIGCHLD)
10858 continue;
10859 /* Interactive bash re-enables SIGHUP which is SIG_IGNed on entry.
10860 * Try:
10861 * trap '' hup; bash; echo RET # type "kill -hup $$", see SIGHUP having effect
10862 * trap '' hup; bash -c 'kill -hup $$; echo ALIVE' # here SIGHUP is SIG_IGNed
10863 */
10864 if (sig == SIGHUP && G_interactive_fd)
10865 continue;
10866 /* Unless one of the above signals, is it SIG_IGN? */
10867 if (old_handler == SIG_IGN) {
10868 /* oops... restore back to IGN, and record this fact */
10869 install_sighandler(sig, old_handler);
10870#if ENABLE_HUSH_TRAP
10871 if (!G_traps)
10872 G_traps = xzalloc(sizeof(G_traps[0]) * NSIG);
10873 free(G_traps[sig]);
10874 G_traps[sig] = xzalloc(1); /* == xstrdup(""); */
10875#endif
10876 }
10877 }
10878}
10879
10880/* Called a few times only (or even once if "sh -c") */
10881static void install_special_sighandlers(void)
10882{
10883 unsigned mask;
10884
10885 /* Which signals are shell-special? */
10886 mask = (1 << SIGQUIT) | (1 << SIGCHLD);
10887 if (G_interactive_fd) {
10888 mask |= SPECIAL_INTERACTIVE_SIGS;
10889 if (G_saved_tty_pgrp) /* we have ctty, job control sigs work */
10890 mask |= SPECIAL_JOBSTOP_SIGS;
10891 }
10892 /* Careful, do not re-install handlers we already installed */
10893 if (G.special_sig_mask != mask) {
10894 unsigned diff = mask & ~G.special_sig_mask;
10895 G.special_sig_mask = mask;
10896 install_sighandlers(diff);
10897 }
10898}
10899
10900#if ENABLE_HUSH_JOB
10901/* helper */
10902/* Set handlers to restore tty pgrp and exit */
10903static void install_fatal_sighandlers(void)
10904{
10905 unsigned mask;
10906
10907 /* We will restore tty pgrp on these signals */
10908 mask = 0
10909 /*+ (1 << SIGILL ) * HUSH_DEBUG*/
10910 /*+ (1 << SIGFPE ) * HUSH_DEBUG*/
10911 + (1 << SIGBUS ) * HUSH_DEBUG
10912 + (1 << SIGSEGV) * HUSH_DEBUG
10913 /*+ (1 << SIGTRAP) * HUSH_DEBUG*/
10914 + (1 << SIGABRT)
10915 /* bash 3.2 seems to handle these just like 'fatal' ones */
10916 + (1 << SIGPIPE)
10917 + (1 << SIGALRM)
10918 /* if we are interactive, SIGHUP, SIGTERM and SIGINT are special sigs.
10919 * if we aren't interactive... but in this case
10920 * we never want to restore pgrp on exit, and this fn is not called
10921 */
10922 /*+ (1 << SIGHUP )*/
10923 /*+ (1 << SIGTERM)*/
10924 /*+ (1 << SIGINT )*/
10925 ;
10926 G_fatal_sig_mask = mask;
10927
10928 install_sighandlers(mask);
10929}
10930#endif
10931
10932static int set_mode(int state, char mode, const char *o_opt)
10933{
10934 int idx;
10935 switch (mode) {
10936 case 'n':
Francis Laniele7ca3a32023-12-22 22:02:42 +010010937 /* set -n has no effect in interactive shell */
10938 /* Try: while set -n; do echo $-; done */
10939 if (!G_interactive_fd)
10940 G.o_opt[OPT_O_NOEXEC] = state;
Francis Laniel110b7692023-12-22 22:02:27 +010010941 break;
10942 case 'x':
10943 IF_HUSH_MODE_X(G_x_mode = state;)
10944 IF_HUSH_MODE_X(if (G.x_mode_fd <= 0) G.x_mode_fd = dup_CLOEXEC(2, 10);)
10945 break;
10946 case 'e':
10947 G.o_opt[OPT_O_ERREXIT] = state;
10948 break;
10949 case 'o':
10950 if (!o_opt) {
10951 /* "set -o" or "set +o" without parameter.
10952 * in bash, set -o produces this output:
10953 * pipefail off
10954 * and set +o:
10955 * set +o pipefail
10956 * We always use the second form.
10957 */
10958 const char *p = o_opt_strings;
10959 idx = 0;
10960 while (*p) {
10961 printf("set %co %s\n", (G.o_opt[idx] ? '-' : '+'), p);
10962 idx++;
10963 p += strlen(p) + 1;
10964 }
10965 break;
10966 }
10967 idx = index_in_strings(o_opt_strings, o_opt);
10968 if (idx >= 0) {
10969 G.o_opt[idx] = state;
10970 break;
10971 }
10972 /* fall through to error */
10973 default:
10974 return EXIT_FAILURE;
10975 }
10976 return EXIT_SUCCESS;
10977}
10978
10979int hush_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
10980int hush_main(int argc, char **argv)
10981{
10982 pid_t cached_getpid;
10983 enum {
10984 OPT_login = (1 << 0),
10985 };
10986 unsigned flags;
10987#if !BB_MMU
10988 unsigned builtin_argc = 0;
10989#endif
10990 char **e;
10991 struct variable *cur_var;
10992 struct variable *shell_ver;
10993
10994 INIT_G();
10995 if (EXIT_SUCCESS != 0) /* if EXIT_SUCCESS == 0, it is already done */
10996 G.last_exitcode = EXIT_SUCCESS;
Francis Laniele7ca3a32023-12-22 22:02:42 +010010997#if !BB_MMU
10998 /* "Big heredoc" support via "sh -< STRING" invocation.
10999 * Check it first (do not bother to run the usual init code,
11000 * it is not needed for this case).
11001 */
11002 if (argv[1]
11003 && argv[1][0] == '-' && argv[1][1] == '<' /*&& !argv[1][2]*/
11004 /*&& argv[2] && !argv[3] - we don't check some conditions */
11005 ) {
11006 full_write1_str(argv[2]);
11007 _exit(0);
11008 }
11009 G.argv0_for_re_execing = argv[0];
11010#endif
Francis Laniel110b7692023-12-22 22:02:27 +010011011#if ENABLE_HUSH_TRAP
11012# if ENABLE_HUSH_FUNCTIONS
11013 G.return_exitcode = -1;
11014# endif
11015 G.pre_trap_exitcode = -1;
11016#endif
11017
11018#if ENABLE_HUSH_FAST
11019 G.count_SIGCHLD++; /* ensure it is != G.handled_SIGCHLD */
11020#endif
Francis Laniel110b7692023-12-22 22:02:27 +010011021
11022 cached_getpid = getpid(); /* for tcsetpgrp() during init */
11023 G.root_pid = cached_getpid; /* for $PID (NOMMU can override via -$HEXPID:HEXPPID:...) */
Francis Laniele7ca3a32023-12-22 22:02:42 +010011024 G.root_ppid = getppid(); /* for $PPID (NOMMU can override) */
Francis Laniel110b7692023-12-22 22:02:27 +010011025
11026 /* Deal with HUSH_VERSION */
11027 debug_printf_env("unsetenv '%s'\n", "HUSH_VERSION");
11028 unsetenv("HUSH_VERSION"); /* in case it exists in initial env */
11029 shell_ver = xzalloc(sizeof(*shell_ver));
11030 shell_ver->flg_export = 1;
11031 shell_ver->flg_read_only = 1;
11032 /* Code which handles ${var<op>...} needs writable values for all variables,
11033 * therefore we xstrdup: */
11034 shell_ver->varstr = xstrdup(hush_version_str);
11035
11036 /* Create shell local variables from the values
11037 * currently living in the environment */
11038 G.top_var = shell_ver;
11039 cur_var = G.top_var;
11040 e = environ;
11041 if (e) while (*e) {
11042 char *value = strchr(*e, '=');
11043 if (value) { /* paranoia */
11044 cur_var->next = xzalloc(sizeof(*cur_var));
11045 cur_var = cur_var->next;
11046 cur_var->varstr = *e;
11047 cur_var->max_len = strlen(*e);
11048 cur_var->flg_export = 1;
11049 }
11050 e++;
11051 }
11052 /* (Re)insert HUSH_VERSION into env (AFTER we scanned the env!) */
11053 debug_printf_env("putenv '%s'\n", shell_ver->varstr);
11054 putenv(shell_ver->varstr);
11055
11056 /* Export PWD */
11057 set_pwd_var(SETFLAG_EXPORT);
11058
11059#if BASH_HOSTNAME_VAR
11060 /* Set (but not export) HOSTNAME unless already set */
11061 if (!get_local_var_value("HOSTNAME")) {
11062 struct utsname uts;
11063 uname(&uts);
11064 set_local_var_from_halves("HOSTNAME", uts.nodename);
11065 }
11066#endif
11067 /* IFS is not inherited from the parent environment */
11068 set_local_var_from_halves("IFS", defifs);
11069
11070 if (!get_local_var_value("PATH"))
11071 set_local_var_from_halves("PATH", bb_default_root_path);
11072
11073 /* PS1/PS2 are set later, if we determine that we are interactive */
11074
11075 /* bash also exports SHLVL and _,
11076 * and sets (but doesn't export) the following variables:
11077 * BASH=/bin/bash
11078 * BASH_VERSINFO=([0]="3" [1]="2" [2]="0" [3]="1" [4]="release" [5]="i386-pc-linux-gnu")
11079 * BASH_VERSION='3.2.0(1)-release'
11080 * HOSTTYPE=i386
11081 * MACHTYPE=i386-pc-linux-gnu
11082 * OSTYPE=linux-gnu
11083 * PPID=<NNNNN> - we also do it elsewhere
11084 * EUID=<NNNNN>
11085 * UID=<NNNNN>
11086 * GROUPS=()
11087 * LINES=<NNN>
11088 * COLUMNS=<NNN>
11089 * BASH_ARGC=()
11090 * BASH_ARGV=()
11091 * BASH_LINENO=()
11092 * BASH_SOURCE=()
11093 * DIRSTACK=()
11094 * PIPESTATUS=([0]="0")
11095 * HISTFILE=/<xxx>/.bash_history
11096 * HISTFILESIZE=500
11097 * HISTSIZE=500
11098 * MAILCHECK=60
11099 * PATH=/usr/gnu/bin:/usr/local/bin:/bin:/usr/bin:.
11100 * SHELL=/bin/bash
11101 * SHELLOPTS=braceexpand:emacs:hashall:histexpand:history:interactive-comments:monitor
11102 * TERM=dumb
11103 * OPTERR=1
11104 * OPTIND=1
11105 * PS4='+ '
11106 */
11107
11108#if NUM_SCRIPTS > 0
11109 if (argc < 0) {
11110 char *script = get_script_content(-argc - 1);
11111 G.global_argv = argv;
11112 G.global_argc = string_array_len(argv);
11113 //install_special_sighandlers(); - needed?
11114 parse_and_run_string(script);
11115 goto final_return;
11116 }
11117#endif
11118
11119 /* Initialize some more globals to non-zero values */
11120 die_func = restore_ttypgrp_and__exit;
11121
11122 /* Shell is non-interactive at first. We need to call
11123 * install_special_sighandlers() if we are going to execute "sh <script>",
11124 * "sh -c <cmds>" or login shell's /etc/profile and friends.
11125 * If we later decide that we are interactive, we run install_special_sighandlers()
11126 * in order to intercept (more) signals.
11127 */
11128
11129 /* Parse options */
11130 /* http://www.opengroup.org/onlinepubs/9699919799/utilities/sh.html */
11131 flags = (argv[0] && argv[0][0] == '-') ? OPT_login : 0;
11132 while (1) {
11133 int opt = getopt(argc, argv, "+" /* stop at 1st non-option */
11134 "cexinsl"
11135#if !BB_MMU
Francis Laniele7ca3a32023-12-22 22:02:42 +010011136 "$:R:V:"
11137# if ENABLE_HUSH_LINENO_VAR
11138 "L:"
11139# endif
Francis Laniel110b7692023-12-22 22:02:27 +010011140# if ENABLE_HUSH_FUNCTIONS
11141 "F:"
11142# endif
11143#endif
11144 );
11145 if (opt <= 0)
11146 break;
11147 switch (opt) {
11148 case 'c':
11149 /* Note: -c is not an option with param!
11150 * "hush -c -l SCRIPT" is valid. "hush -cSCRIPT" is not.
11151 */
11152 G.opt_c = 1;
11153 break;
11154 case 'i':
11155 /* Well, we cannot just declare interactiveness,
11156 * we have to have some stuff (ctty, etc) */
11157 /* G_interactive_fd++; */
Francis Laniele7ca3a32023-12-22 22:02:42 +010011158//There are a few cases where bash -i -c 'SCRIPT'
11159//has visible effect (differs from bash -c 'SCRIPT'):
11160//it ignores TERM:
11161// bash -i -c 'kill $$; echo ALIVE'
11162// ALIVE
11163//it resets SIG_IGNed HUP to SIG_DFL:
11164// trap '' hup; bash -i -c 'kill -hup $$; echo ALIVE'
11165// Hangup [the message is not printed by bash, it's the shell which started it]
11166//is talkative about jobs and exiting:
11167// bash -i -c 'sleep 1 & exit'
11168// [1] 16170
11169// exit
11170//includes $ENV file (only if run as "sh"):
11171// echo last >/tmp/ENV; ENV=/tmp/ENV sh -i -c 'echo HERE'
11172// last: cannot open /var/log/wtmp: No such file or directory
11173// HERE
11174//(under "bash", it's the opposite: it runs $BASH_ENV file only *without* -i).
11175//
11176//ash -i -c 'sleep 3; sleep 3', on ^C, drops into a prompt instead of exiting
11177//(this may be a bug, bash does not do this).
11178//(ash -i -c 'sleep 3' won't show this, the last command gets auto-"exec"ed)
11179//
11180//None of the above feel like useful features people would rely on.
Francis Laniel110b7692023-12-22 22:02:27 +010011181 break;
11182 case 's':
11183 G.opt_s = 1;
11184 break;
11185 case 'l':
11186 flags |= OPT_login;
11187 break;
11188#if !BB_MMU
Francis Laniel110b7692023-12-22 22:02:27 +010011189 case '$': {
11190 unsigned long long empty_trap_mask;
11191
11192 G.root_pid = bb_strtou(optarg, &optarg, 16);
11193 optarg++;
11194 G.root_ppid = bb_strtou(optarg, &optarg, 16);
11195 optarg++;
11196 G.last_bg_pid = bb_strtou(optarg, &optarg, 16);
11197 optarg++;
11198 G.last_exitcode = bb_strtou(optarg, &optarg, 16);
11199 optarg++;
11200 builtin_argc = bb_strtou(optarg, &optarg, 16);
11201 optarg++;
11202 empty_trap_mask = bb_strtoull(optarg, &optarg, 16);
11203 if (empty_trap_mask != 0) {
11204 IF_HUSH_TRAP(int sig;)
11205 install_special_sighandlers();
11206# if ENABLE_HUSH_TRAP
11207 G_traps = xzalloc(sizeof(G_traps[0]) * NSIG);
11208 for (sig = 1; sig < NSIG; sig++) {
11209 if (empty_trap_mask & (1LL << sig)) {
11210 G_traps[sig] = xzalloc(1); /* == xstrdup(""); */
11211 install_sighandler(sig, SIG_IGN);
11212 }
11213 }
11214# endif
11215 }
11216# if ENABLE_HUSH_LOOPS
11217 optarg++;
11218 G.depth_of_loop = bb_strtou(optarg, &optarg, 16);
11219# endif
11220 /* Suppress "killed by signal" message, -$ hack is used
11221 * for subshells: echo `sh -c 'kill -9 $$'`
11222 * should be silent.
11223 */
11224 IF_HUSH_JOB(G.run_list_level = 1;)
11225# if ENABLE_HUSH_FUNCTIONS
11226 /* nommu uses re-exec trick for "... | func | ...",
11227 * should allow "return".
11228 * This accidentally allows returns in subshells.
11229 */
11230 G_flag_return_in_progress = -1;
11231# endif
11232 break;
11233 }
11234 case 'R':
11235 case 'V':
11236 set_local_var(xstrdup(optarg), opt == 'R' ? SETFLAG_MAKE_RO : 0);
11237 break;
Francis Laniele7ca3a32023-12-22 22:02:42 +010011238# if ENABLE_HUSH_LINENO_VAR
11239 case 'L':
11240 G.parse_lineno = xatou(optarg);
11241 break;
11242# endif
Francis Laniel110b7692023-12-22 22:02:27 +010011243# if ENABLE_HUSH_FUNCTIONS
11244 case 'F': {
11245 struct function *funcp = new_function(optarg);
11246 /* funcp->name is already set to optarg */
11247 /* funcp->body is set to NULL. It's a special case. */
11248 funcp->body_as_string = argv[optind];
11249 optind++;
11250 break;
11251 }
11252# endif
11253#endif
11254 /*case '?': invalid option encountered (set_mode('?') will fail) */
11255 /*case 'n':*/
11256 /*case 'x':*/
11257 /*case 'e':*/
11258 default:
11259 if (set_mode(1, opt, NULL) == 0) /* no error */
11260 break;
11261 bb_show_usage();
11262 }
11263 } /* option parsing loop */
11264
11265 /* Skip options. Try "hush -l": $1 should not be "-l"! */
11266 G.global_argc = argc - (optind - 1);
11267 G.global_argv = argv + (optind - 1);
11268 G.global_argv[0] = argv[0];
11269
11270 /* If we are login shell... */
11271 if (flags & OPT_login) {
11272 const char *hp = NULL;
11273 HFILE *input;
11274
11275 debug_printf("sourcing /etc/profile\n");
11276 input = hfopen("/etc/profile");
11277 run_profile:
11278 if (input != NULL) {
11279 install_special_sighandlers();
11280 parse_and_run_file(input);
11281 hfclose(input);
11282 }
11283 /* bash: after sourcing /etc/profile,
11284 * tries to source (in the given order):
11285 * ~/.bash_profile, ~/.bash_login, ~/.profile,
11286 * stopping on first found. --noprofile turns this off.
11287 * bash also sources ~/.bash_logout on exit.
11288 * If called as sh, skips .bash_XXX files.
11289 */
11290 if (!hp) { /* unless we looped on the "goto" already */
11291 hp = get_local_var_value("HOME");
11292 if (hp && hp[0]) {
11293 debug_printf("sourcing ~/.profile\n");
11294 hp = concat_path_file(hp, ".profile");
11295 input = hfopen(hp);
11296 free((char*)hp);
11297 goto run_profile;
11298 }
11299 }
11300 }
11301
Francis Laniel36836fc2023-12-22 22:02:28 +010011302#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +010011303 /* -c takes effect *after* -l */
11304 if (G.opt_c) {
11305 /* Possibilities:
11306 * sh ... -c 'script'
11307 * sh ... -c 'script' ARG0 [ARG1...]
11308 * On NOMMU, if builtin_argc != 0,
11309 * sh ... -c 'builtin' BARGV... "" ARG0 [ARG1...]
11310 * "" needs to be replaced with NULL
11311 * and BARGV vector fed to builtin function.
11312 * Note: the form without ARG0 never happens:
11313 * sh ... -c 'builtin' BARGV... ""
11314 */
11315 char *script;
11316
11317 install_special_sighandlers();
11318
11319 G.global_argc--;
11320 G.global_argv++;
11321#if !BB_MMU
11322 if (builtin_argc) {
11323 /* -c 'builtin' [BARGV...] "" ARG0 [ARG1...] */
11324 const struct built_in_command *x;
11325 x = find_builtin(G.global_argv[0]);
11326 if (x) { /* paranoia */
11327 argv = G.global_argv;
11328 G.global_argc -= builtin_argc + 1; /* skip [BARGV...] "" */
11329 G.global_argv += builtin_argc + 1;
11330 G.global_argv[-1] = NULL; /* replace "" */
11331 G.last_exitcode = x->b_function(argv);
11332 }
11333 goto final_return;
11334 }
11335#endif
11336
11337 script = G.global_argv[0];
11338 if (!script)
11339 bb_error_msg_and_die(bb_msg_requires_arg, "-c");
11340 if (!G.global_argv[1]) {
11341 /* -c 'script' (no params): prevent empty $0 */
11342 G.global_argv[0] = argv[0];
11343 } else { /* else -c 'script' ARG0 [ARG1...]: $0 is ARG0 */
11344 G.global_argc--;
11345 G.global_argv++;
11346 }
11347 parse_and_run_string(script);
11348 goto final_return;
11349 }
11350
11351 /* -s is: hush -s ARGV1 ARGV2 (no SCRIPT) */
11352 if (!G.opt_s && G.global_argv[1]) {
11353 HFILE *input;
11354 /*
11355 * "bash <script>" (which is never interactive (unless -i?))
11356 * sources $BASH_ENV here (without scanning $PATH).
11357 * If called as sh, does the same but with $ENV.
11358 * Also NB, per POSIX, $ENV should undergo parameter expansion.
11359 */
11360 G.global_argc--;
11361 G.global_argv++;
11362 debug_printf("running script '%s'\n", G.global_argv[0]);
11363 xfunc_error_retval = 127; /* for "hush /does/not/exist" case */
11364 input = hfopen(G.global_argv[0]);
11365 if (!input) {
11366 bb_simple_perror_msg_and_die(G.global_argv[0]);
11367 }
11368 xfunc_error_retval = 1;
11369 install_special_sighandlers();
11370 parse_and_run_file(input);
11371#if ENABLE_FEATURE_CLEAN_UP
11372 hfclose(input);
11373#endif
11374 goto final_return;
11375 }
11376 /* "implicit" -s: bare interactive hush shows 's' in $- */
11377 G.opt_s = 1;
11378
Francis Laniel36836fc2023-12-22 22:02:28 +010011379#endif /* __U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +010011380 /* Up to here, shell was non-interactive. Now it may become one.
11381 * NB: don't forget to (re)run install_special_sighandlers() as needed.
11382 */
11383
11384 /* A shell is interactive if the '-i' flag was given,
11385 * or if all of the following conditions are met:
11386 * no -c command
11387 * no arguments remaining or the -s flag given
11388 * standard input is a terminal
11389 * standard output is a terminal
11390 * Refer to Posix.2, the description of the 'sh' utility.
11391 */
11392#if ENABLE_HUSH_JOB
11393 if (isatty(STDIN_FILENO) && isatty(STDOUT_FILENO)) {
11394 G_saved_tty_pgrp = tcgetpgrp(STDIN_FILENO);
11395 debug_printf("saved_tty_pgrp:%d\n", G_saved_tty_pgrp);
11396 if (G_saved_tty_pgrp < 0)
11397 G_saved_tty_pgrp = 0;
11398
11399 /* try to dup stdin to high fd#, >= 255 */
11400 G_interactive_fd = dup_CLOEXEC(STDIN_FILENO, 254);
11401 if (G_interactive_fd < 0) {
11402 /* try to dup to any fd */
11403 G_interactive_fd = dup(STDIN_FILENO);
11404 if (G_interactive_fd < 0) {
11405 /* give up */
11406 G_interactive_fd = 0;
11407 G_saved_tty_pgrp = 0;
11408 }
11409 }
11410 }
11411 debug_printf("interactive_fd:%d\n", G_interactive_fd);
11412 if (G_interactive_fd) {
11413 close_on_exec_on(G_interactive_fd);
11414
11415 if (G_saved_tty_pgrp) {
11416 /* If we were run as 'hush &', sleep until we are
11417 * in the foreground (tty pgrp == our pgrp).
11418 * If we get started under a job aware app (like bash),
11419 * make sure we are now in charge so we don't fight over
11420 * who gets the foreground */
11421 while (1) {
11422 pid_t shell_pgrp = getpgrp();
11423 G_saved_tty_pgrp = tcgetpgrp(G_interactive_fd);
11424 if (G_saved_tty_pgrp == shell_pgrp)
11425 break;
11426 /* send TTIN to ourself (should stop us) */
11427 kill(- shell_pgrp, SIGTTIN);
11428 }
11429 }
11430
11431 /* Install more signal handlers */
11432 install_special_sighandlers();
11433
11434 if (G_saved_tty_pgrp) {
11435 /* Set other signals to restore saved_tty_pgrp */
11436 install_fatal_sighandlers();
11437 /* Put ourselves in our own process group
11438 * (bash, too, does this only if ctty is available) */
11439 bb_setpgrp(); /* is the same as setpgid(our_pid, our_pid); */
11440 /* Grab control of the terminal */
11441 tcsetpgrp(G_interactive_fd, cached_getpid);
11442 }
11443 enable_restore_tty_pgrp_on_exit();
11444
11445# if ENABLE_FEATURE_EDITING
11446 G.line_input_state = new_line_input_t(FOR_SHELL);
Francis Laniele7ca3a32023-12-22 22:02:42 +010011447# if ENABLE_FEATURE_TAB_COMPLETION
11448 G.line_input_state->get_exe_name = hush_command_name;
11449# endif
11450# if EDITING_HAS_sh_get_var
11451 G.line_input_state->sh_get_var = get_local_var_value;
Francis Laniel110b7692023-12-22 22:02:27 +010011452# endif
11453# endif
11454# if ENABLE_HUSH_SAVEHISTORY && MAX_HISTORY > 0
11455 {
11456 const char *hp = get_local_var_value("HISTFILE");
11457 if (!hp) {
11458 hp = get_local_var_value("HOME");
11459 if (hp)
11460 hp = concat_path_file(hp, ".hush_history");
11461 } else {
11462 hp = xstrdup(hp);
11463 }
11464 if (hp) {
11465 G.line_input_state->hist_file = hp;
11466 //set_local_var(xasprintf("HISTFILE=%s", ...));
11467 }
11468# if ENABLE_FEATURE_SH_HISTFILESIZE
11469 hp = get_local_var_value("HISTFILESIZE");
11470 G.line_input_state->max_history = size_from_HISTFILESIZE(hp);
11471# endif
11472 }
11473# endif
11474 } else {
11475 install_special_sighandlers();
11476 }
11477#elif ENABLE_HUSH_INTERACTIVE
11478 /* No job control compiled in, only prompt/line editing */
11479 if (isatty(STDIN_FILENO) && isatty(STDOUT_FILENO)) {
11480 G_interactive_fd = dup_CLOEXEC(STDIN_FILENO, 254);
11481 if (G_interactive_fd < 0) {
11482 /* try to dup to any fd */
11483 G_interactive_fd = dup_CLOEXEC(STDIN_FILENO, -1);
11484 if (G_interactive_fd < 0)
11485 /* give up */
11486 G_interactive_fd = 0;
11487 }
11488 }
11489 if (G_interactive_fd) {
11490 close_on_exec_on(G_interactive_fd);
11491 }
11492 install_special_sighandlers();
11493#else
11494 /* We have interactiveness code disabled */
11495 install_special_sighandlers();
11496#endif
11497 /* bash:
11498 * if interactive but not a login shell, sources ~/.bashrc
11499 * (--norc turns this off, --rcfile <file> overrides)
11500 */
11501
11502 if (G_interactive_fd) {
11503#if ENABLE_HUSH_INTERACTIVE && ENABLE_FEATURE_EDITING_FANCY_PROMPT
11504 /* Set (but not export) PS1/2 unless already set */
11505 if (!get_local_var_value("PS1"))
11506 set_local_var_from_halves("PS1", "\\w \\$ ");
11507 if (!get_local_var_value("PS2"))
11508 set_local_var_from_halves("PS2", "> ");
11509#endif
11510 if (!ENABLE_FEATURE_SH_EXTRA_QUIET) {
11511 /* note: ash and hush share this string */
11512 printf("\n\n%s %s\n"
11513 IF_HUSH_HELP("Enter 'help' for a list of built-in commands.\n")
11514 "\n",
11515 bb_banner,
11516 "hush - the humble shell"
11517 );
11518 }
11519 }
11520
11521 parse_and_run_file(hfopen(NULL)); /* stdin */
11522
11523 final_return:
11524 hush_exit(G.last_exitcode);
11525}
11526
11527
Francis Laniel36836fc2023-12-22 22:02:28 +010011528
Francis Laniel110b7692023-12-22 22:02:27 +010011529/*
11530 * Built-ins
11531 */
11532static int FAST_FUNC builtin_true(char **argv UNUSED_PARAM)
11533{
11534 return 0;
11535}
11536
Francis Laniele7ca3a32023-12-22 22:02:42 +010011537static int FAST_FUNC builtin_false(char **argv UNUSED_PARAM)
11538{
11539 return 1;
11540}
11541
Francis Laniel110b7692023-12-22 22:02:27 +010011542#if ENABLE_HUSH_TEST || ENABLE_HUSH_ECHO || ENABLE_HUSH_PRINTF || ENABLE_HUSH_KILL
11543static NOINLINE int run_applet_main(char **argv, int (*applet_main_func)(int argc, char **argv))
11544{
11545 int argc = string_array_len(argv);
11546 return applet_main_func(argc, argv);
11547}
11548#endif
11549#if ENABLE_HUSH_TEST || BASH_TEST2
11550static int FAST_FUNC builtin_test(char **argv)
11551{
11552 return run_applet_main(argv, test_main);
11553}
11554#endif
11555#if ENABLE_HUSH_ECHO
11556static int FAST_FUNC builtin_echo(char **argv)
11557{
11558 return run_applet_main(argv, echo_main);
11559}
11560#endif
11561#if ENABLE_HUSH_PRINTF
11562static int FAST_FUNC builtin_printf(char **argv)
11563{
11564 return run_applet_main(argv, printf_main);
11565}
11566#endif
11567
11568#if ENABLE_HUSH_HELP
11569static int FAST_FUNC builtin_help(char **argv UNUSED_PARAM)
11570{
11571 const struct built_in_command *x;
11572
11573 printf(
11574 "Built-in commands:\n"
11575 "------------------\n");
11576 for (x = bltins1; x != &bltins1[ARRAY_SIZE(bltins1)]; x++) {
11577 if (x->b_descr)
11578 printf("%-10s%s\n", x->b_cmd, x->b_descr);
11579 }
11580 return EXIT_SUCCESS;
11581}
11582#endif
11583
11584#if MAX_HISTORY && ENABLE_FEATURE_EDITING
11585static int FAST_FUNC builtin_history(char **argv UNUSED_PARAM)
11586{
11587 show_history(G.line_input_state);
11588 return EXIT_SUCCESS;
11589}
11590#endif
11591
Francis Laniel110b7692023-12-22 22:02:27 +010011592static int FAST_FUNC builtin_cd(char **argv)
11593{
11594 const char *newdir;
11595
11596 argv = skip_dash_dash(argv);
11597 newdir = argv[0];
11598 if (newdir == NULL) {
11599 /* bash does nothing (exitcode 0) if HOME is ""; if it's unset,
11600 * bash says "bash: cd: HOME not set" and does nothing
11601 * (exitcode 1)
11602 */
11603 const char *home = get_local_var_value("HOME");
11604 newdir = home ? home : "/";
11605 }
11606 if (chdir(newdir)) {
11607 /* Mimic bash message exactly */
11608 bb_perror_msg("cd: %s", newdir);
11609 return EXIT_FAILURE;
11610 }
11611 /* Read current dir (get_cwd(1) is inside) and set PWD.
11612 * Note: do not enforce exporting. If PWD was unset or unexported,
11613 * set it again, but do not export. bash does the same.
11614 */
11615 set_pwd_var(/*flag:*/ 0);
11616 return EXIT_SUCCESS;
11617}
11618
11619static int FAST_FUNC builtin_pwd(char **argv UNUSED_PARAM)
11620{
11621 puts(get_cwd(0));
11622 return EXIT_SUCCESS;
11623}
11624
11625static int FAST_FUNC builtin_eval(char **argv)
11626{
11627 argv = skip_dash_dash(argv);
11628
11629 if (!argv[0])
11630 return EXIT_SUCCESS;
11631
11632 IF_HUSH_MODE_X(G.x_mode_depth++;)
11633 //bb_error_msg("%s: ++x_mode_depth=%d", __func__, G.x_mode_depth);
11634 if (!argv[1]) {
11635 /* bash:
11636 * eval "echo Hi; done" ("done" is syntax error):
11637 * "echo Hi" will not execute too.
11638 */
11639 parse_and_run_string(argv[0]);
11640 } else {
11641 /* "The eval utility shall construct a command by
11642 * concatenating arguments together, separating
11643 * each with a <space> character."
11644 */
11645 char *str, *p;
11646 unsigned len = 0;
11647 char **pp = argv;
11648 do
11649 len += strlen(*pp) + 1;
11650 while (*++pp);
11651 str = p = xmalloc(len);
11652 pp = argv;
11653 for (;;) {
11654 p = stpcpy(p, *pp);
11655 pp++;
11656 if (!*pp)
11657 break;
11658 *p++ = ' ';
11659 }
11660 parse_and_run_string(str);
11661 free(str);
11662 }
11663 IF_HUSH_MODE_X(G.x_mode_depth--;)
11664 //bb_error_msg("%s: --x_mode_depth=%d", __func__, G.x_mode_depth);
11665 return G.last_exitcode;
11666}
11667
11668static int FAST_FUNC builtin_exec(char **argv)
11669{
11670 argv = skip_dash_dash(argv);
11671 if (argv[0] == NULL)
11672 return EXIT_SUCCESS; /* bash does this */
11673
11674 /* Careful: we can end up here after [v]fork. Do not restore
11675 * tty pgrp then, only top-level shell process does that */
11676 if (G_saved_tty_pgrp && getpid() == G.root_pid)
11677 tcsetpgrp(G_interactive_fd, G_saved_tty_pgrp);
11678
11679 /* Saved-redirect fds, script fds and G_interactive_fd are still
11680 * open here. However, they are all CLOEXEC, and execv below
11681 * closes them. Try interactive "exec ls -l /proc/self/fd",
11682 * it should show no extra open fds in the "ls" process.
11683 * If we'd try to run builtins/NOEXECs, this would need improving.
11684 */
11685 //close_saved_fds_and_FILE_fds();
11686
11687 /* TODO: if exec fails, bash does NOT exit! We do.
11688 * We'll need to undo trap cleanup (it's inside execvp_or_die)
11689 * and tcsetpgrp, and this is inherently racy.
11690 */
11691 execvp_or_die(argv);
11692}
11693
11694static int FAST_FUNC builtin_exit(char **argv)
11695{
11696 debug_printf_exec("%s()\n", __func__);
11697
11698 /* interactive bash:
11699 * # trap "echo EEE" EXIT
11700 * # exit
11701 * exit
11702 * There are stopped jobs.
11703 * (if there are _stopped_ jobs, running ones don't count)
11704 * # exit
11705 * exit
11706 * EEE (then bash exits)
11707 *
11708 * TODO: we can use G.exiting = -1 as indicator "last cmd was exit"
11709 */
11710
11711 /* note: EXIT trap is run by hush_exit */
11712 argv = skip_dash_dash(argv);
11713 if (argv[0] == NULL) {
11714#if ENABLE_HUSH_TRAP
11715 if (G.pre_trap_exitcode >= 0) /* "exit" in trap uses $? from before the trap */
11716 hush_exit(G.pre_trap_exitcode);
11717#endif
11718 hush_exit(G.last_exitcode);
11719 }
11720 /* mimic bash: exit 123abc == exit 255 + error msg */
11721 xfunc_error_retval = 255;
11722 /* bash: exit -2 == exit 254, no error msg */
11723 hush_exit(xatoi(argv[0]) & 0xff);
11724}
11725
11726#if ENABLE_HUSH_TYPE
11727/* http://www.opengroup.org/onlinepubs/9699919799/utilities/type.html */
11728static int FAST_FUNC builtin_type(char **argv)
11729{
11730 int ret = EXIT_SUCCESS;
11731
11732 while (*++argv) {
11733 const char *type;
11734 char *path = NULL;
11735
11736 if (0) {} /* make conditional compile easier below */
11737 /*else if (find_alias(*argv))
11738 type = "an alias";*/
11739# if ENABLE_HUSH_FUNCTIONS
11740 else if (find_function(*argv))
11741 type = "a function";
11742# endif
11743 else if (find_builtin(*argv))
11744 type = "a shell builtin";
11745 else if ((path = find_in_path(*argv)) != NULL)
11746 type = path;
11747 else {
11748 bb_error_msg("type: %s: not found", *argv);
11749 ret = EXIT_FAILURE;
11750 continue;
11751 }
11752
11753 printf("%s is %s\n", *argv, type);
11754 free(path);
11755 }
11756
11757 return ret;
11758}
11759#endif
11760
11761#if ENABLE_HUSH_READ
11762/* Interruptibility of read builtin in bash
11763 * (tested on bash-4.2.8 by sending signals (not by ^C)):
11764 *
11765 * Empty trap makes read ignore corresponding signal, for any signal.
11766 *
11767 * SIGINT:
11768 * - terminates non-interactive shell;
11769 * - interrupts read in interactive shell;
11770 * if it has non-empty trap:
11771 * - executes trap and returns to command prompt in interactive shell;
11772 * - executes trap and returns to read in non-interactive shell;
11773 * SIGTERM:
11774 * - is ignored (does not interrupt) read in interactive shell;
11775 * - terminates non-interactive shell;
11776 * if it has non-empty trap:
11777 * - executes trap and returns to read;
11778 * SIGHUP:
11779 * - terminates shell (regardless of interactivity);
11780 * if it has non-empty trap:
11781 * - executes trap and returns to read;
11782 * SIGCHLD from children:
11783 * - does not interrupt read regardless of interactivity:
11784 * try: sleep 1 & read x; echo $x
11785 */
11786static int FAST_FUNC builtin_read(char **argv)
11787{
11788 const char *r;
11789 struct builtin_read_params params;
11790
11791 memset(&params, 0, sizeof(params));
11792
11793 /* "!": do not abort on errors.
11794 * Option string must start with "sr" to match BUILTIN_READ_xxx
11795 */
11796 params.read_flags = getopt32(argv,
11797# if BASH_READ_D
11798 IF_NOT_HUSH_BASH_COMPAT("^")
11799 "!srn:p:t:u:d:" IF_NOT_HUSH_BASH_COMPAT("\0" "-1"/*min 1 arg*/),
11800 &params.opt_n, &params.opt_p, &params.opt_t, &params.opt_u, &params.opt_d
11801# else
11802 IF_NOT_HUSH_BASH_COMPAT("^")
11803 "!srn:p:t:u:" IF_NOT_HUSH_BASH_COMPAT("\0" "-1"/*min 1 arg*/),
11804 &params.opt_n, &params.opt_p, &params.opt_t, &params.opt_u
11805# endif
11806//TODO: print "read: need variable name"
11807//for the case of !BASH "read" with no args (now it fails silently)
11808//(or maybe extend getopt32() to emit a message if "-1" fails)
11809 );
11810 if ((uint32_t)params.read_flags == (uint32_t)-1)
11811 return EXIT_FAILURE;
11812 argv += optind;
11813 params.argv = argv;
11814 params.setvar = set_local_var_from_halves;
11815 params.ifs = get_local_var_value("IFS"); /* can be NULL */
11816
11817 again:
11818 r = shell_builtin_read(&params);
11819
11820 if ((uintptr_t)r == 1 && errno == EINTR) {
11821 unsigned sig = check_and_run_traps();
11822 if (sig != SIGINT)
11823 goto again;
11824 }
11825
11826 if ((uintptr_t)r > 1) {
11827 bb_simple_error_msg(r);
11828 r = (char*)(uintptr_t)1;
11829 }
11830
11831 return (uintptr_t)r;
11832}
11833#endif
11834
11835#if ENABLE_HUSH_UMASK
11836static int FAST_FUNC builtin_umask(char **argv)
11837{
11838 int rc;
11839 mode_t mask;
11840
11841 rc = 1;
11842 mask = umask(0);
11843 argv = skip_dash_dash(argv);
11844 if (argv[0]) {
11845 mode_t old_mask = mask;
11846
11847 /* numeric umasks are taken as-is */
11848 /* symbolic umasks are inverted: "umask a=rx" calls umask(222) */
11849 if (!isdigit(argv[0][0]))
11850 mask ^= 0777;
11851 mask = bb_parse_mode(argv[0], mask);
11852 if (!isdigit(argv[0][0]))
11853 mask ^= 0777;
11854 if ((unsigned)mask > 0777) {
11855 mask = old_mask;
11856 /* bash messages:
11857 * bash: umask: 'q': invalid symbolic mode operator
11858 * bash: umask: 999: octal number out of range
11859 */
11860 bb_error_msg("%s: invalid mode '%s'", "umask", argv[0]);
11861 rc = 0;
11862 }
11863 } else {
11864 /* Mimic bash */
11865 printf("%04o\n", (unsigned) mask);
11866 /* fall through and restore mask which we set to 0 */
11867 }
11868 umask(mask);
11869
11870 return !rc; /* rc != 0 - success */
11871}
11872#endif
11873
Francis Laniele7ca3a32023-12-22 22:02:42 +010011874#if ENABLE_HUSH_EXPORT || ENABLE_HUSH_READONLY || ENABLE_HUSH_SET || ENABLE_HUSH_TRAP
Francis Laniel110b7692023-12-22 22:02:27 +010011875static void print_escaped(const char *s)
11876{
Francis Laniele7ca3a32023-12-22 22:02:42 +010011877//TODO? bash "set" does not quote variables which contain only alnums and "%+,-./:=@_~",
11878// (but "export" quotes all variables, even with only these chars).
11879// I think quoting strings with %+,=~ looks better
11880// (example: "set" printing var== instead of var='=' looks strange)
11881// IOW: do not quote "-./:@_": / is used in pathnames, : in PATH, -._ often in file names, @ in emails
11882
Francis Laniel110b7692023-12-22 22:02:27 +010011883 if (*s == '\'')
11884 goto squote;
11885 do {
11886 const char *p = strchrnul(s, '\'');
11887 /* print 'xxxx', possibly just '' */
11888 printf("'%.*s'", (int)(p - s), s);
11889 if (*p == '\0')
11890 break;
11891 s = p;
11892 squote:
11893 /* s points to '; print "'''...'''" */
11894 putchar('"');
11895 do putchar('\''); while (*++s == '\'');
11896 putchar('"');
11897 } while (*s);
11898}
11899#endif
11900
11901#if ENABLE_HUSH_EXPORT || ENABLE_HUSH_LOCAL || ENABLE_HUSH_READONLY
11902static int helper_export_local(char **argv, unsigned flags)
11903{
11904 do {
11905 char *name = *argv;
11906 const char *name_end = endofname(name);
11907
11908 if (*name_end == '\0') {
11909 struct variable *var, **vpp;
11910
Francis Laniele7ca3a32023-12-22 22:02:42 +010011911 vpp = get_ptr_to_local_var(name);
Francis Laniel110b7692023-12-22 22:02:27 +010011912 var = vpp ? *vpp : NULL;
11913
11914 if (flags & SETFLAG_UNEXPORT) {
11915 /* export -n NAME (without =VALUE) */
11916 if (var) {
11917 var->flg_export = 0;
11918 debug_printf_env("%s: unsetenv '%s'\n", __func__, name);
11919 unsetenv(name);
11920 } /* else: export -n NOT_EXISTING_VAR: no-op */
11921 continue;
11922 }
11923 if (flags & SETFLAG_EXPORT) {
11924 /* export NAME (without =VALUE) */
11925 if (var) {
11926 var->flg_export = 1;
11927 debug_printf_env("%s: putenv '%s'\n", __func__, var->varstr);
11928 putenv(var->varstr);
11929 continue;
11930 }
11931 }
11932 if (flags & SETFLAG_MAKE_RO) {
11933 /* readonly NAME (without =VALUE) */
11934 if (var) {
11935 var->flg_read_only = 1;
11936 continue;
11937 }
11938 }
11939# if ENABLE_HUSH_LOCAL
11940 /* Is this "local" bltin? */
11941 if (!(flags & (SETFLAG_EXPORT|SETFLAG_UNEXPORT|SETFLAG_MAKE_RO))) {
11942 unsigned lvl = flags >> SETFLAG_VARLVL_SHIFT;
11943 if (var && var->var_nest_level == lvl) {
11944 /* "local x=abc; ...; local x" - ignore second local decl */
11945 continue;
11946 }
11947 }
11948# endif
11949 /* Exporting non-existing variable.
11950 * bash does not put it in environment,
11951 * but remembers that it is exported,
11952 * and does put it in env when it is set later.
11953 * We just set it to "" and export.
11954 */
11955 /* Or, it's "local NAME" (without =VALUE).
11956 * bash sets the value to "".
11957 */
11958 /* Or, it's "readonly NAME" (without =VALUE).
11959 * bash remembers NAME and disallows its creation
11960 * in the future.
11961 */
11962 name = xasprintf("%s=", name);
11963 } else {
11964 if (*name_end != '=') {
11965 bb_error_msg("'%s': bad variable name", name);
11966 /* do not parse following argv[]s: */
11967 return 1;
11968 }
11969 /* (Un)exporting/making local NAME=VALUE */
11970 name = xstrdup(name);
11971 /* Testcase: export PS1='\w \$ ' */
11972 unbackslash(name);
11973 }
11974 debug_printf_env("%s: set_local_var('%s')\n", __func__, name);
11975 if (set_local_var(name, flags))
11976 return EXIT_FAILURE;
11977 } while (*++argv);
11978 return EXIT_SUCCESS;
11979}
11980#endif
11981
11982#if ENABLE_HUSH_EXPORT
11983static int FAST_FUNC builtin_export(char **argv)
11984{
11985 unsigned opt_unexport;
11986
11987# if ENABLE_HUSH_EXPORT_N
11988 /* "!": do not abort on errors */
11989 opt_unexport = getopt32(argv, "!n");
11990 if (opt_unexport == (uint32_t)-1)
11991 return EXIT_FAILURE;
11992 argv += optind;
11993# else
11994 opt_unexport = 0;
11995 argv++;
11996# endif
11997
11998 if (argv[0] == NULL) {
11999 char **e = environ;
12000 if (e) {
12001 while (*e) {
12002# if 0
12003 puts(*e++);
12004# else
12005 /* ash emits: export VAR='VAL'
12006 * bash: declare -x VAR="VAL"
12007 * we follow ash example */
12008 const char *s = *e++;
12009 const char *p = strchr(s, '=');
12010
12011 if (!p) /* wtf? take next variable */
12012 continue;
Francis Laniele7ca3a32023-12-22 22:02:42 +010012013 /* "export VAR=" */
12014 printf("%s %.*s", "export", (int)(p - s) + 1, s);
Francis Laniel110b7692023-12-22 22:02:27 +010012015 print_escaped(p + 1);
12016 putchar('\n');
12017# endif
12018 }
12019 /*fflush_all(); - done after each builtin anyway */
12020 }
12021 return EXIT_SUCCESS;
12022 }
12023
12024 return helper_export_local(argv, opt_unexport ? SETFLAG_UNEXPORT : SETFLAG_EXPORT);
12025}
12026#endif
12027
12028#if ENABLE_HUSH_LOCAL
12029static int FAST_FUNC builtin_local(char **argv)
12030{
12031 if (G.func_nest_level == 0) {
12032 bb_error_msg("%s: not in a function", argv[0]);
12033 return EXIT_FAILURE; /* bash compat */
12034 }
Francis Laniele7ca3a32023-12-22 22:02:42 +010012035//TODO? ash and bash support "local -" special form,
12036//which saves/restores $- around function call (including async returns, such as ^C)
12037//(IOW: it makes "set +/-..." effects local)
Francis Laniel110b7692023-12-22 22:02:27 +010012038 argv++;
12039 /* Since all builtins run in a nested variable level,
12040 * need to use level - 1 here. Or else the variable will be removed at once
12041 * after builtin returns.
12042 */
12043 return helper_export_local(argv, (G.var_nest_level - 1) << SETFLAG_VARLVL_SHIFT);
12044}
12045#endif
12046
12047#if ENABLE_HUSH_READONLY
12048static int FAST_FUNC builtin_readonly(char **argv)
12049{
12050 argv++;
12051 if (*argv == NULL) {
12052 /* bash: readonly [-p]: list all readonly VARs
12053 * (-p has no effect in bash)
12054 */
12055 struct variable *e;
12056 for (e = G.top_var; e; e = e->next) {
12057 if (e->flg_read_only) {
Francis Laniele7ca3a32023-12-22 22:02:42 +010012058 const char *s = e->varstr;
12059 const char *p = strchr(s, '=');
12060
12061 if (!p) /* wtf? take next variable */
12062 continue;
12063 /* "readonly VAR=" */
12064 printf("%s %.*s", "readonly", (int)(p - s) + 1, s);
12065 print_escaped(p + 1);
12066 putchar('\n');
Francis Laniel110b7692023-12-22 22:02:27 +010012067 }
12068 }
12069 return EXIT_SUCCESS;
12070 }
12071 return helper_export_local(argv, SETFLAG_MAKE_RO);
12072}
12073#endif
12074
12075#if ENABLE_HUSH_UNSET
12076/* http://www.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#unset */
12077static int FAST_FUNC builtin_unset(char **argv)
12078{
12079 int ret;
12080 unsigned opts;
12081
12082 /* "!": do not abort on errors */
12083 /* "+": stop at 1st non-option */
12084 opts = getopt32(argv, "!+vf");
12085 if (opts == (unsigned)-1)
12086 return EXIT_FAILURE;
12087 if (opts == 3) {
12088 bb_simple_error_msg("unset: -v and -f are exclusive");
12089 return EXIT_FAILURE;
12090 }
12091 argv += optind;
12092
12093 ret = EXIT_SUCCESS;
12094 while (*argv) {
12095 if (!(opts & 2)) { /* not -f */
12096 if (unset_local_var(*argv)) {
12097 /* unset <nonexistent_var> doesn't fail.
12098 * Error is when one tries to unset RO var.
12099 * Message was printed by unset_local_var. */
12100 ret = EXIT_FAILURE;
12101 }
12102 }
12103# if ENABLE_HUSH_FUNCTIONS
12104 else {
12105 unset_func(*argv);
12106 }
12107# endif
12108 argv++;
12109 }
12110 return ret;
12111}
12112#endif
12113
12114#if ENABLE_HUSH_SET
12115/* http://www.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#set
12116 * built-in 'set' handler
12117 * SUSv3 says:
12118 * set [-abCefhmnuvx] [-o option] [argument...]
12119 * set [+abCefhmnuvx] [+o option] [argument...]
12120 * set -- [argument...]
12121 * set -o
12122 * set +o
12123 * Implementations shall support the options in both their hyphen and
12124 * plus-sign forms. These options can also be specified as options to sh.
12125 * Examples:
12126 * Write out all variables and their values: set
12127 * Set $1, $2, and $3 and set "$#" to 3: set c a b
12128 * Turn on the -x and -v options: set -xv
12129 * Unset all positional parameters: set --
12130 * Set $1 to the value of x, even if it begins with '-' or '+': set -- "$x"
12131 * Set the positional parameters to the expansion of x, even if x expands
12132 * with a leading '-' or '+': set -- $x
12133 *
12134 * So far, we only support "set -- [argument...]" and some of the short names.
12135 */
12136static int FAST_FUNC builtin_set(char **argv)
12137{
12138 int n;
12139 char **pp, **g_argv;
12140 char *arg = *++argv;
12141
12142 if (arg == NULL) {
12143 struct variable *e;
Francis Laniele7ca3a32023-12-22 22:02:42 +010012144 for (e = G.top_var; e; e = e->next) {
12145 const char *s = e->varstr;
12146 const char *p = strchr(s, '=');
12147
12148 if (!p) /* wtf? take next variable */
12149 continue;
12150 /* var= */
12151 printf("%.*s", (int)(p - s) + 1, s);
12152 print_escaped(p + 1);
12153 putchar('\n');
12154 }
Francis Laniel110b7692023-12-22 22:02:27 +010012155 return EXIT_SUCCESS;
12156 }
12157
12158 do {
12159 if (strcmp(arg, "--") == 0) {
12160 ++argv;
12161 goto set_argv;
12162 }
12163 if (arg[0] != '+' && arg[0] != '-')
12164 break;
12165 for (n = 1; arg[n]; ++n) {
12166 if (set_mode((arg[0] == '-'), arg[n], argv[1])) {
12167 bb_error_msg("%s: %s: invalid option", "set", arg);
12168 return EXIT_FAILURE;
12169 }
12170 if (arg[n] == 'o' && argv[1])
12171 argv++;
12172 }
12173 } while ((arg = *++argv) != NULL);
12174 /* Now argv[0] is 1st argument */
12175
12176 if (arg == NULL)
12177 return EXIT_SUCCESS;
12178 set_argv:
12179
12180 /* NB: G.global_argv[0] ($0) is never freed/changed */
12181 g_argv = G.global_argv;
12182 if (G.global_args_malloced) {
12183 pp = g_argv;
12184 while (*++pp)
12185 free(*pp);
12186 g_argv[1] = NULL;
12187 } else {
12188 G.global_args_malloced = 1;
12189 pp = xzalloc(sizeof(pp[0]) * 2);
12190 pp[0] = g_argv[0]; /* retain $0 */
12191 g_argv = pp;
12192 }
12193 /* This realloc's G.global_argv */
12194 G.global_argv = pp = add_strings_to_strings(g_argv, argv, /*dup:*/ 1);
12195
12196 G.global_argc = 1 + string_array_len(pp + 1);
12197
12198 return EXIT_SUCCESS;
12199}
12200#endif
12201
12202static int FAST_FUNC builtin_shift(char **argv)
12203{
12204 int n = 1;
12205 argv = skip_dash_dash(argv);
12206 if (argv[0]) {
12207 n = bb_strtou(argv[0], NULL, 10);
12208 if (errno || n < 0) {
12209 /* shared string with ash.c */
12210 bb_error_msg("Illegal number: %s", argv[0]);
12211 /*
12212 * ash aborts in this case.
12213 * bash prints error message and set $? to 1.
12214 * Interestingly, for "shift 99999" bash does not
12215 * print error message, but does set $? to 1
12216 * (and does no shifting at all).
12217 */
12218 }
12219 }
12220 if (n >= 0 && n < G.global_argc) {
12221 if (G_global_args_malloced) {
12222 int m = 1;
12223 while (m <= n)
12224 free(G.global_argv[m++]);
12225 }
12226 G.global_argc -= n;
12227 memmove(&G.global_argv[1], &G.global_argv[n+1],
12228 G.global_argc * sizeof(G.global_argv[0]));
12229 return EXIT_SUCCESS;
12230 }
12231 return EXIT_FAILURE;
12232}
12233
12234#if ENABLE_HUSH_GETOPTS
12235static int FAST_FUNC builtin_getopts(char **argv)
12236{
12237/* http://pubs.opengroup.org/onlinepubs/9699919799/utilities/getopts.html
12238
12239TODO:
12240If a required argument is not found, and getopts is not silent,
12241a question mark (?) is placed in VAR, OPTARG is unset, and a
12242diagnostic message is printed. If getopts is silent, then a
12243colon (:) is placed in VAR and OPTARG is set to the option
12244character found.
12245
12246Test that VAR is a valid variable name?
12247
12248"Whenever the shell is invoked, OPTIND shall be initialized to 1"
12249*/
12250 char cbuf[2];
12251 const char *cp, *optstring, *var;
12252 int c, n, exitcode, my_opterr;
12253 unsigned count;
12254
12255 optstring = *++argv;
12256 if (!optstring || !(var = *++argv)) {
12257 bb_simple_error_msg("usage: getopts OPTSTRING VAR [ARGS]");
12258 return EXIT_FAILURE;
12259 }
12260
12261 if (argv[1])
12262 argv[0] = G.global_argv[0]; /* for error messages in getopt() */
12263 else
12264 argv = G.global_argv;
12265 cbuf[1] = '\0';
12266
12267 my_opterr = 0;
12268 if (optstring[0] != ':') {
12269 cp = get_local_var_value("OPTERR");
12270 /* 0 if "OPTERR=0", 1 otherwise */
12271 my_opterr = (!cp || NOT_LONE_CHAR(cp, '0'));
12272 }
12273
12274 /* getopts stops on first non-option. Add "+" to force that */
12275 /*if (optstring[0] != '+')*/ {
12276 char *s = alloca(strlen(optstring) + 2);
12277 sprintf(s, "+%s", optstring);
12278 optstring = s;
12279 }
12280
12281 /* Naively, now we should just
12282 * cp = get_local_var_value("OPTIND");
12283 * optind = cp ? atoi(cp) : 0;
12284 * optarg = NULL;
12285 * opterr = my_opterr;
12286 * c = getopt(string_array_len(argv), argv, optstring);
12287 * and be done? Not so fast...
12288 * Unlike normal getopt() usage in C programs, here
12289 * each successive call will (usually) have the same argv[] CONTENTS,
12290 * but not the ADDRESSES. Worse yet, it's possible that between
12291 * invocations of "getopts", there will be calls to shell builtins
12292 * which use getopt() internally. Example:
12293 * while getopts "abc" RES -a -bc -abc de; do
12294 * unset -ff func
12295 * done
12296 * This would not work correctly: getopt() call inside "unset"
12297 * modifies internal libc state which is tracking position in
12298 * multi-option strings ("-abc"). At best, it can skip options
12299 * or return the same option infinitely. With glibc implementation
12300 * of getopt(), it would use outright invalid pointers and return
12301 * garbage even _without_ "unset" mangling internal state.
12302 *
12303 * We resort to resetting getopt() state and calling it N times,
12304 * until we get Nth result (or failure).
12305 * (N == G.getopt_count is reset to 0 whenever OPTIND is [un]set).
12306 */
12307 GETOPT_RESET();
12308 count = 0;
12309 n = string_array_len(argv);
12310 do {
12311 optarg = NULL;
12312 opterr = (count < G.getopt_count) ? 0 : my_opterr;
12313 c = getopt(n, argv, optstring);
12314 if (c < 0)
12315 break;
12316 count++;
12317 } while (count <= G.getopt_count);
12318
12319 /* Set OPTIND. Prevent resetting of the magic counter! */
12320 set_local_var_from_halves("OPTIND", utoa(optind));
12321 G.getopt_count = count; /* "next time, give me N+1'th result" */
12322 GETOPT_RESET(); /* just in case */
12323
12324 /* Set OPTARG */
12325 /* Always set or unset, never left as-is, even on exit/error:
12326 * "If no option was found, or if the option that was found
12327 * does not have an option-argument, OPTARG shall be unset."
12328 */
12329 cp = optarg;
12330 if (c == '?') {
12331 /* If ":optstring" and unknown option is seen,
12332 * it is stored to OPTARG.
12333 */
12334 if (optstring[1] == ':') {
12335 cbuf[0] = optopt;
12336 cp = cbuf;
12337 }
12338 }
12339 if (cp)
12340 set_local_var_from_halves("OPTARG", cp);
12341 else
12342 unset_local_var("OPTARG");
12343
12344 /* Convert -1 to "?" */
12345 exitcode = EXIT_SUCCESS;
12346 if (c < 0) { /* -1: end of options */
12347 exitcode = EXIT_FAILURE;
12348 c = '?';
12349 }
12350
12351 /* Set VAR */
12352 cbuf[0] = c;
12353 set_local_var_from_halves(var, cbuf);
12354
12355 return exitcode;
12356}
12357#endif
12358
12359static int FAST_FUNC builtin_source(char **argv)
12360{
12361 char *arg_path, *filename;
12362 HFILE *input;
12363 save_arg_t sv;
12364 char *args_need_save;
12365#if ENABLE_HUSH_FUNCTIONS
12366 smallint sv_flg;
12367#endif
12368
12369 argv = skip_dash_dash(argv);
12370 filename = argv[0];
12371 if (!filename) {
12372 /* bash says: "bash: .: filename argument required" */
12373 return 2; /* bash compat */
12374 }
12375 arg_path = NULL;
12376 if (!strchr(filename, '/')) {
12377 arg_path = find_in_path(filename);
12378 if (arg_path)
12379 filename = arg_path;
12380 else if (!ENABLE_HUSH_BASH_SOURCE_CURDIR) {
12381 errno = ENOENT;
12382 bb_simple_perror_msg(filename);
12383 return EXIT_FAILURE;
12384 }
12385 }
12386 input = hfopen(filename);
12387 free(arg_path);
12388 if (!input) {
12389 bb_perror_msg("%s", filename);
12390 /* POSIX: non-interactive shell should abort here,
12391 * not merely fail. So far no one complained :)
12392 */
12393 return EXIT_FAILURE;
12394 }
12395
12396#if ENABLE_HUSH_FUNCTIONS
12397 sv_flg = G_flag_return_in_progress;
12398 /* "we are inside sourced file, ok to use return" */
12399 G_flag_return_in_progress = -1;
12400#endif
12401 args_need_save = argv[1]; /* used as a boolean variable */
12402 if (args_need_save)
12403 save_and_replace_G_args(&sv, argv);
12404
12405 /* "false; . ./empty_line; echo Zero:$?" should print 0 */
12406 G.last_exitcode = 0;
12407 parse_and_run_file(input);
12408 hfclose(input);
12409
12410 if (args_need_save) /* can't use argv[1] instead: "shift" can mangle it */
12411 restore_G_args(&sv, argv);
12412#if ENABLE_HUSH_FUNCTIONS
12413 G_flag_return_in_progress = sv_flg;
12414#endif
12415
12416 return G.last_exitcode;
12417}
12418
12419#if ENABLE_HUSH_TRAP
12420static int FAST_FUNC builtin_trap(char **argv)
12421{
12422 int sig;
12423 char *new_cmd;
12424
12425 if (!G_traps)
12426 G_traps = xzalloc(sizeof(G_traps[0]) * NSIG);
12427
12428 argv++;
12429 if (!*argv) {
12430 int i;
12431 /* No args: print all trapped */
12432 for (i = 0; i < NSIG; ++i) {
12433 if (G_traps[i]) {
12434 printf("trap -- ");
12435 print_escaped(G_traps[i]);
12436 /* note: bash adds "SIG", but only if invoked
12437 * as "bash". If called as "sh", or if set -o posix,
12438 * then it prints short signal names.
12439 * We are printing short names: */
12440 printf(" %s\n", get_signame(i));
12441 }
12442 }
12443 /*fflush_all(); - done after each builtin anyway */
12444 return EXIT_SUCCESS;
12445 }
12446
12447 new_cmd = NULL;
12448 /* If first arg is a number: reset all specified signals */
12449 sig = bb_strtou(*argv, NULL, 10);
12450 if (errno == 0) {
12451 int ret;
12452 process_sig_list:
12453 ret = EXIT_SUCCESS;
12454 while (*argv) {
12455 sighandler_t handler;
12456
12457 sig = get_signum(*argv++);
12458 if (sig < 0) {
12459 ret = EXIT_FAILURE;
12460 /* Mimic bash message exactly */
12461 bb_error_msg("trap: %s: invalid signal specification", argv[-1]);
12462 continue;
12463 }
12464
12465 free(G_traps[sig]);
12466 G_traps[sig] = xstrdup(new_cmd);
12467
12468 debug_printf("trap: setting SIG%s (%i) to '%s'\n",
12469 get_signame(sig), sig, G_traps[sig]);
12470
12471 /* There is no signal for 0 (EXIT) */
12472 if (sig == 0)
12473 continue;
12474
12475 if (new_cmd)
12476 handler = (new_cmd[0] ? record_pending_signo : SIG_IGN);
12477 else
12478 /* We are removing trap handler */
12479 handler = pick_sighandler(sig);
12480 install_sighandler(sig, handler);
12481 }
12482 return ret;
12483 }
12484
12485 if (!argv[1]) { /* no second arg */
12486 bb_simple_error_msg("trap: invalid arguments");
12487 return EXIT_FAILURE;
12488 }
12489
12490 /* First arg is "-": reset all specified to default */
12491 /* First arg is "--": skip it, the rest is "handler SIGs..." */
12492 /* Everything else: set arg as signal handler
12493 * (includes "" case, which ignores signal) */
12494 if (argv[0][0] == '-') {
12495 if (argv[0][1] == '\0') { /* "-" */
12496 /* new_cmd remains NULL: "reset these sigs" */
12497 goto reset_traps;
12498 }
12499 if (argv[0][1] == '-' && argv[0][2] == '\0') { /* "--" */
12500 argv++;
12501 }
12502 /* else: "-something", no special meaning */
12503 }
12504 new_cmd = *argv;
12505 reset_traps:
12506 argv++;
12507 goto process_sig_list;
12508}
12509#endif
12510
12511#if ENABLE_HUSH_JOB
12512static struct pipe *parse_jobspec(const char *str)
12513{
12514 struct pipe *pi;
12515 unsigned jobnum;
12516
12517 if (sscanf(str, "%%%u", &jobnum) != 1) {
12518 if (str[0] != '%'
12519 || (str[1] != '%' && str[1] != '+' && str[1] != '\0')
12520 ) {
12521 bb_error_msg("bad argument '%s'", str);
12522 return NULL;
12523 }
12524 /* It is "%%", "%+" or "%" - current job */
12525 jobnum = G.last_jobid;
12526 if (jobnum == 0) {
12527 bb_simple_error_msg("no current job");
12528 return NULL;
12529 }
12530 }
12531 for (pi = G.job_list; pi; pi = pi->next) {
12532 if (pi->jobid == jobnum) {
12533 return pi;
12534 }
12535 }
12536 bb_error_msg("%u: no such job", jobnum);
12537 return NULL;
12538}
12539
12540static int FAST_FUNC builtin_jobs(char **argv UNUSED_PARAM)
12541{
12542 struct pipe *job;
12543 const char *status_string;
12544
12545 checkjobs(NULL, 0 /*(no pid to wait for)*/);
12546 for (job = G.job_list; job; job = job->next) {
12547 if (job->alive_cmds == job->stopped_cmds)
12548 status_string = "Stopped";
12549 else
12550 status_string = "Running";
12551
12552 printf(JOB_STATUS_FORMAT, job->jobid, status_string, job->cmdtext);
12553 }
12554
12555 clean_up_last_dead_job();
12556
12557 return EXIT_SUCCESS;
12558}
12559
12560/* built-in 'fg' and 'bg' handler */
12561static int FAST_FUNC builtin_fg_bg(char **argv)
12562{
12563 int i;
12564 struct pipe *pi;
12565
12566 if (!G_interactive_fd)
12567 return EXIT_FAILURE;
12568
12569 /* If they gave us no args, assume they want the last backgrounded task */
12570 if (!argv[1]) {
12571 for (pi = G.job_list; pi; pi = pi->next) {
12572 if (pi->jobid == G.last_jobid) {
12573 goto found;
12574 }
12575 }
12576 bb_error_msg("%s: no current job", argv[0]);
12577 return EXIT_FAILURE;
12578 }
12579
12580 pi = parse_jobspec(argv[1]);
12581 if (!pi)
12582 return EXIT_FAILURE;
12583 found:
12584 /* TODO: bash prints a string representation
12585 * of job being foregrounded (like "sleep 1 | cat") */
12586 if (argv[0][0] == 'f' && G_saved_tty_pgrp) {
Francis Laniele7ca3a32023-12-22 22:02:42 +010012587 /* Put the job into the foreground. */
Francis Laniel110b7692023-12-22 22:02:27 +010012588 tcsetpgrp(G_interactive_fd, pi->pgrp);
12589 }
12590
12591 /* Restart the processes in the job */
12592 debug_printf_jobs("reviving %d procs, pgrp %d\n", pi->num_cmds, pi->pgrp);
12593 for (i = 0; i < pi->num_cmds; i++) {
12594 debug_printf_jobs("reviving pid %d\n", pi->cmds[i].pid);
12595 }
12596 pi->stopped_cmds = 0;
12597
12598 i = kill(- pi->pgrp, SIGCONT);
12599 if (i < 0) {
12600 if (errno == ESRCH) {
12601 delete_finished_job(pi);
12602 return EXIT_SUCCESS;
12603 }
12604 bb_simple_perror_msg("kill (SIGCONT)");
12605 }
12606
12607 if (argv[0][0] == 'f') {
12608 remove_job_from_table(pi); /* FG job shouldn't be in job table */
12609 return checkjobs_and_fg_shell(pi);
12610 }
12611 return EXIT_SUCCESS;
12612}
12613#endif
12614
12615#if ENABLE_HUSH_KILL
12616static int FAST_FUNC builtin_kill(char **argv)
12617{
12618 int ret = 0;
12619
12620# if ENABLE_HUSH_JOB
12621 if (argv[1] && strcmp(argv[1], "-l") != 0) {
12622 int i = 1;
12623
12624 do {
12625 struct pipe *pi;
12626 char *dst;
12627 int j, n;
12628
12629 if (argv[i][0] != '%')
12630 continue;
12631 /*
12632 * "kill %N" - job kill
12633 * Converting to pgrp / pid kill
12634 */
12635 pi = parse_jobspec(argv[i]);
12636 if (!pi) {
12637 /* Eat bad jobspec */
12638 j = i;
12639 do {
12640 j++;
12641 argv[j - 1] = argv[j];
12642 } while (argv[j]);
12643 ret = 1;
12644 i--;
12645 continue;
12646 }
12647 /*
12648 * In jobs started under job control, we signal
12649 * entire process group by kill -PGRP_ID.
12650 * This happens, f.e., in interactive shell.
12651 *
12652 * Otherwise, we signal each child via
12653 * kill PID1 PID2 PID3.
12654 * Testcases:
12655 * sh -c 'sleep 1|sleep 1 & kill %1'
12656 * sh -c 'true|sleep 2 & sleep 1; kill %1'
12657 * sh -c 'true|sleep 1 & sleep 2; kill %1'
12658 */
12659 n = G_interactive_fd ? 1 : pi->num_cmds;
12660 dst = alloca(n * sizeof(int)*4);
12661 argv[i] = dst;
12662 if (G_interactive_fd)
12663 dst += sprintf(dst, " -%u", (int)pi->pgrp);
12664 else for (j = 0; j < n; j++) {
12665 struct command *cmd = &pi->cmds[j];
12666 /* Skip exited members of the job */
12667 if (cmd->pid == 0)
12668 continue;
12669 /*
12670 * kill_main has matching code to expect
12671 * leading space. Needed to not confuse
12672 * negative pids with "kill -SIGNAL_NO" syntax
12673 */
12674 dst += sprintf(dst, " %u", (int)cmd->pid);
12675 }
12676 *dst = '\0';
12677 } while (argv[++i]);
12678 }
12679# endif
12680
12681 if (argv[1] || ret == 0) {
12682 ret = run_applet_main(argv, kill_main);
12683 }
12684 /* else: ret = 1, "kill %bad_jobspec" case */
12685 return ret;
12686}
12687#endif
12688
12689#if ENABLE_HUSH_WAIT
12690/* http://www.opengroup.org/onlinepubs/9699919799/utilities/wait.html */
12691# if !ENABLE_HUSH_JOB
12692# define wait_for_child_or_signal(pipe,pid) wait_for_child_or_signal(pid)
12693# endif
12694static int wait_for_child_or_signal(struct pipe *waitfor_pipe, pid_t waitfor_pid)
12695{
12696 int ret = 0;
12697 for (;;) {
12698 int sig;
12699 sigset_t oldset;
12700
12701 if (!sigisemptyset(&G.pending_set))
12702 goto check_sig;
12703
12704 /* waitpid is not interruptible by SA_RESTARTed
12705 * signals which we use. Thus, this ugly dance:
12706 */
12707
12708 /* Make sure possible SIGCHLD is stored in kernel's
12709 * pending signal mask before we call waitpid.
12710 * Or else we may race with SIGCHLD, lose it,
12711 * and get stuck in sigsuspend...
12712 */
12713 sigfillset(&oldset); /* block all signals, remember old set */
12714 sigprocmask2(SIG_SETMASK, &oldset);
12715
12716 if (!sigisemptyset(&G.pending_set)) {
12717 /* Crap! we raced with some signal! */
12718 goto restore;
12719 }
12720
12721 /*errno = 0; - checkjobs does this */
12722/* Can't pass waitfor_pipe into checkjobs(): it won't be interruptible */
12723 ret = checkjobs(NULL, waitfor_pid); /* waitpid(WNOHANG) inside */
12724 debug_printf_exec("checkjobs:%d\n", ret);
12725# if ENABLE_HUSH_JOB
12726 if (waitfor_pipe) {
12727 int rcode = job_exited_or_stopped(waitfor_pipe);
12728 debug_printf_exec("job_exited_or_stopped:%d\n", rcode);
12729 if (rcode >= 0) {
12730 ret = rcode;
12731 sigprocmask(SIG_SETMASK, &oldset, NULL);
12732 break;
12733 }
12734 }
12735# endif
12736 /* if ECHILD, there are no children (ret is -1 or 0) */
12737 /* if ret == 0, no children changed state */
12738 /* if ret != 0, it's exitcode+1 of exited waitfor_pid child */
12739 if (errno == ECHILD || ret) {
12740 ret--;
12741 if (ret < 0) /* if ECHILD, may need to fix "ret" */
12742 ret = 0;
12743# if ENABLE_HUSH_BASH_COMPAT
12744 if (waitfor_pid == -1 && errno == ECHILD) {
12745 /* exitcode of "wait -n" with no children is 127, not 0 */
12746 ret = 127;
12747 }
12748# endif
12749 sigprocmask(SIG_SETMASK, &oldset, NULL);
12750 break;
12751 }
12752 /* Wait for SIGCHLD or any other signal */
12753 /* It is vitally important for sigsuspend that SIGCHLD has non-DFL handler! */
12754 /* Note: sigsuspend invokes signal handler */
12755 sigsuspend(&oldset);
12756 /* ^^^ add "sigdelset(&oldset, SIGCHLD)" before sigsuspend
12757 * to make sure SIGCHLD is not masked off?
12758 * It was reported that this:
12759 * fn() { : | return; }
12760 * shopt -s lastpipe
12761 * fn
12762 * exec hush SCRIPT
12763 * under bash 4.4.23 runs SCRIPT with SIGCHLD masked,
12764 * making "wait" commands in SCRIPT block forever.
12765 */
12766 restore:
12767 sigprocmask(SIG_SETMASK, &oldset, NULL);
12768 check_sig:
12769 /* So, did we get a signal? */
12770 sig = check_and_run_traps();
12771 if (sig /*&& sig != SIGCHLD - always true */) {
12772 /* Do this for any (non-ignored) signal, not only for ^C */
12773 ret = 128 | sig;
12774 break;
12775 }
12776 /* SIGCHLD, or no signal, or ignored one, such as SIGQUIT. Repeat */
12777 }
12778 return ret;
12779}
12780
12781static int FAST_FUNC builtin_wait(char **argv)
12782{
12783 int ret;
12784 int status;
12785
12786 argv = skip_dash_dash(argv);
12787# if ENABLE_HUSH_BASH_COMPAT
12788 if (argv[0] && strcmp(argv[0], "-n") == 0) {
12789 /* wait -n */
12790 /* (bash accepts "wait -n PID" too and ignores PID) */
12791 G.dead_job_exitcode = -1;
12792 return wait_for_child_or_signal(NULL, -1 /*no job, wait for one job*/);
12793 }
12794# endif
12795 if (argv[0] == NULL) {
12796 /* Don't care about wait results */
12797 /* Note 1: must wait until there are no more children */
12798 /* Note 2: must be interruptible */
12799 /* Examples:
12800 * $ sleep 3 & sleep 6 & wait
12801 * [1] 30934 sleep 3
12802 * [2] 30935 sleep 6
12803 * [1] Done sleep 3
12804 * [2] Done sleep 6
12805 * $ sleep 3 & sleep 6 & wait
12806 * [1] 30936 sleep 3
12807 * [2] 30937 sleep 6
12808 * [1] Done sleep 3
12809 * ^C <-- after ~4 sec from keyboard
12810 * $
12811 */
12812 return wait_for_child_or_signal(NULL, 0 /*no job and no pid to wait for*/);
12813 }
12814
12815 do {
12816 pid_t pid = bb_strtou(*argv, NULL, 10);
12817 if (errno || pid <= 0) {
12818# if ENABLE_HUSH_JOB
12819 if (argv[0][0] == '%') {
12820 struct pipe *wait_pipe;
12821 ret = 127; /* bash compat for bad jobspecs */
12822 wait_pipe = parse_jobspec(*argv);
12823 if (wait_pipe) {
12824 ret = job_exited_or_stopped(wait_pipe);
12825 if (ret < 0) {
12826 ret = wait_for_child_or_signal(wait_pipe, 0);
12827 } else {
12828 /* waiting on "last dead job" removes it */
12829 clean_up_last_dead_job();
12830 }
12831 }
12832 /* else: parse_jobspec() already emitted error msg */
12833 continue;
12834 }
12835# endif
12836 /* mimic bash message */
12837 bb_error_msg("wait: '%s': not a pid or valid job spec", *argv);
12838 ret = EXIT_FAILURE;
12839 continue; /* bash checks all argv[] */
12840 }
12841
12842 /* Do we have such child? */
12843 ret = waitpid(pid, &status, WNOHANG);
12844 if (ret < 0) {
12845 /* No */
12846 ret = 127;
12847 if (errno == ECHILD) {
12848 if (pid == G.last_bg_pid) {
12849 /* "wait $!" but last bg task has already exited. Try:
12850 * (sleep 1; exit 3) & sleep 2; echo $?; wait $!; echo $?
12851 * In bash it prints exitcode 0, then 3.
12852 * In dash, it is 127.
12853 */
12854 ret = G.last_bg_pid_exitcode;
12855 } else {
12856 /* Example: "wait 1". mimic bash message */
12857 bb_error_msg("wait: pid %u is not a child of this shell", (unsigned)pid);
12858 }
12859 } else {
12860 /* ??? */
12861 bb_perror_msg("wait %s", *argv);
12862 }
12863 continue; /* bash checks all argv[] */
12864 }
12865 if (ret == 0) {
12866 /* Yes, and it still runs */
12867 ret = wait_for_child_or_signal(NULL, pid);
12868 } else {
12869 /* Yes, and it just exited */
12870 process_wait_result(NULL, pid, status);
12871 ret = WEXITSTATUS(status);
12872 if (WIFSIGNALED(status))
12873 ret = 128 | WTERMSIG(status);
12874 }
12875 } while (*++argv);
12876
12877 return ret;
12878}
12879#endif
12880
12881#if ENABLE_HUSH_LOOPS || ENABLE_HUSH_FUNCTIONS
12882static unsigned parse_numeric_argv1(char **argv, unsigned def, unsigned def_min)
12883{
12884 if (argv[1]) {
12885 def = bb_strtou(argv[1], NULL, 10);
12886 if (errno || def < def_min || argv[2]) {
12887 bb_error_msg("%s: bad arguments", argv[0]);
12888 def = UINT_MAX;
12889 }
12890 }
12891 return def;
12892}
12893#endif
12894
12895#if ENABLE_HUSH_LOOPS
12896static int FAST_FUNC builtin_break(char **argv)
12897{
12898 unsigned depth;
12899 if (G.depth_of_loop == 0) {
12900 bb_error_msg("%s: only meaningful in a loop", argv[0]);
12901 /* if we came from builtin_continue(), need to undo "= 1" */
12902 G.flag_break_continue = 0;
12903 return EXIT_SUCCESS; /* bash compat */
12904 }
12905 G.flag_break_continue++; /* BC_BREAK = 1, or BC_CONTINUE = 2 */
12906
12907 G.depth_break_continue = depth = parse_numeric_argv1(argv, 1, 1);
12908 if (depth == UINT_MAX)
12909 G.flag_break_continue = BC_BREAK;
12910 if (G.depth_of_loop < depth)
12911 G.depth_break_continue = G.depth_of_loop;
12912
12913 return EXIT_SUCCESS;
12914}
12915
12916static int FAST_FUNC builtin_continue(char **argv)
12917{
12918 G.flag_break_continue = 1; /* BC_CONTINUE = 2 = 1+1 */
12919 return builtin_break(argv);
12920}
12921#endif
12922
12923#if ENABLE_HUSH_FUNCTIONS
12924static int FAST_FUNC builtin_return(char **argv)
12925{
12926 int rc;
12927
12928 if (G_flag_return_in_progress != -1) {
12929 bb_error_msg("%s: not in a function or sourced script", argv[0]);
12930 return EXIT_FAILURE; /* bash compat */
12931 }
12932
12933 G_flag_return_in_progress = 1;
12934
12935 /* bash:
12936 * out of range: wraps around at 256, does not error out
12937 * non-numeric param:
12938 * f() { false; return qwe; }; f; echo $?
12939 * bash: return: qwe: numeric argument required <== we do this
12940 * 255 <== we also do this
12941 */
12942 rc = parse_numeric_argv1(argv, G.last_exitcode, 0);
12943# if ENABLE_HUSH_TRAP
12944 if (argv[1]) { /* "return ARG" inside a running trap sets $? */
12945 debug_printf_exec("G.return_exitcode=%d\n", rc);
12946 G.return_exitcode = rc;
12947 }
12948# endif
12949 return rc;
12950}
12951#endif
12952
12953#if ENABLE_HUSH_TIMES
12954static int FAST_FUNC builtin_times(char **argv UNUSED_PARAM)
12955{
12956 static const uint8_t times_tbl[] ALIGN1 = {
12957 ' ', offsetof(struct tms, tms_utime),
12958 '\n', offsetof(struct tms, tms_stime),
12959 ' ', offsetof(struct tms, tms_cutime),
12960 '\n', offsetof(struct tms, tms_cstime),
12961 0
12962 };
12963 const uint8_t *p;
12964 unsigned clk_tck;
12965 struct tms buf;
12966
12967 clk_tck = bb_clk_tck();
12968
12969 times(&buf);
12970 p = times_tbl;
12971 do {
12972 unsigned sec, frac;
12973 unsigned long t;
12974 t = *(clock_t *)(((char *) &buf) + p[1]);
12975 sec = t / clk_tck;
12976 frac = t % clk_tck;
12977 printf("%um%u.%03us%c",
12978 sec / 60, sec % 60,
12979 (frac * 1000) / clk_tck,
12980 p[0]);
12981 p += 2;
12982 } while (*p);
12983
12984 return EXIT_SUCCESS;
12985}
12986#endif
12987
12988#if ENABLE_HUSH_MEMLEAK
12989static int FAST_FUNC builtin_memleak(char **argv UNUSED_PARAM)
12990{
12991 void *p;
12992 unsigned long l;
12993
12994# ifdef M_TRIM_THRESHOLD
12995 /* Optional. Reduces probability of false positives */
12996 malloc_trim(0);
12997# endif
12998 /* Crude attempt to find where "free memory" starts,
12999 * sans fragmentation. */
13000 p = malloc(240);
13001 l = (unsigned long)p;
13002 free(p);
13003 p = malloc(3400);
13004 if (l < (unsigned long)p) l = (unsigned long)p;
13005 free(p);
13006
13007
13008# if 0 /* debug */
13009 {
13010 struct mallinfo mi = mallinfo();
13011 printf("top alloc:0x%lx malloced:%d+%d=%d\n", l,
13012 mi.arena, mi.hblkhd, mi.arena + mi.hblkhd);
13013 }
13014# endif
13015
13016 if (!G.memleak_value)
13017 G.memleak_value = l;
13018
13019 l -= G.memleak_value;
13020 if ((long)l < 0)
13021 l = 0;
13022 l /= 1024;
13023 if (l > 127)
13024 l = 127;
13025
13026 /* Exitcode is "how many kilobytes we leaked since 1st call" */
13027 return l;
13028}
13029#endif
Francis Laniel36836fc2023-12-22 22:02:28 +010013030#endif /* !__U_BOOT__ */