blob: a56ea6c5c8ff77819d42c08ab78078cb68844060 [file] [log] [blame]
Francis Lanielb234f7e2023-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
94//config: bool "hush (68 kb)"
95//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
342//usage: "[-enxl] [-c 'SCRIPT' [ARG0 ARGS] | FILE [ARGS] | -s [ARGS]]"
343//usage:#define hush_full_usage "\n\n"
344//usage: "Unix shell interpreter"
345
Francis Laniel8197f012023-12-22 22:02:28 +0100346#ifndef __U_BOOT__
Francis Lanielb234f7e2023-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
377#if ENABLE_FEATURE_SH_EMBEDDED_SCRIPTS && !(ENABLE_ASH || ENABLE_SH_IS_ASH || ENABLE_BASH_IS_ASH)
378# include "embedded_scripts.h"
379#else
380# define NUM_SCRIPTS 0
381#endif
Francis Laniel8197f012023-12-22 22:02:28 +0100382#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-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 Laniel8197f012023-12-22 22:02:28 +0100426#ifndef __U_BOOT__
Francis Lanielb234f7e2023-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 Lanielb234f7e2023-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 Laniel8197f012023-12-22 22:02:28 +0100437#endif /* __U_BOOT__ */
Francis Lanielb234f7e2023-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 Laniel8197f012023-12-22 22:02:28 +0100483#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +0100484#define SPECIAL_VARS_STR ("_*@$!?#-" + 1)
485#define NUMERIC_SPECVARS_STR ("_*@$!?#-" + 3)
Francis Laniel8197f012023-12-22 22:02:28 +0100486#else /* __U_BOOT__ */
487#define SPECIAL_VARS_STR "*@$!?#-"
488#define NUMERIC_SPECVARS_STR "$!?#-"
489#endif /* __U_BOOT__ */
Francis Lanielb234f7e2023-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 Laniel8197f012023-12-22 22:02:28 +0100522
Francis Lanielb234f7e2023-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
576static const char *const assignment_flag[] = {
577 "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 Laniel8197f012023-12-22 22:02:28 +0100606#ifndef __U_BOOT__
Francis Lanielb234f7e2023-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 Laniel8197f012023-12-22 22:02:28 +0100653#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +0100654
655struct command {
Francis Laniel8197f012023-12-22 22:02:28 +0100656#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +0100657 pid_t pid; /* 0 if exited */
Francis Laniel8197f012023-12-22 22:02:28 +0100658#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-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 Laniel8197f012023-12-22 22:02:28 +0100701#ifdef __U_BOOT__
702 int argc; /* number of program arguments */
703#endif
Francis Lanielb234f7e2023-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 Laniel8197f012023-12-22 22:02:28 +0100711#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +0100712 struct redir_struct *redirects; /* I/O redirections */
Francis Laniel8197f012023-12-22 22:02:28 +0100713#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +0100714};
715/* Is there anything in this command at all? */
Francis Laniel8197f012023-12-22 22:02:28 +0100716#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +0100717#define IS_NULL_CMD(cmd) \
718 (!(cmd)->group && !(cmd)->argv && !(cmd)->redirects)
719
Francis Laniel8197f012023-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 Lanielb234f7e2023-12-22 22:02:27 +0100724struct pipe {
725 struct pipe *next;
726 int num_cmds; /* total number of commands in pipe */
Francis Laniel8197f012023-12-22 22:02:28 +0100727#ifndef __U_BOOT__
Francis Lanielb234f7e2023-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 Laniel8197f012023-12-22 22:02:28 +0100735#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-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 Laniel8197f012023-12-22 22:02:28 +0100759#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +0100760 /* last redirect in command->redirects list */
761 struct redir_struct *pending_redirect;
Francis Laniel8197f012023-12-22 22:02:28 +0100762#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-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 Laniel8197f012023-12-22 22:02:28 +0100796#ifndef __U_BOOT__
Francis Lanielb234f7e2023-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 Laniel8197f012023-12-22 22:02:28 +0100803#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-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 Laniel8197f012023-12-22 22:02:28 +0100808#ifndef __U_BOOT__
Francis Lanielb234f7e2023-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 Laniel8197f012023-12-22 22:02:28 +0100812#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-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 Laniel8197f012023-12-22 22:02:28 +0100884#ifndef __U_BOOT__
Francis Lanielb234f7e2023-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 Laniel8197f012023-12-22 22:02:28 +0100905#else /* __U_BOOT__ */
906# define G_interactive_fd 0
907#endif /* __U_BOOT__ */
908#ifndef __U_BOOT__
Francis Lanielb234f7e2023-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 Laniel8197f012023-12-22 22:02:28 +0100927#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +0100928 /* How deeply are we in context where "set -e" is ignored */
929 int errexit_depth;
Francis Laniel8197f012023-12-22 22:02:28 +0100930#ifndef __U_BOOT__
Francis Lanielb234f7e2023-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 Laniel8197f012023-12-22 22:02:28 +0100956#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +0100957#if ENABLE_HUSH_INTERACTIVE
958 smallint promptmode; /* 0: PS1, 1: PS2 */
959#endif
960 smallint flag_SIGINT;
Francis Laniel8197f012023-12-22 22:02:28 +0100961#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +0100962#if ENABLE_HUSH_LOOPS
963 smallint flag_break_continue;
964#endif
Francis Laniel8197f012023-12-22 22:02:28 +0100965#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +0100966#if ENABLE_HUSH_FUNCTIONS
967 /* 0: outside of a function (or sourced file)
968 * -1: inside of a function, ok to use return builtin
969 * 1: return is invoked, skip all till end of func
970 */
971 smallint flag_return_in_progress;
972# define G_flag_return_in_progress (G.flag_return_in_progress)
973#else
974# define G_flag_return_in_progress 0
975#endif
976 smallint exiting; /* used to prevent EXIT trap recursion */
977 /* These support $? */
978 smalluint last_exitcode;
979 smalluint expand_exitcode;
980 smalluint last_bg_pid_exitcode;
Francis Laniel8197f012023-12-22 22:02:28 +0100981#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +0100982#if ENABLE_HUSH_SET
983 /* are global_argv and global_argv[1..n] malloced? (note: not [0]) */
984 smalluint global_args_malloced;
985# define G_global_args_malloced (G.global_args_malloced)
986#else
987# define G_global_args_malloced 0
988#endif
989#if ENABLE_HUSH_BASH_COMPAT
990 int dead_job_exitcode; /* for "wait -n" */
991#endif
Francis Laniel8197f012023-12-22 22:02:28 +0100992#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +0100993 /* how many non-NULL argv's we have. NB: $# + 1 */
994 int global_argc;
995 char **global_argv;
996#if !BB_MMU
997 char *argv0_for_re_execing;
998#endif
999#if ENABLE_HUSH_LOOPS
Francis Laniel8197f012023-12-22 22:02:28 +01001000#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01001001 unsigned depth_break_continue;
Francis Laniel8197f012023-12-22 22:02:28 +01001002#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01001003 unsigned depth_of_loop;
1004#endif
Francis Laniel8197f012023-12-22 22:02:28 +01001005#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01001006#if ENABLE_HUSH_GETOPTS
1007 unsigned getopt_count;
1008#endif
Francis Laniel8197f012023-12-22 22:02:28 +01001009#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01001010 const char *ifs;
Francis Laniel8197f012023-12-22 22:02:28 +01001011#ifdef __U_BOOT__
1012 int flag_repeat;
1013 int do_repeat;
Francis Laniel3b13faf2023-12-22 22:02:34 +01001014 int run_command_flags;
Francis Laniel8197f012023-12-22 22:02:28 +01001015#endif /* __U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01001016 char *ifs_whitespace; /* = G.ifs or malloced */
Francis Laniel8197f012023-12-22 22:02:28 +01001017#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01001018 const char *cwd;
Francis Laniel8197f012023-12-22 22:02:28 +01001019#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01001020 struct variable *top_var;
1021 char **expanded_assignments;
1022 struct variable **shadowed_vars_pp;
1023 unsigned var_nest_level;
Francis Laniel8197f012023-12-22 22:02:28 +01001024#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01001025#if ENABLE_HUSH_FUNCTIONS
1026# if ENABLE_HUSH_LOCAL
1027 unsigned func_nest_level; /* solely to prevent "local v" in non-functions */
1028# endif
1029 struct function *top_func;
1030#endif
1031 /* Signal and trap handling */
1032#if ENABLE_HUSH_FAST
1033 unsigned count_SIGCHLD;
1034 unsigned handled_SIGCHLD;
1035 smallint we_have_children;
1036#endif
1037#if ENABLE_HUSH_LINENO_VAR
1038 unsigned parse_lineno;
1039 unsigned execute_lineno;
1040#endif
1041 HFILE *HFILE_list;
1042 HFILE *HFILE_stdin;
1043 /* Which signals have non-DFL handler (even with no traps set)?
1044 * Set at the start to:
1045 * (SIGQUIT + maybe SPECIAL_INTERACTIVE_SIGS + maybe SPECIAL_JOBSTOP_SIGS)
1046 * SPECIAL_INTERACTIVE_SIGS are cleared after fork.
1047 * The rest is cleared right before execv syscalls.
1048 * Other than these two times, never modified.
1049 */
1050 unsigned special_sig_mask;
1051#if ENABLE_HUSH_JOB
1052 unsigned fatal_sig_mask;
1053# define G_fatal_sig_mask (G.fatal_sig_mask)
1054#else
1055# define G_fatal_sig_mask 0
1056#endif
1057#if ENABLE_HUSH_TRAP
1058 int pre_trap_exitcode;
1059# if ENABLE_HUSH_FUNCTIONS
1060 int return_exitcode;
1061# endif
1062 char **traps; /* char *traps[NSIG] */
1063# define G_traps G.traps
1064#else
1065# define G_traps ((char**)NULL)
1066#endif
1067 sigset_t pending_set;
1068#if ENABLE_HUSH_MEMLEAK
1069 unsigned long memleak_value;
1070#endif
1071#if ENABLE_HUSH_MODE_X
1072 unsigned x_mode_depth;
1073 /* "set -x" output should not be redirectable with subsequent 2>FILE.
1074 * We dup fd#2 to x_mode_fd when "set -x" is executed, and use it
1075 * for all subsequent output.
1076 */
1077 int x_mode_fd;
1078 o_string x_mode_buf;
1079#endif
Francis Laniel8197f012023-12-22 22:02:28 +01001080#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01001081#if HUSH_DEBUG >= 2
1082 int debug_indent;
1083#endif
Francis Laniel8197f012023-12-22 22:02:28 +01001084#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01001085 struct sigaction sa;
1086 char optstring_buf[sizeof("eixcs")];
1087#if BASH_EPOCH_VARS
1088 char epoch_buf[sizeof("%llu.nnnnnn") + sizeof(long long)*3];
1089#endif
1090#if ENABLE_FEATURE_EDITING
1091 char user_input_buf[CONFIG_FEATURE_EDITING_MAX_LEN];
1092#endif
Francis Laniel8197f012023-12-22 22:02:28 +01001093#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01001094};
Francis Laniel8197f012023-12-22 22:02:28 +01001095#ifdef __U_BOOT__
1096struct globals *ptr_to_globals;
1097#endif /* __U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01001098#define G (*ptr_to_globals)
1099/* Not #defining name to G.name - this quickly gets unwieldy
1100 * (too many defines). Also, I actually prefer to see when a variable
1101 * is global, thus "G." prefix is a useful hint */
Francis Laniel8197f012023-12-22 22:02:28 +01001102#ifdef __U_BOOT__
1103#define SET_PTR_TO_GLOBALS(x) do { \
1104 (*(struct globals**)&ptr_to_globals) = (void*)(x); \
1105 barrier(); \
1106} while (0)
1107#define INIT_G() do { \
1108 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
1109 G.promptmode = 1; \
1110} while (0)
1111#else /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01001112#define INIT_G() do { \
1113 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
1114 /* memset(&G.sa, 0, sizeof(G.sa)); */ \
1115 sigfillset(&G.sa.sa_mask); \
1116 G.sa.sa_flags = SA_RESTART; \
1117} while (0)
Francis Laniel8197f012023-12-22 22:02:28 +01001118#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01001119
1120
Francis Laniel8197f012023-12-22 22:02:28 +01001121#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01001122/* Function prototypes for builtins */
1123static int builtin_cd(char **argv) FAST_FUNC;
1124#if ENABLE_HUSH_ECHO
1125static int builtin_echo(char **argv) FAST_FUNC;
1126#endif
1127static int builtin_eval(char **argv) FAST_FUNC;
1128static int builtin_exec(char **argv) FAST_FUNC;
1129static int builtin_exit(char **argv) FAST_FUNC;
1130#if ENABLE_HUSH_EXPORT
1131static int builtin_export(char **argv) FAST_FUNC;
1132#endif
1133#if ENABLE_HUSH_READONLY
1134static int builtin_readonly(char **argv) FAST_FUNC;
1135#endif
1136#if ENABLE_HUSH_JOB
1137static int builtin_fg_bg(char **argv) FAST_FUNC;
1138static int builtin_jobs(char **argv) FAST_FUNC;
1139#endif
1140#if ENABLE_HUSH_GETOPTS
1141static int builtin_getopts(char **argv) FAST_FUNC;
1142#endif
1143#if ENABLE_HUSH_HELP
1144static int builtin_help(char **argv) FAST_FUNC;
1145#endif
1146#if MAX_HISTORY && ENABLE_FEATURE_EDITING
1147static int builtin_history(char **argv) FAST_FUNC;
1148#endif
1149#if ENABLE_HUSH_LOCAL
1150static int builtin_local(char **argv) FAST_FUNC;
1151#endif
1152#if ENABLE_HUSH_MEMLEAK
1153static int builtin_memleak(char **argv) FAST_FUNC;
1154#endif
1155#if ENABLE_HUSH_PRINTF
1156static int builtin_printf(char **argv) FAST_FUNC;
1157#endif
1158static int builtin_pwd(char **argv) FAST_FUNC;
1159#if ENABLE_HUSH_READ
1160static int builtin_read(char **argv) FAST_FUNC;
1161#endif
1162#if ENABLE_HUSH_SET
1163static int builtin_set(char **argv) FAST_FUNC;
1164#endif
1165static int builtin_shift(char **argv) FAST_FUNC;
1166static int builtin_source(char **argv) FAST_FUNC;
1167#if ENABLE_HUSH_TEST || BASH_TEST2
1168static int builtin_test(char **argv) FAST_FUNC;
1169#endif
1170#if ENABLE_HUSH_TRAP
1171static int builtin_trap(char **argv) FAST_FUNC;
1172#endif
1173#if ENABLE_HUSH_TYPE
1174static int builtin_type(char **argv) FAST_FUNC;
1175#endif
1176#if ENABLE_HUSH_TIMES
1177static int builtin_times(char **argv) FAST_FUNC;
1178#endif
1179static int builtin_true(char **argv) FAST_FUNC;
1180#if ENABLE_HUSH_UMASK
1181static int builtin_umask(char **argv) FAST_FUNC;
1182#endif
1183#if ENABLE_HUSH_UNSET
1184static int builtin_unset(char **argv) FAST_FUNC;
1185#endif
1186#if ENABLE_HUSH_KILL
1187static int builtin_kill(char **argv) FAST_FUNC;
1188#endif
1189#if ENABLE_HUSH_WAIT
1190static int builtin_wait(char **argv) FAST_FUNC;
1191#endif
1192#if ENABLE_HUSH_LOOPS
1193static int builtin_break(char **argv) FAST_FUNC;
1194static int builtin_continue(char **argv) FAST_FUNC;
1195#endif
1196#if ENABLE_HUSH_FUNCTIONS
1197static int builtin_return(char **argv) FAST_FUNC;
1198#endif
1199
1200/* Table of built-in functions. They can be forked or not, depending on
1201 * context: within pipes, they fork. As simple commands, they do not.
1202 * When used in non-forking context, they can change global variables
1203 * in the parent shell process. If forked, of course they cannot.
1204 * For example, 'unset foo | whatever' will parse and run, but foo will
1205 * still be set at the end. */
1206struct built_in_command {
1207 const char *b_cmd;
1208 int (*b_function)(char **argv) FAST_FUNC;
1209#if ENABLE_HUSH_HELP
1210 const char *b_descr;
1211# define BLTIN(cmd, func, help) { cmd, func, help }
1212#else
1213# define BLTIN(cmd, func, help) { cmd, func }
1214#endif
1215};
1216
1217static const struct built_in_command bltins1[] ALIGN_PTR = {
1218 BLTIN("." , builtin_source , "Run commands in file"),
1219 BLTIN(":" , builtin_true , NULL),
1220#if ENABLE_HUSH_JOB
1221 BLTIN("bg" , builtin_fg_bg , "Resume job in background"),
1222#endif
1223#if ENABLE_HUSH_LOOPS
1224 BLTIN("break" , builtin_break , "Exit loop"),
1225#endif
1226 BLTIN("cd" , builtin_cd , "Change directory"),
1227#if ENABLE_HUSH_LOOPS
1228 BLTIN("continue" , builtin_continue, "Start new loop iteration"),
1229#endif
1230 BLTIN("eval" , builtin_eval , "Construct and run shell command"),
1231 BLTIN("exec" , builtin_exec , "Execute command, don't return to shell"),
1232 BLTIN("exit" , builtin_exit , NULL),
1233#if ENABLE_HUSH_EXPORT
1234 BLTIN("export" , builtin_export , "Set environment variables"),
1235#endif
1236#if ENABLE_HUSH_JOB
1237 BLTIN("fg" , builtin_fg_bg , "Bring job to foreground"),
1238#endif
1239#if ENABLE_HUSH_GETOPTS
1240 BLTIN("getopts" , builtin_getopts , NULL),
1241#endif
1242#if ENABLE_HUSH_HELP
1243 BLTIN("help" , builtin_help , NULL),
1244#endif
1245#if MAX_HISTORY && ENABLE_FEATURE_EDITING
1246 BLTIN("history" , builtin_history , "Show history"),
1247#endif
1248#if ENABLE_HUSH_JOB
1249 BLTIN("jobs" , builtin_jobs , "List jobs"),
1250#endif
1251#if ENABLE_HUSH_KILL
1252 BLTIN("kill" , builtin_kill , "Send signals to processes"),
1253#endif
1254#if ENABLE_HUSH_LOCAL
1255 BLTIN("local" , builtin_local , "Set local variables"),
1256#endif
1257#if ENABLE_HUSH_MEMLEAK
1258 BLTIN("memleak" , builtin_memleak , NULL),
1259#endif
1260#if ENABLE_HUSH_READ
1261 BLTIN("read" , builtin_read , "Input into variable"),
1262#endif
1263#if ENABLE_HUSH_READONLY
1264 BLTIN("readonly" , builtin_readonly, "Make variables read-only"),
1265#endif
1266#if ENABLE_HUSH_FUNCTIONS
1267 BLTIN("return" , builtin_return , "Return from function"),
1268#endif
1269#if ENABLE_HUSH_SET
1270 BLTIN("set" , builtin_set , "Set positional parameters"),
1271#endif
1272 BLTIN("shift" , builtin_shift , "Shift positional parameters"),
1273#if BASH_SOURCE
1274 BLTIN("source" , builtin_source , NULL),
1275#endif
1276#if ENABLE_HUSH_TIMES
1277 BLTIN("times" , builtin_times , NULL),
1278#endif
1279#if ENABLE_HUSH_TRAP
1280 BLTIN("trap" , builtin_trap , "Trap signals"),
1281#endif
1282 BLTIN("true" , builtin_true , NULL),
1283#if ENABLE_HUSH_TYPE
1284 BLTIN("type" , builtin_type , "Show command type"),
1285#endif
1286#if ENABLE_HUSH_ULIMIT
1287 BLTIN("ulimit" , shell_builtin_ulimit, "Control resource limits"),
1288#endif
1289#if ENABLE_HUSH_UMASK
1290 BLTIN("umask" , builtin_umask , "Set file creation mask"),
1291#endif
1292#if ENABLE_HUSH_UNSET
1293 BLTIN("unset" , builtin_unset , "Unset variables"),
1294#endif
1295#if ENABLE_HUSH_WAIT
1296 BLTIN("wait" , builtin_wait , "Wait for process to finish"),
1297#endif
1298};
1299/* These builtins won't be used if we are on NOMMU and need to re-exec
1300 * (it's cheaper to run an external program in this case):
1301 */
1302static const struct built_in_command bltins2[] ALIGN_PTR = {
1303#if ENABLE_HUSH_TEST
1304 BLTIN("[" , builtin_test , NULL),
1305#endif
1306#if BASH_TEST2
1307 BLTIN("[[" , builtin_test , NULL),
1308#endif
1309#if ENABLE_HUSH_ECHO
1310 BLTIN("echo" , builtin_echo , NULL),
1311#endif
1312#if ENABLE_HUSH_PRINTF
1313 BLTIN("printf" , builtin_printf , NULL),
1314#endif
1315 BLTIN("pwd" , builtin_pwd , NULL),
1316#if ENABLE_HUSH_TEST
1317 BLTIN("test" , builtin_test , NULL),
1318#endif
1319};
1320
Francis Laniel8197f012023-12-22 22:02:28 +01001321#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01001322
1323/* Debug printouts.
1324 */
1325#if HUSH_DEBUG >= 2
1326/* prevent disasters with G.debug_indent < 0 */
1327# define indent() fdprintf(2, "%*s", (G.debug_indent * 2) & 0xff, "")
1328# define debug_enter() (G.debug_indent++)
1329# define debug_leave() (G.debug_indent--)
1330#else
1331# define indent() ((void)0)
1332# define debug_enter() ((void)0)
1333# define debug_leave() ((void)0)
1334#endif
1335
1336#ifndef debug_printf
1337# define debug_printf(...) (indent(), fdprintf(2, __VA_ARGS__))
1338#endif
1339
1340#ifndef debug_printf_parse
1341# define debug_printf_parse(...) (indent(), fdprintf(2, __VA_ARGS__))
1342#endif
1343
1344#ifndef debug_printf_heredoc
1345# define debug_printf_heredoc(...) (indent(), fdprintf(2, __VA_ARGS__))
1346#endif
1347
1348#ifndef debug_printf_exec
1349#define debug_printf_exec(...) (indent(), fdprintf(2, __VA_ARGS__))
1350#endif
1351
1352#ifndef debug_printf_env
1353# define debug_printf_env(...) (indent(), fdprintf(2, __VA_ARGS__))
1354#endif
1355
1356#ifndef debug_printf_jobs
1357# define debug_printf_jobs(...) (indent(), fdprintf(2, __VA_ARGS__))
1358# define DEBUG_JOBS 1
1359#else
1360# define DEBUG_JOBS 0
1361#endif
1362
1363#ifndef debug_printf_expand
1364# define debug_printf_expand(...) (indent(), fdprintf(2, __VA_ARGS__))
1365# define DEBUG_EXPAND 1
1366#else
1367# define DEBUG_EXPAND 0
1368#endif
1369
1370#ifndef debug_printf_varexp
1371# define debug_printf_varexp(...) (indent(), fdprintf(2, __VA_ARGS__))
1372#endif
1373
1374#ifndef debug_printf_glob
1375# define debug_printf_glob(...) (indent(), fdprintf(2, __VA_ARGS__))
1376# define DEBUG_GLOB 1
1377#else
1378# define DEBUG_GLOB 0
1379#endif
1380
1381#ifndef debug_printf_redir
1382# define debug_printf_redir(...) (indent(), fdprintf(2, __VA_ARGS__))
1383#endif
1384
1385#ifndef debug_printf_list
1386# define debug_printf_list(...) (indent(), fdprintf(2, __VA_ARGS__))
1387#endif
1388
1389#ifndef debug_printf_subst
1390# define debug_printf_subst(...) (indent(), fdprintf(2, __VA_ARGS__))
1391#endif
1392
1393#ifndef debug_printf_prompt
1394# define debug_printf_prompt(...) (indent(), fdprintf(2, __VA_ARGS__))
1395#endif
1396
1397#ifndef debug_printf_clean
1398# define debug_printf_clean(...) (indent(), fdprintf(2, __VA_ARGS__))
1399# define DEBUG_CLEAN 1
1400#else
1401# define DEBUG_CLEAN 0
1402#endif
1403
1404#if DEBUG_EXPAND
1405static void debug_print_strings(const char *prefix, char **vv)
1406{
1407 indent();
1408 fdprintf(2, "%s:\n", prefix);
1409 while (*vv)
1410 fdprintf(2, " '%s'\n", *vv++);
1411}
1412#else
1413# define debug_print_strings(prefix, vv) ((void)0)
1414#endif
1415
1416
1417/* Leak hunting. Use hush_leaktool.sh for post-processing.
1418 */
1419#if LEAK_HUNTING
1420static void *xxmalloc(int lineno, size_t size)
1421{
1422 void *ptr = xmalloc((size + 0xff) & ~0xff);
1423 fdprintf(2, "line %d: malloc %p\n", lineno, ptr);
1424 return ptr;
1425}
1426static void *xxrealloc(int lineno, void *ptr, size_t size)
1427{
1428 ptr = xrealloc(ptr, (size + 0xff) & ~0xff);
1429 fdprintf(2, "line %d: realloc %p\n", lineno, ptr);
1430 return ptr;
1431}
1432static char *xxstrdup(int lineno, const char *str)
1433{
1434 char *ptr = xstrdup(str);
1435 fdprintf(2, "line %d: strdup %p\n", lineno, ptr);
1436 return ptr;
1437}
1438static void xxfree(void *ptr)
1439{
1440 fdprintf(2, "free %p\n", ptr);
1441 free(ptr);
1442}
1443# define xmalloc(s) xxmalloc(__LINE__, s)
1444# define xrealloc(p, s) xxrealloc(__LINE__, p, s)
1445# define xstrdup(s) xxstrdup(__LINE__, s)
1446# define free(p) xxfree(p)
1447#endif
1448
1449
1450/* Syntax and runtime errors. They always abort scripts.
1451 * In interactive use they usually discard unparsed and/or unexecuted commands
1452 * and return to the prompt.
1453 * HUSH_DEBUG >= 2 prints line number in this file where it was detected.
1454 */
1455#if HUSH_DEBUG < 2
1456# define msg_and_die_if_script(lineno, ...) msg_and_die_if_script(__VA_ARGS__)
1457# define syntax_error(lineno, msg) syntax_error(msg)
1458# define syntax_error_at(lineno, msg) syntax_error_at(msg)
1459# define syntax_error_unterm_ch(lineno, ch) syntax_error_unterm_ch(ch)
1460# define syntax_error_unterm_str(lineno, s) syntax_error_unterm_str(s)
1461# define syntax_error_unexpected_ch(lineno, ch) syntax_error_unexpected_ch(ch)
1462#endif
1463
1464static void die_if_script(void)
1465{
1466 if (!G_interactive_fd) {
1467 if (G.last_exitcode) /* sometines it's 2, not 1 (bash compat) */
1468 xfunc_error_retval = G.last_exitcode;
1469 xfunc_die();
1470 }
1471}
1472
Francis Laniel8197f012023-12-22 22:02:28 +01001473#ifdef __U_BOOT__
1474static void __maybe_unused msg_and_die_if_script(unsigned lineno, const char *fmt, ...)
1475#else /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01001476static void msg_and_die_if_script(unsigned lineno, const char *fmt, ...)
Francis Laniel8197f012023-12-22 22:02:28 +01001477#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01001478{
1479 va_list p;
1480
1481#if HUSH_DEBUG >= 2
1482 bb_error_msg("hush.c:%u", lineno);
1483#endif
1484 va_start(p, fmt);
1485 bb_verror_msg(fmt, p, NULL);
1486 va_end(p);
1487 die_if_script();
1488}
1489
Francis Laniel8197f012023-12-22 22:02:28 +01001490#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01001491static void syntax_error(unsigned lineno UNUSED_PARAM, const char *msg)
1492{
1493 if (msg)
1494 bb_error_msg("syntax error: %s", msg);
1495 else
1496 bb_simple_error_msg("syntax error");
1497 die_if_script();
1498}
Francis Laniel8197f012023-12-22 22:02:28 +01001499#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01001500
1501static void syntax_error_at(unsigned lineno UNUSED_PARAM, const char *msg)
1502{
1503 bb_error_msg("syntax error at '%s'", msg);
1504 die_if_script();
1505}
1506
1507static void syntax_error_unterm_str(unsigned lineno UNUSED_PARAM, const char *s)
1508{
1509 bb_error_msg("syntax error: unterminated %s", s);
1510//? source4.tests fails: in bash, echo ${^} in script does not terminate the script
1511// die_if_script();
1512}
1513
1514static void syntax_error_unterm_ch(unsigned lineno, char ch)
1515{
1516 char msg[2] = { ch, '\0' };
1517 syntax_error_unterm_str(lineno, msg);
1518}
1519
1520static void syntax_error_unexpected_ch(unsigned lineno UNUSED_PARAM, int ch)
1521{
1522 char msg[2];
1523 msg[0] = ch;
1524 msg[1] = '\0';
1525#if HUSH_DEBUG >= 2
1526 bb_error_msg("hush.c:%u", lineno);
1527#endif
1528 bb_error_msg("syntax error: unexpected %s", ch == EOF ? "EOF" : msg);
1529 die_if_script();
1530}
1531
1532#if HUSH_DEBUG < 2
1533# undef msg_and_die_if_script
1534# undef syntax_error
1535# undef syntax_error_at
1536# undef syntax_error_unterm_ch
1537# undef syntax_error_unterm_str
1538# undef syntax_error_unexpected_ch
1539#else
1540# define msg_and_die_if_script(...) msg_and_die_if_script(__LINE__, __VA_ARGS__)
1541# define syntax_error(msg) syntax_error(__LINE__, msg)
1542# define syntax_error_at(msg) syntax_error_at(__LINE__, msg)
1543# define syntax_error_unterm_ch(ch) syntax_error_unterm_ch(__LINE__, ch)
1544# define syntax_error_unterm_str(s) syntax_error_unterm_str(__LINE__, s)
1545# define syntax_error_unexpected_ch(ch) syntax_error_unexpected_ch(__LINE__, ch)
1546#endif
1547
Francis Lanielb234f7e2023-12-22 22:02:27 +01001548/* Utility functions
1549 */
1550/* Replace each \x with x in place, return ptr past NUL. */
1551static char *unbackslash(char *src)
1552{
Francis Laniel8197f012023-12-22 22:02:28 +01001553#ifdef __U_BOOT__
1554 char *dst = src = (char *)strchrnul(src, '\\');
1555#else /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01001556 char *dst = src = strchrnul(src, '\\');
Francis Laniel8197f012023-12-22 22:02:28 +01001557#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01001558 while (1) {
1559 if (*src == '\\') {
1560 src++;
1561 if (*src != '\0') {
1562 /* \x -> x */
1563 *dst++ = *src++;
1564 continue;
1565 }
1566 /* else: "\<nul>". Do not delete this backslash.
1567 * Testcase: eval 'echo ok\'
1568 */
1569 *dst++ = '\\';
1570 /* fallthrough */
1571 }
1572 if ((*dst++ = *src++) == '\0')
1573 break;
1574 }
1575 return dst;
1576}
1577
1578static char **add_strings_to_strings(char **strings, char **add, int need_to_dup)
1579{
1580 int i;
1581 unsigned count1;
1582 unsigned count2;
1583 char **v;
1584
1585 v = strings;
1586 count1 = 0;
1587 if (v) {
1588 while (*v) {
1589 count1++;
1590 v++;
1591 }
1592 }
1593 count2 = 0;
1594 v = add;
1595 while (*v) {
1596 count2++;
1597 v++;
1598 }
1599 v = xrealloc(strings, (count1 + count2 + 1) * sizeof(char*));
1600 v[count1 + count2] = NULL;
1601 i = count2;
1602 while (--i >= 0)
1603 v[count1 + i] = (need_to_dup ? xstrdup(add[i]) : add[i]);
1604 return v;
1605}
1606#if LEAK_HUNTING
1607static char **xx_add_strings_to_strings(int lineno, char **strings, char **add, int need_to_dup)
1608{
1609 char **ptr = add_strings_to_strings(strings, add, need_to_dup);
1610 fdprintf(2, "line %d: add_strings_to_strings %p\n", lineno, ptr);
1611 return ptr;
1612}
1613#define add_strings_to_strings(strings, add, need_to_dup) \
1614 xx_add_strings_to_strings(__LINE__, strings, add, need_to_dup)
1615#endif
1616
1617/* Note: takes ownership of "add" ptr (it is not strdup'ed) */
1618static char **add_string_to_strings(char **strings, char *add)
1619{
1620 char *v[2];
1621 v[0] = add;
1622 v[1] = NULL;
1623 return add_strings_to_strings(strings, v, /*dup:*/ 0);
1624}
Francis Laniel8197f012023-12-22 22:02:28 +01001625
Francis Lanielb234f7e2023-12-22 22:02:27 +01001626#if LEAK_HUNTING
1627static char **xx_add_string_to_strings(int lineno, char **strings, char *add)
1628{
1629 char **ptr = add_string_to_strings(strings, add);
1630 fdprintf(2, "line %d: add_string_to_strings %p\n", lineno, ptr);
1631 return ptr;
1632}
1633#define add_string_to_strings(strings, add) \
1634 xx_add_string_to_strings(__LINE__, strings, add)
1635#endif
1636
1637static void free_strings(char **strings)
1638{
1639 char **v;
1640
1641 if (!strings)
1642 return;
1643 v = strings;
1644 while (*v) {
1645 free(*v);
1646 v++;
1647 }
1648 free(strings);
1649}
1650
Francis Laniel8197f012023-12-22 22:02:28 +01001651#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01001652static int dup_CLOEXEC(int fd, int avoid_fd)
1653{
1654 int newfd;
1655 repeat:
1656 newfd = fcntl(fd, F_DUPFD_CLOEXEC, avoid_fd + 1);
1657 if (newfd >= 0) {
1658 if (F_DUPFD_CLOEXEC == F_DUPFD) /* if old libc (w/o F_DUPFD_CLOEXEC) */
1659 fcntl(newfd, F_SETFD, FD_CLOEXEC);
1660 } else { /* newfd < 0 */
1661 if (errno == EBUSY)
1662 goto repeat;
1663 if (errno == EINTR)
1664 goto repeat;
1665 }
1666 return newfd;
1667}
1668
1669static int xdup_CLOEXEC_and_close(int fd, int avoid_fd)
1670{
1671 int newfd;
1672 repeat:
1673 newfd = fcntl(fd, F_DUPFD_CLOEXEC, avoid_fd + 1);
1674 if (newfd < 0) {
1675 if (errno == EBUSY)
1676 goto repeat;
1677 if (errno == EINTR)
1678 goto repeat;
1679 /* fd was not open? */
1680 if (errno == EBADF)
1681 return fd;
1682 xfunc_die();
1683 }
1684 if (F_DUPFD_CLOEXEC == F_DUPFD) /* if old libc (w/o F_DUPFD_CLOEXEC) */
1685 fcntl(newfd, F_SETFD, FD_CLOEXEC);
1686 close(fd);
1687 return newfd;
1688}
1689
1690
1691/* Manipulating HFILEs */
1692static HFILE *hfopen(const char *name)
1693{
1694 HFILE *fp;
1695 int fd;
1696
1697 fd = STDIN_FILENO;
1698 if (name) {
1699 fd = open(name, O_RDONLY | O_CLOEXEC);
1700 if (fd < 0)
1701 return NULL;
1702 if (O_CLOEXEC == 0) /* ancient libc */
1703 close_on_exec_on(fd);
1704 }
1705
1706 fp = xmalloc(sizeof(*fp));
1707 if (name == NULL)
1708 G.HFILE_stdin = fp;
1709 fp->fd = fd;
1710 fp->cur = fp->end = fp->buf;
1711 fp->next_hfile = G.HFILE_list;
1712 G.HFILE_list = fp;
1713 return fp;
1714}
1715static void hfclose(HFILE *fp)
1716{
1717 HFILE **pp = &G.HFILE_list;
1718 while (*pp) {
1719 HFILE *cur = *pp;
1720 if (cur == fp) {
1721 *pp = cur->next_hfile;
1722 break;
1723 }
1724 pp = &cur->next_hfile;
1725 }
1726 if (fp->fd >= 0)
1727 close(fp->fd);
1728 free(fp);
1729}
1730static int refill_HFILE_and_getc(HFILE *fp)
1731{
1732 int n;
1733
1734 if (fp->fd < 0) {
1735 /* Already saw EOF */
1736 return EOF;
1737 }
1738#if ENABLE_HUSH_INTERACTIVE && !ENABLE_FEATURE_EDITING
1739 /* If user presses ^C, read() restarts after SIGINT (we use SA_RESTART).
1740 * IOW: ^C will not immediately stop line input.
1741 * But poll() is different: it does NOT restart after signals.
1742 */
1743 if (fp == G.HFILE_stdin) {
1744 struct pollfd pfd[1];
1745 pfd[0].fd = fp->fd;
1746 pfd[0].events = POLLIN;
1747 n = poll(pfd, 1, -1);
1748 if (n < 0
1749 /*&& errno == EINTR - assumed true */
1750 && sigismember(&G.pending_set, SIGINT)
1751 ) {
1752 return '\0';
1753 }
1754 }
1755#else
1756/* if FEATURE_EDITING=y, we do not use this routine for interactive input */
1757#endif
1758 /* Try to buffer more input */
1759 n = safe_read(fp->fd, fp->buf, sizeof(fp->buf));
1760 if (n < 0) {
1761 bb_simple_perror_msg("read error");
1762 n = 0;
1763 }
1764 fp->cur = fp->buf;
1765 fp->end = fp->buf + n;
1766 if (n == 0) {
1767 /* EOF/error */
1768 close(fp->fd);
1769 fp->fd = -1;
1770 return EOF;
1771 }
1772 return (unsigned char)(*fp->cur++);
1773}
1774/* Inlined for common case of non-empty buffer.
1775 */
1776static ALWAYS_INLINE int hfgetc(HFILE *fp)
1777{
1778 if (fp->cur < fp->end)
1779 return (unsigned char)(*fp->cur++);
1780 /* Buffer empty */
1781 return refill_HFILE_and_getc(fp);
1782}
1783static int move_HFILEs_on_redirect(int fd, int avoid_fd)
1784{
1785 HFILE *fl = G.HFILE_list;
1786 while (fl) {
1787 if (fd == fl->fd) {
1788 /* We use it only on script files, they are all CLOEXEC */
1789 fl->fd = xdup_CLOEXEC_and_close(fd, avoid_fd);
1790 debug_printf_redir("redirect_fd %d: matches a script fd, moving it to %d\n", fd, fl->fd);
1791 return 1; /* "found and moved" */
1792 }
1793 fl = fl->next_hfile;
1794 }
1795#if ENABLE_HUSH_MODE_X
1796 if (G.x_mode_fd > 0 && fd == G.x_mode_fd) {
1797 G.x_mode_fd = xdup_CLOEXEC_and_close(fd, avoid_fd);
1798 return 1; /* "found and moved" */
1799 }
1800#endif
1801 return 0; /* "not in the list" */
1802}
1803#if ENABLE_FEATURE_SH_STANDALONE && BB_MMU
1804static void close_all_HFILE_list(void)
1805{
1806 HFILE *fl = G.HFILE_list;
1807 while (fl) {
1808 /* hfclose would also free HFILE object.
1809 * It is disastrous if we share memory with a vforked parent.
1810 * I'm not sure we never come here after vfork.
1811 * Therefore just close fd, nothing more.
1812 *
1813 * ">" instead of ">=": we don't close fd#0,
1814 * interactive shell uses hfopen(NULL) as stdin input
1815 * which has fl->fd == 0, but fd#0 gets redirected in pipes.
1816 * If we'd close it here, then e.g. interactive "set | sort"
1817 * with NOFORKed sort, would have sort's input fd closed.
1818 */
1819 if (fl->fd > 0)
1820 /*hfclose(fl); - unsafe */
1821 close(fl->fd);
1822 fl = fl->next_hfile;
1823 }
1824}
1825#endif
1826static int fd_in_HFILEs(int fd)
1827{
1828 HFILE *fl = G.HFILE_list;
1829 while (fl) {
1830 if (fl->fd == fd)
1831 return 1;
1832 fl = fl->next_hfile;
1833 }
1834 return 0;
1835}
1836
Francis Laniel8197f012023-12-22 22:02:28 +01001837#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01001838
1839/* Helpers for setting new $n and restoring them back
1840 */
1841typedef struct save_arg_t {
1842 char *sv_argv0;
1843 char **sv_g_argv;
1844 int sv_g_argc;
Francis Laniel8197f012023-12-22 22:02:28 +01001845#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01001846 IF_HUSH_SET(smallint sv_g_malloced;)
Francis Laniel8197f012023-12-22 22:02:28 +01001847#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01001848} save_arg_t;
1849
Francis Laniel8197f012023-12-22 22:02:28 +01001850#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01001851static void save_and_replace_G_args(save_arg_t *sv, char **argv)
1852{
1853 sv->sv_argv0 = argv[0];
1854 sv->sv_g_argv = G.global_argv;
1855 sv->sv_g_argc = G.global_argc;
1856 IF_HUSH_SET(sv->sv_g_malloced = G.global_args_malloced;)
1857
1858 argv[0] = G.global_argv[0]; /* retain $0 */
1859 G.global_argv = argv;
1860 IF_HUSH_SET(G.global_args_malloced = 0;)
1861
1862 G.global_argc = 1 + string_array_len(argv + 1);
1863}
1864
1865static void restore_G_args(save_arg_t *sv, char **argv)
1866{
1867#if ENABLE_HUSH_SET
1868 if (G.global_args_malloced) {
1869 /* someone ran "set -- arg1 arg2 ...", undo */
1870 char **pp = G.global_argv;
1871 while (*++pp) /* note: does not free $0 */
1872 free(*pp);
1873 free(G.global_argv);
1874 }
1875#endif
1876 argv[0] = sv->sv_argv0;
1877 G.global_argv = sv->sv_g_argv;
1878 G.global_argc = sv->sv_g_argc;
1879 IF_HUSH_SET(G.global_args_malloced = sv->sv_g_malloced;)
1880}
Francis Laniel8197f012023-12-22 22:02:28 +01001881#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01001882
1883
Francis Laniel8197f012023-12-22 22:02:28 +01001884#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01001885/* Basic theory of signal handling in shell
1886 * ========================================
1887 * This does not describe what hush does, rather, it is current understanding
1888 * what it _should_ do. If it doesn't, it's a bug.
1889 * http://www.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#trap
1890 *
1891 * Signals are handled only after each pipe ("cmd | cmd | cmd" thing)
1892 * is finished or backgrounded. It is the same in interactive and
1893 * non-interactive shells, and is the same regardless of whether
1894 * a user trap handler is installed or a shell special one is in effect.
1895 * ^C or ^Z from keyboard seems to execute "at once" because it usually
1896 * backgrounds (i.e. stops) or kills all members of currently running
1897 * pipe.
1898 *
1899 * Wait builtin is interruptible by signals for which user trap is set
1900 * or by SIGINT in interactive shell.
1901 *
1902 * Trap handlers will execute even within trap handlers. (right?)
1903 *
1904 * User trap handlers are forgotten when subshell ("(cmd)") is entered,
1905 * except for handlers set to '' (empty string).
1906 *
1907 * If job control is off, backgrounded commands ("cmd &")
1908 * have SIGINT, SIGQUIT set to SIG_IGN.
1909 *
1910 * Commands which are run in command substitution ("`cmd`")
1911 * have SIGTTIN, SIGTTOU, SIGTSTP set to SIG_IGN.
1912 *
1913 * Ordinary commands have signals set to SIG_IGN/DFL as inherited
1914 * by the shell from its parent.
1915 *
1916 * Signals which differ from SIG_DFL action
1917 * (note: child (i.e., [v]forked) shell is not an interactive shell):
1918 *
1919 * SIGQUIT: ignore
1920 * SIGTERM (interactive): ignore
1921 * SIGHUP (interactive):
1922 * send SIGCONT to stopped jobs, send SIGHUP to all jobs and exit
1923 * SIGTTIN, SIGTTOU, SIGTSTP (if job control is on): ignore
1924 * Note that ^Z is handled not by trapping SIGTSTP, but by seeing
1925 * that all pipe members are stopped. Try this in bash:
1926 * while :; do :; done - ^Z does not background it
1927 * (while :; do :; done) - ^Z backgrounds it
1928 * SIGINT (interactive): wait for last pipe, ignore the rest
1929 * of the command line, show prompt. NB: ^C does not send SIGINT
1930 * to interactive shell while shell is waiting for a pipe,
1931 * since shell is bg'ed (is not in foreground process group).
1932 * Example 1: this waits 5 sec, but does not execute ls:
1933 * "echo $$; sleep 5; ls -l" + "kill -INT <pid>"
1934 * Example 2: this does not wait and does not execute ls:
1935 * "echo $$; sleep 5 & wait; ls -l" + "kill -INT <pid>"
1936 * Example 3: this does not wait 5 sec, but executes ls:
1937 * "sleep 5; ls -l" + press ^C
1938 * Example 4: this does not wait and does not execute ls:
1939 * "sleep 5 & wait; ls -l" + press ^C
1940 *
1941 * (What happens to signals which are IGN on shell start?)
1942 * (What happens with signal mask on shell start?)
1943 *
1944 * Old implementation
1945 * ==================
1946 * We use in-kernel pending signal mask to determine which signals were sent.
1947 * We block all signals which we don't want to take action immediately,
1948 * i.e. we block all signals which need to have special handling as described
1949 * above, and all signals which have traps set.
1950 * After each pipe execution, we extract any pending signals via sigtimedwait()
1951 * and act on them.
1952 *
1953 * unsigned special_sig_mask: a mask of such "special" signals
1954 * sigset_t blocked_set: current blocked signal set
1955 *
1956 * "trap - SIGxxx":
1957 * clear bit in blocked_set unless it is also in special_sig_mask
1958 * "trap 'cmd' SIGxxx":
1959 * set bit in blocked_set (even if 'cmd' is '')
1960 * after [v]fork, if we plan to be a shell:
1961 * unblock signals with special interactive handling
1962 * (child shell is not interactive),
1963 * unset all traps except '' (note: regardless of child shell's type - {}, (), etc)
1964 * after [v]fork, if we plan to exec:
1965 * POSIX says fork clears pending signal mask in child - no need to clear it.
1966 * Restore blocked signal set to one inherited by shell just prior to exec.
1967 *
1968 * Note: as a result, we do not use signal handlers much. The only uses
1969 * are to count SIGCHLDs
1970 * and to restore tty pgrp on signal-induced exit.
1971 *
1972 * Note 2 (compat):
1973 * Standard says "When a subshell is entered, traps that are not being ignored
1974 * are set to the default actions". bash interprets it so that traps which
1975 * are set to '' (ignore) are NOT reset to defaults. We do the same.
1976 *
1977 * Problem: the above approach makes it unwieldy to catch signals while
1978 * we are in read builtin, or while we read commands from stdin:
1979 * masked signals are not visible!
1980 *
1981 * New implementation
1982 * ==================
1983 * We record each signal we are interested in by installing signal handler
1984 * for them - a bit like emulating kernel pending signal mask in userspace.
1985 * We are interested in: signals which need to have special handling
1986 * as described above, and all signals which have traps set.
1987 * Signals are recorded in pending_set.
1988 * After each pipe execution, we extract any pending signals
1989 * and act on them.
1990 *
1991 * unsigned special_sig_mask: a mask of shell-special signals.
1992 * unsigned fatal_sig_mask: a mask of signals on which we restore tty pgrp.
1993 * char *traps[sig] if trap for sig is set (even if it's '').
1994 * sigset_t pending_set: set of sigs we received.
1995 *
1996 * "trap - SIGxxx":
1997 * if sig is in special_sig_mask, set handler back to:
1998 * record_pending_signo, or to IGN if it's a tty stop signal
1999 * if sig is in fatal_sig_mask, set handler back to sigexit.
2000 * else: set handler back to SIG_DFL
2001 * "trap 'cmd' SIGxxx":
2002 * set handler to record_pending_signo.
2003 * "trap '' SIGxxx":
2004 * set handler to SIG_IGN.
2005 * after [v]fork, if we plan to be a shell:
2006 * set signals with special interactive handling to SIG_DFL
2007 * (because child shell is not interactive),
2008 * unset all traps except '' (note: regardless of child shell's type - {}, (), etc)
2009 * after [v]fork, if we plan to exec:
2010 * POSIX says fork clears pending signal mask in child - no need to clear it.
2011 *
2012 * To make wait builtin interruptible, we handle SIGCHLD as special signal,
2013 * otherwise (if we leave it SIG_DFL) sigsuspend in wait builtin will not wake up on it.
2014 *
2015 * Note (compat):
2016 * Standard says "When a subshell is entered, traps that are not being ignored
2017 * are set to the default actions". bash interprets it so that traps which
2018 * are set to '' (ignore) are NOT reset to defaults. We do the same.
2019 */
2020enum {
2021 SPECIAL_INTERACTIVE_SIGS = 0
2022 | (1 << SIGTERM)
2023 | (1 << SIGINT)
2024 | (1 << SIGHUP)
2025 ,
2026 SPECIAL_JOBSTOP_SIGS = 0
2027#if ENABLE_HUSH_JOB
2028 | (1 << SIGTTIN)
2029 | (1 << SIGTTOU)
2030 | (1 << SIGTSTP)
2031#endif
2032 ,
2033};
2034
2035static void record_pending_signo(int sig)
2036{
2037 sigaddset(&G.pending_set, sig);
2038#if ENABLE_HUSH_FAST
2039 if (sig == SIGCHLD) {
2040 G.count_SIGCHLD++;
2041//bb_error_msg("[%d] SIGCHLD_handler: G.count_SIGCHLD:%d G.handled_SIGCHLD:%d", getpid(), G.count_SIGCHLD, G.handled_SIGCHLD);
2042 }
2043#endif
2044}
2045
2046static sighandler_t install_sighandler(int sig, sighandler_t handler)
2047{
2048 struct sigaction old_sa;
2049
2050 /* We could use signal() to install handlers... almost:
2051 * except that we need to mask ALL signals while handlers run.
2052 * I saw signal nesting in strace, race window isn't small.
2053 * SA_RESTART is also needed, but in Linux, signal()
2054 * sets SA_RESTART too.
2055 */
2056 /* memset(&G.sa, 0, sizeof(G.sa)); - already done */
2057 /* sigfillset(&G.sa.sa_mask); - already done */
2058 /* G.sa.sa_flags = SA_RESTART; - already done */
2059 G.sa.sa_handler = handler;
2060 sigaction(sig, &G.sa, &old_sa);
2061 return old_sa.sa_handler;
2062}
Francis Laniel8197f012023-12-22 22:02:28 +01002063#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01002064
Francis Laniel8197f012023-12-22 22:02:28 +01002065#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01002066static void hush_exit(int exitcode) NORETURN;
2067
2068static void restore_ttypgrp_and__exit(void) NORETURN;
2069static void restore_ttypgrp_and__exit(void)
2070{
2071 /* xfunc has failed! die die die */
2072 /* no EXIT traps, this is an escape hatch! */
2073 G.exiting = 1;
2074 hush_exit(xfunc_error_retval);
2075}
2076
2077#if ENABLE_HUSH_JOB
2078
2079/* Needed only on some libc:
2080 * It was observed that on exit(), fgetc'ed buffered data
2081 * gets "unwound" via lseek(fd, -NUM, SEEK_CUR).
2082 * With the net effect that even after fork(), not vfork(),
2083 * exit() in NOEXECed applet in "sh SCRIPT":
2084 * noexec_applet_here
2085 * echo END_OF_SCRIPT
2086 * lseeks fd in input FILE object from EOF to "e" in "echo END_OF_SCRIPT".
2087 * This makes "echo END_OF_SCRIPT" executed twice.
2088 * Similar problems can be seen with msg_and_die_if_script() -> xfunc_die()
2089 * and in `cmd` handling.
2090 * If set as die_func(), this makes xfunc_die() exit via _exit(), not exit():
2091 */
2092static void fflush_and__exit(void) NORETURN;
2093static void fflush_and__exit(void)
2094{
2095 fflush_all();
2096 _exit(xfunc_error_retval);
2097}
2098
2099/* After [v]fork, in child: do not restore tty pgrp on xfunc death */
2100# define disable_restore_tty_pgrp_on_exit() (die_func = fflush_and__exit)
2101/* After [v]fork, in parent: restore tty pgrp on xfunc death */
2102# define enable_restore_tty_pgrp_on_exit() (die_func = restore_ttypgrp_and__exit)
2103
2104/* Restores tty foreground process group, and exits.
2105 * May be called as signal handler for fatal signal
2106 * (will resend signal to itself, producing correct exit state)
2107 * or called directly with -EXITCODE.
2108 * We also call it if xfunc is exiting.
2109 */
2110static void sigexit(int sig) NORETURN;
2111static void sigexit(int sig)
2112{
2113 /* Careful: we can end up here after [v]fork. Do not restore
2114 * tty pgrp then, only top-level shell process does that */
2115 if (G_saved_tty_pgrp && getpid() == G.root_pid) {
2116 /* Disable all signals: job control, SIGPIPE, etc.
2117 * Mostly paranoid measure, to prevent infinite SIGTTOU.
2118 */
2119 sigprocmask_allsigs(SIG_BLOCK);
2120 tcsetpgrp(G_interactive_fd, G_saved_tty_pgrp);
2121 }
2122
2123 /* Not a signal, just exit */
2124 if (sig <= 0)
2125 _exit(- sig);
2126
2127 kill_myself_with_sig(sig); /* does not return */
2128}
2129#else
2130
2131# define disable_restore_tty_pgrp_on_exit() ((void)0)
2132# define enable_restore_tty_pgrp_on_exit() ((void)0)
2133
2134#endif
Francis Laniel8197f012023-12-22 22:02:28 +01002135#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01002136
Francis Laniel8197f012023-12-22 22:02:28 +01002137#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01002138static sighandler_t pick_sighandler(unsigned sig)
2139{
2140 sighandler_t handler = SIG_DFL;
2141 if (sig < sizeof(unsigned)*8) {
2142 unsigned sigmask = (1 << sig);
2143
2144#if ENABLE_HUSH_JOB
2145 /* is sig fatal? */
2146 if (G_fatal_sig_mask & sigmask)
2147 handler = sigexit;
2148 else
2149#endif
2150 /* sig has special handling? */
2151 if (G.special_sig_mask & sigmask) {
2152 handler = record_pending_signo;
2153 /* TTIN/TTOU/TSTP can't be set to record_pending_signo
2154 * in order to ignore them: they will be raised
2155 * in an endless loop when we try to do some
2156 * terminal ioctls! We do have to _ignore_ these.
2157 */
2158 if (SPECIAL_JOBSTOP_SIGS & sigmask)
2159 handler = SIG_IGN;
2160 }
2161 }
2162 return handler;
2163}
Francis Laniel8197f012023-12-22 22:02:28 +01002164#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01002165
Francis Laniel8197f012023-12-22 22:02:28 +01002166#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01002167/* Restores tty foreground process group, and exits. */
2168static void hush_exit(int exitcode)
2169{
2170#if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT
2171 save_history(G.line_input_state); /* may be NULL */
2172#endif
2173
2174 fflush_all();
2175 if (G.exiting <= 0 && G_traps && G_traps[0] && G_traps[0][0]) {
2176 char *argv[3];
2177 /* argv[0] is unused */
2178 argv[1] = xstrdup(G_traps[0]); /* copy, since EXIT trap handler may modify G_traps[0] */
2179 argv[2] = NULL;
2180 G.exiting = 1; /* prevent EXIT trap recursion */
2181 /* Note: G_traps[0] is not cleared!
2182 * "trap" will still show it, if executed
2183 * in the handler */
2184 builtin_eval(argv);
2185 }
2186
2187#if ENABLE_FEATURE_CLEAN_UP
2188 {
2189 struct variable *cur_var;
2190 if (G.cwd != bb_msg_unknown)
2191 free((char*)G.cwd);
2192 cur_var = G.top_var;
2193 while (cur_var) {
2194 struct variable *tmp = cur_var;
2195 if (!cur_var->max_len)
2196 free(cur_var->varstr);
2197 cur_var = cur_var->next;
2198 free(tmp);
2199 }
2200 }
2201#endif
2202
2203 fflush_all();
2204#if ENABLE_HUSH_JOB
2205 sigexit(- (exitcode & 0xff));
2206#else
2207 _exit(exitcode);
2208#endif
2209}
2210
2211//TODO: return a mask of ALL handled sigs?
2212static int check_and_run_traps(void)
2213{
2214 int last_sig = 0;
2215
2216 while (1) {
2217 int sig;
2218
2219 if (sigisemptyset(&G.pending_set))
2220 break;
2221 sig = 0;
2222 do {
2223 sig++;
2224 if (sigismember(&G.pending_set, sig)) {
2225 sigdelset(&G.pending_set, sig);
2226 goto got_sig;
2227 }
2228 } while (sig < NSIG);
2229 break;
2230 got_sig:
2231#if ENABLE_HUSH_TRAP
2232 if (G_traps && G_traps[sig]) {
2233 debug_printf_exec("%s: sig:%d handler:'%s'\n", __func__, sig, G.traps[sig]);
2234 if (G_traps[sig][0]) {
2235 /* We have user-defined handler */
2236 smalluint save_rcode;
2237 int save_pre;
2238 char *argv[3];
2239 /* argv[0] is unused */
2240 argv[1] = xstrdup(G_traps[sig]);
2241 /* why strdup? trap can modify itself: trap 'trap "echo oops" INT' INT */
2242 argv[2] = NULL;
2243 save_pre = G.pre_trap_exitcode;
2244 G.pre_trap_exitcode = save_rcode = G.last_exitcode;
2245 builtin_eval(argv);
2246 free(argv[1]);
2247 G.pre_trap_exitcode = save_pre;
2248 G.last_exitcode = save_rcode;
2249# if ENABLE_HUSH_FUNCTIONS
2250 if (G.return_exitcode >= 0) {
2251 debug_printf_exec("trap exitcode:%d\n", G.return_exitcode);
2252 G.last_exitcode = G.return_exitcode;
2253 }
2254# endif
2255 last_sig = sig;
2256 } /* else: "" trap, ignoring signal */
2257 continue;
2258 }
2259#endif
2260 /* not a trap: special action */
2261 switch (sig) {
2262 case SIGINT:
2263 debug_printf_exec("%s: sig:%d default SIGINT handler\n", __func__, sig);
2264 G.flag_SIGINT = 1;
2265 last_sig = sig;
2266 break;
2267#if ENABLE_HUSH_JOB
2268 case SIGHUP: {
2269//TODO: why are we doing this? ash and dash don't do this,
2270//they have no handler for SIGHUP at all,
2271//they rely on kernel to send SIGHUP+SIGCONT to orphaned process groups
2272 struct pipe *job;
2273 debug_printf_exec("%s: sig:%d default SIGHUP handler\n", __func__, sig);
2274 /* bash is observed to signal whole process groups,
2275 * not individual processes */
2276 for (job = G.job_list; job; job = job->next) {
2277 if (job->pgrp <= 0)
2278 continue;
2279 debug_printf_exec("HUPing pgrp %d\n", job->pgrp);
2280 if (kill(- job->pgrp, SIGHUP) == 0)
2281 kill(- job->pgrp, SIGCONT);
2282 }
2283 sigexit(SIGHUP);
2284 }
2285#endif
2286#if ENABLE_HUSH_FAST
2287 case SIGCHLD:
2288 debug_printf_exec("%s: sig:%d default SIGCHLD handler\n", __func__, sig);
2289 G.count_SIGCHLD++;
2290//bb_error_msg("[%d] check_and_run_traps: G.count_SIGCHLD:%d G.handled_SIGCHLD:%d", getpid(), G.count_SIGCHLD, G.handled_SIGCHLD);
2291 /* Note:
2292 * We don't do 'last_sig = sig' here -> NOT returning this sig.
2293 * This simplifies wait builtin a bit.
2294 */
2295 break;
2296#endif
2297 default: /* ignored: */
2298 debug_printf_exec("%s: sig:%d default handling is to ignore\n", __func__, sig);
2299 /* SIGTERM, SIGQUIT, SIGTTIN, SIGTTOU, SIGTSTP */
2300 /* Note:
2301 * We don't do 'last_sig = sig' here -> NOT returning this sig.
2302 * Example: wait is not interrupted by TERM
2303 * in interactive shell, because TERM is ignored.
2304 */
2305 break;
2306 }
2307 }
2308 return last_sig;
2309}
2310
2311
2312static const char *get_cwd(int force)
2313{
2314 if (force || G.cwd == NULL) {
2315 /* xrealloc_getcwd_or_warn(arg) calls free(arg),
2316 * we must not try to free(bb_msg_unknown) */
2317 if (G.cwd == bb_msg_unknown)
2318 G.cwd = NULL;
2319 G.cwd = xrealloc_getcwd_or_warn((char *)G.cwd);
2320 if (!G.cwd)
2321 G.cwd = bb_msg_unknown;
2322 }
2323 return G.cwd;
2324}
2325
Francis Laniel8197f012023-12-22 22:02:28 +01002326#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01002327
2328/*
2329 * Shell and environment variable support
2330 */
2331static struct variable **get_ptr_to_local_var(const char *name, unsigned len)
2332{
2333 struct variable **pp;
2334 struct variable *cur;
2335
2336 pp = &G.top_var;
2337 while ((cur = *pp) != NULL) {
2338 if (strncmp(cur->varstr, name, len) == 0 && cur->varstr[len] == '=')
2339 return pp;
2340 pp = &cur->next;
2341 }
2342 return NULL;
2343}
2344
2345static const char* FAST_FUNC get_local_var_value(const char *name)
2346{
2347 struct variable **vpp;
2348 unsigned len = strlen(name);
2349
2350 if (G.expanded_assignments) {
2351 char **cpp = G.expanded_assignments;
2352 while (*cpp) {
2353 char *cp = *cpp;
2354 if (strncmp(cp, name, len) == 0 && cp[len] == '=')
2355 return cp + len + 1;
2356 cpp++;
2357 }
2358 }
2359
2360 vpp = get_ptr_to_local_var(name, len);
2361 if (vpp)
2362 return (*vpp)->varstr + len + 1;
2363
Francis Laniel8197f012023-12-22 22:02:28 +01002364#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01002365 if (strcmp(name, "PPID") == 0)
2366 return utoa(G.root_ppid);
Francis Laniel8197f012023-12-22 22:02:28 +01002367#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01002368 // bash compat: UID? EUID?
2369#if ENABLE_HUSH_RANDOM_SUPPORT
2370 if (strcmp(name, "RANDOM") == 0)
2371 return utoa(next_random(&G.random_gen));
2372#endif
2373#if ENABLE_HUSH_LINENO_VAR
2374 if (strcmp(name, "LINENO") == 0)
2375 return utoa(G.execute_lineno);
2376#endif
2377#if BASH_EPOCH_VARS
2378 {
2379 const char *fmt = NULL;
2380 if (strcmp(name, "EPOCHSECONDS") == 0)
2381 fmt = "%llu";
2382 else if (strcmp(name, "EPOCHREALTIME") == 0)
2383 fmt = "%llu.%06u";
2384 if (fmt) {
2385 struct timeval tv;
2386 xgettimeofday(&tv);
2387 sprintf(G.epoch_buf, fmt, (unsigned long long)tv.tv_sec,
2388 (unsigned)tv.tv_usec);
2389 return G.epoch_buf;
2390 }
2391 }
2392#endif
2393 return NULL;
2394}
2395
Francis Laniel8197f012023-12-22 22:02:28 +01002396#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01002397#if ENABLE_HUSH_GETOPTS
2398static void handle_changed_special_names(const char *name, unsigned name_len)
2399{
2400 if (name_len == 6) {
2401 if (strncmp(name, "OPTIND", 6) == 0) {
2402 G.getopt_count = 0;
2403 return;
2404 }
2405 }
2406}
2407#else
2408/* Do not even bother evaluating arguments */
2409# define handle_changed_special_names(...) ((void)0)
2410#endif
Francis Laniel8197f012023-12-22 22:02:28 +01002411#else /* __U_BOOT__ */
2412/* Do not even bother evaluating arguments */
2413# define handle_changed_special_names(...) ((void)0)
2414#endif /* __U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01002415
2416/* str holds "NAME=VAL" and is expected to be malloced.
2417 * We take ownership of it.
2418 */
Francis Laniel8197f012023-12-22 22:02:28 +01002419#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01002420#define SETFLAG_EXPORT (1 << 0)
2421#define SETFLAG_UNEXPORT (1 << 1)
2422#define SETFLAG_MAKE_RO (1 << 2)
Francis Laniel8197f012023-12-22 22:02:28 +01002423#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01002424#define SETFLAG_VARLVL_SHIFT 3
Francis Laniel8197f012023-12-22 22:02:28 +01002425#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01002426static int set_local_var(char *str, unsigned flags)
Francis Laniel8197f012023-12-22 22:02:28 +01002427#else /* __U_BOOT__ */
2428int set_local_var_modern(char *str, int flags)
2429#endif /* __U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01002430{
2431 struct variable **cur_pp;
2432 struct variable *cur;
2433 char *free_me = NULL;
2434 char *eq_sign;
2435 int name_len;
2436 int retval;
Francis Laniel8197f012023-12-22 22:02:28 +01002437#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01002438 unsigned local_lvl = (flags >> SETFLAG_VARLVL_SHIFT);
Francis Laniel8197f012023-12-22 22:02:28 +01002439#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01002440
2441 eq_sign = strchr(str, '=');
2442 if (HUSH_DEBUG && !eq_sign)
2443 bb_simple_error_msg_and_die("BUG in setvar");
2444
2445 name_len = eq_sign - str + 1; /* including '=' */
2446 cur_pp = &G.top_var;
2447 while ((cur = *cur_pp) != NULL) {
2448 if (strncmp(cur->varstr, str, name_len) != 0) {
2449 cur_pp = &cur->next;
2450 continue;
2451 }
2452
Francis Laniel8197f012023-12-22 22:02:28 +01002453#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01002454 /* We found an existing var with this name */
2455 if (cur->flg_read_only) {
2456 bb_error_msg("%s: readonly variable", str);
2457 free(str);
2458//NOTE: in bash, assignment in "export READONLY_VAR=Z" fails, and sets $?=1,
2459//but export per se succeeds (does put the var in env). We don't mimic that.
2460 return -1;
2461 }
2462 if (flags & SETFLAG_UNEXPORT) { // && cur->flg_export ?
2463 debug_printf_env("%s: unsetenv '%s'\n", __func__, str);
2464 *eq_sign = '\0';
2465 unsetenv(str);
2466 *eq_sign = '=';
2467 }
2468 if (cur->var_nest_level < local_lvl) {
2469 /* bash 3.2.33(1) and exported vars:
2470 * # export z=z
2471 * # f() { local z=a; env | grep ^z; }
2472 * # f
2473 * z=a
2474 * # env | grep ^z
2475 * z=z
2476 */
2477 if (cur->flg_export)
2478 flags |= SETFLAG_EXPORT;
2479 /* New variable is local ("local VAR=VAL" or
2480 * "VAR=VAL cmd")
2481 * and existing one is global, or local
2482 * on a lower level that new one.
2483 * Remove it from global variable list:
2484 */
2485 *cur_pp = cur->next;
2486 if (G.shadowed_vars_pp) {
2487 /* Save in "shadowed" list */
2488 debug_printf_env("shadowing %s'%s'/%u by '%s'/%u\n",
2489 cur->flg_export ? "exported " : "",
2490 cur->varstr, cur->var_nest_level, str, local_lvl
2491 );
2492 cur->next = *G.shadowed_vars_pp;
2493 *G.shadowed_vars_pp = cur;
2494 } else {
2495 /* Came from pseudo_exec_argv(), no need to save: delete it */
2496 debug_printf_env("shadow-deleting %s'%s'/%u by '%s'/%u\n",
2497 cur->flg_export ? "exported " : "",
2498 cur->varstr, cur->var_nest_level, str, local_lvl
2499 );
2500 if (cur->max_len == 0) /* allocated "VAR=VAL"? */
2501 free_me = cur->varstr; /* then free it later */
2502 free(cur);
2503 }
2504 break;
2505 }
Francis Laniel8197f012023-12-22 22:02:28 +01002506#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01002507
2508 if (strcmp(cur->varstr + name_len, eq_sign + 1) == 0) {
2509 debug_printf_env("assignement '%s' does not change anything\n", str);
2510 free_and_exp:
2511 free(str);
2512 goto exp;
2513 }
2514
2515 /* Replace the value in the found "struct variable" */
2516 if (cur->max_len != 0) {
2517 if (cur->max_len >= strnlen(str, cur->max_len + 1)) {
2518 /* This one is from startup env, reuse space */
2519 debug_printf_env("reusing startup env for '%s'\n", str);
2520 strcpy(cur->varstr, str);
2521 goto free_and_exp;
2522 }
2523 /* Can't reuse */
2524 cur->max_len = 0;
2525 goto set_str_and_exp;
2526 }
2527 /* max_len == 0 signifies "malloced" var, which we can
2528 * (and have to) free. But we can't free(cur->varstr) here:
2529 * if cur->flg_export is 1, it is in the environment.
2530 * We should either unsetenv+free, or wait until putenv,
2531 * then putenv(new)+free(old).
2532 */
2533 free_me = cur->varstr;
2534 goto set_str_and_exp;
2535 }
2536
2537 /* Not found or shadowed - create new variable struct */
Francis Laniel8197f012023-12-22 22:02:28 +01002538#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01002539 debug_printf_env("%s: alloc new var '%s'/%u\n", __func__, str, local_lvl);
Francis Laniel8197f012023-12-22 22:02:28 +01002540#else /* __U_BOOT__ */
2541 debug_printf_env("%s: alloc new var '%s'\n", __func__, str);
2542#endif /* __U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01002543 cur = xzalloc(sizeof(*cur));
Francis Laniel8197f012023-12-22 22:02:28 +01002544#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01002545 cur->var_nest_level = local_lvl;
Francis Laniel8197f012023-12-22 22:02:28 +01002546#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01002547 cur->next = *cur_pp;
2548 *cur_pp = cur;
2549
2550 set_str_and_exp:
2551 cur->varstr = str;
2552 exp:
Francis Laniel8197f012023-12-22 22:02:28 +01002553#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01002554#if !BB_MMU || ENABLE_HUSH_READONLY
2555 if (flags & SETFLAG_MAKE_RO) {
2556 cur->flg_read_only = 1;
2557 }
2558#endif
2559 if (flags & SETFLAG_EXPORT)
2560 cur->flg_export = 1;
Francis Laniel8197f012023-12-22 22:02:28 +01002561#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01002562 retval = 0;
Francis Laniel8197f012023-12-22 22:02:28 +01002563#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01002564 if (cur->flg_export) {
2565 if (flags & SETFLAG_UNEXPORT) {
2566 cur->flg_export = 0;
2567 /* unsetenv was already done */
2568 } else {
2569 debug_printf_env("%s: putenv '%s'/%u\n", __func__, cur->varstr, cur->var_nest_level);
2570 retval = putenv(cur->varstr);
2571 /* fall through to "free(free_me)" -
2572 * only now we can free old exported malloced string
2573 */
2574 }
2575 }
Francis Laniel8197f012023-12-22 22:02:28 +01002576#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01002577 free(free_me);
2578
2579 handle_changed_special_names(cur->varstr, name_len - 1);
2580
2581 return retval;
2582}
2583
Francis Laniel8197f012023-12-22 22:02:28 +01002584#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01002585static void FAST_FUNC set_local_var_from_halves(const char *name, const char *val)
2586{
2587 char *var = xasprintf("%s=%s", name, val);
2588 set_local_var(var, /*flag:*/ 0);
2589}
2590
2591/* Used at startup and after each cd */
2592static void set_pwd_var(unsigned flag)
2593{
2594 set_local_var(xasprintf("PWD=%s", get_cwd(/*force:*/ 1)), flag);
2595}
Francis Laniel8197f012023-12-22 22:02:28 +01002596#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01002597
2598#if ENABLE_HUSH_UNSET || ENABLE_HUSH_GETOPTS
2599static int unset_local_var_len(const char *name, int name_len)
2600{
2601 struct variable *cur;
2602 struct variable **cur_pp;
2603
2604 cur_pp = &G.top_var;
2605 while ((cur = *cur_pp) != NULL) {
2606 if (strncmp(cur->varstr, name, name_len) == 0
2607 && cur->varstr[name_len] == '='
2608 ) {
2609 if (cur->flg_read_only) {
2610 bb_error_msg("%s: readonly variable", name);
2611 return EXIT_FAILURE;
2612 }
2613
2614 *cur_pp = cur->next;
2615 debug_printf_env("%s: unsetenv '%s'\n", __func__, cur->varstr);
2616 bb_unsetenv(cur->varstr);
2617 if (!cur->max_len)
2618 free(cur->varstr);
2619 free(cur);
2620
2621 break;
2622 }
2623 cur_pp = &cur->next;
2624 }
2625
2626 /* Handle "unset LINENO" et al even if did not find the variable to unset */
2627 handle_changed_special_names(name, name_len);
2628
2629 return EXIT_SUCCESS;
2630}
2631
2632static int unset_local_var(const char *name)
2633{
2634 return unset_local_var_len(name, strlen(name));
2635}
2636#endif
2637
2638
Francis Laniel8197f012023-12-22 22:02:28 +01002639#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01002640/*
2641 * Helpers for "var1=val1 var2=val2 cmd" feature
2642 */
2643static void add_vars(struct variable *var)
2644{
2645 struct variable *next;
2646
2647 while (var) {
2648 next = var->next;
2649 var->next = G.top_var;
2650 G.top_var = var;
2651 if (var->flg_export) {
2652 debug_printf_env("%s: restoring exported '%s'/%u\n", __func__, var->varstr, var->var_nest_level);
2653 putenv(var->varstr);
2654 } else {
2655 debug_printf_env("%s: restoring variable '%s'/%u\n", __func__, var->varstr, var->var_nest_level);
2656 }
2657 var = next;
2658 }
2659}
2660
2661/* We put strings[i] into variable table and possibly putenv them.
2662 * If variable is read only, we can free the strings[i]
2663 * which attempts to overwrite it.
2664 * The strings[] vector itself is freed.
2665 */
2666static void set_vars_and_save_old(char **strings)
2667{
2668 char **s;
2669
2670 if (!strings)
2671 return;
2672
2673 s = strings;
2674 while (*s) {
2675 struct variable *var_p;
2676 struct variable **var_pp;
2677 char *eq;
2678
2679 eq = strchr(*s, '=');
2680 if (HUSH_DEBUG && !eq)
2681 bb_simple_error_msg_and_die("BUG in varexp4");
2682 var_pp = get_ptr_to_local_var(*s, eq - *s);
2683 if (var_pp) {
2684 var_p = *var_pp;
2685 if (var_p->flg_read_only) {
2686 char **p;
2687 bb_error_msg("%s: readonly variable", *s);
2688 /*
2689 * "VAR=V BLTIN" unsets VARs after BLTIN completes.
2690 * If VAR is readonly, leaving it in the list
2691 * after asssignment error (msg above)
2692 * causes doubled error message later, on unset.
2693 */
2694 debug_printf_env("removing/freeing '%s' element\n", *s);
2695 free(*s);
2696 p = s;
2697 do { *p = p[1]; p++; } while (*p);
2698 goto next;
2699 }
2700 /* below, set_local_var() with nest level will
2701 * "shadow" (remove) this variable from
2702 * global linked list.
2703 */
2704 }
2705 debug_printf_env("%s: env override '%s'/%u\n", __func__, *s, G.var_nest_level);
2706 set_local_var(*s, (G.var_nest_level << SETFLAG_VARLVL_SHIFT) | SETFLAG_EXPORT);
2707 s++;
2708 next: ;
2709 }
2710 free(strings);
2711}
2712
2713
2714/*
2715 * Unicode helper
2716 */
2717static void reinit_unicode_for_hush(void)
2718{
2719 /* Unicode support should be activated even if LANG is set
2720 * _during_ shell execution, not only if it was set when
2721 * shell was started. Therefore, re-check LANG every time:
2722 */
2723 if (ENABLE_FEATURE_CHECK_UNICODE_IN_ENV
2724 || ENABLE_UNICODE_USING_LOCALE
2725 ) {
2726 const char *s = get_local_var_value("LC_ALL");
2727 if (!s) s = get_local_var_value("LC_CTYPE");
2728 if (!s) s = get_local_var_value("LANG");
2729 reinit_unicode(s);
2730 }
2731}
2732
Francis Laniel8197f012023-12-22 22:02:28 +01002733#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01002734/*
2735 * in_str support (strings, and "strings" read from files).
2736 */
2737
2738#if ENABLE_HUSH_INTERACTIVE
Francis Laniel8197f012023-12-22 22:02:28 +01002739#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01002740/* To test correct lineedit/interactive behavior, type from command line:
2741 * echo $P\
2742 * \
2743 * AT\
2744 * H\
2745 * \
2746 * It exercises a lot of corner cases.
2747 */
2748static const char *setup_prompt_string(void)
2749{
2750 const char *prompt_str;
2751
2752 debug_printf_prompt("%s promptmode:%d\n", __func__, G.promptmode);
2753
2754# if ENABLE_FEATURE_EDITING_FANCY_PROMPT
2755 prompt_str = get_local_var_value(G.promptmode == 0 ? "PS1" : "PS2");
2756 if (!prompt_str)
2757 prompt_str = "";
2758# else
2759 prompt_str = "> "; /* if PS2, else... */
2760 if (G.promptmode == 0) { /* PS1 */
2761 /* No fancy prompts supported, (re)generate "CURDIR $ " by hand */
2762 free(G.PS1);
2763 /* bash uses $PWD value, even if it is set by user.
2764 * It uses current dir only if PWD is unset.
2765 * We always use current dir. */
2766 prompt_str = G.PS1 = xasprintf("%s %c ", get_cwd(0), (geteuid() != 0) ? '$' : '#');
2767 }
2768# endif
2769 debug_printf("prompt_str '%s'\n", prompt_str);
2770 return prompt_str;
2771}
Francis Laniel8197f012023-12-22 22:02:28 +01002772#endif /* !__U_BOOT__ */
2773
2774#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01002775static int get_user_input(struct in_str *i)
Francis Laniel8197f012023-12-22 22:02:28 +01002776#else /* __U_BOOT__ */
2777static void get_user_input(struct in_str *i)
2778#endif /* __U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01002779{
Francis Laniel8197f012023-12-22 22:02:28 +01002780#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01002781# if ENABLE_FEATURE_EDITING
2782 /* In EDITING case, this function reads next input line,
2783 * saves it in i->p, then returns 1st char of it.
2784 */
2785 int r;
2786 const char *prompt_str;
2787
2788 prompt_str = setup_prompt_string();
2789 for (;;) {
2790 reinit_unicode_for_hush();
2791 G.flag_SIGINT = 0;
2792 /* buglet: SIGINT will not make new prompt to appear _at once_,
2793 * only after <Enter>. (^C works immediately) */
2794 r = read_line_input(G.line_input_state, prompt_str,
2795 G.user_input_buf, CONFIG_FEATURE_EDITING_MAX_LEN-1
2796 );
2797 /* read_line_input intercepts ^C, "convert" it to SIGINT */
2798 if (r == 0) {
2799 raise(SIGINT);
2800 }
2801 check_and_run_traps();
2802 if (r != 0 && !G.flag_SIGINT)
2803 break;
2804 /* ^C or SIGINT: repeat */
2805 /* bash prints ^C even on real SIGINT (non-kbd generated) */
2806 write(STDOUT_FILENO, "^C\n", 3);
2807 G.last_exitcode = 128 | SIGINT;
2808 }
2809 if (r < 0) {
2810 /* EOF/error detected */
2811 /* ^D on interactive input goes to next line before exiting: */
2812 write(STDOUT_FILENO, "\n", 1);
2813 i->p = NULL;
2814 i->peek_buf[0] = r = EOF;
2815 return r;
2816 }
2817 i->p = G.user_input_buf;
2818 return (unsigned char)*i->p++;
2819# else
2820 /* In !EDITING case, this function gets called for every char.
2821 * Buffering happens deeper in the call chain, in hfgetc(i->file).
2822 */
2823 int r;
2824
2825 for (;;) {
2826 G.flag_SIGINT = 0;
2827 if (i->last_char == '\0' || i->last_char == '\n') {
2828 const char *prompt_str = setup_prompt_string();
2829 /* Why check_and_run_traps here? Try this interactively:
2830 * $ trap 'echo INT' INT; (sleep 2; kill -INT $$) &
2831 * $ <[enter], repeatedly...>
2832 * Without check_and_run_traps, handler never runs.
2833 */
2834 check_and_run_traps();
2835 fputs_stdout(prompt_str);
2836 fflush_all();
2837 }
2838 r = hfgetc(i->file);
2839 /* In !ENABLE_FEATURE_EDITING we don't use read_line_input,
2840 * no ^C masking happens during fgetc, no special code for ^C:
2841 * it generates SIGINT as usual.
2842 */
2843 check_and_run_traps();
2844 if (r != '\0' && !G.flag_SIGINT)
2845 break;
2846 if (G.flag_SIGINT) {
2847 /* ^C or SIGINT: repeat */
2848 /* bash prints ^C even on real SIGINT (non-kbd generated) */
2849 /* kernel prints "^C" itself, just print newline: */
2850 write(STDOUT_FILENO, "\n", 1);
2851 G.last_exitcode = 128 | SIGINT;
2852 }
2853 }
2854 return r;
2855# endif
Francis Laniel8197f012023-12-22 22:02:28 +01002856#else /* __U_BOOT__ */
2857 int n;
2858 int promptme;
2859 static char the_command[CONFIG_SYS_CBSIZE + 1];
2860
2861 bootretry_reset_cmd_timeout();
2862 promptme = 1;
2863 n = u_boot_cli_readline(i);
2864
2865# ifdef CONFIG_BOOT_RETRY_TIME
2866 if (n == -2) {
2867 puts("\nTimeout waiting for command\n");
2868# ifdef CONFIG_RESET_TO_RETRY
2869 do_reset(NULL, 0, 0, NULL);
2870# else
2871# error "This currently only works with CONFIG_RESET_TO_RETRY enabled"
2872# endif
2873 }
2874# endif
2875 if (n == -1 ) {
2876 G.flag_repeat = 0;
2877 promptme = 0;
2878 }
2879 n = strlen(console_buffer);
2880 console_buffer[n] = '\n';
2881 console_buffer[n+1]= '\0';
2882 if (had_ctrlc())
2883 G.flag_repeat = 0;
2884 clear_ctrlc();
2885 G.do_repeat = 0;
2886#ifndef __U_BOOT__
2887 if (G.promptmode == 1) {
2888#else /* __U_BOOT__ */
2889 if (!G.promptmode) {
2890#endif /* __U_BOOT__ */
2891 if (console_buffer[0] == '\n'&& G.flag_repeat == 0) {
2892 strcpy(the_command, console_buffer);
2893 }
2894 else {
2895 if (console_buffer[0] != '\n') {
2896 strcpy(the_command, console_buffer);
2897 G.flag_repeat = 1;
2898 }
2899 else {
2900 G.do_repeat = 1;
2901 }
2902 }
2903 i->p = the_command;
2904 }
2905 else {
2906 if (console_buffer[0] != '\n') {
2907 if (strlen(the_command) + strlen(console_buffer)
2908 < CONFIG_SYS_CBSIZE) {
2909 n = strlen(the_command);
2910#ifdef __U_BOOT__
2911 /*
2912 * To avoid writing to bad places, we check if
2913 * n is greater than 0.
2914 * This bug was found by Harald Seiler.
2915 */
2916 if (n > 0)
2917 the_command[n-1] = ' ';
2918 strcpy(&the_command[n], console_buffer);
2919#else /* !__U_BOOT__ */
2920 the_command[n-1] = ' ';
2921 strcpy(&the_command[n], console_buffer);
2922#endif /* !__U_BOOT__ */
2923 }
2924 else {
2925 the_command[0] = '\n';
2926 the_command[1] = '\0';
2927 G.flag_repeat = 0;
2928 }
2929 }
2930 if (promptme == 0) {
2931 the_command[0] = '\n';
2932 the_command[1] = '\0';
2933 }
2934 i->p = console_buffer;
2935 }
2936#endif /* __U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01002937}
2938/* This is the magic location that prints prompts
2939 * and gets data back from the user */
2940static int fgetc_interactive(struct in_str *i)
2941{
2942 int ch;
Francis Laniel8197f012023-12-22 22:02:28 +01002943#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01002944 /* If it's interactive stdin, get new line. */
2945 if (G_interactive_fd && i->file == G.HFILE_stdin) {
Francis Laniel8197f012023-12-22 22:02:28 +01002946#endif /* !__U_BOOT__ */
2947#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01002948 /* Returns first char (or EOF), the rest is in i->p[] */
2949 ch = get_user_input(i);
Francis Laniel8197f012023-12-22 22:02:28 +01002950#else /* __U_BOOT__ */
2951 /* Avoid garbage value and make clang happy. */
2952 ch = 0;
2953 /*
2954 * get_user_input() does not return anything when used in
2955 * U-Boot.
2956 * So, we need to take the read character from i->p[].
2957 */
2958 get_user_input(i);
2959 if (i->p && *i->p) {
2960 ch = *i->p++;
2961 }
2962#endif /* __U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01002963 G.promptmode = 1; /* PS2 */
2964 debug_printf_prompt("%s promptmode=%d\n", __func__, G.promptmode);
Francis Laniel8197f012023-12-22 22:02:28 +01002965#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01002966 } else {
2967 /* Not stdin: script file, sourced file, etc */
2968 do ch = hfgetc(i->file); while (ch == '\0');
2969 }
Francis Laniel8197f012023-12-22 22:02:28 +01002970#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01002971 return ch;
2972}
2973#else /* !INTERACTIVE */
Francis Laniel8197f012023-12-22 22:02:28 +01002974#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01002975static ALWAYS_INLINE int fgetc_interactive(struct in_str *i)
2976{
2977 int ch;
2978 do ch = hfgetc(i->file); while (ch == '\0');
2979 return ch;
2980}
Francis Laniel8197f012023-12-22 22:02:28 +01002981#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01002982#endif /* !INTERACTIVE */
2983
2984static int i_getch(struct in_str *i)
2985{
2986 int ch;
2987
Francis Laniel8197f012023-12-22 22:02:28 +01002988#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01002989 if (!i->file) {
2990 /* string-based in_str */
2991 ch = (unsigned char)*i->p;
2992 if (ch != '\0') {
2993 i->p++;
2994 i->last_char = ch;
2995 return ch;
2996 }
2997 return EOF;
2998 }
2999
Francis Laniel8197f012023-12-22 22:02:28 +01003000#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01003001 /* FILE-based in_str */
3002
3003#if ENABLE_FEATURE_EDITING
3004 /* This can be stdin, check line editing char[] buffer */
3005 if (i->p && *i->p != '\0') {
3006 ch = (unsigned char)*i->p++;
3007 goto out;
Francis Laniel3b13faf2023-12-22 22:02:34 +01003008#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01003009 }
Francis Laniel3b13faf2023-12-22 22:02:34 +01003010#else /* __U_BOOT__ */
3011 /*
3012 * There are two ways for command to be called:
3013 * 1. The first one is when they are typed by the user.
3014 * 2. The second one is through run_command() (NOTE command run
3015 * internally calls run_command()).
3016 *
3017 * In the second case, we do not get input from the user, so once we
3018 * get a '\0', it means we need to stop.
3019 * NOTE G.run_command_flags is only set on run_command call stack, so
3020 * we use this to know if we come from user input or run_command().
3021 */
3022 } else if (i->p && *i->p == '\0' && G.run_command_flags){
3023 return EOF;
3024 }
3025#endif /* __U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01003026#endif
Francis Laniel8197f012023-12-22 22:02:28 +01003027#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01003028 /* peek_buf[] is an int array, not char. Can contain EOF. */
3029 ch = i->peek_buf[0];
3030 if (ch != 0) {
3031 int ch2 = i->peek_buf[1];
3032 i->peek_buf[0] = ch2;
3033 if (ch2 == 0) /* very likely, avoid redundant write */
3034 goto out;
3035 i->peek_buf[1] = 0;
3036 goto out;
3037 }
3038
Francis Laniel8197f012023-12-22 22:02:28 +01003039#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01003040 ch = fgetc_interactive(i);
3041 out:
3042 debug_printf("file_get: got '%c' %d\n", ch, ch);
3043 i->last_char = ch;
3044#if ENABLE_HUSH_LINENO_VAR
3045 if (ch == '\n') {
3046 G.parse_lineno++;
3047 debug_printf_parse("G.parse_lineno++ = %u\n", G.parse_lineno);
3048 }
3049#endif
3050 return ch;
3051}
3052
3053static int i_peek(struct in_str *i)
3054{
Francis Laniel8197f012023-12-22 22:02:28 +01003055#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01003056 int ch;
3057
3058 if (!i->file) {
3059 /* string-based in_str */
3060 /* Doesn't report EOF on NUL. None of the callers care. */
3061 return (unsigned char)*i->p;
3062 }
3063
3064 /* FILE-based in_str */
3065
3066#if ENABLE_FEATURE_EDITING && ENABLE_HUSH_INTERACTIVE
3067 /* This can be stdin, check line editing char[] buffer */
3068 if (i->p && *i->p != '\0')
3069 return (unsigned char)*i->p;
3070#endif
3071 /* peek_buf[] is an int array, not char. Can contain EOF. */
3072 ch = i->peek_buf[0];
3073 if (ch != 0)
3074 return ch;
3075
3076 /* Need to get a new char */
3077 ch = fgetc_interactive(i);
3078 debug_printf("file_peek: got '%c' %d\n", ch, ch);
3079
3080 /* Save it by either rolling back line editing buffer, or in i->peek_buf[0] */
3081#if ENABLE_FEATURE_EDITING && ENABLE_HUSH_INTERACTIVE
3082 if (i->p) {
3083 i->p -= 1;
3084 return ch;
3085 }
3086#endif
3087 i->peek_buf[0] = ch;
3088 /*i->peek_buf[1] = 0; - already is */
3089 return ch;
Francis Laniel8197f012023-12-22 22:02:28 +01003090#else /* __U_BOOT__ */
3091 /* string-based in_str */
3092 /* Doesn't report EOF on NUL. None of the callers care. */
3093 return (unsigned char)*i->p;
3094#endif /* __U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01003095}
3096
3097/* Only ever called if i_peek() was called, and did not return EOF.
3098 * IOW: we know the previous peek saw an ordinary char, not EOF, not NUL,
3099 * not end-of-line. Therefore we never need to read a new editing line here.
3100 */
3101static int i_peek2(struct in_str *i)
3102{
Francis Laniel8197f012023-12-22 22:02:28 +01003103#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01003104 int ch;
Francis Laniel8197f012023-12-22 22:02:28 +01003105#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01003106
3107 /* There are two cases when i->p[] buffer exists.
3108 * (1) it's a string in_str.
3109 * (2) It's a file, and we have a saved line editing buffer.
3110 * In both cases, we know that i->p[0] exists and not NUL, and
3111 * the peek2 result is in i->p[1].
3112 */
3113 if (i->p)
3114 return (unsigned char)i->p[1];
3115
Francis Laniel8197f012023-12-22 22:02:28 +01003116#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01003117 /* Now we know it is a file-based in_str. */
3118
3119 /* peek_buf[] is an int array, not char. Can contain EOF. */
3120 /* Is there 2nd char? */
3121 ch = i->peek_buf[1];
3122 if (ch == 0) {
3123 /* We did not read it yet, get it now */
3124 do ch = hfgetc(i->file); while (ch == '\0');
3125 i->peek_buf[1] = ch;
3126 }
3127
3128 debug_printf("file_peek2: got '%c' %d\n", ch, ch);
3129 return ch;
Francis Laniel8197f012023-12-22 22:02:28 +01003130#else
3131 return 0;
3132#endif /* __U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01003133}
3134
3135static int i_getch_and_eat_bkslash_nl(struct in_str *input)
3136{
3137 for (;;) {
3138 int ch, ch2;
3139
3140 ch = i_getch(input);
3141 if (ch != '\\')
3142 return ch;
3143 ch2 = i_peek(input);
3144 if (ch2 != '\n')
3145 return ch;
3146 /* backslash+newline, skip it */
3147 i_getch(input);
3148 }
3149}
3150
3151/* Note: this function _eats_ \<newline> pairs, safe to use plain
3152 * i_getch() after it instead of i_getch_and_eat_bkslash_nl().
3153 */
3154static int i_peek_and_eat_bkslash_nl(struct in_str *input)
3155{
3156 for (;;) {
3157 int ch, ch2;
3158
3159 ch = i_peek(input);
3160 if (ch != '\\')
3161 return ch;
3162 ch2 = i_peek2(input);
3163 if (ch2 != '\n')
3164 return ch;
3165 /* backslash+newline, skip it */
3166 i_getch(input);
3167 i_getch(input);
3168 }
3169}
3170
Francis Laniel8197f012023-12-22 22:02:28 +01003171#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01003172static void setup_file_in_str(struct in_str *i, HFILE *fp)
Francis Laniel8197f012023-12-22 22:02:28 +01003173#else /* __U_BOOT__ */
3174static void setup_file_in_str(struct in_str *i)
3175#endif /* __U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01003176{
3177 memset(i, 0, sizeof(*i));
Francis Laniel8197f012023-12-22 22:02:28 +01003178#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01003179 i->file = fp;
3180 /* i->p = NULL; */
Francis Laniel8197f012023-12-22 22:02:28 +01003181#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01003182}
3183
3184static void setup_string_in_str(struct in_str *i, const char *s)
3185{
3186 memset(i, 0, sizeof(*i));
3187 /*i->file = NULL */;
3188 i->p = s;
3189}
3190
3191
3192/*
3193 * o_string support
3194 */
3195#define B_CHUNK (32 * sizeof(char*))
3196
3197static void o_reset_to_empty_unquoted(o_string *o)
3198{
3199 o->length = 0;
3200 o->has_quoted_part = 0;
3201 if (o->data)
3202 o->data[0] = '\0';
3203}
3204
3205static void o_free_and_set_NULL(o_string *o)
3206{
3207 free(o->data);
3208 memset(o, 0, sizeof(*o));
3209}
3210
3211static ALWAYS_INLINE void o_free(o_string *o)
3212{
3213 free(o->data);
3214}
3215
3216static void o_grow_by(o_string *o, int len)
3217{
3218 if (o->length + len > o->maxlen) {
3219 o->maxlen += (2 * len) | (B_CHUNK-1);
3220 o->data = xrealloc(o->data, 1 + o->maxlen);
3221 }
3222}
3223
3224static void o_addchr(o_string *o, int ch)
3225{
3226 debug_printf("o_addchr: '%c' o->length=%d o=%p\n", ch, o->length, o);
3227 if (o->length < o->maxlen) {
3228 /* likely. avoid o_grow_by() call */
3229 add:
3230 o->data[o->length] = ch;
3231 o->length++;
3232 o->data[o->length] = '\0';
3233 return;
3234 }
3235 o_grow_by(o, 1);
3236 goto add;
3237}
3238
3239#if 0
3240/* Valid only if we know o_string is not empty */
3241static void o_delchr(o_string *o)
3242{
3243 o->length--;
3244 o->data[o->length] = '\0';
3245}
3246#endif
3247
3248static void o_addblock(o_string *o, const char *str, int len)
3249{
3250 o_grow_by(o, len);
3251 ((char*)mempcpy(&o->data[o->length], str, len))[0] = '\0';
3252 o->length += len;
3253}
3254
3255static void o_addstr(o_string *o, const char *str)
3256{
3257 o_addblock(o, str, strlen(str));
3258}
3259
Francis Laniel8197f012023-12-22 22:02:28 +01003260#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01003261static void o_addstr_with_NUL(o_string *o, const char *str)
3262{
3263 o_addblock(o, str, strlen(str) + 1);
3264}
Francis Laniel8197f012023-12-22 22:02:28 +01003265#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01003266
3267#if !BB_MMU
3268static void nommu_addchr(o_string *o, int ch)
3269{
3270 if (o)
3271 o_addchr(o, ch);
3272}
3273#else
3274# define nommu_addchr(o, str) ((void)0)
3275#endif
3276
Francis Laniel8197f012023-12-22 22:02:28 +01003277#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01003278#if ENABLE_HUSH_MODE_X
3279static void x_mode_addchr(int ch)
3280{
3281 o_addchr(&G.x_mode_buf, ch);
3282}
3283static void x_mode_addstr(const char *str)
3284{
3285 o_addstr(&G.x_mode_buf, str);
3286}
3287static void x_mode_addblock(const char *str, int len)
3288{
3289 o_addblock(&G.x_mode_buf, str, len);
3290}
3291static void x_mode_prefix(void)
3292{
3293 int n = G.x_mode_depth;
3294 do x_mode_addchr('+'); while (--n >= 0);
3295}
3296static void x_mode_flush(void)
3297{
3298 int len = G.x_mode_buf.length;
3299 if (len <= 0)
3300 return;
3301 if (G.x_mode_fd > 0) {
3302 G.x_mode_buf.data[len] = '\n';
3303 full_write(G.x_mode_fd, G.x_mode_buf.data, len + 1);
3304 }
3305 G.x_mode_buf.length = 0;
3306}
3307#endif
Francis Laniel8197f012023-12-22 22:02:28 +01003308#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01003309
3310/*
3311 * HUSH_BRACE_EXPANSION code needs corresponding quoting on variable expansion side.
3312 * Currently, "v='{q,w}'; echo $v" erroneously expands braces in $v.
3313 * Apparently, on unquoted $v bash still does globbing
3314 * ("v='*.txt'; echo $v" prints all .txt files),
3315 * but NOT brace expansion! Thus, there should be TWO independent
3316 * quoting mechanisms on $v expansion side: one protects
3317 * $v from brace expansion, and other additionally protects "$v" against globbing.
3318 * We have only second one.
3319 */
3320
3321#if ENABLE_HUSH_BRACE_EXPANSION
3322# define MAYBE_BRACES "{}"
3323#else
3324# define MAYBE_BRACES ""
3325#endif
3326
3327/* My analysis of quoting semantics tells me that state information
3328 * is associated with a destination, not a source.
3329 */
3330static void o_addqchr(o_string *o, int ch)
3331{
3332 int sz = 1;
3333 /* '-' is included because of this case:
3334 * >filename0 >filename1 >filename9; v='-'; echo filename[0"$v"9]
3335 */
3336 char *found = strchr("*?[-\\" MAYBE_BRACES, ch);
3337 if (found)
3338 sz++;
3339 o_grow_by(o, sz);
3340 if (found) {
3341 o->data[o->length] = '\\';
3342 o->length++;
3343 }
3344 o->data[o->length] = ch;
3345 o->length++;
3346 o->data[o->length] = '\0';
3347}
3348
3349static void o_addQchr(o_string *o, int ch)
3350{
3351 int sz = 1;
3352 if ((o->o_expflags & EXP_FLAG_ESC_GLOB_CHARS)
3353 && strchr("*?[-\\" MAYBE_BRACES, ch)
3354 ) {
3355 sz++;
3356 o->data[o->length] = '\\';
3357 o->length++;
3358 }
3359 o_grow_by(o, sz);
3360 o->data[o->length] = ch;
3361 o->length++;
3362 o->data[o->length] = '\0';
3363}
3364
3365static void o_addqblock(o_string *o, const char *str, int len)
3366{
3367 while (len) {
3368 char ch;
3369 int sz;
3370 int ordinary_cnt = strcspn(str, "*?[-\\" MAYBE_BRACES);
3371 if (ordinary_cnt > len) /* paranoia */
3372 ordinary_cnt = len;
3373 o_addblock(o, str, ordinary_cnt);
3374 if (ordinary_cnt == len)
3375 return; /* NUL is already added by o_addblock */
3376 str += ordinary_cnt;
3377 len -= ordinary_cnt + 1; /* we are processing + 1 char below */
3378
3379 ch = *str++;
3380 sz = 1;
3381 if (ch) { /* it is necessarily one of "*?[-\\" MAYBE_BRACES */
3382 sz++;
3383 o->data[o->length] = '\\';
3384 o->length++;
3385 }
3386 o_grow_by(o, sz);
3387 o->data[o->length] = ch;
3388 o->length++;
3389 }
3390 o->data[o->length] = '\0';
3391}
3392
3393static void o_addQblock(o_string *o, const char *str, int len)
3394{
3395 if (!(o->o_expflags & EXP_FLAG_ESC_GLOB_CHARS)) {
3396 o_addblock(o, str, len);
3397 return;
3398 }
3399 o_addqblock(o, str, len);
3400}
3401
3402static void o_addQstr(o_string *o, const char *str)
3403{
3404 o_addQblock(o, str, strlen(str));
3405}
3406
3407/* A special kind of o_string for $VAR and `cmd` expansion.
3408 * It contains char* list[] at the beginning, which is grown in 16 element
3409 * increments. Actual string data starts at the next multiple of 16 * (char*).
3410 * list[i] contains an INDEX (int!) into this string data.
3411 * It means that if list[] needs to grow, data needs to be moved higher up
3412 * but list[i]'s need not be modified.
3413 * NB: remembering how many list[i]'s you have there is crucial.
3414 * o_finalize_list() operation post-processes this structure - calculates
3415 * and stores actual char* ptrs in list[]. Oh, it NULL terminates it as well.
3416 */
3417#if DEBUG_EXPAND || DEBUG_GLOB
3418static void debug_print_list(const char *prefix, o_string *o, int n)
3419{
3420 char **list = (char**)o->data;
3421 int string_start = ((n + 0xf) & ~0xf) * sizeof(list[0]);
3422 int i = 0;
3423
3424 indent();
3425 fdprintf(2, "%s: list:%p n:%d string_start:%d length:%d maxlen:%d glob:%d quoted:%d escape:%d\n",
3426 prefix, list, n, string_start, o->length, o->maxlen,
3427 !!(o->o_expflags & EXP_FLAG_GLOB),
3428 o->has_quoted_part,
3429 !!(o->o_expflags & EXP_FLAG_ESC_GLOB_CHARS));
3430 while (i < n) {
3431 indent();
3432 fdprintf(2, " list[%d]=%d '%s' %p\n", i, (int)(uintptr_t)list[i],
3433 o->data + (int)(uintptr_t)list[i] + string_start,
3434 o->data + (int)(uintptr_t)list[i] + string_start);
3435 i++;
3436 }
3437 if (n) {
3438 const char *p = o->data + (int)(uintptr_t)list[n - 1] + string_start;
3439 indent();
Francis Laniel8197f012023-12-22 22:02:28 +01003440#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01003441 fdprintf(2, " total_sz:%ld\n", (long)((p + strlen(p) + 1) - o->data));
Francis Laniel8197f012023-12-22 22:02:28 +01003442#else /* __U_BOOT__ */
3443 printf(" total_sz:%ld\n", (long)((p + strlen(p) + 1) - o->data));
3444#endif /* __U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01003445 }
3446}
3447#else
3448# define debug_print_list(prefix, o, n) ((void)0)
3449#endif
3450
3451/* n = o_save_ptr_helper(str, n) "starts new string" by storing an index value
3452 * in list[n] so that it points past last stored byte so far.
3453 * It returns n+1. */
3454static int o_save_ptr_helper(o_string *o, int n)
3455{
3456 char **list = (char**)o->data;
3457 int string_start;
3458 int string_len;
3459
3460 if (!o->has_empty_slot) {
3461 string_start = ((n + 0xf) & ~0xf) * sizeof(list[0]);
3462 string_len = o->length - string_start;
3463 if (!(n & 0xf)) { /* 0, 0x10, 0x20...? */
3464 debug_printf_list("list[%d]=%d string_start=%d (growing)\n", n, string_len, string_start);
3465 /* list[n] points to string_start, make space for 16 more pointers */
3466 o->maxlen += 0x10 * sizeof(list[0]);
3467 o->data = xrealloc(o->data, o->maxlen + 1);
3468 list = (char**)o->data;
3469 memmove(list + n + 0x10, list + n, string_len);
3470 /*
3471 * expand_on_ifs() has a "previous argv[] ends in IFS?"
3472 * check. (grep for -prev-ifs-check-).
3473 * Ensure that argv[-1][last] is not garbage
3474 * but zero bytes, to save index check there.
3475 */
3476 list[n + 0x10 - 1] = 0;
3477 o->length += 0x10 * sizeof(list[0]);
3478 } else {
3479 debug_printf_list("list[%d]=%d string_start=%d\n",
3480 n, string_len, string_start);
3481 }
3482 } else {
3483 /* We have empty slot at list[n], reuse without growth */
3484 string_start = ((n+1 + 0xf) & ~0xf) * sizeof(list[0]); /* NB: n+1! */
3485 string_len = o->length - string_start;
3486 debug_printf_list("list[%d]=%d string_start=%d (empty slot)\n",
3487 n, string_len, string_start);
3488 o->has_empty_slot = 0;
3489 }
3490 o->has_quoted_part = 0;
3491 list[n] = (char*)(uintptr_t)string_len;
3492 return n + 1;
3493}
3494
3495/* "What was our last o_save_ptr'ed position (byte offset relative o->data)?" */
3496static int o_get_last_ptr(o_string *o, int n)
3497{
3498 char **list = (char**)o->data;
3499 int string_start = ((n + 0xf) & ~0xf) * sizeof(list[0]);
3500
3501 return ((int)(uintptr_t)list[n-1]) + string_start;
3502}
3503
3504/*
3505 * Globbing routines.
3506 *
3507 * Most words in commands need to be globbed, even ones which are
3508 * (single or double) quoted. This stems from the possiblity of
3509 * constructs like "abc"* and 'abc'* - these should be globbed.
3510 * Having a different code path for fully-quoted strings ("abc",
3511 * 'abc') would only help performance-wise, but we still need
3512 * code for partially-quoted strings.
3513 *
3514 * Unfortunately, if we want to match bash and ash behavior in all cases,
3515 * the logic can't be "shell-syntax argument is first transformed
3516 * to a string, then globbed, and if globbing does not match anything,
3517 * it is used verbatim". Here are two examples where it fails:
3518 *
3519 * echo 'b\*'?
3520 *
3521 * The globbing can't be avoided (because of '?' at the end).
3522 * The glob pattern is: b\\\*? - IOW, both \ and * are literals
3523 * and are glob-escaped. If this does not match, bash/ash print b\*?
3524 * - IOW: they "unbackslash" the glob pattern.
3525 * Now, look at this:
3526 *
3527 * v='\\\*'; echo b$v?
3528 *
3529 * The glob pattern is the same here: b\\\*? - the unquoted $v expansion
3530 * should be used as glob pattern with no changes. However, if glob
3531 * does not match, bash/ash print b\\\*? - NOT THE SAME as first example!
3532 *
3533 * ash implements this by having an encoded representation of the word
3534 * to glob, which IS NOT THE SAME as the glob pattern - it has more data.
3535 * Glob pattern is derived from it. If glob fails, the decision what result
3536 * should be is made using that encoded representation. Not glob pattern.
3537 */
3538
3539#if ENABLE_HUSH_BRACE_EXPANSION
3540/* There in a GNU extension, GLOB_BRACE, but it is not usable:
3541 * first, it processes even {a} (no commas), second,
3542 * I didn't manage to make it return strings when they don't match
3543 * existing files. Need to re-implement it.
3544 */
3545
3546/* Helper */
3547static int glob_needed(const char *s)
3548{
3549 while (*s) {
3550 if (*s == '\\') {
3551 if (!s[1])
3552 return 0;
3553 s += 2;
3554 continue;
3555 }
3556 if (*s == '*' || *s == '[' || *s == '?' || *s == '{')
3557 return 1;
3558 s++;
3559 }
3560 return 0;
3561}
3562/* Return pointer to next closing brace or to comma */
3563static const char *next_brace_sub(const char *cp)
3564{
3565 unsigned depth = 0;
3566 cp++;
3567 while (*cp != '\0') {
3568 if (*cp == '\\') {
3569 if (*++cp == '\0')
3570 break;
3571 cp++;
3572 continue;
3573 }
3574 if ((*cp == '}' && depth-- == 0) || (*cp == ',' && depth == 0))
3575 break;
3576 if (*cp++ == '{')
3577 depth++;
3578 }
3579
3580 return *cp != '\0' ? cp : NULL;
3581}
3582/* Recursive brace globber. Note: may garble pattern[]. */
3583static int glob_brace(char *pattern, o_string *o, int n)
3584{
3585 char *new_pattern_buf;
3586 const char *begin;
3587 const char *next;
3588 const char *rest;
3589 const char *p;
3590 size_t rest_len;
3591
3592 debug_printf_glob("glob_brace('%s')\n", pattern);
3593
3594 begin = pattern;
3595 while (1) {
3596 if (*begin == '\0')
3597 goto simple_glob;
3598 if (*begin == '{') {
3599 /* Find the first sub-pattern and at the same time
3600 * find the rest after the closing brace */
3601 next = next_brace_sub(begin);
3602 if (next == NULL) {
3603 /* An illegal expression */
3604 goto simple_glob;
3605 }
3606 if (*next == '}') {
3607 /* "{abc}" with no commas - illegal
3608 * brace expr, disregard and skip it */
3609 begin = next + 1;
3610 continue;
3611 }
3612 break;
3613 }
3614 if (*begin == '\\' && begin[1] != '\0')
3615 begin++;
3616 begin++;
3617 }
3618 debug_printf_glob("begin:%s\n", begin);
3619 debug_printf_glob("next:%s\n", next);
3620
3621 /* Now find the end of the whole brace expression */
3622 rest = next;
3623 while (*rest != '}') {
3624 rest = next_brace_sub(rest);
3625 if (rest == NULL) {
3626 /* An illegal expression */
3627 goto simple_glob;
3628 }
3629 debug_printf_glob("rest:%s\n", rest);
3630 }
3631 rest_len = strlen(++rest) + 1;
3632
3633 /* We are sure the brace expression is well-formed */
3634
3635 /* Allocate working buffer large enough for our work */
3636 new_pattern_buf = xmalloc(strlen(pattern));
3637
3638 /* We have a brace expression. BEGIN points to the opening {,
3639 * NEXT points past the terminator of the first element, and REST
3640 * points past the final }. We will accumulate result names from
3641 * recursive runs for each brace alternative in the buffer using
3642 * GLOB_APPEND. */
3643
3644 p = begin + 1;
3645 while (1) {
3646 /* Construct the new glob expression */
3647 memcpy(
3648 mempcpy(
3649 mempcpy(new_pattern_buf,
3650 /* We know the prefix for all sub-patterns */
3651 pattern, begin - pattern),
3652 p, next - p),
3653 rest, rest_len);
3654
3655 /* Note: glob_brace() may garble new_pattern_buf[].
3656 * That's why we re-copy prefix every time (1st memcpy above).
3657 */
3658 n = glob_brace(new_pattern_buf, o, n);
3659 if (*next == '}') {
3660 /* We saw the last entry */
3661 break;
3662 }
3663 p = next + 1;
3664 next = next_brace_sub(next);
3665 }
3666 free(new_pattern_buf);
3667 return n;
3668
3669 simple_glob:
3670 {
3671 int gr;
3672 glob_t globdata;
3673
3674 memset(&globdata, 0, sizeof(globdata));
3675 gr = glob(pattern, 0, NULL, &globdata);
3676 debug_printf_glob("glob('%s'):%d\n", pattern, gr);
3677 if (gr != 0) {
3678 if (gr == GLOB_NOMATCH) {
3679 globfree(&globdata);
3680 /* NB: garbles parameter */
3681 unbackslash(pattern);
3682 o_addstr_with_NUL(o, pattern);
3683 debug_printf_glob("glob pattern '%s' is literal\n", pattern);
3684 return o_save_ptr_helper(o, n);
3685 }
3686 if (gr == GLOB_NOSPACE)
3687 bb_die_memory_exhausted();
3688 /* GLOB_ABORTED? Only happens with GLOB_ERR flag,
3689 * but we didn't specify it. Paranoia again. */
3690 bb_error_msg_and_die("glob error %d on '%s'", gr, pattern);
3691 }
3692 if (globdata.gl_pathv && globdata.gl_pathv[0]) {
3693 char **argv = globdata.gl_pathv;
3694 while (1) {
3695 o_addstr_with_NUL(o, *argv);
3696 n = o_save_ptr_helper(o, n);
3697 argv++;
3698 if (!*argv)
3699 break;
3700 }
3701 }
3702 globfree(&globdata);
3703 }
3704 return n;
3705}
3706/* Performs globbing on last list[],
3707 * saving each result as a new list[].
3708 */
3709static int perform_glob(o_string *o, int n)
3710{
3711 char *pattern, *copy;
3712
3713 debug_printf_glob("start perform_glob: n:%d o->data:%p\n", n, o->data);
3714 if (!o->data)
3715 return o_save_ptr_helper(o, n);
3716 pattern = o->data + o_get_last_ptr(o, n);
3717 debug_printf_glob("glob pattern '%s'\n", pattern);
3718 if (!glob_needed(pattern)) {
3719 /* unbackslash last string in o in place, fix length */
3720 o->length = unbackslash(pattern) - o->data;
3721 debug_printf_glob("glob pattern '%s' is literal\n", pattern);
3722 return o_save_ptr_helper(o, n);
3723 }
3724
3725 copy = xstrdup(pattern);
3726 /* "forget" pattern in o */
3727 o->length = pattern - o->data;
3728 n = glob_brace(copy, o, n);
3729 free(copy);
3730 if (DEBUG_GLOB)
3731 debug_print_list("perform_glob returning", o, n);
3732 return n;
3733}
3734
3735#else /* !HUSH_BRACE_EXPANSION */
3736
3737/* Helper */
3738static int glob_needed(const char *s)
3739{
3740 while (*s) {
3741 if (*s == '\\') {
3742 if (!s[1])
3743 return 0;
3744 s += 2;
3745 continue;
3746 }
3747 if (*s == '*' || *s == '[' || *s == '?')
3748 return 1;
3749 s++;
3750 }
3751 return 0;
3752}
3753/* Performs globbing on last list[],
3754 * saving each result as a new list[].
3755 */
3756static int perform_glob(o_string *o, int n)
3757{
Francis Laniel74e42542023-12-22 22:02:33 +01003758#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01003759 glob_t globdata;
3760 int gr;
Francis Laniel74e42542023-12-22 22:02:33 +01003761#endif /* __U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01003762 char *pattern;
3763
3764 debug_printf_glob("start perform_glob: n:%d o->data:%p\n", n, o->data);
3765 if (!o->data)
3766 return o_save_ptr_helper(o, n);
3767 pattern = o->data + o_get_last_ptr(o, n);
3768 debug_printf_glob("glob pattern '%s'\n", pattern);
3769 if (!glob_needed(pattern)) {
Francis Laniel74e42542023-12-22 22:02:33 +01003770#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01003771 literal:
Francis Laniel74e42542023-12-22 22:02:33 +01003772#endif /* __U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01003773 /* unbackslash last string in o in place, fix length */
3774 o->length = unbackslash(pattern) - o->data;
3775 debug_printf_glob("glob pattern '%s' is literal\n", pattern);
3776 return o_save_ptr_helper(o, n);
3777 }
3778
Francis Laniel74e42542023-12-22 22:02:33 +01003779#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01003780 memset(&globdata, 0, sizeof(globdata));
3781 /* Can't use GLOB_NOCHECK: it does not unescape the string.
3782 * If we glob "*.\*" and don't find anything, we need
3783 * to fall back to using literal "*.*", but GLOB_NOCHECK
3784 * will return "*.\*"!
3785 */
3786 gr = glob(pattern, 0, NULL, &globdata);
3787 debug_printf_glob("glob('%s'):%d\n", pattern, gr);
3788 if (gr != 0) {
3789 if (gr == GLOB_NOMATCH) {
3790 globfree(&globdata);
3791 goto literal;
3792 }
3793 if (gr == GLOB_NOSPACE)
3794 bb_die_memory_exhausted();
3795 /* GLOB_ABORTED? Only happens with GLOB_ERR flag,
3796 * but we didn't specify it. Paranoia again. */
3797 bb_error_msg_and_die("glob error %d on '%s'", gr, pattern);
3798 }
3799 if (globdata.gl_pathv && globdata.gl_pathv[0]) {
3800 char **argv = globdata.gl_pathv;
3801 /* "forget" pattern in o */
3802 o->length = pattern - o->data;
3803 while (1) {
3804 o_addstr_with_NUL(o, *argv);
3805 n = o_save_ptr_helper(o, n);
3806 argv++;
3807 if (!*argv)
3808 break;
3809 }
3810 }
3811 globfree(&globdata);
3812 if (DEBUG_GLOB)
3813 debug_print_list("perform_glob returning", o, n);
3814 return n;
Francis Laniel74e42542023-12-22 22:02:33 +01003815#else /* __U_BOOT__ */
3816 /*
3817 * NOTE We only use perform glob to call unbackslash to remove backslash
3818 * from string once expanded.
3819 * So, it seems OK to return this if no previous return was done.
3820 */
3821 return o_save_ptr_helper(o, n);
3822#endif /* __U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01003823}
3824
3825#endif /* !HUSH_BRACE_EXPANSION */
3826
3827/* If o->o_expflags & EXP_FLAG_GLOB, glob the string so far remembered.
3828 * Otherwise, just finish current list[] and start new */
3829static int o_save_ptr(o_string *o, int n)
3830{
3831 if (o->o_expflags & EXP_FLAG_GLOB) {
3832 /* If o->has_empty_slot, list[n] was already globbed
3833 * (if it was requested back then when it was filled)
3834 * so don't do that again! */
3835 if (!o->has_empty_slot)
3836 return perform_glob(o, n); /* o_save_ptr_helper is inside */
3837 }
3838 return o_save_ptr_helper(o, n);
3839}
3840
3841/* "Please convert list[n] to real char* ptrs, and NULL terminate it." */
3842static char **o_finalize_list(o_string *o, int n)
3843{
3844 char **list;
3845 int string_start;
3846
3847 if (DEBUG_EXPAND)
3848 debug_print_list("finalized", o, n);
3849 debug_printf_expand("finalized n:%d\n", n);
3850 list = (char**)o->data;
3851 string_start = ((n + 0xf) & ~0xf) * sizeof(list[0]);
3852 list[--n] = NULL;
3853 while (n) {
3854 n--;
3855 list[n] = o->data + (int)(uintptr_t)list[n] + string_start;
3856 }
3857 return list;
3858}
3859
3860static void free_pipe_list(struct pipe *pi);
3861
3862/* Returns pi->next - next pipe in the list */
3863static struct pipe *free_pipe(struct pipe *pi)
3864{
3865 struct pipe *next;
3866 int i;
3867
Francis Laniel8197f012023-12-22 22:02:28 +01003868#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01003869 debug_printf_clean("free_pipe (pid %d)\n", getpid());
Francis Laniel8197f012023-12-22 22:02:28 +01003870#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01003871 for (i = 0; i < pi->num_cmds; i++) {
3872 struct command *command;
Francis Laniel8197f012023-12-22 22:02:28 +01003873#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01003874 struct redir_struct *r, *rnext;
Francis Laniel8197f012023-12-22 22:02:28 +01003875#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01003876
3877 command = &pi->cmds[i];
3878 debug_printf_clean(" command %d:\n", i);
3879 if (command->argv) {
3880 if (DEBUG_CLEAN) {
3881 int a;
3882 char **p;
3883 for (a = 0, p = command->argv; *p; a++, p++) {
3884 debug_printf_clean(" argv[%d] = %s\n", a, *p);
3885 }
3886 }
3887 free_strings(command->argv);
3888 //command->argv = NULL;
3889 }
3890 /* not "else if": on syntax error, we may have both! */
3891 if (command->group) {
3892 debug_printf_clean(" begin group (cmd_type:%d)\n",
3893 command->cmd_type);
3894 free_pipe_list(command->group);
3895 debug_printf_clean(" end group\n");
3896 //command->group = NULL;
3897 }
3898 /* else is crucial here.
3899 * If group != NULL, child_func is meaningless */
3900#if ENABLE_HUSH_FUNCTIONS
3901 else if (command->child_func) {
3902 debug_printf_exec("cmd %p releases child func at %p\n", command, command->child_func);
3903 command->child_func->parent_cmd = NULL;
3904 }
3905#endif
3906#if !BB_MMU
3907 free(command->group_as_string);
3908 //command->group_as_string = NULL;
3909#endif
Francis Laniel8197f012023-12-22 22:02:28 +01003910#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01003911 for (r = command->redirects; r; r = rnext) {
3912 debug_printf_clean(" redirect %d%s",
3913 r->rd_fd, redir_table[r->rd_type].descrip);
3914 /* guard against the case >$FOO, where foo is unset or blank */
3915 if (r->rd_filename) {
3916 debug_printf_clean(" fname:'%s'\n", r->rd_filename);
3917 free(r->rd_filename);
3918 //r->rd_filename = NULL;
3919 }
3920 debug_printf_clean(" rd_dup:%d\n", r->rd_dup);
3921 rnext = r->next;
3922 free(r);
3923 }
3924 //command->redirects = NULL;
Francis Laniel8197f012023-12-22 22:02:28 +01003925#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01003926 }
3927 free(pi->cmds); /* children are an array, they get freed all at once */
3928 //pi->cmds = NULL;
Francis Laniel8197f012023-12-22 22:02:28 +01003929#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01003930#if ENABLE_HUSH_JOB
3931 free(pi->cmdtext);
3932 //pi->cmdtext = NULL;
3933#endif
Francis Laniel8197f012023-12-22 22:02:28 +01003934#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01003935
3936 next = pi->next;
3937 free(pi);
3938 return next;
3939}
3940
3941static void free_pipe_list(struct pipe *pi)
3942{
3943 while (pi) {
3944#if HAS_KEYWORDS
3945 debug_printf_clean("pipe reserved word %d\n", pi->res_word);
3946#endif
3947 debug_printf_clean("pipe followup code %d\n", pi->followup);
3948 pi = free_pipe(pi);
3949 }
3950}
3951
3952
3953/*** Parsing routines ***/
3954
3955#ifndef debug_print_tree
3956static void debug_print_tree(struct pipe *pi, int lvl)
3957{
3958 static const char *const PIPE[] = {
3959 [PIPE_SEQ] = "SEQ",
3960 [PIPE_AND] = "AND",
3961 [PIPE_OR ] = "OR" ,
3962 [PIPE_BG ] = "BG" ,
3963 };
Francis Laniel8197f012023-12-22 22:02:28 +01003964#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01003965 static const char *RES[] = {
3966 [RES_NONE ] = "NONE" ,
3967# if ENABLE_HUSH_IF
3968 [RES_IF ] = "IF" ,
3969 [RES_THEN ] = "THEN" ,
3970 [RES_ELIF ] = "ELIF" ,
3971 [RES_ELSE ] = "ELSE" ,
3972 [RES_FI ] = "FI" ,
3973# endif
3974# if ENABLE_HUSH_LOOPS
3975 [RES_FOR ] = "FOR" ,
3976 [RES_WHILE] = "WHILE",
3977 [RES_UNTIL] = "UNTIL",
3978 [RES_DO ] = "DO" ,
3979 [RES_DONE ] = "DONE" ,
3980# endif
3981# if ENABLE_HUSH_LOOPS || ENABLE_HUSH_CASE
3982 [RES_IN ] = "IN" ,
3983# endif
3984# if ENABLE_HUSH_CASE
3985 [RES_CASE ] = "CASE" ,
3986 [RES_CASE_IN ] = "CASE_IN" ,
3987 [RES_MATCH] = "MATCH",
3988 [RES_CASE_BODY] = "CASE_BODY",
3989 [RES_ESAC ] = "ESAC" ,
3990# endif
3991 [RES_XXXX ] = "XXXX" ,
3992 [RES_SNTX ] = "SNTX" ,
3993 };
Francis Laniel8197f012023-12-22 22:02:28 +01003994#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01003995 static const char *const CMDTYPE[] = {
3996 "{}",
3997 "()",
3998 "[noglob]",
3999# if ENABLE_HUSH_FUNCTIONS
4000 "func()",
4001# endif
4002 };
4003
4004 int pin, prn;
4005
4006 pin = 0;
4007 while (pi) {
4008 fdprintf(2, "%*spipe %d #cmds:%d %sres_word=%s followup=%d %s\n",
4009 lvl*2, "",
4010 pin,
4011 pi->num_cmds,
Francis Laniel8197f012023-12-22 22:02:28 +01004012#ifdef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01004013 (IF_HAS_KEYWORDS(pi->pi_inverted ? "! " :) ""),
4014 RES[pi->res_word],
Francis Laniel8197f012023-12-22 22:02:28 +01004015#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01004016 pi->followup, PIPE[pi->followup]
4017 );
4018 prn = 0;
4019 while (prn < pi->num_cmds) {
4020 struct command *command = &pi->cmds[prn];
4021 char **argv = command->argv;
4022
4023 fdprintf(2, "%*s cmd %d assignment_cnt:%d",
4024 lvl*2, "", prn,
4025 command->assignment_cnt);
4026# if ENABLE_HUSH_LINENO_VAR
4027 fdprintf(2, " LINENO:%u", command->lineno);
4028# endif
4029 if (command->group) {
4030 fdprintf(2, " group %s: (argv=%p)%s%s\n",
4031 CMDTYPE[command->cmd_type],
4032 argv
4033# if !BB_MMU
4034 , " group_as_string:", command->group_as_string
4035# else
4036 , "", ""
4037# endif
4038 );
4039 debug_print_tree(command->group, lvl+1);
4040 prn++;
4041 continue;
4042 }
4043 if (argv) while (*argv) {
4044 fdprintf(2, " '%s'", *argv);
4045 argv++;
4046 }
Francis Laniel8197f012023-12-22 22:02:28 +01004047#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01004048 if (command->redirects)
4049 fdprintf(2, " {redir}");
Francis Laniel8197f012023-12-22 22:02:28 +01004050#endif /* __U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01004051 fdprintf(2, "\n");
4052 prn++;
4053 }
4054 pi = pi->next;
4055 pin++;
4056 }
4057}
4058#endif /* debug_print_tree */
4059
4060static struct pipe *new_pipe(void)
4061{
4062 struct pipe *pi;
4063 pi = xzalloc(sizeof(struct pipe));
4064 /*pi->res_word = RES_NONE; - RES_NONE is 0 anyway */
4065 return pi;
4066}
4067
4068/* Command (member of a pipe) is complete, or we start a new pipe
4069 * if ctx->command is NULL.
4070 * No errors possible here.
4071 */
4072static int done_command(struct parse_context *ctx)
4073{
4074 /* The command is really already in the pipe structure, so
4075 * advance the pipe counter and make a new, null command. */
4076 struct pipe *pi = ctx->pipe;
4077 struct command *command = ctx->command;
4078
4079#if 0 /* Instead we emit error message at run time */
4080 if (ctx->pending_redirect) {
4081 /* For example, "cmd >" (no filename to redirect to) */
4082 syntax_error("invalid redirect");
4083 ctx->pending_redirect = NULL;
4084 }
4085#endif
4086
4087 if (command) {
4088 if (IS_NULL_CMD(command)) {
4089 debug_printf_parse("done_command: skipping null cmd, num_cmds=%d\n", pi->num_cmds);
4090 goto clear_and_ret;
4091 }
4092 pi->num_cmds++;
4093 debug_printf_parse("done_command: ++num_cmds=%d\n", pi->num_cmds);
4094 //debug_print_tree(ctx->list_head, 20);
4095 } else {
4096 debug_printf_parse("done_command: initializing, num_cmds=%d\n", pi->num_cmds);
4097 }
4098
4099 /* Only real trickiness here is that the uncommitted
4100 * command structure is not counted in pi->num_cmds. */
4101 pi->cmds = xrealloc(pi->cmds, sizeof(*pi->cmds) * (pi->num_cmds+1));
4102 ctx->command = command = &pi->cmds[pi->num_cmds];
4103 clear_and_ret:
4104 memset(command, 0, sizeof(*command));
4105#if ENABLE_HUSH_LINENO_VAR
4106 command->lineno = G.parse_lineno;
4107 debug_printf_parse("command->lineno = G.parse_lineno (%u)\n", G.parse_lineno);
4108#endif
4109 return pi->num_cmds; /* used only for 0/nonzero check */
4110}
4111
4112static void done_pipe(struct parse_context *ctx, pipe_style type)
4113{
4114 int not_null;
4115
4116 debug_printf_parse("done_pipe entered, followup %d\n", type);
4117 /* Close previous command */
4118 not_null = done_command(ctx);
4119#if HAS_KEYWORDS
4120 ctx->pipe->pi_inverted = ctx->ctx_inverted;
4121 ctx->ctx_inverted = 0;
4122 ctx->pipe->res_word = ctx->ctx_res_w;
4123#endif
4124 if (type == PIPE_BG && ctx->list_head != ctx->pipe) {
4125 /* Necessary since && and || have precedence over &:
4126 * "cmd1 && cmd2 &" must spawn both cmds, not only cmd2,
4127 * in a backgrounded subshell.
4128 */
4129 struct pipe *pi;
4130 struct command *command;
4131
4132 /* Is this actually this construct, all pipes end with && or ||? */
4133 pi = ctx->list_head;
4134 while (pi != ctx->pipe) {
4135 if (pi->followup != PIPE_AND && pi->followup != PIPE_OR)
4136 goto no_conv;
4137 pi = pi->next;
4138 }
4139
4140 debug_printf_parse("BG with more than one pipe, converting to { p1 &&...pN; } &\n");
4141 pi->followup = PIPE_SEQ; /* close pN _not_ with "&"! */
4142 pi = xzalloc(sizeof(*pi));
4143 pi->followup = PIPE_BG;
4144 pi->num_cmds = 1;
4145 pi->cmds = xzalloc(sizeof(pi->cmds[0]));
4146 command = &pi->cmds[0];
4147 if (CMD_NORMAL != 0) /* "if xzalloc didn't do that already" */
4148 command->cmd_type = CMD_NORMAL;
4149 command->group = ctx->list_head;
4150#if !BB_MMU
4151 command->group_as_string = xstrndup(
4152 ctx->as_string.data,
4153 ctx->as_string.length - 1 /* do not copy last char, "&" */
4154 );
4155#endif
4156 /* Replace all pipes in ctx with one newly created */
4157 ctx->list_head = ctx->pipe = pi;
4158 /* for cases like "cmd && &", do not be tricked by last command
4159 * being null - the entire {...} & is NOT null! */
4160 not_null = 1;
4161 } else {
4162 no_conv:
4163 ctx->pipe->followup = type;
4164 }
4165
4166 /* Without this check, even just <enter> on command line generates
4167 * tree of three NOPs (!). Which is harmless but annoying.
4168 * IOW: it is safe to do it unconditionally. */
4169 if (not_null
4170#if ENABLE_HUSH_IF
4171 || ctx->ctx_res_w == RES_FI
4172#endif
4173#if ENABLE_HUSH_LOOPS
4174 || ctx->ctx_res_w == RES_DONE
4175 || ctx->ctx_res_w == RES_FOR
4176 || ctx->ctx_res_w == RES_IN
4177#endif
4178#if ENABLE_HUSH_CASE
4179 || ctx->ctx_res_w == RES_ESAC
4180#endif
4181 ) {
4182 struct pipe *new_p;
4183 debug_printf_parse("done_pipe: adding new pipe: "
Francis Laniel8197f012023-12-22 22:02:28 +01004184#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01004185 "not_null:%d ctx->ctx_res_w:%d\n",
4186 not_null, ctx->ctx_res_w);
Francis Laniel8197f012023-12-22 22:02:28 +01004187#else /* __U_BOOT__ */
4188 "not_null:%d\n",
4189 not_null);
4190#endif /* __U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01004191 new_p = new_pipe();
4192 ctx->pipe->next = new_p;
4193 ctx->pipe = new_p;
4194 /* RES_THEN, RES_DO etc are "sticky" -
4195 * they remain set for pipes inside if/while.
4196 * This is used to control execution.
4197 * RES_FOR and RES_IN are NOT sticky (needed to support
4198 * cases where variable or value happens to match a keyword):
4199 */
4200#if ENABLE_HUSH_LOOPS
4201 if (ctx->ctx_res_w == RES_FOR
4202 || ctx->ctx_res_w == RES_IN)
4203 ctx->ctx_res_w = RES_NONE;
4204#endif
4205#if ENABLE_HUSH_CASE
4206 if (ctx->ctx_res_w == RES_MATCH)
4207 ctx->ctx_res_w = RES_CASE_BODY;
4208 if (ctx->ctx_res_w == RES_CASE)
4209 ctx->ctx_res_w = RES_CASE_IN;
4210#endif
4211 ctx->command = NULL; /* trick done_command below */
4212 /* Create the memory for command, roughly:
4213 * ctx->pipe->cmds = new struct command;
4214 * ctx->command = &ctx->pipe->cmds[0];
4215 */
4216 done_command(ctx);
4217 //debug_print_tree(ctx->list_head, 10);
4218 }
4219 debug_printf_parse("done_pipe return\n");
4220}
4221
4222static void initialize_context(struct parse_context *ctx)
4223{
4224 memset(ctx, 0, sizeof(*ctx));
4225 if (MAYBE_ASSIGNMENT != 0)
4226 ctx->is_assignment = MAYBE_ASSIGNMENT;
4227 ctx->pipe = ctx->list_head = new_pipe();
4228 /* Create the memory for command, roughly:
4229 * ctx->pipe->cmds = new struct command;
4230 * ctx->command = &ctx->pipe->cmds[0];
4231 */
4232 done_command(ctx);
4233}
4234
4235/* If a reserved word is found and processed, parse context is modified
4236 * and 1 is returned.
4237 */
4238#if HAS_KEYWORDS
4239struct reserved_combo {
4240 char literal[6];
4241 unsigned char res;
4242 unsigned char assignment_flag;
4243 uint32_t flag;
4244};
4245enum {
4246 FLAG_END = (1 << RES_NONE ),
4247# if ENABLE_HUSH_IF
4248 FLAG_IF = (1 << RES_IF ),
4249 FLAG_THEN = (1 << RES_THEN ),
4250 FLAG_ELIF = (1 << RES_ELIF ),
4251 FLAG_ELSE = (1 << RES_ELSE ),
4252 FLAG_FI = (1 << RES_FI ),
4253# endif
4254# if ENABLE_HUSH_LOOPS
4255 FLAG_FOR = (1 << RES_FOR ),
4256 FLAG_WHILE = (1 << RES_WHILE),
4257 FLAG_UNTIL = (1 << RES_UNTIL),
4258 FLAG_DO = (1 << RES_DO ),
4259 FLAG_DONE = (1 << RES_DONE ),
4260 FLAG_IN = (1 << RES_IN ),
4261# endif
4262# if ENABLE_HUSH_CASE
4263 FLAG_MATCH = (1 << RES_MATCH),
4264 FLAG_ESAC = (1 << RES_ESAC ),
4265# endif
4266 FLAG_START = (1 << RES_XXXX ),
4267};
4268
4269static const struct reserved_combo* match_reserved_word(o_string *word)
4270{
4271 /* Mostly a list of accepted follow-up reserved words.
4272 * FLAG_END means we are done with the sequence, and are ready
4273 * to turn the compound list into a command.
4274 * FLAG_START means the word must start a new compound list.
4275 */
4276 static const struct reserved_combo reserved_list[] ALIGN4 = {
4277# if ENABLE_HUSH_IF
4278 { "!", RES_NONE, NOT_ASSIGNMENT , 0 },
4279 { "if", RES_IF, MAYBE_ASSIGNMENT, FLAG_THEN | FLAG_START },
4280 { "then", RES_THEN, MAYBE_ASSIGNMENT, FLAG_ELIF | FLAG_ELSE | FLAG_FI },
4281 { "elif", RES_ELIF, MAYBE_ASSIGNMENT, FLAG_THEN },
4282 { "else", RES_ELSE, MAYBE_ASSIGNMENT, FLAG_FI },
4283 { "fi", RES_FI, NOT_ASSIGNMENT , FLAG_END },
4284# endif
4285# if ENABLE_HUSH_LOOPS
4286 { "for", RES_FOR, NOT_ASSIGNMENT , FLAG_IN | FLAG_DO | FLAG_START },
4287 { "while", RES_WHILE, MAYBE_ASSIGNMENT, FLAG_DO | FLAG_START },
4288 { "until", RES_UNTIL, MAYBE_ASSIGNMENT, FLAG_DO | FLAG_START },
4289 { "in", RES_IN, NOT_ASSIGNMENT , FLAG_DO },
4290 { "do", RES_DO, MAYBE_ASSIGNMENT, FLAG_DONE },
4291 { "done", RES_DONE, NOT_ASSIGNMENT , FLAG_END },
4292# endif
4293# if ENABLE_HUSH_CASE
4294 { "case", RES_CASE, NOT_ASSIGNMENT , FLAG_MATCH | FLAG_START },
4295 { "esac", RES_ESAC, NOT_ASSIGNMENT , FLAG_END },
4296# endif
4297 };
4298 const struct reserved_combo *r;
4299
4300 for (r = reserved_list; r < reserved_list + ARRAY_SIZE(reserved_list); r++) {
4301 if (strcmp(word->data, r->literal) == 0)
4302 return r;
4303 }
4304 return NULL;
4305}
4306/* Return NULL: not a keyword, else: keyword
4307 */
4308static const struct reserved_combo* reserved_word(struct parse_context *ctx)
4309{
4310# if ENABLE_HUSH_CASE
4311 static const struct reserved_combo reserved_match = {
4312 "", RES_MATCH, NOT_ASSIGNMENT , FLAG_MATCH | FLAG_ESAC
4313 };
4314# endif
4315 const struct reserved_combo *r;
4316
4317 if (ctx->word.has_quoted_part)
4318 return 0;
4319 r = match_reserved_word(&ctx->word);
4320 if (!r)
4321 return r; /* NULL */
4322
4323 debug_printf("found reserved word %s, res %d\n", r->literal, r->res);
4324# if ENABLE_HUSH_CASE
4325 if (r->res == RES_IN && ctx->ctx_res_w == RES_CASE_IN) {
4326 /* "case word IN ..." - IN part starts first MATCH part */
4327 r = &reserved_match;
4328 } else
4329# endif
4330 if (r->flag == 0) { /* '!' */
4331 if (ctx->ctx_inverted) { /* bash doesn't accept '! ! true' */
4332 syntax_error("! ! command");
4333 ctx->ctx_res_w = RES_SNTX;
4334 }
4335 ctx->ctx_inverted = 1;
4336 return r;
4337 }
4338 if (r->flag & FLAG_START) {
4339 struct parse_context *old;
4340
4341 old = xmemdup(ctx, sizeof(*ctx));
4342 debug_printf_parse("push stack %p\n", old);
4343 initialize_context(ctx);
4344 ctx->stack = old;
4345 } else if (/*ctx->ctx_res_w == RES_NONE ||*/ !(ctx->old_flag & (1 << r->res))) {
4346 syntax_error_at(ctx->word.data);
4347 ctx->ctx_res_w = RES_SNTX;
4348 return r;
4349 } else {
4350 /* "{...} fi" is ok. "{...} if" is not
4351 * Example:
4352 * if { echo foo; } then { echo bar; } fi */
4353 if (ctx->command->group)
4354 done_pipe(ctx, PIPE_SEQ);
4355 }
4356
4357 ctx->ctx_res_w = r->res;
4358 ctx->old_flag = r->flag;
4359 ctx->is_assignment = r->assignment_flag;
4360 debug_printf_parse("ctx->is_assignment='%s'\n", assignment_flag[ctx->is_assignment]);
4361
4362 if (ctx->old_flag & FLAG_END) {
4363 struct parse_context *old;
4364
4365 done_pipe(ctx, PIPE_SEQ);
4366 debug_printf_parse("pop stack %p\n", ctx->stack);
4367 old = ctx->stack;
4368 old->command->group = ctx->list_head;
4369 old->command->cmd_type = CMD_NORMAL;
4370# if !BB_MMU
4371 /* At this point, the compound command's string is in
4372 * ctx->as_string... except for the leading keyword!
4373 * Consider this example: "echo a | if true; then echo a; fi"
4374 * ctx->as_string will contain "true; then echo a; fi",
4375 * with "if " remaining in old->as_string!
4376 */
4377 {
4378 char *str;
4379 int len = old->as_string.length;
4380 /* Concatenate halves */
4381 o_addstr(&old->as_string, ctx->as_string.data);
4382 o_free(&ctx->as_string);
4383 /* Find where leading keyword starts in first half */
4384 str = old->as_string.data + len;
4385 if (str > old->as_string.data)
4386 str--; /* skip whitespace after keyword */
4387 while (str > old->as_string.data && isalpha(str[-1]))
4388 str--;
4389 /* Ugh, we're done with this horrid hack */
4390 old->command->group_as_string = xstrdup(str);
4391 debug_printf_parse("pop, remembering as:'%s'\n",
4392 old->command->group_as_string);
4393 }
4394# endif
4395 *ctx = *old; /* physical copy */
4396 free(old);
4397 }
4398 return r;
4399}
4400#endif /* HAS_KEYWORDS */
4401
4402/* Word is complete, look at it and update parsing context.
4403 * Normal return is 0. Syntax errors return 1.
4404 * Note: on return, word is reset, but not o_free'd!
4405 */
4406static int done_word(struct parse_context *ctx)
4407{
4408 struct command *command = ctx->command;
4409
4410 debug_printf_parse("done_word entered: '%s' %p\n", ctx->word.data, command);
4411 if (ctx->word.length == 0 && !ctx->word.has_quoted_part) {
4412 debug_printf_parse("done_word return 0: true null, ignored\n");
4413 return 0;
4414 }
4415
Francis Laniel8197f012023-12-22 22:02:28 +01004416#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01004417 if (ctx->pending_redirect) {
4418 /* We do not glob in e.g. >*.tmp case. bash seems to glob here
4419 * only if run as "bash", not "sh" */
4420 /* http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html
4421 * "2.7 Redirection
4422 * If the redirection operator is "<<" or "<<-", the word
4423 * that follows the redirection operator shall be
4424 * subjected to quote removal; it is unspecified whether
4425 * any of the other expansions occur. For the other
4426 * redirection operators, the word that follows the
4427 * redirection operator shall be subjected to tilde
4428 * expansion, parameter expansion, command substitution,
4429 * arithmetic expansion, and quote removal.
4430 * Pathname expansion shall not be performed
4431 * on the word by a non-interactive shell; an interactive
4432 * shell may perform it, but shall do so only when
4433 * the expansion would result in one word."
4434 */
4435//bash does not do parameter/command substitution or arithmetic expansion
4436//for _heredoc_ redirection word: these constructs look for exact eof marker
4437// as written:
4438// <<EOF$t
4439// <<EOF$((1))
4440// <<EOF`true` [this case also makes heredoc "quoted", a-la <<"EOF". Probably bash-4.3.43 bug]
4441
4442 ctx->pending_redirect->rd_filename = xstrdup(ctx->word.data);
4443 /* Cater for >\file case:
4444 * >\a creates file a; >\\a, >"\a", >"\\a" create file \a
4445 * Same with heredocs:
4446 * for <<\H delim is H; <<\\H, <<"\H", <<"\\H" - \H
4447 */
4448 if (ctx->pending_redirect->rd_type == REDIRECT_HEREDOC) {
4449 unbackslash(ctx->pending_redirect->rd_filename);
4450 /* Is it <<"HEREDOC"? */
4451 if (ctx->word.has_quoted_part) {
4452 ctx->pending_redirect->rd_dup |= HEREDOC_QUOTED;
4453 }
4454 }
4455 debug_printf_parse("word stored in rd_filename: '%s'\n", ctx->word.data);
4456 ctx->pending_redirect = NULL;
4457 } else {
Francis Laniel8197f012023-12-22 22:02:28 +01004458#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01004459#if HAS_KEYWORDS
4460# if ENABLE_HUSH_CASE
4461 if (ctx->ctx_dsemicolon
4462 && strcmp(ctx->word.data, "esac") != 0 /* not "... pattern) cmd;; esac" */
4463 ) {
4464 /* already done when ctx_dsemicolon was set to 1: */
4465 /* ctx->ctx_res_w = RES_MATCH; */
4466 ctx->ctx_dsemicolon = 0;
4467 } else
4468# endif
4469# if defined(CMD_TEST2_SINGLEWORD_NOGLOB)
4470 if (command->cmd_type == CMD_TEST2_SINGLEWORD_NOGLOB
4471 && strcmp(ctx->word.data, "]]") == 0
4472 ) {
4473 /* allow "[[ ]] >file" etc */
4474 command->cmd_type = CMD_SINGLEWORD_NOGLOB;
4475 } else
4476# endif
4477 if (!command->argv /* if it's the first word... */
4478# if ENABLE_HUSH_LOOPS
4479 && ctx->ctx_res_w != RES_FOR /* ...not after FOR or IN */
4480 && ctx->ctx_res_w != RES_IN
4481# endif
4482# if ENABLE_HUSH_CASE
4483 && ctx->ctx_res_w != RES_CASE
4484# endif
4485 ) {
4486 const struct reserved_combo *reserved;
4487 reserved = reserved_word(ctx);
4488 debug_printf_parse("checking for reserved-ness: %d\n", !!reserved);
4489 if (reserved) {
4490# if ENABLE_HUSH_LINENO_VAR
4491/* Case:
4492 * "while ...; do
4493 * cmd ..."
4494 * If we don't close the pipe _now_, immediately after "do", lineno logic
4495 * sees "cmd" as starting at "do" - i.e., at the previous line.
4496 */
4497 if (0
4498 IF_HUSH_IF(|| reserved->res == RES_THEN)
4499 IF_HUSH_IF(|| reserved->res == RES_ELIF)
4500 IF_HUSH_IF(|| reserved->res == RES_ELSE)
4501 IF_HUSH_LOOPS(|| reserved->res == RES_DO)
4502 ) {
4503 done_pipe(ctx, PIPE_SEQ);
4504 }
4505# endif
4506 o_reset_to_empty_unquoted(&ctx->word);
4507 debug_printf_parse("done_word return %d\n",
4508 (ctx->ctx_res_w == RES_SNTX));
4509 return (ctx->ctx_res_w == RES_SNTX);
4510 }
4511# if defined(CMD_TEST2_SINGLEWORD_NOGLOB)
4512 if (strcmp(ctx->word.data, "[[") == 0) {
4513 command->cmd_type = CMD_TEST2_SINGLEWORD_NOGLOB;
4514 } else
4515# endif
4516# if defined(CMD_SINGLEWORD_NOGLOB)
4517 if (0
4518 /* In bash, local/export/readonly are special, args
4519 * are assignments and therefore expansion of them
4520 * should be "one-word" expansion:
4521 * $ export i=`echo 'a b'` # one arg: "i=a b"
4522 * compare with:
4523 * $ ls i=`echo 'a b'` # two args: "i=a" and "b"
4524 * ls: cannot access i=a: No such file or directory
4525 * ls: cannot access b: No such file or directory
4526 * Note: bash 3.2.33(1) does this only if export word
4527 * itself is not quoted:
4528 * $ export i=`echo 'aaa bbb'`; echo "$i"
4529 * aaa bbb
4530 * $ "export" i=`echo 'aaa bbb'`; echo "$i"
4531 * aaa
4532 */
4533 IF_HUSH_LOCAL( || strcmp(ctx->word.data, "local") == 0)
4534 IF_HUSH_EXPORT( || strcmp(ctx->word.data, "export") == 0)
4535 IF_HUSH_READONLY(|| strcmp(ctx->word.data, "readonly") == 0)
4536 ) {
4537 command->cmd_type = CMD_SINGLEWORD_NOGLOB;
4538 }
4539# else
4540 { /* empty block to pair "if ... else" */ }
4541# endif
4542 }
4543#endif /* HAS_KEYWORDS */
4544
4545 if (command->group) {
4546 /* "{ echo foo; } echo bar" - bad */
4547 syntax_error_at(ctx->word.data);
4548 debug_printf_parse("done_word return 1: syntax error, "
4549 "groups and arglists don't mix\n");
4550 return 1;
4551 }
4552
4553 /* If this word wasn't an assignment, next ones definitely
4554 * can't be assignments. Even if they look like ones. */
4555 if (ctx->is_assignment != DEFINITELY_ASSIGNMENT
4556 && ctx->is_assignment != WORD_IS_KEYWORD
4557 ) {
4558 ctx->is_assignment = NOT_ASSIGNMENT;
4559 } else {
4560 if (ctx->is_assignment == DEFINITELY_ASSIGNMENT) {
4561 command->assignment_cnt++;
4562 debug_printf_parse("++assignment_cnt=%d\n", command->assignment_cnt);
4563 }
4564 debug_printf_parse("ctx->is_assignment was:'%s'\n", assignment_flag[ctx->is_assignment]);
4565 ctx->is_assignment = MAYBE_ASSIGNMENT;
4566 }
4567 debug_printf_parse("ctx->is_assignment='%s'\n", assignment_flag[ctx->is_assignment]);
4568 command->argv = add_string_to_strings(command->argv, xstrdup(ctx->word.data));
Francis Laniel8197f012023-12-22 22:02:28 +01004569#ifdef __U_BOOT__
4570 command->argc++;
4571#endif /* __U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01004572 debug_print_strings("word appended to argv", command->argv);
Francis Laniel8197f012023-12-22 22:02:28 +01004573
4574#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01004575 }
Francis Laniel8197f012023-12-22 22:02:28 +01004576#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01004577
4578#if ENABLE_HUSH_LOOPS
4579 if (ctx->ctx_res_w == RES_FOR) {
4580 if (ctx->word.has_quoted_part
4581 || endofname(command->argv[0])[0] != '\0'
4582 ) {
4583 /* bash says just "not a valid identifier" */
4584 syntax_error("bad variable name in for");
4585 return 1;
4586 }
4587 /* Force FOR to have just one word (variable name) */
4588 /* NB: basically, this makes hush see "for v in ..."
4589 * syntax as if it is "for v; in ...". FOR and IN become
4590 * two pipe structs in parse tree. */
4591 done_pipe(ctx, PIPE_SEQ);
4592 }
4593#endif
4594#if ENABLE_HUSH_CASE
4595 /* Force CASE to have just one word */
4596 if (ctx->ctx_res_w == RES_CASE) {
4597 done_pipe(ctx, PIPE_SEQ);
4598 }
4599#endif
4600
4601 o_reset_to_empty_unquoted(&ctx->word);
4602
4603 debug_printf_parse("done_word return 0\n");
4604 return 0;
4605}
4606
4607
Francis Laniel8197f012023-12-22 22:02:28 +01004608#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01004609/* Peek ahead in the input to find out if we have a "&n" construct,
4610 * as in "2>&1", that represents duplicating a file descriptor.
4611 * Return:
4612 * REDIRFD_CLOSE if >&- "close fd" construct is seen,
4613 * REDIRFD_SYNTAX_ERR if syntax error,
4614 * REDIRFD_TO_FILE if no & was seen,
4615 * or the number found.
4616 */
4617#if BB_MMU
4618#define parse_redir_right_fd(as_string, input) \
4619 parse_redir_right_fd(input)
4620#endif
4621static int parse_redir_right_fd(o_string *as_string, struct in_str *input)
4622{
4623 int ch, d, ok;
4624
4625 ch = i_peek(input);
4626 if (ch != '&')
4627 return REDIRFD_TO_FILE;
4628
4629 ch = i_getch(input); /* get the & */
4630 nommu_addchr(as_string, ch);
4631 ch = i_peek(input);
4632 if (ch == '-') {
4633 ch = i_getch(input);
4634 nommu_addchr(as_string, ch);
4635 return REDIRFD_CLOSE;
4636 }
4637 d = 0;
4638 ok = 0;
4639 while (ch != EOF && isdigit(ch)) {
4640 d = d*10 + (ch-'0');
4641 ok = 1;
4642 ch = i_getch(input);
4643 nommu_addchr(as_string, ch);
4644 ch = i_peek(input);
4645 }
4646 if (ok) return d;
4647
4648//TODO: this is the place to catch ">&file" bashism (redirect both fd 1 and 2)
4649
4650 bb_simple_error_msg("ambiguous redirect");
4651 return REDIRFD_SYNTAX_ERR;
4652}
4653
4654/* Return code is 0 normal, 1 if a syntax error is detected
4655 */
4656static int parse_redirect(struct parse_context *ctx,
4657 int fd,
4658 redir_type style,
4659 struct in_str *input)
4660{
4661 struct command *command = ctx->command;
4662 struct redir_struct *redir;
4663 struct redir_struct **redirp;
4664 int dup_num;
4665
4666 dup_num = REDIRFD_TO_FILE;
4667 if (style != REDIRECT_HEREDOC) {
4668 /* Check for a '>&1' type redirect */
4669 dup_num = parse_redir_right_fd(&ctx->as_string, input);
4670 if (dup_num == REDIRFD_SYNTAX_ERR)
4671 return 1;
4672 } else {
4673 int ch = i_peek_and_eat_bkslash_nl(input);
4674 dup_num = (ch == '-'); /* HEREDOC_SKIPTABS bit is 1 */
4675 if (dup_num) { /* <<-... */
4676 ch = i_getch(input);
4677 nommu_addchr(&ctx->as_string, ch);
4678 ch = i_peek(input);
4679 }
4680 }
4681
4682 if (style == REDIRECT_OVERWRITE && dup_num == REDIRFD_TO_FILE) {
4683 int ch = i_peek_and_eat_bkslash_nl(input);
4684 if (ch == '|') {
4685 /* >|FILE redirect ("clobbering" >).
4686 * Since we do not support "set -o noclobber" yet,
4687 * >| and > are the same for now. Just eat |.
4688 */
4689 ch = i_getch(input);
4690 nommu_addchr(&ctx->as_string, ch);
4691 }
4692 }
4693
4694 /* Create a new redir_struct and append it to the linked list */
4695 redirp = &command->redirects;
4696 while ((redir = *redirp) != NULL) {
4697 redirp = &(redir->next);
4698 }
4699 *redirp = redir = xzalloc(sizeof(*redir));
4700 /* redir->next = NULL; */
4701 /* redir->rd_filename = NULL; */
4702 redir->rd_type = style;
4703 redir->rd_fd = (fd == -1) ? redir_table[style].default_fd : fd;
4704
4705 debug_printf_parse("redirect type %d %s\n", redir->rd_fd,
4706 redir_table[style].descrip);
4707
4708 redir->rd_dup = dup_num;
4709 if (style != REDIRECT_HEREDOC && dup_num != REDIRFD_TO_FILE) {
4710 /* Erik had a check here that the file descriptor in question
4711 * is legit; I postpone that to "run time"
4712 * A "-" representation of "close me" shows up as a -3 here */
4713 debug_printf_parse("duplicating redirect '%d>&%d'\n",
4714 redir->rd_fd, redir->rd_dup);
4715 } else {
4716#if 0 /* Instead we emit error message at run time */
4717 if (ctx->pending_redirect) {
4718 /* For example, "cmd > <file" */
4719 syntax_error("invalid redirect");
4720 }
4721#endif
4722 /* Set ctx->pending_redirect, so we know what to do at the
4723 * end of the next parsed word. */
4724 ctx->pending_redirect = redir;
4725 }
4726 return 0;
4727}
4728
4729/* If a redirect is immediately preceded by a number, that number is
4730 * supposed to tell which file descriptor to redirect. This routine
4731 * looks for such preceding numbers. In an ideal world this routine
4732 * needs to handle all the following classes of redirects...
4733 * echo 2>foo # redirects fd 2 to file "foo", nothing passed to echo
4734 * echo 49>foo # redirects fd 49 to file "foo", nothing passed to echo
4735 * echo -2>foo # redirects fd 1 to file "foo", "-2" passed to echo
4736 * echo 49x>foo # redirects fd 1 to file "foo", "49x" passed to echo
4737 *
4738 * http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html
4739 * "2.7 Redirection
4740 * ... If n is quoted, the number shall not be recognized as part of
4741 * the redirection expression. For example:
4742 * echo \2>a
4743 * writes the character 2 into file a"
4744 * We are getting it right by setting ->has_quoted_part on any \<char>
4745 *
4746 * A -1 return means no valid number was found,
4747 * the caller should use the appropriate default for this redirection.
4748 */
4749static int redirect_opt_num(o_string *o)
4750{
4751 int num;
4752
4753 if (o->data == NULL)
4754 return -1;
4755 num = bb_strtou(o->data, NULL, 10);
4756 if (errno || num < 0)
4757 return -1;
4758 o_reset_to_empty_unquoted(o);
4759 return num;
4760}
4761
4762#if BB_MMU
4763#define fetch_till_str(as_string, input, word, skip_tabs) \
4764 fetch_till_str(input, word, skip_tabs)
4765#endif
4766static char *fetch_till_str(o_string *as_string,
4767 struct in_str *input,
4768 const char *word,
4769 int heredoc_flags)
4770{
4771 o_string heredoc = NULL_O_STRING;
4772 unsigned past_EOL;
4773 int prev = 0; /* not \ */
4774 int ch;
4775
4776 /* Starting with "" is necessary for this case:
4777 * cat <<EOF
4778 *
4779 * xxx
4780 * EOF
4781 */
4782 heredoc.data = xzalloc(1); /* start as "", not as NULL */
4783
4784 goto jump_in;
4785
4786 while (1) {
4787 ch = i_getch(input);
4788 if (ch != EOF)
4789 nommu_addchr(as_string, ch);
4790 if (ch == '\n' || ch == EOF) {
4791 check_heredoc_end:
Francis Laniel8197f012023-12-22 22:02:28 +01004792#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01004793 if ((heredoc_flags & HEREDOC_QUOTED) || prev != '\\') {
Francis Laniel8197f012023-12-22 22:02:28 +01004794#else /* __U_BOOT__ */
4795 if (prev != '\\') {
4796#endif
Francis Lanielb234f7e2023-12-22 22:02:27 +01004797 /* End-of-line, and not a line continuation */
4798 if (strcmp(heredoc.data + past_EOL, word) == 0) {
4799 heredoc.data[past_EOL] = '\0';
4800 debug_printf_heredoc("parsed '%s' heredoc '%s'\n", word, heredoc.data);
4801 return heredoc.data;
4802 }
4803 if (ch == '\n') {
4804 /* This is a new line.
4805 * Remember position and backslash-escaping status.
4806 */
4807 o_addchr(&heredoc, ch);
4808 prev = ch;
4809 jump_in:
4810 past_EOL = heredoc.length;
4811 /* Get 1st char of next line, possibly skipping leading tabs */
4812 do {
4813 ch = i_getch(input);
4814 if (ch != EOF)
4815 nommu_addchr(as_string, ch);
Francis Laniel8197f012023-12-22 22:02:28 +01004816#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01004817 } while ((heredoc_flags & HEREDOC_SKIPTABS) && ch == '\t');
Francis Laniel8197f012023-12-22 22:02:28 +01004818#else /* __U_BOOT__ */
4819 } while (ch == '\t');
4820#endif
Francis Lanielb234f7e2023-12-22 22:02:27 +01004821 /* If this immediately ended the line,
4822 * go back to end-of-line checks.
4823 */
4824 if (ch == '\n')
4825 goto check_heredoc_end;
4826 }
4827 } else {
4828 /* Backslash-line continuation in an unquoted
4829 * heredoc. This does not need special handling
4830 * for heredoc body (unquoted heredocs are
4831 * expanded on "execution" and that would take
4832 * care of this case too), but not the case
4833 * of line continuation *in terminator*:
4834 * cat <<EOF
4835 * Ok1
4836 * EO\
4837 * F
4838 */
4839 heredoc.data[--heredoc.length] = '\0';
4840 prev = 0; /* not '\' */
4841 continue;
4842 }
4843 }
4844 if (ch == EOF) {
4845 o_free(&heredoc);
4846 return NULL; /* error */
4847 }
4848 o_addchr(&heredoc, ch);
4849 nommu_addchr(as_string, ch);
4850 if (prev == '\\' && ch == '\\')
4851 /* Correctly handle foo\\<eol> (not a line cont.) */
4852 prev = 0; /* not '\' */
4853 else
4854 prev = ch;
4855 }
4856}
Francis Laniel8197f012023-12-22 22:02:28 +01004857#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01004858
4859/* Look at entire parse tree for not-yet-loaded REDIRECT_HEREDOCs
4860 * and load them all. There should be exactly heredoc_cnt of them.
4861 */
4862#if BB_MMU
4863#define fetch_heredocs(as_string, pi, heredoc_cnt, input) \
4864 fetch_heredocs(pi, heredoc_cnt, input)
4865#endif
4866static int fetch_heredocs(o_string *as_string, struct pipe *pi, int heredoc_cnt, struct in_str *input)
4867{
4868 while (pi && heredoc_cnt) {
4869 int i;
4870 struct command *cmd = pi->cmds;
4871
4872 debug_printf_heredoc("fetch_heredocs: num_cmds:%d cmd argv0:'%s'\n",
4873 pi->num_cmds,
4874 cmd->argv ? cmd->argv[0] : "NONE"
4875 );
4876 for (i = 0; i < pi->num_cmds; i++) {
Francis Laniel8197f012023-12-22 22:02:28 +01004877#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01004878 struct redir_struct *redir = cmd->redirects;
4879
Francis Laniel8197f012023-12-22 22:02:28 +01004880#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01004881 debug_printf_heredoc("fetch_heredocs: %d cmd argv0:'%s'\n",
4882 i, cmd->argv ? cmd->argv[0] : "NONE");
Francis Laniel8197f012023-12-22 22:02:28 +01004883#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01004884 while (redir) {
4885 if (redir->rd_type == REDIRECT_HEREDOC) {
4886 char *p;
4887
4888 redir->rd_type = REDIRECT_HEREDOC2;
4889 /* redir->rd_dup is (ab)used to indicate <<- */
4890 p = fetch_till_str(as_string, input,
4891 redir->rd_filename, redir->rd_dup);
4892 if (!p) {
4893 syntax_error("unexpected EOF in here document");
4894 return -1;
4895 }
4896 free(redir->rd_filename);
4897 redir->rd_filename = p;
4898 heredoc_cnt--;
4899 }
4900 redir = redir->next;
4901 }
Francis Laniel8197f012023-12-22 22:02:28 +01004902#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01004903 if (cmd->group) {
4904 //bb_error_msg("%s:%u heredoc_cnt:%d", __func__, __LINE__, heredoc_cnt);
4905 heredoc_cnt = fetch_heredocs(as_string, cmd->group, heredoc_cnt, input);
4906 //bb_error_msg("%s:%u heredoc_cnt:%d", __func__, __LINE__, heredoc_cnt);
4907 if (heredoc_cnt < 0)
4908 return heredoc_cnt; /* error */
4909 }
4910 cmd++;
4911 }
4912 pi = pi->next;
4913 }
4914 return heredoc_cnt;
4915}
4916
4917
4918static int run_list(struct pipe *pi);
4919#if BB_MMU
4920#define parse_stream(pstring, heredoc_cnt_ptr, input, end_trigger) \
4921 parse_stream(heredoc_cnt_ptr, input, end_trigger)
4922#endif
4923static struct pipe *parse_stream(char **pstring,
4924 int *heredoc_cnt_ptr,
4925 struct in_str *input,
4926 int end_trigger);
4927
4928/* Returns number of heredocs not yet consumed,
4929 * or -1 on error.
4930 */
4931static int parse_group(struct parse_context *ctx,
4932 struct in_str *input, int ch)
4933{
4934 /* ctx->word contains characters seen prior to ( or {.
4935 * Typically it's empty, but for function defs,
4936 * it contains function name (without '()'). */
4937#if BB_MMU
4938# define as_string NULL
4939#else
4940 char *as_string = NULL;
4941#endif
4942 struct pipe *pipe_list;
4943 int heredoc_cnt = 0;
4944 int endch;
4945 struct command *command = ctx->command;
4946
4947 debug_printf_parse("parse_group entered\n");
4948#if ENABLE_HUSH_FUNCTIONS
4949 if (ch == '(' && !ctx->word.has_quoted_part) {
4950 if (ctx->word.length)
4951 if (done_word(ctx))
4952 return -1;
4953 if (!command->argv)
4954 goto skip; /* (... */
4955 if (command->argv[1]) { /* word word ... (... */
4956 syntax_error_unexpected_ch('(');
4957 return -1;
4958 }
4959 /* it is "word(..." or "word (..." */
4960 do
4961 ch = i_getch(input);
4962 while (ch == ' ' || ch == '\t');
4963 if (ch != ')') {
4964 syntax_error_unexpected_ch(ch);
4965 return -1;
4966 }
4967 nommu_addchr(&ctx->as_string, ch);
4968 do
4969 ch = i_getch(input);
4970 while (ch == ' ' || ch == '\t' || ch == '\n');
4971 if (ch != '{' && ch != '(') {
4972 syntax_error_unexpected_ch(ch);
4973 return -1;
4974 }
4975 nommu_addchr(&ctx->as_string, ch);
4976 command->cmd_type = CMD_FUNCDEF;
4977 goto skip;
4978 }
4979#endif
4980
4981#if 0 /* Prevented by caller */
4982 if (command->argv /* word [word]{... */
4983 || ctx->word.length /* word{... */
4984 || ctx->word.has_quoted_part /* ""{... */
4985 ) {
4986 syntax_error(NULL);
4987 debug_printf_parse("parse_group return -1: "
4988 "syntax error, groups and arglists don't mix\n");
4989 return -1;
4990 }
4991#endif
4992
Francis Laniel8197f012023-12-22 22:02:28 +01004993#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01004994 IF_HUSH_FUNCTIONS(skip:)
Francis Laniel8197f012023-12-22 22:02:28 +01004995#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01004996
4997 endch = '}';
4998 if (ch == '(') {
4999 endch = ')';
Francis Laniel8197f012023-12-22 22:02:28 +01005000#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01005001 IF_HUSH_FUNCTIONS(if (command->cmd_type != CMD_FUNCDEF))
5002 command->cmd_type = CMD_SUBSHELL;
Francis Laniel8197f012023-12-22 22:02:28 +01005003#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01005004 } else {
5005 /* bash does not allow "{echo...", requires whitespace */
5006 ch = i_peek(input);
5007 if (ch != ' ' && ch != '\t' && ch != '\n'
5008 && ch != '(' /* but "{(..." is allowed (without whitespace) */
5009 ) {
5010 syntax_error_unexpected_ch(ch);
5011 return -1;
5012 }
5013 if (ch != '(') {
5014 ch = i_getch(input);
5015 nommu_addchr(&ctx->as_string, ch);
5016 }
5017 }
5018
5019 debug_printf_heredoc("calling parse_stream, heredoc_cnt:%d\n", heredoc_cnt);
5020 pipe_list = parse_stream(&as_string, &heredoc_cnt, input, endch);
5021 debug_printf_heredoc("parse_stream returned: heredoc_cnt:%d\n", heredoc_cnt);
5022#if !BB_MMU
5023 if (as_string)
5024 o_addstr(&ctx->as_string, as_string);
5025#endif
5026
5027 /* empty ()/{} or parse error? */
5028 if (!pipe_list || pipe_list == ERR_PTR) {
5029 /* parse_stream already emitted error msg */
5030 if (!BB_MMU)
5031 free(as_string);
5032 debug_printf_parse("parse_group return -1: "
5033 "parse_stream returned %p\n", pipe_list);
5034 return -1;
5035 }
5036#if !BB_MMU
5037 as_string[strlen(as_string) - 1] = '\0'; /* plink ')' or '}' */
5038 command->group_as_string = as_string;
5039 debug_printf_parse("end of group, remembering as:'%s'\n",
5040 command->group_as_string);
5041#endif
5042
5043#if ENABLE_HUSH_FUNCTIONS
5044 /* Convert "f() (cmds)" to "f() {(cmds)}" */
5045 if (command->cmd_type == CMD_FUNCDEF && endch == ')') {
5046 struct command *cmd2;
5047
5048 cmd2 = xzalloc(sizeof(*cmd2));
5049 cmd2->cmd_type = CMD_SUBSHELL;
5050 cmd2->group = pipe_list;
5051# if !BB_MMU
5052//UNTESTED!
5053 cmd2->group_as_string = command->group_as_string;
5054 command->group_as_string = xasprintf("(%s)", command->group_as_string);
5055# endif
5056
5057 pipe_list = new_pipe();
5058 pipe_list->cmds = cmd2;
5059 pipe_list->num_cmds = 1;
5060 }
5061#endif
5062
5063 command->group = pipe_list;
5064
5065 debug_printf_parse("parse_group return %d\n", heredoc_cnt);
5066 return heredoc_cnt;
5067 /* command remains "open", available for possible redirects */
5068#undef as_string
5069}
5070
Francis Laniel8197f012023-12-22 22:02:28 +01005071#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01005072#if ENABLE_HUSH_TICK || ENABLE_FEATURE_SH_MATH || ENABLE_HUSH_DOLLAR_OPS
5073/* Subroutines for copying $(...) and `...` things */
5074/* '...' */
5075static int add_till_single_quote(o_string *dest, struct in_str *input)
5076{
5077 while (1) {
5078 int ch = i_getch(input);
5079 if (ch == EOF) {
5080 syntax_error_unterm_ch('\'');
5081 return 0;
5082 }
5083 if (ch == '\'')
5084 return 1;
5085 o_addchr(dest, ch);
5086 }
5087}
5088static int add_till_single_quote_dquoted(o_string *dest, struct in_str *input)
5089{
5090 while (1) {
5091 int ch = i_getch(input);
5092 if (ch == EOF) {
5093 syntax_error_unterm_ch('\'');
5094 return 0;
5095 }
5096 if (ch == '\'')
5097 return 1;
5098 o_addqchr(dest, ch);
5099 }
5100}
Francis Laniel8197f012023-12-22 22:02:28 +01005101
Francis Lanielb234f7e2023-12-22 22:02:27 +01005102/* "...\"...`..`...." - do we need to handle "...$(..)..." too? */
5103static int add_till_backquote(o_string *dest, struct in_str *input, int in_dquote);
5104static int add_till_double_quote(o_string *dest, struct in_str *input)
5105{
5106 while (1) {
5107 int ch = i_getch(input);
5108 if (ch == EOF) {
5109 syntax_error_unterm_ch('"');
5110 return 0;
5111 }
5112 if (ch == '"')
5113 return 1;
5114 if (ch == '\\') { /* \x. Copy both chars. */
5115 o_addchr(dest, ch);
5116 ch = i_getch(input);
5117 }
5118 o_addchr(dest, ch);
5119 if (ch == '`') {
5120 if (!add_till_backquote(dest, input, /*in_dquote:*/ 1))
5121 return 0;
5122 o_addchr(dest, ch);
5123 continue;
5124 }
5125 //if (ch == '$') ...
5126 }
5127}
Francis Laniel8197f012023-12-22 22:02:28 +01005128
5129
Francis Lanielb234f7e2023-12-22 22:02:27 +01005130/* Process `cmd` - copy contents until "`" is seen. Complicated by
5131 * \` quoting.
5132 * "Within the backquoted style of command substitution, backslash
5133 * shall retain its literal meaning, except when followed by: '$', '`', or '\'.
5134 * The search for the matching backquote shall be satisfied by the first
5135 * backquote found without a preceding backslash; during this search,
5136 * if a non-escaped backquote is encountered within a shell comment,
5137 * a here-document, an embedded command substitution of the $(command)
5138 * form, or a quoted string, undefined results occur. A single-quoted
5139 * or double-quoted string that begins, but does not end, within the
5140 * "`...`" sequence produces undefined results."
5141 * Example Output
5142 * echo `echo '\'TEST\`echo ZZ\`BEST` \TESTZZBEST
5143 */
5144static int add_till_backquote(o_string *dest, struct in_str *input, int in_dquote)
5145{
5146 while (1) {
5147 int ch = i_getch(input);
5148 if (ch == '`')
5149 return 1;
5150 if (ch == '\\') {
5151 /* \x. Copy both unless it is \`, \$, \\ and maybe \" */
5152 ch = i_getch(input);
5153 if (ch != '`'
5154 && ch != '$'
5155 && ch != '\\'
5156 && (!in_dquote || ch != '"')
5157 ) {
5158 o_addchr(dest, '\\');
5159 }
5160 }
5161 if (ch == EOF) {
5162 syntax_error_unterm_ch('`');
5163 return 0;
5164 }
5165 o_addchr(dest, ch);
5166 }
5167}
5168/* Process $(cmd) - copy contents until ")" is seen. Complicated by
5169 * quoting and nested ()s.
5170 * "With the $(command) style of command substitution, all characters
5171 * following the open parenthesis to the matching closing parenthesis
5172 * constitute the command. Any valid shell script can be used for command,
5173 * except a script consisting solely of redirections which produces
5174 * unspecified results."
5175 * Example Output
5176 * echo $(echo '(TEST)' BEST) (TEST) BEST
5177 * echo $(echo 'TEST)' BEST) TEST) BEST
5178 * echo $(echo \(\(TEST\) BEST) ((TEST) BEST
5179 *
5180 * Also adapted to eat ${var%...} and $((...)) constructs, since ... part
5181 * can contain arbitrary constructs, just like $(cmd).
5182 * In bash compat mode, it needs to also be able to stop on ':' or '/'
5183 * for ${var:N[:M]} and ${var/P[/R]} parsing.
5184 */
5185#define DOUBLE_CLOSE_CHAR_FLAG 0x80
5186static int add_till_closing_bracket(o_string *dest, struct in_str *input, unsigned end_ch)
5187{
5188 int ch;
5189 char dbl = end_ch & DOUBLE_CLOSE_CHAR_FLAG;
5190# if BASH_SUBSTR || BASH_PATTERN_SUBST
5191 char end_char2 = end_ch >> 8;
5192# endif
5193 end_ch &= (DOUBLE_CLOSE_CHAR_FLAG - 1);
5194
5195# if ENABLE_HUSH_INTERACTIVE
5196 G.promptmode = 1; /* PS2 */
5197# endif
5198 debug_printf_prompt("%s promptmode=%d\n", __func__, G.promptmode);
5199
5200 while (1) {
5201 ch = i_getch(input);
5202 if (ch == EOF) {
5203 syntax_error_unterm_ch(end_ch);
5204 return 0;
5205 }
5206 if (ch == end_ch
5207# if BASH_SUBSTR || BASH_PATTERN_SUBST
5208 || ch == end_char2
5209# endif
5210 ) {
5211 if (!dbl)
5212 break;
5213 /* we look for closing )) of $((EXPR)) */
5214 if (i_peek_and_eat_bkslash_nl(input) == end_ch) {
5215 i_getch(input); /* eat second ')' */
5216 break;
5217 }
5218 }
5219 o_addchr(dest, ch);
5220 //bb_error_msg("%s:o_addchr('%c')", __func__, ch);
5221 if (ch == '(' || ch == '{') {
5222 ch = (ch == '(' ? ')' : '}');
5223 if (!add_till_closing_bracket(dest, input, ch))
5224 return 0;
5225 o_addchr(dest, ch);
5226 continue;
5227 }
5228 if (ch == '\'') {
5229 if (!add_till_single_quote(dest, input))
5230 return 0;
5231 o_addchr(dest, ch);
5232 continue;
5233 }
5234 if (ch == '"') {
5235 if (!add_till_double_quote(dest, input))
5236 return 0;
5237 o_addchr(dest, ch);
5238 continue;
5239 }
5240 if (ch == '`') {
5241 if (!add_till_backquote(dest, input, /*in_dquote:*/ 0))
5242 return 0;
5243 o_addchr(dest, ch);
5244 continue;
5245 }
5246 if (ch == '\\') {
5247 /* \x. Copy verbatim. Important for \(, \) */
5248 ch = i_getch(input);
5249 if (ch == EOF) {
5250 syntax_error_unterm_ch(end_ch);
5251 return 0;
5252 }
5253# if 0
5254 if (ch == '\n') {
5255 /* "backslash+newline", ignore both */
5256 o_delchr(dest); /* undo insertion of '\' */
5257 continue;
5258 }
5259# endif
5260 o_addchr(dest, ch);
5261 //bb_error_msg("%s:o_addchr('%c') after '\\'", __func__, ch);
5262 continue;
5263 }
5264 }
5265 debug_printf_parse("%s return '%s' ch:'%c'\n", __func__, dest->data, ch);
5266 return ch;
5267}
5268#endif /* ENABLE_HUSH_TICK || ENABLE_FEATURE_SH_MATH || ENABLE_HUSH_DOLLAR_OPS */
5269
5270#if BASH_DOLLAR_SQUOTE
5271/* Return code: 1 for "found and parsed", 0 for "seen something else" */
5272# if BB_MMU
5273#define parse_dollar_squote(as_string, dest, input) \
5274 parse_dollar_squote(dest, input)
5275#define as_string NULL
5276# endif
5277static int parse_dollar_squote(o_string *as_string, o_string *dest, struct in_str *input)
5278{
5279 int start;
5280 int ch = i_peek_and_eat_bkslash_nl(input); /* first character after the $ */
5281 debug_printf_parse("parse_dollar_squote entered: ch='%c'\n", ch);
5282 if (ch != '\'')
5283 return 0;
5284
5285 dest->has_quoted_part = 1;
5286 start = dest->length;
5287
5288 ch = i_getch(input); /* eat ' */
5289 nommu_addchr(as_string, ch);
5290 while (1) {
5291 ch = i_getch(input);
5292 nommu_addchr(as_string, ch);
5293 if (ch == EOF) {
5294 syntax_error_unterm_ch('\'');
5295 return 0;
5296 }
5297 if (ch == '\'')
5298 break;
5299 if (ch == SPECIAL_VAR_SYMBOL) {
5300 /* Convert raw ^C to corresponding special variable reference */
5301 o_addchr(dest, SPECIAL_VAR_SYMBOL);
5302 o_addchr(dest, SPECIAL_VAR_QUOTED_SVS);
5303 /* will addchr() another SPECIAL_VAR_SYMBOL (see after the if() block) */
5304 } else if (ch == '\\') {
5305 static const char C_escapes[] ALIGN1 = "nrbtfav""x\\01234567";
5306
5307 ch = i_getch(input);
5308 nommu_addchr(as_string, ch);
5309 if (strchr(C_escapes, ch)) {
5310 char buf[4];
5311 char *p = buf;
5312 int cnt = 2;
5313
5314 buf[0] = ch;
5315 if ((unsigned char)(ch - '0') <= 7) { /* \ooo */
5316 do {
5317 ch = i_peek(input);
5318 if ((unsigned char)(ch - '0') > 7)
5319 break;
5320 *++p = ch = i_getch(input);
5321 nommu_addchr(as_string, ch);
5322 } while (--cnt != 0);
5323 } else if (ch == 'x') { /* \xHH */
5324 do {
5325 ch = i_peek(input);
5326 if (!isxdigit(ch))
5327 break;
5328 *++p = ch = i_getch(input);
5329 nommu_addchr(as_string, ch);
5330 } while (--cnt != 0);
5331 if (cnt == 2) { /* \x but next char is "bad" */
5332 ch = 'x';
5333 goto unrecognized;
5334 }
5335 } /* else simple seq like \\ or \t */
5336 *++p = '\0';
5337 p = buf;
5338 ch = bb_process_escape_sequence((void*)&p);
5339 //bb_error_msg("buf:'%s' ch:%x", buf, ch);
5340 if (ch == '\0')
5341 continue; /* bash compat: $'...\0...' emits nothing */
5342 } else { /* unrecognized "\z": encode both chars unless ' or " */
5343 if (ch != '\'' && ch != '"') {
5344 unrecognized:
5345 o_addqchr(dest, '\\');
5346 }
5347 }
5348 } /* if (\...) */
5349 o_addqchr(dest, ch);
5350 }
5351
5352 if (dest->length == start) {
5353 /* $'', $'\0', $'\000\x00' and the like */
5354 o_addchr(dest, SPECIAL_VAR_SYMBOL);
5355 o_addchr(dest, SPECIAL_VAR_SYMBOL);
5356 }
5357
5358 return 1;
5359# undef as_string
5360}
5361#else
5362# #define parse_dollar_squote(as_string, dest, input) 0
5363#endif /* BASH_DOLLAR_SQUOTE */
Francis Laniel8197f012023-12-22 22:02:28 +01005364#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01005365
5366/* Return code: 0 for OK, 1 for syntax error */
5367#if BB_MMU
5368#define parse_dollar(as_string, dest, input, quote_mask) \
5369 parse_dollar(dest, input, quote_mask)
5370#define as_string NULL
5371#endif
5372static int parse_dollar(o_string *as_string,
5373 o_string *dest,
5374 struct in_str *input, unsigned char quote_mask)
5375{
5376 int ch = i_peek_and_eat_bkslash_nl(input); /* first character after the $ */
5377
5378 debug_printf_parse("parse_dollar entered: ch='%c' quote_mask:0x%x\n", ch, quote_mask);
5379 if (isalpha(ch)) {
5380 make_var:
5381 ch = i_getch(input);
5382 nommu_addchr(as_string, ch);
5383 /*make_var1:*/
5384 o_addchr(dest, SPECIAL_VAR_SYMBOL);
5385 while (1) {
5386 debug_printf_parse(": '%c'\n", ch);
5387 o_addchr(dest, ch | quote_mask);
5388 quote_mask = 0;
5389 ch = i_peek_and_eat_bkslash_nl(input);
5390 if (!isalnum(ch) && ch != '_') {
5391 /* End of variable name reached */
5392 break;
5393 }
5394 ch = i_getch(input);
5395 nommu_addchr(as_string, ch);
5396 }
5397 o_addchr(dest, SPECIAL_VAR_SYMBOL);
5398 } else if (isdigit(ch)) {
5399 make_one_char_var:
5400 ch = i_getch(input);
5401 nommu_addchr(as_string, ch);
5402 o_addchr(dest, SPECIAL_VAR_SYMBOL);
5403 debug_printf_parse(": '%c'\n", ch);
5404 o_addchr(dest, ch | quote_mask);
5405 o_addchr(dest, SPECIAL_VAR_SYMBOL);
5406 } else switch (ch) {
Francis Laniel8197f012023-12-22 22:02:28 +01005407#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01005408 case '$': /* pid */
5409 case '!': /* last bg pid */
Francis Laniel8197f012023-12-22 22:02:28 +01005410#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01005411 case '?': /* last exit code */
5412 case '#': /* number of args */
5413 case '*': /* args */
5414 case '@': /* args */
5415 case '-': /* $- option flags set by set builtin or shell options (-i etc) */
5416 goto make_one_char_var;
5417 case '{': {
5418 char len_single_ch;
5419
5420 o_addchr(dest, SPECIAL_VAR_SYMBOL);
5421
5422 ch = i_getch(input); /* eat '{' */
5423 nommu_addchr(as_string, ch);
5424
5425 ch = i_getch_and_eat_bkslash_nl(input); /* first char after '{' */
5426 /* It should be ${?}, or ${#var},
5427 * or even ${?+subst} - operator acting on a special variable,
5428 * or the beginning of variable name.
5429 */
5430 if (ch == EOF
5431 || (!strchr(_SPECIAL_VARS_STR, ch) && !isalnum(ch)) /* not one of those */
5432 ) {
5433 bad_dollar_syntax:
5434 syntax_error_unterm_str("${name}");
5435 debug_printf_parse("parse_dollar return 0: unterminated ${name}\n");
5436 return 0;
5437 }
5438 nommu_addchr(as_string, ch);
5439 len_single_ch = ch;
5440 ch |= quote_mask;
5441
5442 /* It's possible to just call add_till_closing_bracket() at this point.
5443 * However, this regresses some of our testsuite cases
5444 * which check invalid constructs like ${%}.
5445 * Oh well... let's check that the var name part is fine... */
5446
5447 if (isdigit(len_single_ch)
5448 || (len_single_ch == '#' && isdigit(i_peek_and_eat_bkslash_nl(input)))
5449 ) {
5450 /* Execution engine uses plain xatoi_positive()
5451 * to interpret ${NNN} and {#NNN},
5452 * check syntax here in the parser.
5453 * (bash does not support expressions in ${#NN},
5454 * e.g. ${#$var} and {#1:+WORD} are not supported).
5455 */
5456 unsigned cnt = 9; /* max 9 digits for ${NN} and 8 for {#NN} */
5457 while (1) {
5458 o_addchr(dest, ch);
5459 debug_printf_parse(": '%c'\n", ch);
5460 ch = i_getch_and_eat_bkslash_nl(input);
5461 nommu_addchr(as_string, ch);
5462 if (ch == '}')
5463 break;
5464 if (--cnt == 0)
5465 goto bad_dollar_syntax;
5466 if (len_single_ch != '#' && strchr(VAR_SUBST_OPS, ch))
5467 /* ${NN<op>...} is valid */
5468 goto eat_until_closing;
5469 if (!isdigit(ch))
5470 goto bad_dollar_syntax;
5471 }
5472 } else
5473 while (1) {
5474 unsigned pos;
5475
5476 o_addchr(dest, ch);
5477 debug_printf_parse(": '%c'\n", ch);
5478
5479 ch = i_getch(input);
5480 nommu_addchr(as_string, ch);
5481 if (ch == '}')
5482 break;
Francis Laniel74e42542023-12-22 22:02:33 +01005483#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01005484 if (!isalnum(ch) && ch != '_') {
Francis Laniel74e42542023-12-22 22:02:33 +01005485#else /* __U_BOOT__ */
5486 /*
5487 * In several places in U-Boot, we use variable like
5488 * foo# (e.g. serial#), particularly in env.
5489 * So, we need to authorize # to appear inside
5490 * variable name and then expand this variable.
5491 * NOTE Having # in variable name is not permitted in
5492 * upstream hush but expansion will be done (even though
5493 * the result will be empty).
5494 */
5495 if (!isalnum(ch) && ch != '_' && ch != '#') {
5496#endif /* __U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01005497 unsigned end_ch;
Francis Laniel8197f012023-12-22 22:02:28 +01005498#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01005499 unsigned char last_ch;
Francis Laniel8197f012023-12-22 22:02:28 +01005500#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01005501 /* handle parameter expansions
5502 * http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_06_02
5503 */
5504 if (!strchr(VAR_SUBST_OPS, ch)) { /* ${var<bad_char>... */
5505 if (len_single_ch != '#'
5506 /*|| !strchr(SPECIAL_VARS_STR, ch) - disallow errors like ${#+} ? */
5507 || i_peek(input) != '}'
5508 ) {
5509 goto bad_dollar_syntax;
5510 }
5511 /* else: it's "length of C" ${#C} op,
5512 * where C is a single char
5513 * special var name, e.g. ${#!}.
5514 */
5515 }
5516 eat_until_closing:
5517 /* Eat everything until closing '}' (or ':') */
5518 end_ch = '}';
Francis Laniel8197f012023-12-22 22:02:28 +01005519#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01005520 if (BASH_SUBSTR
5521 && ch == ':'
5522 && !strchr(MINUS_PLUS_EQUAL_QUESTION, i_peek(input))
5523 ) {
5524 /* It's ${var:N[:M]} thing */
5525 end_ch = '}' * 0x100 + ':';
5526 }
5527 if (BASH_PATTERN_SUBST
5528 && ch == '/'
5529 ) {
5530 /* It's ${var/[/]pattern[/repl]} thing */
5531 if (i_peek(input) == '/') { /* ${var//pattern[/repl]}? */
5532 i_getch(input);
5533 nommu_addchr(as_string, '/');
5534 ch = '\\';
5535 }
5536 end_ch = '}' * 0x100 + '/';
5537 }
Francis Laniel8197f012023-12-22 22:02:28 +01005538#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01005539 o_addchr(dest, ch);
5540 /* The pattern can't be empty.
5541 * IOW: if the first char after "${v//" is a slash,
5542 * it does not terminate the pattern - it's the first char of the pattern:
5543 * v=/dev/ram; echo ${v////-} prints -dev-ram (pattern is "/")
5544 * v=/dev/ram; echo ${v///r/-} prints /dev-am (pattern is "/r")
5545 */
5546 if (i_peek(input) == '/') {
5547 o_addchr(dest, i_getch(input));
5548 }
Francis Laniel8197f012023-12-22 22:02:28 +01005549#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01005550 again:
Francis Laniel8197f012023-12-22 22:02:28 +01005551#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01005552 if (!BB_MMU)
5553 pos = dest->length;
5554#if ENABLE_HUSH_DOLLAR_OPS
Francis Laniel8197f012023-12-22 22:02:28 +01005555#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01005556 last_ch = add_till_closing_bracket(dest, input, end_ch);
5557 if (last_ch == 0) /* error? */
5558 return 0;
Francis Laniel8197f012023-12-22 22:02:28 +01005559#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01005560#else
5561# error Simple code to only allow ${var} is not implemented
5562#endif
5563 if (as_string) {
5564 o_addstr(as_string, dest->data + pos);
Francis Laniel8197f012023-12-22 22:02:28 +01005565#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01005566 o_addchr(as_string, last_ch);
Francis Laniel8197f012023-12-22 22:02:28 +01005567#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01005568 }
5569
Francis Laniel8197f012023-12-22 22:02:28 +01005570#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01005571 if ((BASH_SUBSTR || BASH_PATTERN_SUBST)
5572 && (end_ch & 0xff00)
5573 ) {
5574 /* close the first block: */
5575 o_addchr(dest, SPECIAL_VAR_SYMBOL);
5576 /* while parsing N from ${var:N[:M]}
5577 * or pattern from ${var/[/]pattern[/repl]} */
5578 if ((end_ch & 0xff) == last_ch) {
5579 /* got ':' or '/'- parse the rest */
5580 end_ch = '}';
5581 goto again;
5582 }
5583 /* got '}' */
5584 if (BASH_SUBSTR && end_ch == '}' * 0x100 + ':') {
5585 /* it's ${var:N} - emulate :999999999 */
5586 o_addstr(dest, "999999999");
5587 } /* else: it's ${var/[/]pattern} */
5588 }
Francis Laniel8197f012023-12-22 22:02:28 +01005589#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01005590 break;
5591 }
5592 len_single_ch = 0; /* it can't be ${#C} op */
5593 }
5594 o_addchr(dest, SPECIAL_VAR_SYMBOL);
5595 break;
5596 }
5597#if ENABLE_FEATURE_SH_MATH || ENABLE_HUSH_TICK
5598 case '(': {
5599 unsigned pos;
5600
5601 ch = i_getch(input);
5602 nommu_addchr(as_string, ch);
5603# if ENABLE_FEATURE_SH_MATH
5604 if (i_peek_and_eat_bkslash_nl(input) == '(') {
5605 ch = i_getch(input);
5606 nommu_addchr(as_string, ch);
5607 o_addchr(dest, SPECIAL_VAR_SYMBOL);
5608 o_addchr(dest, quote_mask | '+');
5609 if (!BB_MMU)
5610 pos = dest->length;
5611 if (!add_till_closing_bracket(dest, input, ')' | DOUBLE_CLOSE_CHAR_FLAG))
5612 return 0; /* error */
5613 if (as_string) {
5614 o_addstr(as_string, dest->data + pos);
5615 o_addchr(as_string, ')');
5616 o_addchr(as_string, ')');
5617 }
5618 o_addchr(dest, SPECIAL_VAR_SYMBOL);
5619 break;
5620 }
5621# endif
5622# if ENABLE_HUSH_TICK
5623 o_addchr(dest, SPECIAL_VAR_SYMBOL);
5624 o_addchr(dest, quote_mask | '`');
5625 if (!BB_MMU)
5626 pos = dest->length;
5627 if (!add_till_closing_bracket(dest, input, ')'))
5628 return 0; /* error */
5629 if (as_string) {
5630 o_addstr(as_string, dest->data + pos);
5631 o_addchr(as_string, ')');
5632 }
5633 o_addchr(dest, SPECIAL_VAR_SYMBOL);
5634# endif
5635 break;
5636 }
5637#endif
5638 case '_':
5639 goto make_var;
5640#if 0
5641 /* TODO: $_: */
5642 /* $_ Shell or shell script name; or last argument of last command
5643 * (if last command wasn't a pipe; if it was, bash sets $_ to "");
5644 * but in command's env, set to full pathname used to invoke it */
5645 ch = i_getch(input);
5646 nommu_addchr(as_string, ch);
5647 ch = i_peek_and_eat_bkslash_nl(input);
5648 if (isalnum(ch)) { /* it's $_name or $_123 */
5649 ch = '_';
5650 goto make_var1;
5651 }
5652 /* else: it's $_ */
5653#endif
5654 default:
5655 o_addQchr(dest, '$');
5656 }
5657 debug_printf_parse("parse_dollar return 1 (ok)\n");
5658 return 1;
5659#undef as_string
5660}
5661
5662#if BB_MMU
5663#define encode_string(as_string, dest, input, dquote_end) \
5664 encode_string(dest, input, dquote_end)
5665#define as_string NULL
5666#endif
5667static int encode_string(o_string *as_string,
5668 o_string *dest,
5669 struct in_str *input,
5670 int dquote_end)
5671{
5672 int ch;
5673 int next;
5674
5675 again:
5676 ch = i_getch(input);
5677 if (ch != EOF)
5678 nommu_addchr(as_string, ch);
5679 if (ch == dquote_end) { /* may be only '"' or EOF */
5680 debug_printf_parse("encode_string return 1 (ok)\n");
5681 return 1;
5682 }
5683 /* note: can't move it above ch == dquote_end check! */
5684 if (ch == EOF) {
5685 syntax_error_unterm_ch('"');
5686 return 0; /* error */
5687 }
5688 next = '\0';
5689 if (ch != '\n') {
5690 next = i_peek(input);
5691 }
5692 debug_printf_parse("\" ch=%c (%d) escape=%d\n",
5693 ch, ch, !!(dest->o_expflags & EXP_FLAG_ESC_GLOB_CHARS));
5694 if (ch == '\\') {
5695 if (next == EOF) {
5696 /* Testcase: in interactive shell a file with
5697 * echo "unterminated string\<eof>
5698 * is sourced.
5699 */
5700 syntax_error_unterm_ch('"');
5701 return 0; /* error */
5702 }
5703 /* bash:
5704 * "The backslash retains its special meaning [in "..."]
5705 * only when followed by one of the following characters:
5706 * $, `, ", \, or <newline>. A double quote may be quoted
5707 * within double quotes by preceding it with a backslash."
5708 * NB: in (unquoted) heredoc, above does not apply to ",
5709 * therefore we check for it by "next == dquote_end" cond.
5710 */
5711 if (next == dquote_end || strchr("$`\\\n", next)) {
5712 ch = i_getch(input); /* eat next */
5713 if (ch == '\n')
5714 goto again; /* skip \<newline> */
5715 } /* else: ch remains == '\\', and we double it below: */
5716 o_addqchr(dest, ch); /* \c if c is a glob char, else just c */
5717 nommu_addchr(as_string, ch);
5718 goto again;
5719 }
5720 if (ch == '$') {
5721 //if (parse_dollar_squote(as_string, dest, input))
5722 // goto again;
5723 if (!parse_dollar(as_string, dest, input, /*quote_mask:*/ 0x80)) {
5724 debug_printf_parse("encode_string return 0: "
5725 "parse_dollar returned 0 (error)\n");
5726 return 0;
5727 }
5728 goto again;
5729 }
5730#if ENABLE_HUSH_TICK
5731 if (ch == '`') {
5732 //unsigned pos = dest->length;
5733 o_addchr(dest, SPECIAL_VAR_SYMBOL);
5734 o_addchr(dest, 0x80 | '`');
5735 if (!add_till_backquote(dest, input, /*in_dquote:*/ dquote_end == '"'))
5736 return 0; /* error */
5737 o_addchr(dest, SPECIAL_VAR_SYMBOL);
5738 //debug_printf_subst("SUBST RES3 '%s'\n", dest->data + pos);
5739 goto again;
5740 }
5741#endif
5742 o_addQchr(dest, ch);
5743 if (ch == SPECIAL_VAR_SYMBOL) {
5744 /* Convert "^C" to corresponding special variable reference */
5745 o_addchr(dest, SPECIAL_VAR_QUOTED_SVS);
5746 o_addchr(dest, SPECIAL_VAR_SYMBOL);
5747 }
5748 goto again;
5749#undef as_string
5750}
5751
5752/*
5753 * Scan input until EOF or end_trigger char.
5754 * Return a list of pipes to execute, or NULL on EOF
5755 * or if end_trigger character is met.
5756 * On syntax error, exit if shell is not interactive,
5757 * reset parsing machinery and start parsing anew,
5758 * or return ERR_PTR.
5759 */
5760static struct pipe *parse_stream(char **pstring,
5761 int *heredoc_cnt_ptr,
5762 struct in_str *input,
5763 int end_trigger)
5764{
5765 struct parse_context ctx;
5766 int heredoc_cnt;
5767
5768 /* Single-quote triggers a bypass of the main loop until its mate is
5769 * found. When recursing, quote state is passed in via ctx.word.o_expflags.
5770 */
5771 debug_printf_parse("parse_stream entered, end_trigger='%c'\n",
5772 end_trigger ? end_trigger : 'X');
5773 debug_enter();
5774
5775 initialize_context(&ctx);
5776
5777 /* If very first arg is "" or '', ctx.word.data may end up NULL.
5778 * Preventing this:
5779 */
5780 ctx.word.data = xzalloc(1); /* start as "", not as NULL */
5781
5782 /* We used to separate words on $IFS here. This was wrong.
5783 * $IFS is used only for word splitting when $var is expanded,
5784 * here we should use blank chars as separators, not $IFS
5785 */
5786
5787 heredoc_cnt = 0;
5788 while (1) {
5789 const char *is_blank;
5790 const char *is_special;
5791 int ch;
5792 int next;
Francis Laniel8197f012023-12-22 22:02:28 +01005793#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01005794 int redir_fd;
5795 redir_type redir_style;
Francis Laniel8197f012023-12-22 22:02:28 +01005796#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01005797
5798 ch = i_getch(input);
5799 debug_printf_parse(": ch=%c (%d) escape=%d\n",
5800 ch, ch, !!(ctx.word.o_expflags & EXP_FLAG_ESC_GLOB_CHARS));
5801 if (ch == EOF) {
5802 struct pipe *pi;
5803
5804 if (heredoc_cnt) {
5805 syntax_error_unterm_str("here document");
5806 goto parse_error_exitcode1;
5807 }
5808 if (end_trigger == ')') {
5809 syntax_error_unterm_ch('(');
5810 goto parse_error_exitcode1;
5811 }
5812 if (end_trigger == '}') {
5813 syntax_error_unterm_ch('{');
5814 goto parse_error_exitcode1;
5815 }
5816
5817 if (done_word(&ctx)) {
5818 goto parse_error_exitcode1;
5819 }
5820 o_free_and_set_NULL(&ctx.word);
5821 done_pipe(&ctx, PIPE_SEQ);
5822 pi = ctx.list_head;
5823 /* If we got nothing... */
5824 /* (this makes bare "&" cmd a no-op.
5825 * bash says: "syntax error near unexpected token '&'") */
5826 if (pi->num_cmds == 0
5827 IF_HAS_KEYWORDS(&& pi->res_word == RES_NONE)
5828 ) {
5829 free_pipe_list(pi);
5830 pi = NULL;
5831 }
5832#if !BB_MMU
5833 debug_printf_parse("as_string1 '%s'\n", ctx.as_string.data);
5834 if (pstring)
5835 *pstring = ctx.as_string.data;
5836 else
5837 o_free(&ctx.as_string);
5838#endif
5839 // heredoc_cnt must be 0 here anyway
5840 //if (heredoc_cnt_ptr)
5841 // *heredoc_cnt_ptr = heredoc_cnt;
5842 debug_leave();
5843 debug_printf_heredoc("parse_stream return heredoc_cnt:%d\n", heredoc_cnt);
5844 debug_printf_parse("parse_stream return %p\n", pi);
5845 return pi;
5846 }
5847
5848 /* Handle "'" and "\" first, as they won't play nice with
5849 * i_peek_and_eat_bkslash_nl() anyway:
5850 * echo z\\
5851 * and
5852 * echo '\
5853 * '
5854 * would break.
5855 */
5856 if (ch == '\\') {
5857 ch = i_getch(input);
5858 if (ch == '\n')
5859 continue; /* drop \<newline>, get next char */
5860 nommu_addchr(&ctx.as_string, '\\');
5861 if (ch == SPECIAL_VAR_SYMBOL) {
5862 nommu_addchr(&ctx.as_string, ch);
5863 /* Convert \^C to corresponding special variable reference */
5864 goto case_SPECIAL_VAR_SYMBOL;
5865 }
5866 o_addchr(&ctx.word, '\\');
5867 if (ch == EOF) {
5868 /* Testcase: eval 'echo Ok\' */
5869 /* bash-4.3.43 was removing backslash,
5870 * but 4.4.19 retains it, most other shells too
5871 */
5872 continue; /* get next char */
5873 }
5874 /* Example: echo Hello \2>file
5875 * we need to know that word 2 is quoted
5876 */
5877 ctx.word.has_quoted_part = 1;
5878 nommu_addchr(&ctx.as_string, ch);
5879 o_addchr(&ctx.word, ch);
5880 continue; /* get next char */
5881 }
5882 nommu_addchr(&ctx.as_string, ch);
5883 if (ch == '\'') {
5884 ctx.word.has_quoted_part = 1;
5885 next = i_getch(input);
Francis Laniel8197f012023-12-22 22:02:28 +01005886#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01005887 if (next == '\'' && !ctx.pending_redirect)
5888 goto insert_empty_quoted_str_marker;
Francis Laniel8197f012023-12-22 22:02:28 +01005889#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01005890
5891 ch = next;
5892 while (1) {
5893 if (ch == EOF) {
5894 syntax_error_unterm_ch('\'');
5895 goto parse_error_exitcode1;
5896 }
5897 nommu_addchr(&ctx.as_string, ch);
5898 if (ch == '\'')
5899 break;
5900 if (ch == SPECIAL_VAR_SYMBOL) {
5901 /* Convert raw ^C to corresponding special variable reference */
5902 o_addchr(&ctx.word, SPECIAL_VAR_SYMBOL);
5903 o_addchr(&ctx.word, SPECIAL_VAR_QUOTED_SVS);
5904 }
5905 o_addqchr(&ctx.word, ch);
5906 ch = i_getch(input);
5907 }
5908 continue; /* get next char */
5909 }
5910
5911 next = '\0';
5912 if (ch != '\n')
5913 next = i_peek_and_eat_bkslash_nl(input);
5914
5915 is_special = "{}<>&|();#" /* special outside of "str" */
Francis Laniel8197f012023-12-22 22:02:28 +01005916#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01005917 "$\"" IF_HUSH_TICK("`") /* always special */
Francis Laniel8197f012023-12-22 22:02:28 +01005918#else /* __U_BOOT__ */
5919 "$\""
5920#endif /* __U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01005921 SPECIAL_VAR_SYMBOL_STR;
5922#if defined(CMD_TEST2_SINGLEWORD_NOGLOB)
5923 if (ctx.command->cmd_type == CMD_TEST2_SINGLEWORD_NOGLOB) {
5924 /* In [[ ]], {}<>&|() are not special */
5925 is_special += 8;
5926 } else
5927#endif
5928 /* Are { and } special here? */
5929 if (ctx.command->argv /* word [word]{... - non-special */
5930 || ctx.word.length /* word{... - non-special */
5931 || ctx.word.has_quoted_part /* ""{... - non-special */
5932 || (next != ';' /* }; - special */
5933 && next != ')' /* }) - special */
5934 && next != '(' /* {( - special */
5935 && next != '&' /* }& and }&& ... - special */
5936 && next != '|' /* }|| ... - special */
5937 && !strchr(defifs, next) /* {word - non-special */
5938 )
5939 ) {
5940 /* They are not special, skip "{}" */
5941 is_special += 2;
5942 }
5943 is_special = strchr(is_special, ch);
5944 is_blank = strchr(defifs, ch);
5945
5946 if (!is_special && !is_blank) { /* ordinary char */
5947 ordinary_char:
5948 o_addQchr(&ctx.word, ch);
5949 if ((ctx.is_assignment == MAYBE_ASSIGNMENT
5950 || ctx.is_assignment == WORD_IS_KEYWORD)
5951 && ch == '='
5952 && endofname(ctx.word.data)[0] == '='
5953 ) {
5954 ctx.is_assignment = DEFINITELY_ASSIGNMENT;
5955 debug_printf_parse("ctx.is_assignment='%s'\n", assignment_flag[ctx.is_assignment]);
5956 }
5957 continue;
5958 }
5959
5960 if (is_blank) {
5961#if ENABLE_HUSH_LINENO_VAR
5962/* Case:
5963 * "while ...; do<whitespace><newline>
5964 * cmd ..."
5965 * would think that "cmd" starts in <whitespace> -
5966 * i.e., at the previous line.
5967 * We need to skip all whitespace before newlines.
5968 */
5969 while (ch != '\n') {
5970 next = i_peek(input);
5971 if (next != ' ' && next != '\t' && next != '\n')
5972 break; /* next char is not ws */
5973 ch = i_getch(input);
5974 }
5975 /* ch == last eaten whitespace char */
5976#endif
5977 if (done_word(&ctx)) {
5978 goto parse_error_exitcode1;
5979 }
5980 if (ch == '\n') {
5981 /* Is this a case when newline is simply ignored?
5982 * Some examples:
5983 * "cmd | <newline> cmd ..."
5984 * "case ... in <newline> word) ..."
5985 */
5986 if (IS_NULL_CMD(ctx.command)
5987 && ctx.word.length == 0
5988 && !ctx.word.has_quoted_part
5989 && heredoc_cnt == 0
5990 ) {
5991 /* This newline can be ignored. But...
5992 * Without check #1, interactive shell
5993 * ignores even bare <newline>,
5994 * and shows the continuation prompt:
5995 * ps1_prompt$ <enter>
5996 * ps2> _ <=== wrong, should be ps1
5997 * Without check #2, "cmd & <newline>"
5998 * is similarly mistreated.
5999 * (BTW, this makes "cmd & cmd"
6000 * and "cmd && cmd" non-orthogonal.
6001 * Really, ask yourself, why
6002 * "cmd && <newline>" doesn't start
6003 * cmd but waits for more input?
6004 * The only reason is that it might be
6005 * a "cmd1 && <nl> cmd2 &" construct,
6006 * cmd1 may need to run in BG).
6007 */
6008 struct pipe *pi = ctx.list_head;
6009 if (pi->num_cmds != 0 /* check #1 */
6010 && pi->followup != PIPE_BG /* check #2 */
6011 ) {
6012 continue;
6013 }
6014 }
6015 /* Treat newline as a command separator. */
6016 done_pipe(&ctx, PIPE_SEQ);
6017 debug_printf_heredoc("heredoc_cnt:%d\n", heredoc_cnt);
6018 if (heredoc_cnt) {
6019 heredoc_cnt = fetch_heredocs(&ctx.as_string, ctx.list_head, heredoc_cnt, input);
6020 if (heredoc_cnt != 0)
6021 goto parse_error_exitcode1;
6022 }
6023 ctx.is_assignment = MAYBE_ASSIGNMENT;
6024 debug_printf_parse("ctx.is_assignment='%s'\n", assignment_flag[ctx.is_assignment]);
6025 ch = ';';
6026 /* note: if (is_blank) continue;
6027 * will still trigger for us */
6028 }
6029 }
6030
6031 /* "cmd}" or "cmd }..." without semicolon or &:
6032 * } is an ordinary char in this case, even inside { cmd; }
6033 * Pathological example: { ""}; } should exec "}" cmd
6034 */
Francis Laniel8197f012023-12-22 22:02:28 +01006035#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01006036 if (ch == '}') {
Francis Laniel8197f012023-12-22 22:02:28 +01006037#else /* __U_BOOT__ */
6038 if (ch == '}' || ch == ')') {
6039#endif /* __U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01006040 if (ctx.word.length != 0 /* word} */
6041 || ctx.word.has_quoted_part /* ""} */
6042 ) {
6043 goto ordinary_char;
6044 }
6045 if (!IS_NULL_CMD(ctx.command)) { /* cmd } */
6046 /* Generally, there should be semicolon: "cmd; }"
6047 * However, bash allows to omit it if "cmd" is
6048 * a group. Examples:
6049 * { { echo 1; } }
6050 * {(echo 1)}
6051 * { echo 0 >&2 | { echo 1; } }
6052 * { while false; do :; done }
6053 * { case a in b) ;; esac }
6054 */
6055 if (ctx.command->group)
6056 goto term_group;
6057 goto ordinary_char;
6058 }
6059 if (!IS_NULL_PIPE(ctx.pipe)) /* cmd | } */
6060 /* Can't be an end of {cmd}, skip the check */
6061 goto skip_end_trigger;
6062 /* else: } does terminate a group */
6063 }
6064 term_group:
6065 if (end_trigger && end_trigger == ch
6066 && (ch != ';' || heredoc_cnt == 0)
6067#if ENABLE_HUSH_CASE
6068 && (ch != ')'
6069 || ctx.ctx_res_w != RES_MATCH
6070 || (!ctx.word.has_quoted_part && strcmp(ctx.word.data, "esac") == 0)
6071 )
6072#endif
6073 ) {
6074 if (done_word(&ctx)) {
6075 goto parse_error_exitcode1;
6076 }
6077 done_pipe(&ctx, PIPE_SEQ);
6078 ctx.is_assignment = MAYBE_ASSIGNMENT;
6079 debug_printf_parse("ctx.is_assignment='%s'\n", assignment_flag[ctx.is_assignment]);
6080 /* Do we sit outside of any if's, loops or case's? */
6081 if (!HAS_KEYWORDS
6082 IF_HAS_KEYWORDS(|| (ctx.ctx_res_w == RES_NONE && ctx.old_flag == 0))
6083 ) {
6084 o_free_and_set_NULL(&ctx.word);
6085#if !BB_MMU
6086 debug_printf_parse("as_string2 '%s'\n", ctx.as_string.data);
6087 if (pstring)
6088 *pstring = ctx.as_string.data;
6089 else
6090 o_free(&ctx.as_string);
6091#endif
6092 if (ch != ';' && IS_NULL_PIPE(ctx.list_head)) {
6093 /* Example: bare "{ }", "()" */
6094 G.last_exitcode = 2; /* bash compat */
6095 syntax_error_unexpected_ch(ch);
6096 goto parse_error;
6097 }
6098 if (heredoc_cnt_ptr)
6099 *heredoc_cnt_ptr = heredoc_cnt;
6100 debug_printf_heredoc("parse_stream return heredoc_cnt:%d\n", heredoc_cnt);
6101 debug_printf_parse("parse_stream return %p: "
6102 "end_trigger char found\n",
6103 ctx.list_head);
6104 debug_leave();
6105 return ctx.list_head;
6106 }
6107 }
6108
6109 if (is_blank)
6110 continue;
6111
6112 /* Catch <, > before deciding whether this word is
6113 * an assignment. a=1 2>z b=2: b=2 is still assignment */
6114 switch (ch) {
Francis Laniel8197f012023-12-22 22:02:28 +01006115#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01006116 case '>':
6117 redir_fd = redirect_opt_num(&ctx.word);
6118 if (done_word(&ctx)) {
6119 goto parse_error_exitcode1;
6120 }
6121 redir_style = REDIRECT_OVERWRITE;
6122 if (next == '>') {
6123 redir_style = REDIRECT_APPEND;
6124 ch = i_getch(input);
6125 nommu_addchr(&ctx.as_string, ch);
6126 }
6127#if 0
6128 else if (next == '(') {
6129 syntax_error(">(process) not supported");
6130 goto parse_error_exitcode1;
6131 }
6132#endif
6133 if (parse_redirect(&ctx, redir_fd, redir_style, input))
6134 goto parse_error_exitcode1;
6135 continue; /* get next char */
6136 case '<':
6137 redir_fd = redirect_opt_num(&ctx.word);
6138 if (done_word(&ctx)) {
6139 goto parse_error_exitcode1;
6140 }
6141 redir_style = REDIRECT_INPUT;
6142 if (next == '<') {
6143 redir_style = REDIRECT_HEREDOC;
6144 heredoc_cnt++;
6145 debug_printf_heredoc("++heredoc_cnt=%d\n", heredoc_cnt);
6146 ch = i_getch(input);
6147 nommu_addchr(&ctx.as_string, ch);
6148 } else if (next == '>') {
6149 redir_style = REDIRECT_IO;
6150 ch = i_getch(input);
6151 nommu_addchr(&ctx.as_string, ch);
6152 }
6153#if 0
6154 else if (next == '(') {
6155 syntax_error("<(process) not supported");
6156 goto parse_error_exitcode1;
6157 }
6158#endif
6159 if (parse_redirect(&ctx, redir_fd, redir_style, input))
6160 goto parse_error_exitcode1;
6161 continue; /* get next char */
Francis Lanield8b25632023-12-22 22:02:38 +01006162#else /* __U_BOOT__ */
6163 /*
6164 * In U-Boot, '<' and '>' can be used in test command to test if
6165 * a string is, alphabetically, before or after another.
6166 * In 2021 Busybox hush, we will keep the same behavior and so not treat
6167 * them as redirection operator.
6168 *
6169 * Indeed, in U-Boot, tests are handled by the test command and not by the
6170 * shell code.
6171 * So, better to give this character as input to test command.
6172 *
6173 * NOTE In my opinion, when you use '<' or '>' I am almost sure
6174 * you wanted to use "-gt" or "-lt" in place, so thinking to
6175 * escape these will make you should check your code (sh syntax
6176 * at this level is, for me, error prone).
6177 */
6178 case '>':
6179 fallthrough;
6180 case '<':
6181 o_addQchr(&ctx.word, ch);
6182 continue;
6183#endif /* __U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01006184 case '#':
6185 if (ctx.word.length == 0 && !ctx.word.has_quoted_part) {
6186 /* skip "#comment" */
6187 /* note: we do not add it to &ctx.as_string */
6188/* TODO: in bash:
6189 * comment inside $() goes to the next \n, even inside quoted string (!):
6190 * cmd "$(cmd2 #comment)" - syntax error
6191 * cmd "`cmd2 #comment`" - ok
6192 * We accept both (comment ends where command subst ends, in both cases).
6193 */
6194 while (1) {
6195 ch = i_peek(input);
6196 if (ch == '\n') {
6197 nommu_addchr(&ctx.as_string, '\n');
6198 break;
6199 }
6200 ch = i_getch(input);
6201 if (ch == EOF)
6202 break;
6203 }
6204 continue; /* get next char */
6205 }
6206 break;
6207 }
6208 skip_end_trigger:
6209
6210 if (ctx.is_assignment == MAYBE_ASSIGNMENT
Francis Laniel8197f012023-12-22 22:02:28 +01006211#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01006212 /* check that we are not in word in "a=1 2>word b=1": */
6213 && !ctx.pending_redirect
Francis Laniel8197f012023-12-22 22:02:28 +01006214#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01006215 ) {
6216 /* ch is a special char and thus this word
6217 * cannot be an assignment */
6218 ctx.is_assignment = NOT_ASSIGNMENT;
6219 debug_printf_parse("ctx.is_assignment='%s'\n", assignment_flag[ctx.is_assignment]);
6220 }
6221
6222 /* Note: nommu_addchr(&ctx.as_string, ch) is already done */
6223
6224 switch (ch) {
6225 case_SPECIAL_VAR_SYMBOL:
6226 case SPECIAL_VAR_SYMBOL:
6227 /* Convert raw ^C to corresponding special variable reference */
6228 o_addchr(&ctx.word, SPECIAL_VAR_SYMBOL);
6229 o_addchr(&ctx.word, SPECIAL_VAR_QUOTED_SVS);
6230 /* fall through */
6231 case '#':
6232 /* non-comment #: "echo a#b" etc */
6233 o_addchr(&ctx.word, ch);
6234 continue; /* get next char */
6235 case '$':
Francis Laniel8197f012023-12-22 22:02:28 +01006236#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01006237 if (parse_dollar_squote(&ctx.as_string, &ctx.word, input))
6238 continue; /* get next char */
Francis Laniel8197f012023-12-22 22:02:28 +01006239#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01006240 if (!parse_dollar(&ctx.as_string, &ctx.word, input, /*quote_mask:*/ 0)) {
6241 debug_printf_parse("parse_stream parse error: "
6242 "parse_dollar returned 0 (error)\n");
6243 goto parse_error_exitcode1;
6244 }
6245 continue; /* get next char */
6246 case '"':
6247 ctx.word.has_quoted_part = 1;
Francis Laniel8197f012023-12-22 22:02:28 +01006248#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01006249 if (next == '"' && !ctx.pending_redirect) {
Francis Laniel8197f012023-12-22 22:02:28 +01006250#else /* __U_BOOT__ */
6251 if (next == '"') {
6252#endif /* __U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01006253 i_getch(input); /* eat second " */
Francis Laniel8197f012023-12-22 22:02:28 +01006254#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01006255 insert_empty_quoted_str_marker:
Francis Laniel8197f012023-12-22 22:02:28 +01006256#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01006257 nommu_addchr(&ctx.as_string, next);
6258 o_addchr(&ctx.word, SPECIAL_VAR_SYMBOL);
6259 o_addchr(&ctx.word, SPECIAL_VAR_SYMBOL);
6260 continue; /* get next char */
6261 }
6262 if (ctx.is_assignment == NOT_ASSIGNMENT)
6263 ctx.word.o_expflags |= EXP_FLAG_ESC_GLOB_CHARS;
6264 if (!encode_string(&ctx.as_string, &ctx.word, input, '"'))
6265 goto parse_error_exitcode1;
6266 ctx.word.o_expflags &= ~EXP_FLAG_ESC_GLOB_CHARS;
6267 continue; /* get next char */
6268#if ENABLE_HUSH_TICK
6269 case '`': {
6270 USE_FOR_NOMMU(unsigned pos;)
6271
6272 o_addchr(&ctx.word, SPECIAL_VAR_SYMBOL);
6273 o_addchr(&ctx.word, '`');
6274 USE_FOR_NOMMU(pos = ctx.word.length;)
6275 if (!add_till_backquote(&ctx.word, input, /*in_dquote:*/ 0))
6276 goto parse_error_exitcode1;
6277# if !BB_MMU
6278 o_addstr(&ctx.as_string, ctx.word.data + pos);
6279 o_addchr(&ctx.as_string, '`');
6280# endif
6281 o_addchr(&ctx.word, SPECIAL_VAR_SYMBOL);
6282 //debug_printf_subst("SUBST RES3 '%s'\n", ctx.word.data + pos);
6283 continue; /* get next char */
6284 }
6285#endif
6286 case ';':
6287#if ENABLE_HUSH_CASE
6288 case_semi:
6289#endif
6290 if (done_word(&ctx)) {
6291 goto parse_error_exitcode1;
6292 }
6293 done_pipe(&ctx, PIPE_SEQ);
6294#if ENABLE_HUSH_CASE
6295 /* Eat multiple semicolons, detect
6296 * whether it means something special */
6297 while (1) {
6298 ch = i_peek_and_eat_bkslash_nl(input);
6299 if (ch != ';')
6300 break;
6301 ch = i_getch(input);
6302 nommu_addchr(&ctx.as_string, ch);
6303 if (ctx.ctx_res_w == RES_CASE_BODY) {
6304 ctx.ctx_dsemicolon = 1;
6305 ctx.ctx_res_w = RES_MATCH;
6306 break;
6307 }
6308 }
6309#endif
6310 new_cmd:
6311 /* We just finished a cmd. New one may start
6312 * with an assignment */
6313 ctx.is_assignment = MAYBE_ASSIGNMENT;
6314 debug_printf_parse("ctx.is_assignment='%s'\n", assignment_flag[ctx.is_assignment]);
6315 continue; /* get next char */
6316 case '&':
6317 if (done_word(&ctx)) {
6318 goto parse_error_exitcode1;
6319 }
6320 if (next == '&') {
6321 ch = i_getch(input);
6322 nommu_addchr(&ctx.as_string, ch);
6323 done_pipe(&ctx, PIPE_AND);
6324 } else {
6325 done_pipe(&ctx, PIPE_BG);
6326 }
6327 goto new_cmd;
6328 case '|':
6329 if (done_word(&ctx)) {
6330 goto parse_error_exitcode1;
6331 }
6332#if ENABLE_HUSH_CASE
6333 if (ctx.ctx_res_w == RES_MATCH)
6334 break; /* we are in case's "word | word)" */
6335#endif
6336 if (next == '|') { /* || */
6337 ch = i_getch(input);
6338 nommu_addchr(&ctx.as_string, ch);
6339 done_pipe(&ctx, PIPE_OR);
6340 } else {
6341 /* we could pick up a file descriptor choice here
6342 * with redirect_opt_num(), but bash doesn't do it.
6343 * "echo foo 2| cat" yields "foo 2". */
6344 done_command(&ctx);
6345 }
6346 goto new_cmd;
6347 case '(':
6348#if ENABLE_HUSH_CASE
6349 /* "case... in [(]word)..." - skip '(' */
6350 if (ctx.ctx_res_w == RES_MATCH
6351 && ctx.command->argv == NULL /* not (word|(... */
6352 && ctx.word.length == 0 /* not word(... */
6353 && ctx.word.has_quoted_part == 0 /* not ""(... */
6354 ) {
6355 continue; /* get next char */
6356 }
6357#endif
6358 /* fall through */
6359 case '{': {
6360 int n = parse_group(&ctx, input, ch);
6361 if (n < 0) {
6362 goto parse_error_exitcode1;
6363 }
6364 debug_printf_heredoc("parse_group done, needs heredocs:%d\n", n);
6365 heredoc_cnt += n;
6366 goto new_cmd;
6367 }
6368 case ')':
6369#if ENABLE_HUSH_CASE
6370 if (ctx.ctx_res_w == RES_MATCH)
6371 goto case_semi;
6372#endif
6373 case '}':
6374 /* proper use of this character is caught by end_trigger:
6375 * if we see {, we call parse_group(..., end_trigger='}')
6376 * and it will match } earlier (not here). */
6377 G.last_exitcode = 2;
6378 syntax_error_unexpected_ch(ch);
6379 goto parse_error;
6380 default:
6381 if (HUSH_DEBUG)
6382 bb_error_msg_and_die("BUG: unexpected %c", ch);
6383 }
6384 } /* while (1) */
6385
6386 parse_error_exitcode1:
6387 G.last_exitcode = 1;
6388 parse_error:
6389 {
6390 struct parse_context *pctx;
6391 IF_HAS_KEYWORDS(struct parse_context *p2;)
6392
6393 /* Clean up allocated tree.
6394 * Sample for finding leaks on syntax error recovery path.
6395 * Run it from interactive shell, watch pmap `pidof hush`.
6396 * while if false; then false; fi; do break; fi
6397 * Samples to catch leaks at execution:
6398 * while if (true | { true;}); then echo ok; fi; do break; done
6399 * while if (true | { true;}); then echo ok; fi; do (if echo ok; break; then :; fi) | cat; break; done
6400 */
6401 pctx = &ctx;
6402 do {
6403 /* Update pipe/command counts,
6404 * otherwise freeing may miss some */
6405 done_pipe(pctx, PIPE_SEQ);
6406 debug_printf_clean("freeing list %p from ctx %p\n",
6407 pctx->list_head, pctx);
6408 debug_print_tree(pctx->list_head, 0);
6409 free_pipe_list(pctx->list_head);
6410 debug_printf_clean("freed list %p\n", pctx->list_head);
6411#if !BB_MMU
6412 o_free(&pctx->as_string);
6413#endif
6414 IF_HAS_KEYWORDS(p2 = pctx->stack;)
6415 if (pctx != &ctx) {
6416 free(pctx);
6417 }
6418 IF_HAS_KEYWORDS(pctx = p2;)
6419 } while (HAS_KEYWORDS && pctx);
6420
6421 o_free(&ctx.word);
6422#if !BB_MMU
6423 if (pstring)
6424 *pstring = NULL;
6425#endif
6426 debug_leave();
6427 return ERR_PTR;
6428 }
6429}
6430
6431
6432/*** Execution routines ***/
6433
6434/* Expansion can recurse, need forward decls: */
6435#if !BASH_PATTERN_SUBST && !ENABLE_HUSH_CASE
6436#define expand_string_to_string(str, EXP_flags, do_unbackslash) \
6437 expand_string_to_string(str)
6438#endif
6439static char *expand_string_to_string(const char *str, int EXP_flags, int do_unbackslash);
6440#if ENABLE_HUSH_TICK
6441static int process_command_subs(o_string *dest, const char *s);
6442#endif
6443static int expand_vars_to_list(o_string *output, int n, char *arg);
6444
6445/* expand_strvec_to_strvec() takes a list of strings, expands
6446 * all variable references within and returns a pointer to
6447 * a list of expanded strings, possibly with larger number
6448 * of strings. (Think VAR="a b"; echo $VAR).
6449 * This new list is allocated as a single malloc block.
6450 * NULL-terminated list of char* pointers is at the beginning of it,
6451 * followed by strings themselves.
6452 * Caller can deallocate entire list by single free(list). */
6453
6454/* A horde of its helpers come first: */
6455
6456static void o_addblock_duplicate_backslash(o_string *o, const char *str, int len)
6457{
6458 while (--len >= 0) {
6459 char c = *str++;
6460
6461#if ENABLE_HUSH_BRACE_EXPANSION
6462 if (c == '{' || c == '}') {
6463 /* { -> \{, } -> \} */
6464 o_addchr(o, '\\');
6465 /* And now we want to add { or } and continue:
6466 * o_addchr(o, c);
6467 * continue;
6468 * luckily, just falling through achieves this.
6469 */
6470 }
6471#endif
6472 o_addchr(o, c);
6473 if (c == '\\') {
6474 /* \z -> \\\z; \<eol> -> \\<eol> */
6475 o_addchr(o, '\\');
6476 if (len) {
6477 len--;
6478 o_addchr(o, '\\');
6479 o_addchr(o, *str++);
6480 }
6481 }
6482 }
6483}
6484
6485/* Store given string, finalizing the word and starting new one whenever
6486 * we encounter IFS char(s). This is used for expanding variable values.
6487 * End-of-string does NOT finalize word: think about 'echo -$VAR-'.
6488 * Return in output->ended_in_ifs:
6489 * 1 - ended with IFS char, else 0 (this includes case of empty str).
6490 */
6491static int expand_on_ifs(o_string *output, int n, const char *str)
6492{
6493 int last_is_ifs = 0;
6494
6495 while (1) {
6496 int word_len;
6497
6498 if (!*str) /* EOL - do not finalize word */
6499 break;
6500 word_len = strcspn(str, G.ifs);
6501 if (word_len) {
6502 /* We have WORD_LEN leading non-IFS chars */
6503 if (!(output->o_expflags & EXP_FLAG_GLOB)) {
6504 o_addblock(output, str, word_len);
6505 } else {
6506 /* Protect backslashes against globbing up :)
6507 * Example: "v='\*'; echo b$v" prints "b\*"
6508 * (and does not try to glob on "*")
6509 */
6510 o_addblock_duplicate_backslash(output, str, word_len);
6511 /*/ Why can't we do it easier? */
6512 /*o_addblock(output, str, word_len); - WRONG: "v='\*'; echo Z$v" prints "Z*" instead of "Z\*" */
6513 /*o_addqblock(output, str, word_len); - WRONG: "v='*'; echo Z$v" prints "Z*" instead of Z* files */
6514 }
6515 last_is_ifs = 0;
6516 str += word_len;
6517 if (!*str) /* EOL - do not finalize word */
6518 break;
6519 }
6520
6521 /* We know str here points to at least one IFS char */
6522 last_is_ifs = 1;
6523 str += strspn(str, G.ifs_whitespace); /* skip IFS whitespace chars */
6524 if (!*str) /* EOL - do not finalize word */
6525 break;
6526
6527 if (G.ifs_whitespace != G.ifs /* usually false ($IFS is usually all whitespace), */
6528 && strchr(G.ifs, *str) /* the second check would fail */
6529 ) {
6530 /* This is a non-whitespace $IFS char */
6531 /* Skip it and IFS whitespace chars, start new word */
6532 str++;
6533 str += strspn(str, G.ifs_whitespace);
6534 goto new_word;
6535 }
6536
6537 /* Start new word... but not always! */
6538 /* Case "v=' a'; echo ''$v": we do need to finalize empty word: */
6539 if (output->has_quoted_part
6540 /*
6541 * Case "v=' a'; echo $v":
6542 * here nothing precedes the space in $v expansion,
6543 * therefore we should not finish the word
6544 * (IOW: if there *is* word to finalize, only then do it):
6545 * It's okay if this accesses the byte before first argv[]:
6546 * past call to o_save_ptr() cleared it to zero byte
6547 * (grep for -prev-ifs-check-).
6548 */
6549 || output->data[output->length - 1]
6550 ) {
6551 new_word:
6552 o_addchr(output, '\0');
6553 debug_print_list("expand_on_ifs", output, n);
6554 n = o_save_ptr(output, n);
6555 }
6556 }
6557
6558 output->ended_in_ifs = last_is_ifs;
6559 debug_print_list("expand_on_ifs[1]", output, n);
6560 return n;
6561}
6562
Francis Laniel8197f012023-12-22 22:02:28 +01006563#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01006564/* Helper to expand $((...)) and heredoc body. These act as if
6565 * they are in double quotes, with the exception that they are not :).
6566 * Just the rules are similar: "expand only $var and `cmd`"
6567 *
6568 * Returns malloced string.
6569 * As an optimization, we return NULL if expansion is not needed.
6570 */
6571static char *encode_then_expand_string(const char *str)
6572{
6573 char *exp_str;
6574 struct in_str input;
6575 o_string dest = NULL_O_STRING;
6576 const char *cp;
6577
6578 cp = str;
6579 for (;;) {
6580 if (!*cp) return NULL; /* string has no special chars */
6581 if (*cp == '$') break;
6582 if (*cp == '\\') break;
6583#if ENABLE_HUSH_TICK
6584 if (*cp == '`') break;
6585#endif
6586 cp++;
6587 }
6588
6589 /* We need to expand. Example:
6590 * echo $(($a + `echo 1`)) $((1 + $((2)) ))
6591 */
6592 setup_string_in_str(&input, str);
6593 encode_string(NULL, &dest, &input, EOF);
6594//TODO: error check (encode_string returns 0 on error)?
6595 //bb_error_msg("'%s' -> '%s'", str, dest.data);
6596 exp_str = expand_string_to_string(dest.data,
6597 EXP_FLAG_ESC_GLOB_CHARS,
6598 /*unbackslash:*/ 1
6599 );
6600 //bb_error_msg("'%s' -> '%s'", dest.data, exp_str);
6601 o_free(&dest);
6602 return exp_str;
6603}
6604
6605static const char *first_special_char_in_vararg(const char *cp)
6606{
6607 for (;;) {
6608 if (!*cp) return NULL; /* string has no special chars */
6609 if (*cp == '$') return cp;
6610 if (*cp == '\\') return cp;
6611 if (*cp == '\'') return cp;
6612 if (*cp == '"') return cp;
6613#if ENABLE_HUSH_TICK
6614 if (*cp == '`') return cp;
6615#endif
6616 /* dquoted "${x:+ARG}" should not glob, therefore
6617 * '*' et al require some non-literal processing: */
6618 if (*cp == '*') return cp;
6619 if (*cp == '?') return cp;
6620 if (*cp == '[') return cp;
6621 cp++;
6622 }
6623}
6624
6625/* Expanding ARG in ${var#ARG}, ${var%ARG}, or ${var/ARG/ARG}.
6626 * These can contain single- and double-quoted strings,
6627 * and treated as if the ARG string is initially unquoted. IOW:
6628 * ${var#ARG} and "${var#ARG}" treat ARG the same (ARG can even be
6629 * a dquoted string: "${var#"zz"}"), the difference only comes later
6630 * (word splitting and globbing of the ${var...} result).
6631 */
6632#if !BASH_PATTERN_SUBST
6633#define encode_then_expand_vararg(str, handle_squotes, do_unbackslash) \
6634 encode_then_expand_vararg(str, handle_squotes)
6635#endif
6636static char *encode_then_expand_vararg(const char *str, int handle_squotes, int do_unbackslash)
6637{
6638#if !BASH_PATTERN_SUBST && ENABLE_HUSH_CASE
6639 const int do_unbackslash = 0;
6640#endif
6641 char *exp_str;
6642 struct in_str input;
6643 o_string dest = NULL_O_STRING;
6644
6645 if (!first_special_char_in_vararg(str)) {
6646 /* string has no special chars */
6647 return NULL;
6648 }
6649
6650 setup_string_in_str(&input, str);
6651 dest.data = xzalloc(1); /* start as "", not as NULL */
6652 exp_str = NULL;
6653
6654 for (;;) {
6655 int ch;
6656
6657 ch = i_getch(&input);
6658 debug_printf_parse("%s: ch=%c (%d) escape=%d\n",
6659 __func__, ch, ch, !!dest.o_expflags);
6660
6661 if (!dest.o_expflags) {
6662 if (ch == EOF)
6663 break;
6664 if (handle_squotes && ch == '\'') {
6665 if (!add_till_single_quote_dquoted(&dest, &input))
6666 goto ret; /* error */
6667 continue;
6668 }
6669 }
6670 if (ch == EOF) {
6671 syntax_error_unterm_ch('"');
6672 goto ret; /* error */
6673 }
6674 if (ch == '"') {
6675 dest.o_expflags ^= EXP_FLAG_ESC_GLOB_CHARS;
6676 continue;
6677 }
6678 if (ch == '\\') {
6679 ch = i_getch(&input);
6680 if (ch == EOF) {
6681//example? error message? syntax_error_unterm_ch('"');
6682 debug_printf_parse("%s: error: \\<eof>\n", __func__);
6683 goto ret;
6684 }
6685 o_addqchr(&dest, ch);
6686 continue;
6687 }
6688 if (ch == '$') {
6689 if (parse_dollar_squote(NULL, &dest, &input))
6690 continue;
6691 if (!parse_dollar(NULL, &dest, &input, /*quote_mask:*/ 0x80)) {
6692 debug_printf_parse("%s: error: parse_dollar returned 0 (error)\n", __func__);
6693 goto ret;
6694 }
6695 continue;
6696 }
6697#if ENABLE_HUSH_TICK
6698 if (ch == '`') {
6699 //unsigned pos = dest->length;
6700 o_addchr(&dest, SPECIAL_VAR_SYMBOL);
6701 o_addchr(&dest, 0x80 | '`');
6702 if (!add_till_backquote(&dest, &input,
6703 /*in_dquote:*/ dest.o_expflags /* nonzero if EXP_FLAG_ESC_GLOB_CHARS set */
6704 )
6705 ) {
6706 goto ret; /* error */
6707 }
6708 o_addchr(&dest, SPECIAL_VAR_SYMBOL);
6709 //debug_printf_subst("SUBST RES3 '%s'\n", dest->data + pos);
6710 continue;
6711 }
6712#endif
6713 o_addQchr(&dest, ch);
6714 } /* for (;;) */
6715
6716 debug_printf_parse("encode: '%s' -> '%s'\n", str, dest.data);
6717 exp_str = expand_string_to_string(dest.data,
6718 do_unbackslash ? EXP_FLAG_ESC_GLOB_CHARS : 0,
6719 do_unbackslash
6720 );
6721 ret:
6722 debug_printf_parse("expand: '%s' -> '%s'\n", dest.data, exp_str);
6723 o_free(&dest);
6724 return exp_str;
6725}
6726
6727/* Expanding ARG in ${var+ARG}, ${var-ARG}
6728 */
6729static int encode_then_append_var_plusminus(o_string *output, int n,
6730 char *str, int dquoted)
6731{
6732 struct in_str input;
6733 o_string dest = NULL_O_STRING;
6734
6735 if (!first_special_char_in_vararg(str)
6736 && '\0' == str[strcspn(str, G.ifs)]
6737 ) {
6738 /* string has no special chars
6739 * && string has no $IFS chars
6740 */
6741 if (dquoted) {
6742 /* Prints 1 (quoted expansion is a "" word, not nothing):
6743 * set -- "${notexist-}"; echo $#
6744 */
6745 output->has_quoted_part = 1;
6746 }
6747 return expand_vars_to_list(output, n, str);
6748 }
6749
6750 setup_string_in_str(&input, str);
6751
6752 for (;;) {
6753 int ch;
6754
6755 ch = i_getch(&input);
6756 debug_printf_parse("%s: ch=%c (%d) escape=%x\n",
6757 __func__, ch, ch, dest.o_expflags);
6758
6759 if (!dest.o_expflags) {
6760 if (ch == EOF)
6761 break;
6762 if (!dquoted && strchr(G.ifs, ch)) {
6763 /* PREFIX${x:d${e}f ...} and we met space: expand "d${e}f" and start new word.
6764 * do not assume we are at the start of the word (PREFIX above).
6765 */
6766 if (dest.data) {
6767 n = expand_vars_to_list(output, n, dest.data);
6768 o_free_and_set_NULL(&dest);
6769 o_addchr(output, '\0');
6770 n = o_save_ptr(output, n); /* create next word */
6771 } else
6772 if (output->length != o_get_last_ptr(output, n)
6773 || output->has_quoted_part
6774 ) {
6775 /* For these cases:
6776 * f() { for i; do echo "|$i|"; done; }; x=x
6777 * f a${x:+ }b # 1st condition
6778 * |a|
6779 * |b|
6780 * f ""${x:+ }b # 2nd condition
6781 * ||
6782 * |b|
6783 */
6784 o_addchr(output, '\0');
6785 n = o_save_ptr(output, n); /* create next word */
6786 }
6787 continue;
6788 }
6789 if (!dquoted && ch == '\'') {
6790 if (!add_till_single_quote_dquoted(&dest, &input))
6791 goto ret; /* error */
6792 o_addchr(&dest, SPECIAL_VAR_SYMBOL);
6793 o_addchr(&dest, SPECIAL_VAR_SYMBOL);
6794 continue;
6795 }
6796 }
6797 if (ch == EOF) {
6798 syntax_error_unterm_ch('"');
6799 goto ret; /* error */
6800 }
6801 if (ch == '"') {
6802 dest.o_expflags ^= EXP_FLAG_ESC_GLOB_CHARS;
6803 if (dest.o_expflags) {
6804 o_addchr(&dest, SPECIAL_VAR_SYMBOL);
6805 o_addchr(&dest, SPECIAL_VAR_SYMBOL);
6806 }
6807 continue;
6808 }
6809 if (ch == '\\') {
6810 ch = i_getch(&input);
6811 if (ch == EOF) {
6812//example? error message? syntax_error_unterm_ch('"');
6813 debug_printf_parse("%s: error: \\<eof>\n", __func__);
6814 goto ret;
6815 }
6816 o_addqchr(&dest, ch);
6817 continue;
6818 }
6819 if (ch == '$') {
6820 if (!parse_dollar(NULL, &dest, &input, /*quote_mask:*/ (dest.o_expflags || dquoted) ? 0x80 : 0)) {
6821 debug_printf_parse("%s: error: parse_dollar returned 0 (error)\n", __func__);
6822 goto ret;
6823 }
6824 continue;
6825 }
6826#if ENABLE_HUSH_TICK
6827 if (ch == '`') {
6828 //unsigned pos = dest->length;
6829 o_addchr(&dest, SPECIAL_VAR_SYMBOL);
6830 o_addchr(&dest, (dest.o_expflags || dquoted) ? 0x80 | '`' : '`');
6831 if (!add_till_backquote(&dest, &input,
6832 /*in_dquote:*/ dest.o_expflags /* nonzero if EXP_FLAG_ESC_GLOB_CHARS set */
6833 )
6834 ) {
6835 goto ret; /* error */
6836 }
6837 o_addchr(&dest, SPECIAL_VAR_SYMBOL);
6838 //debug_printf_subst("SUBST RES3 '%s'\n", dest->data + pos);
6839 continue;
6840 }
6841#endif
6842 if (dquoted) {
6843 /* Always glob-protect if in dquotes:
6844 * x=x; echo "${x:+/bin/c*}" - prints: /bin/c*
6845 * x=x; echo "${x:+"/bin/c*"}" - prints: /bin/c*
6846 */
6847 o_addqchr(&dest, ch);
6848 } else {
6849 /* Glob-protect only if char is quoted:
6850 * x=x; echo ${x:+/bin/c*} - prints many filenames
6851 * x=x; echo ${x:+"/bin/c*"} - prints: /bin/c*
6852 */
6853 o_addQchr(&dest, ch);
6854 }
6855 } /* for (;;) */
6856
6857 if (dest.data) {
6858 n = expand_vars_to_list(output, n, dest.data);
6859 }
6860 ret:
6861 o_free(&dest);
6862 return n;
6863}
Francis Laniel8197f012023-12-22 22:02:28 +01006864#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01006865
Francis Laniel8197f012023-12-22 22:02:28 +01006866#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01006867#if ENABLE_FEATURE_SH_MATH
6868static arith_t expand_and_evaluate_arith(const char *arg, const char **errmsg_p)
6869{
6870 arith_state_t math_state;
6871 arith_t res;
6872 char *exp_str;
6873
6874 math_state.lookupvar = get_local_var_value;
6875 math_state.setvar = set_local_var_from_halves;
6876 //math_state.endofname = endofname;
6877 exp_str = encode_then_expand_string(arg);
6878 res = arith(&math_state, exp_str ? exp_str : arg);
6879 free(exp_str);
6880 if (errmsg_p)
6881 *errmsg_p = math_state.errmsg;
6882 if (math_state.errmsg)
6883 msg_and_die_if_script(math_state.errmsg);
6884 return res;
6885}
6886#endif
Francis Laniel8197f012023-12-22 22:02:28 +01006887#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01006888
Francis Laniel8197f012023-12-22 22:02:28 +01006889#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01006890#if BASH_PATTERN_SUBST
6891/* ${var/[/]pattern[/repl]} helpers */
6892static char *strstr_pattern(char *val, const char *pattern, int *size)
6893{
6894 int sz = strcspn(pattern, "*?[\\");
6895 if (pattern[sz] == '\0') {
6896 /* Optimization for trivial patterns.
6897 * Testcase for very slow replace (performs about 22k replaces):
6898 * x=::::::::::::::::::::::
6899 * 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}
6900 * echo "${x//:/|}"
6901 */
6902 *size = sz;
6903 return strstr(val, pattern);
6904 }
6905
6906 while (1) {
6907 char *end = scan_and_match(val, pattern, SCAN_MOVE_FROM_RIGHT + SCAN_MATCH_LEFT_HALF);
6908 debug_printf_varexp("val:'%s' pattern:'%s' end:'%s'\n", val, pattern, end);
6909 if (end) {
6910 *size = end - val;
6911 return val;
6912 }
6913 if (*val == '\0')
6914 return NULL;
6915 /* Optimization: if "*pat" did not match the start of "string",
6916 * we know that "tring", "ring" etc will not match too:
6917 */
6918 if (pattern[0] == '*')
6919 return NULL;
6920 val++;
6921 }
6922}
6923static char *replace_pattern(char *val, const char *pattern, const char *repl, char exp_op)
6924{
6925 char *result = NULL;
6926 unsigned res_len = 0;
6927 unsigned repl_len = strlen(repl);
6928
6929 /* Null pattern never matches, including if "var" is empty */
6930 if (!pattern[0])
6931 return result; /* NULL, no replaces happened */
6932
6933 while (1) {
6934 int size;
6935 char *s = strstr_pattern(val, pattern, &size);
6936 if (!s)
6937 break;
6938
6939 result = xrealloc(result, res_len + (s - val) + repl_len + 1);
6940 strcpy(mempcpy(result + res_len, val, s - val), repl);
6941 res_len += (s - val) + repl_len;
6942 debug_printf_varexp("val:'%s' s:'%s' result:'%s'\n", val, s, result);
6943
6944 val = s + size;
6945 if (exp_op == '/')
6946 break;
6947 }
6948 if (*val && result) {
6949 result = xrealloc(result, res_len + strlen(val) + 1);
6950 strcpy(result + res_len, val);
6951 debug_printf_varexp("val:'%s' result:'%s'\n", val, result);
6952 }
6953 debug_printf_varexp("result:'%s'\n", result);
6954 return result;
6955}
6956#endif /* BASH_PATTERN_SUBST */
Francis Laniel8197f012023-12-22 22:02:28 +01006957#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01006958
6959static int append_str_maybe_ifs_split(o_string *output, int n,
6960 int first_ch, const char *val)
6961{
6962 if (!(first_ch & 0x80)) { /* unquoted $VAR */
6963 debug_printf_expand("unquoted '%s', output->o_escape:%d\n", val,
6964 !!(output->o_expflags & EXP_FLAG_ESC_GLOB_CHARS));
6965 if (val && val[0])
6966 n = expand_on_ifs(output, n, val);
6967 } else { /* quoted "$VAR" */
6968 output->has_quoted_part = 1;
6969 debug_printf_expand("quoted '%s', output->o_escape:%d\n", val,
6970 !!(output->o_expflags & EXP_FLAG_ESC_GLOB_CHARS));
6971 if (val && val[0])
6972 o_addQstr(output, val);
6973 }
6974 return n;
6975}
6976
6977/* Handle <SPECIAL_VAR_SYMBOL>varname...<SPECIAL_VAR_SYMBOL> construct.
6978 */
6979static NOINLINE int expand_one_var(o_string *output, int n,
6980 int first_ch, char *arg, char **pp)
6981{
6982 const char *val;
6983 char *to_be_freed;
6984 char *p;
6985 char *var;
6986 char exp_op;
6987 char exp_save = exp_save; /* for compiler */
6988 char *exp_saveptr; /* points to expansion operator */
6989 char *exp_word = exp_word; /* for compiler */
6990 char arg0;
6991
6992 val = NULL;
6993 to_be_freed = NULL;
6994 p = *pp;
6995 *p = '\0'; /* replace trailing SPECIAL_VAR_SYMBOL */
6996 var = arg;
6997 exp_saveptr = arg[1] ? strchr(VAR_ENCODED_SUBST_OPS, arg[1]) : NULL;
6998 arg0 = arg[0];
6999 arg[0] = (arg0 & 0x7f);
7000 exp_op = 0;
7001
7002 if (arg[0] == '#' && arg[1] /* ${#...} but not ${#} */
7003 && (!exp_saveptr /* and ( not(${#<op_char>...}) */
7004 || (arg[2] == '\0' && strchr(SPECIAL_VARS_STR, arg[1])) /* or ${#C} "len of $C" ) */
7005 ) /* NB: skipping ^^^specvar check mishandles ${#::2} */
7006 ) {
7007 /* It must be length operator: ${#var} */
7008 var++;
7009 exp_op = 'L';
7010 } else {
7011 /* Maybe handle parameter expansion */
7012 if (exp_saveptr /* if 2nd char is one of expansion operators */
7013 && strchr(NUMERIC_SPECVARS_STR, arg[0]) /* 1st char is special variable */
7014 ) {
7015 /* ${?:0}, ${#[:]%0} etc */
7016 exp_saveptr = var + 1;
7017 } else {
7018 /* ${?}, ${var}, ${var:0}, ${var[:]%0} etc */
7019 exp_saveptr = var+1 + strcspn(var+1, VAR_ENCODED_SUBST_OPS);
7020 }
7021 exp_op = exp_save = *exp_saveptr;
Francis Laniel8197f012023-12-22 22:02:28 +01007022#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01007023 if (exp_op) {
7024 exp_word = exp_saveptr + 1;
7025 if (exp_op == ':') {
7026 exp_op = *exp_word++;
7027//TODO: try ${var:} and ${var:bogus} in non-bash config
7028 if (BASH_SUBSTR
7029 && (!exp_op || !strchr(MINUS_PLUS_EQUAL_QUESTION, exp_op))
7030 ) {
7031 /* oops... it's ${var:N[:M]}, not ${var:?xxx} or some such */
7032 exp_op = ':';
7033 exp_word--;
7034 }
7035 }
7036 *exp_saveptr = '\0';
7037 } /* else: it's not an expansion op, but bare ${var} */
Francis Laniel8197f012023-12-22 22:02:28 +01007038#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01007039 }
7040
7041 /* Look up the variable in question */
7042 if (isdigit(var[0])) {
7043 /* parse_dollar should have vetted var for us */
Francis Laniel8197f012023-12-22 22:02:28 +01007044#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01007045 int nn = xatoi_positive(var);
Francis Laniel8197f012023-12-22 22:02:28 +01007046#else /* __U_BOOT__ */
7047 int nn = simple_strtoul(var, NULL, 10);
7048#endif /* __U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01007049 if (nn < G.global_argc)
7050 val = G.global_argv[nn];
7051 /* else val remains NULL: $N with too big N */
7052 } else {
7053 switch (var[0]) {
Francis Laniel8197f012023-12-22 22:02:28 +01007054#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01007055 case '$': /* pid */
7056 val = utoa(G.root_pid);
7057 break;
7058 case '!': /* bg pid */
7059 val = G.last_bg_pid ? utoa(G.last_bg_pid) : "";
7060 break;
Francis Laniel8197f012023-12-22 22:02:28 +01007061#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01007062 case '?': /* exitcode */
7063 val = utoa(G.last_exitcode);
7064 break;
7065 case '#': /* argc */
7066 val = utoa(G.global_argc ? G.global_argc-1 : 0);
7067 break;
Francis Laniel8197f012023-12-22 22:02:28 +01007068#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01007069 case '-': { /* active options */
7070 /* Check set_mode() to see what option chars we support */
7071 char *cp;
7072 val = cp = G.optstring_buf;
7073 if (G.o_opt[OPT_O_ERREXIT])
7074 *cp++ = 'e';
7075 if (G_interactive_fd)
7076 *cp++ = 'i';
7077 if (G_x_mode)
7078 *cp++ = 'x';
7079 /* If G.o_opt[OPT_O_NOEXEC] is true,
7080 * commands read but are not executed,
7081 * so $- can not execute too, 'n' is never seen in $-.
7082 */
7083 if (G.opt_c)
7084 *cp++ = 'c';
7085 if (G.opt_s)
7086 *cp++ = 's';
7087 *cp = '\0';
7088 break;
7089 }
Francis Laniel8197f012023-12-22 22:02:28 +01007090#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01007091 default:
Francis Laniel74e42542023-12-22 22:02:33 +01007092#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01007093 val = get_local_var_value(var);
Francis Laniel74e42542023-12-22 22:02:33 +01007094#else /* __U_BOOT__ */
7095 /*
7096 * Environment variable set with setenv* have to be
7097 * expanded.
7098 * So, we first search if the variable exists in
7099 * environment, if this is not the case, we default to
7100 * local value.
7101 */
7102 val = env_get(var);
7103 if (!val)
7104 val = get_local_var_value(var);
7105#endif /* __U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01007106 }
7107 }
7108
Francis Laniel8197f012023-12-22 22:02:28 +01007109#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01007110 /* Handle any expansions */
7111 if (exp_op == 'L') {
7112 reinit_unicode_for_hush();
7113 debug_printf_expand("expand: length(%s)=", val);
7114 val = utoa(val ? unicode_strlen(val) : 0);
7115 debug_printf_expand("%s\n", val);
7116 } else if (exp_op) {
7117 if (exp_op == '%' || exp_op == '#') {
7118 /* Standard-mandated substring removal ops:
7119 * ${parameter%word} - remove smallest suffix pattern
7120 * ${parameter%%word} - remove largest suffix pattern
7121 * ${parameter#word} - remove smallest prefix pattern
7122 * ${parameter##word} - remove largest prefix pattern
7123 *
7124 * Word is expanded to produce a glob pattern.
7125 * Then var's value is matched to it and matching part removed.
7126 */
7127 /* bash compat: if x is "" and no shrinking of it is possible,
7128 * inner ${...} is not evaluated. Example:
7129 * unset b; : ${a%${b=B}}; echo $b
7130 * assignment b=B only happens if $a is not "".
7131 */
7132 if (val && val[0]) {
7133 char *t;
7134 char *exp_exp_word;
7135 char *loc;
7136 unsigned scan_flags = pick_scan(exp_op, *exp_word);
7137 if (exp_op == *exp_word) /* ## or %% */
7138 exp_word++;
7139 debug_printf_expand("expand: exp_word:'%s'\n", exp_word);
7140 exp_exp_word = encode_then_expand_vararg(exp_word, /*handle_squotes:*/ 1, /*unbackslash:*/ 0);
7141 if (exp_exp_word)
7142 exp_word = exp_exp_word;
7143 debug_printf_expand("expand: exp_word:'%s'\n", exp_word);
7144 /*
7145 * HACK ALERT. We depend here on the fact that
7146 * G.global_argv and results of utoa and get_local_var_value
7147 * are actually in writable memory:
7148 * scan_and_match momentarily stores NULs there.
7149 */
7150 t = (char*)val;
7151 loc = scan_and_match(t, exp_word, scan_flags);
7152 debug_printf_expand("op:%c str:'%s' pat:'%s' res:'%s'\n", exp_op, t, exp_word, loc);
7153 free(exp_exp_word);
7154 if (loc) { /* match was found */
7155 if (scan_flags & SCAN_MATCH_LEFT_HALF) /* #[#] */
7156 val = loc; /* take right part */
7157 else /* %[%] */
7158 val = to_be_freed = xstrndup(val, loc - val); /* left */
7159 }
7160 }
7161 }
7162#if BASH_PATTERN_SUBST
7163 else if (exp_op == '/' || exp_op == '\\') {
7164 /* It's ${var/[/]pattern[/repl]} thing.
7165 * Note that in encoded form it has TWO parts:
7166 * var/pattern<SPECIAL_VAR_SYMBOL>repl<SPECIAL_VAR_SYMBOL>
7167 * and if // is used, it is encoded as \:
7168 * var\pattern<SPECIAL_VAR_SYMBOL>repl<SPECIAL_VAR_SYMBOL>
7169 */
7170 /* bash compat: if var is "", both pattern and repl
7171 * are still evaluated, if it is unset, then not:
7172 * unset b; a=; : ${a/z/${b=3}}; echo $b # b=3
7173 * unset b; unset a; : ${a/z/${b=3}}; echo $b # b not set
7174 */
7175 if (val /*&& val[0]*/) {
7176 /* pattern uses non-standard expansion.
7177 * repl should be unbackslashed and globbed
7178 * by the usual expansion rules:
7179 * >az >bz
7180 * v='a bz'; echo "${v/a*z/a*z}" #prints "a*z"
7181 * v='a bz'; echo "${v/a*z/\z}" #prints "z"
7182 * v='a bz'; echo ${v/a*z/a*z} #prints "az"
7183 * v='a bz'; echo ${v/a*z/\z} #prints "z"
7184 * (note that a*z _pattern_ is never globbed!)
7185 */
7186 char *pattern, *repl, *t;
7187 pattern = encode_then_expand_vararg(exp_word, /*handle_squotes:*/ 1, /*unbackslash:*/ 0);
7188 if (!pattern)
7189 pattern = xstrdup(exp_word);
7190 debug_printf_varexp("pattern:'%s'->'%s'\n", exp_word, pattern);
7191 *p++ = SPECIAL_VAR_SYMBOL;
7192 exp_word = p;
7193 p = strchr(p, SPECIAL_VAR_SYMBOL);
7194 *p = '\0';
7195 repl = encode_then_expand_vararg(exp_word, /*handle_squotes:*/ 1, /*unbackslash:*/ 1);
7196 debug_printf_varexp("repl:'%s'->'%s'\n", exp_word, repl);
7197 /* HACK ALERT. We depend here on the fact that
7198 * G.global_argv and results of utoa and get_local_var_value
7199 * are actually in writable memory:
7200 * replace_pattern momentarily stores NULs there. */
7201 t = (char*)val;
7202 to_be_freed = replace_pattern(t,
7203 pattern,
7204 (repl ? repl : exp_word),
7205 exp_op);
7206 if (to_be_freed) /* at least one replace happened */
7207 val = to_be_freed;
7208 free(pattern);
7209 free(repl);
7210 } else {
7211 /* Unset variable always gives nothing */
7212 // a=; echo ${a/*/w} # "w"
7213 // unset a; echo ${a/*/w} # ""
7214 /* Just skip "replace" part */
7215 *p++ = SPECIAL_VAR_SYMBOL;
7216 p = strchr(p, SPECIAL_VAR_SYMBOL);
7217 *p = '\0';
7218 }
7219 }
7220#endif /* BASH_PATTERN_SUBST */
7221 else if (exp_op == ':') {
7222#if BASH_SUBSTR && ENABLE_FEATURE_SH_MATH
7223 /* It's ${var:N[:M]} bashism.
7224 * Note that in encoded form it has TWO parts:
7225 * var:N<SPECIAL_VAR_SYMBOL>M<SPECIAL_VAR_SYMBOL>
7226 */
7227 arith_t beg, len;
7228 unsigned vallen;
7229 const char *errmsg;
7230
7231 beg = expand_and_evaluate_arith(exp_word, &errmsg);
7232 if (errmsg)
7233 goto empty_result;
7234 debug_printf_varexp("beg:'%s'=%lld\n", exp_word, (long long)beg);
7235 *p++ = SPECIAL_VAR_SYMBOL;
7236 exp_word = p;
7237 p = strchr(p, SPECIAL_VAR_SYMBOL);
7238 *p = '\0';
7239 vallen = val ? strlen(val) : 0;
7240 if (beg < 0) {
7241 /* negative beg counts from the end */
7242 beg = (arith_t)vallen + beg;
7243 }
7244 /* If expansion will be empty, do not even evaluate len */
7245 if (!val || beg < 0 || beg > vallen) {
7246 /* Why > vallen, not >=? bash:
7247 * unset b; a=ab; : ${a:2:${b=3}}; echo $b # "", b=3 (!!!)
7248 * unset b; a=a; : ${a:2:${b=3}}; echo $b # "", b not set
7249 */
7250 goto empty_result;
7251 }
7252 len = expand_and_evaluate_arith(exp_word, &errmsg);
7253 if (errmsg)
7254 goto empty_result;
7255 debug_printf_varexp("len:'%s'=%lld\n", exp_word, (long long)len);
7256 debug_printf_varexp("from val:'%s'\n", val);
7257 if (len < 0) {
7258 /* in bash, len=-n means strlen()-n */
7259 len = (arith_t)vallen - beg + len;
7260 if (len < 0) /* bash compat */
7261 msg_and_die_if_script("%s: substring expression < 0", var);
7262 }
7263 if (len <= 0 || !val /*|| beg >= vallen*/) {
7264 empty_result:
7265 val = NULL;
7266 } else {
7267 /* Paranoia. What if user entered 9999999999999
7268 * which fits in arith_t but not int? */
7269 if (len > INT_MAX)
7270 len = INT_MAX;
7271 val = to_be_freed = xstrndup(val + beg, len);
7272 }
7273 debug_printf_varexp("val:'%s'\n", val);
7274#else /* not (HUSH_SUBSTR_EXPANSION && FEATURE_SH_MATH) */
7275 msg_and_die_if_script("malformed ${%s:...}", var);
7276 val = NULL;
7277#endif
7278 } else { /* one of "-=+?" */
7279 /* Standard-mandated substitution ops:
7280 * ${var?word} - indicate error if unset
7281 * If var is unset, word (or a message indicating it is unset
7282 * if word is null) is written to standard error
7283 * and the shell exits with a non-zero exit status.
7284 * Otherwise, the value of var is substituted.
7285 * ${var-word} - use default value
7286 * If var is unset, word is substituted.
7287 * ${var=word} - assign and use default value
7288 * If var is unset, word is assigned to var.
7289 * In all cases, final value of var is substituted.
7290 * ${var+word} - use alternative value
7291 * If var is unset, null is substituted.
7292 * Otherwise, word is substituted.
7293 *
7294 * Word is subjected to tilde expansion, parameter expansion,
7295 * command substitution, and arithmetic expansion.
7296 * If word is not needed, it is not expanded.
7297 *
7298 * Colon forms (${var:-word}, ${var:=word} etc) do the same,
7299 * but also treat null var as if it is unset.
7300 *
7301 * Word-splitting and single quote behavior:
7302 *
7303 * $ f() { for i; do echo "|$i|"; done; }
7304 *
7305 * $ x=; f ${x:?'x y' z}; echo $?
7306 * bash: x: x y z # neither f nor "echo $?" executes
7307 * (if interactive, bash does not exit, but merely aborts to prompt. $? is set to 1)
7308 * $ x=; f "${x:?'x y' z}"
7309 * bash: x: x y z # dash prints: dash: x: 'x y' z
7310 *
7311 * $ x=; f ${x:='x y' z}
7312 * |x|
7313 * |y|
7314 * |z|
7315 * $ x=; f "${x:='x y' z}"
7316 * |'x y' z|
7317 *
7318 * $ x=x; f ${x:+'x y' z}
7319 * |x y|
7320 * |z|
7321 * $ x=x; f "${x:+'x y' z}"
7322 * |'x y' z|
7323 *
7324 * $ x=; f ${x:-'x y' z}
7325 * |x y|
7326 * |z|
7327 * $ x=; f "${x:-'x y' z}"
7328 * |'x y' z|
7329 */
7330 int use_word = (!val || ((exp_save == ':') && !val[0]));
7331 if (exp_op == '+')
7332 use_word = !use_word;
7333 debug_printf_expand("expand: op:%c (null:%s) test:%i\n", exp_op,
7334 (exp_save == ':') ? "true" : "false", use_word);
7335 if (use_word) {
7336 if (exp_op == '+' || exp_op == '-') {
7337 /* ${var+word} - use alternative value */
7338 /* ${var-word} - use default value */
7339 n = encode_then_append_var_plusminus(output, n, exp_word,
7340 /*dquoted:*/ (arg0 & 0x80)
7341 );
7342 val = NULL;
7343 } else {
7344 /* ${var?word} - indicate error if unset */
7345 /* ${var=word} - assign and use default value */
7346 to_be_freed = encode_then_expand_vararg(exp_word,
7347 /*handle_squotes:*/ !(arg0 & 0x80),
7348 /*unbackslash:*/ 0
7349 );
7350 if (to_be_freed)
7351 exp_word = to_be_freed;
7352 if (exp_op == '?') {
7353 /* mimic bash message */
7354 msg_and_die_if_script("%s: %s",
7355 var,
7356 exp_word[0]
7357 ? exp_word
7358 : "parameter null or not set"
7359 /* ash has more specific messages, a-la: */
7360 /*: (exp_save == ':' ? "parameter null or not set" : "parameter not set")*/
7361 );
7362//TODO: how interactive bash aborts expansion mid-command?
7363//It aborts the entire line, returns to prompt:
7364// $ f() { for i; do echo "|$i|"; done; }; x=; f "${x:?'x y' z}"; echo YO
7365// bash: x: x y z
7366// $
7367// ("echo YO" is not executed, neither the f function call)
7368 } else {
7369 val = exp_word;
7370 }
7371 if (exp_op == '=') {
7372 /* ${var=[word]} or ${var:=[word]} */
7373 if (isdigit(var[0]) || var[0] == '#') {
7374 /* mimic bash message */
7375 msg_and_die_if_script("$%s: cannot assign in this way", var);
7376 val = NULL;
7377 } else {
7378 char *new_var = xasprintf("%s=%s", var, val);
7379 set_local_var(new_var, /*flag:*/ 0);
7380 }
7381 }
7382 }
7383 }
7384 } /* one of "-=+?" */
7385
7386 *exp_saveptr = exp_save;
7387 } /* if (exp_op) */
7388
Francis Laniel8197f012023-12-22 22:02:28 +01007389#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01007390 arg[0] = arg0;
7391 *pp = p;
7392
7393 n = append_str_maybe_ifs_split(output, n, first_ch, val);
7394
7395 free(to_be_freed);
7396 return n;
7397}
7398
7399/* Expand all variable references in given string, adding words to list[]
7400 * at n, n+1,... positions. Return updated n (so that list[n] is next one
7401 * to be filled). This routine is extremely tricky: has to deal with
7402 * variables/parameters with whitespace, $* and $@, and constructs like
7403 * 'echo -$*-'. If you play here, you must run testsuite afterwards! */
7404static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg)
7405{
7406 /* output->o_expflags & EXP_FLAG_SINGLEWORD (0x80) if we are in
7407 * expansion of right-hand side of assignment == 1-element expand.
7408 */
7409 char cant_be_null = 0; /* only bit 0x80 matters */
7410 char *p;
7411
7412 debug_printf_expand("expand_vars_to_list: arg:'%s' singleword:%x\n", arg,
7413 !!(output->o_expflags & EXP_FLAG_SINGLEWORD));
7414 debug_print_list("expand_vars_to_list[0]", output, n);
7415
7416 while ((p = strchr(arg, SPECIAL_VAR_SYMBOL)) != NULL) {
7417 char first_ch;
7418#if ENABLE_FEATURE_SH_MATH
7419 char arith_buf[sizeof(arith_t)*3 + 2];
7420#endif
7421
7422 if (output->ended_in_ifs) {
7423 o_addchr(output, '\0');
7424 n = o_save_ptr(output, n);
7425 output->ended_in_ifs = 0;
7426 }
7427
7428 o_addblock(output, arg, p - arg);
7429 debug_print_list("expand_vars_to_list[1]", output, n);
7430 arg = ++p;
7431 p = strchr(p, SPECIAL_VAR_SYMBOL);
7432
7433 /* Fetch special var name (if it is indeed one of them)
7434 * and quote bit, force the bit on if singleword expansion -
7435 * important for not getting v=$@ expand to many words. */
7436 first_ch = arg[0] | (output->o_expflags & EXP_FLAG_SINGLEWORD);
7437
7438 /* Is this variable quoted and thus expansion can't be null?
7439 * "$@" is special. Even if quoted, it can still
7440 * expand to nothing (not even an empty string),
7441 * thus it is excluded. */
7442 if ((first_ch & 0x7f) != '@')
7443 cant_be_null |= first_ch;
7444
7445 switch (first_ch & 0x7f) {
7446 /* Highest bit in first_ch indicates that var is double-quoted */
7447 case '*':
7448 case '@': {
7449 int i;
Francis Laniel74e42542023-12-22 22:02:33 +01007450#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01007451 if (!G.global_argv[1])
Francis Laniel74e42542023-12-22 22:02:33 +01007452#else /* __U_BOOT__ */
7453 if (!G.global_argv || !G.global_argv[1])
7454#endif /* __U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01007455 break;
7456 i = 1;
7457 cant_be_null |= first_ch; /* do it for "$@" _now_, when we know it's not empty */
7458 if (!(first_ch & 0x80)) { /* unquoted $* or $@ */
7459 while (G.global_argv[i]) {
7460 n = expand_on_ifs(output, n, G.global_argv[i]);
7461 debug_printf_expand("expand_vars_to_list: argv %d (last %d)\n", i, G.global_argc - 1);
7462 if (G.global_argv[i++][0] && G.global_argv[i]) {
7463 /* this argv[] is not empty and not last:
7464 * put terminating NUL, start new word */
7465 o_addchr(output, '\0');
7466 debug_print_list("expand_vars_to_list[2]", output, n);
7467 n = o_save_ptr(output, n);
7468 debug_print_list("expand_vars_to_list[3]", output, n);
7469 }
7470 }
7471 } else
7472 /* If EXP_FLAG_SINGLEWORD, we handle assignment 'a=....$@.....'
7473 * and in this case should treat it like '$*' - see 'else...' below */
7474 if (first_ch == (char)('@'|0x80) /* quoted $@ */
7475 && !(output->o_expflags & EXP_FLAG_SINGLEWORD) /* not v="$@" case */
7476 ) {
7477 while (1) {
7478 o_addQstr(output, G.global_argv[i]);
7479 if (++i >= G.global_argc)
7480 break;
7481 o_addchr(output, '\0');
7482 debug_print_list("expand_vars_to_list[4]", output, n);
7483 n = o_save_ptr(output, n);
7484 }
7485 } else { /* quoted $* (or v="$@" case): add as one word */
7486 while (1) {
7487 o_addQstr(output, G.global_argv[i]);
7488 if (!G.global_argv[++i])
7489 break;
7490 if (G.ifs[0])
7491 o_addchr(output, G.ifs[0]);
7492 }
7493 output->has_quoted_part = 1;
7494 }
7495 break;
7496 }
7497 case SPECIAL_VAR_SYMBOL: {
7498 /* <SPECIAL_VAR_SYMBOL><SPECIAL_VAR_SYMBOL> */
7499 /* "Empty variable", used to make "" etc to not disappear */
7500 output->has_quoted_part = 1;
7501 cant_be_null = 0x80;
7502 arg++;
7503 break;
7504 }
7505 case SPECIAL_VAR_QUOTED_SVS:
7506 /* <SPECIAL_VAR_SYMBOL><SPECIAL_VAR_QUOTED_SVS><SPECIAL_VAR_SYMBOL> */
7507 /* "^C variable", represents literal ^C char (possible in scripts) */
7508 o_addchr(output, SPECIAL_VAR_SYMBOL);
7509 arg++;
7510 break;
7511#if ENABLE_HUSH_TICK
7512 case '`': {
7513 /* <SPECIAL_VAR_SYMBOL>`cmd<SPECIAL_VAR_SYMBOL> */
7514 o_string subst_result = NULL_O_STRING;
7515
7516 *p = '\0'; /* replace trailing <SPECIAL_VAR_SYMBOL> */
7517 arg++;
7518 /* Can't just stuff it into output o_string,
7519 * expanded result may need to be globbed
7520 * and $IFS-split */
7521 debug_printf_subst("SUBST '%s' first_ch %x\n", arg, first_ch);
7522 G.last_exitcode = process_command_subs(&subst_result, arg);
7523 G.expand_exitcode = G.last_exitcode;
7524 debug_printf_subst("SUBST RES:%d '%s'\n", G.last_exitcode, subst_result.data);
7525 n = append_str_maybe_ifs_split(output, n, first_ch, subst_result.data);
7526 o_free(&subst_result);
7527 break;
7528 }
7529#endif
7530#if ENABLE_FEATURE_SH_MATH
7531 case '+': {
7532 /* <SPECIAL_VAR_SYMBOL>+arith<SPECIAL_VAR_SYMBOL> */
7533 arith_t res;
7534
7535 arg++; /* skip '+' */
7536 *p = '\0'; /* replace trailing <SPECIAL_VAR_SYMBOL> */
7537 debug_printf_subst("ARITH '%s' first_ch %x\n", arg, first_ch);
7538 res = expand_and_evaluate_arith(arg, NULL);
7539 debug_printf_subst("ARITH RES '"ARITH_FMT"'\n", res);
7540 sprintf(arith_buf, ARITH_FMT, res);
7541 if (res < 0
7542 && first_ch == (char)('+'|0x80)
7543 /* && (output->o_expflags & EXP_FLAG_ESC_GLOB_CHARS) */
7544 ) {
7545 /* Quoted negative ariths, like filename[0"$((-9))"],
7546 * should not be interpreted as glob ranges.
7547 * Convert leading '-' to '\-':
7548 */
7549 o_grow_by(output, 1);
7550 output->data[output->length++] = '\\';
7551 }
7552 o_addstr(output, arith_buf);
7553 break;
7554 }
7555#endif
7556 default:
7557 /* <SPECIAL_VAR_SYMBOL>varname[ops]<SPECIAL_VAR_SYMBOL> */
7558 n = expand_one_var(output, n, first_ch, arg, &p);
7559 break;
7560 } /* switch (char after <SPECIAL_VAR_SYMBOL>) */
7561
7562 /* Restore NULL'ed SPECIAL_VAR_SYMBOL.
7563 * Do the check to avoid writing to a const string. */
7564 if (*p != SPECIAL_VAR_SYMBOL)
7565 *p = SPECIAL_VAR_SYMBOL;
7566 arg = ++p;
7567 } /* end of "while (SPECIAL_VAR_SYMBOL is found) ..." */
7568
7569 if (*arg) {
7570 /* handle trailing string */
7571 if (output->ended_in_ifs) {
7572 o_addchr(output, '\0');
7573 n = o_save_ptr(output, n);
7574 }
7575 debug_print_list("expand_vars_to_list[a]", output, n);
7576 /* this part is literal, and it was already pre-quoted
7577 * if needed (much earlier), do not use o_addQstr here!
7578 */
7579 o_addstr(output, arg);
7580 debug_print_list("expand_vars_to_list[b]", output, n);
7581 } else
7582 if (output->length == o_get_last_ptr(output, n) /* expansion is empty */
7583 && !(cant_be_null & 0x80) /* and all vars were not quoted */
7584 && !output->has_quoted_part
7585 ) {
7586 n--;
7587 /* allow to reuse list[n] later without re-growth */
7588 output->has_empty_slot = 1;
7589 }
7590
7591 return n;
7592}
7593
7594static char **expand_variables(char **argv, unsigned expflags)
7595{
7596 int n;
7597 char **list;
7598 o_string output = NULL_O_STRING;
7599
7600 output.o_expflags = expflags;
7601
7602 n = 0;
7603 for (;;) {
7604 /* go to next list[n] */
7605 output.ended_in_ifs = 0;
7606 n = o_save_ptr(&output, n);
7607
7608 if (!*argv)
7609 break;
7610
7611 /* expand argv[i] */
7612 n = expand_vars_to_list(&output, n, *argv++);
7613 /* if (!output->has_empty_slot) -- need this?? */
7614 o_addchr(&output, '\0');
7615 }
7616 debug_print_list("expand_variables", &output, n);
7617
7618 /* output.data (malloced in one block) gets returned in "list" */
7619 list = o_finalize_list(&output, n);
7620 debug_print_strings("expand_variables[1]", list);
7621 return list;
7622}
7623
7624static char **expand_strvec_to_strvec(char **argv)
7625{
7626 return expand_variables(argv, EXP_FLAG_GLOB | EXP_FLAG_ESC_GLOB_CHARS);
7627}
7628
7629#if defined(CMD_SINGLEWORD_NOGLOB) || defined(CMD_TEST2_SINGLEWORD_NOGLOB)
7630static char **expand_strvec_to_strvec_singleword_noglob(char **argv)
7631{
7632 return expand_variables(argv, EXP_FLAG_SINGLEWORD);
7633}
7634#endif
7635
7636/* Used for expansion of right hand of assignments,
7637 * $((...)), heredocs, variable expansion parts.
7638 *
7639 * NB: should NOT do globbing!
7640 * "export v=/bin/c*; env | grep ^v=" outputs "v=/bin/c*"
7641 */
7642static char *expand_string_to_string(const char *str, int EXP_flags, int do_unbackslash)
7643{
7644#if !BASH_PATTERN_SUBST && !ENABLE_HUSH_CASE
7645 const int do_unbackslash = 1;
7646 const int EXP_flags = EXP_FLAG_ESC_GLOB_CHARS;
7647#endif
7648 char *argv[2], **list;
7649
7650 debug_printf_expand("string_to_string<='%s'\n", str);
7651 /* This is generally an optimization, but it also
7652 * handles "", which otherwise trips over !list[0] check below.
7653 * (is this ever happens that we actually get str="" here?)
7654 */
7655 if (!strchr(str, SPECIAL_VAR_SYMBOL) && !strchr(str, '\\')) {
7656 //TODO: Can use on strings with \ too, just unbackslash() them?
7657 debug_printf_expand("string_to_string(fast)=>'%s'\n", str);
7658 return xstrdup(str);
7659 }
7660
7661 argv[0] = (char*)str;
7662 argv[1] = NULL;
7663 list = expand_variables(argv, EXP_flags | EXP_FLAG_SINGLEWORD);
7664 if (!list[0]) {
7665 /* Example where it happens:
7666 * x=; echo ${x:-"$@"}
7667 */
7668 ((char*)list)[0] = '\0';
7669 } else {
7670 if (HUSH_DEBUG)
7671 if (list[1])
7672 bb_simple_error_msg_and_die("BUG in varexp2");
7673 /* actually, just move string 2*sizeof(char*) bytes back */
7674 overlapping_strcpy((char*)list, list[0]);
7675 if (do_unbackslash)
7676 unbackslash((char*)list);
7677 }
7678 debug_printf_expand("string_to_string=>'%s'\n", (char*)list);
7679 return (char*)list;
7680}
7681
7682#if 0
7683static char* expand_strvec_to_string(char **argv)
7684{
7685 char **list;
7686
7687 list = expand_variables(argv, EXP_FLAG_SINGLEWORD);
7688 /* Convert all NULs to spaces */
7689 if (list[0]) {
7690 int n = 1;
7691 while (list[n]) {
7692 if (HUSH_DEBUG)
7693 if (list[n-1] + strlen(list[n-1]) + 1 != list[n])
7694 bb_error_msg_and_die("BUG in varexp3");
7695 /* bash uses ' ' regardless of $IFS contents */
7696 list[n][-1] = ' ';
7697 n++;
7698 }
7699 }
7700 overlapping_strcpy((char*)list, list[0] ? list[0] : "");
7701 debug_printf_expand("strvec_to_string='%s'\n", (char*)list);
7702 return (char*)list;
7703}
7704#endif
7705
Francis Laniel8197f012023-12-22 22:02:28 +01007706#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01007707static char **expand_assignments(char **argv, int count)
7708{
7709 int i;
7710 char **p;
7711
7712 G.expanded_assignments = p = NULL;
7713 /* Expand assignments into one string each */
7714 for (i = 0; i < count; i++) {
7715 p = add_string_to_strings(p,
7716 expand_string_to_string(argv[i],
7717 EXP_FLAG_ESC_GLOB_CHARS,
7718 /*unbackslash:*/ 1
7719 )
7720 );
7721 G.expanded_assignments = p;
7722 }
7723 G.expanded_assignments = NULL;
7724 return p;
7725}
7726
7727
7728static void switch_off_special_sigs(unsigned mask)
7729{
7730 unsigned sig = 0;
7731 while ((mask >>= 1) != 0) {
7732 sig++;
7733 if (!(mask & 1))
7734 continue;
7735#if ENABLE_HUSH_TRAP
7736 if (G_traps) {
7737 if (G_traps[sig] && !G_traps[sig][0])
7738 /* trap is '', has to remain SIG_IGN */
7739 continue;
7740 free(G_traps[sig]);
7741 G_traps[sig] = NULL;
7742 }
7743#endif
7744 /* We are here only if no trap or trap was not '' */
7745 install_sighandler(sig, SIG_DFL);
7746 }
7747}
Francis Laniel8197f012023-12-22 22:02:28 +01007748#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01007749
Francis Laniel8197f012023-12-22 22:02:28 +01007750#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01007751#if BB_MMU
7752/* never called */
7753void re_execute_shell(char ***to_free, const char *s,
7754 char *g_argv0, char **g_argv,
7755 char **builtin_argv) NORETURN;
7756
7757static void reset_traps_to_defaults(void)
7758{
7759 /* This function is always called in a child shell
7760 * after fork (not vfork, NOMMU doesn't use this function).
7761 */
7762 IF_HUSH_TRAP(unsigned sig;)
7763 unsigned mask;
7764
7765 /* Child shells are not interactive.
7766 * SIGTTIN/SIGTTOU/SIGTSTP should not have special handling.
7767 * Testcase: (while :; do :; done) + ^Z should background.
7768 * Same goes for SIGTERM, SIGHUP, SIGINT.
7769 */
7770 mask = (G.special_sig_mask & SPECIAL_INTERACTIVE_SIGS) | G_fatal_sig_mask;
7771 if (!G_traps && !mask)
7772 return; /* already no traps and no special sigs */
7773
7774 /* Switch off special sigs */
7775 switch_off_special_sigs(mask);
7776# if ENABLE_HUSH_JOB
7777 G_fatal_sig_mask = 0;
7778# endif
7779 G.special_sig_mask &= ~SPECIAL_INTERACTIVE_SIGS;
7780 /* SIGQUIT,SIGCHLD and maybe SPECIAL_JOBSTOP_SIGS
7781 * remain set in G.special_sig_mask */
7782
7783# if ENABLE_HUSH_TRAP
7784 if (!G_traps)
7785 return;
7786
7787 /* Reset all sigs to default except ones with empty traps */
7788 for (sig = 0; sig < NSIG; sig++) {
7789 if (!G_traps[sig])
7790 continue; /* no trap: nothing to do */
7791 if (!G_traps[sig][0])
7792 continue; /* empty trap: has to remain SIG_IGN */
7793 /* sig has non-empty trap, reset it: */
7794 free(G_traps[sig]);
7795 G_traps[sig] = NULL;
7796 /* There is no signal for trap 0 (EXIT) */
7797 if (sig == 0)
7798 continue;
7799 install_sighandler(sig, pick_sighandler(sig));
7800 }
7801# endif
7802}
7803
7804#else /* !BB_MMU */
7805
7806static void re_execute_shell(char ***to_free, const char *s,
7807 char *g_argv0, char **g_argv,
7808 char **builtin_argv) NORETURN;
7809static void re_execute_shell(char ***to_free, const char *s,
7810 char *g_argv0, char **g_argv,
7811 char **builtin_argv)
7812{
7813# define NOMMU_HACK_FMT ("-$%x:%x:%x:%x:%x:%llx" IF_HUSH_LOOPS(":%x"))
7814 /* delims + 2 * (number of bytes in printed hex numbers) */
7815 char param_buf[sizeof(NOMMU_HACK_FMT) + 2 * (sizeof(int)*6 + sizeof(long long)*1)];
7816 char *heredoc_argv[4];
7817 struct variable *cur;
7818# if ENABLE_HUSH_FUNCTIONS
7819 struct function *funcp;
7820# endif
7821 char **argv, **pp;
7822 unsigned cnt;
7823 unsigned long long empty_trap_mask;
7824
7825 if (!g_argv0) { /* heredoc */
7826 argv = heredoc_argv;
7827 argv[0] = (char *) G.argv0_for_re_execing;
7828 argv[1] = (char *) "-<";
7829 argv[2] = (char *) s;
7830 argv[3] = NULL;
7831 pp = &argv[3]; /* used as pointer to empty environment */
7832 goto do_exec;
7833 }
7834
7835 cnt = 0;
7836 pp = builtin_argv;
7837 if (pp) while (*pp++)
7838 cnt++;
7839
7840 empty_trap_mask = 0;
7841 if (G_traps) {
7842 int sig;
7843 for (sig = 1; sig < NSIG; sig++) {
7844 if (G_traps[sig] && !G_traps[sig][0])
7845 empty_trap_mask |= 1LL << sig;
7846 }
7847 }
7848
7849 sprintf(param_buf, NOMMU_HACK_FMT
7850 , (unsigned) G.root_pid
7851 , (unsigned) G.root_ppid
7852 , (unsigned) G.last_bg_pid
7853 , (unsigned) G.last_exitcode
7854 , cnt
7855 , empty_trap_mask
7856 IF_HUSH_LOOPS(, G.depth_of_loop)
7857 );
7858# undef NOMMU_HACK_FMT
7859 /* 1:hush 2:-$<pid>:<pid>:<exitcode>:<etc...> <vars...> <funcs...>
7860 * 3:-c 4:<cmd> 5:<arg0> <argN...> 6:NULL
7861 */
7862 cnt += 6;
7863 for (cur = G.top_var; cur; cur = cur->next) {
7864 if (!cur->flg_export || cur->flg_read_only)
7865 cnt += 2;
7866 }
7867# if ENABLE_HUSH_FUNCTIONS
7868 for (funcp = G.top_func; funcp; funcp = funcp->next)
7869 cnt += 3;
7870# endif
7871 pp = g_argv;
7872 while (*pp++)
7873 cnt++;
7874 *to_free = argv = pp = xzalloc(sizeof(argv[0]) * cnt);
7875 *pp++ = (char *) G.argv0_for_re_execing;
7876 *pp++ = param_buf;
7877 for (cur = G.top_var; cur; cur = cur->next) {
7878 if (strcmp(cur->varstr, hush_version_str) == 0)
7879 continue;
7880 if (cur->flg_read_only) {
7881 *pp++ = (char *) "-R";
7882 *pp++ = cur->varstr;
7883 } else if (!cur->flg_export) {
7884 *pp++ = (char *) "-V";
7885 *pp++ = cur->varstr;
7886 }
7887 }
7888# if ENABLE_HUSH_FUNCTIONS
7889 for (funcp = G.top_func; funcp; funcp = funcp->next) {
7890 *pp++ = (char *) "-F";
7891 *pp++ = funcp->name;
7892 *pp++ = funcp->body_as_string;
7893 }
7894# endif
7895 /* We can pass activated traps here. Say, -Tnn:trap_string
7896 *
7897 * However, POSIX says that subshells reset signals with traps
7898 * to SIG_DFL.
7899 * I tested bash-3.2 and it not only does that with true subshells
7900 * of the form ( list ), but with any forked children shells.
7901 * I set trap "echo W" WINCH; and then tried:
7902 *
7903 * { echo 1; sleep 20; echo 2; } &
7904 * while true; do echo 1; sleep 20; echo 2; break; done &
7905 * true | { echo 1; sleep 20; echo 2; } | cat
7906 *
7907 * In all these cases sending SIGWINCH to the child shell
7908 * did not run the trap. If I add trap "echo V" WINCH;
7909 * _inside_ group (just before echo 1), it works.
7910 *
7911 * I conclude it means we don't need to pass active traps here.
7912 */
7913 *pp++ = (char *) "-c";
7914 *pp++ = (char *) s;
7915 if (builtin_argv) {
7916 while (*++builtin_argv)
7917 *pp++ = *builtin_argv;
7918 *pp++ = (char *) "";
7919 }
7920 *pp++ = g_argv0;
7921 while (*g_argv)
7922 *pp++ = *g_argv++;
7923 /* *pp = NULL; - is already there */
7924 pp = environ;
7925
7926 do_exec:
7927 debug_printf_exec("re_execute_shell pid:%d cmd:'%s'\n", getpid(), s);
7928 /* Don't propagate SIG_IGN to the child */
7929 if (SPECIAL_JOBSTOP_SIGS != 0)
7930 switch_off_special_sigs(G.special_sig_mask & SPECIAL_JOBSTOP_SIGS);
7931 execve(bb_busybox_exec_path, argv, pp);
7932 /* Fallback. Useful for init=/bin/hush usage etc */
7933 if (argv[0][0] == '/')
7934 execve(argv[0], argv, pp);
7935 xfunc_error_retval = 127;
7936 bb_simple_error_msg_and_die("can't re-execute the shell");
7937}
7938#endif /* !BB_MMU */
7939
Francis Laniel8197f012023-12-22 22:02:28 +01007940#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01007941
7942static int run_and_free_list(struct pipe *pi);
7943
7944/* Executing from string: eval, sh -c '...'
7945 * or from file: /etc/profile, . file, sh <script>, sh (intereactive)
7946 * end_trigger controls how often we stop parsing
7947 * NUL: parse all, execute, return
7948 * ';': parse till ';' or newline, execute, repeat till EOF
7949 */
Francis Laniel3b13faf2023-12-22 22:02:34 +01007950#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01007951static void parse_and_run_stream(struct in_str *inp, int end_trigger)
Francis Laniel3b13faf2023-12-22 22:02:34 +01007952#else /* __U_BOOT__ */
7953static int parse_and_run_stream(struct in_str *inp, int end_trigger)
7954#endif /* __U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01007955{
7956 /* Why we need empty flag?
7957 * An obscure corner case "false; ``; echo $?":
7958 * empty command in `` should still set $? to 0.
7959 * But we can't just set $? to 0 at the start,
7960 * this breaks "false; echo `echo $?`" case.
7961 */
7962 bool empty = 1;
Francis Laniel3b13faf2023-12-22 22:02:34 +01007963#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01007964 while (1) {
Francis Laniel3b13faf2023-12-22 22:02:34 +01007965#else /* __U_BOOT__ */
7966 do {
7967#endif /* __U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01007968 struct pipe *pipe_list;
7969
7970#if ENABLE_HUSH_INTERACTIVE
7971 if (end_trigger == ';') {
7972 G.promptmode = 0; /* PS1 */
7973 debug_printf_prompt("%s promptmode=%d\n", __func__, G.promptmode);
7974 }
7975#endif
7976 pipe_list = parse_stream(NULL, NULL, inp, end_trigger);
7977 if (!pipe_list || pipe_list == ERR_PTR) { /* EOF/error */
7978 /* If we are in "big" script
7979 * (not in `cmd` or something similar)...
7980 */
7981 if (pipe_list == ERR_PTR && end_trigger == ';') {
7982 /* Discard cached input (rest of line) */
7983 int ch = inp->last_char;
7984 while (ch != EOF && ch != '\n') {
7985 //bb_error_msg("Discarded:'%c'", ch);
7986 ch = i_getch(inp);
7987 }
7988 /* Force prompt */
7989 inp->p = NULL;
7990 /* This stream isn't empty */
7991 empty = 0;
7992 continue;
7993 }
7994 if (!pipe_list && empty)
7995 G.last_exitcode = 0;
7996 break;
7997 }
7998 debug_print_tree(pipe_list, 0);
7999 debug_printf_exec("parse_and_run_stream: run_and_free_list\n");
Francis Laniel9a068372023-12-22 22:02:32 +01008000#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01008001 run_and_free_list(pipe_list);
Francis Laniel9a068372023-12-22 22:02:32 +01008002#else /* __U_BOOT__ */
8003 int rcode = run_and_free_list(pipe_list);
8004 /*
8005 * We reset input string to not run the following command, so running
8006 * 'exit; echo foo' does not print foo.
8007 */
8008 if (rcode <= EXIT_RET_CODE)
8009 setup_file_in_str(inp);
8010#endif /* __U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01008011 empty = 0;
8012 if (G_flag_return_in_progress == 1)
8013 break;
Francis Laniel3b13faf2023-12-22 22:02:34 +01008014#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01008015 }
Francis Laniel3b13faf2023-12-22 22:02:34 +01008016#else /* __U_BOOT__ */
8017 /*
8018 * This do/while is needed by run_command to avoid looping on a command
8019 * with syntax error.
8020 */
8021 } while (!(G.run_command_flags & FLAG_EXIT_FROM_LOOP));
8022
8023 return G.last_exitcode;
8024#endif /* __U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01008025}
8026
Francis Laniel8197f012023-12-22 22:02:28 +01008027#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01008028static void parse_and_run_string(const char *s)
Francis Laniel3b13faf2023-12-22 22:02:34 +01008029#else /* __U_BOOT__ */
8030static int parse_and_run_string(const char *s)
8031#endif /* __U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01008032{
8033 struct in_str input;
8034 //IF_HUSH_LINENO_VAR(unsigned sv = G.parse_lineno;)
8035
8036 setup_string_in_str(&input, s);
Francis Laniel3b13faf2023-12-22 22:02:34 +01008037#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01008038 parse_and_run_stream(&input, '\0');
Francis Laniel3b13faf2023-12-22 22:02:34 +01008039#else /* __U_BOOT__ */
8040 return parse_and_run_stream(&input, '\0');
8041#endif /* __U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01008042 //IF_HUSH_LINENO_VAR(G.parse_lineno = sv;)
8043}
8044
Francis Laniel3b13faf2023-12-22 22:02:34 +01008045#ifdef __U_BOOT__
Francis Laniel3ea3c572023-12-22 22:02:35 +01008046int parse_string_outer_modern(const char *cmd, int flags)
Francis Laniel3b13faf2023-12-22 22:02:34 +01008047{
8048 int ret;
8049 int old_flags;
8050
8051 /*
8052 * Keep old values of run_command to be able to restore them once
8053 * command was executed.
8054 */
8055 old_flags = G.run_command_flags;
8056 G.run_command_flags = flags;
8057
8058 ret = parse_and_run_string(cmd);
8059
8060 G.run_command_flags = old_flags;
8061
8062 return ret;
8063}
8064#endif /* __U_BOOT__ */
Francis Laniel8197f012023-12-22 22:02:28 +01008065#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01008066static void parse_and_run_file(HFILE *fp)
Francis Laniel8197f012023-12-22 22:02:28 +01008067#else /* __U_BOOT__ */
8068void parse_and_run_file(void)
8069#endif /* __U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01008070{
8071 struct in_str input;
Francis Laniel8197f012023-12-22 22:02:28 +01008072#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01008073 IF_HUSH_LINENO_VAR(unsigned sv = G.parse_lineno;)
8074
8075 IF_HUSH_LINENO_VAR(G.parse_lineno = 1;)
8076 setup_file_in_str(&input, fp);
Francis Laniel8197f012023-12-22 22:02:28 +01008077#else /* __U_BOOT__ */
8078 setup_file_in_str(&input);
8079#endif /* __U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01008080 parse_and_run_stream(&input, ';');
Francis Laniel8197f012023-12-22 22:02:28 +01008081#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01008082 IF_HUSH_LINENO_VAR(G.parse_lineno = sv;)
Francis Laniel8197f012023-12-22 22:02:28 +01008083#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01008084}
8085
Francis Laniel8197f012023-12-22 22:02:28 +01008086#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01008087#if ENABLE_HUSH_TICK
8088static int generate_stream_from_string(const char *s, pid_t *pid_p)
8089{
8090 pid_t pid;
8091 int channel[2];
8092# if !BB_MMU
8093 char **to_free = NULL;
8094# endif
8095
8096 xpipe(channel);
8097 pid = BB_MMU ? xfork() : xvfork();
8098 if (pid == 0) { /* child */
8099 disable_restore_tty_pgrp_on_exit();
8100 /* Process substitution is not considered to be usual
8101 * 'command execution'.
8102 * SUSv3 says ctrl-Z should be ignored, ctrl-C should not.
8103 */
8104 bb_signals(0
8105 + (1 << SIGTSTP)
8106 + (1 << SIGTTIN)
8107 + (1 << SIGTTOU)
8108 , SIG_IGN);
8109 close(channel[0]); /* NB: close _first_, then move fd! */
8110 xmove_fd(channel[1], 1);
8111# if ENABLE_HUSH_TRAP
8112 /* Awful hack for `trap` or $(trap).
8113 *
8114 * http://www.opengroup.org/onlinepubs/009695399/utilities/trap.html
8115 * contains an example where "trap" is executed in a subshell:
8116 *
8117 * save_traps=$(trap)
8118 * ...
8119 * eval "$save_traps"
8120 *
8121 * Standard does not say that "trap" in subshell shall print
8122 * parent shell's traps. It only says that its output
8123 * must have suitable form, but then, in the above example
8124 * (which is not supposed to be normative), it implies that.
8125 *
8126 * bash (and probably other shell) does implement it
8127 * (traps are reset to defaults, but "trap" still shows them),
8128 * but as a result, "trap" logic is hopelessly messed up:
8129 *
8130 * # trap
8131 * trap -- 'echo Ho' SIGWINCH <--- we have a handler
8132 * # (trap) <--- trap is in subshell - no output (correct, traps are reset)
8133 * # true | trap <--- trap is in subshell - no output (ditto)
8134 * # echo `true | trap` <--- in subshell - output (but traps are reset!)
8135 * trap -- 'echo Ho' SIGWINCH
8136 * # echo `(trap)` <--- in subshell in subshell - output
8137 * trap -- 'echo Ho' SIGWINCH
8138 * # echo `true | (trap)` <--- in subshell in subshell in subshell - output!
8139 * trap -- 'echo Ho' SIGWINCH
8140 *
8141 * The rules when to forget and when to not forget traps
8142 * get really complex and nonsensical.
8143 *
8144 * Our solution: ONLY bare $(trap) or `trap` is special.
8145 */
8146 s = skip_whitespace(s);
8147 if (is_prefixed_with(s, "trap")
8148 && skip_whitespace(s + 4)[0] == '\0'
8149 ) {
8150 static const char *const argv[] = { NULL, NULL };
8151 builtin_trap((char**)argv);
8152 fflush_all(); /* important */
8153 _exit(0);
8154 }
8155# endif
8156# if BB_MMU
8157 /* Prevent it from trying to handle ctrl-z etc */
8158 IF_HUSH_JOB(G.run_list_level = 1;)
8159 CLEAR_RANDOM_T(&G.random_gen); /* or else $RANDOM repeats in child */
8160 reset_traps_to_defaults();
8161 IF_HUSH_MODE_X(G.x_mode_depth++;)
8162 //bb_error_msg("%s: ++x_mode_depth=%d", __func__, G.x_mode_depth);
8163 parse_and_run_string(s);
8164 _exit(G.last_exitcode);
8165# else
8166 /* We re-execute after vfork on NOMMU. This makes this script safe:
8167 * yes "0123456789012345678901234567890" | dd bs=32 count=64k >BIG
8168 * huge=`cat BIG` # was blocking here forever
8169 * echo OK
8170 */
8171 re_execute_shell(&to_free,
8172 s,
8173 G.global_argv[0],
8174 G.global_argv + 1,
8175 NULL);
8176# endif
8177 }
8178
8179 /* parent */
8180 *pid_p = pid;
8181# if ENABLE_HUSH_FAST
8182 G.count_SIGCHLD++;
8183//bb_error_msg("[%d] fork in generate_stream_from_string:"
8184// " G.count_SIGCHLD:%d G.handled_SIGCHLD:%d",
8185// getpid(), G.count_SIGCHLD, G.handled_SIGCHLD);
8186# endif
8187 enable_restore_tty_pgrp_on_exit();
8188# if !BB_MMU
8189 free(to_free);
8190# endif
8191 close(channel[1]);
8192 return channel[0];
8193}
8194
8195/* Return code is exit status of the process that is run. */
8196static int process_command_subs(o_string *dest, const char *s)
8197{
8198 FILE *fp;
8199 pid_t pid;
8200 int status, ch, eol_cnt;
8201
8202 fp = xfdopen_for_read(generate_stream_from_string(s, &pid));
8203
8204 /* Now send results of command back into original context */
8205 eol_cnt = 0;
8206 while ((ch = getc(fp)) != EOF) {
8207 if (ch == '\0')
8208 continue;
8209 if (ch == '\n') {
8210 eol_cnt++;
8211 continue;
8212 }
8213 while (eol_cnt) {
8214 o_addchr(dest, '\n');
8215 eol_cnt--;
8216 }
8217 o_addQchr(dest, ch);
8218 }
8219
8220 debug_printf("done reading from `cmd` pipe, closing it\n");
8221 fclose(fp);
8222 /* We need to extract exitcode. Test case
8223 * "true; echo `sleep 1; false` $?"
8224 * should print 1 */
8225 safe_waitpid(pid, &status, 0);
8226 debug_printf("child exited. returning its exitcode:%d\n", WEXITSTATUS(status));
8227 return WEXITSTATUS(status);
8228}
8229#endif /* ENABLE_HUSH_TICK */
8230
8231
8232static void setup_heredoc(struct redir_struct *redir)
8233{
8234 struct fd_pair pair;
8235 pid_t pid;
8236 int len, written;
8237 /* the _body_ of heredoc (misleading field name) */
8238 const char *heredoc = redir->rd_filename;
8239 char *expanded;
8240#if !BB_MMU
8241 char **to_free;
8242#endif
8243
8244 expanded = NULL;
8245 if (!(redir->rd_dup & HEREDOC_QUOTED)) {
8246 expanded = encode_then_expand_string(heredoc);
8247 if (expanded)
8248 heredoc = expanded;
8249 }
8250 len = strlen(heredoc);
8251
8252 close(redir->rd_fd); /* often saves dup2+close in xmove_fd */
8253 xpiped_pair(pair);
8254 xmove_fd(pair.rd, redir->rd_fd);
8255
8256 /* Try writing without forking. Newer kernels have
8257 * dynamically growing pipes. Must use non-blocking write! */
8258 ndelay_on(pair.wr);
8259 while (1) {
8260 written = write(pair.wr, heredoc, len);
8261 if (written <= 0)
8262 break;
8263 len -= written;
8264 if (len == 0) {
8265 close(pair.wr);
8266 free(expanded);
8267 return;
8268 }
8269 heredoc += written;
8270 }
8271 ndelay_off(pair.wr);
8272
8273 /* Okay, pipe buffer was not big enough */
8274 /* Note: we must not create a stray child (bastard? :)
8275 * for the unsuspecting parent process. Child creates a grandchild
8276 * and exits before parent execs the process which consumes heredoc
8277 * (that exec happens after we return from this function) */
8278#if !BB_MMU
8279 to_free = NULL;
8280#endif
8281 pid = xvfork();
8282 if (pid == 0) {
8283 /* child */
8284 disable_restore_tty_pgrp_on_exit();
8285 pid = BB_MMU ? xfork() : xvfork();
8286 if (pid != 0)
8287 _exit(0);
8288 /* grandchild */
8289 close(redir->rd_fd); /* read side of the pipe */
8290#if BB_MMU
8291 full_write(pair.wr, heredoc, len); /* may loop or block */
8292 _exit(0);
8293#else
8294 /* Delegate blocking writes to another process */
8295 xmove_fd(pair.wr, STDOUT_FILENO);
8296 re_execute_shell(&to_free, heredoc, NULL, NULL, NULL);
8297#endif
8298 }
8299 /* parent */
8300#if ENABLE_HUSH_FAST
8301 G.count_SIGCHLD++;
8302//bb_error_msg("[%d] fork in setup_heredoc: G.count_SIGCHLD:%d G.handled_SIGCHLD:%d", getpid(), G.count_SIGCHLD, G.handled_SIGCHLD);
8303#endif
8304 enable_restore_tty_pgrp_on_exit();
8305#if !BB_MMU
8306 free(to_free);
8307#endif
8308 close(pair.wr);
8309 free(expanded);
8310 wait(NULL); /* wait till child has died */
8311}
8312
8313struct squirrel {
8314 int orig_fd;
8315 int moved_to;
8316 /* moved_to = n: fd was moved to n; restore back to orig_fd after redir */
8317 /* moved_to = -1: fd was opened by redirect; close orig_fd after redir */
8318};
8319
8320static struct squirrel *append_squirrel(struct squirrel *sq, int i, int orig, int moved)
8321{
8322 sq = xrealloc(sq, (i + 2) * sizeof(sq[0]));
8323 sq[i].orig_fd = orig;
8324 sq[i].moved_to = moved;
8325 sq[i+1].orig_fd = -1; /* end marker */
8326 return sq;
8327}
8328
8329static struct squirrel *add_squirrel(struct squirrel *sq, int fd, int avoid_fd)
8330{
8331 int moved_to;
8332 int i;
8333
8334 i = 0;
8335 if (sq) for (; sq[i].orig_fd >= 0; i++) {
8336 /* If we collide with an already moved fd... */
8337 if (fd == sq[i].moved_to) {
8338 sq[i].moved_to = dup_CLOEXEC(sq[i].moved_to, avoid_fd);
8339 debug_printf_redir("redirect_fd %d: already busy, moving to %d\n", fd, sq[i].moved_to);
8340 if (sq[i].moved_to < 0) /* what? */
8341 xfunc_die();
8342 return sq;
8343 }
8344 if (fd == sq[i].orig_fd) {
8345 /* Example: echo Hello >/dev/null 1>&2 */
8346 debug_printf_redir("redirect_fd %d: already moved\n", fd);
8347 return sq;
8348 }
8349 }
8350
8351 /* If this fd is open, we move and remember it; if it's closed, moved_to = -1 */
8352 moved_to = dup_CLOEXEC(fd, avoid_fd);
8353 debug_printf_redir("redirect_fd %d: previous fd is moved to %d (-1 if it was closed)\n", fd, moved_to);
8354 if (moved_to < 0 && errno != EBADF)
8355 xfunc_die();
8356 return append_squirrel(sq, i, fd, moved_to);
8357}
8358
8359static struct squirrel *add_squirrel_closed(struct squirrel *sq, int fd)
8360{
8361 int i;
8362
8363 i = 0;
8364 if (sq) for (; sq[i].orig_fd >= 0; i++) {
8365 /* If we collide with an already moved fd... */
8366 if (fd == sq[i].orig_fd) {
8367 /* Examples:
8368 * "echo 3>FILE 3>&- 3>FILE"
8369 * "echo 3>&- 3>FILE"
8370 * No need for last redirect to insert
8371 * another "need to close 3" indicator.
8372 */
8373 debug_printf_redir("redirect_fd %d: already moved or closed\n", fd);
8374 return sq;
8375 }
8376 }
8377
8378 debug_printf_redir("redirect_fd %d: previous fd was closed\n", fd);
8379 return append_squirrel(sq, i, fd, -1);
8380}
8381
8382/* fd: redirect wants this fd to be used (e.g. 3>file).
8383 * Move all conflicting internally used fds,
8384 * and remember them so that we can restore them later.
8385 */
8386static int save_fd_on_redirect(int fd, int avoid_fd, struct squirrel **sqp)
8387{
8388 if (avoid_fd < 9) /* the important case here is that it can be -1 */
8389 avoid_fd = 9;
8390
8391#if ENABLE_HUSH_INTERACTIVE
8392 if (fd != 0 /* don't trigger for G_interactive_fd == 0 (that's "not interactive" flag) */
8393 && fd == G_interactive_fd
8394 ) {
8395 /* Testcase: "ls -l /proc/$$/fd 255>&-" should work */
8396 G_interactive_fd = xdup_CLOEXEC_and_close(G_interactive_fd, avoid_fd);
8397 debug_printf_redir("redirect_fd %d: matches interactive_fd, moving it to %d\n", fd, G_interactive_fd);
8398 return 1; /* "we closed fd" */
8399 }
8400#endif
8401 /* Are we called from setup_redirects(squirrel==NULL)
8402 * in redirect in a [v]forked child?
8403 */
8404 if (sqp == NULL) {
8405 /* No need to move script fds.
8406 * For NOMMU case, it's actively wrong: we'd change ->fd
8407 * fields in memory for the parent, but parent's fds
8408 * aren't moved, it would use wrong fd!
8409 * Reproducer: "cmd 3>FILE" in script.
8410 * If we would call move_HFILEs_on_redirect(), child would:
8411 * fcntl64(3, F_DUPFD_CLOEXEC, 10) = 10
8412 * close(3) = 0
8413 * and change ->fd to 10 if fd#3 is a script fd. WRONG.
8414 */
8415 //bb_error_msg("sqp == NULL: [v]forked child");
8416 return 0;
8417 }
8418
8419 /* If this one of script's fds? */
8420 if (move_HFILEs_on_redirect(fd, avoid_fd))
8421 return 1; /* yes. "we closed fd" (actually moved it) */
8422
8423 /* Are we called for "exec 3>FILE"? Came through
8424 * redirect_and_varexp_helper(squirrel=ERR_PTR) -> setup_redirects(ERR_PTR)
8425 * This case used to fail for this script:
8426 * exec 3>FILE
8427 * echo Ok
8428 * ...100000 more lines...
8429 * echo Ok
8430 * as follows:
8431 * read(3, "exec 3>FILE\necho Ok\necho Ok"..., 1024) = 1024
8432 * open("FILE", O_WRONLY|O_CREAT|O_TRUNC|O_LARGEFILE, 0666) = 4
8433 * dup2(4, 3) = 3
8434 * ^^^^^^^^ oops, we lost fd#3 opened to our script!
8435 * close(4) = 0
8436 * write(1, "Ok\n", 3) = 3
8437 * ... = 3
8438 * write(1, "Ok\n", 3) = 3
8439 * read(3, 0x94fbc08, 1024) = -1 EBADF (Bad file descriptor)
8440 * ^^^^^^^^ oops, wrong fd!!!
8441 * With this case separate from sqp == NULL and *after* move_HFILEs,
8442 * it now works:
8443 */
8444 if (sqp == ERR_PTR) {
8445 /* Don't preserve redirected fds: exec is _meant_ to change these */
8446 //bb_error_msg("sqp == ERR_PTR: exec >FILE");
8447 return 0;
8448 }
8449
8450 /* Check whether it collides with any open fds (e.g. stdio), save fds as needed */
8451 *sqp = add_squirrel(*sqp, fd, avoid_fd);
8452 return 0; /* "we did not close fd" */
8453}
8454
8455static void restore_redirects(struct squirrel *sq)
8456{
8457 if (sq) {
8458 int i;
8459 for (i = 0; sq[i].orig_fd >= 0; i++) {
8460 if (sq[i].moved_to >= 0) {
8461 /* We simply die on error */
8462 debug_printf_redir("restoring redirected fd from %d to %d\n", sq[i].moved_to, sq[i].orig_fd);
8463 xmove_fd(sq[i].moved_to, sq[i].orig_fd);
8464 } else {
8465 /* cmd1 9>FILE; cmd2_should_see_fd9_closed */
8466 debug_printf_redir("restoring redirected fd %d: closing it\n", sq[i].orig_fd);
8467 close(sq[i].orig_fd);
8468 }
8469 }
8470 free(sq);
8471 }
8472 if (G.HFILE_stdin
8473 && G.HFILE_stdin->fd > STDIN_FILENO
8474 /* we compare > STDIN, not == STDIN, since hfgetc()
8475 * closes fd and sets ->fd to -1 if EOF is reached.
8476 * Testcase: echo 'pwd' | hush
8477 */
8478 ) {
8479 /* Testcase: interactive "read r <FILE; echo $r; read r; echo $r".
8480 * Redirect moves ->fd to e.g. 10,
8481 * and it is not restored above (we do not restore script fds
8482 * after redirects, we just use new, "moved" fds).
8483 * However for stdin, get_user_input() -> read_line_input(),
8484 * and read builtin, depend on fd == STDIN_FILENO.
8485 */
8486 debug_printf_redir("restoring %d to stdin\n", G.HFILE_stdin->fd);
8487 xmove_fd(G.HFILE_stdin->fd, STDIN_FILENO);
8488 G.HFILE_stdin->fd = STDIN_FILENO;
8489 }
8490
8491 /* If moved, G_interactive_fd stays on new fd, not restoring it */
8492}
8493
8494#if ENABLE_FEATURE_SH_STANDALONE && BB_MMU
8495static void close_saved_fds_and_FILE_fds(void)
8496{
8497 if (G_interactive_fd)
8498 close(G_interactive_fd);
8499 close_all_HFILE_list();
8500}
8501#endif
8502
8503static int internally_opened_fd(int fd, struct squirrel *sq)
8504{
8505 int i;
8506
8507#if ENABLE_HUSH_INTERACTIVE
8508 if (fd == G_interactive_fd)
8509 return 1;
8510#endif
8511 /* If this one of script's fds? */
8512 if (fd_in_HFILEs(fd))
8513 return 1;
8514
8515 if (sq) for (i = 0; sq[i].orig_fd >= 0; i++) {
8516 if (fd == sq[i].moved_to)
8517 return 1;
8518 }
8519 return 0;
8520}
8521
8522/* squirrel != NULL means we squirrel away copies of stdin, stdout,
8523 * and stderr if they are redirected. */
8524static int setup_redirects(struct command *prog, struct squirrel **sqp)
8525{
8526 struct redir_struct *redir;
8527
8528 for (redir = prog->redirects; redir; redir = redir->next) {
8529 int newfd;
8530 int closed;
8531
8532 if (redir->rd_type == REDIRECT_HEREDOC2) {
8533 /* "rd_fd<<HERE" case */
8534 save_fd_on_redirect(redir->rd_fd, /*avoid:*/ 0, sqp);
8535 /* for REDIRECT_HEREDOC2, rd_filename holds _contents_
8536 * of the heredoc */
8537 debug_printf_redir("set heredoc '%s'\n",
8538 redir->rd_filename);
8539 setup_heredoc(redir);
8540 continue;
8541 }
8542
8543 if (redir->rd_dup == REDIRFD_TO_FILE) {
8544 /* "rd_fd<*>file" case (<*> is <,>,>>,<>) */
8545 char *p;
8546 int mode;
8547
8548 if (redir->rd_filename == NULL) {
8549 /* Examples:
8550 * "cmd >" (no filename)
8551 * "cmd > <file" (2nd redirect starts too early)
8552 */
8553 syntax_error("invalid redirect");
8554 continue;
8555 }
8556 mode = redir_table[redir->rd_type].mode;
8557 p = expand_string_to_string(redir->rd_filename,
8558 EXP_FLAG_ESC_GLOB_CHARS, /*unbackslash:*/ 1);
8559 newfd = open_or_warn(p, mode);
8560 free(p);
8561 if (newfd < 0) {
8562 /* Error message from open_or_warn can be lost
8563 * if stderr has been redirected, but bash
8564 * and ash both lose it as well
8565 * (though zsh doesn't!)
8566 */
8567 return 1;
8568 }
8569 if (newfd == redir->rd_fd && sqp) {
8570 /* open() gave us precisely the fd we wanted.
8571 * This means that this fd was not busy
8572 * (not opened to anywhere).
8573 * Remember to close it on restore:
8574 */
8575 *sqp = add_squirrel_closed(*sqp, newfd);
8576 debug_printf_redir("redir to previously closed fd %d\n", newfd);
8577 }
8578 } else {
8579 /* "rd_fd>&rd_dup" or "rd_fd>&-" case */
8580 newfd = redir->rd_dup;
8581 }
8582
8583 if (newfd == redir->rd_fd)
8584 continue;
8585
8586 /* if "N>FILE": move newfd to redir->rd_fd */
8587 /* if "N>&M": dup newfd to redir->rd_fd */
8588 /* if "N>&-": close redir->rd_fd (newfd is REDIRFD_CLOSE) */
8589
8590 closed = save_fd_on_redirect(redir->rd_fd, /*avoid:*/ newfd, sqp);
8591 if (newfd == REDIRFD_CLOSE) {
8592 /* "N>&-" means "close me" */
8593 if (!closed) {
8594 /* ^^^ optimization: saving may already
8595 * have closed it. If not... */
8596 close(redir->rd_fd);
8597 }
8598 /* Sometimes we do another close on restore, getting EBADF.
8599 * Consider "echo 3>FILE 3>&-"
8600 * first redirect remembers "need to close 3",
8601 * and second redirect closes 3! Restore code then closes 3 again.
8602 */
8603 } else {
8604 /* if newfd is a script fd or saved fd, simulate EBADF */
8605 if (internally_opened_fd(newfd, sqp && sqp != ERR_PTR ? *sqp : NULL)) {
8606 //errno = EBADF;
8607 //bb_perror_msg_and_die("can't duplicate file descriptor");
8608 newfd = -1; /* same effect as code above */
8609 }
8610 xdup2(newfd, redir->rd_fd);
8611 if (redir->rd_dup == REDIRFD_TO_FILE)
8612 /* "rd_fd > FILE" */
8613 close(newfd);
8614 /* else: "rd_fd > rd_dup" */
8615 }
8616 }
8617 return 0;
8618}
8619
8620static char *find_in_path(const char *arg)
8621{
8622 char *ret = NULL;
8623 const char *PATH = get_local_var_value("PATH");
8624
8625 if (!PATH)
8626 return NULL;
8627
8628 while (1) {
8629 const char *end = strchrnul(PATH, ':');
8630 int sz = end - PATH; /* must be int! */
8631
8632 free(ret);
8633 if (sz != 0) {
8634 ret = xasprintf("%.*s/%s", sz, PATH, arg);
8635 } else {
8636 /* We have xxx::yyyy in $PATH,
8637 * it means "use current dir" */
8638 ret = xstrdup(arg);
8639 }
8640 if (access(ret, F_OK) == 0)
8641 break;
8642
8643 if (*end == '\0') {
8644 free(ret);
8645 return NULL;
8646 }
8647 PATH = end + 1;
8648 }
8649
8650 return ret;
8651}
8652
8653static const struct built_in_command *find_builtin_helper(const char *name,
8654 const struct built_in_command *x,
8655 const struct built_in_command *end)
8656{
8657 while (x != end) {
8658 if (strcmp(name, x->b_cmd) != 0) {
8659 x++;
8660 continue;
8661 }
8662 debug_printf_exec("found builtin '%s'\n", name);
8663 return x;
8664 }
8665 return NULL;
8666}
8667static const struct built_in_command *find_builtin1(const char *name)
8668{
8669 return find_builtin_helper(name, bltins1, &bltins1[ARRAY_SIZE(bltins1)]);
8670}
8671static const struct built_in_command *find_builtin(const char *name)
8672{
8673 const struct built_in_command *x = find_builtin1(name);
8674 if (x)
8675 return x;
8676 return find_builtin_helper(name, bltins2, &bltins2[ARRAY_SIZE(bltins2)]);
8677}
8678
8679#if ENABLE_HUSH_JOB && EDITING_HAS_get_exe_name
8680static const char * FAST_FUNC get_builtin_name(int i)
8681{
8682 if (/*i >= 0 && */ i < ARRAY_SIZE(bltins1)) {
8683 return bltins1[i].b_cmd;
8684 }
8685 i -= ARRAY_SIZE(bltins1);
8686 if (i < ARRAY_SIZE(bltins2)) {
8687 return bltins2[i].b_cmd;
8688 }
8689 return NULL;
8690}
8691#endif
Francis Laniel8197f012023-12-22 22:02:28 +01008692#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01008693
Francis Laniel8197f012023-12-22 22:02:28 +01008694#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01008695static void remove_nested_vars(void)
8696{
8697 struct variable *cur;
8698 struct variable **cur_pp;
8699
8700 cur_pp = &G.top_var;
8701 while ((cur = *cur_pp) != NULL) {
8702 if (cur->var_nest_level <= G.var_nest_level) {
8703 cur_pp = &cur->next;
8704 continue;
8705 }
8706 /* Unexport */
8707 if (cur->flg_export) {
8708 debug_printf_env("unexporting nested '%s'/%u\n", cur->varstr, cur->var_nest_level);
8709 bb_unsetenv(cur->varstr);
8710 }
8711 /* Remove from global list */
8712 *cur_pp = cur->next;
8713 /* Free */
8714 if (!cur->max_len) {
8715 debug_printf_env("freeing nested '%s'/%u\n", cur->varstr, cur->var_nest_level);
8716 free(cur->varstr);
8717 }
8718 free(cur);
8719 }
8720}
8721
8722static void enter_var_nest_level(void)
8723{
8724 G.var_nest_level++;
8725 debug_printf_env("var_nest_level++ %u\n", G.var_nest_level);
8726
8727 /* Try: f() { echo -n .; f; }; f
8728 * struct variable::var_nest_level is uint16_t,
8729 * thus limiting recursion to < 2^16.
8730 * In any case, with 8 Mbyte stack SEGV happens
8731 * not too long after 2^16 recursions anyway.
8732 */
8733 if (G.var_nest_level > 0xff00)
8734 bb_error_msg_and_die("fatal recursion (depth %u)", G.var_nest_level);
8735}
8736
8737static void leave_var_nest_level(void)
8738{
8739 G.var_nest_level--;
8740 debug_printf_env("var_nest_level-- %u\n", G.var_nest_level);
8741 if (HUSH_DEBUG && (int)G.var_nest_level < 0)
8742 bb_simple_error_msg_and_die("BUG: nesting underflow");
8743
8744 remove_nested_vars();
8745}
Francis Laniel8197f012023-12-22 22:02:28 +01008746#endif /* __U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01008747
8748#if ENABLE_HUSH_FUNCTIONS
8749static struct function **find_function_slot(const char *name)
8750{
8751 struct function *funcp;
8752 struct function **funcpp = &G.top_func;
8753
8754 while ((funcp = *funcpp) != NULL) {
8755 if (strcmp(name, funcp->name) == 0) {
8756 debug_printf_exec("found function '%s'\n", name);
8757 break;
8758 }
8759 funcpp = &funcp->next;
8760 }
8761 return funcpp;
8762}
8763
8764static ALWAYS_INLINE const struct function *find_function(const char *name)
8765{
8766 const struct function *funcp = *find_function_slot(name);
8767 return funcp;
8768}
8769
8770/* Note: takes ownership on name ptr */
8771static struct function *new_function(char *name)
8772{
8773 struct function **funcpp = find_function_slot(name);
8774 struct function *funcp = *funcpp;
8775
8776 if (funcp != NULL) {
8777 struct command *cmd = funcp->parent_cmd;
8778 debug_printf_exec("func %p parent_cmd %p\n", funcp, cmd);
8779 if (!cmd) {
8780 debug_printf_exec("freeing & replacing function '%s'\n", funcp->name);
8781 free(funcp->name);
8782 /* Note: if !funcp->body, do not free body_as_string!
8783 * This is a special case of "-F name body" function:
8784 * body_as_string was not malloced! */
8785 if (funcp->body) {
8786 free_pipe_list(funcp->body);
8787# if !BB_MMU
8788 free(funcp->body_as_string);
8789# endif
8790 }
8791 } else {
8792 debug_printf_exec("reinserting in tree & replacing function '%s'\n", funcp->name);
8793 cmd->argv[0] = funcp->name;
8794 cmd->group = funcp->body;
8795# if !BB_MMU
8796 cmd->group_as_string = funcp->body_as_string;
8797# endif
8798 }
8799 } else {
8800 debug_printf_exec("remembering new function '%s'\n", name);
8801 funcp = *funcpp = xzalloc(sizeof(*funcp));
8802 /*funcp->next = NULL;*/
8803 }
8804
8805 funcp->name = name;
8806 return funcp;
8807}
8808
8809# if ENABLE_HUSH_UNSET
8810static void unset_func(const char *name)
8811{
8812 struct function **funcpp = find_function_slot(name);
8813 struct function *funcp = *funcpp;
8814
8815 if (funcp != NULL) {
8816 debug_printf_exec("freeing function '%s'\n", funcp->name);
8817 *funcpp = funcp->next;
8818 /* funcp is unlinked now, deleting it.
8819 * Note: if !funcp->body, the function was created by
8820 * "-F name body", do not free ->body_as_string
8821 * and ->name as they were not malloced. */
8822 if (funcp->body) {
8823 free_pipe_list(funcp->body);
8824 free(funcp->name);
8825# if !BB_MMU
8826 free(funcp->body_as_string);
8827# endif
8828 }
8829 free(funcp);
8830 }
8831}
8832# endif
8833
8834# if BB_MMU
8835#define exec_function(to_free, funcp, argv) \
8836 exec_function(funcp, argv)
8837# endif
8838static void exec_function(char ***to_free,
8839 const struct function *funcp,
8840 char **argv) NORETURN;
8841static void exec_function(char ***to_free,
8842 const struct function *funcp,
8843 char **argv)
8844{
8845# if BB_MMU
8846 int n;
8847
8848 argv[0] = G.global_argv[0];
8849 G.global_argv = argv;
8850 G.global_argc = n = 1 + string_array_len(argv + 1);
8851
8852// Example when we are here: "cmd | func"
8853// func will run with saved-redirect fds open.
8854// $ f() { echo /proc/self/fd/*; }
8855// $ true | f
8856// /proc/self/fd/0 /proc/self/fd/1 /proc/self/fd/2 /proc/self/fd/255 /proc/self/fd/3
8857// stdio^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ G_interactive_fd^ DIR fd for glob
8858// Same in script:
8859// $ . ./SCRIPT
8860// /proc/self/fd/0 /proc/self/fd/1 /proc/self/fd/2 /proc/self/fd/255 /proc/self/fd/3 /proc/self/fd/4
8861// stdio^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ G_interactive_fd^ opened ./SCRIPT DIR fd for glob
8862// They are CLOEXEC so external programs won't see them, but
8863// for "more correctness" we might want to close those extra fds here:
8864//? close_saved_fds_and_FILE_fds();
8865
8866 /* "we are in a function, ok to use return" */
8867 G_flag_return_in_progress = -1;
8868 enter_var_nest_level();
8869 IF_HUSH_LOCAL(G.func_nest_level++;)
8870
8871 /* On MMU, funcp->body is always non-NULL */
8872 n = run_list(funcp->body);
8873 _exit(n);
8874# else
8875//? close_saved_fds_and_FILE_fds();
8876
8877//TODO: check whether "true | func_with_return" works
8878
8879 re_execute_shell(to_free,
8880 funcp->body_as_string,
8881 G.global_argv[0],
8882 argv + 1,
8883 NULL);
8884# endif
8885}
8886
8887static int run_function(const struct function *funcp, char **argv)
8888{
8889 int rc;
8890 save_arg_t sv;
8891 smallint sv_flg;
8892
8893 save_and_replace_G_args(&sv, argv);
8894
8895 /* "We are in function, ok to use return" */
8896 sv_flg = G_flag_return_in_progress;
8897 G_flag_return_in_progress = -1;
8898
8899 /* Make "local" variables properly shadow previous ones */
8900 IF_HUSH_LOCAL(enter_var_nest_level();)
8901 IF_HUSH_LOCAL(G.func_nest_level++;)
8902
8903 /* On MMU, funcp->body is always non-NULL */
8904# if !BB_MMU
8905 if (!funcp->body) {
8906 /* Function defined by -F */
8907 parse_and_run_string(funcp->body_as_string);
8908 rc = G.last_exitcode;
8909 } else
8910# endif
8911 {
8912 rc = run_list(funcp->body);
8913 }
8914
8915 IF_HUSH_LOCAL(G.func_nest_level--;)
8916 IF_HUSH_LOCAL(leave_var_nest_level();)
8917
8918 G_flag_return_in_progress = sv_flg;
8919# if ENABLE_HUSH_TRAP
8920 debug_printf_exec("G.return_exitcode=-1\n");
8921 G.return_exitcode = -1; /* invalidate stashed return value */
8922# endif
8923
8924 restore_G_args(&sv, argv);
8925
8926 return rc;
8927}
8928#endif /* ENABLE_HUSH_FUNCTIONS */
8929
8930
Francis Laniel8197f012023-12-22 22:02:28 +01008931#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01008932#if BB_MMU
8933#define exec_builtin(to_free, x, argv) \
8934 exec_builtin(x, argv)
8935#else
8936#define exec_builtin(to_free, x, argv) \
8937 exec_builtin(to_free, argv)
8938#endif
8939static void exec_builtin(char ***to_free,
8940 const struct built_in_command *x,
8941 char **argv) NORETURN;
8942static void exec_builtin(char ***to_free,
8943 const struct built_in_command *x,
8944 char **argv)
8945{
8946#if BB_MMU
8947 int rcode;
8948//? close_saved_fds_and_FILE_fds();
8949 rcode = x->b_function(argv);
8950 fflush_all();
8951 _exit(rcode);
8952#else
8953 fflush_all();
8954 /* On NOMMU, we must never block!
8955 * Example: { sleep 99 | read line; } & echo Ok
8956 */
8957 re_execute_shell(to_free,
8958 argv[0],
8959 G.global_argv[0],
8960 G.global_argv + 1,
8961 argv);
8962#endif
8963}
Francis Laniel8197f012023-12-22 22:02:28 +01008964#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01008965
8966
Francis Laniel8197f012023-12-22 22:02:28 +01008967#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01008968static void execvp_or_die(char **argv) NORETURN;
8969static void execvp_or_die(char **argv)
8970{
8971 int e;
8972 debug_printf_exec("execing '%s'\n", argv[0]);
8973 /* Don't propagate SIG_IGN to the child */
8974 if (SPECIAL_JOBSTOP_SIGS != 0)
8975 switch_off_special_sigs(G.special_sig_mask & SPECIAL_JOBSTOP_SIGS);
8976 execvp(argv[0], argv);
8977 e = 2;
8978 if (errno == EACCES) e = 126;
8979 if (errno == ENOENT) e = 127;
8980 bb_perror_msg("can't execute '%s'", argv[0]);
8981 _exit(e);
8982}
8983
8984#if ENABLE_HUSH_MODE_X
8985static void x_mode_print_optionally_squoted(const char *str)
8986{
8987 unsigned len;
8988 const char *cp;
8989
8990 cp = str;
8991
8992 /* the set of chars which-cause-string-to-be-squoted mimics bash */
8993 /* test a char with: bash -c 'set -x; echo "CH"' */
8994 if (str[strcspn(str, "\\\"'`$(){}[]<>;#&|~*?!^"
8995 " " "\001\002\003\004\005\006\007"
8996 "\010\011\012\013\014\015\016\017"
8997 "\020\021\022\023\024\025\026\027"
8998 "\030\031\032\033\034\035\036\037"
8999 )
9000 ] == '\0'
9001 ) {
9002 /* string has no special chars */
9003 x_mode_addstr(str);
9004 return;
9005 }
9006
9007 cp = str;
9008 for (;;) {
9009 /* print '....' up to EOL or first squote */
9010 len = (int)(strchrnul(cp, '\'') - cp);
9011 if (len != 0) {
9012 x_mode_addchr('\'');
9013 x_mode_addblock(cp, len);
9014 x_mode_addchr('\'');
9015 cp += len;
9016 }
9017 if (*cp == '\0')
9018 break;
9019 /* string contains squote(s), print them as \' */
9020 x_mode_addchr('\\');
9021 x_mode_addchr('\'');
9022 cp++;
9023 }
9024}
9025static void dump_cmd_in_x_mode(char **argv)
9026{
9027 if (G_x_mode && argv) {
9028 unsigned n;
9029
9030 /* "+[+++...][ cmd...]\n\0" */
9031 x_mode_prefix();
9032 n = 0;
9033 while (argv[n]) {
9034 x_mode_addchr(' ');
9035 if (argv[n][0] == '\0') {
9036 x_mode_addchr('\'');
9037 x_mode_addchr('\'');
9038 } else {
9039 x_mode_print_optionally_squoted(argv[n]);
9040 }
9041 n++;
9042 }
9043 x_mode_flush();
9044 }
9045}
9046#else
9047# define dump_cmd_in_x_mode(argv) ((void)0)
9048#endif
Francis Laniel8197f012023-12-22 22:02:28 +01009049#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01009050
Francis Laniel8197f012023-12-22 22:02:28 +01009051#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01009052#if ENABLE_HUSH_COMMAND
9053static void if_command_vV_print_and_exit(char opt_vV, char *cmd, const char *explanation)
9054{
9055 char *to_free;
9056
9057 if (!opt_vV)
9058 return;
9059
9060 to_free = NULL;
9061 if (!explanation) {
9062 char *path = getenv("PATH");
9063 explanation = to_free = find_executable(cmd, &path); /* path == NULL is ok */
9064 if (!explanation)
9065 _exit(1); /* PROG was not found */
9066 if (opt_vV != 'V')
9067 cmd = to_free; /* -v PROG prints "/path/to/PROG" */
9068 }
9069 printf((opt_vV == 'V') ? "%s is %s\n" : "%s\n", cmd, explanation);
9070 free(to_free);
9071 fflush_all();
9072 _exit(0);
9073}
9074#else
9075# define if_command_vV_print_and_exit(a,b,c) ((void)0)
9076#endif
Francis Laniel8197f012023-12-22 22:02:28 +01009077#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01009078
9079#if BB_MMU
9080#define pseudo_exec_argv(nommu_save, argv, assignment_cnt, argv_expanded) \
9081 pseudo_exec_argv(argv, assignment_cnt, argv_expanded)
9082#define pseudo_exec(nommu_save, command, argv_expanded) \
9083 pseudo_exec(command, argv_expanded)
9084#endif
9085
Francis Laniel8197f012023-12-22 22:02:28 +01009086#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01009087/* Called after [v]fork() in run_pipe, or from builtin_exec.
9088 * Never returns.
9089 * Don't exit() here. If you don't exec, use _exit instead.
9090 * The at_exit handlers apparently confuse the calling process,
9091 * in particular stdin handling. Not sure why? -- because of vfork! (vda)
9092 */
9093static void pseudo_exec_argv(nommu_save_t *nommu_save,
9094 char **argv, int assignment_cnt,
9095 char **argv_expanded) NORETURN;
9096static NOINLINE void pseudo_exec_argv(nommu_save_t *nommu_save,
9097 char **argv, int assignment_cnt,
9098 char **argv_expanded)
9099{
9100 const struct built_in_command *x;
9101 struct variable **sv_shadowed;
9102 char **new_env;
9103 IF_HUSH_COMMAND(char opt_vV = 0;)
9104 IF_HUSH_FUNCTIONS(const struct function *funcp;)
9105
9106 new_env = expand_assignments(argv, assignment_cnt);
9107 dump_cmd_in_x_mode(new_env);
9108
9109 if (!argv[assignment_cnt]) {
9110 /* Case when we are here: ... | var=val | ...
9111 * (note that we do not exit early, i.e., do not optimize out
9112 * expand_assignments(): think about ... | var=`sleep 1` | ...
9113 */
9114 free_strings(new_env);
9115 _exit(EXIT_SUCCESS);
9116 }
9117
9118 sv_shadowed = G.shadowed_vars_pp;
9119#if BB_MMU
9120 G.shadowed_vars_pp = NULL; /* "don't save, free them instead" */
9121#else
9122 G.shadowed_vars_pp = &nommu_save->old_vars;
9123 G.var_nest_level++;
9124#endif
9125 set_vars_and_save_old(new_env);
9126 G.shadowed_vars_pp = sv_shadowed;
9127
9128 if (argv_expanded) {
9129 argv = argv_expanded;
9130 } else {
9131 argv = expand_strvec_to_strvec(argv + assignment_cnt);
9132#if !BB_MMU
9133 nommu_save->argv = argv;
9134#endif
9135 }
9136 dump_cmd_in_x_mode(argv);
9137
9138#if ENABLE_FEATURE_SH_STANDALONE || BB_MMU
9139 if (strchr(argv[0], '/') != NULL)
9140 goto skip;
9141#endif
9142
9143#if ENABLE_HUSH_FUNCTIONS
9144 /* Check if the command matches any functions (this goes before bltins) */
9145 funcp = find_function(argv[0]);
9146 if (funcp)
9147 exec_function(&nommu_save->argv_from_re_execing, funcp, argv);
9148#endif
9149
9150#if ENABLE_HUSH_COMMAND
9151 /* "command BAR": run BAR without looking it up among functions
9152 * "command -v BAR": print "BAR" or "/path/to/BAR"; or exit 1
9153 * "command -V BAR": print "BAR is {a function,a shell builtin,/path/to/BAR}"
9154 */
9155 while (strcmp(argv[0], "command") == 0 && argv[1]) {
9156 char *p;
9157
9158 argv++;
9159 p = *argv;
9160 if (p[0] != '-' || !p[1])
9161 continue; /* bash allows "command command command [-OPT] BAR" */
9162
9163 for (;;) {
9164 p++;
9165 switch (*p) {
9166 case '\0':
9167 argv++;
9168 p = *argv;
9169 if (p[0] != '-' || !p[1])
9170 goto after_opts;
9171 continue; /* next arg is also -opts, process it too */
9172 case 'v':
9173 case 'V':
9174 opt_vV = *p;
9175 continue;
9176 default:
9177 bb_error_msg_and_die("%s: %s: invalid option", "command", argv[0]);
9178 }
9179 }
9180 }
9181 after_opts:
9182# if ENABLE_HUSH_FUNCTIONS
9183 if (opt_vV && find_function(argv[0]))
9184 if_command_vV_print_and_exit(opt_vV, argv[0], "a function");
9185# endif
9186#endif
9187
9188 /* Check if the command matches any of the builtins.
9189 * Depending on context, this might be redundant. But it's
9190 * easier to waste a few CPU cycles than it is to figure out
9191 * if this is one of those cases.
9192 */
9193 /* Why "BB_MMU ? :" difference in logic? -
9194 * On NOMMU, it is more expensive to re-execute shell
9195 * just in order to run echo or test builtin.
9196 * It's better to skip it here and run corresponding
9197 * non-builtin later. */
9198 x = BB_MMU ? find_builtin(argv[0]) : find_builtin1(argv[0]);
9199 if (x) {
9200 if_command_vV_print_and_exit(opt_vV, argv[0], "a shell builtin");
9201 exec_builtin(&nommu_save->argv_from_re_execing, x, argv);
9202 }
9203
9204#if ENABLE_FEATURE_SH_STANDALONE
9205 /* Check if the command matches any busybox applets */
9206 {
9207 int a = find_applet_by_name(argv[0]);
9208 if (a >= 0) {
9209 if_command_vV_print_and_exit(opt_vV, argv[0], "an applet");
9210# if BB_MMU /* see above why on NOMMU it is not allowed */
9211 if (APPLET_IS_NOEXEC(a)) {
9212 /* Do not leak open fds from opened script files etc.
9213 * Testcase: interactive "ls -l /proc/self/fd"
9214 * should not show tty fd open.
9215 */
9216 close_saved_fds_and_FILE_fds();
9217//FIXME: should also close saved redir fds
9218//This casuses test failures in
9219//redir_children_should_not_see_saved_fd_2.tests
9220//redir_children_should_not_see_saved_fd_3.tests
9221//if you replace "busybox find" with just "find" in them
9222 /* Without this, "rm -i FILE" can't be ^C'ed: */
9223 switch_off_special_sigs(G.special_sig_mask);
9224 debug_printf_exec("running applet '%s'\n", argv[0]);
9225 run_noexec_applet_and_exit(a, argv[0], argv);
9226 }
9227# endif
9228 /* Re-exec ourselves */
9229 debug_printf_exec("re-execing applet '%s'\n", argv[0]);
9230 /* Don't propagate SIG_IGN to the child */
9231 if (SPECIAL_JOBSTOP_SIGS != 0)
9232 switch_off_special_sigs(G.special_sig_mask & SPECIAL_JOBSTOP_SIGS);
9233 execv(bb_busybox_exec_path, argv);
9234 /* If they called chroot or otherwise made the binary no longer
9235 * executable, fall through */
9236 }
9237 }
9238#endif
9239
9240#if ENABLE_FEATURE_SH_STANDALONE || BB_MMU
9241 skip:
9242#endif
9243 if_command_vV_print_and_exit(opt_vV, argv[0], NULL);
9244 execvp_or_die(argv);
9245}
9246
9247/* Called after [v]fork() in run_pipe
9248 */
9249static void pseudo_exec(nommu_save_t *nommu_save,
9250 struct command *command,
9251 char **argv_expanded) NORETURN;
9252static void pseudo_exec(nommu_save_t *nommu_save,
9253 struct command *command,
9254 char **argv_expanded)
9255{
9256#if ENABLE_HUSH_FUNCTIONS
9257 if (command->cmd_type == CMD_FUNCDEF) {
9258 /* Ignore funcdefs in pipes:
9259 * true | f() { cmd }
9260 */
9261 _exit(0);
9262 }
9263#endif
9264
9265 if (command->argv) {
9266 pseudo_exec_argv(nommu_save, command->argv,
9267 command->assignment_cnt, argv_expanded);
9268 }
9269
9270 if (command->group) {
9271 /* Cases when we are here:
9272 * ( list )
9273 * { list } &
9274 * ... | ( list ) | ...
9275 * ... | { list } | ...
9276 */
9277#if BB_MMU
9278 int rcode;
9279 debug_printf_exec("pseudo_exec: run_list\n");
9280 reset_traps_to_defaults();
9281 rcode = run_list(command->group);
9282 /* OK to leak memory by not calling free_pipe_list,
9283 * since this process is about to exit */
9284 _exit(rcode);
9285#else
9286 re_execute_shell(&nommu_save->argv_from_re_execing,
9287 command->group_as_string,
9288 G.global_argv[0],
9289 G.global_argv + 1,
9290 NULL);
9291#endif
9292 }
9293
9294 /* Case when we are here: ... | >file */
9295 debug_printf_exec("pseudo_exec'ed null command\n");
9296 _exit(EXIT_SUCCESS);
9297}
9298
9299#if ENABLE_HUSH_JOB
9300static const char *get_cmdtext(struct pipe *pi)
9301{
9302 char **argv;
9303 char *p;
9304 int len;
9305
9306 /* This is subtle. ->cmdtext is created only on first backgrounding.
9307 * (Think "cat, <ctrl-z>, fg, <ctrl-z>, fg, <ctrl-z>...." here...)
9308 * On subsequent bg argv is trashed, but we won't use it */
9309 if (pi->cmdtext)
9310 return pi->cmdtext;
9311
9312 argv = pi->cmds[0].argv;
9313 if (!argv) {
9314 pi->cmdtext = xzalloc(1);
9315 return pi->cmdtext;
9316 }
9317 len = 0;
9318 do {
9319 len += strlen(*argv) + 1;
9320 } while (*++argv);
9321 p = xmalloc(len);
9322 pi->cmdtext = p;
9323 argv = pi->cmds[0].argv;
9324 do {
9325 p = stpcpy(p, *argv);
9326 *p++ = ' ';
9327 } while (*++argv);
9328 p[-1] = '\0';
9329 return pi->cmdtext;
9330}
9331
9332static void remove_job_from_table(struct pipe *pi)
9333{
9334 struct pipe *prev_pipe;
9335
9336 if (pi == G.job_list) {
9337 G.job_list = pi->next;
9338 } else {
9339 prev_pipe = G.job_list;
9340 while (prev_pipe->next != pi)
9341 prev_pipe = prev_pipe->next;
9342 prev_pipe->next = pi->next;
9343 }
9344 G.last_jobid = 0;
9345 if (G.job_list)
9346 G.last_jobid = G.job_list->jobid;
9347}
9348
9349static void delete_finished_job(struct pipe *pi)
9350{
9351 remove_job_from_table(pi);
9352 free_pipe(pi);
9353}
9354
9355static void clean_up_last_dead_job(void)
9356{
9357 if (G.job_list && !G.job_list->alive_cmds)
9358 delete_finished_job(G.job_list);
9359}
9360
9361static void insert_job_into_table(struct pipe *pi)
9362{
9363 struct pipe *job, **jobp;
9364 int i;
9365
9366 clean_up_last_dead_job();
9367
9368 /* Find the end of the list, and find next job ID to use */
9369 i = 0;
9370 jobp = &G.job_list;
9371 while ((job = *jobp) != NULL) {
9372 if (job->jobid > i)
9373 i = job->jobid;
9374 jobp = &job->next;
9375 }
9376 pi->jobid = i + 1;
9377
9378 /* Create a new job struct at the end */
9379 job = *jobp = xmemdup(pi, sizeof(*pi));
9380 job->next = NULL;
9381 job->cmds = xzalloc(sizeof(pi->cmds[0]) * pi->num_cmds);
9382 /* Cannot copy entire pi->cmds[] vector! This causes double frees */
9383 for (i = 0; i < pi->num_cmds; i++) {
9384 job->cmds[i].pid = pi->cmds[i].pid;
9385 /* all other fields are not used and stay zero */
9386 }
9387 job->cmdtext = xstrdup(get_cmdtext(pi));
9388
9389 if (G_interactive_fd)
9390 printf("[%u] %u %s\n", job->jobid, (unsigned)job->cmds[0].pid, job->cmdtext);
9391 G.last_jobid = job->jobid;
9392}
9393#endif /* JOB */
9394
9395static int job_exited_or_stopped(struct pipe *pi)
9396{
9397 int rcode, i;
9398
9399 if (pi->alive_cmds != pi->stopped_cmds)
9400 return -1;
9401
9402 /* All processes in fg pipe have exited or stopped */
9403 rcode = 0;
9404 i = pi->num_cmds;
9405 while (--i >= 0) {
9406 rcode = pi->cmds[i].cmd_exitcode;
9407 /* usually last process gives overall exitstatus,
9408 * but with "set -o pipefail", last *failed* process does */
9409 if (G.o_opt[OPT_O_PIPEFAIL] == 0 || rcode != 0)
9410 break;
9411 }
9412 IF_HAS_KEYWORDS(if (pi->pi_inverted) rcode = !rcode;)
9413 return rcode;
9414}
9415
9416static int process_wait_result(struct pipe *fg_pipe, pid_t childpid, int status)
9417{
9418#if ENABLE_HUSH_JOB
9419 struct pipe *pi;
9420#endif
9421 int i, dead;
9422
9423 dead = WIFEXITED(status) || WIFSIGNALED(status);
9424
9425#if DEBUG_JOBS
9426 if (WIFSTOPPED(status))
9427 debug_printf_jobs("pid %d stopped by sig %d (exitcode %d)\n",
9428 childpid, WSTOPSIG(status), WEXITSTATUS(status));
9429 if (WIFSIGNALED(status))
9430 debug_printf_jobs("pid %d killed by sig %d (exitcode %d)\n",
9431 childpid, WTERMSIG(status), WEXITSTATUS(status));
9432 if (WIFEXITED(status))
9433 debug_printf_jobs("pid %d exited, exitcode %d\n",
9434 childpid, WEXITSTATUS(status));
9435#endif
9436 /* Were we asked to wait for a fg pipe? */
9437 if (fg_pipe) {
9438 i = fg_pipe->num_cmds;
9439
9440 while (--i >= 0) {
9441 int rcode;
9442
9443 debug_printf_jobs("check pid %d\n", fg_pipe->cmds[i].pid);
9444 if (fg_pipe->cmds[i].pid != childpid)
9445 continue;
9446 if (dead) {
9447 int ex;
9448 fg_pipe->cmds[i].pid = 0;
9449 fg_pipe->alive_cmds--;
9450 ex = WEXITSTATUS(status);
9451 /* bash prints killer signal's name for *last*
9452 * process in pipe (prints just newline for SIGINT/SIGPIPE).
9453 * Mimic this. Example: "sleep 5" + (^\ or kill -QUIT)
9454 */
9455 if (WIFSIGNALED(status)) {
9456 int sig = WTERMSIG(status);
9457#if ENABLE_HUSH_JOB
9458 if (G.run_list_level == 1
9459 /* ^^^^^ Do not print in nested contexts, example:
9460 * echo `sleep 1; sh -c 'kill -9 $$'` - prints "137", NOT "Killed 137"
9461 */
9462 && i == fg_pipe->num_cmds-1
9463 ) {
9464 /* strsignal() is for bash compat. ~600 bloat versus bbox's get_signame() */
9465 puts(sig == SIGINT || sig == SIGPIPE ? "" : strsignal(sig));
9466 }
9467#endif
9468 /* TODO: if (WCOREDUMP(status)) + " (core dumped)"; */
9469 /* MIPS has 128 sigs (1..128), if sig==128,
9470 * 128 + sig would result in exitcode 256 -> 0!
9471 */
9472 ex = 128 | sig;
9473 }
9474 fg_pipe->cmds[i].cmd_exitcode = ex;
9475 } else {
9476 fg_pipe->stopped_cmds++;
9477 }
9478 debug_printf_jobs("fg_pipe: alive_cmds %d stopped_cmds %d\n",
9479 fg_pipe->alive_cmds, fg_pipe->stopped_cmds);
9480 rcode = job_exited_or_stopped(fg_pipe);
9481 if (rcode >= 0) {
9482/* Note: *non-interactive* bash does not continue if all processes in fg pipe
9483 * are stopped. Testcase: "cat | cat" in a script (not on command line!)
9484 * and "killall -STOP cat" */
9485 if (G_interactive_fd) {
9486#if ENABLE_HUSH_JOB
9487 if (fg_pipe->alive_cmds != 0)
9488 insert_job_into_table(fg_pipe);
9489#endif
9490 return rcode;
9491 }
9492 if (fg_pipe->alive_cmds == 0)
9493 return rcode;
9494 }
9495 /* There are still running processes in the fg_pipe */
9496 return -1;
9497 }
9498 /* It wasn't in fg_pipe, look for process in bg pipes */
9499 }
9500
9501#if ENABLE_HUSH_JOB
9502 /* We were asked to wait for bg or orphaned children */
9503 /* No need to remember exitcode in this case */
9504 for (pi = G.job_list; pi; pi = pi->next) {
9505 for (i = 0; i < pi->num_cmds; i++) {
9506 if (pi->cmds[i].pid == childpid)
9507 goto found_pi_and_prognum;
9508 }
9509 }
9510 /* Happens when shell is used as init process (init=/bin/sh) */
9511 debug_printf("checkjobs: pid %d was not in our list!\n", childpid);
9512 return -1; /* this wasn't a process from fg_pipe */
9513
9514 found_pi_and_prognum:
9515 if (dead) {
9516 /* child exited */
9517 int rcode = WEXITSTATUS(status);
9518 if (WIFSIGNALED(status))
9519 /* NB: not 128 + sig, MIPS has sig 128 */
9520 rcode = 128 | WTERMSIG(status);
9521 pi->cmds[i].cmd_exitcode = rcode;
9522 if (G.last_bg_pid == pi->cmds[i].pid)
9523 G.last_bg_pid_exitcode = rcode;
9524 pi->cmds[i].pid = 0;
9525 pi->alive_cmds--;
9526 if (!pi->alive_cmds) {
9527# if ENABLE_HUSH_BASH_COMPAT
9528 G.dead_job_exitcode = job_exited_or_stopped(pi);
9529# endif
9530 if (G_interactive_fd) {
9531 printf(JOB_STATUS_FORMAT, pi->jobid,
9532 "Done", pi->cmdtext);
9533 delete_finished_job(pi);
9534 } else {
9535/*
9536 * bash deletes finished jobs from job table only in interactive mode,
9537 * after "jobs" cmd, or if pid of a new process matches one of the old ones
9538 * (see cleanup_dead_jobs(), delete_old_job(), J_NOTIFIED in bash source).
9539 * Testcase script: "(exit 3) & sleep 1; wait %1; echo $?" prints 3 in bash.
9540 * We only retain one "dead" job, if it's the single job on the list.
9541 * This covers most of real-world scenarios where this is useful.
9542 */
9543 if (pi != G.job_list)
9544 delete_finished_job(pi);
9545 }
9546 }
9547 } else {
9548 /* child stopped */
9549 pi->stopped_cmds++;
9550 }
9551#endif
9552 return -1; /* this wasn't a process from fg_pipe */
9553}
9554
9555/* Check to see if any processes have exited -- if they have,
9556 * figure out why and see if a job has completed.
9557 *
9558 * If non-NULL fg_pipe: wait for its completion or stop.
9559 * Return its exitcode or zero if stopped.
9560 *
9561 * Alternatively (fg_pipe == NULL, waitfor_pid != 0):
9562 * waitpid(WNOHANG), if waitfor_pid exits or stops, return exitcode+1,
9563 * else return <0 if waitpid errors out (e.g. ECHILD: nothing to wait for)
9564 * or 0 if no children changed status.
9565 *
9566 * Alternatively (fg_pipe == NULL, waitfor_pid == 0),
9567 * return <0 if waitpid errors out (e.g. ECHILD: nothing to wait for)
9568 * or 0 if no children changed status.
9569 */
9570static int checkjobs(struct pipe *fg_pipe, pid_t waitfor_pid)
9571{
9572 int attributes;
9573 int status;
9574 int rcode = 0;
9575
9576 debug_printf_jobs("checkjobs %p\n", fg_pipe);
9577
9578 attributes = WUNTRACED;
9579 if (fg_pipe == NULL)
9580 attributes |= WNOHANG;
9581
9582 errno = 0;
9583#if ENABLE_HUSH_FAST
9584 if (G.handled_SIGCHLD == G.count_SIGCHLD) {
9585//bb_error_msg("[%d] checkjobs: G.count_SIGCHLD:%d G.handled_SIGCHLD:%d children?:%d fg_pipe:%p",
9586//getpid(), G.count_SIGCHLD, G.handled_SIGCHLD, G.we_have_children, fg_pipe);
9587 /* There was neither fork nor SIGCHLD since last waitpid */
9588 /* Avoid doing waitpid syscall if possible */
9589 if (!G.we_have_children) {
9590 errno = ECHILD;
9591 return -1;
9592 }
9593 if (fg_pipe == NULL) { /* is WNOHANG set? */
9594 /* We have children, but they did not exit
9595 * or stop yet (we saw no SIGCHLD) */
9596 return 0;
9597 }
9598 /* else: !WNOHANG, waitpid will block, can't short-circuit */
9599 }
9600#endif
9601
9602/* Do we do this right?
9603 * bash-3.00# sleep 20 | false
9604 * <ctrl-Z pressed>
9605 * [3]+ Stopped sleep 20 | false
9606 * bash-3.00# echo $?
9607 * 1 <========== bg pipe is not fully done, but exitcode is already known!
9608 * [hush 1.14.0: yes we do it right]
9609 */
9610 while (1) {
9611 pid_t childpid;
9612#if ENABLE_HUSH_FAST
9613 int i;
9614 i = G.count_SIGCHLD;
9615#endif
9616 childpid = waitpid(-1, &status, attributes);
9617 if (childpid <= 0) {
9618 if (childpid && errno != ECHILD)
9619 bb_simple_perror_msg("waitpid");
9620#if ENABLE_HUSH_FAST
9621 else { /* Until next SIGCHLD, waitpid's are useless */
9622 G.we_have_children = (childpid == 0);
9623 G.handled_SIGCHLD = i;
9624//bb_error_msg("[%d] checkjobs: waitpid returned <= 0, G.count_SIGCHLD:%d G.handled_SIGCHLD:%d", getpid(), G.count_SIGCHLD, G.handled_SIGCHLD);
9625 }
9626#endif
9627 /* ECHILD (no children), or 0 (no change in children status) */
9628 rcode = childpid;
9629 break;
9630 }
9631 rcode = process_wait_result(fg_pipe, childpid, status);
9632 if (rcode >= 0) {
9633 /* fg_pipe exited or stopped */
9634 break;
9635 }
9636 if (childpid == waitfor_pid) { /* "wait PID" */
9637 debug_printf_exec("childpid==waitfor_pid:%d status:0x%08x\n", childpid, status);
9638 rcode = WEXITSTATUS(status);
9639 if (WIFSIGNALED(status))
9640 rcode = 128 | WTERMSIG(status);
9641 if (WIFSTOPPED(status))
9642 /* bash: "cmd & wait $!" and cmd stops: $? = 128 | stopsig */
9643 rcode = 128 | WSTOPSIG(status);
9644 rcode++;
9645 break; /* "wait PID" called us, give it exitcode+1 */
9646 }
9647#if ENABLE_HUSH_BASH_COMPAT
9648 if (-1 == waitfor_pid /* "wait -n" (wait for any one job) */
9649 && G.dead_job_exitcode >= 0 /* some job did finish */
9650 ) {
9651 debug_printf_exec("waitfor_pid:-1\n");
9652 rcode = G.dead_job_exitcode + 1;
9653 break;
9654 }
9655#endif
9656 /* This wasn't one of our processes, or */
9657 /* fg_pipe still has running processes, do waitpid again */
9658 } /* while (waitpid succeeds)... */
9659
9660 return rcode;
9661}
9662
9663#if ENABLE_HUSH_JOB
9664static int checkjobs_and_fg_shell(struct pipe *fg_pipe)
9665{
9666 pid_t p;
9667 int rcode = checkjobs(fg_pipe, 0 /*(no pid to wait for)*/);
9668 if (G_saved_tty_pgrp) {
9669 /* Job finished, move the shell to the foreground */
9670 p = getpgrp(); /* our process group id */
9671 debug_printf_jobs("fg'ing ourself: getpgrp()=%d\n", (int)p);
9672 tcsetpgrp(G_interactive_fd, p);
9673 }
9674 return rcode;
9675}
9676#endif
9677
9678/* Start all the jobs, but don't wait for anything to finish.
9679 * See checkjobs().
9680 *
9681 * Return code is normally -1, when the caller has to wait for children
9682 * to finish to determine the exit status of the pipe. If the pipe
9683 * is a simple builtin command, however, the action is done by the
9684 * time run_pipe returns, and the exit code is provided as the
9685 * return value.
9686 *
9687 * Returns -1 only if started some children. IOW: we have to
9688 * mask out retvals of builtins etc with 0xff!
9689 *
9690 * The only case when we do not need to [v]fork is when the pipe
9691 * is single, non-backgrounded, non-subshell command. Examples:
9692 * cmd ; ... { list } ; ...
9693 * cmd && ... { list } && ...
9694 * cmd || ... { list } || ...
9695 * If it is, then we can run cmd as a builtin, NOFORK,
9696 * or (if SH_STANDALONE) an applet, and we can run the { list }
9697 * with run_list. If it isn't one of these, we fork and exec cmd.
9698 *
9699 * Cases when we must fork:
9700 * non-single: cmd | cmd
9701 * backgrounded: cmd & { list } &
9702 * subshell: ( list ) [&]
9703 */
9704#if !ENABLE_HUSH_MODE_X
9705#define redirect_and_varexp_helper(command, sqp, argv_expanded) \
9706 redirect_and_varexp_helper(command, sqp)
9707#endif
9708static int redirect_and_varexp_helper(
9709 struct command *command,
9710 struct squirrel **sqp,
9711 char **argv_expanded)
9712{
9713 /* Assignments occur before redirects. Try:
9714 * a=`sleep 1` sleep 2 3>/qwe/rty
9715 */
9716
9717 char **new_env = expand_assignments(command->argv, command->assignment_cnt);
9718 dump_cmd_in_x_mode(new_env);
9719 dump_cmd_in_x_mode(argv_expanded);
9720 /* this takes ownership of new_env[i] elements, and frees new_env: */
9721 set_vars_and_save_old(new_env);
9722
9723 return setup_redirects(command, sqp);
9724}
Francis Laniel8197f012023-12-22 22:02:28 +01009725#endif /* !__U_BOOT__ */
9726
Francis Lanielb234f7e2023-12-22 22:02:27 +01009727static NOINLINE int run_pipe(struct pipe *pi)
9728{
9729 static const char *const null_ptr = NULL;
9730
9731 int cmd_no;
Francis Laniel8197f012023-12-22 22:02:28 +01009732#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01009733 int next_infd;
Francis Laniel8197f012023-12-22 22:02:28 +01009734#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01009735 struct command *command;
9736 char **argv_expanded;
9737 char **argv;
Francis Laniel8197f012023-12-22 22:02:28 +01009738#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01009739 struct squirrel *squirrel = NULL;
Francis Laniel8197f012023-12-22 22:02:28 +01009740#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01009741 int rcode;
9742
Francis Laniel8197f012023-12-22 22:02:28 +01009743#ifdef __U_BOOT__
9744 /*
9745 * Set rcode here to avoid returning a garbage value in the middle of
9746 * the function.
9747 * Also, if an error occurs, rcode value would be changed and last
9748 * return will signal the error.
9749 */
9750 rcode = 0;
9751#endif /* __U_BOOT__ */
9752
Francis Lanielb234f7e2023-12-22 22:02:27 +01009753 debug_printf_exec("run_pipe start: members:%d\n", pi->num_cmds);
9754 debug_enter();
9755
9756 /* Testcase: set -- q w e; (IFS='' echo "$*"; IFS=''; echo "$*"); echo "$*"
9757 * Result should be 3 lines: q w e, qwe, q w e
9758 */
9759 if (G.ifs_whitespace != G.ifs)
9760 free(G.ifs_whitespace);
9761 G.ifs = get_local_var_value("IFS");
9762 if (G.ifs) {
9763 char *p;
9764 G.ifs_whitespace = (char*)G.ifs;
9765 p = skip_whitespace(G.ifs);
9766 if (*p) {
9767 /* Not all $IFS is whitespace */
9768 char *d;
9769 int len = p - G.ifs;
9770 p = skip_non_whitespace(p);
9771 G.ifs_whitespace = xmalloc(len + strlen(p) + 1); /* can overestimate */
9772 d = mempcpy(G.ifs_whitespace, G.ifs, len);
9773 while (*p) {
9774 if (isspace(*p))
9775 *d++ = *p;
9776 p++;
9777 }
9778 *d = '\0';
9779 }
9780 } else {
9781 G.ifs = defifs;
9782 G.ifs_whitespace = (char*)G.ifs;
9783 }
9784
Francis Laniel8197f012023-12-22 22:02:28 +01009785#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01009786 IF_HUSH_JOB(pi->pgrp = -1;)
9787 pi->stopped_cmds = 0;
Francis Laniel8197f012023-12-22 22:02:28 +01009788#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01009789 command = &pi->cmds[0];
9790 argv_expanded = NULL;
9791
Francis Laniel8197f012023-12-22 22:02:28 +01009792#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01009793 if (pi->num_cmds != 1
9794 || pi->followup == PIPE_BG
9795 || command->cmd_type == CMD_SUBSHELL
9796 ) {
9797 goto must_fork;
9798 }
9799
9800 pi->alive_cmds = 1;
Francis Laniel8197f012023-12-22 22:02:28 +01009801#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01009802
9803 debug_printf_exec(": group:%p argv:'%s'\n",
9804 command->group, command->argv ? command->argv[0] : "NONE");
9805
9806 if (command->group) {
9807#if ENABLE_HUSH_FUNCTIONS
9808 if (command->cmd_type == CMD_FUNCDEF) {
9809 /* "executing" func () { list } */
9810 struct function *funcp;
9811
9812 funcp = new_function(command->argv[0]);
9813 /* funcp->name is already set to argv[0] */
9814 funcp->body = command->group;
9815# if !BB_MMU
9816 funcp->body_as_string = command->group_as_string;
9817 command->group_as_string = NULL;
9818# endif
9819 command->group = NULL;
9820 command->argv[0] = NULL;
9821 debug_printf_exec("cmd %p has child func at %p\n", command, funcp);
9822 funcp->parent_cmd = command;
9823 command->child_func = funcp;
9824
9825 debug_printf_exec("run_pipe: return EXIT_SUCCESS\n");
9826 debug_leave();
9827 return EXIT_SUCCESS;
9828 }
9829#endif
9830 /* { list } */
9831 debug_printf_exec("non-subshell group\n");
9832 rcode = 1; /* exitcode if redir failed */
Francis Laniel8197f012023-12-22 22:02:28 +01009833#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01009834 if (setup_redirects(command, &squirrel) == 0) {
9835 debug_printf_exec(": run_list\n");
9836//FIXME: we need to pass squirrel down into run_list()
9837//for SH_STANDALONE case, or else this construct:
9838// { find /proc/self/fd; true; } >FILE; cmd2
9839//has no way of closing saved fd#1 for "find",
9840//and in SH_STANDALONE mode, "find" is not execed,
9841//therefore CLOEXEC on saved fd does not help.
9842 rcode = run_list(command->group) & 0xff;
9843 }
9844 restore_redirects(squirrel);
9845 IF_HAS_KEYWORDS(if (pi->pi_inverted) rcode = !rcode;)
Francis Laniel8197f012023-12-22 22:02:28 +01009846#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01009847 debug_leave();
9848 debug_printf_exec("run_pipe: return %d\n", rcode);
9849 return rcode;
9850 }
9851
9852 argv = command->argv ? command->argv : (char **) &null_ptr;
9853 {
Francis Laniel8197f012023-12-22 22:02:28 +01009854#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01009855 const struct built_in_command *x;
9856 IF_HUSH_FUNCTIONS(const struct function *funcp;)
9857 IF_NOT_HUSH_FUNCTIONS(enum { funcp = 0 };)
9858 struct variable **sv_shadowed;
Francis Laniel8197f012023-12-22 22:02:28 +01009859#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01009860 struct variable *old_vars;
9861
9862#if ENABLE_HUSH_LINENO_VAR
9863 G.execute_lineno = command->lineno;
9864#endif
9865
9866 if (argv[command->assignment_cnt] == NULL) {
9867 /* Assignments, but no command.
9868 * Ensure redirects take effect (that is, create files).
9869 * Try "a=t >file"
9870 */
9871 unsigned i;
9872 G.expand_exitcode = 0;
9873 only_assignments:
Francis Laniel8197f012023-12-22 22:02:28 +01009874#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01009875 rcode = setup_redirects(command, &squirrel);
9876 restore_redirects(squirrel);
Francis Laniel8197f012023-12-22 22:02:28 +01009877#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01009878
9879 /* Set shell variables */
9880 i = 0;
9881 while (i < command->assignment_cnt) {
9882 char *p = expand_string_to_string(argv[i],
9883 EXP_FLAG_ESC_GLOB_CHARS,
9884 /*unbackslash:*/ 1
9885 );
9886#if ENABLE_HUSH_MODE_X
9887 if (G_x_mode) {
9888 char *eq;
9889 if (i == 0)
9890 x_mode_prefix();
9891 x_mode_addchr(' ');
9892 eq = strchrnul(p, '=');
9893 if (*eq) eq++;
9894 x_mode_addblock(p, (eq - p));
9895 x_mode_print_optionally_squoted(eq);
9896 x_mode_flush();
9897 }
9898#endif
9899 debug_printf_env("set shell var:'%s'->'%s'\n", *argv, p);
Francis Laniel8197f012023-12-22 22:02:28 +01009900#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01009901 if (set_local_var(p, /*flag:*/ 0)) {
Francis Laniel8197f012023-12-22 22:02:28 +01009902#else /* __U_BOOT__ */
9903 if (set_local_var_modern(p, /*flag:*/ 0)) {
9904#endif
Francis Lanielb234f7e2023-12-22 22:02:27 +01009905 /* assignment to readonly var / putenv error? */
9906 rcode = 1;
9907 }
9908 i++;
9909 }
9910 /* Redirect error sets $? to 1. Otherwise,
9911 * if evaluating assignment value set $?, retain it.
9912 * Else, clear $?:
9913 * false; q=`exit 2`; echo $? - should print 2
9914 * false; x=1; echo $? - should print 0
9915 * Because of the 2nd case, we can't just use G.last_exitcode.
9916 */
9917 if (rcode == 0)
9918 rcode = G.expand_exitcode;
9919 IF_HAS_KEYWORDS(if (pi->pi_inverted) rcode = !rcode;)
9920 debug_leave();
9921 debug_printf_exec("run_pipe: return %d\n", rcode);
9922 return rcode;
9923 }
9924
9925 /* Expand the rest into (possibly) many strings each */
9926#if defined(CMD_TEST2_SINGLEWORD_NOGLOB)
9927 if (command->cmd_type == CMD_TEST2_SINGLEWORD_NOGLOB)
9928 argv_expanded = expand_strvec_to_strvec_singleword_noglob(argv + command->assignment_cnt);
9929 else
9930#endif
9931#if defined(CMD_SINGLEWORD_NOGLOB)
9932 if (command->cmd_type == CMD_SINGLEWORD_NOGLOB)
9933 argv_expanded = expand_strvec_to_strvec_singleword_noglob(argv + command->assignment_cnt);
9934 else
9935#endif
9936 argv_expanded = expand_strvec_to_strvec(argv + command->assignment_cnt);
9937
9938 /* If someone gives us an empty string: `cmd with empty output` */
9939 if (!argv_expanded[0]) {
9940 free(argv_expanded);
9941 /* `false` still has to set exitcode 1 */
9942 G.expand_exitcode = G.last_exitcode;
9943 goto only_assignments;
9944 }
9945
9946 old_vars = NULL;
Francis Laniel8197f012023-12-22 22:02:28 +01009947#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01009948 sv_shadowed = G.shadowed_vars_pp;
9949
9950 /* Check if argv[0] matches any functions (this goes before bltins) */
9951 IF_HUSH_FUNCTIONS(funcp = find_function(argv_expanded[0]);)
9952 IF_HUSH_FUNCTIONS(x = NULL;)
9953 IF_HUSH_FUNCTIONS(if (!funcp))
9954 x = find_builtin(argv_expanded[0]);
9955 if (x || funcp) {
9956 if (x && x->b_function == builtin_exec && argv_expanded[1] == NULL) {
9957 debug_printf("exec with redirects only\n");
9958 /*
9959 * Variable assignments are executed, but then "forgotten":
9960 * a=`sleep 1;echo A` exec 3>&-; echo $a
9961 * sleeps, but prints nothing.
9962 */
9963 enter_var_nest_level();
9964 G.shadowed_vars_pp = &old_vars;
9965 rcode = redirect_and_varexp_helper(command,
9966 /*squirrel:*/ ERR_PTR,
9967 argv_expanded
9968 );
9969 G.shadowed_vars_pp = sv_shadowed;
9970 /* rcode=1 can be if redir file can't be opened */
9971
9972 goto clean_up_and_ret1;
9973 }
9974
9975 /* Bump var nesting, or this will leak exported $a:
9976 * a=b true; env | grep ^a=
9977 */
9978 enter_var_nest_level();
9979 /* Collect all variables "shadowed" by helper
9980 * (IOW: old vars overridden by "var1=val1 var2=val2 cmd..." syntax)
9981 * into old_vars list:
9982 */
9983 G.shadowed_vars_pp = &old_vars;
9984 rcode = redirect_and_varexp_helper(command, &squirrel, argv_expanded);
9985 if (rcode == 0) {
9986 if (!funcp) {
9987 /* Do not collect *to old_vars list* vars shadowed
9988 * by e.g. "local VAR" builtin (collect them
9989 * in the previously nested list instead):
9990 * don't want them to be restored immediately
9991 * after "local" completes.
9992 */
9993 G.shadowed_vars_pp = sv_shadowed;
9994
9995 debug_printf_exec(": builtin '%s' '%s'...\n",
9996 x->b_cmd, argv_expanded[1]);
9997 fflush_all();
9998 rcode = x->b_function(argv_expanded) & 0xff;
9999 fflush_all();
10000 }
10001#if ENABLE_HUSH_FUNCTIONS
10002 else {
10003 debug_printf_exec(": function '%s' '%s'...\n",
10004 funcp->name, argv_expanded[1]);
10005 rcode = run_function(funcp, argv_expanded) & 0xff;
10006 /*
10007 * But do collect *to old_vars list* vars shadowed
10008 * within function execution. To that end, restore
10009 * this pointer _after_ function run:
10010 */
10011 G.shadowed_vars_pp = sv_shadowed;
10012 }
10013#endif
10014 }
10015 } else
10016 if (ENABLE_FEATURE_SH_NOFORK && NUM_APPLETS > 1) {
10017 int n = find_applet_by_name(argv_expanded[0]);
10018 if (n < 0 || !APPLET_IS_NOFORK(n))
10019 goto must_fork;
10020
10021 enter_var_nest_level();
10022 /* Collect all variables "shadowed" by helper into old_vars list */
10023 G.shadowed_vars_pp = &old_vars;
10024 rcode = redirect_and_varexp_helper(command, &squirrel, argv_expanded);
10025 G.shadowed_vars_pp = sv_shadowed;
10026
10027 if (rcode == 0) {
10028 debug_printf_exec(": run_nofork_applet '%s' '%s'...\n",
10029 argv_expanded[0], argv_expanded[1]);
10030 /*
10031 * Note: signals (^C) can't interrupt here.
10032 * We remember them and they will be acted upon
10033 * after applet returns.
10034 * This makes applets which can run for a long time
10035 * and/or wait for user input ineligible for NOFORK:
10036 * for example, "yes" or "rm" (rm -i waits for input).
10037 */
10038 rcode = run_nofork_applet(n, argv_expanded);
10039 }
10040 } else
10041 goto must_fork;
10042
10043 restore_redirects(squirrel);
10044 clean_up_and_ret1:
10045 leave_var_nest_level();
10046 add_vars(old_vars);
10047
10048 /*
10049 * Try "usleep 99999999" + ^C + "echo $?"
10050 * with FEATURE_SH_NOFORK=y.
10051 */
10052 if (!funcp) {
10053 /* It was builtin or nofork.
10054 * if this would be a real fork/execed program,
10055 * it should have died if a fatal sig was received.
10056 * But OTOH, there was no separate process,
10057 * the sig was sent to _shell_, not to non-existing
10058 * child.
10059 * Let's just handle ^C only, this one is obvious:
10060 * we aren't ok with exitcode 0 when ^C was pressed
10061 * during builtin/nofork.
10062 */
10063 if (sigismember(&G.pending_set, SIGINT))
10064 rcode = 128 | SIGINT;
10065 }
10066 free(argv_expanded);
10067 IF_HAS_KEYWORDS(if (pi->pi_inverted) rcode = !rcode;)
10068 debug_leave();
10069 debug_printf_exec("run_pipe return %d\n", rcode);
10070 return rcode;
Francis Laniel8197f012023-12-22 22:02:28 +010010071#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +010010072 }
10073
Francis Laniel8197f012023-12-22 22:02:28 +010010074#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +010010075 must_fork:
10076 /* NB: argv_expanded may already be created, and that
10077 * might include `cmd` runs! Do not rerun it! We *must*
10078 * use argv_expanded if it's non-NULL */
10079
10080 /* Going to fork a child per each pipe member */
10081 pi->alive_cmds = 0;
10082 next_infd = 0;
Francis Laniel8197f012023-12-22 22:02:28 +010010083#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +010010084
10085 cmd_no = 0;
10086 while (cmd_no < pi->num_cmds) {
Francis Laniel8197f012023-12-22 22:02:28 +010010087#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +010010088 struct fd_pair pipefds;
10089#if !BB_MMU
10090 int sv_var_nest_level = G.var_nest_level;
10091 volatile nommu_save_t nommu_save;
10092 nommu_save.old_vars = NULL;
10093 nommu_save.argv = NULL;
10094 nommu_save.argv_from_re_execing = NULL;
10095#endif
Francis Laniel8197f012023-12-22 22:02:28 +010010096#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +010010097 command = &pi->cmds[cmd_no];
10098 cmd_no++;
Francis Laniel74e42542023-12-22 22:02:33 +010010099
10100#ifdef __U_BOOT__
10101 /* Replace argv and argc by expanded if it exists. */
10102 if (argv_expanded) {
10103 /*
10104 * We need to save a pointer to argv, we will restore it
10105 * later, so it will be freed when pipe is freed.
10106 */
10107 argv = command->argv;
10108
10109 /*
10110 * After expansion, there can be more or less argument, so we need to
10111 * update argc, for example:
10112 * - More arguments:
10113 * foo='bar quuz'
10114 * echo $foo
10115 * - Less arguments:
10116 * echo $foo (if foo was never set)
10117 */
10118 command->argc = list_size(argv_expanded);
10119 command->argv = argv_expanded;
10120 }
10121#endif /* __U_BOOT__ */
10122 if (command->argv) {
Francis Lanielb234f7e2023-12-22 22:02:27 +010010123 debug_printf_exec(": pipe member '%s' '%s'...\n",
10124 command->argv[0], command->argv[1]);
10125 } else {
10126 debug_printf_exec(": pipe member with no argv\n");
10127 }
10128
Francis Laniel8197f012023-12-22 22:02:28 +010010129#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +010010130 /* pipes are inserted between pairs of commands */
10131 pipefds.rd = 0;
10132 pipefds.wr = 1;
10133 if (cmd_no < pi->num_cmds)
10134 xpiped_pair(pipefds);
10135
10136#if ENABLE_HUSH_LINENO_VAR
10137 G.execute_lineno = command->lineno;
10138#endif
10139
10140 command->pid = BB_MMU ? fork() : vfork();
10141 if (!command->pid) { /* child */
10142#if ENABLE_HUSH_JOB
10143 disable_restore_tty_pgrp_on_exit();
10144 CLEAR_RANDOM_T(&G.random_gen); /* or else $RANDOM repeats in child */
10145
10146 /* Every child adds itself to new process group
10147 * with pgid == pid_of_first_child_in_pipe */
10148 if (G.run_list_level == 1 && G_interactive_fd) {
10149 pid_t pgrp;
10150 pgrp = pi->pgrp;
10151 if (pgrp < 0) /* true for 1st process only */
10152 pgrp = getpid();
10153 if (setpgid(0, pgrp) == 0
10154 && pi->followup != PIPE_BG
10155 && G_saved_tty_pgrp /* we have ctty */
10156 ) {
10157 /* We do it in *every* child, not just first,
10158 * to avoid races */
10159 tcsetpgrp(G_interactive_fd, pgrp);
10160 }
10161 }
10162#endif
10163 if (pi->alive_cmds == 0 && pi->followup == PIPE_BG) {
10164 /* 1st cmd in backgrounded pipe
10165 * should have its stdin /dev/null'ed */
10166 close(0);
10167 if (open(bb_dev_null, O_RDONLY))
10168 xopen("/", O_RDONLY);
10169 } else {
10170 xmove_fd(next_infd, 0);
10171 }
10172 xmove_fd(pipefds.wr, 1);
10173 if (pipefds.rd > 1)
10174 close(pipefds.rd);
10175 /* Like bash, explicit redirects override pipes,
10176 * and the pipe fd (fd#1) is available for dup'ing:
10177 * "cmd1 2>&1 | cmd2": fd#1 is duped to fd#2, thus stderr
10178 * of cmd1 goes into pipe.
10179 */
10180 if (setup_redirects(command, NULL)) {
10181 /* Happens when redir file can't be opened:
10182 * $ hush -c 'echo FOO >&2 | echo BAR 3>/qwe/rty; echo BAZ'
10183 * FOO
10184 * hush: can't open '/qwe/rty': No such file or directory
10185 * BAZ
10186 * (echo BAR is not executed, it hits _exit(1) below)
10187 */
10188 _exit(1);
10189 }
10190
10191 /* Stores to nommu_save list of env vars putenv'ed
10192 * (NOMMU, on MMU we don't need that) */
10193 /* cast away volatility... */
10194 pseudo_exec((nommu_save_t*) &nommu_save, command, argv_expanded);
10195 /* pseudo_exec() does not return */
10196 }
10197
10198 /* parent or error */
10199#if ENABLE_HUSH_FAST
10200 G.count_SIGCHLD++;
10201//bb_error_msg("[%d] fork in run_pipe: G.count_SIGCHLD:%d G.handled_SIGCHLD:%d", getpid(), G.count_SIGCHLD, G.handled_SIGCHLD);
10202#endif
10203 enable_restore_tty_pgrp_on_exit();
10204#if !BB_MMU
10205 /* Clean up after vforked child */
10206 free(nommu_save.argv);
10207 free(nommu_save.argv_from_re_execing);
10208 G.var_nest_level = sv_var_nest_level;
10209 remove_nested_vars();
10210 add_vars(nommu_save.old_vars);
10211#endif
10212 free(argv_expanded);
10213 argv_expanded = NULL;
10214 if (command->pid < 0) { /* [v]fork failed */
10215 /* Clearly indicate, was it fork or vfork */
10216 bb_simple_perror_msg(BB_MMU ? "vfork"+1 : "vfork");
10217 } else {
10218 pi->alive_cmds++;
10219#if ENABLE_HUSH_JOB
10220 /* Second and next children need to know pid of first one */
10221 if (pi->pgrp < 0)
10222 pi->pgrp = command->pid;
10223#endif
10224 }
10225
10226 if (cmd_no > 1)
10227 close(next_infd);
10228 if (cmd_no < pi->num_cmds)
10229 close(pipefds.wr);
10230 /* Pass read (output) pipe end to next iteration */
10231 next_infd = pipefds.rd;
Francis Laniel8197f012023-12-22 22:02:28 +010010232#else /* __U_BOOT__ */
10233 /* Process the command */
10234 rcode = cmd_process(G.do_repeat ? CMD_FLAG_REPEAT : 0,
10235 command->argc, command->argv,
10236 &(G.flag_repeat), NULL);
Francis Laniel74e42542023-12-22 22:02:33 +010010237
10238 if (argv_expanded) {
10239 /*
10240 * expand_strvec_to_strvec() allocates memory to expand
10241 * argv, we need to free it.
10242 */
10243 free(argv_expanded);
10244
10245 /*
10246 * We also restore command->argv to its original value
10247 * so no memory leak happens.
10248 */
10249 command->argv = argv;
10250
10251 /*
10252 * NOTE argc exists only in U-Boot, so argv freeing does
10253 * not rely on it as this code exists in BusyBox.
10254 */
10255 }
Francis Laniel8197f012023-12-22 22:02:28 +010010256#endif /* __U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +010010257 }
10258
Francis Laniel8197f012023-12-22 22:02:28 +010010259#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +010010260 if (!pi->alive_cmds) {
10261 debug_leave();
10262 debug_printf_exec("run_pipe return 1 (all forks failed, no children)\n");
10263 return 1;
10264 }
Francis Laniel8197f012023-12-22 22:02:28 +010010265#endif /* __U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +010010266
10267 debug_leave();
Francis Laniel8197f012023-12-22 22:02:28 +010010268#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +010010269 debug_printf_exec("run_pipe return -1 (%u children started)\n", pi->alive_cmds);
10270 return -1;
Francis Laniel8197f012023-12-22 22:02:28 +010010271#else /* __U_BOOT__ */
10272 debug_printf_exec("run_pipe return %d\n", rcode);
10273 return rcode;
10274#endif /* __U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +010010275}
10276
10277/* NB: called by pseudo_exec, and therefore must not modify any
10278 * global data until exec/_exit (we can be a child after vfork!) */
10279static int run_list(struct pipe *pi)
10280{
10281#if ENABLE_HUSH_CASE
10282 char *case_word = NULL;
10283#endif
10284#if ENABLE_HUSH_LOOPS
10285 struct pipe *loop_top = NULL;
10286 char **for_lcur = NULL;
10287 char **for_list = NULL;
10288#endif
10289 smallint last_followup;
10290 smalluint rcode;
10291#if ENABLE_HUSH_IF || ENABLE_HUSH_CASE
10292 smalluint cond_code = 0;
10293#else
10294 enum { cond_code = 0 };
10295#endif
10296#if HAS_KEYWORDS
10297 smallint rword; /* RES_foo */
10298 smallint last_rword; /* ditto */
10299#endif
10300
Francis Laniel8197f012023-12-22 22:02:28 +010010301#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +010010302 debug_printf_exec("run_list start lvl %d\n", G.run_list_level);
10303 debug_enter();
Francis Laniel8197f012023-12-22 22:02:28 +010010304#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +010010305
10306#if ENABLE_HUSH_LOOPS
10307 /* Check syntax for "for" */
10308 {
10309 struct pipe *cpipe;
10310 for (cpipe = pi; cpipe; cpipe = cpipe->next) {
10311 if (cpipe->res_word != RES_FOR && cpipe->res_word != RES_IN)
10312 continue;
10313 /* current word is FOR or IN (BOLD in comments below) */
10314 if (cpipe->next == NULL) {
10315 syntax_error("malformed for");
10316 debug_leave();
10317 debug_printf_exec("run_list lvl %d return 1\n", G.run_list_level);
10318 return 1;
10319 }
10320 /* "FOR v; do ..." and "for v IN a b; do..." are ok */
10321 if (cpipe->next->res_word == RES_DO)
10322 continue;
10323 /* next word is not "do". It must be "in" then ("FOR v in ...") */
10324 if (cpipe->res_word == RES_IN /* "for v IN a b; not_do..."? */
10325 || cpipe->next->res_word != RES_IN /* FOR v not_do_and_not_in..."? */
10326 ) {
10327 syntax_error("malformed for");
10328 debug_leave();
10329 debug_printf_exec("run_list lvl %d return 1\n", G.run_list_level);
10330 return 1;
10331 }
10332 }
10333 }
10334#endif
10335
10336 /* Past this point, all code paths should jump to ret: label
10337 * in order to return, no direct "return" statements please.
10338 * This helps to ensure that no memory is leaked. */
10339
10340#if ENABLE_HUSH_JOB
10341 G.run_list_level++;
10342#endif
10343
10344#if HAS_KEYWORDS
10345 rword = RES_NONE;
10346 last_rword = RES_XXXX;
10347#endif
10348 last_followup = PIPE_SEQ;
10349 rcode = G.last_exitcode;
10350
10351 /* Go through list of pipes, (maybe) executing them. */
Francis Laniel8197f012023-12-22 22:02:28 +010010352#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +010010353 for (; pi; pi = IF_HUSH_LOOPS(rword == RES_DONE ? loop_top : ) pi->next) {
Francis Laniel8197f012023-12-22 22:02:28 +010010354#else /* __U_BOOT__ */
10355 for (; pi; pi = pi->next) {
10356#endif /* __U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +010010357 int r;
10358 int sv_errexit_depth;
10359
Francis Laniel8197f012023-12-22 22:02:28 +010010360#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +010010361 if (G.flag_SIGINT)
10362 break;
10363 if (G_flag_return_in_progress == 1)
10364 break;
10365
10366 IF_HAS_KEYWORDS(rword = pi->res_word;)
10367 debug_printf_exec(": rword=%d cond_code=%d last_rword=%d\n",
10368 rword, cond_code, last_rword);
10369
Francis Laniel8197f012023-12-22 22:02:28 +010010370#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +010010371 sv_errexit_depth = G.errexit_depth;
10372 if (
10373#if ENABLE_HUSH_IF
10374 rword == RES_IF || rword == RES_ELIF ||
10375#endif
10376 pi->followup != PIPE_SEQ
10377 ) {
10378 G.errexit_depth++;
10379 }
10380#if ENABLE_HUSH_LOOPS
10381 if ((rword == RES_WHILE || rword == RES_UNTIL || rword == RES_FOR)
10382 && loop_top == NULL /* avoid bumping G.depth_of_loop twice */
10383 ) {
10384 /* start of a loop: remember where loop starts */
10385 loop_top = pi;
10386 G.depth_of_loop++;
10387 }
10388#endif
10389 /* Still in the same "if...", "then..." or "do..." branch? */
10390 if (IF_HAS_KEYWORDS(rword == last_rword &&) 1) {
10391 if ((rcode == 0 && last_followup == PIPE_OR)
10392 || (rcode != 0 && last_followup == PIPE_AND)
10393 ) {
10394 /* It is "<true> || CMD" or "<false> && CMD"
10395 * and we should not execute CMD */
10396 debug_printf_exec("skipped cmd because of || or &&\n");
10397 last_followup = pi->followup;
10398 goto dont_check_jobs_but_continue;
10399 }
10400 }
10401 last_followup = pi->followup;
10402 IF_HAS_KEYWORDS(last_rword = rword;)
10403#if ENABLE_HUSH_IF
10404 if (cond_code) {
10405 if (rword == RES_THEN) {
10406 /* if false; then ... fi has exitcode 0! */
10407 G.last_exitcode = rcode = EXIT_SUCCESS;
10408 /* "if <false> THEN cmd": skip cmd */
10409 continue;
10410 }
10411 } else {
10412 if (rword == RES_ELSE || rword == RES_ELIF) {
10413 /* "if <true> then ... ELSE/ELIF cmd":
10414 * skip cmd and all following ones */
10415 break;
10416 }
10417 }
10418#endif
10419#if ENABLE_HUSH_LOOPS
10420 if (rword == RES_FOR) { /* && pi->num_cmds - always == 1 */
10421 if (!for_lcur) {
10422 /* first loop through for */
10423
10424 static const char encoded_dollar_at[] ALIGN1 = {
10425 SPECIAL_VAR_SYMBOL, '@' | 0x80, SPECIAL_VAR_SYMBOL, '\0'
10426 }; /* encoded representation of "$@" */
10427 static const char *const encoded_dollar_at_argv[] = {
10428 encoded_dollar_at, NULL
10429 }; /* argv list with one element: "$@" */
10430 char **vals;
10431
10432 G.last_exitcode = rcode = EXIT_SUCCESS;
10433 vals = (char**)encoded_dollar_at_argv;
10434 if (pi->next->res_word == RES_IN) {
10435 /* if no variable values after "in" we skip "for" */
10436 if (!pi->next->cmds[0].argv) {
10437 debug_printf_exec(": null FOR: exitcode EXIT_SUCCESS\n");
10438 break;
10439 }
10440 vals = pi->next->cmds[0].argv;
10441 } /* else: "for var; do..." -> assume "$@" list */
10442 /* create list of variable values */
10443 debug_print_strings("for_list made from", vals);
10444 for_list = expand_strvec_to_strvec(vals);
10445 for_lcur = for_list;
10446 debug_print_strings("for_list", for_list);
10447 }
10448 if (!*for_lcur) {
10449 /* "for" loop is over, clean up */
10450 free(for_list);
10451 for_list = NULL;
10452 for_lcur = NULL;
10453 break;
10454 }
10455 /* Insert next value from for_lcur */
10456 /* note: *for_lcur already has quotes removed, $var expanded, etc */
10457 set_local_var(xasprintf("%s=%s", pi->cmds[0].argv[0], *for_lcur++), /*flag:*/ 0);
10458 continue;
10459 }
10460 if (rword == RES_IN) {
10461 continue; /* "for v IN list;..." - "in" has no cmds anyway */
10462 }
10463 if (rword == RES_DONE) {
10464 continue; /* "done" has no cmds too */
10465 }
10466#endif
10467#if ENABLE_HUSH_CASE
10468 if (rword == RES_CASE) {
10469 debug_printf_exec("CASE cond_code:%d\n", cond_code);
10470 case_word = expand_string_to_string(pi->cmds->argv[0],
10471 EXP_FLAG_ESC_GLOB_CHARS, /*unbackslash:*/ 1);
10472 debug_printf_exec("CASE word1:'%s'\n", case_word);
10473 //unbackslash(case_word);
10474 //debug_printf_exec("CASE word2:'%s'\n", case_word);
10475 continue;
10476 }
10477 if (rword == RES_MATCH) {
10478 char **argv;
10479
10480 debug_printf_exec("MATCH cond_code:%d\n", cond_code);
10481 if (!case_word) /* "case ... matched_word) ... WORD)": we executed selected branch, stop */
10482 break;
10483 /* all prev words didn't match, does this one match? */
10484 argv = pi->cmds->argv;
10485 while (*argv) {
10486 char *pattern;
10487 debug_printf_exec("expand_string_to_string('%s')\n", *argv);
10488 pattern = expand_string_to_string(*argv,
10489 EXP_FLAG_ESC_GLOB_CHARS,
10490 /*unbackslash:*/ 0
10491 );
10492 /* TODO: which FNM_xxx flags to use? */
10493 cond_code = (fnmatch(pattern, case_word, /*flags:*/ 0) != 0);
10494 debug_printf_exec("fnmatch(pattern:'%s',str:'%s'):%d\n",
10495 pattern, case_word, cond_code);
10496 free(pattern);
10497 if (cond_code == 0) {
10498 /* match! we will execute this branch */
10499 free(case_word);
10500 case_word = NULL; /* make future "word)" stop */
10501 break;
10502 }
10503 argv++;
10504 }
10505 continue;
10506 }
10507 if (rword == RES_CASE_BODY) { /* inside of a case branch */
10508 debug_printf_exec("CASE_BODY cond_code:%d\n", cond_code);
10509 if (cond_code != 0)
10510 continue; /* not matched yet, skip this pipe */
10511 }
10512 if (rword == RES_ESAC) {
10513 debug_printf_exec("ESAC cond_code:%d\n", cond_code);
10514 if (case_word) {
10515 /* "case" did not match anything: still set $? (to 0) */
10516 G.last_exitcode = rcode = EXIT_SUCCESS;
10517 }
10518 }
10519#endif
10520 /* Just pressing <enter> in shell should check for jobs.
10521 * OTOH, in non-interactive shell this is useless
10522 * and only leads to extra job checks */
10523 if (pi->num_cmds == 0) {
Francis Laniel8197f012023-12-22 22:02:28 +010010524#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +010010525 if (G_interactive_fd)
10526 goto check_jobs_and_continue;
Francis Laniel8197f012023-12-22 22:02:28 +010010527#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +010010528 continue;
10529 }
10530
10531 /* After analyzing all keywords and conditions, we decided
10532 * to execute this pipe. NB: have to do checkjobs(NULL)
10533 * after run_pipe to collect any background children,
10534 * even if list execution is to be stopped. */
10535 debug_printf_exec(": run_pipe with %d members\n", pi->num_cmds);
Francis Laniel8197f012023-12-22 22:02:28 +010010536#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +010010537#if ENABLE_HUSH_LOOPS
10538 G.flag_break_continue = 0;
10539#endif
Francis Laniel8197f012023-12-22 22:02:28 +010010540#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +010010541 rcode = r = run_pipe(pi); /* NB: rcode is a smalluint, r is int */
Francis Laniel8197f012023-12-22 22:02:28 +010010542#ifdef __U_BOOT__
Francis Laniel9a068372023-12-22 22:02:32 +010010543 if (r <= EXIT_RET_CODE) {
10544 int previous_rcode = G.last_exitcode;
10545 /*
10546 * This magic is to get the exit code given by the user.
10547 * Contrary to old shell code, we use + EXIT_RET_CODE as EXIT_RET_CODE
10548 * equals -2.
10549 */
10550 G.last_exitcode = -r + EXIT_RET_CODE;
Francis Laniel8197f012023-12-22 22:02:28 +010010551
Francis Laniel9a068372023-12-22 22:02:32 +010010552 /*
10553 * This case deals with the following:
10554 * => setenv inner 'echo entry inner; exit; echo inner done'
10555 * => setenv outer 'echo entry outer; run inner; echo outer done'
10556 * => run outer
10557 * So, if we are in inner, we need to break and not run the other
10558 * commands.
10559 * Otherwise, we just continue in outer.
10560 * As return code are propagated, we use the previous value to check if
10561 * exit was just called or was propagated.
10562 */
10563 if (previous_rcode != r) {
10564 /*
10565 * If run from run_command, run_command_flags will be set, so we check
10566 * this to know if we are in main input shell.
10567 */
10568 if (!G.run_command_flags)
10569 printf("exit not allowed from main input shell.\n");
10570
10571 break;
10572 }
10573 continue;
Francis Laniel8197f012023-12-22 22:02:28 +010010574 }
Francis Laniel9a068372023-12-22 22:02:32 +010010575#endif /* __U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +010010576 if (r != -1) {
10577 /* We ran a builtin, function, or group.
10578 * rcode is already known
10579 * and we don't need to wait for anything. */
10580 debug_printf_exec(": builtin/func exitcode %d\n", rcode);
10581 G.last_exitcode = rcode;
Francis Laniel8197f012023-12-22 22:02:28 +010010582#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +010010583 check_and_run_traps();
Francis Laniel8197f012023-12-22 22:02:28 +010010584#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +010010585#if ENABLE_HUSH_TRAP && ENABLE_HUSH_FUNCTIONS
10586 rcode = G.last_exitcode; /* "return" in trap can change it, read back */
10587#endif
Francis Laniel8197f012023-12-22 22:02:28 +010010588#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +010010589#if ENABLE_HUSH_LOOPS
10590 /* Was it "break" or "continue"? */
10591 if (G.flag_break_continue) {
10592 smallint fbc = G.flag_break_continue;
10593 /* We might fall into outer *loop*,
10594 * don't want to break it too */
10595 if (loop_top) {
10596 G.depth_break_continue--;
10597 if (G.depth_break_continue == 0)
10598 G.flag_break_continue = 0;
10599 /* else: e.g. "continue 2" should *break* once, *then* continue */
10600 } /* else: "while... do... { we are here (innermost list is not a loop!) };...done" */
10601 if (G.depth_break_continue != 0 || fbc == BC_BREAK) {
10602 checkjobs(NULL, 0 /*(no pid to wait for)*/);
10603 break;
10604 }
10605 /* "continue": simulate end of loop */
10606 rword = RES_DONE;
10607 continue;
10608 }
10609#endif
10610 if (G_flag_return_in_progress == 1) {
10611 checkjobs(NULL, 0 /*(no pid to wait for)*/);
10612 break;
10613 }
Francis Laniel8197f012023-12-22 22:02:28 +010010614
Francis Lanielb234f7e2023-12-22 22:02:27 +010010615 } else if (pi->followup == PIPE_BG) {
10616 /* What does bash do with attempts to background builtins? */
10617 /* even bash 3.2 doesn't do that well with nested bg:
10618 * try "{ { sleep 10; echo DEEP; } & echo HERE; } &".
10619 * I'm NOT treating inner &'s as jobs */
10620#if ENABLE_HUSH_JOB
10621 if (G.run_list_level == 1)
10622 insert_job_into_table(pi);
10623#endif
10624 /* Last command's pid goes to $! */
10625 G.last_bg_pid = pi->cmds[pi->num_cmds - 1].pid;
10626 G.last_bg_pid_exitcode = 0;
10627 debug_printf_exec(": cmd&: exitcode EXIT_SUCCESS\n");
10628/* Check pi->pi_inverted? "! sleep 1 & echo $?": bash says 1. dash and ash say 0 */
10629 rcode = EXIT_SUCCESS;
10630 goto check_traps;
10631 } else {
10632#if ENABLE_HUSH_JOB
10633 if (G.run_list_level == 1 && G_interactive_fd) {
10634 /* Waits for completion, then fg's main shell */
10635 rcode = checkjobs_and_fg_shell(pi);
10636 debug_printf_exec(": checkjobs_and_fg_shell exitcode %d\n", rcode);
10637 goto check_traps;
10638 }
10639#endif
10640 /* This one just waits for completion */
10641 rcode = checkjobs(pi, 0 /*(no pid to wait for)*/);
10642 debug_printf_exec(": checkjobs exitcode %d\n", rcode);
10643 check_traps:
10644 G.last_exitcode = rcode;
10645 check_and_run_traps();
10646#if ENABLE_HUSH_TRAP && ENABLE_HUSH_FUNCTIONS
10647 rcode = G.last_exitcode; /* "return" in trap can change it, read back */
10648#endif
10649 }
Francis Laniel8197f012023-12-22 22:02:28 +010010650#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +010010651
Francis Laniel8197f012023-12-22 22:02:28 +010010652#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +010010653 /* Handle "set -e" */
10654 if (rcode != 0 && G.o_opt[OPT_O_ERREXIT]) {
10655 debug_printf_exec("ERREXIT:1 errexit_depth:%d\n", G.errexit_depth);
10656 if (G.errexit_depth == 0)
10657 hush_exit(rcode);
10658 }
Francis Laniel8197f012023-12-22 22:02:28 +010010659#else /* __U_BOOT__ */
10660 } /* if (r != -1) */
10661#endif /* __U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +010010662 G.errexit_depth = sv_errexit_depth;
10663
10664 /* Analyze how result affects subsequent commands */
10665#if ENABLE_HUSH_IF
10666 if (rword == RES_IF || rword == RES_ELIF)
10667 cond_code = rcode;
10668#endif
Francis Laniel8197f012023-12-22 22:02:28 +010010669#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +010010670 check_jobs_and_continue:
10671 checkjobs(NULL, 0 /*(no pid to wait for)*/);
Francis Laniel8197f012023-12-22 22:02:28 +010010672#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +010010673 dont_check_jobs_but_continue: ;
10674#if ENABLE_HUSH_LOOPS
10675 /* Beware of "while false; true; do ..."! */
10676 if (pi->next
10677 && (pi->next->res_word == RES_DO || pi->next->res_word == RES_DONE)
10678 /* check for RES_DONE is needed for "while ...; do \n done" case */
10679 ) {
10680 if (rword == RES_WHILE) {
10681 if (rcode) {
10682 /* "while false; do...done" - exitcode 0 */
10683 G.last_exitcode = rcode = EXIT_SUCCESS;
10684 debug_printf_exec(": while expr is false: breaking (exitcode:EXIT_SUCCESS)\n");
10685 break;
10686 }
10687 }
10688 if (rword == RES_UNTIL) {
10689 if (!rcode) {
10690 debug_printf_exec(": until expr is true: breaking\n");
10691 break;
10692 }
10693 }
10694 }
10695#endif
10696 } /* for (pi) */
10697
10698#if ENABLE_HUSH_JOB
10699 G.run_list_level--;
10700#endif
10701#if ENABLE_HUSH_LOOPS
10702 if (loop_top)
10703 G.depth_of_loop--;
10704 free(for_list);
10705#endif
10706#if ENABLE_HUSH_CASE
10707 free(case_word);
10708#endif
Francis Laniel8197f012023-12-22 22:02:28 +010010709#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +010010710 debug_leave();
10711 debug_printf_exec("run_list lvl %d return %d\n", G.run_list_level + 1, rcode);
Francis Laniel8197f012023-12-22 22:02:28 +010010712#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +010010713 return rcode;
10714}
10715
10716/* Select which version we will use */
10717static int run_and_free_list(struct pipe *pi)
10718{
10719 int rcode = 0;
10720 debug_printf_exec("run_and_free_list entered\n");
Francis Laniel8197f012023-12-22 22:02:28 +010010721#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +010010722 if (!G.o_opt[OPT_O_NOEXEC]) {
Francis Laniel8197f012023-12-22 22:02:28 +010010723#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +010010724 debug_printf_exec(": run_list: 1st pipe with %d cmds\n", pi->num_cmds);
10725 rcode = run_list(pi);
Francis Laniel8197f012023-12-22 22:02:28 +010010726#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +010010727 }
Francis Laniel8197f012023-12-22 22:02:28 +010010728#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +010010729 /* free_pipe_list has the side effect of clearing memory.
10730 * In the long run that function can be merged with run_list,
10731 * but doing that now would hobble the debugging effort. */
10732 free_pipe_list(pi);
10733 debug_printf_exec("run_and_free_list return %d\n", rcode);
10734 return rcode;
10735}
10736
10737
Francis Laniel8197f012023-12-22 22:02:28 +010010738#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +010010739static void install_sighandlers(unsigned mask)
10740{
10741 sighandler_t old_handler;
10742 unsigned sig = 0;
10743 while ((mask >>= 1) != 0) {
10744 sig++;
10745 if (!(mask & 1))
10746 continue;
10747 old_handler = install_sighandler(sig, pick_sighandler(sig));
10748 /* POSIX allows shell to re-enable SIGCHLD
10749 * even if it was SIG_IGN on entry.
10750 * Therefore we skip IGN check for it:
10751 */
10752 if (sig == SIGCHLD)
10753 continue;
10754 /* Interactive bash re-enables SIGHUP which is SIG_IGNed on entry.
10755 * Try:
10756 * trap '' hup; bash; echo RET # type "kill -hup $$", see SIGHUP having effect
10757 * trap '' hup; bash -c 'kill -hup $$; echo ALIVE' # here SIGHUP is SIG_IGNed
10758 */
10759 if (sig == SIGHUP && G_interactive_fd)
10760 continue;
10761 /* Unless one of the above signals, is it SIG_IGN? */
10762 if (old_handler == SIG_IGN) {
10763 /* oops... restore back to IGN, and record this fact */
10764 install_sighandler(sig, old_handler);
10765#if ENABLE_HUSH_TRAP
10766 if (!G_traps)
10767 G_traps = xzalloc(sizeof(G_traps[0]) * NSIG);
10768 free(G_traps[sig]);
10769 G_traps[sig] = xzalloc(1); /* == xstrdup(""); */
10770#endif
10771 }
10772 }
10773}
10774
10775/* Called a few times only (or even once if "sh -c") */
10776static void install_special_sighandlers(void)
10777{
10778 unsigned mask;
10779
10780 /* Which signals are shell-special? */
10781 mask = (1 << SIGQUIT) | (1 << SIGCHLD);
10782 if (G_interactive_fd) {
10783 mask |= SPECIAL_INTERACTIVE_SIGS;
10784 if (G_saved_tty_pgrp) /* we have ctty, job control sigs work */
10785 mask |= SPECIAL_JOBSTOP_SIGS;
10786 }
10787 /* Careful, do not re-install handlers we already installed */
10788 if (G.special_sig_mask != mask) {
10789 unsigned diff = mask & ~G.special_sig_mask;
10790 G.special_sig_mask = mask;
10791 install_sighandlers(diff);
10792 }
10793}
10794
10795#if ENABLE_HUSH_JOB
10796/* helper */
10797/* Set handlers to restore tty pgrp and exit */
10798static void install_fatal_sighandlers(void)
10799{
10800 unsigned mask;
10801
10802 /* We will restore tty pgrp on these signals */
10803 mask = 0
10804 /*+ (1 << SIGILL ) * HUSH_DEBUG*/
10805 /*+ (1 << SIGFPE ) * HUSH_DEBUG*/
10806 + (1 << SIGBUS ) * HUSH_DEBUG
10807 + (1 << SIGSEGV) * HUSH_DEBUG
10808 /*+ (1 << SIGTRAP) * HUSH_DEBUG*/
10809 + (1 << SIGABRT)
10810 /* bash 3.2 seems to handle these just like 'fatal' ones */
10811 + (1 << SIGPIPE)
10812 + (1 << SIGALRM)
10813 /* if we are interactive, SIGHUP, SIGTERM and SIGINT are special sigs.
10814 * if we aren't interactive... but in this case
10815 * we never want to restore pgrp on exit, and this fn is not called
10816 */
10817 /*+ (1 << SIGHUP )*/
10818 /*+ (1 << SIGTERM)*/
10819 /*+ (1 << SIGINT )*/
10820 ;
10821 G_fatal_sig_mask = mask;
10822
10823 install_sighandlers(mask);
10824}
10825#endif
10826
10827static int set_mode(int state, char mode, const char *o_opt)
10828{
10829 int idx;
10830 switch (mode) {
10831 case 'n':
10832 G.o_opt[OPT_O_NOEXEC] = state;
10833 break;
10834 case 'x':
10835 IF_HUSH_MODE_X(G_x_mode = state;)
10836 IF_HUSH_MODE_X(if (G.x_mode_fd <= 0) G.x_mode_fd = dup_CLOEXEC(2, 10);)
10837 break;
10838 case 'e':
10839 G.o_opt[OPT_O_ERREXIT] = state;
10840 break;
10841 case 'o':
10842 if (!o_opt) {
10843 /* "set -o" or "set +o" without parameter.
10844 * in bash, set -o produces this output:
10845 * pipefail off
10846 * and set +o:
10847 * set +o pipefail
10848 * We always use the second form.
10849 */
10850 const char *p = o_opt_strings;
10851 idx = 0;
10852 while (*p) {
10853 printf("set %co %s\n", (G.o_opt[idx] ? '-' : '+'), p);
10854 idx++;
10855 p += strlen(p) + 1;
10856 }
10857 break;
10858 }
10859 idx = index_in_strings(o_opt_strings, o_opt);
10860 if (idx >= 0) {
10861 G.o_opt[idx] = state;
10862 break;
10863 }
10864 /* fall through to error */
10865 default:
10866 return EXIT_FAILURE;
10867 }
10868 return EXIT_SUCCESS;
10869}
10870
10871int hush_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
10872int hush_main(int argc, char **argv)
10873{
10874 pid_t cached_getpid;
10875 enum {
10876 OPT_login = (1 << 0),
10877 };
10878 unsigned flags;
10879#if !BB_MMU
10880 unsigned builtin_argc = 0;
10881#endif
10882 char **e;
10883 struct variable *cur_var;
10884 struct variable *shell_ver;
10885
10886 INIT_G();
10887 if (EXIT_SUCCESS != 0) /* if EXIT_SUCCESS == 0, it is already done */
10888 G.last_exitcode = EXIT_SUCCESS;
10889#if ENABLE_HUSH_TRAP
10890# if ENABLE_HUSH_FUNCTIONS
10891 G.return_exitcode = -1;
10892# endif
10893 G.pre_trap_exitcode = -1;
10894#endif
10895
10896#if ENABLE_HUSH_FAST
10897 G.count_SIGCHLD++; /* ensure it is != G.handled_SIGCHLD */
10898#endif
10899#if !BB_MMU
10900 G.argv0_for_re_execing = argv[0];
10901#endif
10902
10903 cached_getpid = getpid(); /* for tcsetpgrp() during init */
10904 G.root_pid = cached_getpid; /* for $PID (NOMMU can override via -$HEXPID:HEXPPID:...) */
10905 G.root_ppid = getppid(); /* for $PPID (NOMMU can override) */
10906
10907 /* Deal with HUSH_VERSION */
10908 debug_printf_env("unsetenv '%s'\n", "HUSH_VERSION");
10909 unsetenv("HUSH_VERSION"); /* in case it exists in initial env */
10910 shell_ver = xzalloc(sizeof(*shell_ver));
10911 shell_ver->flg_export = 1;
10912 shell_ver->flg_read_only = 1;
10913 /* Code which handles ${var<op>...} needs writable values for all variables,
10914 * therefore we xstrdup: */
10915 shell_ver->varstr = xstrdup(hush_version_str);
10916
10917 /* Create shell local variables from the values
10918 * currently living in the environment */
10919 G.top_var = shell_ver;
10920 cur_var = G.top_var;
10921 e = environ;
10922 if (e) while (*e) {
10923 char *value = strchr(*e, '=');
10924 if (value) { /* paranoia */
10925 cur_var->next = xzalloc(sizeof(*cur_var));
10926 cur_var = cur_var->next;
10927 cur_var->varstr = *e;
10928 cur_var->max_len = strlen(*e);
10929 cur_var->flg_export = 1;
10930 }
10931 e++;
10932 }
10933 /* (Re)insert HUSH_VERSION into env (AFTER we scanned the env!) */
10934 debug_printf_env("putenv '%s'\n", shell_ver->varstr);
10935 putenv(shell_ver->varstr);
10936
10937 /* Export PWD */
10938 set_pwd_var(SETFLAG_EXPORT);
10939
10940#if BASH_HOSTNAME_VAR
10941 /* Set (but not export) HOSTNAME unless already set */
10942 if (!get_local_var_value("HOSTNAME")) {
10943 struct utsname uts;
10944 uname(&uts);
10945 set_local_var_from_halves("HOSTNAME", uts.nodename);
10946 }
10947#endif
10948 /* IFS is not inherited from the parent environment */
10949 set_local_var_from_halves("IFS", defifs);
10950
10951 if (!get_local_var_value("PATH"))
10952 set_local_var_from_halves("PATH", bb_default_root_path);
10953
10954 /* PS1/PS2 are set later, if we determine that we are interactive */
10955
10956 /* bash also exports SHLVL and _,
10957 * and sets (but doesn't export) the following variables:
10958 * BASH=/bin/bash
10959 * BASH_VERSINFO=([0]="3" [1]="2" [2]="0" [3]="1" [4]="release" [5]="i386-pc-linux-gnu")
10960 * BASH_VERSION='3.2.0(1)-release'
10961 * HOSTTYPE=i386
10962 * MACHTYPE=i386-pc-linux-gnu
10963 * OSTYPE=linux-gnu
10964 * PPID=<NNNNN> - we also do it elsewhere
10965 * EUID=<NNNNN>
10966 * UID=<NNNNN>
10967 * GROUPS=()
10968 * LINES=<NNN>
10969 * COLUMNS=<NNN>
10970 * BASH_ARGC=()
10971 * BASH_ARGV=()
10972 * BASH_LINENO=()
10973 * BASH_SOURCE=()
10974 * DIRSTACK=()
10975 * PIPESTATUS=([0]="0")
10976 * HISTFILE=/<xxx>/.bash_history
10977 * HISTFILESIZE=500
10978 * HISTSIZE=500
10979 * MAILCHECK=60
10980 * PATH=/usr/gnu/bin:/usr/local/bin:/bin:/usr/bin:.
10981 * SHELL=/bin/bash
10982 * SHELLOPTS=braceexpand:emacs:hashall:histexpand:history:interactive-comments:monitor
10983 * TERM=dumb
10984 * OPTERR=1
10985 * OPTIND=1
10986 * PS4='+ '
10987 */
10988
10989#if NUM_SCRIPTS > 0
10990 if (argc < 0) {
10991 char *script = get_script_content(-argc - 1);
10992 G.global_argv = argv;
10993 G.global_argc = string_array_len(argv);
10994 //install_special_sighandlers(); - needed?
10995 parse_and_run_string(script);
10996 goto final_return;
10997 }
10998#endif
10999
11000 /* Initialize some more globals to non-zero values */
11001 die_func = restore_ttypgrp_and__exit;
11002
11003 /* Shell is non-interactive at first. We need to call
11004 * install_special_sighandlers() if we are going to execute "sh <script>",
11005 * "sh -c <cmds>" or login shell's /etc/profile and friends.
11006 * If we later decide that we are interactive, we run install_special_sighandlers()
11007 * in order to intercept (more) signals.
11008 */
11009
11010 /* Parse options */
11011 /* http://www.opengroup.org/onlinepubs/9699919799/utilities/sh.html */
11012 flags = (argv[0] && argv[0][0] == '-') ? OPT_login : 0;
11013 while (1) {
11014 int opt = getopt(argc, argv, "+" /* stop at 1st non-option */
11015 "cexinsl"
11016#if !BB_MMU
11017 "<:$:R:V:"
11018# if ENABLE_HUSH_FUNCTIONS
11019 "F:"
11020# endif
11021#endif
11022 );
11023 if (opt <= 0)
11024 break;
11025 switch (opt) {
11026 case 'c':
11027 /* Note: -c is not an option with param!
11028 * "hush -c -l SCRIPT" is valid. "hush -cSCRIPT" is not.
11029 */
11030 G.opt_c = 1;
11031 break;
11032 case 'i':
11033 /* Well, we cannot just declare interactiveness,
11034 * we have to have some stuff (ctty, etc) */
11035 /* G_interactive_fd++; */
11036 break;
11037 case 's':
11038 G.opt_s = 1;
11039 break;
11040 case 'l':
11041 flags |= OPT_login;
11042 break;
11043#if !BB_MMU
11044 case '<': /* "big heredoc" support */
11045 full_write1_str(optarg);
11046 _exit(0);
11047 case '$': {
11048 unsigned long long empty_trap_mask;
11049
11050 G.root_pid = bb_strtou(optarg, &optarg, 16);
11051 optarg++;
11052 G.root_ppid = bb_strtou(optarg, &optarg, 16);
11053 optarg++;
11054 G.last_bg_pid = bb_strtou(optarg, &optarg, 16);
11055 optarg++;
11056 G.last_exitcode = bb_strtou(optarg, &optarg, 16);
11057 optarg++;
11058 builtin_argc = bb_strtou(optarg, &optarg, 16);
11059 optarg++;
11060 empty_trap_mask = bb_strtoull(optarg, &optarg, 16);
11061 if (empty_trap_mask != 0) {
11062 IF_HUSH_TRAP(int sig;)
11063 install_special_sighandlers();
11064# if ENABLE_HUSH_TRAP
11065 G_traps = xzalloc(sizeof(G_traps[0]) * NSIG);
11066 for (sig = 1; sig < NSIG; sig++) {
11067 if (empty_trap_mask & (1LL << sig)) {
11068 G_traps[sig] = xzalloc(1); /* == xstrdup(""); */
11069 install_sighandler(sig, SIG_IGN);
11070 }
11071 }
11072# endif
11073 }
11074# if ENABLE_HUSH_LOOPS
11075 optarg++;
11076 G.depth_of_loop = bb_strtou(optarg, &optarg, 16);
11077# endif
11078 /* Suppress "killed by signal" message, -$ hack is used
11079 * for subshells: echo `sh -c 'kill -9 $$'`
11080 * should be silent.
11081 */
11082 IF_HUSH_JOB(G.run_list_level = 1;)
11083# if ENABLE_HUSH_FUNCTIONS
11084 /* nommu uses re-exec trick for "... | func | ...",
11085 * should allow "return".
11086 * This accidentally allows returns in subshells.
11087 */
11088 G_flag_return_in_progress = -1;
11089# endif
11090 break;
11091 }
11092 case 'R':
11093 case 'V':
11094 set_local_var(xstrdup(optarg), opt == 'R' ? SETFLAG_MAKE_RO : 0);
11095 break;
11096# if ENABLE_HUSH_FUNCTIONS
11097 case 'F': {
11098 struct function *funcp = new_function(optarg);
11099 /* funcp->name is already set to optarg */
11100 /* funcp->body is set to NULL. It's a special case. */
11101 funcp->body_as_string = argv[optind];
11102 optind++;
11103 break;
11104 }
11105# endif
11106#endif
11107 /*case '?': invalid option encountered (set_mode('?') will fail) */
11108 /*case 'n':*/
11109 /*case 'x':*/
11110 /*case 'e':*/
11111 default:
11112 if (set_mode(1, opt, NULL) == 0) /* no error */
11113 break;
11114 bb_show_usage();
11115 }
11116 } /* option parsing loop */
11117
11118 /* Skip options. Try "hush -l": $1 should not be "-l"! */
11119 G.global_argc = argc - (optind - 1);
11120 G.global_argv = argv + (optind - 1);
11121 G.global_argv[0] = argv[0];
11122
11123 /* If we are login shell... */
11124 if (flags & OPT_login) {
11125 const char *hp = NULL;
11126 HFILE *input;
11127
11128 debug_printf("sourcing /etc/profile\n");
11129 input = hfopen("/etc/profile");
11130 run_profile:
11131 if (input != NULL) {
11132 install_special_sighandlers();
11133 parse_and_run_file(input);
11134 hfclose(input);
11135 }
11136 /* bash: after sourcing /etc/profile,
11137 * tries to source (in the given order):
11138 * ~/.bash_profile, ~/.bash_login, ~/.profile,
11139 * stopping on first found. --noprofile turns this off.
11140 * bash also sources ~/.bash_logout on exit.
11141 * If called as sh, skips .bash_XXX files.
11142 */
11143 if (!hp) { /* unless we looped on the "goto" already */
11144 hp = get_local_var_value("HOME");
11145 if (hp && hp[0]) {
11146 debug_printf("sourcing ~/.profile\n");
11147 hp = concat_path_file(hp, ".profile");
11148 input = hfopen(hp);
11149 free((char*)hp);
11150 goto run_profile;
11151 }
11152 }
11153 }
11154
Francis Laniel8197f012023-12-22 22:02:28 +010011155#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +010011156 /* -c takes effect *after* -l */
11157 if (G.opt_c) {
11158 /* Possibilities:
11159 * sh ... -c 'script'
11160 * sh ... -c 'script' ARG0 [ARG1...]
11161 * On NOMMU, if builtin_argc != 0,
11162 * sh ... -c 'builtin' BARGV... "" ARG0 [ARG1...]
11163 * "" needs to be replaced with NULL
11164 * and BARGV vector fed to builtin function.
11165 * Note: the form without ARG0 never happens:
11166 * sh ... -c 'builtin' BARGV... ""
11167 */
11168 char *script;
11169
11170 install_special_sighandlers();
11171
11172 G.global_argc--;
11173 G.global_argv++;
11174#if !BB_MMU
11175 if (builtin_argc) {
11176 /* -c 'builtin' [BARGV...] "" ARG0 [ARG1...] */
11177 const struct built_in_command *x;
11178 x = find_builtin(G.global_argv[0]);
11179 if (x) { /* paranoia */
11180 argv = G.global_argv;
11181 G.global_argc -= builtin_argc + 1; /* skip [BARGV...] "" */
11182 G.global_argv += builtin_argc + 1;
11183 G.global_argv[-1] = NULL; /* replace "" */
11184 G.last_exitcode = x->b_function(argv);
11185 }
11186 goto final_return;
11187 }
11188#endif
11189
11190 script = G.global_argv[0];
11191 if (!script)
11192 bb_error_msg_and_die(bb_msg_requires_arg, "-c");
11193 if (!G.global_argv[1]) {
11194 /* -c 'script' (no params): prevent empty $0 */
11195 G.global_argv[0] = argv[0];
11196 } else { /* else -c 'script' ARG0 [ARG1...]: $0 is ARG0 */
11197 G.global_argc--;
11198 G.global_argv++;
11199 }
11200 parse_and_run_string(script);
11201 goto final_return;
11202 }
11203
11204 /* -s is: hush -s ARGV1 ARGV2 (no SCRIPT) */
11205 if (!G.opt_s && G.global_argv[1]) {
11206 HFILE *input;
11207 /*
11208 * "bash <script>" (which is never interactive (unless -i?))
11209 * sources $BASH_ENV here (without scanning $PATH).
11210 * If called as sh, does the same but with $ENV.
11211 * Also NB, per POSIX, $ENV should undergo parameter expansion.
11212 */
11213 G.global_argc--;
11214 G.global_argv++;
11215 debug_printf("running script '%s'\n", G.global_argv[0]);
11216 xfunc_error_retval = 127; /* for "hush /does/not/exist" case */
11217 input = hfopen(G.global_argv[0]);
11218 if (!input) {
11219 bb_simple_perror_msg_and_die(G.global_argv[0]);
11220 }
11221 xfunc_error_retval = 1;
11222 install_special_sighandlers();
11223 parse_and_run_file(input);
11224#if ENABLE_FEATURE_CLEAN_UP
11225 hfclose(input);
11226#endif
11227 goto final_return;
11228 }
11229 /* "implicit" -s: bare interactive hush shows 's' in $- */
11230 G.opt_s = 1;
11231
Francis Laniel8197f012023-12-22 22:02:28 +010011232#endif /* __U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +010011233 /* Up to here, shell was non-interactive. Now it may become one.
11234 * NB: don't forget to (re)run install_special_sighandlers() as needed.
11235 */
11236
11237 /* A shell is interactive if the '-i' flag was given,
11238 * or if all of the following conditions are met:
11239 * no -c command
11240 * no arguments remaining or the -s flag given
11241 * standard input is a terminal
11242 * standard output is a terminal
11243 * Refer to Posix.2, the description of the 'sh' utility.
11244 */
11245#if ENABLE_HUSH_JOB
11246 if (isatty(STDIN_FILENO) && isatty(STDOUT_FILENO)) {
11247 G_saved_tty_pgrp = tcgetpgrp(STDIN_FILENO);
11248 debug_printf("saved_tty_pgrp:%d\n", G_saved_tty_pgrp);
11249 if (G_saved_tty_pgrp < 0)
11250 G_saved_tty_pgrp = 0;
11251
11252 /* try to dup stdin to high fd#, >= 255 */
11253 G_interactive_fd = dup_CLOEXEC(STDIN_FILENO, 254);
11254 if (G_interactive_fd < 0) {
11255 /* try to dup to any fd */
11256 G_interactive_fd = dup(STDIN_FILENO);
11257 if (G_interactive_fd < 0) {
11258 /* give up */
11259 G_interactive_fd = 0;
11260 G_saved_tty_pgrp = 0;
11261 }
11262 }
11263 }
11264 debug_printf("interactive_fd:%d\n", G_interactive_fd);
11265 if (G_interactive_fd) {
11266 close_on_exec_on(G_interactive_fd);
11267
11268 if (G_saved_tty_pgrp) {
11269 /* If we were run as 'hush &', sleep until we are
11270 * in the foreground (tty pgrp == our pgrp).
11271 * If we get started under a job aware app (like bash),
11272 * make sure we are now in charge so we don't fight over
11273 * who gets the foreground */
11274 while (1) {
11275 pid_t shell_pgrp = getpgrp();
11276 G_saved_tty_pgrp = tcgetpgrp(G_interactive_fd);
11277 if (G_saved_tty_pgrp == shell_pgrp)
11278 break;
11279 /* send TTIN to ourself (should stop us) */
11280 kill(- shell_pgrp, SIGTTIN);
11281 }
11282 }
11283
11284 /* Install more signal handlers */
11285 install_special_sighandlers();
11286
11287 if (G_saved_tty_pgrp) {
11288 /* Set other signals to restore saved_tty_pgrp */
11289 install_fatal_sighandlers();
11290 /* Put ourselves in our own process group
11291 * (bash, too, does this only if ctty is available) */
11292 bb_setpgrp(); /* is the same as setpgid(our_pid, our_pid); */
11293 /* Grab control of the terminal */
11294 tcsetpgrp(G_interactive_fd, cached_getpid);
11295 }
11296 enable_restore_tty_pgrp_on_exit();
11297
11298# if ENABLE_FEATURE_EDITING
11299 G.line_input_state = new_line_input_t(FOR_SHELL);
11300# if EDITING_HAS_get_exe_name
11301 G.line_input_state->get_exe_name = get_builtin_name;
11302# endif
11303# endif
11304# if ENABLE_HUSH_SAVEHISTORY && MAX_HISTORY > 0
11305 {
11306 const char *hp = get_local_var_value("HISTFILE");
11307 if (!hp) {
11308 hp = get_local_var_value("HOME");
11309 if (hp)
11310 hp = concat_path_file(hp, ".hush_history");
11311 } else {
11312 hp = xstrdup(hp);
11313 }
11314 if (hp) {
11315 G.line_input_state->hist_file = hp;
11316 //set_local_var(xasprintf("HISTFILE=%s", ...));
11317 }
11318# if ENABLE_FEATURE_SH_HISTFILESIZE
11319 hp = get_local_var_value("HISTFILESIZE");
11320 G.line_input_state->max_history = size_from_HISTFILESIZE(hp);
11321# endif
11322 }
11323# endif
11324 } else {
11325 install_special_sighandlers();
11326 }
11327#elif ENABLE_HUSH_INTERACTIVE
11328 /* No job control compiled in, only prompt/line editing */
11329 if (isatty(STDIN_FILENO) && isatty(STDOUT_FILENO)) {
11330 G_interactive_fd = dup_CLOEXEC(STDIN_FILENO, 254);
11331 if (G_interactive_fd < 0) {
11332 /* try to dup to any fd */
11333 G_interactive_fd = dup_CLOEXEC(STDIN_FILENO, -1);
11334 if (G_interactive_fd < 0)
11335 /* give up */
11336 G_interactive_fd = 0;
11337 }
11338 }
11339 if (G_interactive_fd) {
11340 close_on_exec_on(G_interactive_fd);
11341 }
11342 install_special_sighandlers();
11343#else
11344 /* We have interactiveness code disabled */
11345 install_special_sighandlers();
11346#endif
11347 /* bash:
11348 * if interactive but not a login shell, sources ~/.bashrc
11349 * (--norc turns this off, --rcfile <file> overrides)
11350 */
11351
11352 if (G_interactive_fd) {
11353#if ENABLE_HUSH_INTERACTIVE && ENABLE_FEATURE_EDITING_FANCY_PROMPT
11354 /* Set (but not export) PS1/2 unless already set */
11355 if (!get_local_var_value("PS1"))
11356 set_local_var_from_halves("PS1", "\\w \\$ ");
11357 if (!get_local_var_value("PS2"))
11358 set_local_var_from_halves("PS2", "> ");
11359#endif
11360 if (!ENABLE_FEATURE_SH_EXTRA_QUIET) {
11361 /* note: ash and hush share this string */
11362 printf("\n\n%s %s\n"
11363 IF_HUSH_HELP("Enter 'help' for a list of built-in commands.\n")
11364 "\n",
11365 bb_banner,
11366 "hush - the humble shell"
11367 );
11368 }
11369 }
11370
11371 parse_and_run_file(hfopen(NULL)); /* stdin */
11372
11373 final_return:
11374 hush_exit(G.last_exitcode);
11375}
11376
11377
Francis Laniel8197f012023-12-22 22:02:28 +010011378
Francis Lanielb234f7e2023-12-22 22:02:27 +010011379/*
11380 * Built-ins
11381 */
11382static int FAST_FUNC builtin_true(char **argv UNUSED_PARAM)
11383{
11384 return 0;
11385}
11386
11387#if ENABLE_HUSH_TEST || ENABLE_HUSH_ECHO || ENABLE_HUSH_PRINTF || ENABLE_HUSH_KILL
11388static NOINLINE int run_applet_main(char **argv, int (*applet_main_func)(int argc, char **argv))
11389{
11390 int argc = string_array_len(argv);
11391 return applet_main_func(argc, argv);
11392}
11393#endif
11394#if ENABLE_HUSH_TEST || BASH_TEST2
11395static int FAST_FUNC builtin_test(char **argv)
11396{
11397 return run_applet_main(argv, test_main);
11398}
11399#endif
11400#if ENABLE_HUSH_ECHO
11401static int FAST_FUNC builtin_echo(char **argv)
11402{
11403 return run_applet_main(argv, echo_main);
11404}
11405#endif
11406#if ENABLE_HUSH_PRINTF
11407static int FAST_FUNC builtin_printf(char **argv)
11408{
11409 return run_applet_main(argv, printf_main);
11410}
11411#endif
11412
11413#if ENABLE_HUSH_HELP
11414static int FAST_FUNC builtin_help(char **argv UNUSED_PARAM)
11415{
11416 const struct built_in_command *x;
11417
11418 printf(
11419 "Built-in commands:\n"
11420 "------------------\n");
11421 for (x = bltins1; x != &bltins1[ARRAY_SIZE(bltins1)]; x++) {
11422 if (x->b_descr)
11423 printf("%-10s%s\n", x->b_cmd, x->b_descr);
11424 }
11425 return EXIT_SUCCESS;
11426}
11427#endif
11428
11429#if MAX_HISTORY && ENABLE_FEATURE_EDITING
11430static int FAST_FUNC builtin_history(char **argv UNUSED_PARAM)
11431{
11432 show_history(G.line_input_state);
11433 return EXIT_SUCCESS;
11434}
11435#endif
11436
11437static char **skip_dash_dash(char **argv)
11438{
11439 argv++;
11440 if (argv[0] && argv[0][0] == '-' && argv[0][1] == '-' && argv[0][2] == '\0')
11441 argv++;
11442 return argv;
11443}
11444
11445static int FAST_FUNC builtin_cd(char **argv)
11446{
11447 const char *newdir;
11448
11449 argv = skip_dash_dash(argv);
11450 newdir = argv[0];
11451 if (newdir == NULL) {
11452 /* bash does nothing (exitcode 0) if HOME is ""; if it's unset,
11453 * bash says "bash: cd: HOME not set" and does nothing
11454 * (exitcode 1)
11455 */
11456 const char *home = get_local_var_value("HOME");
11457 newdir = home ? home : "/";
11458 }
11459 if (chdir(newdir)) {
11460 /* Mimic bash message exactly */
11461 bb_perror_msg("cd: %s", newdir);
11462 return EXIT_FAILURE;
11463 }
11464 /* Read current dir (get_cwd(1) is inside) and set PWD.
11465 * Note: do not enforce exporting. If PWD was unset or unexported,
11466 * set it again, but do not export. bash does the same.
11467 */
11468 set_pwd_var(/*flag:*/ 0);
11469 return EXIT_SUCCESS;
11470}
11471
11472static int FAST_FUNC builtin_pwd(char **argv UNUSED_PARAM)
11473{
11474 puts(get_cwd(0));
11475 return EXIT_SUCCESS;
11476}
11477
11478static int FAST_FUNC builtin_eval(char **argv)
11479{
11480 argv = skip_dash_dash(argv);
11481
11482 if (!argv[0])
11483 return EXIT_SUCCESS;
11484
11485 IF_HUSH_MODE_X(G.x_mode_depth++;)
11486 //bb_error_msg("%s: ++x_mode_depth=%d", __func__, G.x_mode_depth);
11487 if (!argv[1]) {
11488 /* bash:
11489 * eval "echo Hi; done" ("done" is syntax error):
11490 * "echo Hi" will not execute too.
11491 */
11492 parse_and_run_string(argv[0]);
11493 } else {
11494 /* "The eval utility shall construct a command by
11495 * concatenating arguments together, separating
11496 * each with a <space> character."
11497 */
11498 char *str, *p;
11499 unsigned len = 0;
11500 char **pp = argv;
11501 do
11502 len += strlen(*pp) + 1;
11503 while (*++pp);
11504 str = p = xmalloc(len);
11505 pp = argv;
11506 for (;;) {
11507 p = stpcpy(p, *pp);
11508 pp++;
11509 if (!*pp)
11510 break;
11511 *p++ = ' ';
11512 }
11513 parse_and_run_string(str);
11514 free(str);
11515 }
11516 IF_HUSH_MODE_X(G.x_mode_depth--;)
11517 //bb_error_msg("%s: --x_mode_depth=%d", __func__, G.x_mode_depth);
11518 return G.last_exitcode;
11519}
11520
11521static int FAST_FUNC builtin_exec(char **argv)
11522{
11523 argv = skip_dash_dash(argv);
11524 if (argv[0] == NULL)
11525 return EXIT_SUCCESS; /* bash does this */
11526
11527 /* Careful: we can end up here after [v]fork. Do not restore
11528 * tty pgrp then, only top-level shell process does that */
11529 if (G_saved_tty_pgrp && getpid() == G.root_pid)
11530 tcsetpgrp(G_interactive_fd, G_saved_tty_pgrp);
11531
11532 /* Saved-redirect fds, script fds and G_interactive_fd are still
11533 * open here. However, they are all CLOEXEC, and execv below
11534 * closes them. Try interactive "exec ls -l /proc/self/fd",
11535 * it should show no extra open fds in the "ls" process.
11536 * If we'd try to run builtins/NOEXECs, this would need improving.
11537 */
11538 //close_saved_fds_and_FILE_fds();
11539
11540 /* TODO: if exec fails, bash does NOT exit! We do.
11541 * We'll need to undo trap cleanup (it's inside execvp_or_die)
11542 * and tcsetpgrp, and this is inherently racy.
11543 */
11544 execvp_or_die(argv);
11545}
11546
11547static int FAST_FUNC builtin_exit(char **argv)
11548{
11549 debug_printf_exec("%s()\n", __func__);
11550
11551 /* interactive bash:
11552 * # trap "echo EEE" EXIT
11553 * # exit
11554 * exit
11555 * There are stopped jobs.
11556 * (if there are _stopped_ jobs, running ones don't count)
11557 * # exit
11558 * exit
11559 * EEE (then bash exits)
11560 *
11561 * TODO: we can use G.exiting = -1 as indicator "last cmd was exit"
11562 */
11563
11564 /* note: EXIT trap is run by hush_exit */
11565 argv = skip_dash_dash(argv);
11566 if (argv[0] == NULL) {
11567#if ENABLE_HUSH_TRAP
11568 if (G.pre_trap_exitcode >= 0) /* "exit" in trap uses $? from before the trap */
11569 hush_exit(G.pre_trap_exitcode);
11570#endif
11571 hush_exit(G.last_exitcode);
11572 }
11573 /* mimic bash: exit 123abc == exit 255 + error msg */
11574 xfunc_error_retval = 255;
11575 /* bash: exit -2 == exit 254, no error msg */
11576 hush_exit(xatoi(argv[0]) & 0xff);
11577}
11578
11579#if ENABLE_HUSH_TYPE
11580/* http://www.opengroup.org/onlinepubs/9699919799/utilities/type.html */
11581static int FAST_FUNC builtin_type(char **argv)
11582{
11583 int ret = EXIT_SUCCESS;
11584
11585 while (*++argv) {
11586 const char *type;
11587 char *path = NULL;
11588
11589 if (0) {} /* make conditional compile easier below */
11590 /*else if (find_alias(*argv))
11591 type = "an alias";*/
11592# if ENABLE_HUSH_FUNCTIONS
11593 else if (find_function(*argv))
11594 type = "a function";
11595# endif
11596 else if (find_builtin(*argv))
11597 type = "a shell builtin";
11598 else if ((path = find_in_path(*argv)) != NULL)
11599 type = path;
11600 else {
11601 bb_error_msg("type: %s: not found", *argv);
11602 ret = EXIT_FAILURE;
11603 continue;
11604 }
11605
11606 printf("%s is %s\n", *argv, type);
11607 free(path);
11608 }
11609
11610 return ret;
11611}
11612#endif
11613
11614#if ENABLE_HUSH_READ
11615/* Interruptibility of read builtin in bash
11616 * (tested on bash-4.2.8 by sending signals (not by ^C)):
11617 *
11618 * Empty trap makes read ignore corresponding signal, for any signal.
11619 *
11620 * SIGINT:
11621 * - terminates non-interactive shell;
11622 * - interrupts read in interactive shell;
11623 * if it has non-empty trap:
11624 * - executes trap and returns to command prompt in interactive shell;
11625 * - executes trap and returns to read in non-interactive shell;
11626 * SIGTERM:
11627 * - is ignored (does not interrupt) read in interactive shell;
11628 * - terminates non-interactive shell;
11629 * if it has non-empty trap:
11630 * - executes trap and returns to read;
11631 * SIGHUP:
11632 * - terminates shell (regardless of interactivity);
11633 * if it has non-empty trap:
11634 * - executes trap and returns to read;
11635 * SIGCHLD from children:
11636 * - does not interrupt read regardless of interactivity:
11637 * try: sleep 1 & read x; echo $x
11638 */
11639static int FAST_FUNC builtin_read(char **argv)
11640{
11641 const char *r;
11642 struct builtin_read_params params;
11643
11644 memset(&params, 0, sizeof(params));
11645
11646 /* "!": do not abort on errors.
11647 * Option string must start with "sr" to match BUILTIN_READ_xxx
11648 */
11649 params.read_flags = getopt32(argv,
11650# if BASH_READ_D
11651 IF_NOT_HUSH_BASH_COMPAT("^")
11652 "!srn:p:t:u:d:" IF_NOT_HUSH_BASH_COMPAT("\0" "-1"/*min 1 arg*/),
11653 &params.opt_n, &params.opt_p, &params.opt_t, &params.opt_u, &params.opt_d
11654# else
11655 IF_NOT_HUSH_BASH_COMPAT("^")
11656 "!srn:p:t:u:" IF_NOT_HUSH_BASH_COMPAT("\0" "-1"/*min 1 arg*/),
11657 &params.opt_n, &params.opt_p, &params.opt_t, &params.opt_u
11658# endif
11659//TODO: print "read: need variable name"
11660//for the case of !BASH "read" with no args (now it fails silently)
11661//(or maybe extend getopt32() to emit a message if "-1" fails)
11662 );
11663 if ((uint32_t)params.read_flags == (uint32_t)-1)
11664 return EXIT_FAILURE;
11665 argv += optind;
11666 params.argv = argv;
11667 params.setvar = set_local_var_from_halves;
11668 params.ifs = get_local_var_value("IFS"); /* can be NULL */
11669
11670 again:
11671 r = shell_builtin_read(&params);
11672
11673 if ((uintptr_t)r == 1 && errno == EINTR) {
11674 unsigned sig = check_and_run_traps();
11675 if (sig != SIGINT)
11676 goto again;
11677 }
11678
11679 if ((uintptr_t)r > 1) {
11680 bb_simple_error_msg(r);
11681 r = (char*)(uintptr_t)1;
11682 }
11683
11684 return (uintptr_t)r;
11685}
11686#endif
11687
11688#if ENABLE_HUSH_UMASK
11689static int FAST_FUNC builtin_umask(char **argv)
11690{
11691 int rc;
11692 mode_t mask;
11693
11694 rc = 1;
11695 mask = umask(0);
11696 argv = skip_dash_dash(argv);
11697 if (argv[0]) {
11698 mode_t old_mask = mask;
11699
11700 /* numeric umasks are taken as-is */
11701 /* symbolic umasks are inverted: "umask a=rx" calls umask(222) */
11702 if (!isdigit(argv[0][0]))
11703 mask ^= 0777;
11704 mask = bb_parse_mode(argv[0], mask);
11705 if (!isdigit(argv[0][0]))
11706 mask ^= 0777;
11707 if ((unsigned)mask > 0777) {
11708 mask = old_mask;
11709 /* bash messages:
11710 * bash: umask: 'q': invalid symbolic mode operator
11711 * bash: umask: 999: octal number out of range
11712 */
11713 bb_error_msg("%s: invalid mode '%s'", "umask", argv[0]);
11714 rc = 0;
11715 }
11716 } else {
11717 /* Mimic bash */
11718 printf("%04o\n", (unsigned) mask);
11719 /* fall through and restore mask which we set to 0 */
11720 }
11721 umask(mask);
11722
11723 return !rc; /* rc != 0 - success */
11724}
11725#endif
11726
11727#if ENABLE_HUSH_EXPORT || ENABLE_HUSH_TRAP
11728static void print_escaped(const char *s)
11729{
11730 if (*s == '\'')
11731 goto squote;
11732 do {
11733 const char *p = strchrnul(s, '\'');
11734 /* print 'xxxx', possibly just '' */
11735 printf("'%.*s'", (int)(p - s), s);
11736 if (*p == '\0')
11737 break;
11738 s = p;
11739 squote:
11740 /* s points to '; print "'''...'''" */
11741 putchar('"');
11742 do putchar('\''); while (*++s == '\'');
11743 putchar('"');
11744 } while (*s);
11745}
11746#endif
11747
11748#if ENABLE_HUSH_EXPORT || ENABLE_HUSH_LOCAL || ENABLE_HUSH_READONLY
11749static int helper_export_local(char **argv, unsigned flags)
11750{
11751 do {
11752 char *name = *argv;
11753 const char *name_end = endofname(name);
11754
11755 if (*name_end == '\0') {
11756 struct variable *var, **vpp;
11757
11758 vpp = get_ptr_to_local_var(name, name_end - name);
11759 var = vpp ? *vpp : NULL;
11760
11761 if (flags & SETFLAG_UNEXPORT) {
11762 /* export -n NAME (without =VALUE) */
11763 if (var) {
11764 var->flg_export = 0;
11765 debug_printf_env("%s: unsetenv '%s'\n", __func__, name);
11766 unsetenv(name);
11767 } /* else: export -n NOT_EXISTING_VAR: no-op */
11768 continue;
11769 }
11770 if (flags & SETFLAG_EXPORT) {
11771 /* export NAME (without =VALUE) */
11772 if (var) {
11773 var->flg_export = 1;
11774 debug_printf_env("%s: putenv '%s'\n", __func__, var->varstr);
11775 putenv(var->varstr);
11776 continue;
11777 }
11778 }
11779 if (flags & SETFLAG_MAKE_RO) {
11780 /* readonly NAME (without =VALUE) */
11781 if (var) {
11782 var->flg_read_only = 1;
11783 continue;
11784 }
11785 }
11786# if ENABLE_HUSH_LOCAL
11787 /* Is this "local" bltin? */
11788 if (!(flags & (SETFLAG_EXPORT|SETFLAG_UNEXPORT|SETFLAG_MAKE_RO))) {
11789 unsigned lvl = flags >> SETFLAG_VARLVL_SHIFT;
11790 if (var && var->var_nest_level == lvl) {
11791 /* "local x=abc; ...; local x" - ignore second local decl */
11792 continue;
11793 }
11794 }
11795# endif
11796 /* Exporting non-existing variable.
11797 * bash does not put it in environment,
11798 * but remembers that it is exported,
11799 * and does put it in env when it is set later.
11800 * We just set it to "" and export.
11801 */
11802 /* Or, it's "local NAME" (without =VALUE).
11803 * bash sets the value to "".
11804 */
11805 /* Or, it's "readonly NAME" (without =VALUE).
11806 * bash remembers NAME and disallows its creation
11807 * in the future.
11808 */
11809 name = xasprintf("%s=", name);
11810 } else {
11811 if (*name_end != '=') {
11812 bb_error_msg("'%s': bad variable name", name);
11813 /* do not parse following argv[]s: */
11814 return 1;
11815 }
11816 /* (Un)exporting/making local NAME=VALUE */
11817 name = xstrdup(name);
11818 /* Testcase: export PS1='\w \$ ' */
11819 unbackslash(name);
11820 }
11821 debug_printf_env("%s: set_local_var('%s')\n", __func__, name);
11822 if (set_local_var(name, flags))
11823 return EXIT_FAILURE;
11824 } while (*++argv);
11825 return EXIT_SUCCESS;
11826}
11827#endif
11828
11829#if ENABLE_HUSH_EXPORT
11830static int FAST_FUNC builtin_export(char **argv)
11831{
11832 unsigned opt_unexport;
11833
11834# if ENABLE_HUSH_EXPORT_N
11835 /* "!": do not abort on errors */
11836 opt_unexport = getopt32(argv, "!n");
11837 if (opt_unexport == (uint32_t)-1)
11838 return EXIT_FAILURE;
11839 argv += optind;
11840# else
11841 opt_unexport = 0;
11842 argv++;
11843# endif
11844
11845 if (argv[0] == NULL) {
11846 char **e = environ;
11847 if (e) {
11848 while (*e) {
11849# if 0
11850 puts(*e++);
11851# else
11852 /* ash emits: export VAR='VAL'
11853 * bash: declare -x VAR="VAL"
11854 * we follow ash example */
11855 const char *s = *e++;
11856 const char *p = strchr(s, '=');
11857
11858 if (!p) /* wtf? take next variable */
11859 continue;
11860 /* export var= */
11861 printf("export %.*s", (int)(p - s) + 1, s);
11862 print_escaped(p + 1);
11863 putchar('\n');
11864# endif
11865 }
11866 /*fflush_all(); - done after each builtin anyway */
11867 }
11868 return EXIT_SUCCESS;
11869 }
11870
11871 return helper_export_local(argv, opt_unexport ? SETFLAG_UNEXPORT : SETFLAG_EXPORT);
11872}
11873#endif
11874
11875#if ENABLE_HUSH_LOCAL
11876static int FAST_FUNC builtin_local(char **argv)
11877{
11878 if (G.func_nest_level == 0) {
11879 bb_error_msg("%s: not in a function", argv[0]);
11880 return EXIT_FAILURE; /* bash compat */
11881 }
11882 argv++;
11883 /* Since all builtins run in a nested variable level,
11884 * need to use level - 1 here. Or else the variable will be removed at once
11885 * after builtin returns.
11886 */
11887 return helper_export_local(argv, (G.var_nest_level - 1) << SETFLAG_VARLVL_SHIFT);
11888}
11889#endif
11890
11891#if ENABLE_HUSH_READONLY
11892static int FAST_FUNC builtin_readonly(char **argv)
11893{
11894 argv++;
11895 if (*argv == NULL) {
11896 /* bash: readonly [-p]: list all readonly VARs
11897 * (-p has no effect in bash)
11898 */
11899 struct variable *e;
11900 for (e = G.top_var; e; e = e->next) {
11901 if (e->flg_read_only) {
11902//TODO: quote value: readonly VAR='VAL'
11903 printf("readonly %s\n", e->varstr);
11904 }
11905 }
11906 return EXIT_SUCCESS;
11907 }
11908 return helper_export_local(argv, SETFLAG_MAKE_RO);
11909}
11910#endif
11911
11912#if ENABLE_HUSH_UNSET
11913/* http://www.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#unset */
11914static int FAST_FUNC builtin_unset(char **argv)
11915{
11916 int ret;
11917 unsigned opts;
11918
11919 /* "!": do not abort on errors */
11920 /* "+": stop at 1st non-option */
11921 opts = getopt32(argv, "!+vf");
11922 if (opts == (unsigned)-1)
11923 return EXIT_FAILURE;
11924 if (opts == 3) {
11925 bb_simple_error_msg("unset: -v and -f are exclusive");
11926 return EXIT_FAILURE;
11927 }
11928 argv += optind;
11929
11930 ret = EXIT_SUCCESS;
11931 while (*argv) {
11932 if (!(opts & 2)) { /* not -f */
11933 if (unset_local_var(*argv)) {
11934 /* unset <nonexistent_var> doesn't fail.
11935 * Error is when one tries to unset RO var.
11936 * Message was printed by unset_local_var. */
11937 ret = EXIT_FAILURE;
11938 }
11939 }
11940# if ENABLE_HUSH_FUNCTIONS
11941 else {
11942 unset_func(*argv);
11943 }
11944# endif
11945 argv++;
11946 }
11947 return ret;
11948}
11949#endif
11950
11951#if ENABLE_HUSH_SET
11952/* http://www.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#set
11953 * built-in 'set' handler
11954 * SUSv3 says:
11955 * set [-abCefhmnuvx] [-o option] [argument...]
11956 * set [+abCefhmnuvx] [+o option] [argument...]
11957 * set -- [argument...]
11958 * set -o
11959 * set +o
11960 * Implementations shall support the options in both their hyphen and
11961 * plus-sign forms. These options can also be specified as options to sh.
11962 * Examples:
11963 * Write out all variables and their values: set
11964 * Set $1, $2, and $3 and set "$#" to 3: set c a b
11965 * Turn on the -x and -v options: set -xv
11966 * Unset all positional parameters: set --
11967 * Set $1 to the value of x, even if it begins with '-' or '+': set -- "$x"
11968 * Set the positional parameters to the expansion of x, even if x expands
11969 * with a leading '-' or '+': set -- $x
11970 *
11971 * So far, we only support "set -- [argument...]" and some of the short names.
11972 */
11973static int FAST_FUNC builtin_set(char **argv)
11974{
11975 int n;
11976 char **pp, **g_argv;
11977 char *arg = *++argv;
11978
11979 if (arg == NULL) {
11980 struct variable *e;
11981 for (e = G.top_var; e; e = e->next)
11982 puts(e->varstr);
11983 return EXIT_SUCCESS;
11984 }
11985
11986 do {
11987 if (strcmp(arg, "--") == 0) {
11988 ++argv;
11989 goto set_argv;
11990 }
11991 if (arg[0] != '+' && arg[0] != '-')
11992 break;
11993 for (n = 1; arg[n]; ++n) {
11994 if (set_mode((arg[0] == '-'), arg[n], argv[1])) {
11995 bb_error_msg("%s: %s: invalid option", "set", arg);
11996 return EXIT_FAILURE;
11997 }
11998 if (arg[n] == 'o' && argv[1])
11999 argv++;
12000 }
12001 } while ((arg = *++argv) != NULL);
12002 /* Now argv[0] is 1st argument */
12003
12004 if (arg == NULL)
12005 return EXIT_SUCCESS;
12006 set_argv:
12007
12008 /* NB: G.global_argv[0] ($0) is never freed/changed */
12009 g_argv = G.global_argv;
12010 if (G.global_args_malloced) {
12011 pp = g_argv;
12012 while (*++pp)
12013 free(*pp);
12014 g_argv[1] = NULL;
12015 } else {
12016 G.global_args_malloced = 1;
12017 pp = xzalloc(sizeof(pp[0]) * 2);
12018 pp[0] = g_argv[0]; /* retain $0 */
12019 g_argv = pp;
12020 }
12021 /* This realloc's G.global_argv */
12022 G.global_argv = pp = add_strings_to_strings(g_argv, argv, /*dup:*/ 1);
12023
12024 G.global_argc = 1 + string_array_len(pp + 1);
12025
12026 return EXIT_SUCCESS;
12027}
12028#endif
12029
12030static int FAST_FUNC builtin_shift(char **argv)
12031{
12032 int n = 1;
12033 argv = skip_dash_dash(argv);
12034 if (argv[0]) {
12035 n = bb_strtou(argv[0], NULL, 10);
12036 if (errno || n < 0) {
12037 /* shared string with ash.c */
12038 bb_error_msg("Illegal number: %s", argv[0]);
12039 /*
12040 * ash aborts in this case.
12041 * bash prints error message and set $? to 1.
12042 * Interestingly, for "shift 99999" bash does not
12043 * print error message, but does set $? to 1
12044 * (and does no shifting at all).
12045 */
12046 }
12047 }
12048 if (n >= 0 && n < G.global_argc) {
12049 if (G_global_args_malloced) {
12050 int m = 1;
12051 while (m <= n)
12052 free(G.global_argv[m++]);
12053 }
12054 G.global_argc -= n;
12055 memmove(&G.global_argv[1], &G.global_argv[n+1],
12056 G.global_argc * sizeof(G.global_argv[0]));
12057 return EXIT_SUCCESS;
12058 }
12059 return EXIT_FAILURE;
12060}
12061
12062#if ENABLE_HUSH_GETOPTS
12063static int FAST_FUNC builtin_getopts(char **argv)
12064{
12065/* http://pubs.opengroup.org/onlinepubs/9699919799/utilities/getopts.html
12066
12067TODO:
12068If a required argument is not found, and getopts is not silent,
12069a question mark (?) is placed in VAR, OPTARG is unset, and a
12070diagnostic message is printed. If getopts is silent, then a
12071colon (:) is placed in VAR and OPTARG is set to the option
12072character found.
12073
12074Test that VAR is a valid variable name?
12075
12076"Whenever the shell is invoked, OPTIND shall be initialized to 1"
12077*/
12078 char cbuf[2];
12079 const char *cp, *optstring, *var;
12080 int c, n, exitcode, my_opterr;
12081 unsigned count;
12082
12083 optstring = *++argv;
12084 if (!optstring || !(var = *++argv)) {
12085 bb_simple_error_msg("usage: getopts OPTSTRING VAR [ARGS]");
12086 return EXIT_FAILURE;
12087 }
12088
12089 if (argv[1])
12090 argv[0] = G.global_argv[0]; /* for error messages in getopt() */
12091 else
12092 argv = G.global_argv;
12093 cbuf[1] = '\0';
12094
12095 my_opterr = 0;
12096 if (optstring[0] != ':') {
12097 cp = get_local_var_value("OPTERR");
12098 /* 0 if "OPTERR=0", 1 otherwise */
12099 my_opterr = (!cp || NOT_LONE_CHAR(cp, '0'));
12100 }
12101
12102 /* getopts stops on first non-option. Add "+" to force that */
12103 /*if (optstring[0] != '+')*/ {
12104 char *s = alloca(strlen(optstring) + 2);
12105 sprintf(s, "+%s", optstring);
12106 optstring = s;
12107 }
12108
12109 /* Naively, now we should just
12110 * cp = get_local_var_value("OPTIND");
12111 * optind = cp ? atoi(cp) : 0;
12112 * optarg = NULL;
12113 * opterr = my_opterr;
12114 * c = getopt(string_array_len(argv), argv, optstring);
12115 * and be done? Not so fast...
12116 * Unlike normal getopt() usage in C programs, here
12117 * each successive call will (usually) have the same argv[] CONTENTS,
12118 * but not the ADDRESSES. Worse yet, it's possible that between
12119 * invocations of "getopts", there will be calls to shell builtins
12120 * which use getopt() internally. Example:
12121 * while getopts "abc" RES -a -bc -abc de; do
12122 * unset -ff func
12123 * done
12124 * This would not work correctly: getopt() call inside "unset"
12125 * modifies internal libc state which is tracking position in
12126 * multi-option strings ("-abc"). At best, it can skip options
12127 * or return the same option infinitely. With glibc implementation
12128 * of getopt(), it would use outright invalid pointers and return
12129 * garbage even _without_ "unset" mangling internal state.
12130 *
12131 * We resort to resetting getopt() state and calling it N times,
12132 * until we get Nth result (or failure).
12133 * (N == G.getopt_count is reset to 0 whenever OPTIND is [un]set).
12134 */
12135 GETOPT_RESET();
12136 count = 0;
12137 n = string_array_len(argv);
12138 do {
12139 optarg = NULL;
12140 opterr = (count < G.getopt_count) ? 0 : my_opterr;
12141 c = getopt(n, argv, optstring);
12142 if (c < 0)
12143 break;
12144 count++;
12145 } while (count <= G.getopt_count);
12146
12147 /* Set OPTIND. Prevent resetting of the magic counter! */
12148 set_local_var_from_halves("OPTIND", utoa(optind));
12149 G.getopt_count = count; /* "next time, give me N+1'th result" */
12150 GETOPT_RESET(); /* just in case */
12151
12152 /* Set OPTARG */
12153 /* Always set or unset, never left as-is, even on exit/error:
12154 * "If no option was found, or if the option that was found
12155 * does not have an option-argument, OPTARG shall be unset."
12156 */
12157 cp = optarg;
12158 if (c == '?') {
12159 /* If ":optstring" and unknown option is seen,
12160 * it is stored to OPTARG.
12161 */
12162 if (optstring[1] == ':') {
12163 cbuf[0] = optopt;
12164 cp = cbuf;
12165 }
12166 }
12167 if (cp)
12168 set_local_var_from_halves("OPTARG", cp);
12169 else
12170 unset_local_var("OPTARG");
12171
12172 /* Convert -1 to "?" */
12173 exitcode = EXIT_SUCCESS;
12174 if (c < 0) { /* -1: end of options */
12175 exitcode = EXIT_FAILURE;
12176 c = '?';
12177 }
12178
12179 /* Set VAR */
12180 cbuf[0] = c;
12181 set_local_var_from_halves(var, cbuf);
12182
12183 return exitcode;
12184}
12185#endif
12186
12187static int FAST_FUNC builtin_source(char **argv)
12188{
12189 char *arg_path, *filename;
12190 HFILE *input;
12191 save_arg_t sv;
12192 char *args_need_save;
12193#if ENABLE_HUSH_FUNCTIONS
12194 smallint sv_flg;
12195#endif
12196
12197 argv = skip_dash_dash(argv);
12198 filename = argv[0];
12199 if (!filename) {
12200 /* bash says: "bash: .: filename argument required" */
12201 return 2; /* bash compat */
12202 }
12203 arg_path = NULL;
12204 if (!strchr(filename, '/')) {
12205 arg_path = find_in_path(filename);
12206 if (arg_path)
12207 filename = arg_path;
12208 else if (!ENABLE_HUSH_BASH_SOURCE_CURDIR) {
12209 errno = ENOENT;
12210 bb_simple_perror_msg(filename);
12211 return EXIT_FAILURE;
12212 }
12213 }
12214 input = hfopen(filename);
12215 free(arg_path);
12216 if (!input) {
12217 bb_perror_msg("%s", filename);
12218 /* POSIX: non-interactive shell should abort here,
12219 * not merely fail. So far no one complained :)
12220 */
12221 return EXIT_FAILURE;
12222 }
12223
12224#if ENABLE_HUSH_FUNCTIONS
12225 sv_flg = G_flag_return_in_progress;
12226 /* "we are inside sourced file, ok to use return" */
12227 G_flag_return_in_progress = -1;
12228#endif
12229 args_need_save = argv[1]; /* used as a boolean variable */
12230 if (args_need_save)
12231 save_and_replace_G_args(&sv, argv);
12232
12233 /* "false; . ./empty_line; echo Zero:$?" should print 0 */
12234 G.last_exitcode = 0;
12235 parse_and_run_file(input);
12236 hfclose(input);
12237
12238 if (args_need_save) /* can't use argv[1] instead: "shift" can mangle it */
12239 restore_G_args(&sv, argv);
12240#if ENABLE_HUSH_FUNCTIONS
12241 G_flag_return_in_progress = sv_flg;
12242#endif
12243
12244 return G.last_exitcode;
12245}
12246
12247#if ENABLE_HUSH_TRAP
12248static int FAST_FUNC builtin_trap(char **argv)
12249{
12250 int sig;
12251 char *new_cmd;
12252
12253 if (!G_traps)
12254 G_traps = xzalloc(sizeof(G_traps[0]) * NSIG);
12255
12256 argv++;
12257 if (!*argv) {
12258 int i;
12259 /* No args: print all trapped */
12260 for (i = 0; i < NSIG; ++i) {
12261 if (G_traps[i]) {
12262 printf("trap -- ");
12263 print_escaped(G_traps[i]);
12264 /* note: bash adds "SIG", but only if invoked
12265 * as "bash". If called as "sh", or if set -o posix,
12266 * then it prints short signal names.
12267 * We are printing short names: */
12268 printf(" %s\n", get_signame(i));
12269 }
12270 }
12271 /*fflush_all(); - done after each builtin anyway */
12272 return EXIT_SUCCESS;
12273 }
12274
12275 new_cmd = NULL;
12276 /* If first arg is a number: reset all specified signals */
12277 sig = bb_strtou(*argv, NULL, 10);
12278 if (errno == 0) {
12279 int ret;
12280 process_sig_list:
12281 ret = EXIT_SUCCESS;
12282 while (*argv) {
12283 sighandler_t handler;
12284
12285 sig = get_signum(*argv++);
12286 if (sig < 0) {
12287 ret = EXIT_FAILURE;
12288 /* Mimic bash message exactly */
12289 bb_error_msg("trap: %s: invalid signal specification", argv[-1]);
12290 continue;
12291 }
12292
12293 free(G_traps[sig]);
12294 G_traps[sig] = xstrdup(new_cmd);
12295
12296 debug_printf("trap: setting SIG%s (%i) to '%s'\n",
12297 get_signame(sig), sig, G_traps[sig]);
12298
12299 /* There is no signal for 0 (EXIT) */
12300 if (sig == 0)
12301 continue;
12302
12303 if (new_cmd)
12304 handler = (new_cmd[0] ? record_pending_signo : SIG_IGN);
12305 else
12306 /* We are removing trap handler */
12307 handler = pick_sighandler(sig);
12308 install_sighandler(sig, handler);
12309 }
12310 return ret;
12311 }
12312
12313 if (!argv[1]) { /* no second arg */
12314 bb_simple_error_msg("trap: invalid arguments");
12315 return EXIT_FAILURE;
12316 }
12317
12318 /* First arg is "-": reset all specified to default */
12319 /* First arg is "--": skip it, the rest is "handler SIGs..." */
12320 /* Everything else: set arg as signal handler
12321 * (includes "" case, which ignores signal) */
12322 if (argv[0][0] == '-') {
12323 if (argv[0][1] == '\0') { /* "-" */
12324 /* new_cmd remains NULL: "reset these sigs" */
12325 goto reset_traps;
12326 }
12327 if (argv[0][1] == '-' && argv[0][2] == '\0') { /* "--" */
12328 argv++;
12329 }
12330 /* else: "-something", no special meaning */
12331 }
12332 new_cmd = *argv;
12333 reset_traps:
12334 argv++;
12335 goto process_sig_list;
12336}
12337#endif
12338
12339#if ENABLE_HUSH_JOB
12340static struct pipe *parse_jobspec(const char *str)
12341{
12342 struct pipe *pi;
12343 unsigned jobnum;
12344
12345 if (sscanf(str, "%%%u", &jobnum) != 1) {
12346 if (str[0] != '%'
12347 || (str[1] != '%' && str[1] != '+' && str[1] != '\0')
12348 ) {
12349 bb_error_msg("bad argument '%s'", str);
12350 return NULL;
12351 }
12352 /* It is "%%", "%+" or "%" - current job */
12353 jobnum = G.last_jobid;
12354 if (jobnum == 0) {
12355 bb_simple_error_msg("no current job");
12356 return NULL;
12357 }
12358 }
12359 for (pi = G.job_list; pi; pi = pi->next) {
12360 if (pi->jobid == jobnum) {
12361 return pi;
12362 }
12363 }
12364 bb_error_msg("%u: no such job", jobnum);
12365 return NULL;
12366}
12367
12368static int FAST_FUNC builtin_jobs(char **argv UNUSED_PARAM)
12369{
12370 struct pipe *job;
12371 const char *status_string;
12372
12373 checkjobs(NULL, 0 /*(no pid to wait for)*/);
12374 for (job = G.job_list; job; job = job->next) {
12375 if (job->alive_cmds == job->stopped_cmds)
12376 status_string = "Stopped";
12377 else
12378 status_string = "Running";
12379
12380 printf(JOB_STATUS_FORMAT, job->jobid, status_string, job->cmdtext);
12381 }
12382
12383 clean_up_last_dead_job();
12384
12385 return EXIT_SUCCESS;
12386}
12387
12388/* built-in 'fg' and 'bg' handler */
12389static int FAST_FUNC builtin_fg_bg(char **argv)
12390{
12391 int i;
12392 struct pipe *pi;
12393
12394 if (!G_interactive_fd)
12395 return EXIT_FAILURE;
12396
12397 /* If they gave us no args, assume they want the last backgrounded task */
12398 if (!argv[1]) {
12399 for (pi = G.job_list; pi; pi = pi->next) {
12400 if (pi->jobid == G.last_jobid) {
12401 goto found;
12402 }
12403 }
12404 bb_error_msg("%s: no current job", argv[0]);
12405 return EXIT_FAILURE;
12406 }
12407
12408 pi = parse_jobspec(argv[1]);
12409 if (!pi)
12410 return EXIT_FAILURE;
12411 found:
12412 /* TODO: bash prints a string representation
12413 * of job being foregrounded (like "sleep 1 | cat") */
12414 if (argv[0][0] == 'f' && G_saved_tty_pgrp) {
12415 /* Put the job into the foreground. */
12416 tcsetpgrp(G_interactive_fd, pi->pgrp);
12417 }
12418
12419 /* Restart the processes in the job */
12420 debug_printf_jobs("reviving %d procs, pgrp %d\n", pi->num_cmds, pi->pgrp);
12421 for (i = 0; i < pi->num_cmds; i++) {
12422 debug_printf_jobs("reviving pid %d\n", pi->cmds[i].pid);
12423 }
12424 pi->stopped_cmds = 0;
12425
12426 i = kill(- pi->pgrp, SIGCONT);
12427 if (i < 0) {
12428 if (errno == ESRCH) {
12429 delete_finished_job(pi);
12430 return EXIT_SUCCESS;
12431 }
12432 bb_simple_perror_msg("kill (SIGCONT)");
12433 }
12434
12435 if (argv[0][0] == 'f') {
12436 remove_job_from_table(pi); /* FG job shouldn't be in job table */
12437 return checkjobs_and_fg_shell(pi);
12438 }
12439 return EXIT_SUCCESS;
12440}
12441#endif
12442
12443#if ENABLE_HUSH_KILL
12444static int FAST_FUNC builtin_kill(char **argv)
12445{
12446 int ret = 0;
12447
12448# if ENABLE_HUSH_JOB
12449 if (argv[1] && strcmp(argv[1], "-l") != 0) {
12450 int i = 1;
12451
12452 do {
12453 struct pipe *pi;
12454 char *dst;
12455 int j, n;
12456
12457 if (argv[i][0] != '%')
12458 continue;
12459 /*
12460 * "kill %N" - job kill
12461 * Converting to pgrp / pid kill
12462 */
12463 pi = parse_jobspec(argv[i]);
12464 if (!pi) {
12465 /* Eat bad jobspec */
12466 j = i;
12467 do {
12468 j++;
12469 argv[j - 1] = argv[j];
12470 } while (argv[j]);
12471 ret = 1;
12472 i--;
12473 continue;
12474 }
12475 /*
12476 * In jobs started under job control, we signal
12477 * entire process group by kill -PGRP_ID.
12478 * This happens, f.e., in interactive shell.
12479 *
12480 * Otherwise, we signal each child via
12481 * kill PID1 PID2 PID3.
12482 * Testcases:
12483 * sh -c 'sleep 1|sleep 1 & kill %1'
12484 * sh -c 'true|sleep 2 & sleep 1; kill %1'
12485 * sh -c 'true|sleep 1 & sleep 2; kill %1'
12486 */
12487 n = G_interactive_fd ? 1 : pi->num_cmds;
12488 dst = alloca(n * sizeof(int)*4);
12489 argv[i] = dst;
12490 if (G_interactive_fd)
12491 dst += sprintf(dst, " -%u", (int)pi->pgrp);
12492 else for (j = 0; j < n; j++) {
12493 struct command *cmd = &pi->cmds[j];
12494 /* Skip exited members of the job */
12495 if (cmd->pid == 0)
12496 continue;
12497 /*
12498 * kill_main has matching code to expect
12499 * leading space. Needed to not confuse
12500 * negative pids with "kill -SIGNAL_NO" syntax
12501 */
12502 dst += sprintf(dst, " %u", (int)cmd->pid);
12503 }
12504 *dst = '\0';
12505 } while (argv[++i]);
12506 }
12507# endif
12508
12509 if (argv[1] || ret == 0) {
12510 ret = run_applet_main(argv, kill_main);
12511 }
12512 /* else: ret = 1, "kill %bad_jobspec" case */
12513 return ret;
12514}
12515#endif
12516
12517#if ENABLE_HUSH_WAIT
12518/* http://www.opengroup.org/onlinepubs/9699919799/utilities/wait.html */
12519# if !ENABLE_HUSH_JOB
12520# define wait_for_child_or_signal(pipe,pid) wait_for_child_or_signal(pid)
12521# endif
12522static int wait_for_child_or_signal(struct pipe *waitfor_pipe, pid_t waitfor_pid)
12523{
12524 int ret = 0;
12525 for (;;) {
12526 int sig;
12527 sigset_t oldset;
12528
12529 if (!sigisemptyset(&G.pending_set))
12530 goto check_sig;
12531
12532 /* waitpid is not interruptible by SA_RESTARTed
12533 * signals which we use. Thus, this ugly dance:
12534 */
12535
12536 /* Make sure possible SIGCHLD is stored in kernel's
12537 * pending signal mask before we call waitpid.
12538 * Or else we may race with SIGCHLD, lose it,
12539 * and get stuck in sigsuspend...
12540 */
12541 sigfillset(&oldset); /* block all signals, remember old set */
12542 sigprocmask2(SIG_SETMASK, &oldset);
12543
12544 if (!sigisemptyset(&G.pending_set)) {
12545 /* Crap! we raced with some signal! */
12546 goto restore;
12547 }
12548
12549 /*errno = 0; - checkjobs does this */
12550/* Can't pass waitfor_pipe into checkjobs(): it won't be interruptible */
12551 ret = checkjobs(NULL, waitfor_pid); /* waitpid(WNOHANG) inside */
12552 debug_printf_exec("checkjobs:%d\n", ret);
12553# if ENABLE_HUSH_JOB
12554 if (waitfor_pipe) {
12555 int rcode = job_exited_or_stopped(waitfor_pipe);
12556 debug_printf_exec("job_exited_or_stopped:%d\n", rcode);
12557 if (rcode >= 0) {
12558 ret = rcode;
12559 sigprocmask(SIG_SETMASK, &oldset, NULL);
12560 break;
12561 }
12562 }
12563# endif
12564 /* if ECHILD, there are no children (ret is -1 or 0) */
12565 /* if ret == 0, no children changed state */
12566 /* if ret != 0, it's exitcode+1 of exited waitfor_pid child */
12567 if (errno == ECHILD || ret) {
12568 ret--;
12569 if (ret < 0) /* if ECHILD, may need to fix "ret" */
12570 ret = 0;
12571# if ENABLE_HUSH_BASH_COMPAT
12572 if (waitfor_pid == -1 && errno == ECHILD) {
12573 /* exitcode of "wait -n" with no children is 127, not 0 */
12574 ret = 127;
12575 }
12576# endif
12577 sigprocmask(SIG_SETMASK, &oldset, NULL);
12578 break;
12579 }
12580 /* Wait for SIGCHLD or any other signal */
12581 /* It is vitally important for sigsuspend that SIGCHLD has non-DFL handler! */
12582 /* Note: sigsuspend invokes signal handler */
12583 sigsuspend(&oldset);
12584 /* ^^^ add "sigdelset(&oldset, SIGCHLD)" before sigsuspend
12585 * to make sure SIGCHLD is not masked off?
12586 * It was reported that this:
12587 * fn() { : | return; }
12588 * shopt -s lastpipe
12589 * fn
12590 * exec hush SCRIPT
12591 * under bash 4.4.23 runs SCRIPT with SIGCHLD masked,
12592 * making "wait" commands in SCRIPT block forever.
12593 */
12594 restore:
12595 sigprocmask(SIG_SETMASK, &oldset, NULL);
12596 check_sig:
12597 /* So, did we get a signal? */
12598 sig = check_and_run_traps();
12599 if (sig /*&& sig != SIGCHLD - always true */) {
12600 /* Do this for any (non-ignored) signal, not only for ^C */
12601 ret = 128 | sig;
12602 break;
12603 }
12604 /* SIGCHLD, or no signal, or ignored one, such as SIGQUIT. Repeat */
12605 }
12606 return ret;
12607}
12608
12609static int FAST_FUNC builtin_wait(char **argv)
12610{
12611 int ret;
12612 int status;
12613
12614 argv = skip_dash_dash(argv);
12615# if ENABLE_HUSH_BASH_COMPAT
12616 if (argv[0] && strcmp(argv[0], "-n") == 0) {
12617 /* wait -n */
12618 /* (bash accepts "wait -n PID" too and ignores PID) */
12619 G.dead_job_exitcode = -1;
12620 return wait_for_child_or_signal(NULL, -1 /*no job, wait for one job*/);
12621 }
12622# endif
12623 if (argv[0] == NULL) {
12624 /* Don't care about wait results */
12625 /* Note 1: must wait until there are no more children */
12626 /* Note 2: must be interruptible */
12627 /* Examples:
12628 * $ sleep 3 & sleep 6 & wait
12629 * [1] 30934 sleep 3
12630 * [2] 30935 sleep 6
12631 * [1] Done sleep 3
12632 * [2] Done sleep 6
12633 * $ sleep 3 & sleep 6 & wait
12634 * [1] 30936 sleep 3
12635 * [2] 30937 sleep 6
12636 * [1] Done sleep 3
12637 * ^C <-- after ~4 sec from keyboard
12638 * $
12639 */
12640 return wait_for_child_or_signal(NULL, 0 /*no job and no pid to wait for*/);
12641 }
12642
12643 do {
12644 pid_t pid = bb_strtou(*argv, NULL, 10);
12645 if (errno || pid <= 0) {
12646# if ENABLE_HUSH_JOB
12647 if (argv[0][0] == '%') {
12648 struct pipe *wait_pipe;
12649 ret = 127; /* bash compat for bad jobspecs */
12650 wait_pipe = parse_jobspec(*argv);
12651 if (wait_pipe) {
12652 ret = job_exited_or_stopped(wait_pipe);
12653 if (ret < 0) {
12654 ret = wait_for_child_or_signal(wait_pipe, 0);
12655 } else {
12656 /* waiting on "last dead job" removes it */
12657 clean_up_last_dead_job();
12658 }
12659 }
12660 /* else: parse_jobspec() already emitted error msg */
12661 continue;
12662 }
12663# endif
12664 /* mimic bash message */
12665 bb_error_msg("wait: '%s': not a pid or valid job spec", *argv);
12666 ret = EXIT_FAILURE;
12667 continue; /* bash checks all argv[] */
12668 }
12669
12670 /* Do we have such child? */
12671 ret = waitpid(pid, &status, WNOHANG);
12672 if (ret < 0) {
12673 /* No */
12674 ret = 127;
12675 if (errno == ECHILD) {
12676 if (pid == G.last_bg_pid) {
12677 /* "wait $!" but last bg task has already exited. Try:
12678 * (sleep 1; exit 3) & sleep 2; echo $?; wait $!; echo $?
12679 * In bash it prints exitcode 0, then 3.
12680 * In dash, it is 127.
12681 */
12682 ret = G.last_bg_pid_exitcode;
12683 } else {
12684 /* Example: "wait 1". mimic bash message */
12685 bb_error_msg("wait: pid %u is not a child of this shell", (unsigned)pid);
12686 }
12687 } else {
12688 /* ??? */
12689 bb_perror_msg("wait %s", *argv);
12690 }
12691 continue; /* bash checks all argv[] */
12692 }
12693 if (ret == 0) {
12694 /* Yes, and it still runs */
12695 ret = wait_for_child_or_signal(NULL, pid);
12696 } else {
12697 /* Yes, and it just exited */
12698 process_wait_result(NULL, pid, status);
12699 ret = WEXITSTATUS(status);
12700 if (WIFSIGNALED(status))
12701 ret = 128 | WTERMSIG(status);
12702 }
12703 } while (*++argv);
12704
12705 return ret;
12706}
12707#endif
12708
12709#if ENABLE_HUSH_LOOPS || ENABLE_HUSH_FUNCTIONS
12710static unsigned parse_numeric_argv1(char **argv, unsigned def, unsigned def_min)
12711{
12712 if (argv[1]) {
12713 def = bb_strtou(argv[1], NULL, 10);
12714 if (errno || def < def_min || argv[2]) {
12715 bb_error_msg("%s: bad arguments", argv[0]);
12716 def = UINT_MAX;
12717 }
12718 }
12719 return def;
12720}
12721#endif
12722
12723#if ENABLE_HUSH_LOOPS
12724static int FAST_FUNC builtin_break(char **argv)
12725{
12726 unsigned depth;
12727 if (G.depth_of_loop == 0) {
12728 bb_error_msg("%s: only meaningful in a loop", argv[0]);
12729 /* if we came from builtin_continue(), need to undo "= 1" */
12730 G.flag_break_continue = 0;
12731 return EXIT_SUCCESS; /* bash compat */
12732 }
12733 G.flag_break_continue++; /* BC_BREAK = 1, or BC_CONTINUE = 2 */
12734
12735 G.depth_break_continue = depth = parse_numeric_argv1(argv, 1, 1);
12736 if (depth == UINT_MAX)
12737 G.flag_break_continue = BC_BREAK;
12738 if (G.depth_of_loop < depth)
12739 G.depth_break_continue = G.depth_of_loop;
12740
12741 return EXIT_SUCCESS;
12742}
12743
12744static int FAST_FUNC builtin_continue(char **argv)
12745{
12746 G.flag_break_continue = 1; /* BC_CONTINUE = 2 = 1+1 */
12747 return builtin_break(argv);
12748}
12749#endif
12750
12751#if ENABLE_HUSH_FUNCTIONS
12752static int FAST_FUNC builtin_return(char **argv)
12753{
12754 int rc;
12755
12756 if (G_flag_return_in_progress != -1) {
12757 bb_error_msg("%s: not in a function or sourced script", argv[0]);
12758 return EXIT_FAILURE; /* bash compat */
12759 }
12760
12761 G_flag_return_in_progress = 1;
12762
12763 /* bash:
12764 * out of range: wraps around at 256, does not error out
12765 * non-numeric param:
12766 * f() { false; return qwe; }; f; echo $?
12767 * bash: return: qwe: numeric argument required <== we do this
12768 * 255 <== we also do this
12769 */
12770 rc = parse_numeric_argv1(argv, G.last_exitcode, 0);
12771# if ENABLE_HUSH_TRAP
12772 if (argv[1]) { /* "return ARG" inside a running trap sets $? */
12773 debug_printf_exec("G.return_exitcode=%d\n", rc);
12774 G.return_exitcode = rc;
12775 }
12776# endif
12777 return rc;
12778}
12779#endif
12780
12781#if ENABLE_HUSH_TIMES
12782static int FAST_FUNC builtin_times(char **argv UNUSED_PARAM)
12783{
12784 static const uint8_t times_tbl[] ALIGN1 = {
12785 ' ', offsetof(struct tms, tms_utime),
12786 '\n', offsetof(struct tms, tms_stime),
12787 ' ', offsetof(struct tms, tms_cutime),
12788 '\n', offsetof(struct tms, tms_cstime),
12789 0
12790 };
12791 const uint8_t *p;
12792 unsigned clk_tck;
12793 struct tms buf;
12794
12795 clk_tck = bb_clk_tck();
12796
12797 times(&buf);
12798 p = times_tbl;
12799 do {
12800 unsigned sec, frac;
12801 unsigned long t;
12802 t = *(clock_t *)(((char *) &buf) + p[1]);
12803 sec = t / clk_tck;
12804 frac = t % clk_tck;
12805 printf("%um%u.%03us%c",
12806 sec / 60, sec % 60,
12807 (frac * 1000) / clk_tck,
12808 p[0]);
12809 p += 2;
12810 } while (*p);
12811
12812 return EXIT_SUCCESS;
12813}
12814#endif
12815
12816#if ENABLE_HUSH_MEMLEAK
12817static int FAST_FUNC builtin_memleak(char **argv UNUSED_PARAM)
12818{
12819 void *p;
12820 unsigned long l;
12821
12822# ifdef M_TRIM_THRESHOLD
12823 /* Optional. Reduces probability of false positives */
12824 malloc_trim(0);
12825# endif
12826 /* Crude attempt to find where "free memory" starts,
12827 * sans fragmentation. */
12828 p = malloc(240);
12829 l = (unsigned long)p;
12830 free(p);
12831 p = malloc(3400);
12832 if (l < (unsigned long)p) l = (unsigned long)p;
12833 free(p);
12834
12835
12836# if 0 /* debug */
12837 {
12838 struct mallinfo mi = mallinfo();
12839 printf("top alloc:0x%lx malloced:%d+%d=%d\n", l,
12840 mi.arena, mi.hblkhd, mi.arena + mi.hblkhd);
12841 }
12842# endif
12843
12844 if (!G.memleak_value)
12845 G.memleak_value = l;
12846
12847 l -= G.memleak_value;
12848 if ((long)l < 0)
12849 l = 0;
12850 l /= 1024;
12851 if (l > 127)
12852 l = 127;
12853
12854 /* Exitcode is "how many kilobytes we leaked since 1st call" */
12855 return l;
12856}
12857#endif
Francis Laniel8197f012023-12-22 22:02:28 +010012858#endif /* !__U_BOOT__ */