blob: 4b6ab20f3b1cd788a20059f48b7abd7ef5c2a308 [file] [log] [blame]
Francis Lanielb234f7e2023-12-22 22:02:27 +01001/* vi: set sw=4 ts=4: */
2/*
3 * A prototype Bourne shell grammar parser.
4 * Intended to follow the original Thompson and Ritchie
5 * "small and simple is beautiful" philosophy, which
6 * incidentally is a good match to today's BusyBox.
7 *
8 * Copyright (C) 2000,2001 Larry Doolittle <larry@doolittle.boa.org>
9 * Copyright (C) 2008,2009 Denys Vlasenko <vda.linux@googlemail.com>
10 *
11 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
12 *
13 * Credits:
14 * The parser routines proper are all original material, first
15 * written Dec 2000 and Jan 2001 by Larry Doolittle. The
16 * execution engine, the builtins, and much of the underlying
17 * support has been adapted from busybox-0.49pre's lash, which is
18 * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
19 * written by Erik Andersen <andersen@codepoet.org>. That, in turn,
20 * is based in part on ladsh.c, by Michael K. Johnson and Erik W.
21 * Troan, which they placed in the public domain. I don't know
22 * how much of the Johnson/Troan code has survived the repeated
23 * rewrites.
24 *
25 * Other credits:
26 * o_addchr derived from similar w_addchar function in glibc-2.2.
27 * parse_redirect, redirect_opt_num, and big chunks of main
28 * and many builtins derived from contributions by Erik Andersen.
29 * Miscellaneous bugfixes from Matt Kraai.
30 *
31 * There are two big (and related) architecture differences between
32 * this parser and the lash parser. One is that this version is
33 * actually designed from the ground up to understand nearly all
34 * of the Bourne grammar. The second, consequential change is that
35 * the parser and input reader have been turned inside out. Now,
36 * the parser is in control, and asks for input as needed. The old
37 * way had the input reader in control, and it asked for parsing to
38 * take place as needed. The new way makes it much easier to properly
39 * handle the recursion implicit in the various substitutions, especially
40 * across continuation lines.
41 *
42 * TODOs:
43 * grep for "TODO" and fix (some of them are easy)
44 * make complex ${var%...} constructs support optional
45 * make here documents optional
46 * special variables (done: PWD, PPID, RANDOM)
47 * follow IFS rules more precisely, including update semantics
48 * tilde expansion
49 * aliases
50 * "command" missing features:
51 * command -p CMD: run CMD using default $PATH
52 * (can use this to override standalone shell as well?)
53 * command BLTIN: disables special-ness (e.g. errors do not abort)
54 * command -V CMD1 CMD2 CMD3 (multiple args) (not in standard)
55 * builtins mandated by standards we don't support:
56 * [un]alias, fc:
57 * fc -l[nr] [BEG] [END]: list range of commands in history
58 * fc [-e EDITOR] [BEG] [END]: edit/rerun range of commands
59 * fc -s [PAT=REP] [CMD]: rerun CMD, replacing PAT with REP
60 *
61 * Bash compat TODO:
62 * redirection of stdout+stderr: &> and >&
63 * reserved words: function select
64 * advanced test: [[ ]]
65 * process substitution: <(list) and >(list)
66 * let EXPR [EXPR...]
67 * Each EXPR is an arithmetic expression (ARITHMETIC EVALUATION)
68 * If the last arg evaluates to 0, let returns 1; 0 otherwise.
69 * NB: let `echo 'a=a + 1'` - error (IOW: multi-word expansion is used)
70 * ((EXPR))
71 * The EXPR is evaluated according to ARITHMETIC EVALUATION.
72 * This is exactly equivalent to let "EXPR".
73 * $[EXPR]: synonym for $((EXPR))
74 * indirect expansion: ${!VAR}
75 * substring op on @: ${@:n:m}
76 *
77 * Won't do:
78 * Some builtins mandated by standards:
79 * newgrp [GRP]: not a builtin in bash but a suid binary
80 * which spawns a new shell with new group ID
81 *
82 * Status of [[ support:
83 * [[ args ]] are CMD_SINGLEWORD_NOGLOB:
84 * v='a b'; [[ $v = 'a b' ]]; echo 0:$?
85 * [[ /bin/n* ]]; echo 0:$?
86 * = is glob match operator, not equality operator: STR = GLOB
87 * == same as =
88 * =~ is regex match operator: STR =~ REGEX
89 * TODO:
90 * quoting needs to be considered (-f is an operator, "-f" and ""-f are not; etc)
91 * in word = GLOB, quoting should be significant on char-by-char basis: a*cd"*"
92 */
93//config:config HUSH
94//config: bool "hush (68 kb)"
95//config: default y
96//config: select SHELL_HUSH
97//config: help
98//config: hush is a small shell. It handles the normal flow control
99//config: constructs such as if/then/elif/else/fi, for/in/do/done, while loops,
100//config: case/esac. Redirections, here documents, $((arithmetic))
101//config: and functions are supported.
102//config:
103//config: It will compile and work on no-mmu systems.
104//config:
105//config: It does not handle select, aliases, tilde expansion,
106//config: &>file and >&file redirection of stdout+stderr.
107//config:
108// This option is visible (has a description) to make it possible to select
109// a "scripted" applet (such as NOLOGIN) but avoid selecting any shells:
110//config:config SHELL_HUSH
111//config: bool "Internal shell for embedded script support"
112//config: default n
113//config:
114//config:# hush options
115//config:# It's only needed to get "nice" menuconfig indenting.
116//config:if SHELL_HUSH || HUSH || SH_IS_HUSH || BASH_IS_HUSH
117//config:
118//config:config HUSH_BASH_COMPAT
119//config: bool "bash-compatible extensions"
120//config: default y
121//config: depends on SHELL_HUSH
122//config:
123//config:config HUSH_BRACE_EXPANSION
124//config: bool "Brace expansion"
125//config: default y
126//config: depends on HUSH_BASH_COMPAT
127//config: help
128//config: Enable {abc,def} extension.
129//config:
130//config:config HUSH_BASH_SOURCE_CURDIR
131//config: bool "'source' and '.' builtins search current directory after $PATH"
132//config: default n # do not encourage non-standard behavior
133//config: depends on HUSH_BASH_COMPAT
134//config: help
135//config: This is not compliant with standards. Avoid if possible.
136//config:
137//config:config HUSH_LINENO_VAR
138//config: bool "$LINENO variable (bashism)"
139//config: default y
140//config: depends on SHELL_HUSH
141//config:
142//config:config HUSH_INTERACTIVE
143//config: bool "Interactive mode"
144//config: default y
145//config: depends on SHELL_HUSH
146//config: help
147//config: Enable interactive mode (prompt and command editing).
148//config: Without this, hush simply reads and executes commands
149//config: from stdin just like a shell script from a file.
150//config: No prompt, no PS1/PS2 magic shell variables.
151//config:
152//config:config HUSH_SAVEHISTORY
153//config: bool "Save command history to .hush_history"
154//config: default y
155//config: depends on HUSH_INTERACTIVE && FEATURE_EDITING_SAVEHISTORY
156//config:
157//config:config HUSH_JOB
158//config: bool "Job control"
159//config: default y
160//config: depends on HUSH_INTERACTIVE
161//config: help
162//config: Enable job control: Ctrl-Z backgrounds, Ctrl-C interrupts current
163//config: command (not entire shell), fg/bg builtins work. Without this option,
164//config: "cmd &" still works by simply spawning a process and immediately
165//config: prompting for next command (or executing next command in a script),
166//config: but no separate process group is formed.
167//config:
168//config:config HUSH_TICK
169//config: bool "Support command substitution"
170//config: default y
171//config: depends on SHELL_HUSH
172//config: help
173//config: Enable `command` and $(command).
174//config:
175//config:config HUSH_IF
176//config: bool "Support if/then/elif/else/fi"
177//config: default y
178//config: depends on SHELL_HUSH
179//config:
180//config:config HUSH_LOOPS
181//config: bool "Support for, while and until loops"
182//config: default y
183//config: depends on SHELL_HUSH
184//config:
185//config:config HUSH_CASE
186//config: bool "Support case ... esac statement"
187//config: default y
188//config: depends on SHELL_HUSH
189//config: help
190//config: Enable case ... esac statement. +400 bytes.
191//config:
192//config:config HUSH_FUNCTIONS
193//config: bool "Support funcname() { commands; } syntax"
194//config: default y
195//config: depends on SHELL_HUSH
196//config: help
197//config: Enable support for shell functions. +800 bytes.
198//config:
199//config:config HUSH_LOCAL
200//config: bool "local builtin"
201//config: default y
202//config: depends on HUSH_FUNCTIONS
203//config: help
204//config: Enable support for local variables in functions.
205//config:
206//config:config HUSH_RANDOM_SUPPORT
207//config: bool "Pseudorandom generator and $RANDOM variable"
208//config: default y
209//config: depends on SHELL_HUSH
210//config: help
211//config: Enable pseudorandom generator and dynamic variable "$RANDOM".
212//config: Each read of "$RANDOM" will generate a new pseudorandom value.
213//config:
214//config:config HUSH_MODE_X
215//config: bool "Support 'hush -x' option and 'set -x' command"
216//config: default y
217//config: depends on SHELL_HUSH
218//config: help
219//config: This instructs hush to print commands before execution.
220//config: Adds ~300 bytes.
221//config:
222//config:config HUSH_ECHO
223//config: bool "echo builtin"
224//config: default y
225//config: depends on SHELL_HUSH
226//config:
227//config:config HUSH_PRINTF
228//config: bool "printf builtin"
229//config: default y
230//config: depends on SHELL_HUSH
231//config:
232//config:config HUSH_TEST
233//config: bool "test builtin"
234//config: default y
235//config: depends on SHELL_HUSH
236//config:
237//config:config HUSH_HELP
238//config: bool "help builtin"
239//config: default y
240//config: depends on SHELL_HUSH
241//config:
242//config:config HUSH_EXPORT
243//config: bool "export builtin"
244//config: default y
245//config: depends on SHELL_HUSH
246//config:
247//config:config HUSH_EXPORT_N
248//config: bool "Support 'export -n' option"
249//config: default y
250//config: depends on HUSH_EXPORT
251//config: help
252//config: export -n unexports variables. It is a bash extension.
253//config:
254//config:config HUSH_READONLY
255//config: bool "readonly builtin"
256//config: default y
257//config: depends on SHELL_HUSH
258//config: help
259//config: Enable support for read-only variables.
260//config:
261//config:config HUSH_KILL
262//config: bool "kill builtin (supports kill %jobspec)"
263//config: default y
264//config: depends on SHELL_HUSH
265//config:
266//config:config HUSH_WAIT
267//config: bool "wait builtin"
268//config: default y
269//config: depends on SHELL_HUSH
270//config:
271//config:config HUSH_COMMAND
272//config: bool "command builtin"
273//config: default y
274//config: depends on SHELL_HUSH
275//config:
276//config:config HUSH_TRAP
277//config: bool "trap builtin"
278//config: default y
279//config: depends on SHELL_HUSH
280//config:
281//config:config HUSH_TYPE
282//config: bool "type builtin"
283//config: default y
284//config: depends on SHELL_HUSH
285//config:
286//config:config HUSH_TIMES
287//config: bool "times builtin"
288//config: default y
289//config: depends on SHELL_HUSH
290//config:
291//config:config HUSH_READ
292//config: bool "read builtin"
293//config: default y
294//config: depends on SHELL_HUSH
295//config:
296//config:config HUSH_SET
297//config: bool "set builtin"
298//config: default y
299//config: depends on SHELL_HUSH
300//config:
301//config:config HUSH_UNSET
302//config: bool "unset builtin"
303//config: default y
304//config: depends on SHELL_HUSH
305//config:
306//config:config HUSH_ULIMIT
307//config: bool "ulimit builtin"
308//config: default y
309//config: depends on SHELL_HUSH
310//config:
311//config:config HUSH_UMASK
312//config: bool "umask builtin"
313//config: default y
314//config: depends on SHELL_HUSH
315//config:
316//config:config HUSH_GETOPTS
317//config: bool "getopts builtin"
318//config: default y
319//config: depends on SHELL_HUSH
320//config:
321//config:config HUSH_MEMLEAK
322//config: bool "memleak builtin (debugging)"
323//config: default n
324//config: depends on SHELL_HUSH
325//config:
326//config:endif # hush options
327
328//applet:IF_HUSH(APPLET(hush, BB_DIR_BIN, BB_SUID_DROP))
329// APPLET_ODDNAME:name main location suid_type help
330//applet:IF_SH_IS_HUSH( APPLET_ODDNAME(sh, hush, BB_DIR_BIN, BB_SUID_DROP, hush))
331//applet:IF_BASH_IS_HUSH(APPLET_ODDNAME(bash, hush, BB_DIR_BIN, BB_SUID_DROP, hush))
332
333//kbuild:lib-$(CONFIG_SHELL_HUSH) += hush.o match.o shell_common.o
334//kbuild:lib-$(CONFIG_HUSH_RANDOM_SUPPORT) += random.o
335
336/* -i (interactive) is also accepted,
337 * but does nothing, therefore not shown in help.
338 * NOMMU-specific options are not meant to be used by users,
339 * therefore we don't show them either.
340 */
341//usage:#define hush_trivial_usage
342//usage: "[-enxl] [-c 'SCRIPT' [ARG0 ARGS] | FILE [ARGS] | -s [ARGS]]"
343//usage:#define hush_full_usage "\n\n"
344//usage: "Unix shell interpreter"
345
Francis Laniel8197f012023-12-22 22:02:28 +0100346#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +0100347#if !(defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__NetBSD__) \
348 || defined(__APPLE__) \
349 )
350# include <malloc.h> /* for malloc_trim */
351#endif
352#include <glob.h>
353/* #include <dmalloc.h> */
354#if ENABLE_HUSH_CASE
355# include <fnmatch.h>
356#endif
357#include <sys/times.h>
358#include <sys/utsname.h> /* for setting $HOSTNAME */
359
360#include "busybox.h" /* for APPLET_IS_NOFORK/NOEXEC */
361#include "unicode.h"
362#include "shell_common.h"
363#include "math.h"
364#include "match.h"
365#if ENABLE_HUSH_RANDOM_SUPPORT
366# include "random.h"
367#else
368# define CLEAR_RANDOM_T(rnd) ((void)0)
369#endif
370#ifndef O_CLOEXEC
371# define O_CLOEXEC 0
372#endif
373#ifndef F_DUPFD_CLOEXEC
374# define F_DUPFD_CLOEXEC F_DUPFD
375#endif
376
377#if ENABLE_FEATURE_SH_EMBEDDED_SCRIPTS && !(ENABLE_ASH || ENABLE_SH_IS_ASH || ENABLE_BASH_IS_ASH)
378# include "embedded_scripts.h"
379#else
380# define NUM_SCRIPTS 0
381#endif
Francis Laniel8197f012023-12-22 22:02:28 +0100382#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +0100383
384/* So far, all bash compat is controlled by one config option */
385/* Separate defines document which part of code implements what */
386#define BASH_PATTERN_SUBST ENABLE_HUSH_BASH_COMPAT
387#define BASH_SUBSTR ENABLE_HUSH_BASH_COMPAT
388#define BASH_SOURCE ENABLE_HUSH_BASH_COMPAT
389#define BASH_DOLLAR_SQUOTE ENABLE_HUSH_BASH_COMPAT
390#define BASH_HOSTNAME_VAR ENABLE_HUSH_BASH_COMPAT
391#define BASH_EPOCH_VARS ENABLE_HUSH_BASH_COMPAT
392#define BASH_TEST2 (ENABLE_HUSH_BASH_COMPAT && ENABLE_HUSH_TEST)
393#define BASH_READ_D ENABLE_HUSH_BASH_COMPAT
394
395
396/* Build knobs */
397#define LEAK_HUNTING 0
398#define BUILD_AS_NOMMU 0
399/* Enable/disable sanity checks. Ok to enable in production,
400 * only adds a bit of bloat. Set to >1 to get non-production level verbosity.
401 * Keeping 1 for now even in released versions.
402 */
403#define HUSH_DEBUG 1
404/* Slightly bigger (+200 bytes), but faster hush.
405 * So far it only enables a trick with counting SIGCHLDs and forks,
406 * which allows us to do fewer waitpid's.
407 * (we can detect a case where neither forks were done nor SIGCHLDs happened
408 * and therefore waitpid will return the same result as last time)
409 */
410#define ENABLE_HUSH_FAST 0
411/* TODO: implement simplified code for users which do not need ${var%...} ops
412 * So far ${var%...} ops are always enabled:
413 */
414#define ENABLE_HUSH_DOLLAR_OPS 1
415
416
417#if BUILD_AS_NOMMU
418# undef BB_MMU
419# undef USE_FOR_NOMMU
420# undef USE_FOR_MMU
421# define BB_MMU 0
422# define USE_FOR_NOMMU(...) __VA_ARGS__
423# define USE_FOR_MMU(...)
424#endif
425
Francis Laniel8197f012023-12-22 22:02:28 +0100426#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +0100427#include "NUM_APPLETS.h"
428#if NUM_APPLETS == 1
429/* STANDALONE does not make sense, and won't compile */
Francis Lanielb234f7e2023-12-22 22:02:27 +0100430# undef ENABLE_FEATURE_SH_STANDALONE
431# undef IF_FEATURE_SH_STANDALONE
432# undef IF_NOT_FEATURE_SH_STANDALONE
433# define ENABLE_FEATURE_SH_STANDALONE 0
434# define IF_FEATURE_SH_STANDALONE(...)
435# define IF_NOT_FEATURE_SH_STANDALONE(...) __VA_ARGS__
436#endif
Francis Laniel8197f012023-12-22 22:02:28 +0100437#endif /* __U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +0100438
439#if !ENABLE_HUSH_INTERACTIVE
440# undef ENABLE_FEATURE_EDITING
441# define ENABLE_FEATURE_EDITING 0
442# undef ENABLE_FEATURE_EDITING_FANCY_PROMPT
443# define ENABLE_FEATURE_EDITING_FANCY_PROMPT 0
444# undef ENABLE_FEATURE_EDITING_SAVE_ON_EXIT
445# define ENABLE_FEATURE_EDITING_SAVE_ON_EXIT 0
446#endif
447
448/* Do we support ANY keywords? */
449#if ENABLE_HUSH_IF || ENABLE_HUSH_LOOPS || ENABLE_HUSH_CASE
450# define HAS_KEYWORDS 1
451# define IF_HAS_KEYWORDS(...) __VA_ARGS__
452# define IF_HAS_NO_KEYWORDS(...)
453#else
454# define HAS_KEYWORDS 0
455# define IF_HAS_KEYWORDS(...)
456# define IF_HAS_NO_KEYWORDS(...) __VA_ARGS__
457#endif
458
459/* If you comment out one of these below, it will be #defined later
460 * to perform debug printfs to stderr: */
461#define debug_printf(...) do {} while (0)
462/* Finer-grained debug switches */
463#define debug_printf_parse(...) do {} while (0)
464#define debug_printf_heredoc(...) do {} while (0)
465#define debug_print_tree(a, b) do {} while (0)
466#define debug_printf_exec(...) do {} while (0)
467#define debug_printf_env(...) do {} while (0)
468#define debug_printf_jobs(...) do {} while (0)
469#define debug_printf_expand(...) do {} while (0)
470#define debug_printf_varexp(...) do {} while (0)
471#define debug_printf_glob(...) do {} while (0)
472#define debug_printf_redir(...) do {} while (0)
473#define debug_printf_list(...) do {} while (0)
474#define debug_printf_subst(...) do {} while (0)
475#define debug_printf_prompt(...) do {} while (0)
476#define debug_printf_clean(...) do {} while (0)
477
478#define ERR_PTR ((void*)(long)1)
479
480#define JOB_STATUS_FORMAT "[%u] %-22s %.40s\n"
481
482#define _SPECIAL_VARS_STR "_*@$!?#-"
Francis Laniel8197f012023-12-22 22:02:28 +0100483#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +0100484#define SPECIAL_VARS_STR ("_*@$!?#-" + 1)
485#define NUMERIC_SPECVARS_STR ("_*@$!?#-" + 3)
Francis Laniel8197f012023-12-22 22:02:28 +0100486#else /* __U_BOOT__ */
487#define SPECIAL_VARS_STR "*@$!?#-"
488#define NUMERIC_SPECVARS_STR "$!?#-"
489#endif /* __U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +0100490#if BASH_PATTERN_SUBST
491/* Support / and // replace ops */
492/* Note that // is stored as \ in "encoded" string representation */
493# define VAR_ENCODED_SUBST_OPS "\\/%#:-=+?"
494# define VAR_SUBST_OPS ("\\/%#:-=+?" + 1)
495# define MINUS_PLUS_EQUAL_QUESTION ("\\/%#:-=+?" + 5)
496#else
497# define VAR_ENCODED_SUBST_OPS "%#:-=+?"
498# define VAR_SUBST_OPS "%#:-=+?"
499# define MINUS_PLUS_EQUAL_QUESTION ("%#:-=+?" + 3)
500#endif
501
502#define SPECIAL_VAR_SYMBOL_STR "\3"
503#define SPECIAL_VAR_SYMBOL 3
504/* The "variable" with name "\1" emits string "\3". Testcase: "echo ^C" */
505#define SPECIAL_VAR_QUOTED_SVS 1
506
507struct variable;
508
509static const char hush_version_str[] ALIGN1 = "HUSH_VERSION="BB_VER;
510
511/* This supports saving pointers malloced in vfork child,
512 * to be freed in the parent.
513 */
514#if !BB_MMU
515typedef struct nommu_save_t {
516 struct variable *old_vars;
517 char **argv;
518 char **argv_from_re_execing;
519} nommu_save_t;
520#endif
521
Francis Laniel8197f012023-12-22 22:02:28 +0100522
Francis Lanielb234f7e2023-12-22 22:02:27 +0100523enum {
524 RES_NONE = 0,
525#if ENABLE_HUSH_IF
526 RES_IF ,
527 RES_THEN ,
528 RES_ELIF ,
529 RES_ELSE ,
530 RES_FI ,
531#endif
532#if ENABLE_HUSH_LOOPS
533 RES_FOR ,
534 RES_WHILE ,
535 RES_UNTIL ,
536 RES_DO ,
537 RES_DONE ,
538#endif
539#if ENABLE_HUSH_LOOPS || ENABLE_HUSH_CASE
540 RES_IN ,
541#endif
542#if ENABLE_HUSH_CASE
543 RES_CASE ,
544 /* three pseudo-keywords support contrived "case" syntax: */
545 RES_CASE_IN, /* "case ... IN", turns into RES_MATCH when IN is observed */
546 RES_MATCH , /* "word)" */
547 RES_CASE_BODY, /* "this command is inside CASE" */
548 RES_ESAC ,
549#endif
550 RES_XXXX ,
551 RES_SNTX
552};
553
554typedef struct o_string {
555 char *data;
556 int length; /* position where data is appended */
557 int maxlen;
558 int o_expflags;
559 /* At least some part of the string was inside '' or "",
560 * possibly empty one: word"", wo''rd etc. */
561 smallint has_quoted_part;
562 smallint has_empty_slot;
563 smallint ended_in_ifs;
564} o_string;
565enum {
566 EXP_FLAG_SINGLEWORD = 0x80, /* must be 0x80 */
567 EXP_FLAG_GLOB = 0x2,
568 /* Protect newly added chars against globbing
569 * by prepending \ to *, ?, [, \ */
570 EXP_FLAG_ESC_GLOB_CHARS = 0x1,
571};
572/* Used for initialization: o_string foo = NULL_O_STRING; */
573#define NULL_O_STRING { NULL }
574
575#ifndef debug_printf_parse
576static const char *const assignment_flag[] = {
577 "MAYBE_ASSIGNMENT",
578 "DEFINITELY_ASSIGNMENT",
579 "NOT_ASSIGNMENT",
580 "WORD_IS_KEYWORD",
581};
582#endif
583
584/* We almost can use standard FILE api, but we need an ability to move
585 * its fd when redirects coincide with it. No api exists for that
586 * (RFE for it at https://sourceware.org/bugzilla/show_bug.cgi?id=21902).
587 * HFILE is our internal alternative. Only supports reading.
588 * Since we now can, we incorporate linked list of all opened HFILEs
589 * into the struct (used to be a separate mini-list).
590 */
591typedef struct HFILE {
592 char *cur;
593 char *end;
594 struct HFILE *next_hfile;
595 int fd;
596 char buf[1024];
597} HFILE;
598
599typedef struct in_str {
600 const char *p;
601 int peek_buf[2];
602 int last_char;
603 HFILE *file;
604} in_str;
605
Francis Laniel8197f012023-12-22 22:02:28 +0100606#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +0100607/* The descrip member of this structure is only used to make
608 * debugging output pretty */
609static const struct {
610 int32_t mode;
611 signed char default_fd;
612 char descrip[3];
613} redir_table[] ALIGN4 = {
614 { O_RDONLY, 0, "<" },
615 { O_CREAT|O_TRUNC|O_WRONLY, 1, ">" },
616 { O_CREAT|O_APPEND|O_WRONLY, 1, ">>" },
617 { O_CREAT|O_RDWR, 1, "<>" },
618 { O_RDONLY, 0, "<<" },
619/* Should not be needed. Bogus default_fd helps in debugging */
620/* { O_RDONLY, 77, "<<" }, */
621};
622
623struct redir_struct {
624 struct redir_struct *next;
625 char *rd_filename; /* filename */
626 int rd_fd; /* fd to redirect */
627 /* fd to redirect to, or -3 if rd_fd is to be closed (n>&-) */
628 int rd_dup;
629 smallint rd_type; /* (enum redir_type) */
630 /* note: for heredocs, rd_filename contains heredoc delimiter,
631 * and subsequently heredoc itself; and rd_dup is a bitmask:
632 * bit 0: do we need to trim leading tabs?
633 * bit 1: is heredoc quoted (<<'delim' syntax) ?
634 */
635};
636typedef enum redir_type {
637 REDIRECT_INPUT = 0,
638 REDIRECT_OVERWRITE = 1,
639 REDIRECT_APPEND = 2,
640 REDIRECT_IO = 3,
641 REDIRECT_HEREDOC = 4,
642 REDIRECT_HEREDOC2 = 5, /* REDIRECT_HEREDOC after heredoc is loaded */
643
644 REDIRFD_CLOSE = -3,
645 REDIRFD_SYNTAX_ERR = -2,
646 REDIRFD_TO_FILE = -1,
647 /* otherwise, rd_fd is redirected to rd_dup */
648
649 HEREDOC_SKIPTABS = 1,
650 HEREDOC_QUOTED = 2,
651} redir_type;
652
Francis Laniel8197f012023-12-22 22:02:28 +0100653#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +0100654
655struct command {
Francis Laniel8197f012023-12-22 22:02:28 +0100656#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +0100657 pid_t pid; /* 0 if exited */
Francis Laniel8197f012023-12-22 22:02:28 +0100658#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +0100659 unsigned assignment_cnt; /* how many argv[i] are assignments? */
660#if ENABLE_HUSH_LINENO_VAR
661 unsigned lineno;
662#endif
663 smallint cmd_type; /* CMD_xxx */
664#define CMD_NORMAL 0
665#define CMD_SUBSHELL 1
666#if BASH_TEST2
667/* used for "[[ EXPR ]]" */
668# define CMD_TEST2_SINGLEWORD_NOGLOB 2
669#endif
670#if BASH_TEST2 || ENABLE_HUSH_LOCAL || ENABLE_HUSH_EXPORT || ENABLE_HUSH_READONLY
671/* used to prevent word splitting and globbing in "export v=t*" */
672# define CMD_SINGLEWORD_NOGLOB 3
673#endif
674#if ENABLE_HUSH_FUNCTIONS
675# define CMD_FUNCDEF 4
676#endif
677
678 smalluint cmd_exitcode;
679 /* if non-NULL, this "command" is { list }, ( list ), or a compound statement */
680 struct pipe *group;
681#if !BB_MMU
682 char *group_as_string;
683#endif
684#if ENABLE_HUSH_FUNCTIONS
685 struct function *child_func;
686/* This field is used to prevent a bug here:
687 * while...do f1() {a;}; f1; f1() {b;}; f1; done
688 * When we execute "f1() {a;}" cmd, we create new function and clear
689 * cmd->group, cmd->group_as_string, cmd->argv[0].
690 * When we execute "f1() {b;}", we notice that f1 exists,
691 * and that its "parent cmd" struct is still "alive",
692 * we put those fields back into cmd->xxx
693 * (struct function has ->parent_cmd ptr to facilitate that).
694 * When we loop back, we can execute "f1() {a;}" again and set f1 correctly.
695 * Without this trick, loop would execute a;b;b;b;...
696 * instead of correct sequence a;b;a;b;...
697 * When command is freed, it severs the link
698 * (sets ->child_func->parent_cmd to NULL).
699 */
700#endif
Francis Laniel8197f012023-12-22 22:02:28 +0100701#ifdef __U_BOOT__
702 int argc; /* number of program arguments */
703#endif
Francis Lanielb234f7e2023-12-22 22:02:27 +0100704 char **argv; /* command name and arguments */
705/* argv vector may contain variable references (^Cvar^C, ^C0^C etc)
706 * and on execution these are substituted with their values.
707 * Substitution can make _several_ words out of one argv[n]!
708 * Example: argv[0]=='.^C*^C.' here: echo .$*.
709 * References of the form ^C`cmd arg^C are `cmd arg` substitutions.
710 */
Francis Laniel8197f012023-12-22 22:02:28 +0100711#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +0100712 struct redir_struct *redirects; /* I/O redirections */
Francis Laniel8197f012023-12-22 22:02:28 +0100713#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +0100714};
715/* Is there anything in this command at all? */
Francis Laniel8197f012023-12-22 22:02:28 +0100716#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +0100717#define IS_NULL_CMD(cmd) \
718 (!(cmd)->group && !(cmd)->argv && !(cmd)->redirects)
719
Francis Laniel8197f012023-12-22 22:02:28 +0100720#else /* __U_BOOT__ */
721#define IS_NULL_CMD(cmd) \
722 (!(cmd)->group && !(cmd)->argv)
723#endif /* __U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +0100724struct pipe {
725 struct pipe *next;
726 int num_cmds; /* total number of commands in pipe */
Francis Laniel8197f012023-12-22 22:02:28 +0100727#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +0100728 int alive_cmds; /* number of commands running (not exited) */
729 int stopped_cmds; /* number of commands alive, but stopped */
730#if ENABLE_HUSH_JOB
731 unsigned jobid; /* job number */
732 pid_t pgrp; /* process group ID for the job */
733 char *cmdtext; /* name of job */
734#endif
Francis Laniel8197f012023-12-22 22:02:28 +0100735#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +0100736 struct command *cmds; /* array of commands in pipe */
737 smallint followup; /* PIPE_BG, PIPE_SEQ, PIPE_OR, PIPE_AND */
738 IF_HAS_KEYWORDS(smallint pi_inverted;) /* "! cmd | cmd" */
739 IF_HAS_KEYWORDS(smallint res_word;) /* needed for if, for, while, until... */
740};
741typedef enum pipe_style {
742 PIPE_SEQ = 0,
743 PIPE_AND = 1,
744 PIPE_OR = 2,
745 PIPE_BG = 3,
746} pipe_style;
747/* Is there anything in this pipe at all? */
748#define IS_NULL_PIPE(pi) \
749 ((pi)->num_cmds == 0 IF_HAS_KEYWORDS( && (pi)->res_word == RES_NONE))
750
751/* This holds pointers to the various results of parsing */
752struct parse_context {
753 /* linked list of pipes */
754 struct pipe *list_head;
755 /* last pipe (being constructed right now) */
756 struct pipe *pipe;
757 /* last command in pipe (being constructed right now) */
758 struct command *command;
Francis Laniel8197f012023-12-22 22:02:28 +0100759#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +0100760 /* last redirect in command->redirects list */
761 struct redir_struct *pending_redirect;
Francis Laniel8197f012023-12-22 22:02:28 +0100762#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +0100763 o_string word;
764#if !BB_MMU
765 o_string as_string;
766#endif
767 smallint is_assignment; /* 0:maybe, 1:yes, 2:no, 3:keyword */
768#if HAS_KEYWORDS
769 smallint ctx_res_w;
770 smallint ctx_inverted; /* "! cmd | cmd" */
771#if ENABLE_HUSH_CASE
772 smallint ctx_dsemicolon; /* ";;" seen */
773#endif
774 /* bitmask of FLAG_xxx, for figuring out valid reserved words */
775 int old_flag;
776 /* group we are enclosed in:
777 * example: "if pipe1; pipe2; then pipe3; fi"
778 * when we see "if" or "then", we malloc and copy current context,
779 * and make ->stack point to it. then we parse pipeN.
780 * when closing "then" / fi" / whatever is found,
781 * we move list_head into ->stack->command->group,
782 * copy ->stack into current context, and delete ->stack.
783 * (parsing of { list } and ( list ) doesn't use this method)
784 */
785 struct parse_context *stack;
786#endif
787};
788enum {
789 MAYBE_ASSIGNMENT = 0,
790 DEFINITELY_ASSIGNMENT = 1,
791 NOT_ASSIGNMENT = 2,
792 /* Not an assignment, but next word may be: "if v=xyz cmd;" */
793 WORD_IS_KEYWORD = 3,
794};
795
Francis Laniel8197f012023-12-22 22:02:28 +0100796#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +0100797/* On program start, environ points to initial environment.
798 * putenv adds new pointers into it, unsetenv removes them.
799 * Neither of these (de)allocates the strings.
800 * setenv allocates new strings in malloc space and does putenv,
801 * and thus setenv is unusable (leaky) for shell's purposes */
802#define setenv(...) setenv_is_leaky_dont_use()
Francis Laniel8197f012023-12-22 22:02:28 +0100803#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +0100804struct variable {
805 struct variable *next;
806 char *varstr; /* points to "name=" portion */
807 int max_len; /* if > 0, name is part of initial env; else name is malloced */
Francis Laniel8197f012023-12-22 22:02:28 +0100808#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +0100809 uint16_t var_nest_level;
810 smallint flg_export; /* putenv should be done on this var */
811 smallint flg_read_only;
Francis Laniel8197f012023-12-22 22:02:28 +0100812#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +0100813};
814
815enum {
816 BC_BREAK = 1,
817 BC_CONTINUE = 2,
818};
819
820#if ENABLE_HUSH_FUNCTIONS
821struct function {
822 struct function *next;
823 char *name;
824 struct command *parent_cmd;
825 struct pipe *body;
826# if !BB_MMU
827 char *body_as_string;
828# endif
829};
830#endif
831
832
833/* set -/+o OPT support. (TODO: make it optional)
834 * bash supports the following opts:
835 * allexport off
836 * braceexpand on
837 * emacs on
838 * errexit off
839 * errtrace off
840 * functrace off
841 * hashall on
842 * histexpand off
843 * history on
844 * ignoreeof off
845 * interactive-comments on
846 * keyword off
847 * monitor on
848 * noclobber off
849 * noexec off
850 * noglob off
851 * nolog off
852 * notify off
853 * nounset off
854 * onecmd off
855 * physical off
856 * pipefail off
857 * posix off
858 * privileged off
859 * verbose off
860 * vi off
861 * xtrace off
862 */
863static const char o_opt_strings[] ALIGN1 =
864 "pipefail\0"
865 "noexec\0"
866 "errexit\0"
867#if ENABLE_HUSH_MODE_X
868 "xtrace\0"
869#endif
870 ;
871enum {
872 OPT_O_PIPEFAIL,
873 OPT_O_NOEXEC,
874 OPT_O_ERREXIT,
875#if ENABLE_HUSH_MODE_X
876 OPT_O_XTRACE,
877#endif
878 NUM_OPT_O
879};
880
881/* "Globals" within this file */
882/* Sorted roughly by size (smaller offsets == smaller code) */
883struct globals {
Francis Laniel8197f012023-12-22 22:02:28 +0100884#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +0100885 /* interactive_fd != 0 means we are an interactive shell.
886 * If we are, then saved_tty_pgrp can also be != 0, meaning
887 * that controlling tty is available. With saved_tty_pgrp == 0,
888 * job control still works, but terminal signals
889 * (^C, ^Z, ^Y, ^\) won't work at all, and background
890 * process groups can only be created with "cmd &".
891 * With saved_tty_pgrp != 0, hush will use tcsetpgrp()
892 * to give tty to the foreground process group,
893 * and will take it back when the group is stopped (^Z)
894 * or killed (^C).
895 */
896#if ENABLE_HUSH_INTERACTIVE
897 /* 'interactive_fd' is a fd# open to ctty, if we have one
898 * _AND_ if we decided to act interactively */
899 int interactive_fd;
900 IF_NOT_FEATURE_EDITING_FANCY_PROMPT(char *PS1;)
901# define G_interactive_fd (G.interactive_fd)
902#else
903# define G_interactive_fd 0
904#endif
Francis Laniel8197f012023-12-22 22:02:28 +0100905#else /* __U_BOOT__ */
906# define G_interactive_fd 0
907#endif /* __U_BOOT__ */
908#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +0100909#if ENABLE_FEATURE_EDITING
910 line_input_t *line_input_state;
911#endif
912 pid_t root_pid;
913 pid_t root_ppid;
914 pid_t last_bg_pid;
915#if ENABLE_HUSH_RANDOM_SUPPORT
916 random_t random_gen;
917#endif
918#if ENABLE_HUSH_JOB
919 int run_list_level;
920 unsigned last_jobid;
921 pid_t saved_tty_pgrp;
922 struct pipe *job_list;
923# define G_saved_tty_pgrp (G.saved_tty_pgrp)
924#else
925# define G_saved_tty_pgrp 0
926#endif
Francis Laniel8197f012023-12-22 22:02:28 +0100927#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +0100928 /* How deeply are we in context where "set -e" is ignored */
929 int errexit_depth;
Francis Laniel8197f012023-12-22 22:02:28 +0100930#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +0100931 /* "set -e" rules (do we follow them correctly?):
932 * Exit if pipe, list, or compound command exits with a non-zero status.
933 * Shell does not exit if failed command is part of condition in
934 * if/while, part of && or || list except the last command, any command
935 * in a pipe but the last, or if the command's return value is being
936 * inverted with !. If a compound command other than a subshell returns a
937 * non-zero status because a command failed while -e was being ignored, the
938 * shell does not exit. A trap on ERR, if set, is executed before the shell
939 * exits [ERR is a bashism].
940 *
941 * If a compound command or function executes in a context where -e is
942 * ignored, none of the commands executed within are affected by the -e
943 * setting. If a compound command or function sets -e while executing in a
944 * context where -e is ignored, that setting does not have any effect until
945 * the compound command or the command containing the function call completes.
946 */
947
948 char o_opt[NUM_OPT_O];
949#if ENABLE_HUSH_MODE_X
950# define G_x_mode (G.o_opt[OPT_O_XTRACE])
951#else
952# define G_x_mode 0
953#endif
954 char opt_s;
955 char opt_c;
Francis Laniel8197f012023-12-22 22:02:28 +0100956#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +0100957#if ENABLE_HUSH_INTERACTIVE
958 smallint promptmode; /* 0: PS1, 1: PS2 */
959#endif
960 smallint flag_SIGINT;
Francis Laniel8197f012023-12-22 22:02:28 +0100961#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +0100962#if ENABLE_HUSH_LOOPS
963 smallint flag_break_continue;
964#endif
Francis Laniel8197f012023-12-22 22:02:28 +0100965#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +0100966#if ENABLE_HUSH_FUNCTIONS
967 /* 0: outside of a function (or sourced file)
968 * -1: inside of a function, ok to use return builtin
969 * 1: return is invoked, skip all till end of func
970 */
971 smallint flag_return_in_progress;
972# define G_flag_return_in_progress (G.flag_return_in_progress)
973#else
974# define G_flag_return_in_progress 0
975#endif
976 smallint exiting; /* used to prevent EXIT trap recursion */
977 /* These support $? */
978 smalluint last_exitcode;
979 smalluint expand_exitcode;
980 smalluint last_bg_pid_exitcode;
Francis Laniel8197f012023-12-22 22:02:28 +0100981#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +0100982#if ENABLE_HUSH_SET
983 /* are global_argv and global_argv[1..n] malloced? (note: not [0]) */
984 smalluint global_args_malloced;
985# define G_global_args_malloced (G.global_args_malloced)
986#else
987# define G_global_args_malloced 0
988#endif
989#if ENABLE_HUSH_BASH_COMPAT
990 int dead_job_exitcode; /* for "wait -n" */
991#endif
Francis Laniel8197f012023-12-22 22:02:28 +0100992#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +0100993 /* how many non-NULL argv's we have. NB: $# + 1 */
994 int global_argc;
995 char **global_argv;
996#if !BB_MMU
997 char *argv0_for_re_execing;
998#endif
999#if ENABLE_HUSH_LOOPS
Francis Laniel8197f012023-12-22 22:02:28 +01001000#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01001001 unsigned depth_break_continue;
Francis Laniel8197f012023-12-22 22:02:28 +01001002#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01001003 unsigned depth_of_loop;
1004#endif
Francis Laniel8197f012023-12-22 22:02:28 +01001005#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01001006#if ENABLE_HUSH_GETOPTS
1007 unsigned getopt_count;
1008#endif
Francis Laniel8197f012023-12-22 22:02:28 +01001009#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01001010 const char *ifs;
Francis Laniel8197f012023-12-22 22:02:28 +01001011#ifdef __U_BOOT__
1012 int flag_repeat;
1013 int do_repeat;
1014#endif /* __U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01001015 char *ifs_whitespace; /* = G.ifs or malloced */
Francis Laniel8197f012023-12-22 22:02:28 +01001016#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01001017 const char *cwd;
Francis Laniel8197f012023-12-22 22:02:28 +01001018#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01001019 struct variable *top_var;
1020 char **expanded_assignments;
1021 struct variable **shadowed_vars_pp;
1022 unsigned var_nest_level;
Francis Laniel8197f012023-12-22 22:02:28 +01001023#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01001024#if ENABLE_HUSH_FUNCTIONS
1025# if ENABLE_HUSH_LOCAL
1026 unsigned func_nest_level; /* solely to prevent "local v" in non-functions */
1027# endif
1028 struct function *top_func;
1029#endif
1030 /* Signal and trap handling */
1031#if ENABLE_HUSH_FAST
1032 unsigned count_SIGCHLD;
1033 unsigned handled_SIGCHLD;
1034 smallint we_have_children;
1035#endif
1036#if ENABLE_HUSH_LINENO_VAR
1037 unsigned parse_lineno;
1038 unsigned execute_lineno;
1039#endif
1040 HFILE *HFILE_list;
1041 HFILE *HFILE_stdin;
1042 /* Which signals have non-DFL handler (even with no traps set)?
1043 * Set at the start to:
1044 * (SIGQUIT + maybe SPECIAL_INTERACTIVE_SIGS + maybe SPECIAL_JOBSTOP_SIGS)
1045 * SPECIAL_INTERACTIVE_SIGS are cleared after fork.
1046 * The rest is cleared right before execv syscalls.
1047 * Other than these two times, never modified.
1048 */
1049 unsigned special_sig_mask;
1050#if ENABLE_HUSH_JOB
1051 unsigned fatal_sig_mask;
1052# define G_fatal_sig_mask (G.fatal_sig_mask)
1053#else
1054# define G_fatal_sig_mask 0
1055#endif
1056#if ENABLE_HUSH_TRAP
1057 int pre_trap_exitcode;
1058# if ENABLE_HUSH_FUNCTIONS
1059 int return_exitcode;
1060# endif
1061 char **traps; /* char *traps[NSIG] */
1062# define G_traps G.traps
1063#else
1064# define G_traps ((char**)NULL)
1065#endif
1066 sigset_t pending_set;
1067#if ENABLE_HUSH_MEMLEAK
1068 unsigned long memleak_value;
1069#endif
1070#if ENABLE_HUSH_MODE_X
1071 unsigned x_mode_depth;
1072 /* "set -x" output should not be redirectable with subsequent 2>FILE.
1073 * We dup fd#2 to x_mode_fd when "set -x" is executed, and use it
1074 * for all subsequent output.
1075 */
1076 int x_mode_fd;
1077 o_string x_mode_buf;
1078#endif
Francis Laniel8197f012023-12-22 22:02:28 +01001079#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01001080#if HUSH_DEBUG >= 2
1081 int debug_indent;
1082#endif
Francis Laniel8197f012023-12-22 22:02:28 +01001083#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01001084 struct sigaction sa;
1085 char optstring_buf[sizeof("eixcs")];
1086#if BASH_EPOCH_VARS
1087 char epoch_buf[sizeof("%llu.nnnnnn") + sizeof(long long)*3];
1088#endif
1089#if ENABLE_FEATURE_EDITING
1090 char user_input_buf[CONFIG_FEATURE_EDITING_MAX_LEN];
1091#endif
Francis Laniel8197f012023-12-22 22:02:28 +01001092#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01001093};
Francis Laniel8197f012023-12-22 22:02:28 +01001094#ifdef __U_BOOT__
1095struct globals *ptr_to_globals;
1096#endif /* __U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01001097#define G (*ptr_to_globals)
1098/* Not #defining name to G.name - this quickly gets unwieldy
1099 * (too many defines). Also, I actually prefer to see when a variable
1100 * is global, thus "G." prefix is a useful hint */
Francis Laniel8197f012023-12-22 22:02:28 +01001101#ifdef __U_BOOT__
1102#define SET_PTR_TO_GLOBALS(x) do { \
1103 (*(struct globals**)&ptr_to_globals) = (void*)(x); \
1104 barrier(); \
1105} while (0)
1106#define INIT_G() do { \
1107 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
1108 G.promptmode = 1; \
1109} while (0)
1110#else /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01001111#define INIT_G() do { \
1112 SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
1113 /* memset(&G.sa, 0, sizeof(G.sa)); */ \
1114 sigfillset(&G.sa.sa_mask); \
1115 G.sa.sa_flags = SA_RESTART; \
1116} while (0)
Francis Laniel8197f012023-12-22 22:02:28 +01001117#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01001118
1119
Francis Laniel8197f012023-12-22 22:02:28 +01001120#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01001121/* Function prototypes for builtins */
1122static int builtin_cd(char **argv) FAST_FUNC;
1123#if ENABLE_HUSH_ECHO
1124static int builtin_echo(char **argv) FAST_FUNC;
1125#endif
1126static int builtin_eval(char **argv) FAST_FUNC;
1127static int builtin_exec(char **argv) FAST_FUNC;
1128static int builtin_exit(char **argv) FAST_FUNC;
1129#if ENABLE_HUSH_EXPORT
1130static int builtin_export(char **argv) FAST_FUNC;
1131#endif
1132#if ENABLE_HUSH_READONLY
1133static int builtin_readonly(char **argv) FAST_FUNC;
1134#endif
1135#if ENABLE_HUSH_JOB
1136static int builtin_fg_bg(char **argv) FAST_FUNC;
1137static int builtin_jobs(char **argv) FAST_FUNC;
1138#endif
1139#if ENABLE_HUSH_GETOPTS
1140static int builtin_getopts(char **argv) FAST_FUNC;
1141#endif
1142#if ENABLE_HUSH_HELP
1143static int builtin_help(char **argv) FAST_FUNC;
1144#endif
1145#if MAX_HISTORY && ENABLE_FEATURE_EDITING
1146static int builtin_history(char **argv) FAST_FUNC;
1147#endif
1148#if ENABLE_HUSH_LOCAL
1149static int builtin_local(char **argv) FAST_FUNC;
1150#endif
1151#if ENABLE_HUSH_MEMLEAK
1152static int builtin_memleak(char **argv) FAST_FUNC;
1153#endif
1154#if ENABLE_HUSH_PRINTF
1155static int builtin_printf(char **argv) FAST_FUNC;
1156#endif
1157static int builtin_pwd(char **argv) FAST_FUNC;
1158#if ENABLE_HUSH_READ
1159static int builtin_read(char **argv) FAST_FUNC;
1160#endif
1161#if ENABLE_HUSH_SET
1162static int builtin_set(char **argv) FAST_FUNC;
1163#endif
1164static int builtin_shift(char **argv) FAST_FUNC;
1165static int builtin_source(char **argv) FAST_FUNC;
1166#if ENABLE_HUSH_TEST || BASH_TEST2
1167static int builtin_test(char **argv) FAST_FUNC;
1168#endif
1169#if ENABLE_HUSH_TRAP
1170static int builtin_trap(char **argv) FAST_FUNC;
1171#endif
1172#if ENABLE_HUSH_TYPE
1173static int builtin_type(char **argv) FAST_FUNC;
1174#endif
1175#if ENABLE_HUSH_TIMES
1176static int builtin_times(char **argv) FAST_FUNC;
1177#endif
1178static int builtin_true(char **argv) FAST_FUNC;
1179#if ENABLE_HUSH_UMASK
1180static int builtin_umask(char **argv) FAST_FUNC;
1181#endif
1182#if ENABLE_HUSH_UNSET
1183static int builtin_unset(char **argv) FAST_FUNC;
1184#endif
1185#if ENABLE_HUSH_KILL
1186static int builtin_kill(char **argv) FAST_FUNC;
1187#endif
1188#if ENABLE_HUSH_WAIT
1189static int builtin_wait(char **argv) FAST_FUNC;
1190#endif
1191#if ENABLE_HUSH_LOOPS
1192static int builtin_break(char **argv) FAST_FUNC;
1193static int builtin_continue(char **argv) FAST_FUNC;
1194#endif
1195#if ENABLE_HUSH_FUNCTIONS
1196static int builtin_return(char **argv) FAST_FUNC;
1197#endif
1198
1199/* Table of built-in functions. They can be forked or not, depending on
1200 * context: within pipes, they fork. As simple commands, they do not.
1201 * When used in non-forking context, they can change global variables
1202 * in the parent shell process. If forked, of course they cannot.
1203 * For example, 'unset foo | whatever' will parse and run, but foo will
1204 * still be set at the end. */
1205struct built_in_command {
1206 const char *b_cmd;
1207 int (*b_function)(char **argv) FAST_FUNC;
1208#if ENABLE_HUSH_HELP
1209 const char *b_descr;
1210# define BLTIN(cmd, func, help) { cmd, func, help }
1211#else
1212# define BLTIN(cmd, func, help) { cmd, func }
1213#endif
1214};
1215
1216static const struct built_in_command bltins1[] ALIGN_PTR = {
1217 BLTIN("." , builtin_source , "Run commands in file"),
1218 BLTIN(":" , builtin_true , NULL),
1219#if ENABLE_HUSH_JOB
1220 BLTIN("bg" , builtin_fg_bg , "Resume job in background"),
1221#endif
1222#if ENABLE_HUSH_LOOPS
1223 BLTIN("break" , builtin_break , "Exit loop"),
1224#endif
1225 BLTIN("cd" , builtin_cd , "Change directory"),
1226#if ENABLE_HUSH_LOOPS
1227 BLTIN("continue" , builtin_continue, "Start new loop iteration"),
1228#endif
1229 BLTIN("eval" , builtin_eval , "Construct and run shell command"),
1230 BLTIN("exec" , builtin_exec , "Execute command, don't return to shell"),
1231 BLTIN("exit" , builtin_exit , NULL),
1232#if ENABLE_HUSH_EXPORT
1233 BLTIN("export" , builtin_export , "Set environment variables"),
1234#endif
1235#if ENABLE_HUSH_JOB
1236 BLTIN("fg" , builtin_fg_bg , "Bring job to foreground"),
1237#endif
1238#if ENABLE_HUSH_GETOPTS
1239 BLTIN("getopts" , builtin_getopts , NULL),
1240#endif
1241#if ENABLE_HUSH_HELP
1242 BLTIN("help" , builtin_help , NULL),
1243#endif
1244#if MAX_HISTORY && ENABLE_FEATURE_EDITING
1245 BLTIN("history" , builtin_history , "Show history"),
1246#endif
1247#if ENABLE_HUSH_JOB
1248 BLTIN("jobs" , builtin_jobs , "List jobs"),
1249#endif
1250#if ENABLE_HUSH_KILL
1251 BLTIN("kill" , builtin_kill , "Send signals to processes"),
1252#endif
1253#if ENABLE_HUSH_LOCAL
1254 BLTIN("local" , builtin_local , "Set local variables"),
1255#endif
1256#if ENABLE_HUSH_MEMLEAK
1257 BLTIN("memleak" , builtin_memleak , NULL),
1258#endif
1259#if ENABLE_HUSH_READ
1260 BLTIN("read" , builtin_read , "Input into variable"),
1261#endif
1262#if ENABLE_HUSH_READONLY
1263 BLTIN("readonly" , builtin_readonly, "Make variables read-only"),
1264#endif
1265#if ENABLE_HUSH_FUNCTIONS
1266 BLTIN("return" , builtin_return , "Return from function"),
1267#endif
1268#if ENABLE_HUSH_SET
1269 BLTIN("set" , builtin_set , "Set positional parameters"),
1270#endif
1271 BLTIN("shift" , builtin_shift , "Shift positional parameters"),
1272#if BASH_SOURCE
1273 BLTIN("source" , builtin_source , NULL),
1274#endif
1275#if ENABLE_HUSH_TIMES
1276 BLTIN("times" , builtin_times , NULL),
1277#endif
1278#if ENABLE_HUSH_TRAP
1279 BLTIN("trap" , builtin_trap , "Trap signals"),
1280#endif
1281 BLTIN("true" , builtin_true , NULL),
1282#if ENABLE_HUSH_TYPE
1283 BLTIN("type" , builtin_type , "Show command type"),
1284#endif
1285#if ENABLE_HUSH_ULIMIT
1286 BLTIN("ulimit" , shell_builtin_ulimit, "Control resource limits"),
1287#endif
1288#if ENABLE_HUSH_UMASK
1289 BLTIN("umask" , builtin_umask , "Set file creation mask"),
1290#endif
1291#if ENABLE_HUSH_UNSET
1292 BLTIN("unset" , builtin_unset , "Unset variables"),
1293#endif
1294#if ENABLE_HUSH_WAIT
1295 BLTIN("wait" , builtin_wait , "Wait for process to finish"),
1296#endif
1297};
1298/* These builtins won't be used if we are on NOMMU and need to re-exec
1299 * (it's cheaper to run an external program in this case):
1300 */
1301static const struct built_in_command bltins2[] ALIGN_PTR = {
1302#if ENABLE_HUSH_TEST
1303 BLTIN("[" , builtin_test , NULL),
1304#endif
1305#if BASH_TEST2
1306 BLTIN("[[" , builtin_test , NULL),
1307#endif
1308#if ENABLE_HUSH_ECHO
1309 BLTIN("echo" , builtin_echo , NULL),
1310#endif
1311#if ENABLE_HUSH_PRINTF
1312 BLTIN("printf" , builtin_printf , NULL),
1313#endif
1314 BLTIN("pwd" , builtin_pwd , NULL),
1315#if ENABLE_HUSH_TEST
1316 BLTIN("test" , builtin_test , NULL),
1317#endif
1318};
1319
Francis Laniel8197f012023-12-22 22:02:28 +01001320#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01001321
1322/* Debug printouts.
1323 */
1324#if HUSH_DEBUG >= 2
1325/* prevent disasters with G.debug_indent < 0 */
1326# define indent() fdprintf(2, "%*s", (G.debug_indent * 2) & 0xff, "")
1327# define debug_enter() (G.debug_indent++)
1328# define debug_leave() (G.debug_indent--)
1329#else
1330# define indent() ((void)0)
1331# define debug_enter() ((void)0)
1332# define debug_leave() ((void)0)
1333#endif
1334
1335#ifndef debug_printf
1336# define debug_printf(...) (indent(), fdprintf(2, __VA_ARGS__))
1337#endif
1338
1339#ifndef debug_printf_parse
1340# define debug_printf_parse(...) (indent(), fdprintf(2, __VA_ARGS__))
1341#endif
1342
1343#ifndef debug_printf_heredoc
1344# define debug_printf_heredoc(...) (indent(), fdprintf(2, __VA_ARGS__))
1345#endif
1346
1347#ifndef debug_printf_exec
1348#define debug_printf_exec(...) (indent(), fdprintf(2, __VA_ARGS__))
1349#endif
1350
1351#ifndef debug_printf_env
1352# define debug_printf_env(...) (indent(), fdprintf(2, __VA_ARGS__))
1353#endif
1354
1355#ifndef debug_printf_jobs
1356# define debug_printf_jobs(...) (indent(), fdprintf(2, __VA_ARGS__))
1357# define DEBUG_JOBS 1
1358#else
1359# define DEBUG_JOBS 0
1360#endif
1361
1362#ifndef debug_printf_expand
1363# define debug_printf_expand(...) (indent(), fdprintf(2, __VA_ARGS__))
1364# define DEBUG_EXPAND 1
1365#else
1366# define DEBUG_EXPAND 0
1367#endif
1368
1369#ifndef debug_printf_varexp
1370# define debug_printf_varexp(...) (indent(), fdprintf(2, __VA_ARGS__))
1371#endif
1372
1373#ifndef debug_printf_glob
1374# define debug_printf_glob(...) (indent(), fdprintf(2, __VA_ARGS__))
1375# define DEBUG_GLOB 1
1376#else
1377# define DEBUG_GLOB 0
1378#endif
1379
1380#ifndef debug_printf_redir
1381# define debug_printf_redir(...) (indent(), fdprintf(2, __VA_ARGS__))
1382#endif
1383
1384#ifndef debug_printf_list
1385# define debug_printf_list(...) (indent(), fdprintf(2, __VA_ARGS__))
1386#endif
1387
1388#ifndef debug_printf_subst
1389# define debug_printf_subst(...) (indent(), fdprintf(2, __VA_ARGS__))
1390#endif
1391
1392#ifndef debug_printf_prompt
1393# define debug_printf_prompt(...) (indent(), fdprintf(2, __VA_ARGS__))
1394#endif
1395
1396#ifndef debug_printf_clean
1397# define debug_printf_clean(...) (indent(), fdprintf(2, __VA_ARGS__))
1398# define DEBUG_CLEAN 1
1399#else
1400# define DEBUG_CLEAN 0
1401#endif
1402
1403#if DEBUG_EXPAND
1404static void debug_print_strings(const char *prefix, char **vv)
1405{
1406 indent();
1407 fdprintf(2, "%s:\n", prefix);
1408 while (*vv)
1409 fdprintf(2, " '%s'\n", *vv++);
1410}
1411#else
1412# define debug_print_strings(prefix, vv) ((void)0)
1413#endif
1414
1415
1416/* Leak hunting. Use hush_leaktool.sh for post-processing.
1417 */
1418#if LEAK_HUNTING
1419static void *xxmalloc(int lineno, size_t size)
1420{
1421 void *ptr = xmalloc((size + 0xff) & ~0xff);
1422 fdprintf(2, "line %d: malloc %p\n", lineno, ptr);
1423 return ptr;
1424}
1425static void *xxrealloc(int lineno, void *ptr, size_t size)
1426{
1427 ptr = xrealloc(ptr, (size + 0xff) & ~0xff);
1428 fdprintf(2, "line %d: realloc %p\n", lineno, ptr);
1429 return ptr;
1430}
1431static char *xxstrdup(int lineno, const char *str)
1432{
1433 char *ptr = xstrdup(str);
1434 fdprintf(2, "line %d: strdup %p\n", lineno, ptr);
1435 return ptr;
1436}
1437static void xxfree(void *ptr)
1438{
1439 fdprintf(2, "free %p\n", ptr);
1440 free(ptr);
1441}
1442# define xmalloc(s) xxmalloc(__LINE__, s)
1443# define xrealloc(p, s) xxrealloc(__LINE__, p, s)
1444# define xstrdup(s) xxstrdup(__LINE__, s)
1445# define free(p) xxfree(p)
1446#endif
1447
1448
1449/* Syntax and runtime errors. They always abort scripts.
1450 * In interactive use they usually discard unparsed and/or unexecuted commands
1451 * and return to the prompt.
1452 * HUSH_DEBUG >= 2 prints line number in this file where it was detected.
1453 */
1454#if HUSH_DEBUG < 2
1455# define msg_and_die_if_script(lineno, ...) msg_and_die_if_script(__VA_ARGS__)
1456# define syntax_error(lineno, msg) syntax_error(msg)
1457# define syntax_error_at(lineno, msg) syntax_error_at(msg)
1458# define syntax_error_unterm_ch(lineno, ch) syntax_error_unterm_ch(ch)
1459# define syntax_error_unterm_str(lineno, s) syntax_error_unterm_str(s)
1460# define syntax_error_unexpected_ch(lineno, ch) syntax_error_unexpected_ch(ch)
1461#endif
1462
1463static void die_if_script(void)
1464{
1465 if (!G_interactive_fd) {
1466 if (G.last_exitcode) /* sometines it's 2, not 1 (bash compat) */
1467 xfunc_error_retval = G.last_exitcode;
1468 xfunc_die();
1469 }
1470}
1471
Francis Laniel8197f012023-12-22 22:02:28 +01001472#ifdef __U_BOOT__
1473static void __maybe_unused msg_and_die_if_script(unsigned lineno, const char *fmt, ...)
1474#else /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01001475static void msg_and_die_if_script(unsigned lineno, const char *fmt, ...)
Francis Laniel8197f012023-12-22 22:02:28 +01001476#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01001477{
1478 va_list p;
1479
1480#if HUSH_DEBUG >= 2
1481 bb_error_msg("hush.c:%u", lineno);
1482#endif
1483 va_start(p, fmt);
1484 bb_verror_msg(fmt, p, NULL);
1485 va_end(p);
1486 die_if_script();
1487}
1488
Francis Laniel8197f012023-12-22 22:02:28 +01001489#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01001490static void syntax_error(unsigned lineno UNUSED_PARAM, const char *msg)
1491{
1492 if (msg)
1493 bb_error_msg("syntax error: %s", msg);
1494 else
1495 bb_simple_error_msg("syntax error");
1496 die_if_script();
1497}
Francis Laniel8197f012023-12-22 22:02:28 +01001498#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01001499
1500static void syntax_error_at(unsigned lineno UNUSED_PARAM, const char *msg)
1501{
1502 bb_error_msg("syntax error at '%s'", msg);
1503 die_if_script();
1504}
1505
1506static void syntax_error_unterm_str(unsigned lineno UNUSED_PARAM, const char *s)
1507{
1508 bb_error_msg("syntax error: unterminated %s", s);
1509//? source4.tests fails: in bash, echo ${^} in script does not terminate the script
1510// die_if_script();
1511}
1512
1513static void syntax_error_unterm_ch(unsigned lineno, char ch)
1514{
1515 char msg[2] = { ch, '\0' };
1516 syntax_error_unterm_str(lineno, msg);
1517}
1518
1519static void syntax_error_unexpected_ch(unsigned lineno UNUSED_PARAM, int ch)
1520{
1521 char msg[2];
1522 msg[0] = ch;
1523 msg[1] = '\0';
1524#if HUSH_DEBUG >= 2
1525 bb_error_msg("hush.c:%u", lineno);
1526#endif
1527 bb_error_msg("syntax error: unexpected %s", ch == EOF ? "EOF" : msg);
1528 die_if_script();
1529}
1530
1531#if HUSH_DEBUG < 2
1532# undef msg_and_die_if_script
1533# undef syntax_error
1534# undef syntax_error_at
1535# undef syntax_error_unterm_ch
1536# undef syntax_error_unterm_str
1537# undef syntax_error_unexpected_ch
1538#else
1539# define msg_and_die_if_script(...) msg_and_die_if_script(__LINE__, __VA_ARGS__)
1540# define syntax_error(msg) syntax_error(__LINE__, msg)
1541# define syntax_error_at(msg) syntax_error_at(__LINE__, msg)
1542# define syntax_error_unterm_ch(ch) syntax_error_unterm_ch(__LINE__, ch)
1543# define syntax_error_unterm_str(s) syntax_error_unterm_str(__LINE__, s)
1544# define syntax_error_unexpected_ch(ch) syntax_error_unexpected_ch(__LINE__, ch)
1545#endif
1546
Francis Lanielb234f7e2023-12-22 22:02:27 +01001547/* Utility functions
1548 */
1549/* Replace each \x with x in place, return ptr past NUL. */
1550static char *unbackslash(char *src)
1551{
Francis Laniel8197f012023-12-22 22:02:28 +01001552#ifdef __U_BOOT__
1553 char *dst = src = (char *)strchrnul(src, '\\');
1554#else /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01001555 char *dst = src = strchrnul(src, '\\');
Francis Laniel8197f012023-12-22 22:02:28 +01001556#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01001557 while (1) {
1558 if (*src == '\\') {
1559 src++;
1560 if (*src != '\0') {
1561 /* \x -> x */
1562 *dst++ = *src++;
1563 continue;
1564 }
1565 /* else: "\<nul>". Do not delete this backslash.
1566 * Testcase: eval 'echo ok\'
1567 */
1568 *dst++ = '\\';
1569 /* fallthrough */
1570 }
1571 if ((*dst++ = *src++) == '\0')
1572 break;
1573 }
1574 return dst;
1575}
1576
1577static char **add_strings_to_strings(char **strings, char **add, int need_to_dup)
1578{
1579 int i;
1580 unsigned count1;
1581 unsigned count2;
1582 char **v;
1583
1584 v = strings;
1585 count1 = 0;
1586 if (v) {
1587 while (*v) {
1588 count1++;
1589 v++;
1590 }
1591 }
1592 count2 = 0;
1593 v = add;
1594 while (*v) {
1595 count2++;
1596 v++;
1597 }
1598 v = xrealloc(strings, (count1 + count2 + 1) * sizeof(char*));
1599 v[count1 + count2] = NULL;
1600 i = count2;
1601 while (--i >= 0)
1602 v[count1 + i] = (need_to_dup ? xstrdup(add[i]) : add[i]);
1603 return v;
1604}
1605#if LEAK_HUNTING
1606static char **xx_add_strings_to_strings(int lineno, char **strings, char **add, int need_to_dup)
1607{
1608 char **ptr = add_strings_to_strings(strings, add, need_to_dup);
1609 fdprintf(2, "line %d: add_strings_to_strings %p\n", lineno, ptr);
1610 return ptr;
1611}
1612#define add_strings_to_strings(strings, add, need_to_dup) \
1613 xx_add_strings_to_strings(__LINE__, strings, add, need_to_dup)
1614#endif
1615
1616/* Note: takes ownership of "add" ptr (it is not strdup'ed) */
1617static char **add_string_to_strings(char **strings, char *add)
1618{
1619 char *v[2];
1620 v[0] = add;
1621 v[1] = NULL;
1622 return add_strings_to_strings(strings, v, /*dup:*/ 0);
1623}
Francis Laniel8197f012023-12-22 22:02:28 +01001624
Francis Lanielb234f7e2023-12-22 22:02:27 +01001625#if LEAK_HUNTING
1626static char **xx_add_string_to_strings(int lineno, char **strings, char *add)
1627{
1628 char **ptr = add_string_to_strings(strings, add);
1629 fdprintf(2, "line %d: add_string_to_strings %p\n", lineno, ptr);
1630 return ptr;
1631}
1632#define add_string_to_strings(strings, add) \
1633 xx_add_string_to_strings(__LINE__, strings, add)
1634#endif
1635
1636static void free_strings(char **strings)
1637{
1638 char **v;
1639
1640 if (!strings)
1641 return;
1642 v = strings;
1643 while (*v) {
1644 free(*v);
1645 v++;
1646 }
1647 free(strings);
1648}
1649
Francis Laniel8197f012023-12-22 22:02:28 +01001650#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01001651static int dup_CLOEXEC(int fd, int avoid_fd)
1652{
1653 int newfd;
1654 repeat:
1655 newfd = fcntl(fd, F_DUPFD_CLOEXEC, avoid_fd + 1);
1656 if (newfd >= 0) {
1657 if (F_DUPFD_CLOEXEC == F_DUPFD) /* if old libc (w/o F_DUPFD_CLOEXEC) */
1658 fcntl(newfd, F_SETFD, FD_CLOEXEC);
1659 } else { /* newfd < 0 */
1660 if (errno == EBUSY)
1661 goto repeat;
1662 if (errno == EINTR)
1663 goto repeat;
1664 }
1665 return newfd;
1666}
1667
1668static int xdup_CLOEXEC_and_close(int fd, int avoid_fd)
1669{
1670 int newfd;
1671 repeat:
1672 newfd = fcntl(fd, F_DUPFD_CLOEXEC, avoid_fd + 1);
1673 if (newfd < 0) {
1674 if (errno == EBUSY)
1675 goto repeat;
1676 if (errno == EINTR)
1677 goto repeat;
1678 /* fd was not open? */
1679 if (errno == EBADF)
1680 return fd;
1681 xfunc_die();
1682 }
1683 if (F_DUPFD_CLOEXEC == F_DUPFD) /* if old libc (w/o F_DUPFD_CLOEXEC) */
1684 fcntl(newfd, F_SETFD, FD_CLOEXEC);
1685 close(fd);
1686 return newfd;
1687}
1688
1689
1690/* Manipulating HFILEs */
1691static HFILE *hfopen(const char *name)
1692{
1693 HFILE *fp;
1694 int fd;
1695
1696 fd = STDIN_FILENO;
1697 if (name) {
1698 fd = open(name, O_RDONLY | O_CLOEXEC);
1699 if (fd < 0)
1700 return NULL;
1701 if (O_CLOEXEC == 0) /* ancient libc */
1702 close_on_exec_on(fd);
1703 }
1704
1705 fp = xmalloc(sizeof(*fp));
1706 if (name == NULL)
1707 G.HFILE_stdin = fp;
1708 fp->fd = fd;
1709 fp->cur = fp->end = fp->buf;
1710 fp->next_hfile = G.HFILE_list;
1711 G.HFILE_list = fp;
1712 return fp;
1713}
1714static void hfclose(HFILE *fp)
1715{
1716 HFILE **pp = &G.HFILE_list;
1717 while (*pp) {
1718 HFILE *cur = *pp;
1719 if (cur == fp) {
1720 *pp = cur->next_hfile;
1721 break;
1722 }
1723 pp = &cur->next_hfile;
1724 }
1725 if (fp->fd >= 0)
1726 close(fp->fd);
1727 free(fp);
1728}
1729static int refill_HFILE_and_getc(HFILE *fp)
1730{
1731 int n;
1732
1733 if (fp->fd < 0) {
1734 /* Already saw EOF */
1735 return EOF;
1736 }
1737#if ENABLE_HUSH_INTERACTIVE && !ENABLE_FEATURE_EDITING
1738 /* If user presses ^C, read() restarts after SIGINT (we use SA_RESTART).
1739 * IOW: ^C will not immediately stop line input.
1740 * But poll() is different: it does NOT restart after signals.
1741 */
1742 if (fp == G.HFILE_stdin) {
1743 struct pollfd pfd[1];
1744 pfd[0].fd = fp->fd;
1745 pfd[0].events = POLLIN;
1746 n = poll(pfd, 1, -1);
1747 if (n < 0
1748 /*&& errno == EINTR - assumed true */
1749 && sigismember(&G.pending_set, SIGINT)
1750 ) {
1751 return '\0';
1752 }
1753 }
1754#else
1755/* if FEATURE_EDITING=y, we do not use this routine for interactive input */
1756#endif
1757 /* Try to buffer more input */
1758 n = safe_read(fp->fd, fp->buf, sizeof(fp->buf));
1759 if (n < 0) {
1760 bb_simple_perror_msg("read error");
1761 n = 0;
1762 }
1763 fp->cur = fp->buf;
1764 fp->end = fp->buf + n;
1765 if (n == 0) {
1766 /* EOF/error */
1767 close(fp->fd);
1768 fp->fd = -1;
1769 return EOF;
1770 }
1771 return (unsigned char)(*fp->cur++);
1772}
1773/* Inlined for common case of non-empty buffer.
1774 */
1775static ALWAYS_INLINE int hfgetc(HFILE *fp)
1776{
1777 if (fp->cur < fp->end)
1778 return (unsigned char)(*fp->cur++);
1779 /* Buffer empty */
1780 return refill_HFILE_and_getc(fp);
1781}
1782static int move_HFILEs_on_redirect(int fd, int avoid_fd)
1783{
1784 HFILE *fl = G.HFILE_list;
1785 while (fl) {
1786 if (fd == fl->fd) {
1787 /* We use it only on script files, they are all CLOEXEC */
1788 fl->fd = xdup_CLOEXEC_and_close(fd, avoid_fd);
1789 debug_printf_redir("redirect_fd %d: matches a script fd, moving it to %d\n", fd, fl->fd);
1790 return 1; /* "found and moved" */
1791 }
1792 fl = fl->next_hfile;
1793 }
1794#if ENABLE_HUSH_MODE_X
1795 if (G.x_mode_fd > 0 && fd == G.x_mode_fd) {
1796 G.x_mode_fd = xdup_CLOEXEC_and_close(fd, avoid_fd);
1797 return 1; /* "found and moved" */
1798 }
1799#endif
1800 return 0; /* "not in the list" */
1801}
1802#if ENABLE_FEATURE_SH_STANDALONE && BB_MMU
1803static void close_all_HFILE_list(void)
1804{
1805 HFILE *fl = G.HFILE_list;
1806 while (fl) {
1807 /* hfclose would also free HFILE object.
1808 * It is disastrous if we share memory with a vforked parent.
1809 * I'm not sure we never come here after vfork.
1810 * Therefore just close fd, nothing more.
1811 *
1812 * ">" instead of ">=": we don't close fd#0,
1813 * interactive shell uses hfopen(NULL) as stdin input
1814 * which has fl->fd == 0, but fd#0 gets redirected in pipes.
1815 * If we'd close it here, then e.g. interactive "set | sort"
1816 * with NOFORKed sort, would have sort's input fd closed.
1817 */
1818 if (fl->fd > 0)
1819 /*hfclose(fl); - unsafe */
1820 close(fl->fd);
1821 fl = fl->next_hfile;
1822 }
1823}
1824#endif
1825static int fd_in_HFILEs(int fd)
1826{
1827 HFILE *fl = G.HFILE_list;
1828 while (fl) {
1829 if (fl->fd == fd)
1830 return 1;
1831 fl = fl->next_hfile;
1832 }
1833 return 0;
1834}
1835
Francis Laniel8197f012023-12-22 22:02:28 +01001836#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01001837
1838/* Helpers for setting new $n and restoring them back
1839 */
1840typedef struct save_arg_t {
1841 char *sv_argv0;
1842 char **sv_g_argv;
1843 int sv_g_argc;
Francis Laniel8197f012023-12-22 22:02:28 +01001844#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01001845 IF_HUSH_SET(smallint sv_g_malloced;)
Francis Laniel8197f012023-12-22 22:02:28 +01001846#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01001847} save_arg_t;
1848
Francis Laniel8197f012023-12-22 22:02:28 +01001849#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01001850static void save_and_replace_G_args(save_arg_t *sv, char **argv)
1851{
1852 sv->sv_argv0 = argv[0];
1853 sv->sv_g_argv = G.global_argv;
1854 sv->sv_g_argc = G.global_argc;
1855 IF_HUSH_SET(sv->sv_g_malloced = G.global_args_malloced;)
1856
1857 argv[0] = G.global_argv[0]; /* retain $0 */
1858 G.global_argv = argv;
1859 IF_HUSH_SET(G.global_args_malloced = 0;)
1860
1861 G.global_argc = 1 + string_array_len(argv + 1);
1862}
1863
1864static void restore_G_args(save_arg_t *sv, char **argv)
1865{
1866#if ENABLE_HUSH_SET
1867 if (G.global_args_malloced) {
1868 /* someone ran "set -- arg1 arg2 ...", undo */
1869 char **pp = G.global_argv;
1870 while (*++pp) /* note: does not free $0 */
1871 free(*pp);
1872 free(G.global_argv);
1873 }
1874#endif
1875 argv[0] = sv->sv_argv0;
1876 G.global_argv = sv->sv_g_argv;
1877 G.global_argc = sv->sv_g_argc;
1878 IF_HUSH_SET(G.global_args_malloced = sv->sv_g_malloced;)
1879}
Francis Laniel8197f012023-12-22 22:02:28 +01001880#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01001881
1882
Francis Laniel8197f012023-12-22 22:02:28 +01001883#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01001884/* Basic theory of signal handling in shell
1885 * ========================================
1886 * This does not describe what hush does, rather, it is current understanding
1887 * what it _should_ do. If it doesn't, it's a bug.
1888 * http://www.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#trap
1889 *
1890 * Signals are handled only after each pipe ("cmd | cmd | cmd" thing)
1891 * is finished or backgrounded. It is the same in interactive and
1892 * non-interactive shells, and is the same regardless of whether
1893 * a user trap handler is installed or a shell special one is in effect.
1894 * ^C or ^Z from keyboard seems to execute "at once" because it usually
1895 * backgrounds (i.e. stops) or kills all members of currently running
1896 * pipe.
1897 *
1898 * Wait builtin is interruptible by signals for which user trap is set
1899 * or by SIGINT in interactive shell.
1900 *
1901 * Trap handlers will execute even within trap handlers. (right?)
1902 *
1903 * User trap handlers are forgotten when subshell ("(cmd)") is entered,
1904 * except for handlers set to '' (empty string).
1905 *
1906 * If job control is off, backgrounded commands ("cmd &")
1907 * have SIGINT, SIGQUIT set to SIG_IGN.
1908 *
1909 * Commands which are run in command substitution ("`cmd`")
1910 * have SIGTTIN, SIGTTOU, SIGTSTP set to SIG_IGN.
1911 *
1912 * Ordinary commands have signals set to SIG_IGN/DFL as inherited
1913 * by the shell from its parent.
1914 *
1915 * Signals which differ from SIG_DFL action
1916 * (note: child (i.e., [v]forked) shell is not an interactive shell):
1917 *
1918 * SIGQUIT: ignore
1919 * SIGTERM (interactive): ignore
1920 * SIGHUP (interactive):
1921 * send SIGCONT to stopped jobs, send SIGHUP to all jobs and exit
1922 * 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);
2037#if ENABLE_HUSH_FAST
2038 if (sig == SIGCHLD) {
2039 G.count_SIGCHLD++;
2040//bb_error_msg("[%d] SIGCHLD_handler: G.count_SIGCHLD:%d G.handled_SIGCHLD:%d", getpid(), G.count_SIGCHLD, G.handled_SIGCHLD);
2041 }
2042#endif
2043}
2044
2045static sighandler_t install_sighandler(int sig, sighandler_t handler)
2046{
2047 struct sigaction old_sa;
2048
2049 /* We could use signal() to install handlers... almost:
2050 * except that we need to mask ALL signals while handlers run.
2051 * I saw signal nesting in strace, race window isn't small.
2052 * SA_RESTART is also needed, but in Linux, signal()
2053 * sets SA_RESTART too.
2054 */
2055 /* memset(&G.sa, 0, sizeof(G.sa)); - already done */
2056 /* sigfillset(&G.sa.sa_mask); - already done */
2057 /* G.sa.sa_flags = SA_RESTART; - already done */
2058 G.sa.sa_handler = handler;
2059 sigaction(sig, &G.sa, &old_sa);
2060 return old_sa.sa_handler;
2061}
Francis Laniel8197f012023-12-22 22:02:28 +01002062#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01002063
Francis Laniel8197f012023-12-22 22:02:28 +01002064#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01002065static void hush_exit(int exitcode) NORETURN;
2066
2067static void restore_ttypgrp_and__exit(void) NORETURN;
2068static void restore_ttypgrp_and__exit(void)
2069{
2070 /* xfunc has failed! die die die */
2071 /* no EXIT traps, this is an escape hatch! */
2072 G.exiting = 1;
2073 hush_exit(xfunc_error_retval);
2074}
2075
2076#if ENABLE_HUSH_JOB
2077
2078/* Needed only on some libc:
2079 * It was observed that on exit(), fgetc'ed buffered data
2080 * gets "unwound" via lseek(fd, -NUM, SEEK_CUR).
2081 * With the net effect that even after fork(), not vfork(),
2082 * exit() in NOEXECed applet in "sh SCRIPT":
2083 * noexec_applet_here
2084 * echo END_OF_SCRIPT
2085 * lseeks fd in input FILE object from EOF to "e" in "echo END_OF_SCRIPT".
2086 * This makes "echo END_OF_SCRIPT" executed twice.
2087 * Similar problems can be seen with msg_and_die_if_script() -> xfunc_die()
2088 * and in `cmd` handling.
2089 * If set as die_func(), this makes xfunc_die() exit via _exit(), not exit():
2090 */
2091static void fflush_and__exit(void) NORETURN;
2092static void fflush_and__exit(void)
2093{
2094 fflush_all();
2095 _exit(xfunc_error_retval);
2096}
2097
2098/* After [v]fork, in child: do not restore tty pgrp on xfunc death */
2099# define disable_restore_tty_pgrp_on_exit() (die_func = fflush_and__exit)
2100/* After [v]fork, in parent: restore tty pgrp on xfunc death */
2101# define enable_restore_tty_pgrp_on_exit() (die_func = restore_ttypgrp_and__exit)
2102
2103/* Restores tty foreground process group, and exits.
2104 * May be called as signal handler for fatal signal
2105 * (will resend signal to itself, producing correct exit state)
2106 * or called directly with -EXITCODE.
2107 * We also call it if xfunc is exiting.
2108 */
2109static void sigexit(int sig) NORETURN;
2110static void sigexit(int sig)
2111{
2112 /* Careful: we can end up here after [v]fork. Do not restore
2113 * tty pgrp then, only top-level shell process does that */
2114 if (G_saved_tty_pgrp && getpid() == G.root_pid) {
2115 /* Disable all signals: job control, SIGPIPE, etc.
2116 * Mostly paranoid measure, to prevent infinite SIGTTOU.
2117 */
2118 sigprocmask_allsigs(SIG_BLOCK);
2119 tcsetpgrp(G_interactive_fd, G_saved_tty_pgrp);
2120 }
2121
2122 /* Not a signal, just exit */
2123 if (sig <= 0)
2124 _exit(- sig);
2125
2126 kill_myself_with_sig(sig); /* does not return */
2127}
2128#else
2129
2130# define disable_restore_tty_pgrp_on_exit() ((void)0)
2131# define enable_restore_tty_pgrp_on_exit() ((void)0)
2132
2133#endif
Francis Laniel8197f012023-12-22 22:02:28 +01002134#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01002135
Francis Laniel8197f012023-12-22 22:02:28 +01002136#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01002137static sighandler_t pick_sighandler(unsigned sig)
2138{
2139 sighandler_t handler = SIG_DFL;
2140 if (sig < sizeof(unsigned)*8) {
2141 unsigned sigmask = (1 << sig);
2142
2143#if ENABLE_HUSH_JOB
2144 /* is sig fatal? */
2145 if (G_fatal_sig_mask & sigmask)
2146 handler = sigexit;
2147 else
2148#endif
2149 /* sig has special handling? */
2150 if (G.special_sig_mask & sigmask) {
2151 handler = record_pending_signo;
2152 /* TTIN/TTOU/TSTP can't be set to record_pending_signo
2153 * in order to ignore them: they will be raised
2154 * in an endless loop when we try to do some
2155 * terminal ioctls! We do have to _ignore_ these.
2156 */
2157 if (SPECIAL_JOBSTOP_SIGS & sigmask)
2158 handler = SIG_IGN;
2159 }
2160 }
2161 return handler;
2162}
Francis Laniel8197f012023-12-22 22:02:28 +01002163#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01002164
Francis Laniel8197f012023-12-22 22:02:28 +01002165#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01002166/* Restores tty foreground process group, and exits. */
2167static void hush_exit(int exitcode)
2168{
2169#if ENABLE_FEATURE_EDITING_SAVE_ON_EXIT
2170 save_history(G.line_input_state); /* may be NULL */
2171#endif
2172
2173 fflush_all();
2174 if (G.exiting <= 0 && G_traps && G_traps[0] && G_traps[0][0]) {
2175 char *argv[3];
2176 /* argv[0] is unused */
2177 argv[1] = xstrdup(G_traps[0]); /* copy, since EXIT trap handler may modify G_traps[0] */
2178 argv[2] = NULL;
2179 G.exiting = 1; /* prevent EXIT trap recursion */
2180 /* Note: G_traps[0] is not cleared!
2181 * "trap" will still show it, if executed
2182 * in the handler */
2183 builtin_eval(argv);
2184 }
2185
2186#if ENABLE_FEATURE_CLEAN_UP
2187 {
2188 struct variable *cur_var;
2189 if (G.cwd != bb_msg_unknown)
2190 free((char*)G.cwd);
2191 cur_var = G.top_var;
2192 while (cur_var) {
2193 struct variable *tmp = cur_var;
2194 if (!cur_var->max_len)
2195 free(cur_var->varstr);
2196 cur_var = cur_var->next;
2197 free(tmp);
2198 }
2199 }
2200#endif
2201
2202 fflush_all();
2203#if ENABLE_HUSH_JOB
2204 sigexit(- (exitcode & 0xff));
2205#else
2206 _exit(exitcode);
2207#endif
2208}
2209
2210//TODO: return a mask of ALL handled sigs?
2211static int check_and_run_traps(void)
2212{
2213 int last_sig = 0;
2214
2215 while (1) {
2216 int sig;
2217
2218 if (sigisemptyset(&G.pending_set))
2219 break;
2220 sig = 0;
2221 do {
2222 sig++;
2223 if (sigismember(&G.pending_set, sig)) {
2224 sigdelset(&G.pending_set, sig);
2225 goto got_sig;
2226 }
2227 } while (sig < NSIG);
2228 break;
2229 got_sig:
2230#if ENABLE_HUSH_TRAP
2231 if (G_traps && G_traps[sig]) {
2232 debug_printf_exec("%s: sig:%d handler:'%s'\n", __func__, sig, G.traps[sig]);
2233 if (G_traps[sig][0]) {
2234 /* We have user-defined handler */
2235 smalluint save_rcode;
2236 int save_pre;
2237 char *argv[3];
2238 /* argv[0] is unused */
2239 argv[1] = xstrdup(G_traps[sig]);
2240 /* why strdup? trap can modify itself: trap 'trap "echo oops" INT' INT */
2241 argv[2] = NULL;
2242 save_pre = G.pre_trap_exitcode;
2243 G.pre_trap_exitcode = save_rcode = G.last_exitcode;
2244 builtin_eval(argv);
2245 free(argv[1]);
2246 G.pre_trap_exitcode = save_pre;
2247 G.last_exitcode = save_rcode;
2248# if ENABLE_HUSH_FUNCTIONS
2249 if (G.return_exitcode >= 0) {
2250 debug_printf_exec("trap exitcode:%d\n", G.return_exitcode);
2251 G.last_exitcode = G.return_exitcode;
2252 }
2253# endif
2254 last_sig = sig;
2255 } /* else: "" trap, ignoring signal */
2256 continue;
2257 }
2258#endif
2259 /* not a trap: special action */
2260 switch (sig) {
2261 case SIGINT:
2262 debug_printf_exec("%s: sig:%d default SIGINT handler\n", __func__, sig);
2263 G.flag_SIGINT = 1;
2264 last_sig = sig;
2265 break;
2266#if ENABLE_HUSH_JOB
2267 case SIGHUP: {
2268//TODO: why are we doing this? ash and dash don't do this,
2269//they have no handler for SIGHUP at all,
2270//they rely on kernel to send SIGHUP+SIGCONT to orphaned process groups
2271 struct pipe *job;
2272 debug_printf_exec("%s: sig:%d default SIGHUP handler\n", __func__, sig);
2273 /* bash is observed to signal whole process groups,
2274 * not individual processes */
2275 for (job = G.job_list; job; job = job->next) {
2276 if (job->pgrp <= 0)
2277 continue;
2278 debug_printf_exec("HUPing pgrp %d\n", job->pgrp);
2279 if (kill(- job->pgrp, SIGHUP) == 0)
2280 kill(- job->pgrp, SIGCONT);
2281 }
2282 sigexit(SIGHUP);
2283 }
2284#endif
2285#if ENABLE_HUSH_FAST
2286 case SIGCHLD:
2287 debug_printf_exec("%s: sig:%d default SIGCHLD handler\n", __func__, sig);
2288 G.count_SIGCHLD++;
2289//bb_error_msg("[%d] check_and_run_traps: G.count_SIGCHLD:%d G.handled_SIGCHLD:%d", getpid(), G.count_SIGCHLD, G.handled_SIGCHLD);
2290 /* Note:
2291 * We don't do 'last_sig = sig' here -> NOT returning this sig.
2292 * This simplifies wait builtin a bit.
2293 */
2294 break;
2295#endif
2296 default: /* ignored: */
2297 debug_printf_exec("%s: sig:%d default handling is to ignore\n", __func__, sig);
2298 /* SIGTERM, SIGQUIT, SIGTTIN, SIGTTOU, SIGTSTP */
2299 /* Note:
2300 * We don't do 'last_sig = sig' here -> NOT returning this sig.
2301 * Example: wait is not interrupted by TERM
2302 * in interactive shell, because TERM is ignored.
2303 */
2304 break;
2305 }
2306 }
2307 return last_sig;
2308}
2309
2310
2311static const char *get_cwd(int force)
2312{
2313 if (force || G.cwd == NULL) {
2314 /* xrealloc_getcwd_or_warn(arg) calls free(arg),
2315 * we must not try to free(bb_msg_unknown) */
2316 if (G.cwd == bb_msg_unknown)
2317 G.cwd = NULL;
2318 G.cwd = xrealloc_getcwd_or_warn((char *)G.cwd);
2319 if (!G.cwd)
2320 G.cwd = bb_msg_unknown;
2321 }
2322 return G.cwd;
2323}
2324
Francis Laniel8197f012023-12-22 22:02:28 +01002325#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01002326
2327/*
2328 * Shell and environment variable support
2329 */
2330static struct variable **get_ptr_to_local_var(const char *name, unsigned len)
2331{
2332 struct variable **pp;
2333 struct variable *cur;
2334
2335 pp = &G.top_var;
2336 while ((cur = *pp) != NULL) {
2337 if (strncmp(cur->varstr, name, len) == 0 && cur->varstr[len] == '=')
2338 return pp;
2339 pp = &cur->next;
2340 }
2341 return NULL;
2342}
2343
2344static const char* FAST_FUNC get_local_var_value(const char *name)
2345{
2346 struct variable **vpp;
2347 unsigned len = strlen(name);
2348
2349 if (G.expanded_assignments) {
2350 char **cpp = G.expanded_assignments;
2351 while (*cpp) {
2352 char *cp = *cpp;
2353 if (strncmp(cp, name, len) == 0 && cp[len] == '=')
2354 return cp + len + 1;
2355 cpp++;
2356 }
2357 }
2358
2359 vpp = get_ptr_to_local_var(name, len);
2360 if (vpp)
2361 return (*vpp)->varstr + len + 1;
2362
Francis Laniel8197f012023-12-22 22:02:28 +01002363#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01002364 if (strcmp(name, "PPID") == 0)
2365 return utoa(G.root_ppid);
Francis Laniel8197f012023-12-22 22:02:28 +01002366#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01002367 // bash compat: UID? EUID?
2368#if ENABLE_HUSH_RANDOM_SUPPORT
2369 if (strcmp(name, "RANDOM") == 0)
2370 return utoa(next_random(&G.random_gen));
2371#endif
2372#if ENABLE_HUSH_LINENO_VAR
2373 if (strcmp(name, "LINENO") == 0)
2374 return utoa(G.execute_lineno);
2375#endif
2376#if BASH_EPOCH_VARS
2377 {
2378 const char *fmt = NULL;
2379 if (strcmp(name, "EPOCHSECONDS") == 0)
2380 fmt = "%llu";
2381 else if (strcmp(name, "EPOCHREALTIME") == 0)
2382 fmt = "%llu.%06u";
2383 if (fmt) {
2384 struct timeval tv;
2385 xgettimeofday(&tv);
2386 sprintf(G.epoch_buf, fmt, (unsigned long long)tv.tv_sec,
2387 (unsigned)tv.tv_usec);
2388 return G.epoch_buf;
2389 }
2390 }
2391#endif
2392 return NULL;
2393}
2394
Francis Laniel8197f012023-12-22 22:02:28 +01002395#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01002396#if ENABLE_HUSH_GETOPTS
2397static void handle_changed_special_names(const char *name, unsigned name_len)
2398{
2399 if (name_len == 6) {
2400 if (strncmp(name, "OPTIND", 6) == 0) {
2401 G.getopt_count = 0;
2402 return;
2403 }
2404 }
2405}
2406#else
2407/* Do not even bother evaluating arguments */
2408# define handle_changed_special_names(...) ((void)0)
2409#endif
Francis Laniel8197f012023-12-22 22:02:28 +01002410#else /* __U_BOOT__ */
2411/* Do not even bother evaluating arguments */
2412# define handle_changed_special_names(...) ((void)0)
2413#endif /* __U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01002414
2415/* str holds "NAME=VAL" and is expected to be malloced.
2416 * We take ownership of it.
2417 */
Francis Laniel8197f012023-12-22 22:02:28 +01002418#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01002419#define SETFLAG_EXPORT (1 << 0)
2420#define SETFLAG_UNEXPORT (1 << 1)
2421#define SETFLAG_MAKE_RO (1 << 2)
Francis Laniel8197f012023-12-22 22:02:28 +01002422#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01002423#define SETFLAG_VARLVL_SHIFT 3
Francis Laniel8197f012023-12-22 22:02:28 +01002424#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01002425static int set_local_var(char *str, unsigned flags)
Francis Laniel8197f012023-12-22 22:02:28 +01002426#else /* __U_BOOT__ */
2427int set_local_var_modern(char *str, int flags)
2428#endif /* __U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01002429{
2430 struct variable **cur_pp;
2431 struct variable *cur;
2432 char *free_me = NULL;
2433 char *eq_sign;
2434 int name_len;
2435 int retval;
Francis Laniel8197f012023-12-22 22:02:28 +01002436#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01002437 unsigned local_lvl = (flags >> SETFLAG_VARLVL_SHIFT);
Francis Laniel8197f012023-12-22 22:02:28 +01002438#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01002439
2440 eq_sign = strchr(str, '=');
2441 if (HUSH_DEBUG && !eq_sign)
2442 bb_simple_error_msg_and_die("BUG in setvar");
2443
2444 name_len = eq_sign - str + 1; /* including '=' */
2445 cur_pp = &G.top_var;
2446 while ((cur = *cur_pp) != NULL) {
2447 if (strncmp(cur->varstr, str, name_len) != 0) {
2448 cur_pp = &cur->next;
2449 continue;
2450 }
2451
Francis Laniel8197f012023-12-22 22:02:28 +01002452#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01002453 /* We found an existing var with this name */
2454 if (cur->flg_read_only) {
2455 bb_error_msg("%s: readonly variable", str);
2456 free(str);
2457//NOTE: in bash, assignment in "export READONLY_VAR=Z" fails, and sets $?=1,
2458//but export per se succeeds (does put the var in env). We don't mimic that.
2459 return -1;
2460 }
2461 if (flags & SETFLAG_UNEXPORT) { // && cur->flg_export ?
2462 debug_printf_env("%s: unsetenv '%s'\n", __func__, str);
2463 *eq_sign = '\0';
2464 unsetenv(str);
2465 *eq_sign = '=';
2466 }
2467 if (cur->var_nest_level < local_lvl) {
2468 /* bash 3.2.33(1) and exported vars:
2469 * # export z=z
2470 * # f() { local z=a; env | grep ^z; }
2471 * # f
2472 * z=a
2473 * # env | grep ^z
2474 * z=z
2475 */
2476 if (cur->flg_export)
2477 flags |= SETFLAG_EXPORT;
2478 /* New variable is local ("local VAR=VAL" or
2479 * "VAR=VAL cmd")
2480 * and existing one is global, or local
2481 * on a lower level that new one.
2482 * Remove it from global variable list:
2483 */
2484 *cur_pp = cur->next;
2485 if (G.shadowed_vars_pp) {
2486 /* Save in "shadowed" list */
2487 debug_printf_env("shadowing %s'%s'/%u by '%s'/%u\n",
2488 cur->flg_export ? "exported " : "",
2489 cur->varstr, cur->var_nest_level, str, local_lvl
2490 );
2491 cur->next = *G.shadowed_vars_pp;
2492 *G.shadowed_vars_pp = cur;
2493 } else {
2494 /* Came from pseudo_exec_argv(), no need to save: delete it */
2495 debug_printf_env("shadow-deleting %s'%s'/%u by '%s'/%u\n",
2496 cur->flg_export ? "exported " : "",
2497 cur->varstr, cur->var_nest_level, str, local_lvl
2498 );
2499 if (cur->max_len == 0) /* allocated "VAR=VAL"? */
2500 free_me = cur->varstr; /* then free it later */
2501 free(cur);
2502 }
2503 break;
2504 }
Francis Laniel8197f012023-12-22 22:02:28 +01002505#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01002506
2507 if (strcmp(cur->varstr + name_len, eq_sign + 1) == 0) {
2508 debug_printf_env("assignement '%s' does not change anything\n", str);
2509 free_and_exp:
2510 free(str);
2511 goto exp;
2512 }
2513
2514 /* Replace the value in the found "struct variable" */
2515 if (cur->max_len != 0) {
2516 if (cur->max_len >= strnlen(str, cur->max_len + 1)) {
2517 /* This one is from startup env, reuse space */
2518 debug_printf_env("reusing startup env for '%s'\n", str);
2519 strcpy(cur->varstr, str);
2520 goto free_and_exp;
2521 }
2522 /* Can't reuse */
2523 cur->max_len = 0;
2524 goto set_str_and_exp;
2525 }
2526 /* max_len == 0 signifies "malloced" var, which we can
2527 * (and have to) free. But we can't free(cur->varstr) here:
2528 * if cur->flg_export is 1, it is in the environment.
2529 * We should either unsetenv+free, or wait until putenv,
2530 * then putenv(new)+free(old).
2531 */
2532 free_me = cur->varstr;
2533 goto set_str_and_exp;
2534 }
2535
2536 /* Not found or shadowed - create new variable struct */
Francis Laniel8197f012023-12-22 22:02:28 +01002537#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01002538 debug_printf_env("%s: alloc new var '%s'/%u\n", __func__, str, local_lvl);
Francis Laniel8197f012023-12-22 22:02:28 +01002539#else /* __U_BOOT__ */
2540 debug_printf_env("%s: alloc new var '%s'\n", __func__, str);
2541#endif /* __U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01002542 cur = xzalloc(sizeof(*cur));
Francis Laniel8197f012023-12-22 22:02:28 +01002543#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01002544 cur->var_nest_level = local_lvl;
Francis Laniel8197f012023-12-22 22:02:28 +01002545#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01002546 cur->next = *cur_pp;
2547 *cur_pp = cur;
2548
2549 set_str_and_exp:
2550 cur->varstr = str;
2551 exp:
Francis Laniel8197f012023-12-22 22:02:28 +01002552#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01002553#if !BB_MMU || ENABLE_HUSH_READONLY
2554 if (flags & SETFLAG_MAKE_RO) {
2555 cur->flg_read_only = 1;
2556 }
2557#endif
2558 if (flags & SETFLAG_EXPORT)
2559 cur->flg_export = 1;
Francis Laniel8197f012023-12-22 22:02:28 +01002560#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01002561 retval = 0;
Francis Laniel8197f012023-12-22 22:02:28 +01002562#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01002563 if (cur->flg_export) {
2564 if (flags & SETFLAG_UNEXPORT) {
2565 cur->flg_export = 0;
2566 /* unsetenv was already done */
2567 } else {
2568 debug_printf_env("%s: putenv '%s'/%u\n", __func__, cur->varstr, cur->var_nest_level);
2569 retval = putenv(cur->varstr);
2570 /* fall through to "free(free_me)" -
2571 * only now we can free old exported malloced string
2572 */
2573 }
2574 }
Francis Laniel8197f012023-12-22 22:02:28 +01002575#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01002576 free(free_me);
2577
2578 handle_changed_special_names(cur->varstr, name_len - 1);
2579
2580 return retval;
2581}
2582
Francis Laniel8197f012023-12-22 22:02:28 +01002583#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01002584static void FAST_FUNC set_local_var_from_halves(const char *name, const char *val)
2585{
2586 char *var = xasprintf("%s=%s", name, val);
2587 set_local_var(var, /*flag:*/ 0);
2588}
2589
2590/* Used at startup and after each cd */
2591static void set_pwd_var(unsigned flag)
2592{
2593 set_local_var(xasprintf("PWD=%s", get_cwd(/*force:*/ 1)), flag);
2594}
Francis Laniel8197f012023-12-22 22:02:28 +01002595#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01002596
2597#if ENABLE_HUSH_UNSET || ENABLE_HUSH_GETOPTS
2598static int unset_local_var_len(const char *name, int name_len)
2599{
2600 struct variable *cur;
2601 struct variable **cur_pp;
2602
2603 cur_pp = &G.top_var;
2604 while ((cur = *cur_pp) != NULL) {
2605 if (strncmp(cur->varstr, name, name_len) == 0
2606 && cur->varstr[name_len] == '='
2607 ) {
2608 if (cur->flg_read_only) {
2609 bb_error_msg("%s: readonly variable", name);
2610 return EXIT_FAILURE;
2611 }
2612
2613 *cur_pp = cur->next;
2614 debug_printf_env("%s: unsetenv '%s'\n", __func__, cur->varstr);
2615 bb_unsetenv(cur->varstr);
2616 if (!cur->max_len)
2617 free(cur->varstr);
2618 free(cur);
2619
2620 break;
2621 }
2622 cur_pp = &cur->next;
2623 }
2624
2625 /* Handle "unset LINENO" et al even if did not find the variable to unset */
2626 handle_changed_special_names(name, name_len);
2627
2628 return EXIT_SUCCESS;
2629}
2630
2631static int unset_local_var(const char *name)
2632{
2633 return unset_local_var_len(name, strlen(name));
2634}
2635#endif
2636
2637
Francis Laniel8197f012023-12-22 22:02:28 +01002638#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01002639/*
2640 * Helpers for "var1=val1 var2=val2 cmd" feature
2641 */
2642static void add_vars(struct variable *var)
2643{
2644 struct variable *next;
2645
2646 while (var) {
2647 next = var->next;
2648 var->next = G.top_var;
2649 G.top_var = var;
2650 if (var->flg_export) {
2651 debug_printf_env("%s: restoring exported '%s'/%u\n", __func__, var->varstr, var->var_nest_level);
2652 putenv(var->varstr);
2653 } else {
2654 debug_printf_env("%s: restoring variable '%s'/%u\n", __func__, var->varstr, var->var_nest_level);
2655 }
2656 var = next;
2657 }
2658}
2659
2660/* We put strings[i] into variable table and possibly putenv them.
2661 * If variable is read only, we can free the strings[i]
2662 * which attempts to overwrite it.
2663 * The strings[] vector itself is freed.
2664 */
2665static void set_vars_and_save_old(char **strings)
2666{
2667 char **s;
2668
2669 if (!strings)
2670 return;
2671
2672 s = strings;
2673 while (*s) {
2674 struct variable *var_p;
2675 struct variable **var_pp;
2676 char *eq;
2677
2678 eq = strchr(*s, '=');
2679 if (HUSH_DEBUG && !eq)
2680 bb_simple_error_msg_and_die("BUG in varexp4");
2681 var_pp = get_ptr_to_local_var(*s, eq - *s);
2682 if (var_pp) {
2683 var_p = *var_pp;
2684 if (var_p->flg_read_only) {
2685 char **p;
2686 bb_error_msg("%s: readonly variable", *s);
2687 /*
2688 * "VAR=V BLTIN" unsets VARs after BLTIN completes.
2689 * If VAR is readonly, leaving it in the list
2690 * after asssignment error (msg above)
2691 * causes doubled error message later, on unset.
2692 */
2693 debug_printf_env("removing/freeing '%s' element\n", *s);
2694 free(*s);
2695 p = s;
2696 do { *p = p[1]; p++; } while (*p);
2697 goto next;
2698 }
2699 /* below, set_local_var() with nest level will
2700 * "shadow" (remove) this variable from
2701 * global linked list.
2702 */
2703 }
2704 debug_printf_env("%s: env override '%s'/%u\n", __func__, *s, G.var_nest_level);
2705 set_local_var(*s, (G.var_nest_level << SETFLAG_VARLVL_SHIFT) | SETFLAG_EXPORT);
2706 s++;
2707 next: ;
2708 }
2709 free(strings);
2710}
2711
2712
2713/*
2714 * Unicode helper
2715 */
2716static void reinit_unicode_for_hush(void)
2717{
2718 /* Unicode support should be activated even if LANG is set
2719 * _during_ shell execution, not only if it was set when
2720 * shell was started. Therefore, re-check LANG every time:
2721 */
2722 if (ENABLE_FEATURE_CHECK_UNICODE_IN_ENV
2723 || ENABLE_UNICODE_USING_LOCALE
2724 ) {
2725 const char *s = get_local_var_value("LC_ALL");
2726 if (!s) s = get_local_var_value("LC_CTYPE");
2727 if (!s) s = get_local_var_value("LANG");
2728 reinit_unicode(s);
2729 }
2730}
2731
Francis Laniel8197f012023-12-22 22:02:28 +01002732#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01002733/*
2734 * in_str support (strings, and "strings" read from files).
2735 */
2736
2737#if ENABLE_HUSH_INTERACTIVE
Francis Laniel8197f012023-12-22 22:02:28 +01002738#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01002739/* To test correct lineedit/interactive behavior, type from command line:
2740 * echo $P\
2741 * \
2742 * AT\
2743 * H\
2744 * \
2745 * It exercises a lot of corner cases.
2746 */
2747static const char *setup_prompt_string(void)
2748{
2749 const char *prompt_str;
2750
2751 debug_printf_prompt("%s promptmode:%d\n", __func__, G.promptmode);
2752
2753# if ENABLE_FEATURE_EDITING_FANCY_PROMPT
2754 prompt_str = get_local_var_value(G.promptmode == 0 ? "PS1" : "PS2");
2755 if (!prompt_str)
2756 prompt_str = "";
2757# else
2758 prompt_str = "> "; /* if PS2, else... */
2759 if (G.promptmode == 0) { /* PS1 */
2760 /* No fancy prompts supported, (re)generate "CURDIR $ " by hand */
2761 free(G.PS1);
2762 /* bash uses $PWD value, even if it is set by user.
2763 * It uses current dir only if PWD is unset.
2764 * We always use current dir. */
2765 prompt_str = G.PS1 = xasprintf("%s %c ", get_cwd(0), (geteuid() != 0) ? '$' : '#');
2766 }
2767# endif
2768 debug_printf("prompt_str '%s'\n", prompt_str);
2769 return prompt_str;
2770}
Francis Laniel8197f012023-12-22 22:02:28 +01002771#endif /* !__U_BOOT__ */
2772
2773#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01002774static int get_user_input(struct in_str *i)
Francis Laniel8197f012023-12-22 22:02:28 +01002775#else /* __U_BOOT__ */
2776static void get_user_input(struct in_str *i)
2777#endif /* __U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01002778{
Francis Laniel8197f012023-12-22 22:02:28 +01002779#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01002780# if ENABLE_FEATURE_EDITING
2781 /* In EDITING case, this function reads next input line,
2782 * saves it in i->p, then returns 1st char of it.
2783 */
2784 int r;
2785 const char *prompt_str;
2786
2787 prompt_str = setup_prompt_string();
2788 for (;;) {
2789 reinit_unicode_for_hush();
2790 G.flag_SIGINT = 0;
2791 /* buglet: SIGINT will not make new prompt to appear _at once_,
2792 * only after <Enter>. (^C works immediately) */
2793 r = read_line_input(G.line_input_state, prompt_str,
2794 G.user_input_buf, CONFIG_FEATURE_EDITING_MAX_LEN-1
2795 );
2796 /* read_line_input intercepts ^C, "convert" it to SIGINT */
2797 if (r == 0) {
2798 raise(SIGINT);
2799 }
2800 check_and_run_traps();
2801 if (r != 0 && !G.flag_SIGINT)
2802 break;
2803 /* ^C or SIGINT: repeat */
2804 /* bash prints ^C even on real SIGINT (non-kbd generated) */
2805 write(STDOUT_FILENO, "^C\n", 3);
2806 G.last_exitcode = 128 | SIGINT;
2807 }
2808 if (r < 0) {
2809 /* EOF/error detected */
2810 /* ^D on interactive input goes to next line before exiting: */
2811 write(STDOUT_FILENO, "\n", 1);
2812 i->p = NULL;
2813 i->peek_buf[0] = r = EOF;
2814 return r;
2815 }
2816 i->p = G.user_input_buf;
2817 return (unsigned char)*i->p++;
2818# else
2819 /* In !EDITING case, this function gets called for every char.
2820 * Buffering happens deeper in the call chain, in hfgetc(i->file).
2821 */
2822 int r;
2823
2824 for (;;) {
2825 G.flag_SIGINT = 0;
2826 if (i->last_char == '\0' || i->last_char == '\n') {
2827 const char *prompt_str = setup_prompt_string();
2828 /* Why check_and_run_traps here? Try this interactively:
2829 * $ trap 'echo INT' INT; (sleep 2; kill -INT $$) &
2830 * $ <[enter], repeatedly...>
2831 * Without check_and_run_traps, handler never runs.
2832 */
2833 check_and_run_traps();
2834 fputs_stdout(prompt_str);
2835 fflush_all();
2836 }
2837 r = hfgetc(i->file);
2838 /* In !ENABLE_FEATURE_EDITING we don't use read_line_input,
2839 * no ^C masking happens during fgetc, no special code for ^C:
2840 * it generates SIGINT as usual.
2841 */
2842 check_and_run_traps();
2843 if (r != '\0' && !G.flag_SIGINT)
2844 break;
2845 if (G.flag_SIGINT) {
2846 /* ^C or SIGINT: repeat */
2847 /* bash prints ^C even on real SIGINT (non-kbd generated) */
2848 /* kernel prints "^C" itself, just print newline: */
2849 write(STDOUT_FILENO, "\n", 1);
2850 G.last_exitcode = 128 | SIGINT;
2851 }
2852 }
2853 return r;
2854# endif
Francis Laniel8197f012023-12-22 22:02:28 +01002855#else /* __U_BOOT__ */
2856 int n;
2857 int promptme;
2858 static char the_command[CONFIG_SYS_CBSIZE + 1];
2859
2860 bootretry_reset_cmd_timeout();
2861 promptme = 1;
2862 n = u_boot_cli_readline(i);
2863
2864# ifdef CONFIG_BOOT_RETRY_TIME
2865 if (n == -2) {
2866 puts("\nTimeout waiting for command\n");
2867# ifdef CONFIG_RESET_TO_RETRY
2868 do_reset(NULL, 0, 0, NULL);
2869# else
2870# error "This currently only works with CONFIG_RESET_TO_RETRY enabled"
2871# endif
2872 }
2873# endif
2874 if (n == -1 ) {
2875 G.flag_repeat = 0;
2876 promptme = 0;
2877 }
2878 n = strlen(console_buffer);
2879 console_buffer[n] = '\n';
2880 console_buffer[n+1]= '\0';
2881 if (had_ctrlc())
2882 G.flag_repeat = 0;
2883 clear_ctrlc();
2884 G.do_repeat = 0;
2885#ifndef __U_BOOT__
2886 if (G.promptmode == 1) {
2887#else /* __U_BOOT__ */
2888 if (!G.promptmode) {
2889#endif /* __U_BOOT__ */
2890 if (console_buffer[0] == '\n'&& G.flag_repeat == 0) {
2891 strcpy(the_command, console_buffer);
2892 }
2893 else {
2894 if (console_buffer[0] != '\n') {
2895 strcpy(the_command, console_buffer);
2896 G.flag_repeat = 1;
2897 }
2898 else {
2899 G.do_repeat = 1;
2900 }
2901 }
2902 i->p = the_command;
2903 }
2904 else {
2905 if (console_buffer[0] != '\n') {
2906 if (strlen(the_command) + strlen(console_buffer)
2907 < CONFIG_SYS_CBSIZE) {
2908 n = strlen(the_command);
2909#ifdef __U_BOOT__
2910 /*
2911 * To avoid writing to bad places, we check if
2912 * n is greater than 0.
2913 * This bug was found by Harald Seiler.
2914 */
2915 if (n > 0)
2916 the_command[n-1] = ' ';
2917 strcpy(&the_command[n], console_buffer);
2918#else /* !__U_BOOT__ */
2919 the_command[n-1] = ' ';
2920 strcpy(&the_command[n], console_buffer);
2921#endif /* !__U_BOOT__ */
2922 }
2923 else {
2924 the_command[0] = '\n';
2925 the_command[1] = '\0';
2926 G.flag_repeat = 0;
2927 }
2928 }
2929 if (promptme == 0) {
2930 the_command[0] = '\n';
2931 the_command[1] = '\0';
2932 }
2933 i->p = console_buffer;
2934 }
2935#endif /* __U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01002936}
2937/* This is the magic location that prints prompts
2938 * and gets data back from the user */
2939static int fgetc_interactive(struct in_str *i)
2940{
2941 int ch;
Francis Laniel8197f012023-12-22 22:02:28 +01002942#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01002943 /* If it's interactive stdin, get new line. */
2944 if (G_interactive_fd && i->file == G.HFILE_stdin) {
Francis Laniel8197f012023-12-22 22:02:28 +01002945#endif /* !__U_BOOT__ */
2946#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01002947 /* Returns first char (or EOF), the rest is in i->p[] */
2948 ch = get_user_input(i);
Francis Laniel8197f012023-12-22 22:02:28 +01002949#else /* __U_BOOT__ */
2950 /* Avoid garbage value and make clang happy. */
2951 ch = 0;
2952 /*
2953 * get_user_input() does not return anything when used in
2954 * U-Boot.
2955 * So, we need to take the read character from i->p[].
2956 */
2957 get_user_input(i);
2958 if (i->p && *i->p) {
2959 ch = *i->p++;
2960 }
2961#endif /* __U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01002962 G.promptmode = 1; /* PS2 */
2963 debug_printf_prompt("%s promptmode=%d\n", __func__, G.promptmode);
Francis Laniel8197f012023-12-22 22:02:28 +01002964#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01002965 } else {
2966 /* Not stdin: script file, sourced file, etc */
2967 do ch = hfgetc(i->file); while (ch == '\0');
2968 }
Francis Laniel8197f012023-12-22 22:02:28 +01002969#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01002970 return ch;
2971}
2972#else /* !INTERACTIVE */
Francis Laniel8197f012023-12-22 22:02:28 +01002973#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01002974static ALWAYS_INLINE int fgetc_interactive(struct in_str *i)
2975{
2976 int ch;
2977 do ch = hfgetc(i->file); while (ch == '\0');
2978 return ch;
2979}
Francis Laniel8197f012023-12-22 22:02:28 +01002980#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01002981#endif /* !INTERACTIVE */
2982
2983static int i_getch(struct in_str *i)
2984{
2985 int ch;
2986
Francis Laniel8197f012023-12-22 22:02:28 +01002987#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01002988 if (!i->file) {
2989 /* string-based in_str */
2990 ch = (unsigned char)*i->p;
2991 if (ch != '\0') {
2992 i->p++;
2993 i->last_char = ch;
2994 return ch;
2995 }
2996 return EOF;
2997 }
2998
Francis Laniel8197f012023-12-22 22:02:28 +01002999#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01003000 /* FILE-based in_str */
3001
3002#if ENABLE_FEATURE_EDITING
3003 /* This can be stdin, check line editing char[] buffer */
3004 if (i->p && *i->p != '\0') {
3005 ch = (unsigned char)*i->p++;
3006 goto out;
3007 }
3008#endif
Francis Laniel8197f012023-12-22 22:02:28 +01003009#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01003010 /* peek_buf[] is an int array, not char. Can contain EOF. */
3011 ch = i->peek_buf[0];
3012 if (ch != 0) {
3013 int ch2 = i->peek_buf[1];
3014 i->peek_buf[0] = ch2;
3015 if (ch2 == 0) /* very likely, avoid redundant write */
3016 goto out;
3017 i->peek_buf[1] = 0;
3018 goto out;
3019 }
3020
Francis Laniel8197f012023-12-22 22:02:28 +01003021#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01003022 ch = fgetc_interactive(i);
3023 out:
3024 debug_printf("file_get: got '%c' %d\n", ch, ch);
3025 i->last_char = ch;
3026#if ENABLE_HUSH_LINENO_VAR
3027 if (ch == '\n') {
3028 G.parse_lineno++;
3029 debug_printf_parse("G.parse_lineno++ = %u\n", G.parse_lineno);
3030 }
3031#endif
3032 return ch;
3033}
3034
3035static int i_peek(struct in_str *i)
3036{
Francis Laniel8197f012023-12-22 22:02:28 +01003037#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01003038 int ch;
3039
3040 if (!i->file) {
3041 /* string-based in_str */
3042 /* Doesn't report EOF on NUL. None of the callers care. */
3043 return (unsigned char)*i->p;
3044 }
3045
3046 /* FILE-based in_str */
3047
3048#if ENABLE_FEATURE_EDITING && ENABLE_HUSH_INTERACTIVE
3049 /* This can be stdin, check line editing char[] buffer */
3050 if (i->p && *i->p != '\0')
3051 return (unsigned char)*i->p;
3052#endif
3053 /* peek_buf[] is an int array, not char. Can contain EOF. */
3054 ch = i->peek_buf[0];
3055 if (ch != 0)
3056 return ch;
3057
3058 /* Need to get a new char */
3059 ch = fgetc_interactive(i);
3060 debug_printf("file_peek: got '%c' %d\n", ch, ch);
3061
3062 /* Save it by either rolling back line editing buffer, or in i->peek_buf[0] */
3063#if ENABLE_FEATURE_EDITING && ENABLE_HUSH_INTERACTIVE
3064 if (i->p) {
3065 i->p -= 1;
3066 return ch;
3067 }
3068#endif
3069 i->peek_buf[0] = ch;
3070 /*i->peek_buf[1] = 0; - already is */
3071 return ch;
Francis Laniel8197f012023-12-22 22:02:28 +01003072#else /* __U_BOOT__ */
3073 /* string-based in_str */
3074 /* Doesn't report EOF on NUL. None of the callers care. */
3075 return (unsigned char)*i->p;
3076#endif /* __U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01003077}
3078
3079/* Only ever called if i_peek() was called, and did not return EOF.
3080 * IOW: we know the previous peek saw an ordinary char, not EOF, not NUL,
3081 * not end-of-line. Therefore we never need to read a new editing line here.
3082 */
3083static int i_peek2(struct in_str *i)
3084{
Francis Laniel8197f012023-12-22 22:02:28 +01003085#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01003086 int ch;
Francis Laniel8197f012023-12-22 22:02:28 +01003087#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01003088
3089 /* There are two cases when i->p[] buffer exists.
3090 * (1) it's a string in_str.
3091 * (2) It's a file, and we have a saved line editing buffer.
3092 * In both cases, we know that i->p[0] exists and not NUL, and
3093 * the peek2 result is in i->p[1].
3094 */
3095 if (i->p)
3096 return (unsigned char)i->p[1];
3097
Francis Laniel8197f012023-12-22 22:02:28 +01003098#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01003099 /* Now we know it is a file-based in_str. */
3100
3101 /* peek_buf[] is an int array, not char. Can contain EOF. */
3102 /* Is there 2nd char? */
3103 ch = i->peek_buf[1];
3104 if (ch == 0) {
3105 /* We did not read it yet, get it now */
3106 do ch = hfgetc(i->file); while (ch == '\0');
3107 i->peek_buf[1] = ch;
3108 }
3109
3110 debug_printf("file_peek2: got '%c' %d\n", ch, ch);
3111 return ch;
Francis Laniel8197f012023-12-22 22:02:28 +01003112#else
3113 return 0;
3114#endif /* __U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01003115}
3116
3117static int i_getch_and_eat_bkslash_nl(struct in_str *input)
3118{
3119 for (;;) {
3120 int ch, ch2;
3121
3122 ch = i_getch(input);
3123 if (ch != '\\')
3124 return ch;
3125 ch2 = i_peek(input);
3126 if (ch2 != '\n')
3127 return ch;
3128 /* backslash+newline, skip it */
3129 i_getch(input);
3130 }
3131}
3132
3133/* Note: this function _eats_ \<newline> pairs, safe to use plain
3134 * i_getch() after it instead of i_getch_and_eat_bkslash_nl().
3135 */
3136static int i_peek_and_eat_bkslash_nl(struct in_str *input)
3137{
3138 for (;;) {
3139 int ch, ch2;
3140
3141 ch = i_peek(input);
3142 if (ch != '\\')
3143 return ch;
3144 ch2 = i_peek2(input);
3145 if (ch2 != '\n')
3146 return ch;
3147 /* backslash+newline, skip it */
3148 i_getch(input);
3149 i_getch(input);
3150 }
3151}
3152
Francis Laniel8197f012023-12-22 22:02:28 +01003153#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01003154static void setup_file_in_str(struct in_str *i, HFILE *fp)
Francis Laniel8197f012023-12-22 22:02:28 +01003155#else /* __U_BOOT__ */
3156static void setup_file_in_str(struct in_str *i)
3157#endif /* __U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01003158{
3159 memset(i, 0, sizeof(*i));
Francis Laniel8197f012023-12-22 22:02:28 +01003160#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01003161 i->file = fp;
3162 /* i->p = NULL; */
Francis Laniel8197f012023-12-22 22:02:28 +01003163#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01003164}
3165
Francis Laniel8197f012023-12-22 22:02:28 +01003166#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01003167static void setup_string_in_str(struct in_str *i, const char *s)
3168{
3169 memset(i, 0, sizeof(*i));
3170 /*i->file = NULL */;
3171 i->p = s;
3172}
3173
Francis Laniel8197f012023-12-22 22:02:28 +01003174#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01003175
3176/*
3177 * o_string support
3178 */
3179#define B_CHUNK (32 * sizeof(char*))
3180
3181static void o_reset_to_empty_unquoted(o_string *o)
3182{
3183 o->length = 0;
3184 o->has_quoted_part = 0;
3185 if (o->data)
3186 o->data[0] = '\0';
3187}
3188
3189static void o_free_and_set_NULL(o_string *o)
3190{
3191 free(o->data);
3192 memset(o, 0, sizeof(*o));
3193}
3194
3195static ALWAYS_INLINE void o_free(o_string *o)
3196{
3197 free(o->data);
3198}
3199
3200static void o_grow_by(o_string *o, int len)
3201{
3202 if (o->length + len > o->maxlen) {
3203 o->maxlen += (2 * len) | (B_CHUNK-1);
3204 o->data = xrealloc(o->data, 1 + o->maxlen);
3205 }
3206}
3207
3208static void o_addchr(o_string *o, int ch)
3209{
3210 debug_printf("o_addchr: '%c' o->length=%d o=%p\n", ch, o->length, o);
3211 if (o->length < o->maxlen) {
3212 /* likely. avoid o_grow_by() call */
3213 add:
3214 o->data[o->length] = ch;
3215 o->length++;
3216 o->data[o->length] = '\0';
3217 return;
3218 }
3219 o_grow_by(o, 1);
3220 goto add;
3221}
3222
3223#if 0
3224/* Valid only if we know o_string is not empty */
3225static void o_delchr(o_string *o)
3226{
3227 o->length--;
3228 o->data[o->length] = '\0';
3229}
3230#endif
3231
3232static void o_addblock(o_string *o, const char *str, int len)
3233{
3234 o_grow_by(o, len);
3235 ((char*)mempcpy(&o->data[o->length], str, len))[0] = '\0';
3236 o->length += len;
3237}
3238
3239static void o_addstr(o_string *o, const char *str)
3240{
3241 o_addblock(o, str, strlen(str));
3242}
3243
Francis Laniel8197f012023-12-22 22:02:28 +01003244#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01003245static void o_addstr_with_NUL(o_string *o, const char *str)
3246{
3247 o_addblock(o, str, strlen(str) + 1);
3248}
Francis Laniel8197f012023-12-22 22:02:28 +01003249#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01003250
3251#if !BB_MMU
3252static void nommu_addchr(o_string *o, int ch)
3253{
3254 if (o)
3255 o_addchr(o, ch);
3256}
3257#else
3258# define nommu_addchr(o, str) ((void)0)
3259#endif
3260
Francis Laniel8197f012023-12-22 22:02:28 +01003261#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01003262#if ENABLE_HUSH_MODE_X
3263static void x_mode_addchr(int ch)
3264{
3265 o_addchr(&G.x_mode_buf, ch);
3266}
3267static void x_mode_addstr(const char *str)
3268{
3269 o_addstr(&G.x_mode_buf, str);
3270}
3271static void x_mode_addblock(const char *str, int len)
3272{
3273 o_addblock(&G.x_mode_buf, str, len);
3274}
3275static void x_mode_prefix(void)
3276{
3277 int n = G.x_mode_depth;
3278 do x_mode_addchr('+'); while (--n >= 0);
3279}
3280static void x_mode_flush(void)
3281{
3282 int len = G.x_mode_buf.length;
3283 if (len <= 0)
3284 return;
3285 if (G.x_mode_fd > 0) {
3286 G.x_mode_buf.data[len] = '\n';
3287 full_write(G.x_mode_fd, G.x_mode_buf.data, len + 1);
3288 }
3289 G.x_mode_buf.length = 0;
3290}
3291#endif
Francis Laniel8197f012023-12-22 22:02:28 +01003292#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01003293
3294/*
3295 * HUSH_BRACE_EXPANSION code needs corresponding quoting on variable expansion side.
3296 * Currently, "v='{q,w}'; echo $v" erroneously expands braces in $v.
3297 * Apparently, on unquoted $v bash still does globbing
3298 * ("v='*.txt'; echo $v" prints all .txt files),
3299 * but NOT brace expansion! Thus, there should be TWO independent
3300 * quoting mechanisms on $v expansion side: one protects
3301 * $v from brace expansion, and other additionally protects "$v" against globbing.
3302 * We have only second one.
3303 */
3304
3305#if ENABLE_HUSH_BRACE_EXPANSION
3306# define MAYBE_BRACES "{}"
3307#else
3308# define MAYBE_BRACES ""
3309#endif
3310
3311/* My analysis of quoting semantics tells me that state information
3312 * is associated with a destination, not a source.
3313 */
3314static void o_addqchr(o_string *o, int ch)
3315{
3316 int sz = 1;
3317 /* '-' is included because of this case:
3318 * >filename0 >filename1 >filename9; v='-'; echo filename[0"$v"9]
3319 */
3320 char *found = strchr("*?[-\\" MAYBE_BRACES, ch);
3321 if (found)
3322 sz++;
3323 o_grow_by(o, sz);
3324 if (found) {
3325 o->data[o->length] = '\\';
3326 o->length++;
3327 }
3328 o->data[o->length] = ch;
3329 o->length++;
3330 o->data[o->length] = '\0';
3331}
3332
3333static void o_addQchr(o_string *o, int ch)
3334{
3335 int sz = 1;
3336 if ((o->o_expflags & EXP_FLAG_ESC_GLOB_CHARS)
3337 && strchr("*?[-\\" MAYBE_BRACES, ch)
3338 ) {
3339 sz++;
3340 o->data[o->length] = '\\';
3341 o->length++;
3342 }
3343 o_grow_by(o, sz);
3344 o->data[o->length] = ch;
3345 o->length++;
3346 o->data[o->length] = '\0';
3347}
3348
3349static void o_addqblock(o_string *o, const char *str, int len)
3350{
3351 while (len) {
3352 char ch;
3353 int sz;
3354 int ordinary_cnt = strcspn(str, "*?[-\\" MAYBE_BRACES);
3355 if (ordinary_cnt > len) /* paranoia */
3356 ordinary_cnt = len;
3357 o_addblock(o, str, ordinary_cnt);
3358 if (ordinary_cnt == len)
3359 return; /* NUL is already added by o_addblock */
3360 str += ordinary_cnt;
3361 len -= ordinary_cnt + 1; /* we are processing + 1 char below */
3362
3363 ch = *str++;
3364 sz = 1;
3365 if (ch) { /* it is necessarily one of "*?[-\\" MAYBE_BRACES */
3366 sz++;
3367 o->data[o->length] = '\\';
3368 o->length++;
3369 }
3370 o_grow_by(o, sz);
3371 o->data[o->length] = ch;
3372 o->length++;
3373 }
3374 o->data[o->length] = '\0';
3375}
3376
3377static void o_addQblock(o_string *o, const char *str, int len)
3378{
3379 if (!(o->o_expflags & EXP_FLAG_ESC_GLOB_CHARS)) {
3380 o_addblock(o, str, len);
3381 return;
3382 }
3383 o_addqblock(o, str, len);
3384}
3385
3386static void o_addQstr(o_string *o, const char *str)
3387{
3388 o_addQblock(o, str, strlen(str));
3389}
3390
3391/* A special kind of o_string for $VAR and `cmd` expansion.
3392 * It contains char* list[] at the beginning, which is grown in 16 element
3393 * increments. Actual string data starts at the next multiple of 16 * (char*).
3394 * list[i] contains an INDEX (int!) into this string data.
3395 * It means that if list[] needs to grow, data needs to be moved higher up
3396 * but list[i]'s need not be modified.
3397 * NB: remembering how many list[i]'s you have there is crucial.
3398 * o_finalize_list() operation post-processes this structure - calculates
3399 * and stores actual char* ptrs in list[]. Oh, it NULL terminates it as well.
3400 */
3401#if DEBUG_EXPAND || DEBUG_GLOB
3402static void debug_print_list(const char *prefix, o_string *o, int n)
3403{
3404 char **list = (char**)o->data;
3405 int string_start = ((n + 0xf) & ~0xf) * sizeof(list[0]);
3406 int i = 0;
3407
3408 indent();
3409 fdprintf(2, "%s: list:%p n:%d string_start:%d length:%d maxlen:%d glob:%d quoted:%d escape:%d\n",
3410 prefix, list, n, string_start, o->length, o->maxlen,
3411 !!(o->o_expflags & EXP_FLAG_GLOB),
3412 o->has_quoted_part,
3413 !!(o->o_expflags & EXP_FLAG_ESC_GLOB_CHARS));
3414 while (i < n) {
3415 indent();
3416 fdprintf(2, " list[%d]=%d '%s' %p\n", i, (int)(uintptr_t)list[i],
3417 o->data + (int)(uintptr_t)list[i] + string_start,
3418 o->data + (int)(uintptr_t)list[i] + string_start);
3419 i++;
3420 }
3421 if (n) {
3422 const char *p = o->data + (int)(uintptr_t)list[n - 1] + string_start;
3423 indent();
Francis Laniel8197f012023-12-22 22:02:28 +01003424#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01003425 fdprintf(2, " total_sz:%ld\n", (long)((p + strlen(p) + 1) - o->data));
Francis Laniel8197f012023-12-22 22:02:28 +01003426#else /* __U_BOOT__ */
3427 printf(" total_sz:%ld\n", (long)((p + strlen(p) + 1) - o->data));
3428#endif /* __U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01003429 }
3430}
3431#else
3432# define debug_print_list(prefix, o, n) ((void)0)
3433#endif
3434
3435/* n = o_save_ptr_helper(str, n) "starts new string" by storing an index value
3436 * in list[n] so that it points past last stored byte so far.
3437 * It returns n+1. */
3438static int o_save_ptr_helper(o_string *o, int n)
3439{
3440 char **list = (char**)o->data;
3441 int string_start;
3442 int string_len;
3443
3444 if (!o->has_empty_slot) {
3445 string_start = ((n + 0xf) & ~0xf) * sizeof(list[0]);
3446 string_len = o->length - string_start;
3447 if (!(n & 0xf)) { /* 0, 0x10, 0x20...? */
3448 debug_printf_list("list[%d]=%d string_start=%d (growing)\n", n, string_len, string_start);
3449 /* list[n] points to string_start, make space for 16 more pointers */
3450 o->maxlen += 0x10 * sizeof(list[0]);
3451 o->data = xrealloc(o->data, o->maxlen + 1);
3452 list = (char**)o->data;
3453 memmove(list + n + 0x10, list + n, string_len);
3454 /*
3455 * expand_on_ifs() has a "previous argv[] ends in IFS?"
3456 * check. (grep for -prev-ifs-check-).
3457 * Ensure that argv[-1][last] is not garbage
3458 * but zero bytes, to save index check there.
3459 */
3460 list[n + 0x10 - 1] = 0;
3461 o->length += 0x10 * sizeof(list[0]);
3462 } else {
3463 debug_printf_list("list[%d]=%d string_start=%d\n",
3464 n, string_len, string_start);
3465 }
3466 } else {
3467 /* We have empty slot at list[n], reuse without growth */
3468 string_start = ((n+1 + 0xf) & ~0xf) * sizeof(list[0]); /* NB: n+1! */
3469 string_len = o->length - string_start;
3470 debug_printf_list("list[%d]=%d string_start=%d (empty slot)\n",
3471 n, string_len, string_start);
3472 o->has_empty_slot = 0;
3473 }
3474 o->has_quoted_part = 0;
3475 list[n] = (char*)(uintptr_t)string_len;
3476 return n + 1;
3477}
3478
3479/* "What was our last o_save_ptr'ed position (byte offset relative o->data)?" */
3480static int o_get_last_ptr(o_string *o, int n)
3481{
3482 char **list = (char**)o->data;
3483 int string_start = ((n + 0xf) & ~0xf) * sizeof(list[0]);
3484
3485 return ((int)(uintptr_t)list[n-1]) + string_start;
3486}
3487
Francis Laniel8197f012023-12-22 22:02:28 +01003488#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01003489/*
3490 * Globbing routines.
3491 *
3492 * Most words in commands need to be globbed, even ones which are
3493 * (single or double) quoted. This stems from the possiblity of
3494 * constructs like "abc"* and 'abc'* - these should be globbed.
3495 * Having a different code path for fully-quoted strings ("abc",
3496 * 'abc') would only help performance-wise, but we still need
3497 * code for partially-quoted strings.
3498 *
3499 * Unfortunately, if we want to match bash and ash behavior in all cases,
3500 * the logic can't be "shell-syntax argument is first transformed
3501 * to a string, then globbed, and if globbing does not match anything,
3502 * it is used verbatim". Here are two examples where it fails:
3503 *
3504 * echo 'b\*'?
3505 *
3506 * The globbing can't be avoided (because of '?' at the end).
3507 * The glob pattern is: b\\\*? - IOW, both \ and * are literals
3508 * and are glob-escaped. If this does not match, bash/ash print b\*?
3509 * - IOW: they "unbackslash" the glob pattern.
3510 * Now, look at this:
3511 *
3512 * v='\\\*'; echo b$v?
3513 *
3514 * The glob pattern is the same here: b\\\*? - the unquoted $v expansion
3515 * should be used as glob pattern with no changes. However, if glob
3516 * does not match, bash/ash print b\\\*? - NOT THE SAME as first example!
3517 *
3518 * ash implements this by having an encoded representation of the word
3519 * to glob, which IS NOT THE SAME as the glob pattern - it has more data.
3520 * Glob pattern is derived from it. If glob fails, the decision what result
3521 * should be is made using that encoded representation. Not glob pattern.
3522 */
3523
3524#if ENABLE_HUSH_BRACE_EXPANSION
3525/* There in a GNU extension, GLOB_BRACE, but it is not usable:
3526 * first, it processes even {a} (no commas), second,
3527 * I didn't manage to make it return strings when they don't match
3528 * existing files. Need to re-implement it.
3529 */
3530
3531/* Helper */
3532static int glob_needed(const char *s)
3533{
3534 while (*s) {
3535 if (*s == '\\') {
3536 if (!s[1])
3537 return 0;
3538 s += 2;
3539 continue;
3540 }
3541 if (*s == '*' || *s == '[' || *s == '?' || *s == '{')
3542 return 1;
3543 s++;
3544 }
3545 return 0;
3546}
3547/* Return pointer to next closing brace or to comma */
3548static const char *next_brace_sub(const char *cp)
3549{
3550 unsigned depth = 0;
3551 cp++;
3552 while (*cp != '\0') {
3553 if (*cp == '\\') {
3554 if (*++cp == '\0')
3555 break;
3556 cp++;
3557 continue;
3558 }
3559 if ((*cp == '}' && depth-- == 0) || (*cp == ',' && depth == 0))
3560 break;
3561 if (*cp++ == '{')
3562 depth++;
3563 }
3564
3565 return *cp != '\0' ? cp : NULL;
3566}
3567/* Recursive brace globber. Note: may garble pattern[]. */
3568static int glob_brace(char *pattern, o_string *o, int n)
3569{
3570 char *new_pattern_buf;
3571 const char *begin;
3572 const char *next;
3573 const char *rest;
3574 const char *p;
3575 size_t rest_len;
3576
3577 debug_printf_glob("glob_brace('%s')\n", pattern);
3578
3579 begin = pattern;
3580 while (1) {
3581 if (*begin == '\0')
3582 goto simple_glob;
3583 if (*begin == '{') {
3584 /* Find the first sub-pattern and at the same time
3585 * find the rest after the closing brace */
3586 next = next_brace_sub(begin);
3587 if (next == NULL) {
3588 /* An illegal expression */
3589 goto simple_glob;
3590 }
3591 if (*next == '}') {
3592 /* "{abc}" with no commas - illegal
3593 * brace expr, disregard and skip it */
3594 begin = next + 1;
3595 continue;
3596 }
3597 break;
3598 }
3599 if (*begin == '\\' && begin[1] != '\0')
3600 begin++;
3601 begin++;
3602 }
3603 debug_printf_glob("begin:%s\n", begin);
3604 debug_printf_glob("next:%s\n", next);
3605
3606 /* Now find the end of the whole brace expression */
3607 rest = next;
3608 while (*rest != '}') {
3609 rest = next_brace_sub(rest);
3610 if (rest == NULL) {
3611 /* An illegal expression */
3612 goto simple_glob;
3613 }
3614 debug_printf_glob("rest:%s\n", rest);
3615 }
3616 rest_len = strlen(++rest) + 1;
3617
3618 /* We are sure the brace expression is well-formed */
3619
3620 /* Allocate working buffer large enough for our work */
3621 new_pattern_buf = xmalloc(strlen(pattern));
3622
3623 /* We have a brace expression. BEGIN points to the opening {,
3624 * NEXT points past the terminator of the first element, and REST
3625 * points past the final }. We will accumulate result names from
3626 * recursive runs for each brace alternative in the buffer using
3627 * GLOB_APPEND. */
3628
3629 p = begin + 1;
3630 while (1) {
3631 /* Construct the new glob expression */
3632 memcpy(
3633 mempcpy(
3634 mempcpy(new_pattern_buf,
3635 /* We know the prefix for all sub-patterns */
3636 pattern, begin - pattern),
3637 p, next - p),
3638 rest, rest_len);
3639
3640 /* Note: glob_brace() may garble new_pattern_buf[].
3641 * That's why we re-copy prefix every time (1st memcpy above).
3642 */
3643 n = glob_brace(new_pattern_buf, o, n);
3644 if (*next == '}') {
3645 /* We saw the last entry */
3646 break;
3647 }
3648 p = next + 1;
3649 next = next_brace_sub(next);
3650 }
3651 free(new_pattern_buf);
3652 return n;
3653
3654 simple_glob:
3655 {
3656 int gr;
3657 glob_t globdata;
3658
3659 memset(&globdata, 0, sizeof(globdata));
3660 gr = glob(pattern, 0, NULL, &globdata);
3661 debug_printf_glob("glob('%s'):%d\n", pattern, gr);
3662 if (gr != 0) {
3663 if (gr == GLOB_NOMATCH) {
3664 globfree(&globdata);
3665 /* NB: garbles parameter */
3666 unbackslash(pattern);
3667 o_addstr_with_NUL(o, pattern);
3668 debug_printf_glob("glob pattern '%s' is literal\n", pattern);
3669 return o_save_ptr_helper(o, n);
3670 }
3671 if (gr == GLOB_NOSPACE)
3672 bb_die_memory_exhausted();
3673 /* GLOB_ABORTED? Only happens with GLOB_ERR flag,
3674 * but we didn't specify it. Paranoia again. */
3675 bb_error_msg_and_die("glob error %d on '%s'", gr, pattern);
3676 }
3677 if (globdata.gl_pathv && globdata.gl_pathv[0]) {
3678 char **argv = globdata.gl_pathv;
3679 while (1) {
3680 o_addstr_with_NUL(o, *argv);
3681 n = o_save_ptr_helper(o, n);
3682 argv++;
3683 if (!*argv)
3684 break;
3685 }
3686 }
3687 globfree(&globdata);
3688 }
3689 return n;
3690}
3691/* Performs globbing on last list[],
3692 * saving each result as a new list[].
3693 */
3694static int perform_glob(o_string *o, int n)
3695{
3696 char *pattern, *copy;
3697
3698 debug_printf_glob("start perform_glob: n:%d o->data:%p\n", n, o->data);
3699 if (!o->data)
3700 return o_save_ptr_helper(o, n);
3701 pattern = o->data + o_get_last_ptr(o, n);
3702 debug_printf_glob("glob pattern '%s'\n", pattern);
3703 if (!glob_needed(pattern)) {
3704 /* unbackslash last string in o in place, fix length */
3705 o->length = unbackslash(pattern) - o->data;
3706 debug_printf_glob("glob pattern '%s' is literal\n", pattern);
3707 return o_save_ptr_helper(o, n);
3708 }
3709
3710 copy = xstrdup(pattern);
3711 /* "forget" pattern in o */
3712 o->length = pattern - o->data;
3713 n = glob_brace(copy, o, n);
3714 free(copy);
3715 if (DEBUG_GLOB)
3716 debug_print_list("perform_glob returning", o, n);
3717 return n;
3718}
3719
3720#else /* !HUSH_BRACE_EXPANSION */
3721
3722/* Helper */
3723static int glob_needed(const char *s)
3724{
3725 while (*s) {
3726 if (*s == '\\') {
3727 if (!s[1])
3728 return 0;
3729 s += 2;
3730 continue;
3731 }
3732 if (*s == '*' || *s == '[' || *s == '?')
3733 return 1;
3734 s++;
3735 }
3736 return 0;
3737}
3738/* Performs globbing on last list[],
3739 * saving each result as a new list[].
3740 */
3741static int perform_glob(o_string *o, int n)
3742{
3743 glob_t globdata;
3744 int gr;
3745 char *pattern;
3746
3747 debug_printf_glob("start perform_glob: n:%d o->data:%p\n", n, o->data);
3748 if (!o->data)
3749 return o_save_ptr_helper(o, n);
3750 pattern = o->data + o_get_last_ptr(o, n);
3751 debug_printf_glob("glob pattern '%s'\n", pattern);
3752 if (!glob_needed(pattern)) {
3753 literal:
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 memset(&globdata, 0, sizeof(globdata));
3761 /* Can't use GLOB_NOCHECK: it does not unescape the string.
3762 * If we glob "*.\*" and don't find anything, we need
3763 * to fall back to using literal "*.*", but GLOB_NOCHECK
3764 * will return "*.\*"!
3765 */
3766 gr = glob(pattern, 0, NULL, &globdata);
3767 debug_printf_glob("glob('%s'):%d\n", pattern, gr);
3768 if (gr != 0) {
3769 if (gr == GLOB_NOMATCH) {
3770 globfree(&globdata);
3771 goto literal;
3772 }
3773 if (gr == GLOB_NOSPACE)
3774 bb_die_memory_exhausted();
3775 /* GLOB_ABORTED? Only happens with GLOB_ERR flag,
3776 * but we didn't specify it. Paranoia again. */
3777 bb_error_msg_and_die("glob error %d on '%s'", gr, pattern);
3778 }
3779 if (globdata.gl_pathv && globdata.gl_pathv[0]) {
3780 char **argv = globdata.gl_pathv;
3781 /* "forget" pattern in o */
3782 o->length = pattern - o->data;
3783 while (1) {
3784 o_addstr_with_NUL(o, *argv);
3785 n = o_save_ptr_helper(o, n);
3786 argv++;
3787 if (!*argv)
3788 break;
3789 }
3790 }
3791 globfree(&globdata);
3792 if (DEBUG_GLOB)
3793 debug_print_list("perform_glob returning", o, n);
3794 return n;
3795}
3796
Francis Laniel8197f012023-12-22 22:02:28 +01003797#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01003798#endif /* !HUSH_BRACE_EXPANSION */
3799
3800/* If o->o_expflags & EXP_FLAG_GLOB, glob the string so far remembered.
3801 * Otherwise, just finish current list[] and start new */
3802static int o_save_ptr(o_string *o, int n)
3803{
Francis Laniel8197f012023-12-22 22:02:28 +01003804#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01003805 if (o->o_expflags & EXP_FLAG_GLOB) {
3806 /* If o->has_empty_slot, list[n] was already globbed
3807 * (if it was requested back then when it was filled)
3808 * so don't do that again! */
3809 if (!o->has_empty_slot)
3810 return perform_glob(o, n); /* o_save_ptr_helper is inside */
3811 }
Francis Laniel8197f012023-12-22 22:02:28 +01003812#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01003813 return o_save_ptr_helper(o, n);
3814}
3815
3816/* "Please convert list[n] to real char* ptrs, and NULL terminate it." */
3817static char **o_finalize_list(o_string *o, int n)
3818{
3819 char **list;
3820 int string_start;
3821
3822 if (DEBUG_EXPAND)
3823 debug_print_list("finalized", o, n);
3824 debug_printf_expand("finalized n:%d\n", n);
3825 list = (char**)o->data;
3826 string_start = ((n + 0xf) & ~0xf) * sizeof(list[0]);
3827 list[--n] = NULL;
3828 while (n) {
3829 n--;
3830 list[n] = o->data + (int)(uintptr_t)list[n] + string_start;
3831 }
3832 return list;
3833}
3834
3835static void free_pipe_list(struct pipe *pi);
3836
3837/* Returns pi->next - next pipe in the list */
3838static struct pipe *free_pipe(struct pipe *pi)
3839{
3840 struct pipe *next;
3841 int i;
3842
Francis Laniel8197f012023-12-22 22:02:28 +01003843#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01003844 debug_printf_clean("free_pipe (pid %d)\n", getpid());
Francis Laniel8197f012023-12-22 22:02:28 +01003845#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01003846 for (i = 0; i < pi->num_cmds; i++) {
3847 struct command *command;
Francis Laniel8197f012023-12-22 22:02:28 +01003848#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01003849 struct redir_struct *r, *rnext;
Francis Laniel8197f012023-12-22 22:02:28 +01003850#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01003851
3852 command = &pi->cmds[i];
3853 debug_printf_clean(" command %d:\n", i);
3854 if (command->argv) {
3855 if (DEBUG_CLEAN) {
3856 int a;
3857 char **p;
3858 for (a = 0, p = command->argv; *p; a++, p++) {
3859 debug_printf_clean(" argv[%d] = %s\n", a, *p);
3860 }
3861 }
3862 free_strings(command->argv);
3863 //command->argv = NULL;
3864 }
3865 /* not "else if": on syntax error, we may have both! */
3866 if (command->group) {
3867 debug_printf_clean(" begin group (cmd_type:%d)\n",
3868 command->cmd_type);
3869 free_pipe_list(command->group);
3870 debug_printf_clean(" end group\n");
3871 //command->group = NULL;
3872 }
3873 /* else is crucial here.
3874 * If group != NULL, child_func is meaningless */
3875#if ENABLE_HUSH_FUNCTIONS
3876 else if (command->child_func) {
3877 debug_printf_exec("cmd %p releases child func at %p\n", command, command->child_func);
3878 command->child_func->parent_cmd = NULL;
3879 }
3880#endif
3881#if !BB_MMU
3882 free(command->group_as_string);
3883 //command->group_as_string = NULL;
3884#endif
Francis Laniel8197f012023-12-22 22:02:28 +01003885#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01003886 for (r = command->redirects; r; r = rnext) {
3887 debug_printf_clean(" redirect %d%s",
3888 r->rd_fd, redir_table[r->rd_type].descrip);
3889 /* guard against the case >$FOO, where foo is unset or blank */
3890 if (r->rd_filename) {
3891 debug_printf_clean(" fname:'%s'\n", r->rd_filename);
3892 free(r->rd_filename);
3893 //r->rd_filename = NULL;
3894 }
3895 debug_printf_clean(" rd_dup:%d\n", r->rd_dup);
3896 rnext = r->next;
3897 free(r);
3898 }
3899 //command->redirects = NULL;
Francis Laniel8197f012023-12-22 22:02:28 +01003900#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01003901 }
3902 free(pi->cmds); /* children are an array, they get freed all at once */
3903 //pi->cmds = NULL;
Francis Laniel8197f012023-12-22 22:02:28 +01003904#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01003905#if ENABLE_HUSH_JOB
3906 free(pi->cmdtext);
3907 //pi->cmdtext = NULL;
3908#endif
Francis Laniel8197f012023-12-22 22:02:28 +01003909#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01003910
3911 next = pi->next;
3912 free(pi);
3913 return next;
3914}
3915
3916static void free_pipe_list(struct pipe *pi)
3917{
3918 while (pi) {
3919#if HAS_KEYWORDS
3920 debug_printf_clean("pipe reserved word %d\n", pi->res_word);
3921#endif
3922 debug_printf_clean("pipe followup code %d\n", pi->followup);
3923 pi = free_pipe(pi);
3924 }
3925}
3926
3927
3928/*** Parsing routines ***/
3929
3930#ifndef debug_print_tree
3931static void debug_print_tree(struct pipe *pi, int lvl)
3932{
3933 static const char *const PIPE[] = {
3934 [PIPE_SEQ] = "SEQ",
3935 [PIPE_AND] = "AND",
3936 [PIPE_OR ] = "OR" ,
3937 [PIPE_BG ] = "BG" ,
3938 };
Francis Laniel8197f012023-12-22 22:02:28 +01003939#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01003940 static const char *RES[] = {
3941 [RES_NONE ] = "NONE" ,
3942# if ENABLE_HUSH_IF
3943 [RES_IF ] = "IF" ,
3944 [RES_THEN ] = "THEN" ,
3945 [RES_ELIF ] = "ELIF" ,
3946 [RES_ELSE ] = "ELSE" ,
3947 [RES_FI ] = "FI" ,
3948# endif
3949# if ENABLE_HUSH_LOOPS
3950 [RES_FOR ] = "FOR" ,
3951 [RES_WHILE] = "WHILE",
3952 [RES_UNTIL] = "UNTIL",
3953 [RES_DO ] = "DO" ,
3954 [RES_DONE ] = "DONE" ,
3955# endif
3956# if ENABLE_HUSH_LOOPS || ENABLE_HUSH_CASE
3957 [RES_IN ] = "IN" ,
3958# endif
3959# if ENABLE_HUSH_CASE
3960 [RES_CASE ] = "CASE" ,
3961 [RES_CASE_IN ] = "CASE_IN" ,
3962 [RES_MATCH] = "MATCH",
3963 [RES_CASE_BODY] = "CASE_BODY",
3964 [RES_ESAC ] = "ESAC" ,
3965# endif
3966 [RES_XXXX ] = "XXXX" ,
3967 [RES_SNTX ] = "SNTX" ,
3968 };
Francis Laniel8197f012023-12-22 22:02:28 +01003969#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01003970 static const char *const CMDTYPE[] = {
3971 "{}",
3972 "()",
3973 "[noglob]",
3974# if ENABLE_HUSH_FUNCTIONS
3975 "func()",
3976# endif
3977 };
3978
3979 int pin, prn;
3980
3981 pin = 0;
3982 while (pi) {
3983 fdprintf(2, "%*spipe %d #cmds:%d %sres_word=%s followup=%d %s\n",
3984 lvl*2, "",
3985 pin,
3986 pi->num_cmds,
Francis Laniel8197f012023-12-22 22:02:28 +01003987#ifdef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01003988 (IF_HAS_KEYWORDS(pi->pi_inverted ? "! " :) ""),
3989 RES[pi->res_word],
Francis Laniel8197f012023-12-22 22:02:28 +01003990#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01003991 pi->followup, PIPE[pi->followup]
3992 );
3993 prn = 0;
3994 while (prn < pi->num_cmds) {
3995 struct command *command = &pi->cmds[prn];
3996 char **argv = command->argv;
3997
3998 fdprintf(2, "%*s cmd %d assignment_cnt:%d",
3999 lvl*2, "", prn,
4000 command->assignment_cnt);
4001# if ENABLE_HUSH_LINENO_VAR
4002 fdprintf(2, " LINENO:%u", command->lineno);
4003# endif
4004 if (command->group) {
4005 fdprintf(2, " group %s: (argv=%p)%s%s\n",
4006 CMDTYPE[command->cmd_type],
4007 argv
4008# if !BB_MMU
4009 , " group_as_string:", command->group_as_string
4010# else
4011 , "", ""
4012# endif
4013 );
4014 debug_print_tree(command->group, lvl+1);
4015 prn++;
4016 continue;
4017 }
4018 if (argv) while (*argv) {
4019 fdprintf(2, " '%s'", *argv);
4020 argv++;
4021 }
Francis Laniel8197f012023-12-22 22:02:28 +01004022#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01004023 if (command->redirects)
4024 fdprintf(2, " {redir}");
Francis Laniel8197f012023-12-22 22:02:28 +01004025#endif /* __U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01004026 fdprintf(2, "\n");
4027 prn++;
4028 }
4029 pi = pi->next;
4030 pin++;
4031 }
4032}
4033#endif /* debug_print_tree */
4034
4035static struct pipe *new_pipe(void)
4036{
4037 struct pipe *pi;
4038 pi = xzalloc(sizeof(struct pipe));
4039 /*pi->res_word = RES_NONE; - RES_NONE is 0 anyway */
4040 return pi;
4041}
4042
4043/* Command (member of a pipe) is complete, or we start a new pipe
4044 * if ctx->command is NULL.
4045 * No errors possible here.
4046 */
4047static int done_command(struct parse_context *ctx)
4048{
4049 /* The command is really already in the pipe structure, so
4050 * advance the pipe counter and make a new, null command. */
4051 struct pipe *pi = ctx->pipe;
4052 struct command *command = ctx->command;
4053
4054#if 0 /* Instead we emit error message at run time */
4055 if (ctx->pending_redirect) {
4056 /* For example, "cmd >" (no filename to redirect to) */
4057 syntax_error("invalid redirect");
4058 ctx->pending_redirect = NULL;
4059 }
4060#endif
4061
4062 if (command) {
4063 if (IS_NULL_CMD(command)) {
4064 debug_printf_parse("done_command: skipping null cmd, num_cmds=%d\n", pi->num_cmds);
4065 goto clear_and_ret;
4066 }
4067 pi->num_cmds++;
4068 debug_printf_parse("done_command: ++num_cmds=%d\n", pi->num_cmds);
4069 //debug_print_tree(ctx->list_head, 20);
4070 } else {
4071 debug_printf_parse("done_command: initializing, num_cmds=%d\n", pi->num_cmds);
4072 }
4073
4074 /* Only real trickiness here is that the uncommitted
4075 * command structure is not counted in pi->num_cmds. */
4076 pi->cmds = xrealloc(pi->cmds, sizeof(*pi->cmds) * (pi->num_cmds+1));
4077 ctx->command = command = &pi->cmds[pi->num_cmds];
4078 clear_and_ret:
4079 memset(command, 0, sizeof(*command));
4080#if ENABLE_HUSH_LINENO_VAR
4081 command->lineno = G.parse_lineno;
4082 debug_printf_parse("command->lineno = G.parse_lineno (%u)\n", G.parse_lineno);
4083#endif
4084 return pi->num_cmds; /* used only for 0/nonzero check */
4085}
4086
4087static void done_pipe(struct parse_context *ctx, pipe_style type)
4088{
4089 int not_null;
4090
4091 debug_printf_parse("done_pipe entered, followup %d\n", type);
4092 /* Close previous command */
4093 not_null = done_command(ctx);
4094#if HAS_KEYWORDS
4095 ctx->pipe->pi_inverted = ctx->ctx_inverted;
4096 ctx->ctx_inverted = 0;
4097 ctx->pipe->res_word = ctx->ctx_res_w;
4098#endif
4099 if (type == PIPE_BG && ctx->list_head != ctx->pipe) {
4100 /* Necessary since && and || have precedence over &:
4101 * "cmd1 && cmd2 &" must spawn both cmds, not only cmd2,
4102 * in a backgrounded subshell.
4103 */
4104 struct pipe *pi;
4105 struct command *command;
4106
4107 /* Is this actually this construct, all pipes end with && or ||? */
4108 pi = ctx->list_head;
4109 while (pi != ctx->pipe) {
4110 if (pi->followup != PIPE_AND && pi->followup != PIPE_OR)
4111 goto no_conv;
4112 pi = pi->next;
4113 }
4114
4115 debug_printf_parse("BG with more than one pipe, converting to { p1 &&...pN; } &\n");
4116 pi->followup = PIPE_SEQ; /* close pN _not_ with "&"! */
4117 pi = xzalloc(sizeof(*pi));
4118 pi->followup = PIPE_BG;
4119 pi->num_cmds = 1;
4120 pi->cmds = xzalloc(sizeof(pi->cmds[0]));
4121 command = &pi->cmds[0];
4122 if (CMD_NORMAL != 0) /* "if xzalloc didn't do that already" */
4123 command->cmd_type = CMD_NORMAL;
4124 command->group = ctx->list_head;
4125#if !BB_MMU
4126 command->group_as_string = xstrndup(
4127 ctx->as_string.data,
4128 ctx->as_string.length - 1 /* do not copy last char, "&" */
4129 );
4130#endif
4131 /* Replace all pipes in ctx with one newly created */
4132 ctx->list_head = ctx->pipe = pi;
4133 /* for cases like "cmd && &", do not be tricked by last command
4134 * being null - the entire {...} & is NOT null! */
4135 not_null = 1;
4136 } else {
4137 no_conv:
4138 ctx->pipe->followup = type;
4139 }
4140
4141 /* Without this check, even just <enter> on command line generates
4142 * tree of three NOPs (!). Which is harmless but annoying.
4143 * IOW: it is safe to do it unconditionally. */
4144 if (not_null
4145#if ENABLE_HUSH_IF
4146 || ctx->ctx_res_w == RES_FI
4147#endif
4148#if ENABLE_HUSH_LOOPS
4149 || ctx->ctx_res_w == RES_DONE
4150 || ctx->ctx_res_w == RES_FOR
4151 || ctx->ctx_res_w == RES_IN
4152#endif
4153#if ENABLE_HUSH_CASE
4154 || ctx->ctx_res_w == RES_ESAC
4155#endif
4156 ) {
4157 struct pipe *new_p;
4158 debug_printf_parse("done_pipe: adding new pipe: "
Francis Laniel8197f012023-12-22 22:02:28 +01004159#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01004160 "not_null:%d ctx->ctx_res_w:%d\n",
4161 not_null, ctx->ctx_res_w);
Francis Laniel8197f012023-12-22 22:02:28 +01004162#else /* __U_BOOT__ */
4163 "not_null:%d\n",
4164 not_null);
4165#endif /* __U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01004166 new_p = new_pipe();
4167 ctx->pipe->next = new_p;
4168 ctx->pipe = new_p;
4169 /* RES_THEN, RES_DO etc are "sticky" -
4170 * they remain set for pipes inside if/while.
4171 * This is used to control execution.
4172 * RES_FOR and RES_IN are NOT sticky (needed to support
4173 * cases where variable or value happens to match a keyword):
4174 */
4175#if ENABLE_HUSH_LOOPS
4176 if (ctx->ctx_res_w == RES_FOR
4177 || ctx->ctx_res_w == RES_IN)
4178 ctx->ctx_res_w = RES_NONE;
4179#endif
4180#if ENABLE_HUSH_CASE
4181 if (ctx->ctx_res_w == RES_MATCH)
4182 ctx->ctx_res_w = RES_CASE_BODY;
4183 if (ctx->ctx_res_w == RES_CASE)
4184 ctx->ctx_res_w = RES_CASE_IN;
4185#endif
4186 ctx->command = NULL; /* trick done_command below */
4187 /* Create the memory for command, roughly:
4188 * ctx->pipe->cmds = new struct command;
4189 * ctx->command = &ctx->pipe->cmds[0];
4190 */
4191 done_command(ctx);
4192 //debug_print_tree(ctx->list_head, 10);
4193 }
4194 debug_printf_parse("done_pipe return\n");
4195}
4196
4197static void initialize_context(struct parse_context *ctx)
4198{
4199 memset(ctx, 0, sizeof(*ctx));
4200 if (MAYBE_ASSIGNMENT != 0)
4201 ctx->is_assignment = MAYBE_ASSIGNMENT;
4202 ctx->pipe = ctx->list_head = new_pipe();
4203 /* Create the memory for command, roughly:
4204 * ctx->pipe->cmds = new struct command;
4205 * ctx->command = &ctx->pipe->cmds[0];
4206 */
4207 done_command(ctx);
4208}
4209
4210/* If a reserved word is found and processed, parse context is modified
4211 * and 1 is returned.
4212 */
4213#if HAS_KEYWORDS
4214struct reserved_combo {
4215 char literal[6];
4216 unsigned char res;
4217 unsigned char assignment_flag;
4218 uint32_t flag;
4219};
4220enum {
4221 FLAG_END = (1 << RES_NONE ),
4222# if ENABLE_HUSH_IF
4223 FLAG_IF = (1 << RES_IF ),
4224 FLAG_THEN = (1 << RES_THEN ),
4225 FLAG_ELIF = (1 << RES_ELIF ),
4226 FLAG_ELSE = (1 << RES_ELSE ),
4227 FLAG_FI = (1 << RES_FI ),
4228# endif
4229# if ENABLE_HUSH_LOOPS
4230 FLAG_FOR = (1 << RES_FOR ),
4231 FLAG_WHILE = (1 << RES_WHILE),
4232 FLAG_UNTIL = (1 << RES_UNTIL),
4233 FLAG_DO = (1 << RES_DO ),
4234 FLAG_DONE = (1 << RES_DONE ),
4235 FLAG_IN = (1 << RES_IN ),
4236# endif
4237# if ENABLE_HUSH_CASE
4238 FLAG_MATCH = (1 << RES_MATCH),
4239 FLAG_ESAC = (1 << RES_ESAC ),
4240# endif
4241 FLAG_START = (1 << RES_XXXX ),
4242};
4243
4244static const struct reserved_combo* match_reserved_word(o_string *word)
4245{
4246 /* Mostly a list of accepted follow-up reserved words.
4247 * FLAG_END means we are done with the sequence, and are ready
4248 * to turn the compound list into a command.
4249 * FLAG_START means the word must start a new compound list.
4250 */
4251 static const struct reserved_combo reserved_list[] ALIGN4 = {
4252# if ENABLE_HUSH_IF
4253 { "!", RES_NONE, NOT_ASSIGNMENT , 0 },
4254 { "if", RES_IF, MAYBE_ASSIGNMENT, FLAG_THEN | FLAG_START },
4255 { "then", RES_THEN, MAYBE_ASSIGNMENT, FLAG_ELIF | FLAG_ELSE | FLAG_FI },
4256 { "elif", RES_ELIF, MAYBE_ASSIGNMENT, FLAG_THEN },
4257 { "else", RES_ELSE, MAYBE_ASSIGNMENT, FLAG_FI },
4258 { "fi", RES_FI, NOT_ASSIGNMENT , FLAG_END },
4259# endif
4260# if ENABLE_HUSH_LOOPS
4261 { "for", RES_FOR, NOT_ASSIGNMENT , FLAG_IN | FLAG_DO | FLAG_START },
4262 { "while", RES_WHILE, MAYBE_ASSIGNMENT, FLAG_DO | FLAG_START },
4263 { "until", RES_UNTIL, MAYBE_ASSIGNMENT, FLAG_DO | FLAG_START },
4264 { "in", RES_IN, NOT_ASSIGNMENT , FLAG_DO },
4265 { "do", RES_DO, MAYBE_ASSIGNMENT, FLAG_DONE },
4266 { "done", RES_DONE, NOT_ASSIGNMENT , FLAG_END },
4267# endif
4268# if ENABLE_HUSH_CASE
4269 { "case", RES_CASE, NOT_ASSIGNMENT , FLAG_MATCH | FLAG_START },
4270 { "esac", RES_ESAC, NOT_ASSIGNMENT , FLAG_END },
4271# endif
4272 };
4273 const struct reserved_combo *r;
4274
4275 for (r = reserved_list; r < reserved_list + ARRAY_SIZE(reserved_list); r++) {
4276 if (strcmp(word->data, r->literal) == 0)
4277 return r;
4278 }
4279 return NULL;
4280}
4281/* Return NULL: not a keyword, else: keyword
4282 */
4283static const struct reserved_combo* reserved_word(struct parse_context *ctx)
4284{
4285# if ENABLE_HUSH_CASE
4286 static const struct reserved_combo reserved_match = {
4287 "", RES_MATCH, NOT_ASSIGNMENT , FLAG_MATCH | FLAG_ESAC
4288 };
4289# endif
4290 const struct reserved_combo *r;
4291
4292 if (ctx->word.has_quoted_part)
4293 return 0;
4294 r = match_reserved_word(&ctx->word);
4295 if (!r)
4296 return r; /* NULL */
4297
4298 debug_printf("found reserved word %s, res %d\n", r->literal, r->res);
4299# if ENABLE_HUSH_CASE
4300 if (r->res == RES_IN && ctx->ctx_res_w == RES_CASE_IN) {
4301 /* "case word IN ..." - IN part starts first MATCH part */
4302 r = &reserved_match;
4303 } else
4304# endif
4305 if (r->flag == 0) { /* '!' */
4306 if (ctx->ctx_inverted) { /* bash doesn't accept '! ! true' */
4307 syntax_error("! ! command");
4308 ctx->ctx_res_w = RES_SNTX;
4309 }
4310 ctx->ctx_inverted = 1;
4311 return r;
4312 }
4313 if (r->flag & FLAG_START) {
4314 struct parse_context *old;
4315
4316 old = xmemdup(ctx, sizeof(*ctx));
4317 debug_printf_parse("push stack %p\n", old);
4318 initialize_context(ctx);
4319 ctx->stack = old;
4320 } else if (/*ctx->ctx_res_w == RES_NONE ||*/ !(ctx->old_flag & (1 << r->res))) {
4321 syntax_error_at(ctx->word.data);
4322 ctx->ctx_res_w = RES_SNTX;
4323 return r;
4324 } else {
4325 /* "{...} fi" is ok. "{...} if" is not
4326 * Example:
4327 * if { echo foo; } then { echo bar; } fi */
4328 if (ctx->command->group)
4329 done_pipe(ctx, PIPE_SEQ);
4330 }
4331
4332 ctx->ctx_res_w = r->res;
4333 ctx->old_flag = r->flag;
4334 ctx->is_assignment = r->assignment_flag;
4335 debug_printf_parse("ctx->is_assignment='%s'\n", assignment_flag[ctx->is_assignment]);
4336
4337 if (ctx->old_flag & FLAG_END) {
4338 struct parse_context *old;
4339
4340 done_pipe(ctx, PIPE_SEQ);
4341 debug_printf_parse("pop stack %p\n", ctx->stack);
4342 old = ctx->stack;
4343 old->command->group = ctx->list_head;
4344 old->command->cmd_type = CMD_NORMAL;
4345# if !BB_MMU
4346 /* At this point, the compound command's string is in
4347 * ctx->as_string... except for the leading keyword!
4348 * Consider this example: "echo a | if true; then echo a; fi"
4349 * ctx->as_string will contain "true; then echo a; fi",
4350 * with "if " remaining in old->as_string!
4351 */
4352 {
4353 char *str;
4354 int len = old->as_string.length;
4355 /* Concatenate halves */
4356 o_addstr(&old->as_string, ctx->as_string.data);
4357 o_free(&ctx->as_string);
4358 /* Find where leading keyword starts in first half */
4359 str = old->as_string.data + len;
4360 if (str > old->as_string.data)
4361 str--; /* skip whitespace after keyword */
4362 while (str > old->as_string.data && isalpha(str[-1]))
4363 str--;
4364 /* Ugh, we're done with this horrid hack */
4365 old->command->group_as_string = xstrdup(str);
4366 debug_printf_parse("pop, remembering as:'%s'\n",
4367 old->command->group_as_string);
4368 }
4369# endif
4370 *ctx = *old; /* physical copy */
4371 free(old);
4372 }
4373 return r;
4374}
4375#endif /* HAS_KEYWORDS */
4376
4377/* Word is complete, look at it and update parsing context.
4378 * Normal return is 0. Syntax errors return 1.
4379 * Note: on return, word is reset, but not o_free'd!
4380 */
4381static int done_word(struct parse_context *ctx)
4382{
4383 struct command *command = ctx->command;
4384
4385 debug_printf_parse("done_word entered: '%s' %p\n", ctx->word.data, command);
4386 if (ctx->word.length == 0 && !ctx->word.has_quoted_part) {
4387 debug_printf_parse("done_word return 0: true null, ignored\n");
4388 return 0;
4389 }
4390
Francis Laniel8197f012023-12-22 22:02:28 +01004391#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01004392 if (ctx->pending_redirect) {
4393 /* We do not glob in e.g. >*.tmp case. bash seems to glob here
4394 * only if run as "bash", not "sh" */
4395 /* http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html
4396 * "2.7 Redirection
4397 * If the redirection operator is "<<" or "<<-", the word
4398 * that follows the redirection operator shall be
4399 * subjected to quote removal; it is unspecified whether
4400 * any of the other expansions occur. For the other
4401 * redirection operators, the word that follows the
4402 * redirection operator shall be subjected to tilde
4403 * expansion, parameter expansion, command substitution,
4404 * arithmetic expansion, and quote removal.
4405 * Pathname expansion shall not be performed
4406 * on the word by a non-interactive shell; an interactive
4407 * shell may perform it, but shall do so only when
4408 * the expansion would result in one word."
4409 */
4410//bash does not do parameter/command substitution or arithmetic expansion
4411//for _heredoc_ redirection word: these constructs look for exact eof marker
4412// as written:
4413// <<EOF$t
4414// <<EOF$((1))
4415// <<EOF`true` [this case also makes heredoc "quoted", a-la <<"EOF". Probably bash-4.3.43 bug]
4416
4417 ctx->pending_redirect->rd_filename = xstrdup(ctx->word.data);
4418 /* Cater for >\file case:
4419 * >\a creates file a; >\\a, >"\a", >"\\a" create file \a
4420 * Same with heredocs:
4421 * for <<\H delim is H; <<\\H, <<"\H", <<"\\H" - \H
4422 */
4423 if (ctx->pending_redirect->rd_type == REDIRECT_HEREDOC) {
4424 unbackslash(ctx->pending_redirect->rd_filename);
4425 /* Is it <<"HEREDOC"? */
4426 if (ctx->word.has_quoted_part) {
4427 ctx->pending_redirect->rd_dup |= HEREDOC_QUOTED;
4428 }
4429 }
4430 debug_printf_parse("word stored in rd_filename: '%s'\n", ctx->word.data);
4431 ctx->pending_redirect = NULL;
4432 } else {
Francis Laniel8197f012023-12-22 22:02:28 +01004433#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01004434#if HAS_KEYWORDS
4435# if ENABLE_HUSH_CASE
4436 if (ctx->ctx_dsemicolon
4437 && strcmp(ctx->word.data, "esac") != 0 /* not "... pattern) cmd;; esac" */
4438 ) {
4439 /* already done when ctx_dsemicolon was set to 1: */
4440 /* ctx->ctx_res_w = RES_MATCH; */
4441 ctx->ctx_dsemicolon = 0;
4442 } else
4443# endif
4444# if defined(CMD_TEST2_SINGLEWORD_NOGLOB)
4445 if (command->cmd_type == CMD_TEST2_SINGLEWORD_NOGLOB
4446 && strcmp(ctx->word.data, "]]") == 0
4447 ) {
4448 /* allow "[[ ]] >file" etc */
4449 command->cmd_type = CMD_SINGLEWORD_NOGLOB;
4450 } else
4451# endif
4452 if (!command->argv /* if it's the first word... */
4453# if ENABLE_HUSH_LOOPS
4454 && ctx->ctx_res_w != RES_FOR /* ...not after FOR or IN */
4455 && ctx->ctx_res_w != RES_IN
4456# endif
4457# if ENABLE_HUSH_CASE
4458 && ctx->ctx_res_w != RES_CASE
4459# endif
4460 ) {
4461 const struct reserved_combo *reserved;
4462 reserved = reserved_word(ctx);
4463 debug_printf_parse("checking for reserved-ness: %d\n", !!reserved);
4464 if (reserved) {
4465# if ENABLE_HUSH_LINENO_VAR
4466/* Case:
4467 * "while ...; do
4468 * cmd ..."
4469 * If we don't close the pipe _now_, immediately after "do", lineno logic
4470 * sees "cmd" as starting at "do" - i.e., at the previous line.
4471 */
4472 if (0
4473 IF_HUSH_IF(|| reserved->res == RES_THEN)
4474 IF_HUSH_IF(|| reserved->res == RES_ELIF)
4475 IF_HUSH_IF(|| reserved->res == RES_ELSE)
4476 IF_HUSH_LOOPS(|| reserved->res == RES_DO)
4477 ) {
4478 done_pipe(ctx, PIPE_SEQ);
4479 }
4480# endif
4481 o_reset_to_empty_unquoted(&ctx->word);
4482 debug_printf_parse("done_word return %d\n",
4483 (ctx->ctx_res_w == RES_SNTX));
4484 return (ctx->ctx_res_w == RES_SNTX);
4485 }
4486# if defined(CMD_TEST2_SINGLEWORD_NOGLOB)
4487 if (strcmp(ctx->word.data, "[[") == 0) {
4488 command->cmd_type = CMD_TEST2_SINGLEWORD_NOGLOB;
4489 } else
4490# endif
4491# if defined(CMD_SINGLEWORD_NOGLOB)
4492 if (0
4493 /* In bash, local/export/readonly are special, args
4494 * are assignments and therefore expansion of them
4495 * should be "one-word" expansion:
4496 * $ export i=`echo 'a b'` # one arg: "i=a b"
4497 * compare with:
4498 * $ ls i=`echo 'a b'` # two args: "i=a" and "b"
4499 * ls: cannot access i=a: No such file or directory
4500 * ls: cannot access b: No such file or directory
4501 * Note: bash 3.2.33(1) does this only if export word
4502 * itself is not quoted:
4503 * $ export i=`echo 'aaa bbb'`; echo "$i"
4504 * aaa bbb
4505 * $ "export" i=`echo 'aaa bbb'`; echo "$i"
4506 * aaa
4507 */
4508 IF_HUSH_LOCAL( || strcmp(ctx->word.data, "local") == 0)
4509 IF_HUSH_EXPORT( || strcmp(ctx->word.data, "export") == 0)
4510 IF_HUSH_READONLY(|| strcmp(ctx->word.data, "readonly") == 0)
4511 ) {
4512 command->cmd_type = CMD_SINGLEWORD_NOGLOB;
4513 }
4514# else
4515 { /* empty block to pair "if ... else" */ }
4516# endif
4517 }
4518#endif /* HAS_KEYWORDS */
4519
4520 if (command->group) {
4521 /* "{ echo foo; } echo bar" - bad */
4522 syntax_error_at(ctx->word.data);
4523 debug_printf_parse("done_word return 1: syntax error, "
4524 "groups and arglists don't mix\n");
4525 return 1;
4526 }
4527
4528 /* If this word wasn't an assignment, next ones definitely
4529 * can't be assignments. Even if they look like ones. */
4530 if (ctx->is_assignment != DEFINITELY_ASSIGNMENT
4531 && ctx->is_assignment != WORD_IS_KEYWORD
4532 ) {
4533 ctx->is_assignment = NOT_ASSIGNMENT;
4534 } else {
4535 if (ctx->is_assignment == DEFINITELY_ASSIGNMENT) {
4536 command->assignment_cnt++;
4537 debug_printf_parse("++assignment_cnt=%d\n", command->assignment_cnt);
4538 }
4539 debug_printf_parse("ctx->is_assignment was:'%s'\n", assignment_flag[ctx->is_assignment]);
4540 ctx->is_assignment = MAYBE_ASSIGNMENT;
4541 }
4542 debug_printf_parse("ctx->is_assignment='%s'\n", assignment_flag[ctx->is_assignment]);
4543 command->argv = add_string_to_strings(command->argv, xstrdup(ctx->word.data));
Francis Laniel8197f012023-12-22 22:02:28 +01004544#ifdef __U_BOOT__
4545 command->argc++;
4546#endif /* __U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01004547 debug_print_strings("word appended to argv", command->argv);
Francis Laniel8197f012023-12-22 22:02:28 +01004548
4549#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01004550 }
Francis Laniel8197f012023-12-22 22:02:28 +01004551#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01004552
4553#if ENABLE_HUSH_LOOPS
4554 if (ctx->ctx_res_w == RES_FOR) {
4555 if (ctx->word.has_quoted_part
4556 || endofname(command->argv[0])[0] != '\0'
4557 ) {
4558 /* bash says just "not a valid identifier" */
4559 syntax_error("bad variable name in for");
4560 return 1;
4561 }
4562 /* Force FOR to have just one word (variable name) */
4563 /* NB: basically, this makes hush see "for v in ..."
4564 * syntax as if it is "for v; in ...". FOR and IN become
4565 * two pipe structs in parse tree. */
4566 done_pipe(ctx, PIPE_SEQ);
4567 }
4568#endif
4569#if ENABLE_HUSH_CASE
4570 /* Force CASE to have just one word */
4571 if (ctx->ctx_res_w == RES_CASE) {
4572 done_pipe(ctx, PIPE_SEQ);
4573 }
4574#endif
4575
4576 o_reset_to_empty_unquoted(&ctx->word);
4577
4578 debug_printf_parse("done_word return 0\n");
4579 return 0;
4580}
4581
4582
Francis Laniel8197f012023-12-22 22:02:28 +01004583#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01004584/* Peek ahead in the input to find out if we have a "&n" construct,
4585 * as in "2>&1", that represents duplicating a file descriptor.
4586 * Return:
4587 * REDIRFD_CLOSE if >&- "close fd" construct is seen,
4588 * REDIRFD_SYNTAX_ERR if syntax error,
4589 * REDIRFD_TO_FILE if no & was seen,
4590 * or the number found.
4591 */
4592#if BB_MMU
4593#define parse_redir_right_fd(as_string, input) \
4594 parse_redir_right_fd(input)
4595#endif
4596static int parse_redir_right_fd(o_string *as_string, struct in_str *input)
4597{
4598 int ch, d, ok;
4599
4600 ch = i_peek(input);
4601 if (ch != '&')
4602 return REDIRFD_TO_FILE;
4603
4604 ch = i_getch(input); /* get the & */
4605 nommu_addchr(as_string, ch);
4606 ch = i_peek(input);
4607 if (ch == '-') {
4608 ch = i_getch(input);
4609 nommu_addchr(as_string, ch);
4610 return REDIRFD_CLOSE;
4611 }
4612 d = 0;
4613 ok = 0;
4614 while (ch != EOF && isdigit(ch)) {
4615 d = d*10 + (ch-'0');
4616 ok = 1;
4617 ch = i_getch(input);
4618 nommu_addchr(as_string, ch);
4619 ch = i_peek(input);
4620 }
4621 if (ok) return d;
4622
4623//TODO: this is the place to catch ">&file" bashism (redirect both fd 1 and 2)
4624
4625 bb_simple_error_msg("ambiguous redirect");
4626 return REDIRFD_SYNTAX_ERR;
4627}
4628
4629/* Return code is 0 normal, 1 if a syntax error is detected
4630 */
4631static int parse_redirect(struct parse_context *ctx,
4632 int fd,
4633 redir_type style,
4634 struct in_str *input)
4635{
4636 struct command *command = ctx->command;
4637 struct redir_struct *redir;
4638 struct redir_struct **redirp;
4639 int dup_num;
4640
4641 dup_num = REDIRFD_TO_FILE;
4642 if (style != REDIRECT_HEREDOC) {
4643 /* Check for a '>&1' type redirect */
4644 dup_num = parse_redir_right_fd(&ctx->as_string, input);
4645 if (dup_num == REDIRFD_SYNTAX_ERR)
4646 return 1;
4647 } else {
4648 int ch = i_peek_and_eat_bkslash_nl(input);
4649 dup_num = (ch == '-'); /* HEREDOC_SKIPTABS bit is 1 */
4650 if (dup_num) { /* <<-... */
4651 ch = i_getch(input);
4652 nommu_addchr(&ctx->as_string, ch);
4653 ch = i_peek(input);
4654 }
4655 }
4656
4657 if (style == REDIRECT_OVERWRITE && dup_num == REDIRFD_TO_FILE) {
4658 int ch = i_peek_and_eat_bkslash_nl(input);
4659 if (ch == '|') {
4660 /* >|FILE redirect ("clobbering" >).
4661 * Since we do not support "set -o noclobber" yet,
4662 * >| and > are the same for now. Just eat |.
4663 */
4664 ch = i_getch(input);
4665 nommu_addchr(&ctx->as_string, ch);
4666 }
4667 }
4668
4669 /* Create a new redir_struct and append it to the linked list */
4670 redirp = &command->redirects;
4671 while ((redir = *redirp) != NULL) {
4672 redirp = &(redir->next);
4673 }
4674 *redirp = redir = xzalloc(sizeof(*redir));
4675 /* redir->next = NULL; */
4676 /* redir->rd_filename = NULL; */
4677 redir->rd_type = style;
4678 redir->rd_fd = (fd == -1) ? redir_table[style].default_fd : fd;
4679
4680 debug_printf_parse("redirect type %d %s\n", redir->rd_fd,
4681 redir_table[style].descrip);
4682
4683 redir->rd_dup = dup_num;
4684 if (style != REDIRECT_HEREDOC && dup_num != REDIRFD_TO_FILE) {
4685 /* Erik had a check here that the file descriptor in question
4686 * is legit; I postpone that to "run time"
4687 * A "-" representation of "close me" shows up as a -3 here */
4688 debug_printf_parse("duplicating redirect '%d>&%d'\n",
4689 redir->rd_fd, redir->rd_dup);
4690 } else {
4691#if 0 /* Instead we emit error message at run time */
4692 if (ctx->pending_redirect) {
4693 /* For example, "cmd > <file" */
4694 syntax_error("invalid redirect");
4695 }
4696#endif
4697 /* Set ctx->pending_redirect, so we know what to do at the
4698 * end of the next parsed word. */
4699 ctx->pending_redirect = redir;
4700 }
4701 return 0;
4702}
4703
4704/* If a redirect is immediately preceded by a number, that number is
4705 * supposed to tell which file descriptor to redirect. This routine
4706 * looks for such preceding numbers. In an ideal world this routine
4707 * needs to handle all the following classes of redirects...
4708 * echo 2>foo # redirects fd 2 to file "foo", nothing passed to echo
4709 * echo 49>foo # redirects fd 49 to file "foo", nothing passed to echo
4710 * echo -2>foo # redirects fd 1 to file "foo", "-2" passed to echo
4711 * echo 49x>foo # redirects fd 1 to file "foo", "49x" passed to echo
4712 *
4713 * http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html
4714 * "2.7 Redirection
4715 * ... If n is quoted, the number shall not be recognized as part of
4716 * the redirection expression. For example:
4717 * echo \2>a
4718 * writes the character 2 into file a"
4719 * We are getting it right by setting ->has_quoted_part on any \<char>
4720 *
4721 * A -1 return means no valid number was found,
4722 * the caller should use the appropriate default for this redirection.
4723 */
4724static int redirect_opt_num(o_string *o)
4725{
4726 int num;
4727
4728 if (o->data == NULL)
4729 return -1;
4730 num = bb_strtou(o->data, NULL, 10);
4731 if (errno || num < 0)
4732 return -1;
4733 o_reset_to_empty_unquoted(o);
4734 return num;
4735}
4736
4737#if BB_MMU
4738#define fetch_till_str(as_string, input, word, skip_tabs) \
4739 fetch_till_str(input, word, skip_tabs)
4740#endif
4741static char *fetch_till_str(o_string *as_string,
4742 struct in_str *input,
4743 const char *word,
4744 int heredoc_flags)
4745{
4746 o_string heredoc = NULL_O_STRING;
4747 unsigned past_EOL;
4748 int prev = 0; /* not \ */
4749 int ch;
4750
4751 /* Starting with "" is necessary for this case:
4752 * cat <<EOF
4753 *
4754 * xxx
4755 * EOF
4756 */
4757 heredoc.data = xzalloc(1); /* start as "", not as NULL */
4758
4759 goto jump_in;
4760
4761 while (1) {
4762 ch = i_getch(input);
4763 if (ch != EOF)
4764 nommu_addchr(as_string, ch);
4765 if (ch == '\n' || ch == EOF) {
4766 check_heredoc_end:
Francis Laniel8197f012023-12-22 22:02:28 +01004767#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01004768 if ((heredoc_flags & HEREDOC_QUOTED) || prev != '\\') {
Francis Laniel8197f012023-12-22 22:02:28 +01004769#else /* __U_BOOT__ */
4770 if (prev != '\\') {
4771#endif
Francis Lanielb234f7e2023-12-22 22:02:27 +01004772 /* End-of-line, and not a line continuation */
4773 if (strcmp(heredoc.data + past_EOL, word) == 0) {
4774 heredoc.data[past_EOL] = '\0';
4775 debug_printf_heredoc("parsed '%s' heredoc '%s'\n", word, heredoc.data);
4776 return heredoc.data;
4777 }
4778 if (ch == '\n') {
4779 /* This is a new line.
4780 * Remember position and backslash-escaping status.
4781 */
4782 o_addchr(&heredoc, ch);
4783 prev = ch;
4784 jump_in:
4785 past_EOL = heredoc.length;
4786 /* Get 1st char of next line, possibly skipping leading tabs */
4787 do {
4788 ch = i_getch(input);
4789 if (ch != EOF)
4790 nommu_addchr(as_string, ch);
Francis Laniel8197f012023-12-22 22:02:28 +01004791#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01004792 } while ((heredoc_flags & HEREDOC_SKIPTABS) && ch == '\t');
Francis Laniel8197f012023-12-22 22:02:28 +01004793#else /* __U_BOOT__ */
4794 } while (ch == '\t');
4795#endif
Francis Lanielb234f7e2023-12-22 22:02:27 +01004796 /* If this immediately ended the line,
4797 * go back to end-of-line checks.
4798 */
4799 if (ch == '\n')
4800 goto check_heredoc_end;
4801 }
4802 } else {
4803 /* Backslash-line continuation in an unquoted
4804 * heredoc. This does not need special handling
4805 * for heredoc body (unquoted heredocs are
4806 * expanded on "execution" and that would take
4807 * care of this case too), but not the case
4808 * of line continuation *in terminator*:
4809 * cat <<EOF
4810 * Ok1
4811 * EO\
4812 * F
4813 */
4814 heredoc.data[--heredoc.length] = '\0';
4815 prev = 0; /* not '\' */
4816 continue;
4817 }
4818 }
4819 if (ch == EOF) {
4820 o_free(&heredoc);
4821 return NULL; /* error */
4822 }
4823 o_addchr(&heredoc, ch);
4824 nommu_addchr(as_string, ch);
4825 if (prev == '\\' && ch == '\\')
4826 /* Correctly handle foo\\<eol> (not a line cont.) */
4827 prev = 0; /* not '\' */
4828 else
4829 prev = ch;
4830 }
4831}
Francis Laniel8197f012023-12-22 22:02:28 +01004832#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01004833
4834/* Look at entire parse tree for not-yet-loaded REDIRECT_HEREDOCs
4835 * and load them all. There should be exactly heredoc_cnt of them.
4836 */
4837#if BB_MMU
4838#define fetch_heredocs(as_string, pi, heredoc_cnt, input) \
4839 fetch_heredocs(pi, heredoc_cnt, input)
4840#endif
4841static int fetch_heredocs(o_string *as_string, struct pipe *pi, int heredoc_cnt, struct in_str *input)
4842{
4843 while (pi && heredoc_cnt) {
4844 int i;
4845 struct command *cmd = pi->cmds;
4846
4847 debug_printf_heredoc("fetch_heredocs: num_cmds:%d cmd argv0:'%s'\n",
4848 pi->num_cmds,
4849 cmd->argv ? cmd->argv[0] : "NONE"
4850 );
4851 for (i = 0; i < pi->num_cmds; i++) {
Francis Laniel8197f012023-12-22 22:02:28 +01004852#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01004853 struct redir_struct *redir = cmd->redirects;
4854
Francis Laniel8197f012023-12-22 22:02:28 +01004855#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01004856 debug_printf_heredoc("fetch_heredocs: %d cmd argv0:'%s'\n",
4857 i, cmd->argv ? cmd->argv[0] : "NONE");
Francis Laniel8197f012023-12-22 22:02:28 +01004858#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01004859 while (redir) {
4860 if (redir->rd_type == REDIRECT_HEREDOC) {
4861 char *p;
4862
4863 redir->rd_type = REDIRECT_HEREDOC2;
4864 /* redir->rd_dup is (ab)used to indicate <<- */
4865 p = fetch_till_str(as_string, input,
4866 redir->rd_filename, redir->rd_dup);
4867 if (!p) {
4868 syntax_error("unexpected EOF in here document");
4869 return -1;
4870 }
4871 free(redir->rd_filename);
4872 redir->rd_filename = p;
4873 heredoc_cnt--;
4874 }
4875 redir = redir->next;
4876 }
Francis Laniel8197f012023-12-22 22:02:28 +01004877#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01004878 if (cmd->group) {
4879 //bb_error_msg("%s:%u heredoc_cnt:%d", __func__, __LINE__, heredoc_cnt);
4880 heredoc_cnt = fetch_heredocs(as_string, cmd->group, heredoc_cnt, input);
4881 //bb_error_msg("%s:%u heredoc_cnt:%d", __func__, __LINE__, heredoc_cnt);
4882 if (heredoc_cnt < 0)
4883 return heredoc_cnt; /* error */
4884 }
4885 cmd++;
4886 }
4887 pi = pi->next;
4888 }
4889 return heredoc_cnt;
4890}
4891
4892
4893static int run_list(struct pipe *pi);
4894#if BB_MMU
4895#define parse_stream(pstring, heredoc_cnt_ptr, input, end_trigger) \
4896 parse_stream(heredoc_cnt_ptr, input, end_trigger)
4897#endif
4898static struct pipe *parse_stream(char **pstring,
4899 int *heredoc_cnt_ptr,
4900 struct in_str *input,
4901 int end_trigger);
4902
4903/* Returns number of heredocs not yet consumed,
4904 * or -1 on error.
4905 */
4906static int parse_group(struct parse_context *ctx,
4907 struct in_str *input, int ch)
4908{
4909 /* ctx->word contains characters seen prior to ( or {.
4910 * Typically it's empty, but for function defs,
4911 * it contains function name (without '()'). */
4912#if BB_MMU
4913# define as_string NULL
4914#else
4915 char *as_string = NULL;
4916#endif
4917 struct pipe *pipe_list;
4918 int heredoc_cnt = 0;
4919 int endch;
4920 struct command *command = ctx->command;
4921
4922 debug_printf_parse("parse_group entered\n");
4923#if ENABLE_HUSH_FUNCTIONS
4924 if (ch == '(' && !ctx->word.has_quoted_part) {
4925 if (ctx->word.length)
4926 if (done_word(ctx))
4927 return -1;
4928 if (!command->argv)
4929 goto skip; /* (... */
4930 if (command->argv[1]) { /* word word ... (... */
4931 syntax_error_unexpected_ch('(');
4932 return -1;
4933 }
4934 /* it is "word(..." or "word (..." */
4935 do
4936 ch = i_getch(input);
4937 while (ch == ' ' || ch == '\t');
4938 if (ch != ')') {
4939 syntax_error_unexpected_ch(ch);
4940 return -1;
4941 }
4942 nommu_addchr(&ctx->as_string, ch);
4943 do
4944 ch = i_getch(input);
4945 while (ch == ' ' || ch == '\t' || ch == '\n');
4946 if (ch != '{' && ch != '(') {
4947 syntax_error_unexpected_ch(ch);
4948 return -1;
4949 }
4950 nommu_addchr(&ctx->as_string, ch);
4951 command->cmd_type = CMD_FUNCDEF;
4952 goto skip;
4953 }
4954#endif
4955
4956#if 0 /* Prevented by caller */
4957 if (command->argv /* word [word]{... */
4958 || ctx->word.length /* word{... */
4959 || ctx->word.has_quoted_part /* ""{... */
4960 ) {
4961 syntax_error(NULL);
4962 debug_printf_parse("parse_group return -1: "
4963 "syntax error, groups and arglists don't mix\n");
4964 return -1;
4965 }
4966#endif
4967
Francis Laniel8197f012023-12-22 22:02:28 +01004968#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01004969 IF_HUSH_FUNCTIONS(skip:)
Francis Laniel8197f012023-12-22 22:02:28 +01004970#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01004971
4972 endch = '}';
4973 if (ch == '(') {
4974 endch = ')';
Francis Laniel8197f012023-12-22 22:02:28 +01004975#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01004976 IF_HUSH_FUNCTIONS(if (command->cmd_type != CMD_FUNCDEF))
4977 command->cmd_type = CMD_SUBSHELL;
Francis Laniel8197f012023-12-22 22:02:28 +01004978#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01004979 } else {
4980 /* bash does not allow "{echo...", requires whitespace */
4981 ch = i_peek(input);
4982 if (ch != ' ' && ch != '\t' && ch != '\n'
4983 && ch != '(' /* but "{(..." is allowed (without whitespace) */
4984 ) {
4985 syntax_error_unexpected_ch(ch);
4986 return -1;
4987 }
4988 if (ch != '(') {
4989 ch = i_getch(input);
4990 nommu_addchr(&ctx->as_string, ch);
4991 }
4992 }
4993
4994 debug_printf_heredoc("calling parse_stream, heredoc_cnt:%d\n", heredoc_cnt);
4995 pipe_list = parse_stream(&as_string, &heredoc_cnt, input, endch);
4996 debug_printf_heredoc("parse_stream returned: heredoc_cnt:%d\n", heredoc_cnt);
4997#if !BB_MMU
4998 if (as_string)
4999 o_addstr(&ctx->as_string, as_string);
5000#endif
5001
5002 /* empty ()/{} or parse error? */
5003 if (!pipe_list || pipe_list == ERR_PTR) {
5004 /* parse_stream already emitted error msg */
5005 if (!BB_MMU)
5006 free(as_string);
5007 debug_printf_parse("parse_group return -1: "
5008 "parse_stream returned %p\n", pipe_list);
5009 return -1;
5010 }
5011#if !BB_MMU
5012 as_string[strlen(as_string) - 1] = '\0'; /* plink ')' or '}' */
5013 command->group_as_string = as_string;
5014 debug_printf_parse("end of group, remembering as:'%s'\n",
5015 command->group_as_string);
5016#endif
5017
5018#if ENABLE_HUSH_FUNCTIONS
5019 /* Convert "f() (cmds)" to "f() {(cmds)}" */
5020 if (command->cmd_type == CMD_FUNCDEF && endch == ')') {
5021 struct command *cmd2;
5022
5023 cmd2 = xzalloc(sizeof(*cmd2));
5024 cmd2->cmd_type = CMD_SUBSHELL;
5025 cmd2->group = pipe_list;
5026# if !BB_MMU
5027//UNTESTED!
5028 cmd2->group_as_string = command->group_as_string;
5029 command->group_as_string = xasprintf("(%s)", command->group_as_string);
5030# endif
5031
5032 pipe_list = new_pipe();
5033 pipe_list->cmds = cmd2;
5034 pipe_list->num_cmds = 1;
5035 }
5036#endif
5037
5038 command->group = pipe_list;
5039
5040 debug_printf_parse("parse_group return %d\n", heredoc_cnt);
5041 return heredoc_cnt;
5042 /* command remains "open", available for possible redirects */
5043#undef as_string
5044}
5045
Francis Laniel8197f012023-12-22 22:02:28 +01005046#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01005047#if ENABLE_HUSH_TICK || ENABLE_FEATURE_SH_MATH || ENABLE_HUSH_DOLLAR_OPS
5048/* Subroutines for copying $(...) and `...` things */
5049/* '...' */
5050static int add_till_single_quote(o_string *dest, struct in_str *input)
5051{
5052 while (1) {
5053 int ch = i_getch(input);
5054 if (ch == EOF) {
5055 syntax_error_unterm_ch('\'');
5056 return 0;
5057 }
5058 if (ch == '\'')
5059 return 1;
5060 o_addchr(dest, ch);
5061 }
5062}
5063static int add_till_single_quote_dquoted(o_string *dest, struct in_str *input)
5064{
5065 while (1) {
5066 int ch = i_getch(input);
5067 if (ch == EOF) {
5068 syntax_error_unterm_ch('\'');
5069 return 0;
5070 }
5071 if (ch == '\'')
5072 return 1;
5073 o_addqchr(dest, ch);
5074 }
5075}
Francis Laniel8197f012023-12-22 22:02:28 +01005076
Francis Lanielb234f7e2023-12-22 22:02:27 +01005077/* "...\"...`..`...." - do we need to handle "...$(..)..." too? */
5078static int add_till_backquote(o_string *dest, struct in_str *input, int in_dquote);
5079static int add_till_double_quote(o_string *dest, struct in_str *input)
5080{
5081 while (1) {
5082 int ch = i_getch(input);
5083 if (ch == EOF) {
5084 syntax_error_unterm_ch('"');
5085 return 0;
5086 }
5087 if (ch == '"')
5088 return 1;
5089 if (ch == '\\') { /* \x. Copy both chars. */
5090 o_addchr(dest, ch);
5091 ch = i_getch(input);
5092 }
5093 o_addchr(dest, ch);
5094 if (ch == '`') {
5095 if (!add_till_backquote(dest, input, /*in_dquote:*/ 1))
5096 return 0;
5097 o_addchr(dest, ch);
5098 continue;
5099 }
5100 //if (ch == '$') ...
5101 }
5102}
Francis Laniel8197f012023-12-22 22:02:28 +01005103
5104
Francis Lanielb234f7e2023-12-22 22:02:27 +01005105/* Process `cmd` - copy contents until "`" is seen. Complicated by
5106 * \` quoting.
5107 * "Within the backquoted style of command substitution, backslash
5108 * shall retain its literal meaning, except when followed by: '$', '`', or '\'.
5109 * The search for the matching backquote shall be satisfied by the first
5110 * backquote found without a preceding backslash; during this search,
5111 * if a non-escaped backquote is encountered within a shell comment,
5112 * a here-document, an embedded command substitution of the $(command)
5113 * form, or a quoted string, undefined results occur. A single-quoted
5114 * or double-quoted string that begins, but does not end, within the
5115 * "`...`" sequence produces undefined results."
5116 * Example Output
5117 * echo `echo '\'TEST\`echo ZZ\`BEST` \TESTZZBEST
5118 */
5119static int add_till_backquote(o_string *dest, struct in_str *input, int in_dquote)
5120{
5121 while (1) {
5122 int ch = i_getch(input);
5123 if (ch == '`')
5124 return 1;
5125 if (ch == '\\') {
5126 /* \x. Copy both unless it is \`, \$, \\ and maybe \" */
5127 ch = i_getch(input);
5128 if (ch != '`'
5129 && ch != '$'
5130 && ch != '\\'
5131 && (!in_dquote || ch != '"')
5132 ) {
5133 o_addchr(dest, '\\');
5134 }
5135 }
5136 if (ch == EOF) {
5137 syntax_error_unterm_ch('`');
5138 return 0;
5139 }
5140 o_addchr(dest, ch);
5141 }
5142}
5143/* Process $(cmd) - copy contents until ")" is seen. Complicated by
5144 * quoting and nested ()s.
5145 * "With the $(command) style of command substitution, all characters
5146 * following the open parenthesis to the matching closing parenthesis
5147 * constitute the command. Any valid shell script can be used for command,
5148 * except a script consisting solely of redirections which produces
5149 * unspecified results."
5150 * Example Output
5151 * echo $(echo '(TEST)' BEST) (TEST) BEST
5152 * echo $(echo 'TEST)' BEST) TEST) BEST
5153 * echo $(echo \(\(TEST\) BEST) ((TEST) BEST
5154 *
5155 * Also adapted to eat ${var%...} and $((...)) constructs, since ... part
5156 * can contain arbitrary constructs, just like $(cmd).
5157 * In bash compat mode, it needs to also be able to stop on ':' or '/'
5158 * for ${var:N[:M]} and ${var/P[/R]} parsing.
5159 */
5160#define DOUBLE_CLOSE_CHAR_FLAG 0x80
5161static int add_till_closing_bracket(o_string *dest, struct in_str *input, unsigned end_ch)
5162{
5163 int ch;
5164 char dbl = end_ch & DOUBLE_CLOSE_CHAR_FLAG;
5165# if BASH_SUBSTR || BASH_PATTERN_SUBST
5166 char end_char2 = end_ch >> 8;
5167# endif
5168 end_ch &= (DOUBLE_CLOSE_CHAR_FLAG - 1);
5169
5170# if ENABLE_HUSH_INTERACTIVE
5171 G.promptmode = 1; /* PS2 */
5172# endif
5173 debug_printf_prompt("%s promptmode=%d\n", __func__, G.promptmode);
5174
5175 while (1) {
5176 ch = i_getch(input);
5177 if (ch == EOF) {
5178 syntax_error_unterm_ch(end_ch);
5179 return 0;
5180 }
5181 if (ch == end_ch
5182# if BASH_SUBSTR || BASH_PATTERN_SUBST
5183 || ch == end_char2
5184# endif
5185 ) {
5186 if (!dbl)
5187 break;
5188 /* we look for closing )) of $((EXPR)) */
5189 if (i_peek_and_eat_bkslash_nl(input) == end_ch) {
5190 i_getch(input); /* eat second ')' */
5191 break;
5192 }
5193 }
5194 o_addchr(dest, ch);
5195 //bb_error_msg("%s:o_addchr('%c')", __func__, ch);
5196 if (ch == '(' || ch == '{') {
5197 ch = (ch == '(' ? ')' : '}');
5198 if (!add_till_closing_bracket(dest, input, ch))
5199 return 0;
5200 o_addchr(dest, ch);
5201 continue;
5202 }
5203 if (ch == '\'') {
5204 if (!add_till_single_quote(dest, input))
5205 return 0;
5206 o_addchr(dest, ch);
5207 continue;
5208 }
5209 if (ch == '"') {
5210 if (!add_till_double_quote(dest, input))
5211 return 0;
5212 o_addchr(dest, ch);
5213 continue;
5214 }
5215 if (ch == '`') {
5216 if (!add_till_backquote(dest, input, /*in_dquote:*/ 0))
5217 return 0;
5218 o_addchr(dest, ch);
5219 continue;
5220 }
5221 if (ch == '\\') {
5222 /* \x. Copy verbatim. Important for \(, \) */
5223 ch = i_getch(input);
5224 if (ch == EOF) {
5225 syntax_error_unterm_ch(end_ch);
5226 return 0;
5227 }
5228# if 0
5229 if (ch == '\n') {
5230 /* "backslash+newline", ignore both */
5231 o_delchr(dest); /* undo insertion of '\' */
5232 continue;
5233 }
5234# endif
5235 o_addchr(dest, ch);
5236 //bb_error_msg("%s:o_addchr('%c') after '\\'", __func__, ch);
5237 continue;
5238 }
5239 }
5240 debug_printf_parse("%s return '%s' ch:'%c'\n", __func__, dest->data, ch);
5241 return ch;
5242}
5243#endif /* ENABLE_HUSH_TICK || ENABLE_FEATURE_SH_MATH || ENABLE_HUSH_DOLLAR_OPS */
5244
5245#if BASH_DOLLAR_SQUOTE
5246/* Return code: 1 for "found and parsed", 0 for "seen something else" */
5247# if BB_MMU
5248#define parse_dollar_squote(as_string, dest, input) \
5249 parse_dollar_squote(dest, input)
5250#define as_string NULL
5251# endif
5252static int parse_dollar_squote(o_string *as_string, o_string *dest, struct in_str *input)
5253{
5254 int start;
5255 int ch = i_peek_and_eat_bkslash_nl(input); /* first character after the $ */
5256 debug_printf_parse("parse_dollar_squote entered: ch='%c'\n", ch);
5257 if (ch != '\'')
5258 return 0;
5259
5260 dest->has_quoted_part = 1;
5261 start = dest->length;
5262
5263 ch = i_getch(input); /* eat ' */
5264 nommu_addchr(as_string, ch);
5265 while (1) {
5266 ch = i_getch(input);
5267 nommu_addchr(as_string, ch);
5268 if (ch == EOF) {
5269 syntax_error_unterm_ch('\'');
5270 return 0;
5271 }
5272 if (ch == '\'')
5273 break;
5274 if (ch == SPECIAL_VAR_SYMBOL) {
5275 /* Convert raw ^C to corresponding special variable reference */
5276 o_addchr(dest, SPECIAL_VAR_SYMBOL);
5277 o_addchr(dest, SPECIAL_VAR_QUOTED_SVS);
5278 /* will addchr() another SPECIAL_VAR_SYMBOL (see after the if() block) */
5279 } else if (ch == '\\') {
5280 static const char C_escapes[] ALIGN1 = "nrbtfav""x\\01234567";
5281
5282 ch = i_getch(input);
5283 nommu_addchr(as_string, ch);
5284 if (strchr(C_escapes, ch)) {
5285 char buf[4];
5286 char *p = buf;
5287 int cnt = 2;
5288
5289 buf[0] = ch;
5290 if ((unsigned char)(ch - '0') <= 7) { /* \ooo */
5291 do {
5292 ch = i_peek(input);
5293 if ((unsigned char)(ch - '0') > 7)
5294 break;
5295 *++p = ch = i_getch(input);
5296 nommu_addchr(as_string, ch);
5297 } while (--cnt != 0);
5298 } else if (ch == 'x') { /* \xHH */
5299 do {
5300 ch = i_peek(input);
5301 if (!isxdigit(ch))
5302 break;
5303 *++p = ch = i_getch(input);
5304 nommu_addchr(as_string, ch);
5305 } while (--cnt != 0);
5306 if (cnt == 2) { /* \x but next char is "bad" */
5307 ch = 'x';
5308 goto unrecognized;
5309 }
5310 } /* else simple seq like \\ or \t */
5311 *++p = '\0';
5312 p = buf;
5313 ch = bb_process_escape_sequence((void*)&p);
5314 //bb_error_msg("buf:'%s' ch:%x", buf, ch);
5315 if (ch == '\0')
5316 continue; /* bash compat: $'...\0...' emits nothing */
5317 } else { /* unrecognized "\z": encode both chars unless ' or " */
5318 if (ch != '\'' && ch != '"') {
5319 unrecognized:
5320 o_addqchr(dest, '\\');
5321 }
5322 }
5323 } /* if (\...) */
5324 o_addqchr(dest, ch);
5325 }
5326
5327 if (dest->length == start) {
5328 /* $'', $'\0', $'\000\x00' and the like */
5329 o_addchr(dest, SPECIAL_VAR_SYMBOL);
5330 o_addchr(dest, SPECIAL_VAR_SYMBOL);
5331 }
5332
5333 return 1;
5334# undef as_string
5335}
5336#else
5337# #define parse_dollar_squote(as_string, dest, input) 0
5338#endif /* BASH_DOLLAR_SQUOTE */
Francis Laniel8197f012023-12-22 22:02:28 +01005339#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01005340
5341/* Return code: 0 for OK, 1 for syntax error */
5342#if BB_MMU
5343#define parse_dollar(as_string, dest, input, quote_mask) \
5344 parse_dollar(dest, input, quote_mask)
5345#define as_string NULL
5346#endif
5347static int parse_dollar(o_string *as_string,
5348 o_string *dest,
5349 struct in_str *input, unsigned char quote_mask)
5350{
5351 int ch = i_peek_and_eat_bkslash_nl(input); /* first character after the $ */
5352
5353 debug_printf_parse("parse_dollar entered: ch='%c' quote_mask:0x%x\n", ch, quote_mask);
5354 if (isalpha(ch)) {
5355 make_var:
5356 ch = i_getch(input);
5357 nommu_addchr(as_string, ch);
5358 /*make_var1:*/
5359 o_addchr(dest, SPECIAL_VAR_SYMBOL);
5360 while (1) {
5361 debug_printf_parse(": '%c'\n", ch);
5362 o_addchr(dest, ch | quote_mask);
5363 quote_mask = 0;
5364 ch = i_peek_and_eat_bkslash_nl(input);
5365 if (!isalnum(ch) && ch != '_') {
5366 /* End of variable name reached */
5367 break;
5368 }
5369 ch = i_getch(input);
5370 nommu_addchr(as_string, ch);
5371 }
5372 o_addchr(dest, SPECIAL_VAR_SYMBOL);
5373 } else if (isdigit(ch)) {
5374 make_one_char_var:
5375 ch = i_getch(input);
5376 nommu_addchr(as_string, ch);
5377 o_addchr(dest, SPECIAL_VAR_SYMBOL);
5378 debug_printf_parse(": '%c'\n", ch);
5379 o_addchr(dest, ch | quote_mask);
5380 o_addchr(dest, SPECIAL_VAR_SYMBOL);
5381 } else switch (ch) {
Francis Laniel8197f012023-12-22 22:02:28 +01005382#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01005383 case '$': /* pid */
5384 case '!': /* last bg pid */
Francis Laniel8197f012023-12-22 22:02:28 +01005385#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01005386 case '?': /* last exit code */
5387 case '#': /* number of args */
5388 case '*': /* args */
5389 case '@': /* args */
5390 case '-': /* $- option flags set by set builtin or shell options (-i etc) */
5391 goto make_one_char_var;
5392 case '{': {
5393 char len_single_ch;
5394
5395 o_addchr(dest, SPECIAL_VAR_SYMBOL);
5396
5397 ch = i_getch(input); /* eat '{' */
5398 nommu_addchr(as_string, ch);
5399
5400 ch = i_getch_and_eat_bkslash_nl(input); /* first char after '{' */
5401 /* It should be ${?}, or ${#var},
5402 * or even ${?+subst} - operator acting on a special variable,
5403 * or the beginning of variable name.
5404 */
5405 if (ch == EOF
5406 || (!strchr(_SPECIAL_VARS_STR, ch) && !isalnum(ch)) /* not one of those */
5407 ) {
5408 bad_dollar_syntax:
5409 syntax_error_unterm_str("${name}");
5410 debug_printf_parse("parse_dollar return 0: unterminated ${name}\n");
5411 return 0;
5412 }
5413 nommu_addchr(as_string, ch);
5414 len_single_ch = ch;
5415 ch |= quote_mask;
5416
5417 /* It's possible to just call add_till_closing_bracket() at this point.
5418 * However, this regresses some of our testsuite cases
5419 * which check invalid constructs like ${%}.
5420 * Oh well... let's check that the var name part is fine... */
5421
5422 if (isdigit(len_single_ch)
5423 || (len_single_ch == '#' && isdigit(i_peek_and_eat_bkslash_nl(input)))
5424 ) {
5425 /* Execution engine uses plain xatoi_positive()
5426 * to interpret ${NNN} and {#NNN},
5427 * check syntax here in the parser.
5428 * (bash does not support expressions in ${#NN},
5429 * e.g. ${#$var} and {#1:+WORD} are not supported).
5430 */
5431 unsigned cnt = 9; /* max 9 digits for ${NN} and 8 for {#NN} */
5432 while (1) {
5433 o_addchr(dest, ch);
5434 debug_printf_parse(": '%c'\n", ch);
5435 ch = i_getch_and_eat_bkslash_nl(input);
5436 nommu_addchr(as_string, ch);
5437 if (ch == '}')
5438 break;
5439 if (--cnt == 0)
5440 goto bad_dollar_syntax;
5441 if (len_single_ch != '#' && strchr(VAR_SUBST_OPS, ch))
5442 /* ${NN<op>...} is valid */
5443 goto eat_until_closing;
5444 if (!isdigit(ch))
5445 goto bad_dollar_syntax;
5446 }
5447 } else
5448 while (1) {
5449 unsigned pos;
5450
5451 o_addchr(dest, ch);
5452 debug_printf_parse(": '%c'\n", ch);
5453
5454 ch = i_getch(input);
5455 nommu_addchr(as_string, ch);
5456 if (ch == '}')
5457 break;
5458 if (!isalnum(ch) && ch != '_') {
5459 unsigned end_ch;
Francis Laniel8197f012023-12-22 22:02:28 +01005460#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01005461 unsigned char last_ch;
Francis Laniel8197f012023-12-22 22:02:28 +01005462#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01005463 /* handle parameter expansions
5464 * http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_06_02
5465 */
5466 if (!strchr(VAR_SUBST_OPS, ch)) { /* ${var<bad_char>... */
5467 if (len_single_ch != '#'
5468 /*|| !strchr(SPECIAL_VARS_STR, ch) - disallow errors like ${#+} ? */
5469 || i_peek(input) != '}'
5470 ) {
5471 goto bad_dollar_syntax;
5472 }
5473 /* else: it's "length of C" ${#C} op,
5474 * where C is a single char
5475 * special var name, e.g. ${#!}.
5476 */
5477 }
5478 eat_until_closing:
5479 /* Eat everything until closing '}' (or ':') */
5480 end_ch = '}';
Francis Laniel8197f012023-12-22 22:02:28 +01005481#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01005482 if (BASH_SUBSTR
5483 && ch == ':'
5484 && !strchr(MINUS_PLUS_EQUAL_QUESTION, i_peek(input))
5485 ) {
5486 /* It's ${var:N[:M]} thing */
5487 end_ch = '}' * 0x100 + ':';
5488 }
5489 if (BASH_PATTERN_SUBST
5490 && ch == '/'
5491 ) {
5492 /* It's ${var/[/]pattern[/repl]} thing */
5493 if (i_peek(input) == '/') { /* ${var//pattern[/repl]}? */
5494 i_getch(input);
5495 nommu_addchr(as_string, '/');
5496 ch = '\\';
5497 }
5498 end_ch = '}' * 0x100 + '/';
5499 }
Francis Laniel8197f012023-12-22 22:02:28 +01005500#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01005501 o_addchr(dest, ch);
5502 /* The pattern can't be empty.
5503 * IOW: if the first char after "${v//" is a slash,
5504 * it does not terminate the pattern - it's the first char of the pattern:
5505 * v=/dev/ram; echo ${v////-} prints -dev-ram (pattern is "/")
5506 * v=/dev/ram; echo ${v///r/-} prints /dev-am (pattern is "/r")
5507 */
5508 if (i_peek(input) == '/') {
5509 o_addchr(dest, i_getch(input));
5510 }
Francis Laniel8197f012023-12-22 22:02:28 +01005511#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01005512 again:
Francis Laniel8197f012023-12-22 22:02:28 +01005513#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01005514 if (!BB_MMU)
5515 pos = dest->length;
5516#if ENABLE_HUSH_DOLLAR_OPS
Francis Laniel8197f012023-12-22 22:02:28 +01005517#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01005518 last_ch = add_till_closing_bracket(dest, input, end_ch);
5519 if (last_ch == 0) /* error? */
5520 return 0;
Francis Laniel8197f012023-12-22 22:02:28 +01005521#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01005522#else
5523# error Simple code to only allow ${var} is not implemented
5524#endif
5525 if (as_string) {
5526 o_addstr(as_string, dest->data + pos);
Francis Laniel8197f012023-12-22 22:02:28 +01005527#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01005528 o_addchr(as_string, last_ch);
Francis Laniel8197f012023-12-22 22:02:28 +01005529#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01005530 }
5531
Francis Laniel8197f012023-12-22 22:02:28 +01005532#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01005533 if ((BASH_SUBSTR || BASH_PATTERN_SUBST)
5534 && (end_ch & 0xff00)
5535 ) {
5536 /* close the first block: */
5537 o_addchr(dest, SPECIAL_VAR_SYMBOL);
5538 /* while parsing N from ${var:N[:M]}
5539 * or pattern from ${var/[/]pattern[/repl]} */
5540 if ((end_ch & 0xff) == last_ch) {
5541 /* got ':' or '/'- parse the rest */
5542 end_ch = '}';
5543 goto again;
5544 }
5545 /* got '}' */
5546 if (BASH_SUBSTR && end_ch == '}' * 0x100 + ':') {
5547 /* it's ${var:N} - emulate :999999999 */
5548 o_addstr(dest, "999999999");
5549 } /* else: it's ${var/[/]pattern} */
5550 }
Francis Laniel8197f012023-12-22 22:02:28 +01005551#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01005552 break;
5553 }
5554 len_single_ch = 0; /* it can't be ${#C} op */
5555 }
5556 o_addchr(dest, SPECIAL_VAR_SYMBOL);
5557 break;
5558 }
5559#if ENABLE_FEATURE_SH_MATH || ENABLE_HUSH_TICK
5560 case '(': {
5561 unsigned pos;
5562
5563 ch = i_getch(input);
5564 nommu_addchr(as_string, ch);
5565# if ENABLE_FEATURE_SH_MATH
5566 if (i_peek_and_eat_bkslash_nl(input) == '(') {
5567 ch = i_getch(input);
5568 nommu_addchr(as_string, ch);
5569 o_addchr(dest, SPECIAL_VAR_SYMBOL);
5570 o_addchr(dest, quote_mask | '+');
5571 if (!BB_MMU)
5572 pos = dest->length;
5573 if (!add_till_closing_bracket(dest, input, ')' | DOUBLE_CLOSE_CHAR_FLAG))
5574 return 0; /* error */
5575 if (as_string) {
5576 o_addstr(as_string, dest->data + pos);
5577 o_addchr(as_string, ')');
5578 o_addchr(as_string, ')');
5579 }
5580 o_addchr(dest, SPECIAL_VAR_SYMBOL);
5581 break;
5582 }
5583# endif
5584# if ENABLE_HUSH_TICK
5585 o_addchr(dest, SPECIAL_VAR_SYMBOL);
5586 o_addchr(dest, quote_mask | '`');
5587 if (!BB_MMU)
5588 pos = dest->length;
5589 if (!add_till_closing_bracket(dest, input, ')'))
5590 return 0; /* error */
5591 if (as_string) {
5592 o_addstr(as_string, dest->data + pos);
5593 o_addchr(as_string, ')');
5594 }
5595 o_addchr(dest, SPECIAL_VAR_SYMBOL);
5596# endif
5597 break;
5598 }
5599#endif
5600 case '_':
5601 goto make_var;
5602#if 0
5603 /* TODO: $_: */
5604 /* $_ Shell or shell script name; or last argument of last command
5605 * (if last command wasn't a pipe; if it was, bash sets $_ to "");
5606 * but in command's env, set to full pathname used to invoke it */
5607 ch = i_getch(input);
5608 nommu_addchr(as_string, ch);
5609 ch = i_peek_and_eat_bkslash_nl(input);
5610 if (isalnum(ch)) { /* it's $_name or $_123 */
5611 ch = '_';
5612 goto make_var1;
5613 }
5614 /* else: it's $_ */
5615#endif
5616 default:
5617 o_addQchr(dest, '$');
5618 }
5619 debug_printf_parse("parse_dollar return 1 (ok)\n");
5620 return 1;
5621#undef as_string
5622}
5623
5624#if BB_MMU
5625#define encode_string(as_string, dest, input, dquote_end) \
5626 encode_string(dest, input, dquote_end)
5627#define as_string NULL
5628#endif
5629static int encode_string(o_string *as_string,
5630 o_string *dest,
5631 struct in_str *input,
5632 int dquote_end)
5633{
5634 int ch;
5635 int next;
5636
5637 again:
5638 ch = i_getch(input);
5639 if (ch != EOF)
5640 nommu_addchr(as_string, ch);
5641 if (ch == dquote_end) { /* may be only '"' or EOF */
5642 debug_printf_parse("encode_string return 1 (ok)\n");
5643 return 1;
5644 }
5645 /* note: can't move it above ch == dquote_end check! */
5646 if (ch == EOF) {
5647 syntax_error_unterm_ch('"');
5648 return 0; /* error */
5649 }
5650 next = '\0';
5651 if (ch != '\n') {
5652 next = i_peek(input);
5653 }
5654 debug_printf_parse("\" ch=%c (%d) escape=%d\n",
5655 ch, ch, !!(dest->o_expflags & EXP_FLAG_ESC_GLOB_CHARS));
5656 if (ch == '\\') {
5657 if (next == EOF) {
5658 /* Testcase: in interactive shell a file with
5659 * echo "unterminated string\<eof>
5660 * is sourced.
5661 */
5662 syntax_error_unterm_ch('"');
5663 return 0; /* error */
5664 }
5665 /* bash:
5666 * "The backslash retains its special meaning [in "..."]
5667 * only when followed by one of the following characters:
5668 * $, `, ", \, or <newline>. A double quote may be quoted
5669 * within double quotes by preceding it with a backslash."
5670 * NB: in (unquoted) heredoc, above does not apply to ",
5671 * therefore we check for it by "next == dquote_end" cond.
5672 */
5673 if (next == dquote_end || strchr("$`\\\n", next)) {
5674 ch = i_getch(input); /* eat next */
5675 if (ch == '\n')
5676 goto again; /* skip \<newline> */
5677 } /* else: ch remains == '\\', and we double it below: */
5678 o_addqchr(dest, ch); /* \c if c is a glob char, else just c */
5679 nommu_addchr(as_string, ch);
5680 goto again;
5681 }
5682 if (ch == '$') {
5683 //if (parse_dollar_squote(as_string, dest, input))
5684 // goto again;
5685 if (!parse_dollar(as_string, dest, input, /*quote_mask:*/ 0x80)) {
5686 debug_printf_parse("encode_string return 0: "
5687 "parse_dollar returned 0 (error)\n");
5688 return 0;
5689 }
5690 goto again;
5691 }
5692#if ENABLE_HUSH_TICK
5693 if (ch == '`') {
5694 //unsigned pos = dest->length;
5695 o_addchr(dest, SPECIAL_VAR_SYMBOL);
5696 o_addchr(dest, 0x80 | '`');
5697 if (!add_till_backquote(dest, input, /*in_dquote:*/ dquote_end == '"'))
5698 return 0; /* error */
5699 o_addchr(dest, SPECIAL_VAR_SYMBOL);
5700 //debug_printf_subst("SUBST RES3 '%s'\n", dest->data + pos);
5701 goto again;
5702 }
5703#endif
5704 o_addQchr(dest, ch);
5705 if (ch == SPECIAL_VAR_SYMBOL) {
5706 /* Convert "^C" to corresponding special variable reference */
5707 o_addchr(dest, SPECIAL_VAR_QUOTED_SVS);
5708 o_addchr(dest, SPECIAL_VAR_SYMBOL);
5709 }
5710 goto again;
5711#undef as_string
5712}
5713
5714/*
5715 * Scan input until EOF or end_trigger char.
5716 * Return a list of pipes to execute, or NULL on EOF
5717 * or if end_trigger character is met.
5718 * On syntax error, exit if shell is not interactive,
5719 * reset parsing machinery and start parsing anew,
5720 * or return ERR_PTR.
5721 */
5722static struct pipe *parse_stream(char **pstring,
5723 int *heredoc_cnt_ptr,
5724 struct in_str *input,
5725 int end_trigger)
5726{
5727 struct parse_context ctx;
5728 int heredoc_cnt;
5729
5730 /* Single-quote triggers a bypass of the main loop until its mate is
5731 * found. When recursing, quote state is passed in via ctx.word.o_expflags.
5732 */
5733 debug_printf_parse("parse_stream entered, end_trigger='%c'\n",
5734 end_trigger ? end_trigger : 'X');
5735 debug_enter();
5736
5737 initialize_context(&ctx);
5738
5739 /* If very first arg is "" or '', ctx.word.data may end up NULL.
5740 * Preventing this:
5741 */
5742 ctx.word.data = xzalloc(1); /* start as "", not as NULL */
5743
5744 /* We used to separate words on $IFS here. This was wrong.
5745 * $IFS is used only for word splitting when $var is expanded,
5746 * here we should use blank chars as separators, not $IFS
5747 */
5748
5749 heredoc_cnt = 0;
5750 while (1) {
5751 const char *is_blank;
5752 const char *is_special;
5753 int ch;
5754 int next;
Francis Laniel8197f012023-12-22 22:02:28 +01005755#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01005756 int redir_fd;
5757 redir_type redir_style;
Francis Laniel8197f012023-12-22 22:02:28 +01005758#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01005759
5760 ch = i_getch(input);
5761 debug_printf_parse(": ch=%c (%d) escape=%d\n",
5762 ch, ch, !!(ctx.word.o_expflags & EXP_FLAG_ESC_GLOB_CHARS));
5763 if (ch == EOF) {
5764 struct pipe *pi;
5765
5766 if (heredoc_cnt) {
5767 syntax_error_unterm_str("here document");
5768 goto parse_error_exitcode1;
5769 }
5770 if (end_trigger == ')') {
5771 syntax_error_unterm_ch('(');
5772 goto parse_error_exitcode1;
5773 }
5774 if (end_trigger == '}') {
5775 syntax_error_unterm_ch('{');
5776 goto parse_error_exitcode1;
5777 }
5778
5779 if (done_word(&ctx)) {
5780 goto parse_error_exitcode1;
5781 }
5782 o_free_and_set_NULL(&ctx.word);
5783 done_pipe(&ctx, PIPE_SEQ);
5784 pi = ctx.list_head;
5785 /* If we got nothing... */
5786 /* (this makes bare "&" cmd a no-op.
5787 * bash says: "syntax error near unexpected token '&'") */
5788 if (pi->num_cmds == 0
5789 IF_HAS_KEYWORDS(&& pi->res_word == RES_NONE)
5790 ) {
5791 free_pipe_list(pi);
5792 pi = NULL;
5793 }
5794#if !BB_MMU
5795 debug_printf_parse("as_string1 '%s'\n", ctx.as_string.data);
5796 if (pstring)
5797 *pstring = ctx.as_string.data;
5798 else
5799 o_free(&ctx.as_string);
5800#endif
5801 // heredoc_cnt must be 0 here anyway
5802 //if (heredoc_cnt_ptr)
5803 // *heredoc_cnt_ptr = heredoc_cnt;
5804 debug_leave();
5805 debug_printf_heredoc("parse_stream return heredoc_cnt:%d\n", heredoc_cnt);
5806 debug_printf_parse("parse_stream return %p\n", pi);
5807 return pi;
5808 }
5809
5810 /* Handle "'" and "\" first, as they won't play nice with
5811 * i_peek_and_eat_bkslash_nl() anyway:
5812 * echo z\\
5813 * and
5814 * echo '\
5815 * '
5816 * would break.
5817 */
5818 if (ch == '\\') {
5819 ch = i_getch(input);
5820 if (ch == '\n')
5821 continue; /* drop \<newline>, get next char */
5822 nommu_addchr(&ctx.as_string, '\\');
5823 if (ch == SPECIAL_VAR_SYMBOL) {
5824 nommu_addchr(&ctx.as_string, ch);
5825 /* Convert \^C to corresponding special variable reference */
5826 goto case_SPECIAL_VAR_SYMBOL;
5827 }
5828 o_addchr(&ctx.word, '\\');
5829 if (ch == EOF) {
5830 /* Testcase: eval 'echo Ok\' */
5831 /* bash-4.3.43 was removing backslash,
5832 * but 4.4.19 retains it, most other shells too
5833 */
5834 continue; /* get next char */
5835 }
5836 /* Example: echo Hello \2>file
5837 * we need to know that word 2 is quoted
5838 */
5839 ctx.word.has_quoted_part = 1;
5840 nommu_addchr(&ctx.as_string, ch);
5841 o_addchr(&ctx.word, ch);
5842 continue; /* get next char */
5843 }
5844 nommu_addchr(&ctx.as_string, ch);
5845 if (ch == '\'') {
5846 ctx.word.has_quoted_part = 1;
5847 next = i_getch(input);
Francis Laniel8197f012023-12-22 22:02:28 +01005848#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01005849 if (next == '\'' && !ctx.pending_redirect)
5850 goto insert_empty_quoted_str_marker;
Francis Laniel8197f012023-12-22 22:02:28 +01005851#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01005852
5853 ch = next;
5854 while (1) {
5855 if (ch == EOF) {
5856 syntax_error_unterm_ch('\'');
5857 goto parse_error_exitcode1;
5858 }
5859 nommu_addchr(&ctx.as_string, ch);
5860 if (ch == '\'')
5861 break;
5862 if (ch == SPECIAL_VAR_SYMBOL) {
5863 /* Convert raw ^C to corresponding special variable reference */
5864 o_addchr(&ctx.word, SPECIAL_VAR_SYMBOL);
5865 o_addchr(&ctx.word, SPECIAL_VAR_QUOTED_SVS);
5866 }
5867 o_addqchr(&ctx.word, ch);
5868 ch = i_getch(input);
5869 }
5870 continue; /* get next char */
5871 }
5872
5873 next = '\0';
5874 if (ch != '\n')
5875 next = i_peek_and_eat_bkslash_nl(input);
5876
5877 is_special = "{}<>&|();#" /* special outside of "str" */
Francis Laniel8197f012023-12-22 22:02:28 +01005878#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01005879 "$\"" IF_HUSH_TICK("`") /* always special */
Francis Laniel8197f012023-12-22 22:02:28 +01005880#else /* __U_BOOT__ */
5881 "$\""
5882#endif /* __U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01005883 SPECIAL_VAR_SYMBOL_STR;
5884#if defined(CMD_TEST2_SINGLEWORD_NOGLOB)
5885 if (ctx.command->cmd_type == CMD_TEST2_SINGLEWORD_NOGLOB) {
5886 /* In [[ ]], {}<>&|() are not special */
5887 is_special += 8;
5888 } else
5889#endif
5890 /* Are { and } special here? */
5891 if (ctx.command->argv /* word [word]{... - non-special */
5892 || ctx.word.length /* word{... - non-special */
5893 || ctx.word.has_quoted_part /* ""{... - non-special */
5894 || (next != ';' /* }; - special */
5895 && next != ')' /* }) - special */
5896 && next != '(' /* {( - special */
5897 && next != '&' /* }& and }&& ... - special */
5898 && next != '|' /* }|| ... - special */
5899 && !strchr(defifs, next) /* {word - non-special */
5900 )
5901 ) {
5902 /* They are not special, skip "{}" */
5903 is_special += 2;
5904 }
5905 is_special = strchr(is_special, ch);
5906 is_blank = strchr(defifs, ch);
5907
5908 if (!is_special && !is_blank) { /* ordinary char */
5909 ordinary_char:
5910 o_addQchr(&ctx.word, ch);
5911 if ((ctx.is_assignment == MAYBE_ASSIGNMENT
5912 || ctx.is_assignment == WORD_IS_KEYWORD)
5913 && ch == '='
5914 && endofname(ctx.word.data)[0] == '='
5915 ) {
5916 ctx.is_assignment = DEFINITELY_ASSIGNMENT;
5917 debug_printf_parse("ctx.is_assignment='%s'\n", assignment_flag[ctx.is_assignment]);
5918 }
5919 continue;
5920 }
5921
5922 if (is_blank) {
5923#if ENABLE_HUSH_LINENO_VAR
5924/* Case:
5925 * "while ...; do<whitespace><newline>
5926 * cmd ..."
5927 * would think that "cmd" starts in <whitespace> -
5928 * i.e., at the previous line.
5929 * We need to skip all whitespace before newlines.
5930 */
5931 while (ch != '\n') {
5932 next = i_peek(input);
5933 if (next != ' ' && next != '\t' && next != '\n')
5934 break; /* next char is not ws */
5935 ch = i_getch(input);
5936 }
5937 /* ch == last eaten whitespace char */
5938#endif
5939 if (done_word(&ctx)) {
5940 goto parse_error_exitcode1;
5941 }
5942 if (ch == '\n') {
5943 /* Is this a case when newline is simply ignored?
5944 * Some examples:
5945 * "cmd | <newline> cmd ..."
5946 * "case ... in <newline> word) ..."
5947 */
5948 if (IS_NULL_CMD(ctx.command)
5949 && ctx.word.length == 0
5950 && !ctx.word.has_quoted_part
5951 && heredoc_cnt == 0
5952 ) {
5953 /* This newline can be ignored. But...
5954 * Without check #1, interactive shell
5955 * ignores even bare <newline>,
5956 * and shows the continuation prompt:
5957 * ps1_prompt$ <enter>
5958 * ps2> _ <=== wrong, should be ps1
5959 * Without check #2, "cmd & <newline>"
5960 * is similarly mistreated.
5961 * (BTW, this makes "cmd & cmd"
5962 * and "cmd && cmd" non-orthogonal.
5963 * Really, ask yourself, why
5964 * "cmd && <newline>" doesn't start
5965 * cmd but waits for more input?
5966 * The only reason is that it might be
5967 * a "cmd1 && <nl> cmd2 &" construct,
5968 * cmd1 may need to run in BG).
5969 */
5970 struct pipe *pi = ctx.list_head;
5971 if (pi->num_cmds != 0 /* check #1 */
5972 && pi->followup != PIPE_BG /* check #2 */
5973 ) {
5974 continue;
5975 }
5976 }
5977 /* Treat newline as a command separator. */
5978 done_pipe(&ctx, PIPE_SEQ);
5979 debug_printf_heredoc("heredoc_cnt:%d\n", heredoc_cnt);
5980 if (heredoc_cnt) {
5981 heredoc_cnt = fetch_heredocs(&ctx.as_string, ctx.list_head, heredoc_cnt, input);
5982 if (heredoc_cnt != 0)
5983 goto parse_error_exitcode1;
5984 }
5985 ctx.is_assignment = MAYBE_ASSIGNMENT;
5986 debug_printf_parse("ctx.is_assignment='%s'\n", assignment_flag[ctx.is_assignment]);
5987 ch = ';';
5988 /* note: if (is_blank) continue;
5989 * will still trigger for us */
5990 }
5991 }
5992
5993 /* "cmd}" or "cmd }..." without semicolon or &:
5994 * } is an ordinary char in this case, even inside { cmd; }
5995 * Pathological example: { ""}; } should exec "}" cmd
5996 */
Francis Laniel8197f012023-12-22 22:02:28 +01005997#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01005998 if (ch == '}') {
Francis Laniel8197f012023-12-22 22:02:28 +01005999#else /* __U_BOOT__ */
6000 if (ch == '}' || ch == ')') {
6001#endif /* __U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01006002 if (ctx.word.length != 0 /* word} */
6003 || ctx.word.has_quoted_part /* ""} */
6004 ) {
6005 goto ordinary_char;
6006 }
6007 if (!IS_NULL_CMD(ctx.command)) { /* cmd } */
6008 /* Generally, there should be semicolon: "cmd; }"
6009 * However, bash allows to omit it if "cmd" is
6010 * a group. Examples:
6011 * { { echo 1; } }
6012 * {(echo 1)}
6013 * { echo 0 >&2 | { echo 1; } }
6014 * { while false; do :; done }
6015 * { case a in b) ;; esac }
6016 */
6017 if (ctx.command->group)
6018 goto term_group;
6019 goto ordinary_char;
6020 }
6021 if (!IS_NULL_PIPE(ctx.pipe)) /* cmd | } */
6022 /* Can't be an end of {cmd}, skip the check */
6023 goto skip_end_trigger;
6024 /* else: } does terminate a group */
6025 }
6026 term_group:
6027 if (end_trigger && end_trigger == ch
6028 && (ch != ';' || heredoc_cnt == 0)
6029#if ENABLE_HUSH_CASE
6030 && (ch != ')'
6031 || ctx.ctx_res_w != RES_MATCH
6032 || (!ctx.word.has_quoted_part && strcmp(ctx.word.data, "esac") == 0)
6033 )
6034#endif
6035 ) {
6036 if (done_word(&ctx)) {
6037 goto parse_error_exitcode1;
6038 }
6039 done_pipe(&ctx, PIPE_SEQ);
6040 ctx.is_assignment = MAYBE_ASSIGNMENT;
6041 debug_printf_parse("ctx.is_assignment='%s'\n", assignment_flag[ctx.is_assignment]);
6042 /* Do we sit outside of any if's, loops or case's? */
6043 if (!HAS_KEYWORDS
6044 IF_HAS_KEYWORDS(|| (ctx.ctx_res_w == RES_NONE && ctx.old_flag == 0))
6045 ) {
6046 o_free_and_set_NULL(&ctx.word);
6047#if !BB_MMU
6048 debug_printf_parse("as_string2 '%s'\n", ctx.as_string.data);
6049 if (pstring)
6050 *pstring = ctx.as_string.data;
6051 else
6052 o_free(&ctx.as_string);
6053#endif
6054 if (ch != ';' && IS_NULL_PIPE(ctx.list_head)) {
6055 /* Example: bare "{ }", "()" */
6056 G.last_exitcode = 2; /* bash compat */
6057 syntax_error_unexpected_ch(ch);
6058 goto parse_error;
6059 }
6060 if (heredoc_cnt_ptr)
6061 *heredoc_cnt_ptr = heredoc_cnt;
6062 debug_printf_heredoc("parse_stream return heredoc_cnt:%d\n", heredoc_cnt);
6063 debug_printf_parse("parse_stream return %p: "
6064 "end_trigger char found\n",
6065 ctx.list_head);
6066 debug_leave();
6067 return ctx.list_head;
6068 }
6069 }
6070
6071 if (is_blank)
6072 continue;
6073
6074 /* Catch <, > before deciding whether this word is
6075 * an assignment. a=1 2>z b=2: b=2 is still assignment */
6076 switch (ch) {
Francis Laniel8197f012023-12-22 22:02:28 +01006077#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01006078 case '>':
6079 redir_fd = redirect_opt_num(&ctx.word);
6080 if (done_word(&ctx)) {
6081 goto parse_error_exitcode1;
6082 }
6083 redir_style = REDIRECT_OVERWRITE;
6084 if (next == '>') {
6085 redir_style = REDIRECT_APPEND;
6086 ch = i_getch(input);
6087 nommu_addchr(&ctx.as_string, ch);
6088 }
6089#if 0
6090 else if (next == '(') {
6091 syntax_error(">(process) not supported");
6092 goto parse_error_exitcode1;
6093 }
6094#endif
6095 if (parse_redirect(&ctx, redir_fd, redir_style, input))
6096 goto parse_error_exitcode1;
6097 continue; /* get next char */
6098 case '<':
6099 redir_fd = redirect_opt_num(&ctx.word);
6100 if (done_word(&ctx)) {
6101 goto parse_error_exitcode1;
6102 }
6103 redir_style = REDIRECT_INPUT;
6104 if (next == '<') {
6105 redir_style = REDIRECT_HEREDOC;
6106 heredoc_cnt++;
6107 debug_printf_heredoc("++heredoc_cnt=%d\n", heredoc_cnt);
6108 ch = i_getch(input);
6109 nommu_addchr(&ctx.as_string, ch);
6110 } else if (next == '>') {
6111 redir_style = REDIRECT_IO;
6112 ch = i_getch(input);
6113 nommu_addchr(&ctx.as_string, ch);
6114 }
6115#if 0
6116 else if (next == '(') {
6117 syntax_error("<(process) not supported");
6118 goto parse_error_exitcode1;
6119 }
6120#endif
6121 if (parse_redirect(&ctx, redir_fd, redir_style, input))
6122 goto parse_error_exitcode1;
6123 continue; /* get next char */
Francis Laniel8197f012023-12-22 22:02:28 +01006124#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01006125 case '#':
6126 if (ctx.word.length == 0 && !ctx.word.has_quoted_part) {
6127 /* skip "#comment" */
6128 /* note: we do not add it to &ctx.as_string */
6129/* TODO: in bash:
6130 * comment inside $() goes to the next \n, even inside quoted string (!):
6131 * cmd "$(cmd2 #comment)" - syntax error
6132 * cmd "`cmd2 #comment`" - ok
6133 * We accept both (comment ends where command subst ends, in both cases).
6134 */
6135 while (1) {
6136 ch = i_peek(input);
6137 if (ch == '\n') {
6138 nommu_addchr(&ctx.as_string, '\n');
6139 break;
6140 }
6141 ch = i_getch(input);
6142 if (ch == EOF)
6143 break;
6144 }
6145 continue; /* get next char */
6146 }
6147 break;
6148 }
6149 skip_end_trigger:
6150
6151 if (ctx.is_assignment == MAYBE_ASSIGNMENT
Francis Laniel8197f012023-12-22 22:02:28 +01006152#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01006153 /* check that we are not in word in "a=1 2>word b=1": */
6154 && !ctx.pending_redirect
Francis Laniel8197f012023-12-22 22:02:28 +01006155#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01006156 ) {
6157 /* ch is a special char and thus this word
6158 * cannot be an assignment */
6159 ctx.is_assignment = NOT_ASSIGNMENT;
6160 debug_printf_parse("ctx.is_assignment='%s'\n", assignment_flag[ctx.is_assignment]);
6161 }
6162
6163 /* Note: nommu_addchr(&ctx.as_string, ch) is already done */
6164
6165 switch (ch) {
6166 case_SPECIAL_VAR_SYMBOL:
6167 case SPECIAL_VAR_SYMBOL:
6168 /* Convert raw ^C to corresponding special variable reference */
6169 o_addchr(&ctx.word, SPECIAL_VAR_SYMBOL);
6170 o_addchr(&ctx.word, SPECIAL_VAR_QUOTED_SVS);
6171 /* fall through */
6172 case '#':
6173 /* non-comment #: "echo a#b" etc */
6174 o_addchr(&ctx.word, ch);
6175 continue; /* get next char */
6176 case '$':
Francis Laniel8197f012023-12-22 22:02:28 +01006177#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01006178 if (parse_dollar_squote(&ctx.as_string, &ctx.word, input))
6179 continue; /* get next char */
Francis Laniel8197f012023-12-22 22:02:28 +01006180#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01006181 if (!parse_dollar(&ctx.as_string, &ctx.word, input, /*quote_mask:*/ 0)) {
6182 debug_printf_parse("parse_stream parse error: "
6183 "parse_dollar returned 0 (error)\n");
6184 goto parse_error_exitcode1;
6185 }
6186 continue; /* get next char */
6187 case '"':
6188 ctx.word.has_quoted_part = 1;
Francis Laniel8197f012023-12-22 22:02:28 +01006189#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01006190 if (next == '"' && !ctx.pending_redirect) {
Francis Laniel8197f012023-12-22 22:02:28 +01006191#else /* __U_BOOT__ */
6192 if (next == '"') {
6193#endif /* __U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01006194 i_getch(input); /* eat second " */
Francis Laniel8197f012023-12-22 22:02:28 +01006195#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01006196 insert_empty_quoted_str_marker:
Francis Laniel8197f012023-12-22 22:02:28 +01006197#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01006198 nommu_addchr(&ctx.as_string, next);
6199 o_addchr(&ctx.word, SPECIAL_VAR_SYMBOL);
6200 o_addchr(&ctx.word, SPECIAL_VAR_SYMBOL);
6201 continue; /* get next char */
6202 }
6203 if (ctx.is_assignment == NOT_ASSIGNMENT)
6204 ctx.word.o_expflags |= EXP_FLAG_ESC_GLOB_CHARS;
6205 if (!encode_string(&ctx.as_string, &ctx.word, input, '"'))
6206 goto parse_error_exitcode1;
6207 ctx.word.o_expflags &= ~EXP_FLAG_ESC_GLOB_CHARS;
6208 continue; /* get next char */
6209#if ENABLE_HUSH_TICK
6210 case '`': {
6211 USE_FOR_NOMMU(unsigned pos;)
6212
6213 o_addchr(&ctx.word, SPECIAL_VAR_SYMBOL);
6214 o_addchr(&ctx.word, '`');
6215 USE_FOR_NOMMU(pos = ctx.word.length;)
6216 if (!add_till_backquote(&ctx.word, input, /*in_dquote:*/ 0))
6217 goto parse_error_exitcode1;
6218# if !BB_MMU
6219 o_addstr(&ctx.as_string, ctx.word.data + pos);
6220 o_addchr(&ctx.as_string, '`');
6221# endif
6222 o_addchr(&ctx.word, SPECIAL_VAR_SYMBOL);
6223 //debug_printf_subst("SUBST RES3 '%s'\n", ctx.word.data + pos);
6224 continue; /* get next char */
6225 }
6226#endif
6227 case ';':
6228#if ENABLE_HUSH_CASE
6229 case_semi:
6230#endif
6231 if (done_word(&ctx)) {
6232 goto parse_error_exitcode1;
6233 }
6234 done_pipe(&ctx, PIPE_SEQ);
6235#if ENABLE_HUSH_CASE
6236 /* Eat multiple semicolons, detect
6237 * whether it means something special */
6238 while (1) {
6239 ch = i_peek_and_eat_bkslash_nl(input);
6240 if (ch != ';')
6241 break;
6242 ch = i_getch(input);
6243 nommu_addchr(&ctx.as_string, ch);
6244 if (ctx.ctx_res_w == RES_CASE_BODY) {
6245 ctx.ctx_dsemicolon = 1;
6246 ctx.ctx_res_w = RES_MATCH;
6247 break;
6248 }
6249 }
6250#endif
6251 new_cmd:
6252 /* We just finished a cmd. New one may start
6253 * with an assignment */
6254 ctx.is_assignment = MAYBE_ASSIGNMENT;
6255 debug_printf_parse("ctx.is_assignment='%s'\n", assignment_flag[ctx.is_assignment]);
6256 continue; /* get next char */
6257 case '&':
6258 if (done_word(&ctx)) {
6259 goto parse_error_exitcode1;
6260 }
6261 if (next == '&') {
6262 ch = i_getch(input);
6263 nommu_addchr(&ctx.as_string, ch);
6264 done_pipe(&ctx, PIPE_AND);
6265 } else {
6266 done_pipe(&ctx, PIPE_BG);
6267 }
6268 goto new_cmd;
6269 case '|':
6270 if (done_word(&ctx)) {
6271 goto parse_error_exitcode1;
6272 }
6273#if ENABLE_HUSH_CASE
6274 if (ctx.ctx_res_w == RES_MATCH)
6275 break; /* we are in case's "word | word)" */
6276#endif
6277 if (next == '|') { /* || */
6278 ch = i_getch(input);
6279 nommu_addchr(&ctx.as_string, ch);
6280 done_pipe(&ctx, PIPE_OR);
6281 } else {
6282 /* we could pick up a file descriptor choice here
6283 * with redirect_opt_num(), but bash doesn't do it.
6284 * "echo foo 2| cat" yields "foo 2". */
6285 done_command(&ctx);
6286 }
6287 goto new_cmd;
6288 case '(':
6289#if ENABLE_HUSH_CASE
6290 /* "case... in [(]word)..." - skip '(' */
6291 if (ctx.ctx_res_w == RES_MATCH
6292 && ctx.command->argv == NULL /* not (word|(... */
6293 && ctx.word.length == 0 /* not word(... */
6294 && ctx.word.has_quoted_part == 0 /* not ""(... */
6295 ) {
6296 continue; /* get next char */
6297 }
6298#endif
6299 /* fall through */
6300 case '{': {
6301 int n = parse_group(&ctx, input, ch);
6302 if (n < 0) {
6303 goto parse_error_exitcode1;
6304 }
6305 debug_printf_heredoc("parse_group done, needs heredocs:%d\n", n);
6306 heredoc_cnt += n;
6307 goto new_cmd;
6308 }
6309 case ')':
6310#if ENABLE_HUSH_CASE
6311 if (ctx.ctx_res_w == RES_MATCH)
6312 goto case_semi;
6313#endif
6314 case '}':
6315 /* proper use of this character is caught by end_trigger:
6316 * if we see {, we call parse_group(..., end_trigger='}')
6317 * and it will match } earlier (not here). */
6318 G.last_exitcode = 2;
6319 syntax_error_unexpected_ch(ch);
6320 goto parse_error;
6321 default:
6322 if (HUSH_DEBUG)
6323 bb_error_msg_and_die("BUG: unexpected %c", ch);
6324 }
6325 } /* while (1) */
6326
6327 parse_error_exitcode1:
6328 G.last_exitcode = 1;
6329 parse_error:
6330 {
6331 struct parse_context *pctx;
6332 IF_HAS_KEYWORDS(struct parse_context *p2;)
6333
6334 /* Clean up allocated tree.
6335 * Sample for finding leaks on syntax error recovery path.
6336 * Run it from interactive shell, watch pmap `pidof hush`.
6337 * while if false; then false; fi; do break; fi
6338 * Samples to catch leaks at execution:
6339 * while if (true | { true;}); then echo ok; fi; do break; done
6340 * while if (true | { true;}); then echo ok; fi; do (if echo ok; break; then :; fi) | cat; break; done
6341 */
6342 pctx = &ctx;
6343 do {
6344 /* Update pipe/command counts,
6345 * otherwise freeing may miss some */
6346 done_pipe(pctx, PIPE_SEQ);
6347 debug_printf_clean("freeing list %p from ctx %p\n",
6348 pctx->list_head, pctx);
6349 debug_print_tree(pctx->list_head, 0);
6350 free_pipe_list(pctx->list_head);
6351 debug_printf_clean("freed list %p\n", pctx->list_head);
6352#if !BB_MMU
6353 o_free(&pctx->as_string);
6354#endif
6355 IF_HAS_KEYWORDS(p2 = pctx->stack;)
6356 if (pctx != &ctx) {
6357 free(pctx);
6358 }
6359 IF_HAS_KEYWORDS(pctx = p2;)
6360 } while (HAS_KEYWORDS && pctx);
6361
6362 o_free(&ctx.word);
6363#if !BB_MMU
6364 if (pstring)
6365 *pstring = NULL;
6366#endif
6367 debug_leave();
6368 return ERR_PTR;
6369 }
6370}
6371
6372
6373/*** Execution routines ***/
6374
6375/* Expansion can recurse, need forward decls: */
6376#if !BASH_PATTERN_SUBST && !ENABLE_HUSH_CASE
6377#define expand_string_to_string(str, EXP_flags, do_unbackslash) \
6378 expand_string_to_string(str)
6379#endif
6380static char *expand_string_to_string(const char *str, int EXP_flags, int do_unbackslash);
6381#if ENABLE_HUSH_TICK
6382static int process_command_subs(o_string *dest, const char *s);
6383#endif
6384static int expand_vars_to_list(o_string *output, int n, char *arg);
6385
6386/* expand_strvec_to_strvec() takes a list of strings, expands
6387 * all variable references within and returns a pointer to
6388 * a list of expanded strings, possibly with larger number
6389 * of strings. (Think VAR="a b"; echo $VAR).
6390 * This new list is allocated as a single malloc block.
6391 * NULL-terminated list of char* pointers is at the beginning of it,
6392 * followed by strings themselves.
6393 * Caller can deallocate entire list by single free(list). */
6394
6395/* A horde of its helpers come first: */
6396
6397static void o_addblock_duplicate_backslash(o_string *o, const char *str, int len)
6398{
6399 while (--len >= 0) {
6400 char c = *str++;
6401
6402#if ENABLE_HUSH_BRACE_EXPANSION
6403 if (c == '{' || c == '}') {
6404 /* { -> \{, } -> \} */
6405 o_addchr(o, '\\');
6406 /* And now we want to add { or } and continue:
6407 * o_addchr(o, c);
6408 * continue;
6409 * luckily, just falling through achieves this.
6410 */
6411 }
6412#endif
6413 o_addchr(o, c);
6414 if (c == '\\') {
6415 /* \z -> \\\z; \<eol> -> \\<eol> */
6416 o_addchr(o, '\\');
6417 if (len) {
6418 len--;
6419 o_addchr(o, '\\');
6420 o_addchr(o, *str++);
6421 }
6422 }
6423 }
6424}
6425
6426/* Store given string, finalizing the word and starting new one whenever
6427 * we encounter IFS char(s). This is used for expanding variable values.
6428 * End-of-string does NOT finalize word: think about 'echo -$VAR-'.
6429 * Return in output->ended_in_ifs:
6430 * 1 - ended with IFS char, else 0 (this includes case of empty str).
6431 */
6432static int expand_on_ifs(o_string *output, int n, const char *str)
6433{
6434 int last_is_ifs = 0;
6435
6436 while (1) {
6437 int word_len;
6438
6439 if (!*str) /* EOL - do not finalize word */
6440 break;
6441 word_len = strcspn(str, G.ifs);
6442 if (word_len) {
6443 /* We have WORD_LEN leading non-IFS chars */
6444 if (!(output->o_expflags & EXP_FLAG_GLOB)) {
6445 o_addblock(output, str, word_len);
6446 } else {
6447 /* Protect backslashes against globbing up :)
6448 * Example: "v='\*'; echo b$v" prints "b\*"
6449 * (and does not try to glob on "*")
6450 */
6451 o_addblock_duplicate_backslash(output, str, word_len);
6452 /*/ Why can't we do it easier? */
6453 /*o_addblock(output, str, word_len); - WRONG: "v='\*'; echo Z$v" prints "Z*" instead of "Z\*" */
6454 /*o_addqblock(output, str, word_len); - WRONG: "v='*'; echo Z$v" prints "Z*" instead of Z* files */
6455 }
6456 last_is_ifs = 0;
6457 str += word_len;
6458 if (!*str) /* EOL - do not finalize word */
6459 break;
6460 }
6461
6462 /* We know str here points to at least one IFS char */
6463 last_is_ifs = 1;
6464 str += strspn(str, G.ifs_whitespace); /* skip IFS whitespace chars */
6465 if (!*str) /* EOL - do not finalize word */
6466 break;
6467
6468 if (G.ifs_whitespace != G.ifs /* usually false ($IFS is usually all whitespace), */
6469 && strchr(G.ifs, *str) /* the second check would fail */
6470 ) {
6471 /* This is a non-whitespace $IFS char */
6472 /* Skip it and IFS whitespace chars, start new word */
6473 str++;
6474 str += strspn(str, G.ifs_whitespace);
6475 goto new_word;
6476 }
6477
6478 /* Start new word... but not always! */
6479 /* Case "v=' a'; echo ''$v": we do need to finalize empty word: */
6480 if (output->has_quoted_part
6481 /*
6482 * Case "v=' a'; echo $v":
6483 * here nothing precedes the space in $v expansion,
6484 * therefore we should not finish the word
6485 * (IOW: if there *is* word to finalize, only then do it):
6486 * It's okay if this accesses the byte before first argv[]:
6487 * past call to o_save_ptr() cleared it to zero byte
6488 * (grep for -prev-ifs-check-).
6489 */
6490 || output->data[output->length - 1]
6491 ) {
6492 new_word:
6493 o_addchr(output, '\0');
6494 debug_print_list("expand_on_ifs", output, n);
6495 n = o_save_ptr(output, n);
6496 }
6497 }
6498
6499 output->ended_in_ifs = last_is_ifs;
6500 debug_print_list("expand_on_ifs[1]", output, n);
6501 return n;
6502}
6503
Francis Laniel8197f012023-12-22 22:02:28 +01006504#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01006505/* Helper to expand $((...)) and heredoc body. These act as if
6506 * they are in double quotes, with the exception that they are not :).
6507 * Just the rules are similar: "expand only $var and `cmd`"
6508 *
6509 * Returns malloced string.
6510 * As an optimization, we return NULL if expansion is not needed.
6511 */
6512static char *encode_then_expand_string(const char *str)
6513{
6514 char *exp_str;
6515 struct in_str input;
6516 o_string dest = NULL_O_STRING;
6517 const char *cp;
6518
6519 cp = str;
6520 for (;;) {
6521 if (!*cp) return NULL; /* string has no special chars */
6522 if (*cp == '$') break;
6523 if (*cp == '\\') break;
6524#if ENABLE_HUSH_TICK
6525 if (*cp == '`') break;
6526#endif
6527 cp++;
6528 }
6529
6530 /* We need to expand. Example:
6531 * echo $(($a + `echo 1`)) $((1 + $((2)) ))
6532 */
6533 setup_string_in_str(&input, str);
6534 encode_string(NULL, &dest, &input, EOF);
6535//TODO: error check (encode_string returns 0 on error)?
6536 //bb_error_msg("'%s' -> '%s'", str, dest.data);
6537 exp_str = expand_string_to_string(dest.data,
6538 EXP_FLAG_ESC_GLOB_CHARS,
6539 /*unbackslash:*/ 1
6540 );
6541 //bb_error_msg("'%s' -> '%s'", dest.data, exp_str);
6542 o_free(&dest);
6543 return exp_str;
6544}
6545
6546static const char *first_special_char_in_vararg(const char *cp)
6547{
6548 for (;;) {
6549 if (!*cp) return NULL; /* string has no special chars */
6550 if (*cp == '$') return cp;
6551 if (*cp == '\\') return cp;
6552 if (*cp == '\'') return cp;
6553 if (*cp == '"') return cp;
6554#if ENABLE_HUSH_TICK
6555 if (*cp == '`') return cp;
6556#endif
6557 /* dquoted "${x:+ARG}" should not glob, therefore
6558 * '*' et al require some non-literal processing: */
6559 if (*cp == '*') return cp;
6560 if (*cp == '?') return cp;
6561 if (*cp == '[') return cp;
6562 cp++;
6563 }
6564}
6565
6566/* Expanding ARG in ${var#ARG}, ${var%ARG}, or ${var/ARG/ARG}.
6567 * These can contain single- and double-quoted strings,
6568 * and treated as if the ARG string is initially unquoted. IOW:
6569 * ${var#ARG} and "${var#ARG}" treat ARG the same (ARG can even be
6570 * a dquoted string: "${var#"zz"}"), the difference only comes later
6571 * (word splitting and globbing of the ${var...} result).
6572 */
6573#if !BASH_PATTERN_SUBST
6574#define encode_then_expand_vararg(str, handle_squotes, do_unbackslash) \
6575 encode_then_expand_vararg(str, handle_squotes)
6576#endif
6577static char *encode_then_expand_vararg(const char *str, int handle_squotes, int do_unbackslash)
6578{
6579#if !BASH_PATTERN_SUBST && ENABLE_HUSH_CASE
6580 const int do_unbackslash = 0;
6581#endif
6582 char *exp_str;
6583 struct in_str input;
6584 o_string dest = NULL_O_STRING;
6585
6586 if (!first_special_char_in_vararg(str)) {
6587 /* string has no special chars */
6588 return NULL;
6589 }
6590
6591 setup_string_in_str(&input, str);
6592 dest.data = xzalloc(1); /* start as "", not as NULL */
6593 exp_str = NULL;
6594
6595 for (;;) {
6596 int ch;
6597
6598 ch = i_getch(&input);
6599 debug_printf_parse("%s: ch=%c (%d) escape=%d\n",
6600 __func__, ch, ch, !!dest.o_expflags);
6601
6602 if (!dest.o_expflags) {
6603 if (ch == EOF)
6604 break;
6605 if (handle_squotes && ch == '\'') {
6606 if (!add_till_single_quote_dquoted(&dest, &input))
6607 goto ret; /* error */
6608 continue;
6609 }
6610 }
6611 if (ch == EOF) {
6612 syntax_error_unterm_ch('"');
6613 goto ret; /* error */
6614 }
6615 if (ch == '"') {
6616 dest.o_expflags ^= EXP_FLAG_ESC_GLOB_CHARS;
6617 continue;
6618 }
6619 if (ch == '\\') {
6620 ch = i_getch(&input);
6621 if (ch == EOF) {
6622//example? error message? syntax_error_unterm_ch('"');
6623 debug_printf_parse("%s: error: \\<eof>\n", __func__);
6624 goto ret;
6625 }
6626 o_addqchr(&dest, ch);
6627 continue;
6628 }
6629 if (ch == '$') {
6630 if (parse_dollar_squote(NULL, &dest, &input))
6631 continue;
6632 if (!parse_dollar(NULL, &dest, &input, /*quote_mask:*/ 0x80)) {
6633 debug_printf_parse("%s: error: parse_dollar returned 0 (error)\n", __func__);
6634 goto ret;
6635 }
6636 continue;
6637 }
6638#if ENABLE_HUSH_TICK
6639 if (ch == '`') {
6640 //unsigned pos = dest->length;
6641 o_addchr(&dest, SPECIAL_VAR_SYMBOL);
6642 o_addchr(&dest, 0x80 | '`');
6643 if (!add_till_backquote(&dest, &input,
6644 /*in_dquote:*/ dest.o_expflags /* nonzero if EXP_FLAG_ESC_GLOB_CHARS set */
6645 )
6646 ) {
6647 goto ret; /* error */
6648 }
6649 o_addchr(&dest, SPECIAL_VAR_SYMBOL);
6650 //debug_printf_subst("SUBST RES3 '%s'\n", dest->data + pos);
6651 continue;
6652 }
6653#endif
6654 o_addQchr(&dest, ch);
6655 } /* for (;;) */
6656
6657 debug_printf_parse("encode: '%s' -> '%s'\n", str, dest.data);
6658 exp_str = expand_string_to_string(dest.data,
6659 do_unbackslash ? EXP_FLAG_ESC_GLOB_CHARS : 0,
6660 do_unbackslash
6661 );
6662 ret:
6663 debug_printf_parse("expand: '%s' -> '%s'\n", dest.data, exp_str);
6664 o_free(&dest);
6665 return exp_str;
6666}
6667
6668/* Expanding ARG in ${var+ARG}, ${var-ARG}
6669 */
6670static int encode_then_append_var_plusminus(o_string *output, int n,
6671 char *str, int dquoted)
6672{
6673 struct in_str input;
6674 o_string dest = NULL_O_STRING;
6675
6676 if (!first_special_char_in_vararg(str)
6677 && '\0' == str[strcspn(str, G.ifs)]
6678 ) {
6679 /* string has no special chars
6680 * && string has no $IFS chars
6681 */
6682 if (dquoted) {
6683 /* Prints 1 (quoted expansion is a "" word, not nothing):
6684 * set -- "${notexist-}"; echo $#
6685 */
6686 output->has_quoted_part = 1;
6687 }
6688 return expand_vars_to_list(output, n, str);
6689 }
6690
6691 setup_string_in_str(&input, str);
6692
6693 for (;;) {
6694 int ch;
6695
6696 ch = i_getch(&input);
6697 debug_printf_parse("%s: ch=%c (%d) escape=%x\n",
6698 __func__, ch, ch, dest.o_expflags);
6699
6700 if (!dest.o_expflags) {
6701 if (ch == EOF)
6702 break;
6703 if (!dquoted && strchr(G.ifs, ch)) {
6704 /* PREFIX${x:d${e}f ...} and we met space: expand "d${e}f" and start new word.
6705 * do not assume we are at the start of the word (PREFIX above).
6706 */
6707 if (dest.data) {
6708 n = expand_vars_to_list(output, n, dest.data);
6709 o_free_and_set_NULL(&dest);
6710 o_addchr(output, '\0');
6711 n = o_save_ptr(output, n); /* create next word */
6712 } else
6713 if (output->length != o_get_last_ptr(output, n)
6714 || output->has_quoted_part
6715 ) {
6716 /* For these cases:
6717 * f() { for i; do echo "|$i|"; done; }; x=x
6718 * f a${x:+ }b # 1st condition
6719 * |a|
6720 * |b|
6721 * f ""${x:+ }b # 2nd condition
6722 * ||
6723 * |b|
6724 */
6725 o_addchr(output, '\0');
6726 n = o_save_ptr(output, n); /* create next word */
6727 }
6728 continue;
6729 }
6730 if (!dquoted && ch == '\'') {
6731 if (!add_till_single_quote_dquoted(&dest, &input))
6732 goto ret; /* error */
6733 o_addchr(&dest, SPECIAL_VAR_SYMBOL);
6734 o_addchr(&dest, SPECIAL_VAR_SYMBOL);
6735 continue;
6736 }
6737 }
6738 if (ch == EOF) {
6739 syntax_error_unterm_ch('"');
6740 goto ret; /* error */
6741 }
6742 if (ch == '"') {
6743 dest.o_expflags ^= EXP_FLAG_ESC_GLOB_CHARS;
6744 if (dest.o_expflags) {
6745 o_addchr(&dest, SPECIAL_VAR_SYMBOL);
6746 o_addchr(&dest, SPECIAL_VAR_SYMBOL);
6747 }
6748 continue;
6749 }
6750 if (ch == '\\') {
6751 ch = i_getch(&input);
6752 if (ch == EOF) {
6753//example? error message? syntax_error_unterm_ch('"');
6754 debug_printf_parse("%s: error: \\<eof>\n", __func__);
6755 goto ret;
6756 }
6757 o_addqchr(&dest, ch);
6758 continue;
6759 }
6760 if (ch == '$') {
6761 if (!parse_dollar(NULL, &dest, &input, /*quote_mask:*/ (dest.o_expflags || dquoted) ? 0x80 : 0)) {
6762 debug_printf_parse("%s: error: parse_dollar returned 0 (error)\n", __func__);
6763 goto ret;
6764 }
6765 continue;
6766 }
6767#if ENABLE_HUSH_TICK
6768 if (ch == '`') {
6769 //unsigned pos = dest->length;
6770 o_addchr(&dest, SPECIAL_VAR_SYMBOL);
6771 o_addchr(&dest, (dest.o_expflags || dquoted) ? 0x80 | '`' : '`');
6772 if (!add_till_backquote(&dest, &input,
6773 /*in_dquote:*/ dest.o_expflags /* nonzero if EXP_FLAG_ESC_GLOB_CHARS set */
6774 )
6775 ) {
6776 goto ret; /* error */
6777 }
6778 o_addchr(&dest, SPECIAL_VAR_SYMBOL);
6779 //debug_printf_subst("SUBST RES3 '%s'\n", dest->data + pos);
6780 continue;
6781 }
6782#endif
6783 if (dquoted) {
6784 /* Always glob-protect if in dquotes:
6785 * x=x; echo "${x:+/bin/c*}" - prints: /bin/c*
6786 * x=x; echo "${x:+"/bin/c*"}" - prints: /bin/c*
6787 */
6788 o_addqchr(&dest, ch);
6789 } else {
6790 /* Glob-protect only if char is quoted:
6791 * x=x; echo ${x:+/bin/c*} - prints many filenames
6792 * x=x; echo ${x:+"/bin/c*"} - prints: /bin/c*
6793 */
6794 o_addQchr(&dest, ch);
6795 }
6796 } /* for (;;) */
6797
6798 if (dest.data) {
6799 n = expand_vars_to_list(output, n, dest.data);
6800 }
6801 ret:
6802 o_free(&dest);
6803 return n;
6804}
Francis Laniel8197f012023-12-22 22:02:28 +01006805#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01006806
Francis Laniel8197f012023-12-22 22:02:28 +01006807#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01006808#if ENABLE_FEATURE_SH_MATH
6809static arith_t expand_and_evaluate_arith(const char *arg, const char **errmsg_p)
6810{
6811 arith_state_t math_state;
6812 arith_t res;
6813 char *exp_str;
6814
6815 math_state.lookupvar = get_local_var_value;
6816 math_state.setvar = set_local_var_from_halves;
6817 //math_state.endofname = endofname;
6818 exp_str = encode_then_expand_string(arg);
6819 res = arith(&math_state, exp_str ? exp_str : arg);
6820 free(exp_str);
6821 if (errmsg_p)
6822 *errmsg_p = math_state.errmsg;
6823 if (math_state.errmsg)
6824 msg_and_die_if_script(math_state.errmsg);
6825 return res;
6826}
6827#endif
Francis Laniel8197f012023-12-22 22:02:28 +01006828#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01006829
Francis Laniel8197f012023-12-22 22:02:28 +01006830#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01006831#if BASH_PATTERN_SUBST
6832/* ${var/[/]pattern[/repl]} helpers */
6833static char *strstr_pattern(char *val, const char *pattern, int *size)
6834{
6835 int sz = strcspn(pattern, "*?[\\");
6836 if (pattern[sz] == '\0') {
6837 /* Optimization for trivial patterns.
6838 * Testcase for very slow replace (performs about 22k replaces):
6839 * x=::::::::::::::::::::::
6840 * 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}
6841 * echo "${x//:/|}"
6842 */
6843 *size = sz;
6844 return strstr(val, pattern);
6845 }
6846
6847 while (1) {
6848 char *end = scan_and_match(val, pattern, SCAN_MOVE_FROM_RIGHT + SCAN_MATCH_LEFT_HALF);
6849 debug_printf_varexp("val:'%s' pattern:'%s' end:'%s'\n", val, pattern, end);
6850 if (end) {
6851 *size = end - val;
6852 return val;
6853 }
6854 if (*val == '\0')
6855 return NULL;
6856 /* Optimization: if "*pat" did not match the start of "string",
6857 * we know that "tring", "ring" etc will not match too:
6858 */
6859 if (pattern[0] == '*')
6860 return NULL;
6861 val++;
6862 }
6863}
6864static char *replace_pattern(char *val, const char *pattern, const char *repl, char exp_op)
6865{
6866 char *result = NULL;
6867 unsigned res_len = 0;
6868 unsigned repl_len = strlen(repl);
6869
6870 /* Null pattern never matches, including if "var" is empty */
6871 if (!pattern[0])
6872 return result; /* NULL, no replaces happened */
6873
6874 while (1) {
6875 int size;
6876 char *s = strstr_pattern(val, pattern, &size);
6877 if (!s)
6878 break;
6879
6880 result = xrealloc(result, res_len + (s - val) + repl_len + 1);
6881 strcpy(mempcpy(result + res_len, val, s - val), repl);
6882 res_len += (s - val) + repl_len;
6883 debug_printf_varexp("val:'%s' s:'%s' result:'%s'\n", val, s, result);
6884
6885 val = s + size;
6886 if (exp_op == '/')
6887 break;
6888 }
6889 if (*val && result) {
6890 result = xrealloc(result, res_len + strlen(val) + 1);
6891 strcpy(result + res_len, val);
6892 debug_printf_varexp("val:'%s' result:'%s'\n", val, result);
6893 }
6894 debug_printf_varexp("result:'%s'\n", result);
6895 return result;
6896}
6897#endif /* BASH_PATTERN_SUBST */
Francis Laniel8197f012023-12-22 22:02:28 +01006898#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01006899
6900static int append_str_maybe_ifs_split(o_string *output, int n,
6901 int first_ch, const char *val)
6902{
6903 if (!(first_ch & 0x80)) { /* unquoted $VAR */
6904 debug_printf_expand("unquoted '%s', output->o_escape:%d\n", val,
6905 !!(output->o_expflags & EXP_FLAG_ESC_GLOB_CHARS));
6906 if (val && val[0])
6907 n = expand_on_ifs(output, n, val);
6908 } else { /* quoted "$VAR" */
6909 output->has_quoted_part = 1;
6910 debug_printf_expand("quoted '%s', output->o_escape:%d\n", val,
6911 !!(output->o_expflags & EXP_FLAG_ESC_GLOB_CHARS));
6912 if (val && val[0])
6913 o_addQstr(output, val);
6914 }
6915 return n;
6916}
6917
6918/* Handle <SPECIAL_VAR_SYMBOL>varname...<SPECIAL_VAR_SYMBOL> construct.
6919 */
6920static NOINLINE int expand_one_var(o_string *output, int n,
6921 int first_ch, char *arg, char **pp)
6922{
6923 const char *val;
6924 char *to_be_freed;
6925 char *p;
6926 char *var;
6927 char exp_op;
6928 char exp_save = exp_save; /* for compiler */
6929 char *exp_saveptr; /* points to expansion operator */
6930 char *exp_word = exp_word; /* for compiler */
6931 char arg0;
6932
6933 val = NULL;
6934 to_be_freed = NULL;
6935 p = *pp;
6936 *p = '\0'; /* replace trailing SPECIAL_VAR_SYMBOL */
6937 var = arg;
6938 exp_saveptr = arg[1] ? strchr(VAR_ENCODED_SUBST_OPS, arg[1]) : NULL;
6939 arg0 = arg[0];
6940 arg[0] = (arg0 & 0x7f);
6941 exp_op = 0;
6942
6943 if (arg[0] == '#' && arg[1] /* ${#...} but not ${#} */
6944 && (!exp_saveptr /* and ( not(${#<op_char>...}) */
6945 || (arg[2] == '\0' && strchr(SPECIAL_VARS_STR, arg[1])) /* or ${#C} "len of $C" ) */
6946 ) /* NB: skipping ^^^specvar check mishandles ${#::2} */
6947 ) {
6948 /* It must be length operator: ${#var} */
6949 var++;
6950 exp_op = 'L';
6951 } else {
6952 /* Maybe handle parameter expansion */
6953 if (exp_saveptr /* if 2nd char is one of expansion operators */
6954 && strchr(NUMERIC_SPECVARS_STR, arg[0]) /* 1st char is special variable */
6955 ) {
6956 /* ${?:0}, ${#[:]%0} etc */
6957 exp_saveptr = var + 1;
6958 } else {
6959 /* ${?}, ${var}, ${var:0}, ${var[:]%0} etc */
6960 exp_saveptr = var+1 + strcspn(var+1, VAR_ENCODED_SUBST_OPS);
6961 }
6962 exp_op = exp_save = *exp_saveptr;
Francis Laniel8197f012023-12-22 22:02:28 +01006963#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01006964 if (exp_op) {
6965 exp_word = exp_saveptr + 1;
6966 if (exp_op == ':') {
6967 exp_op = *exp_word++;
6968//TODO: try ${var:} and ${var:bogus} in non-bash config
6969 if (BASH_SUBSTR
6970 && (!exp_op || !strchr(MINUS_PLUS_EQUAL_QUESTION, exp_op))
6971 ) {
6972 /* oops... it's ${var:N[:M]}, not ${var:?xxx} or some such */
6973 exp_op = ':';
6974 exp_word--;
6975 }
6976 }
6977 *exp_saveptr = '\0';
6978 } /* else: it's not an expansion op, but bare ${var} */
Francis Laniel8197f012023-12-22 22:02:28 +01006979#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01006980 }
6981
6982 /* Look up the variable in question */
6983 if (isdigit(var[0])) {
6984 /* parse_dollar should have vetted var for us */
Francis Laniel8197f012023-12-22 22:02:28 +01006985#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01006986 int nn = xatoi_positive(var);
Francis Laniel8197f012023-12-22 22:02:28 +01006987#else /* __U_BOOT__ */
6988 int nn = simple_strtoul(var, NULL, 10);
6989#endif /* __U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01006990 if (nn < G.global_argc)
6991 val = G.global_argv[nn];
6992 /* else val remains NULL: $N with too big N */
6993 } else {
6994 switch (var[0]) {
Francis Laniel8197f012023-12-22 22:02:28 +01006995#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01006996 case '$': /* pid */
6997 val = utoa(G.root_pid);
6998 break;
6999 case '!': /* bg pid */
7000 val = G.last_bg_pid ? utoa(G.last_bg_pid) : "";
7001 break;
Francis Laniel8197f012023-12-22 22:02:28 +01007002#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01007003 case '?': /* exitcode */
7004 val = utoa(G.last_exitcode);
7005 break;
7006 case '#': /* argc */
7007 val = utoa(G.global_argc ? G.global_argc-1 : 0);
7008 break;
Francis Laniel8197f012023-12-22 22:02:28 +01007009#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01007010 case '-': { /* active options */
7011 /* Check set_mode() to see what option chars we support */
7012 char *cp;
7013 val = cp = G.optstring_buf;
7014 if (G.o_opt[OPT_O_ERREXIT])
7015 *cp++ = 'e';
7016 if (G_interactive_fd)
7017 *cp++ = 'i';
7018 if (G_x_mode)
7019 *cp++ = 'x';
7020 /* If G.o_opt[OPT_O_NOEXEC] is true,
7021 * commands read but are not executed,
7022 * so $- can not execute too, 'n' is never seen in $-.
7023 */
7024 if (G.opt_c)
7025 *cp++ = 'c';
7026 if (G.opt_s)
7027 *cp++ = 's';
7028 *cp = '\0';
7029 break;
7030 }
Francis Laniel8197f012023-12-22 22:02:28 +01007031#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01007032 default:
7033 val = get_local_var_value(var);
7034 }
7035 }
7036
Francis Laniel8197f012023-12-22 22:02:28 +01007037#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01007038 /* Handle any expansions */
7039 if (exp_op == 'L') {
7040 reinit_unicode_for_hush();
7041 debug_printf_expand("expand: length(%s)=", val);
7042 val = utoa(val ? unicode_strlen(val) : 0);
7043 debug_printf_expand("%s\n", val);
7044 } else if (exp_op) {
7045 if (exp_op == '%' || exp_op == '#') {
7046 /* Standard-mandated substring removal ops:
7047 * ${parameter%word} - remove smallest suffix pattern
7048 * ${parameter%%word} - remove largest suffix pattern
7049 * ${parameter#word} - remove smallest prefix pattern
7050 * ${parameter##word} - remove largest prefix pattern
7051 *
7052 * Word is expanded to produce a glob pattern.
7053 * Then var's value is matched to it and matching part removed.
7054 */
7055 /* bash compat: if x is "" and no shrinking of it is possible,
7056 * inner ${...} is not evaluated. Example:
7057 * unset b; : ${a%${b=B}}; echo $b
7058 * assignment b=B only happens if $a is not "".
7059 */
7060 if (val && val[0]) {
7061 char *t;
7062 char *exp_exp_word;
7063 char *loc;
7064 unsigned scan_flags = pick_scan(exp_op, *exp_word);
7065 if (exp_op == *exp_word) /* ## or %% */
7066 exp_word++;
7067 debug_printf_expand("expand: exp_word:'%s'\n", exp_word);
7068 exp_exp_word = encode_then_expand_vararg(exp_word, /*handle_squotes:*/ 1, /*unbackslash:*/ 0);
7069 if (exp_exp_word)
7070 exp_word = exp_exp_word;
7071 debug_printf_expand("expand: exp_word:'%s'\n", exp_word);
7072 /*
7073 * HACK ALERT. We depend here on the fact that
7074 * G.global_argv and results of utoa and get_local_var_value
7075 * are actually in writable memory:
7076 * scan_and_match momentarily stores NULs there.
7077 */
7078 t = (char*)val;
7079 loc = scan_and_match(t, exp_word, scan_flags);
7080 debug_printf_expand("op:%c str:'%s' pat:'%s' res:'%s'\n", exp_op, t, exp_word, loc);
7081 free(exp_exp_word);
7082 if (loc) { /* match was found */
7083 if (scan_flags & SCAN_MATCH_LEFT_HALF) /* #[#] */
7084 val = loc; /* take right part */
7085 else /* %[%] */
7086 val = to_be_freed = xstrndup(val, loc - val); /* left */
7087 }
7088 }
7089 }
7090#if BASH_PATTERN_SUBST
7091 else if (exp_op == '/' || exp_op == '\\') {
7092 /* It's ${var/[/]pattern[/repl]} thing.
7093 * Note that in encoded form it has TWO parts:
7094 * var/pattern<SPECIAL_VAR_SYMBOL>repl<SPECIAL_VAR_SYMBOL>
7095 * and if // is used, it is encoded as \:
7096 * var\pattern<SPECIAL_VAR_SYMBOL>repl<SPECIAL_VAR_SYMBOL>
7097 */
7098 /* bash compat: if var is "", both pattern and repl
7099 * are still evaluated, if it is unset, then not:
7100 * unset b; a=; : ${a/z/${b=3}}; echo $b # b=3
7101 * unset b; unset a; : ${a/z/${b=3}}; echo $b # b not set
7102 */
7103 if (val /*&& val[0]*/) {
7104 /* pattern uses non-standard expansion.
7105 * repl should be unbackslashed and globbed
7106 * by the usual expansion rules:
7107 * >az >bz
7108 * v='a bz'; echo "${v/a*z/a*z}" #prints "a*z"
7109 * v='a bz'; echo "${v/a*z/\z}" #prints "z"
7110 * v='a bz'; echo ${v/a*z/a*z} #prints "az"
7111 * v='a bz'; echo ${v/a*z/\z} #prints "z"
7112 * (note that a*z _pattern_ is never globbed!)
7113 */
7114 char *pattern, *repl, *t;
7115 pattern = encode_then_expand_vararg(exp_word, /*handle_squotes:*/ 1, /*unbackslash:*/ 0);
7116 if (!pattern)
7117 pattern = xstrdup(exp_word);
7118 debug_printf_varexp("pattern:'%s'->'%s'\n", exp_word, pattern);
7119 *p++ = SPECIAL_VAR_SYMBOL;
7120 exp_word = p;
7121 p = strchr(p, SPECIAL_VAR_SYMBOL);
7122 *p = '\0';
7123 repl = encode_then_expand_vararg(exp_word, /*handle_squotes:*/ 1, /*unbackslash:*/ 1);
7124 debug_printf_varexp("repl:'%s'->'%s'\n", exp_word, repl);
7125 /* HACK ALERT. We depend here on the fact that
7126 * G.global_argv and results of utoa and get_local_var_value
7127 * are actually in writable memory:
7128 * replace_pattern momentarily stores NULs there. */
7129 t = (char*)val;
7130 to_be_freed = replace_pattern(t,
7131 pattern,
7132 (repl ? repl : exp_word),
7133 exp_op);
7134 if (to_be_freed) /* at least one replace happened */
7135 val = to_be_freed;
7136 free(pattern);
7137 free(repl);
7138 } else {
7139 /* Unset variable always gives nothing */
7140 // a=; echo ${a/*/w} # "w"
7141 // unset a; echo ${a/*/w} # ""
7142 /* Just skip "replace" part */
7143 *p++ = SPECIAL_VAR_SYMBOL;
7144 p = strchr(p, SPECIAL_VAR_SYMBOL);
7145 *p = '\0';
7146 }
7147 }
7148#endif /* BASH_PATTERN_SUBST */
7149 else if (exp_op == ':') {
7150#if BASH_SUBSTR && ENABLE_FEATURE_SH_MATH
7151 /* It's ${var:N[:M]} bashism.
7152 * Note that in encoded form it has TWO parts:
7153 * var:N<SPECIAL_VAR_SYMBOL>M<SPECIAL_VAR_SYMBOL>
7154 */
7155 arith_t beg, len;
7156 unsigned vallen;
7157 const char *errmsg;
7158
7159 beg = expand_and_evaluate_arith(exp_word, &errmsg);
7160 if (errmsg)
7161 goto empty_result;
7162 debug_printf_varexp("beg:'%s'=%lld\n", exp_word, (long long)beg);
7163 *p++ = SPECIAL_VAR_SYMBOL;
7164 exp_word = p;
7165 p = strchr(p, SPECIAL_VAR_SYMBOL);
7166 *p = '\0';
7167 vallen = val ? strlen(val) : 0;
7168 if (beg < 0) {
7169 /* negative beg counts from the end */
7170 beg = (arith_t)vallen + beg;
7171 }
7172 /* If expansion will be empty, do not even evaluate len */
7173 if (!val || beg < 0 || beg > vallen) {
7174 /* Why > vallen, not >=? bash:
7175 * unset b; a=ab; : ${a:2:${b=3}}; echo $b # "", b=3 (!!!)
7176 * unset b; a=a; : ${a:2:${b=3}}; echo $b # "", b not set
7177 */
7178 goto empty_result;
7179 }
7180 len = expand_and_evaluate_arith(exp_word, &errmsg);
7181 if (errmsg)
7182 goto empty_result;
7183 debug_printf_varexp("len:'%s'=%lld\n", exp_word, (long long)len);
7184 debug_printf_varexp("from val:'%s'\n", val);
7185 if (len < 0) {
7186 /* in bash, len=-n means strlen()-n */
7187 len = (arith_t)vallen - beg + len;
7188 if (len < 0) /* bash compat */
7189 msg_and_die_if_script("%s: substring expression < 0", var);
7190 }
7191 if (len <= 0 || !val /*|| beg >= vallen*/) {
7192 empty_result:
7193 val = NULL;
7194 } else {
7195 /* Paranoia. What if user entered 9999999999999
7196 * which fits in arith_t but not int? */
7197 if (len > INT_MAX)
7198 len = INT_MAX;
7199 val = to_be_freed = xstrndup(val + beg, len);
7200 }
7201 debug_printf_varexp("val:'%s'\n", val);
7202#else /* not (HUSH_SUBSTR_EXPANSION && FEATURE_SH_MATH) */
7203 msg_and_die_if_script("malformed ${%s:...}", var);
7204 val = NULL;
7205#endif
7206 } else { /* one of "-=+?" */
7207 /* Standard-mandated substitution ops:
7208 * ${var?word} - indicate error if unset
7209 * If var is unset, word (or a message indicating it is unset
7210 * if word is null) is written to standard error
7211 * and the shell exits with a non-zero exit status.
7212 * Otherwise, the value of var is substituted.
7213 * ${var-word} - use default value
7214 * If var is unset, word is substituted.
7215 * ${var=word} - assign and use default value
7216 * If var is unset, word is assigned to var.
7217 * In all cases, final value of var is substituted.
7218 * ${var+word} - use alternative value
7219 * If var is unset, null is substituted.
7220 * Otherwise, word is substituted.
7221 *
7222 * Word is subjected to tilde expansion, parameter expansion,
7223 * command substitution, and arithmetic expansion.
7224 * If word is not needed, it is not expanded.
7225 *
7226 * Colon forms (${var:-word}, ${var:=word} etc) do the same,
7227 * but also treat null var as if it is unset.
7228 *
7229 * Word-splitting and single quote behavior:
7230 *
7231 * $ f() { for i; do echo "|$i|"; done; }
7232 *
7233 * $ x=; f ${x:?'x y' z}; echo $?
7234 * bash: x: x y z # neither f nor "echo $?" executes
7235 * (if interactive, bash does not exit, but merely aborts to prompt. $? is set to 1)
7236 * $ x=; f "${x:?'x y' z}"
7237 * bash: x: x y z # dash prints: dash: x: 'x y' z
7238 *
7239 * $ x=; f ${x:='x y' z}
7240 * |x|
7241 * |y|
7242 * |z|
7243 * $ x=; f "${x:='x y' z}"
7244 * |'x y' z|
7245 *
7246 * $ x=x; f ${x:+'x y' z}
7247 * |x y|
7248 * |z|
7249 * $ x=x; f "${x:+'x y' z}"
7250 * |'x y' z|
7251 *
7252 * $ x=; f ${x:-'x y' z}
7253 * |x y|
7254 * |z|
7255 * $ x=; f "${x:-'x y' z}"
7256 * |'x y' z|
7257 */
7258 int use_word = (!val || ((exp_save == ':') && !val[0]));
7259 if (exp_op == '+')
7260 use_word = !use_word;
7261 debug_printf_expand("expand: op:%c (null:%s) test:%i\n", exp_op,
7262 (exp_save == ':') ? "true" : "false", use_word);
7263 if (use_word) {
7264 if (exp_op == '+' || exp_op == '-') {
7265 /* ${var+word} - use alternative value */
7266 /* ${var-word} - use default value */
7267 n = encode_then_append_var_plusminus(output, n, exp_word,
7268 /*dquoted:*/ (arg0 & 0x80)
7269 );
7270 val = NULL;
7271 } else {
7272 /* ${var?word} - indicate error if unset */
7273 /* ${var=word} - assign and use default value */
7274 to_be_freed = encode_then_expand_vararg(exp_word,
7275 /*handle_squotes:*/ !(arg0 & 0x80),
7276 /*unbackslash:*/ 0
7277 );
7278 if (to_be_freed)
7279 exp_word = to_be_freed;
7280 if (exp_op == '?') {
7281 /* mimic bash message */
7282 msg_and_die_if_script("%s: %s",
7283 var,
7284 exp_word[0]
7285 ? exp_word
7286 : "parameter null or not set"
7287 /* ash has more specific messages, a-la: */
7288 /*: (exp_save == ':' ? "parameter null or not set" : "parameter not set")*/
7289 );
7290//TODO: how interactive bash aborts expansion mid-command?
7291//It aborts the entire line, returns to prompt:
7292// $ f() { for i; do echo "|$i|"; done; }; x=; f "${x:?'x y' z}"; echo YO
7293// bash: x: x y z
7294// $
7295// ("echo YO" is not executed, neither the f function call)
7296 } else {
7297 val = exp_word;
7298 }
7299 if (exp_op == '=') {
7300 /* ${var=[word]} or ${var:=[word]} */
7301 if (isdigit(var[0]) || var[0] == '#') {
7302 /* mimic bash message */
7303 msg_and_die_if_script("$%s: cannot assign in this way", var);
7304 val = NULL;
7305 } else {
7306 char *new_var = xasprintf("%s=%s", var, val);
7307 set_local_var(new_var, /*flag:*/ 0);
7308 }
7309 }
7310 }
7311 }
7312 } /* one of "-=+?" */
7313
7314 *exp_saveptr = exp_save;
7315 } /* if (exp_op) */
7316
Francis Laniel8197f012023-12-22 22:02:28 +01007317#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01007318 arg[0] = arg0;
7319 *pp = p;
7320
7321 n = append_str_maybe_ifs_split(output, n, first_ch, val);
7322
7323 free(to_be_freed);
7324 return n;
7325}
7326
7327/* Expand all variable references in given string, adding words to list[]
7328 * at n, n+1,... positions. Return updated n (so that list[n] is next one
7329 * to be filled). This routine is extremely tricky: has to deal with
7330 * variables/parameters with whitespace, $* and $@, and constructs like
7331 * 'echo -$*-'. If you play here, you must run testsuite afterwards! */
7332static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg)
7333{
7334 /* output->o_expflags & EXP_FLAG_SINGLEWORD (0x80) if we are in
7335 * expansion of right-hand side of assignment == 1-element expand.
7336 */
7337 char cant_be_null = 0; /* only bit 0x80 matters */
7338 char *p;
7339
7340 debug_printf_expand("expand_vars_to_list: arg:'%s' singleword:%x\n", arg,
7341 !!(output->o_expflags & EXP_FLAG_SINGLEWORD));
7342 debug_print_list("expand_vars_to_list[0]", output, n);
7343
7344 while ((p = strchr(arg, SPECIAL_VAR_SYMBOL)) != NULL) {
7345 char first_ch;
7346#if ENABLE_FEATURE_SH_MATH
7347 char arith_buf[sizeof(arith_t)*3 + 2];
7348#endif
7349
7350 if (output->ended_in_ifs) {
7351 o_addchr(output, '\0');
7352 n = o_save_ptr(output, n);
7353 output->ended_in_ifs = 0;
7354 }
7355
7356 o_addblock(output, arg, p - arg);
7357 debug_print_list("expand_vars_to_list[1]", output, n);
7358 arg = ++p;
7359 p = strchr(p, SPECIAL_VAR_SYMBOL);
7360
7361 /* Fetch special var name (if it is indeed one of them)
7362 * and quote bit, force the bit on if singleword expansion -
7363 * important for not getting v=$@ expand to many words. */
7364 first_ch = arg[0] | (output->o_expflags & EXP_FLAG_SINGLEWORD);
7365
7366 /* Is this variable quoted and thus expansion can't be null?
7367 * "$@" is special. Even if quoted, it can still
7368 * expand to nothing (not even an empty string),
7369 * thus it is excluded. */
7370 if ((first_ch & 0x7f) != '@')
7371 cant_be_null |= first_ch;
7372
7373 switch (first_ch & 0x7f) {
7374 /* Highest bit in first_ch indicates that var is double-quoted */
7375 case '*':
7376 case '@': {
7377 int i;
7378 if (!G.global_argv[1])
7379 break;
7380 i = 1;
7381 cant_be_null |= first_ch; /* do it for "$@" _now_, when we know it's not empty */
7382 if (!(first_ch & 0x80)) { /* unquoted $* or $@ */
7383 while (G.global_argv[i]) {
7384 n = expand_on_ifs(output, n, G.global_argv[i]);
7385 debug_printf_expand("expand_vars_to_list: argv %d (last %d)\n", i, G.global_argc - 1);
7386 if (G.global_argv[i++][0] && G.global_argv[i]) {
7387 /* this argv[] is not empty and not last:
7388 * put terminating NUL, start new word */
7389 o_addchr(output, '\0');
7390 debug_print_list("expand_vars_to_list[2]", output, n);
7391 n = o_save_ptr(output, n);
7392 debug_print_list("expand_vars_to_list[3]", output, n);
7393 }
7394 }
7395 } else
7396 /* If EXP_FLAG_SINGLEWORD, we handle assignment 'a=....$@.....'
7397 * and in this case should treat it like '$*' - see 'else...' below */
7398 if (first_ch == (char)('@'|0x80) /* quoted $@ */
7399 && !(output->o_expflags & EXP_FLAG_SINGLEWORD) /* not v="$@" case */
7400 ) {
7401 while (1) {
7402 o_addQstr(output, G.global_argv[i]);
7403 if (++i >= G.global_argc)
7404 break;
7405 o_addchr(output, '\0');
7406 debug_print_list("expand_vars_to_list[4]", output, n);
7407 n = o_save_ptr(output, n);
7408 }
7409 } else { /* quoted $* (or v="$@" case): add as one word */
7410 while (1) {
7411 o_addQstr(output, G.global_argv[i]);
7412 if (!G.global_argv[++i])
7413 break;
7414 if (G.ifs[0])
7415 o_addchr(output, G.ifs[0]);
7416 }
7417 output->has_quoted_part = 1;
7418 }
7419 break;
7420 }
7421 case SPECIAL_VAR_SYMBOL: {
7422 /* <SPECIAL_VAR_SYMBOL><SPECIAL_VAR_SYMBOL> */
7423 /* "Empty variable", used to make "" etc to not disappear */
7424 output->has_quoted_part = 1;
7425 cant_be_null = 0x80;
7426 arg++;
7427 break;
7428 }
7429 case SPECIAL_VAR_QUOTED_SVS:
7430 /* <SPECIAL_VAR_SYMBOL><SPECIAL_VAR_QUOTED_SVS><SPECIAL_VAR_SYMBOL> */
7431 /* "^C variable", represents literal ^C char (possible in scripts) */
7432 o_addchr(output, SPECIAL_VAR_SYMBOL);
7433 arg++;
7434 break;
7435#if ENABLE_HUSH_TICK
7436 case '`': {
7437 /* <SPECIAL_VAR_SYMBOL>`cmd<SPECIAL_VAR_SYMBOL> */
7438 o_string subst_result = NULL_O_STRING;
7439
7440 *p = '\0'; /* replace trailing <SPECIAL_VAR_SYMBOL> */
7441 arg++;
7442 /* Can't just stuff it into output o_string,
7443 * expanded result may need to be globbed
7444 * and $IFS-split */
7445 debug_printf_subst("SUBST '%s' first_ch %x\n", arg, first_ch);
7446 G.last_exitcode = process_command_subs(&subst_result, arg);
7447 G.expand_exitcode = G.last_exitcode;
7448 debug_printf_subst("SUBST RES:%d '%s'\n", G.last_exitcode, subst_result.data);
7449 n = append_str_maybe_ifs_split(output, n, first_ch, subst_result.data);
7450 o_free(&subst_result);
7451 break;
7452 }
7453#endif
7454#if ENABLE_FEATURE_SH_MATH
7455 case '+': {
7456 /* <SPECIAL_VAR_SYMBOL>+arith<SPECIAL_VAR_SYMBOL> */
7457 arith_t res;
7458
7459 arg++; /* skip '+' */
7460 *p = '\0'; /* replace trailing <SPECIAL_VAR_SYMBOL> */
7461 debug_printf_subst("ARITH '%s' first_ch %x\n", arg, first_ch);
7462 res = expand_and_evaluate_arith(arg, NULL);
7463 debug_printf_subst("ARITH RES '"ARITH_FMT"'\n", res);
7464 sprintf(arith_buf, ARITH_FMT, res);
7465 if (res < 0
7466 && first_ch == (char)('+'|0x80)
7467 /* && (output->o_expflags & EXP_FLAG_ESC_GLOB_CHARS) */
7468 ) {
7469 /* Quoted negative ariths, like filename[0"$((-9))"],
7470 * should not be interpreted as glob ranges.
7471 * Convert leading '-' to '\-':
7472 */
7473 o_grow_by(output, 1);
7474 output->data[output->length++] = '\\';
7475 }
7476 o_addstr(output, arith_buf);
7477 break;
7478 }
7479#endif
7480 default:
7481 /* <SPECIAL_VAR_SYMBOL>varname[ops]<SPECIAL_VAR_SYMBOL> */
7482 n = expand_one_var(output, n, first_ch, arg, &p);
7483 break;
7484 } /* switch (char after <SPECIAL_VAR_SYMBOL>) */
7485
7486 /* Restore NULL'ed SPECIAL_VAR_SYMBOL.
7487 * Do the check to avoid writing to a const string. */
7488 if (*p != SPECIAL_VAR_SYMBOL)
7489 *p = SPECIAL_VAR_SYMBOL;
7490 arg = ++p;
7491 } /* end of "while (SPECIAL_VAR_SYMBOL is found) ..." */
7492
7493 if (*arg) {
7494 /* handle trailing string */
7495 if (output->ended_in_ifs) {
7496 o_addchr(output, '\0');
7497 n = o_save_ptr(output, n);
7498 }
7499 debug_print_list("expand_vars_to_list[a]", output, n);
7500 /* this part is literal, and it was already pre-quoted
7501 * if needed (much earlier), do not use o_addQstr here!
7502 */
7503 o_addstr(output, arg);
7504 debug_print_list("expand_vars_to_list[b]", output, n);
7505 } else
7506 if (output->length == o_get_last_ptr(output, n) /* expansion is empty */
7507 && !(cant_be_null & 0x80) /* and all vars were not quoted */
7508 && !output->has_quoted_part
7509 ) {
7510 n--;
7511 /* allow to reuse list[n] later without re-growth */
7512 output->has_empty_slot = 1;
7513 }
7514
7515 return n;
7516}
7517
7518static char **expand_variables(char **argv, unsigned expflags)
7519{
7520 int n;
7521 char **list;
7522 o_string output = NULL_O_STRING;
7523
7524 output.o_expflags = expflags;
7525
7526 n = 0;
7527 for (;;) {
7528 /* go to next list[n] */
7529 output.ended_in_ifs = 0;
7530 n = o_save_ptr(&output, n);
7531
7532 if (!*argv)
7533 break;
7534
7535 /* expand argv[i] */
7536 n = expand_vars_to_list(&output, n, *argv++);
7537 /* if (!output->has_empty_slot) -- need this?? */
7538 o_addchr(&output, '\0');
7539 }
7540 debug_print_list("expand_variables", &output, n);
7541
7542 /* output.data (malloced in one block) gets returned in "list" */
7543 list = o_finalize_list(&output, n);
7544 debug_print_strings("expand_variables[1]", list);
7545 return list;
7546}
7547
7548static char **expand_strvec_to_strvec(char **argv)
7549{
7550 return expand_variables(argv, EXP_FLAG_GLOB | EXP_FLAG_ESC_GLOB_CHARS);
7551}
7552
7553#if defined(CMD_SINGLEWORD_NOGLOB) || defined(CMD_TEST2_SINGLEWORD_NOGLOB)
7554static char **expand_strvec_to_strvec_singleword_noglob(char **argv)
7555{
7556 return expand_variables(argv, EXP_FLAG_SINGLEWORD);
7557}
7558#endif
7559
7560/* Used for expansion of right hand of assignments,
7561 * $((...)), heredocs, variable expansion parts.
7562 *
7563 * NB: should NOT do globbing!
7564 * "export v=/bin/c*; env | grep ^v=" outputs "v=/bin/c*"
7565 */
7566static char *expand_string_to_string(const char *str, int EXP_flags, int do_unbackslash)
7567{
7568#if !BASH_PATTERN_SUBST && !ENABLE_HUSH_CASE
7569 const int do_unbackslash = 1;
7570 const int EXP_flags = EXP_FLAG_ESC_GLOB_CHARS;
7571#endif
7572 char *argv[2], **list;
7573
7574 debug_printf_expand("string_to_string<='%s'\n", str);
7575 /* This is generally an optimization, but it also
7576 * handles "", which otherwise trips over !list[0] check below.
7577 * (is this ever happens that we actually get str="" here?)
7578 */
7579 if (!strchr(str, SPECIAL_VAR_SYMBOL) && !strchr(str, '\\')) {
7580 //TODO: Can use on strings with \ too, just unbackslash() them?
7581 debug_printf_expand("string_to_string(fast)=>'%s'\n", str);
7582 return xstrdup(str);
7583 }
7584
7585 argv[0] = (char*)str;
7586 argv[1] = NULL;
7587 list = expand_variables(argv, EXP_flags | EXP_FLAG_SINGLEWORD);
7588 if (!list[0]) {
7589 /* Example where it happens:
7590 * x=; echo ${x:-"$@"}
7591 */
7592 ((char*)list)[0] = '\0';
7593 } else {
7594 if (HUSH_DEBUG)
7595 if (list[1])
7596 bb_simple_error_msg_and_die("BUG in varexp2");
7597 /* actually, just move string 2*sizeof(char*) bytes back */
7598 overlapping_strcpy((char*)list, list[0]);
7599 if (do_unbackslash)
7600 unbackslash((char*)list);
7601 }
7602 debug_printf_expand("string_to_string=>'%s'\n", (char*)list);
7603 return (char*)list;
7604}
7605
7606#if 0
7607static char* expand_strvec_to_string(char **argv)
7608{
7609 char **list;
7610
7611 list = expand_variables(argv, EXP_FLAG_SINGLEWORD);
7612 /* Convert all NULs to spaces */
7613 if (list[0]) {
7614 int n = 1;
7615 while (list[n]) {
7616 if (HUSH_DEBUG)
7617 if (list[n-1] + strlen(list[n-1]) + 1 != list[n])
7618 bb_error_msg_and_die("BUG in varexp3");
7619 /* bash uses ' ' regardless of $IFS contents */
7620 list[n][-1] = ' ';
7621 n++;
7622 }
7623 }
7624 overlapping_strcpy((char*)list, list[0] ? list[0] : "");
7625 debug_printf_expand("strvec_to_string='%s'\n", (char*)list);
7626 return (char*)list;
7627}
7628#endif
7629
Francis Laniel8197f012023-12-22 22:02:28 +01007630#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01007631static char **expand_assignments(char **argv, int count)
7632{
7633 int i;
7634 char **p;
7635
7636 G.expanded_assignments = p = NULL;
7637 /* Expand assignments into one string each */
7638 for (i = 0; i < count; i++) {
7639 p = add_string_to_strings(p,
7640 expand_string_to_string(argv[i],
7641 EXP_FLAG_ESC_GLOB_CHARS,
7642 /*unbackslash:*/ 1
7643 )
7644 );
7645 G.expanded_assignments = p;
7646 }
7647 G.expanded_assignments = NULL;
7648 return p;
7649}
7650
7651
7652static void switch_off_special_sigs(unsigned mask)
7653{
7654 unsigned sig = 0;
7655 while ((mask >>= 1) != 0) {
7656 sig++;
7657 if (!(mask & 1))
7658 continue;
7659#if ENABLE_HUSH_TRAP
7660 if (G_traps) {
7661 if (G_traps[sig] && !G_traps[sig][0])
7662 /* trap is '', has to remain SIG_IGN */
7663 continue;
7664 free(G_traps[sig]);
7665 G_traps[sig] = NULL;
7666 }
7667#endif
7668 /* We are here only if no trap or trap was not '' */
7669 install_sighandler(sig, SIG_DFL);
7670 }
7671}
Francis Laniel8197f012023-12-22 22:02:28 +01007672#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01007673
Francis Laniel8197f012023-12-22 22:02:28 +01007674#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01007675#if BB_MMU
7676/* never called */
7677void re_execute_shell(char ***to_free, const char *s,
7678 char *g_argv0, char **g_argv,
7679 char **builtin_argv) NORETURN;
7680
7681static void reset_traps_to_defaults(void)
7682{
7683 /* This function is always called in a child shell
7684 * after fork (not vfork, NOMMU doesn't use this function).
7685 */
7686 IF_HUSH_TRAP(unsigned sig;)
7687 unsigned mask;
7688
7689 /* Child shells are not interactive.
7690 * SIGTTIN/SIGTTOU/SIGTSTP should not have special handling.
7691 * Testcase: (while :; do :; done) + ^Z should background.
7692 * Same goes for SIGTERM, SIGHUP, SIGINT.
7693 */
7694 mask = (G.special_sig_mask & SPECIAL_INTERACTIVE_SIGS) | G_fatal_sig_mask;
7695 if (!G_traps && !mask)
7696 return; /* already no traps and no special sigs */
7697
7698 /* Switch off special sigs */
7699 switch_off_special_sigs(mask);
7700# if ENABLE_HUSH_JOB
7701 G_fatal_sig_mask = 0;
7702# endif
7703 G.special_sig_mask &= ~SPECIAL_INTERACTIVE_SIGS;
7704 /* SIGQUIT,SIGCHLD and maybe SPECIAL_JOBSTOP_SIGS
7705 * remain set in G.special_sig_mask */
7706
7707# if ENABLE_HUSH_TRAP
7708 if (!G_traps)
7709 return;
7710
7711 /* Reset all sigs to default except ones with empty traps */
7712 for (sig = 0; sig < NSIG; sig++) {
7713 if (!G_traps[sig])
7714 continue; /* no trap: nothing to do */
7715 if (!G_traps[sig][0])
7716 continue; /* empty trap: has to remain SIG_IGN */
7717 /* sig has non-empty trap, reset it: */
7718 free(G_traps[sig]);
7719 G_traps[sig] = NULL;
7720 /* There is no signal for trap 0 (EXIT) */
7721 if (sig == 0)
7722 continue;
7723 install_sighandler(sig, pick_sighandler(sig));
7724 }
7725# endif
7726}
7727
7728#else /* !BB_MMU */
7729
7730static void re_execute_shell(char ***to_free, const char *s,
7731 char *g_argv0, char **g_argv,
7732 char **builtin_argv) NORETURN;
7733static void re_execute_shell(char ***to_free, const char *s,
7734 char *g_argv0, char **g_argv,
7735 char **builtin_argv)
7736{
7737# define NOMMU_HACK_FMT ("-$%x:%x:%x:%x:%x:%llx" IF_HUSH_LOOPS(":%x"))
7738 /* delims + 2 * (number of bytes in printed hex numbers) */
7739 char param_buf[sizeof(NOMMU_HACK_FMT) + 2 * (sizeof(int)*6 + sizeof(long long)*1)];
7740 char *heredoc_argv[4];
7741 struct variable *cur;
7742# if ENABLE_HUSH_FUNCTIONS
7743 struct function *funcp;
7744# endif
7745 char **argv, **pp;
7746 unsigned cnt;
7747 unsigned long long empty_trap_mask;
7748
7749 if (!g_argv0) { /* heredoc */
7750 argv = heredoc_argv;
7751 argv[0] = (char *) G.argv0_for_re_execing;
7752 argv[1] = (char *) "-<";
7753 argv[2] = (char *) s;
7754 argv[3] = NULL;
7755 pp = &argv[3]; /* used as pointer to empty environment */
7756 goto do_exec;
7757 }
7758
7759 cnt = 0;
7760 pp = builtin_argv;
7761 if (pp) while (*pp++)
7762 cnt++;
7763
7764 empty_trap_mask = 0;
7765 if (G_traps) {
7766 int sig;
7767 for (sig = 1; sig < NSIG; sig++) {
7768 if (G_traps[sig] && !G_traps[sig][0])
7769 empty_trap_mask |= 1LL << sig;
7770 }
7771 }
7772
7773 sprintf(param_buf, NOMMU_HACK_FMT
7774 , (unsigned) G.root_pid
7775 , (unsigned) G.root_ppid
7776 , (unsigned) G.last_bg_pid
7777 , (unsigned) G.last_exitcode
7778 , cnt
7779 , empty_trap_mask
7780 IF_HUSH_LOOPS(, G.depth_of_loop)
7781 );
7782# undef NOMMU_HACK_FMT
7783 /* 1:hush 2:-$<pid>:<pid>:<exitcode>:<etc...> <vars...> <funcs...>
7784 * 3:-c 4:<cmd> 5:<arg0> <argN...> 6:NULL
7785 */
7786 cnt += 6;
7787 for (cur = G.top_var; cur; cur = cur->next) {
7788 if (!cur->flg_export || cur->flg_read_only)
7789 cnt += 2;
7790 }
7791# if ENABLE_HUSH_FUNCTIONS
7792 for (funcp = G.top_func; funcp; funcp = funcp->next)
7793 cnt += 3;
7794# endif
7795 pp = g_argv;
7796 while (*pp++)
7797 cnt++;
7798 *to_free = argv = pp = xzalloc(sizeof(argv[0]) * cnt);
7799 *pp++ = (char *) G.argv0_for_re_execing;
7800 *pp++ = param_buf;
7801 for (cur = G.top_var; cur; cur = cur->next) {
7802 if (strcmp(cur->varstr, hush_version_str) == 0)
7803 continue;
7804 if (cur->flg_read_only) {
7805 *pp++ = (char *) "-R";
7806 *pp++ = cur->varstr;
7807 } else if (!cur->flg_export) {
7808 *pp++ = (char *) "-V";
7809 *pp++ = cur->varstr;
7810 }
7811 }
7812# if ENABLE_HUSH_FUNCTIONS
7813 for (funcp = G.top_func; funcp; funcp = funcp->next) {
7814 *pp++ = (char *) "-F";
7815 *pp++ = funcp->name;
7816 *pp++ = funcp->body_as_string;
7817 }
7818# endif
7819 /* We can pass activated traps here. Say, -Tnn:trap_string
7820 *
7821 * However, POSIX says that subshells reset signals with traps
7822 * to SIG_DFL.
7823 * I tested bash-3.2 and it not only does that with true subshells
7824 * of the form ( list ), but with any forked children shells.
7825 * I set trap "echo W" WINCH; and then tried:
7826 *
7827 * { echo 1; sleep 20; echo 2; } &
7828 * while true; do echo 1; sleep 20; echo 2; break; done &
7829 * true | { echo 1; sleep 20; echo 2; } | cat
7830 *
7831 * In all these cases sending SIGWINCH to the child shell
7832 * did not run the trap. If I add trap "echo V" WINCH;
7833 * _inside_ group (just before echo 1), it works.
7834 *
7835 * I conclude it means we don't need to pass active traps here.
7836 */
7837 *pp++ = (char *) "-c";
7838 *pp++ = (char *) s;
7839 if (builtin_argv) {
7840 while (*++builtin_argv)
7841 *pp++ = *builtin_argv;
7842 *pp++ = (char *) "";
7843 }
7844 *pp++ = g_argv0;
7845 while (*g_argv)
7846 *pp++ = *g_argv++;
7847 /* *pp = NULL; - is already there */
7848 pp = environ;
7849
7850 do_exec:
7851 debug_printf_exec("re_execute_shell pid:%d cmd:'%s'\n", getpid(), s);
7852 /* Don't propagate SIG_IGN to the child */
7853 if (SPECIAL_JOBSTOP_SIGS != 0)
7854 switch_off_special_sigs(G.special_sig_mask & SPECIAL_JOBSTOP_SIGS);
7855 execve(bb_busybox_exec_path, argv, pp);
7856 /* Fallback. Useful for init=/bin/hush usage etc */
7857 if (argv[0][0] == '/')
7858 execve(argv[0], argv, pp);
7859 xfunc_error_retval = 127;
7860 bb_simple_error_msg_and_die("can't re-execute the shell");
7861}
7862#endif /* !BB_MMU */
7863
Francis Laniel8197f012023-12-22 22:02:28 +01007864#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01007865
7866static int run_and_free_list(struct pipe *pi);
7867
7868/* Executing from string: eval, sh -c '...'
7869 * or from file: /etc/profile, . file, sh <script>, sh (intereactive)
7870 * end_trigger controls how often we stop parsing
7871 * NUL: parse all, execute, return
7872 * ';': parse till ';' or newline, execute, repeat till EOF
7873 */
7874static void parse_and_run_stream(struct in_str *inp, int end_trigger)
7875{
7876 /* Why we need empty flag?
7877 * An obscure corner case "false; ``; echo $?":
7878 * empty command in `` should still set $? to 0.
7879 * But we can't just set $? to 0 at the start,
7880 * this breaks "false; echo `echo $?`" case.
7881 */
7882 bool empty = 1;
7883 while (1) {
7884 struct pipe *pipe_list;
7885
7886#if ENABLE_HUSH_INTERACTIVE
7887 if (end_trigger == ';') {
7888 G.promptmode = 0; /* PS1 */
7889 debug_printf_prompt("%s promptmode=%d\n", __func__, G.promptmode);
7890 }
7891#endif
7892 pipe_list = parse_stream(NULL, NULL, inp, end_trigger);
7893 if (!pipe_list || pipe_list == ERR_PTR) { /* EOF/error */
7894 /* If we are in "big" script
7895 * (not in `cmd` or something similar)...
7896 */
7897 if (pipe_list == ERR_PTR && end_trigger == ';') {
7898 /* Discard cached input (rest of line) */
7899 int ch = inp->last_char;
7900 while (ch != EOF && ch != '\n') {
7901 //bb_error_msg("Discarded:'%c'", ch);
7902 ch = i_getch(inp);
7903 }
7904 /* Force prompt */
7905 inp->p = NULL;
7906 /* This stream isn't empty */
7907 empty = 0;
7908 continue;
7909 }
7910 if (!pipe_list && empty)
7911 G.last_exitcode = 0;
7912 break;
7913 }
7914 debug_print_tree(pipe_list, 0);
7915 debug_printf_exec("parse_and_run_stream: run_and_free_list\n");
Francis Laniel9a068372023-12-22 22:02:32 +01007916#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01007917 run_and_free_list(pipe_list);
Francis Laniel9a068372023-12-22 22:02:32 +01007918#else /* __U_BOOT__ */
7919 int rcode = run_and_free_list(pipe_list);
7920 /*
7921 * We reset input string to not run the following command, so running
7922 * 'exit; echo foo' does not print foo.
7923 */
7924 if (rcode <= EXIT_RET_CODE)
7925 setup_file_in_str(inp);
7926#endif /* __U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01007927 empty = 0;
7928 if (G_flag_return_in_progress == 1)
7929 break;
7930 }
7931}
7932
Francis Laniel8197f012023-12-22 22:02:28 +01007933#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01007934static void parse_and_run_string(const char *s)
7935{
7936 struct in_str input;
7937 //IF_HUSH_LINENO_VAR(unsigned sv = G.parse_lineno;)
7938
7939 setup_string_in_str(&input, s);
7940 parse_and_run_stream(&input, '\0');
7941 //IF_HUSH_LINENO_VAR(G.parse_lineno = sv;)
7942}
Francis Laniel8197f012023-12-22 22:02:28 +01007943#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01007944
Francis Laniel8197f012023-12-22 22:02:28 +01007945#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01007946static void parse_and_run_file(HFILE *fp)
Francis Laniel8197f012023-12-22 22:02:28 +01007947#else /* __U_BOOT__ */
7948void parse_and_run_file(void)
7949#endif /* __U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01007950{
7951 struct in_str input;
Francis Laniel8197f012023-12-22 22:02:28 +01007952#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01007953 IF_HUSH_LINENO_VAR(unsigned sv = G.parse_lineno;)
7954
7955 IF_HUSH_LINENO_VAR(G.parse_lineno = 1;)
7956 setup_file_in_str(&input, fp);
Francis Laniel8197f012023-12-22 22:02:28 +01007957#else /* __U_BOOT__ */
7958 setup_file_in_str(&input);
7959#endif /* __U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01007960 parse_and_run_stream(&input, ';');
Francis Laniel8197f012023-12-22 22:02:28 +01007961#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01007962 IF_HUSH_LINENO_VAR(G.parse_lineno = sv;)
Francis Laniel8197f012023-12-22 22:02:28 +01007963#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01007964}
7965
Francis Laniel8197f012023-12-22 22:02:28 +01007966#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01007967#if ENABLE_HUSH_TICK
7968static int generate_stream_from_string(const char *s, pid_t *pid_p)
7969{
7970 pid_t pid;
7971 int channel[2];
7972# if !BB_MMU
7973 char **to_free = NULL;
7974# endif
7975
7976 xpipe(channel);
7977 pid = BB_MMU ? xfork() : xvfork();
7978 if (pid == 0) { /* child */
7979 disable_restore_tty_pgrp_on_exit();
7980 /* Process substitution is not considered to be usual
7981 * 'command execution'.
7982 * SUSv3 says ctrl-Z should be ignored, ctrl-C should not.
7983 */
7984 bb_signals(0
7985 + (1 << SIGTSTP)
7986 + (1 << SIGTTIN)
7987 + (1 << SIGTTOU)
7988 , SIG_IGN);
7989 close(channel[0]); /* NB: close _first_, then move fd! */
7990 xmove_fd(channel[1], 1);
7991# if ENABLE_HUSH_TRAP
7992 /* Awful hack for `trap` or $(trap).
7993 *
7994 * http://www.opengroup.org/onlinepubs/009695399/utilities/trap.html
7995 * contains an example where "trap" is executed in a subshell:
7996 *
7997 * save_traps=$(trap)
7998 * ...
7999 * eval "$save_traps"
8000 *
8001 * Standard does not say that "trap" in subshell shall print
8002 * parent shell's traps. It only says that its output
8003 * must have suitable form, but then, in the above example
8004 * (which is not supposed to be normative), it implies that.
8005 *
8006 * bash (and probably other shell) does implement it
8007 * (traps are reset to defaults, but "trap" still shows them),
8008 * but as a result, "trap" logic is hopelessly messed up:
8009 *
8010 * # trap
8011 * trap -- 'echo Ho' SIGWINCH <--- we have a handler
8012 * # (trap) <--- trap is in subshell - no output (correct, traps are reset)
8013 * # true | trap <--- trap is in subshell - no output (ditto)
8014 * # echo `true | trap` <--- in subshell - output (but traps are reset!)
8015 * trap -- 'echo Ho' SIGWINCH
8016 * # echo `(trap)` <--- in subshell in subshell - output
8017 * trap -- 'echo Ho' SIGWINCH
8018 * # echo `true | (trap)` <--- in subshell in subshell in subshell - output!
8019 * trap -- 'echo Ho' SIGWINCH
8020 *
8021 * The rules when to forget and when to not forget traps
8022 * get really complex and nonsensical.
8023 *
8024 * Our solution: ONLY bare $(trap) or `trap` is special.
8025 */
8026 s = skip_whitespace(s);
8027 if (is_prefixed_with(s, "trap")
8028 && skip_whitespace(s + 4)[0] == '\0'
8029 ) {
8030 static const char *const argv[] = { NULL, NULL };
8031 builtin_trap((char**)argv);
8032 fflush_all(); /* important */
8033 _exit(0);
8034 }
8035# endif
8036# if BB_MMU
8037 /* Prevent it from trying to handle ctrl-z etc */
8038 IF_HUSH_JOB(G.run_list_level = 1;)
8039 CLEAR_RANDOM_T(&G.random_gen); /* or else $RANDOM repeats in child */
8040 reset_traps_to_defaults();
8041 IF_HUSH_MODE_X(G.x_mode_depth++;)
8042 //bb_error_msg("%s: ++x_mode_depth=%d", __func__, G.x_mode_depth);
8043 parse_and_run_string(s);
8044 _exit(G.last_exitcode);
8045# else
8046 /* We re-execute after vfork on NOMMU. This makes this script safe:
8047 * yes "0123456789012345678901234567890" | dd bs=32 count=64k >BIG
8048 * huge=`cat BIG` # was blocking here forever
8049 * echo OK
8050 */
8051 re_execute_shell(&to_free,
8052 s,
8053 G.global_argv[0],
8054 G.global_argv + 1,
8055 NULL);
8056# endif
8057 }
8058
8059 /* parent */
8060 *pid_p = pid;
8061# if ENABLE_HUSH_FAST
8062 G.count_SIGCHLD++;
8063//bb_error_msg("[%d] fork in generate_stream_from_string:"
8064// " G.count_SIGCHLD:%d G.handled_SIGCHLD:%d",
8065// getpid(), G.count_SIGCHLD, G.handled_SIGCHLD);
8066# endif
8067 enable_restore_tty_pgrp_on_exit();
8068# if !BB_MMU
8069 free(to_free);
8070# endif
8071 close(channel[1]);
8072 return channel[0];
8073}
8074
8075/* Return code is exit status of the process that is run. */
8076static int process_command_subs(o_string *dest, const char *s)
8077{
8078 FILE *fp;
8079 pid_t pid;
8080 int status, ch, eol_cnt;
8081
8082 fp = xfdopen_for_read(generate_stream_from_string(s, &pid));
8083
8084 /* Now send results of command back into original context */
8085 eol_cnt = 0;
8086 while ((ch = getc(fp)) != EOF) {
8087 if (ch == '\0')
8088 continue;
8089 if (ch == '\n') {
8090 eol_cnt++;
8091 continue;
8092 }
8093 while (eol_cnt) {
8094 o_addchr(dest, '\n');
8095 eol_cnt--;
8096 }
8097 o_addQchr(dest, ch);
8098 }
8099
8100 debug_printf("done reading from `cmd` pipe, closing it\n");
8101 fclose(fp);
8102 /* We need to extract exitcode. Test case
8103 * "true; echo `sleep 1; false` $?"
8104 * should print 1 */
8105 safe_waitpid(pid, &status, 0);
8106 debug_printf("child exited. returning its exitcode:%d\n", WEXITSTATUS(status));
8107 return WEXITSTATUS(status);
8108}
8109#endif /* ENABLE_HUSH_TICK */
8110
8111
8112static void setup_heredoc(struct redir_struct *redir)
8113{
8114 struct fd_pair pair;
8115 pid_t pid;
8116 int len, written;
8117 /* the _body_ of heredoc (misleading field name) */
8118 const char *heredoc = redir->rd_filename;
8119 char *expanded;
8120#if !BB_MMU
8121 char **to_free;
8122#endif
8123
8124 expanded = NULL;
8125 if (!(redir->rd_dup & HEREDOC_QUOTED)) {
8126 expanded = encode_then_expand_string(heredoc);
8127 if (expanded)
8128 heredoc = expanded;
8129 }
8130 len = strlen(heredoc);
8131
8132 close(redir->rd_fd); /* often saves dup2+close in xmove_fd */
8133 xpiped_pair(pair);
8134 xmove_fd(pair.rd, redir->rd_fd);
8135
8136 /* Try writing without forking. Newer kernels have
8137 * dynamically growing pipes. Must use non-blocking write! */
8138 ndelay_on(pair.wr);
8139 while (1) {
8140 written = write(pair.wr, heredoc, len);
8141 if (written <= 0)
8142 break;
8143 len -= written;
8144 if (len == 0) {
8145 close(pair.wr);
8146 free(expanded);
8147 return;
8148 }
8149 heredoc += written;
8150 }
8151 ndelay_off(pair.wr);
8152
8153 /* Okay, pipe buffer was not big enough */
8154 /* Note: we must not create a stray child (bastard? :)
8155 * for the unsuspecting parent process. Child creates a grandchild
8156 * and exits before parent execs the process which consumes heredoc
8157 * (that exec happens after we return from this function) */
8158#if !BB_MMU
8159 to_free = NULL;
8160#endif
8161 pid = xvfork();
8162 if (pid == 0) {
8163 /* child */
8164 disable_restore_tty_pgrp_on_exit();
8165 pid = BB_MMU ? xfork() : xvfork();
8166 if (pid != 0)
8167 _exit(0);
8168 /* grandchild */
8169 close(redir->rd_fd); /* read side of the pipe */
8170#if BB_MMU
8171 full_write(pair.wr, heredoc, len); /* may loop or block */
8172 _exit(0);
8173#else
8174 /* Delegate blocking writes to another process */
8175 xmove_fd(pair.wr, STDOUT_FILENO);
8176 re_execute_shell(&to_free, heredoc, NULL, NULL, NULL);
8177#endif
8178 }
8179 /* parent */
8180#if ENABLE_HUSH_FAST
8181 G.count_SIGCHLD++;
8182//bb_error_msg("[%d] fork in setup_heredoc: G.count_SIGCHLD:%d G.handled_SIGCHLD:%d", getpid(), G.count_SIGCHLD, G.handled_SIGCHLD);
8183#endif
8184 enable_restore_tty_pgrp_on_exit();
8185#if !BB_MMU
8186 free(to_free);
8187#endif
8188 close(pair.wr);
8189 free(expanded);
8190 wait(NULL); /* wait till child has died */
8191}
8192
8193struct squirrel {
8194 int orig_fd;
8195 int moved_to;
8196 /* moved_to = n: fd was moved to n; restore back to orig_fd after redir */
8197 /* moved_to = -1: fd was opened by redirect; close orig_fd after redir */
8198};
8199
8200static struct squirrel *append_squirrel(struct squirrel *sq, int i, int orig, int moved)
8201{
8202 sq = xrealloc(sq, (i + 2) * sizeof(sq[0]));
8203 sq[i].orig_fd = orig;
8204 sq[i].moved_to = moved;
8205 sq[i+1].orig_fd = -1; /* end marker */
8206 return sq;
8207}
8208
8209static struct squirrel *add_squirrel(struct squirrel *sq, int fd, int avoid_fd)
8210{
8211 int moved_to;
8212 int i;
8213
8214 i = 0;
8215 if (sq) for (; sq[i].orig_fd >= 0; i++) {
8216 /* If we collide with an already moved fd... */
8217 if (fd == sq[i].moved_to) {
8218 sq[i].moved_to = dup_CLOEXEC(sq[i].moved_to, avoid_fd);
8219 debug_printf_redir("redirect_fd %d: already busy, moving to %d\n", fd, sq[i].moved_to);
8220 if (sq[i].moved_to < 0) /* what? */
8221 xfunc_die();
8222 return sq;
8223 }
8224 if (fd == sq[i].orig_fd) {
8225 /* Example: echo Hello >/dev/null 1>&2 */
8226 debug_printf_redir("redirect_fd %d: already moved\n", fd);
8227 return sq;
8228 }
8229 }
8230
8231 /* If this fd is open, we move and remember it; if it's closed, moved_to = -1 */
8232 moved_to = dup_CLOEXEC(fd, avoid_fd);
8233 debug_printf_redir("redirect_fd %d: previous fd is moved to %d (-1 if it was closed)\n", fd, moved_to);
8234 if (moved_to < 0 && errno != EBADF)
8235 xfunc_die();
8236 return append_squirrel(sq, i, fd, moved_to);
8237}
8238
8239static struct squirrel *add_squirrel_closed(struct squirrel *sq, int fd)
8240{
8241 int i;
8242
8243 i = 0;
8244 if (sq) for (; sq[i].orig_fd >= 0; i++) {
8245 /* If we collide with an already moved fd... */
8246 if (fd == sq[i].orig_fd) {
8247 /* Examples:
8248 * "echo 3>FILE 3>&- 3>FILE"
8249 * "echo 3>&- 3>FILE"
8250 * No need for last redirect to insert
8251 * another "need to close 3" indicator.
8252 */
8253 debug_printf_redir("redirect_fd %d: already moved or closed\n", fd);
8254 return sq;
8255 }
8256 }
8257
8258 debug_printf_redir("redirect_fd %d: previous fd was closed\n", fd);
8259 return append_squirrel(sq, i, fd, -1);
8260}
8261
8262/* fd: redirect wants this fd to be used (e.g. 3>file).
8263 * Move all conflicting internally used fds,
8264 * and remember them so that we can restore them later.
8265 */
8266static int save_fd_on_redirect(int fd, int avoid_fd, struct squirrel **sqp)
8267{
8268 if (avoid_fd < 9) /* the important case here is that it can be -1 */
8269 avoid_fd = 9;
8270
8271#if ENABLE_HUSH_INTERACTIVE
8272 if (fd != 0 /* don't trigger for G_interactive_fd == 0 (that's "not interactive" flag) */
8273 && fd == G_interactive_fd
8274 ) {
8275 /* Testcase: "ls -l /proc/$$/fd 255>&-" should work */
8276 G_interactive_fd = xdup_CLOEXEC_and_close(G_interactive_fd, avoid_fd);
8277 debug_printf_redir("redirect_fd %d: matches interactive_fd, moving it to %d\n", fd, G_interactive_fd);
8278 return 1; /* "we closed fd" */
8279 }
8280#endif
8281 /* Are we called from setup_redirects(squirrel==NULL)
8282 * in redirect in a [v]forked child?
8283 */
8284 if (sqp == NULL) {
8285 /* No need to move script fds.
8286 * For NOMMU case, it's actively wrong: we'd change ->fd
8287 * fields in memory for the parent, but parent's fds
8288 * aren't moved, it would use wrong fd!
8289 * Reproducer: "cmd 3>FILE" in script.
8290 * If we would call move_HFILEs_on_redirect(), child would:
8291 * fcntl64(3, F_DUPFD_CLOEXEC, 10) = 10
8292 * close(3) = 0
8293 * and change ->fd to 10 if fd#3 is a script fd. WRONG.
8294 */
8295 //bb_error_msg("sqp == NULL: [v]forked child");
8296 return 0;
8297 }
8298
8299 /* If this one of script's fds? */
8300 if (move_HFILEs_on_redirect(fd, avoid_fd))
8301 return 1; /* yes. "we closed fd" (actually moved it) */
8302
8303 /* Are we called for "exec 3>FILE"? Came through
8304 * redirect_and_varexp_helper(squirrel=ERR_PTR) -> setup_redirects(ERR_PTR)
8305 * This case used to fail for this script:
8306 * exec 3>FILE
8307 * echo Ok
8308 * ...100000 more lines...
8309 * echo Ok
8310 * as follows:
8311 * read(3, "exec 3>FILE\necho Ok\necho Ok"..., 1024) = 1024
8312 * open("FILE", O_WRONLY|O_CREAT|O_TRUNC|O_LARGEFILE, 0666) = 4
8313 * dup2(4, 3) = 3
8314 * ^^^^^^^^ oops, we lost fd#3 opened to our script!
8315 * close(4) = 0
8316 * write(1, "Ok\n", 3) = 3
8317 * ... = 3
8318 * write(1, "Ok\n", 3) = 3
8319 * read(3, 0x94fbc08, 1024) = -1 EBADF (Bad file descriptor)
8320 * ^^^^^^^^ oops, wrong fd!!!
8321 * With this case separate from sqp == NULL and *after* move_HFILEs,
8322 * it now works:
8323 */
8324 if (sqp == ERR_PTR) {
8325 /* Don't preserve redirected fds: exec is _meant_ to change these */
8326 //bb_error_msg("sqp == ERR_PTR: exec >FILE");
8327 return 0;
8328 }
8329
8330 /* Check whether it collides with any open fds (e.g. stdio), save fds as needed */
8331 *sqp = add_squirrel(*sqp, fd, avoid_fd);
8332 return 0; /* "we did not close fd" */
8333}
8334
8335static void restore_redirects(struct squirrel *sq)
8336{
8337 if (sq) {
8338 int i;
8339 for (i = 0; sq[i].orig_fd >= 0; i++) {
8340 if (sq[i].moved_to >= 0) {
8341 /* We simply die on error */
8342 debug_printf_redir("restoring redirected fd from %d to %d\n", sq[i].moved_to, sq[i].orig_fd);
8343 xmove_fd(sq[i].moved_to, sq[i].orig_fd);
8344 } else {
8345 /* cmd1 9>FILE; cmd2_should_see_fd9_closed */
8346 debug_printf_redir("restoring redirected fd %d: closing it\n", sq[i].orig_fd);
8347 close(sq[i].orig_fd);
8348 }
8349 }
8350 free(sq);
8351 }
8352 if (G.HFILE_stdin
8353 && G.HFILE_stdin->fd > STDIN_FILENO
8354 /* we compare > STDIN, not == STDIN, since hfgetc()
8355 * closes fd and sets ->fd to -1 if EOF is reached.
8356 * Testcase: echo 'pwd' | hush
8357 */
8358 ) {
8359 /* Testcase: interactive "read r <FILE; echo $r; read r; echo $r".
8360 * Redirect moves ->fd to e.g. 10,
8361 * and it is not restored above (we do not restore script fds
8362 * after redirects, we just use new, "moved" fds).
8363 * However for stdin, get_user_input() -> read_line_input(),
8364 * and read builtin, depend on fd == STDIN_FILENO.
8365 */
8366 debug_printf_redir("restoring %d to stdin\n", G.HFILE_stdin->fd);
8367 xmove_fd(G.HFILE_stdin->fd, STDIN_FILENO);
8368 G.HFILE_stdin->fd = STDIN_FILENO;
8369 }
8370
8371 /* If moved, G_interactive_fd stays on new fd, not restoring it */
8372}
8373
8374#if ENABLE_FEATURE_SH_STANDALONE && BB_MMU
8375static void close_saved_fds_and_FILE_fds(void)
8376{
8377 if (G_interactive_fd)
8378 close(G_interactive_fd);
8379 close_all_HFILE_list();
8380}
8381#endif
8382
8383static int internally_opened_fd(int fd, struct squirrel *sq)
8384{
8385 int i;
8386
8387#if ENABLE_HUSH_INTERACTIVE
8388 if (fd == G_interactive_fd)
8389 return 1;
8390#endif
8391 /* If this one of script's fds? */
8392 if (fd_in_HFILEs(fd))
8393 return 1;
8394
8395 if (sq) for (i = 0; sq[i].orig_fd >= 0; i++) {
8396 if (fd == sq[i].moved_to)
8397 return 1;
8398 }
8399 return 0;
8400}
8401
8402/* squirrel != NULL means we squirrel away copies of stdin, stdout,
8403 * and stderr if they are redirected. */
8404static int setup_redirects(struct command *prog, struct squirrel **sqp)
8405{
8406 struct redir_struct *redir;
8407
8408 for (redir = prog->redirects; redir; redir = redir->next) {
8409 int newfd;
8410 int closed;
8411
8412 if (redir->rd_type == REDIRECT_HEREDOC2) {
8413 /* "rd_fd<<HERE" case */
8414 save_fd_on_redirect(redir->rd_fd, /*avoid:*/ 0, sqp);
8415 /* for REDIRECT_HEREDOC2, rd_filename holds _contents_
8416 * of the heredoc */
8417 debug_printf_redir("set heredoc '%s'\n",
8418 redir->rd_filename);
8419 setup_heredoc(redir);
8420 continue;
8421 }
8422
8423 if (redir->rd_dup == REDIRFD_TO_FILE) {
8424 /* "rd_fd<*>file" case (<*> is <,>,>>,<>) */
8425 char *p;
8426 int mode;
8427
8428 if (redir->rd_filename == NULL) {
8429 /* Examples:
8430 * "cmd >" (no filename)
8431 * "cmd > <file" (2nd redirect starts too early)
8432 */
8433 syntax_error("invalid redirect");
8434 continue;
8435 }
8436 mode = redir_table[redir->rd_type].mode;
8437 p = expand_string_to_string(redir->rd_filename,
8438 EXP_FLAG_ESC_GLOB_CHARS, /*unbackslash:*/ 1);
8439 newfd = open_or_warn(p, mode);
8440 free(p);
8441 if (newfd < 0) {
8442 /* Error message from open_or_warn can be lost
8443 * if stderr has been redirected, but bash
8444 * and ash both lose it as well
8445 * (though zsh doesn't!)
8446 */
8447 return 1;
8448 }
8449 if (newfd == redir->rd_fd && sqp) {
8450 /* open() gave us precisely the fd we wanted.
8451 * This means that this fd was not busy
8452 * (not opened to anywhere).
8453 * Remember to close it on restore:
8454 */
8455 *sqp = add_squirrel_closed(*sqp, newfd);
8456 debug_printf_redir("redir to previously closed fd %d\n", newfd);
8457 }
8458 } else {
8459 /* "rd_fd>&rd_dup" or "rd_fd>&-" case */
8460 newfd = redir->rd_dup;
8461 }
8462
8463 if (newfd == redir->rd_fd)
8464 continue;
8465
8466 /* if "N>FILE": move newfd to redir->rd_fd */
8467 /* if "N>&M": dup newfd to redir->rd_fd */
8468 /* if "N>&-": close redir->rd_fd (newfd is REDIRFD_CLOSE) */
8469
8470 closed = save_fd_on_redirect(redir->rd_fd, /*avoid:*/ newfd, sqp);
8471 if (newfd == REDIRFD_CLOSE) {
8472 /* "N>&-" means "close me" */
8473 if (!closed) {
8474 /* ^^^ optimization: saving may already
8475 * have closed it. If not... */
8476 close(redir->rd_fd);
8477 }
8478 /* Sometimes we do another close on restore, getting EBADF.
8479 * Consider "echo 3>FILE 3>&-"
8480 * first redirect remembers "need to close 3",
8481 * and second redirect closes 3! Restore code then closes 3 again.
8482 */
8483 } else {
8484 /* if newfd is a script fd or saved fd, simulate EBADF */
8485 if (internally_opened_fd(newfd, sqp && sqp != ERR_PTR ? *sqp : NULL)) {
8486 //errno = EBADF;
8487 //bb_perror_msg_and_die("can't duplicate file descriptor");
8488 newfd = -1; /* same effect as code above */
8489 }
8490 xdup2(newfd, redir->rd_fd);
8491 if (redir->rd_dup == REDIRFD_TO_FILE)
8492 /* "rd_fd > FILE" */
8493 close(newfd);
8494 /* else: "rd_fd > rd_dup" */
8495 }
8496 }
8497 return 0;
8498}
8499
8500static char *find_in_path(const char *arg)
8501{
8502 char *ret = NULL;
8503 const char *PATH = get_local_var_value("PATH");
8504
8505 if (!PATH)
8506 return NULL;
8507
8508 while (1) {
8509 const char *end = strchrnul(PATH, ':');
8510 int sz = end - PATH; /* must be int! */
8511
8512 free(ret);
8513 if (sz != 0) {
8514 ret = xasprintf("%.*s/%s", sz, PATH, arg);
8515 } else {
8516 /* We have xxx::yyyy in $PATH,
8517 * it means "use current dir" */
8518 ret = xstrdup(arg);
8519 }
8520 if (access(ret, F_OK) == 0)
8521 break;
8522
8523 if (*end == '\0') {
8524 free(ret);
8525 return NULL;
8526 }
8527 PATH = end + 1;
8528 }
8529
8530 return ret;
8531}
8532
8533static const struct built_in_command *find_builtin_helper(const char *name,
8534 const struct built_in_command *x,
8535 const struct built_in_command *end)
8536{
8537 while (x != end) {
8538 if (strcmp(name, x->b_cmd) != 0) {
8539 x++;
8540 continue;
8541 }
8542 debug_printf_exec("found builtin '%s'\n", name);
8543 return x;
8544 }
8545 return NULL;
8546}
8547static const struct built_in_command *find_builtin1(const char *name)
8548{
8549 return find_builtin_helper(name, bltins1, &bltins1[ARRAY_SIZE(bltins1)]);
8550}
8551static const struct built_in_command *find_builtin(const char *name)
8552{
8553 const struct built_in_command *x = find_builtin1(name);
8554 if (x)
8555 return x;
8556 return find_builtin_helper(name, bltins2, &bltins2[ARRAY_SIZE(bltins2)]);
8557}
8558
8559#if ENABLE_HUSH_JOB && EDITING_HAS_get_exe_name
8560static const char * FAST_FUNC get_builtin_name(int i)
8561{
8562 if (/*i >= 0 && */ i < ARRAY_SIZE(bltins1)) {
8563 return bltins1[i].b_cmd;
8564 }
8565 i -= ARRAY_SIZE(bltins1);
8566 if (i < ARRAY_SIZE(bltins2)) {
8567 return bltins2[i].b_cmd;
8568 }
8569 return NULL;
8570}
8571#endif
Francis Laniel8197f012023-12-22 22:02:28 +01008572#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01008573
Francis Laniel8197f012023-12-22 22:02:28 +01008574#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01008575static void remove_nested_vars(void)
8576{
8577 struct variable *cur;
8578 struct variable **cur_pp;
8579
8580 cur_pp = &G.top_var;
8581 while ((cur = *cur_pp) != NULL) {
8582 if (cur->var_nest_level <= G.var_nest_level) {
8583 cur_pp = &cur->next;
8584 continue;
8585 }
8586 /* Unexport */
8587 if (cur->flg_export) {
8588 debug_printf_env("unexporting nested '%s'/%u\n", cur->varstr, cur->var_nest_level);
8589 bb_unsetenv(cur->varstr);
8590 }
8591 /* Remove from global list */
8592 *cur_pp = cur->next;
8593 /* Free */
8594 if (!cur->max_len) {
8595 debug_printf_env("freeing nested '%s'/%u\n", cur->varstr, cur->var_nest_level);
8596 free(cur->varstr);
8597 }
8598 free(cur);
8599 }
8600}
8601
8602static void enter_var_nest_level(void)
8603{
8604 G.var_nest_level++;
8605 debug_printf_env("var_nest_level++ %u\n", G.var_nest_level);
8606
8607 /* Try: f() { echo -n .; f; }; f
8608 * struct variable::var_nest_level is uint16_t,
8609 * thus limiting recursion to < 2^16.
8610 * In any case, with 8 Mbyte stack SEGV happens
8611 * not too long after 2^16 recursions anyway.
8612 */
8613 if (G.var_nest_level > 0xff00)
8614 bb_error_msg_and_die("fatal recursion (depth %u)", G.var_nest_level);
8615}
8616
8617static void leave_var_nest_level(void)
8618{
8619 G.var_nest_level--;
8620 debug_printf_env("var_nest_level-- %u\n", G.var_nest_level);
8621 if (HUSH_DEBUG && (int)G.var_nest_level < 0)
8622 bb_simple_error_msg_and_die("BUG: nesting underflow");
8623
8624 remove_nested_vars();
8625}
Francis Laniel8197f012023-12-22 22:02:28 +01008626#endif /* __U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01008627
8628#if ENABLE_HUSH_FUNCTIONS
8629static struct function **find_function_slot(const char *name)
8630{
8631 struct function *funcp;
8632 struct function **funcpp = &G.top_func;
8633
8634 while ((funcp = *funcpp) != NULL) {
8635 if (strcmp(name, funcp->name) == 0) {
8636 debug_printf_exec("found function '%s'\n", name);
8637 break;
8638 }
8639 funcpp = &funcp->next;
8640 }
8641 return funcpp;
8642}
8643
8644static ALWAYS_INLINE const struct function *find_function(const char *name)
8645{
8646 const struct function *funcp = *find_function_slot(name);
8647 return funcp;
8648}
8649
8650/* Note: takes ownership on name ptr */
8651static struct function *new_function(char *name)
8652{
8653 struct function **funcpp = find_function_slot(name);
8654 struct function *funcp = *funcpp;
8655
8656 if (funcp != NULL) {
8657 struct command *cmd = funcp->parent_cmd;
8658 debug_printf_exec("func %p parent_cmd %p\n", funcp, cmd);
8659 if (!cmd) {
8660 debug_printf_exec("freeing & replacing function '%s'\n", funcp->name);
8661 free(funcp->name);
8662 /* Note: if !funcp->body, do not free body_as_string!
8663 * This is a special case of "-F name body" function:
8664 * body_as_string was not malloced! */
8665 if (funcp->body) {
8666 free_pipe_list(funcp->body);
8667# if !BB_MMU
8668 free(funcp->body_as_string);
8669# endif
8670 }
8671 } else {
8672 debug_printf_exec("reinserting in tree & replacing function '%s'\n", funcp->name);
8673 cmd->argv[0] = funcp->name;
8674 cmd->group = funcp->body;
8675# if !BB_MMU
8676 cmd->group_as_string = funcp->body_as_string;
8677# endif
8678 }
8679 } else {
8680 debug_printf_exec("remembering new function '%s'\n", name);
8681 funcp = *funcpp = xzalloc(sizeof(*funcp));
8682 /*funcp->next = NULL;*/
8683 }
8684
8685 funcp->name = name;
8686 return funcp;
8687}
8688
8689# if ENABLE_HUSH_UNSET
8690static void unset_func(const char *name)
8691{
8692 struct function **funcpp = find_function_slot(name);
8693 struct function *funcp = *funcpp;
8694
8695 if (funcp != NULL) {
8696 debug_printf_exec("freeing function '%s'\n", funcp->name);
8697 *funcpp = funcp->next;
8698 /* funcp is unlinked now, deleting it.
8699 * Note: if !funcp->body, the function was created by
8700 * "-F name body", do not free ->body_as_string
8701 * and ->name as they were not malloced. */
8702 if (funcp->body) {
8703 free_pipe_list(funcp->body);
8704 free(funcp->name);
8705# if !BB_MMU
8706 free(funcp->body_as_string);
8707# endif
8708 }
8709 free(funcp);
8710 }
8711}
8712# endif
8713
8714# if BB_MMU
8715#define exec_function(to_free, funcp, argv) \
8716 exec_function(funcp, argv)
8717# endif
8718static void exec_function(char ***to_free,
8719 const struct function *funcp,
8720 char **argv) NORETURN;
8721static void exec_function(char ***to_free,
8722 const struct function *funcp,
8723 char **argv)
8724{
8725# if BB_MMU
8726 int n;
8727
8728 argv[0] = G.global_argv[0];
8729 G.global_argv = argv;
8730 G.global_argc = n = 1 + string_array_len(argv + 1);
8731
8732// Example when we are here: "cmd | func"
8733// func will run with saved-redirect fds open.
8734// $ f() { echo /proc/self/fd/*; }
8735// $ true | f
8736// /proc/self/fd/0 /proc/self/fd/1 /proc/self/fd/2 /proc/self/fd/255 /proc/self/fd/3
8737// stdio^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ G_interactive_fd^ DIR fd for glob
8738// Same in script:
8739// $ . ./SCRIPT
8740// /proc/self/fd/0 /proc/self/fd/1 /proc/self/fd/2 /proc/self/fd/255 /proc/self/fd/3 /proc/self/fd/4
8741// stdio^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ G_interactive_fd^ opened ./SCRIPT DIR fd for glob
8742// They are CLOEXEC so external programs won't see them, but
8743// for "more correctness" we might want to close those extra fds here:
8744//? close_saved_fds_and_FILE_fds();
8745
8746 /* "we are in a function, ok to use return" */
8747 G_flag_return_in_progress = -1;
8748 enter_var_nest_level();
8749 IF_HUSH_LOCAL(G.func_nest_level++;)
8750
8751 /* On MMU, funcp->body is always non-NULL */
8752 n = run_list(funcp->body);
8753 _exit(n);
8754# else
8755//? close_saved_fds_and_FILE_fds();
8756
8757//TODO: check whether "true | func_with_return" works
8758
8759 re_execute_shell(to_free,
8760 funcp->body_as_string,
8761 G.global_argv[0],
8762 argv + 1,
8763 NULL);
8764# endif
8765}
8766
8767static int run_function(const struct function *funcp, char **argv)
8768{
8769 int rc;
8770 save_arg_t sv;
8771 smallint sv_flg;
8772
8773 save_and_replace_G_args(&sv, argv);
8774
8775 /* "We are in function, ok to use return" */
8776 sv_flg = G_flag_return_in_progress;
8777 G_flag_return_in_progress = -1;
8778
8779 /* Make "local" variables properly shadow previous ones */
8780 IF_HUSH_LOCAL(enter_var_nest_level();)
8781 IF_HUSH_LOCAL(G.func_nest_level++;)
8782
8783 /* On MMU, funcp->body is always non-NULL */
8784# if !BB_MMU
8785 if (!funcp->body) {
8786 /* Function defined by -F */
8787 parse_and_run_string(funcp->body_as_string);
8788 rc = G.last_exitcode;
8789 } else
8790# endif
8791 {
8792 rc = run_list(funcp->body);
8793 }
8794
8795 IF_HUSH_LOCAL(G.func_nest_level--;)
8796 IF_HUSH_LOCAL(leave_var_nest_level();)
8797
8798 G_flag_return_in_progress = sv_flg;
8799# if ENABLE_HUSH_TRAP
8800 debug_printf_exec("G.return_exitcode=-1\n");
8801 G.return_exitcode = -1; /* invalidate stashed return value */
8802# endif
8803
8804 restore_G_args(&sv, argv);
8805
8806 return rc;
8807}
8808#endif /* ENABLE_HUSH_FUNCTIONS */
8809
8810
Francis Laniel8197f012023-12-22 22:02:28 +01008811#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01008812#if BB_MMU
8813#define exec_builtin(to_free, x, argv) \
8814 exec_builtin(x, argv)
8815#else
8816#define exec_builtin(to_free, x, argv) \
8817 exec_builtin(to_free, argv)
8818#endif
8819static void exec_builtin(char ***to_free,
8820 const struct built_in_command *x,
8821 char **argv) NORETURN;
8822static void exec_builtin(char ***to_free,
8823 const struct built_in_command *x,
8824 char **argv)
8825{
8826#if BB_MMU
8827 int rcode;
8828//? close_saved_fds_and_FILE_fds();
8829 rcode = x->b_function(argv);
8830 fflush_all();
8831 _exit(rcode);
8832#else
8833 fflush_all();
8834 /* On NOMMU, we must never block!
8835 * Example: { sleep 99 | read line; } & echo Ok
8836 */
8837 re_execute_shell(to_free,
8838 argv[0],
8839 G.global_argv[0],
8840 G.global_argv + 1,
8841 argv);
8842#endif
8843}
Francis Laniel8197f012023-12-22 22:02:28 +01008844#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01008845
8846
Francis Laniel8197f012023-12-22 22:02:28 +01008847#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01008848static void execvp_or_die(char **argv) NORETURN;
8849static void execvp_or_die(char **argv)
8850{
8851 int e;
8852 debug_printf_exec("execing '%s'\n", argv[0]);
8853 /* Don't propagate SIG_IGN to the child */
8854 if (SPECIAL_JOBSTOP_SIGS != 0)
8855 switch_off_special_sigs(G.special_sig_mask & SPECIAL_JOBSTOP_SIGS);
8856 execvp(argv[0], argv);
8857 e = 2;
8858 if (errno == EACCES) e = 126;
8859 if (errno == ENOENT) e = 127;
8860 bb_perror_msg("can't execute '%s'", argv[0]);
8861 _exit(e);
8862}
8863
8864#if ENABLE_HUSH_MODE_X
8865static void x_mode_print_optionally_squoted(const char *str)
8866{
8867 unsigned len;
8868 const char *cp;
8869
8870 cp = str;
8871
8872 /* the set of chars which-cause-string-to-be-squoted mimics bash */
8873 /* test a char with: bash -c 'set -x; echo "CH"' */
8874 if (str[strcspn(str, "\\\"'`$(){}[]<>;#&|~*?!^"
8875 " " "\001\002\003\004\005\006\007"
8876 "\010\011\012\013\014\015\016\017"
8877 "\020\021\022\023\024\025\026\027"
8878 "\030\031\032\033\034\035\036\037"
8879 )
8880 ] == '\0'
8881 ) {
8882 /* string has no special chars */
8883 x_mode_addstr(str);
8884 return;
8885 }
8886
8887 cp = str;
8888 for (;;) {
8889 /* print '....' up to EOL or first squote */
8890 len = (int)(strchrnul(cp, '\'') - cp);
8891 if (len != 0) {
8892 x_mode_addchr('\'');
8893 x_mode_addblock(cp, len);
8894 x_mode_addchr('\'');
8895 cp += len;
8896 }
8897 if (*cp == '\0')
8898 break;
8899 /* string contains squote(s), print them as \' */
8900 x_mode_addchr('\\');
8901 x_mode_addchr('\'');
8902 cp++;
8903 }
8904}
8905static void dump_cmd_in_x_mode(char **argv)
8906{
8907 if (G_x_mode && argv) {
8908 unsigned n;
8909
8910 /* "+[+++...][ cmd...]\n\0" */
8911 x_mode_prefix();
8912 n = 0;
8913 while (argv[n]) {
8914 x_mode_addchr(' ');
8915 if (argv[n][0] == '\0') {
8916 x_mode_addchr('\'');
8917 x_mode_addchr('\'');
8918 } else {
8919 x_mode_print_optionally_squoted(argv[n]);
8920 }
8921 n++;
8922 }
8923 x_mode_flush();
8924 }
8925}
8926#else
8927# define dump_cmd_in_x_mode(argv) ((void)0)
8928#endif
Francis Laniel8197f012023-12-22 22:02:28 +01008929#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01008930
Francis Laniel8197f012023-12-22 22:02:28 +01008931#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01008932#if ENABLE_HUSH_COMMAND
8933static void if_command_vV_print_and_exit(char opt_vV, char *cmd, const char *explanation)
8934{
8935 char *to_free;
8936
8937 if (!opt_vV)
8938 return;
8939
8940 to_free = NULL;
8941 if (!explanation) {
8942 char *path = getenv("PATH");
8943 explanation = to_free = find_executable(cmd, &path); /* path == NULL is ok */
8944 if (!explanation)
8945 _exit(1); /* PROG was not found */
8946 if (opt_vV != 'V')
8947 cmd = to_free; /* -v PROG prints "/path/to/PROG" */
8948 }
8949 printf((opt_vV == 'V') ? "%s is %s\n" : "%s\n", cmd, explanation);
8950 free(to_free);
8951 fflush_all();
8952 _exit(0);
8953}
8954#else
8955# define if_command_vV_print_and_exit(a,b,c) ((void)0)
8956#endif
Francis Laniel8197f012023-12-22 22:02:28 +01008957#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01008958
8959#if BB_MMU
8960#define pseudo_exec_argv(nommu_save, argv, assignment_cnt, argv_expanded) \
8961 pseudo_exec_argv(argv, assignment_cnt, argv_expanded)
8962#define pseudo_exec(nommu_save, command, argv_expanded) \
8963 pseudo_exec(command, argv_expanded)
8964#endif
8965
Francis Laniel8197f012023-12-22 22:02:28 +01008966#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01008967/* Called after [v]fork() in run_pipe, or from builtin_exec.
8968 * Never returns.
8969 * Don't exit() here. If you don't exec, use _exit instead.
8970 * The at_exit handlers apparently confuse the calling process,
8971 * in particular stdin handling. Not sure why? -- because of vfork! (vda)
8972 */
8973static void pseudo_exec_argv(nommu_save_t *nommu_save,
8974 char **argv, int assignment_cnt,
8975 char **argv_expanded) NORETURN;
8976static NOINLINE void pseudo_exec_argv(nommu_save_t *nommu_save,
8977 char **argv, int assignment_cnt,
8978 char **argv_expanded)
8979{
8980 const struct built_in_command *x;
8981 struct variable **sv_shadowed;
8982 char **new_env;
8983 IF_HUSH_COMMAND(char opt_vV = 0;)
8984 IF_HUSH_FUNCTIONS(const struct function *funcp;)
8985
8986 new_env = expand_assignments(argv, assignment_cnt);
8987 dump_cmd_in_x_mode(new_env);
8988
8989 if (!argv[assignment_cnt]) {
8990 /* Case when we are here: ... | var=val | ...
8991 * (note that we do not exit early, i.e., do not optimize out
8992 * expand_assignments(): think about ... | var=`sleep 1` | ...
8993 */
8994 free_strings(new_env);
8995 _exit(EXIT_SUCCESS);
8996 }
8997
8998 sv_shadowed = G.shadowed_vars_pp;
8999#if BB_MMU
9000 G.shadowed_vars_pp = NULL; /* "don't save, free them instead" */
9001#else
9002 G.shadowed_vars_pp = &nommu_save->old_vars;
9003 G.var_nest_level++;
9004#endif
9005 set_vars_and_save_old(new_env);
9006 G.shadowed_vars_pp = sv_shadowed;
9007
9008 if (argv_expanded) {
9009 argv = argv_expanded;
9010 } else {
9011 argv = expand_strvec_to_strvec(argv + assignment_cnt);
9012#if !BB_MMU
9013 nommu_save->argv = argv;
9014#endif
9015 }
9016 dump_cmd_in_x_mode(argv);
9017
9018#if ENABLE_FEATURE_SH_STANDALONE || BB_MMU
9019 if (strchr(argv[0], '/') != NULL)
9020 goto skip;
9021#endif
9022
9023#if ENABLE_HUSH_FUNCTIONS
9024 /* Check if the command matches any functions (this goes before bltins) */
9025 funcp = find_function(argv[0]);
9026 if (funcp)
9027 exec_function(&nommu_save->argv_from_re_execing, funcp, argv);
9028#endif
9029
9030#if ENABLE_HUSH_COMMAND
9031 /* "command BAR": run BAR without looking it up among functions
9032 * "command -v BAR": print "BAR" or "/path/to/BAR"; or exit 1
9033 * "command -V BAR": print "BAR is {a function,a shell builtin,/path/to/BAR}"
9034 */
9035 while (strcmp(argv[0], "command") == 0 && argv[1]) {
9036 char *p;
9037
9038 argv++;
9039 p = *argv;
9040 if (p[0] != '-' || !p[1])
9041 continue; /* bash allows "command command command [-OPT] BAR" */
9042
9043 for (;;) {
9044 p++;
9045 switch (*p) {
9046 case '\0':
9047 argv++;
9048 p = *argv;
9049 if (p[0] != '-' || !p[1])
9050 goto after_opts;
9051 continue; /* next arg is also -opts, process it too */
9052 case 'v':
9053 case 'V':
9054 opt_vV = *p;
9055 continue;
9056 default:
9057 bb_error_msg_and_die("%s: %s: invalid option", "command", argv[0]);
9058 }
9059 }
9060 }
9061 after_opts:
9062# if ENABLE_HUSH_FUNCTIONS
9063 if (opt_vV && find_function(argv[0]))
9064 if_command_vV_print_and_exit(opt_vV, argv[0], "a function");
9065# endif
9066#endif
9067
9068 /* Check if the command matches any of the builtins.
9069 * Depending on context, this might be redundant. But it's
9070 * easier to waste a few CPU cycles than it is to figure out
9071 * if this is one of those cases.
9072 */
9073 /* Why "BB_MMU ? :" difference in logic? -
9074 * On NOMMU, it is more expensive to re-execute shell
9075 * just in order to run echo or test builtin.
9076 * It's better to skip it here and run corresponding
9077 * non-builtin later. */
9078 x = BB_MMU ? find_builtin(argv[0]) : find_builtin1(argv[0]);
9079 if (x) {
9080 if_command_vV_print_and_exit(opt_vV, argv[0], "a shell builtin");
9081 exec_builtin(&nommu_save->argv_from_re_execing, x, argv);
9082 }
9083
9084#if ENABLE_FEATURE_SH_STANDALONE
9085 /* Check if the command matches any busybox applets */
9086 {
9087 int a = find_applet_by_name(argv[0]);
9088 if (a >= 0) {
9089 if_command_vV_print_and_exit(opt_vV, argv[0], "an applet");
9090# if BB_MMU /* see above why on NOMMU it is not allowed */
9091 if (APPLET_IS_NOEXEC(a)) {
9092 /* Do not leak open fds from opened script files etc.
9093 * Testcase: interactive "ls -l /proc/self/fd"
9094 * should not show tty fd open.
9095 */
9096 close_saved_fds_and_FILE_fds();
9097//FIXME: should also close saved redir fds
9098//This casuses test failures in
9099//redir_children_should_not_see_saved_fd_2.tests
9100//redir_children_should_not_see_saved_fd_3.tests
9101//if you replace "busybox find" with just "find" in them
9102 /* Without this, "rm -i FILE" can't be ^C'ed: */
9103 switch_off_special_sigs(G.special_sig_mask);
9104 debug_printf_exec("running applet '%s'\n", argv[0]);
9105 run_noexec_applet_and_exit(a, argv[0], argv);
9106 }
9107# endif
9108 /* Re-exec ourselves */
9109 debug_printf_exec("re-execing applet '%s'\n", argv[0]);
9110 /* Don't propagate SIG_IGN to the child */
9111 if (SPECIAL_JOBSTOP_SIGS != 0)
9112 switch_off_special_sigs(G.special_sig_mask & SPECIAL_JOBSTOP_SIGS);
9113 execv(bb_busybox_exec_path, argv);
9114 /* If they called chroot or otherwise made the binary no longer
9115 * executable, fall through */
9116 }
9117 }
9118#endif
9119
9120#if ENABLE_FEATURE_SH_STANDALONE || BB_MMU
9121 skip:
9122#endif
9123 if_command_vV_print_and_exit(opt_vV, argv[0], NULL);
9124 execvp_or_die(argv);
9125}
9126
9127/* Called after [v]fork() in run_pipe
9128 */
9129static void pseudo_exec(nommu_save_t *nommu_save,
9130 struct command *command,
9131 char **argv_expanded) NORETURN;
9132static void pseudo_exec(nommu_save_t *nommu_save,
9133 struct command *command,
9134 char **argv_expanded)
9135{
9136#if ENABLE_HUSH_FUNCTIONS
9137 if (command->cmd_type == CMD_FUNCDEF) {
9138 /* Ignore funcdefs in pipes:
9139 * true | f() { cmd }
9140 */
9141 _exit(0);
9142 }
9143#endif
9144
9145 if (command->argv) {
9146 pseudo_exec_argv(nommu_save, command->argv,
9147 command->assignment_cnt, argv_expanded);
9148 }
9149
9150 if (command->group) {
9151 /* Cases when we are here:
9152 * ( list )
9153 * { list } &
9154 * ... | ( list ) | ...
9155 * ... | { list } | ...
9156 */
9157#if BB_MMU
9158 int rcode;
9159 debug_printf_exec("pseudo_exec: run_list\n");
9160 reset_traps_to_defaults();
9161 rcode = run_list(command->group);
9162 /* OK to leak memory by not calling free_pipe_list,
9163 * since this process is about to exit */
9164 _exit(rcode);
9165#else
9166 re_execute_shell(&nommu_save->argv_from_re_execing,
9167 command->group_as_string,
9168 G.global_argv[0],
9169 G.global_argv + 1,
9170 NULL);
9171#endif
9172 }
9173
9174 /* Case when we are here: ... | >file */
9175 debug_printf_exec("pseudo_exec'ed null command\n");
9176 _exit(EXIT_SUCCESS);
9177}
9178
9179#if ENABLE_HUSH_JOB
9180static const char *get_cmdtext(struct pipe *pi)
9181{
9182 char **argv;
9183 char *p;
9184 int len;
9185
9186 /* This is subtle. ->cmdtext is created only on first backgrounding.
9187 * (Think "cat, <ctrl-z>, fg, <ctrl-z>, fg, <ctrl-z>...." here...)
9188 * On subsequent bg argv is trashed, but we won't use it */
9189 if (pi->cmdtext)
9190 return pi->cmdtext;
9191
9192 argv = pi->cmds[0].argv;
9193 if (!argv) {
9194 pi->cmdtext = xzalloc(1);
9195 return pi->cmdtext;
9196 }
9197 len = 0;
9198 do {
9199 len += strlen(*argv) + 1;
9200 } while (*++argv);
9201 p = xmalloc(len);
9202 pi->cmdtext = p;
9203 argv = pi->cmds[0].argv;
9204 do {
9205 p = stpcpy(p, *argv);
9206 *p++ = ' ';
9207 } while (*++argv);
9208 p[-1] = '\0';
9209 return pi->cmdtext;
9210}
9211
9212static void remove_job_from_table(struct pipe *pi)
9213{
9214 struct pipe *prev_pipe;
9215
9216 if (pi == G.job_list) {
9217 G.job_list = pi->next;
9218 } else {
9219 prev_pipe = G.job_list;
9220 while (prev_pipe->next != pi)
9221 prev_pipe = prev_pipe->next;
9222 prev_pipe->next = pi->next;
9223 }
9224 G.last_jobid = 0;
9225 if (G.job_list)
9226 G.last_jobid = G.job_list->jobid;
9227}
9228
9229static void delete_finished_job(struct pipe *pi)
9230{
9231 remove_job_from_table(pi);
9232 free_pipe(pi);
9233}
9234
9235static void clean_up_last_dead_job(void)
9236{
9237 if (G.job_list && !G.job_list->alive_cmds)
9238 delete_finished_job(G.job_list);
9239}
9240
9241static void insert_job_into_table(struct pipe *pi)
9242{
9243 struct pipe *job, **jobp;
9244 int i;
9245
9246 clean_up_last_dead_job();
9247
9248 /* Find the end of the list, and find next job ID to use */
9249 i = 0;
9250 jobp = &G.job_list;
9251 while ((job = *jobp) != NULL) {
9252 if (job->jobid > i)
9253 i = job->jobid;
9254 jobp = &job->next;
9255 }
9256 pi->jobid = i + 1;
9257
9258 /* Create a new job struct at the end */
9259 job = *jobp = xmemdup(pi, sizeof(*pi));
9260 job->next = NULL;
9261 job->cmds = xzalloc(sizeof(pi->cmds[0]) * pi->num_cmds);
9262 /* Cannot copy entire pi->cmds[] vector! This causes double frees */
9263 for (i = 0; i < pi->num_cmds; i++) {
9264 job->cmds[i].pid = pi->cmds[i].pid;
9265 /* all other fields are not used and stay zero */
9266 }
9267 job->cmdtext = xstrdup(get_cmdtext(pi));
9268
9269 if (G_interactive_fd)
9270 printf("[%u] %u %s\n", job->jobid, (unsigned)job->cmds[0].pid, job->cmdtext);
9271 G.last_jobid = job->jobid;
9272}
9273#endif /* JOB */
9274
9275static int job_exited_or_stopped(struct pipe *pi)
9276{
9277 int rcode, i;
9278
9279 if (pi->alive_cmds != pi->stopped_cmds)
9280 return -1;
9281
9282 /* All processes in fg pipe have exited or stopped */
9283 rcode = 0;
9284 i = pi->num_cmds;
9285 while (--i >= 0) {
9286 rcode = pi->cmds[i].cmd_exitcode;
9287 /* usually last process gives overall exitstatus,
9288 * but with "set -o pipefail", last *failed* process does */
9289 if (G.o_opt[OPT_O_PIPEFAIL] == 0 || rcode != 0)
9290 break;
9291 }
9292 IF_HAS_KEYWORDS(if (pi->pi_inverted) rcode = !rcode;)
9293 return rcode;
9294}
9295
9296static int process_wait_result(struct pipe *fg_pipe, pid_t childpid, int status)
9297{
9298#if ENABLE_HUSH_JOB
9299 struct pipe *pi;
9300#endif
9301 int i, dead;
9302
9303 dead = WIFEXITED(status) || WIFSIGNALED(status);
9304
9305#if DEBUG_JOBS
9306 if (WIFSTOPPED(status))
9307 debug_printf_jobs("pid %d stopped by sig %d (exitcode %d)\n",
9308 childpid, WSTOPSIG(status), WEXITSTATUS(status));
9309 if (WIFSIGNALED(status))
9310 debug_printf_jobs("pid %d killed by sig %d (exitcode %d)\n",
9311 childpid, WTERMSIG(status), WEXITSTATUS(status));
9312 if (WIFEXITED(status))
9313 debug_printf_jobs("pid %d exited, exitcode %d\n",
9314 childpid, WEXITSTATUS(status));
9315#endif
9316 /* Were we asked to wait for a fg pipe? */
9317 if (fg_pipe) {
9318 i = fg_pipe->num_cmds;
9319
9320 while (--i >= 0) {
9321 int rcode;
9322
9323 debug_printf_jobs("check pid %d\n", fg_pipe->cmds[i].pid);
9324 if (fg_pipe->cmds[i].pid != childpid)
9325 continue;
9326 if (dead) {
9327 int ex;
9328 fg_pipe->cmds[i].pid = 0;
9329 fg_pipe->alive_cmds--;
9330 ex = WEXITSTATUS(status);
9331 /* bash prints killer signal's name for *last*
9332 * process in pipe (prints just newline for SIGINT/SIGPIPE).
9333 * Mimic this. Example: "sleep 5" + (^\ or kill -QUIT)
9334 */
9335 if (WIFSIGNALED(status)) {
9336 int sig = WTERMSIG(status);
9337#if ENABLE_HUSH_JOB
9338 if (G.run_list_level == 1
9339 /* ^^^^^ Do not print in nested contexts, example:
9340 * echo `sleep 1; sh -c 'kill -9 $$'` - prints "137", NOT "Killed 137"
9341 */
9342 && i == fg_pipe->num_cmds-1
9343 ) {
9344 /* strsignal() is for bash compat. ~600 bloat versus bbox's get_signame() */
9345 puts(sig == SIGINT || sig == SIGPIPE ? "" : strsignal(sig));
9346 }
9347#endif
9348 /* TODO: if (WCOREDUMP(status)) + " (core dumped)"; */
9349 /* MIPS has 128 sigs (1..128), if sig==128,
9350 * 128 + sig would result in exitcode 256 -> 0!
9351 */
9352 ex = 128 | sig;
9353 }
9354 fg_pipe->cmds[i].cmd_exitcode = ex;
9355 } else {
9356 fg_pipe->stopped_cmds++;
9357 }
9358 debug_printf_jobs("fg_pipe: alive_cmds %d stopped_cmds %d\n",
9359 fg_pipe->alive_cmds, fg_pipe->stopped_cmds);
9360 rcode = job_exited_or_stopped(fg_pipe);
9361 if (rcode >= 0) {
9362/* Note: *non-interactive* bash does not continue if all processes in fg pipe
9363 * are stopped. Testcase: "cat | cat" in a script (not on command line!)
9364 * and "killall -STOP cat" */
9365 if (G_interactive_fd) {
9366#if ENABLE_HUSH_JOB
9367 if (fg_pipe->alive_cmds != 0)
9368 insert_job_into_table(fg_pipe);
9369#endif
9370 return rcode;
9371 }
9372 if (fg_pipe->alive_cmds == 0)
9373 return rcode;
9374 }
9375 /* There are still running processes in the fg_pipe */
9376 return -1;
9377 }
9378 /* It wasn't in fg_pipe, look for process in bg pipes */
9379 }
9380
9381#if ENABLE_HUSH_JOB
9382 /* We were asked to wait for bg or orphaned children */
9383 /* No need to remember exitcode in this case */
9384 for (pi = G.job_list; pi; pi = pi->next) {
9385 for (i = 0; i < pi->num_cmds; i++) {
9386 if (pi->cmds[i].pid == childpid)
9387 goto found_pi_and_prognum;
9388 }
9389 }
9390 /* Happens when shell is used as init process (init=/bin/sh) */
9391 debug_printf("checkjobs: pid %d was not in our list!\n", childpid);
9392 return -1; /* this wasn't a process from fg_pipe */
9393
9394 found_pi_and_prognum:
9395 if (dead) {
9396 /* child exited */
9397 int rcode = WEXITSTATUS(status);
9398 if (WIFSIGNALED(status))
9399 /* NB: not 128 + sig, MIPS has sig 128 */
9400 rcode = 128 | WTERMSIG(status);
9401 pi->cmds[i].cmd_exitcode = rcode;
9402 if (G.last_bg_pid == pi->cmds[i].pid)
9403 G.last_bg_pid_exitcode = rcode;
9404 pi->cmds[i].pid = 0;
9405 pi->alive_cmds--;
9406 if (!pi->alive_cmds) {
9407# if ENABLE_HUSH_BASH_COMPAT
9408 G.dead_job_exitcode = job_exited_or_stopped(pi);
9409# endif
9410 if (G_interactive_fd) {
9411 printf(JOB_STATUS_FORMAT, pi->jobid,
9412 "Done", pi->cmdtext);
9413 delete_finished_job(pi);
9414 } else {
9415/*
9416 * bash deletes finished jobs from job table only in interactive mode,
9417 * after "jobs" cmd, or if pid of a new process matches one of the old ones
9418 * (see cleanup_dead_jobs(), delete_old_job(), J_NOTIFIED in bash source).
9419 * Testcase script: "(exit 3) & sleep 1; wait %1; echo $?" prints 3 in bash.
9420 * We only retain one "dead" job, if it's the single job on the list.
9421 * This covers most of real-world scenarios where this is useful.
9422 */
9423 if (pi != G.job_list)
9424 delete_finished_job(pi);
9425 }
9426 }
9427 } else {
9428 /* child stopped */
9429 pi->stopped_cmds++;
9430 }
9431#endif
9432 return -1; /* this wasn't a process from fg_pipe */
9433}
9434
9435/* Check to see if any processes have exited -- if they have,
9436 * figure out why and see if a job has completed.
9437 *
9438 * If non-NULL fg_pipe: wait for its completion or stop.
9439 * Return its exitcode or zero if stopped.
9440 *
9441 * Alternatively (fg_pipe == NULL, waitfor_pid != 0):
9442 * waitpid(WNOHANG), if waitfor_pid exits or stops, return exitcode+1,
9443 * else return <0 if waitpid errors out (e.g. ECHILD: nothing to wait for)
9444 * or 0 if no children changed status.
9445 *
9446 * Alternatively (fg_pipe == NULL, waitfor_pid == 0),
9447 * return <0 if waitpid errors out (e.g. ECHILD: nothing to wait for)
9448 * or 0 if no children changed status.
9449 */
9450static int checkjobs(struct pipe *fg_pipe, pid_t waitfor_pid)
9451{
9452 int attributes;
9453 int status;
9454 int rcode = 0;
9455
9456 debug_printf_jobs("checkjobs %p\n", fg_pipe);
9457
9458 attributes = WUNTRACED;
9459 if (fg_pipe == NULL)
9460 attributes |= WNOHANG;
9461
9462 errno = 0;
9463#if ENABLE_HUSH_FAST
9464 if (G.handled_SIGCHLD == G.count_SIGCHLD) {
9465//bb_error_msg("[%d] checkjobs: G.count_SIGCHLD:%d G.handled_SIGCHLD:%d children?:%d fg_pipe:%p",
9466//getpid(), G.count_SIGCHLD, G.handled_SIGCHLD, G.we_have_children, fg_pipe);
9467 /* There was neither fork nor SIGCHLD since last waitpid */
9468 /* Avoid doing waitpid syscall if possible */
9469 if (!G.we_have_children) {
9470 errno = ECHILD;
9471 return -1;
9472 }
9473 if (fg_pipe == NULL) { /* is WNOHANG set? */
9474 /* We have children, but they did not exit
9475 * or stop yet (we saw no SIGCHLD) */
9476 return 0;
9477 }
9478 /* else: !WNOHANG, waitpid will block, can't short-circuit */
9479 }
9480#endif
9481
9482/* Do we do this right?
9483 * bash-3.00# sleep 20 | false
9484 * <ctrl-Z pressed>
9485 * [3]+ Stopped sleep 20 | false
9486 * bash-3.00# echo $?
9487 * 1 <========== bg pipe is not fully done, but exitcode is already known!
9488 * [hush 1.14.0: yes we do it right]
9489 */
9490 while (1) {
9491 pid_t childpid;
9492#if ENABLE_HUSH_FAST
9493 int i;
9494 i = G.count_SIGCHLD;
9495#endif
9496 childpid = waitpid(-1, &status, attributes);
9497 if (childpid <= 0) {
9498 if (childpid && errno != ECHILD)
9499 bb_simple_perror_msg("waitpid");
9500#if ENABLE_HUSH_FAST
9501 else { /* Until next SIGCHLD, waitpid's are useless */
9502 G.we_have_children = (childpid == 0);
9503 G.handled_SIGCHLD = i;
9504//bb_error_msg("[%d] checkjobs: waitpid returned <= 0, G.count_SIGCHLD:%d G.handled_SIGCHLD:%d", getpid(), G.count_SIGCHLD, G.handled_SIGCHLD);
9505 }
9506#endif
9507 /* ECHILD (no children), or 0 (no change in children status) */
9508 rcode = childpid;
9509 break;
9510 }
9511 rcode = process_wait_result(fg_pipe, childpid, status);
9512 if (rcode >= 0) {
9513 /* fg_pipe exited or stopped */
9514 break;
9515 }
9516 if (childpid == waitfor_pid) { /* "wait PID" */
9517 debug_printf_exec("childpid==waitfor_pid:%d status:0x%08x\n", childpid, status);
9518 rcode = WEXITSTATUS(status);
9519 if (WIFSIGNALED(status))
9520 rcode = 128 | WTERMSIG(status);
9521 if (WIFSTOPPED(status))
9522 /* bash: "cmd & wait $!" and cmd stops: $? = 128 | stopsig */
9523 rcode = 128 | WSTOPSIG(status);
9524 rcode++;
9525 break; /* "wait PID" called us, give it exitcode+1 */
9526 }
9527#if ENABLE_HUSH_BASH_COMPAT
9528 if (-1 == waitfor_pid /* "wait -n" (wait for any one job) */
9529 && G.dead_job_exitcode >= 0 /* some job did finish */
9530 ) {
9531 debug_printf_exec("waitfor_pid:-1\n");
9532 rcode = G.dead_job_exitcode + 1;
9533 break;
9534 }
9535#endif
9536 /* This wasn't one of our processes, or */
9537 /* fg_pipe still has running processes, do waitpid again */
9538 } /* while (waitpid succeeds)... */
9539
9540 return rcode;
9541}
9542
9543#if ENABLE_HUSH_JOB
9544static int checkjobs_and_fg_shell(struct pipe *fg_pipe)
9545{
9546 pid_t p;
9547 int rcode = checkjobs(fg_pipe, 0 /*(no pid to wait for)*/);
9548 if (G_saved_tty_pgrp) {
9549 /* Job finished, move the shell to the foreground */
9550 p = getpgrp(); /* our process group id */
9551 debug_printf_jobs("fg'ing ourself: getpgrp()=%d\n", (int)p);
9552 tcsetpgrp(G_interactive_fd, p);
9553 }
9554 return rcode;
9555}
9556#endif
9557
9558/* Start all the jobs, but don't wait for anything to finish.
9559 * See checkjobs().
9560 *
9561 * Return code is normally -1, when the caller has to wait for children
9562 * to finish to determine the exit status of the pipe. If the pipe
9563 * is a simple builtin command, however, the action is done by the
9564 * time run_pipe returns, and the exit code is provided as the
9565 * return value.
9566 *
9567 * Returns -1 only if started some children. IOW: we have to
9568 * mask out retvals of builtins etc with 0xff!
9569 *
9570 * The only case when we do not need to [v]fork is when the pipe
9571 * is single, non-backgrounded, non-subshell command. Examples:
9572 * cmd ; ... { list } ; ...
9573 * cmd && ... { list } && ...
9574 * cmd || ... { list } || ...
9575 * If it is, then we can run cmd as a builtin, NOFORK,
9576 * or (if SH_STANDALONE) an applet, and we can run the { list }
9577 * with run_list. If it isn't one of these, we fork and exec cmd.
9578 *
9579 * Cases when we must fork:
9580 * non-single: cmd | cmd
9581 * backgrounded: cmd & { list } &
9582 * subshell: ( list ) [&]
9583 */
9584#if !ENABLE_HUSH_MODE_X
9585#define redirect_and_varexp_helper(command, sqp, argv_expanded) \
9586 redirect_and_varexp_helper(command, sqp)
9587#endif
9588static int redirect_and_varexp_helper(
9589 struct command *command,
9590 struct squirrel **sqp,
9591 char **argv_expanded)
9592{
9593 /* Assignments occur before redirects. Try:
9594 * a=`sleep 1` sleep 2 3>/qwe/rty
9595 */
9596
9597 char **new_env = expand_assignments(command->argv, command->assignment_cnt);
9598 dump_cmd_in_x_mode(new_env);
9599 dump_cmd_in_x_mode(argv_expanded);
9600 /* this takes ownership of new_env[i] elements, and frees new_env: */
9601 set_vars_and_save_old(new_env);
9602
9603 return setup_redirects(command, sqp);
9604}
Francis Laniel8197f012023-12-22 22:02:28 +01009605#endif /* !__U_BOOT__ */
9606
Francis Lanielb234f7e2023-12-22 22:02:27 +01009607static NOINLINE int run_pipe(struct pipe *pi)
9608{
9609 static const char *const null_ptr = NULL;
9610
9611 int cmd_no;
Francis Laniel8197f012023-12-22 22:02:28 +01009612#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01009613 int next_infd;
Francis Laniel8197f012023-12-22 22:02:28 +01009614#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01009615 struct command *command;
9616 char **argv_expanded;
9617 char **argv;
Francis Laniel8197f012023-12-22 22:02:28 +01009618#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01009619 struct squirrel *squirrel = NULL;
Francis Laniel8197f012023-12-22 22:02:28 +01009620#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01009621 int rcode;
9622
Francis Laniel8197f012023-12-22 22:02:28 +01009623#ifdef __U_BOOT__
9624 /*
9625 * Set rcode here to avoid returning a garbage value in the middle of
9626 * the function.
9627 * Also, if an error occurs, rcode value would be changed and last
9628 * return will signal the error.
9629 */
9630 rcode = 0;
9631#endif /* __U_BOOT__ */
9632
Francis Lanielb234f7e2023-12-22 22:02:27 +01009633 debug_printf_exec("run_pipe start: members:%d\n", pi->num_cmds);
9634 debug_enter();
9635
9636 /* Testcase: set -- q w e; (IFS='' echo "$*"; IFS=''; echo "$*"); echo "$*"
9637 * Result should be 3 lines: q w e, qwe, q w e
9638 */
9639 if (G.ifs_whitespace != G.ifs)
9640 free(G.ifs_whitespace);
9641 G.ifs = get_local_var_value("IFS");
9642 if (G.ifs) {
9643 char *p;
9644 G.ifs_whitespace = (char*)G.ifs;
9645 p = skip_whitespace(G.ifs);
9646 if (*p) {
9647 /* Not all $IFS is whitespace */
9648 char *d;
9649 int len = p - G.ifs;
9650 p = skip_non_whitespace(p);
9651 G.ifs_whitespace = xmalloc(len + strlen(p) + 1); /* can overestimate */
9652 d = mempcpy(G.ifs_whitespace, G.ifs, len);
9653 while (*p) {
9654 if (isspace(*p))
9655 *d++ = *p;
9656 p++;
9657 }
9658 *d = '\0';
9659 }
9660 } else {
9661 G.ifs = defifs;
9662 G.ifs_whitespace = (char*)G.ifs;
9663 }
9664
Francis Laniel8197f012023-12-22 22:02:28 +01009665#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01009666 IF_HUSH_JOB(pi->pgrp = -1;)
9667 pi->stopped_cmds = 0;
Francis Laniel8197f012023-12-22 22:02:28 +01009668#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01009669 command = &pi->cmds[0];
9670 argv_expanded = NULL;
9671
Francis Laniel8197f012023-12-22 22:02:28 +01009672#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01009673 if (pi->num_cmds != 1
9674 || pi->followup == PIPE_BG
9675 || command->cmd_type == CMD_SUBSHELL
9676 ) {
9677 goto must_fork;
9678 }
9679
9680 pi->alive_cmds = 1;
Francis Laniel8197f012023-12-22 22:02:28 +01009681#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01009682
9683 debug_printf_exec(": group:%p argv:'%s'\n",
9684 command->group, command->argv ? command->argv[0] : "NONE");
9685
9686 if (command->group) {
9687#if ENABLE_HUSH_FUNCTIONS
9688 if (command->cmd_type == CMD_FUNCDEF) {
9689 /* "executing" func () { list } */
9690 struct function *funcp;
9691
9692 funcp = new_function(command->argv[0]);
9693 /* funcp->name is already set to argv[0] */
9694 funcp->body = command->group;
9695# if !BB_MMU
9696 funcp->body_as_string = command->group_as_string;
9697 command->group_as_string = NULL;
9698# endif
9699 command->group = NULL;
9700 command->argv[0] = NULL;
9701 debug_printf_exec("cmd %p has child func at %p\n", command, funcp);
9702 funcp->parent_cmd = command;
9703 command->child_func = funcp;
9704
9705 debug_printf_exec("run_pipe: return EXIT_SUCCESS\n");
9706 debug_leave();
9707 return EXIT_SUCCESS;
9708 }
9709#endif
9710 /* { list } */
9711 debug_printf_exec("non-subshell group\n");
9712 rcode = 1; /* exitcode if redir failed */
Francis Laniel8197f012023-12-22 22:02:28 +01009713#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01009714 if (setup_redirects(command, &squirrel) == 0) {
9715 debug_printf_exec(": run_list\n");
9716//FIXME: we need to pass squirrel down into run_list()
9717//for SH_STANDALONE case, or else this construct:
9718// { find /proc/self/fd; true; } >FILE; cmd2
9719//has no way of closing saved fd#1 for "find",
9720//and in SH_STANDALONE mode, "find" is not execed,
9721//therefore CLOEXEC on saved fd does not help.
9722 rcode = run_list(command->group) & 0xff;
9723 }
9724 restore_redirects(squirrel);
9725 IF_HAS_KEYWORDS(if (pi->pi_inverted) rcode = !rcode;)
Francis Laniel8197f012023-12-22 22:02:28 +01009726#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01009727 debug_leave();
9728 debug_printf_exec("run_pipe: return %d\n", rcode);
9729 return rcode;
9730 }
9731
9732 argv = command->argv ? command->argv : (char **) &null_ptr;
9733 {
Francis Laniel8197f012023-12-22 22:02:28 +01009734#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01009735 const struct built_in_command *x;
9736 IF_HUSH_FUNCTIONS(const struct function *funcp;)
9737 IF_NOT_HUSH_FUNCTIONS(enum { funcp = 0 };)
9738 struct variable **sv_shadowed;
Francis Laniel8197f012023-12-22 22:02:28 +01009739#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01009740 struct variable *old_vars;
9741
9742#if ENABLE_HUSH_LINENO_VAR
9743 G.execute_lineno = command->lineno;
9744#endif
9745
9746 if (argv[command->assignment_cnt] == NULL) {
9747 /* Assignments, but no command.
9748 * Ensure redirects take effect (that is, create files).
9749 * Try "a=t >file"
9750 */
9751 unsigned i;
9752 G.expand_exitcode = 0;
9753 only_assignments:
Francis Laniel8197f012023-12-22 22:02:28 +01009754#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01009755 rcode = setup_redirects(command, &squirrel);
9756 restore_redirects(squirrel);
Francis Laniel8197f012023-12-22 22:02:28 +01009757#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01009758
9759 /* Set shell variables */
9760 i = 0;
9761 while (i < command->assignment_cnt) {
9762 char *p = expand_string_to_string(argv[i],
9763 EXP_FLAG_ESC_GLOB_CHARS,
9764 /*unbackslash:*/ 1
9765 );
9766#if ENABLE_HUSH_MODE_X
9767 if (G_x_mode) {
9768 char *eq;
9769 if (i == 0)
9770 x_mode_prefix();
9771 x_mode_addchr(' ');
9772 eq = strchrnul(p, '=');
9773 if (*eq) eq++;
9774 x_mode_addblock(p, (eq - p));
9775 x_mode_print_optionally_squoted(eq);
9776 x_mode_flush();
9777 }
9778#endif
9779 debug_printf_env("set shell var:'%s'->'%s'\n", *argv, p);
Francis Laniel8197f012023-12-22 22:02:28 +01009780#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01009781 if (set_local_var(p, /*flag:*/ 0)) {
Francis Laniel8197f012023-12-22 22:02:28 +01009782#else /* __U_BOOT__ */
9783 if (set_local_var_modern(p, /*flag:*/ 0)) {
9784#endif
Francis Lanielb234f7e2023-12-22 22:02:27 +01009785 /* assignment to readonly var / putenv error? */
9786 rcode = 1;
9787 }
9788 i++;
9789 }
9790 /* Redirect error sets $? to 1. Otherwise,
9791 * if evaluating assignment value set $?, retain it.
9792 * Else, clear $?:
9793 * false; q=`exit 2`; echo $? - should print 2
9794 * false; x=1; echo $? - should print 0
9795 * Because of the 2nd case, we can't just use G.last_exitcode.
9796 */
9797 if (rcode == 0)
9798 rcode = G.expand_exitcode;
9799 IF_HAS_KEYWORDS(if (pi->pi_inverted) rcode = !rcode;)
9800 debug_leave();
9801 debug_printf_exec("run_pipe: return %d\n", rcode);
9802 return rcode;
9803 }
9804
9805 /* Expand the rest into (possibly) many strings each */
9806#if defined(CMD_TEST2_SINGLEWORD_NOGLOB)
9807 if (command->cmd_type == CMD_TEST2_SINGLEWORD_NOGLOB)
9808 argv_expanded = expand_strvec_to_strvec_singleword_noglob(argv + command->assignment_cnt);
9809 else
9810#endif
9811#if defined(CMD_SINGLEWORD_NOGLOB)
9812 if (command->cmd_type == CMD_SINGLEWORD_NOGLOB)
9813 argv_expanded = expand_strvec_to_strvec_singleword_noglob(argv + command->assignment_cnt);
9814 else
9815#endif
9816 argv_expanded = expand_strvec_to_strvec(argv + command->assignment_cnt);
9817
9818 /* If someone gives us an empty string: `cmd with empty output` */
9819 if (!argv_expanded[0]) {
9820 free(argv_expanded);
9821 /* `false` still has to set exitcode 1 */
9822 G.expand_exitcode = G.last_exitcode;
9823 goto only_assignments;
9824 }
9825
9826 old_vars = NULL;
Francis Laniel8197f012023-12-22 22:02:28 +01009827#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01009828 sv_shadowed = G.shadowed_vars_pp;
9829
9830 /* Check if argv[0] matches any functions (this goes before bltins) */
9831 IF_HUSH_FUNCTIONS(funcp = find_function(argv_expanded[0]);)
9832 IF_HUSH_FUNCTIONS(x = NULL;)
9833 IF_HUSH_FUNCTIONS(if (!funcp))
9834 x = find_builtin(argv_expanded[0]);
9835 if (x || funcp) {
9836 if (x && x->b_function == builtin_exec && argv_expanded[1] == NULL) {
9837 debug_printf("exec with redirects only\n");
9838 /*
9839 * Variable assignments are executed, but then "forgotten":
9840 * a=`sleep 1;echo A` exec 3>&-; echo $a
9841 * sleeps, but prints nothing.
9842 */
9843 enter_var_nest_level();
9844 G.shadowed_vars_pp = &old_vars;
9845 rcode = redirect_and_varexp_helper(command,
9846 /*squirrel:*/ ERR_PTR,
9847 argv_expanded
9848 );
9849 G.shadowed_vars_pp = sv_shadowed;
9850 /* rcode=1 can be if redir file can't be opened */
9851
9852 goto clean_up_and_ret1;
9853 }
9854
9855 /* Bump var nesting, or this will leak exported $a:
9856 * a=b true; env | grep ^a=
9857 */
9858 enter_var_nest_level();
9859 /* Collect all variables "shadowed" by helper
9860 * (IOW: old vars overridden by "var1=val1 var2=val2 cmd..." syntax)
9861 * into old_vars list:
9862 */
9863 G.shadowed_vars_pp = &old_vars;
9864 rcode = redirect_and_varexp_helper(command, &squirrel, argv_expanded);
9865 if (rcode == 0) {
9866 if (!funcp) {
9867 /* Do not collect *to old_vars list* vars shadowed
9868 * by e.g. "local VAR" builtin (collect them
9869 * in the previously nested list instead):
9870 * don't want them to be restored immediately
9871 * after "local" completes.
9872 */
9873 G.shadowed_vars_pp = sv_shadowed;
9874
9875 debug_printf_exec(": builtin '%s' '%s'...\n",
9876 x->b_cmd, argv_expanded[1]);
9877 fflush_all();
9878 rcode = x->b_function(argv_expanded) & 0xff;
9879 fflush_all();
9880 }
9881#if ENABLE_HUSH_FUNCTIONS
9882 else {
9883 debug_printf_exec(": function '%s' '%s'...\n",
9884 funcp->name, argv_expanded[1]);
9885 rcode = run_function(funcp, argv_expanded) & 0xff;
9886 /*
9887 * But do collect *to old_vars list* vars shadowed
9888 * within function execution. To that end, restore
9889 * this pointer _after_ function run:
9890 */
9891 G.shadowed_vars_pp = sv_shadowed;
9892 }
9893#endif
9894 }
9895 } else
9896 if (ENABLE_FEATURE_SH_NOFORK && NUM_APPLETS > 1) {
9897 int n = find_applet_by_name(argv_expanded[0]);
9898 if (n < 0 || !APPLET_IS_NOFORK(n))
9899 goto must_fork;
9900
9901 enter_var_nest_level();
9902 /* Collect all variables "shadowed" by helper into old_vars list */
9903 G.shadowed_vars_pp = &old_vars;
9904 rcode = redirect_and_varexp_helper(command, &squirrel, argv_expanded);
9905 G.shadowed_vars_pp = sv_shadowed;
9906
9907 if (rcode == 0) {
9908 debug_printf_exec(": run_nofork_applet '%s' '%s'...\n",
9909 argv_expanded[0], argv_expanded[1]);
9910 /*
9911 * Note: signals (^C) can't interrupt here.
9912 * We remember them and they will be acted upon
9913 * after applet returns.
9914 * This makes applets which can run for a long time
9915 * and/or wait for user input ineligible for NOFORK:
9916 * for example, "yes" or "rm" (rm -i waits for input).
9917 */
9918 rcode = run_nofork_applet(n, argv_expanded);
9919 }
9920 } else
9921 goto must_fork;
9922
9923 restore_redirects(squirrel);
9924 clean_up_and_ret1:
9925 leave_var_nest_level();
9926 add_vars(old_vars);
9927
9928 /*
9929 * Try "usleep 99999999" + ^C + "echo $?"
9930 * with FEATURE_SH_NOFORK=y.
9931 */
9932 if (!funcp) {
9933 /* It was builtin or nofork.
9934 * if this would be a real fork/execed program,
9935 * it should have died if a fatal sig was received.
9936 * But OTOH, there was no separate process,
9937 * the sig was sent to _shell_, not to non-existing
9938 * child.
9939 * Let's just handle ^C only, this one is obvious:
9940 * we aren't ok with exitcode 0 when ^C was pressed
9941 * during builtin/nofork.
9942 */
9943 if (sigismember(&G.pending_set, SIGINT))
9944 rcode = 128 | SIGINT;
9945 }
9946 free(argv_expanded);
9947 IF_HAS_KEYWORDS(if (pi->pi_inverted) rcode = !rcode;)
9948 debug_leave();
9949 debug_printf_exec("run_pipe return %d\n", rcode);
9950 return rcode;
Francis Laniel8197f012023-12-22 22:02:28 +01009951#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01009952 }
9953
Francis Laniel8197f012023-12-22 22:02:28 +01009954#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01009955 must_fork:
9956 /* NB: argv_expanded may already be created, and that
9957 * might include `cmd` runs! Do not rerun it! We *must*
9958 * use argv_expanded if it's non-NULL */
9959
9960 /* Going to fork a child per each pipe member */
9961 pi->alive_cmds = 0;
9962 next_infd = 0;
Francis Laniel8197f012023-12-22 22:02:28 +01009963#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01009964
9965 cmd_no = 0;
9966 while (cmd_no < pi->num_cmds) {
Francis Laniel8197f012023-12-22 22:02:28 +01009967#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01009968 struct fd_pair pipefds;
9969#if !BB_MMU
9970 int sv_var_nest_level = G.var_nest_level;
9971 volatile nommu_save_t nommu_save;
9972 nommu_save.old_vars = NULL;
9973 nommu_save.argv = NULL;
9974 nommu_save.argv_from_re_execing = NULL;
9975#endif
Francis Laniel8197f012023-12-22 22:02:28 +01009976#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01009977 command = &pi->cmds[cmd_no];
9978 cmd_no++;
9979 if (command->argv) {
9980 debug_printf_exec(": pipe member '%s' '%s'...\n",
9981 command->argv[0], command->argv[1]);
9982 } else {
9983 debug_printf_exec(": pipe member with no argv\n");
9984 }
9985
Francis Laniel8197f012023-12-22 22:02:28 +01009986#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01009987 /* pipes are inserted between pairs of commands */
9988 pipefds.rd = 0;
9989 pipefds.wr = 1;
9990 if (cmd_no < pi->num_cmds)
9991 xpiped_pair(pipefds);
9992
9993#if ENABLE_HUSH_LINENO_VAR
9994 G.execute_lineno = command->lineno;
9995#endif
9996
9997 command->pid = BB_MMU ? fork() : vfork();
9998 if (!command->pid) { /* child */
9999#if ENABLE_HUSH_JOB
10000 disable_restore_tty_pgrp_on_exit();
10001 CLEAR_RANDOM_T(&G.random_gen); /* or else $RANDOM repeats in child */
10002
10003 /* Every child adds itself to new process group
10004 * with pgid == pid_of_first_child_in_pipe */
10005 if (G.run_list_level == 1 && G_interactive_fd) {
10006 pid_t pgrp;
10007 pgrp = pi->pgrp;
10008 if (pgrp < 0) /* true for 1st process only */
10009 pgrp = getpid();
10010 if (setpgid(0, pgrp) == 0
10011 && pi->followup != PIPE_BG
10012 && G_saved_tty_pgrp /* we have ctty */
10013 ) {
10014 /* We do it in *every* child, not just first,
10015 * to avoid races */
10016 tcsetpgrp(G_interactive_fd, pgrp);
10017 }
10018 }
10019#endif
10020 if (pi->alive_cmds == 0 && pi->followup == PIPE_BG) {
10021 /* 1st cmd in backgrounded pipe
10022 * should have its stdin /dev/null'ed */
10023 close(0);
10024 if (open(bb_dev_null, O_RDONLY))
10025 xopen("/", O_RDONLY);
10026 } else {
10027 xmove_fd(next_infd, 0);
10028 }
10029 xmove_fd(pipefds.wr, 1);
10030 if (pipefds.rd > 1)
10031 close(pipefds.rd);
10032 /* Like bash, explicit redirects override pipes,
10033 * and the pipe fd (fd#1) is available for dup'ing:
10034 * "cmd1 2>&1 | cmd2": fd#1 is duped to fd#2, thus stderr
10035 * of cmd1 goes into pipe.
10036 */
10037 if (setup_redirects(command, NULL)) {
10038 /* Happens when redir file can't be opened:
10039 * $ hush -c 'echo FOO >&2 | echo BAR 3>/qwe/rty; echo BAZ'
10040 * FOO
10041 * hush: can't open '/qwe/rty': No such file or directory
10042 * BAZ
10043 * (echo BAR is not executed, it hits _exit(1) below)
10044 */
10045 _exit(1);
10046 }
10047
10048 /* Stores to nommu_save list of env vars putenv'ed
10049 * (NOMMU, on MMU we don't need that) */
10050 /* cast away volatility... */
10051 pseudo_exec((nommu_save_t*) &nommu_save, command, argv_expanded);
10052 /* pseudo_exec() does not return */
10053 }
10054
10055 /* parent or error */
10056#if ENABLE_HUSH_FAST
10057 G.count_SIGCHLD++;
10058//bb_error_msg("[%d] fork in run_pipe: G.count_SIGCHLD:%d G.handled_SIGCHLD:%d", getpid(), G.count_SIGCHLD, G.handled_SIGCHLD);
10059#endif
10060 enable_restore_tty_pgrp_on_exit();
10061#if !BB_MMU
10062 /* Clean up after vforked child */
10063 free(nommu_save.argv);
10064 free(nommu_save.argv_from_re_execing);
10065 G.var_nest_level = sv_var_nest_level;
10066 remove_nested_vars();
10067 add_vars(nommu_save.old_vars);
10068#endif
10069 free(argv_expanded);
10070 argv_expanded = NULL;
10071 if (command->pid < 0) { /* [v]fork failed */
10072 /* Clearly indicate, was it fork or vfork */
10073 bb_simple_perror_msg(BB_MMU ? "vfork"+1 : "vfork");
10074 } else {
10075 pi->alive_cmds++;
10076#if ENABLE_HUSH_JOB
10077 /* Second and next children need to know pid of first one */
10078 if (pi->pgrp < 0)
10079 pi->pgrp = command->pid;
10080#endif
10081 }
10082
10083 if (cmd_no > 1)
10084 close(next_infd);
10085 if (cmd_no < pi->num_cmds)
10086 close(pipefds.wr);
10087 /* Pass read (output) pipe end to next iteration */
10088 next_infd = pipefds.rd;
Francis Laniel8197f012023-12-22 22:02:28 +010010089#else /* __U_BOOT__ */
10090 /* Process the command */
10091 rcode = cmd_process(G.do_repeat ? CMD_FLAG_REPEAT : 0,
10092 command->argc, command->argv,
10093 &(G.flag_repeat), NULL);
10094#endif /* __U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +010010095 }
10096
Francis Laniel8197f012023-12-22 22:02:28 +010010097#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +010010098 if (!pi->alive_cmds) {
10099 debug_leave();
10100 debug_printf_exec("run_pipe return 1 (all forks failed, no children)\n");
10101 return 1;
10102 }
Francis Laniel8197f012023-12-22 22:02:28 +010010103#endif /* __U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +010010104
10105 debug_leave();
Francis Laniel8197f012023-12-22 22:02:28 +010010106#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +010010107 debug_printf_exec("run_pipe return -1 (%u children started)\n", pi->alive_cmds);
10108 return -1;
Francis Laniel8197f012023-12-22 22:02:28 +010010109#else /* __U_BOOT__ */
10110 debug_printf_exec("run_pipe return %d\n", rcode);
10111 return rcode;
10112#endif /* __U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +010010113}
10114
10115/* NB: called by pseudo_exec, and therefore must not modify any
10116 * global data until exec/_exit (we can be a child after vfork!) */
10117static int run_list(struct pipe *pi)
10118{
10119#if ENABLE_HUSH_CASE
10120 char *case_word = NULL;
10121#endif
10122#if ENABLE_HUSH_LOOPS
10123 struct pipe *loop_top = NULL;
10124 char **for_lcur = NULL;
10125 char **for_list = NULL;
10126#endif
10127 smallint last_followup;
10128 smalluint rcode;
10129#if ENABLE_HUSH_IF || ENABLE_HUSH_CASE
10130 smalluint cond_code = 0;
10131#else
10132 enum { cond_code = 0 };
10133#endif
10134#if HAS_KEYWORDS
10135 smallint rword; /* RES_foo */
10136 smallint last_rword; /* ditto */
10137#endif
10138
Francis Laniel8197f012023-12-22 22:02:28 +010010139#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +010010140 debug_printf_exec("run_list start lvl %d\n", G.run_list_level);
10141 debug_enter();
Francis Laniel8197f012023-12-22 22:02:28 +010010142#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +010010143
10144#if ENABLE_HUSH_LOOPS
10145 /* Check syntax for "for" */
10146 {
10147 struct pipe *cpipe;
10148 for (cpipe = pi; cpipe; cpipe = cpipe->next) {
10149 if (cpipe->res_word != RES_FOR && cpipe->res_word != RES_IN)
10150 continue;
10151 /* current word is FOR or IN (BOLD in comments below) */
10152 if (cpipe->next == NULL) {
10153 syntax_error("malformed for");
10154 debug_leave();
10155 debug_printf_exec("run_list lvl %d return 1\n", G.run_list_level);
10156 return 1;
10157 }
10158 /* "FOR v; do ..." and "for v IN a b; do..." are ok */
10159 if (cpipe->next->res_word == RES_DO)
10160 continue;
10161 /* next word is not "do". It must be "in" then ("FOR v in ...") */
10162 if (cpipe->res_word == RES_IN /* "for v IN a b; not_do..."? */
10163 || cpipe->next->res_word != RES_IN /* FOR v not_do_and_not_in..."? */
10164 ) {
10165 syntax_error("malformed for");
10166 debug_leave();
10167 debug_printf_exec("run_list lvl %d return 1\n", G.run_list_level);
10168 return 1;
10169 }
10170 }
10171 }
10172#endif
10173
10174 /* Past this point, all code paths should jump to ret: label
10175 * in order to return, no direct "return" statements please.
10176 * This helps to ensure that no memory is leaked. */
10177
10178#if ENABLE_HUSH_JOB
10179 G.run_list_level++;
10180#endif
10181
10182#if HAS_KEYWORDS
10183 rword = RES_NONE;
10184 last_rword = RES_XXXX;
10185#endif
10186 last_followup = PIPE_SEQ;
10187 rcode = G.last_exitcode;
10188
10189 /* Go through list of pipes, (maybe) executing them. */
Francis Laniel8197f012023-12-22 22:02:28 +010010190#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +010010191 for (; pi; pi = IF_HUSH_LOOPS(rword == RES_DONE ? loop_top : ) pi->next) {
Francis Laniel8197f012023-12-22 22:02:28 +010010192#else /* __U_BOOT__ */
10193 for (; pi; pi = pi->next) {
10194#endif /* __U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +010010195 int r;
10196 int sv_errexit_depth;
10197
Francis Laniel8197f012023-12-22 22:02:28 +010010198#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +010010199 if (G.flag_SIGINT)
10200 break;
10201 if (G_flag_return_in_progress == 1)
10202 break;
10203
10204 IF_HAS_KEYWORDS(rword = pi->res_word;)
10205 debug_printf_exec(": rword=%d cond_code=%d last_rword=%d\n",
10206 rword, cond_code, last_rword);
10207
Francis Laniel8197f012023-12-22 22:02:28 +010010208#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +010010209 sv_errexit_depth = G.errexit_depth;
10210 if (
10211#if ENABLE_HUSH_IF
10212 rword == RES_IF || rword == RES_ELIF ||
10213#endif
10214 pi->followup != PIPE_SEQ
10215 ) {
10216 G.errexit_depth++;
10217 }
10218#if ENABLE_HUSH_LOOPS
10219 if ((rword == RES_WHILE || rword == RES_UNTIL || rword == RES_FOR)
10220 && loop_top == NULL /* avoid bumping G.depth_of_loop twice */
10221 ) {
10222 /* start of a loop: remember where loop starts */
10223 loop_top = pi;
10224 G.depth_of_loop++;
10225 }
10226#endif
10227 /* Still in the same "if...", "then..." or "do..." branch? */
10228 if (IF_HAS_KEYWORDS(rword == last_rword &&) 1) {
10229 if ((rcode == 0 && last_followup == PIPE_OR)
10230 || (rcode != 0 && last_followup == PIPE_AND)
10231 ) {
10232 /* It is "<true> || CMD" or "<false> && CMD"
10233 * and we should not execute CMD */
10234 debug_printf_exec("skipped cmd because of || or &&\n");
10235 last_followup = pi->followup;
10236 goto dont_check_jobs_but_continue;
10237 }
10238 }
10239 last_followup = pi->followup;
10240 IF_HAS_KEYWORDS(last_rword = rword;)
10241#if ENABLE_HUSH_IF
10242 if (cond_code) {
10243 if (rword == RES_THEN) {
10244 /* if false; then ... fi has exitcode 0! */
10245 G.last_exitcode = rcode = EXIT_SUCCESS;
10246 /* "if <false> THEN cmd": skip cmd */
10247 continue;
10248 }
10249 } else {
10250 if (rword == RES_ELSE || rword == RES_ELIF) {
10251 /* "if <true> then ... ELSE/ELIF cmd":
10252 * skip cmd and all following ones */
10253 break;
10254 }
10255 }
10256#endif
10257#if ENABLE_HUSH_LOOPS
10258 if (rword == RES_FOR) { /* && pi->num_cmds - always == 1 */
10259 if (!for_lcur) {
10260 /* first loop through for */
10261
10262 static const char encoded_dollar_at[] ALIGN1 = {
10263 SPECIAL_VAR_SYMBOL, '@' | 0x80, SPECIAL_VAR_SYMBOL, '\0'
10264 }; /* encoded representation of "$@" */
10265 static const char *const encoded_dollar_at_argv[] = {
10266 encoded_dollar_at, NULL
10267 }; /* argv list with one element: "$@" */
10268 char **vals;
10269
10270 G.last_exitcode = rcode = EXIT_SUCCESS;
10271 vals = (char**)encoded_dollar_at_argv;
10272 if (pi->next->res_word == RES_IN) {
10273 /* if no variable values after "in" we skip "for" */
10274 if (!pi->next->cmds[0].argv) {
10275 debug_printf_exec(": null FOR: exitcode EXIT_SUCCESS\n");
10276 break;
10277 }
10278 vals = pi->next->cmds[0].argv;
10279 } /* else: "for var; do..." -> assume "$@" list */
10280 /* create list of variable values */
10281 debug_print_strings("for_list made from", vals);
10282 for_list = expand_strvec_to_strvec(vals);
10283 for_lcur = for_list;
10284 debug_print_strings("for_list", for_list);
10285 }
10286 if (!*for_lcur) {
10287 /* "for" loop is over, clean up */
10288 free(for_list);
10289 for_list = NULL;
10290 for_lcur = NULL;
10291 break;
10292 }
10293 /* Insert next value from for_lcur */
10294 /* note: *for_lcur already has quotes removed, $var expanded, etc */
10295 set_local_var(xasprintf("%s=%s", pi->cmds[0].argv[0], *for_lcur++), /*flag:*/ 0);
10296 continue;
10297 }
10298 if (rword == RES_IN) {
10299 continue; /* "for v IN list;..." - "in" has no cmds anyway */
10300 }
10301 if (rword == RES_DONE) {
10302 continue; /* "done" has no cmds too */
10303 }
10304#endif
10305#if ENABLE_HUSH_CASE
10306 if (rword == RES_CASE) {
10307 debug_printf_exec("CASE cond_code:%d\n", cond_code);
10308 case_word = expand_string_to_string(pi->cmds->argv[0],
10309 EXP_FLAG_ESC_GLOB_CHARS, /*unbackslash:*/ 1);
10310 debug_printf_exec("CASE word1:'%s'\n", case_word);
10311 //unbackslash(case_word);
10312 //debug_printf_exec("CASE word2:'%s'\n", case_word);
10313 continue;
10314 }
10315 if (rword == RES_MATCH) {
10316 char **argv;
10317
10318 debug_printf_exec("MATCH cond_code:%d\n", cond_code);
10319 if (!case_word) /* "case ... matched_word) ... WORD)": we executed selected branch, stop */
10320 break;
10321 /* all prev words didn't match, does this one match? */
10322 argv = pi->cmds->argv;
10323 while (*argv) {
10324 char *pattern;
10325 debug_printf_exec("expand_string_to_string('%s')\n", *argv);
10326 pattern = expand_string_to_string(*argv,
10327 EXP_FLAG_ESC_GLOB_CHARS,
10328 /*unbackslash:*/ 0
10329 );
10330 /* TODO: which FNM_xxx flags to use? */
10331 cond_code = (fnmatch(pattern, case_word, /*flags:*/ 0) != 0);
10332 debug_printf_exec("fnmatch(pattern:'%s',str:'%s'):%d\n",
10333 pattern, case_word, cond_code);
10334 free(pattern);
10335 if (cond_code == 0) {
10336 /* match! we will execute this branch */
10337 free(case_word);
10338 case_word = NULL; /* make future "word)" stop */
10339 break;
10340 }
10341 argv++;
10342 }
10343 continue;
10344 }
10345 if (rword == RES_CASE_BODY) { /* inside of a case branch */
10346 debug_printf_exec("CASE_BODY cond_code:%d\n", cond_code);
10347 if (cond_code != 0)
10348 continue; /* not matched yet, skip this pipe */
10349 }
10350 if (rword == RES_ESAC) {
10351 debug_printf_exec("ESAC cond_code:%d\n", cond_code);
10352 if (case_word) {
10353 /* "case" did not match anything: still set $? (to 0) */
10354 G.last_exitcode = rcode = EXIT_SUCCESS;
10355 }
10356 }
10357#endif
10358 /* Just pressing <enter> in shell should check for jobs.
10359 * OTOH, in non-interactive shell this is useless
10360 * and only leads to extra job checks */
10361 if (pi->num_cmds == 0) {
Francis Laniel8197f012023-12-22 22:02:28 +010010362#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +010010363 if (G_interactive_fd)
10364 goto check_jobs_and_continue;
Francis Laniel8197f012023-12-22 22:02:28 +010010365#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +010010366 continue;
10367 }
10368
10369 /* After analyzing all keywords and conditions, we decided
10370 * to execute this pipe. NB: have to do checkjobs(NULL)
10371 * after run_pipe to collect any background children,
10372 * even if list execution is to be stopped. */
10373 debug_printf_exec(": run_pipe with %d members\n", pi->num_cmds);
Francis Laniel8197f012023-12-22 22:02:28 +010010374#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +010010375#if ENABLE_HUSH_LOOPS
10376 G.flag_break_continue = 0;
10377#endif
Francis Laniel8197f012023-12-22 22:02:28 +010010378#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +010010379 rcode = r = run_pipe(pi); /* NB: rcode is a smalluint, r is int */
Francis Laniel8197f012023-12-22 22:02:28 +010010380#ifdef __U_BOOT__
Francis Laniel9a068372023-12-22 22:02:32 +010010381 if (r <= EXIT_RET_CODE) {
10382 int previous_rcode = G.last_exitcode;
10383 /*
10384 * This magic is to get the exit code given by the user.
10385 * Contrary to old shell code, we use + EXIT_RET_CODE as EXIT_RET_CODE
10386 * equals -2.
10387 */
10388 G.last_exitcode = -r + EXIT_RET_CODE;
Francis Laniel8197f012023-12-22 22:02:28 +010010389
Francis Laniel9a068372023-12-22 22:02:32 +010010390 /*
10391 * This case deals with the following:
10392 * => setenv inner 'echo entry inner; exit; echo inner done'
10393 * => setenv outer 'echo entry outer; run inner; echo outer done'
10394 * => run outer
10395 * So, if we are in inner, we need to break and not run the other
10396 * commands.
10397 * Otherwise, we just continue in outer.
10398 * As return code are propagated, we use the previous value to check if
10399 * exit was just called or was propagated.
10400 */
10401 if (previous_rcode != r) {
10402 /*
10403 * If run from run_command, run_command_flags will be set, so we check
10404 * this to know if we are in main input shell.
10405 */
10406 if (!G.run_command_flags)
10407 printf("exit not allowed from main input shell.\n");
10408
10409 break;
10410 }
10411 continue;
Francis Laniel8197f012023-12-22 22:02:28 +010010412 }
Francis Laniel9a068372023-12-22 22:02:32 +010010413#endif /* __U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +010010414 if (r != -1) {
10415 /* We ran a builtin, function, or group.
10416 * rcode is already known
10417 * and we don't need to wait for anything. */
10418 debug_printf_exec(": builtin/func exitcode %d\n", rcode);
10419 G.last_exitcode = rcode;
Francis Laniel8197f012023-12-22 22:02:28 +010010420#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +010010421 check_and_run_traps();
Francis Laniel8197f012023-12-22 22:02:28 +010010422#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +010010423#if ENABLE_HUSH_TRAP && ENABLE_HUSH_FUNCTIONS
10424 rcode = G.last_exitcode; /* "return" in trap can change it, read back */
10425#endif
Francis Laniel8197f012023-12-22 22:02:28 +010010426#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +010010427#if ENABLE_HUSH_LOOPS
10428 /* Was it "break" or "continue"? */
10429 if (G.flag_break_continue) {
10430 smallint fbc = G.flag_break_continue;
10431 /* We might fall into outer *loop*,
10432 * don't want to break it too */
10433 if (loop_top) {
10434 G.depth_break_continue--;
10435 if (G.depth_break_continue == 0)
10436 G.flag_break_continue = 0;
10437 /* else: e.g. "continue 2" should *break* once, *then* continue */
10438 } /* else: "while... do... { we are here (innermost list is not a loop!) };...done" */
10439 if (G.depth_break_continue != 0 || fbc == BC_BREAK) {
10440 checkjobs(NULL, 0 /*(no pid to wait for)*/);
10441 break;
10442 }
10443 /* "continue": simulate end of loop */
10444 rword = RES_DONE;
10445 continue;
10446 }
10447#endif
10448 if (G_flag_return_in_progress == 1) {
10449 checkjobs(NULL, 0 /*(no pid to wait for)*/);
10450 break;
10451 }
Francis Laniel8197f012023-12-22 22:02:28 +010010452
Francis Lanielb234f7e2023-12-22 22:02:27 +010010453 } else if (pi->followup == PIPE_BG) {
10454 /* What does bash do with attempts to background builtins? */
10455 /* even bash 3.2 doesn't do that well with nested bg:
10456 * try "{ { sleep 10; echo DEEP; } & echo HERE; } &".
10457 * I'm NOT treating inner &'s as jobs */
10458#if ENABLE_HUSH_JOB
10459 if (G.run_list_level == 1)
10460 insert_job_into_table(pi);
10461#endif
10462 /* Last command's pid goes to $! */
10463 G.last_bg_pid = pi->cmds[pi->num_cmds - 1].pid;
10464 G.last_bg_pid_exitcode = 0;
10465 debug_printf_exec(": cmd&: exitcode EXIT_SUCCESS\n");
10466/* Check pi->pi_inverted? "! sleep 1 & echo $?": bash says 1. dash and ash say 0 */
10467 rcode = EXIT_SUCCESS;
10468 goto check_traps;
10469 } else {
10470#if ENABLE_HUSH_JOB
10471 if (G.run_list_level == 1 && G_interactive_fd) {
10472 /* Waits for completion, then fg's main shell */
10473 rcode = checkjobs_and_fg_shell(pi);
10474 debug_printf_exec(": checkjobs_and_fg_shell exitcode %d\n", rcode);
10475 goto check_traps;
10476 }
10477#endif
10478 /* This one just waits for completion */
10479 rcode = checkjobs(pi, 0 /*(no pid to wait for)*/);
10480 debug_printf_exec(": checkjobs exitcode %d\n", rcode);
10481 check_traps:
10482 G.last_exitcode = rcode;
10483 check_and_run_traps();
10484#if ENABLE_HUSH_TRAP && ENABLE_HUSH_FUNCTIONS
10485 rcode = G.last_exitcode; /* "return" in trap can change it, read back */
10486#endif
10487 }
Francis Laniel8197f012023-12-22 22:02:28 +010010488#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +010010489
Francis Laniel8197f012023-12-22 22:02:28 +010010490#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +010010491 /* Handle "set -e" */
10492 if (rcode != 0 && G.o_opt[OPT_O_ERREXIT]) {
10493 debug_printf_exec("ERREXIT:1 errexit_depth:%d\n", G.errexit_depth);
10494 if (G.errexit_depth == 0)
10495 hush_exit(rcode);
10496 }
Francis Laniel8197f012023-12-22 22:02:28 +010010497#else /* __U_BOOT__ */
10498 } /* if (r != -1) */
10499#endif /* __U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +010010500 G.errexit_depth = sv_errexit_depth;
10501
10502 /* Analyze how result affects subsequent commands */
10503#if ENABLE_HUSH_IF
10504 if (rword == RES_IF || rword == RES_ELIF)
10505 cond_code = rcode;
10506#endif
Francis Laniel8197f012023-12-22 22:02:28 +010010507#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +010010508 check_jobs_and_continue:
10509 checkjobs(NULL, 0 /*(no pid to wait for)*/);
Francis Laniel8197f012023-12-22 22:02:28 +010010510#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +010010511 dont_check_jobs_but_continue: ;
10512#if ENABLE_HUSH_LOOPS
10513 /* Beware of "while false; true; do ..."! */
10514 if (pi->next
10515 && (pi->next->res_word == RES_DO || pi->next->res_word == RES_DONE)
10516 /* check for RES_DONE is needed for "while ...; do \n done" case */
10517 ) {
10518 if (rword == RES_WHILE) {
10519 if (rcode) {
10520 /* "while false; do...done" - exitcode 0 */
10521 G.last_exitcode = rcode = EXIT_SUCCESS;
10522 debug_printf_exec(": while expr is false: breaking (exitcode:EXIT_SUCCESS)\n");
10523 break;
10524 }
10525 }
10526 if (rword == RES_UNTIL) {
10527 if (!rcode) {
10528 debug_printf_exec(": until expr is true: breaking\n");
10529 break;
10530 }
10531 }
10532 }
10533#endif
10534 } /* for (pi) */
10535
10536#if ENABLE_HUSH_JOB
10537 G.run_list_level--;
10538#endif
10539#if ENABLE_HUSH_LOOPS
10540 if (loop_top)
10541 G.depth_of_loop--;
10542 free(for_list);
10543#endif
10544#if ENABLE_HUSH_CASE
10545 free(case_word);
10546#endif
Francis Laniel8197f012023-12-22 22:02:28 +010010547#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +010010548 debug_leave();
10549 debug_printf_exec("run_list lvl %d return %d\n", G.run_list_level + 1, rcode);
Francis Laniel8197f012023-12-22 22:02:28 +010010550#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +010010551 return rcode;
10552}
10553
10554/* Select which version we will use */
10555static int run_and_free_list(struct pipe *pi)
10556{
10557 int rcode = 0;
10558 debug_printf_exec("run_and_free_list entered\n");
Francis Laniel8197f012023-12-22 22:02:28 +010010559#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +010010560 if (!G.o_opt[OPT_O_NOEXEC]) {
Francis Laniel8197f012023-12-22 22:02:28 +010010561#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +010010562 debug_printf_exec(": run_list: 1st pipe with %d cmds\n", pi->num_cmds);
10563 rcode = run_list(pi);
Francis Laniel8197f012023-12-22 22:02:28 +010010564#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +010010565 }
Francis Laniel8197f012023-12-22 22:02:28 +010010566#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +010010567 /* free_pipe_list has the side effect of clearing memory.
10568 * In the long run that function can be merged with run_list,
10569 * but doing that now would hobble the debugging effort. */
10570 free_pipe_list(pi);
10571 debug_printf_exec("run_and_free_list return %d\n", rcode);
10572 return rcode;
10573}
10574
10575
Francis Laniel8197f012023-12-22 22:02:28 +010010576#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +010010577static void install_sighandlers(unsigned mask)
10578{
10579 sighandler_t old_handler;
10580 unsigned sig = 0;
10581 while ((mask >>= 1) != 0) {
10582 sig++;
10583 if (!(mask & 1))
10584 continue;
10585 old_handler = install_sighandler(sig, pick_sighandler(sig));
10586 /* POSIX allows shell to re-enable SIGCHLD
10587 * even if it was SIG_IGN on entry.
10588 * Therefore we skip IGN check for it:
10589 */
10590 if (sig == SIGCHLD)
10591 continue;
10592 /* Interactive bash re-enables SIGHUP which is SIG_IGNed on entry.
10593 * Try:
10594 * trap '' hup; bash; echo RET # type "kill -hup $$", see SIGHUP having effect
10595 * trap '' hup; bash -c 'kill -hup $$; echo ALIVE' # here SIGHUP is SIG_IGNed
10596 */
10597 if (sig == SIGHUP && G_interactive_fd)
10598 continue;
10599 /* Unless one of the above signals, is it SIG_IGN? */
10600 if (old_handler == SIG_IGN) {
10601 /* oops... restore back to IGN, and record this fact */
10602 install_sighandler(sig, old_handler);
10603#if ENABLE_HUSH_TRAP
10604 if (!G_traps)
10605 G_traps = xzalloc(sizeof(G_traps[0]) * NSIG);
10606 free(G_traps[sig]);
10607 G_traps[sig] = xzalloc(1); /* == xstrdup(""); */
10608#endif
10609 }
10610 }
10611}
10612
10613/* Called a few times only (or even once if "sh -c") */
10614static void install_special_sighandlers(void)
10615{
10616 unsigned mask;
10617
10618 /* Which signals are shell-special? */
10619 mask = (1 << SIGQUIT) | (1 << SIGCHLD);
10620 if (G_interactive_fd) {
10621 mask |= SPECIAL_INTERACTIVE_SIGS;
10622 if (G_saved_tty_pgrp) /* we have ctty, job control sigs work */
10623 mask |= SPECIAL_JOBSTOP_SIGS;
10624 }
10625 /* Careful, do not re-install handlers we already installed */
10626 if (G.special_sig_mask != mask) {
10627 unsigned diff = mask & ~G.special_sig_mask;
10628 G.special_sig_mask = mask;
10629 install_sighandlers(diff);
10630 }
10631}
10632
10633#if ENABLE_HUSH_JOB
10634/* helper */
10635/* Set handlers to restore tty pgrp and exit */
10636static void install_fatal_sighandlers(void)
10637{
10638 unsigned mask;
10639
10640 /* We will restore tty pgrp on these signals */
10641 mask = 0
10642 /*+ (1 << SIGILL ) * HUSH_DEBUG*/
10643 /*+ (1 << SIGFPE ) * HUSH_DEBUG*/
10644 + (1 << SIGBUS ) * HUSH_DEBUG
10645 + (1 << SIGSEGV) * HUSH_DEBUG
10646 /*+ (1 << SIGTRAP) * HUSH_DEBUG*/
10647 + (1 << SIGABRT)
10648 /* bash 3.2 seems to handle these just like 'fatal' ones */
10649 + (1 << SIGPIPE)
10650 + (1 << SIGALRM)
10651 /* if we are interactive, SIGHUP, SIGTERM and SIGINT are special sigs.
10652 * if we aren't interactive... but in this case
10653 * we never want to restore pgrp on exit, and this fn is not called
10654 */
10655 /*+ (1 << SIGHUP )*/
10656 /*+ (1 << SIGTERM)*/
10657 /*+ (1 << SIGINT )*/
10658 ;
10659 G_fatal_sig_mask = mask;
10660
10661 install_sighandlers(mask);
10662}
10663#endif
10664
10665static int set_mode(int state, char mode, const char *o_opt)
10666{
10667 int idx;
10668 switch (mode) {
10669 case 'n':
10670 G.o_opt[OPT_O_NOEXEC] = state;
10671 break;
10672 case 'x':
10673 IF_HUSH_MODE_X(G_x_mode = state;)
10674 IF_HUSH_MODE_X(if (G.x_mode_fd <= 0) G.x_mode_fd = dup_CLOEXEC(2, 10);)
10675 break;
10676 case 'e':
10677 G.o_opt[OPT_O_ERREXIT] = state;
10678 break;
10679 case 'o':
10680 if (!o_opt) {
10681 /* "set -o" or "set +o" without parameter.
10682 * in bash, set -o produces this output:
10683 * pipefail off
10684 * and set +o:
10685 * set +o pipefail
10686 * We always use the second form.
10687 */
10688 const char *p = o_opt_strings;
10689 idx = 0;
10690 while (*p) {
10691 printf("set %co %s\n", (G.o_opt[idx] ? '-' : '+'), p);
10692 idx++;
10693 p += strlen(p) + 1;
10694 }
10695 break;
10696 }
10697 idx = index_in_strings(o_opt_strings, o_opt);
10698 if (idx >= 0) {
10699 G.o_opt[idx] = state;
10700 break;
10701 }
10702 /* fall through to error */
10703 default:
10704 return EXIT_FAILURE;
10705 }
10706 return EXIT_SUCCESS;
10707}
10708
10709int hush_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
10710int hush_main(int argc, char **argv)
10711{
10712 pid_t cached_getpid;
10713 enum {
10714 OPT_login = (1 << 0),
10715 };
10716 unsigned flags;
10717#if !BB_MMU
10718 unsigned builtin_argc = 0;
10719#endif
10720 char **e;
10721 struct variable *cur_var;
10722 struct variable *shell_ver;
10723
10724 INIT_G();
10725 if (EXIT_SUCCESS != 0) /* if EXIT_SUCCESS == 0, it is already done */
10726 G.last_exitcode = EXIT_SUCCESS;
10727#if ENABLE_HUSH_TRAP
10728# if ENABLE_HUSH_FUNCTIONS
10729 G.return_exitcode = -1;
10730# endif
10731 G.pre_trap_exitcode = -1;
10732#endif
10733
10734#if ENABLE_HUSH_FAST
10735 G.count_SIGCHLD++; /* ensure it is != G.handled_SIGCHLD */
10736#endif
10737#if !BB_MMU
10738 G.argv0_for_re_execing = argv[0];
10739#endif
10740
10741 cached_getpid = getpid(); /* for tcsetpgrp() during init */
10742 G.root_pid = cached_getpid; /* for $PID (NOMMU can override via -$HEXPID:HEXPPID:...) */
10743 G.root_ppid = getppid(); /* for $PPID (NOMMU can override) */
10744
10745 /* Deal with HUSH_VERSION */
10746 debug_printf_env("unsetenv '%s'\n", "HUSH_VERSION");
10747 unsetenv("HUSH_VERSION"); /* in case it exists in initial env */
10748 shell_ver = xzalloc(sizeof(*shell_ver));
10749 shell_ver->flg_export = 1;
10750 shell_ver->flg_read_only = 1;
10751 /* Code which handles ${var<op>...} needs writable values for all variables,
10752 * therefore we xstrdup: */
10753 shell_ver->varstr = xstrdup(hush_version_str);
10754
10755 /* Create shell local variables from the values
10756 * currently living in the environment */
10757 G.top_var = shell_ver;
10758 cur_var = G.top_var;
10759 e = environ;
10760 if (e) while (*e) {
10761 char *value = strchr(*e, '=');
10762 if (value) { /* paranoia */
10763 cur_var->next = xzalloc(sizeof(*cur_var));
10764 cur_var = cur_var->next;
10765 cur_var->varstr = *e;
10766 cur_var->max_len = strlen(*e);
10767 cur_var->flg_export = 1;
10768 }
10769 e++;
10770 }
10771 /* (Re)insert HUSH_VERSION into env (AFTER we scanned the env!) */
10772 debug_printf_env("putenv '%s'\n", shell_ver->varstr);
10773 putenv(shell_ver->varstr);
10774
10775 /* Export PWD */
10776 set_pwd_var(SETFLAG_EXPORT);
10777
10778#if BASH_HOSTNAME_VAR
10779 /* Set (but not export) HOSTNAME unless already set */
10780 if (!get_local_var_value("HOSTNAME")) {
10781 struct utsname uts;
10782 uname(&uts);
10783 set_local_var_from_halves("HOSTNAME", uts.nodename);
10784 }
10785#endif
10786 /* IFS is not inherited from the parent environment */
10787 set_local_var_from_halves("IFS", defifs);
10788
10789 if (!get_local_var_value("PATH"))
10790 set_local_var_from_halves("PATH", bb_default_root_path);
10791
10792 /* PS1/PS2 are set later, if we determine that we are interactive */
10793
10794 /* bash also exports SHLVL and _,
10795 * and sets (but doesn't export) the following variables:
10796 * BASH=/bin/bash
10797 * BASH_VERSINFO=([0]="3" [1]="2" [2]="0" [3]="1" [4]="release" [5]="i386-pc-linux-gnu")
10798 * BASH_VERSION='3.2.0(1)-release'
10799 * HOSTTYPE=i386
10800 * MACHTYPE=i386-pc-linux-gnu
10801 * OSTYPE=linux-gnu
10802 * PPID=<NNNNN> - we also do it elsewhere
10803 * EUID=<NNNNN>
10804 * UID=<NNNNN>
10805 * GROUPS=()
10806 * LINES=<NNN>
10807 * COLUMNS=<NNN>
10808 * BASH_ARGC=()
10809 * BASH_ARGV=()
10810 * BASH_LINENO=()
10811 * BASH_SOURCE=()
10812 * DIRSTACK=()
10813 * PIPESTATUS=([0]="0")
10814 * HISTFILE=/<xxx>/.bash_history
10815 * HISTFILESIZE=500
10816 * HISTSIZE=500
10817 * MAILCHECK=60
10818 * PATH=/usr/gnu/bin:/usr/local/bin:/bin:/usr/bin:.
10819 * SHELL=/bin/bash
10820 * SHELLOPTS=braceexpand:emacs:hashall:histexpand:history:interactive-comments:monitor
10821 * TERM=dumb
10822 * OPTERR=1
10823 * OPTIND=1
10824 * PS4='+ '
10825 */
10826
10827#if NUM_SCRIPTS > 0
10828 if (argc < 0) {
10829 char *script = get_script_content(-argc - 1);
10830 G.global_argv = argv;
10831 G.global_argc = string_array_len(argv);
10832 //install_special_sighandlers(); - needed?
10833 parse_and_run_string(script);
10834 goto final_return;
10835 }
10836#endif
10837
10838 /* Initialize some more globals to non-zero values */
10839 die_func = restore_ttypgrp_and__exit;
10840
10841 /* Shell is non-interactive at first. We need to call
10842 * install_special_sighandlers() if we are going to execute "sh <script>",
10843 * "sh -c <cmds>" or login shell's /etc/profile and friends.
10844 * If we later decide that we are interactive, we run install_special_sighandlers()
10845 * in order to intercept (more) signals.
10846 */
10847
10848 /* Parse options */
10849 /* http://www.opengroup.org/onlinepubs/9699919799/utilities/sh.html */
10850 flags = (argv[0] && argv[0][0] == '-') ? OPT_login : 0;
10851 while (1) {
10852 int opt = getopt(argc, argv, "+" /* stop at 1st non-option */
10853 "cexinsl"
10854#if !BB_MMU
10855 "<:$:R:V:"
10856# if ENABLE_HUSH_FUNCTIONS
10857 "F:"
10858# endif
10859#endif
10860 );
10861 if (opt <= 0)
10862 break;
10863 switch (opt) {
10864 case 'c':
10865 /* Note: -c is not an option with param!
10866 * "hush -c -l SCRIPT" is valid. "hush -cSCRIPT" is not.
10867 */
10868 G.opt_c = 1;
10869 break;
10870 case 'i':
10871 /* Well, we cannot just declare interactiveness,
10872 * we have to have some stuff (ctty, etc) */
10873 /* G_interactive_fd++; */
10874 break;
10875 case 's':
10876 G.opt_s = 1;
10877 break;
10878 case 'l':
10879 flags |= OPT_login;
10880 break;
10881#if !BB_MMU
10882 case '<': /* "big heredoc" support */
10883 full_write1_str(optarg);
10884 _exit(0);
10885 case '$': {
10886 unsigned long long empty_trap_mask;
10887
10888 G.root_pid = bb_strtou(optarg, &optarg, 16);
10889 optarg++;
10890 G.root_ppid = bb_strtou(optarg, &optarg, 16);
10891 optarg++;
10892 G.last_bg_pid = bb_strtou(optarg, &optarg, 16);
10893 optarg++;
10894 G.last_exitcode = bb_strtou(optarg, &optarg, 16);
10895 optarg++;
10896 builtin_argc = bb_strtou(optarg, &optarg, 16);
10897 optarg++;
10898 empty_trap_mask = bb_strtoull(optarg, &optarg, 16);
10899 if (empty_trap_mask != 0) {
10900 IF_HUSH_TRAP(int sig;)
10901 install_special_sighandlers();
10902# if ENABLE_HUSH_TRAP
10903 G_traps = xzalloc(sizeof(G_traps[0]) * NSIG);
10904 for (sig = 1; sig < NSIG; sig++) {
10905 if (empty_trap_mask & (1LL << sig)) {
10906 G_traps[sig] = xzalloc(1); /* == xstrdup(""); */
10907 install_sighandler(sig, SIG_IGN);
10908 }
10909 }
10910# endif
10911 }
10912# if ENABLE_HUSH_LOOPS
10913 optarg++;
10914 G.depth_of_loop = bb_strtou(optarg, &optarg, 16);
10915# endif
10916 /* Suppress "killed by signal" message, -$ hack is used
10917 * for subshells: echo `sh -c 'kill -9 $$'`
10918 * should be silent.
10919 */
10920 IF_HUSH_JOB(G.run_list_level = 1;)
10921# if ENABLE_HUSH_FUNCTIONS
10922 /* nommu uses re-exec trick for "... | func | ...",
10923 * should allow "return".
10924 * This accidentally allows returns in subshells.
10925 */
10926 G_flag_return_in_progress = -1;
10927# endif
10928 break;
10929 }
10930 case 'R':
10931 case 'V':
10932 set_local_var(xstrdup(optarg), opt == 'R' ? SETFLAG_MAKE_RO : 0);
10933 break;
10934# if ENABLE_HUSH_FUNCTIONS
10935 case 'F': {
10936 struct function *funcp = new_function(optarg);
10937 /* funcp->name is already set to optarg */
10938 /* funcp->body is set to NULL. It's a special case. */
10939 funcp->body_as_string = argv[optind];
10940 optind++;
10941 break;
10942 }
10943# endif
10944#endif
10945 /*case '?': invalid option encountered (set_mode('?') will fail) */
10946 /*case 'n':*/
10947 /*case 'x':*/
10948 /*case 'e':*/
10949 default:
10950 if (set_mode(1, opt, NULL) == 0) /* no error */
10951 break;
10952 bb_show_usage();
10953 }
10954 } /* option parsing loop */
10955
10956 /* Skip options. Try "hush -l": $1 should not be "-l"! */
10957 G.global_argc = argc - (optind - 1);
10958 G.global_argv = argv + (optind - 1);
10959 G.global_argv[0] = argv[0];
10960
10961 /* If we are login shell... */
10962 if (flags & OPT_login) {
10963 const char *hp = NULL;
10964 HFILE *input;
10965
10966 debug_printf("sourcing /etc/profile\n");
10967 input = hfopen("/etc/profile");
10968 run_profile:
10969 if (input != NULL) {
10970 install_special_sighandlers();
10971 parse_and_run_file(input);
10972 hfclose(input);
10973 }
10974 /* bash: after sourcing /etc/profile,
10975 * tries to source (in the given order):
10976 * ~/.bash_profile, ~/.bash_login, ~/.profile,
10977 * stopping on first found. --noprofile turns this off.
10978 * bash also sources ~/.bash_logout on exit.
10979 * If called as sh, skips .bash_XXX files.
10980 */
10981 if (!hp) { /* unless we looped on the "goto" already */
10982 hp = get_local_var_value("HOME");
10983 if (hp && hp[0]) {
10984 debug_printf("sourcing ~/.profile\n");
10985 hp = concat_path_file(hp, ".profile");
10986 input = hfopen(hp);
10987 free((char*)hp);
10988 goto run_profile;
10989 }
10990 }
10991 }
10992
Francis Laniel8197f012023-12-22 22:02:28 +010010993#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +010010994 /* -c takes effect *after* -l */
10995 if (G.opt_c) {
10996 /* Possibilities:
10997 * sh ... -c 'script'
10998 * sh ... -c 'script' ARG0 [ARG1...]
10999 * On NOMMU, if builtin_argc != 0,
11000 * sh ... -c 'builtin' BARGV... "" ARG0 [ARG1...]
11001 * "" needs to be replaced with NULL
11002 * and BARGV vector fed to builtin function.
11003 * Note: the form without ARG0 never happens:
11004 * sh ... -c 'builtin' BARGV... ""
11005 */
11006 char *script;
11007
11008 install_special_sighandlers();
11009
11010 G.global_argc--;
11011 G.global_argv++;
11012#if !BB_MMU
11013 if (builtin_argc) {
11014 /* -c 'builtin' [BARGV...] "" ARG0 [ARG1...] */
11015 const struct built_in_command *x;
11016 x = find_builtin(G.global_argv[0]);
11017 if (x) { /* paranoia */
11018 argv = G.global_argv;
11019 G.global_argc -= builtin_argc + 1; /* skip [BARGV...] "" */
11020 G.global_argv += builtin_argc + 1;
11021 G.global_argv[-1] = NULL; /* replace "" */
11022 G.last_exitcode = x->b_function(argv);
11023 }
11024 goto final_return;
11025 }
11026#endif
11027
11028 script = G.global_argv[0];
11029 if (!script)
11030 bb_error_msg_and_die(bb_msg_requires_arg, "-c");
11031 if (!G.global_argv[1]) {
11032 /* -c 'script' (no params): prevent empty $0 */
11033 G.global_argv[0] = argv[0];
11034 } else { /* else -c 'script' ARG0 [ARG1...]: $0 is ARG0 */
11035 G.global_argc--;
11036 G.global_argv++;
11037 }
11038 parse_and_run_string(script);
11039 goto final_return;
11040 }
11041
11042 /* -s is: hush -s ARGV1 ARGV2 (no SCRIPT) */
11043 if (!G.opt_s && G.global_argv[1]) {
11044 HFILE *input;
11045 /*
11046 * "bash <script>" (which is never interactive (unless -i?))
11047 * sources $BASH_ENV here (without scanning $PATH).
11048 * If called as sh, does the same but with $ENV.
11049 * Also NB, per POSIX, $ENV should undergo parameter expansion.
11050 */
11051 G.global_argc--;
11052 G.global_argv++;
11053 debug_printf("running script '%s'\n", G.global_argv[0]);
11054 xfunc_error_retval = 127; /* for "hush /does/not/exist" case */
11055 input = hfopen(G.global_argv[0]);
11056 if (!input) {
11057 bb_simple_perror_msg_and_die(G.global_argv[0]);
11058 }
11059 xfunc_error_retval = 1;
11060 install_special_sighandlers();
11061 parse_and_run_file(input);
11062#if ENABLE_FEATURE_CLEAN_UP
11063 hfclose(input);
11064#endif
11065 goto final_return;
11066 }
11067 /* "implicit" -s: bare interactive hush shows 's' in $- */
11068 G.opt_s = 1;
11069
Francis Laniel8197f012023-12-22 22:02:28 +010011070#endif /* __U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +010011071 /* Up to here, shell was non-interactive. Now it may become one.
11072 * NB: don't forget to (re)run install_special_sighandlers() as needed.
11073 */
11074
11075 /* A shell is interactive if the '-i' flag was given,
11076 * or if all of the following conditions are met:
11077 * no -c command
11078 * no arguments remaining or the -s flag given
11079 * standard input is a terminal
11080 * standard output is a terminal
11081 * Refer to Posix.2, the description of the 'sh' utility.
11082 */
11083#if ENABLE_HUSH_JOB
11084 if (isatty(STDIN_FILENO) && isatty(STDOUT_FILENO)) {
11085 G_saved_tty_pgrp = tcgetpgrp(STDIN_FILENO);
11086 debug_printf("saved_tty_pgrp:%d\n", G_saved_tty_pgrp);
11087 if (G_saved_tty_pgrp < 0)
11088 G_saved_tty_pgrp = 0;
11089
11090 /* try to dup stdin to high fd#, >= 255 */
11091 G_interactive_fd = dup_CLOEXEC(STDIN_FILENO, 254);
11092 if (G_interactive_fd < 0) {
11093 /* try to dup to any fd */
11094 G_interactive_fd = dup(STDIN_FILENO);
11095 if (G_interactive_fd < 0) {
11096 /* give up */
11097 G_interactive_fd = 0;
11098 G_saved_tty_pgrp = 0;
11099 }
11100 }
11101 }
11102 debug_printf("interactive_fd:%d\n", G_interactive_fd);
11103 if (G_interactive_fd) {
11104 close_on_exec_on(G_interactive_fd);
11105
11106 if (G_saved_tty_pgrp) {
11107 /* If we were run as 'hush &', sleep until we are
11108 * in the foreground (tty pgrp == our pgrp).
11109 * If we get started under a job aware app (like bash),
11110 * make sure we are now in charge so we don't fight over
11111 * who gets the foreground */
11112 while (1) {
11113 pid_t shell_pgrp = getpgrp();
11114 G_saved_tty_pgrp = tcgetpgrp(G_interactive_fd);
11115 if (G_saved_tty_pgrp == shell_pgrp)
11116 break;
11117 /* send TTIN to ourself (should stop us) */
11118 kill(- shell_pgrp, SIGTTIN);
11119 }
11120 }
11121
11122 /* Install more signal handlers */
11123 install_special_sighandlers();
11124
11125 if (G_saved_tty_pgrp) {
11126 /* Set other signals to restore saved_tty_pgrp */
11127 install_fatal_sighandlers();
11128 /* Put ourselves in our own process group
11129 * (bash, too, does this only if ctty is available) */
11130 bb_setpgrp(); /* is the same as setpgid(our_pid, our_pid); */
11131 /* Grab control of the terminal */
11132 tcsetpgrp(G_interactive_fd, cached_getpid);
11133 }
11134 enable_restore_tty_pgrp_on_exit();
11135
11136# if ENABLE_FEATURE_EDITING
11137 G.line_input_state = new_line_input_t(FOR_SHELL);
11138# if EDITING_HAS_get_exe_name
11139 G.line_input_state->get_exe_name = get_builtin_name;
11140# endif
11141# endif
11142# if ENABLE_HUSH_SAVEHISTORY && MAX_HISTORY > 0
11143 {
11144 const char *hp = get_local_var_value("HISTFILE");
11145 if (!hp) {
11146 hp = get_local_var_value("HOME");
11147 if (hp)
11148 hp = concat_path_file(hp, ".hush_history");
11149 } else {
11150 hp = xstrdup(hp);
11151 }
11152 if (hp) {
11153 G.line_input_state->hist_file = hp;
11154 //set_local_var(xasprintf("HISTFILE=%s", ...));
11155 }
11156# if ENABLE_FEATURE_SH_HISTFILESIZE
11157 hp = get_local_var_value("HISTFILESIZE");
11158 G.line_input_state->max_history = size_from_HISTFILESIZE(hp);
11159# endif
11160 }
11161# endif
11162 } else {
11163 install_special_sighandlers();
11164 }
11165#elif ENABLE_HUSH_INTERACTIVE
11166 /* No job control compiled in, only prompt/line editing */
11167 if (isatty(STDIN_FILENO) && isatty(STDOUT_FILENO)) {
11168 G_interactive_fd = dup_CLOEXEC(STDIN_FILENO, 254);
11169 if (G_interactive_fd < 0) {
11170 /* try to dup to any fd */
11171 G_interactive_fd = dup_CLOEXEC(STDIN_FILENO, -1);
11172 if (G_interactive_fd < 0)
11173 /* give up */
11174 G_interactive_fd = 0;
11175 }
11176 }
11177 if (G_interactive_fd) {
11178 close_on_exec_on(G_interactive_fd);
11179 }
11180 install_special_sighandlers();
11181#else
11182 /* We have interactiveness code disabled */
11183 install_special_sighandlers();
11184#endif
11185 /* bash:
11186 * if interactive but not a login shell, sources ~/.bashrc
11187 * (--norc turns this off, --rcfile <file> overrides)
11188 */
11189
11190 if (G_interactive_fd) {
11191#if ENABLE_HUSH_INTERACTIVE && ENABLE_FEATURE_EDITING_FANCY_PROMPT
11192 /* Set (but not export) PS1/2 unless already set */
11193 if (!get_local_var_value("PS1"))
11194 set_local_var_from_halves("PS1", "\\w \\$ ");
11195 if (!get_local_var_value("PS2"))
11196 set_local_var_from_halves("PS2", "> ");
11197#endif
11198 if (!ENABLE_FEATURE_SH_EXTRA_QUIET) {
11199 /* note: ash and hush share this string */
11200 printf("\n\n%s %s\n"
11201 IF_HUSH_HELP("Enter 'help' for a list of built-in commands.\n")
11202 "\n",
11203 bb_banner,
11204 "hush - the humble shell"
11205 );
11206 }
11207 }
11208
11209 parse_and_run_file(hfopen(NULL)); /* stdin */
11210
11211 final_return:
11212 hush_exit(G.last_exitcode);
11213}
11214
11215
Francis Laniel8197f012023-12-22 22:02:28 +010011216
Francis Lanielb234f7e2023-12-22 22:02:27 +010011217/*
11218 * Built-ins
11219 */
11220static int FAST_FUNC builtin_true(char **argv UNUSED_PARAM)
11221{
11222 return 0;
11223}
11224
11225#if ENABLE_HUSH_TEST || ENABLE_HUSH_ECHO || ENABLE_HUSH_PRINTF || ENABLE_HUSH_KILL
11226static NOINLINE int run_applet_main(char **argv, int (*applet_main_func)(int argc, char **argv))
11227{
11228 int argc = string_array_len(argv);
11229 return applet_main_func(argc, argv);
11230}
11231#endif
11232#if ENABLE_HUSH_TEST || BASH_TEST2
11233static int FAST_FUNC builtin_test(char **argv)
11234{
11235 return run_applet_main(argv, test_main);
11236}
11237#endif
11238#if ENABLE_HUSH_ECHO
11239static int FAST_FUNC builtin_echo(char **argv)
11240{
11241 return run_applet_main(argv, echo_main);
11242}
11243#endif
11244#if ENABLE_HUSH_PRINTF
11245static int FAST_FUNC builtin_printf(char **argv)
11246{
11247 return run_applet_main(argv, printf_main);
11248}
11249#endif
11250
11251#if ENABLE_HUSH_HELP
11252static int FAST_FUNC builtin_help(char **argv UNUSED_PARAM)
11253{
11254 const struct built_in_command *x;
11255
11256 printf(
11257 "Built-in commands:\n"
11258 "------------------\n");
11259 for (x = bltins1; x != &bltins1[ARRAY_SIZE(bltins1)]; x++) {
11260 if (x->b_descr)
11261 printf("%-10s%s\n", x->b_cmd, x->b_descr);
11262 }
11263 return EXIT_SUCCESS;
11264}
11265#endif
11266
11267#if MAX_HISTORY && ENABLE_FEATURE_EDITING
11268static int FAST_FUNC builtin_history(char **argv UNUSED_PARAM)
11269{
11270 show_history(G.line_input_state);
11271 return EXIT_SUCCESS;
11272}
11273#endif
11274
11275static char **skip_dash_dash(char **argv)
11276{
11277 argv++;
11278 if (argv[0] && argv[0][0] == '-' && argv[0][1] == '-' && argv[0][2] == '\0')
11279 argv++;
11280 return argv;
11281}
11282
11283static int FAST_FUNC builtin_cd(char **argv)
11284{
11285 const char *newdir;
11286
11287 argv = skip_dash_dash(argv);
11288 newdir = argv[0];
11289 if (newdir == NULL) {
11290 /* bash does nothing (exitcode 0) if HOME is ""; if it's unset,
11291 * bash says "bash: cd: HOME not set" and does nothing
11292 * (exitcode 1)
11293 */
11294 const char *home = get_local_var_value("HOME");
11295 newdir = home ? home : "/";
11296 }
11297 if (chdir(newdir)) {
11298 /* Mimic bash message exactly */
11299 bb_perror_msg("cd: %s", newdir);
11300 return EXIT_FAILURE;
11301 }
11302 /* Read current dir (get_cwd(1) is inside) and set PWD.
11303 * Note: do not enforce exporting. If PWD was unset or unexported,
11304 * set it again, but do not export. bash does the same.
11305 */
11306 set_pwd_var(/*flag:*/ 0);
11307 return EXIT_SUCCESS;
11308}
11309
11310static int FAST_FUNC builtin_pwd(char **argv UNUSED_PARAM)
11311{
11312 puts(get_cwd(0));
11313 return EXIT_SUCCESS;
11314}
11315
11316static int FAST_FUNC builtin_eval(char **argv)
11317{
11318 argv = skip_dash_dash(argv);
11319
11320 if (!argv[0])
11321 return EXIT_SUCCESS;
11322
11323 IF_HUSH_MODE_X(G.x_mode_depth++;)
11324 //bb_error_msg("%s: ++x_mode_depth=%d", __func__, G.x_mode_depth);
11325 if (!argv[1]) {
11326 /* bash:
11327 * eval "echo Hi; done" ("done" is syntax error):
11328 * "echo Hi" will not execute too.
11329 */
11330 parse_and_run_string(argv[0]);
11331 } else {
11332 /* "The eval utility shall construct a command by
11333 * concatenating arguments together, separating
11334 * each with a <space> character."
11335 */
11336 char *str, *p;
11337 unsigned len = 0;
11338 char **pp = argv;
11339 do
11340 len += strlen(*pp) + 1;
11341 while (*++pp);
11342 str = p = xmalloc(len);
11343 pp = argv;
11344 for (;;) {
11345 p = stpcpy(p, *pp);
11346 pp++;
11347 if (!*pp)
11348 break;
11349 *p++ = ' ';
11350 }
11351 parse_and_run_string(str);
11352 free(str);
11353 }
11354 IF_HUSH_MODE_X(G.x_mode_depth--;)
11355 //bb_error_msg("%s: --x_mode_depth=%d", __func__, G.x_mode_depth);
11356 return G.last_exitcode;
11357}
11358
11359static int FAST_FUNC builtin_exec(char **argv)
11360{
11361 argv = skip_dash_dash(argv);
11362 if (argv[0] == NULL)
11363 return EXIT_SUCCESS; /* bash does this */
11364
11365 /* Careful: we can end up here after [v]fork. Do not restore
11366 * tty pgrp then, only top-level shell process does that */
11367 if (G_saved_tty_pgrp && getpid() == G.root_pid)
11368 tcsetpgrp(G_interactive_fd, G_saved_tty_pgrp);
11369
11370 /* Saved-redirect fds, script fds and G_interactive_fd are still
11371 * open here. However, they are all CLOEXEC, and execv below
11372 * closes them. Try interactive "exec ls -l /proc/self/fd",
11373 * it should show no extra open fds in the "ls" process.
11374 * If we'd try to run builtins/NOEXECs, this would need improving.
11375 */
11376 //close_saved_fds_and_FILE_fds();
11377
11378 /* TODO: if exec fails, bash does NOT exit! We do.
11379 * We'll need to undo trap cleanup (it's inside execvp_or_die)
11380 * and tcsetpgrp, and this is inherently racy.
11381 */
11382 execvp_or_die(argv);
11383}
11384
11385static int FAST_FUNC builtin_exit(char **argv)
11386{
11387 debug_printf_exec("%s()\n", __func__);
11388
11389 /* interactive bash:
11390 * # trap "echo EEE" EXIT
11391 * # exit
11392 * exit
11393 * There are stopped jobs.
11394 * (if there are _stopped_ jobs, running ones don't count)
11395 * # exit
11396 * exit
11397 * EEE (then bash exits)
11398 *
11399 * TODO: we can use G.exiting = -1 as indicator "last cmd was exit"
11400 */
11401
11402 /* note: EXIT trap is run by hush_exit */
11403 argv = skip_dash_dash(argv);
11404 if (argv[0] == NULL) {
11405#if ENABLE_HUSH_TRAP
11406 if (G.pre_trap_exitcode >= 0) /* "exit" in trap uses $? from before the trap */
11407 hush_exit(G.pre_trap_exitcode);
11408#endif
11409 hush_exit(G.last_exitcode);
11410 }
11411 /* mimic bash: exit 123abc == exit 255 + error msg */
11412 xfunc_error_retval = 255;
11413 /* bash: exit -2 == exit 254, no error msg */
11414 hush_exit(xatoi(argv[0]) & 0xff);
11415}
11416
11417#if ENABLE_HUSH_TYPE
11418/* http://www.opengroup.org/onlinepubs/9699919799/utilities/type.html */
11419static int FAST_FUNC builtin_type(char **argv)
11420{
11421 int ret = EXIT_SUCCESS;
11422
11423 while (*++argv) {
11424 const char *type;
11425 char *path = NULL;
11426
11427 if (0) {} /* make conditional compile easier below */
11428 /*else if (find_alias(*argv))
11429 type = "an alias";*/
11430# if ENABLE_HUSH_FUNCTIONS
11431 else if (find_function(*argv))
11432 type = "a function";
11433# endif
11434 else if (find_builtin(*argv))
11435 type = "a shell builtin";
11436 else if ((path = find_in_path(*argv)) != NULL)
11437 type = path;
11438 else {
11439 bb_error_msg("type: %s: not found", *argv);
11440 ret = EXIT_FAILURE;
11441 continue;
11442 }
11443
11444 printf("%s is %s\n", *argv, type);
11445 free(path);
11446 }
11447
11448 return ret;
11449}
11450#endif
11451
11452#if ENABLE_HUSH_READ
11453/* Interruptibility of read builtin in bash
11454 * (tested on bash-4.2.8 by sending signals (not by ^C)):
11455 *
11456 * Empty trap makes read ignore corresponding signal, for any signal.
11457 *
11458 * SIGINT:
11459 * - terminates non-interactive shell;
11460 * - interrupts read in interactive shell;
11461 * if it has non-empty trap:
11462 * - executes trap and returns to command prompt in interactive shell;
11463 * - executes trap and returns to read in non-interactive shell;
11464 * SIGTERM:
11465 * - is ignored (does not interrupt) read in interactive shell;
11466 * - terminates non-interactive shell;
11467 * if it has non-empty trap:
11468 * - executes trap and returns to read;
11469 * SIGHUP:
11470 * - terminates shell (regardless of interactivity);
11471 * if it has non-empty trap:
11472 * - executes trap and returns to read;
11473 * SIGCHLD from children:
11474 * - does not interrupt read regardless of interactivity:
11475 * try: sleep 1 & read x; echo $x
11476 */
11477static int FAST_FUNC builtin_read(char **argv)
11478{
11479 const char *r;
11480 struct builtin_read_params params;
11481
11482 memset(&params, 0, sizeof(params));
11483
11484 /* "!": do not abort on errors.
11485 * Option string must start with "sr" to match BUILTIN_READ_xxx
11486 */
11487 params.read_flags = getopt32(argv,
11488# if BASH_READ_D
11489 IF_NOT_HUSH_BASH_COMPAT("^")
11490 "!srn:p:t:u:d:" IF_NOT_HUSH_BASH_COMPAT("\0" "-1"/*min 1 arg*/),
11491 &params.opt_n, &params.opt_p, &params.opt_t, &params.opt_u, &params.opt_d
11492# else
11493 IF_NOT_HUSH_BASH_COMPAT("^")
11494 "!srn:p:t:u:" IF_NOT_HUSH_BASH_COMPAT("\0" "-1"/*min 1 arg*/),
11495 &params.opt_n, &params.opt_p, &params.opt_t, &params.opt_u
11496# endif
11497//TODO: print "read: need variable name"
11498//for the case of !BASH "read" with no args (now it fails silently)
11499//(or maybe extend getopt32() to emit a message if "-1" fails)
11500 );
11501 if ((uint32_t)params.read_flags == (uint32_t)-1)
11502 return EXIT_FAILURE;
11503 argv += optind;
11504 params.argv = argv;
11505 params.setvar = set_local_var_from_halves;
11506 params.ifs = get_local_var_value("IFS"); /* can be NULL */
11507
11508 again:
11509 r = shell_builtin_read(&params);
11510
11511 if ((uintptr_t)r == 1 && errno == EINTR) {
11512 unsigned sig = check_and_run_traps();
11513 if (sig != SIGINT)
11514 goto again;
11515 }
11516
11517 if ((uintptr_t)r > 1) {
11518 bb_simple_error_msg(r);
11519 r = (char*)(uintptr_t)1;
11520 }
11521
11522 return (uintptr_t)r;
11523}
11524#endif
11525
11526#if ENABLE_HUSH_UMASK
11527static int FAST_FUNC builtin_umask(char **argv)
11528{
11529 int rc;
11530 mode_t mask;
11531
11532 rc = 1;
11533 mask = umask(0);
11534 argv = skip_dash_dash(argv);
11535 if (argv[0]) {
11536 mode_t old_mask = mask;
11537
11538 /* numeric umasks are taken as-is */
11539 /* symbolic umasks are inverted: "umask a=rx" calls umask(222) */
11540 if (!isdigit(argv[0][0]))
11541 mask ^= 0777;
11542 mask = bb_parse_mode(argv[0], mask);
11543 if (!isdigit(argv[0][0]))
11544 mask ^= 0777;
11545 if ((unsigned)mask > 0777) {
11546 mask = old_mask;
11547 /* bash messages:
11548 * bash: umask: 'q': invalid symbolic mode operator
11549 * bash: umask: 999: octal number out of range
11550 */
11551 bb_error_msg("%s: invalid mode '%s'", "umask", argv[0]);
11552 rc = 0;
11553 }
11554 } else {
11555 /* Mimic bash */
11556 printf("%04o\n", (unsigned) mask);
11557 /* fall through and restore mask which we set to 0 */
11558 }
11559 umask(mask);
11560
11561 return !rc; /* rc != 0 - success */
11562}
11563#endif
11564
11565#if ENABLE_HUSH_EXPORT || ENABLE_HUSH_TRAP
11566static void print_escaped(const char *s)
11567{
11568 if (*s == '\'')
11569 goto squote;
11570 do {
11571 const char *p = strchrnul(s, '\'');
11572 /* print 'xxxx', possibly just '' */
11573 printf("'%.*s'", (int)(p - s), s);
11574 if (*p == '\0')
11575 break;
11576 s = p;
11577 squote:
11578 /* s points to '; print "'''...'''" */
11579 putchar('"');
11580 do putchar('\''); while (*++s == '\'');
11581 putchar('"');
11582 } while (*s);
11583}
11584#endif
11585
11586#if ENABLE_HUSH_EXPORT || ENABLE_HUSH_LOCAL || ENABLE_HUSH_READONLY
11587static int helper_export_local(char **argv, unsigned flags)
11588{
11589 do {
11590 char *name = *argv;
11591 const char *name_end = endofname(name);
11592
11593 if (*name_end == '\0') {
11594 struct variable *var, **vpp;
11595
11596 vpp = get_ptr_to_local_var(name, name_end - name);
11597 var = vpp ? *vpp : NULL;
11598
11599 if (flags & SETFLAG_UNEXPORT) {
11600 /* export -n NAME (without =VALUE) */
11601 if (var) {
11602 var->flg_export = 0;
11603 debug_printf_env("%s: unsetenv '%s'\n", __func__, name);
11604 unsetenv(name);
11605 } /* else: export -n NOT_EXISTING_VAR: no-op */
11606 continue;
11607 }
11608 if (flags & SETFLAG_EXPORT) {
11609 /* export NAME (without =VALUE) */
11610 if (var) {
11611 var->flg_export = 1;
11612 debug_printf_env("%s: putenv '%s'\n", __func__, var->varstr);
11613 putenv(var->varstr);
11614 continue;
11615 }
11616 }
11617 if (flags & SETFLAG_MAKE_RO) {
11618 /* readonly NAME (without =VALUE) */
11619 if (var) {
11620 var->flg_read_only = 1;
11621 continue;
11622 }
11623 }
11624# if ENABLE_HUSH_LOCAL
11625 /* Is this "local" bltin? */
11626 if (!(flags & (SETFLAG_EXPORT|SETFLAG_UNEXPORT|SETFLAG_MAKE_RO))) {
11627 unsigned lvl = flags >> SETFLAG_VARLVL_SHIFT;
11628 if (var && var->var_nest_level == lvl) {
11629 /* "local x=abc; ...; local x" - ignore second local decl */
11630 continue;
11631 }
11632 }
11633# endif
11634 /* Exporting non-existing variable.
11635 * bash does not put it in environment,
11636 * but remembers that it is exported,
11637 * and does put it in env when it is set later.
11638 * We just set it to "" and export.
11639 */
11640 /* Or, it's "local NAME" (without =VALUE).
11641 * bash sets the value to "".
11642 */
11643 /* Or, it's "readonly NAME" (without =VALUE).
11644 * bash remembers NAME and disallows its creation
11645 * in the future.
11646 */
11647 name = xasprintf("%s=", name);
11648 } else {
11649 if (*name_end != '=') {
11650 bb_error_msg("'%s': bad variable name", name);
11651 /* do not parse following argv[]s: */
11652 return 1;
11653 }
11654 /* (Un)exporting/making local NAME=VALUE */
11655 name = xstrdup(name);
11656 /* Testcase: export PS1='\w \$ ' */
11657 unbackslash(name);
11658 }
11659 debug_printf_env("%s: set_local_var('%s')\n", __func__, name);
11660 if (set_local_var(name, flags))
11661 return EXIT_FAILURE;
11662 } while (*++argv);
11663 return EXIT_SUCCESS;
11664}
11665#endif
11666
11667#if ENABLE_HUSH_EXPORT
11668static int FAST_FUNC builtin_export(char **argv)
11669{
11670 unsigned opt_unexport;
11671
11672# if ENABLE_HUSH_EXPORT_N
11673 /* "!": do not abort on errors */
11674 opt_unexport = getopt32(argv, "!n");
11675 if (opt_unexport == (uint32_t)-1)
11676 return EXIT_FAILURE;
11677 argv += optind;
11678# else
11679 opt_unexport = 0;
11680 argv++;
11681# endif
11682
11683 if (argv[0] == NULL) {
11684 char **e = environ;
11685 if (e) {
11686 while (*e) {
11687# if 0
11688 puts(*e++);
11689# else
11690 /* ash emits: export VAR='VAL'
11691 * bash: declare -x VAR="VAL"
11692 * we follow ash example */
11693 const char *s = *e++;
11694 const char *p = strchr(s, '=');
11695
11696 if (!p) /* wtf? take next variable */
11697 continue;
11698 /* export var= */
11699 printf("export %.*s", (int)(p - s) + 1, s);
11700 print_escaped(p + 1);
11701 putchar('\n');
11702# endif
11703 }
11704 /*fflush_all(); - done after each builtin anyway */
11705 }
11706 return EXIT_SUCCESS;
11707 }
11708
11709 return helper_export_local(argv, opt_unexport ? SETFLAG_UNEXPORT : SETFLAG_EXPORT);
11710}
11711#endif
11712
11713#if ENABLE_HUSH_LOCAL
11714static int FAST_FUNC builtin_local(char **argv)
11715{
11716 if (G.func_nest_level == 0) {
11717 bb_error_msg("%s: not in a function", argv[0]);
11718 return EXIT_FAILURE; /* bash compat */
11719 }
11720 argv++;
11721 /* Since all builtins run in a nested variable level,
11722 * need to use level - 1 here. Or else the variable will be removed at once
11723 * after builtin returns.
11724 */
11725 return helper_export_local(argv, (G.var_nest_level - 1) << SETFLAG_VARLVL_SHIFT);
11726}
11727#endif
11728
11729#if ENABLE_HUSH_READONLY
11730static int FAST_FUNC builtin_readonly(char **argv)
11731{
11732 argv++;
11733 if (*argv == NULL) {
11734 /* bash: readonly [-p]: list all readonly VARs
11735 * (-p has no effect in bash)
11736 */
11737 struct variable *e;
11738 for (e = G.top_var; e; e = e->next) {
11739 if (e->flg_read_only) {
11740//TODO: quote value: readonly VAR='VAL'
11741 printf("readonly %s\n", e->varstr);
11742 }
11743 }
11744 return EXIT_SUCCESS;
11745 }
11746 return helper_export_local(argv, SETFLAG_MAKE_RO);
11747}
11748#endif
11749
11750#if ENABLE_HUSH_UNSET
11751/* http://www.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#unset */
11752static int FAST_FUNC builtin_unset(char **argv)
11753{
11754 int ret;
11755 unsigned opts;
11756
11757 /* "!": do not abort on errors */
11758 /* "+": stop at 1st non-option */
11759 opts = getopt32(argv, "!+vf");
11760 if (opts == (unsigned)-1)
11761 return EXIT_FAILURE;
11762 if (opts == 3) {
11763 bb_simple_error_msg("unset: -v and -f are exclusive");
11764 return EXIT_FAILURE;
11765 }
11766 argv += optind;
11767
11768 ret = EXIT_SUCCESS;
11769 while (*argv) {
11770 if (!(opts & 2)) { /* not -f */
11771 if (unset_local_var(*argv)) {
11772 /* unset <nonexistent_var> doesn't fail.
11773 * Error is when one tries to unset RO var.
11774 * Message was printed by unset_local_var. */
11775 ret = EXIT_FAILURE;
11776 }
11777 }
11778# if ENABLE_HUSH_FUNCTIONS
11779 else {
11780 unset_func(*argv);
11781 }
11782# endif
11783 argv++;
11784 }
11785 return ret;
11786}
11787#endif
11788
11789#if ENABLE_HUSH_SET
11790/* http://www.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#set
11791 * built-in 'set' handler
11792 * SUSv3 says:
11793 * set [-abCefhmnuvx] [-o option] [argument...]
11794 * set [+abCefhmnuvx] [+o option] [argument...]
11795 * set -- [argument...]
11796 * set -o
11797 * set +o
11798 * Implementations shall support the options in both their hyphen and
11799 * plus-sign forms. These options can also be specified as options to sh.
11800 * Examples:
11801 * Write out all variables and their values: set
11802 * Set $1, $2, and $3 and set "$#" to 3: set c a b
11803 * Turn on the -x and -v options: set -xv
11804 * Unset all positional parameters: set --
11805 * Set $1 to the value of x, even if it begins with '-' or '+': set -- "$x"
11806 * Set the positional parameters to the expansion of x, even if x expands
11807 * with a leading '-' or '+': set -- $x
11808 *
11809 * So far, we only support "set -- [argument...]" and some of the short names.
11810 */
11811static int FAST_FUNC builtin_set(char **argv)
11812{
11813 int n;
11814 char **pp, **g_argv;
11815 char *arg = *++argv;
11816
11817 if (arg == NULL) {
11818 struct variable *e;
11819 for (e = G.top_var; e; e = e->next)
11820 puts(e->varstr);
11821 return EXIT_SUCCESS;
11822 }
11823
11824 do {
11825 if (strcmp(arg, "--") == 0) {
11826 ++argv;
11827 goto set_argv;
11828 }
11829 if (arg[0] != '+' && arg[0] != '-')
11830 break;
11831 for (n = 1; arg[n]; ++n) {
11832 if (set_mode((arg[0] == '-'), arg[n], argv[1])) {
11833 bb_error_msg("%s: %s: invalid option", "set", arg);
11834 return EXIT_FAILURE;
11835 }
11836 if (arg[n] == 'o' && argv[1])
11837 argv++;
11838 }
11839 } while ((arg = *++argv) != NULL);
11840 /* Now argv[0] is 1st argument */
11841
11842 if (arg == NULL)
11843 return EXIT_SUCCESS;
11844 set_argv:
11845
11846 /* NB: G.global_argv[0] ($0) is never freed/changed */
11847 g_argv = G.global_argv;
11848 if (G.global_args_malloced) {
11849 pp = g_argv;
11850 while (*++pp)
11851 free(*pp);
11852 g_argv[1] = NULL;
11853 } else {
11854 G.global_args_malloced = 1;
11855 pp = xzalloc(sizeof(pp[0]) * 2);
11856 pp[0] = g_argv[0]; /* retain $0 */
11857 g_argv = pp;
11858 }
11859 /* This realloc's G.global_argv */
11860 G.global_argv = pp = add_strings_to_strings(g_argv, argv, /*dup:*/ 1);
11861
11862 G.global_argc = 1 + string_array_len(pp + 1);
11863
11864 return EXIT_SUCCESS;
11865}
11866#endif
11867
11868static int FAST_FUNC builtin_shift(char **argv)
11869{
11870 int n = 1;
11871 argv = skip_dash_dash(argv);
11872 if (argv[0]) {
11873 n = bb_strtou(argv[0], NULL, 10);
11874 if (errno || n < 0) {
11875 /* shared string with ash.c */
11876 bb_error_msg("Illegal number: %s", argv[0]);
11877 /*
11878 * ash aborts in this case.
11879 * bash prints error message and set $? to 1.
11880 * Interestingly, for "shift 99999" bash does not
11881 * print error message, but does set $? to 1
11882 * (and does no shifting at all).
11883 */
11884 }
11885 }
11886 if (n >= 0 && n < G.global_argc) {
11887 if (G_global_args_malloced) {
11888 int m = 1;
11889 while (m <= n)
11890 free(G.global_argv[m++]);
11891 }
11892 G.global_argc -= n;
11893 memmove(&G.global_argv[1], &G.global_argv[n+1],
11894 G.global_argc * sizeof(G.global_argv[0]));
11895 return EXIT_SUCCESS;
11896 }
11897 return EXIT_FAILURE;
11898}
11899
11900#if ENABLE_HUSH_GETOPTS
11901static int FAST_FUNC builtin_getopts(char **argv)
11902{
11903/* http://pubs.opengroup.org/onlinepubs/9699919799/utilities/getopts.html
11904
11905TODO:
11906If a required argument is not found, and getopts is not silent,
11907a question mark (?) is placed in VAR, OPTARG is unset, and a
11908diagnostic message is printed. If getopts is silent, then a
11909colon (:) is placed in VAR and OPTARG is set to the option
11910character found.
11911
11912Test that VAR is a valid variable name?
11913
11914"Whenever the shell is invoked, OPTIND shall be initialized to 1"
11915*/
11916 char cbuf[2];
11917 const char *cp, *optstring, *var;
11918 int c, n, exitcode, my_opterr;
11919 unsigned count;
11920
11921 optstring = *++argv;
11922 if (!optstring || !(var = *++argv)) {
11923 bb_simple_error_msg("usage: getopts OPTSTRING VAR [ARGS]");
11924 return EXIT_FAILURE;
11925 }
11926
11927 if (argv[1])
11928 argv[0] = G.global_argv[0]; /* for error messages in getopt() */
11929 else
11930 argv = G.global_argv;
11931 cbuf[1] = '\0';
11932
11933 my_opterr = 0;
11934 if (optstring[0] != ':') {
11935 cp = get_local_var_value("OPTERR");
11936 /* 0 if "OPTERR=0", 1 otherwise */
11937 my_opterr = (!cp || NOT_LONE_CHAR(cp, '0'));
11938 }
11939
11940 /* getopts stops on first non-option. Add "+" to force that */
11941 /*if (optstring[0] != '+')*/ {
11942 char *s = alloca(strlen(optstring) + 2);
11943 sprintf(s, "+%s", optstring);
11944 optstring = s;
11945 }
11946
11947 /* Naively, now we should just
11948 * cp = get_local_var_value("OPTIND");
11949 * optind = cp ? atoi(cp) : 0;
11950 * optarg = NULL;
11951 * opterr = my_opterr;
11952 * c = getopt(string_array_len(argv), argv, optstring);
11953 * and be done? Not so fast...
11954 * Unlike normal getopt() usage in C programs, here
11955 * each successive call will (usually) have the same argv[] CONTENTS,
11956 * but not the ADDRESSES. Worse yet, it's possible that between
11957 * invocations of "getopts", there will be calls to shell builtins
11958 * which use getopt() internally. Example:
11959 * while getopts "abc" RES -a -bc -abc de; do
11960 * unset -ff func
11961 * done
11962 * This would not work correctly: getopt() call inside "unset"
11963 * modifies internal libc state which is tracking position in
11964 * multi-option strings ("-abc"). At best, it can skip options
11965 * or return the same option infinitely. With glibc implementation
11966 * of getopt(), it would use outright invalid pointers and return
11967 * garbage even _without_ "unset" mangling internal state.
11968 *
11969 * We resort to resetting getopt() state and calling it N times,
11970 * until we get Nth result (or failure).
11971 * (N == G.getopt_count is reset to 0 whenever OPTIND is [un]set).
11972 */
11973 GETOPT_RESET();
11974 count = 0;
11975 n = string_array_len(argv);
11976 do {
11977 optarg = NULL;
11978 opterr = (count < G.getopt_count) ? 0 : my_opterr;
11979 c = getopt(n, argv, optstring);
11980 if (c < 0)
11981 break;
11982 count++;
11983 } while (count <= G.getopt_count);
11984
11985 /* Set OPTIND. Prevent resetting of the magic counter! */
11986 set_local_var_from_halves("OPTIND", utoa(optind));
11987 G.getopt_count = count; /* "next time, give me N+1'th result" */
11988 GETOPT_RESET(); /* just in case */
11989
11990 /* Set OPTARG */
11991 /* Always set or unset, never left as-is, even on exit/error:
11992 * "If no option was found, or if the option that was found
11993 * does not have an option-argument, OPTARG shall be unset."
11994 */
11995 cp = optarg;
11996 if (c == '?') {
11997 /* If ":optstring" and unknown option is seen,
11998 * it is stored to OPTARG.
11999 */
12000 if (optstring[1] == ':') {
12001 cbuf[0] = optopt;
12002 cp = cbuf;
12003 }
12004 }
12005 if (cp)
12006 set_local_var_from_halves("OPTARG", cp);
12007 else
12008 unset_local_var("OPTARG");
12009
12010 /* Convert -1 to "?" */
12011 exitcode = EXIT_SUCCESS;
12012 if (c < 0) { /* -1: end of options */
12013 exitcode = EXIT_FAILURE;
12014 c = '?';
12015 }
12016
12017 /* Set VAR */
12018 cbuf[0] = c;
12019 set_local_var_from_halves(var, cbuf);
12020
12021 return exitcode;
12022}
12023#endif
12024
12025static int FAST_FUNC builtin_source(char **argv)
12026{
12027 char *arg_path, *filename;
12028 HFILE *input;
12029 save_arg_t sv;
12030 char *args_need_save;
12031#if ENABLE_HUSH_FUNCTIONS
12032 smallint sv_flg;
12033#endif
12034
12035 argv = skip_dash_dash(argv);
12036 filename = argv[0];
12037 if (!filename) {
12038 /* bash says: "bash: .: filename argument required" */
12039 return 2; /* bash compat */
12040 }
12041 arg_path = NULL;
12042 if (!strchr(filename, '/')) {
12043 arg_path = find_in_path(filename);
12044 if (arg_path)
12045 filename = arg_path;
12046 else if (!ENABLE_HUSH_BASH_SOURCE_CURDIR) {
12047 errno = ENOENT;
12048 bb_simple_perror_msg(filename);
12049 return EXIT_FAILURE;
12050 }
12051 }
12052 input = hfopen(filename);
12053 free(arg_path);
12054 if (!input) {
12055 bb_perror_msg("%s", filename);
12056 /* POSIX: non-interactive shell should abort here,
12057 * not merely fail. So far no one complained :)
12058 */
12059 return EXIT_FAILURE;
12060 }
12061
12062#if ENABLE_HUSH_FUNCTIONS
12063 sv_flg = G_flag_return_in_progress;
12064 /* "we are inside sourced file, ok to use return" */
12065 G_flag_return_in_progress = -1;
12066#endif
12067 args_need_save = argv[1]; /* used as a boolean variable */
12068 if (args_need_save)
12069 save_and_replace_G_args(&sv, argv);
12070
12071 /* "false; . ./empty_line; echo Zero:$?" should print 0 */
12072 G.last_exitcode = 0;
12073 parse_and_run_file(input);
12074 hfclose(input);
12075
12076 if (args_need_save) /* can't use argv[1] instead: "shift" can mangle it */
12077 restore_G_args(&sv, argv);
12078#if ENABLE_HUSH_FUNCTIONS
12079 G_flag_return_in_progress = sv_flg;
12080#endif
12081
12082 return G.last_exitcode;
12083}
12084
12085#if ENABLE_HUSH_TRAP
12086static int FAST_FUNC builtin_trap(char **argv)
12087{
12088 int sig;
12089 char *new_cmd;
12090
12091 if (!G_traps)
12092 G_traps = xzalloc(sizeof(G_traps[0]) * NSIG);
12093
12094 argv++;
12095 if (!*argv) {
12096 int i;
12097 /* No args: print all trapped */
12098 for (i = 0; i < NSIG; ++i) {
12099 if (G_traps[i]) {
12100 printf("trap -- ");
12101 print_escaped(G_traps[i]);
12102 /* note: bash adds "SIG", but only if invoked
12103 * as "bash". If called as "sh", or if set -o posix,
12104 * then it prints short signal names.
12105 * We are printing short names: */
12106 printf(" %s\n", get_signame(i));
12107 }
12108 }
12109 /*fflush_all(); - done after each builtin anyway */
12110 return EXIT_SUCCESS;
12111 }
12112
12113 new_cmd = NULL;
12114 /* If first arg is a number: reset all specified signals */
12115 sig = bb_strtou(*argv, NULL, 10);
12116 if (errno == 0) {
12117 int ret;
12118 process_sig_list:
12119 ret = EXIT_SUCCESS;
12120 while (*argv) {
12121 sighandler_t handler;
12122
12123 sig = get_signum(*argv++);
12124 if (sig < 0) {
12125 ret = EXIT_FAILURE;
12126 /* Mimic bash message exactly */
12127 bb_error_msg("trap: %s: invalid signal specification", argv[-1]);
12128 continue;
12129 }
12130
12131 free(G_traps[sig]);
12132 G_traps[sig] = xstrdup(new_cmd);
12133
12134 debug_printf("trap: setting SIG%s (%i) to '%s'\n",
12135 get_signame(sig), sig, G_traps[sig]);
12136
12137 /* There is no signal for 0 (EXIT) */
12138 if (sig == 0)
12139 continue;
12140
12141 if (new_cmd)
12142 handler = (new_cmd[0] ? record_pending_signo : SIG_IGN);
12143 else
12144 /* We are removing trap handler */
12145 handler = pick_sighandler(sig);
12146 install_sighandler(sig, handler);
12147 }
12148 return ret;
12149 }
12150
12151 if (!argv[1]) { /* no second arg */
12152 bb_simple_error_msg("trap: invalid arguments");
12153 return EXIT_FAILURE;
12154 }
12155
12156 /* First arg is "-": reset all specified to default */
12157 /* First arg is "--": skip it, the rest is "handler SIGs..." */
12158 /* Everything else: set arg as signal handler
12159 * (includes "" case, which ignores signal) */
12160 if (argv[0][0] == '-') {
12161 if (argv[0][1] == '\0') { /* "-" */
12162 /* new_cmd remains NULL: "reset these sigs" */
12163 goto reset_traps;
12164 }
12165 if (argv[0][1] == '-' && argv[0][2] == '\0') { /* "--" */
12166 argv++;
12167 }
12168 /* else: "-something", no special meaning */
12169 }
12170 new_cmd = *argv;
12171 reset_traps:
12172 argv++;
12173 goto process_sig_list;
12174}
12175#endif
12176
12177#if ENABLE_HUSH_JOB
12178static struct pipe *parse_jobspec(const char *str)
12179{
12180 struct pipe *pi;
12181 unsigned jobnum;
12182
12183 if (sscanf(str, "%%%u", &jobnum) != 1) {
12184 if (str[0] != '%'
12185 || (str[1] != '%' && str[1] != '+' && str[1] != '\0')
12186 ) {
12187 bb_error_msg("bad argument '%s'", str);
12188 return NULL;
12189 }
12190 /* It is "%%", "%+" or "%" - current job */
12191 jobnum = G.last_jobid;
12192 if (jobnum == 0) {
12193 bb_simple_error_msg("no current job");
12194 return NULL;
12195 }
12196 }
12197 for (pi = G.job_list; pi; pi = pi->next) {
12198 if (pi->jobid == jobnum) {
12199 return pi;
12200 }
12201 }
12202 bb_error_msg("%u: no such job", jobnum);
12203 return NULL;
12204}
12205
12206static int FAST_FUNC builtin_jobs(char **argv UNUSED_PARAM)
12207{
12208 struct pipe *job;
12209 const char *status_string;
12210
12211 checkjobs(NULL, 0 /*(no pid to wait for)*/);
12212 for (job = G.job_list; job; job = job->next) {
12213 if (job->alive_cmds == job->stopped_cmds)
12214 status_string = "Stopped";
12215 else
12216 status_string = "Running";
12217
12218 printf(JOB_STATUS_FORMAT, job->jobid, status_string, job->cmdtext);
12219 }
12220
12221 clean_up_last_dead_job();
12222
12223 return EXIT_SUCCESS;
12224}
12225
12226/* built-in 'fg' and 'bg' handler */
12227static int FAST_FUNC builtin_fg_bg(char **argv)
12228{
12229 int i;
12230 struct pipe *pi;
12231
12232 if (!G_interactive_fd)
12233 return EXIT_FAILURE;
12234
12235 /* If they gave us no args, assume they want the last backgrounded task */
12236 if (!argv[1]) {
12237 for (pi = G.job_list; pi; pi = pi->next) {
12238 if (pi->jobid == G.last_jobid) {
12239 goto found;
12240 }
12241 }
12242 bb_error_msg("%s: no current job", argv[0]);
12243 return EXIT_FAILURE;
12244 }
12245
12246 pi = parse_jobspec(argv[1]);
12247 if (!pi)
12248 return EXIT_FAILURE;
12249 found:
12250 /* TODO: bash prints a string representation
12251 * of job being foregrounded (like "sleep 1 | cat") */
12252 if (argv[0][0] == 'f' && G_saved_tty_pgrp) {
12253 /* Put the job into the foreground. */
12254 tcsetpgrp(G_interactive_fd, pi->pgrp);
12255 }
12256
12257 /* Restart the processes in the job */
12258 debug_printf_jobs("reviving %d procs, pgrp %d\n", pi->num_cmds, pi->pgrp);
12259 for (i = 0; i < pi->num_cmds; i++) {
12260 debug_printf_jobs("reviving pid %d\n", pi->cmds[i].pid);
12261 }
12262 pi->stopped_cmds = 0;
12263
12264 i = kill(- pi->pgrp, SIGCONT);
12265 if (i < 0) {
12266 if (errno == ESRCH) {
12267 delete_finished_job(pi);
12268 return EXIT_SUCCESS;
12269 }
12270 bb_simple_perror_msg("kill (SIGCONT)");
12271 }
12272
12273 if (argv[0][0] == 'f') {
12274 remove_job_from_table(pi); /* FG job shouldn't be in job table */
12275 return checkjobs_and_fg_shell(pi);
12276 }
12277 return EXIT_SUCCESS;
12278}
12279#endif
12280
12281#if ENABLE_HUSH_KILL
12282static int FAST_FUNC builtin_kill(char **argv)
12283{
12284 int ret = 0;
12285
12286# if ENABLE_HUSH_JOB
12287 if (argv[1] && strcmp(argv[1], "-l") != 0) {
12288 int i = 1;
12289
12290 do {
12291 struct pipe *pi;
12292 char *dst;
12293 int j, n;
12294
12295 if (argv[i][0] != '%')
12296 continue;
12297 /*
12298 * "kill %N" - job kill
12299 * Converting to pgrp / pid kill
12300 */
12301 pi = parse_jobspec(argv[i]);
12302 if (!pi) {
12303 /* Eat bad jobspec */
12304 j = i;
12305 do {
12306 j++;
12307 argv[j - 1] = argv[j];
12308 } while (argv[j]);
12309 ret = 1;
12310 i--;
12311 continue;
12312 }
12313 /*
12314 * In jobs started under job control, we signal
12315 * entire process group by kill -PGRP_ID.
12316 * This happens, f.e., in interactive shell.
12317 *
12318 * Otherwise, we signal each child via
12319 * kill PID1 PID2 PID3.
12320 * Testcases:
12321 * sh -c 'sleep 1|sleep 1 & kill %1'
12322 * sh -c 'true|sleep 2 & sleep 1; kill %1'
12323 * sh -c 'true|sleep 1 & sleep 2; kill %1'
12324 */
12325 n = G_interactive_fd ? 1 : pi->num_cmds;
12326 dst = alloca(n * sizeof(int)*4);
12327 argv[i] = dst;
12328 if (G_interactive_fd)
12329 dst += sprintf(dst, " -%u", (int)pi->pgrp);
12330 else for (j = 0; j < n; j++) {
12331 struct command *cmd = &pi->cmds[j];
12332 /* Skip exited members of the job */
12333 if (cmd->pid == 0)
12334 continue;
12335 /*
12336 * kill_main has matching code to expect
12337 * leading space. Needed to not confuse
12338 * negative pids with "kill -SIGNAL_NO" syntax
12339 */
12340 dst += sprintf(dst, " %u", (int)cmd->pid);
12341 }
12342 *dst = '\0';
12343 } while (argv[++i]);
12344 }
12345# endif
12346
12347 if (argv[1] || ret == 0) {
12348 ret = run_applet_main(argv, kill_main);
12349 }
12350 /* else: ret = 1, "kill %bad_jobspec" case */
12351 return ret;
12352}
12353#endif
12354
12355#if ENABLE_HUSH_WAIT
12356/* http://www.opengroup.org/onlinepubs/9699919799/utilities/wait.html */
12357# if !ENABLE_HUSH_JOB
12358# define wait_for_child_or_signal(pipe,pid) wait_for_child_or_signal(pid)
12359# endif
12360static int wait_for_child_or_signal(struct pipe *waitfor_pipe, pid_t waitfor_pid)
12361{
12362 int ret = 0;
12363 for (;;) {
12364 int sig;
12365 sigset_t oldset;
12366
12367 if (!sigisemptyset(&G.pending_set))
12368 goto check_sig;
12369
12370 /* waitpid is not interruptible by SA_RESTARTed
12371 * signals which we use. Thus, this ugly dance:
12372 */
12373
12374 /* Make sure possible SIGCHLD is stored in kernel's
12375 * pending signal mask before we call waitpid.
12376 * Or else we may race with SIGCHLD, lose it,
12377 * and get stuck in sigsuspend...
12378 */
12379 sigfillset(&oldset); /* block all signals, remember old set */
12380 sigprocmask2(SIG_SETMASK, &oldset);
12381
12382 if (!sigisemptyset(&G.pending_set)) {
12383 /* Crap! we raced with some signal! */
12384 goto restore;
12385 }
12386
12387 /*errno = 0; - checkjobs does this */
12388/* Can't pass waitfor_pipe into checkjobs(): it won't be interruptible */
12389 ret = checkjobs(NULL, waitfor_pid); /* waitpid(WNOHANG) inside */
12390 debug_printf_exec("checkjobs:%d\n", ret);
12391# if ENABLE_HUSH_JOB
12392 if (waitfor_pipe) {
12393 int rcode = job_exited_or_stopped(waitfor_pipe);
12394 debug_printf_exec("job_exited_or_stopped:%d\n", rcode);
12395 if (rcode >= 0) {
12396 ret = rcode;
12397 sigprocmask(SIG_SETMASK, &oldset, NULL);
12398 break;
12399 }
12400 }
12401# endif
12402 /* if ECHILD, there are no children (ret is -1 or 0) */
12403 /* if ret == 0, no children changed state */
12404 /* if ret != 0, it's exitcode+1 of exited waitfor_pid child */
12405 if (errno == ECHILD || ret) {
12406 ret--;
12407 if (ret < 0) /* if ECHILD, may need to fix "ret" */
12408 ret = 0;
12409# if ENABLE_HUSH_BASH_COMPAT
12410 if (waitfor_pid == -1 && errno == ECHILD) {
12411 /* exitcode of "wait -n" with no children is 127, not 0 */
12412 ret = 127;
12413 }
12414# endif
12415 sigprocmask(SIG_SETMASK, &oldset, NULL);
12416 break;
12417 }
12418 /* Wait for SIGCHLD or any other signal */
12419 /* It is vitally important for sigsuspend that SIGCHLD has non-DFL handler! */
12420 /* Note: sigsuspend invokes signal handler */
12421 sigsuspend(&oldset);
12422 /* ^^^ add "sigdelset(&oldset, SIGCHLD)" before sigsuspend
12423 * to make sure SIGCHLD is not masked off?
12424 * It was reported that this:
12425 * fn() { : | return; }
12426 * shopt -s lastpipe
12427 * fn
12428 * exec hush SCRIPT
12429 * under bash 4.4.23 runs SCRIPT with SIGCHLD masked,
12430 * making "wait" commands in SCRIPT block forever.
12431 */
12432 restore:
12433 sigprocmask(SIG_SETMASK, &oldset, NULL);
12434 check_sig:
12435 /* So, did we get a signal? */
12436 sig = check_and_run_traps();
12437 if (sig /*&& sig != SIGCHLD - always true */) {
12438 /* Do this for any (non-ignored) signal, not only for ^C */
12439 ret = 128 | sig;
12440 break;
12441 }
12442 /* SIGCHLD, or no signal, or ignored one, such as SIGQUIT. Repeat */
12443 }
12444 return ret;
12445}
12446
12447static int FAST_FUNC builtin_wait(char **argv)
12448{
12449 int ret;
12450 int status;
12451
12452 argv = skip_dash_dash(argv);
12453# if ENABLE_HUSH_BASH_COMPAT
12454 if (argv[0] && strcmp(argv[0], "-n") == 0) {
12455 /* wait -n */
12456 /* (bash accepts "wait -n PID" too and ignores PID) */
12457 G.dead_job_exitcode = -1;
12458 return wait_for_child_or_signal(NULL, -1 /*no job, wait for one job*/);
12459 }
12460# endif
12461 if (argv[0] == NULL) {
12462 /* Don't care about wait results */
12463 /* Note 1: must wait until there are no more children */
12464 /* Note 2: must be interruptible */
12465 /* Examples:
12466 * $ sleep 3 & sleep 6 & wait
12467 * [1] 30934 sleep 3
12468 * [2] 30935 sleep 6
12469 * [1] Done sleep 3
12470 * [2] Done sleep 6
12471 * $ sleep 3 & sleep 6 & wait
12472 * [1] 30936 sleep 3
12473 * [2] 30937 sleep 6
12474 * [1] Done sleep 3
12475 * ^C <-- after ~4 sec from keyboard
12476 * $
12477 */
12478 return wait_for_child_or_signal(NULL, 0 /*no job and no pid to wait for*/);
12479 }
12480
12481 do {
12482 pid_t pid = bb_strtou(*argv, NULL, 10);
12483 if (errno || pid <= 0) {
12484# if ENABLE_HUSH_JOB
12485 if (argv[0][0] == '%') {
12486 struct pipe *wait_pipe;
12487 ret = 127; /* bash compat for bad jobspecs */
12488 wait_pipe = parse_jobspec(*argv);
12489 if (wait_pipe) {
12490 ret = job_exited_or_stopped(wait_pipe);
12491 if (ret < 0) {
12492 ret = wait_for_child_or_signal(wait_pipe, 0);
12493 } else {
12494 /* waiting on "last dead job" removes it */
12495 clean_up_last_dead_job();
12496 }
12497 }
12498 /* else: parse_jobspec() already emitted error msg */
12499 continue;
12500 }
12501# endif
12502 /* mimic bash message */
12503 bb_error_msg("wait: '%s': not a pid or valid job spec", *argv);
12504 ret = EXIT_FAILURE;
12505 continue; /* bash checks all argv[] */
12506 }
12507
12508 /* Do we have such child? */
12509 ret = waitpid(pid, &status, WNOHANG);
12510 if (ret < 0) {
12511 /* No */
12512 ret = 127;
12513 if (errno == ECHILD) {
12514 if (pid == G.last_bg_pid) {
12515 /* "wait $!" but last bg task has already exited. Try:
12516 * (sleep 1; exit 3) & sleep 2; echo $?; wait $!; echo $?
12517 * In bash it prints exitcode 0, then 3.
12518 * In dash, it is 127.
12519 */
12520 ret = G.last_bg_pid_exitcode;
12521 } else {
12522 /* Example: "wait 1". mimic bash message */
12523 bb_error_msg("wait: pid %u is not a child of this shell", (unsigned)pid);
12524 }
12525 } else {
12526 /* ??? */
12527 bb_perror_msg("wait %s", *argv);
12528 }
12529 continue; /* bash checks all argv[] */
12530 }
12531 if (ret == 0) {
12532 /* Yes, and it still runs */
12533 ret = wait_for_child_or_signal(NULL, pid);
12534 } else {
12535 /* Yes, and it just exited */
12536 process_wait_result(NULL, pid, status);
12537 ret = WEXITSTATUS(status);
12538 if (WIFSIGNALED(status))
12539 ret = 128 | WTERMSIG(status);
12540 }
12541 } while (*++argv);
12542
12543 return ret;
12544}
12545#endif
12546
12547#if ENABLE_HUSH_LOOPS || ENABLE_HUSH_FUNCTIONS
12548static unsigned parse_numeric_argv1(char **argv, unsigned def, unsigned def_min)
12549{
12550 if (argv[1]) {
12551 def = bb_strtou(argv[1], NULL, 10);
12552 if (errno || def < def_min || argv[2]) {
12553 bb_error_msg("%s: bad arguments", argv[0]);
12554 def = UINT_MAX;
12555 }
12556 }
12557 return def;
12558}
12559#endif
12560
12561#if ENABLE_HUSH_LOOPS
12562static int FAST_FUNC builtin_break(char **argv)
12563{
12564 unsigned depth;
12565 if (G.depth_of_loop == 0) {
12566 bb_error_msg("%s: only meaningful in a loop", argv[0]);
12567 /* if we came from builtin_continue(), need to undo "= 1" */
12568 G.flag_break_continue = 0;
12569 return EXIT_SUCCESS; /* bash compat */
12570 }
12571 G.flag_break_continue++; /* BC_BREAK = 1, or BC_CONTINUE = 2 */
12572
12573 G.depth_break_continue = depth = parse_numeric_argv1(argv, 1, 1);
12574 if (depth == UINT_MAX)
12575 G.flag_break_continue = BC_BREAK;
12576 if (G.depth_of_loop < depth)
12577 G.depth_break_continue = G.depth_of_loop;
12578
12579 return EXIT_SUCCESS;
12580}
12581
12582static int FAST_FUNC builtin_continue(char **argv)
12583{
12584 G.flag_break_continue = 1; /* BC_CONTINUE = 2 = 1+1 */
12585 return builtin_break(argv);
12586}
12587#endif
12588
12589#if ENABLE_HUSH_FUNCTIONS
12590static int FAST_FUNC builtin_return(char **argv)
12591{
12592 int rc;
12593
12594 if (G_flag_return_in_progress != -1) {
12595 bb_error_msg("%s: not in a function or sourced script", argv[0]);
12596 return EXIT_FAILURE; /* bash compat */
12597 }
12598
12599 G_flag_return_in_progress = 1;
12600
12601 /* bash:
12602 * out of range: wraps around at 256, does not error out
12603 * non-numeric param:
12604 * f() { false; return qwe; }; f; echo $?
12605 * bash: return: qwe: numeric argument required <== we do this
12606 * 255 <== we also do this
12607 */
12608 rc = parse_numeric_argv1(argv, G.last_exitcode, 0);
12609# if ENABLE_HUSH_TRAP
12610 if (argv[1]) { /* "return ARG" inside a running trap sets $? */
12611 debug_printf_exec("G.return_exitcode=%d\n", rc);
12612 G.return_exitcode = rc;
12613 }
12614# endif
12615 return rc;
12616}
12617#endif
12618
12619#if ENABLE_HUSH_TIMES
12620static int FAST_FUNC builtin_times(char **argv UNUSED_PARAM)
12621{
12622 static const uint8_t times_tbl[] ALIGN1 = {
12623 ' ', offsetof(struct tms, tms_utime),
12624 '\n', offsetof(struct tms, tms_stime),
12625 ' ', offsetof(struct tms, tms_cutime),
12626 '\n', offsetof(struct tms, tms_cstime),
12627 0
12628 };
12629 const uint8_t *p;
12630 unsigned clk_tck;
12631 struct tms buf;
12632
12633 clk_tck = bb_clk_tck();
12634
12635 times(&buf);
12636 p = times_tbl;
12637 do {
12638 unsigned sec, frac;
12639 unsigned long t;
12640 t = *(clock_t *)(((char *) &buf) + p[1]);
12641 sec = t / clk_tck;
12642 frac = t % clk_tck;
12643 printf("%um%u.%03us%c",
12644 sec / 60, sec % 60,
12645 (frac * 1000) / clk_tck,
12646 p[0]);
12647 p += 2;
12648 } while (*p);
12649
12650 return EXIT_SUCCESS;
12651}
12652#endif
12653
12654#if ENABLE_HUSH_MEMLEAK
12655static int FAST_FUNC builtin_memleak(char **argv UNUSED_PARAM)
12656{
12657 void *p;
12658 unsigned long l;
12659
12660# ifdef M_TRIM_THRESHOLD
12661 /* Optional. Reduces probability of false positives */
12662 malloc_trim(0);
12663# endif
12664 /* Crude attempt to find where "free memory" starts,
12665 * sans fragmentation. */
12666 p = malloc(240);
12667 l = (unsigned long)p;
12668 free(p);
12669 p = malloc(3400);
12670 if (l < (unsigned long)p) l = (unsigned long)p;
12671 free(p);
12672
12673
12674# if 0 /* debug */
12675 {
12676 struct mallinfo mi = mallinfo();
12677 printf("top alloc:0x%lx malloced:%d+%d=%d\n", l,
12678 mi.arena, mi.hblkhd, mi.arena + mi.hblkhd);
12679 }
12680# endif
12681
12682 if (!G.memleak_value)
12683 G.memleak_value = l;
12684
12685 l -= G.memleak_value;
12686 if ((long)l < 0)
12687 l = 0;
12688 l /= 1024;
12689 if (l > 127)
12690 l = 127;
12691
12692 /* Exitcode is "how many kilobytes we leaked since 1st call" */
12693 return l;
12694}
12695#endif
Francis Laniel8197f012023-12-22 22:02:28 +010012696#endif /* !__U_BOOT__ */