blob: 7874b399d141e5ef421af642edec8521b37248fd [file] [log] [blame]
Francis Laniel110b7692023-12-22 22:02:27 +01001/* vi: set sw=4 ts=4: */
2/*
3 * A prototype Bourne shell grammar parser.
4 * Intended to follow the original Thompson and Ritchie
5 * "small and simple is beautiful" philosophy, which
6 * incidentally is a good match to today's BusyBox.
7 *
8 * Copyright (C) 2000,2001 Larry Doolittle <larry@doolittle.boa.org>
9 * Copyright (C) 2008,2009 Denys Vlasenko <vda.linux@googlemail.com>
10 *
11 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
12 *
13 * Credits:
14 * The parser routines proper are all original material, first
15 * written Dec 2000 and Jan 2001 by Larry Doolittle. The
16 * execution engine, the builtins, and much of the underlying
17 * support has been adapted from busybox-0.49pre's lash, which is
18 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
19 * written by Erik Andersen <andersen@codepoet.org>. That, in turn,
20 * is based in part on ladsh.c, by Michael K. Johnson and Erik W.
21 * Troan, which they placed in the public domain. I don't know
22 * how much of the Johnson/Troan code has survived the repeated
23 * rewrites.
24 *
25 * Other credits:
26 * o_addchr derived from similar w_addchar function in glibc-2.2.
27 * parse_redirect, redirect_opt_num, and big chunks of main
28 * and many builtins derived from contributions by Erik Andersen.
29 * Miscellaneous bugfixes from Matt Kraai.
30 *
31 * There are two big (and related) architecture differences between
32 * this parser and the lash parser. One is that this version is
33 * actually designed from the ground up to understand nearly all
34 * of the Bourne grammar. The second, consequential change is that
35 * the parser and input reader have been turned inside out. Now,
36 * the parser is in control, and asks for input as needed. The old
37 * way had the input reader in control, and it asked for parsing to
38 * take place as needed. The new way makes it much easier to properly
39 * handle the recursion implicit in the various substitutions, especially
40 * across continuation lines.
41 *
42 * TODOs:
43 * grep for "TODO" and fix (some of them are easy)
44 * make complex ${var%...} constructs support optional
45 * make here documents optional
46 * special variables (done: PWD, PPID, RANDOM)
47 * follow IFS rules more precisely, including update semantics
48 * tilde expansion
49 * aliases
50 * "command" missing features:
51 * command -p CMD: run CMD using default $PATH
52 * (can use this to override standalone shell as well?)
53 * command BLTIN: disables special-ness (e.g. errors do not abort)
54 * command -V CMD1 CMD2 CMD3 (multiple args) (not in standard)
55 * builtins mandated by standards we don't support:
56 * [un]alias, fc:
57 * fc -l[nr] [BEG] [END]: list range of commands in history
58 * fc [-e EDITOR] [BEG] [END]: edit/rerun range of commands
59 * fc -s [PAT=REP] [CMD]: rerun CMD, replacing PAT with REP
60 *
61 * Bash compat TODO:
62 * redirection of stdout+stderr: &> and >&
63 * reserved words: function select
64 * advanced test: [[ ]]
65 * process substitution: <(list) and >(list)
66 * let EXPR [EXPR...]
67 * Each EXPR is an arithmetic expression (ARITHMETIC EVALUATION)
68 * If the last arg evaluates to 0, let returns 1; 0 otherwise.
69 * NB: let `echo 'a=a + 1'` - error (IOW: multi-word expansion is used)
70 * ((EXPR))
71 * The EXPR is evaluated according to ARITHMETIC EVALUATION.
72 * This is exactly equivalent to let "EXPR".
73 * $[EXPR]: synonym for $((EXPR))
74 * indirect expansion: ${!VAR}
75 * substring op on @: ${@:n:m}
76 *
77 * Won't do:
78 * Some builtins mandated by standards:
79 * newgrp [GRP]: not a builtin in bash but a suid binary
80 * which spawns a new shell with new group ID
81 *
82 * Status of [[ support:
83 * [[ args ]] are CMD_SINGLEWORD_NOGLOB:
84 * v='a b'; [[ $v = 'a b' ]]; echo 0:$?
85 * [[ /bin/n* ]]; echo 0:$?
86 * = is glob match operator, not equality operator: STR = GLOB
87 * == same as =
88 * =~ is regex match operator: STR =~ REGEX
89 * TODO:
90 * quoting needs to be considered (-f is an operator, "-f" and ""-f are not; etc)
91 * in word = GLOB, quoting should be significant on char-by-char basis: a*cd"*"
92 */
93//config:config HUSH
Francis Laniele7ca3a32023-12-22 22:02:42 +010094//config: bool "hush (70 kb)"
Francis Laniel110b7692023-12-22 22:02:27 +010095//config: default y
96//config: select SHELL_HUSH
97//config: help
98//config: hush is a small shell. It handles the normal flow control
99//config: constructs such as if/then/elif/else/fi, for/in/do/done, while loops,
100//config: case/esac. Redirections, here documents, $((arithmetic))
101//config: and functions are supported.
102//config:
103//config: It will compile and work on no-mmu systems.
104//config:
105//config: It does not handle select, aliases, tilde expansion,
106//config: &>file and >&file redirection of stdout+stderr.
107//config:
108// This option is visible (has a description) to make it possible to select
109// a "scripted" applet (such as NOLOGIN) but avoid selecting any shells:
110//config:config SHELL_HUSH
111//config: bool "Internal shell for embedded script support"
112//config: default n
113//config:
114//config:# hush options
115//config:# It's only needed to get "nice" menuconfig indenting.
116//config:if SHELL_HUSH || HUSH || SH_IS_HUSH || BASH_IS_HUSH
117//config:
118//config:config HUSH_BASH_COMPAT
119//config: bool "bash-compatible extensions"
120//config: default y
121//config: depends on SHELL_HUSH
122//config:
123//config:config HUSH_BRACE_EXPANSION
124//config: bool "Brace expansion"
125//config: default y
126//config: depends on HUSH_BASH_COMPAT
127//config: help
128//config: Enable {abc,def} extension.
129//config:
130//config:config HUSH_BASH_SOURCE_CURDIR
131//config: bool "'source' and '.' builtins search current directory after $PATH"
132//config: default n # do not encourage non-standard behavior
133//config: depends on HUSH_BASH_COMPAT
134//config: help
135//config: This is not compliant with standards. Avoid if possible.
136//config:
137//config:config HUSH_LINENO_VAR
138//config: bool "$LINENO variable (bashism)"
139//config: default y
140//config: depends on SHELL_HUSH
141//config:
142//config:config HUSH_INTERACTIVE
143//config: bool "Interactive mode"
144//config: default y
145//config: depends on SHELL_HUSH
146//config: help
147//config: Enable interactive mode (prompt and command editing).
148//config: Without this, hush simply reads and executes commands
149//config: from stdin just like a shell script from a file.
150//config: No prompt, no PS1/PS2 magic shell variables.
151//config:
152//config:config HUSH_SAVEHISTORY
153//config: bool "Save command history to .hush_history"
154//config: default y
155//config: depends on HUSH_INTERACTIVE && FEATURE_EDITING_SAVEHISTORY
156//config:
157//config:config HUSH_JOB
158//config: bool "Job control"
159//config: default y
160//config: depends on HUSH_INTERACTIVE
161//config: help
162//config: Enable job control: Ctrl-Z backgrounds, Ctrl-C interrupts current
163//config: command (not entire shell), fg/bg builtins work. Without this option,
164//config: "cmd &" still works by simply spawning a process and immediately
165//config: prompting for next command (or executing next command in a script),
166//config: but no separate process group is formed.
167//config:
168//config:config HUSH_TICK
169//config: bool "Support command substitution"
170//config: default y
171//config: depends on SHELL_HUSH
172//config: help
173//config: Enable `command` and $(command).
174//config:
175//config:config HUSH_IF
176//config: bool "Support if/then/elif/else/fi"
177//config: default y
178//config: depends on SHELL_HUSH
179//config:
180//config:config HUSH_LOOPS
181//config: bool "Support for, while and until loops"
182//config: default y
183//config: depends on SHELL_HUSH
184//config:
185//config:config HUSH_CASE
186//config: bool "Support case ... esac statement"
187//config: default y
188//config: depends on SHELL_HUSH
189//config: help
190//config: Enable case ... esac statement. +400 bytes.
191//config:
192//config:config HUSH_FUNCTIONS
193//config: bool "Support funcname() { commands; } syntax"
194//config: default y
195//config: depends on SHELL_HUSH
196//config: help
197//config: Enable support for shell functions. +800 bytes.
198//config:
199//config:config HUSH_LOCAL
200//config: bool "local builtin"
201//config: default y
202//config: depends on HUSH_FUNCTIONS
203//config: help
204//config: Enable support for local variables in functions.
205//config:
206//config:config HUSH_RANDOM_SUPPORT
207//config: bool "Pseudorandom generator and $RANDOM variable"
208//config: default y
209//config: depends on SHELL_HUSH
210//config: help
211//config: Enable pseudorandom generator and dynamic variable "$RANDOM".
212//config: Each read of "$RANDOM" will generate a new pseudorandom value.
213//config:
214//config:config HUSH_MODE_X
215//config: bool "Support 'hush -x' option and 'set -x' command"
216//config: default y
217//config: depends on SHELL_HUSH
218//config: help
219//config: This instructs hush to print commands before execution.
220//config: Adds ~300 bytes.
221//config:
222//config:config HUSH_ECHO
223//config: bool "echo builtin"
224//config: default y
225//config: depends on SHELL_HUSH
226//config:
227//config:config HUSH_PRINTF
228//config: bool "printf builtin"
229//config: default y
230//config: depends on SHELL_HUSH
231//config:
232//config:config HUSH_TEST
233//config: bool "test builtin"
234//config: default y
235//config: depends on SHELL_HUSH
236//config:
237//config:config HUSH_HELP
238//config: bool "help builtin"
239//config: default y
240//config: depends on SHELL_HUSH
241//config:
242//config:config HUSH_EXPORT
243//config: bool "export builtin"
244//config: default y
245//config: depends on SHELL_HUSH
246//config:
247//config:config HUSH_EXPORT_N
248//config: bool "Support 'export -n' option"
249//config: default y
250//config: depends on HUSH_EXPORT
251//config: help
252//config: export -n unexports variables. It is a bash extension.
253//config:
254//config:config HUSH_READONLY
255//config: bool "readonly builtin"
256//config: default y
257//config: depends on SHELL_HUSH
258//config: help
259//config: Enable support for read-only variables.
260//config:
261//config:config HUSH_KILL
262//config: bool "kill builtin (supports kill %jobspec)"
263//config: default y
264//config: depends on SHELL_HUSH
265//config:
266//config:config HUSH_WAIT
267//config: bool "wait builtin"
268//config: default y
269//config: depends on SHELL_HUSH
270//config:
271//config:config HUSH_COMMAND
272//config: bool "command builtin"
273//config: default y
274//config: depends on SHELL_HUSH
275//config:
276//config:config HUSH_TRAP
277//config: bool "trap builtin"
278//config: default y
279//config: depends on SHELL_HUSH
280//config:
281//config:config HUSH_TYPE
282//config: bool "type builtin"
283//config: default y
284//config: depends on SHELL_HUSH
285//config:
286//config:config HUSH_TIMES
287//config: bool "times builtin"
288//config: default y
289//config: depends on SHELL_HUSH
290//config:
291//config:config HUSH_READ
292//config: bool "read builtin"
293//config: default y
294//config: depends on SHELL_HUSH
295//config:
296//config:config HUSH_SET
297//config: bool "set builtin"
298//config: default y
299//config: depends on SHELL_HUSH
300//config:
301//config:config HUSH_UNSET
302//config: bool "unset builtin"
303//config: default y
304//config: depends on SHELL_HUSH
305//config:
306//config:config HUSH_ULIMIT
307//config: bool "ulimit builtin"
308//config: default y
309//config: depends on SHELL_HUSH
310//config:
311//config:config HUSH_UMASK
312//config: bool "umask builtin"
313//config: default y
314//config: depends on SHELL_HUSH
315//config:
316//config:config HUSH_GETOPTS
317//config: bool "getopts builtin"
318//config: default y
319//config: depends on SHELL_HUSH
320//config:
321//config:config HUSH_MEMLEAK
322//config: bool "memleak builtin (debugging)"
323//config: default n
324//config: depends on SHELL_HUSH
325//config:
326//config:endif # hush options
327
328//applet:IF_HUSH(APPLET(hush, BB_DIR_BIN, BB_SUID_DROP))
329// APPLET_ODDNAME:name main location suid_type help
330//applet:IF_SH_IS_HUSH( APPLET_ODDNAME(sh, hush, BB_DIR_BIN, BB_SUID_DROP, hush))
331//applet:IF_BASH_IS_HUSH(APPLET_ODDNAME(bash, hush, BB_DIR_BIN, BB_SUID_DROP, hush))
332
333//kbuild:lib-$(CONFIG_SHELL_HUSH) += hush.o match.o shell_common.o
334//kbuild:lib-$(CONFIG_HUSH_RANDOM_SUPPORT) += random.o
335
336/* -i (interactive) is also accepted,
337 * but does nothing, therefore not shown in help.
338 * NOMMU-specific options are not meant to be used by users,
339 * therefore we don't show them either.
340 */
341//usage:#define hush_trivial_usage
Francis Laniele7ca3a32023-12-22 22:02:42 +0100342//usage: "[-enxl] [-c 'SCRIPT' [ARG0 ARGS] | FILE ARGS | -s ARGS]"
Francis Laniel110b7692023-12-22 22:02:27 +0100343//usage:#define hush_full_usage "\n\n"
344//usage: "Unix shell interpreter"
345
Francis Laniel36836fc2023-12-22 22:02:28 +0100346#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +0100347#if !(defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) \
348 || defined(__APPLE__) \
349 )
350# include <malloc.h> /* for malloc_trim */
351#endif
352#include <glob.h>
353/* #include <dmalloc.h> */
354#if ENABLE_HUSH_CASE
355# include <fnmatch.h>
356#endif
357#include <sys/times.h>
358#include <sys/utsname.h> /* for setting $HOSTNAME */
359
360#include "busybox.h" /* for APPLET_IS_NOFORK/NOEXEC */
361#include "unicode.h"
362#include "shell_common.h"
363#include "math.h"
364#include "match.h"
365#if ENABLE_HUSH_RANDOM_SUPPORT
366# include "random.h"
367#else
368# define CLEAR_RANDOM_T(rnd) ((void)0)
369#endif
370#ifndef O_CLOEXEC
371# define O_CLOEXEC 0
372#endif
373#ifndef F_DUPFD_CLOEXEC
374# define F_DUPFD_CLOEXEC F_DUPFD
375#endif
376
Francis Laniele7ca3a32023-12-22 22:02:42 +0100377#if ENABLE_FEATURE_SH_EMBEDDED_SCRIPTS && !ENABLE_SHELL_ASH
Francis Laniel110b7692023-12-22 22:02:27 +0100378# include "embedded_scripts.h"
379#else
380# define NUM_SCRIPTS 0
381#endif
Francis Laniel36836fc2023-12-22 22:02:28 +0100382#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +0100383
384/* So far, all bash compat is controlled by one config option */
385/* Separate defines document which part of code implements what */
386#define BASH_PATTERN_SUBST ENABLE_HUSH_BASH_COMPAT
387#define BASH_SUBSTR ENABLE_HUSH_BASH_COMPAT
388#define BASH_SOURCE ENABLE_HUSH_BASH_COMPAT
389#define BASH_DOLLAR_SQUOTE ENABLE_HUSH_BASH_COMPAT
390#define BASH_HOSTNAME_VAR ENABLE_HUSH_BASH_COMPAT
391#define BASH_EPOCH_VARS ENABLE_HUSH_BASH_COMPAT
392#define BASH_TEST2 (ENABLE_HUSH_BASH_COMPAT && ENABLE_HUSH_TEST)
393#define BASH_READ_D ENABLE_HUSH_BASH_COMPAT
394
Francis Laniel110b7692023-12-22 22:02:27 +0100395/* Build knobs */
396#define LEAK_HUNTING 0
397#define BUILD_AS_NOMMU 0
398/* Enable/disable sanity checks. Ok to enable in production,
399 * only adds a bit of bloat. Set to >1 to get non-production level verbosity.
400 * Keeping 1 for now even in released versions.
401 */
402#define HUSH_DEBUG 1
403/* Slightly bigger (+200 bytes), but faster hush.
404 * So far it only enables a trick with counting SIGCHLDs and forks,
405 * which allows us to do fewer waitpid's.
406 * (we can detect a case where neither forks were done nor SIGCHLDs happened
407 * and therefore waitpid will return the same result as last time)
408 */
409#define ENABLE_HUSH_FAST 0
410/* TODO: implement simplified code for users which do not need ${var%...} ops
411 * So far ${var%...} ops are always enabled:
412 */
413#define ENABLE_HUSH_DOLLAR_OPS 1
414
Francis Laniel110b7692023-12-22 22:02:27 +0100415#if BUILD_AS_NOMMU
416# undef BB_MMU
417# undef USE_FOR_NOMMU
418# undef USE_FOR_MMU
419# define BB_MMU 0
420# define USE_FOR_NOMMU(...) __VA_ARGS__
421# define USE_FOR_MMU(...)
422#endif
423
Francis Laniel36836fc2023-12-22 22:02:28 +0100424#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +0100425#include "NUM_APPLETS.h"
426#if NUM_APPLETS == 1
427/* STANDALONE does not make sense, and won't compile */
Francis Laniel110b7692023-12-22 22:02:27 +0100428# undef ENABLE_FEATURE_SH_STANDALONE
429# undef IF_FEATURE_SH_STANDALONE
430# undef IF_NOT_FEATURE_SH_STANDALONE
431# define ENABLE_FEATURE_SH_STANDALONE 0
432# define IF_FEATURE_SH_STANDALONE(...)
433# define IF_NOT_FEATURE_SH_STANDALONE(...) __VA_ARGS__
434#endif
Francis Laniel36836fc2023-12-22 22:02:28 +0100435#endif /* __U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +0100436
437#if !ENABLE_HUSH_INTERACTIVE
438# undef ENABLE_FEATURE_EDITING
439# define ENABLE_FEATURE_EDITING 0
440# undef ENABLE_FEATURE_EDITING_FANCY_PROMPT
441# define ENABLE_FEATURE_EDITING_FANCY_PROMPT 0
442# undef ENABLE_FEATURE_EDITING_SAVE_ON_EXIT
443# define ENABLE_FEATURE_EDITING_SAVE_ON_EXIT 0
444#endif
445
446/* Do we support ANY keywords? */
447#if ENABLE_HUSH_IF || ENABLE_HUSH_LOOPS || ENABLE_HUSH_CASE
448# define HAS_KEYWORDS 1
449# define IF_HAS_KEYWORDS(...) __VA_ARGS__
450# define IF_HAS_NO_KEYWORDS(...)
451#else
452# define HAS_KEYWORDS 0
453# define IF_HAS_KEYWORDS(...)
454# define IF_HAS_NO_KEYWORDS(...) __VA_ARGS__
455#endif
456
457/* If you comment out one of these below, it will be #defined later
458 * to perform debug printfs to stderr: */
459#define debug_printf(...) do {} while (0)
460/* Finer-grained debug switches */
461#define debug_printf_parse(...) do {} while (0)
462#define debug_printf_heredoc(...) do {} while (0)
463#define debug_print_tree(a, b) do {} while (0)
464#define debug_printf_exec(...) do {} while (0)
465#define debug_printf_env(...) do {} while (0)
466#define debug_printf_jobs(...) do {} while (0)
467#define debug_printf_expand(...) do {} while (0)
468#define debug_printf_varexp(...) do {} while (0)
469#define debug_printf_glob(...) do {} while (0)
470#define debug_printf_redir(...) do {} while (0)
471#define debug_printf_list(...) do {} while (0)
472#define debug_printf_subst(...) do {} while (0)
473#define debug_printf_prompt(...) do {} while (0)
474#define debug_printf_clean(...) do {} while (0)
475
476#define ERR_PTR ((void*)(long)1)
477
478#define JOB_STATUS_FORMAT "[%u] %-22s %.40s\n"
479
480#define _SPECIAL_VARS_STR "_*@$!?#-"
Francis Laniel36836fc2023-12-22 22:02:28 +0100481#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +0100482#define SPECIAL_VARS_STR ("_*@$!?#-" + 1)
483#define NUMERIC_SPECVARS_STR ("_*@$!?#-" + 3)
Francis Laniel36836fc2023-12-22 22:02:28 +0100484#else /* __U_BOOT__ */
485#define SPECIAL_VARS_STR "*@$!?#-"
486#define NUMERIC_SPECVARS_STR "$!?#-"
487#endif /* __U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +0100488#if BASH_PATTERN_SUBST
489/* Support / and // replace ops */
490/* Note that // is stored as \ in "encoded" string representation */
491# define VAR_ENCODED_SUBST_OPS "\\/%#:-=+?"
492# define VAR_SUBST_OPS ("\\/%#:-=+?" + 1)
493# define MINUS_PLUS_EQUAL_QUESTION ("\\/%#:-=+?" + 5)
494#else
495# define VAR_ENCODED_SUBST_OPS "%#:-=+?"
496# define VAR_SUBST_OPS "%#:-=+?"
497# define MINUS_PLUS_EQUAL_QUESTION ("%#:-=+?" + 3)
498#endif
499
500#define SPECIAL_VAR_SYMBOL_STR "\3"
501#define SPECIAL_VAR_SYMBOL 3
502/* The "variable" with name "\1" emits string "\3". Testcase: "echo ^C" */
503#define SPECIAL_VAR_QUOTED_SVS 1
504
505struct variable;
506
507static const char hush_version_str[] ALIGN1 = "HUSH_VERSION="BB_VER;
508
509/* This supports saving pointers malloced in vfork child,
510 * to be freed in the parent.
511 */
512#if !BB_MMU
513typedef struct nommu_save_t {
514 struct variable *old_vars;
515 char **argv;
516 char **argv_from_re_execing;
517} nommu_save_t;
518#endif
519
520enum {
521 RES_NONE = 0,
522#if ENABLE_HUSH_IF
523 RES_IF ,
524 RES_THEN ,
525 RES_ELIF ,
526 RES_ELSE ,
527 RES_FI ,
528#endif
529#if ENABLE_HUSH_LOOPS
530 RES_FOR ,
531 RES_WHILE ,
532 RES_UNTIL ,
533 RES_DO ,
534 RES_DONE ,
535#endif
536#if ENABLE_HUSH_LOOPS || ENABLE_HUSH_CASE
537 RES_IN ,
538#endif
539#if ENABLE_HUSH_CASE
540 RES_CASE ,
541 /* three pseudo-keywords support contrived "case" syntax: */
542 RES_CASE_IN, /* "case ... IN", turns into RES_MATCH when IN is observed */
543 RES_MATCH , /* "word)" */
544 RES_CASE_BODY, /* "this command is inside CASE" */
545 RES_ESAC ,
546#endif
547 RES_XXXX ,
548 RES_SNTX
549};
550
551typedef struct o_string {
552 char *data;
553 int length; /* position where data is appended */
554 int maxlen;
555 int o_expflags;
556 /* At least some part of the string was inside '' or "",
557 * possibly empty one: word"", wo''rd etc. */
558 smallint has_quoted_part;
559 smallint has_empty_slot;
560 smallint ended_in_ifs;
561} o_string;
562enum {
563 EXP_FLAG_SINGLEWORD = 0x80, /* must be 0x80 */
564 EXP_FLAG_GLOB = 0x2,
565 /* Protect newly added chars against globbing
566 * by prepending \ to *, ?, [, \ */
567 EXP_FLAG_ESC_GLOB_CHARS = 0x1,
568};
569/* Used for initialization: o_string foo = NULL_O_STRING; */
570#define NULL_O_STRING { NULL }
571
572#ifndef debug_printf_parse
Francis Laniele7ca3a32023-12-22 22:02:42 +0100573static const char *const assignment_flag[] ALIGN_PTR = {
Francis Laniel110b7692023-12-22 22:02:27 +0100574 "MAYBE_ASSIGNMENT",
575 "DEFINITELY_ASSIGNMENT",
576 "NOT_ASSIGNMENT",
577 "WORD_IS_KEYWORD",
578};
579#endif
580
581/* We almost can use standard FILE api, but we need an ability to move
582 * its fd when redirects coincide with it. No api exists for that
583 * (RFE for it at https://sourceware.org/bugzilla/show_bug.cgi?id=21902).
584 * HFILE is our internal alternative. Only supports reading.
585 * Since we now can, we incorporate linked list of all opened HFILEs
586 * into the struct (used to be a separate mini-list).
587 */
588typedef struct HFILE {
589 char *cur;
590 char *end;
591 struct HFILE *next_hfile;
592 int fd;
593 char buf[1024];
594} HFILE;
595
596typedef struct in_str {
597 const char *p;
598 int peek_buf[2];
599 int last_char;
600 HFILE *file;
601} in_str;
602
Francis Laniel36836fc2023-12-22 22:02:28 +0100603#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +0100604/* The descrip member of this structure is only used to make
605 * debugging output pretty */
606static const struct {
607 int32_t mode;
608 signed char default_fd;
609 char descrip[3];
610} redir_table[] ALIGN4 = {
611 { O_RDONLY, 0, "<" },
612 { O_CREAT|O_TRUNC|O_WRONLY, 1, ">" },
613 { O_CREAT|O_APPEND|O_WRONLY, 1, ">>" },
614 { O_CREAT|O_RDWR, 1, "<>" },
615 { O_RDONLY, 0, "<<" },
616/* Should not be needed. Bogus default_fd helps in debugging */
617/* { O_RDONLY, 77, "<<" }, */
618};
619
620struct redir_struct {
621 struct redir_struct *next;
622 char *rd_filename; /* filename */
623 int rd_fd; /* fd to redirect */
624 /* fd to redirect to, or -3 if rd_fd is to be closed (n>&-) */
625 int rd_dup;
626 smallint rd_type; /* (enum redir_type) */
627 /* note: for heredocs, rd_filename contains heredoc delimiter,
628 * and subsequently heredoc itself; and rd_dup is a bitmask:
629 * bit 0: do we need to trim leading tabs?
630 * bit 1: is heredoc quoted (<<'delim' syntax) ?
631 */
632};
633typedef enum redir_type {
634 REDIRECT_INPUT = 0,
635 REDIRECT_OVERWRITE = 1,
636 REDIRECT_APPEND = 2,
637 REDIRECT_IO = 3,
638 REDIRECT_HEREDOC = 4,
639 REDIRECT_HEREDOC2 = 5, /* REDIRECT_HEREDOC after heredoc is loaded */
640
641 REDIRFD_CLOSE = -3,
642 REDIRFD_SYNTAX_ERR = -2,
643 REDIRFD_TO_FILE = -1,
644 /* otherwise, rd_fd is redirected to rd_dup */
645
646 HEREDOC_SKIPTABS = 1,
647 HEREDOC_QUOTED = 2,
648} redir_type;
649
Francis Laniel36836fc2023-12-22 22:02:28 +0100650#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +0100651
652struct command {
Francis Laniel36836fc2023-12-22 22:02:28 +0100653#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +0100654 pid_t pid; /* 0 if exited */
Francis Laniel36836fc2023-12-22 22:02:28 +0100655#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +0100656 unsigned assignment_cnt; /* how many argv[i] are assignments? */
657#if ENABLE_HUSH_LINENO_VAR
658 unsigned lineno;
659#endif
660 smallint cmd_type; /* CMD_xxx */
661#define CMD_NORMAL 0
662#define CMD_SUBSHELL 1
663#if BASH_TEST2
664/* used for "[[ EXPR ]]" */
665# define CMD_TEST2_SINGLEWORD_NOGLOB 2
666#endif
667#if BASH_TEST2 || ENABLE_HUSH_LOCAL || ENABLE_HUSH_EXPORT || ENABLE_HUSH_READONLY
668/* used to prevent word splitting and globbing in "export v=t*" */
669# define CMD_SINGLEWORD_NOGLOB 3
670#endif
671#if ENABLE_HUSH_FUNCTIONS
672# define CMD_FUNCDEF 4
673#endif
674
675 smalluint cmd_exitcode;
676 /* if non-NULL, this "command" is { list }, ( list ), or a compound statement */
677 struct pipe *group;
678#if !BB_MMU
679 char *group_as_string;
680#endif
681#if ENABLE_HUSH_FUNCTIONS
682 struct function *child_func;
683/* This field is used to prevent a bug here:
684 * while...do f1() {a;}; f1; f1() {b;}; f1; done
685 * When we execute "f1() {a;}" cmd, we create new function and clear
686 * cmd->group, cmd->group_as_string, cmd->argv[0].
687 * When we execute "f1() {b;}", we notice that f1 exists,
688 * and that its "parent cmd" struct is still "alive",
689 * we put those fields back into cmd->xxx
690 * (struct function has ->parent_cmd ptr to facilitate that).
691 * When we loop back, we can execute "f1() {a;}" again and set f1 correctly.
692 * Without this trick, loop would execute a;b;b;b;...
693 * instead of correct sequence a;b;a;b;...
694 * When command is freed, it severs the link
695 * (sets ->child_func->parent_cmd to NULL).
696 */
697#endif
Francis Laniel36836fc2023-12-22 22:02:28 +0100698#ifdef __U_BOOT__
699 int argc; /* number of program arguments */
700#endif
Francis Laniel110b7692023-12-22 22:02:27 +0100701 char **argv; /* command name and arguments */
702/* argv vector may contain variable references (^Cvar^C, ^C0^C etc)
703 * and on execution these are substituted with their values.
704 * Substitution can make _several_ words out of one argv[n]!
705 * Example: argv[0]=='.^C*^C.' here: echo .$*.
706 * References of the form ^C`cmd arg^C are `cmd arg` substitutions.
707 */
Francis Laniel36836fc2023-12-22 22:02:28 +0100708#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +0100709 struct redir_struct *redirects; /* I/O redirections */
Francis Laniel36836fc2023-12-22 22:02:28 +0100710#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +0100711};
712/* Is there anything in this command at all? */
Francis Laniel36836fc2023-12-22 22:02:28 +0100713#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +0100714#define IS_NULL_CMD(cmd) \
715 (!(cmd)->group && !(cmd)->argv && !(cmd)->redirects)
716
Francis Laniel36836fc2023-12-22 22:02:28 +0100717#else /* __U_BOOT__ */
718#define IS_NULL_CMD(cmd) \
719 (!(cmd)->group && !(cmd)->argv)
720#endif /* __U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +0100721struct pipe {
722 struct pipe *next;
723 int num_cmds; /* total number of commands in pipe */
Francis Laniel36836fc2023-12-22 22:02:28 +0100724#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +0100725 int alive_cmds; /* number of commands running (not exited) */
726 int stopped_cmds; /* number of commands alive, but stopped */
727#if ENABLE_HUSH_JOB
728 unsigned jobid; /* job number */
729 pid_t pgrp; /* process group ID for the job */
730 char *cmdtext; /* name of job */
731#endif
Francis Laniel36836fc2023-12-22 22:02:28 +0100732#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +0100733 struct command *cmds; /* array of commands in pipe */
734 smallint followup; /* PIPE_BG, PIPE_SEQ, PIPE_OR, PIPE_AND */
735 IF_HAS_KEYWORDS(smallint pi_inverted;) /* "! cmd | cmd" */
736 IF_HAS_KEYWORDS(smallint res_word;) /* needed for if, for, while, until... */
737};
738typedef enum pipe_style {
739 PIPE_SEQ = 0,
740 PIPE_AND = 1,
741 PIPE_OR = 2,
742 PIPE_BG = 3,
743} pipe_style;
744/* Is there anything in this pipe at all? */
745#define IS_NULL_PIPE(pi) \
746 ((pi)->num_cmds == 0 IF_HAS_KEYWORDS( && (pi)->res_word == RES_NONE))
747
748/* This holds pointers to the various results of parsing */
749struct parse_context {
750 /* linked list of pipes */
751 struct pipe *list_head;
752 /* last pipe (being constructed right now) */
753 struct pipe *pipe;
754 /* last command in pipe (being constructed right now) */
755 struct command *command;
Francis Laniel36836fc2023-12-22 22:02:28 +0100756#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +0100757 /* last redirect in command->redirects list */
758 struct redir_struct *pending_redirect;
Francis Laniel36836fc2023-12-22 22:02:28 +0100759#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +0100760 o_string word;
761#if !BB_MMU
762 o_string as_string;
763#endif
764 smallint is_assignment; /* 0:maybe, 1:yes, 2:no, 3:keyword */
765#if HAS_KEYWORDS
766 smallint ctx_res_w;
767 smallint ctx_inverted; /* "! cmd | cmd" */
768#if ENABLE_HUSH_CASE
769 smallint ctx_dsemicolon; /* ";;" seen */
770#endif
771 /* bitmask of FLAG_xxx, for figuring out valid reserved words */
772 int old_flag;
773 /* group we are enclosed in:
774 * example: "if pipe1; pipe2; then pipe3; fi"
775 * when we see "if" or "then", we malloc and copy current context,
776 * and make ->stack point to it. then we parse pipeN.
777 * when closing "then" / fi" / whatever is found,
778 * we move list_head into ->stack->command->group,
779 * copy ->stack into current context, and delete ->stack.
780 * (parsing of { list } and ( list ) doesn't use this method)
781 */
782 struct parse_context *stack;
783#endif
784};
785enum {
786 MAYBE_ASSIGNMENT = 0,
787 DEFINITELY_ASSIGNMENT = 1,
788 NOT_ASSIGNMENT = 2,
789 /* Not an assignment, but next word may be: "if v=xyz cmd;" */
790 WORD_IS_KEYWORD = 3,
791};
792
Francis Laniel36836fc2023-12-22 22:02:28 +0100793#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +0100794/* On program start, environ points to initial environment.
795 * putenv adds new pointers into it, unsetenv removes them.
796 * Neither of these (de)allocates the strings.
797 * setenv allocates new strings in malloc space and does putenv,
798 * and thus setenv is unusable (leaky) for shell's purposes */
799#define setenv(...) setenv_is_leaky_dont_use()
Francis Laniel36836fc2023-12-22 22:02:28 +0100800#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +0100801struct variable {
802 struct variable *next;
803 char *varstr; /* points to "name=" portion */
804 int max_len; /* if > 0, name is part of initial env; else name is malloced */
Francis Laniel36836fc2023-12-22 22:02:28 +0100805#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +0100806 uint16_t var_nest_level;
807 smallint flg_export; /* putenv should be done on this var */
808 smallint flg_read_only;
Francis Laniel36836fc2023-12-22 22:02:28 +0100809#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +0100810};
811
812enum {
813 BC_BREAK = 1,
814 BC_CONTINUE = 2,
815};
816
817#if ENABLE_HUSH_FUNCTIONS
818struct function {
819 struct function *next;
820 char *name;
821 struct command *parent_cmd;
822 struct pipe *body;
823# if !BB_MMU
824 char *body_as_string;
825# endif
826};
827#endif
828
Francis Laniel110b7692023-12-22 22:02:27 +0100829/* set -/+o OPT support. (TODO: make it optional)
830 * bash supports the following opts:
831 * allexport off
832 * braceexpand on
833 * emacs on
834 * errexit off
835 * errtrace off
836 * functrace off
837 * hashall on
838 * histexpand off
839 * history on
840 * ignoreeof off
841 * interactive-comments on
842 * keyword off
843 * monitor on
844 * noclobber off
845 * noexec off
846 * noglob off
847 * nolog off
848 * notify off
849 * nounset off
850 * onecmd off
851 * physical off
852 * pipefail off
853 * posix off
854 * privileged off
855 * verbose off
856 * vi off
857 * xtrace off
858 */
859static const char o_opt_strings[] ALIGN1 =
860 "pipefail\0"
861 "noexec\0"
862 "errexit\0"
863#if ENABLE_HUSH_MODE_X
864 "xtrace\0"
865#endif
866 ;
867enum {
868 OPT_O_PIPEFAIL,
869 OPT_O_NOEXEC,
870 OPT_O_ERREXIT,
871#if ENABLE_HUSH_MODE_X
872 OPT_O_XTRACE,
873#endif
874 NUM_OPT_O
875};
876
877/* "Globals" within this file */
878/* Sorted roughly by size (smaller offsets == smaller code) */
879struct globals {
Francis Laniel36836fc2023-12-22 22:02:28 +0100880#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +0100881 /* interactive_fd != 0 means we are an interactive shell.
882 * If we are, then saved_tty_pgrp can also be != 0, meaning
883 * that controlling tty is available. With saved_tty_pgrp == 0,
884 * job control still works, but terminal signals
885 * (^C, ^Z, ^Y, ^\) won't work at all, and background
886 * process groups can only be created with "cmd &".
887 * With saved_tty_pgrp != 0, hush will use tcsetpgrp()
888 * to give tty to the foreground process group,
889 * and will take it back when the group is stopped (^Z)
890 * or killed (^C).
891 */
892#if ENABLE_HUSH_INTERACTIVE
893 /* 'interactive_fd' is a fd# open to ctty, if we have one
894 * _AND_ if we decided to act interactively */
895 int interactive_fd;
896 IF_NOT_FEATURE_EDITING_FANCY_PROMPT(char *PS1;)
897# define G_interactive_fd (G.interactive_fd)
898#else
899# define G_interactive_fd 0
900#endif
Francis Laniel36836fc2023-12-22 22:02:28 +0100901#else /* __U_BOOT__ */
902# define G_interactive_fd 0
903#endif /* __U_BOOT__ */
904#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +0100905#if ENABLE_FEATURE_EDITING
906 line_input_t *line_input_state;
907#endif
908 pid_t root_pid;
909 pid_t root_ppid;
910 pid_t last_bg_pid;
911#if ENABLE_HUSH_RANDOM_SUPPORT
912 random_t random_gen;
913#endif
914#if ENABLE_HUSH_JOB
915 int run_list_level;
916 unsigned last_jobid;
917 pid_t saved_tty_pgrp;
918 struct pipe *job_list;
919# define G_saved_tty_pgrp (G.saved_tty_pgrp)
920#else
921# define G_saved_tty_pgrp 0
922#endif
Francis Laniel36836fc2023-12-22 22:02:28 +0100923#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +0100924 /* How deeply are we in context where "set -e" is ignored */
925 int errexit_depth;
Francis Laniel36836fc2023-12-22 22:02:28 +0100926#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +0100927 /* "set -e" rules (do we follow them correctly?):
928 * Exit if pipe, list, or compound command exits with a non-zero status.
929 * Shell does not exit if failed command is part of condition in
930 * if/while, part of && or || list except the last command, any command
931 * in a pipe but the last, or if the command's return value is being
932 * inverted with !. If a compound command other than a subshell returns a
933 * non-zero status because a command failed while -e was being ignored, the
934 * shell does not exit. A trap on ERR, if set, is executed before the shell
935 * exits [ERR is a bashism].
936 *
937 * If a compound command or function executes in a context where -e is
938 * ignored, none of the commands executed within are affected by the -e
939 * setting. If a compound command or function sets -e while executing in a
940 * context where -e is ignored, that setting does not have any effect until
941 * the compound command or the command containing the function call completes.
942 */
943
944 char o_opt[NUM_OPT_O];
945#if ENABLE_HUSH_MODE_X
946# define G_x_mode (G.o_opt[OPT_O_XTRACE])
947#else
948# define G_x_mode 0
949#endif
950 char opt_s;
951 char opt_c;
Francis Laniel36836fc2023-12-22 22:02:28 +0100952#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +0100953#if ENABLE_HUSH_INTERACTIVE
954 smallint promptmode; /* 0: PS1, 1: PS2 */
955#endif
Francis Laniele7ca3a32023-12-22 22:02:42 +0100956 /* set by signal handler if SIGINT is received _and_ its trap is not set */
Francis Laniel110b7692023-12-22 22:02:27 +0100957 smallint flag_SIGINT;
Francis Laniel36836fc2023-12-22 22:02:28 +0100958#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +0100959#if ENABLE_HUSH_LOOPS
960 smallint flag_break_continue;
961#endif
Francis Laniel36836fc2023-12-22 22:02:28 +0100962#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +0100963#if ENABLE_HUSH_FUNCTIONS
964 /* 0: outside of a function (or sourced file)
965 * -1: inside of a function, ok to use return builtin
966 * 1: return is invoked, skip all till end of func
967 */
968 smallint flag_return_in_progress;
969# define G_flag_return_in_progress (G.flag_return_in_progress)
970#else
971# define G_flag_return_in_progress 0
972#endif
973 smallint exiting; /* used to prevent EXIT trap recursion */
974 /* These support $? */
975 smalluint last_exitcode;
976 smalluint expand_exitcode;
977 smalluint last_bg_pid_exitcode;
Francis Laniel36836fc2023-12-22 22:02:28 +0100978#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +0100979#if ENABLE_HUSH_SET
980 /* are global_argv and global_argv[1..n] malloced? (note: not [0]) */
981 smalluint global_args_malloced;
982# define G_global_args_malloced (G.global_args_malloced)
983#else
984# define G_global_args_malloced 0
985#endif
986#if ENABLE_HUSH_BASH_COMPAT
987 int dead_job_exitcode; /* for "wait -n" */
988#endif
Francis Laniel36836fc2023-12-22 22:02:28 +0100989#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +0100990 /* how many non-NULL argv's we have. NB: $# + 1 */
991 int global_argc;
992 char **global_argv;
993#if !BB_MMU
994 char *argv0_for_re_execing;
995#endif
996#if ENABLE_HUSH_LOOPS
Francis Laniel36836fc2023-12-22 22:02:28 +0100997#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +0100998 unsigned depth_break_continue;
Francis Laniel36836fc2023-12-22 22:02:28 +0100999#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01001000 unsigned depth_of_loop;
1001#endif
Francis Laniel36836fc2023-12-22 22:02:28 +01001002#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01001003#if ENABLE_HUSH_GETOPTS
1004 unsigned getopt_count;
1005#endif
Francis Laniel36836fc2023-12-22 22:02:28 +01001006#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01001007 const char *ifs;
Francis Laniel36836fc2023-12-22 22:02:28 +01001008#ifdef __U_BOOT__
1009 int flag_repeat;
1010 int do_repeat;
Francis Laniel26cafe12023-12-22 22:02:34 +01001011 int run_command_flags;
Francis Laniel36836fc2023-12-22 22:02:28 +01001012#endif /* __U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01001013 char *ifs_whitespace; /* = G.ifs or malloced */
Francis Laniel36836fc2023-12-22 22:02:28 +01001014#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01001015 const char *cwd;
Francis Laniel36836fc2023-12-22 22:02:28 +01001016#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01001017 struct variable *top_var;
1018 char **expanded_assignments;
1019 struct variable **shadowed_vars_pp;
1020 unsigned var_nest_level;
Francis Laniel36836fc2023-12-22 22:02:28 +01001021#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01001022#if ENABLE_HUSH_FUNCTIONS
1023# if ENABLE_HUSH_LOCAL
1024 unsigned func_nest_level; /* solely to prevent "local v" in non-functions */
1025# endif
1026 struct function *top_func;
1027#endif
1028 /* Signal and trap handling */
1029#if ENABLE_HUSH_FAST
1030 unsigned count_SIGCHLD;
1031 unsigned handled_SIGCHLD;
1032 smallint we_have_children;
1033#endif
1034#if ENABLE_HUSH_LINENO_VAR
1035 unsigned parse_lineno;
1036 unsigned execute_lineno;
1037#endif
1038 HFILE *HFILE_list;
1039 HFILE *HFILE_stdin;
1040 /* Which signals have non-DFL handler (even with no traps set)?
1041 * Set at the start to:
1042 * (SIGQUIT + maybe SPECIAL_INTERACTIVE_SIGS + maybe SPECIAL_JOBSTOP_SIGS)
1043 * SPECIAL_INTERACTIVE_SIGS are cleared after fork.
1044 * The rest is cleared right before execv syscalls.
1045 * Other than these two times, never modified.
1046 */
1047 unsigned special_sig_mask;
1048#if ENABLE_HUSH_JOB
1049 unsigned fatal_sig_mask;
1050# define G_fatal_sig_mask (G.fatal_sig_mask)
1051#else
1052# define G_fatal_sig_mask 0
1053#endif
1054#if ENABLE_HUSH_TRAP
1055 int pre_trap_exitcode;
1056# if ENABLE_HUSH_FUNCTIONS
1057 int return_exitcode;
1058# endif
1059 char **traps; /* char *traps[NSIG] */
1060# define G_traps G.traps
1061#else
1062# define G_traps ((char**)NULL)
1063#endif
1064 sigset_t pending_set;
1065#if ENABLE_HUSH_MEMLEAK
1066 unsigned long memleak_value;
1067#endif
1068#if ENABLE_HUSH_MODE_X
1069 unsigned x_mode_depth;
1070 /* "set -x" output should not be redirectable with subsequent 2>FILE.
1071 * We dup fd#2 to x_mode_fd when "set -x" is executed, and use it
1072 * for all subsequent output.
1073 */
1074 int x_mode_fd;
1075 o_string x_mode_buf;
1076#endif
Francis Laniel36836fc2023-12-22 22:02:28 +01001077#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01001078#if HUSH_DEBUG >= 2
1079 int debug_indent;
1080#endif
Francis Laniel36836fc2023-12-22 22:02:28 +01001081#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01001082 struct sigaction sa;
1083 char optstring_buf[sizeof("eixcs")];
1084#if BASH_EPOCH_VARS
1085 char epoch_buf[sizeof("%llu.nnnnnn") + sizeof(long long)*3];
1086#endif
1087#if ENABLE_FEATURE_EDITING
1088 char user_input_buf[CONFIG_FEATURE_EDITING_MAX_LEN];
1089#endif
Francis Laniel36836fc2023-12-22 22:02:28 +01001090#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01001091};
Francis Laniel36836fc2023-12-22 22:02:28 +01001092#ifdef __U_BOOT__
1093struct globals *ptr_to_globals;
1094#endif /* __U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01001095#define G (*ptr_to_globals)
1096/* Not #defining name to G.name - this quickly gets unwieldy
1097 * (too many defines). Also, I actually prefer to see when a variable
1098 * is global, thus "G." prefix is a useful hint */
Francis Laniel36836fc2023-12-22 22:02:28 +01001099#ifdef __U_BOOT__
1100#define SET_PTR_TO_GLOBALS(x) do { \
1101 (*(struct globals**)&ptr_to_globals) = (void*)(x); \
1102 barrier(); \
1103} while (0)
1104#define INIT_G() do { \
1105 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
1106 G.promptmode = 1; \
1107} while (0)
1108#else /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01001109#define INIT_G() do { \
1110 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
1111 /* memset(&G.sa, 0, sizeof(G.sa)); */ \
1112 sigfillset(&G.sa.sa_mask); \
1113 G.sa.sa_flags = SA_RESTART; \
1114} while (0)
Francis Laniel36836fc2023-12-22 22:02:28 +01001115#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01001116
Francis Laniel36836fc2023-12-22 22:02:28 +01001117#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01001118/* Function prototypes for builtins */
1119static int builtin_cd(char **argv) FAST_FUNC;
1120#if ENABLE_HUSH_ECHO
1121static int builtin_echo(char **argv) FAST_FUNC;
1122#endif
1123static int builtin_eval(char **argv) FAST_FUNC;
1124static int builtin_exec(char **argv) FAST_FUNC;
1125static int builtin_exit(char **argv) FAST_FUNC;
1126#if ENABLE_HUSH_EXPORT
1127static int builtin_export(char **argv) FAST_FUNC;
1128#endif
1129#if ENABLE_HUSH_READONLY
1130static int builtin_readonly(char **argv) FAST_FUNC;
1131#endif
Francis Laniele7ca3a32023-12-22 22:02:42 +01001132static int builtin_false(char **argv) FAST_FUNC;
Francis Laniel110b7692023-12-22 22:02:27 +01001133#if ENABLE_HUSH_JOB
1134static int builtin_fg_bg(char **argv) FAST_FUNC;
1135static int builtin_jobs(char **argv) FAST_FUNC;
1136#endif
1137#if ENABLE_HUSH_GETOPTS
1138static int builtin_getopts(char **argv) FAST_FUNC;
1139#endif
1140#if ENABLE_HUSH_HELP
1141static int builtin_help(char **argv) FAST_FUNC;
1142#endif
1143#if MAX_HISTORY && ENABLE_FEATURE_EDITING
1144static int builtin_history(char **argv) FAST_FUNC;
1145#endif
1146#if ENABLE_HUSH_LOCAL
1147static int builtin_local(char **argv) FAST_FUNC;
1148#endif
1149#if ENABLE_HUSH_MEMLEAK
1150static int builtin_memleak(char **argv) FAST_FUNC;
1151#endif
1152#if ENABLE_HUSH_PRINTF
1153static int builtin_printf(char **argv) FAST_FUNC;
1154#endif
1155static int builtin_pwd(char **argv) FAST_FUNC;
1156#if ENABLE_HUSH_READ
1157static int builtin_read(char **argv) FAST_FUNC;
1158#endif
1159#if ENABLE_HUSH_SET
1160static int builtin_set(char **argv) FAST_FUNC;
1161#endif
1162static int builtin_shift(char **argv) FAST_FUNC;
1163static int builtin_source(char **argv) FAST_FUNC;
1164#if ENABLE_HUSH_TEST || BASH_TEST2
1165static int builtin_test(char **argv) FAST_FUNC;
1166#endif
1167#if ENABLE_HUSH_TRAP
1168static int builtin_trap(char **argv) FAST_FUNC;
1169#endif
1170#if ENABLE_HUSH_TYPE
1171static int builtin_type(char **argv) FAST_FUNC;
1172#endif
1173#if ENABLE_HUSH_TIMES
1174static int builtin_times(char **argv) FAST_FUNC;
1175#endif
1176static int builtin_true(char **argv) FAST_FUNC;
1177#if ENABLE_HUSH_UMASK
1178static int builtin_umask(char **argv) FAST_FUNC;
1179#endif
1180#if ENABLE_HUSH_UNSET
1181static int builtin_unset(char **argv) FAST_FUNC;
1182#endif
1183#if ENABLE_HUSH_KILL
1184static int builtin_kill(char **argv) FAST_FUNC;
1185#endif
1186#if ENABLE_HUSH_WAIT
1187static int builtin_wait(char **argv) FAST_FUNC;
1188#endif
1189#if ENABLE_HUSH_LOOPS
1190static int builtin_break(char **argv) FAST_FUNC;
1191static int builtin_continue(char **argv) FAST_FUNC;
1192#endif
1193#if ENABLE_HUSH_FUNCTIONS
1194static int builtin_return(char **argv) FAST_FUNC;
1195#endif
1196
1197/* Table of built-in functions. They can be forked or not, depending on
1198 * context: within pipes, they fork. As simple commands, they do not.
1199 * When used in non-forking context, they can change global variables
1200 * in the parent shell process. If forked, of course they cannot.
1201 * For example, 'unset foo | whatever' will parse and run, but foo will
1202 * still be set at the end. */
1203struct built_in_command {
1204 const char *b_cmd;
1205 int (*b_function)(char **argv) FAST_FUNC;
1206#if ENABLE_HUSH_HELP
1207 const char *b_descr;
1208# define BLTIN(cmd, func, help) { cmd, func, help }
1209#else
1210# define BLTIN(cmd, func, help) { cmd, func }
1211#endif
1212};
1213
1214static const struct built_in_command bltins1[] ALIGN_PTR = {
1215 BLTIN("." , builtin_source , "Run commands in file"),
1216 BLTIN(":" , builtin_true , NULL),
1217#if ENABLE_HUSH_JOB
1218 BLTIN("bg" , builtin_fg_bg , "Resume job in background"),
1219#endif
1220#if ENABLE_HUSH_LOOPS
1221 BLTIN("break" , builtin_break , "Exit loop"),
1222#endif
1223 BLTIN("cd" , builtin_cd , "Change directory"),
1224#if ENABLE_HUSH_LOOPS
1225 BLTIN("continue" , builtin_continue, "Start new loop iteration"),
1226#endif
1227 BLTIN("eval" , builtin_eval , "Construct and run shell command"),
1228 BLTIN("exec" , builtin_exec , "Execute command, don't return to shell"),
1229 BLTIN("exit" , builtin_exit , NULL),
1230#if ENABLE_HUSH_EXPORT
1231 BLTIN("export" , builtin_export , "Set environment variables"),
1232#endif
Francis Laniele7ca3a32023-12-22 22:02:42 +01001233 BLTIN("false" , builtin_false , NULL),
Francis Laniel110b7692023-12-22 22:02:27 +01001234#if ENABLE_HUSH_JOB
1235 BLTIN("fg" , builtin_fg_bg , "Bring job to foreground"),
1236#endif
1237#if ENABLE_HUSH_GETOPTS
1238 BLTIN("getopts" , builtin_getopts , NULL),
1239#endif
1240#if ENABLE_HUSH_HELP
1241 BLTIN("help" , builtin_help , NULL),
1242#endif
1243#if MAX_HISTORY && ENABLE_FEATURE_EDITING
1244 BLTIN("history" , builtin_history , "Show history"),
1245#endif
1246#if ENABLE_HUSH_JOB
1247 BLTIN("jobs" , builtin_jobs , "List jobs"),
1248#endif
1249#if ENABLE_HUSH_KILL
1250 BLTIN("kill" , builtin_kill , "Send signals to processes"),
1251#endif
1252#if ENABLE_HUSH_LOCAL
1253 BLTIN("local" , builtin_local , "Set local variables"),
1254#endif
1255#if ENABLE_HUSH_MEMLEAK
1256 BLTIN("memleak" , builtin_memleak , NULL),
1257#endif
1258#if ENABLE_HUSH_READ
1259 BLTIN("read" , builtin_read , "Input into variable"),
1260#endif
1261#if ENABLE_HUSH_READONLY
1262 BLTIN("readonly" , builtin_readonly, "Make variables read-only"),
1263#endif
1264#if ENABLE_HUSH_FUNCTIONS
1265 BLTIN("return" , builtin_return , "Return from function"),
1266#endif
1267#if ENABLE_HUSH_SET
1268 BLTIN("set" , builtin_set , "Set positional parameters"),
1269#endif
1270 BLTIN("shift" , builtin_shift , "Shift positional parameters"),
1271#if BASH_SOURCE
1272 BLTIN("source" , builtin_source , NULL),
1273#endif
1274#if ENABLE_HUSH_TIMES
1275 BLTIN("times" , builtin_times , NULL),
1276#endif
1277#if ENABLE_HUSH_TRAP
1278 BLTIN("trap" , builtin_trap , "Trap signals"),
1279#endif
1280 BLTIN("true" , builtin_true , NULL),
1281#if ENABLE_HUSH_TYPE
1282 BLTIN("type" , builtin_type , "Show command type"),
1283#endif
1284#if ENABLE_HUSH_ULIMIT
1285 BLTIN("ulimit" , shell_builtin_ulimit, "Control resource limits"),
1286#endif
1287#if ENABLE_HUSH_UMASK
1288 BLTIN("umask" , builtin_umask , "Set file creation mask"),
1289#endif
1290#if ENABLE_HUSH_UNSET
1291 BLTIN("unset" , builtin_unset , "Unset variables"),
1292#endif
1293#if ENABLE_HUSH_WAIT
1294 BLTIN("wait" , builtin_wait , "Wait for process to finish"),
1295#endif
1296};
1297/* These builtins won't be used if we are on NOMMU and need to re-exec
1298 * (it's cheaper to run an external program in this case):
1299 */
1300static const struct built_in_command bltins2[] ALIGN_PTR = {
1301#if ENABLE_HUSH_TEST
1302 BLTIN("[" , builtin_test , NULL),
1303#endif
1304#if BASH_TEST2
1305 BLTIN("[[" , builtin_test , NULL),
1306#endif
1307#if ENABLE_HUSH_ECHO
1308 BLTIN("echo" , builtin_echo , NULL),
1309#endif
1310#if ENABLE_HUSH_PRINTF
1311 BLTIN("printf" , builtin_printf , NULL),
1312#endif
1313 BLTIN("pwd" , builtin_pwd , NULL),
1314#if ENABLE_HUSH_TEST
1315 BLTIN("test" , builtin_test , NULL),
1316#endif
1317};
1318
Francis Laniel36836fc2023-12-22 22:02:28 +01001319#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01001320
1321/* Debug printouts.
1322 */
1323#if HUSH_DEBUG >= 2
1324/* prevent disasters with G.debug_indent < 0 */
1325# define indent() fdprintf(2, "%*s", (G.debug_indent * 2) & 0xff, "")
1326# define debug_enter() (G.debug_indent++)
1327# define debug_leave() (G.debug_indent--)
1328#else
1329# define indent() ((void)0)
1330# define debug_enter() ((void)0)
1331# define debug_leave() ((void)0)
1332#endif
1333
1334#ifndef debug_printf
1335# define debug_printf(...) (indent(), fdprintf(2, __VA_ARGS__))
1336#endif
1337
1338#ifndef debug_printf_parse
1339# define debug_printf_parse(...) (indent(), fdprintf(2, __VA_ARGS__))
1340#endif
1341
1342#ifndef debug_printf_heredoc
1343# define debug_printf_heredoc(...) (indent(), fdprintf(2, __VA_ARGS__))
1344#endif
1345
1346#ifndef debug_printf_exec
1347#define debug_printf_exec(...) (indent(), fdprintf(2, __VA_ARGS__))
1348#endif
1349
1350#ifndef debug_printf_env
1351# define debug_printf_env(...) (indent(), fdprintf(2, __VA_ARGS__))
1352#endif
1353
1354#ifndef debug_printf_jobs
1355# define debug_printf_jobs(...) (indent(), fdprintf(2, __VA_ARGS__))
1356# define DEBUG_JOBS 1
1357#else
1358# define DEBUG_JOBS 0
1359#endif
1360
1361#ifndef debug_printf_expand
1362# define debug_printf_expand(...) (indent(), fdprintf(2, __VA_ARGS__))
1363# define DEBUG_EXPAND 1
1364#else
1365# define DEBUG_EXPAND 0
1366#endif
1367
1368#ifndef debug_printf_varexp
1369# define debug_printf_varexp(...) (indent(), fdprintf(2, __VA_ARGS__))
1370#endif
1371
1372#ifndef debug_printf_glob
1373# define debug_printf_glob(...) (indent(), fdprintf(2, __VA_ARGS__))
1374# define DEBUG_GLOB 1
1375#else
1376# define DEBUG_GLOB 0
1377#endif
1378
1379#ifndef debug_printf_redir
1380# define debug_printf_redir(...) (indent(), fdprintf(2, __VA_ARGS__))
1381#endif
1382
1383#ifndef debug_printf_list
1384# define debug_printf_list(...) (indent(), fdprintf(2, __VA_ARGS__))
1385#endif
1386
1387#ifndef debug_printf_subst
1388# define debug_printf_subst(...) (indent(), fdprintf(2, __VA_ARGS__))
1389#endif
1390
1391#ifndef debug_printf_prompt
1392# define debug_printf_prompt(...) (indent(), fdprintf(2, __VA_ARGS__))
1393#endif
1394
1395#ifndef debug_printf_clean
1396# define debug_printf_clean(...) (indent(), fdprintf(2, __VA_ARGS__))
1397# define DEBUG_CLEAN 1
1398#else
1399# define DEBUG_CLEAN 0
1400#endif
1401
1402#if DEBUG_EXPAND
1403static void debug_print_strings(const char *prefix, char **vv)
1404{
1405 indent();
1406 fdprintf(2, "%s:\n", prefix);
1407 while (*vv)
1408 fdprintf(2, " '%s'\n", *vv++);
1409}
1410#else
1411# define debug_print_strings(prefix, vv) ((void)0)
1412#endif
1413
Francis Laniel110b7692023-12-22 22:02:27 +01001414/* Leak hunting. Use hush_leaktool.sh for post-processing.
1415 */
1416#if LEAK_HUNTING
1417static void *xxmalloc(int lineno, size_t size)
1418{
1419 void *ptr = xmalloc((size + 0xff) & ~0xff);
1420 fdprintf(2, "line %d: malloc %p\n", lineno, ptr);
1421 return ptr;
1422}
1423static void *xxrealloc(int lineno, void *ptr, size_t size)
1424{
1425 ptr = xrealloc(ptr, (size + 0xff) & ~0xff);
1426 fdprintf(2, "line %d: realloc %p\n", lineno, ptr);
1427 return ptr;
1428}
1429static char *xxstrdup(int lineno, const char *str)
1430{
1431 char *ptr = xstrdup(str);
1432 fdprintf(2, "line %d: strdup %p\n", lineno, ptr);
1433 return ptr;
1434}
1435static void xxfree(void *ptr)
1436{
1437 fdprintf(2, "free %p\n", ptr);
1438 free(ptr);
1439}
1440# define xmalloc(s) xxmalloc(__LINE__, s)
1441# define xrealloc(p, s) xxrealloc(__LINE__, p, s)
1442# define xstrdup(s) xxstrdup(__LINE__, s)
1443# define free(p) xxfree(p)
1444#endif
1445
Francis Laniel110b7692023-12-22 22:02:27 +01001446/* Syntax and runtime errors. They always abort scripts.
1447 * In interactive use they usually discard unparsed and/or unexecuted commands
1448 * and return to the prompt.
1449 * HUSH_DEBUG >= 2 prints line number in this file where it was detected.
1450 */
1451#if HUSH_DEBUG < 2
1452# define msg_and_die_if_script(lineno, ...) msg_and_die_if_script(__VA_ARGS__)
1453# define syntax_error(lineno, msg) syntax_error(msg)
1454# define syntax_error_at(lineno, msg) syntax_error_at(msg)
1455# define syntax_error_unterm_ch(lineno, ch) syntax_error_unterm_ch(ch)
1456# define syntax_error_unterm_str(lineno, s) syntax_error_unterm_str(s)
1457# define syntax_error_unexpected_ch(lineno, ch) syntax_error_unexpected_ch(ch)
1458#endif
1459
1460static void die_if_script(void)
1461{
1462 if (!G_interactive_fd) {
1463 if (G.last_exitcode) /* sometines it's 2, not 1 (bash compat) */
1464 xfunc_error_retval = G.last_exitcode;
1465 xfunc_die();
1466 }
1467}
1468
Francis Laniel36836fc2023-12-22 22:02:28 +01001469#ifdef __U_BOOT__
1470static void __maybe_unused msg_and_die_if_script(unsigned lineno, const char *fmt, ...)
1471#else /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01001472static void msg_and_die_if_script(unsigned lineno, const char *fmt, ...)
Francis Laniel36836fc2023-12-22 22:02:28 +01001473#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01001474{
1475 va_list p;
1476
1477#if HUSH_DEBUG >= 2
1478 bb_error_msg("hush.c:%u", lineno);
1479#endif
1480 va_start(p, fmt);
1481 bb_verror_msg(fmt, p, NULL);
1482 va_end(p);
1483 die_if_script();
1484}
1485
1486static void syntax_error(unsigned lineno UNUSED_PARAM, const char *msg)
1487{
1488 if (msg)
1489 bb_error_msg("syntax error: %s", msg);
1490 else
1491 bb_simple_error_msg("syntax error");
1492 die_if_script();
1493}
1494
1495static void syntax_error_at(unsigned lineno UNUSED_PARAM, const char *msg)
1496{
1497 bb_error_msg("syntax error at '%s'", msg);
1498 die_if_script();
1499}
1500
1501static void syntax_error_unterm_str(unsigned lineno UNUSED_PARAM, const char *s)
1502{
1503 bb_error_msg("syntax error: unterminated %s", s);
1504//? source4.tests fails: in bash, echo ${^} in script does not terminate the script
Francis Laniele7ca3a32023-12-22 22:02:42 +01001505// (but bash --posix, or if bash is run as "sh", does terminate in script, so maybe uncomment this?)
Francis Laniel110b7692023-12-22 22:02:27 +01001506// die_if_script();
1507}
1508
1509static void syntax_error_unterm_ch(unsigned lineno, char ch)
1510{
1511 char msg[2] = { ch, '\0' };
1512 syntax_error_unterm_str(lineno, msg);
1513}
1514
1515static void syntax_error_unexpected_ch(unsigned lineno UNUSED_PARAM, int ch)
1516{
1517 char msg[2];
1518 msg[0] = ch;
1519 msg[1] = '\0';
1520#if HUSH_DEBUG >= 2
1521 bb_error_msg("hush.c:%u", lineno);
1522#endif
1523 bb_error_msg("syntax error: unexpected %s", ch == EOF ? "EOF" : msg);
1524 die_if_script();
1525}
1526
1527#if HUSH_DEBUG < 2
1528# undef msg_and_die_if_script
1529# undef syntax_error
1530# undef syntax_error_at
1531# undef syntax_error_unterm_ch
1532# undef syntax_error_unterm_str
1533# undef syntax_error_unexpected_ch
1534#else
1535# define msg_and_die_if_script(...) msg_and_die_if_script(__LINE__, __VA_ARGS__)
1536# define syntax_error(msg) syntax_error(__LINE__, msg)
1537# define syntax_error_at(msg) syntax_error_at(__LINE__, msg)
1538# define syntax_error_unterm_ch(ch) syntax_error_unterm_ch(__LINE__, ch)
1539# define syntax_error_unterm_str(s) syntax_error_unterm_str(__LINE__, s)
1540# define syntax_error_unexpected_ch(ch) syntax_error_unexpected_ch(__LINE__, ch)
1541#endif
1542
Francis Laniel110b7692023-12-22 22:02:27 +01001543/* Utility functions
1544 */
1545/* Replace each \x with x in place, return ptr past NUL. */
1546static char *unbackslash(char *src)
1547{
Francis Laniel36836fc2023-12-22 22:02:28 +01001548#ifdef __U_BOOT__
1549 char *dst = src = (char *)strchrnul(src, '\\');
1550#else /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01001551 char *dst = src = strchrnul(src, '\\');
Francis Laniel36836fc2023-12-22 22:02:28 +01001552#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01001553 while (1) {
1554 if (*src == '\\') {
1555 src++;
1556 if (*src != '\0') {
1557 /* \x -> x */
1558 *dst++ = *src++;
1559 continue;
1560 }
1561 /* else: "\<nul>". Do not delete this backslash.
1562 * Testcase: eval 'echo ok\'
1563 */
1564 *dst++ = '\\';
1565 /* fallthrough */
1566 }
1567 if ((*dst++ = *src++) == '\0')
1568 break;
1569 }
1570 return dst;
1571}
1572
1573static char **add_strings_to_strings(char **strings, char **add, int need_to_dup)
1574{
1575 int i;
1576 unsigned count1;
1577 unsigned count2;
1578 char **v;
1579
1580 v = strings;
1581 count1 = 0;
1582 if (v) {
1583 while (*v) {
1584 count1++;
1585 v++;
1586 }
1587 }
1588 count2 = 0;
1589 v = add;
1590 while (*v) {
1591 count2++;
1592 v++;
1593 }
1594 v = xrealloc(strings, (count1 + count2 + 1) * sizeof(char*));
1595 v[count1 + count2] = NULL;
1596 i = count2;
1597 while (--i >= 0)
1598 v[count1 + i] = (need_to_dup ? xstrdup(add[i]) : add[i]);
1599 return v;
1600}
1601#if LEAK_HUNTING
1602static char **xx_add_strings_to_strings(int lineno, char **strings, char **add, int need_to_dup)
1603{
1604 char **ptr = add_strings_to_strings(strings, add, need_to_dup);
1605 fdprintf(2, "line %d: add_strings_to_strings %p\n", lineno, ptr);
1606 return ptr;
1607}
1608#define add_strings_to_strings(strings, add, need_to_dup) \
1609 xx_add_strings_to_strings(__LINE__, strings, add, need_to_dup)
1610#endif
1611
1612/* Note: takes ownership of "add" ptr (it is not strdup'ed) */
1613static char **add_string_to_strings(char **strings, char *add)
1614{
1615 char *v[2];
1616 v[0] = add;
1617 v[1] = NULL;
1618 return add_strings_to_strings(strings, v, /*dup:*/ 0);
1619}
Francis Laniel36836fc2023-12-22 22:02:28 +01001620
Francis Laniel110b7692023-12-22 22:02:27 +01001621#if LEAK_HUNTING
1622static char **xx_add_string_to_strings(int lineno, char **strings, char *add)
1623{
1624 char **ptr = add_string_to_strings(strings, add);
1625 fdprintf(2, "line %d: add_string_to_strings %p\n", lineno, ptr);
1626 return ptr;
1627}
1628#define add_string_to_strings(strings, add) \
1629 xx_add_string_to_strings(__LINE__, strings, add)
1630#endif
1631
1632static void free_strings(char **strings)
1633{
1634 char **v;
1635
1636 if (!strings)
1637 return;
1638 v = strings;
1639 while (*v) {
1640 free(*v);
1641 v++;
1642 }
1643 free(strings);
1644}
1645
Francis Laniel36836fc2023-12-22 22:02:28 +01001646#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01001647static int dup_CLOEXEC(int fd, int avoid_fd)
1648{
1649 int newfd;
1650 repeat:
1651 newfd = fcntl(fd, F_DUPFD_CLOEXEC, avoid_fd + 1);
1652 if (newfd >= 0) {
1653 if (F_DUPFD_CLOEXEC == F_DUPFD) /* if old libc (w/o F_DUPFD_CLOEXEC) */
1654 fcntl(newfd, F_SETFD, FD_CLOEXEC);
1655 } else { /* newfd < 0 */
1656 if (errno == EBUSY)
1657 goto repeat;
1658 if (errno == EINTR)
1659 goto repeat;
1660 }
1661 return newfd;
1662}
1663
1664static int xdup_CLOEXEC_and_close(int fd, int avoid_fd)
1665{
1666 int newfd;
1667 repeat:
1668 newfd = fcntl(fd, F_DUPFD_CLOEXEC, avoid_fd + 1);
1669 if (newfd < 0) {
1670 if (errno == EBUSY)
1671 goto repeat;
1672 if (errno == EINTR)
1673 goto repeat;
1674 /* fd was not open? */
1675 if (errno == EBADF)
1676 return fd;
1677 xfunc_die();
1678 }
1679 if (F_DUPFD_CLOEXEC == F_DUPFD) /* if old libc (w/o F_DUPFD_CLOEXEC) */
1680 fcntl(newfd, F_SETFD, FD_CLOEXEC);
1681 close(fd);
1682 return newfd;
1683}
1684
Francis Laniel110b7692023-12-22 22:02:27 +01001685/* Manipulating HFILEs */
1686static HFILE *hfopen(const char *name)
1687{
1688 HFILE *fp;
1689 int fd;
1690
1691 fd = STDIN_FILENO;
1692 if (name) {
1693 fd = open(name, O_RDONLY | O_CLOEXEC);
1694 if (fd < 0)
1695 return NULL;
1696 if (O_CLOEXEC == 0) /* ancient libc */
1697 close_on_exec_on(fd);
1698 }
1699
1700 fp = xmalloc(sizeof(*fp));
1701 if (name == NULL)
1702 G.HFILE_stdin = fp;
1703 fp->fd = fd;
1704 fp->cur = fp->end = fp->buf;
1705 fp->next_hfile = G.HFILE_list;
1706 G.HFILE_list = fp;
1707 return fp;
1708}
1709static void hfclose(HFILE *fp)
1710{
1711 HFILE **pp = &G.HFILE_list;
1712 while (*pp) {
1713 HFILE *cur = *pp;
1714 if (cur == fp) {
1715 *pp = cur->next_hfile;
1716 break;
1717 }
1718 pp = &cur->next_hfile;
1719 }
1720 if (fp->fd >= 0)
1721 close(fp->fd);
1722 free(fp);
1723}
1724static int refill_HFILE_and_getc(HFILE *fp)
1725{
1726 int n;
1727
1728 if (fp->fd < 0) {
1729 /* Already saw EOF */
1730 return EOF;
1731 }
1732#if ENABLE_HUSH_INTERACTIVE && !ENABLE_FEATURE_EDITING
1733 /* If user presses ^C, read() restarts after SIGINT (we use SA_RESTART).
1734 * IOW: ^C will not immediately stop line input.
1735 * But poll() is different: it does NOT restart after signals.
1736 */
1737 if (fp == G.HFILE_stdin) {
1738 struct pollfd pfd[1];
1739 pfd[0].fd = fp->fd;
1740 pfd[0].events = POLLIN;
1741 n = poll(pfd, 1, -1);
1742 if (n < 0
1743 /*&& errno == EINTR - assumed true */
1744 && sigismember(&G.pending_set, SIGINT)
1745 ) {
1746 return '\0';
1747 }
1748 }
1749#else
1750/* if FEATURE_EDITING=y, we do not use this routine for interactive input */
1751#endif
1752 /* Try to buffer more input */
1753 n = safe_read(fp->fd, fp->buf, sizeof(fp->buf));
1754 if (n < 0) {
1755 bb_simple_perror_msg("read error");
1756 n = 0;
1757 }
1758 fp->cur = fp->buf;
1759 fp->end = fp->buf + n;
1760 if (n == 0) {
1761 /* EOF/error */
1762 close(fp->fd);
1763 fp->fd = -1;
1764 return EOF;
1765 }
1766 return (unsigned char)(*fp->cur++);
1767}
1768/* Inlined for common case of non-empty buffer.
1769 */
1770static ALWAYS_INLINE int hfgetc(HFILE *fp)
1771{
1772 if (fp->cur < fp->end)
1773 return (unsigned char)(*fp->cur++);
1774 /* Buffer empty */
1775 return refill_HFILE_and_getc(fp);
1776}
1777static int move_HFILEs_on_redirect(int fd, int avoid_fd)
1778{
1779 HFILE *fl = G.HFILE_list;
1780 while (fl) {
1781 if (fd == fl->fd) {
1782 /* We use it only on script files, they are all CLOEXEC */
1783 fl->fd = xdup_CLOEXEC_and_close(fd, avoid_fd);
1784 debug_printf_redir("redirect_fd %d: matches a script fd, moving it to %d\n", fd, fl->fd);
1785 return 1; /* "found and moved" */
1786 }
1787 fl = fl->next_hfile;
1788 }
1789#if ENABLE_HUSH_MODE_X
1790 if (G.x_mode_fd > 0 && fd == G.x_mode_fd) {
1791 G.x_mode_fd = xdup_CLOEXEC_and_close(fd, avoid_fd);
1792 return 1; /* "found and moved" */
1793 }
1794#endif
1795 return 0; /* "not in the list" */
1796}
1797#if ENABLE_FEATURE_SH_STANDALONE && BB_MMU
1798static void close_all_HFILE_list(void)
1799{
1800 HFILE *fl = G.HFILE_list;
1801 while (fl) {
1802 /* hfclose would also free HFILE object.
1803 * It is disastrous if we share memory with a vforked parent.
1804 * I'm not sure we never come here after vfork.
1805 * Therefore just close fd, nothing more.
1806 *
1807 * ">" instead of ">=": we don't close fd#0,
1808 * interactive shell uses hfopen(NULL) as stdin input
1809 * which has fl->fd == 0, but fd#0 gets redirected in pipes.
1810 * If we'd close it here, then e.g. interactive "set | sort"
1811 * with NOFORKed sort, would have sort's input fd closed.
1812 */
1813 if (fl->fd > 0)
1814 /*hfclose(fl); - unsafe */
1815 close(fl->fd);
1816 fl = fl->next_hfile;
1817 }
1818}
1819#endif
1820static int fd_in_HFILEs(int fd)
1821{
1822 HFILE *fl = G.HFILE_list;
1823 while (fl) {
1824 if (fl->fd == fd)
1825 return 1;
1826 fl = fl->next_hfile;
1827 }
1828 return 0;
1829}
1830
Francis Laniel36836fc2023-12-22 22:02:28 +01001831#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01001832
1833/* Helpers for setting new $n and restoring them back
1834 */
1835typedef struct save_arg_t {
1836 char *sv_argv0;
1837 char **sv_g_argv;
1838 int sv_g_argc;
Francis Laniel36836fc2023-12-22 22:02:28 +01001839#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01001840 IF_HUSH_SET(smallint sv_g_malloced;)
Francis Laniel36836fc2023-12-22 22:02:28 +01001841#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01001842} save_arg_t;
1843
Francis Laniel36836fc2023-12-22 22:02:28 +01001844#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01001845static void save_and_replace_G_args(save_arg_t *sv, char **argv)
1846{
1847 sv->sv_argv0 = argv[0];
1848 sv->sv_g_argv = G.global_argv;
1849 sv->sv_g_argc = G.global_argc;
1850 IF_HUSH_SET(sv->sv_g_malloced = G.global_args_malloced;)
1851
1852 argv[0] = G.global_argv[0]; /* retain $0 */
1853 G.global_argv = argv;
1854 IF_HUSH_SET(G.global_args_malloced = 0;)
1855
1856 G.global_argc = 1 + string_array_len(argv + 1);
1857}
1858
1859static void restore_G_args(save_arg_t *sv, char **argv)
1860{
1861#if ENABLE_HUSH_SET
1862 if (G.global_args_malloced) {
1863 /* someone ran "set -- arg1 arg2 ...", undo */
1864 char **pp = G.global_argv;
1865 while (*++pp) /* note: does not free $0 */
1866 free(*pp);
1867 free(G.global_argv);
1868 }
1869#endif
1870 argv[0] = sv->sv_argv0;
1871 G.global_argv = sv->sv_g_argv;
1872 G.global_argc = sv->sv_g_argc;
1873 IF_HUSH_SET(G.global_args_malloced = sv->sv_g_malloced;)
1874}
Francis Laniel36836fc2023-12-22 22:02:28 +01001875#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01001876
Francis Laniel36836fc2023-12-22 22:02:28 +01001877#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01001878/* Basic theory of signal handling in shell
1879 * ========================================
1880 * This does not describe what hush does, rather, it is current understanding
1881 * what it _should_ do. If it doesn't, it's a bug.
1882 * http://www.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#trap
1883 *
1884 * Signals are handled only after each pipe ("cmd | cmd | cmd" thing)
1885 * is finished or backgrounded. It is the same in interactive and
1886 * non-interactive shells, and is the same regardless of whether
1887 * a user trap handler is installed or a shell special one is in effect.
1888 * ^C or ^Z from keyboard seems to execute "at once" because it usually
1889 * backgrounds (i.e. stops) or kills all members of currently running
1890 * pipe.
1891 *
1892 * Wait builtin is interruptible by signals for which user trap is set
1893 * or by SIGINT in interactive shell.
1894 *
1895 * Trap handlers will execute even within trap handlers. (right?)
1896 *
1897 * User trap handlers are forgotten when subshell ("(cmd)") is entered,
1898 * except for handlers set to '' (empty string).
1899 *
1900 * If job control is off, backgrounded commands ("cmd &")
1901 * have SIGINT, SIGQUIT set to SIG_IGN.
1902 *
1903 * Commands which are run in command substitution ("`cmd`")
1904 * have SIGTTIN, SIGTTOU, SIGTSTP set to SIG_IGN.
1905 *
1906 * Ordinary commands have signals set to SIG_IGN/DFL as inherited
1907 * by the shell from its parent.
1908 *
1909 * Signals which differ from SIG_DFL action
1910 * (note: child (i.e., [v]forked) shell is not an interactive shell):
1911 *
1912 * SIGQUIT: ignore
1913 * SIGTERM (interactive): ignore
1914 * SIGHUP (interactive):
Francis Laniele7ca3a32023-12-22 22:02:42 +01001915 * Send SIGCONT to stopped jobs, send SIGHUP to all jobs and exit.
1916 * Kernel would do this for us ("orphaned process group" handling
1917 * according to POSIX) if we are a session leader and thus our death
1918 * frees the controlling tty, but to be bash-compatible, we also do it
1919 * for every interactive shell's death by SIGHUP.
1920 * (Also, we need to restore tty pgrp, otherwise e.g. Midnight Commander
1921 * backgrounds when hush started from it gets killed by SIGHUP).
Francis Laniel110b7692023-12-22 22:02:27 +01001922 * SIGTTIN, SIGTTOU, SIGTSTP (if job control is on): ignore
1923 * Note that ^Z is handled not by trapping SIGTSTP, but by seeing
1924 * that all pipe members are stopped. Try this in bash:
1925 * while :; do :; done - ^Z does not background it
1926 * (while :; do :; done) - ^Z backgrounds it
1927 * SIGINT (interactive): wait for last pipe, ignore the rest
1928 * of the command line, show prompt. NB: ^C does not send SIGINT
1929 * to interactive shell while shell is waiting for a pipe,
1930 * since shell is bg'ed (is not in foreground process group).
1931 * Example 1: this waits 5 sec, but does not execute ls:
1932 * "echo $$; sleep 5; ls -l" + "kill -INT <pid>"
1933 * Example 2: this does not wait and does not execute ls:
1934 * "echo $$; sleep 5 & wait; ls -l" + "kill -INT <pid>"
1935 * Example 3: this does not wait 5 sec, but executes ls:
1936 * "sleep 5; ls -l" + press ^C
1937 * Example 4: this does not wait and does not execute ls:
1938 * "sleep 5 & wait; ls -l" + press ^C
1939 *
1940 * (What happens to signals which are IGN on shell start?)
1941 * (What happens with signal mask on shell start?)
1942 *
1943 * Old implementation
1944 * ==================
1945 * We use in-kernel pending signal mask to determine which signals were sent.
1946 * We block all signals which we don't want to take action immediately,
1947 * i.e. we block all signals which need to have special handling as described
1948 * above, and all signals which have traps set.
1949 * After each pipe execution, we extract any pending signals via sigtimedwait()
1950 * and act on them.
1951 *
1952 * unsigned special_sig_mask: a mask of such "special" signals
1953 * sigset_t blocked_set: current blocked signal set
1954 *
1955 * "trap - SIGxxx":
1956 * clear bit in blocked_set unless it is also in special_sig_mask
1957 * "trap 'cmd' SIGxxx":
1958 * set bit in blocked_set (even if 'cmd' is '')
1959 * after [v]fork, if we plan to be a shell:
1960 * unblock signals with special interactive handling
1961 * (child shell is not interactive),
1962 * unset all traps except '' (note: regardless of child shell's type - {}, (), etc)
1963 * after [v]fork, if we plan to exec:
1964 * POSIX says fork clears pending signal mask in child - no need to clear it.
1965 * Restore blocked signal set to one inherited by shell just prior to exec.
1966 *
1967 * Note: as a result, we do not use signal handlers much. The only uses
1968 * are to count SIGCHLDs
1969 * and to restore tty pgrp on signal-induced exit.
1970 *
1971 * Note 2 (compat):
1972 * Standard says "When a subshell is entered, traps that are not being ignored
1973 * are set to the default actions". bash interprets it so that traps which
1974 * are set to '' (ignore) are NOT reset to defaults. We do the same.
1975 *
1976 * Problem: the above approach makes it unwieldy to catch signals while
1977 * we are in read builtin, or while we read commands from stdin:
1978 * masked signals are not visible!
1979 *
1980 * New implementation
1981 * ==================
1982 * We record each signal we are interested in by installing signal handler
1983 * for them - a bit like emulating kernel pending signal mask in userspace.
1984 * We are interested in: signals which need to have special handling
1985 * as described above, and all signals which have traps set.
1986 * Signals are recorded in pending_set.
1987 * After each pipe execution, we extract any pending signals
1988 * and act on them.
1989 *
1990 * unsigned special_sig_mask: a mask of shell-special signals.
1991 * unsigned fatal_sig_mask: a mask of signals on which we restore tty pgrp.
1992 * char *traps[sig] if trap for sig is set (even if it's '').
1993 * sigset_t pending_set: set of sigs we received.
1994 *
1995 * "trap - SIGxxx":
1996 * if sig is in special_sig_mask, set handler back to:
1997 * record_pending_signo, or to IGN if it's a tty stop signal
1998 * if sig is in fatal_sig_mask, set handler back to sigexit.
1999 * else: set handler back to SIG_DFL
2000 * "trap 'cmd' SIGxxx":
2001 * set handler to record_pending_signo.
2002 * "trap '' SIGxxx":
2003 * set handler to SIG_IGN.
2004 * after [v]fork, if we plan to be a shell:
2005 * set signals with special interactive handling to SIG_DFL
2006 * (because child shell is not interactive),
2007 * unset all traps except '' (note: regardless of child shell's type - {}, (), etc)
2008 * after [v]fork, if we plan to exec:
2009 * POSIX says fork clears pending signal mask in child - no need to clear it.
2010 *
2011 * To make wait builtin interruptible, we handle SIGCHLD as special signal,
2012 * otherwise (if we leave it SIG_DFL) sigsuspend in wait builtin will not wake up on it.
2013 *
2014 * Note (compat):
2015 * Standard says "When a subshell is entered, traps that are not being ignored
2016 * are set to the default actions". bash interprets it so that traps which
2017 * are set to '' (ignore) are NOT reset to defaults. We do the same.
2018 */
2019enum {
2020 SPECIAL_INTERACTIVE_SIGS = 0
2021 | (1 << SIGTERM)
2022 | (1 << SIGINT)
2023 | (1 << SIGHUP)
2024 ,
2025 SPECIAL_JOBSTOP_SIGS = 0
2026#if ENABLE_HUSH_JOB
2027 | (1 << SIGTTIN)
2028 | (1 << SIGTTOU)
2029 | (1 << SIGTSTP)
2030#endif
2031 ,
2032};
2033
2034static void record_pending_signo(int sig)
2035{
2036 sigaddset(&G.pending_set, sig);
Francis Laniele7ca3a32023-12-22 22:02:42 +01002037#if ENABLE_FEATURE_EDITING
2038 if (sig != SIGCHLD
2039 || (G_traps && G_traps[SIGCHLD] && G_traps[SIGCHLD][0])
2040 /* ^^^ if SIGCHLD, interrupt line reading only if it has a trap */
2041 ) {
2042 bb_got_signal = sig; /* for read_line_input: "we got a signal" */
2043 }
2044#endif
Francis Laniel110b7692023-12-22 22:02:27 +01002045#if ENABLE_HUSH_FAST
2046 if (sig == SIGCHLD) {
2047 G.count_SIGCHLD++;
2048//bb_error_msg("[%d] SIGCHLD_handler: G.count_SIGCHLD:%d G.handled_SIGCHLD:%d", getpid(), G.count_SIGCHLD, G.handled_SIGCHLD);
2049 }
2050#endif
2051}
2052
2053static sighandler_t install_sighandler(int sig, sighandler_t handler)
2054{
2055 struct sigaction old_sa;
2056
2057 /* We could use signal() to install handlers... almost:
2058 * except that we need to mask ALL signals while handlers run.
2059 * I saw signal nesting in strace, race window isn't small.
2060 * SA_RESTART is also needed, but in Linux, signal()
2061 * sets SA_RESTART too.
2062 */
2063 /* memset(&G.sa, 0, sizeof(G.sa)); - already done */
2064 /* sigfillset(&G.sa.sa_mask); - already done */
2065 /* G.sa.sa_flags = SA_RESTART; - already done */
2066 G.sa.sa_handler = handler;
2067 sigaction(sig, &G.sa, &old_sa);
2068 return old_sa.sa_handler;
2069}
Francis Laniel36836fc2023-12-22 22:02:28 +01002070#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01002071
Francis Laniel36836fc2023-12-22 22:02:28 +01002072#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01002073static void hush_exit(int exitcode) NORETURN;
2074
2075static void restore_ttypgrp_and__exit(void) NORETURN;
2076static void restore_ttypgrp_and__exit(void)
2077{
2078 /* xfunc has failed! die die die */
2079 /* no EXIT traps, this is an escape hatch! */
2080 G.exiting = 1;
2081 hush_exit(xfunc_error_retval);
2082}
2083
2084#if ENABLE_HUSH_JOB
2085
2086/* Needed only on some libc:
2087 * It was observed that on exit(), fgetc'ed buffered data
2088 * gets "unwound" via lseek(fd, -NUM, SEEK_CUR).
2089 * With the net effect that even after fork(), not vfork(),
2090 * exit() in NOEXECed applet in "sh SCRIPT":
2091 * noexec_applet_here
2092 * echo END_OF_SCRIPT
2093 * lseeks fd in input FILE object from EOF to "e" in "echo END_OF_SCRIPT".
2094 * This makes "echo END_OF_SCRIPT" executed twice.
2095 * Similar problems can be seen with msg_and_die_if_script() -> xfunc_die()
2096 * and in `cmd` handling.
2097 * If set as die_func(), this makes xfunc_die() exit via _exit(), not exit():
2098 */
2099static void fflush_and__exit(void) NORETURN;
2100static void fflush_and__exit(void)
2101{
2102 fflush_all();
2103 _exit(xfunc_error_retval);
2104}
2105
2106/* After [v]fork, in child: do not restore tty pgrp on xfunc death */
2107# define disable_restore_tty_pgrp_on_exit() (die_func = fflush_and__exit)
2108/* After [v]fork, in parent: restore tty pgrp on xfunc death */
2109# define enable_restore_tty_pgrp_on_exit() (die_func = restore_ttypgrp_and__exit)
2110
2111/* Restores tty foreground process group, and exits.
2112 * May be called as signal handler for fatal signal
2113 * (will resend signal to itself, producing correct exit state)
2114 * or called directly with -EXITCODE.
2115 * We also call it if xfunc is exiting.
2116 */
2117static void sigexit(int sig) NORETURN;
2118static void sigexit(int sig)
2119{
2120 /* Careful: we can end up here after [v]fork. Do not restore
2121 * tty pgrp then, only top-level shell process does that */
2122 if (G_saved_tty_pgrp && getpid() == G.root_pid) {
2123 /* Disable all signals: job control, SIGPIPE, etc.
2124 * Mostly paranoid measure, to prevent infinite SIGTTOU.
2125 */
2126 sigprocmask_allsigs(SIG_BLOCK);
2127 tcsetpgrp(G_interactive_fd, G_saved_tty_pgrp);
2128 }
2129
2130 /* Not a signal, just exit */
2131 if (sig <= 0)
2132 _exit(- sig);
2133
2134 kill_myself_with_sig(sig); /* does not return */
2135}
2136#else
2137
2138# define disable_restore_tty_pgrp_on_exit() ((void)0)
2139# define enable_restore_tty_pgrp_on_exit() ((void)0)
2140
2141#endif
Francis Laniel36836fc2023-12-22 22:02:28 +01002142#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01002143
Francis Laniel36836fc2023-12-22 22:02:28 +01002144#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01002145static sighandler_t pick_sighandler(unsigned sig)
2146{
2147 sighandler_t handler = SIG_DFL;
2148 if (sig < sizeof(unsigned)*8) {
2149 unsigned sigmask = (1 << sig);
2150
2151#if ENABLE_HUSH_JOB
2152 /* is sig fatal? */
2153 if (G_fatal_sig_mask & sigmask)
2154 handler = sigexit;
2155 else
2156#endif
2157 /* sig has special handling? */
2158 if (G.special_sig_mask & sigmask) {
2159 handler = record_pending_signo;
2160 /* TTIN/TTOU/TSTP can't be set to record_pending_signo
2161 * in order to ignore them: they will be raised
2162 * in an endless loop when we try to do some
2163 * terminal ioctls! We do have to _ignore_ these.
2164 */
2165 if (SPECIAL_JOBSTOP_SIGS & sigmask)
2166 handler = SIG_IGN;
2167 }
2168 }
2169 return handler;
2170}
Francis Laniel36836fc2023-12-22 22:02:28 +01002171#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01002172
Francis Laniel36836fc2023-12-22 22:02:28 +01002173#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01002174/* Restores tty foreground process group, and exits. */
2175static void hush_exit(int exitcode)
2176{
2177#if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT
2178 save_history(G.line_input_state); /* may be NULL */
2179#endif
2180
2181 fflush_all();
2182 if (G.exiting <= 0 && G_traps && G_traps[0] && G_traps[0][0]) {
2183 char *argv[3];
2184 /* argv[0] is unused */
2185 argv[1] = xstrdup(G_traps[0]); /* copy, since EXIT trap handler may modify G_traps[0] */
2186 argv[2] = NULL;
2187 G.exiting = 1; /* prevent EXIT trap recursion */
2188 /* Note: G_traps[0] is not cleared!
2189 * "trap" will still show it, if executed
2190 * in the handler */
2191 builtin_eval(argv);
2192 }
2193
2194#if ENABLE_FEATURE_CLEAN_UP
2195 {
2196 struct variable *cur_var;
2197 if (G.cwd != bb_msg_unknown)
2198 free((char*)G.cwd);
2199 cur_var = G.top_var;
2200 while (cur_var) {
2201 struct variable *tmp = cur_var;
2202 if (!cur_var->max_len)
2203 free(cur_var->varstr);
2204 cur_var = cur_var->next;
2205 free(tmp);
2206 }
2207 }
2208#endif
2209
2210 fflush_all();
2211#if ENABLE_HUSH_JOB
2212 sigexit(- (exitcode & 0xff));
2213#else
2214 _exit(exitcode);
2215#endif
2216}
2217
2218//TODO: return a mask of ALL handled sigs?
2219static int check_and_run_traps(void)
2220{
2221 int last_sig = 0;
2222
2223 while (1) {
2224 int sig;
2225
2226 if (sigisemptyset(&G.pending_set))
2227 break;
2228 sig = 0;
2229 do {
2230 sig++;
2231 if (sigismember(&G.pending_set, sig)) {
2232 sigdelset(&G.pending_set, sig);
2233 goto got_sig;
2234 }
2235 } while (sig < NSIG);
2236 break;
2237 got_sig:
2238#if ENABLE_HUSH_TRAP
2239 if (G_traps && G_traps[sig]) {
2240 debug_printf_exec("%s: sig:%d handler:'%s'\n", __func__, sig, G.traps[sig]);
2241 if (G_traps[sig][0]) {
2242 /* We have user-defined handler */
2243 smalluint save_rcode;
2244 int save_pre;
2245 char *argv[3];
2246 /* argv[0] is unused */
2247 argv[1] = xstrdup(G_traps[sig]);
2248 /* why strdup? trap can modify itself: trap 'trap "echo oops" INT' INT */
2249 argv[2] = NULL;
2250 save_pre = G.pre_trap_exitcode;
2251 G.pre_trap_exitcode = save_rcode = G.last_exitcode;
2252 builtin_eval(argv);
2253 free(argv[1]);
2254 G.pre_trap_exitcode = save_pre;
2255 G.last_exitcode = save_rcode;
2256# if ENABLE_HUSH_FUNCTIONS
2257 if (G.return_exitcode >= 0) {
2258 debug_printf_exec("trap exitcode:%d\n", G.return_exitcode);
2259 G.last_exitcode = G.return_exitcode;
2260 }
2261# endif
2262 last_sig = sig;
2263 } /* else: "" trap, ignoring signal */
2264 continue;
2265 }
2266#endif
2267 /* not a trap: special action */
2268 switch (sig) {
2269 case SIGINT:
2270 debug_printf_exec("%s: sig:%d default SIGINT handler\n", __func__, sig);
2271 G.flag_SIGINT = 1;
2272 last_sig = sig;
2273 break;
2274#if ENABLE_HUSH_JOB
2275 case SIGHUP: {
Francis Laniele7ca3a32023-12-22 22:02:42 +01002276 /* if (G_interactive_fd) - no need to check, the handler
2277 * is only installed if we *are* interactive */
2278 {
2279 /* bash compat: "Before exiting, an interactive
2280 * shell resends the SIGHUP to all jobs, running
2281 * or stopped. Stopped jobs are sent SIGCONT
2282 * to ensure that they receive the SIGHUP."
2283 */
2284 struct pipe *job;
2285 debug_printf_exec("%s: sig:%d default SIGHUP handler\n", __func__, sig);
2286 /* bash is observed to signal whole process groups,
2287 * not individual processes */
2288 for (job = G.job_list; job; job = job->next) {
2289 if (job->pgrp <= 0)
2290 continue;
2291 debug_printf_exec("HUPing pgrp %d\n", job->pgrp);
2292 if (kill(- job->pgrp, SIGHUP) == 0)
2293 kill(- job->pgrp, SIGCONT);
2294 }
Francis Laniel110b7692023-12-22 22:02:27 +01002295 }
Francis Laniele7ca3a32023-12-22 22:02:42 +01002296 /* this restores tty pgrp, then kills us with SIGHUP */
Francis Laniel110b7692023-12-22 22:02:27 +01002297 sigexit(SIGHUP);
2298 }
2299#endif
2300#if ENABLE_HUSH_FAST
2301 case SIGCHLD:
2302 debug_printf_exec("%s: sig:%d default SIGCHLD handler\n", __func__, sig);
2303 G.count_SIGCHLD++;
2304//bb_error_msg("[%d] check_and_run_traps: G.count_SIGCHLD:%d G.handled_SIGCHLD:%d", getpid(), G.count_SIGCHLD, G.handled_SIGCHLD);
2305 /* Note:
2306 * We don't do 'last_sig = sig' here -> NOT returning this sig.
2307 * This simplifies wait builtin a bit.
2308 */
2309 break;
2310#endif
2311 default: /* ignored: */
2312 debug_printf_exec("%s: sig:%d default handling is to ignore\n", __func__, sig);
2313 /* SIGTERM, SIGQUIT, SIGTTIN, SIGTTOU, SIGTSTP */
2314 /* Note:
2315 * We don't do 'last_sig = sig' here -> NOT returning this sig.
2316 * Example: wait is not interrupted by TERM
2317 * in interactive shell, because TERM is ignored.
2318 */
2319 break;
2320 }
2321 }
2322 return last_sig;
2323}
2324
Francis Laniel110b7692023-12-22 22:02:27 +01002325static const char *get_cwd(int force)
2326{
2327 if (force || G.cwd == NULL) {
2328 /* xrealloc_getcwd_or_warn(arg) calls free(arg),
2329 * we must not try to free(bb_msg_unknown) */
2330 if (G.cwd == bb_msg_unknown)
2331 G.cwd = NULL;
2332 G.cwd = xrealloc_getcwd_or_warn((char *)G.cwd);
2333 if (!G.cwd)
2334 G.cwd = bb_msg_unknown;
2335 }
2336 return G.cwd;
2337}
2338
Francis Laniel36836fc2023-12-22 22:02:28 +01002339#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01002340
2341/*
2342 * Shell and environment variable support
2343 */
Francis Laniele7ca3a32023-12-22 22:02:42 +01002344static struct variable **get_ptr_to_local_var(const char *name)
Francis Laniel110b7692023-12-22 22:02:27 +01002345{
2346 struct variable **pp;
2347 struct variable *cur;
2348
2349 pp = &G.top_var;
2350 while ((cur = *pp) != NULL) {
Francis Laniele7ca3a32023-12-22 22:02:42 +01002351 if (varcmp(cur->varstr, name) == 0)
Francis Laniel110b7692023-12-22 22:02:27 +01002352 return pp;
2353 pp = &cur->next;
2354 }
2355 return NULL;
2356}
2357
2358static const char* FAST_FUNC get_local_var_value(const char *name)
2359{
2360 struct variable **vpp;
Francis Laniel110b7692023-12-22 22:02:27 +01002361
2362 if (G.expanded_assignments) {
2363 char **cpp = G.expanded_assignments;
2364 while (*cpp) {
2365 char *cp = *cpp;
Francis Laniele7ca3a32023-12-22 22:02:42 +01002366 if (varcmp(cp, name) == 0)
2367 return strchr(cp, '=') + 1;
Francis Laniel110b7692023-12-22 22:02:27 +01002368 cpp++;
2369 }
2370 }
2371
Francis Laniele7ca3a32023-12-22 22:02:42 +01002372 vpp = get_ptr_to_local_var(name);
Francis Laniel110b7692023-12-22 22:02:27 +01002373 if (vpp)
Francis Laniele7ca3a32023-12-22 22:02:42 +01002374 return strchr((*vpp)->varstr, '=') + 1;
Francis Laniel110b7692023-12-22 22:02:27 +01002375
Francis Laniel36836fc2023-12-22 22:02:28 +01002376#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01002377 if (strcmp(name, "PPID") == 0)
2378 return utoa(G.root_ppid);
Francis Laniel36836fc2023-12-22 22:02:28 +01002379#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01002380 // bash compat: UID? EUID?
2381#if ENABLE_HUSH_RANDOM_SUPPORT
2382 if (strcmp(name, "RANDOM") == 0)
2383 return utoa(next_random(&G.random_gen));
2384#endif
2385#if ENABLE_HUSH_LINENO_VAR
2386 if (strcmp(name, "LINENO") == 0)
2387 return utoa(G.execute_lineno);
2388#endif
2389#if BASH_EPOCH_VARS
2390 {
2391 const char *fmt = NULL;
2392 if (strcmp(name, "EPOCHSECONDS") == 0)
2393 fmt = "%llu";
2394 else if (strcmp(name, "EPOCHREALTIME") == 0)
2395 fmt = "%llu.%06u";
2396 if (fmt) {
2397 struct timeval tv;
2398 xgettimeofday(&tv);
2399 sprintf(G.epoch_buf, fmt, (unsigned long long)tv.tv_sec,
2400 (unsigned)tv.tv_usec);
2401 return G.epoch_buf;
2402 }
2403 }
2404#endif
2405 return NULL;
2406}
2407
Francis Laniel36836fc2023-12-22 22:02:28 +01002408#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01002409#if ENABLE_HUSH_GETOPTS
Francis Laniele7ca3a32023-12-22 22:02:42 +01002410static void handle_changed_special_names(const char *name)
Francis Laniel110b7692023-12-22 22:02:27 +01002411{
Francis Laniele7ca3a32023-12-22 22:02:42 +01002412 if (varcmp(name, "OPTIND") == 0) {
2413 G.getopt_count = 0;
2414 return;
Francis Laniel110b7692023-12-22 22:02:27 +01002415 }
2416}
2417#else
2418/* Do not even bother evaluating arguments */
2419# define handle_changed_special_names(...) ((void)0)
2420#endif
Francis Laniel36836fc2023-12-22 22:02:28 +01002421#else /* __U_BOOT__ */
2422/* Do not even bother evaluating arguments */
2423# define handle_changed_special_names(...) ((void)0)
2424#endif /* __U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01002425
2426/* str holds "NAME=VAL" and is expected to be malloced.
2427 * We take ownership of it.
2428 */
Francis Laniel36836fc2023-12-22 22:02:28 +01002429#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01002430#define SETFLAG_EXPORT (1 << 0)
2431#define SETFLAG_UNEXPORT (1 << 1)
2432#define SETFLAG_MAKE_RO (1 << 2)
Francis Laniel36836fc2023-12-22 22:02:28 +01002433#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01002434#define SETFLAG_VARLVL_SHIFT 3
Francis Laniel36836fc2023-12-22 22:02:28 +01002435#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01002436static int set_local_var(char *str, unsigned flags)
Francis Laniel36836fc2023-12-22 22:02:28 +01002437#else /* __U_BOOT__ */
2438int set_local_var_modern(char *str, int flags)
2439#endif /* __U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01002440{
2441 struct variable **cur_pp;
2442 struct variable *cur;
2443 char *free_me = NULL;
2444 char *eq_sign;
2445 int name_len;
2446 int retval;
Francis Laniel36836fc2023-12-22 22:02:28 +01002447#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01002448 unsigned local_lvl = (flags >> SETFLAG_VARLVL_SHIFT);
Francis Laniel36836fc2023-12-22 22:02:28 +01002449#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01002450
2451 eq_sign = strchr(str, '=');
2452 if (HUSH_DEBUG && !eq_sign)
2453 bb_simple_error_msg_and_die("BUG in setvar");
2454
2455 name_len = eq_sign - str + 1; /* including '=' */
2456 cur_pp = &G.top_var;
2457 while ((cur = *cur_pp) != NULL) {
2458 if (strncmp(cur->varstr, str, name_len) != 0) {
2459 cur_pp = &cur->next;
2460 continue;
2461 }
2462
Francis Laniel36836fc2023-12-22 22:02:28 +01002463#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01002464 /* We found an existing var with this name */
2465 if (cur->flg_read_only) {
2466 bb_error_msg("%s: readonly variable", str);
2467 free(str);
2468//NOTE: in bash, assignment in "export READONLY_VAR=Z" fails, and sets $?=1,
2469//but export per se succeeds (does put the var in env). We don't mimic that.
2470 return -1;
2471 }
2472 if (flags & SETFLAG_UNEXPORT) { // && cur->flg_export ?
2473 debug_printf_env("%s: unsetenv '%s'\n", __func__, str);
2474 *eq_sign = '\0';
2475 unsetenv(str);
2476 *eq_sign = '=';
2477 }
2478 if (cur->var_nest_level < local_lvl) {
2479 /* bash 3.2.33(1) and exported vars:
2480 * # export z=z
2481 * # f() { local z=a; env | grep ^z; }
2482 * # f
2483 * z=a
2484 * # env | grep ^z
2485 * z=z
2486 */
2487 if (cur->flg_export)
2488 flags |= SETFLAG_EXPORT;
2489 /* New variable is local ("local VAR=VAL" or
2490 * "VAR=VAL cmd")
2491 * and existing one is global, or local
2492 * on a lower level that new one.
2493 * Remove it from global variable list:
2494 */
2495 *cur_pp = cur->next;
2496 if (G.shadowed_vars_pp) {
2497 /* Save in "shadowed" list */
2498 debug_printf_env("shadowing %s'%s'/%u by '%s'/%u\n",
2499 cur->flg_export ? "exported " : "",
2500 cur->varstr, cur->var_nest_level, str, local_lvl
2501 );
2502 cur->next = *G.shadowed_vars_pp;
2503 *G.shadowed_vars_pp = cur;
2504 } else {
2505 /* Came from pseudo_exec_argv(), no need to save: delete it */
2506 debug_printf_env("shadow-deleting %s'%s'/%u by '%s'/%u\n",
2507 cur->flg_export ? "exported " : "",
2508 cur->varstr, cur->var_nest_level, str, local_lvl
2509 );
2510 if (cur->max_len == 0) /* allocated "VAR=VAL"? */
2511 free_me = cur->varstr; /* then free it later */
2512 free(cur);
2513 }
2514 break;
2515 }
Francis Laniel36836fc2023-12-22 22:02:28 +01002516#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01002517
2518 if (strcmp(cur->varstr + name_len, eq_sign + 1) == 0) {
2519 debug_printf_env("assignement '%s' does not change anything\n", str);
2520 free_and_exp:
2521 free(str);
2522 goto exp;
2523 }
2524
2525 /* Replace the value in the found "struct variable" */
2526 if (cur->max_len != 0) {
2527 if (cur->max_len >= strnlen(str, cur->max_len + 1)) {
2528 /* This one is from startup env, reuse space */
2529 debug_printf_env("reusing startup env for '%s'\n", str);
2530 strcpy(cur->varstr, str);
2531 goto free_and_exp;
2532 }
2533 /* Can't reuse */
2534 cur->max_len = 0;
2535 goto set_str_and_exp;
2536 }
2537 /* max_len == 0 signifies "malloced" var, which we can
2538 * (and have to) free. But we can't free(cur->varstr) here:
2539 * if cur->flg_export is 1, it is in the environment.
2540 * We should either unsetenv+free, or wait until putenv,
2541 * then putenv(new)+free(old).
2542 */
2543 free_me = cur->varstr;
2544 goto set_str_and_exp;
2545 }
2546
2547 /* Not found or shadowed - create new variable struct */
Francis Laniel36836fc2023-12-22 22:02:28 +01002548#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01002549 debug_printf_env("%s: alloc new var '%s'/%u\n", __func__, str, local_lvl);
Francis Laniel36836fc2023-12-22 22:02:28 +01002550#else /* __U_BOOT__ */
2551 debug_printf_env("%s: alloc new var '%s'\n", __func__, str);
2552#endif /* __U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01002553 cur = xzalloc(sizeof(*cur));
Francis Laniel36836fc2023-12-22 22:02:28 +01002554#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01002555 cur->var_nest_level = local_lvl;
Francis Laniel36836fc2023-12-22 22:02:28 +01002556#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01002557 cur->next = *cur_pp;
2558 *cur_pp = cur;
2559
2560 set_str_and_exp:
2561 cur->varstr = str;
2562 exp:
Francis Laniel36836fc2023-12-22 22:02:28 +01002563#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01002564#if !BB_MMU || ENABLE_HUSH_READONLY
2565 if (flags & SETFLAG_MAKE_RO) {
2566 cur->flg_read_only = 1;
2567 }
2568#endif
2569 if (flags & SETFLAG_EXPORT)
2570 cur->flg_export = 1;
Francis Laniel36836fc2023-12-22 22:02:28 +01002571#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01002572 retval = 0;
Francis Laniel36836fc2023-12-22 22:02:28 +01002573#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01002574 if (cur->flg_export) {
2575 if (flags & SETFLAG_UNEXPORT) {
2576 cur->flg_export = 0;
2577 /* unsetenv was already done */
2578 } else {
2579 debug_printf_env("%s: putenv '%s'/%u\n", __func__, cur->varstr, cur->var_nest_level);
2580 retval = putenv(cur->varstr);
2581 /* fall through to "free(free_me)" -
2582 * only now we can free old exported malloced string
2583 */
2584 }
2585 }
Francis Laniel36836fc2023-12-22 22:02:28 +01002586#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01002587 free(free_me);
2588
Francis Laniele7ca3a32023-12-22 22:02:42 +01002589 handle_changed_special_names(cur->varstr);
Francis Laniel110b7692023-12-22 22:02:27 +01002590
2591 return retval;
2592}
2593
Francis Laniel36836fc2023-12-22 22:02:28 +01002594#ifndef __U_BOOT__
Francis Laniele7ca3a32023-12-22 22:02:42 +01002595static int set_local_var0(char *str)
2596{
2597 return set_local_var(str, 0);
2598}
2599
Francis Laniel110b7692023-12-22 22:02:27 +01002600static void FAST_FUNC set_local_var_from_halves(const char *name, const char *val)
2601{
2602 char *var = xasprintf("%s=%s", name, val);
Francis Laniele7ca3a32023-12-22 22:02:42 +01002603 set_local_var0(var);
Francis Laniel110b7692023-12-22 22:02:27 +01002604}
2605
2606/* Used at startup and after each cd */
2607static void set_pwd_var(unsigned flag)
2608{
2609 set_local_var(xasprintf("PWD=%s", get_cwd(/*force:*/ 1)), flag);
2610}
Francis Laniel36836fc2023-12-22 22:02:28 +01002611#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01002612
2613#if ENABLE_HUSH_UNSET || ENABLE_HUSH_GETOPTS
Francis Laniele7ca3a32023-12-22 22:02:42 +01002614static int unset_local_var(const char *name)
Francis Laniel110b7692023-12-22 22:02:27 +01002615{
2616 struct variable *cur;
2617 struct variable **cur_pp;
2618
2619 cur_pp = &G.top_var;
2620 while ((cur = *cur_pp) != NULL) {
Francis Laniele7ca3a32023-12-22 22:02:42 +01002621 if (varcmp(cur->varstr, name) == 0) {
Francis Laniel110b7692023-12-22 22:02:27 +01002622 if (cur->flg_read_only) {
2623 bb_error_msg("%s: readonly variable", name);
2624 return EXIT_FAILURE;
2625 }
2626
2627 *cur_pp = cur->next;
2628 debug_printf_env("%s: unsetenv '%s'\n", __func__, cur->varstr);
2629 bb_unsetenv(cur->varstr);
2630 if (!cur->max_len)
2631 free(cur->varstr);
2632 free(cur);
2633
2634 break;
2635 }
2636 cur_pp = &cur->next;
2637 }
2638
2639 /* Handle "unset LINENO" et al even if did not find the variable to unset */
Francis Laniele7ca3a32023-12-22 22:02:42 +01002640 handle_changed_special_names(name);
Francis Laniel110b7692023-12-22 22:02:27 +01002641
2642 return EXIT_SUCCESS;
2643}
Francis Laniel110b7692023-12-22 22:02:27 +01002644#endif
2645
Francis Laniel36836fc2023-12-22 22:02:28 +01002646#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01002647/*
2648 * Helpers for "var1=val1 var2=val2 cmd" feature
2649 */
2650static void add_vars(struct variable *var)
2651{
2652 struct variable *next;
2653
2654 while (var) {
2655 next = var->next;
2656 var->next = G.top_var;
2657 G.top_var = var;
2658 if (var->flg_export) {
2659 debug_printf_env("%s: restoring exported '%s'/%u\n", __func__, var->varstr, var->var_nest_level);
2660 putenv(var->varstr);
2661 } else {
2662 debug_printf_env("%s: restoring variable '%s'/%u\n", __func__, var->varstr, var->var_nest_level);
2663 }
2664 var = next;
2665 }
2666}
2667
2668/* We put strings[i] into variable table and possibly putenv them.
2669 * If variable is read only, we can free the strings[i]
2670 * which attempts to overwrite it.
2671 * The strings[] vector itself is freed.
2672 */
2673static void set_vars_and_save_old(char **strings)
2674{
2675 char **s;
2676
2677 if (!strings)
2678 return;
2679
2680 s = strings;
2681 while (*s) {
2682 struct variable *var_p;
2683 struct variable **var_pp;
2684 char *eq;
2685
2686 eq = strchr(*s, '=');
2687 if (HUSH_DEBUG && !eq)
2688 bb_simple_error_msg_and_die("BUG in varexp4");
Francis Laniele7ca3a32023-12-22 22:02:42 +01002689 var_pp = get_ptr_to_local_var(*s);
Francis Laniel110b7692023-12-22 22:02:27 +01002690 if (var_pp) {
2691 var_p = *var_pp;
2692 if (var_p->flg_read_only) {
2693 char **p;
2694 bb_error_msg("%s: readonly variable", *s);
2695 /*
2696 * "VAR=V BLTIN" unsets VARs after BLTIN completes.
2697 * If VAR is readonly, leaving it in the list
2698 * after asssignment error (msg above)
2699 * causes doubled error message later, on unset.
2700 */
2701 debug_printf_env("removing/freeing '%s' element\n", *s);
2702 free(*s);
2703 p = s;
2704 do { *p = p[1]; p++; } while (*p);
2705 goto next;
2706 }
2707 /* below, set_local_var() with nest level will
2708 * "shadow" (remove) this variable from
2709 * global linked list.
2710 */
2711 }
2712 debug_printf_env("%s: env override '%s'/%u\n", __func__, *s, G.var_nest_level);
2713 set_local_var(*s, (G.var_nest_level << SETFLAG_VARLVL_SHIFT) | SETFLAG_EXPORT);
2714 s++;
2715 next: ;
2716 }
2717 free(strings);
2718}
2719
Francis Laniel110b7692023-12-22 22:02:27 +01002720/*
2721 * Unicode helper
2722 */
2723static void reinit_unicode_for_hush(void)
2724{
2725 /* Unicode support should be activated even if LANG is set
2726 * _during_ shell execution, not only if it was set when
2727 * shell was started. Therefore, re-check LANG every time:
2728 */
2729 if (ENABLE_FEATURE_CHECK_UNICODE_IN_ENV
2730 || ENABLE_UNICODE_USING_LOCALE
2731 ) {
2732 const char *s = get_local_var_value("LC_ALL");
2733 if (!s) s = get_local_var_value("LC_CTYPE");
2734 if (!s) s = get_local_var_value("LANG");
2735 reinit_unicode(s);
2736 }
2737}
2738
Francis Laniel36836fc2023-12-22 22:02:28 +01002739#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01002740/*
2741 * in_str support (strings, and "strings" read from files).
2742 */
2743
2744#if ENABLE_HUSH_INTERACTIVE
Francis Laniel36836fc2023-12-22 22:02:28 +01002745#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01002746/* To test correct lineedit/interactive behavior, type from command line:
2747 * echo $P\
2748 * \
2749 * AT\
2750 * H\
2751 * \
2752 * It exercises a lot of corner cases.
2753 */
2754static const char *setup_prompt_string(void)
2755{
2756 const char *prompt_str;
2757
2758 debug_printf_prompt("%s promptmode:%d\n", __func__, G.promptmode);
2759
2760# if ENABLE_FEATURE_EDITING_FANCY_PROMPT
2761 prompt_str = get_local_var_value(G.promptmode == 0 ? "PS1" : "PS2");
2762 if (!prompt_str)
2763 prompt_str = "";
2764# else
2765 prompt_str = "> "; /* if PS2, else... */
2766 if (G.promptmode == 0) { /* PS1 */
2767 /* No fancy prompts supported, (re)generate "CURDIR $ " by hand */
2768 free(G.PS1);
2769 /* bash uses $PWD value, even if it is set by user.
2770 * It uses current dir only if PWD is unset.
2771 * We always use current dir. */
2772 prompt_str = G.PS1 = xasprintf("%s %c ", get_cwd(0), (geteuid() != 0) ? '$' : '#');
2773 }
2774# endif
2775 debug_printf("prompt_str '%s'\n", prompt_str);
2776 return prompt_str;
2777}
Francis Laniel36836fc2023-12-22 22:02:28 +01002778#endif /* !__U_BOOT__ */
2779
2780#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01002781static int get_user_input(struct in_str *i)
Francis Laniel36836fc2023-12-22 22:02:28 +01002782#else /* __U_BOOT__ */
2783static void get_user_input(struct in_str *i)
2784#endif /* __U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01002785{
Francis Laniel36836fc2023-12-22 22:02:28 +01002786#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01002787# if ENABLE_FEATURE_EDITING
2788 /* In EDITING case, this function reads next input line,
2789 * saves it in i->p, then returns 1st char of it.
2790 */
2791 int r;
2792 const char *prompt_str;
2793
2794 prompt_str = setup_prompt_string();
2795 for (;;) {
2796 reinit_unicode_for_hush();
2797 G.flag_SIGINT = 0;
Francis Laniele7ca3a32023-12-22 22:02:42 +01002798
2799 bb_got_signal = 0;
2800 if (!sigisemptyset(&G.pending_set)) {
2801 /* Whoops, already got a signal, do not call read_line_input */
2802 bb_got_signal = r = -1;
2803 } else {
2804 /* For shell, LI_INTERRUPTIBLE is set:
2805 * read_line_input will abort on either
2806 * getting EINTR in poll() and bb_got_signal became != 0,
2807 * or if it sees bb_got_signal != 0
2808 * (IOW: if signal arrives before poll() is reached).
2809 * Interactive testcases:
2810 * (while kill -INT $$; do sleep 1; done) &
2811 * #^^^ prints ^C, prints prompt, repeats
2812 * trap 'echo I' int; (while kill -INT $$; do sleep 1; done) &
2813 * #^^^ prints ^C, prints "I", prints prompt, repeats
2814 * trap 'echo T' term; (while kill $$; do sleep 1; done) &
2815 * #^^^ prints "T", prints prompt, repeats
2816 * #(bash 5.0.17 exits after first "T", looks like a bug)
2817 */
2818 r = read_line_input(G.line_input_state, prompt_str,
Francis Laniel110b7692023-12-22 22:02:27 +01002819 G.user_input_buf, CONFIG_FEATURE_EDITING_MAX_LEN-1
Francis Laniele7ca3a32023-12-22 22:02:42 +01002820 );
2821 /* read_line_input intercepts ^C, "convert" it to SIGINT */
2822 if (r == 0)
2823 raise(SIGINT);
2824 }
2825 /* bash prints ^C (before running a trap, if any)
2826 * both on keyboard ^C and on real SIGINT (non-kbd generated).
2827 */
2828 if (sigismember(&G.pending_set, SIGINT)) {
2829 write(STDOUT_FILENO, "^C\n", 3);
2830 G.last_exitcode = 128 | SIGINT;
Francis Laniel110b7692023-12-22 22:02:27 +01002831 }
2832 check_and_run_traps();
Francis Laniele7ca3a32023-12-22 22:02:42 +01002833 if (r == 0) /* keyboard ^C? */
2834 continue; /* go back, read another input line */
2835 if (r > 0) /* normal input? (no ^C, no ^D, no signals) */
Francis Laniel110b7692023-12-22 22:02:27 +01002836 break;
Francis Laniele7ca3a32023-12-22 22:02:42 +01002837 if (!bb_got_signal) {
2838 /* r < 0: ^D/EOF/error detected (but not signal) */
2839 /* ^D on interactive input goes to next line before exiting: */
2840 write(STDOUT_FILENO, "\n", 1);
2841 i->p = NULL;
2842 i->peek_buf[0] = r = EOF;
2843 return r;
2844 }
2845 /* it was a signal: go back, read another input line */
Francis Laniel110b7692023-12-22 22:02:27 +01002846 }
2847 i->p = G.user_input_buf;
2848 return (unsigned char)*i->p++;
2849# else
2850 /* In !EDITING case, this function gets called for every char.
2851 * Buffering happens deeper in the call chain, in hfgetc(i->file).
2852 */
2853 int r;
2854
2855 for (;;) {
2856 G.flag_SIGINT = 0;
2857 if (i->last_char == '\0' || i->last_char == '\n') {
2858 const char *prompt_str = setup_prompt_string();
2859 /* Why check_and_run_traps here? Try this interactively:
2860 * $ trap 'echo INT' INT; (sleep 2; kill -INT $$) &
2861 * $ <[enter], repeatedly...>
2862 * Without check_and_run_traps, handler never runs.
2863 */
2864 check_and_run_traps();
2865 fputs_stdout(prompt_str);
2866 fflush_all();
2867 }
2868 r = hfgetc(i->file);
2869 /* In !ENABLE_FEATURE_EDITING we don't use read_line_input,
2870 * no ^C masking happens during fgetc, no special code for ^C:
2871 * it generates SIGINT as usual.
2872 */
2873 check_and_run_traps();
2874 if (r != '\0' && !G.flag_SIGINT)
2875 break;
2876 if (G.flag_SIGINT) {
2877 /* ^C or SIGINT: repeat */
2878 /* bash prints ^C even on real SIGINT (non-kbd generated) */
2879 /* kernel prints "^C" itself, just print newline: */
2880 write(STDOUT_FILENO, "\n", 1);
2881 G.last_exitcode = 128 | SIGINT;
2882 }
2883 }
2884 return r;
2885# endif
Francis Laniel36836fc2023-12-22 22:02:28 +01002886#else /* __U_BOOT__ */
2887 int n;
2888 int promptme;
2889 static char the_command[CONFIG_SYS_CBSIZE + 1];
2890
2891 bootretry_reset_cmd_timeout();
2892 promptme = 1;
2893 n = u_boot_cli_readline(i);
2894
2895# ifdef CONFIG_BOOT_RETRY_TIME
2896 if (n == -2) {
2897 puts("\nTimeout waiting for command\n");
2898# ifdef CONFIG_RESET_TO_RETRY
2899 do_reset(NULL, 0, 0, NULL);
2900# else
2901# error "This currently only works with CONFIG_RESET_TO_RETRY enabled"
2902# endif
2903 }
2904# endif
2905 if (n == -1 ) {
2906 G.flag_repeat = 0;
2907 promptme = 0;
2908 }
2909 n = strlen(console_buffer);
2910 console_buffer[n] = '\n';
2911 console_buffer[n+1]= '\0';
2912 if (had_ctrlc())
2913 G.flag_repeat = 0;
2914 clear_ctrlc();
2915 G.do_repeat = 0;
2916#ifndef __U_BOOT__
2917 if (G.promptmode == 1) {
2918#else /* __U_BOOT__ */
2919 if (!G.promptmode) {
2920#endif /* __U_BOOT__ */
2921 if (console_buffer[0] == '\n'&& G.flag_repeat == 0) {
2922 strcpy(the_command, console_buffer);
2923 }
2924 else {
2925 if (console_buffer[0] != '\n') {
2926 strcpy(the_command, console_buffer);
2927 G.flag_repeat = 1;
2928 }
2929 else {
2930 G.do_repeat = 1;
2931 }
2932 }
2933 i->p = the_command;
2934 }
2935 else {
2936 if (console_buffer[0] != '\n') {
2937 if (strlen(the_command) + strlen(console_buffer)
2938 < CONFIG_SYS_CBSIZE) {
2939 n = strlen(the_command);
2940#ifdef __U_BOOT__
2941 /*
2942 * To avoid writing to bad places, we check if
2943 * n is greater than 0.
2944 * This bug was found by Harald Seiler.
2945 */
2946 if (n > 0)
2947 the_command[n-1] = ' ';
2948 strcpy(&the_command[n], console_buffer);
2949#else /* !__U_BOOT__ */
2950 the_command[n-1] = ' ';
2951 strcpy(&the_command[n], console_buffer);
2952#endif /* !__U_BOOT__ */
2953 }
2954 else {
2955 the_command[0] = '\n';
2956 the_command[1] = '\0';
2957 G.flag_repeat = 0;
2958 }
2959 }
2960 if (promptme == 0) {
2961 the_command[0] = '\n';
2962 the_command[1] = '\0';
2963 }
2964 i->p = console_buffer;
2965 }
2966#endif /* __U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01002967}
2968/* This is the magic location that prints prompts
2969 * and gets data back from the user */
2970static int fgetc_interactive(struct in_str *i)
2971{
2972 int ch;
Francis Laniel36836fc2023-12-22 22:02:28 +01002973#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01002974 /* If it's interactive stdin, get new line. */
2975 if (G_interactive_fd && i->file == G.HFILE_stdin) {
Francis Laniel36836fc2023-12-22 22:02:28 +01002976#endif /* !__U_BOOT__ */
2977#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01002978 /* Returns first char (or EOF), the rest is in i->p[] */
2979 ch = get_user_input(i);
Francis Laniel36836fc2023-12-22 22:02:28 +01002980#else /* __U_BOOT__ */
2981 /* Avoid garbage value and make clang happy. */
2982 ch = 0;
2983 /*
2984 * get_user_input() does not return anything when used in
2985 * U-Boot.
2986 * So, we need to take the read character from i->p[].
2987 */
2988 get_user_input(i);
2989 if (i->p && *i->p) {
2990 ch = *i->p++;
2991 }
2992#endif /* __U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01002993 G.promptmode = 1; /* PS2 */
2994 debug_printf_prompt("%s promptmode=%d\n", __func__, G.promptmode);
Francis Laniel36836fc2023-12-22 22:02:28 +01002995#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01002996 } else {
2997 /* Not stdin: script file, sourced file, etc */
2998 do ch = hfgetc(i->file); while (ch == '\0');
2999 }
Francis Laniel36836fc2023-12-22 22:02:28 +01003000#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01003001 return ch;
3002}
3003#else /* !INTERACTIVE */
Francis Laniel36836fc2023-12-22 22:02:28 +01003004#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01003005static ALWAYS_INLINE int fgetc_interactive(struct in_str *i)
3006{
3007 int ch;
3008 do ch = hfgetc(i->file); while (ch == '\0');
3009 return ch;
3010}
Francis Laniel36836fc2023-12-22 22:02:28 +01003011#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01003012#endif /* !INTERACTIVE */
3013
3014static int i_getch(struct in_str *i)
3015{
3016 int ch;
3017
Francis Laniel36836fc2023-12-22 22:02:28 +01003018#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01003019 if (!i->file) {
3020 /* string-based in_str */
3021 ch = (unsigned char)*i->p;
3022 if (ch != '\0') {
3023 i->p++;
3024 i->last_char = ch;
Francis Laniele7ca3a32023-12-22 22:02:42 +01003025#if ENABLE_HUSH_LINENO_VAR
3026 if (ch == '\n') {
3027 G.parse_lineno++;
3028 debug_printf_parse("G.parse_lineno++ = %u\n", G.parse_lineno);
3029 }
3030#endif
Francis Laniel110b7692023-12-22 22:02:27 +01003031 return ch;
3032 }
3033 return EOF;
3034 }
3035
Francis Laniel36836fc2023-12-22 22:02:28 +01003036#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01003037 /* FILE-based in_str */
3038
3039#if ENABLE_FEATURE_EDITING
3040 /* This can be stdin, check line editing char[] buffer */
3041 if (i->p && *i->p != '\0') {
3042 ch = (unsigned char)*i->p++;
3043 goto out;
Francis Laniel26cafe12023-12-22 22:02:34 +01003044#ifndef __U_BOOT__
3045 }
3046#else /* __U_BOOT__ */
3047 /*
3048 * There are two ways for command to be called:
3049 * 1. The first one is when they are typed by the user.
3050 * 2. The second one is through run_command() (NOTE command run
3051 * internally calls run_command()).
3052 *
3053 * In the second case, we do not get input from the user, so once we
3054 * get a '\0', it means we need to stop.
3055 * NOTE G.run_command_flags is only set on run_command call stack, so
3056 * we use this to know if we come from user input or run_command().
3057 */
3058 } else if (i->p && *i->p == '\0' && G.run_command_flags){
3059 return EOF;
Francis Laniel110b7692023-12-22 22:02:27 +01003060 }
Francis Laniel26cafe12023-12-22 22:02:34 +01003061#endif /* __U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01003062#endif
Francis Laniel36836fc2023-12-22 22:02:28 +01003063#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01003064 /* peek_buf[] is an int array, not char. Can contain EOF. */
3065 ch = i->peek_buf[0];
3066 if (ch != 0) {
3067 int ch2 = i->peek_buf[1];
3068 i->peek_buf[0] = ch2;
3069 if (ch2 == 0) /* very likely, avoid redundant write */
3070 goto out;
3071 i->peek_buf[1] = 0;
3072 goto out;
3073 }
3074
Francis Laniel36836fc2023-12-22 22:02:28 +01003075#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01003076 ch = fgetc_interactive(i);
3077 out:
3078 debug_printf("file_get: got '%c' %d\n", ch, ch);
3079 i->last_char = ch;
3080#if ENABLE_HUSH_LINENO_VAR
3081 if (ch == '\n') {
3082 G.parse_lineno++;
3083 debug_printf_parse("G.parse_lineno++ = %u\n", G.parse_lineno);
3084 }
3085#endif
3086 return ch;
3087}
3088
3089static int i_peek(struct in_str *i)
3090{
Francis Laniel36836fc2023-12-22 22:02:28 +01003091#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01003092 int ch;
3093
3094 if (!i->file) {
3095 /* string-based in_str */
3096 /* Doesn't report EOF on NUL. None of the callers care. */
3097 return (unsigned char)*i->p;
3098 }
3099
3100 /* FILE-based in_str */
3101
3102#if ENABLE_FEATURE_EDITING && ENABLE_HUSH_INTERACTIVE
3103 /* This can be stdin, check line editing char[] buffer */
3104 if (i->p && *i->p != '\0')
3105 return (unsigned char)*i->p;
3106#endif
3107 /* peek_buf[] is an int array, not char. Can contain EOF. */
3108 ch = i->peek_buf[0];
3109 if (ch != 0)
3110 return ch;
3111
3112 /* Need to get a new char */
3113 ch = fgetc_interactive(i);
3114 debug_printf("file_peek: got '%c' %d\n", ch, ch);
3115
3116 /* Save it by either rolling back line editing buffer, or in i->peek_buf[0] */
3117#if ENABLE_FEATURE_EDITING && ENABLE_HUSH_INTERACTIVE
3118 if (i->p) {
3119 i->p -= 1;
3120 return ch;
3121 }
3122#endif
3123 i->peek_buf[0] = ch;
3124 /*i->peek_buf[1] = 0; - already is */
3125 return ch;
Francis Laniel36836fc2023-12-22 22:02:28 +01003126#else /* __U_BOOT__ */
3127 /* string-based in_str */
3128 /* Doesn't report EOF on NUL. None of the callers care. */
3129 return (unsigned char)*i->p;
3130#endif /* __U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01003131}
3132
3133/* Only ever called if i_peek() was called, and did not return EOF.
3134 * IOW: we know the previous peek saw an ordinary char, not EOF, not NUL,
3135 * not end-of-line. Therefore we never need to read a new editing line here.
3136 */
3137static int i_peek2(struct in_str *i)
3138{
Francis Laniel36836fc2023-12-22 22:02:28 +01003139#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01003140 int ch;
Francis Laniel36836fc2023-12-22 22:02:28 +01003141#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01003142
3143 /* There are two cases when i->p[] buffer exists.
3144 * (1) it's a string in_str.
3145 * (2) It's a file, and we have a saved line editing buffer.
3146 * In both cases, we know that i->p[0] exists and not NUL, and
3147 * the peek2 result is in i->p[1].
3148 */
3149 if (i->p)
3150 return (unsigned char)i->p[1];
3151
Francis Laniel36836fc2023-12-22 22:02:28 +01003152#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01003153 /* Now we know it is a file-based in_str. */
3154
3155 /* peek_buf[] is an int array, not char. Can contain EOF. */
3156 /* Is there 2nd char? */
3157 ch = i->peek_buf[1];
3158 if (ch == 0) {
3159 /* We did not read it yet, get it now */
3160 do ch = hfgetc(i->file); while (ch == '\0');
3161 i->peek_buf[1] = ch;
3162 }
3163
3164 debug_printf("file_peek2: got '%c' %d\n", ch, ch);
3165 return ch;
Francis Laniel36836fc2023-12-22 22:02:28 +01003166#else
3167 return 0;
3168#endif /* __U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01003169}
3170
3171static int i_getch_and_eat_bkslash_nl(struct in_str *input)
3172{
3173 for (;;) {
3174 int ch, ch2;
3175
3176 ch = i_getch(input);
3177 if (ch != '\\')
3178 return ch;
3179 ch2 = i_peek(input);
3180 if (ch2 != '\n')
3181 return ch;
3182 /* backslash+newline, skip it */
3183 i_getch(input);
3184 }
3185}
3186
3187/* Note: this function _eats_ \<newline> pairs, safe to use plain
3188 * i_getch() after it instead of i_getch_and_eat_bkslash_nl().
3189 */
3190static int i_peek_and_eat_bkslash_nl(struct in_str *input)
3191{
3192 for (;;) {
3193 int ch, ch2;
3194
3195 ch = i_peek(input);
3196 if (ch != '\\')
3197 return ch;
3198 ch2 = i_peek2(input);
3199 if (ch2 != '\n')
3200 return ch;
3201 /* backslash+newline, skip it */
3202 i_getch(input);
3203 i_getch(input);
3204 }
3205}
3206
Francis Laniel36836fc2023-12-22 22:02:28 +01003207#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01003208static void setup_file_in_str(struct in_str *i, HFILE *fp)
Francis Laniel36836fc2023-12-22 22:02:28 +01003209#else /* __U_BOOT__ */
3210static void setup_file_in_str(struct in_str *i)
3211#endif /* __U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01003212{
3213 memset(i, 0, sizeof(*i));
Francis Laniel36836fc2023-12-22 22:02:28 +01003214#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01003215 i->file = fp;
3216 /* i->p = NULL; */
Francis Laniel36836fc2023-12-22 22:02:28 +01003217#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01003218}
3219
3220static void setup_string_in_str(struct in_str *i, const char *s)
3221{
3222 memset(i, 0, sizeof(*i));
3223 /*i->file = NULL */;
3224 i->p = s;
3225}
3226
Francis Laniel110b7692023-12-22 22:02:27 +01003227/*
3228 * o_string support
3229 */
3230#define B_CHUNK (32 * sizeof(char*))
3231
3232static void o_reset_to_empty_unquoted(o_string *o)
3233{
3234 o->length = 0;
3235 o->has_quoted_part = 0;
3236 if (o->data)
3237 o->data[0] = '\0';
3238}
3239
3240static void o_free_and_set_NULL(o_string *o)
3241{
3242 free(o->data);
3243 memset(o, 0, sizeof(*o));
3244}
3245
3246static ALWAYS_INLINE void o_free(o_string *o)
3247{
3248 free(o->data);
3249}
3250
3251static void o_grow_by(o_string *o, int len)
3252{
3253 if (o->length + len > o->maxlen) {
3254 o->maxlen += (2 * len) | (B_CHUNK-1);
3255 o->data = xrealloc(o->data, 1 + o->maxlen);
3256 }
3257}
3258
3259static void o_addchr(o_string *o, int ch)
3260{
3261 debug_printf("o_addchr: '%c' o->length=%d o=%p\n", ch, o->length, o);
3262 if (o->length < o->maxlen) {
3263 /* likely. avoid o_grow_by() call */
3264 add:
3265 o->data[o->length] = ch;
3266 o->length++;
3267 o->data[o->length] = '\0';
3268 return;
3269 }
3270 o_grow_by(o, 1);
3271 goto add;
3272}
3273
3274#if 0
3275/* Valid only if we know o_string is not empty */
3276static void o_delchr(o_string *o)
3277{
3278 o->length--;
3279 o->data[o->length] = '\0';
3280}
3281#endif
3282
3283static void o_addblock(o_string *o, const char *str, int len)
3284{
3285 o_grow_by(o, len);
3286 ((char*)mempcpy(&o->data[o->length], str, len))[0] = '\0';
3287 o->length += len;
3288}
3289
3290static void o_addstr(o_string *o, const char *str)
3291{
3292 o_addblock(o, str, strlen(str));
3293}
3294
Francis Laniel36836fc2023-12-22 22:02:28 +01003295#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01003296static void o_addstr_with_NUL(o_string *o, const char *str)
3297{
3298 o_addblock(o, str, strlen(str) + 1);
3299}
Francis Laniel36836fc2023-12-22 22:02:28 +01003300#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01003301
3302#if !BB_MMU
3303static void nommu_addchr(o_string *o, int ch)
3304{
3305 if (o)
3306 o_addchr(o, ch);
3307}
3308#else
3309# define nommu_addchr(o, str) ((void)0)
3310#endif
3311
Francis Laniel36836fc2023-12-22 22:02:28 +01003312#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01003313#if ENABLE_HUSH_MODE_X
3314static void x_mode_addchr(int ch)
3315{
3316 o_addchr(&G.x_mode_buf, ch);
3317}
3318static void x_mode_addstr(const char *str)
3319{
3320 o_addstr(&G.x_mode_buf, str);
3321}
3322static void x_mode_addblock(const char *str, int len)
3323{
3324 o_addblock(&G.x_mode_buf, str, len);
3325}
3326static void x_mode_prefix(void)
3327{
3328 int n = G.x_mode_depth;
3329 do x_mode_addchr('+'); while (--n >= 0);
3330}
3331static void x_mode_flush(void)
3332{
3333 int len = G.x_mode_buf.length;
3334 if (len <= 0)
3335 return;
3336 if (G.x_mode_fd > 0) {
3337 G.x_mode_buf.data[len] = '\n';
3338 full_write(G.x_mode_fd, G.x_mode_buf.data, len + 1);
3339 }
3340 G.x_mode_buf.length = 0;
3341}
3342#endif
Francis Laniel36836fc2023-12-22 22:02:28 +01003343#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01003344
3345/*
3346 * HUSH_BRACE_EXPANSION code needs corresponding quoting on variable expansion side.
3347 * Currently, "v='{q,w}'; echo $v" erroneously expands braces in $v.
3348 * Apparently, on unquoted $v bash still does globbing
3349 * ("v='*.txt'; echo $v" prints all .txt files),
3350 * but NOT brace expansion! Thus, there should be TWO independent
3351 * quoting mechanisms on $v expansion side: one protects
3352 * $v from brace expansion, and other additionally protects "$v" against globbing.
3353 * We have only second one.
3354 */
3355
3356#if ENABLE_HUSH_BRACE_EXPANSION
3357# define MAYBE_BRACES "{}"
3358#else
3359# define MAYBE_BRACES ""
3360#endif
3361
3362/* My analysis of quoting semantics tells me that state information
3363 * is associated with a destination, not a source.
3364 */
3365static void o_addqchr(o_string *o, int ch)
3366{
3367 int sz = 1;
3368 /* '-' is included because of this case:
3369 * >filename0 >filename1 >filename9; v='-'; echo filename[0"$v"9]
3370 */
3371 char *found = strchr("*?[-\\" MAYBE_BRACES, ch);
3372 if (found)
3373 sz++;
3374 o_grow_by(o, sz);
3375 if (found) {
3376 o->data[o->length] = '\\';
3377 o->length++;
3378 }
3379 o->data[o->length] = ch;
3380 o->length++;
3381 o->data[o->length] = '\0';
3382}
3383
3384static void o_addQchr(o_string *o, int ch)
3385{
3386 int sz = 1;
3387 if ((o->o_expflags & EXP_FLAG_ESC_GLOB_CHARS)
3388 && strchr("*?[-\\" MAYBE_BRACES, ch)
3389 ) {
3390 sz++;
3391 o->data[o->length] = '\\';
3392 o->length++;
3393 }
3394 o_grow_by(o, sz);
3395 o->data[o->length] = ch;
3396 o->length++;
3397 o->data[o->length] = '\0';
3398}
3399
3400static void o_addqblock(o_string *o, const char *str, int len)
3401{
3402 while (len) {
3403 char ch;
3404 int sz;
3405 int ordinary_cnt = strcspn(str, "*?[-\\" MAYBE_BRACES);
3406 if (ordinary_cnt > len) /* paranoia */
3407 ordinary_cnt = len;
3408 o_addblock(o, str, ordinary_cnt);
3409 if (ordinary_cnt == len)
3410 return; /* NUL is already added by o_addblock */
3411 str += ordinary_cnt;
3412 len -= ordinary_cnt + 1; /* we are processing + 1 char below */
3413
3414 ch = *str++;
3415 sz = 1;
3416 if (ch) { /* it is necessarily one of "*?[-\\" MAYBE_BRACES */
3417 sz++;
3418 o->data[o->length] = '\\';
3419 o->length++;
3420 }
3421 o_grow_by(o, sz);
3422 o->data[o->length] = ch;
3423 o->length++;
3424 }
3425 o->data[o->length] = '\0';
3426}
3427
3428static void o_addQblock(o_string *o, const char *str, int len)
3429{
3430 if (!(o->o_expflags & EXP_FLAG_ESC_GLOB_CHARS)) {
3431 o_addblock(o, str, len);
3432 return;
3433 }
3434 o_addqblock(o, str, len);
3435}
3436
3437static void o_addQstr(o_string *o, const char *str)
3438{
3439 o_addQblock(o, str, strlen(str));
3440}
3441
3442/* A special kind of o_string for $VAR and `cmd` expansion.
3443 * It contains char* list[] at the beginning, which is grown in 16 element
3444 * increments. Actual string data starts at the next multiple of 16 * (char*).
3445 * list[i] contains an INDEX (int!) into this string data.
3446 * It means that if list[] needs to grow, data needs to be moved higher up
3447 * but list[i]'s need not be modified.
3448 * NB: remembering how many list[i]'s you have there is crucial.
3449 * o_finalize_list() operation post-processes this structure - calculates
3450 * and stores actual char* ptrs in list[]. Oh, it NULL terminates it as well.
3451 */
3452#if DEBUG_EXPAND || DEBUG_GLOB
3453static void debug_print_list(const char *prefix, o_string *o, int n)
3454{
3455 char **list = (char**)o->data;
3456 int string_start = ((n + 0xf) & ~0xf) * sizeof(list[0]);
3457 int i = 0;
3458
3459 indent();
3460 fdprintf(2, "%s: list:%p n:%d string_start:%d length:%d maxlen:%d glob:%d quoted:%d escape:%d\n",
3461 prefix, list, n, string_start, o->length, o->maxlen,
3462 !!(o->o_expflags & EXP_FLAG_GLOB),
3463 o->has_quoted_part,
3464 !!(o->o_expflags & EXP_FLAG_ESC_GLOB_CHARS));
3465 while (i < n) {
3466 indent();
3467 fdprintf(2, " list[%d]=%d '%s' %p\n", i, (int)(uintptr_t)list[i],
3468 o->data + (int)(uintptr_t)list[i] + string_start,
3469 o->data + (int)(uintptr_t)list[i] + string_start);
3470 i++;
3471 }
3472 if (n) {
3473 const char *p = o->data + (int)(uintptr_t)list[n - 1] + string_start;
3474 indent();
Francis Laniel36836fc2023-12-22 22:02:28 +01003475#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01003476 fdprintf(2, " total_sz:%ld\n", (long)((p + strlen(p) + 1) - o->data));
Francis Laniel36836fc2023-12-22 22:02:28 +01003477#else /* __U_BOOT__ */
3478 printf(" total_sz:%ld\n", (long)((p + strlen(p) + 1) - o->data));
3479#endif /* __U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01003480 }
3481}
3482#else
3483# define debug_print_list(prefix, o, n) ((void)0)
3484#endif
3485
3486/* n = o_save_ptr_helper(str, n) "starts new string" by storing an index value
3487 * in list[n] so that it points past last stored byte so far.
3488 * It returns n+1. */
3489static int o_save_ptr_helper(o_string *o, int n)
3490{
3491 char **list = (char**)o->data;
3492 int string_start;
3493 int string_len;
3494
3495 if (!o->has_empty_slot) {
3496 string_start = ((n + 0xf) & ~0xf) * sizeof(list[0]);
3497 string_len = o->length - string_start;
3498 if (!(n & 0xf)) { /* 0, 0x10, 0x20...? */
3499 debug_printf_list("list[%d]=%d string_start=%d (growing)\n", n, string_len, string_start);
3500 /* list[n] points to string_start, make space for 16 more pointers */
3501 o->maxlen += 0x10 * sizeof(list[0]);
3502 o->data = xrealloc(o->data, o->maxlen + 1);
3503 list = (char**)o->data;
3504 memmove(list + n + 0x10, list + n, string_len);
3505 /*
3506 * expand_on_ifs() has a "previous argv[] ends in IFS?"
3507 * check. (grep for -prev-ifs-check-).
3508 * Ensure that argv[-1][last] is not garbage
3509 * but zero bytes, to save index check there.
3510 */
3511 list[n + 0x10 - 1] = 0;
3512 o->length += 0x10 * sizeof(list[0]);
3513 } else {
3514 debug_printf_list("list[%d]=%d string_start=%d\n",
3515 n, string_len, string_start);
3516 }
3517 } else {
3518 /* We have empty slot at list[n], reuse without growth */
3519 string_start = ((n+1 + 0xf) & ~0xf) * sizeof(list[0]); /* NB: n+1! */
3520 string_len = o->length - string_start;
3521 debug_printf_list("list[%d]=%d string_start=%d (empty slot)\n",
3522 n, string_len, string_start);
3523 o->has_empty_slot = 0;
3524 }
3525 o->has_quoted_part = 0;
3526 list[n] = (char*)(uintptr_t)string_len;
3527 return n + 1;
3528}
3529
3530/* "What was our last o_save_ptr'ed position (byte offset relative o->data)?" */
3531static int o_get_last_ptr(o_string *o, int n)
3532{
3533 char **list = (char**)o->data;
3534 int string_start = ((n + 0xf) & ~0xf) * sizeof(list[0]);
3535
3536 return ((int)(uintptr_t)list[n-1]) + string_start;
3537}
3538
3539/*
3540 * Globbing routines.
3541 *
3542 * Most words in commands need to be globbed, even ones which are
3543 * (single or double) quoted. This stems from the possiblity of
3544 * constructs like "abc"* and 'abc'* - these should be globbed.
3545 * Having a different code path for fully-quoted strings ("abc",
3546 * 'abc') would only help performance-wise, but we still need
3547 * code for partially-quoted strings.
3548 *
3549 * Unfortunately, if we want to match bash and ash behavior in all cases,
3550 * the logic can't be "shell-syntax argument is first transformed
3551 * to a string, then globbed, and if globbing does not match anything,
3552 * it is used verbatim". Here are two examples where it fails:
3553 *
3554 * echo 'b\*'?
3555 *
3556 * The globbing can't be avoided (because of '?' at the end).
3557 * The glob pattern is: b\\\*? - IOW, both \ and * are literals
3558 * and are glob-escaped. If this does not match, bash/ash print b\*?
3559 * - IOW: they "unbackslash" the glob pattern.
3560 * Now, look at this:
3561 *
3562 * v='\\\*'; echo b$v?
3563 *
3564 * The glob pattern is the same here: b\\\*? - the unquoted $v expansion
3565 * should be used as glob pattern with no changes. However, if glob
3566 * does not match, bash/ash print b\\\*? - NOT THE SAME as first example!
3567 *
3568 * ash implements this by having an encoded representation of the word
3569 * to glob, which IS NOT THE SAME as the glob pattern - it has more data.
3570 * Glob pattern is derived from it. If glob fails, the decision what result
3571 * should be is made using that encoded representation. Not glob pattern.
3572 */
3573
3574#if ENABLE_HUSH_BRACE_EXPANSION
3575/* There in a GNU extension, GLOB_BRACE, but it is not usable:
3576 * first, it processes even {a} (no commas), second,
3577 * I didn't manage to make it return strings when they don't match
3578 * existing files. Need to re-implement it.
3579 */
3580
3581/* Helper */
3582static int glob_needed(const char *s)
3583{
3584 while (*s) {
3585 if (*s == '\\') {
3586 if (!s[1])
3587 return 0;
3588 s += 2;
3589 continue;
3590 }
3591 if (*s == '*' || *s == '[' || *s == '?' || *s == '{')
3592 return 1;
3593 s++;
3594 }
3595 return 0;
3596}
3597/* Return pointer to next closing brace or to comma */
3598static const char *next_brace_sub(const char *cp)
3599{
3600 unsigned depth = 0;
3601 cp++;
3602 while (*cp != '\0') {
3603 if (*cp == '\\') {
3604 if (*++cp == '\0')
3605 break;
3606 cp++;
3607 continue;
3608 }
3609 if ((*cp == '}' && depth-- == 0) || (*cp == ',' && depth == 0))
3610 break;
3611 if (*cp++ == '{')
3612 depth++;
3613 }
3614
3615 return *cp != '\0' ? cp : NULL;
3616}
3617/* Recursive brace globber. Note: may garble pattern[]. */
3618static int glob_brace(char *pattern, o_string *o, int n)
3619{
3620 char *new_pattern_buf;
3621 const char *begin;
3622 const char *next;
3623 const char *rest;
3624 const char *p;
3625 size_t rest_len;
3626
3627 debug_printf_glob("glob_brace('%s')\n", pattern);
3628
3629 begin = pattern;
3630 while (1) {
3631 if (*begin == '\0')
3632 goto simple_glob;
3633 if (*begin == '{') {
3634 /* Find the first sub-pattern and at the same time
3635 * find the rest after the closing brace */
3636 next = next_brace_sub(begin);
3637 if (next == NULL) {
3638 /* An illegal expression */
3639 goto simple_glob;
3640 }
3641 if (*next == '}') {
3642 /* "{abc}" with no commas - illegal
3643 * brace expr, disregard and skip it */
3644 begin = next + 1;
3645 continue;
3646 }
3647 break;
3648 }
3649 if (*begin == '\\' && begin[1] != '\0')
3650 begin++;
3651 begin++;
3652 }
3653 debug_printf_glob("begin:%s\n", begin);
3654 debug_printf_glob("next:%s\n", next);
3655
3656 /* Now find the end of the whole brace expression */
3657 rest = next;
3658 while (*rest != '}') {
3659 rest = next_brace_sub(rest);
3660 if (rest == NULL) {
3661 /* An illegal expression */
3662 goto simple_glob;
3663 }
3664 debug_printf_glob("rest:%s\n", rest);
3665 }
3666 rest_len = strlen(++rest) + 1;
3667
3668 /* We are sure the brace expression is well-formed */
3669
3670 /* Allocate working buffer large enough for our work */
3671 new_pattern_buf = xmalloc(strlen(pattern));
3672
3673 /* We have a brace expression. BEGIN points to the opening {,
3674 * NEXT points past the terminator of the first element, and REST
3675 * points past the final }. We will accumulate result names from
3676 * recursive runs for each brace alternative in the buffer using
Francis Laniele7ca3a32023-12-22 22:02:42 +01003677 * GLOB_APPEND. */
Francis Laniel110b7692023-12-22 22:02:27 +01003678
3679 p = begin + 1;
3680 while (1) {
3681 /* Construct the new glob expression */
3682 memcpy(
3683 mempcpy(
3684 mempcpy(new_pattern_buf,
3685 /* We know the prefix for all sub-patterns */
3686 pattern, begin - pattern),
3687 p, next - p),
3688 rest, rest_len);
3689
3690 /* Note: glob_brace() may garble new_pattern_buf[].
3691 * That's why we re-copy prefix every time (1st memcpy above).
3692 */
3693 n = glob_brace(new_pattern_buf, o, n);
3694 if (*next == '}') {
3695 /* We saw the last entry */
3696 break;
3697 }
3698 p = next + 1;
3699 next = next_brace_sub(next);
3700 }
3701 free(new_pattern_buf);
3702 return n;
3703
3704 simple_glob:
3705 {
3706 int gr;
3707 glob_t globdata;
3708
3709 memset(&globdata, 0, sizeof(globdata));
3710 gr = glob(pattern, 0, NULL, &globdata);
3711 debug_printf_glob("glob('%s'):%d\n", pattern, gr);
3712 if (gr != 0) {
3713 if (gr == GLOB_NOMATCH) {
3714 globfree(&globdata);
3715 /* NB: garbles parameter */
3716 unbackslash(pattern);
3717 o_addstr_with_NUL(o, pattern);
3718 debug_printf_glob("glob pattern '%s' is literal\n", pattern);
3719 return o_save_ptr_helper(o, n);
3720 }
3721 if (gr == GLOB_NOSPACE)
3722 bb_die_memory_exhausted();
3723 /* GLOB_ABORTED? Only happens with GLOB_ERR flag,
3724 * but we didn't specify it. Paranoia again. */
3725 bb_error_msg_and_die("glob error %d on '%s'", gr, pattern);
3726 }
3727 if (globdata.gl_pathv && globdata.gl_pathv[0]) {
3728 char **argv = globdata.gl_pathv;
3729 while (1) {
3730 o_addstr_with_NUL(o, *argv);
3731 n = o_save_ptr_helper(o, n);
3732 argv++;
3733 if (!*argv)
3734 break;
3735 }
3736 }
3737 globfree(&globdata);
3738 }
3739 return n;
3740}
3741/* Performs globbing on last list[],
3742 * saving each result as a new list[].
3743 */
3744static int perform_glob(o_string *o, int n)
3745{
3746 char *pattern, *copy;
3747
3748 debug_printf_glob("start perform_glob: n:%d o->data:%p\n", n, o->data);
3749 if (!o->data)
3750 return o_save_ptr_helper(o, n);
3751 pattern = o->data + o_get_last_ptr(o, n);
3752 debug_printf_glob("glob pattern '%s'\n", pattern);
3753 if (!glob_needed(pattern)) {
3754 /* unbackslash last string in o in place, fix length */
3755 o->length = unbackslash(pattern) - o->data;
3756 debug_printf_glob("glob pattern '%s' is literal\n", pattern);
3757 return o_save_ptr_helper(o, n);
3758 }
3759
3760 copy = xstrdup(pattern);
3761 /* "forget" pattern in o */
3762 o->length = pattern - o->data;
3763 n = glob_brace(copy, o, n);
3764 free(copy);
3765 if (DEBUG_GLOB)
3766 debug_print_list("perform_glob returning", o, n);
3767 return n;
3768}
3769
3770#else /* !HUSH_BRACE_EXPANSION */
3771
3772/* Helper */
3773static int glob_needed(const char *s)
3774{
3775 while (*s) {
3776 if (*s == '\\') {
3777 if (!s[1])
3778 return 0;
3779 s += 2;
3780 continue;
3781 }
3782 if (*s == '*' || *s == '[' || *s == '?')
3783 return 1;
3784 s++;
3785 }
3786 return 0;
3787}
3788/* Performs globbing on last list[],
3789 * saving each result as a new list[].
3790 */
3791static int perform_glob(o_string *o, int n)
3792{
Francis Lanielbfc406a2023-12-22 22:02:33 +01003793#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01003794 glob_t globdata;
3795 int gr;
Francis Lanielbfc406a2023-12-22 22:02:33 +01003796#endif /* __U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01003797 char *pattern;
3798
3799 debug_printf_glob("start perform_glob: n:%d o->data:%p\n", n, o->data);
3800 if (!o->data)
3801 return o_save_ptr_helper(o, n);
3802 pattern = o->data + o_get_last_ptr(o, n);
3803 debug_printf_glob("glob pattern '%s'\n", pattern);
3804 if (!glob_needed(pattern)) {
Francis Lanielbfc406a2023-12-22 22:02:33 +01003805#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01003806 literal:
Francis Lanielbfc406a2023-12-22 22:02:33 +01003807#endif /* __U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01003808 /* unbackslash last string in o in place, fix length */
3809 o->length = unbackslash(pattern) - o->data;
3810 debug_printf_glob("glob pattern '%s' is literal\n", pattern);
3811 return o_save_ptr_helper(o, n);
3812 }
3813
Francis Lanielbfc406a2023-12-22 22:02:33 +01003814#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01003815 memset(&globdata, 0, sizeof(globdata));
3816 /* Can't use GLOB_NOCHECK: it does not unescape the string.
3817 * If we glob "*.\*" and don't find anything, we need
3818 * to fall back to using literal "*.*", but GLOB_NOCHECK
3819 * will return "*.\*"!
3820 */
3821 gr = glob(pattern, 0, NULL, &globdata);
3822 debug_printf_glob("glob('%s'):%d\n", pattern, gr);
3823 if (gr != 0) {
3824 if (gr == GLOB_NOMATCH) {
3825 globfree(&globdata);
3826 goto literal;
3827 }
3828 if (gr == GLOB_NOSPACE)
3829 bb_die_memory_exhausted();
3830 /* GLOB_ABORTED? Only happens with GLOB_ERR flag,
3831 * but we didn't specify it. Paranoia again. */
3832 bb_error_msg_and_die("glob error %d on '%s'", gr, pattern);
3833 }
3834 if (globdata.gl_pathv && globdata.gl_pathv[0]) {
3835 char **argv = globdata.gl_pathv;
3836 /* "forget" pattern in o */
3837 o->length = pattern - o->data;
3838 while (1) {
3839 o_addstr_with_NUL(o, *argv);
3840 n = o_save_ptr_helper(o, n);
3841 argv++;
3842 if (!*argv)
3843 break;
3844 }
3845 }
3846 globfree(&globdata);
3847 if (DEBUG_GLOB)
3848 debug_print_list("perform_glob returning", o, n);
3849 return n;
Francis Lanielbfc406a2023-12-22 22:02:33 +01003850#else /* __U_BOOT__ */
3851 /*
3852 * NOTE We only use perform glob to call unbackslash to remove backslash
3853 * from string once expanded.
3854 * So, it seems OK to return this if no previous return was done.
3855 */
3856 return o_save_ptr_helper(o, n);
3857#endif /* __U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01003858}
3859
3860#endif /* !HUSH_BRACE_EXPANSION */
3861
3862/* If o->o_expflags & EXP_FLAG_GLOB, glob the string so far remembered.
3863 * Otherwise, just finish current list[] and start new */
3864static int o_save_ptr(o_string *o, int n)
3865{
3866 if (o->o_expflags & EXP_FLAG_GLOB) {
3867 /* If o->has_empty_slot, list[n] was already globbed
3868 * (if it was requested back then when it was filled)
3869 * so don't do that again! */
3870 if (!o->has_empty_slot)
3871 return perform_glob(o, n); /* o_save_ptr_helper is inside */
3872 }
3873 return o_save_ptr_helper(o, n);
3874}
3875
3876/* "Please convert list[n] to real char* ptrs, and NULL terminate it." */
3877static char **o_finalize_list(o_string *o, int n)
3878{
3879 char **list;
3880 int string_start;
3881
3882 if (DEBUG_EXPAND)
3883 debug_print_list("finalized", o, n);
3884 debug_printf_expand("finalized n:%d\n", n);
3885 list = (char**)o->data;
3886 string_start = ((n + 0xf) & ~0xf) * sizeof(list[0]);
3887 list[--n] = NULL;
3888 while (n) {
3889 n--;
3890 list[n] = o->data + (int)(uintptr_t)list[n] + string_start;
3891 }
3892 return list;
3893}
3894
3895static void free_pipe_list(struct pipe *pi);
3896
3897/* Returns pi->next - next pipe in the list */
3898static struct pipe *free_pipe(struct pipe *pi)
3899{
3900 struct pipe *next;
3901 int i;
3902
Francis Laniel36836fc2023-12-22 22:02:28 +01003903#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01003904 debug_printf_clean("free_pipe (pid %d)\n", getpid());
Francis Laniel36836fc2023-12-22 22:02:28 +01003905#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01003906 for (i = 0; i < pi->num_cmds; i++) {
3907 struct command *command;
Francis Laniel36836fc2023-12-22 22:02:28 +01003908#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01003909 struct redir_struct *r, *rnext;
Francis Laniel36836fc2023-12-22 22:02:28 +01003910#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01003911
3912 command = &pi->cmds[i];
3913 debug_printf_clean(" command %d:\n", i);
3914 if (command->argv) {
3915 if (DEBUG_CLEAN) {
3916 int a;
3917 char **p;
3918 for (a = 0, p = command->argv; *p; a++, p++) {
3919 debug_printf_clean(" argv[%d] = %s\n", a, *p);
3920 }
3921 }
3922 free_strings(command->argv);
3923 //command->argv = NULL;
3924 }
3925 /* not "else if": on syntax error, we may have both! */
3926 if (command->group) {
3927 debug_printf_clean(" begin group (cmd_type:%d)\n",
3928 command->cmd_type);
3929 free_pipe_list(command->group);
3930 debug_printf_clean(" end group\n");
3931 //command->group = NULL;
3932 }
3933 /* else is crucial here.
3934 * If group != NULL, child_func is meaningless */
3935#if ENABLE_HUSH_FUNCTIONS
3936 else if (command->child_func) {
3937 debug_printf_exec("cmd %p releases child func at %p\n", command, command->child_func);
3938 command->child_func->parent_cmd = NULL;
3939 }
3940#endif
3941#if !BB_MMU
3942 free(command->group_as_string);
3943 //command->group_as_string = NULL;
3944#endif
Francis Laniel36836fc2023-12-22 22:02:28 +01003945#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01003946 for (r = command->redirects; r; r = rnext) {
3947 debug_printf_clean(" redirect %d%s",
3948 r->rd_fd, redir_table[r->rd_type].descrip);
3949 /* guard against the case >$FOO, where foo is unset or blank */
3950 if (r->rd_filename) {
3951 debug_printf_clean(" fname:'%s'\n", r->rd_filename);
3952 free(r->rd_filename);
3953 //r->rd_filename = NULL;
3954 }
3955 debug_printf_clean(" rd_dup:%d\n", r->rd_dup);
3956 rnext = r->next;
3957 free(r);
3958 }
3959 //command->redirects = NULL;
Francis Laniel36836fc2023-12-22 22:02:28 +01003960#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01003961 }
3962 free(pi->cmds); /* children are an array, they get freed all at once */
3963 //pi->cmds = NULL;
Francis Laniel36836fc2023-12-22 22:02:28 +01003964#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01003965#if ENABLE_HUSH_JOB
3966 free(pi->cmdtext);
3967 //pi->cmdtext = NULL;
3968#endif
Francis Laniel36836fc2023-12-22 22:02:28 +01003969#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01003970
3971 next = pi->next;
3972 free(pi);
3973 return next;
3974}
3975
3976static void free_pipe_list(struct pipe *pi)
3977{
3978 while (pi) {
3979#if HAS_KEYWORDS
3980 debug_printf_clean("pipe reserved word %d\n", pi->res_word);
3981#endif
3982 debug_printf_clean("pipe followup code %d\n", pi->followup);
3983 pi = free_pipe(pi);
3984 }
3985}
3986
Francis Laniel110b7692023-12-22 22:02:27 +01003987/*** Parsing routines ***/
3988
3989#ifndef debug_print_tree
3990static void debug_print_tree(struct pipe *pi, int lvl)
3991{
Francis Laniele7ca3a32023-12-22 22:02:42 +01003992 static const char *const PIPE[] ALIGN_PTR = {
Francis Laniel110b7692023-12-22 22:02:27 +01003993 [PIPE_SEQ] = "SEQ",
3994 [PIPE_AND] = "AND",
3995 [PIPE_OR ] = "OR" ,
3996 [PIPE_BG ] = "BG" ,
3997 };
3998 static const char *RES[] = {
3999 [RES_NONE ] = "NONE" ,
4000# if ENABLE_HUSH_IF
4001 [RES_IF ] = "IF" ,
4002 [RES_THEN ] = "THEN" ,
4003 [RES_ELIF ] = "ELIF" ,
4004 [RES_ELSE ] = "ELSE" ,
4005 [RES_FI ] = "FI" ,
4006# endif
4007# if ENABLE_HUSH_LOOPS
4008 [RES_FOR ] = "FOR" ,
4009 [RES_WHILE] = "WHILE",
4010 [RES_UNTIL] = "UNTIL",
4011 [RES_DO ] = "DO" ,
4012 [RES_DONE ] = "DONE" ,
4013# endif
4014# if ENABLE_HUSH_LOOPS || ENABLE_HUSH_CASE
4015 [RES_IN ] = "IN" ,
4016# endif
4017# if ENABLE_HUSH_CASE
4018 [RES_CASE ] = "CASE" ,
4019 [RES_CASE_IN ] = "CASE_IN" ,
4020 [RES_MATCH] = "MATCH",
4021 [RES_CASE_BODY] = "CASE_BODY",
4022 [RES_ESAC ] = "ESAC" ,
4023# endif
4024 [RES_XXXX ] = "XXXX" ,
4025 [RES_SNTX ] = "SNTX" ,
4026 };
Francis Laniele7ca3a32023-12-22 22:02:42 +01004027 static const char *const CMDTYPE[] ALIGN_PTR = {
Francis Laniel110b7692023-12-22 22:02:27 +01004028 "{}",
4029 "()",
4030 "[noglob]",
4031# if ENABLE_HUSH_FUNCTIONS
4032 "func()",
4033# endif
4034 };
4035
4036 int pin, prn;
4037
4038 pin = 0;
4039 while (pi) {
4040 fdprintf(2, "%*spipe %d #cmds:%d %sres_word=%s followup=%d %s\n",
4041 lvl*2, "",
4042 pin,
4043 pi->num_cmds,
4044 (IF_HAS_KEYWORDS(pi->pi_inverted ? "! " :) ""),
4045 RES[pi->res_word],
4046 pi->followup, PIPE[pi->followup]
4047 );
4048 prn = 0;
4049 while (prn < pi->num_cmds) {
4050 struct command *command = &pi->cmds[prn];
4051 char **argv = command->argv;
4052
4053 fdprintf(2, "%*s cmd %d assignment_cnt:%d",
4054 lvl*2, "", prn,
4055 command->assignment_cnt);
4056# if ENABLE_HUSH_LINENO_VAR
4057 fdprintf(2, " LINENO:%u", command->lineno);
4058# endif
4059 if (command->group) {
4060 fdprintf(2, " group %s: (argv=%p)%s%s\n",
4061 CMDTYPE[command->cmd_type],
4062 argv
4063# if !BB_MMU
4064 , " group_as_string:", command->group_as_string
4065# else
4066 , "", ""
4067# endif
4068 );
4069 debug_print_tree(command->group, lvl+1);
4070 prn++;
4071 continue;
4072 }
4073 if (argv) while (*argv) {
4074 fdprintf(2, " '%s'", *argv);
4075 argv++;
4076 }
Francis Laniel36836fc2023-12-22 22:02:28 +01004077#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01004078 if (command->redirects)
4079 fdprintf(2, " {redir}");
Francis Laniel36836fc2023-12-22 22:02:28 +01004080#endif /* __U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01004081 fdprintf(2, "\n");
4082 prn++;
4083 }
4084 pi = pi->next;
4085 pin++;
4086 }
4087}
4088#endif /* debug_print_tree */
4089
4090static struct pipe *new_pipe(void)
4091{
4092 struct pipe *pi;
4093 pi = xzalloc(sizeof(struct pipe));
4094 /*pi->res_word = RES_NONE; - RES_NONE is 0 anyway */
4095 return pi;
4096}
4097
4098/* Command (member of a pipe) is complete, or we start a new pipe
4099 * if ctx->command is NULL.
4100 * No errors possible here.
4101 */
4102static int done_command(struct parse_context *ctx)
4103{
4104 /* The command is really already in the pipe structure, so
4105 * advance the pipe counter and make a new, null command. */
4106 struct pipe *pi = ctx->pipe;
4107 struct command *command = ctx->command;
4108
4109#if 0 /* Instead we emit error message at run time */
4110 if (ctx->pending_redirect) {
4111 /* For example, "cmd >" (no filename to redirect to) */
4112 syntax_error("invalid redirect");
4113 ctx->pending_redirect = NULL;
4114 }
4115#endif
4116
4117 if (command) {
4118 if (IS_NULL_CMD(command)) {
4119 debug_printf_parse("done_command: skipping null cmd, num_cmds=%d\n", pi->num_cmds);
4120 goto clear_and_ret;
4121 }
4122 pi->num_cmds++;
4123 debug_printf_parse("done_command: ++num_cmds=%d\n", pi->num_cmds);
4124 //debug_print_tree(ctx->list_head, 20);
4125 } else {
4126 debug_printf_parse("done_command: initializing, num_cmds=%d\n", pi->num_cmds);
4127 }
4128
4129 /* Only real trickiness here is that the uncommitted
4130 * command structure is not counted in pi->num_cmds. */
4131 pi->cmds = xrealloc(pi->cmds, sizeof(*pi->cmds) * (pi->num_cmds+1));
4132 ctx->command = command = &pi->cmds[pi->num_cmds];
4133 clear_and_ret:
4134 memset(command, 0, sizeof(*command));
4135#if ENABLE_HUSH_LINENO_VAR
4136 command->lineno = G.parse_lineno;
4137 debug_printf_parse("command->lineno = G.parse_lineno (%u)\n", G.parse_lineno);
4138#endif
4139 return pi->num_cmds; /* used only for 0/nonzero check */
4140}
4141
4142static void done_pipe(struct parse_context *ctx, pipe_style type)
4143{
4144 int not_null;
4145
4146 debug_printf_parse("done_pipe entered, followup %d\n", type);
4147 /* Close previous command */
4148 not_null = done_command(ctx);
4149#if HAS_KEYWORDS
4150 ctx->pipe->pi_inverted = ctx->ctx_inverted;
4151 ctx->ctx_inverted = 0;
4152 ctx->pipe->res_word = ctx->ctx_res_w;
4153#endif
4154 if (type == PIPE_BG && ctx->list_head != ctx->pipe) {
4155 /* Necessary since && and || have precedence over &:
4156 * "cmd1 && cmd2 &" must spawn both cmds, not only cmd2,
4157 * in a backgrounded subshell.
4158 */
4159 struct pipe *pi;
4160 struct command *command;
4161
4162 /* Is this actually this construct, all pipes end with && or ||? */
4163 pi = ctx->list_head;
4164 while (pi != ctx->pipe) {
4165 if (pi->followup != PIPE_AND && pi->followup != PIPE_OR)
4166 goto no_conv;
4167 pi = pi->next;
4168 }
4169
4170 debug_printf_parse("BG with more than one pipe, converting to { p1 &&...pN; } &\n");
4171 pi->followup = PIPE_SEQ; /* close pN _not_ with "&"! */
4172 pi = xzalloc(sizeof(*pi));
4173 pi->followup = PIPE_BG;
4174 pi->num_cmds = 1;
4175 pi->cmds = xzalloc(sizeof(pi->cmds[0]));
4176 command = &pi->cmds[0];
4177 if (CMD_NORMAL != 0) /* "if xzalloc didn't do that already" */
4178 command->cmd_type = CMD_NORMAL;
4179 command->group = ctx->list_head;
4180#if !BB_MMU
4181 command->group_as_string = xstrndup(
4182 ctx->as_string.data,
4183 ctx->as_string.length - 1 /* do not copy last char, "&" */
4184 );
4185#endif
4186 /* Replace all pipes in ctx with one newly created */
4187 ctx->list_head = ctx->pipe = pi;
4188 /* for cases like "cmd && &", do not be tricked by last command
4189 * being null - the entire {...} & is NOT null! */
4190 not_null = 1;
4191 } else {
4192 no_conv:
4193 ctx->pipe->followup = type;
4194 }
4195
4196 /* Without this check, even just <enter> on command line generates
4197 * tree of three NOPs (!). Which is harmless but annoying.
4198 * IOW: it is safe to do it unconditionally. */
4199 if (not_null
4200#if ENABLE_HUSH_IF
4201 || ctx->ctx_res_w == RES_FI
4202#endif
4203#if ENABLE_HUSH_LOOPS
4204 || ctx->ctx_res_w == RES_DONE
4205 || ctx->ctx_res_w == RES_FOR
4206 || ctx->ctx_res_w == RES_IN
4207#endif
4208#if ENABLE_HUSH_CASE
4209 || ctx->ctx_res_w == RES_ESAC
4210#endif
4211 ) {
4212 struct pipe *new_p;
4213 debug_printf_parse("done_pipe: adding new pipe: "
Francis Laniel36836fc2023-12-22 22:02:28 +01004214#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01004215 "not_null:%d ctx->ctx_res_w:%d\n",
4216 not_null, ctx->ctx_res_w);
Francis Laniel36836fc2023-12-22 22:02:28 +01004217#else /* __U_BOOT__ */
4218 "not_null:%d\n",
4219 not_null);
4220#endif /* __U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01004221 new_p = new_pipe();
4222 ctx->pipe->next = new_p;
4223 ctx->pipe = new_p;
4224 /* RES_THEN, RES_DO etc are "sticky" -
4225 * they remain set for pipes inside if/while.
4226 * This is used to control execution.
4227 * RES_FOR and RES_IN are NOT sticky (needed to support
4228 * cases where variable or value happens to match a keyword):
4229 */
4230#if ENABLE_HUSH_LOOPS
4231 if (ctx->ctx_res_w == RES_FOR
4232 || ctx->ctx_res_w == RES_IN)
4233 ctx->ctx_res_w = RES_NONE;
4234#endif
4235#if ENABLE_HUSH_CASE
4236 if (ctx->ctx_res_w == RES_MATCH)
4237 ctx->ctx_res_w = RES_CASE_BODY;
4238 if (ctx->ctx_res_w == RES_CASE)
4239 ctx->ctx_res_w = RES_CASE_IN;
4240#endif
4241 ctx->command = NULL; /* trick done_command below */
4242 /* Create the memory for command, roughly:
4243 * ctx->pipe->cmds = new struct command;
4244 * ctx->command = &ctx->pipe->cmds[0];
4245 */
4246 done_command(ctx);
4247 //debug_print_tree(ctx->list_head, 10);
4248 }
4249 debug_printf_parse("done_pipe return\n");
4250}
4251
4252static void initialize_context(struct parse_context *ctx)
4253{
4254 memset(ctx, 0, sizeof(*ctx));
4255 if (MAYBE_ASSIGNMENT != 0)
4256 ctx->is_assignment = MAYBE_ASSIGNMENT;
4257 ctx->pipe = ctx->list_head = new_pipe();
4258 /* Create the memory for command, roughly:
4259 * ctx->pipe->cmds = new struct command;
4260 * ctx->command = &ctx->pipe->cmds[0];
4261 */
4262 done_command(ctx);
4263}
4264
4265/* If a reserved word is found and processed, parse context is modified
4266 * and 1 is returned.
4267 */
4268#if HAS_KEYWORDS
4269struct reserved_combo {
4270 char literal[6];
4271 unsigned char res;
4272 unsigned char assignment_flag;
4273 uint32_t flag;
4274};
4275enum {
4276 FLAG_END = (1 << RES_NONE ),
4277# if ENABLE_HUSH_IF
4278 FLAG_IF = (1 << RES_IF ),
4279 FLAG_THEN = (1 << RES_THEN ),
4280 FLAG_ELIF = (1 << RES_ELIF ),
4281 FLAG_ELSE = (1 << RES_ELSE ),
4282 FLAG_FI = (1 << RES_FI ),
4283# endif
4284# if ENABLE_HUSH_LOOPS
4285 FLAG_FOR = (1 << RES_FOR ),
4286 FLAG_WHILE = (1 << RES_WHILE),
4287 FLAG_UNTIL = (1 << RES_UNTIL),
4288 FLAG_DO = (1 << RES_DO ),
4289 FLAG_DONE = (1 << RES_DONE ),
4290 FLAG_IN = (1 << RES_IN ),
4291# endif
4292# if ENABLE_HUSH_CASE
4293 FLAG_MATCH = (1 << RES_MATCH),
4294 FLAG_ESAC = (1 << RES_ESAC ),
4295# endif
4296 FLAG_START = (1 << RES_XXXX ),
4297};
4298
4299static const struct reserved_combo* match_reserved_word(o_string *word)
4300{
4301 /* Mostly a list of accepted follow-up reserved words.
4302 * FLAG_END means we are done with the sequence, and are ready
4303 * to turn the compound list into a command.
4304 * FLAG_START means the word must start a new compound list.
4305 */
4306 static const struct reserved_combo reserved_list[] ALIGN4 = {
4307# if ENABLE_HUSH_IF
4308 { "!", RES_NONE, NOT_ASSIGNMENT , 0 },
4309 { "if", RES_IF, MAYBE_ASSIGNMENT, FLAG_THEN | FLAG_START },
4310 { "then", RES_THEN, MAYBE_ASSIGNMENT, FLAG_ELIF | FLAG_ELSE | FLAG_FI },
4311 { "elif", RES_ELIF, MAYBE_ASSIGNMENT, FLAG_THEN },
4312 { "else", RES_ELSE, MAYBE_ASSIGNMENT, FLAG_FI },
4313 { "fi", RES_FI, NOT_ASSIGNMENT , FLAG_END },
4314# endif
4315# if ENABLE_HUSH_LOOPS
4316 { "for", RES_FOR, NOT_ASSIGNMENT , FLAG_IN | FLAG_DO | FLAG_START },
4317 { "while", RES_WHILE, MAYBE_ASSIGNMENT, FLAG_DO | FLAG_START },
4318 { "until", RES_UNTIL, MAYBE_ASSIGNMENT, FLAG_DO | FLAG_START },
4319 { "in", RES_IN, NOT_ASSIGNMENT , FLAG_DO },
4320 { "do", RES_DO, MAYBE_ASSIGNMENT, FLAG_DONE },
4321 { "done", RES_DONE, NOT_ASSIGNMENT , FLAG_END },
4322# endif
4323# if ENABLE_HUSH_CASE
4324 { "case", RES_CASE, NOT_ASSIGNMENT , FLAG_MATCH | FLAG_START },
4325 { "esac", RES_ESAC, NOT_ASSIGNMENT , FLAG_END },
4326# endif
4327 };
4328 const struct reserved_combo *r;
4329
4330 for (r = reserved_list; r < reserved_list + ARRAY_SIZE(reserved_list); r++) {
4331 if (strcmp(word->data, r->literal) == 0)
4332 return r;
4333 }
4334 return NULL;
4335}
4336/* Return NULL: not a keyword, else: keyword
4337 */
4338static const struct reserved_combo* reserved_word(struct parse_context *ctx)
4339{
4340# if ENABLE_HUSH_CASE
4341 static const struct reserved_combo reserved_match = {
4342 "", RES_MATCH, NOT_ASSIGNMENT , FLAG_MATCH | FLAG_ESAC
4343 };
4344# endif
4345 const struct reserved_combo *r;
4346
4347 if (ctx->word.has_quoted_part)
4348 return 0;
4349 r = match_reserved_word(&ctx->word);
4350 if (!r)
4351 return r; /* NULL */
4352
4353 debug_printf("found reserved word %s, res %d\n", r->literal, r->res);
4354# if ENABLE_HUSH_CASE
4355 if (r->res == RES_IN && ctx->ctx_res_w == RES_CASE_IN) {
4356 /* "case word IN ..." - IN part starts first MATCH part */
4357 r = &reserved_match;
4358 } else
4359# endif
4360 if (r->flag == 0) { /* '!' */
4361 if (ctx->ctx_inverted) { /* bash doesn't accept '! ! true' */
4362 syntax_error("! ! command");
4363 ctx->ctx_res_w = RES_SNTX;
4364 }
4365 ctx->ctx_inverted = 1;
4366 return r;
4367 }
4368 if (r->flag & FLAG_START) {
4369 struct parse_context *old;
4370
4371 old = xmemdup(ctx, sizeof(*ctx));
4372 debug_printf_parse("push stack %p\n", old);
4373 initialize_context(ctx);
4374 ctx->stack = old;
4375 } else if (/*ctx->ctx_res_w == RES_NONE ||*/ !(ctx->old_flag & (1 << r->res))) {
4376 syntax_error_at(ctx->word.data);
4377 ctx->ctx_res_w = RES_SNTX;
4378 return r;
4379 } else {
4380 /* "{...} fi" is ok. "{...} if" is not
4381 * Example:
4382 * if { echo foo; } then { echo bar; } fi */
4383 if (ctx->command->group)
4384 done_pipe(ctx, PIPE_SEQ);
4385 }
4386
4387 ctx->ctx_res_w = r->res;
4388 ctx->old_flag = r->flag;
4389 ctx->is_assignment = r->assignment_flag;
4390 debug_printf_parse("ctx->is_assignment='%s'\n", assignment_flag[ctx->is_assignment]);
4391
4392 if (ctx->old_flag & FLAG_END) {
4393 struct parse_context *old;
4394
4395 done_pipe(ctx, PIPE_SEQ);
4396 debug_printf_parse("pop stack %p\n", ctx->stack);
4397 old = ctx->stack;
4398 old->command->group = ctx->list_head;
4399 old->command->cmd_type = CMD_NORMAL;
4400# if !BB_MMU
4401 /* At this point, the compound command's string is in
4402 * ctx->as_string... except for the leading keyword!
4403 * Consider this example: "echo a | if true; then echo a; fi"
4404 * ctx->as_string will contain "true; then echo a; fi",
4405 * with "if " remaining in old->as_string!
4406 */
4407 {
4408 char *str;
4409 int len = old->as_string.length;
4410 /* Concatenate halves */
4411 o_addstr(&old->as_string, ctx->as_string.data);
4412 o_free(&ctx->as_string);
4413 /* Find where leading keyword starts in first half */
4414 str = old->as_string.data + len;
4415 if (str > old->as_string.data)
4416 str--; /* skip whitespace after keyword */
4417 while (str > old->as_string.data && isalpha(str[-1]))
4418 str--;
4419 /* Ugh, we're done with this horrid hack */
4420 old->command->group_as_string = xstrdup(str);
4421 debug_printf_parse("pop, remembering as:'%s'\n",
4422 old->command->group_as_string);
4423 }
4424# endif
4425 *ctx = *old; /* physical copy */
4426 free(old);
4427 }
4428 return r;
4429}
4430#endif /* HAS_KEYWORDS */
4431
4432/* Word is complete, look at it and update parsing context.
4433 * Normal return is 0. Syntax errors return 1.
4434 * Note: on return, word is reset, but not o_free'd!
4435 */
4436static int done_word(struct parse_context *ctx)
4437{
4438 struct command *command = ctx->command;
4439
4440 debug_printf_parse("done_word entered: '%s' %p\n", ctx->word.data, command);
4441 if (ctx->word.length == 0 && !ctx->word.has_quoted_part) {
4442 debug_printf_parse("done_word return 0: true null, ignored\n");
4443 return 0;
4444 }
4445
Francis Laniel36836fc2023-12-22 22:02:28 +01004446#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01004447 if (ctx->pending_redirect) {
4448 /* We do not glob in e.g. >*.tmp case. bash seems to glob here
4449 * only if run as "bash", not "sh" */
4450 /* http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html
4451 * "2.7 Redirection
4452 * If the redirection operator is "<<" or "<<-", the word
4453 * that follows the redirection operator shall be
4454 * subjected to quote removal; it is unspecified whether
4455 * any of the other expansions occur. For the other
4456 * redirection operators, the word that follows the
4457 * redirection operator shall be subjected to tilde
4458 * expansion, parameter expansion, command substitution,
4459 * arithmetic expansion, and quote removal.
4460 * Pathname expansion shall not be performed
4461 * on the word by a non-interactive shell; an interactive
4462 * shell may perform it, but shall do so only when
4463 * the expansion would result in one word."
4464 */
4465//bash does not do parameter/command substitution or arithmetic expansion
4466//for _heredoc_ redirection word: these constructs look for exact eof marker
4467// as written:
4468// <<EOF$t
4469// <<EOF$((1))
4470// <<EOF`true` [this case also makes heredoc "quoted", a-la <<"EOF". Probably bash-4.3.43 bug]
4471
4472 ctx->pending_redirect->rd_filename = xstrdup(ctx->word.data);
4473 /* Cater for >\file case:
4474 * >\a creates file a; >\\a, >"\a", >"\\a" create file \a
4475 * Same with heredocs:
4476 * for <<\H delim is H; <<\\H, <<"\H", <<"\\H" - \H
4477 */
4478 if (ctx->pending_redirect->rd_type == REDIRECT_HEREDOC) {
4479 unbackslash(ctx->pending_redirect->rd_filename);
4480 /* Is it <<"HEREDOC"? */
4481 if (ctx->word.has_quoted_part) {
4482 ctx->pending_redirect->rd_dup |= HEREDOC_QUOTED;
4483 }
4484 }
4485 debug_printf_parse("word stored in rd_filename: '%s'\n", ctx->word.data);
4486 ctx->pending_redirect = NULL;
4487 } else {
Francis Laniel36836fc2023-12-22 22:02:28 +01004488#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01004489#if HAS_KEYWORDS
4490# if ENABLE_HUSH_CASE
4491 if (ctx->ctx_dsemicolon
4492 && strcmp(ctx->word.data, "esac") != 0 /* not "... pattern) cmd;; esac" */
4493 ) {
4494 /* already done when ctx_dsemicolon was set to 1: */
4495 /* ctx->ctx_res_w = RES_MATCH; */
4496 ctx->ctx_dsemicolon = 0;
4497 } else
4498# endif
4499# if defined(CMD_TEST2_SINGLEWORD_NOGLOB)
4500 if (command->cmd_type == CMD_TEST2_SINGLEWORD_NOGLOB
4501 && strcmp(ctx->word.data, "]]") == 0
4502 ) {
4503 /* allow "[[ ]] >file" etc */
4504 command->cmd_type = CMD_SINGLEWORD_NOGLOB;
4505 } else
4506# endif
4507 if (!command->argv /* if it's the first word... */
4508# if ENABLE_HUSH_LOOPS
4509 && ctx->ctx_res_w != RES_FOR /* ...not after FOR or IN */
4510 && ctx->ctx_res_w != RES_IN
4511# endif
4512# if ENABLE_HUSH_CASE
4513 && ctx->ctx_res_w != RES_CASE
4514# endif
4515 ) {
4516 const struct reserved_combo *reserved;
4517 reserved = reserved_word(ctx);
4518 debug_printf_parse("checking for reserved-ness: %d\n", !!reserved);
4519 if (reserved) {
4520# if ENABLE_HUSH_LINENO_VAR
4521/* Case:
4522 * "while ...; do
4523 * cmd ..."
4524 * If we don't close the pipe _now_, immediately after "do", lineno logic
4525 * sees "cmd" as starting at "do" - i.e., at the previous line.
4526 */
4527 if (0
4528 IF_HUSH_IF(|| reserved->res == RES_THEN)
4529 IF_HUSH_IF(|| reserved->res == RES_ELIF)
4530 IF_HUSH_IF(|| reserved->res == RES_ELSE)
4531 IF_HUSH_LOOPS(|| reserved->res == RES_DO)
4532 ) {
4533 done_pipe(ctx, PIPE_SEQ);
4534 }
4535# endif
4536 o_reset_to_empty_unquoted(&ctx->word);
4537 debug_printf_parse("done_word return %d\n",
4538 (ctx->ctx_res_w == RES_SNTX));
4539 return (ctx->ctx_res_w == RES_SNTX);
4540 }
4541# if defined(CMD_TEST2_SINGLEWORD_NOGLOB)
4542 if (strcmp(ctx->word.data, "[[") == 0) {
4543 command->cmd_type = CMD_TEST2_SINGLEWORD_NOGLOB;
4544 } else
4545# endif
4546# if defined(CMD_SINGLEWORD_NOGLOB)
4547 if (0
4548 /* In bash, local/export/readonly are special, args
4549 * are assignments and therefore expansion of them
4550 * should be "one-word" expansion:
4551 * $ export i=`echo 'a b'` # one arg: "i=a b"
4552 * compare with:
4553 * $ ls i=`echo 'a b'` # two args: "i=a" and "b"
4554 * ls: cannot access i=a: No such file or directory
4555 * ls: cannot access b: No such file or directory
4556 * Note: bash 3.2.33(1) does this only if export word
4557 * itself is not quoted:
4558 * $ export i=`echo 'aaa bbb'`; echo "$i"
4559 * aaa bbb
4560 * $ "export" i=`echo 'aaa bbb'`; echo "$i"
4561 * aaa
4562 */
4563 IF_HUSH_LOCAL( || strcmp(ctx->word.data, "local") == 0)
4564 IF_HUSH_EXPORT( || strcmp(ctx->word.data, "export") == 0)
4565 IF_HUSH_READONLY(|| strcmp(ctx->word.data, "readonly") == 0)
4566 ) {
4567 command->cmd_type = CMD_SINGLEWORD_NOGLOB;
4568 }
4569# else
4570 { /* empty block to pair "if ... else" */ }
4571# endif
4572 }
4573#endif /* HAS_KEYWORDS */
4574
4575 if (command->group) {
4576 /* "{ echo foo; } echo bar" - bad */
4577 syntax_error_at(ctx->word.data);
4578 debug_printf_parse("done_word return 1: syntax error, "
4579 "groups and arglists don't mix\n");
4580 return 1;
4581 }
4582
4583 /* If this word wasn't an assignment, next ones definitely
4584 * can't be assignments. Even if they look like ones. */
4585 if (ctx->is_assignment != DEFINITELY_ASSIGNMENT
4586 && ctx->is_assignment != WORD_IS_KEYWORD
4587 ) {
4588 ctx->is_assignment = NOT_ASSIGNMENT;
4589 } else {
4590 if (ctx->is_assignment == DEFINITELY_ASSIGNMENT) {
4591 command->assignment_cnt++;
4592 debug_printf_parse("++assignment_cnt=%d\n", command->assignment_cnt);
4593 }
4594 debug_printf_parse("ctx->is_assignment was:'%s'\n", assignment_flag[ctx->is_assignment]);
4595 ctx->is_assignment = MAYBE_ASSIGNMENT;
4596 }
4597 debug_printf_parse("ctx->is_assignment='%s'\n", assignment_flag[ctx->is_assignment]);
4598 command->argv = add_string_to_strings(command->argv, xstrdup(ctx->word.data));
Francis Laniel36836fc2023-12-22 22:02:28 +01004599#ifdef __U_BOOT__
4600 command->argc++;
4601#endif /* __U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01004602 debug_print_strings("word appended to argv", command->argv);
Francis Laniel36836fc2023-12-22 22:02:28 +01004603
4604#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01004605 }
Francis Laniel36836fc2023-12-22 22:02:28 +01004606#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01004607
4608#if ENABLE_HUSH_LOOPS
4609 if (ctx->ctx_res_w == RES_FOR) {
4610 if (ctx->word.has_quoted_part
4611 || endofname(command->argv[0])[0] != '\0'
4612 ) {
4613 /* bash says just "not a valid identifier" */
Francis Laniele7ca3a32023-12-22 22:02:42 +01004614 syntax_error("bad for loop variable");
Francis Laniel110b7692023-12-22 22:02:27 +01004615 return 1;
4616 }
4617 /* Force FOR to have just one word (variable name) */
4618 /* NB: basically, this makes hush see "for v in ..."
4619 * syntax as if it is "for v; in ...". FOR and IN become
4620 * two pipe structs in parse tree. */
4621 done_pipe(ctx, PIPE_SEQ);
4622 }
4623#endif
4624#if ENABLE_HUSH_CASE
4625 /* Force CASE to have just one word */
4626 if (ctx->ctx_res_w == RES_CASE) {
4627 done_pipe(ctx, PIPE_SEQ);
4628 }
4629#endif
4630
4631 o_reset_to_empty_unquoted(&ctx->word);
4632
4633 debug_printf_parse("done_word return 0\n");
4634 return 0;
4635}
4636
Francis Laniel36836fc2023-12-22 22:02:28 +01004637#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01004638/* Peek ahead in the input to find out if we have a "&n" construct,
4639 * as in "2>&1", that represents duplicating a file descriptor.
4640 * Return:
4641 * REDIRFD_CLOSE if >&- "close fd" construct is seen,
4642 * REDIRFD_SYNTAX_ERR if syntax error,
4643 * REDIRFD_TO_FILE if no & was seen,
4644 * or the number found.
4645 */
4646#if BB_MMU
4647#define parse_redir_right_fd(as_string, input) \
4648 parse_redir_right_fd(input)
4649#endif
4650static int parse_redir_right_fd(o_string *as_string, struct in_str *input)
4651{
4652 int ch, d, ok;
4653
4654 ch = i_peek(input);
4655 if (ch != '&')
4656 return REDIRFD_TO_FILE;
4657
4658 ch = i_getch(input); /* get the & */
4659 nommu_addchr(as_string, ch);
4660 ch = i_peek(input);
4661 if (ch == '-') {
4662 ch = i_getch(input);
4663 nommu_addchr(as_string, ch);
4664 return REDIRFD_CLOSE;
4665 }
4666 d = 0;
4667 ok = 0;
4668 while (ch != EOF && isdigit(ch)) {
4669 d = d*10 + (ch-'0');
4670 ok = 1;
4671 ch = i_getch(input);
4672 nommu_addchr(as_string, ch);
4673 ch = i_peek(input);
4674 }
4675 if (ok) return d;
4676
4677//TODO: this is the place to catch ">&file" bashism (redirect both fd 1 and 2)
4678
4679 bb_simple_error_msg("ambiguous redirect");
4680 return REDIRFD_SYNTAX_ERR;
4681}
4682
4683/* Return code is 0 normal, 1 if a syntax error is detected
4684 */
4685static int parse_redirect(struct parse_context *ctx,
4686 int fd,
4687 redir_type style,
4688 struct in_str *input)
4689{
4690 struct command *command = ctx->command;
4691 struct redir_struct *redir;
4692 struct redir_struct **redirp;
4693 int dup_num;
4694
4695 dup_num = REDIRFD_TO_FILE;
4696 if (style != REDIRECT_HEREDOC) {
4697 /* Check for a '>&1' type redirect */
4698 dup_num = parse_redir_right_fd(&ctx->as_string, input);
4699 if (dup_num == REDIRFD_SYNTAX_ERR)
4700 return 1;
4701 } else {
4702 int ch = i_peek_and_eat_bkslash_nl(input);
4703 dup_num = (ch == '-'); /* HEREDOC_SKIPTABS bit is 1 */
4704 if (dup_num) { /* <<-... */
4705 ch = i_getch(input);
4706 nommu_addchr(&ctx->as_string, ch);
4707 ch = i_peek(input);
4708 }
4709 }
4710
4711 if (style == REDIRECT_OVERWRITE && dup_num == REDIRFD_TO_FILE) {
4712 int ch = i_peek_and_eat_bkslash_nl(input);
4713 if (ch == '|') {
4714 /* >|FILE redirect ("clobbering" >).
4715 * Since we do not support "set -o noclobber" yet,
4716 * >| and > are the same for now. Just eat |.
4717 */
4718 ch = i_getch(input);
4719 nommu_addchr(&ctx->as_string, ch);
4720 }
4721 }
4722
4723 /* Create a new redir_struct and append it to the linked list */
4724 redirp = &command->redirects;
4725 while ((redir = *redirp) != NULL) {
4726 redirp = &(redir->next);
4727 }
4728 *redirp = redir = xzalloc(sizeof(*redir));
4729 /* redir->next = NULL; */
4730 /* redir->rd_filename = NULL; */
4731 redir->rd_type = style;
4732 redir->rd_fd = (fd == -1) ? redir_table[style].default_fd : fd;
4733
4734 debug_printf_parse("redirect type %d %s\n", redir->rd_fd,
4735 redir_table[style].descrip);
4736
4737 redir->rd_dup = dup_num;
4738 if (style != REDIRECT_HEREDOC && dup_num != REDIRFD_TO_FILE) {
4739 /* Erik had a check here that the file descriptor in question
4740 * is legit; I postpone that to "run time"
4741 * A "-" representation of "close me" shows up as a -3 here */
4742 debug_printf_parse("duplicating redirect '%d>&%d'\n",
4743 redir->rd_fd, redir->rd_dup);
4744 } else {
4745#if 0 /* Instead we emit error message at run time */
4746 if (ctx->pending_redirect) {
4747 /* For example, "cmd > <file" */
4748 syntax_error("invalid redirect");
4749 }
4750#endif
4751 /* Set ctx->pending_redirect, so we know what to do at the
4752 * end of the next parsed word. */
4753 ctx->pending_redirect = redir;
4754 }
4755 return 0;
4756}
4757
4758/* If a redirect is immediately preceded by a number, that number is
4759 * supposed to tell which file descriptor to redirect. This routine
4760 * looks for such preceding numbers. In an ideal world this routine
4761 * needs to handle all the following classes of redirects...
4762 * echo 2>foo # redirects fd 2 to file "foo", nothing passed to echo
4763 * echo 49>foo # redirects fd 49 to file "foo", nothing passed to echo
4764 * echo -2>foo # redirects fd 1 to file "foo", "-2" passed to echo
4765 * echo 49x>foo # redirects fd 1 to file "foo", "49x" passed to echo
4766 *
4767 * http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html
4768 * "2.7 Redirection
4769 * ... If n is quoted, the number shall not be recognized as part of
4770 * the redirection expression. For example:
4771 * echo \2>a
4772 * writes the character 2 into file a"
4773 * We are getting it right by setting ->has_quoted_part on any \<char>
4774 *
4775 * A -1 return means no valid number was found,
4776 * the caller should use the appropriate default for this redirection.
4777 */
4778static int redirect_opt_num(o_string *o)
4779{
4780 int num;
4781
4782 if (o->data == NULL)
4783 return -1;
4784 num = bb_strtou(o->data, NULL, 10);
4785 if (errno || num < 0)
4786 return -1;
4787 o_reset_to_empty_unquoted(o);
4788 return num;
4789}
4790
4791#if BB_MMU
4792#define fetch_till_str(as_string, input, word, skip_tabs) \
4793 fetch_till_str(input, word, skip_tabs)
4794#endif
4795static char *fetch_till_str(o_string *as_string,
4796 struct in_str *input,
4797 const char *word,
4798 int heredoc_flags)
4799{
4800 o_string heredoc = NULL_O_STRING;
4801 unsigned past_EOL;
4802 int prev = 0; /* not \ */
4803 int ch;
4804
4805 /* Starting with "" is necessary for this case:
4806 * cat <<EOF
4807 *
4808 * xxx
4809 * EOF
4810 */
4811 heredoc.data = xzalloc(1); /* start as "", not as NULL */
4812
4813 goto jump_in;
4814
4815 while (1) {
4816 ch = i_getch(input);
4817 if (ch != EOF)
4818 nommu_addchr(as_string, ch);
4819 if (ch == '\n' || ch == EOF) {
4820 check_heredoc_end:
Francis Laniel36836fc2023-12-22 22:02:28 +01004821#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01004822 if ((heredoc_flags & HEREDOC_QUOTED) || prev != '\\') {
Francis Laniel36836fc2023-12-22 22:02:28 +01004823#else /* __U_BOOT__ */
4824 if (prev != '\\') {
4825#endif
Francis Laniel110b7692023-12-22 22:02:27 +01004826 /* End-of-line, and not a line continuation */
4827 if (strcmp(heredoc.data + past_EOL, word) == 0) {
4828 heredoc.data[past_EOL] = '\0';
4829 debug_printf_heredoc("parsed '%s' heredoc '%s'\n", word, heredoc.data);
4830 return heredoc.data;
4831 }
4832 if (ch == '\n') {
4833 /* This is a new line.
4834 * Remember position and backslash-escaping status.
4835 */
4836 o_addchr(&heredoc, ch);
4837 prev = ch;
4838 jump_in:
4839 past_EOL = heredoc.length;
4840 /* Get 1st char of next line, possibly skipping leading tabs */
4841 do {
4842 ch = i_getch(input);
4843 if (ch != EOF)
4844 nommu_addchr(as_string, ch);
Francis Laniel36836fc2023-12-22 22:02:28 +01004845#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01004846 } while ((heredoc_flags & HEREDOC_SKIPTABS) && ch == '\t');
Francis Laniel36836fc2023-12-22 22:02:28 +01004847#else /* __U_BOOT__ */
4848 } while (ch == '\t');
4849#endif
Francis Laniel110b7692023-12-22 22:02:27 +01004850 /* If this immediately ended the line,
4851 * go back to end-of-line checks.
4852 */
4853 if (ch == '\n')
4854 goto check_heredoc_end;
4855 }
4856 } else {
4857 /* Backslash-line continuation in an unquoted
4858 * heredoc. This does not need special handling
4859 * for heredoc body (unquoted heredocs are
4860 * expanded on "execution" and that would take
4861 * care of this case too), but not the case
4862 * of line continuation *in terminator*:
4863 * cat <<EOF
4864 * Ok1
4865 * EO\
4866 * F
4867 */
4868 heredoc.data[--heredoc.length] = '\0';
4869 prev = 0; /* not '\' */
4870 continue;
4871 }
4872 }
4873 if (ch == EOF) {
4874 o_free(&heredoc);
4875 return NULL; /* error */
4876 }
4877 o_addchr(&heredoc, ch);
4878 nommu_addchr(as_string, ch);
4879 if (prev == '\\' && ch == '\\')
4880 /* Correctly handle foo\\<eol> (not a line cont.) */
4881 prev = 0; /* not '\' */
4882 else
4883 prev = ch;
4884 }
4885}
Francis Laniel36836fc2023-12-22 22:02:28 +01004886#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01004887
4888/* Look at entire parse tree for not-yet-loaded REDIRECT_HEREDOCs
4889 * and load them all. There should be exactly heredoc_cnt of them.
4890 */
4891#if BB_MMU
4892#define fetch_heredocs(as_string, pi, heredoc_cnt, input) \
4893 fetch_heredocs(pi, heredoc_cnt, input)
4894#endif
4895static int fetch_heredocs(o_string *as_string, struct pipe *pi, int heredoc_cnt, struct in_str *input)
4896{
4897 while (pi && heredoc_cnt) {
4898 int i;
4899 struct command *cmd = pi->cmds;
4900
4901 debug_printf_heredoc("fetch_heredocs: num_cmds:%d cmd argv0:'%s'\n",
4902 pi->num_cmds,
4903 cmd->argv ? cmd->argv[0] : "NONE"
4904 );
4905 for (i = 0; i < pi->num_cmds; i++) {
Francis Laniel36836fc2023-12-22 22:02:28 +01004906#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01004907 struct redir_struct *redir = cmd->redirects;
4908
Francis Laniel36836fc2023-12-22 22:02:28 +01004909#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01004910 debug_printf_heredoc("fetch_heredocs: %d cmd argv0:'%s'\n",
4911 i, cmd->argv ? cmd->argv[0] : "NONE");
Francis Laniel36836fc2023-12-22 22:02:28 +01004912#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01004913 while (redir) {
4914 if (redir->rd_type == REDIRECT_HEREDOC) {
4915 char *p;
4916
4917 redir->rd_type = REDIRECT_HEREDOC2;
4918 /* redir->rd_dup is (ab)used to indicate <<- */
4919 p = fetch_till_str(as_string, input,
4920 redir->rd_filename, redir->rd_dup);
4921 if (!p) {
4922 syntax_error("unexpected EOF in here document");
4923 return -1;
4924 }
4925 free(redir->rd_filename);
4926 redir->rd_filename = p;
4927 heredoc_cnt--;
4928 }
4929 redir = redir->next;
4930 }
Francis Laniel36836fc2023-12-22 22:02:28 +01004931#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01004932 if (cmd->group) {
4933 //bb_error_msg("%s:%u heredoc_cnt:%d", __func__, __LINE__, heredoc_cnt);
4934 heredoc_cnt = fetch_heredocs(as_string, cmd->group, heredoc_cnt, input);
4935 //bb_error_msg("%s:%u heredoc_cnt:%d", __func__, __LINE__, heredoc_cnt);
4936 if (heredoc_cnt < 0)
4937 return heredoc_cnt; /* error */
4938 }
4939 cmd++;
4940 }
4941 pi = pi->next;
4942 }
4943 return heredoc_cnt;
4944}
4945
Francis Laniel110b7692023-12-22 22:02:27 +01004946static int run_list(struct pipe *pi);
4947#if BB_MMU
4948#define parse_stream(pstring, heredoc_cnt_ptr, input, end_trigger) \
4949 parse_stream(heredoc_cnt_ptr, input, end_trigger)
4950#endif
4951static struct pipe *parse_stream(char **pstring,
4952 int *heredoc_cnt_ptr,
4953 struct in_str *input,
4954 int end_trigger);
4955
4956/* Returns number of heredocs not yet consumed,
4957 * or -1 on error.
4958 */
4959static int parse_group(struct parse_context *ctx,
4960 struct in_str *input, int ch)
4961{
4962 /* ctx->word contains characters seen prior to ( or {.
4963 * Typically it's empty, but for function defs,
4964 * it contains function name (without '()'). */
4965#if BB_MMU
4966# define as_string NULL
4967#else
4968 char *as_string = NULL;
4969#endif
4970 struct pipe *pipe_list;
4971 int heredoc_cnt = 0;
4972 int endch;
4973 struct command *command = ctx->command;
4974
4975 debug_printf_parse("parse_group entered\n");
4976#if ENABLE_HUSH_FUNCTIONS
4977 if (ch == '(' && !ctx->word.has_quoted_part) {
4978 if (ctx->word.length)
4979 if (done_word(ctx))
4980 return -1;
4981 if (!command->argv)
4982 goto skip; /* (... */
4983 if (command->argv[1]) { /* word word ... (... */
4984 syntax_error_unexpected_ch('(');
4985 return -1;
4986 }
4987 /* it is "word(..." or "word (..." */
4988 do
4989 ch = i_getch(input);
4990 while (ch == ' ' || ch == '\t');
4991 if (ch != ')') {
4992 syntax_error_unexpected_ch(ch);
4993 return -1;
4994 }
4995 nommu_addchr(&ctx->as_string, ch);
4996 do
4997 ch = i_getch(input);
4998 while (ch == ' ' || ch == '\t' || ch == '\n');
4999 if (ch != '{' && ch != '(') {
5000 syntax_error_unexpected_ch(ch);
5001 return -1;
5002 }
Francis Laniele7ca3a32023-12-22 22:02:42 +01005003//bash allows functions named "123", "..", "return"!
5004// if (endofname(command->argv[0])[0] != '\0') {
5005// syntax_error("bad function name");
5006// return -1;
5007// }
Francis Laniel110b7692023-12-22 22:02:27 +01005008 nommu_addchr(&ctx->as_string, ch);
5009 command->cmd_type = CMD_FUNCDEF;
5010 goto skip;
5011 }
5012#endif
5013
5014#if 0 /* Prevented by caller */
5015 if (command->argv /* word [word]{... */
5016 || ctx->word.length /* word{... */
5017 || ctx->word.has_quoted_part /* ""{... */
5018 ) {
5019 syntax_error(NULL);
5020 debug_printf_parse("parse_group return -1: "
5021 "syntax error, groups and arglists don't mix\n");
5022 return -1;
5023 }
5024#endif
5025
Francis Laniel36836fc2023-12-22 22:02:28 +01005026#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01005027 IF_HUSH_FUNCTIONS(skip:)
Francis Laniel36836fc2023-12-22 22:02:28 +01005028#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01005029
5030 endch = '}';
5031 if (ch == '(') {
5032 endch = ')';
Francis Laniel36836fc2023-12-22 22:02:28 +01005033#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01005034 IF_HUSH_FUNCTIONS(if (command->cmd_type != CMD_FUNCDEF))
5035 command->cmd_type = CMD_SUBSHELL;
Francis Laniel36836fc2023-12-22 22:02:28 +01005036#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01005037 } else {
5038 /* bash does not allow "{echo...", requires whitespace */
5039 ch = i_peek(input);
5040 if (ch != ' ' && ch != '\t' && ch != '\n'
5041 && ch != '(' /* but "{(..." is allowed (without whitespace) */
5042 ) {
5043 syntax_error_unexpected_ch(ch);
5044 return -1;
5045 }
5046 if (ch != '(') {
5047 ch = i_getch(input);
5048 nommu_addchr(&ctx->as_string, ch);
5049 }
5050 }
5051
5052 debug_printf_heredoc("calling parse_stream, heredoc_cnt:%d\n", heredoc_cnt);
5053 pipe_list = parse_stream(&as_string, &heredoc_cnt, input, endch);
5054 debug_printf_heredoc("parse_stream returned: heredoc_cnt:%d\n", heredoc_cnt);
5055#if !BB_MMU
5056 if (as_string)
5057 o_addstr(&ctx->as_string, as_string);
5058#endif
5059
5060 /* empty ()/{} or parse error? */
5061 if (!pipe_list || pipe_list == ERR_PTR) {
5062 /* parse_stream already emitted error msg */
5063 if (!BB_MMU)
5064 free(as_string);
5065 debug_printf_parse("parse_group return -1: "
5066 "parse_stream returned %p\n", pipe_list);
5067 return -1;
5068 }
5069#if !BB_MMU
5070 as_string[strlen(as_string) - 1] = '\0'; /* plink ')' or '}' */
5071 command->group_as_string = as_string;
5072 debug_printf_parse("end of group, remembering as:'%s'\n",
5073 command->group_as_string);
5074#endif
5075
5076#if ENABLE_HUSH_FUNCTIONS
5077 /* Convert "f() (cmds)" to "f() {(cmds)}" */
5078 if (command->cmd_type == CMD_FUNCDEF && endch == ')') {
5079 struct command *cmd2;
5080
5081 cmd2 = xzalloc(sizeof(*cmd2));
5082 cmd2->cmd_type = CMD_SUBSHELL;
5083 cmd2->group = pipe_list;
5084# if !BB_MMU
5085//UNTESTED!
5086 cmd2->group_as_string = command->group_as_string;
5087 command->group_as_string = xasprintf("(%s)", command->group_as_string);
5088# endif
5089
5090 pipe_list = new_pipe();
5091 pipe_list->cmds = cmd2;
5092 pipe_list->num_cmds = 1;
5093 }
5094#endif
5095
5096 command->group = pipe_list;
5097
5098 debug_printf_parse("parse_group return %d\n", heredoc_cnt);
5099 return heredoc_cnt;
5100 /* command remains "open", available for possible redirects */
5101#undef as_string
5102}
5103
Francis Laniel36836fc2023-12-22 22:02:28 +01005104#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01005105#if ENABLE_HUSH_TICK || ENABLE_FEATURE_SH_MATH || ENABLE_HUSH_DOLLAR_OPS
5106/* Subroutines for copying $(...) and `...` things */
5107/* '...' */
5108static int add_till_single_quote(o_string *dest, struct in_str *input)
5109{
5110 while (1) {
5111 int ch = i_getch(input);
5112 if (ch == EOF) {
5113 syntax_error_unterm_ch('\'');
5114 return 0;
5115 }
5116 if (ch == '\'')
5117 return 1;
5118 o_addchr(dest, ch);
5119 }
5120}
5121static int add_till_single_quote_dquoted(o_string *dest, struct in_str *input)
5122{
5123 while (1) {
5124 int ch = i_getch(input);
5125 if (ch == EOF) {
5126 syntax_error_unterm_ch('\'');
5127 return 0;
5128 }
5129 if (ch == '\'')
5130 return 1;
5131 o_addqchr(dest, ch);
5132 }
5133}
Francis Laniel36836fc2023-12-22 22:02:28 +01005134
Francis Laniel110b7692023-12-22 22:02:27 +01005135/* "...\"...`..`...." - do we need to handle "...$(..)..." too? */
5136static int add_till_backquote(o_string *dest, struct in_str *input, int in_dquote);
5137static int add_till_double_quote(o_string *dest, struct in_str *input)
5138{
5139 while (1) {
5140 int ch = i_getch(input);
5141 if (ch == EOF) {
5142 syntax_error_unterm_ch('"');
5143 return 0;
5144 }
5145 if (ch == '"')
5146 return 1;
5147 if (ch == '\\') { /* \x. Copy both chars. */
5148 o_addchr(dest, ch);
5149 ch = i_getch(input);
5150 }
5151 o_addchr(dest, ch);
5152 if (ch == '`') {
5153 if (!add_till_backquote(dest, input, /*in_dquote:*/ 1))
5154 return 0;
5155 o_addchr(dest, ch);
5156 continue;
5157 }
5158 //if (ch == '$') ...
5159 }
5160}
Francis Laniel36836fc2023-12-22 22:02:28 +01005161
Francis Laniel110b7692023-12-22 22:02:27 +01005162/* Process `cmd` - copy contents until "`" is seen. Complicated by
5163 * \` quoting.
5164 * "Within the backquoted style of command substitution, backslash
5165 * shall retain its literal meaning, except when followed by: '$', '`', or '\'.
5166 * The search for the matching backquote shall be satisfied by the first
5167 * backquote found without a preceding backslash; during this search,
5168 * if a non-escaped backquote is encountered within a shell comment,
5169 * a here-document, an embedded command substitution of the $(command)
5170 * form, or a quoted string, undefined results occur. A single-quoted
5171 * or double-quoted string that begins, but does not end, within the
5172 * "`...`" sequence produces undefined results."
5173 * Example Output
5174 * echo `echo '\'TEST\`echo ZZ\`BEST` \TESTZZBEST
5175 */
5176static int add_till_backquote(o_string *dest, struct in_str *input, int in_dquote)
5177{
5178 while (1) {
5179 int ch = i_getch(input);
5180 if (ch == '`')
5181 return 1;
5182 if (ch == '\\') {
5183 /* \x. Copy both unless it is \`, \$, \\ and maybe \" */
5184 ch = i_getch(input);
5185 if (ch != '`'
5186 && ch != '$'
5187 && ch != '\\'
5188 && (!in_dquote || ch != '"')
5189 ) {
5190 o_addchr(dest, '\\');
5191 }
5192 }
5193 if (ch == EOF) {
5194 syntax_error_unterm_ch('`');
5195 return 0;
5196 }
5197 o_addchr(dest, ch);
5198 }
5199}
5200/* Process $(cmd) - copy contents until ")" is seen. Complicated by
5201 * quoting and nested ()s.
5202 * "With the $(command) style of command substitution, all characters
5203 * following the open parenthesis to the matching closing parenthesis
5204 * constitute the command. Any valid shell script can be used for command,
5205 * except a script consisting solely of redirections which produces
5206 * unspecified results."
5207 * Example Output
5208 * echo $(echo '(TEST)' BEST) (TEST) BEST
5209 * echo $(echo 'TEST)' BEST) TEST) BEST
5210 * echo $(echo \(\(TEST\) BEST) ((TEST) BEST
5211 *
5212 * Also adapted to eat ${var%...} and $((...)) constructs, since ... part
5213 * can contain arbitrary constructs, just like $(cmd).
5214 * In bash compat mode, it needs to also be able to stop on ':' or '/'
5215 * for ${var:N[:M]} and ${var/P[/R]} parsing.
5216 */
5217#define DOUBLE_CLOSE_CHAR_FLAG 0x80
5218static int add_till_closing_bracket(o_string *dest, struct in_str *input, unsigned end_ch)
5219{
5220 int ch;
5221 char dbl = end_ch & DOUBLE_CLOSE_CHAR_FLAG;
5222# if BASH_SUBSTR || BASH_PATTERN_SUBST
5223 char end_char2 = end_ch >> 8;
5224# endif
5225 end_ch &= (DOUBLE_CLOSE_CHAR_FLAG - 1);
5226
5227# if ENABLE_HUSH_INTERACTIVE
5228 G.promptmode = 1; /* PS2 */
5229# endif
5230 debug_printf_prompt("%s promptmode=%d\n", __func__, G.promptmode);
5231
5232 while (1) {
5233 ch = i_getch(input);
5234 if (ch == EOF) {
5235 syntax_error_unterm_ch(end_ch);
5236 return 0;
5237 }
5238 if (ch == end_ch
5239# if BASH_SUBSTR || BASH_PATTERN_SUBST
5240 || ch == end_char2
5241# endif
5242 ) {
5243 if (!dbl)
5244 break;
5245 /* we look for closing )) of $((EXPR)) */
5246 if (i_peek_and_eat_bkslash_nl(input) == end_ch) {
5247 i_getch(input); /* eat second ')' */
5248 break;
5249 }
5250 }
5251 o_addchr(dest, ch);
5252 //bb_error_msg("%s:o_addchr('%c')", __func__, ch);
5253 if (ch == '(' || ch == '{') {
5254 ch = (ch == '(' ? ')' : '}');
5255 if (!add_till_closing_bracket(dest, input, ch))
5256 return 0;
5257 o_addchr(dest, ch);
5258 continue;
5259 }
5260 if (ch == '\'') {
5261 if (!add_till_single_quote(dest, input))
5262 return 0;
5263 o_addchr(dest, ch);
5264 continue;
5265 }
5266 if (ch == '"') {
5267 if (!add_till_double_quote(dest, input))
5268 return 0;
5269 o_addchr(dest, ch);
5270 continue;
5271 }
5272 if (ch == '`') {
5273 if (!add_till_backquote(dest, input, /*in_dquote:*/ 0))
5274 return 0;
5275 o_addchr(dest, ch);
5276 continue;
5277 }
5278 if (ch == '\\') {
5279 /* \x. Copy verbatim. Important for \(, \) */
5280 ch = i_getch(input);
5281 if (ch == EOF) {
5282 syntax_error_unterm_ch(end_ch);
5283 return 0;
5284 }
5285# if 0
5286 if (ch == '\n') {
5287 /* "backslash+newline", ignore both */
5288 o_delchr(dest); /* undo insertion of '\' */
5289 continue;
5290 }
5291# endif
5292 o_addchr(dest, ch);
5293 //bb_error_msg("%s:o_addchr('%c') after '\\'", __func__, ch);
5294 continue;
5295 }
5296 }
5297 debug_printf_parse("%s return '%s' ch:'%c'\n", __func__, dest->data, ch);
5298 return ch;
5299}
5300#endif /* ENABLE_HUSH_TICK || ENABLE_FEATURE_SH_MATH || ENABLE_HUSH_DOLLAR_OPS */
5301
5302#if BASH_DOLLAR_SQUOTE
5303/* Return code: 1 for "found and parsed", 0 for "seen something else" */
5304# if BB_MMU
5305#define parse_dollar_squote(as_string, dest, input) \
5306 parse_dollar_squote(dest, input)
5307#define as_string NULL
5308# endif
5309static int parse_dollar_squote(o_string *as_string, o_string *dest, struct in_str *input)
5310{
5311 int start;
5312 int ch = i_peek_and_eat_bkslash_nl(input); /* first character after the $ */
5313 debug_printf_parse("parse_dollar_squote entered: ch='%c'\n", ch);
5314 if (ch != '\'')
5315 return 0;
5316
5317 dest->has_quoted_part = 1;
5318 start = dest->length;
5319
5320 ch = i_getch(input); /* eat ' */
5321 nommu_addchr(as_string, ch);
5322 while (1) {
5323 ch = i_getch(input);
5324 nommu_addchr(as_string, ch);
5325 if (ch == EOF) {
5326 syntax_error_unterm_ch('\'');
5327 return 0;
5328 }
5329 if (ch == '\'')
5330 break;
5331 if (ch == SPECIAL_VAR_SYMBOL) {
5332 /* Convert raw ^C to corresponding special variable reference */
5333 o_addchr(dest, SPECIAL_VAR_SYMBOL);
5334 o_addchr(dest, SPECIAL_VAR_QUOTED_SVS);
5335 /* will addchr() another SPECIAL_VAR_SYMBOL (see after the if() block) */
5336 } else if (ch == '\\') {
5337 static const char C_escapes[] ALIGN1 = "nrbtfav""x\\01234567";
5338
5339 ch = i_getch(input);
5340 nommu_addchr(as_string, ch);
5341 if (strchr(C_escapes, ch)) {
5342 char buf[4];
5343 char *p = buf;
5344 int cnt = 2;
5345
5346 buf[0] = ch;
5347 if ((unsigned char)(ch - '0') <= 7) { /* \ooo */
5348 do {
5349 ch = i_peek(input);
5350 if ((unsigned char)(ch - '0') > 7)
5351 break;
5352 *++p = ch = i_getch(input);
5353 nommu_addchr(as_string, ch);
5354 } while (--cnt != 0);
5355 } else if (ch == 'x') { /* \xHH */
5356 do {
5357 ch = i_peek(input);
5358 if (!isxdigit(ch))
5359 break;
5360 *++p = ch = i_getch(input);
5361 nommu_addchr(as_string, ch);
5362 } while (--cnt != 0);
5363 if (cnt == 2) { /* \x but next char is "bad" */
5364 ch = 'x';
5365 goto unrecognized;
5366 }
5367 } /* else simple seq like \\ or \t */
5368 *++p = '\0';
5369 p = buf;
5370 ch = bb_process_escape_sequence((void*)&p);
5371 //bb_error_msg("buf:'%s' ch:%x", buf, ch);
5372 if (ch == '\0')
5373 continue; /* bash compat: $'...\0...' emits nothing */
5374 } else { /* unrecognized "\z": encode both chars unless ' or " */
5375 if (ch != '\'' && ch != '"') {
5376 unrecognized:
5377 o_addqchr(dest, '\\');
5378 }
5379 }
5380 } /* if (\...) */
5381 o_addqchr(dest, ch);
5382 }
5383
5384 if (dest->length == start) {
5385 /* $'', $'\0', $'\000\x00' and the like */
5386 o_addchr(dest, SPECIAL_VAR_SYMBOL);
5387 o_addchr(dest, SPECIAL_VAR_SYMBOL);
5388 }
5389
5390 return 1;
5391# undef as_string
5392}
5393#else
Francis Laniele7ca3a32023-12-22 22:02:42 +01005394# define parse_dollar_squote(as_string, dest, input) 0
Francis Laniel110b7692023-12-22 22:02:27 +01005395#endif /* BASH_DOLLAR_SQUOTE */
Francis Laniel36836fc2023-12-22 22:02:28 +01005396#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01005397
5398/* Return code: 0 for OK, 1 for syntax error */
5399#if BB_MMU
5400#define parse_dollar(as_string, dest, input, quote_mask) \
5401 parse_dollar(dest, input, quote_mask)
5402#define as_string NULL
5403#endif
5404static int parse_dollar(o_string *as_string,
5405 o_string *dest,
5406 struct in_str *input, unsigned char quote_mask)
5407{
5408 int ch = i_peek_and_eat_bkslash_nl(input); /* first character after the $ */
5409
5410 debug_printf_parse("parse_dollar entered: ch='%c' quote_mask:0x%x\n", ch, quote_mask);
5411 if (isalpha(ch)) {
5412 make_var:
5413 ch = i_getch(input);
5414 nommu_addchr(as_string, ch);
5415 /*make_var1:*/
5416 o_addchr(dest, SPECIAL_VAR_SYMBOL);
5417 while (1) {
5418 debug_printf_parse(": '%c'\n", ch);
5419 o_addchr(dest, ch | quote_mask);
5420 quote_mask = 0;
5421 ch = i_peek_and_eat_bkslash_nl(input);
5422 if (!isalnum(ch) && ch != '_') {
5423 /* End of variable name reached */
5424 break;
5425 }
5426 ch = i_getch(input);
5427 nommu_addchr(as_string, ch);
5428 }
5429 o_addchr(dest, SPECIAL_VAR_SYMBOL);
5430 } else if (isdigit(ch)) {
5431 make_one_char_var:
5432 ch = i_getch(input);
5433 nommu_addchr(as_string, ch);
5434 o_addchr(dest, SPECIAL_VAR_SYMBOL);
5435 debug_printf_parse(": '%c'\n", ch);
5436 o_addchr(dest, ch | quote_mask);
5437 o_addchr(dest, SPECIAL_VAR_SYMBOL);
5438 } else switch (ch) {
Francis Laniel36836fc2023-12-22 22:02:28 +01005439#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01005440 case '$': /* pid */
5441 case '!': /* last bg pid */
Francis Laniel36836fc2023-12-22 22:02:28 +01005442#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01005443 case '?': /* last exit code */
5444 case '#': /* number of args */
5445 case '*': /* args */
5446 case '@': /* args */
5447 case '-': /* $- option flags set by set builtin or shell options (-i etc) */
5448 goto make_one_char_var;
5449 case '{': {
5450 char len_single_ch;
5451
5452 o_addchr(dest, SPECIAL_VAR_SYMBOL);
5453
5454 ch = i_getch(input); /* eat '{' */
5455 nommu_addchr(as_string, ch);
5456
5457 ch = i_getch_and_eat_bkslash_nl(input); /* first char after '{' */
5458 /* It should be ${?}, or ${#var},
5459 * or even ${?+subst} - operator acting on a special variable,
5460 * or the beginning of variable name.
5461 */
5462 if (ch == EOF
5463 || (!strchr(_SPECIAL_VARS_STR, ch) && !isalnum(ch)) /* not one of those */
5464 ) {
5465 bad_dollar_syntax:
5466 syntax_error_unterm_str("${name}");
5467 debug_printf_parse("parse_dollar return 0: unterminated ${name}\n");
5468 return 0;
5469 }
5470 nommu_addchr(as_string, ch);
5471 len_single_ch = ch;
5472 ch |= quote_mask;
5473
5474 /* It's possible to just call add_till_closing_bracket() at this point.
5475 * However, this regresses some of our testsuite cases
5476 * which check invalid constructs like ${%}.
5477 * Oh well... let's check that the var name part is fine... */
5478
5479 if (isdigit(len_single_ch)
5480 || (len_single_ch == '#' && isdigit(i_peek_and_eat_bkslash_nl(input)))
5481 ) {
5482 /* Execution engine uses plain xatoi_positive()
5483 * to interpret ${NNN} and {#NNN},
5484 * check syntax here in the parser.
5485 * (bash does not support expressions in ${#NN},
5486 * e.g. ${#$var} and {#1:+WORD} are not supported).
5487 */
5488 unsigned cnt = 9; /* max 9 digits for ${NN} and 8 for {#NN} */
5489 while (1) {
5490 o_addchr(dest, ch);
5491 debug_printf_parse(": '%c'\n", ch);
5492 ch = i_getch_and_eat_bkslash_nl(input);
5493 nommu_addchr(as_string, ch);
5494 if (ch == '}')
5495 break;
5496 if (--cnt == 0)
5497 goto bad_dollar_syntax;
5498 if (len_single_ch != '#' && strchr(VAR_SUBST_OPS, ch))
5499 /* ${NN<op>...} is valid */
5500 goto eat_until_closing;
5501 if (!isdigit(ch))
5502 goto bad_dollar_syntax;
5503 }
5504 } else
5505 while (1) {
5506 unsigned pos;
5507
5508 o_addchr(dest, ch);
5509 debug_printf_parse(": '%c'\n", ch);
5510
5511 ch = i_getch(input);
5512 nommu_addchr(as_string, ch);
5513 if (ch == '}')
5514 break;
Francis Lanielbfc406a2023-12-22 22:02:33 +01005515#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01005516 if (!isalnum(ch) && ch != '_') {
Francis Lanielbfc406a2023-12-22 22:02:33 +01005517#else /* __U_BOOT__ */
5518 /*
5519 * In several places in U-Boot, we use variable like
5520 * foo# (e.g. serial#), particularly in env.
5521 * So, we need to authorize # to appear inside
5522 * variable name and then expand this variable.
5523 * NOTE Having # in variable name is not permitted in
5524 * upstream hush but expansion will be done (even though
5525 * the result will be empty).
5526 */
5527 if (!isalnum(ch) && ch != '_' && ch != '#') {
5528#endif /* __U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01005529 unsigned end_ch;
Francis Laniel36836fc2023-12-22 22:02:28 +01005530#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01005531 unsigned char last_ch;
Francis Laniel36836fc2023-12-22 22:02:28 +01005532#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01005533 /* handle parameter expansions
5534 * http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_06_02
5535 */
5536 if (!strchr(VAR_SUBST_OPS, ch)) { /* ${var<bad_char>... */
5537 if (len_single_ch != '#'
5538 /*|| !strchr(SPECIAL_VARS_STR, ch) - disallow errors like ${#+} ? */
5539 || i_peek(input) != '}'
5540 ) {
5541 goto bad_dollar_syntax;
5542 }
5543 /* else: it's "length of C" ${#C} op,
5544 * where C is a single char
5545 * special var name, e.g. ${#!}.
5546 */
5547 }
5548 eat_until_closing:
5549 /* Eat everything until closing '}' (or ':') */
5550 end_ch = '}';
Francis Laniel36836fc2023-12-22 22:02:28 +01005551#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01005552 if (BASH_SUBSTR
5553 && ch == ':'
5554 && !strchr(MINUS_PLUS_EQUAL_QUESTION, i_peek(input))
5555 ) {
5556 /* It's ${var:N[:M]} thing */
5557 end_ch = '}' * 0x100 + ':';
5558 }
5559 if (BASH_PATTERN_SUBST
5560 && ch == '/'
5561 ) {
5562 /* It's ${var/[/]pattern[/repl]} thing */
5563 if (i_peek(input) == '/') { /* ${var//pattern[/repl]}? */
5564 i_getch(input);
5565 nommu_addchr(as_string, '/');
5566 ch = '\\';
5567 }
5568 end_ch = '}' * 0x100 + '/';
5569 }
Francis Laniel36836fc2023-12-22 22:02:28 +01005570#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01005571 o_addchr(dest, ch);
5572 /* The pattern can't be empty.
5573 * IOW: if the first char after "${v//" is a slash,
5574 * it does not terminate the pattern - it's the first char of the pattern:
5575 * v=/dev/ram; echo ${v////-} prints -dev-ram (pattern is "/")
5576 * v=/dev/ram; echo ${v///r/-} prints /dev-am (pattern is "/r")
5577 */
5578 if (i_peek(input) == '/') {
5579 o_addchr(dest, i_getch(input));
5580 }
Francis Laniel36836fc2023-12-22 22:02:28 +01005581#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01005582 again:
Francis Laniel36836fc2023-12-22 22:02:28 +01005583#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01005584 if (!BB_MMU)
5585 pos = dest->length;
5586#if ENABLE_HUSH_DOLLAR_OPS
Francis Laniel36836fc2023-12-22 22:02:28 +01005587#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01005588 last_ch = add_till_closing_bracket(dest, input, end_ch);
5589 if (last_ch == 0) /* error? */
5590 return 0;
Francis Laniel36836fc2023-12-22 22:02:28 +01005591#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01005592#else
5593# error Simple code to only allow ${var} is not implemented
5594#endif
5595 if (as_string) {
5596 o_addstr(as_string, dest->data + pos);
Francis Laniel36836fc2023-12-22 22:02:28 +01005597#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01005598 o_addchr(as_string, last_ch);
Francis Laniel36836fc2023-12-22 22:02:28 +01005599#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01005600 }
5601
Francis Laniel36836fc2023-12-22 22:02:28 +01005602#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01005603 if ((BASH_SUBSTR || BASH_PATTERN_SUBST)
5604 && (end_ch & 0xff00)
5605 ) {
5606 /* close the first block: */
5607 o_addchr(dest, SPECIAL_VAR_SYMBOL);
5608 /* while parsing N from ${var:N[:M]}
5609 * or pattern from ${var/[/]pattern[/repl]} */
5610 if ((end_ch & 0xff) == last_ch) {
5611 /* got ':' or '/'- parse the rest */
5612 end_ch = '}';
5613 goto again;
5614 }
5615 /* got '}' */
5616 if (BASH_SUBSTR && end_ch == '}' * 0x100 + ':') {
5617 /* it's ${var:N} - emulate :999999999 */
5618 o_addstr(dest, "999999999");
5619 } /* else: it's ${var/[/]pattern} */
5620 }
Francis Laniel36836fc2023-12-22 22:02:28 +01005621#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01005622 break;
5623 }
5624 len_single_ch = 0; /* it can't be ${#C} op */
5625 }
5626 o_addchr(dest, SPECIAL_VAR_SYMBOL);
5627 break;
5628 }
5629#if ENABLE_FEATURE_SH_MATH || ENABLE_HUSH_TICK
5630 case '(': {
5631 unsigned pos;
5632
5633 ch = i_getch(input);
5634 nommu_addchr(as_string, ch);
5635# if ENABLE_FEATURE_SH_MATH
5636 if (i_peek_and_eat_bkslash_nl(input) == '(') {
5637 ch = i_getch(input);
5638 nommu_addchr(as_string, ch);
5639 o_addchr(dest, SPECIAL_VAR_SYMBOL);
5640 o_addchr(dest, quote_mask | '+');
5641 if (!BB_MMU)
5642 pos = dest->length;
5643 if (!add_till_closing_bracket(dest, input, ')' | DOUBLE_CLOSE_CHAR_FLAG))
5644 return 0; /* error */
5645 if (as_string) {
5646 o_addstr(as_string, dest->data + pos);
5647 o_addchr(as_string, ')');
5648 o_addchr(as_string, ')');
5649 }
5650 o_addchr(dest, SPECIAL_VAR_SYMBOL);
5651 break;
5652 }
5653# endif
5654# if ENABLE_HUSH_TICK
5655 o_addchr(dest, SPECIAL_VAR_SYMBOL);
5656 o_addchr(dest, quote_mask | '`');
5657 if (!BB_MMU)
5658 pos = dest->length;
5659 if (!add_till_closing_bracket(dest, input, ')'))
5660 return 0; /* error */
5661 if (as_string) {
5662 o_addstr(as_string, dest->data + pos);
5663 o_addchr(as_string, ')');
5664 }
5665 o_addchr(dest, SPECIAL_VAR_SYMBOL);
5666# endif
5667 break;
5668 }
5669#endif
5670 case '_':
5671 goto make_var;
5672#if 0
5673 /* TODO: $_: */
5674 /* $_ Shell or shell script name; or last argument of last command
5675 * (if last command wasn't a pipe; if it was, bash sets $_ to "");
5676 * but in command's env, set to full pathname used to invoke it */
5677 ch = i_getch(input);
5678 nommu_addchr(as_string, ch);
5679 ch = i_peek_and_eat_bkslash_nl(input);
5680 if (isalnum(ch)) { /* it's $_name or $_123 */
5681 ch = '_';
5682 goto make_var1;
5683 }
5684 /* else: it's $_ */
5685#endif
5686 default:
5687 o_addQchr(dest, '$');
5688 }
5689 debug_printf_parse("parse_dollar return 1 (ok)\n");
5690 return 1;
5691#undef as_string
5692}
5693
5694#if BB_MMU
5695#define encode_string(as_string, dest, input, dquote_end) \
5696 encode_string(dest, input, dquote_end)
5697#define as_string NULL
5698#endif
5699static int encode_string(o_string *as_string,
5700 o_string *dest,
5701 struct in_str *input,
5702 int dquote_end)
5703{
5704 int ch;
5705 int next;
5706
5707 again:
5708 ch = i_getch(input);
5709 if (ch != EOF)
5710 nommu_addchr(as_string, ch);
5711 if (ch == dquote_end) { /* may be only '"' or EOF */
5712 debug_printf_parse("encode_string return 1 (ok)\n");
5713 return 1;
5714 }
5715 /* note: can't move it above ch == dquote_end check! */
5716 if (ch == EOF) {
5717 syntax_error_unterm_ch('"');
5718 return 0; /* error */
5719 }
5720 next = '\0';
5721 if (ch != '\n') {
5722 next = i_peek(input);
5723 }
5724 debug_printf_parse("\" ch=%c (%d) escape=%d\n",
5725 ch, ch, !!(dest->o_expflags & EXP_FLAG_ESC_GLOB_CHARS));
5726 if (ch == '\\') {
5727 if (next == EOF) {
5728 /* Testcase: in interactive shell a file with
5729 * echo "unterminated string\<eof>
5730 * is sourced.
5731 */
5732 syntax_error_unterm_ch('"');
5733 return 0; /* error */
5734 }
5735 /* bash:
5736 * "The backslash retains its special meaning [in "..."]
5737 * only when followed by one of the following characters:
5738 * $, `, ", \, or <newline>. A double quote may be quoted
5739 * within double quotes by preceding it with a backslash."
5740 * NB: in (unquoted) heredoc, above does not apply to ",
5741 * therefore we check for it by "next == dquote_end" cond.
5742 */
5743 if (next == dquote_end || strchr("$`\\\n", next)) {
5744 ch = i_getch(input); /* eat next */
5745 if (ch == '\n')
5746 goto again; /* skip \<newline> */
5747 } /* else: ch remains == '\\', and we double it below: */
5748 o_addqchr(dest, ch); /* \c if c is a glob char, else just c */
5749 nommu_addchr(as_string, ch);
5750 goto again;
5751 }
5752 if (ch == '$') {
5753 //if (parse_dollar_squote(as_string, dest, input))
5754 // goto again;
5755 if (!parse_dollar(as_string, dest, input, /*quote_mask:*/ 0x80)) {
5756 debug_printf_parse("encode_string return 0: "
5757 "parse_dollar returned 0 (error)\n");
5758 return 0;
5759 }
5760 goto again;
5761 }
5762#if ENABLE_HUSH_TICK
5763 if (ch == '`') {
5764 //unsigned pos = dest->length;
5765 o_addchr(dest, SPECIAL_VAR_SYMBOL);
5766 o_addchr(dest, 0x80 | '`');
5767 if (!add_till_backquote(dest, input, /*in_dquote:*/ dquote_end == '"'))
5768 return 0; /* error */
5769 o_addchr(dest, SPECIAL_VAR_SYMBOL);
5770 //debug_printf_subst("SUBST RES3 '%s'\n", dest->data + pos);
5771 goto again;
5772 }
5773#endif
5774 o_addQchr(dest, ch);
5775 if (ch == SPECIAL_VAR_SYMBOL) {
5776 /* Convert "^C" to corresponding special variable reference */
5777 o_addchr(dest, SPECIAL_VAR_QUOTED_SVS);
5778 o_addchr(dest, SPECIAL_VAR_SYMBOL);
5779 }
5780 goto again;
5781#undef as_string
5782}
5783
5784/*
5785 * Scan input until EOF or end_trigger char.
5786 * Return a list of pipes to execute, or NULL on EOF
5787 * or if end_trigger character is met.
5788 * On syntax error, exit if shell is not interactive,
5789 * reset parsing machinery and start parsing anew,
5790 * or return ERR_PTR.
5791 */
5792static struct pipe *parse_stream(char **pstring,
5793 int *heredoc_cnt_ptr,
5794 struct in_str *input,
5795 int end_trigger)
5796{
5797 struct parse_context ctx;
5798 int heredoc_cnt;
5799
5800 /* Single-quote triggers a bypass of the main loop until its mate is
5801 * found. When recursing, quote state is passed in via ctx.word.o_expflags.
5802 */
5803 debug_printf_parse("parse_stream entered, end_trigger='%c'\n",
5804 end_trigger ? end_trigger : 'X');
5805 debug_enter();
5806
5807 initialize_context(&ctx);
5808
5809 /* If very first arg is "" or '', ctx.word.data may end up NULL.
5810 * Preventing this:
5811 */
5812 ctx.word.data = xzalloc(1); /* start as "", not as NULL */
5813
5814 /* We used to separate words on $IFS here. This was wrong.
5815 * $IFS is used only for word splitting when $var is expanded,
5816 * here we should use blank chars as separators, not $IFS
5817 */
5818
5819 heredoc_cnt = 0;
5820 while (1) {
5821 const char *is_blank;
5822 const char *is_special;
5823 int ch;
5824 int next;
Francis Laniel36836fc2023-12-22 22:02:28 +01005825#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01005826 int redir_fd;
5827 redir_type redir_style;
Francis Laniel36836fc2023-12-22 22:02:28 +01005828#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01005829
5830 ch = i_getch(input);
5831 debug_printf_parse(": ch=%c (%d) escape=%d\n",
5832 ch, ch, !!(ctx.word.o_expflags & EXP_FLAG_ESC_GLOB_CHARS));
5833 if (ch == EOF) {
5834 struct pipe *pi;
5835
5836 if (heredoc_cnt) {
5837 syntax_error_unterm_str("here document");
5838 goto parse_error_exitcode1;
5839 }
5840 if (end_trigger == ')') {
5841 syntax_error_unterm_ch('(');
5842 goto parse_error_exitcode1;
5843 }
5844 if (end_trigger == '}') {
5845 syntax_error_unterm_ch('{');
5846 goto parse_error_exitcode1;
5847 }
5848
5849 if (done_word(&ctx)) {
5850 goto parse_error_exitcode1;
5851 }
5852 o_free_and_set_NULL(&ctx.word);
5853 done_pipe(&ctx, PIPE_SEQ);
5854 pi = ctx.list_head;
5855 /* If we got nothing... */
5856 /* (this makes bare "&" cmd a no-op.
5857 * bash says: "syntax error near unexpected token '&'") */
5858 if (pi->num_cmds == 0
5859 IF_HAS_KEYWORDS(&& pi->res_word == RES_NONE)
5860 ) {
5861 free_pipe_list(pi);
5862 pi = NULL;
5863 }
5864#if !BB_MMU
5865 debug_printf_parse("as_string1 '%s'\n", ctx.as_string.data);
5866 if (pstring)
5867 *pstring = ctx.as_string.data;
5868 else
5869 o_free(&ctx.as_string);
5870#endif
5871 // heredoc_cnt must be 0 here anyway
5872 //if (heredoc_cnt_ptr)
5873 // *heredoc_cnt_ptr = heredoc_cnt;
5874 debug_leave();
5875 debug_printf_heredoc("parse_stream return heredoc_cnt:%d\n", heredoc_cnt);
5876 debug_printf_parse("parse_stream return %p\n", pi);
5877 return pi;
5878 }
5879
5880 /* Handle "'" and "\" first, as they won't play nice with
5881 * i_peek_and_eat_bkslash_nl() anyway:
5882 * echo z\\
5883 * and
5884 * echo '\
5885 * '
5886 * would break.
5887 */
5888 if (ch == '\\') {
5889 ch = i_getch(input);
5890 if (ch == '\n')
5891 continue; /* drop \<newline>, get next char */
5892 nommu_addchr(&ctx.as_string, '\\');
5893 if (ch == SPECIAL_VAR_SYMBOL) {
5894 nommu_addchr(&ctx.as_string, ch);
5895 /* Convert \^C to corresponding special variable reference */
5896 goto case_SPECIAL_VAR_SYMBOL;
5897 }
5898 o_addchr(&ctx.word, '\\');
5899 if (ch == EOF) {
5900 /* Testcase: eval 'echo Ok\' */
5901 /* bash-4.3.43 was removing backslash,
5902 * but 4.4.19 retains it, most other shells too
5903 */
5904 continue; /* get next char */
5905 }
5906 /* Example: echo Hello \2>file
5907 * we need to know that word 2 is quoted
5908 */
5909 ctx.word.has_quoted_part = 1;
5910 nommu_addchr(&ctx.as_string, ch);
5911 o_addchr(&ctx.word, ch);
5912 continue; /* get next char */
5913 }
5914 nommu_addchr(&ctx.as_string, ch);
5915 if (ch == '\'') {
5916 ctx.word.has_quoted_part = 1;
5917 next = i_getch(input);
Francis Laniel36836fc2023-12-22 22:02:28 +01005918#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01005919 if (next == '\'' && !ctx.pending_redirect)
5920 goto insert_empty_quoted_str_marker;
Francis Laniel36836fc2023-12-22 22:02:28 +01005921#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01005922
5923 ch = next;
5924 while (1) {
5925 if (ch == EOF) {
5926 syntax_error_unterm_ch('\'');
5927 goto parse_error_exitcode1;
5928 }
5929 nommu_addchr(&ctx.as_string, ch);
5930 if (ch == '\'')
5931 break;
5932 if (ch == SPECIAL_VAR_SYMBOL) {
5933 /* Convert raw ^C to corresponding special variable reference */
5934 o_addchr(&ctx.word, SPECIAL_VAR_SYMBOL);
5935 o_addchr(&ctx.word, SPECIAL_VAR_QUOTED_SVS);
5936 }
5937 o_addqchr(&ctx.word, ch);
5938 ch = i_getch(input);
5939 }
5940 continue; /* get next char */
5941 }
5942
5943 next = '\0';
5944 if (ch != '\n')
5945 next = i_peek_and_eat_bkslash_nl(input);
5946
5947 is_special = "{}<>&|();#" /* special outside of "str" */
Francis Laniel36836fc2023-12-22 22:02:28 +01005948#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01005949 "$\"" IF_HUSH_TICK("`") /* always special */
Francis Laniel36836fc2023-12-22 22:02:28 +01005950#else /* __U_BOOT__ */
5951 "$\""
5952#endif /* __U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01005953 SPECIAL_VAR_SYMBOL_STR;
5954#if defined(CMD_TEST2_SINGLEWORD_NOGLOB)
5955 if (ctx.command->cmd_type == CMD_TEST2_SINGLEWORD_NOGLOB) {
5956 /* In [[ ]], {}<>&|() are not special */
5957 is_special += 8;
5958 } else
5959#endif
5960 /* Are { and } special here? */
5961 if (ctx.command->argv /* word [word]{... - non-special */
5962 || ctx.word.length /* word{... - non-special */
5963 || ctx.word.has_quoted_part /* ""{... - non-special */
5964 || (next != ';' /* }; - special */
5965 && next != ')' /* }) - special */
5966 && next != '(' /* {( - special */
5967 && next != '&' /* }& and }&& ... - special */
5968 && next != '|' /* }|| ... - special */
5969 && !strchr(defifs, next) /* {word - non-special */
5970 )
5971 ) {
5972 /* They are not special, skip "{}" */
5973 is_special += 2;
5974 }
5975 is_special = strchr(is_special, ch);
5976 is_blank = strchr(defifs, ch);
5977
5978 if (!is_special && !is_blank) { /* ordinary char */
5979 ordinary_char:
5980 o_addQchr(&ctx.word, ch);
5981 if ((ctx.is_assignment == MAYBE_ASSIGNMENT
5982 || ctx.is_assignment == WORD_IS_KEYWORD)
5983 && ch == '='
5984 && endofname(ctx.word.data)[0] == '='
5985 ) {
5986 ctx.is_assignment = DEFINITELY_ASSIGNMENT;
5987 debug_printf_parse("ctx.is_assignment='%s'\n", assignment_flag[ctx.is_assignment]);
5988 }
5989 continue;
5990 }
5991
5992 if (is_blank) {
5993#if ENABLE_HUSH_LINENO_VAR
5994/* Case:
5995 * "while ...; do<whitespace><newline>
5996 * cmd ..."
5997 * would think that "cmd" starts in <whitespace> -
5998 * i.e., at the previous line.
5999 * We need to skip all whitespace before newlines.
6000 */
6001 while (ch != '\n') {
6002 next = i_peek(input);
6003 if (next != ' ' && next != '\t' && next != '\n')
6004 break; /* next char is not ws */
6005 ch = i_getch(input);
6006 }
6007 /* ch == last eaten whitespace char */
6008#endif
6009 if (done_word(&ctx)) {
6010 goto parse_error_exitcode1;
6011 }
6012 if (ch == '\n') {
6013 /* Is this a case when newline is simply ignored?
6014 * Some examples:
6015 * "cmd | <newline> cmd ..."
6016 * "case ... in <newline> word) ..."
6017 */
6018 if (IS_NULL_CMD(ctx.command)
6019 && ctx.word.length == 0
6020 && !ctx.word.has_quoted_part
6021 && heredoc_cnt == 0
6022 ) {
6023 /* This newline can be ignored. But...
6024 * Without check #1, interactive shell
6025 * ignores even bare <newline>,
6026 * and shows the continuation prompt:
6027 * ps1_prompt$ <enter>
6028 * ps2> _ <=== wrong, should be ps1
6029 * Without check #2, "cmd & <newline>"
6030 * is similarly mistreated.
6031 * (BTW, this makes "cmd & cmd"
6032 * and "cmd && cmd" non-orthogonal.
6033 * Really, ask yourself, why
6034 * "cmd && <newline>" doesn't start
6035 * cmd but waits for more input?
6036 * The only reason is that it might be
6037 * a "cmd1 && <nl> cmd2 &" construct,
6038 * cmd1 may need to run in BG).
6039 */
6040 struct pipe *pi = ctx.list_head;
6041 if (pi->num_cmds != 0 /* check #1 */
6042 && pi->followup != PIPE_BG /* check #2 */
6043 ) {
6044 continue;
6045 }
6046 }
6047 /* Treat newline as a command separator. */
6048 done_pipe(&ctx, PIPE_SEQ);
6049 debug_printf_heredoc("heredoc_cnt:%d\n", heredoc_cnt);
6050 if (heredoc_cnt) {
6051 heredoc_cnt = fetch_heredocs(&ctx.as_string, ctx.list_head, heredoc_cnt, input);
6052 if (heredoc_cnt != 0)
6053 goto parse_error_exitcode1;
6054 }
6055 ctx.is_assignment = MAYBE_ASSIGNMENT;
6056 debug_printf_parse("ctx.is_assignment='%s'\n", assignment_flag[ctx.is_assignment]);
6057 ch = ';';
6058 /* note: if (is_blank) continue;
6059 * will still trigger for us */
6060 }
6061 }
6062
6063 /* "cmd}" or "cmd }..." without semicolon or &:
6064 * } is an ordinary char in this case, even inside { cmd; }
6065 * Pathological example: { ""}; } should exec "}" cmd
6066 */
Francis Laniel36836fc2023-12-22 22:02:28 +01006067#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01006068 if (ch == '}') {
Francis Laniel36836fc2023-12-22 22:02:28 +01006069#else /* __U_BOOT__ */
6070 if (ch == '}' || ch == ')') {
6071#endif /* __U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01006072 if (ctx.word.length != 0 /* word} */
6073 || ctx.word.has_quoted_part /* ""} */
6074 ) {
6075 goto ordinary_char;
6076 }
6077 if (!IS_NULL_CMD(ctx.command)) { /* cmd } */
6078 /* Generally, there should be semicolon: "cmd; }"
6079 * However, bash allows to omit it if "cmd" is
6080 * a group. Examples:
6081 * { { echo 1; } }
6082 * {(echo 1)}
6083 * { echo 0 >&2 | { echo 1; } }
6084 * { while false; do :; done }
6085 * { case a in b) ;; esac }
6086 */
6087 if (ctx.command->group)
6088 goto term_group;
6089 goto ordinary_char;
6090 }
6091 if (!IS_NULL_PIPE(ctx.pipe)) /* cmd | } */
6092 /* Can't be an end of {cmd}, skip the check */
6093 goto skip_end_trigger;
6094 /* else: } does terminate a group */
6095 }
6096 term_group:
6097 if (end_trigger && end_trigger == ch
6098 && (ch != ';' || heredoc_cnt == 0)
6099#if ENABLE_HUSH_CASE
6100 && (ch != ')'
6101 || ctx.ctx_res_w != RES_MATCH
6102 || (!ctx.word.has_quoted_part && strcmp(ctx.word.data, "esac") == 0)
6103 )
6104#endif
6105 ) {
6106 if (done_word(&ctx)) {
6107 goto parse_error_exitcode1;
6108 }
6109 done_pipe(&ctx, PIPE_SEQ);
6110 ctx.is_assignment = MAYBE_ASSIGNMENT;
6111 debug_printf_parse("ctx.is_assignment='%s'\n", assignment_flag[ctx.is_assignment]);
6112 /* Do we sit outside of any if's, loops or case's? */
6113 if (!HAS_KEYWORDS
6114 IF_HAS_KEYWORDS(|| (ctx.ctx_res_w == RES_NONE && ctx.old_flag == 0))
6115 ) {
6116 o_free_and_set_NULL(&ctx.word);
6117#if !BB_MMU
6118 debug_printf_parse("as_string2 '%s'\n", ctx.as_string.data);
6119 if (pstring)
6120 *pstring = ctx.as_string.data;
6121 else
6122 o_free(&ctx.as_string);
6123#endif
6124 if (ch != ';' && IS_NULL_PIPE(ctx.list_head)) {
6125 /* Example: bare "{ }", "()" */
6126 G.last_exitcode = 2; /* bash compat */
6127 syntax_error_unexpected_ch(ch);
6128 goto parse_error;
6129 }
6130 if (heredoc_cnt_ptr)
6131 *heredoc_cnt_ptr = heredoc_cnt;
6132 debug_printf_heredoc("parse_stream return heredoc_cnt:%d\n", heredoc_cnt);
6133 debug_printf_parse("parse_stream return %p: "
6134 "end_trigger char found\n",
6135 ctx.list_head);
6136 debug_leave();
6137 return ctx.list_head;
6138 }
6139 }
6140
6141 if (is_blank)
6142 continue;
6143
6144 /* Catch <, > before deciding whether this word is
6145 * an assignment. a=1 2>z b=2: b=2 is still assignment */
6146 switch (ch) {
Francis Laniel36836fc2023-12-22 22:02:28 +01006147#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01006148 case '>':
6149 redir_fd = redirect_opt_num(&ctx.word);
6150 if (done_word(&ctx)) {
6151 goto parse_error_exitcode1;
6152 }
6153 redir_style = REDIRECT_OVERWRITE;
6154 if (next == '>') {
6155 redir_style = REDIRECT_APPEND;
6156 ch = i_getch(input);
6157 nommu_addchr(&ctx.as_string, ch);
6158 }
6159#if 0
6160 else if (next == '(') {
6161 syntax_error(">(process) not supported");
6162 goto parse_error_exitcode1;
6163 }
6164#endif
6165 if (parse_redirect(&ctx, redir_fd, redir_style, input))
6166 goto parse_error_exitcode1;
6167 continue; /* get next char */
6168 case '<':
6169 redir_fd = redirect_opt_num(&ctx.word);
6170 if (done_word(&ctx)) {
6171 goto parse_error_exitcode1;
6172 }
6173 redir_style = REDIRECT_INPUT;
6174 if (next == '<') {
6175 redir_style = REDIRECT_HEREDOC;
6176 heredoc_cnt++;
6177 debug_printf_heredoc("++heredoc_cnt=%d\n", heredoc_cnt);
6178 ch = i_getch(input);
6179 nommu_addchr(&ctx.as_string, ch);
6180 } else if (next == '>') {
6181 redir_style = REDIRECT_IO;
6182 ch = i_getch(input);
6183 nommu_addchr(&ctx.as_string, ch);
6184 }
6185#if 0
6186 else if (next == '(') {
6187 syntax_error("<(process) not supported");
6188 goto parse_error_exitcode1;
6189 }
6190#endif
6191 if (parse_redirect(&ctx, redir_fd, redir_style, input))
6192 goto parse_error_exitcode1;
6193 continue; /* get next char */
Francis Lanielaa44c262023-12-22 22:02:38 +01006194#else /* __U_BOOT__ */
6195 /*
6196 * In U-Boot, '<' and '>' can be used in test command to test if
6197 * a string is, alphabetically, before or after another.
6198 * In 2021 Busybox hush, we will keep the same behavior and so not treat
6199 * them as redirection operator.
6200 *
6201 * Indeed, in U-Boot, tests are handled by the test command and not by the
6202 * shell code.
6203 * So, better to give this character as input to test command.
6204 *
6205 * NOTE In my opinion, when you use '<' or '>' I am almost sure
6206 * you wanted to use "-gt" or "-lt" in place, so thinking to
6207 * escape these will make you should check your code (sh syntax
6208 * at this level is, for me, error prone).
6209 */
6210 case '>':
6211 fallthrough;
6212 case '<':
6213 o_addQchr(&ctx.word, ch);
6214 continue;
6215#endif /* __U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01006216 case '#':
6217 if (ctx.word.length == 0 && !ctx.word.has_quoted_part) {
6218 /* skip "#comment" */
6219 /* note: we do not add it to &ctx.as_string */
6220/* TODO: in bash:
6221 * comment inside $() goes to the next \n, even inside quoted string (!):
6222 * cmd "$(cmd2 #comment)" - syntax error
6223 * cmd "`cmd2 #comment`" - ok
6224 * We accept both (comment ends where command subst ends, in both cases).
6225 */
6226 while (1) {
6227 ch = i_peek(input);
6228 if (ch == '\n') {
6229 nommu_addchr(&ctx.as_string, '\n');
6230 break;
6231 }
6232 ch = i_getch(input);
6233 if (ch == EOF)
6234 break;
6235 }
6236 continue; /* get next char */
6237 }
6238 break;
6239 }
6240 skip_end_trigger:
6241
6242 if (ctx.is_assignment == MAYBE_ASSIGNMENT
Francis Laniel36836fc2023-12-22 22:02:28 +01006243#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01006244 /* check that we are not in word in "a=1 2>word b=1": */
6245 && !ctx.pending_redirect
Francis Laniel36836fc2023-12-22 22:02:28 +01006246#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01006247 ) {
6248 /* ch is a special char and thus this word
6249 * cannot be an assignment */
6250 ctx.is_assignment = NOT_ASSIGNMENT;
6251 debug_printf_parse("ctx.is_assignment='%s'\n", assignment_flag[ctx.is_assignment]);
6252 }
6253
6254 /* Note: nommu_addchr(&ctx.as_string, ch) is already done */
6255
6256 switch (ch) {
6257 case_SPECIAL_VAR_SYMBOL:
6258 case SPECIAL_VAR_SYMBOL:
6259 /* Convert raw ^C to corresponding special variable reference */
6260 o_addchr(&ctx.word, SPECIAL_VAR_SYMBOL);
6261 o_addchr(&ctx.word, SPECIAL_VAR_QUOTED_SVS);
6262 /* fall through */
6263 case '#':
6264 /* non-comment #: "echo a#b" etc */
6265 o_addchr(&ctx.word, ch);
6266 continue; /* get next char */
6267 case '$':
Francis Laniel36836fc2023-12-22 22:02:28 +01006268#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01006269 if (parse_dollar_squote(&ctx.as_string, &ctx.word, input))
6270 continue; /* get next char */
Francis Laniel36836fc2023-12-22 22:02:28 +01006271#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01006272 if (!parse_dollar(&ctx.as_string, &ctx.word, input, /*quote_mask:*/ 0)) {
6273 debug_printf_parse("parse_stream parse error: "
6274 "parse_dollar returned 0 (error)\n");
6275 goto parse_error_exitcode1;
6276 }
6277 continue; /* get next char */
6278 case '"':
6279 ctx.word.has_quoted_part = 1;
Francis Laniel36836fc2023-12-22 22:02:28 +01006280#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01006281 if (next == '"' && !ctx.pending_redirect) {
Francis Laniel36836fc2023-12-22 22:02:28 +01006282#else /* __U_BOOT__ */
6283 if (next == '"') {
6284#endif /* __U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01006285 i_getch(input); /* eat second " */
Francis Laniel36836fc2023-12-22 22:02:28 +01006286#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01006287 insert_empty_quoted_str_marker:
Francis Laniel36836fc2023-12-22 22:02:28 +01006288#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01006289 nommu_addchr(&ctx.as_string, next);
6290 o_addchr(&ctx.word, SPECIAL_VAR_SYMBOL);
6291 o_addchr(&ctx.word, SPECIAL_VAR_SYMBOL);
6292 continue; /* get next char */
6293 }
6294 if (ctx.is_assignment == NOT_ASSIGNMENT)
6295 ctx.word.o_expflags |= EXP_FLAG_ESC_GLOB_CHARS;
6296 if (!encode_string(&ctx.as_string, &ctx.word, input, '"'))
6297 goto parse_error_exitcode1;
6298 ctx.word.o_expflags &= ~EXP_FLAG_ESC_GLOB_CHARS;
6299 continue; /* get next char */
6300#if ENABLE_HUSH_TICK
6301 case '`': {
6302 USE_FOR_NOMMU(unsigned pos;)
6303
6304 o_addchr(&ctx.word, SPECIAL_VAR_SYMBOL);
6305 o_addchr(&ctx.word, '`');
6306 USE_FOR_NOMMU(pos = ctx.word.length;)
6307 if (!add_till_backquote(&ctx.word, input, /*in_dquote:*/ 0))
6308 goto parse_error_exitcode1;
6309# if !BB_MMU
6310 o_addstr(&ctx.as_string, ctx.word.data + pos);
6311 o_addchr(&ctx.as_string, '`');
6312# endif
6313 o_addchr(&ctx.word, SPECIAL_VAR_SYMBOL);
6314 //debug_printf_subst("SUBST RES3 '%s'\n", ctx.word.data + pos);
6315 continue; /* get next char */
6316 }
6317#endif
6318 case ';':
6319#if ENABLE_HUSH_CASE
6320 case_semi:
6321#endif
6322 if (done_word(&ctx)) {
6323 goto parse_error_exitcode1;
6324 }
6325 done_pipe(&ctx, PIPE_SEQ);
6326#if ENABLE_HUSH_CASE
6327 /* Eat multiple semicolons, detect
6328 * whether it means something special */
6329 while (1) {
6330 ch = i_peek_and_eat_bkslash_nl(input);
6331 if (ch != ';')
6332 break;
6333 ch = i_getch(input);
6334 nommu_addchr(&ctx.as_string, ch);
6335 if (ctx.ctx_res_w == RES_CASE_BODY) {
6336 ctx.ctx_dsemicolon = 1;
6337 ctx.ctx_res_w = RES_MATCH;
6338 break;
6339 }
6340 }
6341#endif
6342 new_cmd:
6343 /* We just finished a cmd. New one may start
6344 * with an assignment */
6345 ctx.is_assignment = MAYBE_ASSIGNMENT;
6346 debug_printf_parse("ctx.is_assignment='%s'\n", assignment_flag[ctx.is_assignment]);
6347 continue; /* get next char */
6348 case '&':
6349 if (done_word(&ctx)) {
6350 goto parse_error_exitcode1;
6351 }
6352 if (next == '&') {
6353 ch = i_getch(input);
6354 nommu_addchr(&ctx.as_string, ch);
6355 done_pipe(&ctx, PIPE_AND);
6356 } else {
6357 done_pipe(&ctx, PIPE_BG);
6358 }
6359 goto new_cmd;
6360 case '|':
6361 if (done_word(&ctx)) {
6362 goto parse_error_exitcode1;
6363 }
6364#if ENABLE_HUSH_CASE
6365 if (ctx.ctx_res_w == RES_MATCH)
6366 break; /* we are in case's "word | word)" */
6367#endif
6368 if (next == '|') { /* || */
6369 ch = i_getch(input);
6370 nommu_addchr(&ctx.as_string, ch);
6371 done_pipe(&ctx, PIPE_OR);
6372 } else {
6373 /* we could pick up a file descriptor choice here
6374 * with redirect_opt_num(), but bash doesn't do it.
6375 * "echo foo 2| cat" yields "foo 2". */
6376 done_command(&ctx);
6377 }
6378 goto new_cmd;
6379 case '(':
6380#if ENABLE_HUSH_CASE
6381 /* "case... in [(]word)..." - skip '(' */
6382 if (ctx.ctx_res_w == RES_MATCH
6383 && ctx.command->argv == NULL /* not (word|(... */
6384 && ctx.word.length == 0 /* not word(... */
6385 && ctx.word.has_quoted_part == 0 /* not ""(... */
6386 ) {
6387 continue; /* get next char */
6388 }
6389#endif
6390 /* fall through */
6391 case '{': {
6392 int n = parse_group(&ctx, input, ch);
6393 if (n < 0) {
6394 goto parse_error_exitcode1;
6395 }
6396 debug_printf_heredoc("parse_group done, needs heredocs:%d\n", n);
6397 heredoc_cnt += n;
6398 goto new_cmd;
6399 }
6400 case ')':
6401#if ENABLE_HUSH_CASE
6402 if (ctx.ctx_res_w == RES_MATCH)
6403 goto case_semi;
6404#endif
6405 case '}':
6406 /* proper use of this character is caught by end_trigger:
6407 * if we see {, we call parse_group(..., end_trigger='}')
6408 * and it will match } earlier (not here). */
6409 G.last_exitcode = 2;
6410 syntax_error_unexpected_ch(ch);
6411 goto parse_error;
6412 default:
6413 if (HUSH_DEBUG)
6414 bb_error_msg_and_die("BUG: unexpected %c", ch);
6415 }
6416 } /* while (1) */
6417
6418 parse_error_exitcode1:
6419 G.last_exitcode = 1;
6420 parse_error:
6421 {
6422 struct parse_context *pctx;
6423 IF_HAS_KEYWORDS(struct parse_context *p2;)
6424
6425 /* Clean up allocated tree.
6426 * Sample for finding leaks on syntax error recovery path.
6427 * Run it from interactive shell, watch pmap `pidof hush`.
6428 * while if false; then false; fi; do break; fi
6429 * Samples to catch leaks at execution:
6430 * while if (true | { true;}); then echo ok; fi; do break; done
6431 * while if (true | { true;}); then echo ok; fi; do (if echo ok; break; then :; fi) | cat; break; done
6432 */
6433 pctx = &ctx;
6434 do {
6435 /* Update pipe/command counts,
6436 * otherwise freeing may miss some */
6437 done_pipe(pctx, PIPE_SEQ);
6438 debug_printf_clean("freeing list %p from ctx %p\n",
6439 pctx->list_head, pctx);
6440 debug_print_tree(pctx->list_head, 0);
6441 free_pipe_list(pctx->list_head);
6442 debug_printf_clean("freed list %p\n", pctx->list_head);
6443#if !BB_MMU
6444 o_free(&pctx->as_string);
6445#endif
6446 IF_HAS_KEYWORDS(p2 = pctx->stack;)
6447 if (pctx != &ctx) {
6448 free(pctx);
6449 }
6450 IF_HAS_KEYWORDS(pctx = p2;)
6451 } while (HAS_KEYWORDS && pctx);
6452
6453 o_free(&ctx.word);
6454#if !BB_MMU
6455 if (pstring)
6456 *pstring = NULL;
6457#endif
6458 debug_leave();
6459 return ERR_PTR;
6460 }
6461}
6462
Francis Laniel110b7692023-12-22 22:02:27 +01006463/*** Execution routines ***/
6464
6465/* Expansion can recurse, need forward decls: */
6466#if !BASH_PATTERN_SUBST && !ENABLE_HUSH_CASE
6467#define expand_string_to_string(str, EXP_flags, do_unbackslash) \
6468 expand_string_to_string(str)
6469#endif
6470static char *expand_string_to_string(const char *str, int EXP_flags, int do_unbackslash);
6471#if ENABLE_HUSH_TICK
6472static int process_command_subs(o_string *dest, const char *s);
6473#endif
6474static int expand_vars_to_list(o_string *output, int n, char *arg);
6475
6476/* expand_strvec_to_strvec() takes a list of strings, expands
6477 * all variable references within and returns a pointer to
6478 * a list of expanded strings, possibly with larger number
6479 * of strings. (Think VAR="a b"; echo $VAR).
6480 * This new list is allocated as a single malloc block.
6481 * NULL-terminated list of char* pointers is at the beginning of it,
6482 * followed by strings themselves.
6483 * Caller can deallocate entire list by single free(list). */
6484
6485/* A horde of its helpers come first: */
6486
6487static void o_addblock_duplicate_backslash(o_string *o, const char *str, int len)
6488{
6489 while (--len >= 0) {
6490 char c = *str++;
6491
6492#if ENABLE_HUSH_BRACE_EXPANSION
6493 if (c == '{' || c == '}') {
6494 /* { -> \{, } -> \} */
6495 o_addchr(o, '\\');
6496 /* And now we want to add { or } and continue:
6497 * o_addchr(o, c);
6498 * continue;
6499 * luckily, just falling through achieves this.
6500 */
6501 }
6502#endif
6503 o_addchr(o, c);
6504 if (c == '\\') {
6505 /* \z -> \\\z; \<eol> -> \\<eol> */
6506 o_addchr(o, '\\');
6507 if (len) {
6508 len--;
6509 o_addchr(o, '\\');
6510 o_addchr(o, *str++);
6511 }
6512 }
6513 }
6514}
6515
6516/* Store given string, finalizing the word and starting new one whenever
6517 * we encounter IFS char(s). This is used for expanding variable values.
6518 * End-of-string does NOT finalize word: think about 'echo -$VAR-'.
6519 * Return in output->ended_in_ifs:
6520 * 1 - ended with IFS char, else 0 (this includes case of empty str).
6521 */
6522static int expand_on_ifs(o_string *output, int n, const char *str)
6523{
6524 int last_is_ifs = 0;
6525
6526 while (1) {
6527 int word_len;
6528
6529 if (!*str) /* EOL - do not finalize word */
6530 break;
6531 word_len = strcspn(str, G.ifs);
6532 if (word_len) {
6533 /* We have WORD_LEN leading non-IFS chars */
6534 if (!(output->o_expflags & EXP_FLAG_GLOB)) {
6535 o_addblock(output, str, word_len);
6536 } else {
6537 /* Protect backslashes against globbing up :)
6538 * Example: "v='\*'; echo b$v" prints "b\*"
6539 * (and does not try to glob on "*")
6540 */
6541 o_addblock_duplicate_backslash(output, str, word_len);
6542 /*/ Why can't we do it easier? */
6543 /*o_addblock(output, str, word_len); - WRONG: "v='\*'; echo Z$v" prints "Z*" instead of "Z\*" */
6544 /*o_addqblock(output, str, word_len); - WRONG: "v='*'; echo Z$v" prints "Z*" instead of Z* files */
6545 }
6546 last_is_ifs = 0;
6547 str += word_len;
6548 if (!*str) /* EOL - do not finalize word */
6549 break;
6550 }
6551
6552 /* We know str here points to at least one IFS char */
6553 last_is_ifs = 1;
6554 str += strspn(str, G.ifs_whitespace); /* skip IFS whitespace chars */
6555 if (!*str) /* EOL - do not finalize word */
6556 break;
6557
6558 if (G.ifs_whitespace != G.ifs /* usually false ($IFS is usually all whitespace), */
6559 && strchr(G.ifs, *str) /* the second check would fail */
6560 ) {
6561 /* This is a non-whitespace $IFS char */
6562 /* Skip it and IFS whitespace chars, start new word */
6563 str++;
6564 str += strspn(str, G.ifs_whitespace);
6565 goto new_word;
6566 }
6567
6568 /* Start new word... but not always! */
6569 /* Case "v=' a'; echo ''$v": we do need to finalize empty word: */
6570 if (output->has_quoted_part
6571 /*
6572 * Case "v=' a'; echo $v":
6573 * here nothing precedes the space in $v expansion,
6574 * therefore we should not finish the word
6575 * (IOW: if there *is* word to finalize, only then do it):
6576 * It's okay if this accesses the byte before first argv[]:
6577 * past call to o_save_ptr() cleared it to zero byte
6578 * (grep for -prev-ifs-check-).
6579 */
6580 || output->data[output->length - 1]
6581 ) {
6582 new_word:
6583 o_addchr(output, '\0');
6584 debug_print_list("expand_on_ifs", output, n);
6585 n = o_save_ptr(output, n);
6586 }
6587 }
6588
6589 output->ended_in_ifs = last_is_ifs;
6590 debug_print_list("expand_on_ifs[1]", output, n);
6591 return n;
6592}
6593
Francis Laniel36836fc2023-12-22 22:02:28 +01006594#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01006595/* Helper to expand $((...)) and heredoc body. These act as if
6596 * they are in double quotes, with the exception that they are not :).
6597 * Just the rules are similar: "expand only $var and `cmd`"
6598 *
6599 * Returns malloced string.
6600 * As an optimization, we return NULL if expansion is not needed.
6601 */
6602static char *encode_then_expand_string(const char *str)
6603{
6604 char *exp_str;
6605 struct in_str input;
6606 o_string dest = NULL_O_STRING;
6607 const char *cp;
6608
6609 cp = str;
6610 for (;;) {
6611 if (!*cp) return NULL; /* string has no special chars */
6612 if (*cp == '$') break;
6613 if (*cp == '\\') break;
6614#if ENABLE_HUSH_TICK
6615 if (*cp == '`') break;
6616#endif
6617 cp++;
6618 }
6619
6620 /* We need to expand. Example:
6621 * echo $(($a + `echo 1`)) $((1 + $((2)) ))
6622 */
6623 setup_string_in_str(&input, str);
6624 encode_string(NULL, &dest, &input, EOF);
6625//TODO: error check (encode_string returns 0 on error)?
6626 //bb_error_msg("'%s' -> '%s'", str, dest.data);
6627 exp_str = expand_string_to_string(dest.data,
6628 EXP_FLAG_ESC_GLOB_CHARS,
6629 /*unbackslash:*/ 1
6630 );
6631 //bb_error_msg("'%s' -> '%s'", dest.data, exp_str);
6632 o_free(&dest);
6633 return exp_str;
6634}
6635
6636static const char *first_special_char_in_vararg(const char *cp)
6637{
6638 for (;;) {
6639 if (!*cp) return NULL; /* string has no special chars */
6640 if (*cp == '$') return cp;
6641 if (*cp == '\\') return cp;
6642 if (*cp == '\'') return cp;
6643 if (*cp == '"') return cp;
6644#if ENABLE_HUSH_TICK
6645 if (*cp == '`') return cp;
6646#endif
6647 /* dquoted "${x:+ARG}" should not glob, therefore
6648 * '*' et al require some non-literal processing: */
6649 if (*cp == '*') return cp;
6650 if (*cp == '?') return cp;
6651 if (*cp == '[') return cp;
6652 cp++;
6653 }
6654}
6655
6656/* Expanding ARG in ${var#ARG}, ${var%ARG}, or ${var/ARG/ARG}.
6657 * These can contain single- and double-quoted strings,
6658 * and treated as if the ARG string is initially unquoted. IOW:
6659 * ${var#ARG} and "${var#ARG}" treat ARG the same (ARG can even be
6660 * a dquoted string: "${var#"zz"}"), the difference only comes later
6661 * (word splitting and globbing of the ${var...} result).
6662 */
6663#if !BASH_PATTERN_SUBST
6664#define encode_then_expand_vararg(str, handle_squotes, do_unbackslash) \
6665 encode_then_expand_vararg(str, handle_squotes)
6666#endif
6667static char *encode_then_expand_vararg(const char *str, int handle_squotes, int do_unbackslash)
6668{
6669#if !BASH_PATTERN_SUBST && ENABLE_HUSH_CASE
6670 const int do_unbackslash = 0;
6671#endif
6672 char *exp_str;
6673 struct in_str input;
6674 o_string dest = NULL_O_STRING;
6675
6676 if (!first_special_char_in_vararg(str)) {
6677 /* string has no special chars */
6678 return NULL;
6679 }
6680
6681 setup_string_in_str(&input, str);
6682 dest.data = xzalloc(1); /* start as "", not as NULL */
6683 exp_str = NULL;
6684
6685 for (;;) {
6686 int ch;
6687
6688 ch = i_getch(&input);
6689 debug_printf_parse("%s: ch=%c (%d) escape=%d\n",
6690 __func__, ch, ch, !!dest.o_expflags);
6691
6692 if (!dest.o_expflags) {
6693 if (ch == EOF)
6694 break;
6695 if (handle_squotes && ch == '\'') {
6696 if (!add_till_single_quote_dquoted(&dest, &input))
6697 goto ret; /* error */
6698 continue;
6699 }
6700 }
6701 if (ch == EOF) {
6702 syntax_error_unterm_ch('"');
6703 goto ret; /* error */
6704 }
6705 if (ch == '"') {
6706 dest.o_expflags ^= EXP_FLAG_ESC_GLOB_CHARS;
6707 continue;
6708 }
6709 if (ch == '\\') {
6710 ch = i_getch(&input);
6711 if (ch == EOF) {
6712//example? error message? syntax_error_unterm_ch('"');
6713 debug_printf_parse("%s: error: \\<eof>\n", __func__);
6714 goto ret;
6715 }
6716 o_addqchr(&dest, ch);
6717 continue;
6718 }
6719 if (ch == '$') {
6720 if (parse_dollar_squote(NULL, &dest, &input))
6721 continue;
6722 if (!parse_dollar(NULL, &dest, &input, /*quote_mask:*/ 0x80)) {
6723 debug_printf_parse("%s: error: parse_dollar returned 0 (error)\n", __func__);
6724 goto ret;
6725 }
6726 continue;
6727 }
6728#if ENABLE_HUSH_TICK
6729 if (ch == '`') {
6730 //unsigned pos = dest->length;
6731 o_addchr(&dest, SPECIAL_VAR_SYMBOL);
6732 o_addchr(&dest, 0x80 | '`');
6733 if (!add_till_backquote(&dest, &input,
6734 /*in_dquote:*/ dest.o_expflags /* nonzero if EXP_FLAG_ESC_GLOB_CHARS set */
6735 )
6736 ) {
6737 goto ret; /* error */
6738 }
6739 o_addchr(&dest, SPECIAL_VAR_SYMBOL);
6740 //debug_printf_subst("SUBST RES3 '%s'\n", dest->data + pos);
6741 continue;
6742 }
6743#endif
6744 o_addQchr(&dest, ch);
6745 } /* for (;;) */
6746
6747 debug_printf_parse("encode: '%s' -> '%s'\n", str, dest.data);
6748 exp_str = expand_string_to_string(dest.data,
6749 do_unbackslash ? EXP_FLAG_ESC_GLOB_CHARS : 0,
6750 do_unbackslash
6751 );
6752 ret:
6753 debug_printf_parse("expand: '%s' -> '%s'\n", dest.data, exp_str);
6754 o_free(&dest);
6755 return exp_str;
6756}
6757
6758/* Expanding ARG in ${var+ARG}, ${var-ARG}
6759 */
Francis Laniele7ca3a32023-12-22 22:02:42 +01006760static NOINLINE int encode_then_append_var_plusminus(o_string *output, int n,
Francis Laniel110b7692023-12-22 22:02:27 +01006761 char *str, int dquoted)
6762{
6763 struct in_str input;
6764 o_string dest = NULL_O_STRING;
6765
6766 if (!first_special_char_in_vararg(str)
6767 && '\0' == str[strcspn(str, G.ifs)]
6768 ) {
6769 /* string has no special chars
6770 * && string has no $IFS chars
6771 */
6772 if (dquoted) {
6773 /* Prints 1 (quoted expansion is a "" word, not nothing):
6774 * set -- "${notexist-}"; echo $#
6775 */
6776 output->has_quoted_part = 1;
6777 }
6778 return expand_vars_to_list(output, n, str);
6779 }
6780
6781 setup_string_in_str(&input, str);
6782
6783 for (;;) {
6784 int ch;
6785
6786 ch = i_getch(&input);
6787 debug_printf_parse("%s: ch=%c (%d) escape=%x\n",
6788 __func__, ch, ch, dest.o_expflags);
6789
6790 if (!dest.o_expflags) {
6791 if (ch == EOF)
6792 break;
Francis Laniele7ca3a32023-12-22 22:02:42 +01006793 if (!dquoted && !(output->o_expflags & EXP_FLAG_SINGLEWORD) && strchr(G.ifs, ch)) {
Francis Laniel110b7692023-12-22 22:02:27 +01006794 /* PREFIX${x:d${e}f ...} and we met space: expand "d${e}f" and start new word.
6795 * do not assume we are at the start of the word (PREFIX above).
6796 */
6797 if (dest.data) {
6798 n = expand_vars_to_list(output, n, dest.data);
6799 o_free_and_set_NULL(&dest);
6800 o_addchr(output, '\0');
6801 n = o_save_ptr(output, n); /* create next word */
6802 } else
6803 if (output->length != o_get_last_ptr(output, n)
6804 || output->has_quoted_part
6805 ) {
6806 /* For these cases:
6807 * f() { for i; do echo "|$i|"; done; }; x=x
6808 * f a${x:+ }b # 1st condition
6809 * |a|
6810 * |b|
6811 * f ""${x:+ }b # 2nd condition
6812 * ||
6813 * |b|
6814 */
6815 o_addchr(output, '\0');
6816 n = o_save_ptr(output, n); /* create next word */
6817 }
6818 continue;
6819 }
6820 if (!dquoted && ch == '\'') {
6821 if (!add_till_single_quote_dquoted(&dest, &input))
6822 goto ret; /* error */
6823 o_addchr(&dest, SPECIAL_VAR_SYMBOL);
6824 o_addchr(&dest, SPECIAL_VAR_SYMBOL);
6825 continue;
6826 }
6827 }
6828 if (ch == EOF) {
6829 syntax_error_unterm_ch('"');
6830 goto ret; /* error */
6831 }
6832 if (ch == '"') {
6833 dest.o_expflags ^= EXP_FLAG_ESC_GLOB_CHARS;
6834 if (dest.o_expflags) {
6835 o_addchr(&dest, SPECIAL_VAR_SYMBOL);
6836 o_addchr(&dest, SPECIAL_VAR_SYMBOL);
6837 }
6838 continue;
6839 }
6840 if (ch == '\\') {
6841 ch = i_getch(&input);
6842 if (ch == EOF) {
6843//example? error message? syntax_error_unterm_ch('"');
6844 debug_printf_parse("%s: error: \\<eof>\n", __func__);
6845 goto ret;
6846 }
6847 o_addqchr(&dest, ch);
6848 continue;
6849 }
6850 if (ch == '$') {
6851 if (!parse_dollar(NULL, &dest, &input, /*quote_mask:*/ (dest.o_expflags || dquoted) ? 0x80 : 0)) {
6852 debug_printf_parse("%s: error: parse_dollar returned 0 (error)\n", __func__);
6853 goto ret;
6854 }
6855 continue;
6856 }
6857#if ENABLE_HUSH_TICK
6858 if (ch == '`') {
6859 //unsigned pos = dest->length;
6860 o_addchr(&dest, SPECIAL_VAR_SYMBOL);
6861 o_addchr(&dest, (dest.o_expflags || dquoted) ? 0x80 | '`' : '`');
6862 if (!add_till_backquote(&dest, &input,
6863 /*in_dquote:*/ dest.o_expflags /* nonzero if EXP_FLAG_ESC_GLOB_CHARS set */
6864 )
6865 ) {
6866 goto ret; /* error */
6867 }
6868 o_addchr(&dest, SPECIAL_VAR_SYMBOL);
6869 //debug_printf_subst("SUBST RES3 '%s'\n", dest->data + pos);
6870 continue;
6871 }
6872#endif
6873 if (dquoted) {
6874 /* Always glob-protect if in dquotes:
6875 * x=x; echo "${x:+/bin/c*}" - prints: /bin/c*
6876 * x=x; echo "${x:+"/bin/c*"}" - prints: /bin/c*
6877 */
6878 o_addqchr(&dest, ch);
6879 } else {
6880 /* Glob-protect only if char is quoted:
6881 * x=x; echo ${x:+/bin/c*} - prints many filenames
6882 * x=x; echo ${x:+"/bin/c*"} - prints: /bin/c*
6883 */
6884 o_addQchr(&dest, ch);
6885 }
6886 } /* for (;;) */
6887
6888 if (dest.data) {
6889 n = expand_vars_to_list(output, n, dest.data);
6890 }
6891 ret:
6892 o_free(&dest);
6893 return n;
6894}
Francis Laniel36836fc2023-12-22 22:02:28 +01006895#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01006896
Francis Laniel36836fc2023-12-22 22:02:28 +01006897#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01006898#if ENABLE_FEATURE_SH_MATH
6899static arith_t expand_and_evaluate_arith(const char *arg, const char **errmsg_p)
6900{
6901 arith_state_t math_state;
6902 arith_t res;
6903 char *exp_str;
6904
6905 math_state.lookupvar = get_local_var_value;
6906 math_state.setvar = set_local_var_from_halves;
6907 //math_state.endofname = endofname;
6908 exp_str = encode_then_expand_string(arg);
6909 res = arith(&math_state, exp_str ? exp_str : arg);
6910 free(exp_str);
6911 if (errmsg_p)
6912 *errmsg_p = math_state.errmsg;
6913 if (math_state.errmsg)
6914 msg_and_die_if_script(math_state.errmsg);
6915 return res;
6916}
6917#endif
Francis Laniel36836fc2023-12-22 22:02:28 +01006918#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01006919
Francis Laniel36836fc2023-12-22 22:02:28 +01006920#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01006921#if BASH_PATTERN_SUBST
6922/* ${var/[/]pattern[/repl]} helpers */
6923static char *strstr_pattern(char *val, const char *pattern, int *size)
6924{
Francis Laniele7ca3a32023-12-22 22:02:42 +01006925 int first_escaped = (pattern[0] == '\\' && pattern[1]);
6926 /* "first_escaped" trick allows to treat e.g. "\*no_glob_chars"
6927 * as literal too (as it is semi-common, and easy to accomodate
6928 * by just using str + 1).
6929 */
6930 int sz = strcspn(pattern + first_escaped * 2, "*?[\\");
6931 if ((pattern + first_escaped * 2)[sz] == '\0') {
Francis Laniel110b7692023-12-22 22:02:27 +01006932 /* Optimization for trivial patterns.
6933 * Testcase for very slow replace (performs about 22k replaces):
6934 * x=::::::::::::::::::::::
6935 * 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}
6936 * echo "${x//:/|}"
6937 */
Francis Laniele7ca3a32023-12-22 22:02:42 +01006938 *size = sz + first_escaped;
6939 return strstr(val, pattern + first_escaped);
Francis Laniel110b7692023-12-22 22:02:27 +01006940 }
6941
6942 while (1) {
6943 char *end = scan_and_match(val, pattern, SCAN_MOVE_FROM_RIGHT + SCAN_MATCH_LEFT_HALF);
6944 debug_printf_varexp("val:'%s' pattern:'%s' end:'%s'\n", val, pattern, end);
6945 if (end) {
6946 *size = end - val;
6947 return val;
6948 }
6949 if (*val == '\0')
6950 return NULL;
6951 /* Optimization: if "*pat" did not match the start of "string",
6952 * we know that "tring", "ring" etc will not match too:
6953 */
6954 if (pattern[0] == '*')
6955 return NULL;
6956 val++;
6957 }
6958}
6959static char *replace_pattern(char *val, const char *pattern, const char *repl, char exp_op)
6960{
6961 char *result = NULL;
6962 unsigned res_len = 0;
6963 unsigned repl_len = strlen(repl);
6964
6965 /* Null pattern never matches, including if "var" is empty */
6966 if (!pattern[0])
6967 return result; /* NULL, no replaces happened */
6968
6969 while (1) {
6970 int size;
6971 char *s = strstr_pattern(val, pattern, &size);
6972 if (!s)
6973 break;
6974
6975 result = xrealloc(result, res_len + (s - val) + repl_len + 1);
6976 strcpy(mempcpy(result + res_len, val, s - val), repl);
6977 res_len += (s - val) + repl_len;
6978 debug_printf_varexp("val:'%s' s:'%s' result:'%s'\n", val, s, result);
6979
6980 val = s + size;
6981 if (exp_op == '/')
6982 break;
6983 }
6984 if (*val && result) {
6985 result = xrealloc(result, res_len + strlen(val) + 1);
6986 strcpy(result + res_len, val);
6987 debug_printf_varexp("val:'%s' result:'%s'\n", val, result);
6988 }
6989 debug_printf_varexp("result:'%s'\n", result);
6990 return result;
6991}
6992#endif /* BASH_PATTERN_SUBST */
Francis Laniel36836fc2023-12-22 22:02:28 +01006993#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01006994
6995static int append_str_maybe_ifs_split(o_string *output, int n,
6996 int first_ch, const char *val)
6997{
6998 if (!(first_ch & 0x80)) { /* unquoted $VAR */
6999 debug_printf_expand("unquoted '%s', output->o_escape:%d\n", val,
7000 !!(output->o_expflags & EXP_FLAG_ESC_GLOB_CHARS));
7001 if (val && val[0])
7002 n = expand_on_ifs(output, n, val);
7003 } else { /* quoted "$VAR" */
7004 output->has_quoted_part = 1;
7005 debug_printf_expand("quoted '%s', output->o_escape:%d\n", val,
7006 !!(output->o_expflags & EXP_FLAG_ESC_GLOB_CHARS));
7007 if (val && val[0])
7008 o_addQstr(output, val);
7009 }
7010 return n;
7011}
7012
7013/* Handle <SPECIAL_VAR_SYMBOL>varname...<SPECIAL_VAR_SYMBOL> construct.
7014 */
7015static NOINLINE int expand_one_var(o_string *output, int n,
7016 int first_ch, char *arg, char **pp)
7017{
7018 const char *val;
7019 char *to_be_freed;
7020 char *p;
7021 char *var;
7022 char exp_op;
7023 char exp_save = exp_save; /* for compiler */
7024 char *exp_saveptr; /* points to expansion operator */
7025 char *exp_word = exp_word; /* for compiler */
7026 char arg0;
7027
7028 val = NULL;
7029 to_be_freed = NULL;
7030 p = *pp;
7031 *p = '\0'; /* replace trailing SPECIAL_VAR_SYMBOL */
7032 var = arg;
7033 exp_saveptr = arg[1] ? strchr(VAR_ENCODED_SUBST_OPS, arg[1]) : NULL;
7034 arg0 = arg[0];
7035 arg[0] = (arg0 & 0x7f);
7036 exp_op = 0;
7037
7038 if (arg[0] == '#' && arg[1] /* ${#...} but not ${#} */
7039 && (!exp_saveptr /* and ( not(${#<op_char>...}) */
7040 || (arg[2] == '\0' && strchr(SPECIAL_VARS_STR, arg[1])) /* or ${#C} "len of $C" ) */
7041 ) /* NB: skipping ^^^specvar check mishandles ${#::2} */
7042 ) {
7043 /* It must be length operator: ${#var} */
7044 var++;
7045 exp_op = 'L';
7046 } else {
7047 /* Maybe handle parameter expansion */
7048 if (exp_saveptr /* if 2nd char is one of expansion operators */
7049 && strchr(NUMERIC_SPECVARS_STR, arg[0]) /* 1st char is special variable */
7050 ) {
7051 /* ${?:0}, ${#[:]%0} etc */
7052 exp_saveptr = var + 1;
7053 } else {
7054 /* ${?}, ${var}, ${var:0}, ${var[:]%0} etc */
7055 exp_saveptr = var+1 + strcspn(var+1, VAR_ENCODED_SUBST_OPS);
7056 }
7057 exp_op = exp_save = *exp_saveptr;
Francis Laniel36836fc2023-12-22 22:02:28 +01007058#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01007059 if (exp_op) {
7060 exp_word = exp_saveptr + 1;
7061 if (exp_op == ':') {
7062 exp_op = *exp_word++;
7063//TODO: try ${var:} and ${var:bogus} in non-bash config
7064 if (BASH_SUBSTR
7065 && (!exp_op || !strchr(MINUS_PLUS_EQUAL_QUESTION, exp_op))
7066 ) {
7067 /* oops... it's ${var:N[:M]}, not ${var:?xxx} or some such */
7068 exp_op = ':';
7069 exp_word--;
7070 }
7071 }
7072 *exp_saveptr = '\0';
7073 } /* else: it's not an expansion op, but bare ${var} */
Francis Laniel36836fc2023-12-22 22:02:28 +01007074#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01007075 }
7076
7077 /* Look up the variable in question */
7078 if (isdigit(var[0])) {
7079 /* parse_dollar should have vetted var for us */
Francis Laniel36836fc2023-12-22 22:02:28 +01007080#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01007081 int nn = xatoi_positive(var);
Francis Laniel36836fc2023-12-22 22:02:28 +01007082#else /* __U_BOOT__ */
7083 int nn = simple_strtoul(var, NULL, 10);
7084#endif /* __U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01007085 if (nn < G.global_argc)
7086 val = G.global_argv[nn];
7087 /* else val remains NULL: $N with too big N */
7088 } else {
7089 switch (var[0]) {
Francis Laniel36836fc2023-12-22 22:02:28 +01007090#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01007091 case '$': /* pid */
7092 val = utoa(G.root_pid);
7093 break;
7094 case '!': /* bg pid */
7095 val = G.last_bg_pid ? utoa(G.last_bg_pid) : "";
7096 break;
Francis Laniel36836fc2023-12-22 22:02:28 +01007097#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01007098 case '?': /* exitcode */
7099 val = utoa(G.last_exitcode);
7100 break;
7101 case '#': /* argc */
7102 val = utoa(G.global_argc ? G.global_argc-1 : 0);
7103 break;
Francis Laniel36836fc2023-12-22 22:02:28 +01007104#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01007105 case '-': { /* active options */
7106 /* Check set_mode() to see what option chars we support */
7107 char *cp;
7108 val = cp = G.optstring_buf;
7109 if (G.o_opt[OPT_O_ERREXIT])
7110 *cp++ = 'e';
7111 if (G_interactive_fd)
7112 *cp++ = 'i';
7113 if (G_x_mode)
7114 *cp++ = 'x';
7115 /* If G.o_opt[OPT_O_NOEXEC] is true,
7116 * commands read but are not executed,
7117 * so $- can not execute too, 'n' is never seen in $-.
7118 */
7119 if (G.opt_c)
7120 *cp++ = 'c';
7121 if (G.opt_s)
7122 *cp++ = 's';
7123 *cp = '\0';
7124 break;
7125 }
Francis Laniel36836fc2023-12-22 22:02:28 +01007126#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01007127 default:
Francis Lanielbfc406a2023-12-22 22:02:33 +01007128#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01007129 val = get_local_var_value(var);
Francis Lanielbfc406a2023-12-22 22:02:33 +01007130#else /* __U_BOOT__ */
7131 /*
7132 * Environment variable set with setenv* have to be
7133 * expanded.
7134 * So, we first search if the variable exists in
7135 * environment, if this is not the case, we default to
7136 * local value.
7137 */
7138 val = env_get(var);
7139 if (!val)
7140 val = get_local_var_value(var);
7141#endif /* __U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01007142 }
7143 }
7144
Francis Laniel36836fc2023-12-22 22:02:28 +01007145#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01007146 /* Handle any expansions */
7147 if (exp_op == 'L') {
7148 reinit_unicode_for_hush();
7149 debug_printf_expand("expand: length(%s)=", val);
7150 val = utoa(val ? unicode_strlen(val) : 0);
7151 debug_printf_expand("%s\n", val);
7152 } else if (exp_op) {
7153 if (exp_op == '%' || exp_op == '#') {
7154 /* Standard-mandated substring removal ops:
7155 * ${parameter%word} - remove smallest suffix pattern
7156 * ${parameter%%word} - remove largest suffix pattern
7157 * ${parameter#word} - remove smallest prefix pattern
7158 * ${parameter##word} - remove largest prefix pattern
7159 *
7160 * Word is expanded to produce a glob pattern.
7161 * Then var's value is matched to it and matching part removed.
7162 */
7163 /* bash compat: if x is "" and no shrinking of it is possible,
7164 * inner ${...} is not evaluated. Example:
7165 * unset b; : ${a%${b=B}}; echo $b
7166 * assignment b=B only happens if $a is not "".
7167 */
7168 if (val && val[0]) {
7169 char *t;
7170 char *exp_exp_word;
7171 char *loc;
7172 unsigned scan_flags = pick_scan(exp_op, *exp_word);
7173 if (exp_op == *exp_word) /* ## or %% */
7174 exp_word++;
7175 debug_printf_expand("expand: exp_word:'%s'\n", exp_word);
7176 exp_exp_word = encode_then_expand_vararg(exp_word, /*handle_squotes:*/ 1, /*unbackslash:*/ 0);
7177 if (exp_exp_word)
7178 exp_word = exp_exp_word;
7179 debug_printf_expand("expand: exp_word:'%s'\n", exp_word);
7180 /*
7181 * HACK ALERT. We depend here on the fact that
7182 * G.global_argv and results of utoa and get_local_var_value
7183 * are actually in writable memory:
7184 * scan_and_match momentarily stores NULs there.
7185 */
7186 t = (char*)val;
7187 loc = scan_and_match(t, exp_word, scan_flags);
7188 debug_printf_expand("op:%c str:'%s' pat:'%s' res:'%s'\n", exp_op, t, exp_word, loc);
7189 free(exp_exp_word);
7190 if (loc) { /* match was found */
7191 if (scan_flags & SCAN_MATCH_LEFT_HALF) /* #[#] */
7192 val = loc; /* take right part */
7193 else /* %[%] */
7194 val = to_be_freed = xstrndup(val, loc - val); /* left */
7195 }
7196 }
7197 }
7198#if BASH_PATTERN_SUBST
7199 else if (exp_op == '/' || exp_op == '\\') {
7200 /* It's ${var/[/]pattern[/repl]} thing.
7201 * Note that in encoded form it has TWO parts:
7202 * var/pattern<SPECIAL_VAR_SYMBOL>repl<SPECIAL_VAR_SYMBOL>
7203 * and if // is used, it is encoded as \:
7204 * var\pattern<SPECIAL_VAR_SYMBOL>repl<SPECIAL_VAR_SYMBOL>
7205 */
7206 /* bash compat: if var is "", both pattern and repl
7207 * are still evaluated, if it is unset, then not:
7208 * unset b; a=; : ${a/z/${b=3}}; echo $b # b=3
7209 * unset b; unset a; : ${a/z/${b=3}}; echo $b # b not set
7210 */
7211 if (val /*&& val[0]*/) {
7212 /* pattern uses non-standard expansion.
7213 * repl should be unbackslashed and globbed
7214 * by the usual expansion rules:
7215 * >az >bz
7216 * v='a bz'; echo "${v/a*z/a*z}" #prints "a*z"
7217 * v='a bz'; echo "${v/a*z/\z}" #prints "z"
7218 * v='a bz'; echo ${v/a*z/a*z} #prints "az"
7219 * v='a bz'; echo ${v/a*z/\z} #prints "z"
7220 * (note that a*z _pattern_ is never globbed!)
7221 */
7222 char *pattern, *repl, *t;
7223 pattern = encode_then_expand_vararg(exp_word, /*handle_squotes:*/ 1, /*unbackslash:*/ 0);
7224 if (!pattern)
7225 pattern = xstrdup(exp_word);
7226 debug_printf_varexp("pattern:'%s'->'%s'\n", exp_word, pattern);
7227 *p++ = SPECIAL_VAR_SYMBOL;
7228 exp_word = p;
7229 p = strchr(p, SPECIAL_VAR_SYMBOL);
7230 *p = '\0';
7231 repl = encode_then_expand_vararg(exp_word, /*handle_squotes:*/ 1, /*unbackslash:*/ 1);
7232 debug_printf_varexp("repl:'%s'->'%s'\n", exp_word, repl);
7233 /* HACK ALERT. We depend here on the fact that
7234 * G.global_argv and results of utoa and get_local_var_value
7235 * are actually in writable memory:
7236 * replace_pattern momentarily stores NULs there. */
7237 t = (char*)val;
7238 to_be_freed = replace_pattern(t,
7239 pattern,
7240 (repl ? repl : exp_word),
7241 exp_op);
7242 if (to_be_freed) /* at least one replace happened */
7243 val = to_be_freed;
7244 free(pattern);
7245 free(repl);
7246 } else {
7247 /* Unset variable always gives nothing */
7248 // a=; echo ${a/*/w} # "w"
7249 // unset a; echo ${a/*/w} # ""
7250 /* Just skip "replace" part */
7251 *p++ = SPECIAL_VAR_SYMBOL;
7252 p = strchr(p, SPECIAL_VAR_SYMBOL);
7253 *p = '\0';
7254 }
7255 }
7256#endif /* BASH_PATTERN_SUBST */
7257 else if (exp_op == ':') {
7258#if BASH_SUBSTR && ENABLE_FEATURE_SH_MATH
7259 /* It's ${var:N[:M]} bashism.
7260 * Note that in encoded form it has TWO parts:
7261 * var:N<SPECIAL_VAR_SYMBOL>M<SPECIAL_VAR_SYMBOL>
7262 */
7263 arith_t beg, len;
7264 unsigned vallen;
7265 const char *errmsg;
7266
7267 beg = expand_and_evaluate_arith(exp_word, &errmsg);
7268 if (errmsg)
7269 goto empty_result;
7270 debug_printf_varexp("beg:'%s'=%lld\n", exp_word, (long long)beg);
7271 *p++ = SPECIAL_VAR_SYMBOL;
7272 exp_word = p;
7273 p = strchr(p, SPECIAL_VAR_SYMBOL);
7274 *p = '\0';
7275 vallen = val ? strlen(val) : 0;
7276 if (beg < 0) {
7277 /* negative beg counts from the end */
7278 beg = (arith_t)vallen + beg;
7279 }
7280 /* If expansion will be empty, do not even evaluate len */
7281 if (!val || beg < 0 || beg > vallen) {
7282 /* Why > vallen, not >=? bash:
7283 * unset b; a=ab; : ${a:2:${b=3}}; echo $b # "", b=3 (!!!)
7284 * unset b; a=a; : ${a:2:${b=3}}; echo $b # "", b not set
7285 */
7286 goto empty_result;
7287 }
7288 len = expand_and_evaluate_arith(exp_word, &errmsg);
7289 if (errmsg)
7290 goto empty_result;
7291 debug_printf_varexp("len:'%s'=%lld\n", exp_word, (long long)len);
7292 debug_printf_varexp("from val:'%s'\n", val);
7293 if (len < 0) {
7294 /* in bash, len=-n means strlen()-n */
7295 len = (arith_t)vallen - beg + len;
7296 if (len < 0) /* bash compat */
7297 msg_and_die_if_script("%s: substring expression < 0", var);
7298 }
7299 if (len <= 0 || !val /*|| beg >= vallen*/) {
7300 empty_result:
7301 val = NULL;
7302 } else {
7303 /* Paranoia. What if user entered 9999999999999
7304 * which fits in arith_t but not int? */
7305 if (len > INT_MAX)
7306 len = INT_MAX;
7307 val = to_be_freed = xstrndup(val + beg, len);
7308 }
7309 debug_printf_varexp("val:'%s'\n", val);
7310#else /* not (HUSH_SUBSTR_EXPANSION && FEATURE_SH_MATH) */
7311 msg_and_die_if_script("malformed ${%s:...}", var);
7312 val = NULL;
7313#endif
7314 } else { /* one of "-=+?" */
7315 /* Standard-mandated substitution ops:
7316 * ${var?word} - indicate error if unset
7317 * If var is unset, word (or a message indicating it is unset
7318 * if word is null) is written to standard error
7319 * and the shell exits with a non-zero exit status.
7320 * Otherwise, the value of var is substituted.
7321 * ${var-word} - use default value
7322 * If var is unset, word is substituted.
7323 * ${var=word} - assign and use default value
7324 * If var is unset, word is assigned to var.
7325 * In all cases, final value of var is substituted.
7326 * ${var+word} - use alternative value
7327 * If var is unset, null is substituted.
7328 * Otherwise, word is substituted.
7329 *
7330 * Word is subjected to tilde expansion, parameter expansion,
7331 * command substitution, and arithmetic expansion.
7332 * If word is not needed, it is not expanded.
7333 *
7334 * Colon forms (${var:-word}, ${var:=word} etc) do the same,
7335 * but also treat null var as if it is unset.
7336 *
7337 * Word-splitting and single quote behavior:
7338 *
7339 * $ f() { for i; do echo "|$i|"; done; }
7340 *
7341 * $ x=; f ${x:?'x y' z}; echo $?
7342 * bash: x: x y z # neither f nor "echo $?" executes
7343 * (if interactive, bash does not exit, but merely aborts to prompt. $? is set to 1)
7344 * $ x=; f "${x:?'x y' z}"
7345 * bash: x: x y z # dash prints: dash: x: 'x y' z
7346 *
7347 * $ x=; f ${x:='x y' z}
7348 * |x|
7349 * |y|
7350 * |z|
7351 * $ x=; f "${x:='x y' z}"
7352 * |'x y' z|
7353 *
7354 * $ x=x; f ${x:+'x y' z}
7355 * |x y|
7356 * |z|
7357 * $ x=x; f "${x:+'x y' z}"
7358 * |'x y' z|
7359 *
7360 * $ x=; f ${x:-'x y' z}
7361 * |x y|
7362 * |z|
7363 * $ x=; f "${x:-'x y' z}"
7364 * |'x y' z|
7365 */
7366 int use_word = (!val || ((exp_save == ':') && !val[0]));
7367 if (exp_op == '+')
7368 use_word = !use_word;
7369 debug_printf_expand("expand: op:%c (null:%s) test:%i\n", exp_op,
7370 (exp_save == ':') ? "true" : "false", use_word);
7371 if (use_word) {
7372 if (exp_op == '+' || exp_op == '-') {
7373 /* ${var+word} - use alternative value */
7374 /* ${var-word} - use default value */
7375 n = encode_then_append_var_plusminus(output, n, exp_word,
7376 /*dquoted:*/ (arg0 & 0x80)
7377 );
7378 val = NULL;
7379 } else {
7380 /* ${var?word} - indicate error if unset */
7381 /* ${var=word} - assign and use default value */
7382 to_be_freed = encode_then_expand_vararg(exp_word,
7383 /*handle_squotes:*/ !(arg0 & 0x80),
7384 /*unbackslash:*/ 0
7385 );
7386 if (to_be_freed)
7387 exp_word = to_be_freed;
7388 if (exp_op == '?') {
7389 /* mimic bash message */
7390 msg_and_die_if_script("%s: %s",
7391 var,
7392 exp_word[0]
7393 ? exp_word
7394 : "parameter null or not set"
7395 /* ash has more specific messages, a-la: */
7396 /*: (exp_save == ':' ? "parameter null or not set" : "parameter not set")*/
7397 );
7398//TODO: how interactive bash aborts expansion mid-command?
7399//It aborts the entire line, returns to prompt:
7400// $ f() { for i; do echo "|$i|"; done; }; x=; f "${x:?'x y' z}"; echo YO
7401// bash: x: x y z
7402// $
7403// ("echo YO" is not executed, neither the f function call)
7404 } else {
7405 val = exp_word;
7406 }
7407 if (exp_op == '=') {
7408 /* ${var=[word]} or ${var:=[word]} */
7409 if (isdigit(var[0]) || var[0] == '#') {
7410 /* mimic bash message */
7411 msg_and_die_if_script("$%s: cannot assign in this way", var);
7412 val = NULL;
7413 } else {
7414 char *new_var = xasprintf("%s=%s", var, val);
Francis Laniele7ca3a32023-12-22 22:02:42 +01007415 set_local_var0(new_var);
Francis Laniel110b7692023-12-22 22:02:27 +01007416 }
7417 }
7418 }
7419 }
7420 } /* one of "-=+?" */
7421
7422 *exp_saveptr = exp_save;
7423 } /* if (exp_op) */
7424
Francis Laniel36836fc2023-12-22 22:02:28 +01007425#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01007426 arg[0] = arg0;
7427 *pp = p;
7428
7429 n = append_str_maybe_ifs_split(output, n, first_ch, val);
7430
7431 free(to_be_freed);
7432 return n;
7433}
7434
7435/* Expand all variable references in given string, adding words to list[]
7436 * at n, n+1,... positions. Return updated n (so that list[n] is next one
7437 * to be filled). This routine is extremely tricky: has to deal with
7438 * variables/parameters with whitespace, $* and $@, and constructs like
7439 * 'echo -$*-'. If you play here, you must run testsuite afterwards! */
7440static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg)
7441{
7442 /* output->o_expflags & EXP_FLAG_SINGLEWORD (0x80) if we are in
7443 * expansion of right-hand side of assignment == 1-element expand.
7444 */
7445 char cant_be_null = 0; /* only bit 0x80 matters */
7446 char *p;
7447
7448 debug_printf_expand("expand_vars_to_list: arg:'%s' singleword:%x\n", arg,
7449 !!(output->o_expflags & EXP_FLAG_SINGLEWORD));
7450 debug_print_list("expand_vars_to_list[0]", output, n);
7451
7452 while ((p = strchr(arg, SPECIAL_VAR_SYMBOL)) != NULL) {
7453 char first_ch;
7454#if ENABLE_FEATURE_SH_MATH
7455 char arith_buf[sizeof(arith_t)*3 + 2];
7456#endif
7457
7458 if (output->ended_in_ifs) {
7459 o_addchr(output, '\0');
7460 n = o_save_ptr(output, n);
7461 output->ended_in_ifs = 0;
7462 }
7463
7464 o_addblock(output, arg, p - arg);
7465 debug_print_list("expand_vars_to_list[1]", output, n);
7466 arg = ++p;
7467 p = strchr(p, SPECIAL_VAR_SYMBOL);
7468
7469 /* Fetch special var name (if it is indeed one of them)
7470 * and quote bit, force the bit on if singleword expansion -
7471 * important for not getting v=$@ expand to many words. */
7472 first_ch = arg[0] | (output->o_expflags & EXP_FLAG_SINGLEWORD);
7473
7474 /* Is this variable quoted and thus expansion can't be null?
7475 * "$@" is special. Even if quoted, it can still
7476 * expand to nothing (not even an empty string),
7477 * thus it is excluded. */
7478 if ((first_ch & 0x7f) != '@')
7479 cant_be_null |= first_ch;
7480
7481 switch (first_ch & 0x7f) {
7482 /* Highest bit in first_ch indicates that var is double-quoted */
7483 case '*':
7484 case '@': {
7485 int i;
Francis Lanielbfc406a2023-12-22 22:02:33 +01007486#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01007487 if (!G.global_argv[1])
Francis Lanielbfc406a2023-12-22 22:02:33 +01007488#else /* __U_BOOT__ */
7489 if (!G.global_argv || !G.global_argv[1])
7490#endif /* __U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01007491 break;
7492 i = 1;
7493 cant_be_null |= first_ch; /* do it for "$@" _now_, when we know it's not empty */
7494 if (!(first_ch & 0x80)) { /* unquoted $* or $@ */
7495 while (G.global_argv[i]) {
7496 n = expand_on_ifs(output, n, G.global_argv[i]);
7497 debug_printf_expand("expand_vars_to_list: argv %d (last %d)\n", i, G.global_argc - 1);
7498 if (G.global_argv[i++][0] && G.global_argv[i]) {
7499 /* this argv[] is not empty and not last:
7500 * put terminating NUL, start new word */
7501 o_addchr(output, '\0');
7502 debug_print_list("expand_vars_to_list[2]", output, n);
7503 n = o_save_ptr(output, n);
7504 debug_print_list("expand_vars_to_list[3]", output, n);
7505 }
7506 }
7507 } else
7508 /* If EXP_FLAG_SINGLEWORD, we handle assignment 'a=....$@.....'
7509 * and in this case should treat it like '$*' - see 'else...' below */
7510 if (first_ch == (char)('@'|0x80) /* quoted $@ */
7511 && !(output->o_expflags & EXP_FLAG_SINGLEWORD) /* not v="$@" case */
7512 ) {
7513 while (1) {
7514 o_addQstr(output, G.global_argv[i]);
7515 if (++i >= G.global_argc)
7516 break;
7517 o_addchr(output, '\0');
7518 debug_print_list("expand_vars_to_list[4]", output, n);
7519 n = o_save_ptr(output, n);
7520 }
7521 } else { /* quoted $* (or v="$@" case): add as one word */
7522 while (1) {
7523 o_addQstr(output, G.global_argv[i]);
7524 if (!G.global_argv[++i])
7525 break;
7526 if (G.ifs[0])
7527 o_addchr(output, G.ifs[0]);
7528 }
7529 output->has_quoted_part = 1;
7530 }
7531 break;
7532 }
7533 case SPECIAL_VAR_SYMBOL: {
7534 /* <SPECIAL_VAR_SYMBOL><SPECIAL_VAR_SYMBOL> */
7535 /* "Empty variable", used to make "" etc to not disappear */
7536 output->has_quoted_part = 1;
7537 cant_be_null = 0x80;
7538 arg++;
7539 break;
7540 }
7541 case SPECIAL_VAR_QUOTED_SVS:
7542 /* <SPECIAL_VAR_SYMBOL><SPECIAL_VAR_QUOTED_SVS><SPECIAL_VAR_SYMBOL> */
7543 /* "^C variable", represents literal ^C char (possible in scripts) */
7544 o_addchr(output, SPECIAL_VAR_SYMBOL);
7545 arg++;
7546 break;
7547#if ENABLE_HUSH_TICK
7548 case '`': {
7549 /* <SPECIAL_VAR_SYMBOL>`cmd<SPECIAL_VAR_SYMBOL> */
7550 o_string subst_result = NULL_O_STRING;
7551
7552 *p = '\0'; /* replace trailing <SPECIAL_VAR_SYMBOL> */
7553 arg++;
7554 /* Can't just stuff it into output o_string,
7555 * expanded result may need to be globbed
7556 * and $IFS-split */
7557 debug_printf_subst("SUBST '%s' first_ch %x\n", arg, first_ch);
7558 G.last_exitcode = process_command_subs(&subst_result, arg);
7559 G.expand_exitcode = G.last_exitcode;
7560 debug_printf_subst("SUBST RES:%d '%s'\n", G.last_exitcode, subst_result.data);
7561 n = append_str_maybe_ifs_split(output, n, first_ch, subst_result.data);
7562 o_free(&subst_result);
7563 break;
7564 }
7565#endif
7566#if ENABLE_FEATURE_SH_MATH
7567 case '+': {
7568 /* <SPECIAL_VAR_SYMBOL>+arith<SPECIAL_VAR_SYMBOL> */
7569 arith_t res;
7570
7571 arg++; /* skip '+' */
7572 *p = '\0'; /* replace trailing <SPECIAL_VAR_SYMBOL> */
7573 debug_printf_subst("ARITH '%s' first_ch %x\n", arg, first_ch);
7574 res = expand_and_evaluate_arith(arg, NULL);
7575 debug_printf_subst("ARITH RES '"ARITH_FMT"'\n", res);
7576 sprintf(arith_buf, ARITH_FMT, res);
7577 if (res < 0
7578 && first_ch == (char)('+'|0x80)
7579 /* && (output->o_expflags & EXP_FLAG_ESC_GLOB_CHARS) */
7580 ) {
7581 /* Quoted negative ariths, like filename[0"$((-9))"],
7582 * should not be interpreted as glob ranges.
7583 * Convert leading '-' to '\-':
7584 */
7585 o_grow_by(output, 1);
7586 output->data[output->length++] = '\\';
7587 }
7588 o_addstr(output, arith_buf);
7589 break;
7590 }
7591#endif
7592 default:
7593 /* <SPECIAL_VAR_SYMBOL>varname[ops]<SPECIAL_VAR_SYMBOL> */
7594 n = expand_one_var(output, n, first_ch, arg, &p);
7595 break;
7596 } /* switch (char after <SPECIAL_VAR_SYMBOL>) */
7597
7598 /* Restore NULL'ed SPECIAL_VAR_SYMBOL.
7599 * Do the check to avoid writing to a const string. */
7600 if (*p != SPECIAL_VAR_SYMBOL)
7601 *p = SPECIAL_VAR_SYMBOL;
7602 arg = ++p;
7603 } /* end of "while (SPECIAL_VAR_SYMBOL is found) ..." */
7604
7605 if (*arg) {
7606 /* handle trailing string */
7607 if (output->ended_in_ifs) {
7608 o_addchr(output, '\0');
7609 n = o_save_ptr(output, n);
7610 }
7611 debug_print_list("expand_vars_to_list[a]", output, n);
7612 /* this part is literal, and it was already pre-quoted
7613 * if needed (much earlier), do not use o_addQstr here!
7614 */
7615 o_addstr(output, arg);
7616 debug_print_list("expand_vars_to_list[b]", output, n);
7617 } else
7618 if (output->length == o_get_last_ptr(output, n) /* expansion is empty */
7619 && !(cant_be_null & 0x80) /* and all vars were not quoted */
7620 && !output->has_quoted_part
7621 ) {
7622 n--;
7623 /* allow to reuse list[n] later without re-growth */
7624 output->has_empty_slot = 1;
7625 }
7626
7627 return n;
7628}
7629
7630static char **expand_variables(char **argv, unsigned expflags)
7631{
7632 int n;
7633 char **list;
7634 o_string output = NULL_O_STRING;
7635
7636 output.o_expflags = expflags;
7637
7638 n = 0;
7639 for (;;) {
7640 /* go to next list[n] */
7641 output.ended_in_ifs = 0;
7642 n = o_save_ptr(&output, n);
7643
7644 if (!*argv)
7645 break;
7646
7647 /* expand argv[i] */
7648 n = expand_vars_to_list(&output, n, *argv++);
7649 /* if (!output->has_empty_slot) -- need this?? */
7650 o_addchr(&output, '\0');
7651 }
7652 debug_print_list("expand_variables", &output, n);
7653
7654 /* output.data (malloced in one block) gets returned in "list" */
7655 list = o_finalize_list(&output, n);
7656 debug_print_strings("expand_variables[1]", list);
7657 return list;
7658}
7659
7660static char **expand_strvec_to_strvec(char **argv)
7661{
7662 return expand_variables(argv, EXP_FLAG_GLOB | EXP_FLAG_ESC_GLOB_CHARS);
7663}
7664
7665#if defined(CMD_SINGLEWORD_NOGLOB) || defined(CMD_TEST2_SINGLEWORD_NOGLOB)
7666static char **expand_strvec_to_strvec_singleword_noglob(char **argv)
7667{
7668 return expand_variables(argv, EXP_FLAG_SINGLEWORD);
7669}
7670#endif
7671
7672/* Used for expansion of right hand of assignments,
7673 * $((...)), heredocs, variable expansion parts.
7674 *
7675 * NB: should NOT do globbing!
7676 * "export v=/bin/c*; env | grep ^v=" outputs "v=/bin/c*"
7677 */
7678static char *expand_string_to_string(const char *str, int EXP_flags, int do_unbackslash)
7679{
7680#if !BASH_PATTERN_SUBST && !ENABLE_HUSH_CASE
7681 const int do_unbackslash = 1;
7682 const int EXP_flags = EXP_FLAG_ESC_GLOB_CHARS;
7683#endif
7684 char *argv[2], **list;
7685
7686 debug_printf_expand("string_to_string<='%s'\n", str);
7687 /* This is generally an optimization, but it also
7688 * handles "", which otherwise trips over !list[0] check below.
7689 * (is this ever happens that we actually get str="" here?)
7690 */
7691 if (!strchr(str, SPECIAL_VAR_SYMBOL) && !strchr(str, '\\')) {
7692 //TODO: Can use on strings with \ too, just unbackslash() them?
7693 debug_printf_expand("string_to_string(fast)=>'%s'\n", str);
7694 return xstrdup(str);
7695 }
7696
7697 argv[0] = (char*)str;
7698 argv[1] = NULL;
7699 list = expand_variables(argv, EXP_flags | EXP_FLAG_SINGLEWORD);
7700 if (!list[0]) {
7701 /* Example where it happens:
7702 * x=; echo ${x:-"$@"}
7703 */
7704 ((char*)list)[0] = '\0';
7705 } else {
7706 if (HUSH_DEBUG)
7707 if (list[1])
7708 bb_simple_error_msg_and_die("BUG in varexp2");
7709 /* actually, just move string 2*sizeof(char*) bytes back */
7710 overlapping_strcpy((char*)list, list[0]);
7711 if (do_unbackslash)
7712 unbackslash((char*)list);
7713 }
7714 debug_printf_expand("string_to_string=>'%s'\n", (char*)list);
7715 return (char*)list;
7716}
7717
7718#if 0
7719static char* expand_strvec_to_string(char **argv)
7720{
7721 char **list;
7722
7723 list = expand_variables(argv, EXP_FLAG_SINGLEWORD);
7724 /* Convert all NULs to spaces */
7725 if (list[0]) {
7726 int n = 1;
7727 while (list[n]) {
7728 if (HUSH_DEBUG)
7729 if (list[n-1] + strlen(list[n-1]) + 1 != list[n])
7730 bb_error_msg_and_die("BUG in varexp3");
7731 /* bash uses ' ' regardless of $IFS contents */
7732 list[n][-1] = ' ';
7733 n++;
7734 }
7735 }
7736 overlapping_strcpy((char*)list, list[0] ? list[0] : "");
7737 debug_printf_expand("strvec_to_string='%s'\n", (char*)list);
7738 return (char*)list;
7739}
7740#endif
7741
Francis Laniel36836fc2023-12-22 22:02:28 +01007742#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01007743static char **expand_assignments(char **argv, int count)
7744{
7745 int i;
7746 char **p;
7747
7748 G.expanded_assignments = p = NULL;
7749 /* Expand assignments into one string each */
7750 for (i = 0; i < count; i++) {
7751 p = add_string_to_strings(p,
7752 expand_string_to_string(argv[i],
7753 EXP_FLAG_ESC_GLOB_CHARS,
7754 /*unbackslash:*/ 1
7755 )
7756 );
7757 G.expanded_assignments = p;
7758 }
7759 G.expanded_assignments = NULL;
7760 return p;
7761}
7762
Francis Laniel110b7692023-12-22 22:02:27 +01007763static void switch_off_special_sigs(unsigned mask)
7764{
7765 unsigned sig = 0;
7766 while ((mask >>= 1) != 0) {
7767 sig++;
7768 if (!(mask & 1))
7769 continue;
7770#if ENABLE_HUSH_TRAP
7771 if (G_traps) {
7772 if (G_traps[sig] && !G_traps[sig][0])
7773 /* trap is '', has to remain SIG_IGN */
7774 continue;
7775 free(G_traps[sig]);
7776 G_traps[sig] = NULL;
7777 }
7778#endif
7779 /* We are here only if no trap or trap was not '' */
7780 install_sighandler(sig, SIG_DFL);
7781 }
7782}
Francis Laniel36836fc2023-12-22 22:02:28 +01007783#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01007784
Francis Laniel36836fc2023-12-22 22:02:28 +01007785#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01007786#if BB_MMU
7787/* never called */
7788void re_execute_shell(char ***to_free, const char *s,
7789 char *g_argv0, char **g_argv,
7790 char **builtin_argv) NORETURN;
7791
7792static void reset_traps_to_defaults(void)
7793{
7794 /* This function is always called in a child shell
7795 * after fork (not vfork, NOMMU doesn't use this function).
7796 */
7797 IF_HUSH_TRAP(unsigned sig;)
7798 unsigned mask;
7799
7800 /* Child shells are not interactive.
7801 * SIGTTIN/SIGTTOU/SIGTSTP should not have special handling.
7802 * Testcase: (while :; do :; done) + ^Z should background.
7803 * Same goes for SIGTERM, SIGHUP, SIGINT.
7804 */
7805 mask = (G.special_sig_mask & SPECIAL_INTERACTIVE_SIGS) | G_fatal_sig_mask;
7806 if (!G_traps && !mask)
7807 return; /* already no traps and no special sigs */
7808
7809 /* Switch off special sigs */
7810 switch_off_special_sigs(mask);
7811# if ENABLE_HUSH_JOB
7812 G_fatal_sig_mask = 0;
7813# endif
7814 G.special_sig_mask &= ~SPECIAL_INTERACTIVE_SIGS;
7815 /* SIGQUIT,SIGCHLD and maybe SPECIAL_JOBSTOP_SIGS
7816 * remain set in G.special_sig_mask */
7817
7818# if ENABLE_HUSH_TRAP
7819 if (!G_traps)
7820 return;
7821
7822 /* Reset all sigs to default except ones with empty traps */
7823 for (sig = 0; sig < NSIG; sig++) {
7824 if (!G_traps[sig])
7825 continue; /* no trap: nothing to do */
7826 if (!G_traps[sig][0])
7827 continue; /* empty trap: has to remain SIG_IGN */
7828 /* sig has non-empty trap, reset it: */
7829 free(G_traps[sig]);
7830 G_traps[sig] = NULL;
7831 /* There is no signal for trap 0 (EXIT) */
7832 if (sig == 0)
7833 continue;
7834 install_sighandler(sig, pick_sighandler(sig));
7835 }
7836# endif
7837}
7838
7839#else /* !BB_MMU */
7840
7841static void re_execute_shell(char ***to_free, const char *s,
7842 char *g_argv0, char **g_argv,
7843 char **builtin_argv) NORETURN;
7844static void re_execute_shell(char ***to_free, const char *s,
7845 char *g_argv0, char **g_argv,
7846 char **builtin_argv)
7847{
7848# define NOMMU_HACK_FMT ("-$%x:%x:%x:%x:%x:%llx" IF_HUSH_LOOPS(":%x"))
7849 /* delims + 2 * (number of bytes in printed hex numbers) */
7850 char param_buf[sizeof(NOMMU_HACK_FMT) + 2 * (sizeof(int)*6 + sizeof(long long)*1)];
7851 char *heredoc_argv[4];
7852 struct variable *cur;
7853# if ENABLE_HUSH_FUNCTIONS
7854 struct function *funcp;
7855# endif
7856 char **argv, **pp;
7857 unsigned cnt;
7858 unsigned long long empty_trap_mask;
7859
7860 if (!g_argv0) { /* heredoc */
7861 argv = heredoc_argv;
7862 argv[0] = (char *) G.argv0_for_re_execing;
7863 argv[1] = (char *) "-<";
7864 argv[2] = (char *) s;
7865 argv[3] = NULL;
7866 pp = &argv[3]; /* used as pointer to empty environment */
7867 goto do_exec;
7868 }
7869
7870 cnt = 0;
7871 pp = builtin_argv;
7872 if (pp) while (*pp++)
7873 cnt++;
7874
7875 empty_trap_mask = 0;
7876 if (G_traps) {
7877 int sig;
7878 for (sig = 1; sig < NSIG; sig++) {
7879 if (G_traps[sig] && !G_traps[sig][0])
7880 empty_trap_mask |= 1LL << sig;
7881 }
7882 }
7883
7884 sprintf(param_buf, NOMMU_HACK_FMT
7885 , (unsigned) G.root_pid
7886 , (unsigned) G.root_ppid
7887 , (unsigned) G.last_bg_pid
7888 , (unsigned) G.last_exitcode
7889 , cnt
7890 , empty_trap_mask
7891 IF_HUSH_LOOPS(, G.depth_of_loop)
7892 );
7893# undef NOMMU_HACK_FMT
7894 /* 1:hush 2:-$<pid>:<pid>:<exitcode>:<etc...> <vars...> <funcs...>
7895 * 3:-c 4:<cmd> 5:<arg0> <argN...> 6:NULL
7896 */
7897 cnt += 6;
7898 for (cur = G.top_var; cur; cur = cur->next) {
7899 if (!cur->flg_export || cur->flg_read_only)
7900 cnt += 2;
7901 }
Francis Laniele7ca3a32023-12-22 22:02:42 +01007902# if ENABLE_HUSH_LINENO_VAR
7903 cnt += 2;
7904# endif
Francis Laniel110b7692023-12-22 22:02:27 +01007905# if ENABLE_HUSH_FUNCTIONS
7906 for (funcp = G.top_func; funcp; funcp = funcp->next)
7907 cnt += 3;
7908# endif
7909 pp = g_argv;
7910 while (*pp++)
7911 cnt++;
7912 *to_free = argv = pp = xzalloc(sizeof(argv[0]) * cnt);
7913 *pp++ = (char *) G.argv0_for_re_execing;
7914 *pp++ = param_buf;
7915 for (cur = G.top_var; cur; cur = cur->next) {
7916 if (strcmp(cur->varstr, hush_version_str) == 0)
7917 continue;
7918 if (cur->flg_read_only) {
7919 *pp++ = (char *) "-R";
7920 *pp++ = cur->varstr;
7921 } else if (!cur->flg_export) {
7922 *pp++ = (char *) "-V";
7923 *pp++ = cur->varstr;
7924 }
7925 }
Francis Laniele7ca3a32023-12-22 22:02:42 +01007926# if ENABLE_HUSH_LINENO_VAR
7927 *pp++ = (char *) "-L";
7928 *pp++ = utoa(G.execute_lineno);
7929# endif
Francis Laniel110b7692023-12-22 22:02:27 +01007930# if ENABLE_HUSH_FUNCTIONS
7931 for (funcp = G.top_func; funcp; funcp = funcp->next) {
7932 *pp++ = (char *) "-F";
7933 *pp++ = funcp->name;
7934 *pp++ = funcp->body_as_string;
7935 }
7936# endif
7937 /* We can pass activated traps here. Say, -Tnn:trap_string
7938 *
7939 * However, POSIX says that subshells reset signals with traps
7940 * to SIG_DFL.
7941 * I tested bash-3.2 and it not only does that with true subshells
7942 * of the form ( list ), but with any forked children shells.
7943 * I set trap "echo W" WINCH; and then tried:
7944 *
7945 * { echo 1; sleep 20; echo 2; } &
7946 * while true; do echo 1; sleep 20; echo 2; break; done &
7947 * true | { echo 1; sleep 20; echo 2; } | cat
7948 *
7949 * In all these cases sending SIGWINCH to the child shell
7950 * did not run the trap. If I add trap "echo V" WINCH;
7951 * _inside_ group (just before echo 1), it works.
7952 *
7953 * I conclude it means we don't need to pass active traps here.
7954 */
7955 *pp++ = (char *) "-c";
7956 *pp++ = (char *) s;
7957 if (builtin_argv) {
7958 while (*++builtin_argv)
7959 *pp++ = *builtin_argv;
7960 *pp++ = (char *) "";
7961 }
7962 *pp++ = g_argv0;
7963 while (*g_argv)
7964 *pp++ = *g_argv++;
7965 /* *pp = NULL; - is already there */
7966 pp = environ;
7967
7968 do_exec:
7969 debug_printf_exec("re_execute_shell pid:%d cmd:'%s'\n", getpid(), s);
7970 /* Don't propagate SIG_IGN to the child */
7971 if (SPECIAL_JOBSTOP_SIGS != 0)
7972 switch_off_special_sigs(G.special_sig_mask & SPECIAL_JOBSTOP_SIGS);
7973 execve(bb_busybox_exec_path, argv, pp);
7974 /* Fallback. Useful for init=/bin/hush usage etc */
7975 if (argv[0][0] == '/')
7976 execve(argv[0], argv, pp);
7977 xfunc_error_retval = 127;
7978 bb_simple_error_msg_and_die("can't re-execute the shell");
7979}
7980#endif /* !BB_MMU */
7981
Francis Laniel36836fc2023-12-22 22:02:28 +01007982#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01007983
7984static int run_and_free_list(struct pipe *pi);
7985
7986/* Executing from string: eval, sh -c '...'
7987 * or from file: /etc/profile, . file, sh <script>, sh (intereactive)
7988 * end_trigger controls how often we stop parsing
7989 * NUL: parse all, execute, return
7990 * ';': parse till ';' or newline, execute, repeat till EOF
7991 */
Francis Laniel26cafe12023-12-22 22:02:34 +01007992#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01007993static void parse_and_run_stream(struct in_str *inp, int end_trigger)
Francis Laniel26cafe12023-12-22 22:02:34 +01007994#else /* __U_BOOT__ */
7995static int parse_and_run_stream(struct in_str *inp, int end_trigger)
7996#endif /* __U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01007997{
7998 /* Why we need empty flag?
7999 * An obscure corner case "false; ``; echo $?":
8000 * empty command in `` should still set $? to 0.
8001 * But we can't just set $? to 0 at the start,
8002 * this breaks "false; echo `echo $?`" case.
8003 */
8004 bool empty = 1;
Francis Laniel26cafe12023-12-22 22:02:34 +01008005#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01008006 while (1) {
Francis Laniel26cafe12023-12-22 22:02:34 +01008007#else /* __U_BOOT__ */
8008 do {
8009#endif /* __U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01008010 struct pipe *pipe_list;
8011
8012#if ENABLE_HUSH_INTERACTIVE
8013 if (end_trigger == ';') {
8014 G.promptmode = 0; /* PS1 */
8015 debug_printf_prompt("%s promptmode=%d\n", __func__, G.promptmode);
8016 }
8017#endif
8018 pipe_list = parse_stream(NULL, NULL, inp, end_trigger);
8019 if (!pipe_list || pipe_list == ERR_PTR) { /* EOF/error */
8020 /* If we are in "big" script
8021 * (not in `cmd` or something similar)...
8022 */
8023 if (pipe_list == ERR_PTR && end_trigger == ';') {
8024 /* Discard cached input (rest of line) */
8025 int ch = inp->last_char;
8026 while (ch != EOF && ch != '\n') {
8027 //bb_error_msg("Discarded:'%c'", ch);
8028 ch = i_getch(inp);
8029 }
8030 /* Force prompt */
8031 inp->p = NULL;
8032 /* This stream isn't empty */
8033 empty = 0;
8034 continue;
8035 }
8036 if (!pipe_list && empty)
8037 G.last_exitcode = 0;
8038 break;
8039 }
8040 debug_print_tree(pipe_list, 0);
8041 debug_printf_exec("parse_and_run_stream: run_and_free_list\n");
Francis Laniel3b66e572023-12-22 22:02:32 +01008042#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01008043 run_and_free_list(pipe_list);
Francis Laniel3b66e572023-12-22 22:02:32 +01008044#else /* __U_BOOT__ */
8045 int rcode = run_and_free_list(pipe_list);
8046 /*
8047 * We reset input string to not run the following command, so running
8048 * 'exit; echo foo' does not print foo.
8049 */
8050 if (rcode <= EXIT_RET_CODE)
8051 setup_file_in_str(inp);
8052#endif /* __U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01008053 empty = 0;
8054 if (G_flag_return_in_progress == 1)
8055 break;
Francis Laniel26cafe12023-12-22 22:02:34 +01008056#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01008057 }
Francis Laniel26cafe12023-12-22 22:02:34 +01008058#else /* __U_BOOT__ */
8059 /*
8060 * This do/while is needed by run_command to avoid looping on a command
8061 * with syntax error.
8062 */
8063 } while (!(G.run_command_flags & FLAG_EXIT_FROM_LOOP));
8064
8065 return G.last_exitcode;
8066#endif /* __U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01008067}
8068
Francis Laniel36836fc2023-12-22 22:02:28 +01008069#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01008070static void parse_and_run_string(const char *s)
Francis Laniel26cafe12023-12-22 22:02:34 +01008071#else /* __U_BOOT__ */
8072static int parse_and_run_string(const char *s)
8073#endif /* __U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01008074{
8075 struct in_str input;
Francis Laniele7ca3a32023-12-22 22:02:42 +01008076#ifndef __U_BOOT__
8077 IF_HUSH_LINENO_VAR(unsigned sv = G.parse_lineno;)
8078#else /* __U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01008079 //IF_HUSH_LINENO_VAR(unsigned sv = G.parse_lineno;)
Francis Laniele7ca3a32023-12-22 22:02:42 +01008080#endif /* __U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01008081
8082 setup_string_in_str(&input, s);
Francis Laniel26cafe12023-12-22 22:02:34 +01008083#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01008084 parse_and_run_stream(&input, '\0');
Francis Laniel26cafe12023-12-22 22:02:34 +01008085#else /* __U_BOOT__ */
8086 return parse_and_run_stream(&input, '\0');
8087#endif /* __U_BOOT__ */
Francis Laniele7ca3a32023-12-22 22:02:42 +01008088#ifndef __U_BOOT__
8089 IF_HUSH_LINENO_VAR(unsigned sv = G.parse_lineno;)
8090#else /* __U_BOOT__ */
8091 //IF_HUSH_LINENO_VAR(unsigned sv = G.parse_lineno;)
8092#endif /* __U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01008093}
8094
Francis Laniel26cafe12023-12-22 22:02:34 +01008095#ifdef __U_BOOT__
Francis Laniel5b64c452023-12-22 22:02:35 +01008096int parse_string_outer_modern(const char *cmd, int flags)
Francis Laniel26cafe12023-12-22 22:02:34 +01008097{
8098 int ret;
8099 int old_flags;
8100
8101 /*
8102 * Keep old values of run_command to be able to restore them once
8103 * command was executed.
8104 */
8105 old_flags = G.run_command_flags;
8106 G.run_command_flags = flags;
8107
8108 ret = parse_and_run_string(cmd);
8109
8110 G.run_command_flags = old_flags;
8111
8112 return ret;
8113}
8114#endif /* __U_BOOT__ */
Francis Laniel36836fc2023-12-22 22:02:28 +01008115#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01008116static void parse_and_run_file(HFILE *fp)
Francis Laniel36836fc2023-12-22 22:02:28 +01008117#else /* __U_BOOT__ */
8118void parse_and_run_file(void)
8119#endif /* __U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01008120{
8121 struct in_str input;
Francis Laniel36836fc2023-12-22 22:02:28 +01008122#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01008123 IF_HUSH_LINENO_VAR(unsigned sv = G.parse_lineno;)
8124
8125 IF_HUSH_LINENO_VAR(G.parse_lineno = 1;)
8126 setup_file_in_str(&input, fp);
Francis Laniel36836fc2023-12-22 22:02:28 +01008127#else /* __U_BOOT__ */
8128 setup_file_in_str(&input);
8129#endif /* __U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01008130 parse_and_run_stream(&input, ';');
Francis Laniel36836fc2023-12-22 22:02:28 +01008131#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01008132 IF_HUSH_LINENO_VAR(G.parse_lineno = sv;)
Francis Laniel36836fc2023-12-22 22:02:28 +01008133#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01008134}
8135
Francis Laniel36836fc2023-12-22 22:02:28 +01008136#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01008137#if ENABLE_HUSH_TICK
8138static int generate_stream_from_string(const char *s, pid_t *pid_p)
8139{
8140 pid_t pid;
8141 int channel[2];
8142# if !BB_MMU
8143 char **to_free = NULL;
8144# endif
8145
8146 xpipe(channel);
8147 pid = BB_MMU ? xfork() : xvfork();
8148 if (pid == 0) { /* child */
8149 disable_restore_tty_pgrp_on_exit();
8150 /* Process substitution is not considered to be usual
8151 * 'command execution'.
8152 * SUSv3 says ctrl-Z should be ignored, ctrl-C should not.
8153 */
8154 bb_signals(0
8155 + (1 << SIGTSTP)
8156 + (1 << SIGTTIN)
8157 + (1 << SIGTTOU)
8158 , SIG_IGN);
8159 close(channel[0]); /* NB: close _first_, then move fd! */
8160 xmove_fd(channel[1], 1);
8161# if ENABLE_HUSH_TRAP
8162 /* Awful hack for `trap` or $(trap).
8163 *
8164 * http://www.opengroup.org/onlinepubs/009695399/utilities/trap.html
8165 * contains an example where "trap" is executed in a subshell:
8166 *
8167 * save_traps=$(trap)
8168 * ...
8169 * eval "$save_traps"
8170 *
8171 * Standard does not say that "trap" in subshell shall print
8172 * parent shell's traps. It only says that its output
8173 * must have suitable form, but then, in the above example
8174 * (which is not supposed to be normative), it implies that.
8175 *
8176 * bash (and probably other shell) does implement it
8177 * (traps are reset to defaults, but "trap" still shows them),
8178 * but as a result, "trap" logic is hopelessly messed up:
8179 *
8180 * # trap
8181 * trap -- 'echo Ho' SIGWINCH <--- we have a handler
8182 * # (trap) <--- trap is in subshell - no output (correct, traps are reset)
8183 * # true | trap <--- trap is in subshell - no output (ditto)
8184 * # echo `true | trap` <--- in subshell - output (but traps are reset!)
8185 * trap -- 'echo Ho' SIGWINCH
8186 * # echo `(trap)` <--- in subshell in subshell - output
8187 * trap -- 'echo Ho' SIGWINCH
8188 * # echo `true | (trap)` <--- in subshell in subshell in subshell - output!
8189 * trap -- 'echo Ho' SIGWINCH
8190 *
8191 * The rules when to forget and when to not forget traps
8192 * get really complex and nonsensical.
8193 *
8194 * Our solution: ONLY bare $(trap) or `trap` is special.
8195 */
8196 s = skip_whitespace(s);
8197 if (is_prefixed_with(s, "trap")
8198 && skip_whitespace(s + 4)[0] == '\0'
8199 ) {
Francis Laniele7ca3a32023-12-22 22:02:42 +01008200 static const char *const argv[] ALIGN_PTR = { NULL, NULL };
Francis Laniel110b7692023-12-22 22:02:27 +01008201 builtin_trap((char**)argv);
8202 fflush_all(); /* important */
8203 _exit(0);
8204 }
8205# endif
8206# if BB_MMU
8207 /* Prevent it from trying to handle ctrl-z etc */
8208 IF_HUSH_JOB(G.run_list_level = 1;)
8209 CLEAR_RANDOM_T(&G.random_gen); /* or else $RANDOM repeats in child */
8210 reset_traps_to_defaults();
8211 IF_HUSH_MODE_X(G.x_mode_depth++;)
8212 //bb_error_msg("%s: ++x_mode_depth=%d", __func__, G.x_mode_depth);
8213 parse_and_run_string(s);
8214 _exit(G.last_exitcode);
8215# else
8216 /* We re-execute after vfork on NOMMU. This makes this script safe:
8217 * yes "0123456789012345678901234567890" | dd bs=32 count=64k >BIG
8218 * huge=`cat BIG` # was blocking here forever
8219 * echo OK
8220 */
8221 re_execute_shell(&to_free,
8222 s,
8223 G.global_argv[0],
8224 G.global_argv + 1,
8225 NULL);
8226# endif
8227 }
8228
8229 /* parent */
8230 *pid_p = pid;
8231# if ENABLE_HUSH_FAST
8232 G.count_SIGCHLD++;
8233//bb_error_msg("[%d] fork in generate_stream_from_string:"
8234// " G.count_SIGCHLD:%d G.handled_SIGCHLD:%d",
8235// getpid(), G.count_SIGCHLD, G.handled_SIGCHLD);
8236# endif
8237 enable_restore_tty_pgrp_on_exit();
8238# if !BB_MMU
8239 free(to_free);
8240# endif
8241 close(channel[1]);
8242 return channel[0];
8243}
8244
8245/* Return code is exit status of the process that is run. */
8246static int process_command_subs(o_string *dest, const char *s)
8247{
8248 FILE *fp;
8249 pid_t pid;
8250 int status, ch, eol_cnt;
8251
8252 fp = xfdopen_for_read(generate_stream_from_string(s, &pid));
8253
8254 /* Now send results of command back into original context */
8255 eol_cnt = 0;
8256 while ((ch = getc(fp)) != EOF) {
8257 if (ch == '\0')
8258 continue;
8259 if (ch == '\n') {
8260 eol_cnt++;
8261 continue;
8262 }
8263 while (eol_cnt) {
8264 o_addchr(dest, '\n');
8265 eol_cnt--;
8266 }
8267 o_addQchr(dest, ch);
8268 }
8269
8270 debug_printf("done reading from `cmd` pipe, closing it\n");
8271 fclose(fp);
8272 /* We need to extract exitcode. Test case
8273 * "true; echo `sleep 1; false` $?"
8274 * should print 1 */
8275 safe_waitpid(pid, &status, 0);
8276 debug_printf("child exited. returning its exitcode:%d\n", WEXITSTATUS(status));
8277 return WEXITSTATUS(status);
8278}
8279#endif /* ENABLE_HUSH_TICK */
8280
Francis Laniel110b7692023-12-22 22:02:27 +01008281static void setup_heredoc(struct redir_struct *redir)
8282{
8283 struct fd_pair pair;
8284 pid_t pid;
8285 int len, written;
8286 /* the _body_ of heredoc (misleading field name) */
8287 const char *heredoc = redir->rd_filename;
8288 char *expanded;
8289#if !BB_MMU
8290 char **to_free;
8291#endif
8292
8293 expanded = NULL;
8294 if (!(redir->rd_dup & HEREDOC_QUOTED)) {
8295 expanded = encode_then_expand_string(heredoc);
8296 if (expanded)
8297 heredoc = expanded;
8298 }
8299 len = strlen(heredoc);
8300
8301 close(redir->rd_fd); /* often saves dup2+close in xmove_fd */
8302 xpiped_pair(pair);
8303 xmove_fd(pair.rd, redir->rd_fd);
8304
8305 /* Try writing without forking. Newer kernels have
8306 * dynamically growing pipes. Must use non-blocking write! */
8307 ndelay_on(pair.wr);
8308 while (1) {
8309 written = write(pair.wr, heredoc, len);
8310 if (written <= 0)
8311 break;
8312 len -= written;
8313 if (len == 0) {
8314 close(pair.wr);
8315 free(expanded);
8316 return;
8317 }
8318 heredoc += written;
8319 }
8320 ndelay_off(pair.wr);
8321
8322 /* Okay, pipe buffer was not big enough */
8323 /* Note: we must not create a stray child (bastard? :)
8324 * for the unsuspecting parent process. Child creates a grandchild
8325 * and exits before parent execs the process which consumes heredoc
8326 * (that exec happens after we return from this function) */
8327#if !BB_MMU
8328 to_free = NULL;
8329#endif
8330 pid = xvfork();
8331 if (pid == 0) {
8332 /* child */
8333 disable_restore_tty_pgrp_on_exit();
8334 pid = BB_MMU ? xfork() : xvfork();
8335 if (pid != 0)
8336 _exit(0);
8337 /* grandchild */
8338 close(redir->rd_fd); /* read side of the pipe */
8339#if BB_MMU
8340 full_write(pair.wr, heredoc, len); /* may loop or block */
8341 _exit(0);
8342#else
8343 /* Delegate blocking writes to another process */
8344 xmove_fd(pair.wr, STDOUT_FILENO);
8345 re_execute_shell(&to_free, heredoc, NULL, NULL, NULL);
8346#endif
8347 }
8348 /* parent */
8349#if ENABLE_HUSH_FAST
8350 G.count_SIGCHLD++;
8351//bb_error_msg("[%d] fork in setup_heredoc: G.count_SIGCHLD:%d G.handled_SIGCHLD:%d", getpid(), G.count_SIGCHLD, G.handled_SIGCHLD);
8352#endif
8353 enable_restore_tty_pgrp_on_exit();
8354#if !BB_MMU
8355 free(to_free);
8356#endif
8357 close(pair.wr);
8358 free(expanded);
8359 wait(NULL); /* wait till child has died */
8360}
8361
8362struct squirrel {
8363 int orig_fd;
8364 int moved_to;
8365 /* moved_to = n: fd was moved to n; restore back to orig_fd after redir */
8366 /* moved_to = -1: fd was opened by redirect; close orig_fd after redir */
8367};
8368
8369static struct squirrel *append_squirrel(struct squirrel *sq, int i, int orig, int moved)
8370{
8371 sq = xrealloc(sq, (i + 2) * sizeof(sq[0]));
8372 sq[i].orig_fd = orig;
8373 sq[i].moved_to = moved;
8374 sq[i+1].orig_fd = -1; /* end marker */
8375 return sq;
8376}
8377
8378static struct squirrel *add_squirrel(struct squirrel *sq, int fd, int avoid_fd)
8379{
8380 int moved_to;
8381 int i;
8382
8383 i = 0;
8384 if (sq) for (; sq[i].orig_fd >= 0; i++) {
8385 /* If we collide with an already moved fd... */
8386 if (fd == sq[i].moved_to) {
8387 sq[i].moved_to = dup_CLOEXEC(sq[i].moved_to, avoid_fd);
8388 debug_printf_redir("redirect_fd %d: already busy, moving to %d\n", fd, sq[i].moved_to);
8389 if (sq[i].moved_to < 0) /* what? */
8390 xfunc_die();
8391 return sq;
8392 }
8393 if (fd == sq[i].orig_fd) {
8394 /* Example: echo Hello >/dev/null 1>&2 */
8395 debug_printf_redir("redirect_fd %d: already moved\n", fd);
8396 return sq;
8397 }
8398 }
8399
8400 /* If this fd is open, we move and remember it; if it's closed, moved_to = -1 */
8401 moved_to = dup_CLOEXEC(fd, avoid_fd);
8402 debug_printf_redir("redirect_fd %d: previous fd is moved to %d (-1 if it was closed)\n", fd, moved_to);
8403 if (moved_to < 0 && errno != EBADF)
8404 xfunc_die();
8405 return append_squirrel(sq, i, fd, moved_to);
8406}
8407
8408static struct squirrel *add_squirrel_closed(struct squirrel *sq, int fd)
8409{
8410 int i;
8411
8412 i = 0;
8413 if (sq) for (; sq[i].orig_fd >= 0; i++) {
8414 /* If we collide with an already moved fd... */
8415 if (fd == sq[i].orig_fd) {
8416 /* Examples:
8417 * "echo 3>FILE 3>&- 3>FILE"
8418 * "echo 3>&- 3>FILE"
8419 * No need for last redirect to insert
8420 * another "need to close 3" indicator.
8421 */
8422 debug_printf_redir("redirect_fd %d: already moved or closed\n", fd);
8423 return sq;
8424 }
8425 }
8426
8427 debug_printf_redir("redirect_fd %d: previous fd was closed\n", fd);
8428 return append_squirrel(sq, i, fd, -1);
8429}
8430
8431/* fd: redirect wants this fd to be used (e.g. 3>file).
8432 * Move all conflicting internally used fds,
8433 * and remember them so that we can restore them later.
8434 */
8435static int save_fd_on_redirect(int fd, int avoid_fd, struct squirrel **sqp)
8436{
8437 if (avoid_fd < 9) /* the important case here is that it can be -1 */
8438 avoid_fd = 9;
8439
8440#if ENABLE_HUSH_INTERACTIVE
8441 if (fd != 0 /* don't trigger for G_interactive_fd == 0 (that's "not interactive" flag) */
8442 && fd == G_interactive_fd
8443 ) {
8444 /* Testcase: "ls -l /proc/$$/fd 255>&-" should work */
8445 G_interactive_fd = xdup_CLOEXEC_and_close(G_interactive_fd, avoid_fd);
8446 debug_printf_redir("redirect_fd %d: matches interactive_fd, moving it to %d\n", fd, G_interactive_fd);
8447 return 1; /* "we closed fd" */
8448 }
8449#endif
8450 /* Are we called from setup_redirects(squirrel==NULL)
8451 * in redirect in a [v]forked child?
8452 */
8453 if (sqp == NULL) {
8454 /* No need to move script fds.
8455 * For NOMMU case, it's actively wrong: we'd change ->fd
8456 * fields in memory for the parent, but parent's fds
8457 * aren't moved, it would use wrong fd!
8458 * Reproducer: "cmd 3>FILE" in script.
8459 * If we would call move_HFILEs_on_redirect(), child would:
8460 * fcntl64(3, F_DUPFD_CLOEXEC, 10) = 10
8461 * close(3) = 0
8462 * and change ->fd to 10 if fd#3 is a script fd. WRONG.
8463 */
8464 //bb_error_msg("sqp == NULL: [v]forked child");
8465 return 0;
8466 }
8467
8468 /* If this one of script's fds? */
8469 if (move_HFILEs_on_redirect(fd, avoid_fd))
8470 return 1; /* yes. "we closed fd" (actually moved it) */
8471
8472 /* Are we called for "exec 3>FILE"? Came through
8473 * redirect_and_varexp_helper(squirrel=ERR_PTR) -> setup_redirects(ERR_PTR)
8474 * This case used to fail for this script:
8475 * exec 3>FILE
8476 * echo Ok
8477 * ...100000 more lines...
8478 * echo Ok
8479 * as follows:
8480 * read(3, "exec 3>FILE\necho Ok\necho Ok"..., 1024) = 1024
8481 * open("FILE", O_WRONLY|O_CREAT|O_TRUNC|O_LARGEFILE, 0666) = 4
8482 * dup2(4, 3) = 3
8483 * ^^^^^^^^ oops, we lost fd#3 opened to our script!
8484 * close(4) = 0
8485 * write(1, "Ok\n", 3) = 3
8486 * ... = 3
8487 * write(1, "Ok\n", 3) = 3
8488 * read(3, 0x94fbc08, 1024) = -1 EBADF (Bad file descriptor)
8489 * ^^^^^^^^ oops, wrong fd!!!
8490 * With this case separate from sqp == NULL and *after* move_HFILEs,
8491 * it now works:
8492 */
8493 if (sqp == ERR_PTR) {
8494 /* Don't preserve redirected fds: exec is _meant_ to change these */
8495 //bb_error_msg("sqp == ERR_PTR: exec >FILE");
8496 return 0;
8497 }
8498
8499 /* Check whether it collides with any open fds (e.g. stdio), save fds as needed */
8500 *sqp = add_squirrel(*sqp, fd, avoid_fd);
8501 return 0; /* "we did not close fd" */
8502}
8503
8504static void restore_redirects(struct squirrel *sq)
8505{
8506 if (sq) {
8507 int i;
8508 for (i = 0; sq[i].orig_fd >= 0; i++) {
8509 if (sq[i].moved_to >= 0) {
8510 /* We simply die on error */
8511 debug_printf_redir("restoring redirected fd from %d to %d\n", sq[i].moved_to, sq[i].orig_fd);
8512 xmove_fd(sq[i].moved_to, sq[i].orig_fd);
8513 } else {
8514 /* cmd1 9>FILE; cmd2_should_see_fd9_closed */
8515 debug_printf_redir("restoring redirected fd %d: closing it\n", sq[i].orig_fd);
8516 close(sq[i].orig_fd);
8517 }
8518 }
8519 free(sq);
8520 }
8521 if (G.HFILE_stdin
8522 && G.HFILE_stdin->fd > STDIN_FILENO
8523 /* we compare > STDIN, not == STDIN, since hfgetc()
8524 * closes fd and sets ->fd to -1 if EOF is reached.
8525 * Testcase: echo 'pwd' | hush
8526 */
8527 ) {
8528 /* Testcase: interactive "read r <FILE; echo $r; read r; echo $r".
8529 * Redirect moves ->fd to e.g. 10,
8530 * and it is not restored above (we do not restore script fds
8531 * after redirects, we just use new, "moved" fds).
8532 * However for stdin, get_user_input() -> read_line_input(),
8533 * and read builtin, depend on fd == STDIN_FILENO.
8534 */
8535 debug_printf_redir("restoring %d to stdin\n", G.HFILE_stdin->fd);
8536 xmove_fd(G.HFILE_stdin->fd, STDIN_FILENO);
8537 G.HFILE_stdin->fd = STDIN_FILENO;
8538 }
8539
8540 /* If moved, G_interactive_fd stays on new fd, not restoring it */
8541}
8542
8543#if ENABLE_FEATURE_SH_STANDALONE && BB_MMU
8544static void close_saved_fds_and_FILE_fds(void)
8545{
8546 if (G_interactive_fd)
8547 close(G_interactive_fd);
8548 close_all_HFILE_list();
8549}
8550#endif
8551
8552static int internally_opened_fd(int fd, struct squirrel *sq)
8553{
8554 int i;
8555
8556#if ENABLE_HUSH_INTERACTIVE
8557 if (fd == G_interactive_fd)
8558 return 1;
8559#endif
8560 /* If this one of script's fds? */
8561 if (fd_in_HFILEs(fd))
8562 return 1;
8563
8564 if (sq) for (i = 0; sq[i].orig_fd >= 0; i++) {
8565 if (fd == sq[i].moved_to)
8566 return 1;
8567 }
8568 return 0;
8569}
8570
8571/* squirrel != NULL means we squirrel away copies of stdin, stdout,
8572 * and stderr if they are redirected. */
8573static int setup_redirects(struct command *prog, struct squirrel **sqp)
8574{
8575 struct redir_struct *redir;
8576
8577 for (redir = prog->redirects; redir; redir = redir->next) {
8578 int newfd;
8579 int closed;
8580
8581 if (redir->rd_type == REDIRECT_HEREDOC2) {
8582 /* "rd_fd<<HERE" case */
8583 save_fd_on_redirect(redir->rd_fd, /*avoid:*/ 0, sqp);
8584 /* for REDIRECT_HEREDOC2, rd_filename holds _contents_
8585 * of the heredoc */
8586 debug_printf_redir("set heredoc '%s'\n",
8587 redir->rd_filename);
8588 setup_heredoc(redir);
8589 continue;
8590 }
8591
8592 if (redir->rd_dup == REDIRFD_TO_FILE) {
8593 /* "rd_fd<*>file" case (<*> is <,>,>>,<>) */
8594 char *p;
8595 int mode;
8596
8597 if (redir->rd_filename == NULL) {
8598 /* Examples:
8599 * "cmd >" (no filename)
8600 * "cmd > <file" (2nd redirect starts too early)
8601 */
8602 syntax_error("invalid redirect");
8603 continue;
8604 }
8605 mode = redir_table[redir->rd_type].mode;
8606 p = expand_string_to_string(redir->rd_filename,
8607 EXP_FLAG_ESC_GLOB_CHARS, /*unbackslash:*/ 1);
8608 newfd = open_or_warn(p, mode);
8609 free(p);
8610 if (newfd < 0) {
8611 /* Error message from open_or_warn can be lost
8612 * if stderr has been redirected, but bash
8613 * and ash both lose it as well
8614 * (though zsh doesn't!)
8615 */
8616 return 1;
8617 }
8618 if (newfd == redir->rd_fd && sqp) {
8619 /* open() gave us precisely the fd we wanted.
8620 * This means that this fd was not busy
8621 * (not opened to anywhere).
8622 * Remember to close it on restore:
8623 */
8624 *sqp = add_squirrel_closed(*sqp, newfd);
8625 debug_printf_redir("redir to previously closed fd %d\n", newfd);
8626 }
8627 } else {
8628 /* "rd_fd>&rd_dup" or "rd_fd>&-" case */
8629 newfd = redir->rd_dup;
8630 }
8631
8632 if (newfd == redir->rd_fd)
8633 continue;
8634
8635 /* if "N>FILE": move newfd to redir->rd_fd */
8636 /* if "N>&M": dup newfd to redir->rd_fd */
8637 /* if "N>&-": close redir->rd_fd (newfd is REDIRFD_CLOSE) */
8638
8639 closed = save_fd_on_redirect(redir->rd_fd, /*avoid:*/ newfd, sqp);
8640 if (newfd == REDIRFD_CLOSE) {
8641 /* "N>&-" means "close me" */
8642 if (!closed) {
8643 /* ^^^ optimization: saving may already
8644 * have closed it. If not... */
8645 close(redir->rd_fd);
8646 }
8647 /* Sometimes we do another close on restore, getting EBADF.
8648 * Consider "echo 3>FILE 3>&-"
8649 * first redirect remembers "need to close 3",
8650 * and second redirect closes 3! Restore code then closes 3 again.
8651 */
8652 } else {
8653 /* if newfd is a script fd or saved fd, simulate EBADF */
8654 if (internally_opened_fd(newfd, sqp && sqp != ERR_PTR ? *sqp : NULL)) {
8655 //errno = EBADF;
8656 //bb_perror_msg_and_die("can't duplicate file descriptor");
8657 newfd = -1; /* same effect as code above */
8658 }
8659 xdup2(newfd, redir->rd_fd);
8660 if (redir->rd_dup == REDIRFD_TO_FILE)
8661 /* "rd_fd > FILE" */
8662 close(newfd);
8663 /* else: "rd_fd > rd_dup" */
8664 }
8665 }
8666 return 0;
8667}
8668
8669static char *find_in_path(const char *arg)
8670{
8671 char *ret = NULL;
8672 const char *PATH = get_local_var_value("PATH");
8673
8674 if (!PATH)
8675 return NULL;
8676
8677 while (1) {
8678 const char *end = strchrnul(PATH, ':');
8679 int sz = end - PATH; /* must be int! */
8680
8681 free(ret);
8682 if (sz != 0) {
8683 ret = xasprintf("%.*s/%s", sz, PATH, arg);
8684 } else {
8685 /* We have xxx::yyyy in $PATH,
8686 * it means "use current dir" */
8687 ret = xstrdup(arg);
8688 }
8689 if (access(ret, F_OK) == 0)
8690 break;
8691
8692 if (*end == '\0') {
8693 free(ret);
8694 return NULL;
8695 }
8696 PATH = end + 1;
8697 }
8698
8699 return ret;
8700}
8701
8702static const struct built_in_command *find_builtin_helper(const char *name,
8703 const struct built_in_command *x,
8704 const struct built_in_command *end)
8705{
8706 while (x != end) {
8707 if (strcmp(name, x->b_cmd) != 0) {
8708 x++;
8709 continue;
8710 }
8711 debug_printf_exec("found builtin '%s'\n", name);
8712 return x;
8713 }
8714 return NULL;
8715}
8716static const struct built_in_command *find_builtin1(const char *name)
8717{
8718 return find_builtin_helper(name, bltins1, &bltins1[ARRAY_SIZE(bltins1)]);
8719}
8720static const struct built_in_command *find_builtin(const char *name)
8721{
8722 const struct built_in_command *x = find_builtin1(name);
8723 if (x)
8724 return x;
8725 return find_builtin_helper(name, bltins2, &bltins2[ARRAY_SIZE(bltins2)]);
8726}
8727
Francis Laniele7ca3a32023-12-22 22:02:42 +01008728#if ENABLE_HUSH_JOB && ENABLE_FEATURE_TAB_COMPLETION
8729static const char * FAST_FUNC hush_command_name(int i)
Francis Laniel110b7692023-12-22 22:02:27 +01008730{
8731 if (/*i >= 0 && */ i < ARRAY_SIZE(bltins1)) {
8732 return bltins1[i].b_cmd;
8733 }
8734 i -= ARRAY_SIZE(bltins1);
8735 if (i < ARRAY_SIZE(bltins2)) {
8736 return bltins2[i].b_cmd;
8737 }
Francis Laniele7ca3a32023-12-22 22:02:42 +01008738# if ENABLE_HUSH_FUNCTIONS
8739 {
8740 struct function *funcp;
8741 i -= ARRAY_SIZE(bltins2);
8742 for (funcp = G.top_func; funcp; funcp = funcp->next) {
8743 if (--i < 0)
8744 return funcp->name;
8745 }
8746 }
8747# endif
Francis Laniel110b7692023-12-22 22:02:27 +01008748 return NULL;
8749}
8750#endif
Francis Laniel36836fc2023-12-22 22:02:28 +01008751#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01008752
Francis Laniel36836fc2023-12-22 22:02:28 +01008753#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01008754static void remove_nested_vars(void)
8755{
8756 struct variable *cur;
8757 struct variable **cur_pp;
8758
8759 cur_pp = &G.top_var;
8760 while ((cur = *cur_pp) != NULL) {
8761 if (cur->var_nest_level <= G.var_nest_level) {
8762 cur_pp = &cur->next;
8763 continue;
8764 }
8765 /* Unexport */
8766 if (cur->flg_export) {
8767 debug_printf_env("unexporting nested '%s'/%u\n", cur->varstr, cur->var_nest_level);
8768 bb_unsetenv(cur->varstr);
8769 }
8770 /* Remove from global list */
8771 *cur_pp = cur->next;
8772 /* Free */
8773 if (!cur->max_len) {
8774 debug_printf_env("freeing nested '%s'/%u\n", cur->varstr, cur->var_nest_level);
8775 free(cur->varstr);
8776 }
8777 free(cur);
8778 }
8779}
8780
8781static void enter_var_nest_level(void)
8782{
8783 G.var_nest_level++;
8784 debug_printf_env("var_nest_level++ %u\n", G.var_nest_level);
8785
8786 /* Try: f() { echo -n .; f; }; f
8787 * struct variable::var_nest_level is uint16_t,
8788 * thus limiting recursion to < 2^16.
8789 * In any case, with 8 Mbyte stack SEGV happens
8790 * not too long after 2^16 recursions anyway.
8791 */
8792 if (G.var_nest_level > 0xff00)
8793 bb_error_msg_and_die("fatal recursion (depth %u)", G.var_nest_level);
8794}
8795
8796static void leave_var_nest_level(void)
8797{
8798 G.var_nest_level--;
8799 debug_printf_env("var_nest_level-- %u\n", G.var_nest_level);
8800 if (HUSH_DEBUG && (int)G.var_nest_level < 0)
8801 bb_simple_error_msg_and_die("BUG: nesting underflow");
8802
8803 remove_nested_vars();
8804}
Francis Laniel36836fc2023-12-22 22:02:28 +01008805#endif /* __U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01008806
8807#if ENABLE_HUSH_FUNCTIONS
8808static struct function **find_function_slot(const char *name)
8809{
8810 struct function *funcp;
8811 struct function **funcpp = &G.top_func;
8812
8813 while ((funcp = *funcpp) != NULL) {
8814 if (strcmp(name, funcp->name) == 0) {
8815 debug_printf_exec("found function '%s'\n", name);
8816 break;
8817 }
8818 funcpp = &funcp->next;
8819 }
8820 return funcpp;
8821}
8822
8823static ALWAYS_INLINE const struct function *find_function(const char *name)
8824{
8825 const struct function *funcp = *find_function_slot(name);
8826 return funcp;
8827}
8828
8829/* Note: takes ownership on name ptr */
8830static struct function *new_function(char *name)
8831{
8832 struct function **funcpp = find_function_slot(name);
8833 struct function *funcp = *funcpp;
8834
8835 if (funcp != NULL) {
8836 struct command *cmd = funcp->parent_cmd;
8837 debug_printf_exec("func %p parent_cmd %p\n", funcp, cmd);
8838 if (!cmd) {
8839 debug_printf_exec("freeing & replacing function '%s'\n", funcp->name);
8840 free(funcp->name);
8841 /* Note: if !funcp->body, do not free body_as_string!
8842 * This is a special case of "-F name body" function:
8843 * body_as_string was not malloced! */
8844 if (funcp->body) {
8845 free_pipe_list(funcp->body);
8846# if !BB_MMU
8847 free(funcp->body_as_string);
8848# endif
8849 }
8850 } else {
8851 debug_printf_exec("reinserting in tree & replacing function '%s'\n", funcp->name);
8852 cmd->argv[0] = funcp->name;
8853 cmd->group = funcp->body;
8854# if !BB_MMU
8855 cmd->group_as_string = funcp->body_as_string;
8856# endif
8857 }
8858 } else {
8859 debug_printf_exec("remembering new function '%s'\n", name);
8860 funcp = *funcpp = xzalloc(sizeof(*funcp));
8861 /*funcp->next = NULL;*/
8862 }
8863
8864 funcp->name = name;
8865 return funcp;
8866}
8867
8868# if ENABLE_HUSH_UNSET
8869static void unset_func(const char *name)
8870{
8871 struct function **funcpp = find_function_slot(name);
8872 struct function *funcp = *funcpp;
8873
8874 if (funcp != NULL) {
8875 debug_printf_exec("freeing function '%s'\n", funcp->name);
8876 *funcpp = funcp->next;
8877 /* funcp is unlinked now, deleting it.
8878 * Note: if !funcp->body, the function was created by
8879 * "-F name body", do not free ->body_as_string
8880 * and ->name as they were not malloced. */
8881 if (funcp->body) {
8882 free_pipe_list(funcp->body);
8883 free(funcp->name);
8884# if !BB_MMU
8885 free(funcp->body_as_string);
8886# endif
8887 }
8888 free(funcp);
8889 }
8890}
8891# endif
8892
8893# if BB_MMU
8894#define exec_function(to_free, funcp, argv) \
8895 exec_function(funcp, argv)
8896# endif
8897static void exec_function(char ***to_free,
8898 const struct function *funcp,
8899 char **argv) NORETURN;
8900static void exec_function(char ***to_free,
8901 const struct function *funcp,
8902 char **argv)
8903{
8904# if BB_MMU
8905 int n;
8906
8907 argv[0] = G.global_argv[0];
8908 G.global_argv = argv;
8909 G.global_argc = n = 1 + string_array_len(argv + 1);
8910
8911// Example when we are here: "cmd | func"
8912// func will run with saved-redirect fds open.
8913// $ f() { echo /proc/self/fd/*; }
8914// $ true | f
8915// /proc/self/fd/0 /proc/self/fd/1 /proc/self/fd/2 /proc/self/fd/255 /proc/self/fd/3
8916// stdio^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ G_interactive_fd^ DIR fd for glob
8917// Same in script:
8918// $ . ./SCRIPT
8919// /proc/self/fd/0 /proc/self/fd/1 /proc/self/fd/2 /proc/self/fd/255 /proc/self/fd/3 /proc/self/fd/4
8920// stdio^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ G_interactive_fd^ opened ./SCRIPT DIR fd for glob
8921// They are CLOEXEC so external programs won't see them, but
8922// for "more correctness" we might want to close those extra fds here:
8923//? close_saved_fds_and_FILE_fds();
8924
8925 /* "we are in a function, ok to use return" */
8926 G_flag_return_in_progress = -1;
8927 enter_var_nest_level();
8928 IF_HUSH_LOCAL(G.func_nest_level++;)
8929
8930 /* On MMU, funcp->body is always non-NULL */
8931 n = run_list(funcp->body);
8932 _exit(n);
8933# else
8934//? close_saved_fds_and_FILE_fds();
8935
8936//TODO: check whether "true | func_with_return" works
8937
8938 re_execute_shell(to_free,
8939 funcp->body_as_string,
8940 G.global_argv[0],
8941 argv + 1,
8942 NULL);
8943# endif
8944}
8945
8946static int run_function(const struct function *funcp, char **argv)
8947{
8948 int rc;
8949 save_arg_t sv;
8950 smallint sv_flg;
8951
8952 save_and_replace_G_args(&sv, argv);
8953
8954 /* "We are in function, ok to use return" */
8955 sv_flg = G_flag_return_in_progress;
8956 G_flag_return_in_progress = -1;
8957
8958 /* Make "local" variables properly shadow previous ones */
8959 IF_HUSH_LOCAL(enter_var_nest_level();)
8960 IF_HUSH_LOCAL(G.func_nest_level++;)
8961
8962 /* On MMU, funcp->body is always non-NULL */
8963# if !BB_MMU
8964 if (!funcp->body) {
8965 /* Function defined by -F */
8966 parse_and_run_string(funcp->body_as_string);
8967 rc = G.last_exitcode;
8968 } else
8969# endif
8970 {
8971 rc = run_list(funcp->body);
8972 }
8973
8974 IF_HUSH_LOCAL(G.func_nest_level--;)
8975 IF_HUSH_LOCAL(leave_var_nest_level();)
8976
8977 G_flag_return_in_progress = sv_flg;
8978# if ENABLE_HUSH_TRAP
8979 debug_printf_exec("G.return_exitcode=-1\n");
8980 G.return_exitcode = -1; /* invalidate stashed return value */
8981# endif
8982
8983 restore_G_args(&sv, argv);
8984
8985 return rc;
8986}
8987#endif /* ENABLE_HUSH_FUNCTIONS */
8988
Francis Laniel36836fc2023-12-22 22:02:28 +01008989#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01008990#if BB_MMU
8991#define exec_builtin(to_free, x, argv) \
8992 exec_builtin(x, argv)
8993#else
8994#define exec_builtin(to_free, x, argv) \
8995 exec_builtin(to_free, argv)
8996#endif
8997static void exec_builtin(char ***to_free,
8998 const struct built_in_command *x,
8999 char **argv) NORETURN;
9000static void exec_builtin(char ***to_free,
9001 const struct built_in_command *x,
9002 char **argv)
9003{
9004#if BB_MMU
9005 int rcode;
9006//? close_saved_fds_and_FILE_fds();
9007 rcode = x->b_function(argv);
9008 fflush_all();
9009 _exit(rcode);
9010#else
9011 fflush_all();
9012 /* On NOMMU, we must never block!
9013 * Example: { sleep 99 | read line; } & echo Ok
9014 */
9015 re_execute_shell(to_free,
9016 argv[0],
9017 G.global_argv[0],
9018 G.global_argv + 1,
9019 argv);
9020#endif
9021}
Francis Laniel36836fc2023-12-22 22:02:28 +01009022#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01009023
Francis Laniel36836fc2023-12-22 22:02:28 +01009024#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01009025static void execvp_or_die(char **argv) NORETURN;
9026static void execvp_or_die(char **argv)
9027{
9028 int e;
9029 debug_printf_exec("execing '%s'\n", argv[0]);
9030 /* Don't propagate SIG_IGN to the child */
9031 if (SPECIAL_JOBSTOP_SIGS != 0)
9032 switch_off_special_sigs(G.special_sig_mask & SPECIAL_JOBSTOP_SIGS);
9033 execvp(argv[0], argv);
9034 e = 2;
9035 if (errno == EACCES) e = 126;
9036 if (errno == ENOENT) e = 127;
9037 bb_perror_msg("can't execute '%s'", argv[0]);
9038 _exit(e);
9039}
9040
9041#if ENABLE_HUSH_MODE_X
9042static void x_mode_print_optionally_squoted(const char *str)
9043{
9044 unsigned len;
9045 const char *cp;
9046
9047 cp = str;
9048
9049 /* the set of chars which-cause-string-to-be-squoted mimics bash */
9050 /* test a char with: bash -c 'set -x; echo "CH"' */
9051 if (str[strcspn(str, "\\\"'`$(){}[]<>;#&|~*?!^"
9052 " " "\001\002\003\004\005\006\007"
9053 "\010\011\012\013\014\015\016\017"
9054 "\020\021\022\023\024\025\026\027"
9055 "\030\031\032\033\034\035\036\037"
9056 )
9057 ] == '\0'
9058 ) {
9059 /* string has no special chars */
9060 x_mode_addstr(str);
9061 return;
9062 }
9063
9064 cp = str;
9065 for (;;) {
9066 /* print '....' up to EOL or first squote */
9067 len = (int)(strchrnul(cp, '\'') - cp);
9068 if (len != 0) {
9069 x_mode_addchr('\'');
9070 x_mode_addblock(cp, len);
9071 x_mode_addchr('\'');
9072 cp += len;
9073 }
9074 if (*cp == '\0')
9075 break;
9076 /* string contains squote(s), print them as \' */
9077 x_mode_addchr('\\');
9078 x_mode_addchr('\'');
9079 cp++;
9080 }
9081}
9082static void dump_cmd_in_x_mode(char **argv)
9083{
9084 if (G_x_mode && argv) {
9085 unsigned n;
9086
9087 /* "+[+++...][ cmd...]\n\0" */
9088 x_mode_prefix();
9089 n = 0;
9090 while (argv[n]) {
9091 x_mode_addchr(' ');
9092 if (argv[n][0] == '\0') {
9093 x_mode_addchr('\'');
9094 x_mode_addchr('\'');
9095 } else {
9096 x_mode_print_optionally_squoted(argv[n]);
9097 }
9098 n++;
9099 }
9100 x_mode_flush();
9101 }
9102}
9103#else
9104# define dump_cmd_in_x_mode(argv) ((void)0)
9105#endif
Francis Laniel36836fc2023-12-22 22:02:28 +01009106#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01009107
Francis Laniel36836fc2023-12-22 22:02:28 +01009108#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01009109#if ENABLE_HUSH_COMMAND
9110static void if_command_vV_print_and_exit(char opt_vV, char *cmd, const char *explanation)
9111{
9112 char *to_free;
9113
9114 if (!opt_vV)
9115 return;
9116
9117 to_free = NULL;
9118 if (!explanation) {
9119 char *path = getenv("PATH");
9120 explanation = to_free = find_executable(cmd, &path); /* path == NULL is ok */
9121 if (!explanation)
9122 _exit(1); /* PROG was not found */
9123 if (opt_vV != 'V')
9124 cmd = to_free; /* -v PROG prints "/path/to/PROG" */
9125 }
9126 printf((opt_vV == 'V') ? "%s is %s\n" : "%s\n", cmd, explanation);
9127 free(to_free);
9128 fflush_all();
9129 _exit(0);
9130}
9131#else
9132# define if_command_vV_print_and_exit(a,b,c) ((void)0)
9133#endif
Francis Laniel36836fc2023-12-22 22:02:28 +01009134#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01009135
9136#if BB_MMU
9137#define pseudo_exec_argv(nommu_save, argv, assignment_cnt, argv_expanded) \
9138 pseudo_exec_argv(argv, assignment_cnt, argv_expanded)
9139#define pseudo_exec(nommu_save, command, argv_expanded) \
9140 pseudo_exec(command, argv_expanded)
9141#endif
9142
Francis Laniel36836fc2023-12-22 22:02:28 +01009143#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01009144/* Called after [v]fork() in run_pipe, or from builtin_exec.
9145 * Never returns.
9146 * Don't exit() here. If you don't exec, use _exit instead.
9147 * The at_exit handlers apparently confuse the calling process,
9148 * in particular stdin handling. Not sure why? -- because of vfork! (vda)
9149 */
9150static void pseudo_exec_argv(nommu_save_t *nommu_save,
9151 char **argv, int assignment_cnt,
9152 char **argv_expanded) NORETURN;
9153static NOINLINE void pseudo_exec_argv(nommu_save_t *nommu_save,
9154 char **argv, int assignment_cnt,
9155 char **argv_expanded)
9156{
9157 const struct built_in_command *x;
9158 struct variable **sv_shadowed;
9159 char **new_env;
9160 IF_HUSH_COMMAND(char opt_vV = 0;)
9161 IF_HUSH_FUNCTIONS(const struct function *funcp;)
9162
9163 new_env = expand_assignments(argv, assignment_cnt);
9164 dump_cmd_in_x_mode(new_env);
9165
9166 if (!argv[assignment_cnt]) {
9167 /* Case when we are here: ... | var=val | ...
9168 * (note that we do not exit early, i.e., do not optimize out
9169 * expand_assignments(): think about ... | var=`sleep 1` | ...
9170 */
9171 free_strings(new_env);
Francis Laniele7ca3a32023-12-22 22:02:42 +01009172 _exit_SUCCESS();
Francis Laniel110b7692023-12-22 22:02:27 +01009173 }
9174
9175 sv_shadowed = G.shadowed_vars_pp;
9176#if BB_MMU
9177 G.shadowed_vars_pp = NULL; /* "don't save, free them instead" */
9178#else
9179 G.shadowed_vars_pp = &nommu_save->old_vars;
9180 G.var_nest_level++;
9181#endif
9182 set_vars_and_save_old(new_env);
9183 G.shadowed_vars_pp = sv_shadowed;
9184
9185 if (argv_expanded) {
9186 argv = argv_expanded;
9187 } else {
9188 argv = expand_strvec_to_strvec(argv + assignment_cnt);
9189#if !BB_MMU
9190 nommu_save->argv = argv;
9191#endif
9192 }
9193 dump_cmd_in_x_mode(argv);
9194
9195#if ENABLE_FEATURE_SH_STANDALONE || BB_MMU
9196 if (strchr(argv[0], '/') != NULL)
9197 goto skip;
9198#endif
9199
9200#if ENABLE_HUSH_FUNCTIONS
9201 /* Check if the command matches any functions (this goes before bltins) */
9202 funcp = find_function(argv[0]);
9203 if (funcp)
9204 exec_function(&nommu_save->argv_from_re_execing, funcp, argv);
9205#endif
9206
9207#if ENABLE_HUSH_COMMAND
9208 /* "command BAR": run BAR without looking it up among functions
9209 * "command -v BAR": print "BAR" or "/path/to/BAR"; or exit 1
9210 * "command -V BAR": print "BAR is {a function,a shell builtin,/path/to/BAR}"
9211 */
9212 while (strcmp(argv[0], "command") == 0 && argv[1]) {
9213 char *p;
9214
9215 argv++;
9216 p = *argv;
9217 if (p[0] != '-' || !p[1])
9218 continue; /* bash allows "command command command [-OPT] BAR" */
9219
9220 for (;;) {
9221 p++;
9222 switch (*p) {
9223 case '\0':
9224 argv++;
9225 p = *argv;
9226 if (p[0] != '-' || !p[1])
9227 goto after_opts;
9228 continue; /* next arg is also -opts, process it too */
9229 case 'v':
9230 case 'V':
9231 opt_vV = *p;
9232 continue;
9233 default:
9234 bb_error_msg_and_die("%s: %s: invalid option", "command", argv[0]);
9235 }
9236 }
9237 }
9238 after_opts:
9239# if ENABLE_HUSH_FUNCTIONS
9240 if (opt_vV && find_function(argv[0]))
9241 if_command_vV_print_and_exit(opt_vV, argv[0], "a function");
9242# endif
9243#endif
9244
9245 /* Check if the command matches any of the builtins.
9246 * Depending on context, this might be redundant. But it's
9247 * easier to waste a few CPU cycles than it is to figure out
9248 * if this is one of those cases.
9249 */
9250 /* Why "BB_MMU ? :" difference in logic? -
9251 * On NOMMU, it is more expensive to re-execute shell
9252 * just in order to run echo or test builtin.
9253 * It's better to skip it here and run corresponding
9254 * non-builtin later. */
9255 x = BB_MMU ? find_builtin(argv[0]) : find_builtin1(argv[0]);
9256 if (x) {
9257 if_command_vV_print_and_exit(opt_vV, argv[0], "a shell builtin");
9258 exec_builtin(&nommu_save->argv_from_re_execing, x, argv);
9259 }
9260
9261#if ENABLE_FEATURE_SH_STANDALONE
9262 /* Check if the command matches any busybox applets */
9263 {
9264 int a = find_applet_by_name(argv[0]);
9265 if (a >= 0) {
9266 if_command_vV_print_and_exit(opt_vV, argv[0], "an applet");
9267# if BB_MMU /* see above why on NOMMU it is not allowed */
9268 if (APPLET_IS_NOEXEC(a)) {
9269 /* Do not leak open fds from opened script files etc.
9270 * Testcase: interactive "ls -l /proc/self/fd"
9271 * should not show tty fd open.
9272 */
9273 close_saved_fds_and_FILE_fds();
9274//FIXME: should also close saved redir fds
9275//This casuses test failures in
9276//redir_children_should_not_see_saved_fd_2.tests
9277//redir_children_should_not_see_saved_fd_3.tests
9278//if you replace "busybox find" with just "find" in them
9279 /* Without this, "rm -i FILE" can't be ^C'ed: */
9280 switch_off_special_sigs(G.special_sig_mask);
9281 debug_printf_exec("running applet '%s'\n", argv[0]);
9282 run_noexec_applet_and_exit(a, argv[0], argv);
9283 }
9284# endif
9285 /* Re-exec ourselves */
9286 debug_printf_exec("re-execing applet '%s'\n", argv[0]);
9287 /* Don't propagate SIG_IGN to the child */
9288 if (SPECIAL_JOBSTOP_SIGS != 0)
9289 switch_off_special_sigs(G.special_sig_mask & SPECIAL_JOBSTOP_SIGS);
9290 execv(bb_busybox_exec_path, argv);
9291 /* If they called chroot or otherwise made the binary no longer
9292 * executable, fall through */
9293 }
9294 }
9295#endif
9296
9297#if ENABLE_FEATURE_SH_STANDALONE || BB_MMU
9298 skip:
9299#endif
9300 if_command_vV_print_and_exit(opt_vV, argv[0], NULL);
9301 execvp_or_die(argv);
9302}
9303
9304/* Called after [v]fork() in run_pipe
9305 */
9306static void pseudo_exec(nommu_save_t *nommu_save,
9307 struct command *command,
9308 char **argv_expanded) NORETURN;
9309static void pseudo_exec(nommu_save_t *nommu_save,
9310 struct command *command,
9311 char **argv_expanded)
9312{
9313#if ENABLE_HUSH_FUNCTIONS
9314 if (command->cmd_type == CMD_FUNCDEF) {
9315 /* Ignore funcdefs in pipes:
9316 * true | f() { cmd }
9317 */
9318 _exit(0);
9319 }
9320#endif
9321
9322 if (command->argv) {
9323 pseudo_exec_argv(nommu_save, command->argv,
9324 command->assignment_cnt, argv_expanded);
9325 }
9326
9327 if (command->group) {
9328 /* Cases when we are here:
9329 * ( list )
9330 * { list } &
9331 * ... | ( list ) | ...
9332 * ... | { list } | ...
9333 */
9334#if BB_MMU
9335 int rcode;
9336 debug_printf_exec("pseudo_exec: run_list\n");
9337 reset_traps_to_defaults();
9338 rcode = run_list(command->group);
9339 /* OK to leak memory by not calling free_pipe_list,
9340 * since this process is about to exit */
9341 _exit(rcode);
9342#else
9343 re_execute_shell(&nommu_save->argv_from_re_execing,
9344 command->group_as_string,
9345 G.global_argv[0],
9346 G.global_argv + 1,
9347 NULL);
9348#endif
9349 }
9350
9351 /* Case when we are here: ... | >file */
9352 debug_printf_exec("pseudo_exec'ed null command\n");
Francis Laniele7ca3a32023-12-22 22:02:42 +01009353 _exit_SUCCESS();
Francis Laniel110b7692023-12-22 22:02:27 +01009354}
9355
9356#if ENABLE_HUSH_JOB
9357static const char *get_cmdtext(struct pipe *pi)
9358{
9359 char **argv;
9360 char *p;
9361 int len;
9362
9363 /* This is subtle. ->cmdtext is created only on first backgrounding.
9364 * (Think "cat, <ctrl-z>, fg, <ctrl-z>, fg, <ctrl-z>...." here...)
9365 * On subsequent bg argv is trashed, but we won't use it */
9366 if (pi->cmdtext)
9367 return pi->cmdtext;
9368
9369 argv = pi->cmds[0].argv;
9370 if (!argv) {
9371 pi->cmdtext = xzalloc(1);
9372 return pi->cmdtext;
9373 }
9374 len = 0;
9375 do {
9376 len += strlen(*argv) + 1;
9377 } while (*++argv);
9378 p = xmalloc(len);
9379 pi->cmdtext = p;
9380 argv = pi->cmds[0].argv;
9381 do {
9382 p = stpcpy(p, *argv);
9383 *p++ = ' ';
9384 } while (*++argv);
9385 p[-1] = '\0';
9386 return pi->cmdtext;
9387}
9388
9389static void remove_job_from_table(struct pipe *pi)
9390{
9391 struct pipe *prev_pipe;
9392
9393 if (pi == G.job_list) {
9394 G.job_list = pi->next;
9395 } else {
9396 prev_pipe = G.job_list;
9397 while (prev_pipe->next != pi)
9398 prev_pipe = prev_pipe->next;
9399 prev_pipe->next = pi->next;
9400 }
9401 G.last_jobid = 0;
9402 if (G.job_list)
9403 G.last_jobid = G.job_list->jobid;
9404}
9405
9406static void delete_finished_job(struct pipe *pi)
9407{
9408 remove_job_from_table(pi);
9409 free_pipe(pi);
9410}
9411
9412static void clean_up_last_dead_job(void)
9413{
9414 if (G.job_list && !G.job_list->alive_cmds)
9415 delete_finished_job(G.job_list);
9416}
9417
9418static void insert_job_into_table(struct pipe *pi)
9419{
9420 struct pipe *job, **jobp;
9421 int i;
9422
9423 clean_up_last_dead_job();
9424
9425 /* Find the end of the list, and find next job ID to use */
9426 i = 0;
9427 jobp = &G.job_list;
9428 while ((job = *jobp) != NULL) {
9429 if (job->jobid > i)
9430 i = job->jobid;
9431 jobp = &job->next;
9432 }
9433 pi->jobid = i + 1;
9434
9435 /* Create a new job struct at the end */
9436 job = *jobp = xmemdup(pi, sizeof(*pi));
9437 job->next = NULL;
9438 job->cmds = xzalloc(sizeof(pi->cmds[0]) * pi->num_cmds);
9439 /* Cannot copy entire pi->cmds[] vector! This causes double frees */
9440 for (i = 0; i < pi->num_cmds; i++) {
9441 job->cmds[i].pid = pi->cmds[i].pid;
9442 /* all other fields are not used and stay zero */
9443 }
9444 job->cmdtext = xstrdup(get_cmdtext(pi));
9445
9446 if (G_interactive_fd)
9447 printf("[%u] %u %s\n", job->jobid, (unsigned)job->cmds[0].pid, job->cmdtext);
9448 G.last_jobid = job->jobid;
9449}
9450#endif /* JOB */
9451
9452static int job_exited_or_stopped(struct pipe *pi)
9453{
9454 int rcode, i;
9455
9456 if (pi->alive_cmds != pi->stopped_cmds)
9457 return -1;
9458
9459 /* All processes in fg pipe have exited or stopped */
9460 rcode = 0;
9461 i = pi->num_cmds;
9462 while (--i >= 0) {
9463 rcode = pi->cmds[i].cmd_exitcode;
9464 /* usually last process gives overall exitstatus,
9465 * but with "set -o pipefail", last *failed* process does */
9466 if (G.o_opt[OPT_O_PIPEFAIL] == 0 || rcode != 0)
9467 break;
9468 }
9469 IF_HAS_KEYWORDS(if (pi->pi_inverted) rcode = !rcode;)
9470 return rcode;
9471}
9472
9473static int process_wait_result(struct pipe *fg_pipe, pid_t childpid, int status)
9474{
9475#if ENABLE_HUSH_JOB
9476 struct pipe *pi;
9477#endif
9478 int i, dead;
9479
9480 dead = WIFEXITED(status) || WIFSIGNALED(status);
9481
9482#if DEBUG_JOBS
9483 if (WIFSTOPPED(status))
9484 debug_printf_jobs("pid %d stopped by sig %d (exitcode %d)\n",
9485 childpid, WSTOPSIG(status), WEXITSTATUS(status));
9486 if (WIFSIGNALED(status))
9487 debug_printf_jobs("pid %d killed by sig %d (exitcode %d)\n",
9488 childpid, WTERMSIG(status), WEXITSTATUS(status));
9489 if (WIFEXITED(status))
9490 debug_printf_jobs("pid %d exited, exitcode %d\n",
9491 childpid, WEXITSTATUS(status));
9492#endif
9493 /* Were we asked to wait for a fg pipe? */
9494 if (fg_pipe) {
9495 i = fg_pipe->num_cmds;
9496
9497 while (--i >= 0) {
9498 int rcode;
9499
9500 debug_printf_jobs("check pid %d\n", fg_pipe->cmds[i].pid);
9501 if (fg_pipe->cmds[i].pid != childpid)
9502 continue;
9503 if (dead) {
9504 int ex;
9505 fg_pipe->cmds[i].pid = 0;
9506 fg_pipe->alive_cmds--;
9507 ex = WEXITSTATUS(status);
9508 /* bash prints killer signal's name for *last*
9509 * process in pipe (prints just newline for SIGINT/SIGPIPE).
9510 * Mimic this. Example: "sleep 5" + (^\ or kill -QUIT)
9511 */
9512 if (WIFSIGNALED(status)) {
9513 int sig = WTERMSIG(status);
9514#if ENABLE_HUSH_JOB
9515 if (G.run_list_level == 1
9516 /* ^^^^^ Do not print in nested contexts, example:
9517 * echo `sleep 1; sh -c 'kill -9 $$'` - prints "137", NOT "Killed 137"
9518 */
9519 && i == fg_pipe->num_cmds-1
9520 ) {
9521 /* strsignal() is for bash compat. ~600 bloat versus bbox's get_signame() */
9522 puts(sig == SIGINT || sig == SIGPIPE ? "" : strsignal(sig));
9523 }
9524#endif
9525 /* TODO: if (WCOREDUMP(status)) + " (core dumped)"; */
9526 /* MIPS has 128 sigs (1..128), if sig==128,
9527 * 128 + sig would result in exitcode 256 -> 0!
9528 */
9529 ex = 128 | sig;
9530 }
9531 fg_pipe->cmds[i].cmd_exitcode = ex;
9532 } else {
9533 fg_pipe->stopped_cmds++;
9534 }
9535 debug_printf_jobs("fg_pipe: alive_cmds %d stopped_cmds %d\n",
9536 fg_pipe->alive_cmds, fg_pipe->stopped_cmds);
9537 rcode = job_exited_or_stopped(fg_pipe);
9538 if (rcode >= 0) {
9539/* Note: *non-interactive* bash does not continue if all processes in fg pipe
9540 * are stopped. Testcase: "cat | cat" in a script (not on command line!)
9541 * and "killall -STOP cat" */
9542 if (G_interactive_fd) {
9543#if ENABLE_HUSH_JOB
9544 if (fg_pipe->alive_cmds != 0)
9545 insert_job_into_table(fg_pipe);
9546#endif
9547 return rcode;
9548 }
9549 if (fg_pipe->alive_cmds == 0)
9550 return rcode;
9551 }
9552 /* There are still running processes in the fg_pipe */
9553 return -1;
9554 }
9555 /* It wasn't in fg_pipe, look for process in bg pipes */
9556 }
9557
9558#if ENABLE_HUSH_JOB
9559 /* We were asked to wait for bg or orphaned children */
9560 /* No need to remember exitcode in this case */
9561 for (pi = G.job_list; pi; pi = pi->next) {
9562 for (i = 0; i < pi->num_cmds; i++) {
9563 if (pi->cmds[i].pid == childpid)
9564 goto found_pi_and_prognum;
9565 }
9566 }
9567 /* Happens when shell is used as init process (init=/bin/sh) */
9568 debug_printf("checkjobs: pid %d was not in our list!\n", childpid);
9569 return -1; /* this wasn't a process from fg_pipe */
9570
9571 found_pi_and_prognum:
9572 if (dead) {
9573 /* child exited */
9574 int rcode = WEXITSTATUS(status);
9575 if (WIFSIGNALED(status))
9576 /* NB: not 128 + sig, MIPS has sig 128 */
9577 rcode = 128 | WTERMSIG(status);
9578 pi->cmds[i].cmd_exitcode = rcode;
9579 if (G.last_bg_pid == pi->cmds[i].pid)
9580 G.last_bg_pid_exitcode = rcode;
9581 pi->cmds[i].pid = 0;
9582 pi->alive_cmds--;
9583 if (!pi->alive_cmds) {
9584# if ENABLE_HUSH_BASH_COMPAT
9585 G.dead_job_exitcode = job_exited_or_stopped(pi);
9586# endif
9587 if (G_interactive_fd) {
9588 printf(JOB_STATUS_FORMAT, pi->jobid,
9589 "Done", pi->cmdtext);
9590 delete_finished_job(pi);
9591 } else {
9592/*
9593 * bash deletes finished jobs from job table only in interactive mode,
9594 * after "jobs" cmd, or if pid of a new process matches one of the old ones
9595 * (see cleanup_dead_jobs(), delete_old_job(), J_NOTIFIED in bash source).
9596 * Testcase script: "(exit 3) & sleep 1; wait %1; echo $?" prints 3 in bash.
9597 * We only retain one "dead" job, if it's the single job on the list.
9598 * This covers most of real-world scenarios where this is useful.
9599 */
9600 if (pi != G.job_list)
9601 delete_finished_job(pi);
9602 }
9603 }
9604 } else {
9605 /* child stopped */
9606 pi->stopped_cmds++;
9607 }
9608#endif
9609 return -1; /* this wasn't a process from fg_pipe */
9610}
9611
9612/* Check to see if any processes have exited -- if they have,
9613 * figure out why and see if a job has completed.
9614 *
9615 * If non-NULL fg_pipe: wait for its completion or stop.
9616 * Return its exitcode or zero if stopped.
9617 *
9618 * Alternatively (fg_pipe == NULL, waitfor_pid != 0):
9619 * waitpid(WNOHANG), if waitfor_pid exits or stops, return exitcode+1,
9620 * else return <0 if waitpid errors out (e.g. ECHILD: nothing to wait for)
9621 * or 0 if no children changed status.
9622 *
9623 * Alternatively (fg_pipe == NULL, waitfor_pid == 0),
9624 * return <0 if waitpid errors out (e.g. ECHILD: nothing to wait for)
9625 * or 0 if no children changed status.
9626 */
9627static int checkjobs(struct pipe *fg_pipe, pid_t waitfor_pid)
9628{
9629 int attributes;
9630 int status;
9631 int rcode = 0;
9632
9633 debug_printf_jobs("checkjobs %p\n", fg_pipe);
9634
9635 attributes = WUNTRACED;
9636 if (fg_pipe == NULL)
9637 attributes |= WNOHANG;
9638
9639 errno = 0;
9640#if ENABLE_HUSH_FAST
9641 if (G.handled_SIGCHLD == G.count_SIGCHLD) {
9642//bb_error_msg("[%d] checkjobs: G.count_SIGCHLD:%d G.handled_SIGCHLD:%d children?:%d fg_pipe:%p",
9643//getpid(), G.count_SIGCHLD, G.handled_SIGCHLD, G.we_have_children, fg_pipe);
9644 /* There was neither fork nor SIGCHLD since last waitpid */
9645 /* Avoid doing waitpid syscall if possible */
9646 if (!G.we_have_children) {
9647 errno = ECHILD;
9648 return -1;
9649 }
9650 if (fg_pipe == NULL) { /* is WNOHANG set? */
9651 /* We have children, but they did not exit
9652 * or stop yet (we saw no SIGCHLD) */
9653 return 0;
9654 }
9655 /* else: !WNOHANG, waitpid will block, can't short-circuit */
9656 }
9657#endif
9658
9659/* Do we do this right?
9660 * bash-3.00# sleep 20 | false
9661 * <ctrl-Z pressed>
9662 * [3]+ Stopped sleep 20 | false
9663 * bash-3.00# echo $?
9664 * 1 <========== bg pipe is not fully done, but exitcode is already known!
9665 * [hush 1.14.0: yes we do it right]
9666 */
9667 while (1) {
9668 pid_t childpid;
9669#if ENABLE_HUSH_FAST
9670 int i;
9671 i = G.count_SIGCHLD;
9672#endif
9673 childpid = waitpid(-1, &status, attributes);
9674 if (childpid <= 0) {
9675 if (childpid && errno != ECHILD)
9676 bb_simple_perror_msg("waitpid");
9677#if ENABLE_HUSH_FAST
9678 else { /* Until next SIGCHLD, waitpid's are useless */
9679 G.we_have_children = (childpid == 0);
9680 G.handled_SIGCHLD = i;
9681//bb_error_msg("[%d] checkjobs: waitpid returned <= 0, G.count_SIGCHLD:%d G.handled_SIGCHLD:%d", getpid(), G.count_SIGCHLD, G.handled_SIGCHLD);
9682 }
9683#endif
9684 /* ECHILD (no children), or 0 (no change in children status) */
9685 rcode = childpid;
9686 break;
9687 }
9688 rcode = process_wait_result(fg_pipe, childpid, status);
9689 if (rcode >= 0) {
9690 /* fg_pipe exited or stopped */
9691 break;
9692 }
9693 if (childpid == waitfor_pid) { /* "wait PID" */
9694 debug_printf_exec("childpid==waitfor_pid:%d status:0x%08x\n", childpid, status);
9695 rcode = WEXITSTATUS(status);
9696 if (WIFSIGNALED(status))
9697 rcode = 128 | WTERMSIG(status);
9698 if (WIFSTOPPED(status))
9699 /* bash: "cmd & wait $!" and cmd stops: $? = 128 | stopsig */
9700 rcode = 128 | WSTOPSIG(status);
9701 rcode++;
9702 break; /* "wait PID" called us, give it exitcode+1 */
9703 }
9704#if ENABLE_HUSH_BASH_COMPAT
9705 if (-1 == waitfor_pid /* "wait -n" (wait for any one job) */
9706 && G.dead_job_exitcode >= 0 /* some job did finish */
9707 ) {
9708 debug_printf_exec("waitfor_pid:-1\n");
9709 rcode = G.dead_job_exitcode + 1;
9710 break;
9711 }
9712#endif
9713 /* This wasn't one of our processes, or */
9714 /* fg_pipe still has running processes, do waitpid again */
9715 } /* while (waitpid succeeds)... */
9716
9717 return rcode;
9718}
9719
9720#if ENABLE_HUSH_JOB
9721static int checkjobs_and_fg_shell(struct pipe *fg_pipe)
9722{
9723 pid_t p;
9724 int rcode = checkjobs(fg_pipe, 0 /*(no pid to wait for)*/);
9725 if (G_saved_tty_pgrp) {
9726 /* Job finished, move the shell to the foreground */
9727 p = getpgrp(); /* our process group id */
9728 debug_printf_jobs("fg'ing ourself: getpgrp()=%d\n", (int)p);
9729 tcsetpgrp(G_interactive_fd, p);
9730 }
9731 return rcode;
9732}
9733#endif
9734
9735/* Start all the jobs, but don't wait for anything to finish.
9736 * See checkjobs().
9737 *
9738 * Return code is normally -1, when the caller has to wait for children
9739 * to finish to determine the exit status of the pipe. If the pipe
9740 * is a simple builtin command, however, the action is done by the
9741 * time run_pipe returns, and the exit code is provided as the
9742 * return value.
9743 *
9744 * Returns -1 only if started some children. IOW: we have to
9745 * mask out retvals of builtins etc with 0xff!
9746 *
9747 * The only case when we do not need to [v]fork is when the pipe
9748 * is single, non-backgrounded, non-subshell command. Examples:
9749 * cmd ; ... { list } ; ...
9750 * cmd && ... { list } && ...
9751 * cmd || ... { list } || ...
9752 * If it is, then we can run cmd as a builtin, NOFORK,
9753 * or (if SH_STANDALONE) an applet, and we can run the { list }
9754 * with run_list. If it isn't one of these, we fork and exec cmd.
9755 *
9756 * Cases when we must fork:
9757 * non-single: cmd | cmd
9758 * backgrounded: cmd & { list } &
9759 * subshell: ( list ) [&]
9760 */
9761#if !ENABLE_HUSH_MODE_X
9762#define redirect_and_varexp_helper(command, sqp, argv_expanded) \
9763 redirect_and_varexp_helper(command, sqp)
9764#endif
9765static int redirect_and_varexp_helper(
9766 struct command *command,
9767 struct squirrel **sqp,
9768 char **argv_expanded)
9769{
9770 /* Assignments occur before redirects. Try:
9771 * a=`sleep 1` sleep 2 3>/qwe/rty
9772 */
9773
9774 char **new_env = expand_assignments(command->argv, command->assignment_cnt);
9775 dump_cmd_in_x_mode(new_env);
9776 dump_cmd_in_x_mode(argv_expanded);
9777 /* this takes ownership of new_env[i] elements, and frees new_env: */
9778 set_vars_and_save_old(new_env);
9779
9780 return setup_redirects(command, sqp);
9781}
Francis Laniel36836fc2023-12-22 22:02:28 +01009782#endif /* !__U_BOOT__ */
9783
Francis Laniel110b7692023-12-22 22:02:27 +01009784static NOINLINE int run_pipe(struct pipe *pi)
9785{
9786 static const char *const null_ptr = NULL;
9787
9788 int cmd_no;
Francis Laniel36836fc2023-12-22 22:02:28 +01009789#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01009790 int next_infd;
Francis Laniel36836fc2023-12-22 22:02:28 +01009791#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01009792 struct command *command;
9793 char **argv_expanded;
9794 char **argv;
Francis Laniel36836fc2023-12-22 22:02:28 +01009795#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01009796 struct squirrel *squirrel = NULL;
Francis Laniel36836fc2023-12-22 22:02:28 +01009797#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01009798 int rcode;
9799
Francis Laniel36836fc2023-12-22 22:02:28 +01009800#ifdef __U_BOOT__
9801 /*
9802 * Set rcode here to avoid returning a garbage value in the middle of
9803 * the function.
9804 * Also, if an error occurs, rcode value would be changed and last
9805 * return will signal the error.
9806 */
9807 rcode = 0;
9808#endif /* __U_BOOT__ */
9809
Francis Laniel110b7692023-12-22 22:02:27 +01009810 debug_printf_exec("run_pipe start: members:%d\n", pi->num_cmds);
9811 debug_enter();
9812
9813 /* Testcase: set -- q w e; (IFS='' echo "$*"; IFS=''; echo "$*"); echo "$*"
9814 * Result should be 3 lines: q w e, qwe, q w e
9815 */
9816 if (G.ifs_whitespace != G.ifs)
9817 free(G.ifs_whitespace);
9818 G.ifs = get_local_var_value("IFS");
9819 if (G.ifs) {
9820 char *p;
9821 G.ifs_whitespace = (char*)G.ifs;
9822 p = skip_whitespace(G.ifs);
9823 if (*p) {
9824 /* Not all $IFS is whitespace */
9825 char *d;
9826 int len = p - G.ifs;
9827 p = skip_non_whitespace(p);
9828 G.ifs_whitespace = xmalloc(len + strlen(p) + 1); /* can overestimate */
9829 d = mempcpy(G.ifs_whitespace, G.ifs, len);
9830 while (*p) {
9831 if (isspace(*p))
9832 *d++ = *p;
9833 p++;
9834 }
9835 *d = '\0';
9836 }
9837 } else {
9838 G.ifs = defifs;
9839 G.ifs_whitespace = (char*)G.ifs;
9840 }
9841
Francis Laniel36836fc2023-12-22 22:02:28 +01009842#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01009843 IF_HUSH_JOB(pi->pgrp = -1;)
9844 pi->stopped_cmds = 0;
Francis Laniel36836fc2023-12-22 22:02:28 +01009845#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01009846 command = &pi->cmds[0];
9847 argv_expanded = NULL;
9848
Francis Laniel36836fc2023-12-22 22:02:28 +01009849#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01009850 if (pi->num_cmds != 1
9851 || pi->followup == PIPE_BG
9852 || command->cmd_type == CMD_SUBSHELL
9853 ) {
9854 goto must_fork;
9855 }
9856
9857 pi->alive_cmds = 1;
Francis Laniel36836fc2023-12-22 22:02:28 +01009858#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01009859
9860 debug_printf_exec(": group:%p argv:'%s'\n",
9861 command->group, command->argv ? command->argv[0] : "NONE");
9862
9863 if (command->group) {
9864#if ENABLE_HUSH_FUNCTIONS
9865 if (command->cmd_type == CMD_FUNCDEF) {
9866 /* "executing" func () { list } */
9867 struct function *funcp;
9868
9869 funcp = new_function(command->argv[0]);
9870 /* funcp->name is already set to argv[0] */
9871 funcp->body = command->group;
9872# if !BB_MMU
9873 funcp->body_as_string = command->group_as_string;
9874 command->group_as_string = NULL;
9875# endif
9876 command->group = NULL;
9877 command->argv[0] = NULL;
9878 debug_printf_exec("cmd %p has child func at %p\n", command, funcp);
9879 funcp->parent_cmd = command;
9880 command->child_func = funcp;
9881
9882 debug_printf_exec("run_pipe: return EXIT_SUCCESS\n");
9883 debug_leave();
9884 return EXIT_SUCCESS;
9885 }
9886#endif
9887 /* { list } */
9888 debug_printf_exec("non-subshell group\n");
9889 rcode = 1; /* exitcode if redir failed */
Francis Laniel36836fc2023-12-22 22:02:28 +01009890#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01009891 if (setup_redirects(command, &squirrel) == 0) {
Francis Laniel9492c942023-12-22 22:02:39 +01009892#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01009893 debug_printf_exec(": run_list\n");
9894//FIXME: we need to pass squirrel down into run_list()
9895//for SH_STANDALONE case, or else this construct:
9896// { find /proc/self/fd; true; } >FILE; cmd2
9897//has no way of closing saved fd#1 for "find",
9898//and in SH_STANDALONE mode, "find" is not execed,
9899//therefore CLOEXEC on saved fd does not help.
9900 rcode = run_list(command->group) & 0xff;
Francis Laniel9492c942023-12-22 22:02:39 +01009901#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01009902 }
9903 restore_redirects(squirrel);
Francis Laniel36836fc2023-12-22 22:02:28 +01009904#endif /* !__U_BOOT__ */
Francis Laniel9492c942023-12-22 22:02:39 +01009905 IF_HAS_KEYWORDS(if (pi->pi_inverted) rcode = !rcode;)
Francis Laniel110b7692023-12-22 22:02:27 +01009906 debug_leave();
9907 debug_printf_exec("run_pipe: return %d\n", rcode);
9908 return rcode;
9909 }
9910
9911 argv = command->argv ? command->argv : (char **) &null_ptr;
9912 {
Francis Laniel36836fc2023-12-22 22:02:28 +01009913#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01009914 const struct built_in_command *x;
9915 IF_HUSH_FUNCTIONS(const struct function *funcp;)
9916 IF_NOT_HUSH_FUNCTIONS(enum { funcp = 0 };)
9917 struct variable **sv_shadowed;
Francis Laniel36836fc2023-12-22 22:02:28 +01009918#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01009919 struct variable *old_vars;
9920
9921#if ENABLE_HUSH_LINENO_VAR
9922 G.execute_lineno = command->lineno;
9923#endif
9924
9925 if (argv[command->assignment_cnt] == NULL) {
9926 /* Assignments, but no command.
9927 * Ensure redirects take effect (that is, create files).
9928 * Try "a=t >file"
9929 */
9930 unsigned i;
9931 G.expand_exitcode = 0;
9932 only_assignments:
Francis Laniel36836fc2023-12-22 22:02:28 +01009933#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +01009934 rcode = setup_redirects(command, &squirrel);
9935 restore_redirects(squirrel);
Francis Laniel36836fc2023-12-22 22:02:28 +01009936#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +01009937
9938 /* Set shell variables */
9939 i = 0;
9940 while (i < command->assignment_cnt) {
9941 char *p = expand_string_to_string(argv[i],
9942 EXP_FLAG_ESC_GLOB_CHARS,
9943 /*unbackslash:*/ 1
9944 );
9945#if ENABLE_HUSH_MODE_X
9946 if (G_x_mode) {
9947 char *eq;
9948 if (i == 0)
9949 x_mode_prefix();
9950 x_mode_addchr(' ');
9951 eq = strchrnul(p, '=');
9952 if (*eq) eq++;
9953 x_mode_addblock(p, (eq - p));
9954 x_mode_print_optionally_squoted(eq);
9955 x_mode_flush();
9956 }
9957#endif
9958 debug_printf_env("set shell var:'%s'->'%s'\n", *argv, p);
Francis Laniel36836fc2023-12-22 22:02:28 +01009959#ifndef __U_BOOT__
Francis Laniele7ca3a32023-12-22 22:02:42 +01009960 if (set_local_var0(p)) {
Francis Laniel36836fc2023-12-22 22:02:28 +01009961#else /* __U_BOOT__ */
9962 if (set_local_var_modern(p, /*flag:*/ 0)) {
9963#endif
Francis Laniel110b7692023-12-22 22:02:27 +01009964 /* assignment to readonly var / putenv error? */
9965 rcode = 1;
9966 }
9967 i++;
9968 }
9969 /* Redirect error sets $? to 1. Otherwise,
9970 * if evaluating assignment value set $?, retain it.
9971 * Else, clear $?:
9972 * false; q=`exit 2`; echo $? - should print 2
9973 * false; x=1; echo $? - should print 0
9974 * Because of the 2nd case, we can't just use G.last_exitcode.
9975 */
9976 if (rcode == 0)
9977 rcode = G.expand_exitcode;
9978 IF_HAS_KEYWORDS(if (pi->pi_inverted) rcode = !rcode;)
9979 debug_leave();
9980 debug_printf_exec("run_pipe: return %d\n", rcode);
9981 return rcode;
9982 }
9983
9984 /* Expand the rest into (possibly) many strings each */
9985#if defined(CMD_TEST2_SINGLEWORD_NOGLOB)
9986 if (command->cmd_type == CMD_TEST2_SINGLEWORD_NOGLOB)
9987 argv_expanded = expand_strvec_to_strvec_singleword_noglob(argv + command->assignment_cnt);
9988 else
9989#endif
9990#if defined(CMD_SINGLEWORD_NOGLOB)
9991 if (command->cmd_type == CMD_SINGLEWORD_NOGLOB)
9992 argv_expanded = expand_strvec_to_strvec_singleword_noglob(argv + command->assignment_cnt);
9993 else
9994#endif
9995 argv_expanded = expand_strvec_to_strvec(argv + command->assignment_cnt);
9996
9997 /* If someone gives us an empty string: `cmd with empty output` */
9998 if (!argv_expanded[0]) {
9999 free(argv_expanded);
10000 /* `false` still has to set exitcode 1 */
10001 G.expand_exitcode = G.last_exitcode;
10002 goto only_assignments;
10003 }
10004
10005 old_vars = NULL;
Francis Laniel36836fc2023-12-22 22:02:28 +010010006#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +010010007 sv_shadowed = G.shadowed_vars_pp;
10008
10009 /* Check if argv[0] matches any functions (this goes before bltins) */
10010 IF_HUSH_FUNCTIONS(funcp = find_function(argv_expanded[0]);)
10011 IF_HUSH_FUNCTIONS(x = NULL;)
10012 IF_HUSH_FUNCTIONS(if (!funcp))
10013 x = find_builtin(argv_expanded[0]);
10014 if (x || funcp) {
10015 if (x && x->b_function == builtin_exec && argv_expanded[1] == NULL) {
10016 debug_printf("exec with redirects only\n");
10017 /*
10018 * Variable assignments are executed, but then "forgotten":
10019 * a=`sleep 1;echo A` exec 3>&-; echo $a
10020 * sleeps, but prints nothing.
10021 */
10022 enter_var_nest_level();
10023 G.shadowed_vars_pp = &old_vars;
10024 rcode = redirect_and_varexp_helper(command,
10025 /*squirrel:*/ ERR_PTR,
10026 argv_expanded
10027 );
10028 G.shadowed_vars_pp = sv_shadowed;
10029 /* rcode=1 can be if redir file can't be opened */
10030
10031 goto clean_up_and_ret1;
10032 }
10033
10034 /* Bump var nesting, or this will leak exported $a:
10035 * a=b true; env | grep ^a=
10036 */
10037 enter_var_nest_level();
10038 /* Collect all variables "shadowed" by helper
10039 * (IOW: old vars overridden by "var1=val1 var2=val2 cmd..." syntax)
10040 * into old_vars list:
10041 */
10042 G.shadowed_vars_pp = &old_vars;
10043 rcode = redirect_and_varexp_helper(command, &squirrel, argv_expanded);
10044 if (rcode == 0) {
10045 if (!funcp) {
10046 /* Do not collect *to old_vars list* vars shadowed
10047 * by e.g. "local VAR" builtin (collect them
10048 * in the previously nested list instead):
10049 * don't want them to be restored immediately
10050 * after "local" completes.
10051 */
10052 G.shadowed_vars_pp = sv_shadowed;
10053
10054 debug_printf_exec(": builtin '%s' '%s'...\n",
10055 x->b_cmd, argv_expanded[1]);
10056 fflush_all();
10057 rcode = x->b_function(argv_expanded) & 0xff;
10058 fflush_all();
10059 }
10060#if ENABLE_HUSH_FUNCTIONS
10061 else {
10062 debug_printf_exec(": function '%s' '%s'...\n",
10063 funcp->name, argv_expanded[1]);
10064 rcode = run_function(funcp, argv_expanded) & 0xff;
10065 /*
10066 * But do collect *to old_vars list* vars shadowed
10067 * within function execution. To that end, restore
10068 * this pointer _after_ function run:
10069 */
10070 G.shadowed_vars_pp = sv_shadowed;
10071 }
10072#endif
10073 }
10074 } else
10075 if (ENABLE_FEATURE_SH_NOFORK && NUM_APPLETS > 1) {
10076 int n = find_applet_by_name(argv_expanded[0]);
10077 if (n < 0 || !APPLET_IS_NOFORK(n))
10078 goto must_fork;
10079
10080 enter_var_nest_level();
10081 /* Collect all variables "shadowed" by helper into old_vars list */
10082 G.shadowed_vars_pp = &old_vars;
10083 rcode = redirect_and_varexp_helper(command, &squirrel, argv_expanded);
10084 G.shadowed_vars_pp = sv_shadowed;
10085
10086 if (rcode == 0) {
10087 debug_printf_exec(": run_nofork_applet '%s' '%s'...\n",
10088 argv_expanded[0], argv_expanded[1]);
10089 /*
10090 * Note: signals (^C) can't interrupt here.
10091 * We remember them and they will be acted upon
10092 * after applet returns.
10093 * This makes applets which can run for a long time
10094 * and/or wait for user input ineligible for NOFORK:
10095 * for example, "yes" or "rm" (rm -i waits for input).
10096 */
10097 rcode = run_nofork_applet(n, argv_expanded);
10098 }
10099 } else
10100 goto must_fork;
10101
10102 restore_redirects(squirrel);
10103 clean_up_and_ret1:
10104 leave_var_nest_level();
10105 add_vars(old_vars);
10106
10107 /*
10108 * Try "usleep 99999999" + ^C + "echo $?"
10109 * with FEATURE_SH_NOFORK=y.
10110 */
10111 if (!funcp) {
10112 /* It was builtin or nofork.
10113 * if this would be a real fork/execed program,
10114 * it should have died if a fatal sig was received.
10115 * But OTOH, there was no separate process,
10116 * the sig was sent to _shell_, not to non-existing
10117 * child.
10118 * Let's just handle ^C only, this one is obvious:
10119 * we aren't ok with exitcode 0 when ^C was pressed
10120 * during builtin/nofork.
10121 */
10122 if (sigismember(&G.pending_set, SIGINT))
10123 rcode = 128 | SIGINT;
10124 }
10125 free(argv_expanded);
10126 IF_HAS_KEYWORDS(if (pi->pi_inverted) rcode = !rcode;)
10127 debug_leave();
10128 debug_printf_exec("run_pipe return %d\n", rcode);
10129 return rcode;
Francis Laniel36836fc2023-12-22 22:02:28 +010010130#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +010010131 }
10132
Francis Laniel36836fc2023-12-22 22:02:28 +010010133#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +010010134 must_fork:
10135 /* NB: argv_expanded may already be created, and that
10136 * might include `cmd` runs! Do not rerun it! We *must*
10137 * use argv_expanded if it's non-NULL */
10138
10139 /* Going to fork a child per each pipe member */
10140 pi->alive_cmds = 0;
10141 next_infd = 0;
Francis Laniel36836fc2023-12-22 22:02:28 +010010142#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +010010143
10144 cmd_no = 0;
10145 while (cmd_no < pi->num_cmds) {
Francis Laniel36836fc2023-12-22 22:02:28 +010010146#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +010010147 struct fd_pair pipefds;
10148#if !BB_MMU
10149 int sv_var_nest_level = G.var_nest_level;
10150 volatile nommu_save_t nommu_save;
10151 nommu_save.old_vars = NULL;
10152 nommu_save.argv = NULL;
10153 nommu_save.argv_from_re_execing = NULL;
10154#endif
Francis Laniel36836fc2023-12-22 22:02:28 +010010155#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +010010156 command = &pi->cmds[cmd_no];
10157 cmd_no++;
Francis Lanielbfc406a2023-12-22 22:02:33 +010010158
10159#ifdef __U_BOOT__
10160 /* Replace argv and argc by expanded if it exists. */
10161 if (argv_expanded) {
10162 /*
10163 * We need to save a pointer to argv, we will restore it
10164 * later, so it will be freed when pipe is freed.
10165 */
10166 argv = command->argv;
10167
10168 /*
10169 * After expansion, there can be more or less argument, so we need to
10170 * update argc, for example:
10171 * - More arguments:
10172 * foo='bar quuz'
10173 * echo $foo
10174 * - Less arguments:
10175 * echo $foo (if foo was never set)
10176 */
10177 command->argc = list_size(argv_expanded);
10178 command->argv = argv_expanded;
10179 }
10180#endif /* __U_BOOT__ */
10181 if (command->argv) {
Francis Laniel110b7692023-12-22 22:02:27 +010010182 debug_printf_exec(": pipe member '%s' '%s'...\n",
10183 command->argv[0], command->argv[1]);
10184 } else {
10185 debug_printf_exec(": pipe member with no argv\n");
10186 }
10187
Francis Laniel36836fc2023-12-22 22:02:28 +010010188#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +010010189 /* pipes are inserted between pairs of commands */
10190 pipefds.rd = 0;
10191 pipefds.wr = 1;
10192 if (cmd_no < pi->num_cmds)
10193 xpiped_pair(pipefds);
10194
10195#if ENABLE_HUSH_LINENO_VAR
10196 G.execute_lineno = command->lineno;
10197#endif
10198
10199 command->pid = BB_MMU ? fork() : vfork();
10200 if (!command->pid) { /* child */
10201#if ENABLE_HUSH_JOB
10202 disable_restore_tty_pgrp_on_exit();
10203 CLEAR_RANDOM_T(&G.random_gen); /* or else $RANDOM repeats in child */
10204
10205 /* Every child adds itself to new process group
10206 * with pgid == pid_of_first_child_in_pipe */
10207 if (G.run_list_level == 1 && G_interactive_fd) {
10208 pid_t pgrp;
10209 pgrp = pi->pgrp;
10210 if (pgrp < 0) /* true for 1st process only */
10211 pgrp = getpid();
10212 if (setpgid(0, pgrp) == 0
10213 && pi->followup != PIPE_BG
10214 && G_saved_tty_pgrp /* we have ctty */
10215 ) {
10216 /* We do it in *every* child, not just first,
10217 * to avoid races */
10218 tcsetpgrp(G_interactive_fd, pgrp);
10219 }
10220 }
10221#endif
10222 if (pi->alive_cmds == 0 && pi->followup == PIPE_BG) {
10223 /* 1st cmd in backgrounded pipe
10224 * should have its stdin /dev/null'ed */
10225 close(0);
10226 if (open(bb_dev_null, O_RDONLY))
10227 xopen("/", O_RDONLY);
10228 } else {
10229 xmove_fd(next_infd, 0);
10230 }
10231 xmove_fd(pipefds.wr, 1);
10232 if (pipefds.rd > 1)
10233 close(pipefds.rd);
10234 /* Like bash, explicit redirects override pipes,
10235 * and the pipe fd (fd#1) is available for dup'ing:
10236 * "cmd1 2>&1 | cmd2": fd#1 is duped to fd#2, thus stderr
10237 * of cmd1 goes into pipe.
10238 */
10239 if (setup_redirects(command, NULL)) {
10240 /* Happens when redir file can't be opened:
10241 * $ hush -c 'echo FOO >&2 | echo BAR 3>/qwe/rty; echo BAZ'
10242 * FOO
10243 * hush: can't open '/qwe/rty': No such file or directory
10244 * BAZ
10245 * (echo BAR is not executed, it hits _exit(1) below)
10246 */
10247 _exit(1);
10248 }
10249
10250 /* Stores to nommu_save list of env vars putenv'ed
10251 * (NOMMU, on MMU we don't need that) */
10252 /* cast away volatility... */
10253 pseudo_exec((nommu_save_t*) &nommu_save, command, argv_expanded);
10254 /* pseudo_exec() does not return */
10255 }
10256
10257 /* parent or error */
10258#if ENABLE_HUSH_FAST
10259 G.count_SIGCHLD++;
10260//bb_error_msg("[%d] fork in run_pipe: G.count_SIGCHLD:%d G.handled_SIGCHLD:%d", getpid(), G.count_SIGCHLD, G.handled_SIGCHLD);
10261#endif
10262 enable_restore_tty_pgrp_on_exit();
10263#if !BB_MMU
10264 /* Clean up after vforked child */
10265 free(nommu_save.argv);
10266 free(nommu_save.argv_from_re_execing);
10267 G.var_nest_level = sv_var_nest_level;
10268 remove_nested_vars();
10269 add_vars(nommu_save.old_vars);
10270#endif
10271 free(argv_expanded);
10272 argv_expanded = NULL;
10273 if (command->pid < 0) { /* [v]fork failed */
10274 /* Clearly indicate, was it fork or vfork */
10275 bb_simple_perror_msg(BB_MMU ? "vfork"+1 : "vfork");
10276 } else {
10277 pi->alive_cmds++;
10278#if ENABLE_HUSH_JOB
10279 /* Second and next children need to know pid of first one */
10280 if (pi->pgrp < 0)
10281 pi->pgrp = command->pid;
10282#endif
10283 }
10284
10285 if (cmd_no > 1)
10286 close(next_infd);
10287 if (cmd_no < pi->num_cmds)
10288 close(pipefds.wr);
10289 /* Pass read (output) pipe end to next iteration */
10290 next_infd = pipefds.rd;
Francis Laniel36836fc2023-12-22 22:02:28 +010010291#else /* __U_BOOT__ */
10292 /* Process the command */
10293 rcode = cmd_process(G.do_repeat ? CMD_FLAG_REPEAT : 0,
10294 command->argc, command->argv,
10295 &(G.flag_repeat), NULL);
Francis Lanielbfc406a2023-12-22 22:02:33 +010010296
10297 if (argv_expanded) {
10298 /*
10299 * expand_strvec_to_strvec() allocates memory to expand
10300 * argv, we need to free it.
10301 */
10302 free(argv_expanded);
10303
10304 /*
10305 * We also restore command->argv to its original value
10306 * so no memory leak happens.
10307 */
10308 command->argv = argv;
10309
10310 /*
10311 * NOTE argc exists only in U-Boot, so argv freeing does
10312 * not rely on it as this code exists in BusyBox.
10313 */
10314 }
Francis Laniel36836fc2023-12-22 22:02:28 +010010315#endif /* __U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +010010316 }
10317
Francis Laniel36836fc2023-12-22 22:02:28 +010010318#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +010010319 if (!pi->alive_cmds) {
10320 debug_leave();
10321 debug_printf_exec("run_pipe return 1 (all forks failed, no children)\n");
10322 return 1;
10323 }
Francis Laniel36836fc2023-12-22 22:02:28 +010010324#endif /* __U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +010010325
10326 debug_leave();
Francis Laniel36836fc2023-12-22 22:02:28 +010010327#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +010010328 debug_printf_exec("run_pipe return -1 (%u children started)\n", pi->alive_cmds);
10329 return -1;
Francis Laniel36836fc2023-12-22 22:02:28 +010010330#else /* __U_BOOT__ */
10331 debug_printf_exec("run_pipe return %d\n", rcode);
10332 return rcode;
10333#endif /* __U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +010010334}
10335
10336/* NB: called by pseudo_exec, and therefore must not modify any
10337 * global data until exec/_exit (we can be a child after vfork!) */
10338static int run_list(struct pipe *pi)
10339{
10340#if ENABLE_HUSH_CASE
10341 char *case_word = NULL;
10342#endif
10343#if ENABLE_HUSH_LOOPS
10344 struct pipe *loop_top = NULL;
10345 char **for_lcur = NULL;
10346 char **for_list = NULL;
10347#endif
10348 smallint last_followup;
10349 smalluint rcode;
10350#if ENABLE_HUSH_IF || ENABLE_HUSH_CASE
10351 smalluint cond_code = 0;
10352#else
10353 enum { cond_code = 0 };
10354#endif
10355#if HAS_KEYWORDS
10356 smallint rword; /* RES_foo */
10357 smallint last_rword; /* ditto */
10358#endif
10359
Francis Laniel36836fc2023-12-22 22:02:28 +010010360#ifndef __U_BOOT__
Francis Laniele7ca3a32023-12-22 22:02:42 +010010361 debug_printf_exec("run_list lvl %d start\n", G.run_list_level);
Francis Laniel110b7692023-12-22 22:02:27 +010010362 debug_enter();
Francis Laniel36836fc2023-12-22 22:02:28 +010010363#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +010010364
10365#if ENABLE_HUSH_LOOPS
10366 /* Check syntax for "for" */
10367 {
10368 struct pipe *cpipe;
10369 for (cpipe = pi; cpipe; cpipe = cpipe->next) {
10370 if (cpipe->res_word != RES_FOR && cpipe->res_word != RES_IN)
10371 continue;
10372 /* current word is FOR or IN (BOLD in comments below) */
10373 if (cpipe->next == NULL) {
10374 syntax_error("malformed for");
10375 debug_leave();
10376 debug_printf_exec("run_list lvl %d return 1\n", G.run_list_level);
10377 return 1;
10378 }
10379 /* "FOR v; do ..." and "for v IN a b; do..." are ok */
10380 if (cpipe->next->res_word == RES_DO)
10381 continue;
10382 /* next word is not "do". It must be "in" then ("FOR v in ...") */
10383 if (cpipe->res_word == RES_IN /* "for v IN a b; not_do..."? */
10384 || cpipe->next->res_word != RES_IN /* FOR v not_do_and_not_in..."? */
10385 ) {
10386 syntax_error("malformed for");
10387 debug_leave();
10388 debug_printf_exec("run_list lvl %d return 1\n", G.run_list_level);
10389 return 1;
10390 }
10391 }
10392 }
10393#endif
10394
10395 /* Past this point, all code paths should jump to ret: label
10396 * in order to return, no direct "return" statements please.
10397 * This helps to ensure that no memory is leaked. */
10398
10399#if ENABLE_HUSH_JOB
10400 G.run_list_level++;
10401#endif
10402
10403#if HAS_KEYWORDS
10404 rword = RES_NONE;
10405 last_rword = RES_XXXX;
10406#endif
10407 last_followup = PIPE_SEQ;
10408 rcode = G.last_exitcode;
10409
10410 /* Go through list of pipes, (maybe) executing them. */
Francis Laniel36836fc2023-12-22 22:02:28 +010010411#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +010010412 for (; pi; pi = IF_HUSH_LOOPS(rword == RES_DONE ? loop_top : ) pi->next) {
Francis Laniel36836fc2023-12-22 22:02:28 +010010413#else /* __U_BOOT__ */
Francis Lanield0003142023-12-22 22:02:40 +010010414 for (; pi; pi = rword == RES_DONE ? loop_top : pi->next) {
Francis Laniel36836fc2023-12-22 22:02:28 +010010415#endif /* __U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +010010416 int r;
10417 int sv_errexit_depth;
10418
Francis Laniel36836fc2023-12-22 22:02:28 +010010419#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +010010420 if (G.flag_SIGINT)
10421 break;
10422 if (G_flag_return_in_progress == 1)
10423 break;
Francis Laniel9492c942023-12-22 22:02:39 +010010424#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +010010425
10426 IF_HAS_KEYWORDS(rword = pi->res_word;)
Francis Laniele7ca3a32023-12-22 22:02:42 +010010427 debug_printf_exec(": rword:%d cond_code:%d last_rword:%d\n",
Francis Laniel110b7692023-12-22 22:02:27 +010010428 rword, cond_code, last_rword);
10429
10430 sv_errexit_depth = G.errexit_depth;
10431 if (
10432#if ENABLE_HUSH_IF
10433 rword == RES_IF || rword == RES_ELIF ||
10434#endif
10435 pi->followup != PIPE_SEQ
10436 ) {
10437 G.errexit_depth++;
10438 }
10439#if ENABLE_HUSH_LOOPS
10440 if ((rword == RES_WHILE || rword == RES_UNTIL || rword == RES_FOR)
10441 && loop_top == NULL /* avoid bumping G.depth_of_loop twice */
10442 ) {
10443 /* start of a loop: remember where loop starts */
10444 loop_top = pi;
10445 G.depth_of_loop++;
10446 }
10447#endif
10448 /* Still in the same "if...", "then..." or "do..." branch? */
10449 if (IF_HAS_KEYWORDS(rword == last_rword &&) 1) {
10450 if ((rcode == 0 && last_followup == PIPE_OR)
10451 || (rcode != 0 && last_followup == PIPE_AND)
10452 ) {
10453 /* It is "<true> || CMD" or "<false> && CMD"
10454 * and we should not execute CMD */
10455 debug_printf_exec("skipped cmd because of || or &&\n");
10456 last_followup = pi->followup;
10457 goto dont_check_jobs_but_continue;
10458 }
10459 }
10460 last_followup = pi->followup;
Francis Laniel110b7692023-12-22 22:02:27 +010010461#if ENABLE_HUSH_IF
Francis Laniele7ca3a32023-12-22 22:02:42 +010010462 if (cond_code != 0) {
Francis Laniel110b7692023-12-22 22:02:27 +010010463 if (rword == RES_THEN) {
10464 /* if false; then ... fi has exitcode 0! */
10465 G.last_exitcode = rcode = EXIT_SUCCESS;
10466 /* "if <false> THEN cmd": skip cmd */
Francis Laniele7ca3a32023-12-22 22:02:42 +010010467 debug_printf_exec("skipped THEN cmd because IF condition was false\n");
10468 last_rword = rword;
Francis Laniel110b7692023-12-22 22:02:27 +010010469 continue;
10470 }
10471 } else {
Francis Laniele7ca3a32023-12-22 22:02:42 +010010472 if (rword == RES_ELSE
10473 || (rword == RES_ELIF && last_rword != RES_ELIF)
10474 ) {
Francis Laniel110b7692023-12-22 22:02:27 +010010475 /* "if <true> then ... ELSE/ELIF cmd":
10476 * skip cmd and all following ones */
Francis Laniele7ca3a32023-12-22 22:02:42 +010010477 debug_printf_exec("skipped ELSE/ELIF branch because IF condition was true\n");
Francis Laniel110b7692023-12-22 22:02:27 +010010478 break;
10479 }
Francis Laniele7ca3a32023-12-22 22:02:42 +010010480 //if (rword == RES_THEN): "if <true> THEN cmd", run cmd (fall through)
Francis Laniel110b7692023-12-22 22:02:27 +010010481 }
10482#endif
Francis Laniele7ca3a32023-12-22 22:02:42 +010010483 IF_HAS_KEYWORDS(last_rword = rword;)
Francis Laniel110b7692023-12-22 22:02:27 +010010484#if ENABLE_HUSH_LOOPS
10485 if (rword == RES_FOR) { /* && pi->num_cmds - always == 1 */
10486 if (!for_lcur) {
10487 /* first loop through for */
10488
10489 static const char encoded_dollar_at[] ALIGN1 = {
10490 SPECIAL_VAR_SYMBOL, '@' | 0x80, SPECIAL_VAR_SYMBOL, '\0'
10491 }; /* encoded representation of "$@" */
Francis Laniele7ca3a32023-12-22 22:02:42 +010010492 static const char *const encoded_dollar_at_argv[] ALIGN_PTR = {
Francis Laniel110b7692023-12-22 22:02:27 +010010493 encoded_dollar_at, NULL
10494 }; /* argv list with one element: "$@" */
10495 char **vals;
10496
10497 G.last_exitcode = rcode = EXIT_SUCCESS;
10498 vals = (char**)encoded_dollar_at_argv;
10499 if (pi->next->res_word == RES_IN) {
10500 /* if no variable values after "in" we skip "for" */
10501 if (!pi->next->cmds[0].argv) {
10502 debug_printf_exec(": null FOR: exitcode EXIT_SUCCESS\n");
10503 break;
10504 }
10505 vals = pi->next->cmds[0].argv;
10506 } /* else: "for var; do..." -> assume "$@" list */
10507 /* create list of variable values */
10508 debug_print_strings("for_list made from", vals);
10509 for_list = expand_strvec_to_strvec(vals);
10510 for_lcur = for_list;
10511 debug_print_strings("for_list", for_list);
10512 }
10513 if (!*for_lcur) {
10514 /* "for" loop is over, clean up */
10515 free(for_list);
10516 for_list = NULL;
10517 for_lcur = NULL;
10518 break;
10519 }
10520 /* Insert next value from for_lcur */
10521 /* note: *for_lcur already has quotes removed, $var expanded, etc */
Francis Lanield0003142023-12-22 22:02:40 +010010522#ifndef __U_BOOT__
Francis Laniele7ca3a32023-12-22 22:02:42 +010010523 set_local_var_from_halves(pi->cmds[0].argv[0], *for_lcur++);
Francis Lanield0003142023-12-22 22:02:40 +010010524#else /* __U_BOOT__ */
10525 /* We cannot use xasprintf, so we emulate it. */
10526 char *full_var;
10527 char *var = pi->cmds[0].argv[0];
10528 char *val = *for_lcur++;
10529
10530 /* + 1 to take into account =. */
10531 full_var = xmalloc(strlen(var) + strlen(val) + 1);
10532 sprintf(full_var, "%s=%s", var, val);
10533
10534 set_local_var_modern(full_var, /*flag:*/ 0);
10535#endif /* __U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +010010536 continue;
10537 }
10538 if (rword == RES_IN) {
10539 continue; /* "for v IN list;..." - "in" has no cmds anyway */
10540 }
10541 if (rword == RES_DONE) {
10542 continue; /* "done" has no cmds too */
10543 }
10544#endif
10545#if ENABLE_HUSH_CASE
10546 if (rword == RES_CASE) {
10547 debug_printf_exec("CASE cond_code:%d\n", cond_code);
10548 case_word = expand_string_to_string(pi->cmds->argv[0],
10549 EXP_FLAG_ESC_GLOB_CHARS, /*unbackslash:*/ 1);
10550 debug_printf_exec("CASE word1:'%s'\n", case_word);
10551 //unbackslash(case_word);
10552 //debug_printf_exec("CASE word2:'%s'\n", case_word);
10553 continue;
10554 }
10555 if (rword == RES_MATCH) {
10556 char **argv;
10557
10558 debug_printf_exec("MATCH cond_code:%d\n", cond_code);
10559 if (!case_word) /* "case ... matched_word) ... WORD)": we executed selected branch, stop */
10560 break;
10561 /* all prev words didn't match, does this one match? */
10562 argv = pi->cmds->argv;
10563 while (*argv) {
10564 char *pattern;
10565 debug_printf_exec("expand_string_to_string('%s')\n", *argv);
10566 pattern = expand_string_to_string(*argv,
10567 EXP_FLAG_ESC_GLOB_CHARS,
10568 /*unbackslash:*/ 0
10569 );
10570 /* TODO: which FNM_xxx flags to use? */
10571 cond_code = (fnmatch(pattern, case_word, /*flags:*/ 0) != 0);
Francis Laniele7ca3a32023-12-22 22:02:42 +010010572 debug_printf_exec("cond_code=fnmatch(pattern:'%s',str:'%s'):%d\n",
Francis Laniel110b7692023-12-22 22:02:27 +010010573 pattern, case_word, cond_code);
10574 free(pattern);
10575 if (cond_code == 0) {
10576 /* match! we will execute this branch */
10577 free(case_word);
10578 case_word = NULL; /* make future "word)" stop */
10579 break;
10580 }
10581 argv++;
10582 }
10583 continue;
10584 }
10585 if (rword == RES_CASE_BODY) { /* inside of a case branch */
10586 debug_printf_exec("CASE_BODY cond_code:%d\n", cond_code);
10587 if (cond_code != 0)
10588 continue; /* not matched yet, skip this pipe */
10589 }
10590 if (rword == RES_ESAC) {
10591 debug_printf_exec("ESAC cond_code:%d\n", cond_code);
10592 if (case_word) {
10593 /* "case" did not match anything: still set $? (to 0) */
10594 G.last_exitcode = rcode = EXIT_SUCCESS;
10595 }
10596 }
10597#endif
10598 /* Just pressing <enter> in shell should check for jobs.
10599 * OTOH, in non-interactive shell this is useless
10600 * and only leads to extra job checks */
10601 if (pi->num_cmds == 0) {
Francis Laniel36836fc2023-12-22 22:02:28 +010010602#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +010010603 if (G_interactive_fd)
10604 goto check_jobs_and_continue;
Francis Laniel36836fc2023-12-22 22:02:28 +010010605#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +010010606 continue;
10607 }
10608
10609 /* After analyzing all keywords and conditions, we decided
10610 * to execute this pipe. NB: have to do checkjobs(NULL)
10611 * after run_pipe to collect any background children,
10612 * even if list execution is to be stopped. */
10613 debug_printf_exec(": run_pipe with %d members\n", pi->num_cmds);
Francis Laniel36836fc2023-12-22 22:02:28 +010010614#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +010010615#if ENABLE_HUSH_LOOPS
10616 G.flag_break_continue = 0;
10617#endif
Francis Laniel36836fc2023-12-22 22:02:28 +010010618#endif /* !__U_BOOT__ */
Francis Laniele7ca3a32023-12-22 22:02:42 +010010619#ifndef __U_BOOT__
10620 rcode = r = G.o_opt[OPT_O_NOEXEC] ? 0 : run_pipe(pi);
10621 /* NB: rcode is a smalluint, r is int */
10622#else /* __U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +010010623 rcode = r = run_pipe(pi); /* NB: rcode is a smalluint, r is int */
Francis Laniel3b66e572023-12-22 22:02:32 +010010624 if (r <= EXIT_RET_CODE) {
10625 int previous_rcode = G.last_exitcode;
10626 /*
10627 * This magic is to get the exit code given by the user.
10628 * Contrary to old shell code, we use + EXIT_RET_CODE as EXIT_RET_CODE
10629 * equals -2.
10630 */
10631 G.last_exitcode = -r + EXIT_RET_CODE;
Francis Laniel36836fc2023-12-22 22:02:28 +010010632
Francis Laniel3b66e572023-12-22 22:02:32 +010010633 /*
10634 * This case deals with the following:
10635 * => setenv inner 'echo entry inner; exit; echo inner done'
10636 * => setenv outer 'echo entry outer; run inner; echo outer done'
10637 * => run outer
10638 * So, if we are in inner, we need to break and not run the other
10639 * commands.
10640 * Otherwise, we just continue in outer.
10641 * As return code are propagated, we use the previous value to check if
10642 * exit was just called or was propagated.
10643 */
10644 if (previous_rcode != r) {
10645 /*
10646 * If run from run_command, run_command_flags will be set, so we check
10647 * this to know if we are in main input shell.
10648 */
10649 if (!G.run_command_flags)
10650 printf("exit not allowed from main input shell.\n");
10651
10652 break;
10653 }
10654 continue;
Francis Laniel36836fc2023-12-22 22:02:28 +010010655 }
Francis Laniel3b66e572023-12-22 22:02:32 +010010656#endif /* __U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +010010657 if (r != -1) {
10658 /* We ran a builtin, function, or group.
10659 * rcode is already known
10660 * and we don't need to wait for anything. */
10661 debug_printf_exec(": builtin/func exitcode %d\n", rcode);
10662 G.last_exitcode = rcode;
Francis Laniel36836fc2023-12-22 22:02:28 +010010663#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +010010664 check_and_run_traps();
Francis Laniel36836fc2023-12-22 22:02:28 +010010665#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +010010666#if ENABLE_HUSH_TRAP && ENABLE_HUSH_FUNCTIONS
10667 rcode = G.last_exitcode; /* "return" in trap can change it, read back */
10668#endif
Francis Laniel36836fc2023-12-22 22:02:28 +010010669#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +010010670#if ENABLE_HUSH_LOOPS
10671 /* Was it "break" or "continue"? */
10672 if (G.flag_break_continue) {
10673 smallint fbc = G.flag_break_continue;
10674 /* We might fall into outer *loop*,
10675 * don't want to break it too */
10676 if (loop_top) {
10677 G.depth_break_continue--;
10678 if (G.depth_break_continue == 0)
10679 G.flag_break_continue = 0;
10680 /* else: e.g. "continue 2" should *break* once, *then* continue */
10681 } /* else: "while... do... { we are here (innermost list is not a loop!) };...done" */
10682 if (G.depth_break_continue != 0 || fbc == BC_BREAK) {
10683 checkjobs(NULL, 0 /*(no pid to wait for)*/);
10684 break;
10685 }
10686 /* "continue": simulate end of loop */
10687 rword = RES_DONE;
10688 continue;
10689 }
10690#endif
10691 if (G_flag_return_in_progress == 1) {
10692 checkjobs(NULL, 0 /*(no pid to wait for)*/);
10693 break;
10694 }
Francis Laniel36836fc2023-12-22 22:02:28 +010010695
Francis Laniel110b7692023-12-22 22:02:27 +010010696 } else if (pi->followup == PIPE_BG) {
10697 /* What does bash do with attempts to background builtins? */
10698 /* even bash 3.2 doesn't do that well with nested bg:
10699 * try "{ { sleep 10; echo DEEP; } & echo HERE; } &".
10700 * I'm NOT treating inner &'s as jobs */
10701#if ENABLE_HUSH_JOB
10702 if (G.run_list_level == 1)
10703 insert_job_into_table(pi);
10704#endif
10705 /* Last command's pid goes to $! */
10706 G.last_bg_pid = pi->cmds[pi->num_cmds - 1].pid;
10707 G.last_bg_pid_exitcode = 0;
10708 debug_printf_exec(": cmd&: exitcode EXIT_SUCCESS\n");
10709/* Check pi->pi_inverted? "! sleep 1 & echo $?": bash says 1. dash and ash say 0 */
10710 rcode = EXIT_SUCCESS;
10711 goto check_traps;
10712 } else {
10713#if ENABLE_HUSH_JOB
10714 if (G.run_list_level == 1 && G_interactive_fd) {
10715 /* Waits for completion, then fg's main shell */
10716 rcode = checkjobs_and_fg_shell(pi);
10717 debug_printf_exec(": checkjobs_and_fg_shell exitcode %d\n", rcode);
10718 goto check_traps;
10719 }
10720#endif
10721 /* This one just waits for completion */
10722 rcode = checkjobs(pi, 0 /*(no pid to wait for)*/);
10723 debug_printf_exec(": checkjobs exitcode %d\n", rcode);
10724 check_traps:
10725 G.last_exitcode = rcode;
10726 check_and_run_traps();
10727#if ENABLE_HUSH_TRAP && ENABLE_HUSH_FUNCTIONS
10728 rcode = G.last_exitcode; /* "return" in trap can change it, read back */
10729#endif
10730 }
Francis Laniel36836fc2023-12-22 22:02:28 +010010731#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +010010732
Francis Laniel36836fc2023-12-22 22:02:28 +010010733#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +010010734 /* Handle "set -e" */
10735 if (rcode != 0 && G.o_opt[OPT_O_ERREXIT]) {
10736 debug_printf_exec("ERREXIT:1 errexit_depth:%d\n", G.errexit_depth);
10737 if (G.errexit_depth == 0)
10738 hush_exit(rcode);
10739 }
Francis Laniel36836fc2023-12-22 22:02:28 +010010740#else /* __U_BOOT__ */
10741 } /* if (r != -1) */
10742#endif /* __U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +010010743 G.errexit_depth = sv_errexit_depth;
10744
10745 /* Analyze how result affects subsequent commands */
10746#if ENABLE_HUSH_IF
Francis Laniele7ca3a32023-12-22 22:02:42 +010010747 if (rword == RES_IF || rword == RES_ELIF) {
10748 debug_printf_exec("cond_code=rcode:%d\n", rcode);
Francis Laniel110b7692023-12-22 22:02:27 +010010749 cond_code = rcode;
Francis Laniele7ca3a32023-12-22 22:02:42 +010010750 }
Francis Laniel110b7692023-12-22 22:02:27 +010010751#endif
Francis Laniel36836fc2023-12-22 22:02:28 +010010752#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +010010753 check_jobs_and_continue:
10754 checkjobs(NULL, 0 /*(no pid to wait for)*/);
Francis Laniel36836fc2023-12-22 22:02:28 +010010755#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +010010756 dont_check_jobs_but_continue: ;
10757#if ENABLE_HUSH_LOOPS
10758 /* Beware of "while false; true; do ..."! */
10759 if (pi->next
10760 && (pi->next->res_word == RES_DO || pi->next->res_word == RES_DONE)
10761 /* check for RES_DONE is needed for "while ...; do \n done" case */
10762 ) {
10763 if (rword == RES_WHILE) {
10764 if (rcode) {
10765 /* "while false; do...done" - exitcode 0 */
10766 G.last_exitcode = rcode = EXIT_SUCCESS;
10767 debug_printf_exec(": while expr is false: breaking (exitcode:EXIT_SUCCESS)\n");
10768 break;
10769 }
10770 }
10771 if (rword == RES_UNTIL) {
10772 if (!rcode) {
10773 debug_printf_exec(": until expr is true: breaking\n");
10774 break;
10775 }
10776 }
10777 }
10778#endif
10779 } /* for (pi) */
10780
10781#if ENABLE_HUSH_JOB
10782 G.run_list_level--;
10783#endif
10784#if ENABLE_HUSH_LOOPS
10785 if (loop_top)
10786 G.depth_of_loop--;
10787 free(for_list);
10788#endif
10789#if ENABLE_HUSH_CASE
10790 free(case_word);
10791#endif
Francis Laniel36836fc2023-12-22 22:02:28 +010010792#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +010010793 debug_leave();
Francis Laniele7ca3a32023-12-22 22:02:42 +010010794 debug_printf_exec("run_list lvl %d return %d\n", G.run_list_level, rcode);
Francis Laniel36836fc2023-12-22 22:02:28 +010010795#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +010010796 return rcode;
10797}
10798
10799/* Select which version we will use */
10800static int run_and_free_list(struct pipe *pi)
10801{
10802 int rcode = 0;
10803 debug_printf_exec("run_and_free_list entered\n");
Francis Laniel36836fc2023-12-22 22:02:28 +010010804#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +010010805 if (!G.o_opt[OPT_O_NOEXEC]) {
Francis Laniel36836fc2023-12-22 22:02:28 +010010806#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +010010807 debug_printf_exec(": run_list: 1st pipe with %d cmds\n", pi->num_cmds);
10808 rcode = run_list(pi);
Francis Laniel36836fc2023-12-22 22:02:28 +010010809#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +010010810 }
Francis Laniel36836fc2023-12-22 22:02:28 +010010811#endif /* !__U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +010010812 /* free_pipe_list has the side effect of clearing memory.
10813 * In the long run that function can be merged with run_list,
10814 * but doing that now would hobble the debugging effort. */
10815 free_pipe_list(pi);
10816 debug_printf_exec("run_and_free_list return %d\n", rcode);
10817 return rcode;
10818}
10819
Francis Laniel36836fc2023-12-22 22:02:28 +010010820#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +010010821static void install_sighandlers(unsigned mask)
10822{
10823 sighandler_t old_handler;
10824 unsigned sig = 0;
10825 while ((mask >>= 1) != 0) {
10826 sig++;
10827 if (!(mask & 1))
10828 continue;
10829 old_handler = install_sighandler(sig, pick_sighandler(sig));
10830 /* POSIX allows shell to re-enable SIGCHLD
10831 * even if it was SIG_IGN on entry.
10832 * Therefore we skip IGN check for it:
10833 */
10834 if (sig == SIGCHLD)
10835 continue;
10836 /* Interactive bash re-enables SIGHUP which is SIG_IGNed on entry.
10837 * Try:
10838 * trap '' hup; bash; echo RET # type "kill -hup $$", see SIGHUP having effect
10839 * trap '' hup; bash -c 'kill -hup $$; echo ALIVE' # here SIGHUP is SIG_IGNed
10840 */
10841 if (sig == SIGHUP && G_interactive_fd)
10842 continue;
10843 /* Unless one of the above signals, is it SIG_IGN? */
10844 if (old_handler == SIG_IGN) {
10845 /* oops... restore back to IGN, and record this fact */
10846 install_sighandler(sig, old_handler);
10847#if ENABLE_HUSH_TRAP
10848 if (!G_traps)
10849 G_traps = xzalloc(sizeof(G_traps[0]) * NSIG);
10850 free(G_traps[sig]);
10851 G_traps[sig] = xzalloc(1); /* == xstrdup(""); */
10852#endif
10853 }
10854 }
10855}
10856
10857/* Called a few times only (or even once if "sh -c") */
10858static void install_special_sighandlers(void)
10859{
10860 unsigned mask;
10861
10862 /* Which signals are shell-special? */
10863 mask = (1 << SIGQUIT) | (1 << SIGCHLD);
10864 if (G_interactive_fd) {
10865 mask |= SPECIAL_INTERACTIVE_SIGS;
10866 if (G_saved_tty_pgrp) /* we have ctty, job control sigs work */
10867 mask |= SPECIAL_JOBSTOP_SIGS;
10868 }
10869 /* Careful, do not re-install handlers we already installed */
10870 if (G.special_sig_mask != mask) {
10871 unsigned diff = mask & ~G.special_sig_mask;
10872 G.special_sig_mask = mask;
10873 install_sighandlers(diff);
10874 }
10875}
10876
10877#if ENABLE_HUSH_JOB
10878/* helper */
10879/* Set handlers to restore tty pgrp and exit */
10880static void install_fatal_sighandlers(void)
10881{
10882 unsigned mask;
10883
10884 /* We will restore tty pgrp on these signals */
10885 mask = 0
10886 /*+ (1 << SIGILL ) * HUSH_DEBUG*/
10887 /*+ (1 << SIGFPE ) * HUSH_DEBUG*/
10888 + (1 << SIGBUS ) * HUSH_DEBUG
10889 + (1 << SIGSEGV) * HUSH_DEBUG
10890 /*+ (1 << SIGTRAP) * HUSH_DEBUG*/
10891 + (1 << SIGABRT)
10892 /* bash 3.2 seems to handle these just like 'fatal' ones */
10893 + (1 << SIGPIPE)
10894 + (1 << SIGALRM)
10895 /* if we are interactive, SIGHUP, SIGTERM and SIGINT are special sigs.
10896 * if we aren't interactive... but in this case
10897 * we never want to restore pgrp on exit, and this fn is not called
10898 */
10899 /*+ (1 << SIGHUP )*/
10900 /*+ (1 << SIGTERM)*/
10901 /*+ (1 << SIGINT )*/
10902 ;
10903 G_fatal_sig_mask = mask;
10904
10905 install_sighandlers(mask);
10906}
10907#endif
10908
10909static int set_mode(int state, char mode, const char *o_opt)
10910{
10911 int idx;
10912 switch (mode) {
10913 case 'n':
Francis Laniele7ca3a32023-12-22 22:02:42 +010010914 /* set -n has no effect in interactive shell */
10915 /* Try: while set -n; do echo $-; done */
10916 if (!G_interactive_fd)
10917 G.o_opt[OPT_O_NOEXEC] = state;
Francis Laniel110b7692023-12-22 22:02:27 +010010918 break;
10919 case 'x':
10920 IF_HUSH_MODE_X(G_x_mode = state;)
10921 IF_HUSH_MODE_X(if (G.x_mode_fd <= 0) G.x_mode_fd = dup_CLOEXEC(2, 10);)
10922 break;
10923 case 'e':
10924 G.o_opt[OPT_O_ERREXIT] = state;
10925 break;
10926 case 'o':
10927 if (!o_opt) {
10928 /* "set -o" or "set +o" without parameter.
10929 * in bash, set -o produces this output:
10930 * pipefail off
10931 * and set +o:
10932 * set +o pipefail
10933 * We always use the second form.
10934 */
10935 const char *p = o_opt_strings;
10936 idx = 0;
10937 while (*p) {
10938 printf("set %co %s\n", (G.o_opt[idx] ? '-' : '+'), p);
10939 idx++;
10940 p += strlen(p) + 1;
10941 }
10942 break;
10943 }
10944 idx = index_in_strings(o_opt_strings, o_opt);
10945 if (idx >= 0) {
10946 G.o_opt[idx] = state;
10947 break;
10948 }
10949 /* fall through to error */
10950 default:
10951 return EXIT_FAILURE;
10952 }
10953 return EXIT_SUCCESS;
10954}
10955
10956int hush_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
10957int hush_main(int argc, char **argv)
10958{
10959 pid_t cached_getpid;
10960 enum {
10961 OPT_login = (1 << 0),
10962 };
10963 unsigned flags;
10964#if !BB_MMU
10965 unsigned builtin_argc = 0;
10966#endif
10967 char **e;
10968 struct variable *cur_var;
10969 struct variable *shell_ver;
10970
10971 INIT_G();
10972 if (EXIT_SUCCESS != 0) /* if EXIT_SUCCESS == 0, it is already done */
10973 G.last_exitcode = EXIT_SUCCESS;
Francis Laniele7ca3a32023-12-22 22:02:42 +010010974#if !BB_MMU
10975 /* "Big heredoc" support via "sh -< STRING" invocation.
10976 * Check it first (do not bother to run the usual init code,
10977 * it is not needed for this case).
10978 */
10979 if (argv[1]
10980 && argv[1][0] == '-' && argv[1][1] == '<' /*&& !argv[1][2]*/
10981 /*&& argv[2] && !argv[3] - we don't check some conditions */
10982 ) {
10983 full_write1_str(argv[2]);
10984 _exit(0);
10985 }
10986 G.argv0_for_re_execing = argv[0];
10987#endif
Francis Laniel110b7692023-12-22 22:02:27 +010010988#if ENABLE_HUSH_TRAP
10989# if ENABLE_HUSH_FUNCTIONS
10990 G.return_exitcode = -1;
10991# endif
10992 G.pre_trap_exitcode = -1;
10993#endif
10994
10995#if ENABLE_HUSH_FAST
10996 G.count_SIGCHLD++; /* ensure it is != G.handled_SIGCHLD */
10997#endif
Francis Laniel110b7692023-12-22 22:02:27 +010010998
10999 cached_getpid = getpid(); /* for tcsetpgrp() during init */
11000 G.root_pid = cached_getpid; /* for $PID (NOMMU can override via -$HEXPID:HEXPPID:...) */
Francis Laniele7ca3a32023-12-22 22:02:42 +010011001 G.root_ppid = getppid(); /* for $PPID (NOMMU can override) */
Francis Laniel110b7692023-12-22 22:02:27 +010011002
11003 /* Deal with HUSH_VERSION */
11004 debug_printf_env("unsetenv '%s'\n", "HUSH_VERSION");
11005 unsetenv("HUSH_VERSION"); /* in case it exists in initial env */
11006 shell_ver = xzalloc(sizeof(*shell_ver));
11007 shell_ver->flg_export = 1;
11008 shell_ver->flg_read_only = 1;
11009 /* Code which handles ${var<op>...} needs writable values for all variables,
11010 * therefore we xstrdup: */
11011 shell_ver->varstr = xstrdup(hush_version_str);
11012
11013 /* Create shell local variables from the values
11014 * currently living in the environment */
11015 G.top_var = shell_ver;
11016 cur_var = G.top_var;
11017 e = environ;
11018 if (e) while (*e) {
11019 char *value = strchr(*e, '=');
11020 if (value) { /* paranoia */
11021 cur_var->next = xzalloc(sizeof(*cur_var));
11022 cur_var = cur_var->next;
11023 cur_var->varstr = *e;
11024 cur_var->max_len = strlen(*e);
11025 cur_var->flg_export = 1;
11026 }
11027 e++;
11028 }
11029 /* (Re)insert HUSH_VERSION into env (AFTER we scanned the env!) */
11030 debug_printf_env("putenv '%s'\n", shell_ver->varstr);
11031 putenv(shell_ver->varstr);
11032
11033 /* Export PWD */
11034 set_pwd_var(SETFLAG_EXPORT);
11035
11036#if BASH_HOSTNAME_VAR
11037 /* Set (but not export) HOSTNAME unless already set */
11038 if (!get_local_var_value("HOSTNAME")) {
11039 struct utsname uts;
11040 uname(&uts);
11041 set_local_var_from_halves("HOSTNAME", uts.nodename);
11042 }
11043#endif
11044 /* IFS is not inherited from the parent environment */
11045 set_local_var_from_halves("IFS", defifs);
11046
11047 if (!get_local_var_value("PATH"))
11048 set_local_var_from_halves("PATH", bb_default_root_path);
11049
11050 /* PS1/PS2 are set later, if we determine that we are interactive */
11051
11052 /* bash also exports SHLVL and _,
11053 * and sets (but doesn't export) the following variables:
11054 * BASH=/bin/bash
11055 * BASH_VERSINFO=([0]="3" [1]="2" [2]="0" [3]="1" [4]="release" [5]="i386-pc-linux-gnu")
11056 * BASH_VERSION='3.2.0(1)-release'
11057 * HOSTTYPE=i386
11058 * MACHTYPE=i386-pc-linux-gnu
11059 * OSTYPE=linux-gnu
11060 * PPID=<NNNNN> - we also do it elsewhere
11061 * EUID=<NNNNN>
11062 * UID=<NNNNN>
11063 * GROUPS=()
11064 * LINES=<NNN>
11065 * COLUMNS=<NNN>
11066 * BASH_ARGC=()
11067 * BASH_ARGV=()
11068 * BASH_LINENO=()
11069 * BASH_SOURCE=()
11070 * DIRSTACK=()
11071 * PIPESTATUS=([0]="0")
11072 * HISTFILE=/<xxx>/.bash_history
11073 * HISTFILESIZE=500
11074 * HISTSIZE=500
11075 * MAILCHECK=60
11076 * PATH=/usr/gnu/bin:/usr/local/bin:/bin:/usr/bin:.
11077 * SHELL=/bin/bash
11078 * SHELLOPTS=braceexpand:emacs:hashall:histexpand:history:interactive-comments:monitor
11079 * TERM=dumb
11080 * OPTERR=1
11081 * OPTIND=1
11082 * PS4='+ '
11083 */
11084
11085#if NUM_SCRIPTS > 0
11086 if (argc < 0) {
11087 char *script = get_script_content(-argc - 1);
11088 G.global_argv = argv;
11089 G.global_argc = string_array_len(argv);
11090 //install_special_sighandlers(); - needed?
11091 parse_and_run_string(script);
11092 goto final_return;
11093 }
11094#endif
11095
11096 /* Initialize some more globals to non-zero values */
11097 die_func = restore_ttypgrp_and__exit;
11098
11099 /* Shell is non-interactive at first. We need to call
11100 * install_special_sighandlers() if we are going to execute "sh <script>",
11101 * "sh -c <cmds>" or login shell's /etc/profile and friends.
11102 * If we later decide that we are interactive, we run install_special_sighandlers()
11103 * in order to intercept (more) signals.
11104 */
11105
11106 /* Parse options */
11107 /* http://www.opengroup.org/onlinepubs/9699919799/utilities/sh.html */
11108 flags = (argv[0] && argv[0][0] == '-') ? OPT_login : 0;
11109 while (1) {
11110 int opt = getopt(argc, argv, "+" /* stop at 1st non-option */
11111 "cexinsl"
11112#if !BB_MMU
Francis Laniele7ca3a32023-12-22 22:02:42 +010011113 "$:R:V:"
11114# if ENABLE_HUSH_LINENO_VAR
11115 "L:"
11116# endif
Francis Laniel110b7692023-12-22 22:02:27 +010011117# if ENABLE_HUSH_FUNCTIONS
11118 "F:"
11119# endif
11120#endif
11121 );
11122 if (opt <= 0)
11123 break;
11124 switch (opt) {
11125 case 'c':
11126 /* Note: -c is not an option with param!
11127 * "hush -c -l SCRIPT" is valid. "hush -cSCRIPT" is not.
11128 */
11129 G.opt_c = 1;
11130 break;
11131 case 'i':
11132 /* Well, we cannot just declare interactiveness,
11133 * we have to have some stuff (ctty, etc) */
11134 /* G_interactive_fd++; */
Francis Laniele7ca3a32023-12-22 22:02:42 +010011135//There are a few cases where bash -i -c 'SCRIPT'
11136//has visible effect (differs from bash -c 'SCRIPT'):
11137//it ignores TERM:
11138// bash -i -c 'kill $$; echo ALIVE'
11139// ALIVE
11140//it resets SIG_IGNed HUP to SIG_DFL:
11141// trap '' hup; bash -i -c 'kill -hup $$; echo ALIVE'
11142// Hangup [the message is not printed by bash, it's the shell which started it]
11143//is talkative about jobs and exiting:
11144// bash -i -c 'sleep 1 & exit'
11145// [1] 16170
11146// exit
11147//includes $ENV file (only if run as "sh"):
11148// echo last >/tmp/ENV; ENV=/tmp/ENV sh -i -c 'echo HERE'
11149// last: cannot open /var/log/wtmp: No such file or directory
11150// HERE
11151//(under "bash", it's the opposite: it runs $BASH_ENV file only *without* -i).
11152//
11153//ash -i -c 'sleep 3; sleep 3', on ^C, drops into a prompt instead of exiting
11154//(this may be a bug, bash does not do this).
11155//(ash -i -c 'sleep 3' won't show this, the last command gets auto-"exec"ed)
11156//
11157//None of the above feel like useful features people would rely on.
Francis Laniel110b7692023-12-22 22:02:27 +010011158 break;
11159 case 's':
11160 G.opt_s = 1;
11161 break;
11162 case 'l':
11163 flags |= OPT_login;
11164 break;
11165#if !BB_MMU
Francis Laniel110b7692023-12-22 22:02:27 +010011166 case '$': {
11167 unsigned long long empty_trap_mask;
11168
11169 G.root_pid = bb_strtou(optarg, &optarg, 16);
11170 optarg++;
11171 G.root_ppid = bb_strtou(optarg, &optarg, 16);
11172 optarg++;
11173 G.last_bg_pid = bb_strtou(optarg, &optarg, 16);
11174 optarg++;
11175 G.last_exitcode = bb_strtou(optarg, &optarg, 16);
11176 optarg++;
11177 builtin_argc = bb_strtou(optarg, &optarg, 16);
11178 optarg++;
11179 empty_trap_mask = bb_strtoull(optarg, &optarg, 16);
11180 if (empty_trap_mask != 0) {
11181 IF_HUSH_TRAP(int sig;)
11182 install_special_sighandlers();
11183# if ENABLE_HUSH_TRAP
11184 G_traps = xzalloc(sizeof(G_traps[0]) * NSIG);
11185 for (sig = 1; sig < NSIG; sig++) {
11186 if (empty_trap_mask & (1LL << sig)) {
11187 G_traps[sig] = xzalloc(1); /* == xstrdup(""); */
11188 install_sighandler(sig, SIG_IGN);
11189 }
11190 }
11191# endif
11192 }
11193# if ENABLE_HUSH_LOOPS
11194 optarg++;
11195 G.depth_of_loop = bb_strtou(optarg, &optarg, 16);
11196# endif
11197 /* Suppress "killed by signal" message, -$ hack is used
11198 * for subshells: echo `sh -c 'kill -9 $$'`
11199 * should be silent.
11200 */
11201 IF_HUSH_JOB(G.run_list_level = 1;)
11202# if ENABLE_HUSH_FUNCTIONS
11203 /* nommu uses re-exec trick for "... | func | ...",
11204 * should allow "return".
11205 * This accidentally allows returns in subshells.
11206 */
11207 G_flag_return_in_progress = -1;
11208# endif
11209 break;
11210 }
11211 case 'R':
11212 case 'V':
11213 set_local_var(xstrdup(optarg), opt == 'R' ? SETFLAG_MAKE_RO : 0);
11214 break;
Francis Laniele7ca3a32023-12-22 22:02:42 +010011215# if ENABLE_HUSH_LINENO_VAR
11216 case 'L':
11217 G.parse_lineno = xatou(optarg);
11218 break;
11219# endif
Francis Laniel110b7692023-12-22 22:02:27 +010011220# if ENABLE_HUSH_FUNCTIONS
11221 case 'F': {
11222 struct function *funcp = new_function(optarg);
11223 /* funcp->name is already set to optarg */
11224 /* funcp->body is set to NULL. It's a special case. */
11225 funcp->body_as_string = argv[optind];
11226 optind++;
11227 break;
11228 }
11229# endif
11230#endif
11231 /*case '?': invalid option encountered (set_mode('?') will fail) */
11232 /*case 'n':*/
11233 /*case 'x':*/
11234 /*case 'e':*/
11235 default:
11236 if (set_mode(1, opt, NULL) == 0) /* no error */
11237 break;
11238 bb_show_usage();
11239 }
11240 } /* option parsing loop */
11241
11242 /* Skip options. Try "hush -l": $1 should not be "-l"! */
11243 G.global_argc = argc - (optind - 1);
11244 G.global_argv = argv + (optind - 1);
11245 G.global_argv[0] = argv[0];
11246
11247 /* If we are login shell... */
11248 if (flags & OPT_login) {
11249 const char *hp = NULL;
11250 HFILE *input;
11251
11252 debug_printf("sourcing /etc/profile\n");
11253 input = hfopen("/etc/profile");
11254 run_profile:
11255 if (input != NULL) {
11256 install_special_sighandlers();
11257 parse_and_run_file(input);
11258 hfclose(input);
11259 }
11260 /* bash: after sourcing /etc/profile,
11261 * tries to source (in the given order):
11262 * ~/.bash_profile, ~/.bash_login, ~/.profile,
11263 * stopping on first found. --noprofile turns this off.
11264 * bash also sources ~/.bash_logout on exit.
11265 * If called as sh, skips .bash_XXX files.
11266 */
11267 if (!hp) { /* unless we looped on the "goto" already */
11268 hp = get_local_var_value("HOME");
11269 if (hp && hp[0]) {
11270 debug_printf("sourcing ~/.profile\n");
11271 hp = concat_path_file(hp, ".profile");
11272 input = hfopen(hp);
11273 free((char*)hp);
11274 goto run_profile;
11275 }
11276 }
11277 }
11278
Francis Laniel36836fc2023-12-22 22:02:28 +010011279#ifndef __U_BOOT__
Francis Laniel110b7692023-12-22 22:02:27 +010011280 /* -c takes effect *after* -l */
11281 if (G.opt_c) {
11282 /* Possibilities:
11283 * sh ... -c 'script'
11284 * sh ... -c 'script' ARG0 [ARG1...]
11285 * On NOMMU, if builtin_argc != 0,
11286 * sh ... -c 'builtin' BARGV... "" ARG0 [ARG1...]
11287 * "" needs to be replaced with NULL
11288 * and BARGV vector fed to builtin function.
11289 * Note: the form without ARG0 never happens:
11290 * sh ... -c 'builtin' BARGV... ""
11291 */
11292 char *script;
11293
11294 install_special_sighandlers();
11295
11296 G.global_argc--;
11297 G.global_argv++;
11298#if !BB_MMU
11299 if (builtin_argc) {
11300 /* -c 'builtin' [BARGV...] "" ARG0 [ARG1...] */
11301 const struct built_in_command *x;
11302 x = find_builtin(G.global_argv[0]);
11303 if (x) { /* paranoia */
11304 argv = G.global_argv;
11305 G.global_argc -= builtin_argc + 1; /* skip [BARGV...] "" */
11306 G.global_argv += builtin_argc + 1;
11307 G.global_argv[-1] = NULL; /* replace "" */
11308 G.last_exitcode = x->b_function(argv);
11309 }
11310 goto final_return;
11311 }
11312#endif
11313
11314 script = G.global_argv[0];
11315 if (!script)
11316 bb_error_msg_and_die(bb_msg_requires_arg, "-c");
11317 if (!G.global_argv[1]) {
11318 /* -c 'script' (no params): prevent empty $0 */
11319 G.global_argv[0] = argv[0];
11320 } else { /* else -c 'script' ARG0 [ARG1...]: $0 is ARG0 */
11321 G.global_argc--;
11322 G.global_argv++;
11323 }
11324 parse_and_run_string(script);
11325 goto final_return;
11326 }
11327
11328 /* -s is: hush -s ARGV1 ARGV2 (no SCRIPT) */
11329 if (!G.opt_s && G.global_argv[1]) {
11330 HFILE *input;
11331 /*
11332 * "bash <script>" (which is never interactive (unless -i?))
11333 * sources $BASH_ENV here (without scanning $PATH).
11334 * If called as sh, does the same but with $ENV.
11335 * Also NB, per POSIX, $ENV should undergo parameter expansion.
11336 */
11337 G.global_argc--;
11338 G.global_argv++;
11339 debug_printf("running script '%s'\n", G.global_argv[0]);
11340 xfunc_error_retval = 127; /* for "hush /does/not/exist" case */
11341 input = hfopen(G.global_argv[0]);
11342 if (!input) {
11343 bb_simple_perror_msg_and_die(G.global_argv[0]);
11344 }
11345 xfunc_error_retval = 1;
11346 install_special_sighandlers();
11347 parse_and_run_file(input);
11348#if ENABLE_FEATURE_CLEAN_UP
11349 hfclose(input);
11350#endif
11351 goto final_return;
11352 }
11353 /* "implicit" -s: bare interactive hush shows 's' in $- */
11354 G.opt_s = 1;
11355
Francis Laniel36836fc2023-12-22 22:02:28 +010011356#endif /* __U_BOOT__ */
Francis Laniel110b7692023-12-22 22:02:27 +010011357 /* Up to here, shell was non-interactive. Now it may become one.
11358 * NB: don't forget to (re)run install_special_sighandlers() as needed.
11359 */
11360
11361 /* A shell is interactive if the '-i' flag was given,
11362 * or if all of the following conditions are met:
11363 * no -c command
11364 * no arguments remaining or the -s flag given
11365 * standard input is a terminal
11366 * standard output is a terminal
11367 * Refer to Posix.2, the description of the 'sh' utility.
11368 */
11369#if ENABLE_HUSH_JOB
11370 if (isatty(STDIN_FILENO) && isatty(STDOUT_FILENO)) {
11371 G_saved_tty_pgrp = tcgetpgrp(STDIN_FILENO);
11372 debug_printf("saved_tty_pgrp:%d\n", G_saved_tty_pgrp);
11373 if (G_saved_tty_pgrp < 0)
11374 G_saved_tty_pgrp = 0;
11375
11376 /* try to dup stdin to high fd#, >= 255 */
11377 G_interactive_fd = dup_CLOEXEC(STDIN_FILENO, 254);
11378 if (G_interactive_fd < 0) {
11379 /* try to dup to any fd */
11380 G_interactive_fd = dup(STDIN_FILENO);
11381 if (G_interactive_fd < 0) {
11382 /* give up */
11383 G_interactive_fd = 0;
11384 G_saved_tty_pgrp = 0;
11385 }
11386 }
11387 }
11388 debug_printf("interactive_fd:%d\n", G_interactive_fd);
11389 if (G_interactive_fd) {
11390 close_on_exec_on(G_interactive_fd);
11391
11392 if (G_saved_tty_pgrp) {
11393 /* If we were run as 'hush &', sleep until we are
11394 * in the foreground (tty pgrp == our pgrp).
11395 * If we get started under a job aware app (like bash),
11396 * make sure we are now in charge so we don't fight over
11397 * who gets the foreground */
11398 while (1) {
11399 pid_t shell_pgrp = getpgrp();
11400 G_saved_tty_pgrp = tcgetpgrp(G_interactive_fd);
11401 if (G_saved_tty_pgrp == shell_pgrp)
11402 break;
11403 /* send TTIN to ourself (should stop us) */
11404 kill(- shell_pgrp, SIGTTIN);
11405 }
11406 }
11407
11408 /* Install more signal handlers */
11409 install_special_sighandlers();
11410
11411 if (G_saved_tty_pgrp) {
11412 /* Set other signals to restore saved_tty_pgrp */
11413 install_fatal_sighandlers();
11414 /* Put ourselves in our own process group
11415 * (bash, too, does this only if ctty is available) */
11416 bb_setpgrp(); /* is the same as setpgid(our_pid, our_pid); */
11417 /* Grab control of the terminal */
11418 tcsetpgrp(G_interactive_fd, cached_getpid);
11419 }
11420 enable_restore_tty_pgrp_on_exit();
11421
11422# if ENABLE_FEATURE_EDITING
11423 G.line_input_state = new_line_input_t(FOR_SHELL);
Francis Laniele7ca3a32023-12-22 22:02:42 +010011424# if ENABLE_FEATURE_TAB_COMPLETION
11425 G.line_input_state->get_exe_name = hush_command_name;
11426# endif
11427# if EDITING_HAS_sh_get_var
11428 G.line_input_state->sh_get_var = get_local_var_value;
Francis Laniel110b7692023-12-22 22:02:27 +010011429# endif
11430# endif
11431# if ENABLE_HUSH_SAVEHISTORY && MAX_HISTORY > 0
11432 {
11433 const char *hp = get_local_var_value("HISTFILE");
11434 if (!hp) {
11435 hp = get_local_var_value("HOME");
11436 if (hp)
11437 hp = concat_path_file(hp, ".hush_history");
11438 } else {
11439 hp = xstrdup(hp);
11440 }
11441 if (hp) {
11442 G.line_input_state->hist_file = hp;
11443 //set_local_var(xasprintf("HISTFILE=%s", ...));
11444 }
11445# if ENABLE_FEATURE_SH_HISTFILESIZE
11446 hp = get_local_var_value("HISTFILESIZE");
11447 G.line_input_state->max_history = size_from_HISTFILESIZE(hp);
11448# endif
11449 }
11450# endif
11451 } else {
11452 install_special_sighandlers();
11453 }
11454#elif ENABLE_HUSH_INTERACTIVE
11455 /* No job control compiled in, only prompt/line editing */
11456 if (isatty(STDIN_FILENO) && isatty(STDOUT_FILENO)) {
11457 G_interactive_fd = dup_CLOEXEC(STDIN_FILENO, 254);
11458 if (G_interactive_fd < 0) {
11459 /* try to dup to any fd */
11460 G_interactive_fd = dup_CLOEXEC(STDIN_FILENO, -1);
11461 if (G_interactive_fd < 0)
11462 /* give up */
11463 G_interactive_fd = 0;
11464 }
11465 }
11466 if (G_interactive_fd) {
11467 close_on_exec_on(G_interactive_fd);
11468 }
11469 install_special_sighandlers();
11470#else
11471 /* We have interactiveness code disabled */
11472 install_special_sighandlers();
11473#endif
11474 /* bash:
11475 * if interactive but not a login shell, sources ~/.bashrc
11476 * (--norc turns this off, --rcfile <file> overrides)
11477 */
11478
11479 if (G_interactive_fd) {
11480#if ENABLE_HUSH_INTERACTIVE && ENABLE_FEATURE_EDITING_FANCY_PROMPT
11481 /* Set (but not export) PS1/2 unless already set */
11482 if (!get_local_var_value("PS1"))
11483 set_local_var_from_halves("PS1", "\\w \\$ ");
11484 if (!get_local_var_value("PS2"))
11485 set_local_var_from_halves("PS2", "> ");
11486#endif
11487 if (!ENABLE_FEATURE_SH_EXTRA_QUIET) {
11488 /* note: ash and hush share this string */
11489 printf("\n\n%s %s\n"
11490 IF_HUSH_HELP("Enter 'help' for a list of built-in commands.\n")
11491 "\n",
11492 bb_banner,
11493 "hush - the humble shell"
11494 );
11495 }
11496 }
11497
11498 parse_and_run_file(hfopen(NULL)); /* stdin */
11499
11500 final_return:
11501 hush_exit(G.last_exitcode);
11502}
11503
Francis Laniel110b7692023-12-22 22:02:27 +010011504/*
11505 * Built-ins
11506 */
11507static int FAST_FUNC builtin_true(char **argv UNUSED_PARAM)
11508{
11509 return 0;
11510}
11511
Francis Laniele7ca3a32023-12-22 22:02:42 +010011512static int FAST_FUNC builtin_false(char **argv UNUSED_PARAM)
11513{
11514 return 1;
11515}
11516
Francis Laniel110b7692023-12-22 22:02:27 +010011517#if ENABLE_HUSH_TEST || ENABLE_HUSH_ECHO || ENABLE_HUSH_PRINTF || ENABLE_HUSH_KILL
11518static NOINLINE int run_applet_main(char **argv, int (*applet_main_func)(int argc, char **argv))
11519{
11520 int argc = string_array_len(argv);
11521 return applet_main_func(argc, argv);
11522}
11523#endif
11524#if ENABLE_HUSH_TEST || BASH_TEST2
11525static int FAST_FUNC builtin_test(char **argv)
11526{
11527 return run_applet_main(argv, test_main);
11528}
11529#endif
11530#if ENABLE_HUSH_ECHO
11531static int FAST_FUNC builtin_echo(char **argv)
11532{
11533 return run_applet_main(argv, echo_main);
11534}
11535#endif
11536#if ENABLE_HUSH_PRINTF
11537static int FAST_FUNC builtin_printf(char **argv)
11538{
11539 return run_applet_main(argv, printf_main);
11540}
11541#endif
11542
11543#if ENABLE_HUSH_HELP
11544static int FAST_FUNC builtin_help(char **argv UNUSED_PARAM)
11545{
11546 const struct built_in_command *x;
11547
11548 printf(
11549 "Built-in commands:\n"
11550 "------------------\n");
11551 for (x = bltins1; x != &bltins1[ARRAY_SIZE(bltins1)]; x++) {
11552 if (x->b_descr)
11553 printf("%-10s%s\n", x->b_cmd, x->b_descr);
11554 }
11555 return EXIT_SUCCESS;
11556}
11557#endif
11558
11559#if MAX_HISTORY && ENABLE_FEATURE_EDITING
11560static int FAST_FUNC builtin_history(char **argv UNUSED_PARAM)
11561{
11562 show_history(G.line_input_state);
11563 return EXIT_SUCCESS;
11564}
11565#endif
11566
Francis Laniel110b7692023-12-22 22:02:27 +010011567static int FAST_FUNC builtin_cd(char **argv)
11568{
11569 const char *newdir;
11570
11571 argv = skip_dash_dash(argv);
11572 newdir = argv[0];
11573 if (newdir == NULL) {
11574 /* bash does nothing (exitcode 0) if HOME is ""; if it's unset,
11575 * bash says "bash: cd: HOME not set" and does nothing
11576 * (exitcode 1)
11577 */
11578 const char *home = get_local_var_value("HOME");
11579 newdir = home ? home : "/";
11580 }
11581 if (chdir(newdir)) {
11582 /* Mimic bash message exactly */
11583 bb_perror_msg("cd: %s", newdir);
11584 return EXIT_FAILURE;
11585 }
11586 /* Read current dir (get_cwd(1) is inside) and set PWD.
11587 * Note: do not enforce exporting. If PWD was unset or unexported,
11588 * set it again, but do not export. bash does the same.
11589 */
11590 set_pwd_var(/*flag:*/ 0);
11591 return EXIT_SUCCESS;
11592}
11593
11594static int FAST_FUNC builtin_pwd(char **argv UNUSED_PARAM)
11595{
11596 puts(get_cwd(0));
11597 return EXIT_SUCCESS;
11598}
11599
11600static int FAST_FUNC builtin_eval(char **argv)
11601{
11602 argv = skip_dash_dash(argv);
11603
11604 if (!argv[0])
11605 return EXIT_SUCCESS;
11606
11607 IF_HUSH_MODE_X(G.x_mode_depth++;)
11608 //bb_error_msg("%s: ++x_mode_depth=%d", __func__, G.x_mode_depth);
11609 if (!argv[1]) {
11610 /* bash:
11611 * eval "echo Hi; done" ("done" is syntax error):
11612 * "echo Hi" will not execute too.
11613 */
11614 parse_and_run_string(argv[0]);
11615 } else {
11616 /* "The eval utility shall construct a command by
11617 * concatenating arguments together, separating
11618 * each with a <space> character."
11619 */
11620 char *str, *p;
11621 unsigned len = 0;
11622 char **pp = argv;
11623 do
11624 len += strlen(*pp) + 1;
11625 while (*++pp);
11626 str = p = xmalloc(len);
11627 pp = argv;
11628 for (;;) {
11629 p = stpcpy(p, *pp);
11630 pp++;
11631 if (!*pp)
11632 break;
11633 *p++ = ' ';
11634 }
11635 parse_and_run_string(str);
11636 free(str);
11637 }
11638 IF_HUSH_MODE_X(G.x_mode_depth--;)
11639 //bb_error_msg("%s: --x_mode_depth=%d", __func__, G.x_mode_depth);
11640 return G.last_exitcode;
11641}
11642
11643static int FAST_FUNC builtin_exec(char **argv)
11644{
11645 argv = skip_dash_dash(argv);
11646 if (argv[0] == NULL)
11647 return EXIT_SUCCESS; /* bash does this */
11648
11649 /* Careful: we can end up here after [v]fork. Do not restore
11650 * tty pgrp then, only top-level shell process does that */
11651 if (G_saved_tty_pgrp && getpid() == G.root_pid)
11652 tcsetpgrp(G_interactive_fd, G_saved_tty_pgrp);
11653
11654 /* Saved-redirect fds, script fds and G_interactive_fd are still
11655 * open here. However, they are all CLOEXEC, and execv below
11656 * closes them. Try interactive "exec ls -l /proc/self/fd",
11657 * it should show no extra open fds in the "ls" process.
11658 * If we'd try to run builtins/NOEXECs, this would need improving.
11659 */
11660 //close_saved_fds_and_FILE_fds();
11661
11662 /* TODO: if exec fails, bash does NOT exit! We do.
11663 * We'll need to undo trap cleanup (it's inside execvp_or_die)
11664 * and tcsetpgrp, and this is inherently racy.
11665 */
11666 execvp_or_die(argv);
11667}
11668
11669static int FAST_FUNC builtin_exit(char **argv)
11670{
11671 debug_printf_exec("%s()\n", __func__);
11672
11673 /* interactive bash:
11674 * # trap "echo EEE" EXIT
11675 * # exit
11676 * exit
11677 * There are stopped jobs.
11678 * (if there are _stopped_ jobs, running ones don't count)
11679 * # exit
11680 * exit
11681 * EEE (then bash exits)
11682 *
11683 * TODO: we can use G.exiting = -1 as indicator "last cmd was exit"
11684 */
11685
11686 /* note: EXIT trap is run by hush_exit */
11687 argv = skip_dash_dash(argv);
11688 if (argv[0] == NULL) {
11689#if ENABLE_HUSH_TRAP
11690 if (G.pre_trap_exitcode >= 0) /* "exit" in trap uses $? from before the trap */
11691 hush_exit(G.pre_trap_exitcode);
11692#endif
11693 hush_exit(G.last_exitcode);
11694 }
11695 /* mimic bash: exit 123abc == exit 255 + error msg */
11696 xfunc_error_retval = 255;
11697 /* bash: exit -2 == exit 254, no error msg */
11698 hush_exit(xatoi(argv[0]) & 0xff);
11699}
11700
11701#if ENABLE_HUSH_TYPE
11702/* http://www.opengroup.org/onlinepubs/9699919799/utilities/type.html */
11703static int FAST_FUNC builtin_type(char **argv)
11704{
11705 int ret = EXIT_SUCCESS;
11706
11707 while (*++argv) {
11708 const char *type;
11709 char *path = NULL;
11710
11711 if (0) {} /* make conditional compile easier below */
11712 /*else if (find_alias(*argv))
11713 type = "an alias";*/
11714# if ENABLE_HUSH_FUNCTIONS
11715 else if (find_function(*argv))
11716 type = "a function";
11717# endif
11718 else if (find_builtin(*argv))
11719 type = "a shell builtin";
11720 else if ((path = find_in_path(*argv)) != NULL)
11721 type = path;
11722 else {
11723 bb_error_msg("type: %s: not found", *argv);
11724 ret = EXIT_FAILURE;
11725 continue;
11726 }
11727
11728 printf("%s is %s\n", *argv, type);
11729 free(path);
11730 }
11731
11732 return ret;
11733}
11734#endif
11735
11736#if ENABLE_HUSH_READ
11737/* Interruptibility of read builtin in bash
11738 * (tested on bash-4.2.8 by sending signals (not by ^C)):
11739 *
11740 * Empty trap makes read ignore corresponding signal, for any signal.
11741 *
11742 * SIGINT:
11743 * - terminates non-interactive shell;
11744 * - interrupts read in interactive shell;
11745 * if it has non-empty trap:
11746 * - executes trap and returns to command prompt in interactive shell;
11747 * - executes trap and returns to read in non-interactive shell;
11748 * SIGTERM:
11749 * - is ignored (does not interrupt) read in interactive shell;
11750 * - terminates non-interactive shell;
11751 * if it has non-empty trap:
11752 * - executes trap and returns to read;
11753 * SIGHUP:
11754 * - terminates shell (regardless of interactivity);
11755 * if it has non-empty trap:
11756 * - executes trap and returns to read;
11757 * SIGCHLD from children:
11758 * - does not interrupt read regardless of interactivity:
11759 * try: sleep 1 & read x; echo $x
11760 */
11761static int FAST_FUNC builtin_read(char **argv)
11762{
11763 const char *r;
11764 struct builtin_read_params params;
11765
11766 memset(&params, 0, sizeof(params));
11767
11768 /* "!": do not abort on errors.
11769 * Option string must start with "sr" to match BUILTIN_READ_xxx
11770 */
11771 params.read_flags = getopt32(argv,
11772# if BASH_READ_D
11773 IF_NOT_HUSH_BASH_COMPAT("^")
11774 "!srn:p:t:u:d:" IF_NOT_HUSH_BASH_COMPAT("\0" "-1"/*min 1 arg*/),
11775 &params.opt_n, &params.opt_p, &params.opt_t, &params.opt_u, &params.opt_d
11776# else
11777 IF_NOT_HUSH_BASH_COMPAT("^")
11778 "!srn:p:t:u:" IF_NOT_HUSH_BASH_COMPAT("\0" "-1"/*min 1 arg*/),
11779 &params.opt_n, &params.opt_p, &params.opt_t, &params.opt_u
11780# endif
11781//TODO: print "read: need variable name"
11782//for the case of !BASH "read" with no args (now it fails silently)
11783//(or maybe extend getopt32() to emit a message if "-1" fails)
11784 );
11785 if ((uint32_t)params.read_flags == (uint32_t)-1)
11786 return EXIT_FAILURE;
11787 argv += optind;
11788 params.argv = argv;
11789 params.setvar = set_local_var_from_halves;
11790 params.ifs = get_local_var_value("IFS"); /* can be NULL */
11791
11792 again:
11793 r = shell_builtin_read(&params);
11794
11795 if ((uintptr_t)r == 1 && errno == EINTR) {
11796 unsigned sig = check_and_run_traps();
11797 if (sig != SIGINT)
11798 goto again;
11799 }
11800
11801 if ((uintptr_t)r > 1) {
11802 bb_simple_error_msg(r);
11803 r = (char*)(uintptr_t)1;
11804 }
11805
11806 return (uintptr_t)r;
11807}
11808#endif
11809
11810#if ENABLE_HUSH_UMASK
11811static int FAST_FUNC builtin_umask(char **argv)
11812{
11813 int rc;
11814 mode_t mask;
11815
11816 rc = 1;
11817 mask = umask(0);
11818 argv = skip_dash_dash(argv);
11819 if (argv[0]) {
11820 mode_t old_mask = mask;
11821
11822 /* numeric umasks are taken as-is */
11823 /* symbolic umasks are inverted: "umask a=rx" calls umask(222) */
11824 if (!isdigit(argv[0][0]))
11825 mask ^= 0777;
11826 mask = bb_parse_mode(argv[0], mask);
11827 if (!isdigit(argv[0][0]))
11828 mask ^= 0777;
11829 if ((unsigned)mask > 0777) {
11830 mask = old_mask;
11831 /* bash messages:
11832 * bash: umask: 'q': invalid symbolic mode operator
11833 * bash: umask: 999: octal number out of range
11834 */
11835 bb_error_msg("%s: invalid mode '%s'", "umask", argv[0]);
11836 rc = 0;
11837 }
11838 } else {
11839 /* Mimic bash */
11840 printf("%04o\n", (unsigned) mask);
11841 /* fall through and restore mask which we set to 0 */
11842 }
11843 umask(mask);
11844
11845 return !rc; /* rc != 0 - success */
11846}
11847#endif
11848
Francis Laniele7ca3a32023-12-22 22:02:42 +010011849#if ENABLE_HUSH_EXPORT || ENABLE_HUSH_READONLY || ENABLE_HUSH_SET || ENABLE_HUSH_TRAP
Francis Laniel110b7692023-12-22 22:02:27 +010011850static void print_escaped(const char *s)
11851{
Francis Laniele7ca3a32023-12-22 22:02:42 +010011852//TODO? bash "set" does not quote variables which contain only alnums and "%+,-./:=@_~",
11853// (but "export" quotes all variables, even with only these chars).
11854// I think quoting strings with %+,=~ looks better
11855// (example: "set" printing var== instead of var='=' looks strange)
11856// IOW: do not quote "-./:@_": / is used in pathnames, : in PATH, -._ often in file names, @ in emails
11857
Francis Laniel110b7692023-12-22 22:02:27 +010011858 if (*s == '\'')
11859 goto squote;
11860 do {
11861 const char *p = strchrnul(s, '\'');
11862 /* print 'xxxx', possibly just '' */
11863 printf("'%.*s'", (int)(p - s), s);
11864 if (*p == '\0')
11865 break;
11866 s = p;
11867 squote:
11868 /* s points to '; print "'''...'''" */
11869 putchar('"');
11870 do putchar('\''); while (*++s == '\'');
11871 putchar('"');
11872 } while (*s);
11873}
11874#endif
11875
11876#if ENABLE_HUSH_EXPORT || ENABLE_HUSH_LOCAL || ENABLE_HUSH_READONLY
11877static int helper_export_local(char **argv, unsigned flags)
11878{
11879 do {
11880 char *name = *argv;
11881 const char *name_end = endofname(name);
11882
11883 if (*name_end == '\0') {
11884 struct variable *var, **vpp;
11885
Francis Laniele7ca3a32023-12-22 22:02:42 +010011886 vpp = get_ptr_to_local_var(name);
Francis Laniel110b7692023-12-22 22:02:27 +010011887 var = vpp ? *vpp : NULL;
11888
11889 if (flags & SETFLAG_UNEXPORT) {
11890 /* export -n NAME (without =VALUE) */
11891 if (var) {
11892 var->flg_export = 0;
11893 debug_printf_env("%s: unsetenv '%s'\n", __func__, name);
11894 unsetenv(name);
11895 } /* else: export -n NOT_EXISTING_VAR: no-op */
11896 continue;
11897 }
11898 if (flags & SETFLAG_EXPORT) {
11899 /* export NAME (without =VALUE) */
11900 if (var) {
11901 var->flg_export = 1;
11902 debug_printf_env("%s: putenv '%s'\n", __func__, var->varstr);
11903 putenv(var->varstr);
11904 continue;
11905 }
11906 }
11907 if (flags & SETFLAG_MAKE_RO) {
11908 /* readonly NAME (without =VALUE) */
11909 if (var) {
11910 var->flg_read_only = 1;
11911 continue;
11912 }
11913 }
11914# if ENABLE_HUSH_LOCAL
11915 /* Is this "local" bltin? */
11916 if (!(flags & (SETFLAG_EXPORT|SETFLAG_UNEXPORT|SETFLAG_MAKE_RO))) {
11917 unsigned lvl = flags >> SETFLAG_VARLVL_SHIFT;
11918 if (var && var->var_nest_level == lvl) {
11919 /* "local x=abc; ...; local x" - ignore second local decl */
11920 continue;
11921 }
11922 }
11923# endif
11924 /* Exporting non-existing variable.
11925 * bash does not put it in environment,
11926 * but remembers that it is exported,
11927 * and does put it in env when it is set later.
11928 * We just set it to "" and export.
11929 */
11930 /* Or, it's "local NAME" (without =VALUE).
11931 * bash sets the value to "".
11932 */
11933 /* Or, it's "readonly NAME" (without =VALUE).
11934 * bash remembers NAME and disallows its creation
11935 * in the future.
11936 */
11937 name = xasprintf("%s=", name);
11938 } else {
11939 if (*name_end != '=') {
11940 bb_error_msg("'%s': bad variable name", name);
11941 /* do not parse following argv[]s: */
11942 return 1;
11943 }
11944 /* (Un)exporting/making local NAME=VALUE */
11945 name = xstrdup(name);
11946 /* Testcase: export PS1='\w \$ ' */
11947 unbackslash(name);
11948 }
11949 debug_printf_env("%s: set_local_var('%s')\n", __func__, name);
11950 if (set_local_var(name, flags))
11951 return EXIT_FAILURE;
11952 } while (*++argv);
11953 return EXIT_SUCCESS;
11954}
11955#endif
11956
11957#if ENABLE_HUSH_EXPORT
11958static int FAST_FUNC builtin_export(char **argv)
11959{
11960 unsigned opt_unexport;
11961
11962# if ENABLE_HUSH_EXPORT_N
11963 /* "!": do not abort on errors */
11964 opt_unexport = getopt32(argv, "!n");
11965 if (opt_unexport == (uint32_t)-1)
11966 return EXIT_FAILURE;
11967 argv += optind;
11968# else
11969 opt_unexport = 0;
11970 argv++;
11971# endif
11972
11973 if (argv[0] == NULL) {
11974 char **e = environ;
11975 if (e) {
11976 while (*e) {
11977# if 0
11978 puts(*e++);
11979# else
11980 /* ash emits: export VAR='VAL'
11981 * bash: declare -x VAR="VAL"
11982 * we follow ash example */
11983 const char *s = *e++;
11984 const char *p = strchr(s, '=');
11985
11986 if (!p) /* wtf? take next variable */
11987 continue;
Francis Laniele7ca3a32023-12-22 22:02:42 +010011988 /* "export VAR=" */
11989 printf("%s %.*s", "export", (int)(p - s) + 1, s);
Francis Laniel110b7692023-12-22 22:02:27 +010011990 print_escaped(p + 1);
11991 putchar('\n');
11992# endif
11993 }
11994 /*fflush_all(); - done after each builtin anyway */
11995 }
11996 return EXIT_SUCCESS;
11997 }
11998
11999 return helper_export_local(argv, opt_unexport ? SETFLAG_UNEXPORT : SETFLAG_EXPORT);
12000}
12001#endif
12002
12003#if ENABLE_HUSH_LOCAL
12004static int FAST_FUNC builtin_local(char **argv)
12005{
12006 if (G.func_nest_level == 0) {
12007 bb_error_msg("%s: not in a function", argv[0]);
12008 return EXIT_FAILURE; /* bash compat */
12009 }
Francis Laniele7ca3a32023-12-22 22:02:42 +010012010//TODO? ash and bash support "local -" special form,
12011//which saves/restores $- around function call (including async returns, such as ^C)
12012//(IOW: it makes "set +/-..." effects local)
Francis Laniel110b7692023-12-22 22:02:27 +010012013 argv++;
12014 /* Since all builtins run in a nested variable level,
12015 * need to use level - 1 here. Or else the variable will be removed at once
12016 * after builtin returns.
12017 */
12018 return helper_export_local(argv, (G.var_nest_level - 1) << SETFLAG_VARLVL_SHIFT);
12019}
12020#endif
12021
12022#if ENABLE_HUSH_READONLY
12023static int FAST_FUNC builtin_readonly(char **argv)
12024{
12025 argv++;
12026 if (*argv == NULL) {
12027 /* bash: readonly [-p]: list all readonly VARs
12028 * (-p has no effect in bash)
12029 */
12030 struct variable *e;
12031 for (e = G.top_var; e; e = e->next) {
12032 if (e->flg_read_only) {
Francis Laniele7ca3a32023-12-22 22:02:42 +010012033 const char *s = e->varstr;
12034 const char *p = strchr(s, '=');
12035
12036 if (!p) /* wtf? take next variable */
12037 continue;
12038 /* "readonly VAR=" */
12039 printf("%s %.*s", "readonly", (int)(p - s) + 1, s);
12040 print_escaped(p + 1);
12041 putchar('\n');
Francis Laniel110b7692023-12-22 22:02:27 +010012042 }
12043 }
12044 return EXIT_SUCCESS;
12045 }
12046 return helper_export_local(argv, SETFLAG_MAKE_RO);
12047}
12048#endif
12049
12050#if ENABLE_HUSH_UNSET
12051/* http://www.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#unset */
12052static int FAST_FUNC builtin_unset(char **argv)
12053{
12054 int ret;
12055 unsigned opts;
12056
12057 /* "!": do not abort on errors */
12058 /* "+": stop at 1st non-option */
12059 opts = getopt32(argv, "!+vf");
12060 if (opts == (unsigned)-1)
12061 return EXIT_FAILURE;
12062 if (opts == 3) {
12063 bb_simple_error_msg("unset: -v and -f are exclusive");
12064 return EXIT_FAILURE;
12065 }
12066 argv += optind;
12067
12068 ret = EXIT_SUCCESS;
12069 while (*argv) {
12070 if (!(opts & 2)) { /* not -f */
12071 if (unset_local_var(*argv)) {
12072 /* unset <nonexistent_var> doesn't fail.
12073 * Error is when one tries to unset RO var.
12074 * Message was printed by unset_local_var. */
12075 ret = EXIT_FAILURE;
12076 }
12077 }
12078# if ENABLE_HUSH_FUNCTIONS
12079 else {
12080 unset_func(*argv);
12081 }
12082# endif
12083 argv++;
12084 }
12085 return ret;
12086}
12087#endif
12088
12089#if ENABLE_HUSH_SET
12090/* http://www.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#set
12091 * built-in 'set' handler
12092 * SUSv3 says:
12093 * set [-abCefhmnuvx] [-o option] [argument...]
12094 * set [+abCefhmnuvx] [+o option] [argument...]
12095 * set -- [argument...]
12096 * set -o
12097 * set +o
12098 * Implementations shall support the options in both their hyphen and
12099 * plus-sign forms. These options can also be specified as options to sh.
12100 * Examples:
12101 * Write out all variables and their values: set
12102 * Set $1, $2, and $3 and set "$#" to 3: set c a b
12103 * Turn on the -x and -v options: set -xv
12104 * Unset all positional parameters: set --
12105 * Set $1 to the value of x, even if it begins with '-' or '+': set -- "$x"
12106 * Set the positional parameters to the expansion of x, even if x expands
12107 * with a leading '-' or '+': set -- $x
12108 *
12109 * So far, we only support "set -- [argument...]" and some of the short names.
12110 */
12111static int FAST_FUNC builtin_set(char **argv)
12112{
12113 int n;
12114 char **pp, **g_argv;
12115 char *arg = *++argv;
12116
12117 if (arg == NULL) {
12118 struct variable *e;
Francis Laniele7ca3a32023-12-22 22:02:42 +010012119 for (e = G.top_var; e; e = e->next) {
12120 const char *s = e->varstr;
12121 const char *p = strchr(s, '=');
12122
12123 if (!p) /* wtf? take next variable */
12124 continue;
12125 /* var= */
12126 printf("%.*s", (int)(p - s) + 1, s);
12127 print_escaped(p + 1);
12128 putchar('\n');
12129 }
Francis Laniel110b7692023-12-22 22:02:27 +010012130 return EXIT_SUCCESS;
12131 }
12132
12133 do {
12134 if (strcmp(arg, "--") == 0) {
12135 ++argv;
12136 goto set_argv;
12137 }
12138 if (arg[0] != '+' && arg[0] != '-')
12139 break;
12140 for (n = 1; arg[n]; ++n) {
12141 if (set_mode((arg[0] == '-'), arg[n], argv[1])) {
12142 bb_error_msg("%s: %s: invalid option", "set", arg);
12143 return EXIT_FAILURE;
12144 }
12145 if (arg[n] == 'o' && argv[1])
12146 argv++;
12147 }
12148 } while ((arg = *++argv) != NULL);
12149 /* Now argv[0] is 1st argument */
12150
12151 if (arg == NULL)
12152 return EXIT_SUCCESS;
12153 set_argv:
12154
12155 /* NB: G.global_argv[0] ($0) is never freed/changed */
12156 g_argv = G.global_argv;
12157 if (G.global_args_malloced) {
12158 pp = g_argv;
12159 while (*++pp)
12160 free(*pp);
12161 g_argv[1] = NULL;
12162 } else {
12163 G.global_args_malloced = 1;
12164 pp = xzalloc(sizeof(pp[0]) * 2);
12165 pp[0] = g_argv[0]; /* retain $0 */
12166 g_argv = pp;
12167 }
12168 /* This realloc's G.global_argv */
12169 G.global_argv = pp = add_strings_to_strings(g_argv, argv, /*dup:*/ 1);
12170
12171 G.global_argc = 1 + string_array_len(pp + 1);
12172
12173 return EXIT_SUCCESS;
12174}
12175#endif
12176
12177static int FAST_FUNC builtin_shift(char **argv)
12178{
12179 int n = 1;
12180 argv = skip_dash_dash(argv);
12181 if (argv[0]) {
12182 n = bb_strtou(argv[0], NULL, 10);
12183 if (errno || n < 0) {
12184 /* shared string with ash.c */
12185 bb_error_msg("Illegal number: %s", argv[0]);
12186 /*
12187 * ash aborts in this case.
12188 * bash prints error message and set $? to 1.
12189 * Interestingly, for "shift 99999" bash does not
12190 * print error message, but does set $? to 1
12191 * (and does no shifting at all).
12192 */
12193 }
12194 }
12195 if (n >= 0 && n < G.global_argc) {
12196 if (G_global_args_malloced) {
12197 int m = 1;
12198 while (m <= n)
12199 free(G.global_argv[m++]);
12200 }
12201 G.global_argc -= n;
12202 memmove(&G.global_argv[1], &G.global_argv[n+1],
12203 G.global_argc * sizeof(G.global_argv[0]));
12204 return EXIT_SUCCESS;
12205 }
12206 return EXIT_FAILURE;
12207}
12208
12209#if ENABLE_HUSH_GETOPTS
12210static int FAST_FUNC builtin_getopts(char **argv)
12211{
12212/* http://pubs.opengroup.org/onlinepubs/9699919799/utilities/getopts.html
12213
12214TODO:
12215If a required argument is not found, and getopts is not silent,
12216a question mark (?) is placed in VAR, OPTARG is unset, and a
12217diagnostic message is printed. If getopts is silent, then a
12218colon (:) is placed in VAR and OPTARG is set to the option
12219character found.
12220
12221Test that VAR is a valid variable name?
12222
12223"Whenever the shell is invoked, OPTIND shall be initialized to 1"
12224*/
12225 char cbuf[2];
12226 const char *cp, *optstring, *var;
12227 int c, n, exitcode, my_opterr;
12228 unsigned count;
12229
12230 optstring = *++argv;
12231 if (!optstring || !(var = *++argv)) {
12232 bb_simple_error_msg("usage: getopts OPTSTRING VAR [ARGS]");
12233 return EXIT_FAILURE;
12234 }
12235
12236 if (argv[1])
12237 argv[0] = G.global_argv[0]; /* for error messages in getopt() */
12238 else
12239 argv = G.global_argv;
12240 cbuf[1] = '\0';
12241
12242 my_opterr = 0;
12243 if (optstring[0] != ':') {
12244 cp = get_local_var_value("OPTERR");
12245 /* 0 if "OPTERR=0", 1 otherwise */
12246 my_opterr = (!cp || NOT_LONE_CHAR(cp, '0'));
12247 }
12248
12249 /* getopts stops on first non-option. Add "+" to force that */
12250 /*if (optstring[0] != '+')*/ {
12251 char *s = alloca(strlen(optstring) + 2);
12252 sprintf(s, "+%s", optstring);
12253 optstring = s;
12254 }
12255
12256 /* Naively, now we should just
12257 * cp = get_local_var_value("OPTIND");
12258 * optind = cp ? atoi(cp) : 0;
12259 * optarg = NULL;
12260 * opterr = my_opterr;
12261 * c = getopt(string_array_len(argv), argv, optstring);
12262 * and be done? Not so fast...
12263 * Unlike normal getopt() usage in C programs, here
12264 * each successive call will (usually) have the same argv[] CONTENTS,
12265 * but not the ADDRESSES. Worse yet, it's possible that between
12266 * invocations of "getopts", there will be calls to shell builtins
12267 * which use getopt() internally. Example:
12268 * while getopts "abc" RES -a -bc -abc de; do
12269 * unset -ff func
12270 * done
12271 * This would not work correctly: getopt() call inside "unset"
12272 * modifies internal libc state which is tracking position in
12273 * multi-option strings ("-abc"). At best, it can skip options
12274 * or return the same option infinitely. With glibc implementation
12275 * of getopt(), it would use outright invalid pointers and return
12276 * garbage even _without_ "unset" mangling internal state.
12277 *
12278 * We resort to resetting getopt() state and calling it N times,
12279 * until we get Nth result (or failure).
12280 * (N == G.getopt_count is reset to 0 whenever OPTIND is [un]set).
12281 */
12282 GETOPT_RESET();
12283 count = 0;
12284 n = string_array_len(argv);
12285 do {
12286 optarg = NULL;
12287 opterr = (count < G.getopt_count) ? 0 : my_opterr;
12288 c = getopt(n, argv, optstring);
12289 if (c < 0)
12290 break;
12291 count++;
12292 } while (count <= G.getopt_count);
12293
12294 /* Set OPTIND. Prevent resetting of the magic counter! */
12295 set_local_var_from_halves("OPTIND", utoa(optind));
12296 G.getopt_count = count; /* "next time, give me N+1'th result" */
12297 GETOPT_RESET(); /* just in case */
12298
12299 /* Set OPTARG */
12300 /* Always set or unset, never left as-is, even on exit/error:
12301 * "If no option was found, or if the option that was found
12302 * does not have an option-argument, OPTARG shall be unset."
12303 */
12304 cp = optarg;
12305 if (c == '?') {
12306 /* If ":optstring" and unknown option is seen,
12307 * it is stored to OPTARG.
12308 */
12309 if (optstring[1] == ':') {
12310 cbuf[0] = optopt;
12311 cp = cbuf;
12312 }
12313 }
12314 if (cp)
12315 set_local_var_from_halves("OPTARG", cp);
12316 else
12317 unset_local_var("OPTARG");
12318
12319 /* Convert -1 to "?" */
12320 exitcode = EXIT_SUCCESS;
12321 if (c < 0) { /* -1: end of options */
12322 exitcode = EXIT_FAILURE;
12323 c = '?';
12324 }
12325
12326 /* Set VAR */
12327 cbuf[0] = c;
12328 set_local_var_from_halves(var, cbuf);
12329
12330 return exitcode;
12331}
12332#endif
12333
12334static int FAST_FUNC builtin_source(char **argv)
12335{
12336 char *arg_path, *filename;
12337 HFILE *input;
12338 save_arg_t sv;
12339 char *args_need_save;
12340#if ENABLE_HUSH_FUNCTIONS
12341 smallint sv_flg;
12342#endif
12343
12344 argv = skip_dash_dash(argv);
12345 filename = argv[0];
12346 if (!filename) {
12347 /* bash says: "bash: .: filename argument required" */
12348 return 2; /* bash compat */
12349 }
12350 arg_path = NULL;
12351 if (!strchr(filename, '/')) {
12352 arg_path = find_in_path(filename);
12353 if (arg_path)
12354 filename = arg_path;
12355 else if (!ENABLE_HUSH_BASH_SOURCE_CURDIR) {
12356 errno = ENOENT;
12357 bb_simple_perror_msg(filename);
12358 return EXIT_FAILURE;
12359 }
12360 }
12361 input = hfopen(filename);
12362 free(arg_path);
12363 if (!input) {
12364 bb_perror_msg("%s", filename);
12365 /* POSIX: non-interactive shell should abort here,
12366 * not merely fail. So far no one complained :)
12367 */
12368 return EXIT_FAILURE;
12369 }
12370
12371#if ENABLE_HUSH_FUNCTIONS
12372 sv_flg = G_flag_return_in_progress;
12373 /* "we are inside sourced file, ok to use return" */
12374 G_flag_return_in_progress = -1;
12375#endif
12376 args_need_save = argv[1]; /* used as a boolean variable */
12377 if (args_need_save)
12378 save_and_replace_G_args(&sv, argv);
12379
12380 /* "false; . ./empty_line; echo Zero:$?" should print 0 */
12381 G.last_exitcode = 0;
12382 parse_and_run_file(input);
12383 hfclose(input);
12384
12385 if (args_need_save) /* can't use argv[1] instead: "shift" can mangle it */
12386 restore_G_args(&sv, argv);
12387#if ENABLE_HUSH_FUNCTIONS
12388 G_flag_return_in_progress = sv_flg;
12389#endif
12390
12391 return G.last_exitcode;
12392}
12393
12394#if ENABLE_HUSH_TRAP
12395static int FAST_FUNC builtin_trap(char **argv)
12396{
12397 int sig;
12398 char *new_cmd;
12399
12400 if (!G_traps)
12401 G_traps = xzalloc(sizeof(G_traps[0]) * NSIG);
12402
12403 argv++;
12404 if (!*argv) {
12405 int i;
12406 /* No args: print all trapped */
12407 for (i = 0; i < NSIG; ++i) {
12408 if (G_traps[i]) {
12409 printf("trap -- ");
12410 print_escaped(G_traps[i]);
12411 /* note: bash adds "SIG", but only if invoked
12412 * as "bash". If called as "sh", or if set -o posix,
12413 * then it prints short signal names.
12414 * We are printing short names: */
12415 printf(" %s\n", get_signame(i));
12416 }
12417 }
12418 /*fflush_all(); - done after each builtin anyway */
12419 return EXIT_SUCCESS;
12420 }
12421
12422 new_cmd = NULL;
12423 /* If first arg is a number: reset all specified signals */
12424 sig = bb_strtou(*argv, NULL, 10);
12425 if (errno == 0) {
12426 int ret;
12427 process_sig_list:
12428 ret = EXIT_SUCCESS;
12429 while (*argv) {
12430 sighandler_t handler;
12431
12432 sig = get_signum(*argv++);
12433 if (sig < 0) {
12434 ret = EXIT_FAILURE;
12435 /* Mimic bash message exactly */
12436 bb_error_msg("trap: %s: invalid signal specification", argv[-1]);
12437 continue;
12438 }
12439
12440 free(G_traps[sig]);
12441 G_traps[sig] = xstrdup(new_cmd);
12442
12443 debug_printf("trap: setting SIG%s (%i) to '%s'\n",
12444 get_signame(sig), sig, G_traps[sig]);
12445
12446 /* There is no signal for 0 (EXIT) */
12447 if (sig == 0)
12448 continue;
12449
12450 if (new_cmd)
12451 handler = (new_cmd[0] ? record_pending_signo : SIG_IGN);
12452 else
12453 /* We are removing trap handler */
12454 handler = pick_sighandler(sig);
12455 install_sighandler(sig, handler);
12456 }
12457 return ret;
12458 }
12459
12460 if (!argv[1]) { /* no second arg */
12461 bb_simple_error_msg("trap: invalid arguments");
12462 return EXIT_FAILURE;
12463 }
12464
12465 /* First arg is "-": reset all specified to default */
12466 /* First arg is "--": skip it, the rest is "handler SIGs..." */
12467 /* Everything else: set arg as signal handler
12468 * (includes "" case, which ignores signal) */
12469 if (argv[0][0] == '-') {
12470 if (argv[0][1] == '\0') { /* "-" */
12471 /* new_cmd remains NULL: "reset these sigs" */
12472 goto reset_traps;
12473 }
12474 if (argv[0][1] == '-' && argv[0][2] == '\0') { /* "--" */
12475 argv++;
12476 }
12477 /* else: "-something", no special meaning */
12478 }
12479 new_cmd = *argv;
12480 reset_traps:
12481 argv++;
12482 goto process_sig_list;
12483}
12484#endif
12485
12486#if ENABLE_HUSH_JOB
12487static struct pipe *parse_jobspec(const char *str)
12488{
12489 struct pipe *pi;
12490 unsigned jobnum;
12491
12492 if (sscanf(str, "%%%u", &jobnum) != 1) {
12493 if (str[0] != '%'
12494 || (str[1] != '%' && str[1] != '+' && str[1] != '\0')
12495 ) {
12496 bb_error_msg("bad argument '%s'", str);
12497 return NULL;
12498 }
12499 /* It is "%%", "%+" or "%" - current job */
12500 jobnum = G.last_jobid;
12501 if (jobnum == 0) {
12502 bb_simple_error_msg("no current job");
12503 return NULL;
12504 }
12505 }
12506 for (pi = G.job_list; pi; pi = pi->next) {
12507 if (pi->jobid == jobnum) {
12508 return pi;
12509 }
12510 }
12511 bb_error_msg("%u: no such job", jobnum);
12512 return NULL;
12513}
12514
12515static int FAST_FUNC builtin_jobs(char **argv UNUSED_PARAM)
12516{
12517 struct pipe *job;
12518 const char *status_string;
12519
12520 checkjobs(NULL, 0 /*(no pid to wait for)*/);
12521 for (job = G.job_list; job; job = job->next) {
12522 if (job->alive_cmds == job->stopped_cmds)
12523 status_string = "Stopped";
12524 else
12525 status_string = "Running";
12526
12527 printf(JOB_STATUS_FORMAT, job->jobid, status_string, job->cmdtext);
12528 }
12529
12530 clean_up_last_dead_job();
12531
12532 return EXIT_SUCCESS;
12533}
12534
12535/* built-in 'fg' and 'bg' handler */
12536static int FAST_FUNC builtin_fg_bg(char **argv)
12537{
12538 int i;
12539 struct pipe *pi;
12540
12541 if (!G_interactive_fd)
12542 return EXIT_FAILURE;
12543
12544 /* If they gave us no args, assume they want the last backgrounded task */
12545 if (!argv[1]) {
12546 for (pi = G.job_list; pi; pi = pi->next) {
12547 if (pi->jobid == G.last_jobid) {
12548 goto found;
12549 }
12550 }
12551 bb_error_msg("%s: no current job", argv[0]);
12552 return EXIT_FAILURE;
12553 }
12554
12555 pi = parse_jobspec(argv[1]);
12556 if (!pi)
12557 return EXIT_FAILURE;
12558 found:
12559 /* TODO: bash prints a string representation
12560 * of job being foregrounded (like "sleep 1 | cat") */
12561 if (argv[0][0] == 'f' && G_saved_tty_pgrp) {
Francis Laniele7ca3a32023-12-22 22:02:42 +010012562 /* Put the job into the foreground. */
Francis Laniel110b7692023-12-22 22:02:27 +010012563 tcsetpgrp(G_interactive_fd, pi->pgrp);
12564 }
12565
12566 /* Restart the processes in the job */
12567 debug_printf_jobs("reviving %d procs, pgrp %d\n", pi->num_cmds, pi->pgrp);
12568 for (i = 0; i < pi->num_cmds; i++) {
12569 debug_printf_jobs("reviving pid %d\n", pi->cmds[i].pid);
12570 }
12571 pi->stopped_cmds = 0;
12572
12573 i = kill(- pi->pgrp, SIGCONT);
12574 if (i < 0) {
12575 if (errno == ESRCH) {
12576 delete_finished_job(pi);
12577 return EXIT_SUCCESS;
12578 }
12579 bb_simple_perror_msg("kill (SIGCONT)");
12580 }
12581
12582 if (argv[0][0] == 'f') {
12583 remove_job_from_table(pi); /* FG job shouldn't be in job table */
12584 return checkjobs_and_fg_shell(pi);
12585 }
12586 return EXIT_SUCCESS;
12587}
12588#endif
12589
12590#if ENABLE_HUSH_KILL
12591static int FAST_FUNC builtin_kill(char **argv)
12592{
12593 int ret = 0;
12594
12595# if ENABLE_HUSH_JOB
12596 if (argv[1] && strcmp(argv[1], "-l") != 0) {
12597 int i = 1;
12598
12599 do {
12600 struct pipe *pi;
12601 char *dst;
12602 int j, n;
12603
12604 if (argv[i][0] != '%')
12605 continue;
12606 /*
12607 * "kill %N" - job kill
12608 * Converting to pgrp / pid kill
12609 */
12610 pi = parse_jobspec(argv[i]);
12611 if (!pi) {
12612 /* Eat bad jobspec */
12613 j = i;
12614 do {
12615 j++;
12616 argv[j - 1] = argv[j];
12617 } while (argv[j]);
12618 ret = 1;
12619 i--;
12620 continue;
12621 }
12622 /*
12623 * In jobs started under job control, we signal
12624 * entire process group by kill -PGRP_ID.
12625 * This happens, f.e., in interactive shell.
12626 *
12627 * Otherwise, we signal each child via
12628 * kill PID1 PID2 PID3.
12629 * Testcases:
12630 * sh -c 'sleep 1|sleep 1 & kill %1'
12631 * sh -c 'true|sleep 2 & sleep 1; kill %1'
12632 * sh -c 'true|sleep 1 & sleep 2; kill %1'
12633 */
12634 n = G_interactive_fd ? 1 : pi->num_cmds;
12635 dst = alloca(n * sizeof(int)*4);
12636 argv[i] = dst;
12637 if (G_interactive_fd)
12638 dst += sprintf(dst, " -%u", (int)pi->pgrp);
12639 else for (j = 0; j < n; j++) {
12640 struct command *cmd = &pi->cmds[j];
12641 /* Skip exited members of the job */
12642 if (cmd->pid == 0)
12643 continue;
12644 /*
12645 * kill_main has matching code to expect
12646 * leading space. Needed to not confuse
12647 * negative pids with "kill -SIGNAL_NO" syntax
12648 */
12649 dst += sprintf(dst, " %u", (int)cmd->pid);
12650 }
12651 *dst = '\0';
12652 } while (argv[++i]);
12653 }
12654# endif
12655
12656 if (argv[1] || ret == 0) {
12657 ret = run_applet_main(argv, kill_main);
12658 }
12659 /* else: ret = 1, "kill %bad_jobspec" case */
12660 return ret;
12661}
12662#endif
12663
12664#if ENABLE_HUSH_WAIT
12665/* http://www.opengroup.org/onlinepubs/9699919799/utilities/wait.html */
12666# if !ENABLE_HUSH_JOB
12667# define wait_for_child_or_signal(pipe,pid) wait_for_child_or_signal(pid)
12668# endif
12669static int wait_for_child_or_signal(struct pipe *waitfor_pipe, pid_t waitfor_pid)
12670{
12671 int ret = 0;
12672 for (;;) {
12673 int sig;
12674 sigset_t oldset;
12675
12676 if (!sigisemptyset(&G.pending_set))
12677 goto check_sig;
12678
12679 /* waitpid is not interruptible by SA_RESTARTed
12680 * signals which we use. Thus, this ugly dance:
12681 */
12682
12683 /* Make sure possible SIGCHLD is stored in kernel's
12684 * pending signal mask before we call waitpid.
12685 * Or else we may race with SIGCHLD, lose it,
12686 * and get stuck in sigsuspend...
12687 */
12688 sigfillset(&oldset); /* block all signals, remember old set */
12689 sigprocmask2(SIG_SETMASK, &oldset);
12690
12691 if (!sigisemptyset(&G.pending_set)) {
12692 /* Crap! we raced with some signal! */
12693 goto restore;
12694 }
12695
12696 /*errno = 0; - checkjobs does this */
12697/* Can't pass waitfor_pipe into checkjobs(): it won't be interruptible */
12698 ret = checkjobs(NULL, waitfor_pid); /* waitpid(WNOHANG) inside */
12699 debug_printf_exec("checkjobs:%d\n", ret);
12700# if ENABLE_HUSH_JOB
12701 if (waitfor_pipe) {
12702 int rcode = job_exited_or_stopped(waitfor_pipe);
12703 debug_printf_exec("job_exited_or_stopped:%d\n", rcode);
12704 if (rcode >= 0) {
12705 ret = rcode;
12706 sigprocmask(SIG_SETMASK, &oldset, NULL);
12707 break;
12708 }
12709 }
12710# endif
12711 /* if ECHILD, there are no children (ret is -1 or 0) */
12712 /* if ret == 0, no children changed state */
12713 /* if ret != 0, it's exitcode+1 of exited waitfor_pid child */
12714 if (errno == ECHILD || ret) {
12715 ret--;
12716 if (ret < 0) /* if ECHILD, may need to fix "ret" */
12717 ret = 0;
12718# if ENABLE_HUSH_BASH_COMPAT
12719 if (waitfor_pid == -1 && errno == ECHILD) {
12720 /* exitcode of "wait -n" with no children is 127, not 0 */
12721 ret = 127;
12722 }
12723# endif
12724 sigprocmask(SIG_SETMASK, &oldset, NULL);
12725 break;
12726 }
12727 /* Wait for SIGCHLD or any other signal */
12728 /* It is vitally important for sigsuspend that SIGCHLD has non-DFL handler! */
12729 /* Note: sigsuspend invokes signal handler */
12730 sigsuspend(&oldset);
12731 /* ^^^ add "sigdelset(&oldset, SIGCHLD)" before sigsuspend
12732 * to make sure SIGCHLD is not masked off?
12733 * It was reported that this:
12734 * fn() { : | return; }
12735 * shopt -s lastpipe
12736 * fn
12737 * exec hush SCRIPT
12738 * under bash 4.4.23 runs SCRIPT with SIGCHLD masked,
12739 * making "wait" commands in SCRIPT block forever.
12740 */
12741 restore:
12742 sigprocmask(SIG_SETMASK, &oldset, NULL);
12743 check_sig:
12744 /* So, did we get a signal? */
12745 sig = check_and_run_traps();
12746 if (sig /*&& sig != SIGCHLD - always true */) {
12747 /* Do this for any (non-ignored) signal, not only for ^C */
12748 ret = 128 | sig;
12749 break;
12750 }
12751 /* SIGCHLD, or no signal, or ignored one, such as SIGQUIT. Repeat */
12752 }
12753 return ret;
12754}
12755
12756static int FAST_FUNC builtin_wait(char **argv)
12757{
12758 int ret;
12759 int status;
12760
12761 argv = skip_dash_dash(argv);
12762# if ENABLE_HUSH_BASH_COMPAT
12763 if (argv[0] && strcmp(argv[0], "-n") == 0) {
12764 /* wait -n */
12765 /* (bash accepts "wait -n PID" too and ignores PID) */
12766 G.dead_job_exitcode = -1;
12767 return wait_for_child_or_signal(NULL, -1 /*no job, wait for one job*/);
12768 }
12769# endif
12770 if (argv[0] == NULL) {
12771 /* Don't care about wait results */
12772 /* Note 1: must wait until there are no more children */
12773 /* Note 2: must be interruptible */
12774 /* Examples:
12775 * $ sleep 3 & sleep 6 & wait
12776 * [1] 30934 sleep 3
12777 * [2] 30935 sleep 6
12778 * [1] Done sleep 3
12779 * [2] Done sleep 6
12780 * $ sleep 3 & sleep 6 & wait
12781 * [1] 30936 sleep 3
12782 * [2] 30937 sleep 6
12783 * [1] Done sleep 3
12784 * ^C <-- after ~4 sec from keyboard
12785 * $
12786 */
12787 return wait_for_child_or_signal(NULL, 0 /*no job and no pid to wait for*/);
12788 }
12789
12790 do {
12791 pid_t pid = bb_strtou(*argv, NULL, 10);
12792 if (errno || pid <= 0) {
12793# if ENABLE_HUSH_JOB
12794 if (argv[0][0] == '%') {
12795 struct pipe *wait_pipe;
12796 ret = 127; /* bash compat for bad jobspecs */
12797 wait_pipe = parse_jobspec(*argv);
12798 if (wait_pipe) {
12799 ret = job_exited_or_stopped(wait_pipe);
12800 if (ret < 0) {
12801 ret = wait_for_child_or_signal(wait_pipe, 0);
12802 } else {
12803 /* waiting on "last dead job" removes it */
12804 clean_up_last_dead_job();
12805 }
12806 }
12807 /* else: parse_jobspec() already emitted error msg */
12808 continue;
12809 }
12810# endif
12811 /* mimic bash message */
12812 bb_error_msg("wait: '%s': not a pid or valid job spec", *argv);
12813 ret = EXIT_FAILURE;
12814 continue; /* bash checks all argv[] */
12815 }
12816
12817 /* Do we have such child? */
12818 ret = waitpid(pid, &status, WNOHANG);
12819 if (ret < 0) {
12820 /* No */
12821 ret = 127;
12822 if (errno == ECHILD) {
12823 if (pid == G.last_bg_pid) {
12824 /* "wait $!" but last bg task has already exited. Try:
12825 * (sleep 1; exit 3) & sleep 2; echo $?; wait $!; echo $?
12826 * In bash it prints exitcode 0, then 3.
12827 * In dash, it is 127.
12828 */
12829 ret = G.last_bg_pid_exitcode;
12830 } else {
12831 /* Example: "wait 1". mimic bash message */
12832 bb_error_msg("wait: pid %u is not a child of this shell", (unsigned)pid);
12833 }
12834 } else {
12835 /* ??? */
12836 bb_perror_msg("wait %s", *argv);
12837 }
12838 continue; /* bash checks all argv[] */
12839 }
12840 if (ret == 0) {
12841 /* Yes, and it still runs */
12842 ret = wait_for_child_or_signal(NULL, pid);
12843 } else {
12844 /* Yes, and it just exited */
12845 process_wait_result(NULL, pid, status);
12846 ret = WEXITSTATUS(status);
12847 if (WIFSIGNALED(status))
12848 ret = 128 | WTERMSIG(status);
12849 }
12850 } while (*++argv);
12851
12852 return ret;
12853}
12854#endif
12855
12856#if ENABLE_HUSH_LOOPS || ENABLE_HUSH_FUNCTIONS
12857static unsigned parse_numeric_argv1(char **argv, unsigned def, unsigned def_min)
12858{
12859 if (argv[1]) {
12860 def = bb_strtou(argv[1], NULL, 10);
12861 if (errno || def < def_min || argv[2]) {
12862 bb_error_msg("%s: bad arguments", argv[0]);
12863 def = UINT_MAX;
12864 }
12865 }
12866 return def;
12867}
12868#endif
12869
12870#if ENABLE_HUSH_LOOPS
12871static int FAST_FUNC builtin_break(char **argv)
12872{
12873 unsigned depth;
12874 if (G.depth_of_loop == 0) {
12875 bb_error_msg("%s: only meaningful in a loop", argv[0]);
12876 /* if we came from builtin_continue(), need to undo "= 1" */
12877 G.flag_break_continue = 0;
12878 return EXIT_SUCCESS; /* bash compat */
12879 }
12880 G.flag_break_continue++; /* BC_BREAK = 1, or BC_CONTINUE = 2 */
12881
12882 G.depth_break_continue = depth = parse_numeric_argv1(argv, 1, 1);
12883 if (depth == UINT_MAX)
12884 G.flag_break_continue = BC_BREAK;
12885 if (G.depth_of_loop < depth)
12886 G.depth_break_continue = G.depth_of_loop;
12887
12888 return EXIT_SUCCESS;
12889}
12890
12891static int FAST_FUNC builtin_continue(char **argv)
12892{
12893 G.flag_break_continue = 1; /* BC_CONTINUE = 2 = 1+1 */
12894 return builtin_break(argv);
12895}
12896#endif
12897
12898#if ENABLE_HUSH_FUNCTIONS
12899static int FAST_FUNC builtin_return(char **argv)
12900{
12901 int rc;
12902
12903 if (G_flag_return_in_progress != -1) {
12904 bb_error_msg("%s: not in a function or sourced script", argv[0]);
12905 return EXIT_FAILURE; /* bash compat */
12906 }
12907
12908 G_flag_return_in_progress = 1;
12909
12910 /* bash:
12911 * out of range: wraps around at 256, does not error out
12912 * non-numeric param:
12913 * f() { false; return qwe; }; f; echo $?
12914 * bash: return: qwe: numeric argument required <== we do this
12915 * 255 <== we also do this
12916 */
12917 rc = parse_numeric_argv1(argv, G.last_exitcode, 0);
12918# if ENABLE_HUSH_TRAP
12919 if (argv[1]) { /* "return ARG" inside a running trap sets $? */
12920 debug_printf_exec("G.return_exitcode=%d\n", rc);
12921 G.return_exitcode = rc;
12922 }
12923# endif
12924 return rc;
12925}
12926#endif
12927
12928#if ENABLE_HUSH_TIMES
12929static int FAST_FUNC builtin_times(char **argv UNUSED_PARAM)
12930{
12931 static const uint8_t times_tbl[] ALIGN1 = {
12932 ' ', offsetof(struct tms, tms_utime),
12933 '\n', offsetof(struct tms, tms_stime),
12934 ' ', offsetof(struct tms, tms_cutime),
12935 '\n', offsetof(struct tms, tms_cstime),
12936 0
12937 };
12938 const uint8_t *p;
12939 unsigned clk_tck;
12940 struct tms buf;
12941
12942 clk_tck = bb_clk_tck();
12943
12944 times(&buf);
12945 p = times_tbl;
12946 do {
12947 unsigned sec, frac;
12948 unsigned long t;
12949 t = *(clock_t *)(((char *) &buf) + p[1]);
12950 sec = t / clk_tck;
12951 frac = t % clk_tck;
12952 printf("%um%u.%03us%c",
12953 sec / 60, sec % 60,
12954 (frac * 1000) / clk_tck,
12955 p[0]);
12956 p += 2;
12957 } while (*p);
12958
12959 return EXIT_SUCCESS;
12960}
12961#endif
12962
12963#if ENABLE_HUSH_MEMLEAK
12964static int FAST_FUNC builtin_memleak(char **argv UNUSED_PARAM)
12965{
12966 void *p;
12967 unsigned long l;
12968
12969# ifdef M_TRIM_THRESHOLD
12970 /* Optional. Reduces probability of false positives */
12971 malloc_trim(0);
12972# endif
12973 /* Crude attempt to find where "free memory" starts,
12974 * sans fragmentation. */
12975 p = malloc(240);
12976 l = (unsigned long)p;
12977 free(p);
12978 p = malloc(3400);
12979 if (l < (unsigned long)p) l = (unsigned long)p;
12980 free(p);
12981
Francis Laniel110b7692023-12-22 22:02:27 +010012982# if 0 /* debug */
12983 {
12984 struct mallinfo mi = mallinfo();
12985 printf("top alloc:0x%lx malloced:%d+%d=%d\n", l,
12986 mi.arena, mi.hblkhd, mi.arena + mi.hblkhd);
12987 }
12988# endif
12989
12990 if (!G.memleak_value)
12991 G.memleak_value = l;
12992
12993 l -= G.memleak_value;
12994 if ((long)l < 0)
12995 l = 0;
12996 l /= 1024;
12997 if (l > 127)
12998 l = 127;
12999
13000 /* Exitcode is "how many kilobytes we leaked since 1st call" */
13001 return l;
13002}
13003#endif
Francis Laniel36836fc2023-12-22 22:02:28 +010013004#endif /* !__U_BOOT__ */