blob: ff4a57c6e3a453c70f2bd29c33fddb7cb49895c1 [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
3488/*
3489 * Globbing routines.
3490 *
3491 * Most words in commands need to be globbed, even ones which are
3492 * (single or double) quoted. This stems from the possiblity of
3493 * constructs like "abc"* and 'abc'* - these should be globbed.
3494 * Having a different code path for fully-quoted strings ("abc",
3495 * 'abc') would only help performance-wise, but we still need
3496 * code for partially-quoted strings.
3497 *
3498 * Unfortunately, if we want to match bash and ash behavior in all cases,
3499 * the logic can't be "shell-syntax argument is first transformed
3500 * to a string, then globbed, and if globbing does not match anything,
3501 * it is used verbatim". Here are two examples where it fails:
3502 *
3503 * echo 'b\*'?
3504 *
3505 * The globbing can't be avoided (because of '?' at the end).
3506 * The glob pattern is: b\\\*? - IOW, both \ and * are literals
3507 * and are glob-escaped. If this does not match, bash/ash print b\*?
3508 * - IOW: they "unbackslash" the glob pattern.
3509 * Now, look at this:
3510 *
3511 * v='\\\*'; echo b$v?
3512 *
3513 * The glob pattern is the same here: b\\\*? - the unquoted $v expansion
3514 * should be used as glob pattern with no changes. However, if glob
3515 * does not match, bash/ash print b\\\*? - NOT THE SAME as first example!
3516 *
3517 * ash implements this by having an encoded representation of the word
3518 * to glob, which IS NOT THE SAME as the glob pattern - it has more data.
3519 * Glob pattern is derived from it. If glob fails, the decision what result
3520 * should be is made using that encoded representation. Not glob pattern.
3521 */
3522
3523#if ENABLE_HUSH_BRACE_EXPANSION
3524/* There in a GNU extension, GLOB_BRACE, but it is not usable:
3525 * first, it processes even {a} (no commas), second,
3526 * I didn't manage to make it return strings when they don't match
3527 * existing files. Need to re-implement it.
3528 */
3529
3530/* Helper */
3531static int glob_needed(const char *s)
3532{
3533 while (*s) {
3534 if (*s == '\\') {
3535 if (!s[1])
3536 return 0;
3537 s += 2;
3538 continue;
3539 }
3540 if (*s == '*' || *s == '[' || *s == '?' || *s == '{')
3541 return 1;
3542 s++;
3543 }
3544 return 0;
3545}
3546/* Return pointer to next closing brace or to comma */
3547static const char *next_brace_sub(const char *cp)
3548{
3549 unsigned depth = 0;
3550 cp++;
3551 while (*cp != '\0') {
3552 if (*cp == '\\') {
3553 if (*++cp == '\0')
3554 break;
3555 cp++;
3556 continue;
3557 }
3558 if ((*cp == '}' && depth-- == 0) || (*cp == ',' && depth == 0))
3559 break;
3560 if (*cp++ == '{')
3561 depth++;
3562 }
3563
3564 return *cp != '\0' ? cp : NULL;
3565}
3566/* Recursive brace globber. Note: may garble pattern[]. */
3567static int glob_brace(char *pattern, o_string *o, int n)
3568{
3569 char *new_pattern_buf;
3570 const char *begin;
3571 const char *next;
3572 const char *rest;
3573 const char *p;
3574 size_t rest_len;
3575
3576 debug_printf_glob("glob_brace('%s')\n", pattern);
3577
3578 begin = pattern;
3579 while (1) {
3580 if (*begin == '\0')
3581 goto simple_glob;
3582 if (*begin == '{') {
3583 /* Find the first sub-pattern and at the same time
3584 * find the rest after the closing brace */
3585 next = next_brace_sub(begin);
3586 if (next == NULL) {
3587 /* An illegal expression */
3588 goto simple_glob;
3589 }
3590 if (*next == '}') {
3591 /* "{abc}" with no commas - illegal
3592 * brace expr, disregard and skip it */
3593 begin = next + 1;
3594 continue;
3595 }
3596 break;
3597 }
3598 if (*begin == '\\' && begin[1] != '\0')
3599 begin++;
3600 begin++;
3601 }
3602 debug_printf_glob("begin:%s\n", begin);
3603 debug_printf_glob("next:%s\n", next);
3604
3605 /* Now find the end of the whole brace expression */
3606 rest = next;
3607 while (*rest != '}') {
3608 rest = next_brace_sub(rest);
3609 if (rest == NULL) {
3610 /* An illegal expression */
3611 goto simple_glob;
3612 }
3613 debug_printf_glob("rest:%s\n", rest);
3614 }
3615 rest_len = strlen(++rest) + 1;
3616
3617 /* We are sure the brace expression is well-formed */
3618
3619 /* Allocate working buffer large enough for our work */
3620 new_pattern_buf = xmalloc(strlen(pattern));
3621
3622 /* We have a brace expression. BEGIN points to the opening {,
3623 * NEXT points past the terminator of the first element, and REST
3624 * points past the final }. We will accumulate result names from
3625 * recursive runs for each brace alternative in the buffer using
3626 * GLOB_APPEND. */
3627
3628 p = begin + 1;
3629 while (1) {
3630 /* Construct the new glob expression */
3631 memcpy(
3632 mempcpy(
3633 mempcpy(new_pattern_buf,
3634 /* We know the prefix for all sub-patterns */
3635 pattern, begin - pattern),
3636 p, next - p),
3637 rest, rest_len);
3638
3639 /* Note: glob_brace() may garble new_pattern_buf[].
3640 * That's why we re-copy prefix every time (1st memcpy above).
3641 */
3642 n = glob_brace(new_pattern_buf, o, n);
3643 if (*next == '}') {
3644 /* We saw the last entry */
3645 break;
3646 }
3647 p = next + 1;
3648 next = next_brace_sub(next);
3649 }
3650 free(new_pattern_buf);
3651 return n;
3652
3653 simple_glob:
3654 {
3655 int gr;
3656 glob_t globdata;
3657
3658 memset(&globdata, 0, sizeof(globdata));
3659 gr = glob(pattern, 0, NULL, &globdata);
3660 debug_printf_glob("glob('%s'):%d\n", pattern, gr);
3661 if (gr != 0) {
3662 if (gr == GLOB_NOMATCH) {
3663 globfree(&globdata);
3664 /* NB: garbles parameter */
3665 unbackslash(pattern);
3666 o_addstr_with_NUL(o, pattern);
3667 debug_printf_glob("glob pattern '%s' is literal\n", pattern);
3668 return o_save_ptr_helper(o, n);
3669 }
3670 if (gr == GLOB_NOSPACE)
3671 bb_die_memory_exhausted();
3672 /* GLOB_ABORTED? Only happens with GLOB_ERR flag,
3673 * but we didn't specify it. Paranoia again. */
3674 bb_error_msg_and_die("glob error %d on '%s'", gr, pattern);
3675 }
3676 if (globdata.gl_pathv && globdata.gl_pathv[0]) {
3677 char **argv = globdata.gl_pathv;
3678 while (1) {
3679 o_addstr_with_NUL(o, *argv);
3680 n = o_save_ptr_helper(o, n);
3681 argv++;
3682 if (!*argv)
3683 break;
3684 }
3685 }
3686 globfree(&globdata);
3687 }
3688 return n;
3689}
3690/* Performs globbing on last list[],
3691 * saving each result as a new list[].
3692 */
3693static int perform_glob(o_string *o, int n)
3694{
3695 char *pattern, *copy;
3696
3697 debug_printf_glob("start perform_glob: n:%d o->data:%p\n", n, o->data);
3698 if (!o->data)
3699 return o_save_ptr_helper(o, n);
3700 pattern = o->data + o_get_last_ptr(o, n);
3701 debug_printf_glob("glob pattern '%s'\n", pattern);
3702 if (!glob_needed(pattern)) {
3703 /* unbackslash last string in o in place, fix length */
3704 o->length = unbackslash(pattern) - o->data;
3705 debug_printf_glob("glob pattern '%s' is literal\n", pattern);
3706 return o_save_ptr_helper(o, n);
3707 }
3708
3709 copy = xstrdup(pattern);
3710 /* "forget" pattern in o */
3711 o->length = pattern - o->data;
3712 n = glob_brace(copy, o, n);
3713 free(copy);
3714 if (DEBUG_GLOB)
3715 debug_print_list("perform_glob returning", o, n);
3716 return n;
3717}
3718
3719#else /* !HUSH_BRACE_EXPANSION */
3720
3721/* Helper */
3722static int glob_needed(const char *s)
3723{
3724 while (*s) {
3725 if (*s == '\\') {
3726 if (!s[1])
3727 return 0;
3728 s += 2;
3729 continue;
3730 }
3731 if (*s == '*' || *s == '[' || *s == '?')
3732 return 1;
3733 s++;
3734 }
3735 return 0;
3736}
3737/* Performs globbing on last list[],
3738 * saving each result as a new list[].
3739 */
3740static int perform_glob(o_string *o, int n)
3741{
Francis Laniel74e42542023-12-22 22:02:33 +01003742#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01003743 glob_t globdata;
3744 int gr;
Francis Laniel74e42542023-12-22 22:02:33 +01003745#endif /* __U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01003746 char *pattern;
3747
3748 debug_printf_glob("start perform_glob: n:%d o->data:%p\n", n, o->data);
3749 if (!o->data)
3750 return o_save_ptr_helper(o, n);
3751 pattern = o->data + o_get_last_ptr(o, n);
3752 debug_printf_glob("glob pattern '%s'\n", pattern);
3753 if (!glob_needed(pattern)) {
Francis Laniel74e42542023-12-22 22:02:33 +01003754#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01003755 literal:
Francis Laniel74e42542023-12-22 22:02:33 +01003756#endif /* __U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01003757 /* unbackslash last string in o in place, fix length */
3758 o->length = unbackslash(pattern) - o->data;
3759 debug_printf_glob("glob pattern '%s' is literal\n", pattern);
3760 return o_save_ptr_helper(o, n);
3761 }
3762
Francis Laniel74e42542023-12-22 22:02:33 +01003763#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01003764 memset(&globdata, 0, sizeof(globdata));
3765 /* Can't use GLOB_NOCHECK: it does not unescape the string.
3766 * If we glob "*.\*" and don't find anything, we need
3767 * to fall back to using literal "*.*", but GLOB_NOCHECK
3768 * will return "*.\*"!
3769 */
3770 gr = glob(pattern, 0, NULL, &globdata);
3771 debug_printf_glob("glob('%s'):%d\n", pattern, gr);
3772 if (gr != 0) {
3773 if (gr == GLOB_NOMATCH) {
3774 globfree(&globdata);
3775 goto literal;
3776 }
3777 if (gr == GLOB_NOSPACE)
3778 bb_die_memory_exhausted();
3779 /* GLOB_ABORTED? Only happens with GLOB_ERR flag,
3780 * but we didn't specify it. Paranoia again. */
3781 bb_error_msg_and_die("glob error %d on '%s'", gr, pattern);
3782 }
3783 if (globdata.gl_pathv && globdata.gl_pathv[0]) {
3784 char **argv = globdata.gl_pathv;
3785 /* "forget" pattern in o */
3786 o->length = pattern - o->data;
3787 while (1) {
3788 o_addstr_with_NUL(o, *argv);
3789 n = o_save_ptr_helper(o, n);
3790 argv++;
3791 if (!*argv)
3792 break;
3793 }
3794 }
3795 globfree(&globdata);
3796 if (DEBUG_GLOB)
3797 debug_print_list("perform_glob returning", o, n);
3798 return n;
Francis Laniel74e42542023-12-22 22:02:33 +01003799#else /* __U_BOOT__ */
3800 /*
3801 * NOTE We only use perform glob to call unbackslash to remove backslash
3802 * from string once expanded.
3803 * So, it seems OK to return this if no previous return was done.
3804 */
3805 return o_save_ptr_helper(o, n);
3806#endif /* __U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01003807}
3808
3809#endif /* !HUSH_BRACE_EXPANSION */
3810
3811/* If o->o_expflags & EXP_FLAG_GLOB, glob the string so far remembered.
3812 * Otherwise, just finish current list[] and start new */
3813static int o_save_ptr(o_string *o, int n)
3814{
3815 if (o->o_expflags & EXP_FLAG_GLOB) {
3816 /* If o->has_empty_slot, list[n] was already globbed
3817 * (if it was requested back then when it was filled)
3818 * so don't do that again! */
3819 if (!o->has_empty_slot)
3820 return perform_glob(o, n); /* o_save_ptr_helper is inside */
3821 }
3822 return o_save_ptr_helper(o, n);
3823}
3824
3825/* "Please convert list[n] to real char* ptrs, and NULL terminate it." */
3826static char **o_finalize_list(o_string *o, int n)
3827{
3828 char **list;
3829 int string_start;
3830
3831 if (DEBUG_EXPAND)
3832 debug_print_list("finalized", o, n);
3833 debug_printf_expand("finalized n:%d\n", n);
3834 list = (char**)o->data;
3835 string_start = ((n + 0xf) & ~0xf) * sizeof(list[0]);
3836 list[--n] = NULL;
3837 while (n) {
3838 n--;
3839 list[n] = o->data + (int)(uintptr_t)list[n] + string_start;
3840 }
3841 return list;
3842}
3843
3844static void free_pipe_list(struct pipe *pi);
3845
3846/* Returns pi->next - next pipe in the list */
3847static struct pipe *free_pipe(struct pipe *pi)
3848{
3849 struct pipe *next;
3850 int i;
3851
Francis Laniel8197f012023-12-22 22:02:28 +01003852#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01003853 debug_printf_clean("free_pipe (pid %d)\n", getpid());
Francis Laniel8197f012023-12-22 22:02:28 +01003854#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01003855 for (i = 0; i < pi->num_cmds; i++) {
3856 struct command *command;
Francis Laniel8197f012023-12-22 22:02:28 +01003857#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01003858 struct redir_struct *r, *rnext;
Francis Laniel8197f012023-12-22 22:02:28 +01003859#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01003860
3861 command = &pi->cmds[i];
3862 debug_printf_clean(" command %d:\n", i);
3863 if (command->argv) {
3864 if (DEBUG_CLEAN) {
3865 int a;
3866 char **p;
3867 for (a = 0, p = command->argv; *p; a++, p++) {
3868 debug_printf_clean(" argv[%d] = %s\n", a, *p);
3869 }
3870 }
3871 free_strings(command->argv);
3872 //command->argv = NULL;
3873 }
3874 /* not "else if": on syntax error, we may have both! */
3875 if (command->group) {
3876 debug_printf_clean(" begin group (cmd_type:%d)\n",
3877 command->cmd_type);
3878 free_pipe_list(command->group);
3879 debug_printf_clean(" end group\n");
3880 //command->group = NULL;
3881 }
3882 /* else is crucial here.
3883 * If group != NULL, child_func is meaningless */
3884#if ENABLE_HUSH_FUNCTIONS
3885 else if (command->child_func) {
3886 debug_printf_exec("cmd %p releases child func at %p\n", command, command->child_func);
3887 command->child_func->parent_cmd = NULL;
3888 }
3889#endif
3890#if !BB_MMU
3891 free(command->group_as_string);
3892 //command->group_as_string = NULL;
3893#endif
Francis Laniel8197f012023-12-22 22:02:28 +01003894#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01003895 for (r = command->redirects; r; r = rnext) {
3896 debug_printf_clean(" redirect %d%s",
3897 r->rd_fd, redir_table[r->rd_type].descrip);
3898 /* guard against the case >$FOO, where foo is unset or blank */
3899 if (r->rd_filename) {
3900 debug_printf_clean(" fname:'%s'\n", r->rd_filename);
3901 free(r->rd_filename);
3902 //r->rd_filename = NULL;
3903 }
3904 debug_printf_clean(" rd_dup:%d\n", r->rd_dup);
3905 rnext = r->next;
3906 free(r);
3907 }
3908 //command->redirects = NULL;
Francis Laniel8197f012023-12-22 22:02:28 +01003909#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01003910 }
3911 free(pi->cmds); /* children are an array, they get freed all at once */
3912 //pi->cmds = NULL;
Francis Laniel8197f012023-12-22 22:02:28 +01003913#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01003914#if ENABLE_HUSH_JOB
3915 free(pi->cmdtext);
3916 //pi->cmdtext = NULL;
3917#endif
Francis Laniel8197f012023-12-22 22:02:28 +01003918#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01003919
3920 next = pi->next;
3921 free(pi);
3922 return next;
3923}
3924
3925static void free_pipe_list(struct pipe *pi)
3926{
3927 while (pi) {
3928#if HAS_KEYWORDS
3929 debug_printf_clean("pipe reserved word %d\n", pi->res_word);
3930#endif
3931 debug_printf_clean("pipe followup code %d\n", pi->followup);
3932 pi = free_pipe(pi);
3933 }
3934}
3935
3936
3937/*** Parsing routines ***/
3938
3939#ifndef debug_print_tree
3940static void debug_print_tree(struct pipe *pi, int lvl)
3941{
3942 static const char *const PIPE[] = {
3943 [PIPE_SEQ] = "SEQ",
3944 [PIPE_AND] = "AND",
3945 [PIPE_OR ] = "OR" ,
3946 [PIPE_BG ] = "BG" ,
3947 };
Francis Laniel8197f012023-12-22 22:02:28 +01003948#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01003949 static const char *RES[] = {
3950 [RES_NONE ] = "NONE" ,
3951# if ENABLE_HUSH_IF
3952 [RES_IF ] = "IF" ,
3953 [RES_THEN ] = "THEN" ,
3954 [RES_ELIF ] = "ELIF" ,
3955 [RES_ELSE ] = "ELSE" ,
3956 [RES_FI ] = "FI" ,
3957# endif
3958# if ENABLE_HUSH_LOOPS
3959 [RES_FOR ] = "FOR" ,
3960 [RES_WHILE] = "WHILE",
3961 [RES_UNTIL] = "UNTIL",
3962 [RES_DO ] = "DO" ,
3963 [RES_DONE ] = "DONE" ,
3964# endif
3965# if ENABLE_HUSH_LOOPS || ENABLE_HUSH_CASE
3966 [RES_IN ] = "IN" ,
3967# endif
3968# if ENABLE_HUSH_CASE
3969 [RES_CASE ] = "CASE" ,
3970 [RES_CASE_IN ] = "CASE_IN" ,
3971 [RES_MATCH] = "MATCH",
3972 [RES_CASE_BODY] = "CASE_BODY",
3973 [RES_ESAC ] = "ESAC" ,
3974# endif
3975 [RES_XXXX ] = "XXXX" ,
3976 [RES_SNTX ] = "SNTX" ,
3977 };
Francis Laniel8197f012023-12-22 22:02:28 +01003978#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01003979 static const char *const CMDTYPE[] = {
3980 "{}",
3981 "()",
3982 "[noglob]",
3983# if ENABLE_HUSH_FUNCTIONS
3984 "func()",
3985# endif
3986 };
3987
3988 int pin, prn;
3989
3990 pin = 0;
3991 while (pi) {
3992 fdprintf(2, "%*spipe %d #cmds:%d %sres_word=%s followup=%d %s\n",
3993 lvl*2, "",
3994 pin,
3995 pi->num_cmds,
Francis Laniel8197f012023-12-22 22:02:28 +01003996#ifdef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01003997 (IF_HAS_KEYWORDS(pi->pi_inverted ? "! " :) ""),
3998 RES[pi->res_word],
Francis Laniel8197f012023-12-22 22:02:28 +01003999#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01004000 pi->followup, PIPE[pi->followup]
4001 );
4002 prn = 0;
4003 while (prn < pi->num_cmds) {
4004 struct command *command = &pi->cmds[prn];
4005 char **argv = command->argv;
4006
4007 fdprintf(2, "%*s cmd %d assignment_cnt:%d",
4008 lvl*2, "", prn,
4009 command->assignment_cnt);
4010# if ENABLE_HUSH_LINENO_VAR
4011 fdprintf(2, " LINENO:%u", command->lineno);
4012# endif
4013 if (command->group) {
4014 fdprintf(2, " group %s: (argv=%p)%s%s\n",
4015 CMDTYPE[command->cmd_type],
4016 argv
4017# if !BB_MMU
4018 , " group_as_string:", command->group_as_string
4019# else
4020 , "", ""
4021# endif
4022 );
4023 debug_print_tree(command->group, lvl+1);
4024 prn++;
4025 continue;
4026 }
4027 if (argv) while (*argv) {
4028 fdprintf(2, " '%s'", *argv);
4029 argv++;
4030 }
Francis Laniel8197f012023-12-22 22:02:28 +01004031#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01004032 if (command->redirects)
4033 fdprintf(2, " {redir}");
Francis Laniel8197f012023-12-22 22:02:28 +01004034#endif /* __U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01004035 fdprintf(2, "\n");
4036 prn++;
4037 }
4038 pi = pi->next;
4039 pin++;
4040 }
4041}
4042#endif /* debug_print_tree */
4043
4044static struct pipe *new_pipe(void)
4045{
4046 struct pipe *pi;
4047 pi = xzalloc(sizeof(struct pipe));
4048 /*pi->res_word = RES_NONE; - RES_NONE is 0 anyway */
4049 return pi;
4050}
4051
4052/* Command (member of a pipe) is complete, or we start a new pipe
4053 * if ctx->command is NULL.
4054 * No errors possible here.
4055 */
4056static int done_command(struct parse_context *ctx)
4057{
4058 /* The command is really already in the pipe structure, so
4059 * advance the pipe counter and make a new, null command. */
4060 struct pipe *pi = ctx->pipe;
4061 struct command *command = ctx->command;
4062
4063#if 0 /* Instead we emit error message at run time */
4064 if (ctx->pending_redirect) {
4065 /* For example, "cmd >" (no filename to redirect to) */
4066 syntax_error("invalid redirect");
4067 ctx->pending_redirect = NULL;
4068 }
4069#endif
4070
4071 if (command) {
4072 if (IS_NULL_CMD(command)) {
4073 debug_printf_parse("done_command: skipping null cmd, num_cmds=%d\n", pi->num_cmds);
4074 goto clear_and_ret;
4075 }
4076 pi->num_cmds++;
4077 debug_printf_parse("done_command: ++num_cmds=%d\n", pi->num_cmds);
4078 //debug_print_tree(ctx->list_head, 20);
4079 } else {
4080 debug_printf_parse("done_command: initializing, num_cmds=%d\n", pi->num_cmds);
4081 }
4082
4083 /* Only real trickiness here is that the uncommitted
4084 * command structure is not counted in pi->num_cmds. */
4085 pi->cmds = xrealloc(pi->cmds, sizeof(*pi->cmds) * (pi->num_cmds+1));
4086 ctx->command = command = &pi->cmds[pi->num_cmds];
4087 clear_and_ret:
4088 memset(command, 0, sizeof(*command));
4089#if ENABLE_HUSH_LINENO_VAR
4090 command->lineno = G.parse_lineno;
4091 debug_printf_parse("command->lineno = G.parse_lineno (%u)\n", G.parse_lineno);
4092#endif
4093 return pi->num_cmds; /* used only for 0/nonzero check */
4094}
4095
4096static void done_pipe(struct parse_context *ctx, pipe_style type)
4097{
4098 int not_null;
4099
4100 debug_printf_parse("done_pipe entered, followup %d\n", type);
4101 /* Close previous command */
4102 not_null = done_command(ctx);
4103#if HAS_KEYWORDS
4104 ctx->pipe->pi_inverted = ctx->ctx_inverted;
4105 ctx->ctx_inverted = 0;
4106 ctx->pipe->res_word = ctx->ctx_res_w;
4107#endif
4108 if (type == PIPE_BG && ctx->list_head != ctx->pipe) {
4109 /* Necessary since && and || have precedence over &:
4110 * "cmd1 && cmd2 &" must spawn both cmds, not only cmd2,
4111 * in a backgrounded subshell.
4112 */
4113 struct pipe *pi;
4114 struct command *command;
4115
4116 /* Is this actually this construct, all pipes end with && or ||? */
4117 pi = ctx->list_head;
4118 while (pi != ctx->pipe) {
4119 if (pi->followup != PIPE_AND && pi->followup != PIPE_OR)
4120 goto no_conv;
4121 pi = pi->next;
4122 }
4123
4124 debug_printf_parse("BG with more than one pipe, converting to { p1 &&...pN; } &\n");
4125 pi->followup = PIPE_SEQ; /* close pN _not_ with "&"! */
4126 pi = xzalloc(sizeof(*pi));
4127 pi->followup = PIPE_BG;
4128 pi->num_cmds = 1;
4129 pi->cmds = xzalloc(sizeof(pi->cmds[0]));
4130 command = &pi->cmds[0];
4131 if (CMD_NORMAL != 0) /* "if xzalloc didn't do that already" */
4132 command->cmd_type = CMD_NORMAL;
4133 command->group = ctx->list_head;
4134#if !BB_MMU
4135 command->group_as_string = xstrndup(
4136 ctx->as_string.data,
4137 ctx->as_string.length - 1 /* do not copy last char, "&" */
4138 );
4139#endif
4140 /* Replace all pipes in ctx with one newly created */
4141 ctx->list_head = ctx->pipe = pi;
4142 /* for cases like "cmd && &", do not be tricked by last command
4143 * being null - the entire {...} & is NOT null! */
4144 not_null = 1;
4145 } else {
4146 no_conv:
4147 ctx->pipe->followup = type;
4148 }
4149
4150 /* Without this check, even just <enter> on command line generates
4151 * tree of three NOPs (!). Which is harmless but annoying.
4152 * IOW: it is safe to do it unconditionally. */
4153 if (not_null
4154#if ENABLE_HUSH_IF
4155 || ctx->ctx_res_w == RES_FI
4156#endif
4157#if ENABLE_HUSH_LOOPS
4158 || ctx->ctx_res_w == RES_DONE
4159 || ctx->ctx_res_w == RES_FOR
4160 || ctx->ctx_res_w == RES_IN
4161#endif
4162#if ENABLE_HUSH_CASE
4163 || ctx->ctx_res_w == RES_ESAC
4164#endif
4165 ) {
4166 struct pipe *new_p;
4167 debug_printf_parse("done_pipe: adding new pipe: "
Francis Laniel8197f012023-12-22 22:02:28 +01004168#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01004169 "not_null:%d ctx->ctx_res_w:%d\n",
4170 not_null, ctx->ctx_res_w);
Francis Laniel8197f012023-12-22 22:02:28 +01004171#else /* __U_BOOT__ */
4172 "not_null:%d\n",
4173 not_null);
4174#endif /* __U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01004175 new_p = new_pipe();
4176 ctx->pipe->next = new_p;
4177 ctx->pipe = new_p;
4178 /* RES_THEN, RES_DO etc are "sticky" -
4179 * they remain set for pipes inside if/while.
4180 * This is used to control execution.
4181 * RES_FOR and RES_IN are NOT sticky (needed to support
4182 * cases where variable or value happens to match a keyword):
4183 */
4184#if ENABLE_HUSH_LOOPS
4185 if (ctx->ctx_res_w == RES_FOR
4186 || ctx->ctx_res_w == RES_IN)
4187 ctx->ctx_res_w = RES_NONE;
4188#endif
4189#if ENABLE_HUSH_CASE
4190 if (ctx->ctx_res_w == RES_MATCH)
4191 ctx->ctx_res_w = RES_CASE_BODY;
4192 if (ctx->ctx_res_w == RES_CASE)
4193 ctx->ctx_res_w = RES_CASE_IN;
4194#endif
4195 ctx->command = NULL; /* trick done_command below */
4196 /* Create the memory for command, roughly:
4197 * ctx->pipe->cmds = new struct command;
4198 * ctx->command = &ctx->pipe->cmds[0];
4199 */
4200 done_command(ctx);
4201 //debug_print_tree(ctx->list_head, 10);
4202 }
4203 debug_printf_parse("done_pipe return\n");
4204}
4205
4206static void initialize_context(struct parse_context *ctx)
4207{
4208 memset(ctx, 0, sizeof(*ctx));
4209 if (MAYBE_ASSIGNMENT != 0)
4210 ctx->is_assignment = MAYBE_ASSIGNMENT;
4211 ctx->pipe = ctx->list_head = new_pipe();
4212 /* Create the memory for command, roughly:
4213 * ctx->pipe->cmds = new struct command;
4214 * ctx->command = &ctx->pipe->cmds[0];
4215 */
4216 done_command(ctx);
4217}
4218
4219/* If a reserved word is found and processed, parse context is modified
4220 * and 1 is returned.
4221 */
4222#if HAS_KEYWORDS
4223struct reserved_combo {
4224 char literal[6];
4225 unsigned char res;
4226 unsigned char assignment_flag;
4227 uint32_t flag;
4228};
4229enum {
4230 FLAG_END = (1 << RES_NONE ),
4231# if ENABLE_HUSH_IF
4232 FLAG_IF = (1 << RES_IF ),
4233 FLAG_THEN = (1 << RES_THEN ),
4234 FLAG_ELIF = (1 << RES_ELIF ),
4235 FLAG_ELSE = (1 << RES_ELSE ),
4236 FLAG_FI = (1 << RES_FI ),
4237# endif
4238# if ENABLE_HUSH_LOOPS
4239 FLAG_FOR = (1 << RES_FOR ),
4240 FLAG_WHILE = (1 << RES_WHILE),
4241 FLAG_UNTIL = (1 << RES_UNTIL),
4242 FLAG_DO = (1 << RES_DO ),
4243 FLAG_DONE = (1 << RES_DONE ),
4244 FLAG_IN = (1 << RES_IN ),
4245# endif
4246# if ENABLE_HUSH_CASE
4247 FLAG_MATCH = (1 << RES_MATCH),
4248 FLAG_ESAC = (1 << RES_ESAC ),
4249# endif
4250 FLAG_START = (1 << RES_XXXX ),
4251};
4252
4253static const struct reserved_combo* match_reserved_word(o_string *word)
4254{
4255 /* Mostly a list of accepted follow-up reserved words.
4256 * FLAG_END means we are done with the sequence, and are ready
4257 * to turn the compound list into a command.
4258 * FLAG_START means the word must start a new compound list.
4259 */
4260 static const struct reserved_combo reserved_list[] ALIGN4 = {
4261# if ENABLE_HUSH_IF
4262 { "!", RES_NONE, NOT_ASSIGNMENT , 0 },
4263 { "if", RES_IF, MAYBE_ASSIGNMENT, FLAG_THEN | FLAG_START },
4264 { "then", RES_THEN, MAYBE_ASSIGNMENT, FLAG_ELIF | FLAG_ELSE | FLAG_FI },
4265 { "elif", RES_ELIF, MAYBE_ASSIGNMENT, FLAG_THEN },
4266 { "else", RES_ELSE, MAYBE_ASSIGNMENT, FLAG_FI },
4267 { "fi", RES_FI, NOT_ASSIGNMENT , FLAG_END },
4268# endif
4269# if ENABLE_HUSH_LOOPS
4270 { "for", RES_FOR, NOT_ASSIGNMENT , FLAG_IN | FLAG_DO | FLAG_START },
4271 { "while", RES_WHILE, MAYBE_ASSIGNMENT, FLAG_DO | FLAG_START },
4272 { "until", RES_UNTIL, MAYBE_ASSIGNMENT, FLAG_DO | FLAG_START },
4273 { "in", RES_IN, NOT_ASSIGNMENT , FLAG_DO },
4274 { "do", RES_DO, MAYBE_ASSIGNMENT, FLAG_DONE },
4275 { "done", RES_DONE, NOT_ASSIGNMENT , FLAG_END },
4276# endif
4277# if ENABLE_HUSH_CASE
4278 { "case", RES_CASE, NOT_ASSIGNMENT , FLAG_MATCH | FLAG_START },
4279 { "esac", RES_ESAC, NOT_ASSIGNMENT , FLAG_END },
4280# endif
4281 };
4282 const struct reserved_combo *r;
4283
4284 for (r = reserved_list; r < reserved_list + ARRAY_SIZE(reserved_list); r++) {
4285 if (strcmp(word->data, r->literal) == 0)
4286 return r;
4287 }
4288 return NULL;
4289}
4290/* Return NULL: not a keyword, else: keyword
4291 */
4292static const struct reserved_combo* reserved_word(struct parse_context *ctx)
4293{
4294# if ENABLE_HUSH_CASE
4295 static const struct reserved_combo reserved_match = {
4296 "", RES_MATCH, NOT_ASSIGNMENT , FLAG_MATCH | FLAG_ESAC
4297 };
4298# endif
4299 const struct reserved_combo *r;
4300
4301 if (ctx->word.has_quoted_part)
4302 return 0;
4303 r = match_reserved_word(&ctx->word);
4304 if (!r)
4305 return r; /* NULL */
4306
4307 debug_printf("found reserved word %s, res %d\n", r->literal, r->res);
4308# if ENABLE_HUSH_CASE
4309 if (r->res == RES_IN && ctx->ctx_res_w == RES_CASE_IN) {
4310 /* "case word IN ..." - IN part starts first MATCH part */
4311 r = &reserved_match;
4312 } else
4313# endif
4314 if (r->flag == 0) { /* '!' */
4315 if (ctx->ctx_inverted) { /* bash doesn't accept '! ! true' */
4316 syntax_error("! ! command");
4317 ctx->ctx_res_w = RES_SNTX;
4318 }
4319 ctx->ctx_inverted = 1;
4320 return r;
4321 }
4322 if (r->flag & FLAG_START) {
4323 struct parse_context *old;
4324
4325 old = xmemdup(ctx, sizeof(*ctx));
4326 debug_printf_parse("push stack %p\n", old);
4327 initialize_context(ctx);
4328 ctx->stack = old;
4329 } else if (/*ctx->ctx_res_w == RES_NONE ||*/ !(ctx->old_flag & (1 << r->res))) {
4330 syntax_error_at(ctx->word.data);
4331 ctx->ctx_res_w = RES_SNTX;
4332 return r;
4333 } else {
4334 /* "{...} fi" is ok. "{...} if" is not
4335 * Example:
4336 * if { echo foo; } then { echo bar; } fi */
4337 if (ctx->command->group)
4338 done_pipe(ctx, PIPE_SEQ);
4339 }
4340
4341 ctx->ctx_res_w = r->res;
4342 ctx->old_flag = r->flag;
4343 ctx->is_assignment = r->assignment_flag;
4344 debug_printf_parse("ctx->is_assignment='%s'\n", assignment_flag[ctx->is_assignment]);
4345
4346 if (ctx->old_flag & FLAG_END) {
4347 struct parse_context *old;
4348
4349 done_pipe(ctx, PIPE_SEQ);
4350 debug_printf_parse("pop stack %p\n", ctx->stack);
4351 old = ctx->stack;
4352 old->command->group = ctx->list_head;
4353 old->command->cmd_type = CMD_NORMAL;
4354# if !BB_MMU
4355 /* At this point, the compound command's string is in
4356 * ctx->as_string... except for the leading keyword!
4357 * Consider this example: "echo a | if true; then echo a; fi"
4358 * ctx->as_string will contain "true; then echo a; fi",
4359 * with "if " remaining in old->as_string!
4360 */
4361 {
4362 char *str;
4363 int len = old->as_string.length;
4364 /* Concatenate halves */
4365 o_addstr(&old->as_string, ctx->as_string.data);
4366 o_free(&ctx->as_string);
4367 /* Find where leading keyword starts in first half */
4368 str = old->as_string.data + len;
4369 if (str > old->as_string.data)
4370 str--; /* skip whitespace after keyword */
4371 while (str > old->as_string.data && isalpha(str[-1]))
4372 str--;
4373 /* Ugh, we're done with this horrid hack */
4374 old->command->group_as_string = xstrdup(str);
4375 debug_printf_parse("pop, remembering as:'%s'\n",
4376 old->command->group_as_string);
4377 }
4378# endif
4379 *ctx = *old; /* physical copy */
4380 free(old);
4381 }
4382 return r;
4383}
4384#endif /* HAS_KEYWORDS */
4385
4386/* Word is complete, look at it and update parsing context.
4387 * Normal return is 0. Syntax errors return 1.
4388 * Note: on return, word is reset, but not o_free'd!
4389 */
4390static int done_word(struct parse_context *ctx)
4391{
4392 struct command *command = ctx->command;
4393
4394 debug_printf_parse("done_word entered: '%s' %p\n", ctx->word.data, command);
4395 if (ctx->word.length == 0 && !ctx->word.has_quoted_part) {
4396 debug_printf_parse("done_word return 0: true null, ignored\n");
4397 return 0;
4398 }
4399
Francis Laniel8197f012023-12-22 22:02:28 +01004400#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01004401 if (ctx->pending_redirect) {
4402 /* We do not glob in e.g. >*.tmp case. bash seems to glob here
4403 * only if run as "bash", not "sh" */
4404 /* http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html
4405 * "2.7 Redirection
4406 * If the redirection operator is "<<" or "<<-", the word
4407 * that follows the redirection operator shall be
4408 * subjected to quote removal; it is unspecified whether
4409 * any of the other expansions occur. For the other
4410 * redirection operators, the word that follows the
4411 * redirection operator shall be subjected to tilde
4412 * expansion, parameter expansion, command substitution,
4413 * arithmetic expansion, and quote removal.
4414 * Pathname expansion shall not be performed
4415 * on the word by a non-interactive shell; an interactive
4416 * shell may perform it, but shall do so only when
4417 * the expansion would result in one word."
4418 */
4419//bash does not do parameter/command substitution or arithmetic expansion
4420//for _heredoc_ redirection word: these constructs look for exact eof marker
4421// as written:
4422// <<EOF$t
4423// <<EOF$((1))
4424// <<EOF`true` [this case also makes heredoc "quoted", a-la <<"EOF". Probably bash-4.3.43 bug]
4425
4426 ctx->pending_redirect->rd_filename = xstrdup(ctx->word.data);
4427 /* Cater for >\file case:
4428 * >\a creates file a; >\\a, >"\a", >"\\a" create file \a
4429 * Same with heredocs:
4430 * for <<\H delim is H; <<\\H, <<"\H", <<"\\H" - \H
4431 */
4432 if (ctx->pending_redirect->rd_type == REDIRECT_HEREDOC) {
4433 unbackslash(ctx->pending_redirect->rd_filename);
4434 /* Is it <<"HEREDOC"? */
4435 if (ctx->word.has_quoted_part) {
4436 ctx->pending_redirect->rd_dup |= HEREDOC_QUOTED;
4437 }
4438 }
4439 debug_printf_parse("word stored in rd_filename: '%s'\n", ctx->word.data);
4440 ctx->pending_redirect = NULL;
4441 } else {
Francis Laniel8197f012023-12-22 22:02:28 +01004442#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01004443#if HAS_KEYWORDS
4444# if ENABLE_HUSH_CASE
4445 if (ctx->ctx_dsemicolon
4446 && strcmp(ctx->word.data, "esac") != 0 /* not "... pattern) cmd;; esac" */
4447 ) {
4448 /* already done when ctx_dsemicolon was set to 1: */
4449 /* ctx->ctx_res_w = RES_MATCH; */
4450 ctx->ctx_dsemicolon = 0;
4451 } else
4452# endif
4453# if defined(CMD_TEST2_SINGLEWORD_NOGLOB)
4454 if (command->cmd_type == CMD_TEST2_SINGLEWORD_NOGLOB
4455 && strcmp(ctx->word.data, "]]") == 0
4456 ) {
4457 /* allow "[[ ]] >file" etc */
4458 command->cmd_type = CMD_SINGLEWORD_NOGLOB;
4459 } else
4460# endif
4461 if (!command->argv /* if it's the first word... */
4462# if ENABLE_HUSH_LOOPS
4463 && ctx->ctx_res_w != RES_FOR /* ...not after FOR or IN */
4464 && ctx->ctx_res_w != RES_IN
4465# endif
4466# if ENABLE_HUSH_CASE
4467 && ctx->ctx_res_w != RES_CASE
4468# endif
4469 ) {
4470 const struct reserved_combo *reserved;
4471 reserved = reserved_word(ctx);
4472 debug_printf_parse("checking for reserved-ness: %d\n", !!reserved);
4473 if (reserved) {
4474# if ENABLE_HUSH_LINENO_VAR
4475/* Case:
4476 * "while ...; do
4477 * cmd ..."
4478 * If we don't close the pipe _now_, immediately after "do", lineno logic
4479 * sees "cmd" as starting at "do" - i.e., at the previous line.
4480 */
4481 if (0
4482 IF_HUSH_IF(|| reserved->res == RES_THEN)
4483 IF_HUSH_IF(|| reserved->res == RES_ELIF)
4484 IF_HUSH_IF(|| reserved->res == RES_ELSE)
4485 IF_HUSH_LOOPS(|| reserved->res == RES_DO)
4486 ) {
4487 done_pipe(ctx, PIPE_SEQ);
4488 }
4489# endif
4490 o_reset_to_empty_unquoted(&ctx->word);
4491 debug_printf_parse("done_word return %d\n",
4492 (ctx->ctx_res_w == RES_SNTX));
4493 return (ctx->ctx_res_w == RES_SNTX);
4494 }
4495# if defined(CMD_TEST2_SINGLEWORD_NOGLOB)
4496 if (strcmp(ctx->word.data, "[[") == 0) {
4497 command->cmd_type = CMD_TEST2_SINGLEWORD_NOGLOB;
4498 } else
4499# endif
4500# if defined(CMD_SINGLEWORD_NOGLOB)
4501 if (0
4502 /* In bash, local/export/readonly are special, args
4503 * are assignments and therefore expansion of them
4504 * should be "one-word" expansion:
4505 * $ export i=`echo 'a b'` # one arg: "i=a b"
4506 * compare with:
4507 * $ ls i=`echo 'a b'` # two args: "i=a" and "b"
4508 * ls: cannot access i=a: No such file or directory
4509 * ls: cannot access b: No such file or directory
4510 * Note: bash 3.2.33(1) does this only if export word
4511 * itself is not quoted:
4512 * $ export i=`echo 'aaa bbb'`; echo "$i"
4513 * aaa bbb
4514 * $ "export" i=`echo 'aaa bbb'`; echo "$i"
4515 * aaa
4516 */
4517 IF_HUSH_LOCAL( || strcmp(ctx->word.data, "local") == 0)
4518 IF_HUSH_EXPORT( || strcmp(ctx->word.data, "export") == 0)
4519 IF_HUSH_READONLY(|| strcmp(ctx->word.data, "readonly") == 0)
4520 ) {
4521 command->cmd_type = CMD_SINGLEWORD_NOGLOB;
4522 }
4523# else
4524 { /* empty block to pair "if ... else" */ }
4525# endif
4526 }
4527#endif /* HAS_KEYWORDS */
4528
4529 if (command->group) {
4530 /* "{ echo foo; } echo bar" - bad */
4531 syntax_error_at(ctx->word.data);
4532 debug_printf_parse("done_word return 1: syntax error, "
4533 "groups and arglists don't mix\n");
4534 return 1;
4535 }
4536
4537 /* If this word wasn't an assignment, next ones definitely
4538 * can't be assignments. Even if they look like ones. */
4539 if (ctx->is_assignment != DEFINITELY_ASSIGNMENT
4540 && ctx->is_assignment != WORD_IS_KEYWORD
4541 ) {
4542 ctx->is_assignment = NOT_ASSIGNMENT;
4543 } else {
4544 if (ctx->is_assignment == DEFINITELY_ASSIGNMENT) {
4545 command->assignment_cnt++;
4546 debug_printf_parse("++assignment_cnt=%d\n", command->assignment_cnt);
4547 }
4548 debug_printf_parse("ctx->is_assignment was:'%s'\n", assignment_flag[ctx->is_assignment]);
4549 ctx->is_assignment = MAYBE_ASSIGNMENT;
4550 }
4551 debug_printf_parse("ctx->is_assignment='%s'\n", assignment_flag[ctx->is_assignment]);
4552 command->argv = add_string_to_strings(command->argv, xstrdup(ctx->word.data));
Francis Laniel8197f012023-12-22 22:02:28 +01004553#ifdef __U_BOOT__
4554 command->argc++;
4555#endif /* __U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01004556 debug_print_strings("word appended to argv", command->argv);
Francis Laniel8197f012023-12-22 22:02:28 +01004557
4558#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01004559 }
Francis Laniel8197f012023-12-22 22:02:28 +01004560#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01004561
4562#if ENABLE_HUSH_LOOPS
4563 if (ctx->ctx_res_w == RES_FOR) {
4564 if (ctx->word.has_quoted_part
4565 || endofname(command->argv[0])[0] != '\0'
4566 ) {
4567 /* bash says just "not a valid identifier" */
4568 syntax_error("bad variable name in for");
4569 return 1;
4570 }
4571 /* Force FOR to have just one word (variable name) */
4572 /* NB: basically, this makes hush see "for v in ..."
4573 * syntax as if it is "for v; in ...". FOR and IN become
4574 * two pipe structs in parse tree. */
4575 done_pipe(ctx, PIPE_SEQ);
4576 }
4577#endif
4578#if ENABLE_HUSH_CASE
4579 /* Force CASE to have just one word */
4580 if (ctx->ctx_res_w == RES_CASE) {
4581 done_pipe(ctx, PIPE_SEQ);
4582 }
4583#endif
4584
4585 o_reset_to_empty_unquoted(&ctx->word);
4586
4587 debug_printf_parse("done_word return 0\n");
4588 return 0;
4589}
4590
4591
Francis Laniel8197f012023-12-22 22:02:28 +01004592#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01004593/* Peek ahead in the input to find out if we have a "&n" construct,
4594 * as in "2>&1", that represents duplicating a file descriptor.
4595 * Return:
4596 * REDIRFD_CLOSE if >&- "close fd" construct is seen,
4597 * REDIRFD_SYNTAX_ERR if syntax error,
4598 * REDIRFD_TO_FILE if no & was seen,
4599 * or the number found.
4600 */
4601#if BB_MMU
4602#define parse_redir_right_fd(as_string, input) \
4603 parse_redir_right_fd(input)
4604#endif
4605static int parse_redir_right_fd(o_string *as_string, struct in_str *input)
4606{
4607 int ch, d, ok;
4608
4609 ch = i_peek(input);
4610 if (ch != '&')
4611 return REDIRFD_TO_FILE;
4612
4613 ch = i_getch(input); /* get the & */
4614 nommu_addchr(as_string, ch);
4615 ch = i_peek(input);
4616 if (ch == '-') {
4617 ch = i_getch(input);
4618 nommu_addchr(as_string, ch);
4619 return REDIRFD_CLOSE;
4620 }
4621 d = 0;
4622 ok = 0;
4623 while (ch != EOF && isdigit(ch)) {
4624 d = d*10 + (ch-'0');
4625 ok = 1;
4626 ch = i_getch(input);
4627 nommu_addchr(as_string, ch);
4628 ch = i_peek(input);
4629 }
4630 if (ok) return d;
4631
4632//TODO: this is the place to catch ">&file" bashism (redirect both fd 1 and 2)
4633
4634 bb_simple_error_msg("ambiguous redirect");
4635 return REDIRFD_SYNTAX_ERR;
4636}
4637
4638/* Return code is 0 normal, 1 if a syntax error is detected
4639 */
4640static int parse_redirect(struct parse_context *ctx,
4641 int fd,
4642 redir_type style,
4643 struct in_str *input)
4644{
4645 struct command *command = ctx->command;
4646 struct redir_struct *redir;
4647 struct redir_struct **redirp;
4648 int dup_num;
4649
4650 dup_num = REDIRFD_TO_FILE;
4651 if (style != REDIRECT_HEREDOC) {
4652 /* Check for a '>&1' type redirect */
4653 dup_num = parse_redir_right_fd(&ctx->as_string, input);
4654 if (dup_num == REDIRFD_SYNTAX_ERR)
4655 return 1;
4656 } else {
4657 int ch = i_peek_and_eat_bkslash_nl(input);
4658 dup_num = (ch == '-'); /* HEREDOC_SKIPTABS bit is 1 */
4659 if (dup_num) { /* <<-... */
4660 ch = i_getch(input);
4661 nommu_addchr(&ctx->as_string, ch);
4662 ch = i_peek(input);
4663 }
4664 }
4665
4666 if (style == REDIRECT_OVERWRITE && dup_num == REDIRFD_TO_FILE) {
4667 int ch = i_peek_and_eat_bkslash_nl(input);
4668 if (ch == '|') {
4669 /* >|FILE redirect ("clobbering" >).
4670 * Since we do not support "set -o noclobber" yet,
4671 * >| and > are the same for now. Just eat |.
4672 */
4673 ch = i_getch(input);
4674 nommu_addchr(&ctx->as_string, ch);
4675 }
4676 }
4677
4678 /* Create a new redir_struct and append it to the linked list */
4679 redirp = &command->redirects;
4680 while ((redir = *redirp) != NULL) {
4681 redirp = &(redir->next);
4682 }
4683 *redirp = redir = xzalloc(sizeof(*redir));
4684 /* redir->next = NULL; */
4685 /* redir->rd_filename = NULL; */
4686 redir->rd_type = style;
4687 redir->rd_fd = (fd == -1) ? redir_table[style].default_fd : fd;
4688
4689 debug_printf_parse("redirect type %d %s\n", redir->rd_fd,
4690 redir_table[style].descrip);
4691
4692 redir->rd_dup = dup_num;
4693 if (style != REDIRECT_HEREDOC && dup_num != REDIRFD_TO_FILE) {
4694 /* Erik had a check here that the file descriptor in question
4695 * is legit; I postpone that to "run time"
4696 * A "-" representation of "close me" shows up as a -3 here */
4697 debug_printf_parse("duplicating redirect '%d>&%d'\n",
4698 redir->rd_fd, redir->rd_dup);
4699 } else {
4700#if 0 /* Instead we emit error message at run time */
4701 if (ctx->pending_redirect) {
4702 /* For example, "cmd > <file" */
4703 syntax_error("invalid redirect");
4704 }
4705#endif
4706 /* Set ctx->pending_redirect, so we know what to do at the
4707 * end of the next parsed word. */
4708 ctx->pending_redirect = redir;
4709 }
4710 return 0;
4711}
4712
4713/* If a redirect is immediately preceded by a number, that number is
4714 * supposed to tell which file descriptor to redirect. This routine
4715 * looks for such preceding numbers. In an ideal world this routine
4716 * needs to handle all the following classes of redirects...
4717 * echo 2>foo # redirects fd 2 to file "foo", nothing passed to echo
4718 * echo 49>foo # redirects fd 49 to file "foo", nothing passed to echo
4719 * echo -2>foo # redirects fd 1 to file "foo", "-2" passed to echo
4720 * echo 49x>foo # redirects fd 1 to file "foo", "49x" passed to echo
4721 *
4722 * http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html
4723 * "2.7 Redirection
4724 * ... If n is quoted, the number shall not be recognized as part of
4725 * the redirection expression. For example:
4726 * echo \2>a
4727 * writes the character 2 into file a"
4728 * We are getting it right by setting ->has_quoted_part on any \<char>
4729 *
4730 * A -1 return means no valid number was found,
4731 * the caller should use the appropriate default for this redirection.
4732 */
4733static int redirect_opt_num(o_string *o)
4734{
4735 int num;
4736
4737 if (o->data == NULL)
4738 return -1;
4739 num = bb_strtou(o->data, NULL, 10);
4740 if (errno || num < 0)
4741 return -1;
4742 o_reset_to_empty_unquoted(o);
4743 return num;
4744}
4745
4746#if BB_MMU
4747#define fetch_till_str(as_string, input, word, skip_tabs) \
4748 fetch_till_str(input, word, skip_tabs)
4749#endif
4750static char *fetch_till_str(o_string *as_string,
4751 struct in_str *input,
4752 const char *word,
4753 int heredoc_flags)
4754{
4755 o_string heredoc = NULL_O_STRING;
4756 unsigned past_EOL;
4757 int prev = 0; /* not \ */
4758 int ch;
4759
4760 /* Starting with "" is necessary for this case:
4761 * cat <<EOF
4762 *
4763 * xxx
4764 * EOF
4765 */
4766 heredoc.data = xzalloc(1); /* start as "", not as NULL */
4767
4768 goto jump_in;
4769
4770 while (1) {
4771 ch = i_getch(input);
4772 if (ch != EOF)
4773 nommu_addchr(as_string, ch);
4774 if (ch == '\n' || ch == EOF) {
4775 check_heredoc_end:
Francis Laniel8197f012023-12-22 22:02:28 +01004776#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01004777 if ((heredoc_flags & HEREDOC_QUOTED) || prev != '\\') {
Francis Laniel8197f012023-12-22 22:02:28 +01004778#else /* __U_BOOT__ */
4779 if (prev != '\\') {
4780#endif
Francis Lanielb234f7e2023-12-22 22:02:27 +01004781 /* End-of-line, and not a line continuation */
4782 if (strcmp(heredoc.data + past_EOL, word) == 0) {
4783 heredoc.data[past_EOL] = '\0';
4784 debug_printf_heredoc("parsed '%s' heredoc '%s'\n", word, heredoc.data);
4785 return heredoc.data;
4786 }
4787 if (ch == '\n') {
4788 /* This is a new line.
4789 * Remember position and backslash-escaping status.
4790 */
4791 o_addchr(&heredoc, ch);
4792 prev = ch;
4793 jump_in:
4794 past_EOL = heredoc.length;
4795 /* Get 1st char of next line, possibly skipping leading tabs */
4796 do {
4797 ch = i_getch(input);
4798 if (ch != EOF)
4799 nommu_addchr(as_string, ch);
Francis Laniel8197f012023-12-22 22:02:28 +01004800#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01004801 } while ((heredoc_flags & HEREDOC_SKIPTABS) && ch == '\t');
Francis Laniel8197f012023-12-22 22:02:28 +01004802#else /* __U_BOOT__ */
4803 } while (ch == '\t');
4804#endif
Francis Lanielb234f7e2023-12-22 22:02:27 +01004805 /* If this immediately ended the line,
4806 * go back to end-of-line checks.
4807 */
4808 if (ch == '\n')
4809 goto check_heredoc_end;
4810 }
4811 } else {
4812 /* Backslash-line continuation in an unquoted
4813 * heredoc. This does not need special handling
4814 * for heredoc body (unquoted heredocs are
4815 * expanded on "execution" and that would take
4816 * care of this case too), but not the case
4817 * of line continuation *in terminator*:
4818 * cat <<EOF
4819 * Ok1
4820 * EO\
4821 * F
4822 */
4823 heredoc.data[--heredoc.length] = '\0';
4824 prev = 0; /* not '\' */
4825 continue;
4826 }
4827 }
4828 if (ch == EOF) {
4829 o_free(&heredoc);
4830 return NULL; /* error */
4831 }
4832 o_addchr(&heredoc, ch);
4833 nommu_addchr(as_string, ch);
4834 if (prev == '\\' && ch == '\\')
4835 /* Correctly handle foo\\<eol> (not a line cont.) */
4836 prev = 0; /* not '\' */
4837 else
4838 prev = ch;
4839 }
4840}
Francis Laniel8197f012023-12-22 22:02:28 +01004841#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01004842
4843/* Look at entire parse tree for not-yet-loaded REDIRECT_HEREDOCs
4844 * and load them all. There should be exactly heredoc_cnt of them.
4845 */
4846#if BB_MMU
4847#define fetch_heredocs(as_string, pi, heredoc_cnt, input) \
4848 fetch_heredocs(pi, heredoc_cnt, input)
4849#endif
4850static int fetch_heredocs(o_string *as_string, struct pipe *pi, int heredoc_cnt, struct in_str *input)
4851{
4852 while (pi && heredoc_cnt) {
4853 int i;
4854 struct command *cmd = pi->cmds;
4855
4856 debug_printf_heredoc("fetch_heredocs: num_cmds:%d cmd argv0:'%s'\n",
4857 pi->num_cmds,
4858 cmd->argv ? cmd->argv[0] : "NONE"
4859 );
4860 for (i = 0; i < pi->num_cmds; i++) {
Francis Laniel8197f012023-12-22 22:02:28 +01004861#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01004862 struct redir_struct *redir = cmd->redirects;
4863
Francis Laniel8197f012023-12-22 22:02:28 +01004864#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01004865 debug_printf_heredoc("fetch_heredocs: %d cmd argv0:'%s'\n",
4866 i, cmd->argv ? cmd->argv[0] : "NONE");
Francis Laniel8197f012023-12-22 22:02:28 +01004867#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01004868 while (redir) {
4869 if (redir->rd_type == REDIRECT_HEREDOC) {
4870 char *p;
4871
4872 redir->rd_type = REDIRECT_HEREDOC2;
4873 /* redir->rd_dup is (ab)used to indicate <<- */
4874 p = fetch_till_str(as_string, input,
4875 redir->rd_filename, redir->rd_dup);
4876 if (!p) {
4877 syntax_error("unexpected EOF in here document");
4878 return -1;
4879 }
4880 free(redir->rd_filename);
4881 redir->rd_filename = p;
4882 heredoc_cnt--;
4883 }
4884 redir = redir->next;
4885 }
Francis Laniel8197f012023-12-22 22:02:28 +01004886#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01004887 if (cmd->group) {
4888 //bb_error_msg("%s:%u heredoc_cnt:%d", __func__, __LINE__, heredoc_cnt);
4889 heredoc_cnt = fetch_heredocs(as_string, cmd->group, heredoc_cnt, input);
4890 //bb_error_msg("%s:%u heredoc_cnt:%d", __func__, __LINE__, heredoc_cnt);
4891 if (heredoc_cnt < 0)
4892 return heredoc_cnt; /* error */
4893 }
4894 cmd++;
4895 }
4896 pi = pi->next;
4897 }
4898 return heredoc_cnt;
4899}
4900
4901
4902static int run_list(struct pipe *pi);
4903#if BB_MMU
4904#define parse_stream(pstring, heredoc_cnt_ptr, input, end_trigger) \
4905 parse_stream(heredoc_cnt_ptr, input, end_trigger)
4906#endif
4907static struct pipe *parse_stream(char **pstring,
4908 int *heredoc_cnt_ptr,
4909 struct in_str *input,
4910 int end_trigger);
4911
4912/* Returns number of heredocs not yet consumed,
4913 * or -1 on error.
4914 */
4915static int parse_group(struct parse_context *ctx,
4916 struct in_str *input, int ch)
4917{
4918 /* ctx->word contains characters seen prior to ( or {.
4919 * Typically it's empty, but for function defs,
4920 * it contains function name (without '()'). */
4921#if BB_MMU
4922# define as_string NULL
4923#else
4924 char *as_string = NULL;
4925#endif
4926 struct pipe *pipe_list;
4927 int heredoc_cnt = 0;
4928 int endch;
4929 struct command *command = ctx->command;
4930
4931 debug_printf_parse("parse_group entered\n");
4932#if ENABLE_HUSH_FUNCTIONS
4933 if (ch == '(' && !ctx->word.has_quoted_part) {
4934 if (ctx->word.length)
4935 if (done_word(ctx))
4936 return -1;
4937 if (!command->argv)
4938 goto skip; /* (... */
4939 if (command->argv[1]) { /* word word ... (... */
4940 syntax_error_unexpected_ch('(');
4941 return -1;
4942 }
4943 /* it is "word(..." or "word (..." */
4944 do
4945 ch = i_getch(input);
4946 while (ch == ' ' || ch == '\t');
4947 if (ch != ')') {
4948 syntax_error_unexpected_ch(ch);
4949 return -1;
4950 }
4951 nommu_addchr(&ctx->as_string, ch);
4952 do
4953 ch = i_getch(input);
4954 while (ch == ' ' || ch == '\t' || ch == '\n');
4955 if (ch != '{' && ch != '(') {
4956 syntax_error_unexpected_ch(ch);
4957 return -1;
4958 }
4959 nommu_addchr(&ctx->as_string, ch);
4960 command->cmd_type = CMD_FUNCDEF;
4961 goto skip;
4962 }
4963#endif
4964
4965#if 0 /* Prevented by caller */
4966 if (command->argv /* word [word]{... */
4967 || ctx->word.length /* word{... */
4968 || ctx->word.has_quoted_part /* ""{... */
4969 ) {
4970 syntax_error(NULL);
4971 debug_printf_parse("parse_group return -1: "
4972 "syntax error, groups and arglists don't mix\n");
4973 return -1;
4974 }
4975#endif
4976
Francis Laniel8197f012023-12-22 22:02:28 +01004977#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01004978 IF_HUSH_FUNCTIONS(skip:)
Francis Laniel8197f012023-12-22 22:02:28 +01004979#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01004980
4981 endch = '}';
4982 if (ch == '(') {
4983 endch = ')';
Francis Laniel8197f012023-12-22 22:02:28 +01004984#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01004985 IF_HUSH_FUNCTIONS(if (command->cmd_type != CMD_FUNCDEF))
4986 command->cmd_type = CMD_SUBSHELL;
Francis Laniel8197f012023-12-22 22:02:28 +01004987#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01004988 } else {
4989 /* bash does not allow "{echo...", requires whitespace */
4990 ch = i_peek(input);
4991 if (ch != ' ' && ch != '\t' && ch != '\n'
4992 && ch != '(' /* but "{(..." is allowed (without whitespace) */
4993 ) {
4994 syntax_error_unexpected_ch(ch);
4995 return -1;
4996 }
4997 if (ch != '(') {
4998 ch = i_getch(input);
4999 nommu_addchr(&ctx->as_string, ch);
5000 }
5001 }
5002
5003 debug_printf_heredoc("calling parse_stream, heredoc_cnt:%d\n", heredoc_cnt);
5004 pipe_list = parse_stream(&as_string, &heredoc_cnt, input, endch);
5005 debug_printf_heredoc("parse_stream returned: heredoc_cnt:%d\n", heredoc_cnt);
5006#if !BB_MMU
5007 if (as_string)
5008 o_addstr(&ctx->as_string, as_string);
5009#endif
5010
5011 /* empty ()/{} or parse error? */
5012 if (!pipe_list || pipe_list == ERR_PTR) {
5013 /* parse_stream already emitted error msg */
5014 if (!BB_MMU)
5015 free(as_string);
5016 debug_printf_parse("parse_group return -1: "
5017 "parse_stream returned %p\n", pipe_list);
5018 return -1;
5019 }
5020#if !BB_MMU
5021 as_string[strlen(as_string) - 1] = '\0'; /* plink ')' or '}' */
5022 command->group_as_string = as_string;
5023 debug_printf_parse("end of group, remembering as:'%s'\n",
5024 command->group_as_string);
5025#endif
5026
5027#if ENABLE_HUSH_FUNCTIONS
5028 /* Convert "f() (cmds)" to "f() {(cmds)}" */
5029 if (command->cmd_type == CMD_FUNCDEF && endch == ')') {
5030 struct command *cmd2;
5031
5032 cmd2 = xzalloc(sizeof(*cmd2));
5033 cmd2->cmd_type = CMD_SUBSHELL;
5034 cmd2->group = pipe_list;
5035# if !BB_MMU
5036//UNTESTED!
5037 cmd2->group_as_string = command->group_as_string;
5038 command->group_as_string = xasprintf("(%s)", command->group_as_string);
5039# endif
5040
5041 pipe_list = new_pipe();
5042 pipe_list->cmds = cmd2;
5043 pipe_list->num_cmds = 1;
5044 }
5045#endif
5046
5047 command->group = pipe_list;
5048
5049 debug_printf_parse("parse_group return %d\n", heredoc_cnt);
5050 return heredoc_cnt;
5051 /* command remains "open", available for possible redirects */
5052#undef as_string
5053}
5054
Francis Laniel8197f012023-12-22 22:02:28 +01005055#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01005056#if ENABLE_HUSH_TICK || ENABLE_FEATURE_SH_MATH || ENABLE_HUSH_DOLLAR_OPS
5057/* Subroutines for copying $(...) and `...` things */
5058/* '...' */
5059static int add_till_single_quote(o_string *dest, struct in_str *input)
5060{
5061 while (1) {
5062 int ch = i_getch(input);
5063 if (ch == EOF) {
5064 syntax_error_unterm_ch('\'');
5065 return 0;
5066 }
5067 if (ch == '\'')
5068 return 1;
5069 o_addchr(dest, ch);
5070 }
5071}
5072static int add_till_single_quote_dquoted(o_string *dest, struct in_str *input)
5073{
5074 while (1) {
5075 int ch = i_getch(input);
5076 if (ch == EOF) {
5077 syntax_error_unterm_ch('\'');
5078 return 0;
5079 }
5080 if (ch == '\'')
5081 return 1;
5082 o_addqchr(dest, ch);
5083 }
5084}
Francis Laniel8197f012023-12-22 22:02:28 +01005085
Francis Lanielb234f7e2023-12-22 22:02:27 +01005086/* "...\"...`..`...." - do we need to handle "...$(..)..." too? */
5087static int add_till_backquote(o_string *dest, struct in_str *input, int in_dquote);
5088static int add_till_double_quote(o_string *dest, struct in_str *input)
5089{
5090 while (1) {
5091 int ch = i_getch(input);
5092 if (ch == EOF) {
5093 syntax_error_unterm_ch('"');
5094 return 0;
5095 }
5096 if (ch == '"')
5097 return 1;
5098 if (ch == '\\') { /* \x. Copy both chars. */
5099 o_addchr(dest, ch);
5100 ch = i_getch(input);
5101 }
5102 o_addchr(dest, ch);
5103 if (ch == '`') {
5104 if (!add_till_backquote(dest, input, /*in_dquote:*/ 1))
5105 return 0;
5106 o_addchr(dest, ch);
5107 continue;
5108 }
5109 //if (ch == '$') ...
5110 }
5111}
Francis Laniel8197f012023-12-22 22:02:28 +01005112
5113
Francis Lanielb234f7e2023-12-22 22:02:27 +01005114/* Process `cmd` - copy contents until "`" is seen. Complicated by
5115 * \` quoting.
5116 * "Within the backquoted style of command substitution, backslash
5117 * shall retain its literal meaning, except when followed by: '$', '`', or '\'.
5118 * The search for the matching backquote shall be satisfied by the first
5119 * backquote found without a preceding backslash; during this search,
5120 * if a non-escaped backquote is encountered within a shell comment,
5121 * a here-document, an embedded command substitution of the $(command)
5122 * form, or a quoted string, undefined results occur. A single-quoted
5123 * or double-quoted string that begins, but does not end, within the
5124 * "`...`" sequence produces undefined results."
5125 * Example Output
5126 * echo `echo '\'TEST\`echo ZZ\`BEST` \TESTZZBEST
5127 */
5128static int add_till_backquote(o_string *dest, struct in_str *input, int in_dquote)
5129{
5130 while (1) {
5131 int ch = i_getch(input);
5132 if (ch == '`')
5133 return 1;
5134 if (ch == '\\') {
5135 /* \x. Copy both unless it is \`, \$, \\ and maybe \" */
5136 ch = i_getch(input);
5137 if (ch != '`'
5138 && ch != '$'
5139 && ch != '\\'
5140 && (!in_dquote || ch != '"')
5141 ) {
5142 o_addchr(dest, '\\');
5143 }
5144 }
5145 if (ch == EOF) {
5146 syntax_error_unterm_ch('`');
5147 return 0;
5148 }
5149 o_addchr(dest, ch);
5150 }
5151}
5152/* Process $(cmd) - copy contents until ")" is seen. Complicated by
5153 * quoting and nested ()s.
5154 * "With the $(command) style of command substitution, all characters
5155 * following the open parenthesis to the matching closing parenthesis
5156 * constitute the command. Any valid shell script can be used for command,
5157 * except a script consisting solely of redirections which produces
5158 * unspecified results."
5159 * Example Output
5160 * echo $(echo '(TEST)' BEST) (TEST) BEST
5161 * echo $(echo 'TEST)' BEST) TEST) BEST
5162 * echo $(echo \(\(TEST\) BEST) ((TEST) BEST
5163 *
5164 * Also adapted to eat ${var%...} and $((...)) constructs, since ... part
5165 * can contain arbitrary constructs, just like $(cmd).
5166 * In bash compat mode, it needs to also be able to stop on ':' or '/'
5167 * for ${var:N[:M]} and ${var/P[/R]} parsing.
5168 */
5169#define DOUBLE_CLOSE_CHAR_FLAG 0x80
5170static int add_till_closing_bracket(o_string *dest, struct in_str *input, unsigned end_ch)
5171{
5172 int ch;
5173 char dbl = end_ch & DOUBLE_CLOSE_CHAR_FLAG;
5174# if BASH_SUBSTR || BASH_PATTERN_SUBST
5175 char end_char2 = end_ch >> 8;
5176# endif
5177 end_ch &= (DOUBLE_CLOSE_CHAR_FLAG - 1);
5178
5179# if ENABLE_HUSH_INTERACTIVE
5180 G.promptmode = 1; /* PS2 */
5181# endif
5182 debug_printf_prompt("%s promptmode=%d\n", __func__, G.promptmode);
5183
5184 while (1) {
5185 ch = i_getch(input);
5186 if (ch == EOF) {
5187 syntax_error_unterm_ch(end_ch);
5188 return 0;
5189 }
5190 if (ch == end_ch
5191# if BASH_SUBSTR || BASH_PATTERN_SUBST
5192 || ch == end_char2
5193# endif
5194 ) {
5195 if (!dbl)
5196 break;
5197 /* we look for closing )) of $((EXPR)) */
5198 if (i_peek_and_eat_bkslash_nl(input) == end_ch) {
5199 i_getch(input); /* eat second ')' */
5200 break;
5201 }
5202 }
5203 o_addchr(dest, ch);
5204 //bb_error_msg("%s:o_addchr('%c')", __func__, ch);
5205 if (ch == '(' || ch == '{') {
5206 ch = (ch == '(' ? ')' : '}');
5207 if (!add_till_closing_bracket(dest, input, ch))
5208 return 0;
5209 o_addchr(dest, ch);
5210 continue;
5211 }
5212 if (ch == '\'') {
5213 if (!add_till_single_quote(dest, input))
5214 return 0;
5215 o_addchr(dest, ch);
5216 continue;
5217 }
5218 if (ch == '"') {
5219 if (!add_till_double_quote(dest, input))
5220 return 0;
5221 o_addchr(dest, ch);
5222 continue;
5223 }
5224 if (ch == '`') {
5225 if (!add_till_backquote(dest, input, /*in_dquote:*/ 0))
5226 return 0;
5227 o_addchr(dest, ch);
5228 continue;
5229 }
5230 if (ch == '\\') {
5231 /* \x. Copy verbatim. Important for \(, \) */
5232 ch = i_getch(input);
5233 if (ch == EOF) {
5234 syntax_error_unterm_ch(end_ch);
5235 return 0;
5236 }
5237# if 0
5238 if (ch == '\n') {
5239 /* "backslash+newline", ignore both */
5240 o_delchr(dest); /* undo insertion of '\' */
5241 continue;
5242 }
5243# endif
5244 o_addchr(dest, ch);
5245 //bb_error_msg("%s:o_addchr('%c') after '\\'", __func__, ch);
5246 continue;
5247 }
5248 }
5249 debug_printf_parse("%s return '%s' ch:'%c'\n", __func__, dest->data, ch);
5250 return ch;
5251}
5252#endif /* ENABLE_HUSH_TICK || ENABLE_FEATURE_SH_MATH || ENABLE_HUSH_DOLLAR_OPS */
5253
5254#if BASH_DOLLAR_SQUOTE
5255/* Return code: 1 for "found and parsed", 0 for "seen something else" */
5256# if BB_MMU
5257#define parse_dollar_squote(as_string, dest, input) \
5258 parse_dollar_squote(dest, input)
5259#define as_string NULL
5260# endif
5261static int parse_dollar_squote(o_string *as_string, o_string *dest, struct in_str *input)
5262{
5263 int start;
5264 int ch = i_peek_and_eat_bkslash_nl(input); /* first character after the $ */
5265 debug_printf_parse("parse_dollar_squote entered: ch='%c'\n", ch);
5266 if (ch != '\'')
5267 return 0;
5268
5269 dest->has_quoted_part = 1;
5270 start = dest->length;
5271
5272 ch = i_getch(input); /* eat ' */
5273 nommu_addchr(as_string, ch);
5274 while (1) {
5275 ch = i_getch(input);
5276 nommu_addchr(as_string, ch);
5277 if (ch == EOF) {
5278 syntax_error_unterm_ch('\'');
5279 return 0;
5280 }
5281 if (ch == '\'')
5282 break;
5283 if (ch == SPECIAL_VAR_SYMBOL) {
5284 /* Convert raw ^C to corresponding special variable reference */
5285 o_addchr(dest, SPECIAL_VAR_SYMBOL);
5286 o_addchr(dest, SPECIAL_VAR_QUOTED_SVS);
5287 /* will addchr() another SPECIAL_VAR_SYMBOL (see after the if() block) */
5288 } else if (ch == '\\') {
5289 static const char C_escapes[] ALIGN1 = "nrbtfav""x\\01234567";
5290
5291 ch = i_getch(input);
5292 nommu_addchr(as_string, ch);
5293 if (strchr(C_escapes, ch)) {
5294 char buf[4];
5295 char *p = buf;
5296 int cnt = 2;
5297
5298 buf[0] = ch;
5299 if ((unsigned char)(ch - '0') <= 7) { /* \ooo */
5300 do {
5301 ch = i_peek(input);
5302 if ((unsigned char)(ch - '0') > 7)
5303 break;
5304 *++p = ch = i_getch(input);
5305 nommu_addchr(as_string, ch);
5306 } while (--cnt != 0);
5307 } else if (ch == 'x') { /* \xHH */
5308 do {
5309 ch = i_peek(input);
5310 if (!isxdigit(ch))
5311 break;
5312 *++p = ch = i_getch(input);
5313 nommu_addchr(as_string, ch);
5314 } while (--cnt != 0);
5315 if (cnt == 2) { /* \x but next char is "bad" */
5316 ch = 'x';
5317 goto unrecognized;
5318 }
5319 } /* else simple seq like \\ or \t */
5320 *++p = '\0';
5321 p = buf;
5322 ch = bb_process_escape_sequence((void*)&p);
5323 //bb_error_msg("buf:'%s' ch:%x", buf, ch);
5324 if (ch == '\0')
5325 continue; /* bash compat: $'...\0...' emits nothing */
5326 } else { /* unrecognized "\z": encode both chars unless ' or " */
5327 if (ch != '\'' && ch != '"') {
5328 unrecognized:
5329 o_addqchr(dest, '\\');
5330 }
5331 }
5332 } /* if (\...) */
5333 o_addqchr(dest, ch);
5334 }
5335
5336 if (dest->length == start) {
5337 /* $'', $'\0', $'\000\x00' and the like */
5338 o_addchr(dest, SPECIAL_VAR_SYMBOL);
5339 o_addchr(dest, SPECIAL_VAR_SYMBOL);
5340 }
5341
5342 return 1;
5343# undef as_string
5344}
5345#else
5346# #define parse_dollar_squote(as_string, dest, input) 0
5347#endif /* BASH_DOLLAR_SQUOTE */
Francis Laniel8197f012023-12-22 22:02:28 +01005348#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01005349
5350/* Return code: 0 for OK, 1 for syntax error */
5351#if BB_MMU
5352#define parse_dollar(as_string, dest, input, quote_mask) \
5353 parse_dollar(dest, input, quote_mask)
5354#define as_string NULL
5355#endif
5356static int parse_dollar(o_string *as_string,
5357 o_string *dest,
5358 struct in_str *input, unsigned char quote_mask)
5359{
5360 int ch = i_peek_and_eat_bkslash_nl(input); /* first character after the $ */
5361
5362 debug_printf_parse("parse_dollar entered: ch='%c' quote_mask:0x%x\n", ch, quote_mask);
5363 if (isalpha(ch)) {
5364 make_var:
5365 ch = i_getch(input);
5366 nommu_addchr(as_string, ch);
5367 /*make_var1:*/
5368 o_addchr(dest, SPECIAL_VAR_SYMBOL);
5369 while (1) {
5370 debug_printf_parse(": '%c'\n", ch);
5371 o_addchr(dest, ch | quote_mask);
5372 quote_mask = 0;
5373 ch = i_peek_and_eat_bkslash_nl(input);
5374 if (!isalnum(ch) && ch != '_') {
5375 /* End of variable name reached */
5376 break;
5377 }
5378 ch = i_getch(input);
5379 nommu_addchr(as_string, ch);
5380 }
5381 o_addchr(dest, SPECIAL_VAR_SYMBOL);
5382 } else if (isdigit(ch)) {
5383 make_one_char_var:
5384 ch = i_getch(input);
5385 nommu_addchr(as_string, ch);
5386 o_addchr(dest, SPECIAL_VAR_SYMBOL);
5387 debug_printf_parse(": '%c'\n", ch);
5388 o_addchr(dest, ch | quote_mask);
5389 o_addchr(dest, SPECIAL_VAR_SYMBOL);
5390 } else switch (ch) {
Francis Laniel8197f012023-12-22 22:02:28 +01005391#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01005392 case '$': /* pid */
5393 case '!': /* last bg pid */
Francis Laniel8197f012023-12-22 22:02:28 +01005394#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01005395 case '?': /* last exit code */
5396 case '#': /* number of args */
5397 case '*': /* args */
5398 case '@': /* args */
5399 case '-': /* $- option flags set by set builtin or shell options (-i etc) */
5400 goto make_one_char_var;
5401 case '{': {
5402 char len_single_ch;
5403
5404 o_addchr(dest, SPECIAL_VAR_SYMBOL);
5405
5406 ch = i_getch(input); /* eat '{' */
5407 nommu_addchr(as_string, ch);
5408
5409 ch = i_getch_and_eat_bkslash_nl(input); /* first char after '{' */
5410 /* It should be ${?}, or ${#var},
5411 * or even ${?+subst} - operator acting on a special variable,
5412 * or the beginning of variable name.
5413 */
5414 if (ch == EOF
5415 || (!strchr(_SPECIAL_VARS_STR, ch) && !isalnum(ch)) /* not one of those */
5416 ) {
5417 bad_dollar_syntax:
5418 syntax_error_unterm_str("${name}");
5419 debug_printf_parse("parse_dollar return 0: unterminated ${name}\n");
5420 return 0;
5421 }
5422 nommu_addchr(as_string, ch);
5423 len_single_ch = ch;
5424 ch |= quote_mask;
5425
5426 /* It's possible to just call add_till_closing_bracket() at this point.
5427 * However, this regresses some of our testsuite cases
5428 * which check invalid constructs like ${%}.
5429 * Oh well... let's check that the var name part is fine... */
5430
5431 if (isdigit(len_single_ch)
5432 || (len_single_ch == '#' && isdigit(i_peek_and_eat_bkslash_nl(input)))
5433 ) {
5434 /* Execution engine uses plain xatoi_positive()
5435 * to interpret ${NNN} and {#NNN},
5436 * check syntax here in the parser.
5437 * (bash does not support expressions in ${#NN},
5438 * e.g. ${#$var} and {#1:+WORD} are not supported).
5439 */
5440 unsigned cnt = 9; /* max 9 digits for ${NN} and 8 for {#NN} */
5441 while (1) {
5442 o_addchr(dest, ch);
5443 debug_printf_parse(": '%c'\n", ch);
5444 ch = i_getch_and_eat_bkslash_nl(input);
5445 nommu_addchr(as_string, ch);
5446 if (ch == '}')
5447 break;
5448 if (--cnt == 0)
5449 goto bad_dollar_syntax;
5450 if (len_single_ch != '#' && strchr(VAR_SUBST_OPS, ch))
5451 /* ${NN<op>...} is valid */
5452 goto eat_until_closing;
5453 if (!isdigit(ch))
5454 goto bad_dollar_syntax;
5455 }
5456 } else
5457 while (1) {
5458 unsigned pos;
5459
5460 o_addchr(dest, ch);
5461 debug_printf_parse(": '%c'\n", ch);
5462
5463 ch = i_getch(input);
5464 nommu_addchr(as_string, ch);
5465 if (ch == '}')
5466 break;
Francis Laniel74e42542023-12-22 22:02:33 +01005467#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01005468 if (!isalnum(ch) && ch != '_') {
Francis Laniel74e42542023-12-22 22:02:33 +01005469#else /* __U_BOOT__ */
5470 /*
5471 * In several places in U-Boot, we use variable like
5472 * foo# (e.g. serial#), particularly in env.
5473 * So, we need to authorize # to appear inside
5474 * variable name and then expand this variable.
5475 * NOTE Having # in variable name is not permitted in
5476 * upstream hush but expansion will be done (even though
5477 * the result will be empty).
5478 */
5479 if (!isalnum(ch) && ch != '_' && ch != '#') {
5480#endif /* __U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01005481 unsigned end_ch;
Francis Laniel8197f012023-12-22 22:02:28 +01005482#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01005483 unsigned char last_ch;
Francis Laniel8197f012023-12-22 22:02:28 +01005484#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01005485 /* handle parameter expansions
5486 * http://www.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_06_02
5487 */
5488 if (!strchr(VAR_SUBST_OPS, ch)) { /* ${var<bad_char>... */
5489 if (len_single_ch != '#'
5490 /*|| !strchr(SPECIAL_VARS_STR, ch) - disallow errors like ${#+} ? */
5491 || i_peek(input) != '}'
5492 ) {
5493 goto bad_dollar_syntax;
5494 }
5495 /* else: it's "length of C" ${#C} op,
5496 * where C is a single char
5497 * special var name, e.g. ${#!}.
5498 */
5499 }
5500 eat_until_closing:
5501 /* Eat everything until closing '}' (or ':') */
5502 end_ch = '}';
Francis Laniel8197f012023-12-22 22:02:28 +01005503#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01005504 if (BASH_SUBSTR
5505 && ch == ':'
5506 && !strchr(MINUS_PLUS_EQUAL_QUESTION, i_peek(input))
5507 ) {
5508 /* It's ${var:N[:M]} thing */
5509 end_ch = '}' * 0x100 + ':';
5510 }
5511 if (BASH_PATTERN_SUBST
5512 && ch == '/'
5513 ) {
5514 /* It's ${var/[/]pattern[/repl]} thing */
5515 if (i_peek(input) == '/') { /* ${var//pattern[/repl]}? */
5516 i_getch(input);
5517 nommu_addchr(as_string, '/');
5518 ch = '\\';
5519 }
5520 end_ch = '}' * 0x100 + '/';
5521 }
Francis Laniel8197f012023-12-22 22:02:28 +01005522#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01005523 o_addchr(dest, ch);
5524 /* The pattern can't be empty.
5525 * IOW: if the first char after "${v//" is a slash,
5526 * it does not terminate the pattern - it's the first char of the pattern:
5527 * v=/dev/ram; echo ${v////-} prints -dev-ram (pattern is "/")
5528 * v=/dev/ram; echo ${v///r/-} prints /dev-am (pattern is "/r")
5529 */
5530 if (i_peek(input) == '/') {
5531 o_addchr(dest, i_getch(input));
5532 }
Francis Laniel8197f012023-12-22 22:02:28 +01005533#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01005534 again:
Francis Laniel8197f012023-12-22 22:02:28 +01005535#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01005536 if (!BB_MMU)
5537 pos = dest->length;
5538#if ENABLE_HUSH_DOLLAR_OPS
Francis Laniel8197f012023-12-22 22:02:28 +01005539#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01005540 last_ch = add_till_closing_bracket(dest, input, end_ch);
5541 if (last_ch == 0) /* error? */
5542 return 0;
Francis Laniel8197f012023-12-22 22:02:28 +01005543#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01005544#else
5545# error Simple code to only allow ${var} is not implemented
5546#endif
5547 if (as_string) {
5548 o_addstr(as_string, dest->data + pos);
Francis Laniel8197f012023-12-22 22:02:28 +01005549#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01005550 o_addchr(as_string, last_ch);
Francis Laniel8197f012023-12-22 22:02:28 +01005551#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01005552 }
5553
Francis Laniel8197f012023-12-22 22:02:28 +01005554#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01005555 if ((BASH_SUBSTR || BASH_PATTERN_SUBST)
5556 && (end_ch & 0xff00)
5557 ) {
5558 /* close the first block: */
5559 o_addchr(dest, SPECIAL_VAR_SYMBOL);
5560 /* while parsing N from ${var:N[:M]}
5561 * or pattern from ${var/[/]pattern[/repl]} */
5562 if ((end_ch & 0xff) == last_ch) {
5563 /* got ':' or '/'- parse the rest */
5564 end_ch = '}';
5565 goto again;
5566 }
5567 /* got '}' */
5568 if (BASH_SUBSTR && end_ch == '}' * 0x100 + ':') {
5569 /* it's ${var:N} - emulate :999999999 */
5570 o_addstr(dest, "999999999");
5571 } /* else: it's ${var/[/]pattern} */
5572 }
Francis Laniel8197f012023-12-22 22:02:28 +01005573#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01005574 break;
5575 }
5576 len_single_ch = 0; /* it can't be ${#C} op */
5577 }
5578 o_addchr(dest, SPECIAL_VAR_SYMBOL);
5579 break;
5580 }
5581#if ENABLE_FEATURE_SH_MATH || ENABLE_HUSH_TICK
5582 case '(': {
5583 unsigned pos;
5584
5585 ch = i_getch(input);
5586 nommu_addchr(as_string, ch);
5587# if ENABLE_FEATURE_SH_MATH
5588 if (i_peek_and_eat_bkslash_nl(input) == '(') {
5589 ch = i_getch(input);
5590 nommu_addchr(as_string, ch);
5591 o_addchr(dest, SPECIAL_VAR_SYMBOL);
5592 o_addchr(dest, quote_mask | '+');
5593 if (!BB_MMU)
5594 pos = dest->length;
5595 if (!add_till_closing_bracket(dest, input, ')' | DOUBLE_CLOSE_CHAR_FLAG))
5596 return 0; /* error */
5597 if (as_string) {
5598 o_addstr(as_string, dest->data + pos);
5599 o_addchr(as_string, ')');
5600 o_addchr(as_string, ')');
5601 }
5602 o_addchr(dest, SPECIAL_VAR_SYMBOL);
5603 break;
5604 }
5605# endif
5606# if ENABLE_HUSH_TICK
5607 o_addchr(dest, SPECIAL_VAR_SYMBOL);
5608 o_addchr(dest, quote_mask | '`');
5609 if (!BB_MMU)
5610 pos = dest->length;
5611 if (!add_till_closing_bracket(dest, input, ')'))
5612 return 0; /* error */
5613 if (as_string) {
5614 o_addstr(as_string, dest->data + pos);
5615 o_addchr(as_string, ')');
5616 }
5617 o_addchr(dest, SPECIAL_VAR_SYMBOL);
5618# endif
5619 break;
5620 }
5621#endif
5622 case '_':
5623 goto make_var;
5624#if 0
5625 /* TODO: $_: */
5626 /* $_ Shell or shell script name; or last argument of last command
5627 * (if last command wasn't a pipe; if it was, bash sets $_ to "");
5628 * but in command's env, set to full pathname used to invoke it */
5629 ch = i_getch(input);
5630 nommu_addchr(as_string, ch);
5631 ch = i_peek_and_eat_bkslash_nl(input);
5632 if (isalnum(ch)) { /* it's $_name or $_123 */
5633 ch = '_';
5634 goto make_var1;
5635 }
5636 /* else: it's $_ */
5637#endif
5638 default:
5639 o_addQchr(dest, '$');
5640 }
5641 debug_printf_parse("parse_dollar return 1 (ok)\n");
5642 return 1;
5643#undef as_string
5644}
5645
5646#if BB_MMU
5647#define encode_string(as_string, dest, input, dquote_end) \
5648 encode_string(dest, input, dquote_end)
5649#define as_string NULL
5650#endif
5651static int encode_string(o_string *as_string,
5652 o_string *dest,
5653 struct in_str *input,
5654 int dquote_end)
5655{
5656 int ch;
5657 int next;
5658
5659 again:
5660 ch = i_getch(input);
5661 if (ch != EOF)
5662 nommu_addchr(as_string, ch);
5663 if (ch == dquote_end) { /* may be only '"' or EOF */
5664 debug_printf_parse("encode_string return 1 (ok)\n");
5665 return 1;
5666 }
5667 /* note: can't move it above ch == dquote_end check! */
5668 if (ch == EOF) {
5669 syntax_error_unterm_ch('"');
5670 return 0; /* error */
5671 }
5672 next = '\0';
5673 if (ch != '\n') {
5674 next = i_peek(input);
5675 }
5676 debug_printf_parse("\" ch=%c (%d) escape=%d\n",
5677 ch, ch, !!(dest->o_expflags & EXP_FLAG_ESC_GLOB_CHARS));
5678 if (ch == '\\') {
5679 if (next == EOF) {
5680 /* Testcase: in interactive shell a file with
5681 * echo "unterminated string\<eof>
5682 * is sourced.
5683 */
5684 syntax_error_unterm_ch('"');
5685 return 0; /* error */
5686 }
5687 /* bash:
5688 * "The backslash retains its special meaning [in "..."]
5689 * only when followed by one of the following characters:
5690 * $, `, ", \, or <newline>. A double quote may be quoted
5691 * within double quotes by preceding it with a backslash."
5692 * NB: in (unquoted) heredoc, above does not apply to ",
5693 * therefore we check for it by "next == dquote_end" cond.
5694 */
5695 if (next == dquote_end || strchr("$`\\\n", next)) {
5696 ch = i_getch(input); /* eat next */
5697 if (ch == '\n')
5698 goto again; /* skip \<newline> */
5699 } /* else: ch remains == '\\', and we double it below: */
5700 o_addqchr(dest, ch); /* \c if c is a glob char, else just c */
5701 nommu_addchr(as_string, ch);
5702 goto again;
5703 }
5704 if (ch == '$') {
5705 //if (parse_dollar_squote(as_string, dest, input))
5706 // goto again;
5707 if (!parse_dollar(as_string, dest, input, /*quote_mask:*/ 0x80)) {
5708 debug_printf_parse("encode_string return 0: "
5709 "parse_dollar returned 0 (error)\n");
5710 return 0;
5711 }
5712 goto again;
5713 }
5714#if ENABLE_HUSH_TICK
5715 if (ch == '`') {
5716 //unsigned pos = dest->length;
5717 o_addchr(dest, SPECIAL_VAR_SYMBOL);
5718 o_addchr(dest, 0x80 | '`');
5719 if (!add_till_backquote(dest, input, /*in_dquote:*/ dquote_end == '"'))
5720 return 0; /* error */
5721 o_addchr(dest, SPECIAL_VAR_SYMBOL);
5722 //debug_printf_subst("SUBST RES3 '%s'\n", dest->data + pos);
5723 goto again;
5724 }
5725#endif
5726 o_addQchr(dest, ch);
5727 if (ch == SPECIAL_VAR_SYMBOL) {
5728 /* Convert "^C" to corresponding special variable reference */
5729 o_addchr(dest, SPECIAL_VAR_QUOTED_SVS);
5730 o_addchr(dest, SPECIAL_VAR_SYMBOL);
5731 }
5732 goto again;
5733#undef as_string
5734}
5735
5736/*
5737 * Scan input until EOF or end_trigger char.
5738 * Return a list of pipes to execute, or NULL on EOF
5739 * or if end_trigger character is met.
5740 * On syntax error, exit if shell is not interactive,
5741 * reset parsing machinery and start parsing anew,
5742 * or return ERR_PTR.
5743 */
5744static struct pipe *parse_stream(char **pstring,
5745 int *heredoc_cnt_ptr,
5746 struct in_str *input,
5747 int end_trigger)
5748{
5749 struct parse_context ctx;
5750 int heredoc_cnt;
5751
5752 /* Single-quote triggers a bypass of the main loop until its mate is
5753 * found. When recursing, quote state is passed in via ctx.word.o_expflags.
5754 */
5755 debug_printf_parse("parse_stream entered, end_trigger='%c'\n",
5756 end_trigger ? end_trigger : 'X');
5757 debug_enter();
5758
5759 initialize_context(&ctx);
5760
5761 /* If very first arg is "" or '', ctx.word.data may end up NULL.
5762 * Preventing this:
5763 */
5764 ctx.word.data = xzalloc(1); /* start as "", not as NULL */
5765
5766 /* We used to separate words on $IFS here. This was wrong.
5767 * $IFS is used only for word splitting when $var is expanded,
5768 * here we should use blank chars as separators, not $IFS
5769 */
5770
5771 heredoc_cnt = 0;
5772 while (1) {
5773 const char *is_blank;
5774 const char *is_special;
5775 int ch;
5776 int next;
Francis Laniel8197f012023-12-22 22:02:28 +01005777#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01005778 int redir_fd;
5779 redir_type redir_style;
Francis Laniel8197f012023-12-22 22:02:28 +01005780#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01005781
5782 ch = i_getch(input);
5783 debug_printf_parse(": ch=%c (%d) escape=%d\n",
5784 ch, ch, !!(ctx.word.o_expflags & EXP_FLAG_ESC_GLOB_CHARS));
5785 if (ch == EOF) {
5786 struct pipe *pi;
5787
5788 if (heredoc_cnt) {
5789 syntax_error_unterm_str("here document");
5790 goto parse_error_exitcode1;
5791 }
5792 if (end_trigger == ')') {
5793 syntax_error_unterm_ch('(');
5794 goto parse_error_exitcode1;
5795 }
5796 if (end_trigger == '}') {
5797 syntax_error_unterm_ch('{');
5798 goto parse_error_exitcode1;
5799 }
5800
5801 if (done_word(&ctx)) {
5802 goto parse_error_exitcode1;
5803 }
5804 o_free_and_set_NULL(&ctx.word);
5805 done_pipe(&ctx, PIPE_SEQ);
5806 pi = ctx.list_head;
5807 /* If we got nothing... */
5808 /* (this makes bare "&" cmd a no-op.
5809 * bash says: "syntax error near unexpected token '&'") */
5810 if (pi->num_cmds == 0
5811 IF_HAS_KEYWORDS(&& pi->res_word == RES_NONE)
5812 ) {
5813 free_pipe_list(pi);
5814 pi = NULL;
5815 }
5816#if !BB_MMU
5817 debug_printf_parse("as_string1 '%s'\n", ctx.as_string.data);
5818 if (pstring)
5819 *pstring = ctx.as_string.data;
5820 else
5821 o_free(&ctx.as_string);
5822#endif
5823 // heredoc_cnt must be 0 here anyway
5824 //if (heredoc_cnt_ptr)
5825 // *heredoc_cnt_ptr = heredoc_cnt;
5826 debug_leave();
5827 debug_printf_heredoc("parse_stream return heredoc_cnt:%d\n", heredoc_cnt);
5828 debug_printf_parse("parse_stream return %p\n", pi);
5829 return pi;
5830 }
5831
5832 /* Handle "'" and "\" first, as they won't play nice with
5833 * i_peek_and_eat_bkslash_nl() anyway:
5834 * echo z\\
5835 * and
5836 * echo '\
5837 * '
5838 * would break.
5839 */
5840 if (ch == '\\') {
5841 ch = i_getch(input);
5842 if (ch == '\n')
5843 continue; /* drop \<newline>, get next char */
5844 nommu_addchr(&ctx.as_string, '\\');
5845 if (ch == SPECIAL_VAR_SYMBOL) {
5846 nommu_addchr(&ctx.as_string, ch);
5847 /* Convert \^C to corresponding special variable reference */
5848 goto case_SPECIAL_VAR_SYMBOL;
5849 }
5850 o_addchr(&ctx.word, '\\');
5851 if (ch == EOF) {
5852 /* Testcase: eval 'echo Ok\' */
5853 /* bash-4.3.43 was removing backslash,
5854 * but 4.4.19 retains it, most other shells too
5855 */
5856 continue; /* get next char */
5857 }
5858 /* Example: echo Hello \2>file
5859 * we need to know that word 2 is quoted
5860 */
5861 ctx.word.has_quoted_part = 1;
5862 nommu_addchr(&ctx.as_string, ch);
5863 o_addchr(&ctx.word, ch);
5864 continue; /* get next char */
5865 }
5866 nommu_addchr(&ctx.as_string, ch);
5867 if (ch == '\'') {
5868 ctx.word.has_quoted_part = 1;
5869 next = i_getch(input);
Francis Laniel8197f012023-12-22 22:02:28 +01005870#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01005871 if (next == '\'' && !ctx.pending_redirect)
5872 goto insert_empty_quoted_str_marker;
Francis Laniel8197f012023-12-22 22:02:28 +01005873#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01005874
5875 ch = next;
5876 while (1) {
5877 if (ch == EOF) {
5878 syntax_error_unterm_ch('\'');
5879 goto parse_error_exitcode1;
5880 }
5881 nommu_addchr(&ctx.as_string, ch);
5882 if (ch == '\'')
5883 break;
5884 if (ch == SPECIAL_VAR_SYMBOL) {
5885 /* Convert raw ^C to corresponding special variable reference */
5886 o_addchr(&ctx.word, SPECIAL_VAR_SYMBOL);
5887 o_addchr(&ctx.word, SPECIAL_VAR_QUOTED_SVS);
5888 }
5889 o_addqchr(&ctx.word, ch);
5890 ch = i_getch(input);
5891 }
5892 continue; /* get next char */
5893 }
5894
5895 next = '\0';
5896 if (ch != '\n')
5897 next = i_peek_and_eat_bkslash_nl(input);
5898
5899 is_special = "{}<>&|();#" /* special outside of "str" */
Francis Laniel8197f012023-12-22 22:02:28 +01005900#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01005901 "$\"" IF_HUSH_TICK("`") /* always special */
Francis Laniel8197f012023-12-22 22:02:28 +01005902#else /* __U_BOOT__ */
5903 "$\""
5904#endif /* __U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01005905 SPECIAL_VAR_SYMBOL_STR;
5906#if defined(CMD_TEST2_SINGLEWORD_NOGLOB)
5907 if (ctx.command->cmd_type == CMD_TEST2_SINGLEWORD_NOGLOB) {
5908 /* In [[ ]], {}<>&|() are not special */
5909 is_special += 8;
5910 } else
5911#endif
5912 /* Are { and } special here? */
5913 if (ctx.command->argv /* word [word]{... - non-special */
5914 || ctx.word.length /* word{... - non-special */
5915 || ctx.word.has_quoted_part /* ""{... - non-special */
5916 || (next != ';' /* }; - special */
5917 && next != ')' /* }) - special */
5918 && next != '(' /* {( - special */
5919 && next != '&' /* }& and }&& ... - special */
5920 && next != '|' /* }|| ... - special */
5921 && !strchr(defifs, next) /* {word - non-special */
5922 )
5923 ) {
5924 /* They are not special, skip "{}" */
5925 is_special += 2;
5926 }
5927 is_special = strchr(is_special, ch);
5928 is_blank = strchr(defifs, ch);
5929
5930 if (!is_special && !is_blank) { /* ordinary char */
5931 ordinary_char:
5932 o_addQchr(&ctx.word, ch);
5933 if ((ctx.is_assignment == MAYBE_ASSIGNMENT
5934 || ctx.is_assignment == WORD_IS_KEYWORD)
5935 && ch == '='
5936 && endofname(ctx.word.data)[0] == '='
5937 ) {
5938 ctx.is_assignment = DEFINITELY_ASSIGNMENT;
5939 debug_printf_parse("ctx.is_assignment='%s'\n", assignment_flag[ctx.is_assignment]);
5940 }
5941 continue;
5942 }
5943
5944 if (is_blank) {
5945#if ENABLE_HUSH_LINENO_VAR
5946/* Case:
5947 * "while ...; do<whitespace><newline>
5948 * cmd ..."
5949 * would think that "cmd" starts in <whitespace> -
5950 * i.e., at the previous line.
5951 * We need to skip all whitespace before newlines.
5952 */
5953 while (ch != '\n') {
5954 next = i_peek(input);
5955 if (next != ' ' && next != '\t' && next != '\n')
5956 break; /* next char is not ws */
5957 ch = i_getch(input);
5958 }
5959 /* ch == last eaten whitespace char */
5960#endif
5961 if (done_word(&ctx)) {
5962 goto parse_error_exitcode1;
5963 }
5964 if (ch == '\n') {
5965 /* Is this a case when newline is simply ignored?
5966 * Some examples:
5967 * "cmd | <newline> cmd ..."
5968 * "case ... in <newline> word) ..."
5969 */
5970 if (IS_NULL_CMD(ctx.command)
5971 && ctx.word.length == 0
5972 && !ctx.word.has_quoted_part
5973 && heredoc_cnt == 0
5974 ) {
5975 /* This newline can be ignored. But...
5976 * Without check #1, interactive shell
5977 * ignores even bare <newline>,
5978 * and shows the continuation prompt:
5979 * ps1_prompt$ <enter>
5980 * ps2> _ <=== wrong, should be ps1
5981 * Without check #2, "cmd & <newline>"
5982 * is similarly mistreated.
5983 * (BTW, this makes "cmd & cmd"
5984 * and "cmd && cmd" non-orthogonal.
5985 * Really, ask yourself, why
5986 * "cmd && <newline>" doesn't start
5987 * cmd but waits for more input?
5988 * The only reason is that it might be
5989 * a "cmd1 && <nl> cmd2 &" construct,
5990 * cmd1 may need to run in BG).
5991 */
5992 struct pipe *pi = ctx.list_head;
5993 if (pi->num_cmds != 0 /* check #1 */
5994 && pi->followup != PIPE_BG /* check #2 */
5995 ) {
5996 continue;
5997 }
5998 }
5999 /* Treat newline as a command separator. */
6000 done_pipe(&ctx, PIPE_SEQ);
6001 debug_printf_heredoc("heredoc_cnt:%d\n", heredoc_cnt);
6002 if (heredoc_cnt) {
6003 heredoc_cnt = fetch_heredocs(&ctx.as_string, ctx.list_head, heredoc_cnt, input);
6004 if (heredoc_cnt != 0)
6005 goto parse_error_exitcode1;
6006 }
6007 ctx.is_assignment = MAYBE_ASSIGNMENT;
6008 debug_printf_parse("ctx.is_assignment='%s'\n", assignment_flag[ctx.is_assignment]);
6009 ch = ';';
6010 /* note: if (is_blank) continue;
6011 * will still trigger for us */
6012 }
6013 }
6014
6015 /* "cmd}" or "cmd }..." without semicolon or &:
6016 * } is an ordinary char in this case, even inside { cmd; }
6017 * Pathological example: { ""}; } should exec "}" cmd
6018 */
Francis Laniel8197f012023-12-22 22:02:28 +01006019#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01006020 if (ch == '}') {
Francis Laniel8197f012023-12-22 22:02:28 +01006021#else /* __U_BOOT__ */
6022 if (ch == '}' || ch == ')') {
6023#endif /* __U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01006024 if (ctx.word.length != 0 /* word} */
6025 || ctx.word.has_quoted_part /* ""} */
6026 ) {
6027 goto ordinary_char;
6028 }
6029 if (!IS_NULL_CMD(ctx.command)) { /* cmd } */
6030 /* Generally, there should be semicolon: "cmd; }"
6031 * However, bash allows to omit it if "cmd" is
6032 * a group. Examples:
6033 * { { echo 1; } }
6034 * {(echo 1)}
6035 * { echo 0 >&2 | { echo 1; } }
6036 * { while false; do :; done }
6037 * { case a in b) ;; esac }
6038 */
6039 if (ctx.command->group)
6040 goto term_group;
6041 goto ordinary_char;
6042 }
6043 if (!IS_NULL_PIPE(ctx.pipe)) /* cmd | } */
6044 /* Can't be an end of {cmd}, skip the check */
6045 goto skip_end_trigger;
6046 /* else: } does terminate a group */
6047 }
6048 term_group:
6049 if (end_trigger && end_trigger == ch
6050 && (ch != ';' || heredoc_cnt == 0)
6051#if ENABLE_HUSH_CASE
6052 && (ch != ')'
6053 || ctx.ctx_res_w != RES_MATCH
6054 || (!ctx.word.has_quoted_part && strcmp(ctx.word.data, "esac") == 0)
6055 )
6056#endif
6057 ) {
6058 if (done_word(&ctx)) {
6059 goto parse_error_exitcode1;
6060 }
6061 done_pipe(&ctx, PIPE_SEQ);
6062 ctx.is_assignment = MAYBE_ASSIGNMENT;
6063 debug_printf_parse("ctx.is_assignment='%s'\n", assignment_flag[ctx.is_assignment]);
6064 /* Do we sit outside of any if's, loops or case's? */
6065 if (!HAS_KEYWORDS
6066 IF_HAS_KEYWORDS(|| (ctx.ctx_res_w == RES_NONE && ctx.old_flag == 0))
6067 ) {
6068 o_free_and_set_NULL(&ctx.word);
6069#if !BB_MMU
6070 debug_printf_parse("as_string2 '%s'\n", ctx.as_string.data);
6071 if (pstring)
6072 *pstring = ctx.as_string.data;
6073 else
6074 o_free(&ctx.as_string);
6075#endif
6076 if (ch != ';' && IS_NULL_PIPE(ctx.list_head)) {
6077 /* Example: bare "{ }", "()" */
6078 G.last_exitcode = 2; /* bash compat */
6079 syntax_error_unexpected_ch(ch);
6080 goto parse_error;
6081 }
6082 if (heredoc_cnt_ptr)
6083 *heredoc_cnt_ptr = heredoc_cnt;
6084 debug_printf_heredoc("parse_stream return heredoc_cnt:%d\n", heredoc_cnt);
6085 debug_printf_parse("parse_stream return %p: "
6086 "end_trigger char found\n",
6087 ctx.list_head);
6088 debug_leave();
6089 return ctx.list_head;
6090 }
6091 }
6092
6093 if (is_blank)
6094 continue;
6095
6096 /* Catch <, > before deciding whether this word is
6097 * an assignment. a=1 2>z b=2: b=2 is still assignment */
6098 switch (ch) {
Francis Laniel8197f012023-12-22 22:02:28 +01006099#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01006100 case '>':
6101 redir_fd = redirect_opt_num(&ctx.word);
6102 if (done_word(&ctx)) {
6103 goto parse_error_exitcode1;
6104 }
6105 redir_style = REDIRECT_OVERWRITE;
6106 if (next == '>') {
6107 redir_style = REDIRECT_APPEND;
6108 ch = i_getch(input);
6109 nommu_addchr(&ctx.as_string, ch);
6110 }
6111#if 0
6112 else if (next == '(') {
6113 syntax_error(">(process) not supported");
6114 goto parse_error_exitcode1;
6115 }
6116#endif
6117 if (parse_redirect(&ctx, redir_fd, redir_style, input))
6118 goto parse_error_exitcode1;
6119 continue; /* get next char */
6120 case '<':
6121 redir_fd = redirect_opt_num(&ctx.word);
6122 if (done_word(&ctx)) {
6123 goto parse_error_exitcode1;
6124 }
6125 redir_style = REDIRECT_INPUT;
6126 if (next == '<') {
6127 redir_style = REDIRECT_HEREDOC;
6128 heredoc_cnt++;
6129 debug_printf_heredoc("++heredoc_cnt=%d\n", heredoc_cnt);
6130 ch = i_getch(input);
6131 nommu_addchr(&ctx.as_string, ch);
6132 } else if (next == '>') {
6133 redir_style = REDIRECT_IO;
6134 ch = i_getch(input);
6135 nommu_addchr(&ctx.as_string, ch);
6136 }
6137#if 0
6138 else if (next == '(') {
6139 syntax_error("<(process) not supported");
6140 goto parse_error_exitcode1;
6141 }
6142#endif
6143 if (parse_redirect(&ctx, redir_fd, redir_style, input))
6144 goto parse_error_exitcode1;
6145 continue; /* get next char */
Francis Laniel8197f012023-12-22 22:02:28 +01006146#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01006147 case '#':
6148 if (ctx.word.length == 0 && !ctx.word.has_quoted_part) {
6149 /* skip "#comment" */
6150 /* note: we do not add it to &ctx.as_string */
6151/* TODO: in bash:
6152 * comment inside $() goes to the next \n, even inside quoted string (!):
6153 * cmd "$(cmd2 #comment)" - syntax error
6154 * cmd "`cmd2 #comment`" - ok
6155 * We accept both (comment ends where command subst ends, in both cases).
6156 */
6157 while (1) {
6158 ch = i_peek(input);
6159 if (ch == '\n') {
6160 nommu_addchr(&ctx.as_string, '\n');
6161 break;
6162 }
6163 ch = i_getch(input);
6164 if (ch == EOF)
6165 break;
6166 }
6167 continue; /* get next char */
6168 }
6169 break;
6170 }
6171 skip_end_trigger:
6172
6173 if (ctx.is_assignment == MAYBE_ASSIGNMENT
Francis Laniel8197f012023-12-22 22:02:28 +01006174#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01006175 /* check that we are not in word in "a=1 2>word b=1": */
6176 && !ctx.pending_redirect
Francis Laniel8197f012023-12-22 22:02:28 +01006177#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01006178 ) {
6179 /* ch is a special char and thus this word
6180 * cannot be an assignment */
6181 ctx.is_assignment = NOT_ASSIGNMENT;
6182 debug_printf_parse("ctx.is_assignment='%s'\n", assignment_flag[ctx.is_assignment]);
6183 }
6184
6185 /* Note: nommu_addchr(&ctx.as_string, ch) is already done */
6186
6187 switch (ch) {
6188 case_SPECIAL_VAR_SYMBOL:
6189 case SPECIAL_VAR_SYMBOL:
6190 /* Convert raw ^C to corresponding special variable reference */
6191 o_addchr(&ctx.word, SPECIAL_VAR_SYMBOL);
6192 o_addchr(&ctx.word, SPECIAL_VAR_QUOTED_SVS);
6193 /* fall through */
6194 case '#':
6195 /* non-comment #: "echo a#b" etc */
6196 o_addchr(&ctx.word, ch);
6197 continue; /* get next char */
6198 case '$':
Francis Laniel8197f012023-12-22 22:02:28 +01006199#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01006200 if (parse_dollar_squote(&ctx.as_string, &ctx.word, input))
6201 continue; /* get next char */
Francis Laniel8197f012023-12-22 22:02:28 +01006202#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01006203 if (!parse_dollar(&ctx.as_string, &ctx.word, input, /*quote_mask:*/ 0)) {
6204 debug_printf_parse("parse_stream parse error: "
6205 "parse_dollar returned 0 (error)\n");
6206 goto parse_error_exitcode1;
6207 }
6208 continue; /* get next char */
6209 case '"':
6210 ctx.word.has_quoted_part = 1;
Francis Laniel8197f012023-12-22 22:02:28 +01006211#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01006212 if (next == '"' && !ctx.pending_redirect) {
Francis Laniel8197f012023-12-22 22:02:28 +01006213#else /* __U_BOOT__ */
6214 if (next == '"') {
6215#endif /* __U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01006216 i_getch(input); /* eat second " */
Francis Laniel8197f012023-12-22 22:02:28 +01006217#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01006218 insert_empty_quoted_str_marker:
Francis Laniel8197f012023-12-22 22:02:28 +01006219#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01006220 nommu_addchr(&ctx.as_string, next);
6221 o_addchr(&ctx.word, SPECIAL_VAR_SYMBOL);
6222 o_addchr(&ctx.word, SPECIAL_VAR_SYMBOL);
6223 continue; /* get next char */
6224 }
6225 if (ctx.is_assignment == NOT_ASSIGNMENT)
6226 ctx.word.o_expflags |= EXP_FLAG_ESC_GLOB_CHARS;
6227 if (!encode_string(&ctx.as_string, &ctx.word, input, '"'))
6228 goto parse_error_exitcode1;
6229 ctx.word.o_expflags &= ~EXP_FLAG_ESC_GLOB_CHARS;
6230 continue; /* get next char */
6231#if ENABLE_HUSH_TICK
6232 case '`': {
6233 USE_FOR_NOMMU(unsigned pos;)
6234
6235 o_addchr(&ctx.word, SPECIAL_VAR_SYMBOL);
6236 o_addchr(&ctx.word, '`');
6237 USE_FOR_NOMMU(pos = ctx.word.length;)
6238 if (!add_till_backquote(&ctx.word, input, /*in_dquote:*/ 0))
6239 goto parse_error_exitcode1;
6240# if !BB_MMU
6241 o_addstr(&ctx.as_string, ctx.word.data + pos);
6242 o_addchr(&ctx.as_string, '`');
6243# endif
6244 o_addchr(&ctx.word, SPECIAL_VAR_SYMBOL);
6245 //debug_printf_subst("SUBST RES3 '%s'\n", ctx.word.data + pos);
6246 continue; /* get next char */
6247 }
6248#endif
6249 case ';':
6250#if ENABLE_HUSH_CASE
6251 case_semi:
6252#endif
6253 if (done_word(&ctx)) {
6254 goto parse_error_exitcode1;
6255 }
6256 done_pipe(&ctx, PIPE_SEQ);
6257#if ENABLE_HUSH_CASE
6258 /* Eat multiple semicolons, detect
6259 * whether it means something special */
6260 while (1) {
6261 ch = i_peek_and_eat_bkslash_nl(input);
6262 if (ch != ';')
6263 break;
6264 ch = i_getch(input);
6265 nommu_addchr(&ctx.as_string, ch);
6266 if (ctx.ctx_res_w == RES_CASE_BODY) {
6267 ctx.ctx_dsemicolon = 1;
6268 ctx.ctx_res_w = RES_MATCH;
6269 break;
6270 }
6271 }
6272#endif
6273 new_cmd:
6274 /* We just finished a cmd. New one may start
6275 * with an assignment */
6276 ctx.is_assignment = MAYBE_ASSIGNMENT;
6277 debug_printf_parse("ctx.is_assignment='%s'\n", assignment_flag[ctx.is_assignment]);
6278 continue; /* get next char */
6279 case '&':
6280 if (done_word(&ctx)) {
6281 goto parse_error_exitcode1;
6282 }
6283 if (next == '&') {
6284 ch = i_getch(input);
6285 nommu_addchr(&ctx.as_string, ch);
6286 done_pipe(&ctx, PIPE_AND);
6287 } else {
6288 done_pipe(&ctx, PIPE_BG);
6289 }
6290 goto new_cmd;
6291 case '|':
6292 if (done_word(&ctx)) {
6293 goto parse_error_exitcode1;
6294 }
6295#if ENABLE_HUSH_CASE
6296 if (ctx.ctx_res_w == RES_MATCH)
6297 break; /* we are in case's "word | word)" */
6298#endif
6299 if (next == '|') { /* || */
6300 ch = i_getch(input);
6301 nommu_addchr(&ctx.as_string, ch);
6302 done_pipe(&ctx, PIPE_OR);
6303 } else {
6304 /* we could pick up a file descriptor choice here
6305 * with redirect_opt_num(), but bash doesn't do it.
6306 * "echo foo 2| cat" yields "foo 2". */
6307 done_command(&ctx);
6308 }
6309 goto new_cmd;
6310 case '(':
6311#if ENABLE_HUSH_CASE
6312 /* "case... in [(]word)..." - skip '(' */
6313 if (ctx.ctx_res_w == RES_MATCH
6314 && ctx.command->argv == NULL /* not (word|(... */
6315 && ctx.word.length == 0 /* not word(... */
6316 && ctx.word.has_quoted_part == 0 /* not ""(... */
6317 ) {
6318 continue; /* get next char */
6319 }
6320#endif
6321 /* fall through */
6322 case '{': {
6323 int n = parse_group(&ctx, input, ch);
6324 if (n < 0) {
6325 goto parse_error_exitcode1;
6326 }
6327 debug_printf_heredoc("parse_group done, needs heredocs:%d\n", n);
6328 heredoc_cnt += n;
6329 goto new_cmd;
6330 }
6331 case ')':
6332#if ENABLE_HUSH_CASE
6333 if (ctx.ctx_res_w == RES_MATCH)
6334 goto case_semi;
6335#endif
6336 case '}':
6337 /* proper use of this character is caught by end_trigger:
6338 * if we see {, we call parse_group(..., end_trigger='}')
6339 * and it will match } earlier (not here). */
6340 G.last_exitcode = 2;
6341 syntax_error_unexpected_ch(ch);
6342 goto parse_error;
6343 default:
6344 if (HUSH_DEBUG)
6345 bb_error_msg_and_die("BUG: unexpected %c", ch);
6346 }
6347 } /* while (1) */
6348
6349 parse_error_exitcode1:
6350 G.last_exitcode = 1;
6351 parse_error:
6352 {
6353 struct parse_context *pctx;
6354 IF_HAS_KEYWORDS(struct parse_context *p2;)
6355
6356 /* Clean up allocated tree.
6357 * Sample for finding leaks on syntax error recovery path.
6358 * Run it from interactive shell, watch pmap `pidof hush`.
6359 * while if false; then false; fi; do break; fi
6360 * Samples to catch leaks at execution:
6361 * while if (true | { true;}); then echo ok; fi; do break; done
6362 * while if (true | { true;}); then echo ok; fi; do (if echo ok; break; then :; fi) | cat; break; done
6363 */
6364 pctx = &ctx;
6365 do {
6366 /* Update pipe/command counts,
6367 * otherwise freeing may miss some */
6368 done_pipe(pctx, PIPE_SEQ);
6369 debug_printf_clean("freeing list %p from ctx %p\n",
6370 pctx->list_head, pctx);
6371 debug_print_tree(pctx->list_head, 0);
6372 free_pipe_list(pctx->list_head);
6373 debug_printf_clean("freed list %p\n", pctx->list_head);
6374#if !BB_MMU
6375 o_free(&pctx->as_string);
6376#endif
6377 IF_HAS_KEYWORDS(p2 = pctx->stack;)
6378 if (pctx != &ctx) {
6379 free(pctx);
6380 }
6381 IF_HAS_KEYWORDS(pctx = p2;)
6382 } while (HAS_KEYWORDS && pctx);
6383
6384 o_free(&ctx.word);
6385#if !BB_MMU
6386 if (pstring)
6387 *pstring = NULL;
6388#endif
6389 debug_leave();
6390 return ERR_PTR;
6391 }
6392}
6393
6394
6395/*** Execution routines ***/
6396
6397/* Expansion can recurse, need forward decls: */
6398#if !BASH_PATTERN_SUBST && !ENABLE_HUSH_CASE
6399#define expand_string_to_string(str, EXP_flags, do_unbackslash) \
6400 expand_string_to_string(str)
6401#endif
6402static char *expand_string_to_string(const char *str, int EXP_flags, int do_unbackslash);
6403#if ENABLE_HUSH_TICK
6404static int process_command_subs(o_string *dest, const char *s);
6405#endif
6406static int expand_vars_to_list(o_string *output, int n, char *arg);
6407
6408/* expand_strvec_to_strvec() takes a list of strings, expands
6409 * all variable references within and returns a pointer to
6410 * a list of expanded strings, possibly with larger number
6411 * of strings. (Think VAR="a b"; echo $VAR).
6412 * This new list is allocated as a single malloc block.
6413 * NULL-terminated list of char* pointers is at the beginning of it,
6414 * followed by strings themselves.
6415 * Caller can deallocate entire list by single free(list). */
6416
6417/* A horde of its helpers come first: */
6418
6419static void o_addblock_duplicate_backslash(o_string *o, const char *str, int len)
6420{
6421 while (--len >= 0) {
6422 char c = *str++;
6423
6424#if ENABLE_HUSH_BRACE_EXPANSION
6425 if (c == '{' || c == '}') {
6426 /* { -> \{, } -> \} */
6427 o_addchr(o, '\\');
6428 /* And now we want to add { or } and continue:
6429 * o_addchr(o, c);
6430 * continue;
6431 * luckily, just falling through achieves this.
6432 */
6433 }
6434#endif
6435 o_addchr(o, c);
6436 if (c == '\\') {
6437 /* \z -> \\\z; \<eol> -> \\<eol> */
6438 o_addchr(o, '\\');
6439 if (len) {
6440 len--;
6441 o_addchr(o, '\\');
6442 o_addchr(o, *str++);
6443 }
6444 }
6445 }
6446}
6447
6448/* Store given string, finalizing the word and starting new one whenever
6449 * we encounter IFS char(s). This is used for expanding variable values.
6450 * End-of-string does NOT finalize word: think about 'echo -$VAR-'.
6451 * Return in output->ended_in_ifs:
6452 * 1 - ended with IFS char, else 0 (this includes case of empty str).
6453 */
6454static int expand_on_ifs(o_string *output, int n, const char *str)
6455{
6456 int last_is_ifs = 0;
6457
6458 while (1) {
6459 int word_len;
6460
6461 if (!*str) /* EOL - do not finalize word */
6462 break;
6463 word_len = strcspn(str, G.ifs);
6464 if (word_len) {
6465 /* We have WORD_LEN leading non-IFS chars */
6466 if (!(output->o_expflags & EXP_FLAG_GLOB)) {
6467 o_addblock(output, str, word_len);
6468 } else {
6469 /* Protect backslashes against globbing up :)
6470 * Example: "v='\*'; echo b$v" prints "b\*"
6471 * (and does not try to glob on "*")
6472 */
6473 o_addblock_duplicate_backslash(output, str, word_len);
6474 /*/ Why can't we do it easier? */
6475 /*o_addblock(output, str, word_len); - WRONG: "v='\*'; echo Z$v" prints "Z*" instead of "Z\*" */
6476 /*o_addqblock(output, str, word_len); - WRONG: "v='*'; echo Z$v" prints "Z*" instead of Z* files */
6477 }
6478 last_is_ifs = 0;
6479 str += word_len;
6480 if (!*str) /* EOL - do not finalize word */
6481 break;
6482 }
6483
6484 /* We know str here points to at least one IFS char */
6485 last_is_ifs = 1;
6486 str += strspn(str, G.ifs_whitespace); /* skip IFS whitespace chars */
6487 if (!*str) /* EOL - do not finalize word */
6488 break;
6489
6490 if (G.ifs_whitespace != G.ifs /* usually false ($IFS is usually all whitespace), */
6491 && strchr(G.ifs, *str) /* the second check would fail */
6492 ) {
6493 /* This is a non-whitespace $IFS char */
6494 /* Skip it and IFS whitespace chars, start new word */
6495 str++;
6496 str += strspn(str, G.ifs_whitespace);
6497 goto new_word;
6498 }
6499
6500 /* Start new word... but not always! */
6501 /* Case "v=' a'; echo ''$v": we do need to finalize empty word: */
6502 if (output->has_quoted_part
6503 /*
6504 * Case "v=' a'; echo $v":
6505 * here nothing precedes the space in $v expansion,
6506 * therefore we should not finish the word
6507 * (IOW: if there *is* word to finalize, only then do it):
6508 * It's okay if this accesses the byte before first argv[]:
6509 * past call to o_save_ptr() cleared it to zero byte
6510 * (grep for -prev-ifs-check-).
6511 */
6512 || output->data[output->length - 1]
6513 ) {
6514 new_word:
6515 o_addchr(output, '\0');
6516 debug_print_list("expand_on_ifs", output, n);
6517 n = o_save_ptr(output, n);
6518 }
6519 }
6520
6521 output->ended_in_ifs = last_is_ifs;
6522 debug_print_list("expand_on_ifs[1]", output, n);
6523 return n;
6524}
6525
Francis Laniel8197f012023-12-22 22:02:28 +01006526#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01006527/* Helper to expand $((...)) and heredoc body. These act as if
6528 * they are in double quotes, with the exception that they are not :).
6529 * Just the rules are similar: "expand only $var and `cmd`"
6530 *
6531 * Returns malloced string.
6532 * As an optimization, we return NULL if expansion is not needed.
6533 */
6534static char *encode_then_expand_string(const char *str)
6535{
6536 char *exp_str;
6537 struct in_str input;
6538 o_string dest = NULL_O_STRING;
6539 const char *cp;
6540
6541 cp = str;
6542 for (;;) {
6543 if (!*cp) return NULL; /* string has no special chars */
6544 if (*cp == '$') break;
6545 if (*cp == '\\') break;
6546#if ENABLE_HUSH_TICK
6547 if (*cp == '`') break;
6548#endif
6549 cp++;
6550 }
6551
6552 /* We need to expand. Example:
6553 * echo $(($a + `echo 1`)) $((1 + $((2)) ))
6554 */
6555 setup_string_in_str(&input, str);
6556 encode_string(NULL, &dest, &input, EOF);
6557//TODO: error check (encode_string returns 0 on error)?
6558 //bb_error_msg("'%s' -> '%s'", str, dest.data);
6559 exp_str = expand_string_to_string(dest.data,
6560 EXP_FLAG_ESC_GLOB_CHARS,
6561 /*unbackslash:*/ 1
6562 );
6563 //bb_error_msg("'%s' -> '%s'", dest.data, exp_str);
6564 o_free(&dest);
6565 return exp_str;
6566}
6567
6568static const char *first_special_char_in_vararg(const char *cp)
6569{
6570 for (;;) {
6571 if (!*cp) return NULL; /* string has no special chars */
6572 if (*cp == '$') return cp;
6573 if (*cp == '\\') return cp;
6574 if (*cp == '\'') return cp;
6575 if (*cp == '"') return cp;
6576#if ENABLE_HUSH_TICK
6577 if (*cp == '`') return cp;
6578#endif
6579 /* dquoted "${x:+ARG}" should not glob, therefore
6580 * '*' et al require some non-literal processing: */
6581 if (*cp == '*') return cp;
6582 if (*cp == '?') return cp;
6583 if (*cp == '[') return cp;
6584 cp++;
6585 }
6586}
6587
6588/* Expanding ARG in ${var#ARG}, ${var%ARG}, or ${var/ARG/ARG}.
6589 * These can contain single- and double-quoted strings,
6590 * and treated as if the ARG string is initially unquoted. IOW:
6591 * ${var#ARG} and "${var#ARG}" treat ARG the same (ARG can even be
6592 * a dquoted string: "${var#"zz"}"), the difference only comes later
6593 * (word splitting and globbing of the ${var...} result).
6594 */
6595#if !BASH_PATTERN_SUBST
6596#define encode_then_expand_vararg(str, handle_squotes, do_unbackslash) \
6597 encode_then_expand_vararg(str, handle_squotes)
6598#endif
6599static char *encode_then_expand_vararg(const char *str, int handle_squotes, int do_unbackslash)
6600{
6601#if !BASH_PATTERN_SUBST && ENABLE_HUSH_CASE
6602 const int do_unbackslash = 0;
6603#endif
6604 char *exp_str;
6605 struct in_str input;
6606 o_string dest = NULL_O_STRING;
6607
6608 if (!first_special_char_in_vararg(str)) {
6609 /* string has no special chars */
6610 return NULL;
6611 }
6612
6613 setup_string_in_str(&input, str);
6614 dest.data = xzalloc(1); /* start as "", not as NULL */
6615 exp_str = NULL;
6616
6617 for (;;) {
6618 int ch;
6619
6620 ch = i_getch(&input);
6621 debug_printf_parse("%s: ch=%c (%d) escape=%d\n",
6622 __func__, ch, ch, !!dest.o_expflags);
6623
6624 if (!dest.o_expflags) {
6625 if (ch == EOF)
6626 break;
6627 if (handle_squotes && ch == '\'') {
6628 if (!add_till_single_quote_dquoted(&dest, &input))
6629 goto ret; /* error */
6630 continue;
6631 }
6632 }
6633 if (ch == EOF) {
6634 syntax_error_unterm_ch('"');
6635 goto ret; /* error */
6636 }
6637 if (ch == '"') {
6638 dest.o_expflags ^= EXP_FLAG_ESC_GLOB_CHARS;
6639 continue;
6640 }
6641 if (ch == '\\') {
6642 ch = i_getch(&input);
6643 if (ch == EOF) {
6644//example? error message? syntax_error_unterm_ch('"');
6645 debug_printf_parse("%s: error: \\<eof>\n", __func__);
6646 goto ret;
6647 }
6648 o_addqchr(&dest, ch);
6649 continue;
6650 }
6651 if (ch == '$') {
6652 if (parse_dollar_squote(NULL, &dest, &input))
6653 continue;
6654 if (!parse_dollar(NULL, &dest, &input, /*quote_mask:*/ 0x80)) {
6655 debug_printf_parse("%s: error: parse_dollar returned 0 (error)\n", __func__);
6656 goto ret;
6657 }
6658 continue;
6659 }
6660#if ENABLE_HUSH_TICK
6661 if (ch == '`') {
6662 //unsigned pos = dest->length;
6663 o_addchr(&dest, SPECIAL_VAR_SYMBOL);
6664 o_addchr(&dest, 0x80 | '`');
6665 if (!add_till_backquote(&dest, &input,
6666 /*in_dquote:*/ dest.o_expflags /* nonzero if EXP_FLAG_ESC_GLOB_CHARS set */
6667 )
6668 ) {
6669 goto ret; /* error */
6670 }
6671 o_addchr(&dest, SPECIAL_VAR_SYMBOL);
6672 //debug_printf_subst("SUBST RES3 '%s'\n", dest->data + pos);
6673 continue;
6674 }
6675#endif
6676 o_addQchr(&dest, ch);
6677 } /* for (;;) */
6678
6679 debug_printf_parse("encode: '%s' -> '%s'\n", str, dest.data);
6680 exp_str = expand_string_to_string(dest.data,
6681 do_unbackslash ? EXP_FLAG_ESC_GLOB_CHARS : 0,
6682 do_unbackslash
6683 );
6684 ret:
6685 debug_printf_parse("expand: '%s' -> '%s'\n", dest.data, exp_str);
6686 o_free(&dest);
6687 return exp_str;
6688}
6689
6690/* Expanding ARG in ${var+ARG}, ${var-ARG}
6691 */
6692static int encode_then_append_var_plusminus(o_string *output, int n,
6693 char *str, int dquoted)
6694{
6695 struct in_str input;
6696 o_string dest = NULL_O_STRING;
6697
6698 if (!first_special_char_in_vararg(str)
6699 && '\0' == str[strcspn(str, G.ifs)]
6700 ) {
6701 /* string has no special chars
6702 * && string has no $IFS chars
6703 */
6704 if (dquoted) {
6705 /* Prints 1 (quoted expansion is a "" word, not nothing):
6706 * set -- "${notexist-}"; echo $#
6707 */
6708 output->has_quoted_part = 1;
6709 }
6710 return expand_vars_to_list(output, n, str);
6711 }
6712
6713 setup_string_in_str(&input, str);
6714
6715 for (;;) {
6716 int ch;
6717
6718 ch = i_getch(&input);
6719 debug_printf_parse("%s: ch=%c (%d) escape=%x\n",
6720 __func__, ch, ch, dest.o_expflags);
6721
6722 if (!dest.o_expflags) {
6723 if (ch == EOF)
6724 break;
6725 if (!dquoted && strchr(G.ifs, ch)) {
6726 /* PREFIX${x:d${e}f ...} and we met space: expand "d${e}f" and start new word.
6727 * do not assume we are at the start of the word (PREFIX above).
6728 */
6729 if (dest.data) {
6730 n = expand_vars_to_list(output, n, dest.data);
6731 o_free_and_set_NULL(&dest);
6732 o_addchr(output, '\0');
6733 n = o_save_ptr(output, n); /* create next word */
6734 } else
6735 if (output->length != o_get_last_ptr(output, n)
6736 || output->has_quoted_part
6737 ) {
6738 /* For these cases:
6739 * f() { for i; do echo "|$i|"; done; }; x=x
6740 * f a${x:+ }b # 1st condition
6741 * |a|
6742 * |b|
6743 * f ""${x:+ }b # 2nd condition
6744 * ||
6745 * |b|
6746 */
6747 o_addchr(output, '\0');
6748 n = o_save_ptr(output, n); /* create next word */
6749 }
6750 continue;
6751 }
6752 if (!dquoted && ch == '\'') {
6753 if (!add_till_single_quote_dquoted(&dest, &input))
6754 goto ret; /* error */
6755 o_addchr(&dest, SPECIAL_VAR_SYMBOL);
6756 o_addchr(&dest, SPECIAL_VAR_SYMBOL);
6757 continue;
6758 }
6759 }
6760 if (ch == EOF) {
6761 syntax_error_unterm_ch('"');
6762 goto ret; /* error */
6763 }
6764 if (ch == '"') {
6765 dest.o_expflags ^= EXP_FLAG_ESC_GLOB_CHARS;
6766 if (dest.o_expflags) {
6767 o_addchr(&dest, SPECIAL_VAR_SYMBOL);
6768 o_addchr(&dest, SPECIAL_VAR_SYMBOL);
6769 }
6770 continue;
6771 }
6772 if (ch == '\\') {
6773 ch = i_getch(&input);
6774 if (ch == EOF) {
6775//example? error message? syntax_error_unterm_ch('"');
6776 debug_printf_parse("%s: error: \\<eof>\n", __func__);
6777 goto ret;
6778 }
6779 o_addqchr(&dest, ch);
6780 continue;
6781 }
6782 if (ch == '$') {
6783 if (!parse_dollar(NULL, &dest, &input, /*quote_mask:*/ (dest.o_expflags || dquoted) ? 0x80 : 0)) {
6784 debug_printf_parse("%s: error: parse_dollar returned 0 (error)\n", __func__);
6785 goto ret;
6786 }
6787 continue;
6788 }
6789#if ENABLE_HUSH_TICK
6790 if (ch == '`') {
6791 //unsigned pos = dest->length;
6792 o_addchr(&dest, SPECIAL_VAR_SYMBOL);
6793 o_addchr(&dest, (dest.o_expflags || dquoted) ? 0x80 | '`' : '`');
6794 if (!add_till_backquote(&dest, &input,
6795 /*in_dquote:*/ dest.o_expflags /* nonzero if EXP_FLAG_ESC_GLOB_CHARS set */
6796 )
6797 ) {
6798 goto ret; /* error */
6799 }
6800 o_addchr(&dest, SPECIAL_VAR_SYMBOL);
6801 //debug_printf_subst("SUBST RES3 '%s'\n", dest->data + pos);
6802 continue;
6803 }
6804#endif
6805 if (dquoted) {
6806 /* Always glob-protect if in dquotes:
6807 * x=x; echo "${x:+/bin/c*}" - prints: /bin/c*
6808 * x=x; echo "${x:+"/bin/c*"}" - prints: /bin/c*
6809 */
6810 o_addqchr(&dest, ch);
6811 } else {
6812 /* Glob-protect only if char is quoted:
6813 * x=x; echo ${x:+/bin/c*} - prints many filenames
6814 * x=x; echo ${x:+"/bin/c*"} - prints: /bin/c*
6815 */
6816 o_addQchr(&dest, ch);
6817 }
6818 } /* for (;;) */
6819
6820 if (dest.data) {
6821 n = expand_vars_to_list(output, n, dest.data);
6822 }
6823 ret:
6824 o_free(&dest);
6825 return n;
6826}
Francis Laniel8197f012023-12-22 22:02:28 +01006827#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01006828
Francis Laniel8197f012023-12-22 22:02:28 +01006829#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01006830#if ENABLE_FEATURE_SH_MATH
6831static arith_t expand_and_evaluate_arith(const char *arg, const char **errmsg_p)
6832{
6833 arith_state_t math_state;
6834 arith_t res;
6835 char *exp_str;
6836
6837 math_state.lookupvar = get_local_var_value;
6838 math_state.setvar = set_local_var_from_halves;
6839 //math_state.endofname = endofname;
6840 exp_str = encode_then_expand_string(arg);
6841 res = arith(&math_state, exp_str ? exp_str : arg);
6842 free(exp_str);
6843 if (errmsg_p)
6844 *errmsg_p = math_state.errmsg;
6845 if (math_state.errmsg)
6846 msg_and_die_if_script(math_state.errmsg);
6847 return res;
6848}
6849#endif
Francis Laniel8197f012023-12-22 22:02:28 +01006850#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01006851
Francis Laniel8197f012023-12-22 22:02:28 +01006852#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01006853#if BASH_PATTERN_SUBST
6854/* ${var/[/]pattern[/repl]} helpers */
6855static char *strstr_pattern(char *val, const char *pattern, int *size)
6856{
6857 int sz = strcspn(pattern, "*?[\\");
6858 if (pattern[sz] == '\0') {
6859 /* Optimization for trivial patterns.
6860 * Testcase for very slow replace (performs about 22k replaces):
6861 * x=::::::::::::::::::::::
6862 * 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}
6863 * echo "${x//:/|}"
6864 */
6865 *size = sz;
6866 return strstr(val, pattern);
6867 }
6868
6869 while (1) {
6870 char *end = scan_and_match(val, pattern, SCAN_MOVE_FROM_RIGHT + SCAN_MATCH_LEFT_HALF);
6871 debug_printf_varexp("val:'%s' pattern:'%s' end:'%s'\n", val, pattern, end);
6872 if (end) {
6873 *size = end - val;
6874 return val;
6875 }
6876 if (*val == '\0')
6877 return NULL;
6878 /* Optimization: if "*pat" did not match the start of "string",
6879 * we know that "tring", "ring" etc will not match too:
6880 */
6881 if (pattern[0] == '*')
6882 return NULL;
6883 val++;
6884 }
6885}
6886static char *replace_pattern(char *val, const char *pattern, const char *repl, char exp_op)
6887{
6888 char *result = NULL;
6889 unsigned res_len = 0;
6890 unsigned repl_len = strlen(repl);
6891
6892 /* Null pattern never matches, including if "var" is empty */
6893 if (!pattern[0])
6894 return result; /* NULL, no replaces happened */
6895
6896 while (1) {
6897 int size;
6898 char *s = strstr_pattern(val, pattern, &size);
6899 if (!s)
6900 break;
6901
6902 result = xrealloc(result, res_len + (s - val) + repl_len + 1);
6903 strcpy(mempcpy(result + res_len, val, s - val), repl);
6904 res_len += (s - val) + repl_len;
6905 debug_printf_varexp("val:'%s' s:'%s' result:'%s'\n", val, s, result);
6906
6907 val = s + size;
6908 if (exp_op == '/')
6909 break;
6910 }
6911 if (*val && result) {
6912 result = xrealloc(result, res_len + strlen(val) + 1);
6913 strcpy(result + res_len, val);
6914 debug_printf_varexp("val:'%s' result:'%s'\n", val, result);
6915 }
6916 debug_printf_varexp("result:'%s'\n", result);
6917 return result;
6918}
6919#endif /* BASH_PATTERN_SUBST */
Francis Laniel8197f012023-12-22 22:02:28 +01006920#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01006921
6922static int append_str_maybe_ifs_split(o_string *output, int n,
6923 int first_ch, const char *val)
6924{
6925 if (!(first_ch & 0x80)) { /* unquoted $VAR */
6926 debug_printf_expand("unquoted '%s', output->o_escape:%d\n", val,
6927 !!(output->o_expflags & EXP_FLAG_ESC_GLOB_CHARS));
6928 if (val && val[0])
6929 n = expand_on_ifs(output, n, val);
6930 } else { /* quoted "$VAR" */
6931 output->has_quoted_part = 1;
6932 debug_printf_expand("quoted '%s', output->o_escape:%d\n", val,
6933 !!(output->o_expflags & EXP_FLAG_ESC_GLOB_CHARS));
6934 if (val && val[0])
6935 o_addQstr(output, val);
6936 }
6937 return n;
6938}
6939
6940/* Handle <SPECIAL_VAR_SYMBOL>varname...<SPECIAL_VAR_SYMBOL> construct.
6941 */
6942static NOINLINE int expand_one_var(o_string *output, int n,
6943 int first_ch, char *arg, char **pp)
6944{
6945 const char *val;
6946 char *to_be_freed;
6947 char *p;
6948 char *var;
6949 char exp_op;
6950 char exp_save = exp_save; /* for compiler */
6951 char *exp_saveptr; /* points to expansion operator */
6952 char *exp_word = exp_word; /* for compiler */
6953 char arg0;
6954
6955 val = NULL;
6956 to_be_freed = NULL;
6957 p = *pp;
6958 *p = '\0'; /* replace trailing SPECIAL_VAR_SYMBOL */
6959 var = arg;
6960 exp_saveptr = arg[1] ? strchr(VAR_ENCODED_SUBST_OPS, arg[1]) : NULL;
6961 arg0 = arg[0];
6962 arg[0] = (arg0 & 0x7f);
6963 exp_op = 0;
6964
6965 if (arg[0] == '#' && arg[1] /* ${#...} but not ${#} */
6966 && (!exp_saveptr /* and ( not(${#<op_char>...}) */
6967 || (arg[2] == '\0' && strchr(SPECIAL_VARS_STR, arg[1])) /* or ${#C} "len of $C" ) */
6968 ) /* NB: skipping ^^^specvar check mishandles ${#::2} */
6969 ) {
6970 /* It must be length operator: ${#var} */
6971 var++;
6972 exp_op = 'L';
6973 } else {
6974 /* Maybe handle parameter expansion */
6975 if (exp_saveptr /* if 2nd char is one of expansion operators */
6976 && strchr(NUMERIC_SPECVARS_STR, arg[0]) /* 1st char is special variable */
6977 ) {
6978 /* ${?:0}, ${#[:]%0} etc */
6979 exp_saveptr = var + 1;
6980 } else {
6981 /* ${?}, ${var}, ${var:0}, ${var[:]%0} etc */
6982 exp_saveptr = var+1 + strcspn(var+1, VAR_ENCODED_SUBST_OPS);
6983 }
6984 exp_op = exp_save = *exp_saveptr;
Francis Laniel8197f012023-12-22 22:02:28 +01006985#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01006986 if (exp_op) {
6987 exp_word = exp_saveptr + 1;
6988 if (exp_op == ':') {
6989 exp_op = *exp_word++;
6990//TODO: try ${var:} and ${var:bogus} in non-bash config
6991 if (BASH_SUBSTR
6992 && (!exp_op || !strchr(MINUS_PLUS_EQUAL_QUESTION, exp_op))
6993 ) {
6994 /* oops... it's ${var:N[:M]}, not ${var:?xxx} or some such */
6995 exp_op = ':';
6996 exp_word--;
6997 }
6998 }
6999 *exp_saveptr = '\0';
7000 } /* else: it's not an expansion op, but bare ${var} */
Francis Laniel8197f012023-12-22 22:02:28 +01007001#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01007002 }
7003
7004 /* Look up the variable in question */
7005 if (isdigit(var[0])) {
7006 /* parse_dollar should have vetted var for us */
Francis Laniel8197f012023-12-22 22:02:28 +01007007#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01007008 int nn = xatoi_positive(var);
Francis Laniel8197f012023-12-22 22:02:28 +01007009#else /* __U_BOOT__ */
7010 int nn = simple_strtoul(var, NULL, 10);
7011#endif /* __U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01007012 if (nn < G.global_argc)
7013 val = G.global_argv[nn];
7014 /* else val remains NULL: $N with too big N */
7015 } else {
7016 switch (var[0]) {
Francis Laniel8197f012023-12-22 22:02:28 +01007017#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01007018 case '$': /* pid */
7019 val = utoa(G.root_pid);
7020 break;
7021 case '!': /* bg pid */
7022 val = G.last_bg_pid ? utoa(G.last_bg_pid) : "";
7023 break;
Francis Laniel8197f012023-12-22 22:02:28 +01007024#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01007025 case '?': /* exitcode */
7026 val = utoa(G.last_exitcode);
7027 break;
7028 case '#': /* argc */
7029 val = utoa(G.global_argc ? G.global_argc-1 : 0);
7030 break;
Francis Laniel8197f012023-12-22 22:02:28 +01007031#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01007032 case '-': { /* active options */
7033 /* Check set_mode() to see what option chars we support */
7034 char *cp;
7035 val = cp = G.optstring_buf;
7036 if (G.o_opt[OPT_O_ERREXIT])
7037 *cp++ = 'e';
7038 if (G_interactive_fd)
7039 *cp++ = 'i';
7040 if (G_x_mode)
7041 *cp++ = 'x';
7042 /* If G.o_opt[OPT_O_NOEXEC] is true,
7043 * commands read but are not executed,
7044 * so $- can not execute too, 'n' is never seen in $-.
7045 */
7046 if (G.opt_c)
7047 *cp++ = 'c';
7048 if (G.opt_s)
7049 *cp++ = 's';
7050 *cp = '\0';
7051 break;
7052 }
Francis Laniel8197f012023-12-22 22:02:28 +01007053#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01007054 default:
Francis Laniel74e42542023-12-22 22:02:33 +01007055#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01007056 val = get_local_var_value(var);
Francis Laniel74e42542023-12-22 22:02:33 +01007057#else /* __U_BOOT__ */
7058 /*
7059 * Environment variable set with setenv* have to be
7060 * expanded.
7061 * So, we first search if the variable exists in
7062 * environment, if this is not the case, we default to
7063 * local value.
7064 */
7065 val = env_get(var);
7066 if (!val)
7067 val = get_local_var_value(var);
7068#endif /* __U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01007069 }
7070 }
7071
Francis Laniel8197f012023-12-22 22:02:28 +01007072#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01007073 /* Handle any expansions */
7074 if (exp_op == 'L') {
7075 reinit_unicode_for_hush();
7076 debug_printf_expand("expand: length(%s)=", val);
7077 val = utoa(val ? unicode_strlen(val) : 0);
7078 debug_printf_expand("%s\n", val);
7079 } else if (exp_op) {
7080 if (exp_op == '%' || exp_op == '#') {
7081 /* Standard-mandated substring removal ops:
7082 * ${parameter%word} - remove smallest suffix pattern
7083 * ${parameter%%word} - remove largest suffix pattern
7084 * ${parameter#word} - remove smallest prefix pattern
7085 * ${parameter##word} - remove largest prefix pattern
7086 *
7087 * Word is expanded to produce a glob pattern.
7088 * Then var's value is matched to it and matching part removed.
7089 */
7090 /* bash compat: if x is "" and no shrinking of it is possible,
7091 * inner ${...} is not evaluated. Example:
7092 * unset b; : ${a%${b=B}}; echo $b
7093 * assignment b=B only happens if $a is not "".
7094 */
7095 if (val && val[0]) {
7096 char *t;
7097 char *exp_exp_word;
7098 char *loc;
7099 unsigned scan_flags = pick_scan(exp_op, *exp_word);
7100 if (exp_op == *exp_word) /* ## or %% */
7101 exp_word++;
7102 debug_printf_expand("expand: exp_word:'%s'\n", exp_word);
7103 exp_exp_word = encode_then_expand_vararg(exp_word, /*handle_squotes:*/ 1, /*unbackslash:*/ 0);
7104 if (exp_exp_word)
7105 exp_word = exp_exp_word;
7106 debug_printf_expand("expand: exp_word:'%s'\n", exp_word);
7107 /*
7108 * HACK ALERT. We depend here on the fact that
7109 * G.global_argv and results of utoa and get_local_var_value
7110 * are actually in writable memory:
7111 * scan_and_match momentarily stores NULs there.
7112 */
7113 t = (char*)val;
7114 loc = scan_and_match(t, exp_word, scan_flags);
7115 debug_printf_expand("op:%c str:'%s' pat:'%s' res:'%s'\n", exp_op, t, exp_word, loc);
7116 free(exp_exp_word);
7117 if (loc) { /* match was found */
7118 if (scan_flags & SCAN_MATCH_LEFT_HALF) /* #[#] */
7119 val = loc; /* take right part */
7120 else /* %[%] */
7121 val = to_be_freed = xstrndup(val, loc - val); /* left */
7122 }
7123 }
7124 }
7125#if BASH_PATTERN_SUBST
7126 else if (exp_op == '/' || exp_op == '\\') {
7127 /* It's ${var/[/]pattern[/repl]} thing.
7128 * Note that in encoded form it has TWO parts:
7129 * var/pattern<SPECIAL_VAR_SYMBOL>repl<SPECIAL_VAR_SYMBOL>
7130 * and if // is used, it is encoded as \:
7131 * var\pattern<SPECIAL_VAR_SYMBOL>repl<SPECIAL_VAR_SYMBOL>
7132 */
7133 /* bash compat: if var is "", both pattern and repl
7134 * are still evaluated, if it is unset, then not:
7135 * unset b; a=; : ${a/z/${b=3}}; echo $b # b=3
7136 * unset b; unset a; : ${a/z/${b=3}}; echo $b # b not set
7137 */
7138 if (val /*&& val[0]*/) {
7139 /* pattern uses non-standard expansion.
7140 * repl should be unbackslashed and globbed
7141 * by the usual expansion rules:
7142 * >az >bz
7143 * v='a bz'; echo "${v/a*z/a*z}" #prints "a*z"
7144 * v='a bz'; echo "${v/a*z/\z}" #prints "z"
7145 * v='a bz'; echo ${v/a*z/a*z} #prints "az"
7146 * v='a bz'; echo ${v/a*z/\z} #prints "z"
7147 * (note that a*z _pattern_ is never globbed!)
7148 */
7149 char *pattern, *repl, *t;
7150 pattern = encode_then_expand_vararg(exp_word, /*handle_squotes:*/ 1, /*unbackslash:*/ 0);
7151 if (!pattern)
7152 pattern = xstrdup(exp_word);
7153 debug_printf_varexp("pattern:'%s'->'%s'\n", exp_word, pattern);
7154 *p++ = SPECIAL_VAR_SYMBOL;
7155 exp_word = p;
7156 p = strchr(p, SPECIAL_VAR_SYMBOL);
7157 *p = '\0';
7158 repl = encode_then_expand_vararg(exp_word, /*handle_squotes:*/ 1, /*unbackslash:*/ 1);
7159 debug_printf_varexp("repl:'%s'->'%s'\n", exp_word, repl);
7160 /* HACK ALERT. We depend here on the fact that
7161 * G.global_argv and results of utoa and get_local_var_value
7162 * are actually in writable memory:
7163 * replace_pattern momentarily stores NULs there. */
7164 t = (char*)val;
7165 to_be_freed = replace_pattern(t,
7166 pattern,
7167 (repl ? repl : exp_word),
7168 exp_op);
7169 if (to_be_freed) /* at least one replace happened */
7170 val = to_be_freed;
7171 free(pattern);
7172 free(repl);
7173 } else {
7174 /* Unset variable always gives nothing */
7175 // a=; echo ${a/*/w} # "w"
7176 // unset a; echo ${a/*/w} # ""
7177 /* Just skip "replace" part */
7178 *p++ = SPECIAL_VAR_SYMBOL;
7179 p = strchr(p, SPECIAL_VAR_SYMBOL);
7180 *p = '\0';
7181 }
7182 }
7183#endif /* BASH_PATTERN_SUBST */
7184 else if (exp_op == ':') {
7185#if BASH_SUBSTR && ENABLE_FEATURE_SH_MATH
7186 /* It's ${var:N[:M]} bashism.
7187 * Note that in encoded form it has TWO parts:
7188 * var:N<SPECIAL_VAR_SYMBOL>M<SPECIAL_VAR_SYMBOL>
7189 */
7190 arith_t beg, len;
7191 unsigned vallen;
7192 const char *errmsg;
7193
7194 beg = expand_and_evaluate_arith(exp_word, &errmsg);
7195 if (errmsg)
7196 goto empty_result;
7197 debug_printf_varexp("beg:'%s'=%lld\n", exp_word, (long long)beg);
7198 *p++ = SPECIAL_VAR_SYMBOL;
7199 exp_word = p;
7200 p = strchr(p, SPECIAL_VAR_SYMBOL);
7201 *p = '\0';
7202 vallen = val ? strlen(val) : 0;
7203 if (beg < 0) {
7204 /* negative beg counts from the end */
7205 beg = (arith_t)vallen + beg;
7206 }
7207 /* If expansion will be empty, do not even evaluate len */
7208 if (!val || beg < 0 || beg > vallen) {
7209 /* Why > vallen, not >=? bash:
7210 * unset b; a=ab; : ${a:2:${b=3}}; echo $b # "", b=3 (!!!)
7211 * unset b; a=a; : ${a:2:${b=3}}; echo $b # "", b not set
7212 */
7213 goto empty_result;
7214 }
7215 len = expand_and_evaluate_arith(exp_word, &errmsg);
7216 if (errmsg)
7217 goto empty_result;
7218 debug_printf_varexp("len:'%s'=%lld\n", exp_word, (long long)len);
7219 debug_printf_varexp("from val:'%s'\n", val);
7220 if (len < 0) {
7221 /* in bash, len=-n means strlen()-n */
7222 len = (arith_t)vallen - beg + len;
7223 if (len < 0) /* bash compat */
7224 msg_and_die_if_script("%s: substring expression < 0", var);
7225 }
7226 if (len <= 0 || !val /*|| beg >= vallen*/) {
7227 empty_result:
7228 val = NULL;
7229 } else {
7230 /* Paranoia. What if user entered 9999999999999
7231 * which fits in arith_t but not int? */
7232 if (len > INT_MAX)
7233 len = INT_MAX;
7234 val = to_be_freed = xstrndup(val + beg, len);
7235 }
7236 debug_printf_varexp("val:'%s'\n", val);
7237#else /* not (HUSH_SUBSTR_EXPANSION && FEATURE_SH_MATH) */
7238 msg_and_die_if_script("malformed ${%s:...}", var);
7239 val = NULL;
7240#endif
7241 } else { /* one of "-=+?" */
7242 /* Standard-mandated substitution ops:
7243 * ${var?word} - indicate error if unset
7244 * If var is unset, word (or a message indicating it is unset
7245 * if word is null) is written to standard error
7246 * and the shell exits with a non-zero exit status.
7247 * Otherwise, the value of var is substituted.
7248 * ${var-word} - use default value
7249 * If var is unset, word is substituted.
7250 * ${var=word} - assign and use default value
7251 * If var is unset, word is assigned to var.
7252 * In all cases, final value of var is substituted.
7253 * ${var+word} - use alternative value
7254 * If var is unset, null is substituted.
7255 * Otherwise, word is substituted.
7256 *
7257 * Word is subjected to tilde expansion, parameter expansion,
7258 * command substitution, and arithmetic expansion.
7259 * If word is not needed, it is not expanded.
7260 *
7261 * Colon forms (${var:-word}, ${var:=word} etc) do the same,
7262 * but also treat null var as if it is unset.
7263 *
7264 * Word-splitting and single quote behavior:
7265 *
7266 * $ f() { for i; do echo "|$i|"; done; }
7267 *
7268 * $ x=; f ${x:?'x y' z}; echo $?
7269 * bash: x: x y z # neither f nor "echo $?" executes
7270 * (if interactive, bash does not exit, but merely aborts to prompt. $? is set to 1)
7271 * $ x=; f "${x:?'x y' z}"
7272 * bash: x: x y z # dash prints: dash: x: 'x y' z
7273 *
7274 * $ x=; f ${x:='x y' z}
7275 * |x|
7276 * |y|
7277 * |z|
7278 * $ x=; f "${x:='x y' z}"
7279 * |'x y' z|
7280 *
7281 * $ x=x; f ${x:+'x y' z}
7282 * |x y|
7283 * |z|
7284 * $ x=x; f "${x:+'x y' z}"
7285 * |'x y' z|
7286 *
7287 * $ x=; f ${x:-'x y' z}
7288 * |x y|
7289 * |z|
7290 * $ x=; f "${x:-'x y' z}"
7291 * |'x y' z|
7292 */
7293 int use_word = (!val || ((exp_save == ':') && !val[0]));
7294 if (exp_op == '+')
7295 use_word = !use_word;
7296 debug_printf_expand("expand: op:%c (null:%s) test:%i\n", exp_op,
7297 (exp_save == ':') ? "true" : "false", use_word);
7298 if (use_word) {
7299 if (exp_op == '+' || exp_op == '-') {
7300 /* ${var+word} - use alternative value */
7301 /* ${var-word} - use default value */
7302 n = encode_then_append_var_plusminus(output, n, exp_word,
7303 /*dquoted:*/ (arg0 & 0x80)
7304 );
7305 val = NULL;
7306 } else {
7307 /* ${var?word} - indicate error if unset */
7308 /* ${var=word} - assign and use default value */
7309 to_be_freed = encode_then_expand_vararg(exp_word,
7310 /*handle_squotes:*/ !(arg0 & 0x80),
7311 /*unbackslash:*/ 0
7312 );
7313 if (to_be_freed)
7314 exp_word = to_be_freed;
7315 if (exp_op == '?') {
7316 /* mimic bash message */
7317 msg_and_die_if_script("%s: %s",
7318 var,
7319 exp_word[0]
7320 ? exp_word
7321 : "parameter null or not set"
7322 /* ash has more specific messages, a-la: */
7323 /*: (exp_save == ':' ? "parameter null or not set" : "parameter not set")*/
7324 );
7325//TODO: how interactive bash aborts expansion mid-command?
7326//It aborts the entire line, returns to prompt:
7327// $ f() { for i; do echo "|$i|"; done; }; x=; f "${x:?'x y' z}"; echo YO
7328// bash: x: x y z
7329// $
7330// ("echo YO" is not executed, neither the f function call)
7331 } else {
7332 val = exp_word;
7333 }
7334 if (exp_op == '=') {
7335 /* ${var=[word]} or ${var:=[word]} */
7336 if (isdigit(var[0]) || var[0] == '#') {
7337 /* mimic bash message */
7338 msg_and_die_if_script("$%s: cannot assign in this way", var);
7339 val = NULL;
7340 } else {
7341 char *new_var = xasprintf("%s=%s", var, val);
7342 set_local_var(new_var, /*flag:*/ 0);
7343 }
7344 }
7345 }
7346 }
7347 } /* one of "-=+?" */
7348
7349 *exp_saveptr = exp_save;
7350 } /* if (exp_op) */
7351
Francis Laniel8197f012023-12-22 22:02:28 +01007352#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01007353 arg[0] = arg0;
7354 *pp = p;
7355
7356 n = append_str_maybe_ifs_split(output, n, first_ch, val);
7357
7358 free(to_be_freed);
7359 return n;
7360}
7361
7362/* Expand all variable references in given string, adding words to list[]
7363 * at n, n+1,... positions. Return updated n (so that list[n] is next one
7364 * to be filled). This routine is extremely tricky: has to deal with
7365 * variables/parameters with whitespace, $* and $@, and constructs like
7366 * 'echo -$*-'. If you play here, you must run testsuite afterwards! */
7367static NOINLINE int expand_vars_to_list(o_string *output, int n, char *arg)
7368{
7369 /* output->o_expflags & EXP_FLAG_SINGLEWORD (0x80) if we are in
7370 * expansion of right-hand side of assignment == 1-element expand.
7371 */
7372 char cant_be_null = 0; /* only bit 0x80 matters */
7373 char *p;
7374
7375 debug_printf_expand("expand_vars_to_list: arg:'%s' singleword:%x\n", arg,
7376 !!(output->o_expflags & EXP_FLAG_SINGLEWORD));
7377 debug_print_list("expand_vars_to_list[0]", output, n);
7378
7379 while ((p = strchr(arg, SPECIAL_VAR_SYMBOL)) != NULL) {
7380 char first_ch;
7381#if ENABLE_FEATURE_SH_MATH
7382 char arith_buf[sizeof(arith_t)*3 + 2];
7383#endif
7384
7385 if (output->ended_in_ifs) {
7386 o_addchr(output, '\0');
7387 n = o_save_ptr(output, n);
7388 output->ended_in_ifs = 0;
7389 }
7390
7391 o_addblock(output, arg, p - arg);
7392 debug_print_list("expand_vars_to_list[1]", output, n);
7393 arg = ++p;
7394 p = strchr(p, SPECIAL_VAR_SYMBOL);
7395
7396 /* Fetch special var name (if it is indeed one of them)
7397 * and quote bit, force the bit on if singleword expansion -
7398 * important for not getting v=$@ expand to many words. */
7399 first_ch = arg[0] | (output->o_expflags & EXP_FLAG_SINGLEWORD);
7400
7401 /* Is this variable quoted and thus expansion can't be null?
7402 * "$@" is special. Even if quoted, it can still
7403 * expand to nothing (not even an empty string),
7404 * thus it is excluded. */
7405 if ((first_ch & 0x7f) != '@')
7406 cant_be_null |= first_ch;
7407
7408 switch (first_ch & 0x7f) {
7409 /* Highest bit in first_ch indicates that var is double-quoted */
7410 case '*':
7411 case '@': {
7412 int i;
Francis Laniel74e42542023-12-22 22:02:33 +01007413#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01007414 if (!G.global_argv[1])
Francis Laniel74e42542023-12-22 22:02:33 +01007415#else /* __U_BOOT__ */
7416 if (!G.global_argv || !G.global_argv[1])
7417#endif /* __U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01007418 break;
7419 i = 1;
7420 cant_be_null |= first_ch; /* do it for "$@" _now_, when we know it's not empty */
7421 if (!(first_ch & 0x80)) { /* unquoted $* or $@ */
7422 while (G.global_argv[i]) {
7423 n = expand_on_ifs(output, n, G.global_argv[i]);
7424 debug_printf_expand("expand_vars_to_list: argv %d (last %d)\n", i, G.global_argc - 1);
7425 if (G.global_argv[i++][0] && G.global_argv[i]) {
7426 /* this argv[] is not empty and not last:
7427 * put terminating NUL, start new word */
7428 o_addchr(output, '\0');
7429 debug_print_list("expand_vars_to_list[2]", output, n);
7430 n = o_save_ptr(output, n);
7431 debug_print_list("expand_vars_to_list[3]", output, n);
7432 }
7433 }
7434 } else
7435 /* If EXP_FLAG_SINGLEWORD, we handle assignment 'a=....$@.....'
7436 * and in this case should treat it like '$*' - see 'else...' below */
7437 if (first_ch == (char)('@'|0x80) /* quoted $@ */
7438 && !(output->o_expflags & EXP_FLAG_SINGLEWORD) /* not v="$@" case */
7439 ) {
7440 while (1) {
7441 o_addQstr(output, G.global_argv[i]);
7442 if (++i >= G.global_argc)
7443 break;
7444 o_addchr(output, '\0');
7445 debug_print_list("expand_vars_to_list[4]", output, n);
7446 n = o_save_ptr(output, n);
7447 }
7448 } else { /* quoted $* (or v="$@" case): add as one word */
7449 while (1) {
7450 o_addQstr(output, G.global_argv[i]);
7451 if (!G.global_argv[++i])
7452 break;
7453 if (G.ifs[0])
7454 o_addchr(output, G.ifs[0]);
7455 }
7456 output->has_quoted_part = 1;
7457 }
7458 break;
7459 }
7460 case SPECIAL_VAR_SYMBOL: {
7461 /* <SPECIAL_VAR_SYMBOL><SPECIAL_VAR_SYMBOL> */
7462 /* "Empty variable", used to make "" etc to not disappear */
7463 output->has_quoted_part = 1;
7464 cant_be_null = 0x80;
7465 arg++;
7466 break;
7467 }
7468 case SPECIAL_VAR_QUOTED_SVS:
7469 /* <SPECIAL_VAR_SYMBOL><SPECIAL_VAR_QUOTED_SVS><SPECIAL_VAR_SYMBOL> */
7470 /* "^C variable", represents literal ^C char (possible in scripts) */
7471 o_addchr(output, SPECIAL_VAR_SYMBOL);
7472 arg++;
7473 break;
7474#if ENABLE_HUSH_TICK
7475 case '`': {
7476 /* <SPECIAL_VAR_SYMBOL>`cmd<SPECIAL_VAR_SYMBOL> */
7477 o_string subst_result = NULL_O_STRING;
7478
7479 *p = '\0'; /* replace trailing <SPECIAL_VAR_SYMBOL> */
7480 arg++;
7481 /* Can't just stuff it into output o_string,
7482 * expanded result may need to be globbed
7483 * and $IFS-split */
7484 debug_printf_subst("SUBST '%s' first_ch %x\n", arg, first_ch);
7485 G.last_exitcode = process_command_subs(&subst_result, arg);
7486 G.expand_exitcode = G.last_exitcode;
7487 debug_printf_subst("SUBST RES:%d '%s'\n", G.last_exitcode, subst_result.data);
7488 n = append_str_maybe_ifs_split(output, n, first_ch, subst_result.data);
7489 o_free(&subst_result);
7490 break;
7491 }
7492#endif
7493#if ENABLE_FEATURE_SH_MATH
7494 case '+': {
7495 /* <SPECIAL_VAR_SYMBOL>+arith<SPECIAL_VAR_SYMBOL> */
7496 arith_t res;
7497
7498 arg++; /* skip '+' */
7499 *p = '\0'; /* replace trailing <SPECIAL_VAR_SYMBOL> */
7500 debug_printf_subst("ARITH '%s' first_ch %x\n", arg, first_ch);
7501 res = expand_and_evaluate_arith(arg, NULL);
7502 debug_printf_subst("ARITH RES '"ARITH_FMT"'\n", res);
7503 sprintf(arith_buf, ARITH_FMT, res);
7504 if (res < 0
7505 && first_ch == (char)('+'|0x80)
7506 /* && (output->o_expflags & EXP_FLAG_ESC_GLOB_CHARS) */
7507 ) {
7508 /* Quoted negative ariths, like filename[0"$((-9))"],
7509 * should not be interpreted as glob ranges.
7510 * Convert leading '-' to '\-':
7511 */
7512 o_grow_by(output, 1);
7513 output->data[output->length++] = '\\';
7514 }
7515 o_addstr(output, arith_buf);
7516 break;
7517 }
7518#endif
7519 default:
7520 /* <SPECIAL_VAR_SYMBOL>varname[ops]<SPECIAL_VAR_SYMBOL> */
7521 n = expand_one_var(output, n, first_ch, arg, &p);
7522 break;
7523 } /* switch (char after <SPECIAL_VAR_SYMBOL>) */
7524
7525 /* Restore NULL'ed SPECIAL_VAR_SYMBOL.
7526 * Do the check to avoid writing to a const string. */
7527 if (*p != SPECIAL_VAR_SYMBOL)
7528 *p = SPECIAL_VAR_SYMBOL;
7529 arg = ++p;
7530 } /* end of "while (SPECIAL_VAR_SYMBOL is found) ..." */
7531
7532 if (*arg) {
7533 /* handle trailing string */
7534 if (output->ended_in_ifs) {
7535 o_addchr(output, '\0');
7536 n = o_save_ptr(output, n);
7537 }
7538 debug_print_list("expand_vars_to_list[a]", output, n);
7539 /* this part is literal, and it was already pre-quoted
7540 * if needed (much earlier), do not use o_addQstr here!
7541 */
7542 o_addstr(output, arg);
7543 debug_print_list("expand_vars_to_list[b]", output, n);
7544 } else
7545 if (output->length == o_get_last_ptr(output, n) /* expansion is empty */
7546 && !(cant_be_null & 0x80) /* and all vars were not quoted */
7547 && !output->has_quoted_part
7548 ) {
7549 n--;
7550 /* allow to reuse list[n] later without re-growth */
7551 output->has_empty_slot = 1;
7552 }
7553
7554 return n;
7555}
7556
7557static char **expand_variables(char **argv, unsigned expflags)
7558{
7559 int n;
7560 char **list;
7561 o_string output = NULL_O_STRING;
7562
7563 output.o_expflags = expflags;
7564
7565 n = 0;
7566 for (;;) {
7567 /* go to next list[n] */
7568 output.ended_in_ifs = 0;
7569 n = o_save_ptr(&output, n);
7570
7571 if (!*argv)
7572 break;
7573
7574 /* expand argv[i] */
7575 n = expand_vars_to_list(&output, n, *argv++);
7576 /* if (!output->has_empty_slot) -- need this?? */
7577 o_addchr(&output, '\0');
7578 }
7579 debug_print_list("expand_variables", &output, n);
7580
7581 /* output.data (malloced in one block) gets returned in "list" */
7582 list = o_finalize_list(&output, n);
7583 debug_print_strings("expand_variables[1]", list);
7584 return list;
7585}
7586
7587static char **expand_strvec_to_strvec(char **argv)
7588{
7589 return expand_variables(argv, EXP_FLAG_GLOB | EXP_FLAG_ESC_GLOB_CHARS);
7590}
7591
7592#if defined(CMD_SINGLEWORD_NOGLOB) || defined(CMD_TEST2_SINGLEWORD_NOGLOB)
7593static char **expand_strvec_to_strvec_singleword_noglob(char **argv)
7594{
7595 return expand_variables(argv, EXP_FLAG_SINGLEWORD);
7596}
7597#endif
7598
7599/* Used for expansion of right hand of assignments,
7600 * $((...)), heredocs, variable expansion parts.
7601 *
7602 * NB: should NOT do globbing!
7603 * "export v=/bin/c*; env | grep ^v=" outputs "v=/bin/c*"
7604 */
7605static char *expand_string_to_string(const char *str, int EXP_flags, int do_unbackslash)
7606{
7607#if !BASH_PATTERN_SUBST && !ENABLE_HUSH_CASE
7608 const int do_unbackslash = 1;
7609 const int EXP_flags = EXP_FLAG_ESC_GLOB_CHARS;
7610#endif
7611 char *argv[2], **list;
7612
7613 debug_printf_expand("string_to_string<='%s'\n", str);
7614 /* This is generally an optimization, but it also
7615 * handles "", which otherwise trips over !list[0] check below.
7616 * (is this ever happens that we actually get str="" here?)
7617 */
7618 if (!strchr(str, SPECIAL_VAR_SYMBOL) && !strchr(str, '\\')) {
7619 //TODO: Can use on strings with \ too, just unbackslash() them?
7620 debug_printf_expand("string_to_string(fast)=>'%s'\n", str);
7621 return xstrdup(str);
7622 }
7623
7624 argv[0] = (char*)str;
7625 argv[1] = NULL;
7626 list = expand_variables(argv, EXP_flags | EXP_FLAG_SINGLEWORD);
7627 if (!list[0]) {
7628 /* Example where it happens:
7629 * x=; echo ${x:-"$@"}
7630 */
7631 ((char*)list)[0] = '\0';
7632 } else {
7633 if (HUSH_DEBUG)
7634 if (list[1])
7635 bb_simple_error_msg_and_die("BUG in varexp2");
7636 /* actually, just move string 2*sizeof(char*) bytes back */
7637 overlapping_strcpy((char*)list, list[0]);
7638 if (do_unbackslash)
7639 unbackslash((char*)list);
7640 }
7641 debug_printf_expand("string_to_string=>'%s'\n", (char*)list);
7642 return (char*)list;
7643}
7644
7645#if 0
7646static char* expand_strvec_to_string(char **argv)
7647{
7648 char **list;
7649
7650 list = expand_variables(argv, EXP_FLAG_SINGLEWORD);
7651 /* Convert all NULs to spaces */
7652 if (list[0]) {
7653 int n = 1;
7654 while (list[n]) {
7655 if (HUSH_DEBUG)
7656 if (list[n-1] + strlen(list[n-1]) + 1 != list[n])
7657 bb_error_msg_and_die("BUG in varexp3");
7658 /* bash uses ' ' regardless of $IFS contents */
7659 list[n][-1] = ' ';
7660 n++;
7661 }
7662 }
7663 overlapping_strcpy((char*)list, list[0] ? list[0] : "");
7664 debug_printf_expand("strvec_to_string='%s'\n", (char*)list);
7665 return (char*)list;
7666}
7667#endif
7668
Francis Laniel8197f012023-12-22 22:02:28 +01007669#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01007670static char **expand_assignments(char **argv, int count)
7671{
7672 int i;
7673 char **p;
7674
7675 G.expanded_assignments = p = NULL;
7676 /* Expand assignments into one string each */
7677 for (i = 0; i < count; i++) {
7678 p = add_string_to_strings(p,
7679 expand_string_to_string(argv[i],
7680 EXP_FLAG_ESC_GLOB_CHARS,
7681 /*unbackslash:*/ 1
7682 )
7683 );
7684 G.expanded_assignments = p;
7685 }
7686 G.expanded_assignments = NULL;
7687 return p;
7688}
7689
7690
7691static void switch_off_special_sigs(unsigned mask)
7692{
7693 unsigned sig = 0;
7694 while ((mask >>= 1) != 0) {
7695 sig++;
7696 if (!(mask & 1))
7697 continue;
7698#if ENABLE_HUSH_TRAP
7699 if (G_traps) {
7700 if (G_traps[sig] && !G_traps[sig][0])
7701 /* trap is '', has to remain SIG_IGN */
7702 continue;
7703 free(G_traps[sig]);
7704 G_traps[sig] = NULL;
7705 }
7706#endif
7707 /* We are here only if no trap or trap was not '' */
7708 install_sighandler(sig, SIG_DFL);
7709 }
7710}
Francis Laniel8197f012023-12-22 22:02:28 +01007711#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01007712
Francis Laniel8197f012023-12-22 22:02:28 +01007713#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01007714#if BB_MMU
7715/* never called */
7716void re_execute_shell(char ***to_free, const char *s,
7717 char *g_argv0, char **g_argv,
7718 char **builtin_argv) NORETURN;
7719
7720static void reset_traps_to_defaults(void)
7721{
7722 /* This function is always called in a child shell
7723 * after fork (not vfork, NOMMU doesn't use this function).
7724 */
7725 IF_HUSH_TRAP(unsigned sig;)
7726 unsigned mask;
7727
7728 /* Child shells are not interactive.
7729 * SIGTTIN/SIGTTOU/SIGTSTP should not have special handling.
7730 * Testcase: (while :; do :; done) + ^Z should background.
7731 * Same goes for SIGTERM, SIGHUP, SIGINT.
7732 */
7733 mask = (G.special_sig_mask & SPECIAL_INTERACTIVE_SIGS) | G_fatal_sig_mask;
7734 if (!G_traps && !mask)
7735 return; /* already no traps and no special sigs */
7736
7737 /* Switch off special sigs */
7738 switch_off_special_sigs(mask);
7739# if ENABLE_HUSH_JOB
7740 G_fatal_sig_mask = 0;
7741# endif
7742 G.special_sig_mask &= ~SPECIAL_INTERACTIVE_SIGS;
7743 /* SIGQUIT,SIGCHLD and maybe SPECIAL_JOBSTOP_SIGS
7744 * remain set in G.special_sig_mask */
7745
7746# if ENABLE_HUSH_TRAP
7747 if (!G_traps)
7748 return;
7749
7750 /* Reset all sigs to default except ones with empty traps */
7751 for (sig = 0; sig < NSIG; sig++) {
7752 if (!G_traps[sig])
7753 continue; /* no trap: nothing to do */
7754 if (!G_traps[sig][0])
7755 continue; /* empty trap: has to remain SIG_IGN */
7756 /* sig has non-empty trap, reset it: */
7757 free(G_traps[sig]);
7758 G_traps[sig] = NULL;
7759 /* There is no signal for trap 0 (EXIT) */
7760 if (sig == 0)
7761 continue;
7762 install_sighandler(sig, pick_sighandler(sig));
7763 }
7764# endif
7765}
7766
7767#else /* !BB_MMU */
7768
7769static void re_execute_shell(char ***to_free, const char *s,
7770 char *g_argv0, char **g_argv,
7771 char **builtin_argv) NORETURN;
7772static void re_execute_shell(char ***to_free, const char *s,
7773 char *g_argv0, char **g_argv,
7774 char **builtin_argv)
7775{
7776# define NOMMU_HACK_FMT ("-$%x:%x:%x:%x:%x:%llx" IF_HUSH_LOOPS(":%x"))
7777 /* delims + 2 * (number of bytes in printed hex numbers) */
7778 char param_buf[sizeof(NOMMU_HACK_FMT) + 2 * (sizeof(int)*6 + sizeof(long long)*1)];
7779 char *heredoc_argv[4];
7780 struct variable *cur;
7781# if ENABLE_HUSH_FUNCTIONS
7782 struct function *funcp;
7783# endif
7784 char **argv, **pp;
7785 unsigned cnt;
7786 unsigned long long empty_trap_mask;
7787
7788 if (!g_argv0) { /* heredoc */
7789 argv = heredoc_argv;
7790 argv[0] = (char *) G.argv0_for_re_execing;
7791 argv[1] = (char *) "-<";
7792 argv[2] = (char *) s;
7793 argv[3] = NULL;
7794 pp = &argv[3]; /* used as pointer to empty environment */
7795 goto do_exec;
7796 }
7797
7798 cnt = 0;
7799 pp = builtin_argv;
7800 if (pp) while (*pp++)
7801 cnt++;
7802
7803 empty_trap_mask = 0;
7804 if (G_traps) {
7805 int sig;
7806 for (sig = 1; sig < NSIG; sig++) {
7807 if (G_traps[sig] && !G_traps[sig][0])
7808 empty_trap_mask |= 1LL << sig;
7809 }
7810 }
7811
7812 sprintf(param_buf, NOMMU_HACK_FMT
7813 , (unsigned) G.root_pid
7814 , (unsigned) G.root_ppid
7815 , (unsigned) G.last_bg_pid
7816 , (unsigned) G.last_exitcode
7817 , cnt
7818 , empty_trap_mask
7819 IF_HUSH_LOOPS(, G.depth_of_loop)
7820 );
7821# undef NOMMU_HACK_FMT
7822 /* 1:hush 2:-$<pid>:<pid>:<exitcode>:<etc...> <vars...> <funcs...>
7823 * 3:-c 4:<cmd> 5:<arg0> <argN...> 6:NULL
7824 */
7825 cnt += 6;
7826 for (cur = G.top_var; cur; cur = cur->next) {
7827 if (!cur->flg_export || cur->flg_read_only)
7828 cnt += 2;
7829 }
7830# if ENABLE_HUSH_FUNCTIONS
7831 for (funcp = G.top_func; funcp; funcp = funcp->next)
7832 cnt += 3;
7833# endif
7834 pp = g_argv;
7835 while (*pp++)
7836 cnt++;
7837 *to_free = argv = pp = xzalloc(sizeof(argv[0]) * cnt);
7838 *pp++ = (char *) G.argv0_for_re_execing;
7839 *pp++ = param_buf;
7840 for (cur = G.top_var; cur; cur = cur->next) {
7841 if (strcmp(cur->varstr, hush_version_str) == 0)
7842 continue;
7843 if (cur->flg_read_only) {
7844 *pp++ = (char *) "-R";
7845 *pp++ = cur->varstr;
7846 } else if (!cur->flg_export) {
7847 *pp++ = (char *) "-V";
7848 *pp++ = cur->varstr;
7849 }
7850 }
7851# if ENABLE_HUSH_FUNCTIONS
7852 for (funcp = G.top_func; funcp; funcp = funcp->next) {
7853 *pp++ = (char *) "-F";
7854 *pp++ = funcp->name;
7855 *pp++ = funcp->body_as_string;
7856 }
7857# endif
7858 /* We can pass activated traps here. Say, -Tnn:trap_string
7859 *
7860 * However, POSIX says that subshells reset signals with traps
7861 * to SIG_DFL.
7862 * I tested bash-3.2 and it not only does that with true subshells
7863 * of the form ( list ), but with any forked children shells.
7864 * I set trap "echo W" WINCH; and then tried:
7865 *
7866 * { echo 1; sleep 20; echo 2; } &
7867 * while true; do echo 1; sleep 20; echo 2; break; done &
7868 * true | { echo 1; sleep 20; echo 2; } | cat
7869 *
7870 * In all these cases sending SIGWINCH to the child shell
7871 * did not run the trap. If I add trap "echo V" WINCH;
7872 * _inside_ group (just before echo 1), it works.
7873 *
7874 * I conclude it means we don't need to pass active traps here.
7875 */
7876 *pp++ = (char *) "-c";
7877 *pp++ = (char *) s;
7878 if (builtin_argv) {
7879 while (*++builtin_argv)
7880 *pp++ = *builtin_argv;
7881 *pp++ = (char *) "";
7882 }
7883 *pp++ = g_argv0;
7884 while (*g_argv)
7885 *pp++ = *g_argv++;
7886 /* *pp = NULL; - is already there */
7887 pp = environ;
7888
7889 do_exec:
7890 debug_printf_exec("re_execute_shell pid:%d cmd:'%s'\n", getpid(), s);
7891 /* Don't propagate SIG_IGN to the child */
7892 if (SPECIAL_JOBSTOP_SIGS != 0)
7893 switch_off_special_sigs(G.special_sig_mask & SPECIAL_JOBSTOP_SIGS);
7894 execve(bb_busybox_exec_path, argv, pp);
7895 /* Fallback. Useful for init=/bin/hush usage etc */
7896 if (argv[0][0] == '/')
7897 execve(argv[0], argv, pp);
7898 xfunc_error_retval = 127;
7899 bb_simple_error_msg_and_die("can't re-execute the shell");
7900}
7901#endif /* !BB_MMU */
7902
Francis Laniel8197f012023-12-22 22:02:28 +01007903#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01007904
7905static int run_and_free_list(struct pipe *pi);
7906
7907/* Executing from string: eval, sh -c '...'
7908 * or from file: /etc/profile, . file, sh <script>, sh (intereactive)
7909 * end_trigger controls how often we stop parsing
7910 * NUL: parse all, execute, return
7911 * ';': parse till ';' or newline, execute, repeat till EOF
7912 */
7913static void parse_and_run_stream(struct in_str *inp, int end_trigger)
7914{
7915 /* Why we need empty flag?
7916 * An obscure corner case "false; ``; echo $?":
7917 * empty command in `` should still set $? to 0.
7918 * But we can't just set $? to 0 at the start,
7919 * this breaks "false; echo `echo $?`" case.
7920 */
7921 bool empty = 1;
7922 while (1) {
7923 struct pipe *pipe_list;
7924
7925#if ENABLE_HUSH_INTERACTIVE
7926 if (end_trigger == ';') {
7927 G.promptmode = 0; /* PS1 */
7928 debug_printf_prompt("%s promptmode=%d\n", __func__, G.promptmode);
7929 }
7930#endif
7931 pipe_list = parse_stream(NULL, NULL, inp, end_trigger);
7932 if (!pipe_list || pipe_list == ERR_PTR) { /* EOF/error */
7933 /* If we are in "big" script
7934 * (not in `cmd` or something similar)...
7935 */
7936 if (pipe_list == ERR_PTR && end_trigger == ';') {
7937 /* Discard cached input (rest of line) */
7938 int ch = inp->last_char;
7939 while (ch != EOF && ch != '\n') {
7940 //bb_error_msg("Discarded:'%c'", ch);
7941 ch = i_getch(inp);
7942 }
7943 /* Force prompt */
7944 inp->p = NULL;
7945 /* This stream isn't empty */
7946 empty = 0;
7947 continue;
7948 }
7949 if (!pipe_list && empty)
7950 G.last_exitcode = 0;
7951 break;
7952 }
7953 debug_print_tree(pipe_list, 0);
7954 debug_printf_exec("parse_and_run_stream: run_and_free_list\n");
Francis Laniel9a068372023-12-22 22:02:32 +01007955#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01007956 run_and_free_list(pipe_list);
Francis Laniel9a068372023-12-22 22:02:32 +01007957#else /* __U_BOOT__ */
7958 int rcode = run_and_free_list(pipe_list);
7959 /*
7960 * We reset input string to not run the following command, so running
7961 * 'exit; echo foo' does not print foo.
7962 */
7963 if (rcode <= EXIT_RET_CODE)
7964 setup_file_in_str(inp);
7965#endif /* __U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01007966 empty = 0;
7967 if (G_flag_return_in_progress == 1)
7968 break;
7969 }
7970}
7971
Francis Laniel8197f012023-12-22 22:02:28 +01007972#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01007973static void parse_and_run_string(const char *s)
7974{
7975 struct in_str input;
7976 //IF_HUSH_LINENO_VAR(unsigned sv = G.parse_lineno;)
7977
7978 setup_string_in_str(&input, s);
7979 parse_and_run_stream(&input, '\0');
7980 //IF_HUSH_LINENO_VAR(G.parse_lineno = sv;)
7981}
Francis Laniel8197f012023-12-22 22:02:28 +01007982#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01007983
Francis Laniel8197f012023-12-22 22:02:28 +01007984#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01007985static void parse_and_run_file(HFILE *fp)
Francis Laniel8197f012023-12-22 22:02:28 +01007986#else /* __U_BOOT__ */
7987void parse_and_run_file(void)
7988#endif /* __U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01007989{
7990 struct in_str input;
Francis Laniel8197f012023-12-22 22:02:28 +01007991#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01007992 IF_HUSH_LINENO_VAR(unsigned sv = G.parse_lineno;)
7993
7994 IF_HUSH_LINENO_VAR(G.parse_lineno = 1;)
7995 setup_file_in_str(&input, fp);
Francis Laniel8197f012023-12-22 22:02:28 +01007996#else /* __U_BOOT__ */
7997 setup_file_in_str(&input);
7998#endif /* __U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01007999 parse_and_run_stream(&input, ';');
Francis Laniel8197f012023-12-22 22:02:28 +01008000#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01008001 IF_HUSH_LINENO_VAR(G.parse_lineno = sv;)
Francis Laniel8197f012023-12-22 22:02:28 +01008002#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01008003}
8004
Francis Laniel8197f012023-12-22 22:02:28 +01008005#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01008006#if ENABLE_HUSH_TICK
8007static int generate_stream_from_string(const char *s, pid_t *pid_p)
8008{
8009 pid_t pid;
8010 int channel[2];
8011# if !BB_MMU
8012 char **to_free = NULL;
8013# endif
8014
8015 xpipe(channel);
8016 pid = BB_MMU ? xfork() : xvfork();
8017 if (pid == 0) { /* child */
8018 disable_restore_tty_pgrp_on_exit();
8019 /* Process substitution is not considered to be usual
8020 * 'command execution'.
8021 * SUSv3 says ctrl-Z should be ignored, ctrl-C should not.
8022 */
8023 bb_signals(0
8024 + (1 << SIGTSTP)
8025 + (1 << SIGTTIN)
8026 + (1 << SIGTTOU)
8027 , SIG_IGN);
8028 close(channel[0]); /* NB: close _first_, then move fd! */
8029 xmove_fd(channel[1], 1);
8030# if ENABLE_HUSH_TRAP
8031 /* Awful hack for `trap` or $(trap).
8032 *
8033 * http://www.opengroup.org/onlinepubs/009695399/utilities/trap.html
8034 * contains an example where "trap" is executed in a subshell:
8035 *
8036 * save_traps=$(trap)
8037 * ...
8038 * eval "$save_traps"
8039 *
8040 * Standard does not say that "trap" in subshell shall print
8041 * parent shell's traps. It only says that its output
8042 * must have suitable form, but then, in the above example
8043 * (which is not supposed to be normative), it implies that.
8044 *
8045 * bash (and probably other shell) does implement it
8046 * (traps are reset to defaults, but "trap" still shows them),
8047 * but as a result, "trap" logic is hopelessly messed up:
8048 *
8049 * # trap
8050 * trap -- 'echo Ho' SIGWINCH <--- we have a handler
8051 * # (trap) <--- trap is in subshell - no output (correct, traps are reset)
8052 * # true | trap <--- trap is in subshell - no output (ditto)
8053 * # echo `true | trap` <--- in subshell - output (but traps are reset!)
8054 * trap -- 'echo Ho' SIGWINCH
8055 * # echo `(trap)` <--- in subshell in subshell - output
8056 * trap -- 'echo Ho' SIGWINCH
8057 * # echo `true | (trap)` <--- in subshell in subshell in subshell - output!
8058 * trap -- 'echo Ho' SIGWINCH
8059 *
8060 * The rules when to forget and when to not forget traps
8061 * get really complex and nonsensical.
8062 *
8063 * Our solution: ONLY bare $(trap) or `trap` is special.
8064 */
8065 s = skip_whitespace(s);
8066 if (is_prefixed_with(s, "trap")
8067 && skip_whitespace(s + 4)[0] == '\0'
8068 ) {
8069 static const char *const argv[] = { NULL, NULL };
8070 builtin_trap((char**)argv);
8071 fflush_all(); /* important */
8072 _exit(0);
8073 }
8074# endif
8075# if BB_MMU
8076 /* Prevent it from trying to handle ctrl-z etc */
8077 IF_HUSH_JOB(G.run_list_level = 1;)
8078 CLEAR_RANDOM_T(&G.random_gen); /* or else $RANDOM repeats in child */
8079 reset_traps_to_defaults();
8080 IF_HUSH_MODE_X(G.x_mode_depth++;)
8081 //bb_error_msg("%s: ++x_mode_depth=%d", __func__, G.x_mode_depth);
8082 parse_and_run_string(s);
8083 _exit(G.last_exitcode);
8084# else
8085 /* We re-execute after vfork on NOMMU. This makes this script safe:
8086 * yes "0123456789012345678901234567890" | dd bs=32 count=64k >BIG
8087 * huge=`cat BIG` # was blocking here forever
8088 * echo OK
8089 */
8090 re_execute_shell(&to_free,
8091 s,
8092 G.global_argv[0],
8093 G.global_argv + 1,
8094 NULL);
8095# endif
8096 }
8097
8098 /* parent */
8099 *pid_p = pid;
8100# if ENABLE_HUSH_FAST
8101 G.count_SIGCHLD++;
8102//bb_error_msg("[%d] fork in generate_stream_from_string:"
8103// " G.count_SIGCHLD:%d G.handled_SIGCHLD:%d",
8104// getpid(), G.count_SIGCHLD, G.handled_SIGCHLD);
8105# endif
8106 enable_restore_tty_pgrp_on_exit();
8107# if !BB_MMU
8108 free(to_free);
8109# endif
8110 close(channel[1]);
8111 return channel[0];
8112}
8113
8114/* Return code is exit status of the process that is run. */
8115static int process_command_subs(o_string *dest, const char *s)
8116{
8117 FILE *fp;
8118 pid_t pid;
8119 int status, ch, eol_cnt;
8120
8121 fp = xfdopen_for_read(generate_stream_from_string(s, &pid));
8122
8123 /* Now send results of command back into original context */
8124 eol_cnt = 0;
8125 while ((ch = getc(fp)) != EOF) {
8126 if (ch == '\0')
8127 continue;
8128 if (ch == '\n') {
8129 eol_cnt++;
8130 continue;
8131 }
8132 while (eol_cnt) {
8133 o_addchr(dest, '\n');
8134 eol_cnt--;
8135 }
8136 o_addQchr(dest, ch);
8137 }
8138
8139 debug_printf("done reading from `cmd` pipe, closing it\n");
8140 fclose(fp);
8141 /* We need to extract exitcode. Test case
8142 * "true; echo `sleep 1; false` $?"
8143 * should print 1 */
8144 safe_waitpid(pid, &status, 0);
8145 debug_printf("child exited. returning its exitcode:%d\n", WEXITSTATUS(status));
8146 return WEXITSTATUS(status);
8147}
8148#endif /* ENABLE_HUSH_TICK */
8149
8150
8151static void setup_heredoc(struct redir_struct *redir)
8152{
8153 struct fd_pair pair;
8154 pid_t pid;
8155 int len, written;
8156 /* the _body_ of heredoc (misleading field name) */
8157 const char *heredoc = redir->rd_filename;
8158 char *expanded;
8159#if !BB_MMU
8160 char **to_free;
8161#endif
8162
8163 expanded = NULL;
8164 if (!(redir->rd_dup & HEREDOC_QUOTED)) {
8165 expanded = encode_then_expand_string(heredoc);
8166 if (expanded)
8167 heredoc = expanded;
8168 }
8169 len = strlen(heredoc);
8170
8171 close(redir->rd_fd); /* often saves dup2+close in xmove_fd */
8172 xpiped_pair(pair);
8173 xmove_fd(pair.rd, redir->rd_fd);
8174
8175 /* Try writing without forking. Newer kernels have
8176 * dynamically growing pipes. Must use non-blocking write! */
8177 ndelay_on(pair.wr);
8178 while (1) {
8179 written = write(pair.wr, heredoc, len);
8180 if (written <= 0)
8181 break;
8182 len -= written;
8183 if (len == 0) {
8184 close(pair.wr);
8185 free(expanded);
8186 return;
8187 }
8188 heredoc += written;
8189 }
8190 ndelay_off(pair.wr);
8191
8192 /* Okay, pipe buffer was not big enough */
8193 /* Note: we must not create a stray child (bastard? :)
8194 * for the unsuspecting parent process. Child creates a grandchild
8195 * and exits before parent execs the process which consumes heredoc
8196 * (that exec happens after we return from this function) */
8197#if !BB_MMU
8198 to_free = NULL;
8199#endif
8200 pid = xvfork();
8201 if (pid == 0) {
8202 /* child */
8203 disable_restore_tty_pgrp_on_exit();
8204 pid = BB_MMU ? xfork() : xvfork();
8205 if (pid != 0)
8206 _exit(0);
8207 /* grandchild */
8208 close(redir->rd_fd); /* read side of the pipe */
8209#if BB_MMU
8210 full_write(pair.wr, heredoc, len); /* may loop or block */
8211 _exit(0);
8212#else
8213 /* Delegate blocking writes to another process */
8214 xmove_fd(pair.wr, STDOUT_FILENO);
8215 re_execute_shell(&to_free, heredoc, NULL, NULL, NULL);
8216#endif
8217 }
8218 /* parent */
8219#if ENABLE_HUSH_FAST
8220 G.count_SIGCHLD++;
8221//bb_error_msg("[%d] fork in setup_heredoc: G.count_SIGCHLD:%d G.handled_SIGCHLD:%d", getpid(), G.count_SIGCHLD, G.handled_SIGCHLD);
8222#endif
8223 enable_restore_tty_pgrp_on_exit();
8224#if !BB_MMU
8225 free(to_free);
8226#endif
8227 close(pair.wr);
8228 free(expanded);
8229 wait(NULL); /* wait till child has died */
8230}
8231
8232struct squirrel {
8233 int orig_fd;
8234 int moved_to;
8235 /* moved_to = n: fd was moved to n; restore back to orig_fd after redir */
8236 /* moved_to = -1: fd was opened by redirect; close orig_fd after redir */
8237};
8238
8239static struct squirrel *append_squirrel(struct squirrel *sq, int i, int orig, int moved)
8240{
8241 sq = xrealloc(sq, (i + 2) * sizeof(sq[0]));
8242 sq[i].orig_fd = orig;
8243 sq[i].moved_to = moved;
8244 sq[i+1].orig_fd = -1; /* end marker */
8245 return sq;
8246}
8247
8248static struct squirrel *add_squirrel(struct squirrel *sq, int fd, int avoid_fd)
8249{
8250 int moved_to;
8251 int i;
8252
8253 i = 0;
8254 if (sq) for (; sq[i].orig_fd >= 0; i++) {
8255 /* If we collide with an already moved fd... */
8256 if (fd == sq[i].moved_to) {
8257 sq[i].moved_to = dup_CLOEXEC(sq[i].moved_to, avoid_fd);
8258 debug_printf_redir("redirect_fd %d: already busy, moving to %d\n", fd, sq[i].moved_to);
8259 if (sq[i].moved_to < 0) /* what? */
8260 xfunc_die();
8261 return sq;
8262 }
8263 if (fd == sq[i].orig_fd) {
8264 /* Example: echo Hello >/dev/null 1>&2 */
8265 debug_printf_redir("redirect_fd %d: already moved\n", fd);
8266 return sq;
8267 }
8268 }
8269
8270 /* If this fd is open, we move and remember it; if it's closed, moved_to = -1 */
8271 moved_to = dup_CLOEXEC(fd, avoid_fd);
8272 debug_printf_redir("redirect_fd %d: previous fd is moved to %d (-1 if it was closed)\n", fd, moved_to);
8273 if (moved_to < 0 && errno != EBADF)
8274 xfunc_die();
8275 return append_squirrel(sq, i, fd, moved_to);
8276}
8277
8278static struct squirrel *add_squirrel_closed(struct squirrel *sq, int fd)
8279{
8280 int i;
8281
8282 i = 0;
8283 if (sq) for (; sq[i].orig_fd >= 0; i++) {
8284 /* If we collide with an already moved fd... */
8285 if (fd == sq[i].orig_fd) {
8286 /* Examples:
8287 * "echo 3>FILE 3>&- 3>FILE"
8288 * "echo 3>&- 3>FILE"
8289 * No need for last redirect to insert
8290 * another "need to close 3" indicator.
8291 */
8292 debug_printf_redir("redirect_fd %d: already moved or closed\n", fd);
8293 return sq;
8294 }
8295 }
8296
8297 debug_printf_redir("redirect_fd %d: previous fd was closed\n", fd);
8298 return append_squirrel(sq, i, fd, -1);
8299}
8300
8301/* fd: redirect wants this fd to be used (e.g. 3>file).
8302 * Move all conflicting internally used fds,
8303 * and remember them so that we can restore them later.
8304 */
8305static int save_fd_on_redirect(int fd, int avoid_fd, struct squirrel **sqp)
8306{
8307 if (avoid_fd < 9) /* the important case here is that it can be -1 */
8308 avoid_fd = 9;
8309
8310#if ENABLE_HUSH_INTERACTIVE
8311 if (fd != 0 /* don't trigger for G_interactive_fd == 0 (that's "not interactive" flag) */
8312 && fd == G_interactive_fd
8313 ) {
8314 /* Testcase: "ls -l /proc/$$/fd 255>&-" should work */
8315 G_interactive_fd = xdup_CLOEXEC_and_close(G_interactive_fd, avoid_fd);
8316 debug_printf_redir("redirect_fd %d: matches interactive_fd, moving it to %d\n", fd, G_interactive_fd);
8317 return 1; /* "we closed fd" */
8318 }
8319#endif
8320 /* Are we called from setup_redirects(squirrel==NULL)
8321 * in redirect in a [v]forked child?
8322 */
8323 if (sqp == NULL) {
8324 /* No need to move script fds.
8325 * For NOMMU case, it's actively wrong: we'd change ->fd
8326 * fields in memory for the parent, but parent's fds
8327 * aren't moved, it would use wrong fd!
8328 * Reproducer: "cmd 3>FILE" in script.
8329 * If we would call move_HFILEs_on_redirect(), child would:
8330 * fcntl64(3, F_DUPFD_CLOEXEC, 10) = 10
8331 * close(3) = 0
8332 * and change ->fd to 10 if fd#3 is a script fd. WRONG.
8333 */
8334 //bb_error_msg("sqp == NULL: [v]forked child");
8335 return 0;
8336 }
8337
8338 /* If this one of script's fds? */
8339 if (move_HFILEs_on_redirect(fd, avoid_fd))
8340 return 1; /* yes. "we closed fd" (actually moved it) */
8341
8342 /* Are we called for "exec 3>FILE"? Came through
8343 * redirect_and_varexp_helper(squirrel=ERR_PTR) -> setup_redirects(ERR_PTR)
8344 * This case used to fail for this script:
8345 * exec 3>FILE
8346 * echo Ok
8347 * ...100000 more lines...
8348 * echo Ok
8349 * as follows:
8350 * read(3, "exec 3>FILE\necho Ok\necho Ok"..., 1024) = 1024
8351 * open("FILE", O_WRONLY|O_CREAT|O_TRUNC|O_LARGEFILE, 0666) = 4
8352 * dup2(4, 3) = 3
8353 * ^^^^^^^^ oops, we lost fd#3 opened to our script!
8354 * close(4) = 0
8355 * write(1, "Ok\n", 3) = 3
8356 * ... = 3
8357 * write(1, "Ok\n", 3) = 3
8358 * read(3, 0x94fbc08, 1024) = -1 EBADF (Bad file descriptor)
8359 * ^^^^^^^^ oops, wrong fd!!!
8360 * With this case separate from sqp == NULL and *after* move_HFILEs,
8361 * it now works:
8362 */
8363 if (sqp == ERR_PTR) {
8364 /* Don't preserve redirected fds: exec is _meant_ to change these */
8365 //bb_error_msg("sqp == ERR_PTR: exec >FILE");
8366 return 0;
8367 }
8368
8369 /* Check whether it collides with any open fds (e.g. stdio), save fds as needed */
8370 *sqp = add_squirrel(*sqp, fd, avoid_fd);
8371 return 0; /* "we did not close fd" */
8372}
8373
8374static void restore_redirects(struct squirrel *sq)
8375{
8376 if (sq) {
8377 int i;
8378 for (i = 0; sq[i].orig_fd >= 0; i++) {
8379 if (sq[i].moved_to >= 0) {
8380 /* We simply die on error */
8381 debug_printf_redir("restoring redirected fd from %d to %d\n", sq[i].moved_to, sq[i].orig_fd);
8382 xmove_fd(sq[i].moved_to, sq[i].orig_fd);
8383 } else {
8384 /* cmd1 9>FILE; cmd2_should_see_fd9_closed */
8385 debug_printf_redir("restoring redirected fd %d: closing it\n", sq[i].orig_fd);
8386 close(sq[i].orig_fd);
8387 }
8388 }
8389 free(sq);
8390 }
8391 if (G.HFILE_stdin
8392 && G.HFILE_stdin->fd > STDIN_FILENO
8393 /* we compare > STDIN, not == STDIN, since hfgetc()
8394 * closes fd and sets ->fd to -1 if EOF is reached.
8395 * Testcase: echo 'pwd' | hush
8396 */
8397 ) {
8398 /* Testcase: interactive "read r <FILE; echo $r; read r; echo $r".
8399 * Redirect moves ->fd to e.g. 10,
8400 * and it is not restored above (we do not restore script fds
8401 * after redirects, we just use new, "moved" fds).
8402 * However for stdin, get_user_input() -> read_line_input(),
8403 * and read builtin, depend on fd == STDIN_FILENO.
8404 */
8405 debug_printf_redir("restoring %d to stdin\n", G.HFILE_stdin->fd);
8406 xmove_fd(G.HFILE_stdin->fd, STDIN_FILENO);
8407 G.HFILE_stdin->fd = STDIN_FILENO;
8408 }
8409
8410 /* If moved, G_interactive_fd stays on new fd, not restoring it */
8411}
8412
8413#if ENABLE_FEATURE_SH_STANDALONE && BB_MMU
8414static void close_saved_fds_and_FILE_fds(void)
8415{
8416 if (G_interactive_fd)
8417 close(G_interactive_fd);
8418 close_all_HFILE_list();
8419}
8420#endif
8421
8422static int internally_opened_fd(int fd, struct squirrel *sq)
8423{
8424 int i;
8425
8426#if ENABLE_HUSH_INTERACTIVE
8427 if (fd == G_interactive_fd)
8428 return 1;
8429#endif
8430 /* If this one of script's fds? */
8431 if (fd_in_HFILEs(fd))
8432 return 1;
8433
8434 if (sq) for (i = 0; sq[i].orig_fd >= 0; i++) {
8435 if (fd == sq[i].moved_to)
8436 return 1;
8437 }
8438 return 0;
8439}
8440
8441/* squirrel != NULL means we squirrel away copies of stdin, stdout,
8442 * and stderr if they are redirected. */
8443static int setup_redirects(struct command *prog, struct squirrel **sqp)
8444{
8445 struct redir_struct *redir;
8446
8447 for (redir = prog->redirects; redir; redir = redir->next) {
8448 int newfd;
8449 int closed;
8450
8451 if (redir->rd_type == REDIRECT_HEREDOC2) {
8452 /* "rd_fd<<HERE" case */
8453 save_fd_on_redirect(redir->rd_fd, /*avoid:*/ 0, sqp);
8454 /* for REDIRECT_HEREDOC2, rd_filename holds _contents_
8455 * of the heredoc */
8456 debug_printf_redir("set heredoc '%s'\n",
8457 redir->rd_filename);
8458 setup_heredoc(redir);
8459 continue;
8460 }
8461
8462 if (redir->rd_dup == REDIRFD_TO_FILE) {
8463 /* "rd_fd<*>file" case (<*> is <,>,>>,<>) */
8464 char *p;
8465 int mode;
8466
8467 if (redir->rd_filename == NULL) {
8468 /* Examples:
8469 * "cmd >" (no filename)
8470 * "cmd > <file" (2nd redirect starts too early)
8471 */
8472 syntax_error("invalid redirect");
8473 continue;
8474 }
8475 mode = redir_table[redir->rd_type].mode;
8476 p = expand_string_to_string(redir->rd_filename,
8477 EXP_FLAG_ESC_GLOB_CHARS, /*unbackslash:*/ 1);
8478 newfd = open_or_warn(p, mode);
8479 free(p);
8480 if (newfd < 0) {
8481 /* Error message from open_or_warn can be lost
8482 * if stderr has been redirected, but bash
8483 * and ash both lose it as well
8484 * (though zsh doesn't!)
8485 */
8486 return 1;
8487 }
8488 if (newfd == redir->rd_fd && sqp) {
8489 /* open() gave us precisely the fd we wanted.
8490 * This means that this fd was not busy
8491 * (not opened to anywhere).
8492 * Remember to close it on restore:
8493 */
8494 *sqp = add_squirrel_closed(*sqp, newfd);
8495 debug_printf_redir("redir to previously closed fd %d\n", newfd);
8496 }
8497 } else {
8498 /* "rd_fd>&rd_dup" or "rd_fd>&-" case */
8499 newfd = redir->rd_dup;
8500 }
8501
8502 if (newfd == redir->rd_fd)
8503 continue;
8504
8505 /* if "N>FILE": move newfd to redir->rd_fd */
8506 /* if "N>&M": dup newfd to redir->rd_fd */
8507 /* if "N>&-": close redir->rd_fd (newfd is REDIRFD_CLOSE) */
8508
8509 closed = save_fd_on_redirect(redir->rd_fd, /*avoid:*/ newfd, sqp);
8510 if (newfd == REDIRFD_CLOSE) {
8511 /* "N>&-" means "close me" */
8512 if (!closed) {
8513 /* ^^^ optimization: saving may already
8514 * have closed it. If not... */
8515 close(redir->rd_fd);
8516 }
8517 /* Sometimes we do another close on restore, getting EBADF.
8518 * Consider "echo 3>FILE 3>&-"
8519 * first redirect remembers "need to close 3",
8520 * and second redirect closes 3! Restore code then closes 3 again.
8521 */
8522 } else {
8523 /* if newfd is a script fd or saved fd, simulate EBADF */
8524 if (internally_opened_fd(newfd, sqp && sqp != ERR_PTR ? *sqp : NULL)) {
8525 //errno = EBADF;
8526 //bb_perror_msg_and_die("can't duplicate file descriptor");
8527 newfd = -1; /* same effect as code above */
8528 }
8529 xdup2(newfd, redir->rd_fd);
8530 if (redir->rd_dup == REDIRFD_TO_FILE)
8531 /* "rd_fd > FILE" */
8532 close(newfd);
8533 /* else: "rd_fd > rd_dup" */
8534 }
8535 }
8536 return 0;
8537}
8538
8539static char *find_in_path(const char *arg)
8540{
8541 char *ret = NULL;
8542 const char *PATH = get_local_var_value("PATH");
8543
8544 if (!PATH)
8545 return NULL;
8546
8547 while (1) {
8548 const char *end = strchrnul(PATH, ':');
8549 int sz = end - PATH; /* must be int! */
8550
8551 free(ret);
8552 if (sz != 0) {
8553 ret = xasprintf("%.*s/%s", sz, PATH, arg);
8554 } else {
8555 /* We have xxx::yyyy in $PATH,
8556 * it means "use current dir" */
8557 ret = xstrdup(arg);
8558 }
8559 if (access(ret, F_OK) == 0)
8560 break;
8561
8562 if (*end == '\0') {
8563 free(ret);
8564 return NULL;
8565 }
8566 PATH = end + 1;
8567 }
8568
8569 return ret;
8570}
8571
8572static const struct built_in_command *find_builtin_helper(const char *name,
8573 const struct built_in_command *x,
8574 const struct built_in_command *end)
8575{
8576 while (x != end) {
8577 if (strcmp(name, x->b_cmd) != 0) {
8578 x++;
8579 continue;
8580 }
8581 debug_printf_exec("found builtin '%s'\n", name);
8582 return x;
8583 }
8584 return NULL;
8585}
8586static const struct built_in_command *find_builtin1(const char *name)
8587{
8588 return find_builtin_helper(name, bltins1, &bltins1[ARRAY_SIZE(bltins1)]);
8589}
8590static const struct built_in_command *find_builtin(const char *name)
8591{
8592 const struct built_in_command *x = find_builtin1(name);
8593 if (x)
8594 return x;
8595 return find_builtin_helper(name, bltins2, &bltins2[ARRAY_SIZE(bltins2)]);
8596}
8597
8598#if ENABLE_HUSH_JOB && EDITING_HAS_get_exe_name
8599static const char * FAST_FUNC get_builtin_name(int i)
8600{
8601 if (/*i >= 0 && */ i < ARRAY_SIZE(bltins1)) {
8602 return bltins1[i].b_cmd;
8603 }
8604 i -= ARRAY_SIZE(bltins1);
8605 if (i < ARRAY_SIZE(bltins2)) {
8606 return bltins2[i].b_cmd;
8607 }
8608 return NULL;
8609}
8610#endif
Francis Laniel8197f012023-12-22 22:02:28 +01008611#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01008612
Francis Laniel8197f012023-12-22 22:02:28 +01008613#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01008614static void remove_nested_vars(void)
8615{
8616 struct variable *cur;
8617 struct variable **cur_pp;
8618
8619 cur_pp = &G.top_var;
8620 while ((cur = *cur_pp) != NULL) {
8621 if (cur->var_nest_level <= G.var_nest_level) {
8622 cur_pp = &cur->next;
8623 continue;
8624 }
8625 /* Unexport */
8626 if (cur->flg_export) {
8627 debug_printf_env("unexporting nested '%s'/%u\n", cur->varstr, cur->var_nest_level);
8628 bb_unsetenv(cur->varstr);
8629 }
8630 /* Remove from global list */
8631 *cur_pp = cur->next;
8632 /* Free */
8633 if (!cur->max_len) {
8634 debug_printf_env("freeing nested '%s'/%u\n", cur->varstr, cur->var_nest_level);
8635 free(cur->varstr);
8636 }
8637 free(cur);
8638 }
8639}
8640
8641static void enter_var_nest_level(void)
8642{
8643 G.var_nest_level++;
8644 debug_printf_env("var_nest_level++ %u\n", G.var_nest_level);
8645
8646 /* Try: f() { echo -n .; f; }; f
8647 * struct variable::var_nest_level is uint16_t,
8648 * thus limiting recursion to < 2^16.
8649 * In any case, with 8 Mbyte stack SEGV happens
8650 * not too long after 2^16 recursions anyway.
8651 */
8652 if (G.var_nest_level > 0xff00)
8653 bb_error_msg_and_die("fatal recursion (depth %u)", G.var_nest_level);
8654}
8655
8656static void leave_var_nest_level(void)
8657{
8658 G.var_nest_level--;
8659 debug_printf_env("var_nest_level-- %u\n", G.var_nest_level);
8660 if (HUSH_DEBUG && (int)G.var_nest_level < 0)
8661 bb_simple_error_msg_and_die("BUG: nesting underflow");
8662
8663 remove_nested_vars();
8664}
Francis Laniel8197f012023-12-22 22:02:28 +01008665#endif /* __U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01008666
8667#if ENABLE_HUSH_FUNCTIONS
8668static struct function **find_function_slot(const char *name)
8669{
8670 struct function *funcp;
8671 struct function **funcpp = &G.top_func;
8672
8673 while ((funcp = *funcpp) != NULL) {
8674 if (strcmp(name, funcp->name) == 0) {
8675 debug_printf_exec("found function '%s'\n", name);
8676 break;
8677 }
8678 funcpp = &funcp->next;
8679 }
8680 return funcpp;
8681}
8682
8683static ALWAYS_INLINE const struct function *find_function(const char *name)
8684{
8685 const struct function *funcp = *find_function_slot(name);
8686 return funcp;
8687}
8688
8689/* Note: takes ownership on name ptr */
8690static struct function *new_function(char *name)
8691{
8692 struct function **funcpp = find_function_slot(name);
8693 struct function *funcp = *funcpp;
8694
8695 if (funcp != NULL) {
8696 struct command *cmd = funcp->parent_cmd;
8697 debug_printf_exec("func %p parent_cmd %p\n", funcp, cmd);
8698 if (!cmd) {
8699 debug_printf_exec("freeing & replacing function '%s'\n", funcp->name);
8700 free(funcp->name);
8701 /* Note: if !funcp->body, do not free body_as_string!
8702 * This is a special case of "-F name body" function:
8703 * body_as_string was not malloced! */
8704 if (funcp->body) {
8705 free_pipe_list(funcp->body);
8706# if !BB_MMU
8707 free(funcp->body_as_string);
8708# endif
8709 }
8710 } else {
8711 debug_printf_exec("reinserting in tree & replacing function '%s'\n", funcp->name);
8712 cmd->argv[0] = funcp->name;
8713 cmd->group = funcp->body;
8714# if !BB_MMU
8715 cmd->group_as_string = funcp->body_as_string;
8716# endif
8717 }
8718 } else {
8719 debug_printf_exec("remembering new function '%s'\n", name);
8720 funcp = *funcpp = xzalloc(sizeof(*funcp));
8721 /*funcp->next = NULL;*/
8722 }
8723
8724 funcp->name = name;
8725 return funcp;
8726}
8727
8728# if ENABLE_HUSH_UNSET
8729static void unset_func(const char *name)
8730{
8731 struct function **funcpp = find_function_slot(name);
8732 struct function *funcp = *funcpp;
8733
8734 if (funcp != NULL) {
8735 debug_printf_exec("freeing function '%s'\n", funcp->name);
8736 *funcpp = funcp->next;
8737 /* funcp is unlinked now, deleting it.
8738 * Note: if !funcp->body, the function was created by
8739 * "-F name body", do not free ->body_as_string
8740 * and ->name as they were not malloced. */
8741 if (funcp->body) {
8742 free_pipe_list(funcp->body);
8743 free(funcp->name);
8744# if !BB_MMU
8745 free(funcp->body_as_string);
8746# endif
8747 }
8748 free(funcp);
8749 }
8750}
8751# endif
8752
8753# if BB_MMU
8754#define exec_function(to_free, funcp, argv) \
8755 exec_function(funcp, argv)
8756# endif
8757static void exec_function(char ***to_free,
8758 const struct function *funcp,
8759 char **argv) NORETURN;
8760static void exec_function(char ***to_free,
8761 const struct function *funcp,
8762 char **argv)
8763{
8764# if BB_MMU
8765 int n;
8766
8767 argv[0] = G.global_argv[0];
8768 G.global_argv = argv;
8769 G.global_argc = n = 1 + string_array_len(argv + 1);
8770
8771// Example when we are here: "cmd | func"
8772// func will run with saved-redirect fds open.
8773// $ f() { echo /proc/self/fd/*; }
8774// $ true | f
8775// /proc/self/fd/0 /proc/self/fd/1 /proc/self/fd/2 /proc/self/fd/255 /proc/self/fd/3
8776// stdio^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ G_interactive_fd^ DIR fd for glob
8777// Same in script:
8778// $ . ./SCRIPT
8779// /proc/self/fd/0 /proc/self/fd/1 /proc/self/fd/2 /proc/self/fd/255 /proc/self/fd/3 /proc/self/fd/4
8780// stdio^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ G_interactive_fd^ opened ./SCRIPT DIR fd for glob
8781// They are CLOEXEC so external programs won't see them, but
8782// for "more correctness" we might want to close those extra fds here:
8783//? close_saved_fds_and_FILE_fds();
8784
8785 /* "we are in a function, ok to use return" */
8786 G_flag_return_in_progress = -1;
8787 enter_var_nest_level();
8788 IF_HUSH_LOCAL(G.func_nest_level++;)
8789
8790 /* On MMU, funcp->body is always non-NULL */
8791 n = run_list(funcp->body);
8792 _exit(n);
8793# else
8794//? close_saved_fds_and_FILE_fds();
8795
8796//TODO: check whether "true | func_with_return" works
8797
8798 re_execute_shell(to_free,
8799 funcp->body_as_string,
8800 G.global_argv[0],
8801 argv + 1,
8802 NULL);
8803# endif
8804}
8805
8806static int run_function(const struct function *funcp, char **argv)
8807{
8808 int rc;
8809 save_arg_t sv;
8810 smallint sv_flg;
8811
8812 save_and_replace_G_args(&sv, argv);
8813
8814 /* "We are in function, ok to use return" */
8815 sv_flg = G_flag_return_in_progress;
8816 G_flag_return_in_progress = -1;
8817
8818 /* Make "local" variables properly shadow previous ones */
8819 IF_HUSH_LOCAL(enter_var_nest_level();)
8820 IF_HUSH_LOCAL(G.func_nest_level++;)
8821
8822 /* On MMU, funcp->body is always non-NULL */
8823# if !BB_MMU
8824 if (!funcp->body) {
8825 /* Function defined by -F */
8826 parse_and_run_string(funcp->body_as_string);
8827 rc = G.last_exitcode;
8828 } else
8829# endif
8830 {
8831 rc = run_list(funcp->body);
8832 }
8833
8834 IF_HUSH_LOCAL(G.func_nest_level--;)
8835 IF_HUSH_LOCAL(leave_var_nest_level();)
8836
8837 G_flag_return_in_progress = sv_flg;
8838# if ENABLE_HUSH_TRAP
8839 debug_printf_exec("G.return_exitcode=-1\n");
8840 G.return_exitcode = -1; /* invalidate stashed return value */
8841# endif
8842
8843 restore_G_args(&sv, argv);
8844
8845 return rc;
8846}
8847#endif /* ENABLE_HUSH_FUNCTIONS */
8848
8849
Francis Laniel8197f012023-12-22 22:02:28 +01008850#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01008851#if BB_MMU
8852#define exec_builtin(to_free, x, argv) \
8853 exec_builtin(x, argv)
8854#else
8855#define exec_builtin(to_free, x, argv) \
8856 exec_builtin(to_free, argv)
8857#endif
8858static void exec_builtin(char ***to_free,
8859 const struct built_in_command *x,
8860 char **argv) NORETURN;
8861static void exec_builtin(char ***to_free,
8862 const struct built_in_command *x,
8863 char **argv)
8864{
8865#if BB_MMU
8866 int rcode;
8867//? close_saved_fds_and_FILE_fds();
8868 rcode = x->b_function(argv);
8869 fflush_all();
8870 _exit(rcode);
8871#else
8872 fflush_all();
8873 /* On NOMMU, we must never block!
8874 * Example: { sleep 99 | read line; } & echo Ok
8875 */
8876 re_execute_shell(to_free,
8877 argv[0],
8878 G.global_argv[0],
8879 G.global_argv + 1,
8880 argv);
8881#endif
8882}
Francis Laniel8197f012023-12-22 22:02:28 +01008883#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01008884
8885
Francis Laniel8197f012023-12-22 22:02:28 +01008886#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01008887static void execvp_or_die(char **argv) NORETURN;
8888static void execvp_or_die(char **argv)
8889{
8890 int e;
8891 debug_printf_exec("execing '%s'\n", argv[0]);
8892 /* Don't propagate SIG_IGN to the child */
8893 if (SPECIAL_JOBSTOP_SIGS != 0)
8894 switch_off_special_sigs(G.special_sig_mask & SPECIAL_JOBSTOP_SIGS);
8895 execvp(argv[0], argv);
8896 e = 2;
8897 if (errno == EACCES) e = 126;
8898 if (errno == ENOENT) e = 127;
8899 bb_perror_msg("can't execute '%s'", argv[0]);
8900 _exit(e);
8901}
8902
8903#if ENABLE_HUSH_MODE_X
8904static void x_mode_print_optionally_squoted(const char *str)
8905{
8906 unsigned len;
8907 const char *cp;
8908
8909 cp = str;
8910
8911 /* the set of chars which-cause-string-to-be-squoted mimics bash */
8912 /* test a char with: bash -c 'set -x; echo "CH"' */
8913 if (str[strcspn(str, "\\\"'`$(){}[]<>;#&|~*?!^"
8914 " " "\001\002\003\004\005\006\007"
8915 "\010\011\012\013\014\015\016\017"
8916 "\020\021\022\023\024\025\026\027"
8917 "\030\031\032\033\034\035\036\037"
8918 )
8919 ] == '\0'
8920 ) {
8921 /* string has no special chars */
8922 x_mode_addstr(str);
8923 return;
8924 }
8925
8926 cp = str;
8927 for (;;) {
8928 /* print '....' up to EOL or first squote */
8929 len = (int)(strchrnul(cp, '\'') - cp);
8930 if (len != 0) {
8931 x_mode_addchr('\'');
8932 x_mode_addblock(cp, len);
8933 x_mode_addchr('\'');
8934 cp += len;
8935 }
8936 if (*cp == '\0')
8937 break;
8938 /* string contains squote(s), print them as \' */
8939 x_mode_addchr('\\');
8940 x_mode_addchr('\'');
8941 cp++;
8942 }
8943}
8944static void dump_cmd_in_x_mode(char **argv)
8945{
8946 if (G_x_mode && argv) {
8947 unsigned n;
8948
8949 /* "+[+++...][ cmd...]\n\0" */
8950 x_mode_prefix();
8951 n = 0;
8952 while (argv[n]) {
8953 x_mode_addchr(' ');
8954 if (argv[n][0] == '\0') {
8955 x_mode_addchr('\'');
8956 x_mode_addchr('\'');
8957 } else {
8958 x_mode_print_optionally_squoted(argv[n]);
8959 }
8960 n++;
8961 }
8962 x_mode_flush();
8963 }
8964}
8965#else
8966# define dump_cmd_in_x_mode(argv) ((void)0)
8967#endif
Francis Laniel8197f012023-12-22 22:02:28 +01008968#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01008969
Francis Laniel8197f012023-12-22 22:02:28 +01008970#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01008971#if ENABLE_HUSH_COMMAND
8972static void if_command_vV_print_and_exit(char opt_vV, char *cmd, const char *explanation)
8973{
8974 char *to_free;
8975
8976 if (!opt_vV)
8977 return;
8978
8979 to_free = NULL;
8980 if (!explanation) {
8981 char *path = getenv("PATH");
8982 explanation = to_free = find_executable(cmd, &path); /* path == NULL is ok */
8983 if (!explanation)
8984 _exit(1); /* PROG was not found */
8985 if (opt_vV != 'V')
8986 cmd = to_free; /* -v PROG prints "/path/to/PROG" */
8987 }
8988 printf((opt_vV == 'V') ? "%s is %s\n" : "%s\n", cmd, explanation);
8989 free(to_free);
8990 fflush_all();
8991 _exit(0);
8992}
8993#else
8994# define if_command_vV_print_and_exit(a,b,c) ((void)0)
8995#endif
Francis Laniel8197f012023-12-22 22:02:28 +01008996#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01008997
8998#if BB_MMU
8999#define pseudo_exec_argv(nommu_save, argv, assignment_cnt, argv_expanded) \
9000 pseudo_exec_argv(argv, assignment_cnt, argv_expanded)
9001#define pseudo_exec(nommu_save, command, argv_expanded) \
9002 pseudo_exec(command, argv_expanded)
9003#endif
9004
Francis Laniel8197f012023-12-22 22:02:28 +01009005#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01009006/* Called after [v]fork() in run_pipe, or from builtin_exec.
9007 * Never returns.
9008 * Don't exit() here. If you don't exec, use _exit instead.
9009 * The at_exit handlers apparently confuse the calling process,
9010 * in particular stdin handling. Not sure why? -- because of vfork! (vda)
9011 */
9012static void pseudo_exec_argv(nommu_save_t *nommu_save,
9013 char **argv, int assignment_cnt,
9014 char **argv_expanded) NORETURN;
9015static NOINLINE void pseudo_exec_argv(nommu_save_t *nommu_save,
9016 char **argv, int assignment_cnt,
9017 char **argv_expanded)
9018{
9019 const struct built_in_command *x;
9020 struct variable **sv_shadowed;
9021 char **new_env;
9022 IF_HUSH_COMMAND(char opt_vV = 0;)
9023 IF_HUSH_FUNCTIONS(const struct function *funcp;)
9024
9025 new_env = expand_assignments(argv, assignment_cnt);
9026 dump_cmd_in_x_mode(new_env);
9027
9028 if (!argv[assignment_cnt]) {
9029 /* Case when we are here: ... | var=val | ...
9030 * (note that we do not exit early, i.e., do not optimize out
9031 * expand_assignments(): think about ... | var=`sleep 1` | ...
9032 */
9033 free_strings(new_env);
9034 _exit(EXIT_SUCCESS);
9035 }
9036
9037 sv_shadowed = G.shadowed_vars_pp;
9038#if BB_MMU
9039 G.shadowed_vars_pp = NULL; /* "don't save, free them instead" */
9040#else
9041 G.shadowed_vars_pp = &nommu_save->old_vars;
9042 G.var_nest_level++;
9043#endif
9044 set_vars_and_save_old(new_env);
9045 G.shadowed_vars_pp = sv_shadowed;
9046
9047 if (argv_expanded) {
9048 argv = argv_expanded;
9049 } else {
9050 argv = expand_strvec_to_strvec(argv + assignment_cnt);
9051#if !BB_MMU
9052 nommu_save->argv = argv;
9053#endif
9054 }
9055 dump_cmd_in_x_mode(argv);
9056
9057#if ENABLE_FEATURE_SH_STANDALONE || BB_MMU
9058 if (strchr(argv[0], '/') != NULL)
9059 goto skip;
9060#endif
9061
9062#if ENABLE_HUSH_FUNCTIONS
9063 /* Check if the command matches any functions (this goes before bltins) */
9064 funcp = find_function(argv[0]);
9065 if (funcp)
9066 exec_function(&nommu_save->argv_from_re_execing, funcp, argv);
9067#endif
9068
9069#if ENABLE_HUSH_COMMAND
9070 /* "command BAR": run BAR without looking it up among functions
9071 * "command -v BAR": print "BAR" or "/path/to/BAR"; or exit 1
9072 * "command -V BAR": print "BAR is {a function,a shell builtin,/path/to/BAR}"
9073 */
9074 while (strcmp(argv[0], "command") == 0 && argv[1]) {
9075 char *p;
9076
9077 argv++;
9078 p = *argv;
9079 if (p[0] != '-' || !p[1])
9080 continue; /* bash allows "command command command [-OPT] BAR" */
9081
9082 for (;;) {
9083 p++;
9084 switch (*p) {
9085 case '\0':
9086 argv++;
9087 p = *argv;
9088 if (p[0] != '-' || !p[1])
9089 goto after_opts;
9090 continue; /* next arg is also -opts, process it too */
9091 case 'v':
9092 case 'V':
9093 opt_vV = *p;
9094 continue;
9095 default:
9096 bb_error_msg_and_die("%s: %s: invalid option", "command", argv[0]);
9097 }
9098 }
9099 }
9100 after_opts:
9101# if ENABLE_HUSH_FUNCTIONS
9102 if (opt_vV && find_function(argv[0]))
9103 if_command_vV_print_and_exit(opt_vV, argv[0], "a function");
9104# endif
9105#endif
9106
9107 /* Check if the command matches any of the builtins.
9108 * Depending on context, this might be redundant. But it's
9109 * easier to waste a few CPU cycles than it is to figure out
9110 * if this is one of those cases.
9111 */
9112 /* Why "BB_MMU ? :" difference in logic? -
9113 * On NOMMU, it is more expensive to re-execute shell
9114 * just in order to run echo or test builtin.
9115 * It's better to skip it here and run corresponding
9116 * non-builtin later. */
9117 x = BB_MMU ? find_builtin(argv[0]) : find_builtin1(argv[0]);
9118 if (x) {
9119 if_command_vV_print_and_exit(opt_vV, argv[0], "a shell builtin");
9120 exec_builtin(&nommu_save->argv_from_re_execing, x, argv);
9121 }
9122
9123#if ENABLE_FEATURE_SH_STANDALONE
9124 /* Check if the command matches any busybox applets */
9125 {
9126 int a = find_applet_by_name(argv[0]);
9127 if (a >= 0) {
9128 if_command_vV_print_and_exit(opt_vV, argv[0], "an applet");
9129# if BB_MMU /* see above why on NOMMU it is not allowed */
9130 if (APPLET_IS_NOEXEC(a)) {
9131 /* Do not leak open fds from opened script files etc.
9132 * Testcase: interactive "ls -l /proc/self/fd"
9133 * should not show tty fd open.
9134 */
9135 close_saved_fds_and_FILE_fds();
9136//FIXME: should also close saved redir fds
9137//This casuses test failures in
9138//redir_children_should_not_see_saved_fd_2.tests
9139//redir_children_should_not_see_saved_fd_3.tests
9140//if you replace "busybox find" with just "find" in them
9141 /* Without this, "rm -i FILE" can't be ^C'ed: */
9142 switch_off_special_sigs(G.special_sig_mask);
9143 debug_printf_exec("running applet '%s'\n", argv[0]);
9144 run_noexec_applet_and_exit(a, argv[0], argv);
9145 }
9146# endif
9147 /* Re-exec ourselves */
9148 debug_printf_exec("re-execing applet '%s'\n", argv[0]);
9149 /* Don't propagate SIG_IGN to the child */
9150 if (SPECIAL_JOBSTOP_SIGS != 0)
9151 switch_off_special_sigs(G.special_sig_mask & SPECIAL_JOBSTOP_SIGS);
9152 execv(bb_busybox_exec_path, argv);
9153 /* If they called chroot or otherwise made the binary no longer
9154 * executable, fall through */
9155 }
9156 }
9157#endif
9158
9159#if ENABLE_FEATURE_SH_STANDALONE || BB_MMU
9160 skip:
9161#endif
9162 if_command_vV_print_and_exit(opt_vV, argv[0], NULL);
9163 execvp_or_die(argv);
9164}
9165
9166/* Called after [v]fork() in run_pipe
9167 */
9168static void pseudo_exec(nommu_save_t *nommu_save,
9169 struct command *command,
9170 char **argv_expanded) NORETURN;
9171static void pseudo_exec(nommu_save_t *nommu_save,
9172 struct command *command,
9173 char **argv_expanded)
9174{
9175#if ENABLE_HUSH_FUNCTIONS
9176 if (command->cmd_type == CMD_FUNCDEF) {
9177 /* Ignore funcdefs in pipes:
9178 * true | f() { cmd }
9179 */
9180 _exit(0);
9181 }
9182#endif
9183
9184 if (command->argv) {
9185 pseudo_exec_argv(nommu_save, command->argv,
9186 command->assignment_cnt, argv_expanded);
9187 }
9188
9189 if (command->group) {
9190 /* Cases when we are here:
9191 * ( list )
9192 * { list } &
9193 * ... | ( list ) | ...
9194 * ... | { list } | ...
9195 */
9196#if BB_MMU
9197 int rcode;
9198 debug_printf_exec("pseudo_exec: run_list\n");
9199 reset_traps_to_defaults();
9200 rcode = run_list(command->group);
9201 /* OK to leak memory by not calling free_pipe_list,
9202 * since this process is about to exit */
9203 _exit(rcode);
9204#else
9205 re_execute_shell(&nommu_save->argv_from_re_execing,
9206 command->group_as_string,
9207 G.global_argv[0],
9208 G.global_argv + 1,
9209 NULL);
9210#endif
9211 }
9212
9213 /* Case when we are here: ... | >file */
9214 debug_printf_exec("pseudo_exec'ed null command\n");
9215 _exit(EXIT_SUCCESS);
9216}
9217
9218#if ENABLE_HUSH_JOB
9219static const char *get_cmdtext(struct pipe *pi)
9220{
9221 char **argv;
9222 char *p;
9223 int len;
9224
9225 /* This is subtle. ->cmdtext is created only on first backgrounding.
9226 * (Think "cat, <ctrl-z>, fg, <ctrl-z>, fg, <ctrl-z>...." here...)
9227 * On subsequent bg argv is trashed, but we won't use it */
9228 if (pi->cmdtext)
9229 return pi->cmdtext;
9230
9231 argv = pi->cmds[0].argv;
9232 if (!argv) {
9233 pi->cmdtext = xzalloc(1);
9234 return pi->cmdtext;
9235 }
9236 len = 0;
9237 do {
9238 len += strlen(*argv) + 1;
9239 } while (*++argv);
9240 p = xmalloc(len);
9241 pi->cmdtext = p;
9242 argv = pi->cmds[0].argv;
9243 do {
9244 p = stpcpy(p, *argv);
9245 *p++ = ' ';
9246 } while (*++argv);
9247 p[-1] = '\0';
9248 return pi->cmdtext;
9249}
9250
9251static void remove_job_from_table(struct pipe *pi)
9252{
9253 struct pipe *prev_pipe;
9254
9255 if (pi == G.job_list) {
9256 G.job_list = pi->next;
9257 } else {
9258 prev_pipe = G.job_list;
9259 while (prev_pipe->next != pi)
9260 prev_pipe = prev_pipe->next;
9261 prev_pipe->next = pi->next;
9262 }
9263 G.last_jobid = 0;
9264 if (G.job_list)
9265 G.last_jobid = G.job_list->jobid;
9266}
9267
9268static void delete_finished_job(struct pipe *pi)
9269{
9270 remove_job_from_table(pi);
9271 free_pipe(pi);
9272}
9273
9274static void clean_up_last_dead_job(void)
9275{
9276 if (G.job_list && !G.job_list->alive_cmds)
9277 delete_finished_job(G.job_list);
9278}
9279
9280static void insert_job_into_table(struct pipe *pi)
9281{
9282 struct pipe *job, **jobp;
9283 int i;
9284
9285 clean_up_last_dead_job();
9286
9287 /* Find the end of the list, and find next job ID to use */
9288 i = 0;
9289 jobp = &G.job_list;
9290 while ((job = *jobp) != NULL) {
9291 if (job->jobid > i)
9292 i = job->jobid;
9293 jobp = &job->next;
9294 }
9295 pi->jobid = i + 1;
9296
9297 /* Create a new job struct at the end */
9298 job = *jobp = xmemdup(pi, sizeof(*pi));
9299 job->next = NULL;
9300 job->cmds = xzalloc(sizeof(pi->cmds[0]) * pi->num_cmds);
9301 /* Cannot copy entire pi->cmds[] vector! This causes double frees */
9302 for (i = 0; i < pi->num_cmds; i++) {
9303 job->cmds[i].pid = pi->cmds[i].pid;
9304 /* all other fields are not used and stay zero */
9305 }
9306 job->cmdtext = xstrdup(get_cmdtext(pi));
9307
9308 if (G_interactive_fd)
9309 printf("[%u] %u %s\n", job->jobid, (unsigned)job->cmds[0].pid, job->cmdtext);
9310 G.last_jobid = job->jobid;
9311}
9312#endif /* JOB */
9313
9314static int job_exited_or_stopped(struct pipe *pi)
9315{
9316 int rcode, i;
9317
9318 if (pi->alive_cmds != pi->stopped_cmds)
9319 return -1;
9320
9321 /* All processes in fg pipe have exited or stopped */
9322 rcode = 0;
9323 i = pi->num_cmds;
9324 while (--i >= 0) {
9325 rcode = pi->cmds[i].cmd_exitcode;
9326 /* usually last process gives overall exitstatus,
9327 * but with "set -o pipefail", last *failed* process does */
9328 if (G.o_opt[OPT_O_PIPEFAIL] == 0 || rcode != 0)
9329 break;
9330 }
9331 IF_HAS_KEYWORDS(if (pi->pi_inverted) rcode = !rcode;)
9332 return rcode;
9333}
9334
9335static int process_wait_result(struct pipe *fg_pipe, pid_t childpid, int status)
9336{
9337#if ENABLE_HUSH_JOB
9338 struct pipe *pi;
9339#endif
9340 int i, dead;
9341
9342 dead = WIFEXITED(status) || WIFSIGNALED(status);
9343
9344#if DEBUG_JOBS
9345 if (WIFSTOPPED(status))
9346 debug_printf_jobs("pid %d stopped by sig %d (exitcode %d)\n",
9347 childpid, WSTOPSIG(status), WEXITSTATUS(status));
9348 if (WIFSIGNALED(status))
9349 debug_printf_jobs("pid %d killed by sig %d (exitcode %d)\n",
9350 childpid, WTERMSIG(status), WEXITSTATUS(status));
9351 if (WIFEXITED(status))
9352 debug_printf_jobs("pid %d exited, exitcode %d\n",
9353 childpid, WEXITSTATUS(status));
9354#endif
9355 /* Were we asked to wait for a fg pipe? */
9356 if (fg_pipe) {
9357 i = fg_pipe->num_cmds;
9358
9359 while (--i >= 0) {
9360 int rcode;
9361
9362 debug_printf_jobs("check pid %d\n", fg_pipe->cmds[i].pid);
9363 if (fg_pipe->cmds[i].pid != childpid)
9364 continue;
9365 if (dead) {
9366 int ex;
9367 fg_pipe->cmds[i].pid = 0;
9368 fg_pipe->alive_cmds--;
9369 ex = WEXITSTATUS(status);
9370 /* bash prints killer signal's name for *last*
9371 * process in pipe (prints just newline for SIGINT/SIGPIPE).
9372 * Mimic this. Example: "sleep 5" + (^\ or kill -QUIT)
9373 */
9374 if (WIFSIGNALED(status)) {
9375 int sig = WTERMSIG(status);
9376#if ENABLE_HUSH_JOB
9377 if (G.run_list_level == 1
9378 /* ^^^^^ Do not print in nested contexts, example:
9379 * echo `sleep 1; sh -c 'kill -9 $$'` - prints "137", NOT "Killed 137"
9380 */
9381 && i == fg_pipe->num_cmds-1
9382 ) {
9383 /* strsignal() is for bash compat. ~600 bloat versus bbox's get_signame() */
9384 puts(sig == SIGINT || sig == SIGPIPE ? "" : strsignal(sig));
9385 }
9386#endif
9387 /* TODO: if (WCOREDUMP(status)) + " (core dumped)"; */
9388 /* MIPS has 128 sigs (1..128), if sig==128,
9389 * 128 + sig would result in exitcode 256 -> 0!
9390 */
9391 ex = 128 | sig;
9392 }
9393 fg_pipe->cmds[i].cmd_exitcode = ex;
9394 } else {
9395 fg_pipe->stopped_cmds++;
9396 }
9397 debug_printf_jobs("fg_pipe: alive_cmds %d stopped_cmds %d\n",
9398 fg_pipe->alive_cmds, fg_pipe->stopped_cmds);
9399 rcode = job_exited_or_stopped(fg_pipe);
9400 if (rcode >= 0) {
9401/* Note: *non-interactive* bash does not continue if all processes in fg pipe
9402 * are stopped. Testcase: "cat | cat" in a script (not on command line!)
9403 * and "killall -STOP cat" */
9404 if (G_interactive_fd) {
9405#if ENABLE_HUSH_JOB
9406 if (fg_pipe->alive_cmds != 0)
9407 insert_job_into_table(fg_pipe);
9408#endif
9409 return rcode;
9410 }
9411 if (fg_pipe->alive_cmds == 0)
9412 return rcode;
9413 }
9414 /* There are still running processes in the fg_pipe */
9415 return -1;
9416 }
9417 /* It wasn't in fg_pipe, look for process in bg pipes */
9418 }
9419
9420#if ENABLE_HUSH_JOB
9421 /* We were asked to wait for bg or orphaned children */
9422 /* No need to remember exitcode in this case */
9423 for (pi = G.job_list; pi; pi = pi->next) {
9424 for (i = 0; i < pi->num_cmds; i++) {
9425 if (pi->cmds[i].pid == childpid)
9426 goto found_pi_and_prognum;
9427 }
9428 }
9429 /* Happens when shell is used as init process (init=/bin/sh) */
9430 debug_printf("checkjobs: pid %d was not in our list!\n", childpid);
9431 return -1; /* this wasn't a process from fg_pipe */
9432
9433 found_pi_and_prognum:
9434 if (dead) {
9435 /* child exited */
9436 int rcode = WEXITSTATUS(status);
9437 if (WIFSIGNALED(status))
9438 /* NB: not 128 + sig, MIPS has sig 128 */
9439 rcode = 128 | WTERMSIG(status);
9440 pi->cmds[i].cmd_exitcode = rcode;
9441 if (G.last_bg_pid == pi->cmds[i].pid)
9442 G.last_bg_pid_exitcode = rcode;
9443 pi->cmds[i].pid = 0;
9444 pi->alive_cmds--;
9445 if (!pi->alive_cmds) {
9446# if ENABLE_HUSH_BASH_COMPAT
9447 G.dead_job_exitcode = job_exited_or_stopped(pi);
9448# endif
9449 if (G_interactive_fd) {
9450 printf(JOB_STATUS_FORMAT, pi->jobid,
9451 "Done", pi->cmdtext);
9452 delete_finished_job(pi);
9453 } else {
9454/*
9455 * bash deletes finished jobs from job table only in interactive mode,
9456 * after "jobs" cmd, or if pid of a new process matches one of the old ones
9457 * (see cleanup_dead_jobs(), delete_old_job(), J_NOTIFIED in bash source).
9458 * Testcase script: "(exit 3) & sleep 1; wait %1; echo $?" prints 3 in bash.
9459 * We only retain one "dead" job, if it's the single job on the list.
9460 * This covers most of real-world scenarios where this is useful.
9461 */
9462 if (pi != G.job_list)
9463 delete_finished_job(pi);
9464 }
9465 }
9466 } else {
9467 /* child stopped */
9468 pi->stopped_cmds++;
9469 }
9470#endif
9471 return -1; /* this wasn't a process from fg_pipe */
9472}
9473
9474/* Check to see if any processes have exited -- if they have,
9475 * figure out why and see if a job has completed.
9476 *
9477 * If non-NULL fg_pipe: wait for its completion or stop.
9478 * Return its exitcode or zero if stopped.
9479 *
9480 * Alternatively (fg_pipe == NULL, waitfor_pid != 0):
9481 * waitpid(WNOHANG), if waitfor_pid exits or stops, return exitcode+1,
9482 * else return <0 if waitpid errors out (e.g. ECHILD: nothing to wait for)
9483 * or 0 if no children changed status.
9484 *
9485 * Alternatively (fg_pipe == NULL, waitfor_pid == 0),
9486 * return <0 if waitpid errors out (e.g. ECHILD: nothing to wait for)
9487 * or 0 if no children changed status.
9488 */
9489static int checkjobs(struct pipe *fg_pipe, pid_t waitfor_pid)
9490{
9491 int attributes;
9492 int status;
9493 int rcode = 0;
9494
9495 debug_printf_jobs("checkjobs %p\n", fg_pipe);
9496
9497 attributes = WUNTRACED;
9498 if (fg_pipe == NULL)
9499 attributes |= WNOHANG;
9500
9501 errno = 0;
9502#if ENABLE_HUSH_FAST
9503 if (G.handled_SIGCHLD == G.count_SIGCHLD) {
9504//bb_error_msg("[%d] checkjobs: G.count_SIGCHLD:%d G.handled_SIGCHLD:%d children?:%d fg_pipe:%p",
9505//getpid(), G.count_SIGCHLD, G.handled_SIGCHLD, G.we_have_children, fg_pipe);
9506 /* There was neither fork nor SIGCHLD since last waitpid */
9507 /* Avoid doing waitpid syscall if possible */
9508 if (!G.we_have_children) {
9509 errno = ECHILD;
9510 return -1;
9511 }
9512 if (fg_pipe == NULL) { /* is WNOHANG set? */
9513 /* We have children, but they did not exit
9514 * or stop yet (we saw no SIGCHLD) */
9515 return 0;
9516 }
9517 /* else: !WNOHANG, waitpid will block, can't short-circuit */
9518 }
9519#endif
9520
9521/* Do we do this right?
9522 * bash-3.00# sleep 20 | false
9523 * <ctrl-Z pressed>
9524 * [3]+ Stopped sleep 20 | false
9525 * bash-3.00# echo $?
9526 * 1 <========== bg pipe is not fully done, but exitcode is already known!
9527 * [hush 1.14.0: yes we do it right]
9528 */
9529 while (1) {
9530 pid_t childpid;
9531#if ENABLE_HUSH_FAST
9532 int i;
9533 i = G.count_SIGCHLD;
9534#endif
9535 childpid = waitpid(-1, &status, attributes);
9536 if (childpid <= 0) {
9537 if (childpid && errno != ECHILD)
9538 bb_simple_perror_msg("waitpid");
9539#if ENABLE_HUSH_FAST
9540 else { /* Until next SIGCHLD, waitpid's are useless */
9541 G.we_have_children = (childpid == 0);
9542 G.handled_SIGCHLD = i;
9543//bb_error_msg("[%d] checkjobs: waitpid returned <= 0, G.count_SIGCHLD:%d G.handled_SIGCHLD:%d", getpid(), G.count_SIGCHLD, G.handled_SIGCHLD);
9544 }
9545#endif
9546 /* ECHILD (no children), or 0 (no change in children status) */
9547 rcode = childpid;
9548 break;
9549 }
9550 rcode = process_wait_result(fg_pipe, childpid, status);
9551 if (rcode >= 0) {
9552 /* fg_pipe exited or stopped */
9553 break;
9554 }
9555 if (childpid == waitfor_pid) { /* "wait PID" */
9556 debug_printf_exec("childpid==waitfor_pid:%d status:0x%08x\n", childpid, status);
9557 rcode = WEXITSTATUS(status);
9558 if (WIFSIGNALED(status))
9559 rcode = 128 | WTERMSIG(status);
9560 if (WIFSTOPPED(status))
9561 /* bash: "cmd & wait $!" and cmd stops: $? = 128 | stopsig */
9562 rcode = 128 | WSTOPSIG(status);
9563 rcode++;
9564 break; /* "wait PID" called us, give it exitcode+1 */
9565 }
9566#if ENABLE_HUSH_BASH_COMPAT
9567 if (-1 == waitfor_pid /* "wait -n" (wait for any one job) */
9568 && G.dead_job_exitcode >= 0 /* some job did finish */
9569 ) {
9570 debug_printf_exec("waitfor_pid:-1\n");
9571 rcode = G.dead_job_exitcode + 1;
9572 break;
9573 }
9574#endif
9575 /* This wasn't one of our processes, or */
9576 /* fg_pipe still has running processes, do waitpid again */
9577 } /* while (waitpid succeeds)... */
9578
9579 return rcode;
9580}
9581
9582#if ENABLE_HUSH_JOB
9583static int checkjobs_and_fg_shell(struct pipe *fg_pipe)
9584{
9585 pid_t p;
9586 int rcode = checkjobs(fg_pipe, 0 /*(no pid to wait for)*/);
9587 if (G_saved_tty_pgrp) {
9588 /* Job finished, move the shell to the foreground */
9589 p = getpgrp(); /* our process group id */
9590 debug_printf_jobs("fg'ing ourself: getpgrp()=%d\n", (int)p);
9591 tcsetpgrp(G_interactive_fd, p);
9592 }
9593 return rcode;
9594}
9595#endif
9596
9597/* Start all the jobs, but don't wait for anything to finish.
9598 * See checkjobs().
9599 *
9600 * Return code is normally -1, when the caller has to wait for children
9601 * to finish to determine the exit status of the pipe. If the pipe
9602 * is a simple builtin command, however, the action is done by the
9603 * time run_pipe returns, and the exit code is provided as the
9604 * return value.
9605 *
9606 * Returns -1 only if started some children. IOW: we have to
9607 * mask out retvals of builtins etc with 0xff!
9608 *
9609 * The only case when we do not need to [v]fork is when the pipe
9610 * is single, non-backgrounded, non-subshell command. Examples:
9611 * cmd ; ... { list } ; ...
9612 * cmd && ... { list } && ...
9613 * cmd || ... { list } || ...
9614 * If it is, then we can run cmd as a builtin, NOFORK,
9615 * or (if SH_STANDALONE) an applet, and we can run the { list }
9616 * with run_list. If it isn't one of these, we fork and exec cmd.
9617 *
9618 * Cases when we must fork:
9619 * non-single: cmd | cmd
9620 * backgrounded: cmd & { list } &
9621 * subshell: ( list ) [&]
9622 */
9623#if !ENABLE_HUSH_MODE_X
9624#define redirect_and_varexp_helper(command, sqp, argv_expanded) \
9625 redirect_and_varexp_helper(command, sqp)
9626#endif
9627static int redirect_and_varexp_helper(
9628 struct command *command,
9629 struct squirrel **sqp,
9630 char **argv_expanded)
9631{
9632 /* Assignments occur before redirects. Try:
9633 * a=`sleep 1` sleep 2 3>/qwe/rty
9634 */
9635
9636 char **new_env = expand_assignments(command->argv, command->assignment_cnt);
9637 dump_cmd_in_x_mode(new_env);
9638 dump_cmd_in_x_mode(argv_expanded);
9639 /* this takes ownership of new_env[i] elements, and frees new_env: */
9640 set_vars_and_save_old(new_env);
9641
9642 return setup_redirects(command, sqp);
9643}
Francis Laniel8197f012023-12-22 22:02:28 +01009644#endif /* !__U_BOOT__ */
9645
Francis Lanielb234f7e2023-12-22 22:02:27 +01009646static NOINLINE int run_pipe(struct pipe *pi)
9647{
9648 static const char *const null_ptr = NULL;
9649
9650 int cmd_no;
Francis Laniel8197f012023-12-22 22:02:28 +01009651#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01009652 int next_infd;
Francis Laniel8197f012023-12-22 22:02:28 +01009653#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01009654 struct command *command;
9655 char **argv_expanded;
9656 char **argv;
Francis Laniel8197f012023-12-22 22:02:28 +01009657#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01009658 struct squirrel *squirrel = NULL;
Francis Laniel8197f012023-12-22 22:02:28 +01009659#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01009660 int rcode;
9661
Francis Laniel8197f012023-12-22 22:02:28 +01009662#ifdef __U_BOOT__
9663 /*
9664 * Set rcode here to avoid returning a garbage value in the middle of
9665 * the function.
9666 * Also, if an error occurs, rcode value would be changed and last
9667 * return will signal the error.
9668 */
9669 rcode = 0;
9670#endif /* __U_BOOT__ */
9671
Francis Lanielb234f7e2023-12-22 22:02:27 +01009672 debug_printf_exec("run_pipe start: members:%d\n", pi->num_cmds);
9673 debug_enter();
9674
9675 /* Testcase: set -- q w e; (IFS='' echo "$*"; IFS=''; echo "$*"); echo "$*"
9676 * Result should be 3 lines: q w e, qwe, q w e
9677 */
9678 if (G.ifs_whitespace != G.ifs)
9679 free(G.ifs_whitespace);
9680 G.ifs = get_local_var_value("IFS");
9681 if (G.ifs) {
9682 char *p;
9683 G.ifs_whitespace = (char*)G.ifs;
9684 p = skip_whitespace(G.ifs);
9685 if (*p) {
9686 /* Not all $IFS is whitespace */
9687 char *d;
9688 int len = p - G.ifs;
9689 p = skip_non_whitespace(p);
9690 G.ifs_whitespace = xmalloc(len + strlen(p) + 1); /* can overestimate */
9691 d = mempcpy(G.ifs_whitespace, G.ifs, len);
9692 while (*p) {
9693 if (isspace(*p))
9694 *d++ = *p;
9695 p++;
9696 }
9697 *d = '\0';
9698 }
9699 } else {
9700 G.ifs = defifs;
9701 G.ifs_whitespace = (char*)G.ifs;
9702 }
9703
Francis Laniel8197f012023-12-22 22:02:28 +01009704#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01009705 IF_HUSH_JOB(pi->pgrp = -1;)
9706 pi->stopped_cmds = 0;
Francis Laniel8197f012023-12-22 22:02:28 +01009707#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01009708 command = &pi->cmds[0];
9709 argv_expanded = NULL;
9710
Francis Laniel8197f012023-12-22 22:02:28 +01009711#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01009712 if (pi->num_cmds != 1
9713 || pi->followup == PIPE_BG
9714 || command->cmd_type == CMD_SUBSHELL
9715 ) {
9716 goto must_fork;
9717 }
9718
9719 pi->alive_cmds = 1;
Francis Laniel8197f012023-12-22 22:02:28 +01009720#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01009721
9722 debug_printf_exec(": group:%p argv:'%s'\n",
9723 command->group, command->argv ? command->argv[0] : "NONE");
9724
9725 if (command->group) {
9726#if ENABLE_HUSH_FUNCTIONS
9727 if (command->cmd_type == CMD_FUNCDEF) {
9728 /* "executing" func () { list } */
9729 struct function *funcp;
9730
9731 funcp = new_function(command->argv[0]);
9732 /* funcp->name is already set to argv[0] */
9733 funcp->body = command->group;
9734# if !BB_MMU
9735 funcp->body_as_string = command->group_as_string;
9736 command->group_as_string = NULL;
9737# endif
9738 command->group = NULL;
9739 command->argv[0] = NULL;
9740 debug_printf_exec("cmd %p has child func at %p\n", command, funcp);
9741 funcp->parent_cmd = command;
9742 command->child_func = funcp;
9743
9744 debug_printf_exec("run_pipe: return EXIT_SUCCESS\n");
9745 debug_leave();
9746 return EXIT_SUCCESS;
9747 }
9748#endif
9749 /* { list } */
9750 debug_printf_exec("non-subshell group\n");
9751 rcode = 1; /* exitcode if redir failed */
Francis Laniel8197f012023-12-22 22:02:28 +01009752#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01009753 if (setup_redirects(command, &squirrel) == 0) {
9754 debug_printf_exec(": run_list\n");
9755//FIXME: we need to pass squirrel down into run_list()
9756//for SH_STANDALONE case, or else this construct:
9757// { find /proc/self/fd; true; } >FILE; cmd2
9758//has no way of closing saved fd#1 for "find",
9759//and in SH_STANDALONE mode, "find" is not execed,
9760//therefore CLOEXEC on saved fd does not help.
9761 rcode = run_list(command->group) & 0xff;
9762 }
9763 restore_redirects(squirrel);
9764 IF_HAS_KEYWORDS(if (pi->pi_inverted) rcode = !rcode;)
Francis Laniel8197f012023-12-22 22:02:28 +01009765#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01009766 debug_leave();
9767 debug_printf_exec("run_pipe: return %d\n", rcode);
9768 return rcode;
9769 }
9770
9771 argv = command->argv ? command->argv : (char **) &null_ptr;
9772 {
Francis Laniel8197f012023-12-22 22:02:28 +01009773#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01009774 const struct built_in_command *x;
9775 IF_HUSH_FUNCTIONS(const struct function *funcp;)
9776 IF_NOT_HUSH_FUNCTIONS(enum { funcp = 0 };)
9777 struct variable **sv_shadowed;
Francis Laniel8197f012023-12-22 22:02:28 +01009778#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01009779 struct variable *old_vars;
9780
9781#if ENABLE_HUSH_LINENO_VAR
9782 G.execute_lineno = command->lineno;
9783#endif
9784
9785 if (argv[command->assignment_cnt] == NULL) {
9786 /* Assignments, but no command.
9787 * Ensure redirects take effect (that is, create files).
9788 * Try "a=t >file"
9789 */
9790 unsigned i;
9791 G.expand_exitcode = 0;
9792 only_assignments:
Francis Laniel8197f012023-12-22 22:02:28 +01009793#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01009794 rcode = setup_redirects(command, &squirrel);
9795 restore_redirects(squirrel);
Francis Laniel8197f012023-12-22 22:02:28 +01009796#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01009797
9798 /* Set shell variables */
9799 i = 0;
9800 while (i < command->assignment_cnt) {
9801 char *p = expand_string_to_string(argv[i],
9802 EXP_FLAG_ESC_GLOB_CHARS,
9803 /*unbackslash:*/ 1
9804 );
9805#if ENABLE_HUSH_MODE_X
9806 if (G_x_mode) {
9807 char *eq;
9808 if (i == 0)
9809 x_mode_prefix();
9810 x_mode_addchr(' ');
9811 eq = strchrnul(p, '=');
9812 if (*eq) eq++;
9813 x_mode_addblock(p, (eq - p));
9814 x_mode_print_optionally_squoted(eq);
9815 x_mode_flush();
9816 }
9817#endif
9818 debug_printf_env("set shell var:'%s'->'%s'\n", *argv, p);
Francis Laniel8197f012023-12-22 22:02:28 +01009819#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01009820 if (set_local_var(p, /*flag:*/ 0)) {
Francis Laniel8197f012023-12-22 22:02:28 +01009821#else /* __U_BOOT__ */
9822 if (set_local_var_modern(p, /*flag:*/ 0)) {
9823#endif
Francis Lanielb234f7e2023-12-22 22:02:27 +01009824 /* assignment to readonly var / putenv error? */
9825 rcode = 1;
9826 }
9827 i++;
9828 }
9829 /* Redirect error sets $? to 1. Otherwise,
9830 * if evaluating assignment value set $?, retain it.
9831 * Else, clear $?:
9832 * false; q=`exit 2`; echo $? - should print 2
9833 * false; x=1; echo $? - should print 0
9834 * Because of the 2nd case, we can't just use G.last_exitcode.
9835 */
9836 if (rcode == 0)
9837 rcode = G.expand_exitcode;
9838 IF_HAS_KEYWORDS(if (pi->pi_inverted) rcode = !rcode;)
9839 debug_leave();
9840 debug_printf_exec("run_pipe: return %d\n", rcode);
9841 return rcode;
9842 }
9843
9844 /* Expand the rest into (possibly) many strings each */
9845#if defined(CMD_TEST2_SINGLEWORD_NOGLOB)
9846 if (command->cmd_type == CMD_TEST2_SINGLEWORD_NOGLOB)
9847 argv_expanded = expand_strvec_to_strvec_singleword_noglob(argv + command->assignment_cnt);
9848 else
9849#endif
9850#if defined(CMD_SINGLEWORD_NOGLOB)
9851 if (command->cmd_type == CMD_SINGLEWORD_NOGLOB)
9852 argv_expanded = expand_strvec_to_strvec_singleword_noglob(argv + command->assignment_cnt);
9853 else
9854#endif
9855 argv_expanded = expand_strvec_to_strvec(argv + command->assignment_cnt);
9856
9857 /* If someone gives us an empty string: `cmd with empty output` */
9858 if (!argv_expanded[0]) {
9859 free(argv_expanded);
9860 /* `false` still has to set exitcode 1 */
9861 G.expand_exitcode = G.last_exitcode;
9862 goto only_assignments;
9863 }
9864
9865 old_vars = NULL;
Francis Laniel8197f012023-12-22 22:02:28 +01009866#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01009867 sv_shadowed = G.shadowed_vars_pp;
9868
9869 /* Check if argv[0] matches any functions (this goes before bltins) */
9870 IF_HUSH_FUNCTIONS(funcp = find_function(argv_expanded[0]);)
9871 IF_HUSH_FUNCTIONS(x = NULL;)
9872 IF_HUSH_FUNCTIONS(if (!funcp))
9873 x = find_builtin(argv_expanded[0]);
9874 if (x || funcp) {
9875 if (x && x->b_function == builtin_exec && argv_expanded[1] == NULL) {
9876 debug_printf("exec with redirects only\n");
9877 /*
9878 * Variable assignments are executed, but then "forgotten":
9879 * a=`sleep 1;echo A` exec 3>&-; echo $a
9880 * sleeps, but prints nothing.
9881 */
9882 enter_var_nest_level();
9883 G.shadowed_vars_pp = &old_vars;
9884 rcode = redirect_and_varexp_helper(command,
9885 /*squirrel:*/ ERR_PTR,
9886 argv_expanded
9887 );
9888 G.shadowed_vars_pp = sv_shadowed;
9889 /* rcode=1 can be if redir file can't be opened */
9890
9891 goto clean_up_and_ret1;
9892 }
9893
9894 /* Bump var nesting, or this will leak exported $a:
9895 * a=b true; env | grep ^a=
9896 */
9897 enter_var_nest_level();
9898 /* Collect all variables "shadowed" by helper
9899 * (IOW: old vars overridden by "var1=val1 var2=val2 cmd..." syntax)
9900 * into old_vars list:
9901 */
9902 G.shadowed_vars_pp = &old_vars;
9903 rcode = redirect_and_varexp_helper(command, &squirrel, argv_expanded);
9904 if (rcode == 0) {
9905 if (!funcp) {
9906 /* Do not collect *to old_vars list* vars shadowed
9907 * by e.g. "local VAR" builtin (collect them
9908 * in the previously nested list instead):
9909 * don't want them to be restored immediately
9910 * after "local" completes.
9911 */
9912 G.shadowed_vars_pp = sv_shadowed;
9913
9914 debug_printf_exec(": builtin '%s' '%s'...\n",
9915 x->b_cmd, argv_expanded[1]);
9916 fflush_all();
9917 rcode = x->b_function(argv_expanded) & 0xff;
9918 fflush_all();
9919 }
9920#if ENABLE_HUSH_FUNCTIONS
9921 else {
9922 debug_printf_exec(": function '%s' '%s'...\n",
9923 funcp->name, argv_expanded[1]);
9924 rcode = run_function(funcp, argv_expanded) & 0xff;
9925 /*
9926 * But do collect *to old_vars list* vars shadowed
9927 * within function execution. To that end, restore
9928 * this pointer _after_ function run:
9929 */
9930 G.shadowed_vars_pp = sv_shadowed;
9931 }
9932#endif
9933 }
9934 } else
9935 if (ENABLE_FEATURE_SH_NOFORK && NUM_APPLETS > 1) {
9936 int n = find_applet_by_name(argv_expanded[0]);
9937 if (n < 0 || !APPLET_IS_NOFORK(n))
9938 goto must_fork;
9939
9940 enter_var_nest_level();
9941 /* Collect all variables "shadowed" by helper into old_vars list */
9942 G.shadowed_vars_pp = &old_vars;
9943 rcode = redirect_and_varexp_helper(command, &squirrel, argv_expanded);
9944 G.shadowed_vars_pp = sv_shadowed;
9945
9946 if (rcode == 0) {
9947 debug_printf_exec(": run_nofork_applet '%s' '%s'...\n",
9948 argv_expanded[0], argv_expanded[1]);
9949 /*
9950 * Note: signals (^C) can't interrupt here.
9951 * We remember them and they will be acted upon
9952 * after applet returns.
9953 * This makes applets which can run for a long time
9954 * and/or wait for user input ineligible for NOFORK:
9955 * for example, "yes" or "rm" (rm -i waits for input).
9956 */
9957 rcode = run_nofork_applet(n, argv_expanded);
9958 }
9959 } else
9960 goto must_fork;
9961
9962 restore_redirects(squirrel);
9963 clean_up_and_ret1:
9964 leave_var_nest_level();
9965 add_vars(old_vars);
9966
9967 /*
9968 * Try "usleep 99999999" + ^C + "echo $?"
9969 * with FEATURE_SH_NOFORK=y.
9970 */
9971 if (!funcp) {
9972 /* It was builtin or nofork.
9973 * if this would be a real fork/execed program,
9974 * it should have died if a fatal sig was received.
9975 * But OTOH, there was no separate process,
9976 * the sig was sent to _shell_, not to non-existing
9977 * child.
9978 * Let's just handle ^C only, this one is obvious:
9979 * we aren't ok with exitcode 0 when ^C was pressed
9980 * during builtin/nofork.
9981 */
9982 if (sigismember(&G.pending_set, SIGINT))
9983 rcode = 128 | SIGINT;
9984 }
9985 free(argv_expanded);
9986 IF_HAS_KEYWORDS(if (pi->pi_inverted) rcode = !rcode;)
9987 debug_leave();
9988 debug_printf_exec("run_pipe return %d\n", rcode);
9989 return rcode;
Francis Laniel8197f012023-12-22 22:02:28 +01009990#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +01009991 }
9992
Francis Laniel8197f012023-12-22 22:02:28 +01009993#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +01009994 must_fork:
9995 /* NB: argv_expanded may already be created, and that
9996 * might include `cmd` runs! Do not rerun it! We *must*
9997 * use argv_expanded if it's non-NULL */
9998
9999 /* Going to fork a child per each pipe member */
10000 pi->alive_cmds = 0;
10001 next_infd = 0;
Francis Laniel8197f012023-12-22 22:02:28 +010010002#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +010010003
10004 cmd_no = 0;
10005 while (cmd_no < pi->num_cmds) {
Francis Laniel8197f012023-12-22 22:02:28 +010010006#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +010010007 struct fd_pair pipefds;
10008#if !BB_MMU
10009 int sv_var_nest_level = G.var_nest_level;
10010 volatile nommu_save_t nommu_save;
10011 nommu_save.old_vars = NULL;
10012 nommu_save.argv = NULL;
10013 nommu_save.argv_from_re_execing = NULL;
10014#endif
Francis Laniel8197f012023-12-22 22:02:28 +010010015#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +010010016 command = &pi->cmds[cmd_no];
10017 cmd_no++;
Francis Laniel74e42542023-12-22 22:02:33 +010010018
10019#ifdef __U_BOOT__
10020 /* Replace argv and argc by expanded if it exists. */
10021 if (argv_expanded) {
10022 /*
10023 * We need to save a pointer to argv, we will restore it
10024 * later, so it will be freed when pipe is freed.
10025 */
10026 argv = command->argv;
10027
10028 /*
10029 * After expansion, there can be more or less argument, so we need to
10030 * update argc, for example:
10031 * - More arguments:
10032 * foo='bar quuz'
10033 * echo $foo
10034 * - Less arguments:
10035 * echo $foo (if foo was never set)
10036 */
10037 command->argc = list_size(argv_expanded);
10038 command->argv = argv_expanded;
10039 }
10040#endif /* __U_BOOT__ */
10041 if (command->argv) {
Francis Lanielb234f7e2023-12-22 22:02:27 +010010042 debug_printf_exec(": pipe member '%s' '%s'...\n",
10043 command->argv[0], command->argv[1]);
10044 } else {
10045 debug_printf_exec(": pipe member with no argv\n");
10046 }
10047
Francis Laniel8197f012023-12-22 22:02:28 +010010048#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +010010049 /* pipes are inserted between pairs of commands */
10050 pipefds.rd = 0;
10051 pipefds.wr = 1;
10052 if (cmd_no < pi->num_cmds)
10053 xpiped_pair(pipefds);
10054
10055#if ENABLE_HUSH_LINENO_VAR
10056 G.execute_lineno = command->lineno;
10057#endif
10058
10059 command->pid = BB_MMU ? fork() : vfork();
10060 if (!command->pid) { /* child */
10061#if ENABLE_HUSH_JOB
10062 disable_restore_tty_pgrp_on_exit();
10063 CLEAR_RANDOM_T(&G.random_gen); /* or else $RANDOM repeats in child */
10064
10065 /* Every child adds itself to new process group
10066 * with pgid == pid_of_first_child_in_pipe */
10067 if (G.run_list_level == 1 && G_interactive_fd) {
10068 pid_t pgrp;
10069 pgrp = pi->pgrp;
10070 if (pgrp < 0) /* true for 1st process only */
10071 pgrp = getpid();
10072 if (setpgid(0, pgrp) == 0
10073 && pi->followup != PIPE_BG
10074 && G_saved_tty_pgrp /* we have ctty */
10075 ) {
10076 /* We do it in *every* child, not just first,
10077 * to avoid races */
10078 tcsetpgrp(G_interactive_fd, pgrp);
10079 }
10080 }
10081#endif
10082 if (pi->alive_cmds == 0 && pi->followup == PIPE_BG) {
10083 /* 1st cmd in backgrounded pipe
10084 * should have its stdin /dev/null'ed */
10085 close(0);
10086 if (open(bb_dev_null, O_RDONLY))
10087 xopen("/", O_RDONLY);
10088 } else {
10089 xmove_fd(next_infd, 0);
10090 }
10091 xmove_fd(pipefds.wr, 1);
10092 if (pipefds.rd > 1)
10093 close(pipefds.rd);
10094 /* Like bash, explicit redirects override pipes,
10095 * and the pipe fd (fd#1) is available for dup'ing:
10096 * "cmd1 2>&1 | cmd2": fd#1 is duped to fd#2, thus stderr
10097 * of cmd1 goes into pipe.
10098 */
10099 if (setup_redirects(command, NULL)) {
10100 /* Happens when redir file can't be opened:
10101 * $ hush -c 'echo FOO >&2 | echo BAR 3>/qwe/rty; echo BAZ'
10102 * FOO
10103 * hush: can't open '/qwe/rty': No such file or directory
10104 * BAZ
10105 * (echo BAR is not executed, it hits _exit(1) below)
10106 */
10107 _exit(1);
10108 }
10109
10110 /* Stores to nommu_save list of env vars putenv'ed
10111 * (NOMMU, on MMU we don't need that) */
10112 /* cast away volatility... */
10113 pseudo_exec((nommu_save_t*) &nommu_save, command, argv_expanded);
10114 /* pseudo_exec() does not return */
10115 }
10116
10117 /* parent or error */
10118#if ENABLE_HUSH_FAST
10119 G.count_SIGCHLD++;
10120//bb_error_msg("[%d] fork in run_pipe: G.count_SIGCHLD:%d G.handled_SIGCHLD:%d", getpid(), G.count_SIGCHLD, G.handled_SIGCHLD);
10121#endif
10122 enable_restore_tty_pgrp_on_exit();
10123#if !BB_MMU
10124 /* Clean up after vforked child */
10125 free(nommu_save.argv);
10126 free(nommu_save.argv_from_re_execing);
10127 G.var_nest_level = sv_var_nest_level;
10128 remove_nested_vars();
10129 add_vars(nommu_save.old_vars);
10130#endif
10131 free(argv_expanded);
10132 argv_expanded = NULL;
10133 if (command->pid < 0) { /* [v]fork failed */
10134 /* Clearly indicate, was it fork or vfork */
10135 bb_simple_perror_msg(BB_MMU ? "vfork"+1 : "vfork");
10136 } else {
10137 pi->alive_cmds++;
10138#if ENABLE_HUSH_JOB
10139 /* Second and next children need to know pid of first one */
10140 if (pi->pgrp < 0)
10141 pi->pgrp = command->pid;
10142#endif
10143 }
10144
10145 if (cmd_no > 1)
10146 close(next_infd);
10147 if (cmd_no < pi->num_cmds)
10148 close(pipefds.wr);
10149 /* Pass read (output) pipe end to next iteration */
10150 next_infd = pipefds.rd;
Francis Laniel8197f012023-12-22 22:02:28 +010010151#else /* __U_BOOT__ */
10152 /* Process the command */
10153 rcode = cmd_process(G.do_repeat ? CMD_FLAG_REPEAT : 0,
10154 command->argc, command->argv,
10155 &(G.flag_repeat), NULL);
Francis Laniel74e42542023-12-22 22:02:33 +010010156
10157 if (argv_expanded) {
10158 /*
10159 * expand_strvec_to_strvec() allocates memory to expand
10160 * argv, we need to free it.
10161 */
10162 free(argv_expanded);
10163
10164 /*
10165 * We also restore command->argv to its original value
10166 * so no memory leak happens.
10167 */
10168 command->argv = argv;
10169
10170 /*
10171 * NOTE argc exists only in U-Boot, so argv freeing does
10172 * not rely on it as this code exists in BusyBox.
10173 */
10174 }
Francis Laniel8197f012023-12-22 22:02:28 +010010175#endif /* __U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +010010176 }
10177
Francis Laniel8197f012023-12-22 22:02:28 +010010178#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +010010179 if (!pi->alive_cmds) {
10180 debug_leave();
10181 debug_printf_exec("run_pipe return 1 (all forks failed, no children)\n");
10182 return 1;
10183 }
Francis Laniel8197f012023-12-22 22:02:28 +010010184#endif /* __U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +010010185
10186 debug_leave();
Francis Laniel8197f012023-12-22 22:02:28 +010010187#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +010010188 debug_printf_exec("run_pipe return -1 (%u children started)\n", pi->alive_cmds);
10189 return -1;
Francis Laniel8197f012023-12-22 22:02:28 +010010190#else /* __U_BOOT__ */
10191 debug_printf_exec("run_pipe return %d\n", rcode);
10192 return rcode;
10193#endif /* __U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +010010194}
10195
10196/* NB: called by pseudo_exec, and therefore must not modify any
10197 * global data until exec/_exit (we can be a child after vfork!) */
10198static int run_list(struct pipe *pi)
10199{
10200#if ENABLE_HUSH_CASE
10201 char *case_word = NULL;
10202#endif
10203#if ENABLE_HUSH_LOOPS
10204 struct pipe *loop_top = NULL;
10205 char **for_lcur = NULL;
10206 char **for_list = NULL;
10207#endif
10208 smallint last_followup;
10209 smalluint rcode;
10210#if ENABLE_HUSH_IF || ENABLE_HUSH_CASE
10211 smalluint cond_code = 0;
10212#else
10213 enum { cond_code = 0 };
10214#endif
10215#if HAS_KEYWORDS
10216 smallint rword; /* RES_foo */
10217 smallint last_rword; /* ditto */
10218#endif
10219
Francis Laniel8197f012023-12-22 22:02:28 +010010220#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +010010221 debug_printf_exec("run_list start lvl %d\n", G.run_list_level);
10222 debug_enter();
Francis Laniel8197f012023-12-22 22:02:28 +010010223#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +010010224
10225#if ENABLE_HUSH_LOOPS
10226 /* Check syntax for "for" */
10227 {
10228 struct pipe *cpipe;
10229 for (cpipe = pi; cpipe; cpipe = cpipe->next) {
10230 if (cpipe->res_word != RES_FOR && cpipe->res_word != RES_IN)
10231 continue;
10232 /* current word is FOR or IN (BOLD in comments below) */
10233 if (cpipe->next == NULL) {
10234 syntax_error("malformed for");
10235 debug_leave();
10236 debug_printf_exec("run_list lvl %d return 1\n", G.run_list_level);
10237 return 1;
10238 }
10239 /* "FOR v; do ..." and "for v IN a b; do..." are ok */
10240 if (cpipe->next->res_word == RES_DO)
10241 continue;
10242 /* next word is not "do". It must be "in" then ("FOR v in ...") */
10243 if (cpipe->res_word == RES_IN /* "for v IN a b; not_do..."? */
10244 || cpipe->next->res_word != RES_IN /* FOR v not_do_and_not_in..."? */
10245 ) {
10246 syntax_error("malformed for");
10247 debug_leave();
10248 debug_printf_exec("run_list lvl %d return 1\n", G.run_list_level);
10249 return 1;
10250 }
10251 }
10252 }
10253#endif
10254
10255 /* Past this point, all code paths should jump to ret: label
10256 * in order to return, no direct "return" statements please.
10257 * This helps to ensure that no memory is leaked. */
10258
10259#if ENABLE_HUSH_JOB
10260 G.run_list_level++;
10261#endif
10262
10263#if HAS_KEYWORDS
10264 rword = RES_NONE;
10265 last_rword = RES_XXXX;
10266#endif
10267 last_followup = PIPE_SEQ;
10268 rcode = G.last_exitcode;
10269
10270 /* Go through list of pipes, (maybe) executing them. */
Francis Laniel8197f012023-12-22 22:02:28 +010010271#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +010010272 for (; pi; pi = IF_HUSH_LOOPS(rword == RES_DONE ? loop_top : ) pi->next) {
Francis Laniel8197f012023-12-22 22:02:28 +010010273#else /* __U_BOOT__ */
10274 for (; pi; pi = pi->next) {
10275#endif /* __U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +010010276 int r;
10277 int sv_errexit_depth;
10278
Francis Laniel8197f012023-12-22 22:02:28 +010010279#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +010010280 if (G.flag_SIGINT)
10281 break;
10282 if (G_flag_return_in_progress == 1)
10283 break;
10284
10285 IF_HAS_KEYWORDS(rword = pi->res_word;)
10286 debug_printf_exec(": rword=%d cond_code=%d last_rword=%d\n",
10287 rword, cond_code, last_rword);
10288
Francis Laniel8197f012023-12-22 22:02:28 +010010289#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +010010290 sv_errexit_depth = G.errexit_depth;
10291 if (
10292#if ENABLE_HUSH_IF
10293 rword == RES_IF || rword == RES_ELIF ||
10294#endif
10295 pi->followup != PIPE_SEQ
10296 ) {
10297 G.errexit_depth++;
10298 }
10299#if ENABLE_HUSH_LOOPS
10300 if ((rword == RES_WHILE || rword == RES_UNTIL || rword == RES_FOR)
10301 && loop_top == NULL /* avoid bumping G.depth_of_loop twice */
10302 ) {
10303 /* start of a loop: remember where loop starts */
10304 loop_top = pi;
10305 G.depth_of_loop++;
10306 }
10307#endif
10308 /* Still in the same "if...", "then..." or "do..." branch? */
10309 if (IF_HAS_KEYWORDS(rword == last_rword &&) 1) {
10310 if ((rcode == 0 && last_followup == PIPE_OR)
10311 || (rcode != 0 && last_followup == PIPE_AND)
10312 ) {
10313 /* It is "<true> || CMD" or "<false> && CMD"
10314 * and we should not execute CMD */
10315 debug_printf_exec("skipped cmd because of || or &&\n");
10316 last_followup = pi->followup;
10317 goto dont_check_jobs_but_continue;
10318 }
10319 }
10320 last_followup = pi->followup;
10321 IF_HAS_KEYWORDS(last_rword = rword;)
10322#if ENABLE_HUSH_IF
10323 if (cond_code) {
10324 if (rword == RES_THEN) {
10325 /* if false; then ... fi has exitcode 0! */
10326 G.last_exitcode = rcode = EXIT_SUCCESS;
10327 /* "if <false> THEN cmd": skip cmd */
10328 continue;
10329 }
10330 } else {
10331 if (rword == RES_ELSE || rword == RES_ELIF) {
10332 /* "if <true> then ... ELSE/ELIF cmd":
10333 * skip cmd and all following ones */
10334 break;
10335 }
10336 }
10337#endif
10338#if ENABLE_HUSH_LOOPS
10339 if (rword == RES_FOR) { /* && pi->num_cmds - always == 1 */
10340 if (!for_lcur) {
10341 /* first loop through for */
10342
10343 static const char encoded_dollar_at[] ALIGN1 = {
10344 SPECIAL_VAR_SYMBOL, '@' | 0x80, SPECIAL_VAR_SYMBOL, '\0'
10345 }; /* encoded representation of "$@" */
10346 static const char *const encoded_dollar_at_argv[] = {
10347 encoded_dollar_at, NULL
10348 }; /* argv list with one element: "$@" */
10349 char **vals;
10350
10351 G.last_exitcode = rcode = EXIT_SUCCESS;
10352 vals = (char**)encoded_dollar_at_argv;
10353 if (pi->next->res_word == RES_IN) {
10354 /* if no variable values after "in" we skip "for" */
10355 if (!pi->next->cmds[0].argv) {
10356 debug_printf_exec(": null FOR: exitcode EXIT_SUCCESS\n");
10357 break;
10358 }
10359 vals = pi->next->cmds[0].argv;
10360 } /* else: "for var; do..." -> assume "$@" list */
10361 /* create list of variable values */
10362 debug_print_strings("for_list made from", vals);
10363 for_list = expand_strvec_to_strvec(vals);
10364 for_lcur = for_list;
10365 debug_print_strings("for_list", for_list);
10366 }
10367 if (!*for_lcur) {
10368 /* "for" loop is over, clean up */
10369 free(for_list);
10370 for_list = NULL;
10371 for_lcur = NULL;
10372 break;
10373 }
10374 /* Insert next value from for_lcur */
10375 /* note: *for_lcur already has quotes removed, $var expanded, etc */
10376 set_local_var(xasprintf("%s=%s", pi->cmds[0].argv[0], *for_lcur++), /*flag:*/ 0);
10377 continue;
10378 }
10379 if (rword == RES_IN) {
10380 continue; /* "for v IN list;..." - "in" has no cmds anyway */
10381 }
10382 if (rword == RES_DONE) {
10383 continue; /* "done" has no cmds too */
10384 }
10385#endif
10386#if ENABLE_HUSH_CASE
10387 if (rword == RES_CASE) {
10388 debug_printf_exec("CASE cond_code:%d\n", cond_code);
10389 case_word = expand_string_to_string(pi->cmds->argv[0],
10390 EXP_FLAG_ESC_GLOB_CHARS, /*unbackslash:*/ 1);
10391 debug_printf_exec("CASE word1:'%s'\n", case_word);
10392 //unbackslash(case_word);
10393 //debug_printf_exec("CASE word2:'%s'\n", case_word);
10394 continue;
10395 }
10396 if (rword == RES_MATCH) {
10397 char **argv;
10398
10399 debug_printf_exec("MATCH cond_code:%d\n", cond_code);
10400 if (!case_word) /* "case ... matched_word) ... WORD)": we executed selected branch, stop */
10401 break;
10402 /* all prev words didn't match, does this one match? */
10403 argv = pi->cmds->argv;
10404 while (*argv) {
10405 char *pattern;
10406 debug_printf_exec("expand_string_to_string('%s')\n", *argv);
10407 pattern = expand_string_to_string(*argv,
10408 EXP_FLAG_ESC_GLOB_CHARS,
10409 /*unbackslash:*/ 0
10410 );
10411 /* TODO: which FNM_xxx flags to use? */
10412 cond_code = (fnmatch(pattern, case_word, /*flags:*/ 0) != 0);
10413 debug_printf_exec("fnmatch(pattern:'%s',str:'%s'):%d\n",
10414 pattern, case_word, cond_code);
10415 free(pattern);
10416 if (cond_code == 0) {
10417 /* match! we will execute this branch */
10418 free(case_word);
10419 case_word = NULL; /* make future "word)" stop */
10420 break;
10421 }
10422 argv++;
10423 }
10424 continue;
10425 }
10426 if (rword == RES_CASE_BODY) { /* inside of a case branch */
10427 debug_printf_exec("CASE_BODY cond_code:%d\n", cond_code);
10428 if (cond_code != 0)
10429 continue; /* not matched yet, skip this pipe */
10430 }
10431 if (rword == RES_ESAC) {
10432 debug_printf_exec("ESAC cond_code:%d\n", cond_code);
10433 if (case_word) {
10434 /* "case" did not match anything: still set $? (to 0) */
10435 G.last_exitcode = rcode = EXIT_SUCCESS;
10436 }
10437 }
10438#endif
10439 /* Just pressing <enter> in shell should check for jobs.
10440 * OTOH, in non-interactive shell this is useless
10441 * and only leads to extra job checks */
10442 if (pi->num_cmds == 0) {
Francis Laniel8197f012023-12-22 22:02:28 +010010443#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +010010444 if (G_interactive_fd)
10445 goto check_jobs_and_continue;
Francis Laniel8197f012023-12-22 22:02:28 +010010446#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +010010447 continue;
10448 }
10449
10450 /* After analyzing all keywords and conditions, we decided
10451 * to execute this pipe. NB: have to do checkjobs(NULL)
10452 * after run_pipe to collect any background children,
10453 * even if list execution is to be stopped. */
10454 debug_printf_exec(": run_pipe with %d members\n", pi->num_cmds);
Francis Laniel8197f012023-12-22 22:02:28 +010010455#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +010010456#if ENABLE_HUSH_LOOPS
10457 G.flag_break_continue = 0;
10458#endif
Francis Laniel8197f012023-12-22 22:02:28 +010010459#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +010010460 rcode = r = run_pipe(pi); /* NB: rcode is a smalluint, r is int */
Francis Laniel8197f012023-12-22 22:02:28 +010010461#ifdef __U_BOOT__
Francis Laniel9a068372023-12-22 22:02:32 +010010462 if (r <= EXIT_RET_CODE) {
10463 int previous_rcode = G.last_exitcode;
10464 /*
10465 * This magic is to get the exit code given by the user.
10466 * Contrary to old shell code, we use + EXIT_RET_CODE as EXIT_RET_CODE
10467 * equals -2.
10468 */
10469 G.last_exitcode = -r + EXIT_RET_CODE;
Francis Laniel8197f012023-12-22 22:02:28 +010010470
Francis Laniel9a068372023-12-22 22:02:32 +010010471 /*
10472 * This case deals with the following:
10473 * => setenv inner 'echo entry inner; exit; echo inner done'
10474 * => setenv outer 'echo entry outer; run inner; echo outer done'
10475 * => run outer
10476 * So, if we are in inner, we need to break and not run the other
10477 * commands.
10478 * Otherwise, we just continue in outer.
10479 * As return code are propagated, we use the previous value to check if
10480 * exit was just called or was propagated.
10481 */
10482 if (previous_rcode != r) {
10483 /*
10484 * If run from run_command, run_command_flags will be set, so we check
10485 * this to know if we are in main input shell.
10486 */
10487 if (!G.run_command_flags)
10488 printf("exit not allowed from main input shell.\n");
10489
10490 break;
10491 }
10492 continue;
Francis Laniel8197f012023-12-22 22:02:28 +010010493 }
Francis Laniel9a068372023-12-22 22:02:32 +010010494#endif /* __U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +010010495 if (r != -1) {
10496 /* We ran a builtin, function, or group.
10497 * rcode is already known
10498 * and we don't need to wait for anything. */
10499 debug_printf_exec(": builtin/func exitcode %d\n", rcode);
10500 G.last_exitcode = rcode;
Francis Laniel8197f012023-12-22 22:02:28 +010010501#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +010010502 check_and_run_traps();
Francis Laniel8197f012023-12-22 22:02:28 +010010503#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +010010504#if ENABLE_HUSH_TRAP && ENABLE_HUSH_FUNCTIONS
10505 rcode = G.last_exitcode; /* "return" in trap can change it, read back */
10506#endif
Francis Laniel8197f012023-12-22 22:02:28 +010010507#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +010010508#if ENABLE_HUSH_LOOPS
10509 /* Was it "break" or "continue"? */
10510 if (G.flag_break_continue) {
10511 smallint fbc = G.flag_break_continue;
10512 /* We might fall into outer *loop*,
10513 * don't want to break it too */
10514 if (loop_top) {
10515 G.depth_break_continue--;
10516 if (G.depth_break_continue == 0)
10517 G.flag_break_continue = 0;
10518 /* else: e.g. "continue 2" should *break* once, *then* continue */
10519 } /* else: "while... do... { we are here (innermost list is not a loop!) };...done" */
10520 if (G.depth_break_continue != 0 || fbc == BC_BREAK) {
10521 checkjobs(NULL, 0 /*(no pid to wait for)*/);
10522 break;
10523 }
10524 /* "continue": simulate end of loop */
10525 rword = RES_DONE;
10526 continue;
10527 }
10528#endif
10529 if (G_flag_return_in_progress == 1) {
10530 checkjobs(NULL, 0 /*(no pid to wait for)*/);
10531 break;
10532 }
Francis Laniel8197f012023-12-22 22:02:28 +010010533
Francis Lanielb234f7e2023-12-22 22:02:27 +010010534 } else if (pi->followup == PIPE_BG) {
10535 /* What does bash do with attempts to background builtins? */
10536 /* even bash 3.2 doesn't do that well with nested bg:
10537 * try "{ { sleep 10; echo DEEP; } & echo HERE; } &".
10538 * I'm NOT treating inner &'s as jobs */
10539#if ENABLE_HUSH_JOB
10540 if (G.run_list_level == 1)
10541 insert_job_into_table(pi);
10542#endif
10543 /* Last command's pid goes to $! */
10544 G.last_bg_pid = pi->cmds[pi->num_cmds - 1].pid;
10545 G.last_bg_pid_exitcode = 0;
10546 debug_printf_exec(": cmd&: exitcode EXIT_SUCCESS\n");
10547/* Check pi->pi_inverted? "! sleep 1 & echo $?": bash says 1. dash and ash say 0 */
10548 rcode = EXIT_SUCCESS;
10549 goto check_traps;
10550 } else {
10551#if ENABLE_HUSH_JOB
10552 if (G.run_list_level == 1 && G_interactive_fd) {
10553 /* Waits for completion, then fg's main shell */
10554 rcode = checkjobs_and_fg_shell(pi);
10555 debug_printf_exec(": checkjobs_and_fg_shell exitcode %d\n", rcode);
10556 goto check_traps;
10557 }
10558#endif
10559 /* This one just waits for completion */
10560 rcode = checkjobs(pi, 0 /*(no pid to wait for)*/);
10561 debug_printf_exec(": checkjobs exitcode %d\n", rcode);
10562 check_traps:
10563 G.last_exitcode = rcode;
10564 check_and_run_traps();
10565#if ENABLE_HUSH_TRAP && ENABLE_HUSH_FUNCTIONS
10566 rcode = G.last_exitcode; /* "return" in trap can change it, read back */
10567#endif
10568 }
Francis Laniel8197f012023-12-22 22:02:28 +010010569#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +010010570
Francis Laniel8197f012023-12-22 22:02:28 +010010571#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +010010572 /* Handle "set -e" */
10573 if (rcode != 0 && G.o_opt[OPT_O_ERREXIT]) {
10574 debug_printf_exec("ERREXIT:1 errexit_depth:%d\n", G.errexit_depth);
10575 if (G.errexit_depth == 0)
10576 hush_exit(rcode);
10577 }
Francis Laniel8197f012023-12-22 22:02:28 +010010578#else /* __U_BOOT__ */
10579 } /* if (r != -1) */
10580#endif /* __U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +010010581 G.errexit_depth = sv_errexit_depth;
10582
10583 /* Analyze how result affects subsequent commands */
10584#if ENABLE_HUSH_IF
10585 if (rword == RES_IF || rword == RES_ELIF)
10586 cond_code = rcode;
10587#endif
Francis Laniel8197f012023-12-22 22:02:28 +010010588#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +010010589 check_jobs_and_continue:
10590 checkjobs(NULL, 0 /*(no pid to wait for)*/);
Francis Laniel8197f012023-12-22 22:02:28 +010010591#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +010010592 dont_check_jobs_but_continue: ;
10593#if ENABLE_HUSH_LOOPS
10594 /* Beware of "while false; true; do ..."! */
10595 if (pi->next
10596 && (pi->next->res_word == RES_DO || pi->next->res_word == RES_DONE)
10597 /* check for RES_DONE is needed for "while ...; do \n done" case */
10598 ) {
10599 if (rword == RES_WHILE) {
10600 if (rcode) {
10601 /* "while false; do...done" - exitcode 0 */
10602 G.last_exitcode = rcode = EXIT_SUCCESS;
10603 debug_printf_exec(": while expr is false: breaking (exitcode:EXIT_SUCCESS)\n");
10604 break;
10605 }
10606 }
10607 if (rword == RES_UNTIL) {
10608 if (!rcode) {
10609 debug_printf_exec(": until expr is true: breaking\n");
10610 break;
10611 }
10612 }
10613 }
10614#endif
10615 } /* for (pi) */
10616
10617#if ENABLE_HUSH_JOB
10618 G.run_list_level--;
10619#endif
10620#if ENABLE_HUSH_LOOPS
10621 if (loop_top)
10622 G.depth_of_loop--;
10623 free(for_list);
10624#endif
10625#if ENABLE_HUSH_CASE
10626 free(case_word);
10627#endif
Francis Laniel8197f012023-12-22 22:02:28 +010010628#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +010010629 debug_leave();
10630 debug_printf_exec("run_list lvl %d return %d\n", G.run_list_level + 1, rcode);
Francis Laniel8197f012023-12-22 22:02:28 +010010631#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +010010632 return rcode;
10633}
10634
10635/* Select which version we will use */
10636static int run_and_free_list(struct pipe *pi)
10637{
10638 int rcode = 0;
10639 debug_printf_exec("run_and_free_list entered\n");
Francis Laniel8197f012023-12-22 22:02:28 +010010640#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +010010641 if (!G.o_opt[OPT_O_NOEXEC]) {
Francis Laniel8197f012023-12-22 22:02:28 +010010642#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +010010643 debug_printf_exec(": run_list: 1st pipe with %d cmds\n", pi->num_cmds);
10644 rcode = run_list(pi);
Francis Laniel8197f012023-12-22 22:02:28 +010010645#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +010010646 }
Francis Laniel8197f012023-12-22 22:02:28 +010010647#endif /* !__U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +010010648 /* free_pipe_list has the side effect of clearing memory.
10649 * In the long run that function can be merged with run_list,
10650 * but doing that now would hobble the debugging effort. */
10651 free_pipe_list(pi);
10652 debug_printf_exec("run_and_free_list return %d\n", rcode);
10653 return rcode;
10654}
10655
10656
Francis Laniel8197f012023-12-22 22:02:28 +010010657#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +010010658static void install_sighandlers(unsigned mask)
10659{
10660 sighandler_t old_handler;
10661 unsigned sig = 0;
10662 while ((mask >>= 1) != 0) {
10663 sig++;
10664 if (!(mask & 1))
10665 continue;
10666 old_handler = install_sighandler(sig, pick_sighandler(sig));
10667 /* POSIX allows shell to re-enable SIGCHLD
10668 * even if it was SIG_IGN on entry.
10669 * Therefore we skip IGN check for it:
10670 */
10671 if (sig == SIGCHLD)
10672 continue;
10673 /* Interactive bash re-enables SIGHUP which is SIG_IGNed on entry.
10674 * Try:
10675 * trap '' hup; bash; echo RET # type "kill -hup $$", see SIGHUP having effect
10676 * trap '' hup; bash -c 'kill -hup $$; echo ALIVE' # here SIGHUP is SIG_IGNed
10677 */
10678 if (sig == SIGHUP && G_interactive_fd)
10679 continue;
10680 /* Unless one of the above signals, is it SIG_IGN? */
10681 if (old_handler == SIG_IGN) {
10682 /* oops... restore back to IGN, and record this fact */
10683 install_sighandler(sig, old_handler);
10684#if ENABLE_HUSH_TRAP
10685 if (!G_traps)
10686 G_traps = xzalloc(sizeof(G_traps[0]) * NSIG);
10687 free(G_traps[sig]);
10688 G_traps[sig] = xzalloc(1); /* == xstrdup(""); */
10689#endif
10690 }
10691 }
10692}
10693
10694/* Called a few times only (or even once if "sh -c") */
10695static void install_special_sighandlers(void)
10696{
10697 unsigned mask;
10698
10699 /* Which signals are shell-special? */
10700 mask = (1 << SIGQUIT) | (1 << SIGCHLD);
10701 if (G_interactive_fd) {
10702 mask |= SPECIAL_INTERACTIVE_SIGS;
10703 if (G_saved_tty_pgrp) /* we have ctty, job control sigs work */
10704 mask |= SPECIAL_JOBSTOP_SIGS;
10705 }
10706 /* Careful, do not re-install handlers we already installed */
10707 if (G.special_sig_mask != mask) {
10708 unsigned diff = mask & ~G.special_sig_mask;
10709 G.special_sig_mask = mask;
10710 install_sighandlers(diff);
10711 }
10712}
10713
10714#if ENABLE_HUSH_JOB
10715/* helper */
10716/* Set handlers to restore tty pgrp and exit */
10717static void install_fatal_sighandlers(void)
10718{
10719 unsigned mask;
10720
10721 /* We will restore tty pgrp on these signals */
10722 mask = 0
10723 /*+ (1 << SIGILL ) * HUSH_DEBUG*/
10724 /*+ (1 << SIGFPE ) * HUSH_DEBUG*/
10725 + (1 << SIGBUS ) * HUSH_DEBUG
10726 + (1 << SIGSEGV) * HUSH_DEBUG
10727 /*+ (1 << SIGTRAP) * HUSH_DEBUG*/
10728 + (1 << SIGABRT)
10729 /* bash 3.2 seems to handle these just like 'fatal' ones */
10730 + (1 << SIGPIPE)
10731 + (1 << SIGALRM)
10732 /* if we are interactive, SIGHUP, SIGTERM and SIGINT are special sigs.
10733 * if we aren't interactive... but in this case
10734 * we never want to restore pgrp on exit, and this fn is not called
10735 */
10736 /*+ (1 << SIGHUP )*/
10737 /*+ (1 << SIGTERM)*/
10738 /*+ (1 << SIGINT )*/
10739 ;
10740 G_fatal_sig_mask = mask;
10741
10742 install_sighandlers(mask);
10743}
10744#endif
10745
10746static int set_mode(int state, char mode, const char *o_opt)
10747{
10748 int idx;
10749 switch (mode) {
10750 case 'n':
10751 G.o_opt[OPT_O_NOEXEC] = state;
10752 break;
10753 case 'x':
10754 IF_HUSH_MODE_X(G_x_mode = state;)
10755 IF_HUSH_MODE_X(if (G.x_mode_fd <= 0) G.x_mode_fd = dup_CLOEXEC(2, 10);)
10756 break;
10757 case 'e':
10758 G.o_opt[OPT_O_ERREXIT] = state;
10759 break;
10760 case 'o':
10761 if (!o_opt) {
10762 /* "set -o" or "set +o" without parameter.
10763 * in bash, set -o produces this output:
10764 * pipefail off
10765 * and set +o:
10766 * set +o pipefail
10767 * We always use the second form.
10768 */
10769 const char *p = o_opt_strings;
10770 idx = 0;
10771 while (*p) {
10772 printf("set %co %s\n", (G.o_opt[idx] ? '-' : '+'), p);
10773 idx++;
10774 p += strlen(p) + 1;
10775 }
10776 break;
10777 }
10778 idx = index_in_strings(o_opt_strings, o_opt);
10779 if (idx >= 0) {
10780 G.o_opt[idx] = state;
10781 break;
10782 }
10783 /* fall through to error */
10784 default:
10785 return EXIT_FAILURE;
10786 }
10787 return EXIT_SUCCESS;
10788}
10789
10790int hush_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
10791int hush_main(int argc, char **argv)
10792{
10793 pid_t cached_getpid;
10794 enum {
10795 OPT_login = (1 << 0),
10796 };
10797 unsigned flags;
10798#if !BB_MMU
10799 unsigned builtin_argc = 0;
10800#endif
10801 char **e;
10802 struct variable *cur_var;
10803 struct variable *shell_ver;
10804
10805 INIT_G();
10806 if (EXIT_SUCCESS != 0) /* if EXIT_SUCCESS == 0, it is already done */
10807 G.last_exitcode = EXIT_SUCCESS;
10808#if ENABLE_HUSH_TRAP
10809# if ENABLE_HUSH_FUNCTIONS
10810 G.return_exitcode = -1;
10811# endif
10812 G.pre_trap_exitcode = -1;
10813#endif
10814
10815#if ENABLE_HUSH_FAST
10816 G.count_SIGCHLD++; /* ensure it is != G.handled_SIGCHLD */
10817#endif
10818#if !BB_MMU
10819 G.argv0_for_re_execing = argv[0];
10820#endif
10821
10822 cached_getpid = getpid(); /* for tcsetpgrp() during init */
10823 G.root_pid = cached_getpid; /* for $PID (NOMMU can override via -$HEXPID:HEXPPID:...) */
10824 G.root_ppid = getppid(); /* for $PPID (NOMMU can override) */
10825
10826 /* Deal with HUSH_VERSION */
10827 debug_printf_env("unsetenv '%s'\n", "HUSH_VERSION");
10828 unsetenv("HUSH_VERSION"); /* in case it exists in initial env */
10829 shell_ver = xzalloc(sizeof(*shell_ver));
10830 shell_ver->flg_export = 1;
10831 shell_ver->flg_read_only = 1;
10832 /* Code which handles ${var<op>...} needs writable values for all variables,
10833 * therefore we xstrdup: */
10834 shell_ver->varstr = xstrdup(hush_version_str);
10835
10836 /* Create shell local variables from the values
10837 * currently living in the environment */
10838 G.top_var = shell_ver;
10839 cur_var = G.top_var;
10840 e = environ;
10841 if (e) while (*e) {
10842 char *value = strchr(*e, '=');
10843 if (value) { /* paranoia */
10844 cur_var->next = xzalloc(sizeof(*cur_var));
10845 cur_var = cur_var->next;
10846 cur_var->varstr = *e;
10847 cur_var->max_len = strlen(*e);
10848 cur_var->flg_export = 1;
10849 }
10850 e++;
10851 }
10852 /* (Re)insert HUSH_VERSION into env (AFTER we scanned the env!) */
10853 debug_printf_env("putenv '%s'\n", shell_ver->varstr);
10854 putenv(shell_ver->varstr);
10855
10856 /* Export PWD */
10857 set_pwd_var(SETFLAG_EXPORT);
10858
10859#if BASH_HOSTNAME_VAR
10860 /* Set (but not export) HOSTNAME unless already set */
10861 if (!get_local_var_value("HOSTNAME")) {
10862 struct utsname uts;
10863 uname(&uts);
10864 set_local_var_from_halves("HOSTNAME", uts.nodename);
10865 }
10866#endif
10867 /* IFS is not inherited from the parent environment */
10868 set_local_var_from_halves("IFS", defifs);
10869
10870 if (!get_local_var_value("PATH"))
10871 set_local_var_from_halves("PATH", bb_default_root_path);
10872
10873 /* PS1/PS2 are set later, if we determine that we are interactive */
10874
10875 /* bash also exports SHLVL and _,
10876 * and sets (but doesn't export) the following variables:
10877 * BASH=/bin/bash
10878 * BASH_VERSINFO=([0]="3" [1]="2" [2]="0" [3]="1" [4]="release" [5]="i386-pc-linux-gnu")
10879 * BASH_VERSION='3.2.0(1)-release'
10880 * HOSTTYPE=i386
10881 * MACHTYPE=i386-pc-linux-gnu
10882 * OSTYPE=linux-gnu
10883 * PPID=<NNNNN> - we also do it elsewhere
10884 * EUID=<NNNNN>
10885 * UID=<NNNNN>
10886 * GROUPS=()
10887 * LINES=<NNN>
10888 * COLUMNS=<NNN>
10889 * BASH_ARGC=()
10890 * BASH_ARGV=()
10891 * BASH_LINENO=()
10892 * BASH_SOURCE=()
10893 * DIRSTACK=()
10894 * PIPESTATUS=([0]="0")
10895 * HISTFILE=/<xxx>/.bash_history
10896 * HISTFILESIZE=500
10897 * HISTSIZE=500
10898 * MAILCHECK=60
10899 * PATH=/usr/gnu/bin:/usr/local/bin:/bin:/usr/bin:.
10900 * SHELL=/bin/bash
10901 * SHELLOPTS=braceexpand:emacs:hashall:histexpand:history:interactive-comments:monitor
10902 * TERM=dumb
10903 * OPTERR=1
10904 * OPTIND=1
10905 * PS4='+ '
10906 */
10907
10908#if NUM_SCRIPTS > 0
10909 if (argc < 0) {
10910 char *script = get_script_content(-argc - 1);
10911 G.global_argv = argv;
10912 G.global_argc = string_array_len(argv);
10913 //install_special_sighandlers(); - needed?
10914 parse_and_run_string(script);
10915 goto final_return;
10916 }
10917#endif
10918
10919 /* Initialize some more globals to non-zero values */
10920 die_func = restore_ttypgrp_and__exit;
10921
10922 /* Shell is non-interactive at first. We need to call
10923 * install_special_sighandlers() if we are going to execute "sh <script>",
10924 * "sh -c <cmds>" or login shell's /etc/profile and friends.
10925 * If we later decide that we are interactive, we run install_special_sighandlers()
10926 * in order to intercept (more) signals.
10927 */
10928
10929 /* Parse options */
10930 /* http://www.opengroup.org/onlinepubs/9699919799/utilities/sh.html */
10931 flags = (argv[0] && argv[0][0] == '-') ? OPT_login : 0;
10932 while (1) {
10933 int opt = getopt(argc, argv, "+" /* stop at 1st non-option */
10934 "cexinsl"
10935#if !BB_MMU
10936 "<:$:R:V:"
10937# if ENABLE_HUSH_FUNCTIONS
10938 "F:"
10939# endif
10940#endif
10941 );
10942 if (opt <= 0)
10943 break;
10944 switch (opt) {
10945 case 'c':
10946 /* Note: -c is not an option with param!
10947 * "hush -c -l SCRIPT" is valid. "hush -cSCRIPT" is not.
10948 */
10949 G.opt_c = 1;
10950 break;
10951 case 'i':
10952 /* Well, we cannot just declare interactiveness,
10953 * we have to have some stuff (ctty, etc) */
10954 /* G_interactive_fd++; */
10955 break;
10956 case 's':
10957 G.opt_s = 1;
10958 break;
10959 case 'l':
10960 flags |= OPT_login;
10961 break;
10962#if !BB_MMU
10963 case '<': /* "big heredoc" support */
10964 full_write1_str(optarg);
10965 _exit(0);
10966 case '$': {
10967 unsigned long long empty_trap_mask;
10968
10969 G.root_pid = bb_strtou(optarg, &optarg, 16);
10970 optarg++;
10971 G.root_ppid = bb_strtou(optarg, &optarg, 16);
10972 optarg++;
10973 G.last_bg_pid = bb_strtou(optarg, &optarg, 16);
10974 optarg++;
10975 G.last_exitcode = bb_strtou(optarg, &optarg, 16);
10976 optarg++;
10977 builtin_argc = bb_strtou(optarg, &optarg, 16);
10978 optarg++;
10979 empty_trap_mask = bb_strtoull(optarg, &optarg, 16);
10980 if (empty_trap_mask != 0) {
10981 IF_HUSH_TRAP(int sig;)
10982 install_special_sighandlers();
10983# if ENABLE_HUSH_TRAP
10984 G_traps = xzalloc(sizeof(G_traps[0]) * NSIG);
10985 for (sig = 1; sig < NSIG; sig++) {
10986 if (empty_trap_mask & (1LL << sig)) {
10987 G_traps[sig] = xzalloc(1); /* == xstrdup(""); */
10988 install_sighandler(sig, SIG_IGN);
10989 }
10990 }
10991# endif
10992 }
10993# if ENABLE_HUSH_LOOPS
10994 optarg++;
10995 G.depth_of_loop = bb_strtou(optarg, &optarg, 16);
10996# endif
10997 /* Suppress "killed by signal" message, -$ hack is used
10998 * for subshells: echo `sh -c 'kill -9 $$'`
10999 * should be silent.
11000 */
11001 IF_HUSH_JOB(G.run_list_level = 1;)
11002# if ENABLE_HUSH_FUNCTIONS
11003 /* nommu uses re-exec trick for "... | func | ...",
11004 * should allow "return".
11005 * This accidentally allows returns in subshells.
11006 */
11007 G_flag_return_in_progress = -1;
11008# endif
11009 break;
11010 }
11011 case 'R':
11012 case 'V':
11013 set_local_var(xstrdup(optarg), opt == 'R' ? SETFLAG_MAKE_RO : 0);
11014 break;
11015# if ENABLE_HUSH_FUNCTIONS
11016 case 'F': {
11017 struct function *funcp = new_function(optarg);
11018 /* funcp->name is already set to optarg */
11019 /* funcp->body is set to NULL. It's a special case. */
11020 funcp->body_as_string = argv[optind];
11021 optind++;
11022 break;
11023 }
11024# endif
11025#endif
11026 /*case '?': invalid option encountered (set_mode('?') will fail) */
11027 /*case 'n':*/
11028 /*case 'x':*/
11029 /*case 'e':*/
11030 default:
11031 if (set_mode(1, opt, NULL) == 0) /* no error */
11032 break;
11033 bb_show_usage();
11034 }
11035 } /* option parsing loop */
11036
11037 /* Skip options. Try "hush -l": $1 should not be "-l"! */
11038 G.global_argc = argc - (optind - 1);
11039 G.global_argv = argv + (optind - 1);
11040 G.global_argv[0] = argv[0];
11041
11042 /* If we are login shell... */
11043 if (flags & OPT_login) {
11044 const char *hp = NULL;
11045 HFILE *input;
11046
11047 debug_printf("sourcing /etc/profile\n");
11048 input = hfopen("/etc/profile");
11049 run_profile:
11050 if (input != NULL) {
11051 install_special_sighandlers();
11052 parse_and_run_file(input);
11053 hfclose(input);
11054 }
11055 /* bash: after sourcing /etc/profile,
11056 * tries to source (in the given order):
11057 * ~/.bash_profile, ~/.bash_login, ~/.profile,
11058 * stopping on first found. --noprofile turns this off.
11059 * bash also sources ~/.bash_logout on exit.
11060 * If called as sh, skips .bash_XXX files.
11061 */
11062 if (!hp) { /* unless we looped on the "goto" already */
11063 hp = get_local_var_value("HOME");
11064 if (hp && hp[0]) {
11065 debug_printf("sourcing ~/.profile\n");
11066 hp = concat_path_file(hp, ".profile");
11067 input = hfopen(hp);
11068 free((char*)hp);
11069 goto run_profile;
11070 }
11071 }
11072 }
11073
Francis Laniel8197f012023-12-22 22:02:28 +010011074#ifndef __U_BOOT__
Francis Lanielb234f7e2023-12-22 22:02:27 +010011075 /* -c takes effect *after* -l */
11076 if (G.opt_c) {
11077 /* Possibilities:
11078 * sh ... -c 'script'
11079 * sh ... -c 'script' ARG0 [ARG1...]
11080 * On NOMMU, if builtin_argc != 0,
11081 * sh ... -c 'builtin' BARGV... "" ARG0 [ARG1...]
11082 * "" needs to be replaced with NULL
11083 * and BARGV vector fed to builtin function.
11084 * Note: the form without ARG0 never happens:
11085 * sh ... -c 'builtin' BARGV... ""
11086 */
11087 char *script;
11088
11089 install_special_sighandlers();
11090
11091 G.global_argc--;
11092 G.global_argv++;
11093#if !BB_MMU
11094 if (builtin_argc) {
11095 /* -c 'builtin' [BARGV...] "" ARG0 [ARG1...] */
11096 const struct built_in_command *x;
11097 x = find_builtin(G.global_argv[0]);
11098 if (x) { /* paranoia */
11099 argv = G.global_argv;
11100 G.global_argc -= builtin_argc + 1; /* skip [BARGV...] "" */
11101 G.global_argv += builtin_argc + 1;
11102 G.global_argv[-1] = NULL; /* replace "" */
11103 G.last_exitcode = x->b_function(argv);
11104 }
11105 goto final_return;
11106 }
11107#endif
11108
11109 script = G.global_argv[0];
11110 if (!script)
11111 bb_error_msg_and_die(bb_msg_requires_arg, "-c");
11112 if (!G.global_argv[1]) {
11113 /* -c 'script' (no params): prevent empty $0 */
11114 G.global_argv[0] = argv[0];
11115 } else { /* else -c 'script' ARG0 [ARG1...]: $0 is ARG0 */
11116 G.global_argc--;
11117 G.global_argv++;
11118 }
11119 parse_and_run_string(script);
11120 goto final_return;
11121 }
11122
11123 /* -s is: hush -s ARGV1 ARGV2 (no SCRIPT) */
11124 if (!G.opt_s && G.global_argv[1]) {
11125 HFILE *input;
11126 /*
11127 * "bash <script>" (which is never interactive (unless -i?))
11128 * sources $BASH_ENV here (without scanning $PATH).
11129 * If called as sh, does the same but with $ENV.
11130 * Also NB, per POSIX, $ENV should undergo parameter expansion.
11131 */
11132 G.global_argc--;
11133 G.global_argv++;
11134 debug_printf("running script '%s'\n", G.global_argv[0]);
11135 xfunc_error_retval = 127; /* for "hush /does/not/exist" case */
11136 input = hfopen(G.global_argv[0]);
11137 if (!input) {
11138 bb_simple_perror_msg_and_die(G.global_argv[0]);
11139 }
11140 xfunc_error_retval = 1;
11141 install_special_sighandlers();
11142 parse_and_run_file(input);
11143#if ENABLE_FEATURE_CLEAN_UP
11144 hfclose(input);
11145#endif
11146 goto final_return;
11147 }
11148 /* "implicit" -s: bare interactive hush shows 's' in $- */
11149 G.opt_s = 1;
11150
Francis Laniel8197f012023-12-22 22:02:28 +010011151#endif /* __U_BOOT__ */
Francis Lanielb234f7e2023-12-22 22:02:27 +010011152 /* Up to here, shell was non-interactive. Now it may become one.
11153 * NB: don't forget to (re)run install_special_sighandlers() as needed.
11154 */
11155
11156 /* A shell is interactive if the '-i' flag was given,
11157 * or if all of the following conditions are met:
11158 * no -c command
11159 * no arguments remaining or the -s flag given
11160 * standard input is a terminal
11161 * standard output is a terminal
11162 * Refer to Posix.2, the description of the 'sh' utility.
11163 */
11164#if ENABLE_HUSH_JOB
11165 if (isatty(STDIN_FILENO) && isatty(STDOUT_FILENO)) {
11166 G_saved_tty_pgrp = tcgetpgrp(STDIN_FILENO);
11167 debug_printf("saved_tty_pgrp:%d\n", G_saved_tty_pgrp);
11168 if (G_saved_tty_pgrp < 0)
11169 G_saved_tty_pgrp = 0;
11170
11171 /* try to dup stdin to high fd#, >= 255 */
11172 G_interactive_fd = dup_CLOEXEC(STDIN_FILENO, 254);
11173 if (G_interactive_fd < 0) {
11174 /* try to dup to any fd */
11175 G_interactive_fd = dup(STDIN_FILENO);
11176 if (G_interactive_fd < 0) {
11177 /* give up */
11178 G_interactive_fd = 0;
11179 G_saved_tty_pgrp = 0;
11180 }
11181 }
11182 }
11183 debug_printf("interactive_fd:%d\n", G_interactive_fd);
11184 if (G_interactive_fd) {
11185 close_on_exec_on(G_interactive_fd);
11186
11187 if (G_saved_tty_pgrp) {
11188 /* If we were run as 'hush &', sleep until we are
11189 * in the foreground (tty pgrp == our pgrp).
11190 * If we get started under a job aware app (like bash),
11191 * make sure we are now in charge so we don't fight over
11192 * who gets the foreground */
11193 while (1) {
11194 pid_t shell_pgrp = getpgrp();
11195 G_saved_tty_pgrp = tcgetpgrp(G_interactive_fd);
11196 if (G_saved_tty_pgrp == shell_pgrp)
11197 break;
11198 /* send TTIN to ourself (should stop us) */
11199 kill(- shell_pgrp, SIGTTIN);
11200 }
11201 }
11202
11203 /* Install more signal handlers */
11204 install_special_sighandlers();
11205
11206 if (G_saved_tty_pgrp) {
11207 /* Set other signals to restore saved_tty_pgrp */
11208 install_fatal_sighandlers();
11209 /* Put ourselves in our own process group
11210 * (bash, too, does this only if ctty is available) */
11211 bb_setpgrp(); /* is the same as setpgid(our_pid, our_pid); */
11212 /* Grab control of the terminal */
11213 tcsetpgrp(G_interactive_fd, cached_getpid);
11214 }
11215 enable_restore_tty_pgrp_on_exit();
11216
11217# if ENABLE_FEATURE_EDITING
11218 G.line_input_state = new_line_input_t(FOR_SHELL);
11219# if EDITING_HAS_get_exe_name
11220 G.line_input_state->get_exe_name = get_builtin_name;
11221# endif
11222# endif
11223# if ENABLE_HUSH_SAVEHISTORY && MAX_HISTORY > 0
11224 {
11225 const char *hp = get_local_var_value("HISTFILE");
11226 if (!hp) {
11227 hp = get_local_var_value("HOME");
11228 if (hp)
11229 hp = concat_path_file(hp, ".hush_history");
11230 } else {
11231 hp = xstrdup(hp);
11232 }
11233 if (hp) {
11234 G.line_input_state->hist_file = hp;
11235 //set_local_var(xasprintf("HISTFILE=%s", ...));
11236 }
11237# if ENABLE_FEATURE_SH_HISTFILESIZE
11238 hp = get_local_var_value("HISTFILESIZE");
11239 G.line_input_state->max_history = size_from_HISTFILESIZE(hp);
11240# endif
11241 }
11242# endif
11243 } else {
11244 install_special_sighandlers();
11245 }
11246#elif ENABLE_HUSH_INTERACTIVE
11247 /* No job control compiled in, only prompt/line editing */
11248 if (isatty(STDIN_FILENO) && isatty(STDOUT_FILENO)) {
11249 G_interactive_fd = dup_CLOEXEC(STDIN_FILENO, 254);
11250 if (G_interactive_fd < 0) {
11251 /* try to dup to any fd */
11252 G_interactive_fd = dup_CLOEXEC(STDIN_FILENO, -1);
11253 if (G_interactive_fd < 0)
11254 /* give up */
11255 G_interactive_fd = 0;
11256 }
11257 }
11258 if (G_interactive_fd) {
11259 close_on_exec_on(G_interactive_fd);
11260 }
11261 install_special_sighandlers();
11262#else
11263 /* We have interactiveness code disabled */
11264 install_special_sighandlers();
11265#endif
11266 /* bash:
11267 * if interactive but not a login shell, sources ~/.bashrc
11268 * (--norc turns this off, --rcfile <file> overrides)
11269 */
11270
11271 if (G_interactive_fd) {
11272#if ENABLE_HUSH_INTERACTIVE && ENABLE_FEATURE_EDITING_FANCY_PROMPT
11273 /* Set (but not export) PS1/2 unless already set */
11274 if (!get_local_var_value("PS1"))
11275 set_local_var_from_halves("PS1", "\\w \\$ ");
11276 if (!get_local_var_value("PS2"))
11277 set_local_var_from_halves("PS2", "> ");
11278#endif
11279 if (!ENABLE_FEATURE_SH_EXTRA_QUIET) {
11280 /* note: ash and hush share this string */
11281 printf("\n\n%s %s\n"
11282 IF_HUSH_HELP("Enter 'help' for a list of built-in commands.\n")
11283 "\n",
11284 bb_banner,
11285 "hush - the humble shell"
11286 );
11287 }
11288 }
11289
11290 parse_and_run_file(hfopen(NULL)); /* stdin */
11291
11292 final_return:
11293 hush_exit(G.last_exitcode);
11294}
11295
11296
Francis Laniel8197f012023-12-22 22:02:28 +010011297
Francis Lanielb234f7e2023-12-22 22:02:27 +010011298/*
11299 * Built-ins
11300 */
11301static int FAST_FUNC builtin_true(char **argv UNUSED_PARAM)
11302{
11303 return 0;
11304}
11305
11306#if ENABLE_HUSH_TEST || ENABLE_HUSH_ECHO || ENABLE_HUSH_PRINTF || ENABLE_HUSH_KILL
11307static NOINLINE int run_applet_main(char **argv, int (*applet_main_func)(int argc, char **argv))
11308{
11309 int argc = string_array_len(argv);
11310 return applet_main_func(argc, argv);
11311}
11312#endif
11313#if ENABLE_HUSH_TEST || BASH_TEST2
11314static int FAST_FUNC builtin_test(char **argv)
11315{
11316 return run_applet_main(argv, test_main);
11317}
11318#endif
11319#if ENABLE_HUSH_ECHO
11320static int FAST_FUNC builtin_echo(char **argv)
11321{
11322 return run_applet_main(argv, echo_main);
11323}
11324#endif
11325#if ENABLE_HUSH_PRINTF
11326static int FAST_FUNC builtin_printf(char **argv)
11327{
11328 return run_applet_main(argv, printf_main);
11329}
11330#endif
11331
11332#if ENABLE_HUSH_HELP
11333static int FAST_FUNC builtin_help(char **argv UNUSED_PARAM)
11334{
11335 const struct built_in_command *x;
11336
11337 printf(
11338 "Built-in commands:\n"
11339 "------------------\n");
11340 for (x = bltins1; x != &bltins1[ARRAY_SIZE(bltins1)]; x++) {
11341 if (x->b_descr)
11342 printf("%-10s%s\n", x->b_cmd, x->b_descr);
11343 }
11344 return EXIT_SUCCESS;
11345}
11346#endif
11347
11348#if MAX_HISTORY && ENABLE_FEATURE_EDITING
11349static int FAST_FUNC builtin_history(char **argv UNUSED_PARAM)
11350{
11351 show_history(G.line_input_state);
11352 return EXIT_SUCCESS;
11353}
11354#endif
11355
11356static char **skip_dash_dash(char **argv)
11357{
11358 argv++;
11359 if (argv[0] && argv[0][0] == '-' && argv[0][1] == '-' && argv[0][2] == '\0')
11360 argv++;
11361 return argv;
11362}
11363
11364static int FAST_FUNC builtin_cd(char **argv)
11365{
11366 const char *newdir;
11367
11368 argv = skip_dash_dash(argv);
11369 newdir = argv[0];
11370 if (newdir == NULL) {
11371 /* bash does nothing (exitcode 0) if HOME is ""; if it's unset,
11372 * bash says "bash: cd: HOME not set" and does nothing
11373 * (exitcode 1)
11374 */
11375 const char *home = get_local_var_value("HOME");
11376 newdir = home ? home : "/";
11377 }
11378 if (chdir(newdir)) {
11379 /* Mimic bash message exactly */
11380 bb_perror_msg("cd: %s", newdir);
11381 return EXIT_FAILURE;
11382 }
11383 /* Read current dir (get_cwd(1) is inside) and set PWD.
11384 * Note: do not enforce exporting. If PWD was unset or unexported,
11385 * set it again, but do not export. bash does the same.
11386 */
11387 set_pwd_var(/*flag:*/ 0);
11388 return EXIT_SUCCESS;
11389}
11390
11391static int FAST_FUNC builtin_pwd(char **argv UNUSED_PARAM)
11392{
11393 puts(get_cwd(0));
11394 return EXIT_SUCCESS;
11395}
11396
11397static int FAST_FUNC builtin_eval(char **argv)
11398{
11399 argv = skip_dash_dash(argv);
11400
11401 if (!argv[0])
11402 return EXIT_SUCCESS;
11403
11404 IF_HUSH_MODE_X(G.x_mode_depth++;)
11405 //bb_error_msg("%s: ++x_mode_depth=%d", __func__, G.x_mode_depth);
11406 if (!argv[1]) {
11407 /* bash:
11408 * eval "echo Hi; done" ("done" is syntax error):
11409 * "echo Hi" will not execute too.
11410 */
11411 parse_and_run_string(argv[0]);
11412 } else {
11413 /* "The eval utility shall construct a command by
11414 * concatenating arguments together, separating
11415 * each with a <space> character."
11416 */
11417 char *str, *p;
11418 unsigned len = 0;
11419 char **pp = argv;
11420 do
11421 len += strlen(*pp) + 1;
11422 while (*++pp);
11423 str = p = xmalloc(len);
11424 pp = argv;
11425 for (;;) {
11426 p = stpcpy(p, *pp);
11427 pp++;
11428 if (!*pp)
11429 break;
11430 *p++ = ' ';
11431 }
11432 parse_and_run_string(str);
11433 free(str);
11434 }
11435 IF_HUSH_MODE_X(G.x_mode_depth--;)
11436 //bb_error_msg("%s: --x_mode_depth=%d", __func__, G.x_mode_depth);
11437 return G.last_exitcode;
11438}
11439
11440static int FAST_FUNC builtin_exec(char **argv)
11441{
11442 argv = skip_dash_dash(argv);
11443 if (argv[0] == NULL)
11444 return EXIT_SUCCESS; /* bash does this */
11445
11446 /* Careful: we can end up here after [v]fork. Do not restore
11447 * tty pgrp then, only top-level shell process does that */
11448 if (G_saved_tty_pgrp && getpid() == G.root_pid)
11449 tcsetpgrp(G_interactive_fd, G_saved_tty_pgrp);
11450
11451 /* Saved-redirect fds, script fds and G_interactive_fd are still
11452 * open here. However, they are all CLOEXEC, and execv below
11453 * closes them. Try interactive "exec ls -l /proc/self/fd",
11454 * it should show no extra open fds in the "ls" process.
11455 * If we'd try to run builtins/NOEXECs, this would need improving.
11456 */
11457 //close_saved_fds_and_FILE_fds();
11458
11459 /* TODO: if exec fails, bash does NOT exit! We do.
11460 * We'll need to undo trap cleanup (it's inside execvp_or_die)
11461 * and tcsetpgrp, and this is inherently racy.
11462 */
11463 execvp_or_die(argv);
11464}
11465
11466static int FAST_FUNC builtin_exit(char **argv)
11467{
11468 debug_printf_exec("%s()\n", __func__);
11469
11470 /* interactive bash:
11471 * # trap "echo EEE" EXIT
11472 * # exit
11473 * exit
11474 * There are stopped jobs.
11475 * (if there are _stopped_ jobs, running ones don't count)
11476 * # exit
11477 * exit
11478 * EEE (then bash exits)
11479 *
11480 * TODO: we can use G.exiting = -1 as indicator "last cmd was exit"
11481 */
11482
11483 /* note: EXIT trap is run by hush_exit */
11484 argv = skip_dash_dash(argv);
11485 if (argv[0] == NULL) {
11486#if ENABLE_HUSH_TRAP
11487 if (G.pre_trap_exitcode >= 0) /* "exit" in trap uses $? from before the trap */
11488 hush_exit(G.pre_trap_exitcode);
11489#endif
11490 hush_exit(G.last_exitcode);
11491 }
11492 /* mimic bash: exit 123abc == exit 255 + error msg */
11493 xfunc_error_retval = 255;
11494 /* bash: exit -2 == exit 254, no error msg */
11495 hush_exit(xatoi(argv[0]) & 0xff);
11496}
11497
11498#if ENABLE_HUSH_TYPE
11499/* http://www.opengroup.org/onlinepubs/9699919799/utilities/type.html */
11500static int FAST_FUNC builtin_type(char **argv)
11501{
11502 int ret = EXIT_SUCCESS;
11503
11504 while (*++argv) {
11505 const char *type;
11506 char *path = NULL;
11507
11508 if (0) {} /* make conditional compile easier below */
11509 /*else if (find_alias(*argv))
11510 type = "an alias";*/
11511# if ENABLE_HUSH_FUNCTIONS
11512 else if (find_function(*argv))
11513 type = "a function";
11514# endif
11515 else if (find_builtin(*argv))
11516 type = "a shell builtin";
11517 else if ((path = find_in_path(*argv)) != NULL)
11518 type = path;
11519 else {
11520 bb_error_msg("type: %s: not found", *argv);
11521 ret = EXIT_FAILURE;
11522 continue;
11523 }
11524
11525 printf("%s is %s\n", *argv, type);
11526 free(path);
11527 }
11528
11529 return ret;
11530}
11531#endif
11532
11533#if ENABLE_HUSH_READ
11534/* Interruptibility of read builtin in bash
11535 * (tested on bash-4.2.8 by sending signals (not by ^C)):
11536 *
11537 * Empty trap makes read ignore corresponding signal, for any signal.
11538 *
11539 * SIGINT:
11540 * - terminates non-interactive shell;
11541 * - interrupts read in interactive shell;
11542 * if it has non-empty trap:
11543 * - executes trap and returns to command prompt in interactive shell;
11544 * - executes trap and returns to read in non-interactive shell;
11545 * SIGTERM:
11546 * - is ignored (does not interrupt) read in interactive shell;
11547 * - terminates non-interactive shell;
11548 * if it has non-empty trap:
11549 * - executes trap and returns to read;
11550 * SIGHUP:
11551 * - terminates shell (regardless of interactivity);
11552 * if it has non-empty trap:
11553 * - executes trap and returns to read;
11554 * SIGCHLD from children:
11555 * - does not interrupt read regardless of interactivity:
11556 * try: sleep 1 & read x; echo $x
11557 */
11558static int FAST_FUNC builtin_read(char **argv)
11559{
11560 const char *r;
11561 struct builtin_read_params params;
11562
11563 memset(&params, 0, sizeof(params));
11564
11565 /* "!": do not abort on errors.
11566 * Option string must start with "sr" to match BUILTIN_READ_xxx
11567 */
11568 params.read_flags = getopt32(argv,
11569# if BASH_READ_D
11570 IF_NOT_HUSH_BASH_COMPAT("^")
11571 "!srn:p:t:u:d:" IF_NOT_HUSH_BASH_COMPAT("\0" "-1"/*min 1 arg*/),
11572 &params.opt_n, &params.opt_p, &params.opt_t, &params.opt_u, &params.opt_d
11573# else
11574 IF_NOT_HUSH_BASH_COMPAT("^")
11575 "!srn:p:t:u:" IF_NOT_HUSH_BASH_COMPAT("\0" "-1"/*min 1 arg*/),
11576 &params.opt_n, &params.opt_p, &params.opt_t, &params.opt_u
11577# endif
11578//TODO: print "read: need variable name"
11579//for the case of !BASH "read" with no args (now it fails silently)
11580//(or maybe extend getopt32() to emit a message if "-1" fails)
11581 );
11582 if ((uint32_t)params.read_flags == (uint32_t)-1)
11583 return EXIT_FAILURE;
11584 argv += optind;
11585 params.argv = argv;
11586 params.setvar = set_local_var_from_halves;
11587 params.ifs = get_local_var_value("IFS"); /* can be NULL */
11588
11589 again:
11590 r = shell_builtin_read(&params);
11591
11592 if ((uintptr_t)r == 1 && errno == EINTR) {
11593 unsigned sig = check_and_run_traps();
11594 if (sig != SIGINT)
11595 goto again;
11596 }
11597
11598 if ((uintptr_t)r > 1) {
11599 bb_simple_error_msg(r);
11600 r = (char*)(uintptr_t)1;
11601 }
11602
11603 return (uintptr_t)r;
11604}
11605#endif
11606
11607#if ENABLE_HUSH_UMASK
11608static int FAST_FUNC builtin_umask(char **argv)
11609{
11610 int rc;
11611 mode_t mask;
11612
11613 rc = 1;
11614 mask = umask(0);
11615 argv = skip_dash_dash(argv);
11616 if (argv[0]) {
11617 mode_t old_mask = mask;
11618
11619 /* numeric umasks are taken as-is */
11620 /* symbolic umasks are inverted: "umask a=rx" calls umask(222) */
11621 if (!isdigit(argv[0][0]))
11622 mask ^= 0777;
11623 mask = bb_parse_mode(argv[0], mask);
11624 if (!isdigit(argv[0][0]))
11625 mask ^= 0777;
11626 if ((unsigned)mask > 0777) {
11627 mask = old_mask;
11628 /* bash messages:
11629 * bash: umask: 'q': invalid symbolic mode operator
11630 * bash: umask: 999: octal number out of range
11631 */
11632 bb_error_msg("%s: invalid mode '%s'", "umask", argv[0]);
11633 rc = 0;
11634 }
11635 } else {
11636 /* Mimic bash */
11637 printf("%04o\n", (unsigned) mask);
11638 /* fall through and restore mask which we set to 0 */
11639 }
11640 umask(mask);
11641
11642 return !rc; /* rc != 0 - success */
11643}
11644#endif
11645
11646#if ENABLE_HUSH_EXPORT || ENABLE_HUSH_TRAP
11647static void print_escaped(const char *s)
11648{
11649 if (*s == '\'')
11650 goto squote;
11651 do {
11652 const char *p = strchrnul(s, '\'');
11653 /* print 'xxxx', possibly just '' */
11654 printf("'%.*s'", (int)(p - s), s);
11655 if (*p == '\0')
11656 break;
11657 s = p;
11658 squote:
11659 /* s points to '; print "'''...'''" */
11660 putchar('"');
11661 do putchar('\''); while (*++s == '\'');
11662 putchar('"');
11663 } while (*s);
11664}
11665#endif
11666
11667#if ENABLE_HUSH_EXPORT || ENABLE_HUSH_LOCAL || ENABLE_HUSH_READONLY
11668static int helper_export_local(char **argv, unsigned flags)
11669{
11670 do {
11671 char *name = *argv;
11672 const char *name_end = endofname(name);
11673
11674 if (*name_end == '\0') {
11675 struct variable *var, **vpp;
11676
11677 vpp = get_ptr_to_local_var(name, name_end - name);
11678 var = vpp ? *vpp : NULL;
11679
11680 if (flags & SETFLAG_UNEXPORT) {
11681 /* export -n NAME (without =VALUE) */
11682 if (var) {
11683 var->flg_export = 0;
11684 debug_printf_env("%s: unsetenv '%s'\n", __func__, name);
11685 unsetenv(name);
11686 } /* else: export -n NOT_EXISTING_VAR: no-op */
11687 continue;
11688 }
11689 if (flags & SETFLAG_EXPORT) {
11690 /* export NAME (without =VALUE) */
11691 if (var) {
11692 var->flg_export = 1;
11693 debug_printf_env("%s: putenv '%s'\n", __func__, var->varstr);
11694 putenv(var->varstr);
11695 continue;
11696 }
11697 }
11698 if (flags & SETFLAG_MAKE_RO) {
11699 /* readonly NAME (without =VALUE) */
11700 if (var) {
11701 var->flg_read_only = 1;
11702 continue;
11703 }
11704 }
11705# if ENABLE_HUSH_LOCAL
11706 /* Is this "local" bltin? */
11707 if (!(flags & (SETFLAG_EXPORT|SETFLAG_UNEXPORT|SETFLAG_MAKE_RO))) {
11708 unsigned lvl = flags >> SETFLAG_VARLVL_SHIFT;
11709 if (var && var->var_nest_level == lvl) {
11710 /* "local x=abc; ...; local x" - ignore second local decl */
11711 continue;
11712 }
11713 }
11714# endif
11715 /* Exporting non-existing variable.
11716 * bash does not put it in environment,
11717 * but remembers that it is exported,
11718 * and does put it in env when it is set later.
11719 * We just set it to "" and export.
11720 */
11721 /* Or, it's "local NAME" (without =VALUE).
11722 * bash sets the value to "".
11723 */
11724 /* Or, it's "readonly NAME" (without =VALUE).
11725 * bash remembers NAME and disallows its creation
11726 * in the future.
11727 */
11728 name = xasprintf("%s=", name);
11729 } else {
11730 if (*name_end != '=') {
11731 bb_error_msg("'%s': bad variable name", name);
11732 /* do not parse following argv[]s: */
11733 return 1;
11734 }
11735 /* (Un)exporting/making local NAME=VALUE */
11736 name = xstrdup(name);
11737 /* Testcase: export PS1='\w \$ ' */
11738 unbackslash(name);
11739 }
11740 debug_printf_env("%s: set_local_var('%s')\n", __func__, name);
11741 if (set_local_var(name, flags))
11742 return EXIT_FAILURE;
11743 } while (*++argv);
11744 return EXIT_SUCCESS;
11745}
11746#endif
11747
11748#if ENABLE_HUSH_EXPORT
11749static int FAST_FUNC builtin_export(char **argv)
11750{
11751 unsigned opt_unexport;
11752
11753# if ENABLE_HUSH_EXPORT_N
11754 /* "!": do not abort on errors */
11755 opt_unexport = getopt32(argv, "!n");
11756 if (opt_unexport == (uint32_t)-1)
11757 return EXIT_FAILURE;
11758 argv += optind;
11759# else
11760 opt_unexport = 0;
11761 argv++;
11762# endif
11763
11764 if (argv[0] == NULL) {
11765 char **e = environ;
11766 if (e) {
11767 while (*e) {
11768# if 0
11769 puts(*e++);
11770# else
11771 /* ash emits: export VAR='VAL'
11772 * bash: declare -x VAR="VAL"
11773 * we follow ash example */
11774 const char *s = *e++;
11775 const char *p = strchr(s, '=');
11776
11777 if (!p) /* wtf? take next variable */
11778 continue;
11779 /* export var= */
11780 printf("export %.*s", (int)(p - s) + 1, s);
11781 print_escaped(p + 1);
11782 putchar('\n');
11783# endif
11784 }
11785 /*fflush_all(); - done after each builtin anyway */
11786 }
11787 return EXIT_SUCCESS;
11788 }
11789
11790 return helper_export_local(argv, opt_unexport ? SETFLAG_UNEXPORT : SETFLAG_EXPORT);
11791}
11792#endif
11793
11794#if ENABLE_HUSH_LOCAL
11795static int FAST_FUNC builtin_local(char **argv)
11796{
11797 if (G.func_nest_level == 0) {
11798 bb_error_msg("%s: not in a function", argv[0]);
11799 return EXIT_FAILURE; /* bash compat */
11800 }
11801 argv++;
11802 /* Since all builtins run in a nested variable level,
11803 * need to use level - 1 here. Or else the variable will be removed at once
11804 * after builtin returns.
11805 */
11806 return helper_export_local(argv, (G.var_nest_level - 1) << SETFLAG_VARLVL_SHIFT);
11807}
11808#endif
11809
11810#if ENABLE_HUSH_READONLY
11811static int FAST_FUNC builtin_readonly(char **argv)
11812{
11813 argv++;
11814 if (*argv == NULL) {
11815 /* bash: readonly [-p]: list all readonly VARs
11816 * (-p has no effect in bash)
11817 */
11818 struct variable *e;
11819 for (e = G.top_var; e; e = e->next) {
11820 if (e->flg_read_only) {
11821//TODO: quote value: readonly VAR='VAL'
11822 printf("readonly %s\n", e->varstr);
11823 }
11824 }
11825 return EXIT_SUCCESS;
11826 }
11827 return helper_export_local(argv, SETFLAG_MAKE_RO);
11828}
11829#endif
11830
11831#if ENABLE_HUSH_UNSET
11832/* http://www.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#unset */
11833static int FAST_FUNC builtin_unset(char **argv)
11834{
11835 int ret;
11836 unsigned opts;
11837
11838 /* "!": do not abort on errors */
11839 /* "+": stop at 1st non-option */
11840 opts = getopt32(argv, "!+vf");
11841 if (opts == (unsigned)-1)
11842 return EXIT_FAILURE;
11843 if (opts == 3) {
11844 bb_simple_error_msg("unset: -v and -f are exclusive");
11845 return EXIT_FAILURE;
11846 }
11847 argv += optind;
11848
11849 ret = EXIT_SUCCESS;
11850 while (*argv) {
11851 if (!(opts & 2)) { /* not -f */
11852 if (unset_local_var(*argv)) {
11853 /* unset <nonexistent_var> doesn't fail.
11854 * Error is when one tries to unset RO var.
11855 * Message was printed by unset_local_var. */
11856 ret = EXIT_FAILURE;
11857 }
11858 }
11859# if ENABLE_HUSH_FUNCTIONS
11860 else {
11861 unset_func(*argv);
11862 }
11863# endif
11864 argv++;
11865 }
11866 return ret;
11867}
11868#endif
11869
11870#if ENABLE_HUSH_SET
11871/* http://www.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#set
11872 * built-in 'set' handler
11873 * SUSv3 says:
11874 * set [-abCefhmnuvx] [-o option] [argument...]
11875 * set [+abCefhmnuvx] [+o option] [argument...]
11876 * set -- [argument...]
11877 * set -o
11878 * set +o
11879 * Implementations shall support the options in both their hyphen and
11880 * plus-sign forms. These options can also be specified as options to sh.
11881 * Examples:
11882 * Write out all variables and their values: set
11883 * Set $1, $2, and $3 and set "$#" to 3: set c a b
11884 * Turn on the -x and -v options: set -xv
11885 * Unset all positional parameters: set --
11886 * Set $1 to the value of x, even if it begins with '-' or '+': set -- "$x"
11887 * Set the positional parameters to the expansion of x, even if x expands
11888 * with a leading '-' or '+': set -- $x
11889 *
11890 * So far, we only support "set -- [argument...]" and some of the short names.
11891 */
11892static int FAST_FUNC builtin_set(char **argv)
11893{
11894 int n;
11895 char **pp, **g_argv;
11896 char *arg = *++argv;
11897
11898 if (arg == NULL) {
11899 struct variable *e;
11900 for (e = G.top_var; e; e = e->next)
11901 puts(e->varstr);
11902 return EXIT_SUCCESS;
11903 }
11904
11905 do {
11906 if (strcmp(arg, "--") == 0) {
11907 ++argv;
11908 goto set_argv;
11909 }
11910 if (arg[0] != '+' && arg[0] != '-')
11911 break;
11912 for (n = 1; arg[n]; ++n) {
11913 if (set_mode((arg[0] == '-'), arg[n], argv[1])) {
11914 bb_error_msg("%s: %s: invalid option", "set", arg);
11915 return EXIT_FAILURE;
11916 }
11917 if (arg[n] == 'o' && argv[1])
11918 argv++;
11919 }
11920 } while ((arg = *++argv) != NULL);
11921 /* Now argv[0] is 1st argument */
11922
11923 if (arg == NULL)
11924 return EXIT_SUCCESS;
11925 set_argv:
11926
11927 /* NB: G.global_argv[0] ($0) is never freed/changed */
11928 g_argv = G.global_argv;
11929 if (G.global_args_malloced) {
11930 pp = g_argv;
11931 while (*++pp)
11932 free(*pp);
11933 g_argv[1] = NULL;
11934 } else {
11935 G.global_args_malloced = 1;
11936 pp = xzalloc(sizeof(pp[0]) * 2);
11937 pp[0] = g_argv[0]; /* retain $0 */
11938 g_argv = pp;
11939 }
11940 /* This realloc's G.global_argv */
11941 G.global_argv = pp = add_strings_to_strings(g_argv, argv, /*dup:*/ 1);
11942
11943 G.global_argc = 1 + string_array_len(pp + 1);
11944
11945 return EXIT_SUCCESS;
11946}
11947#endif
11948
11949static int FAST_FUNC builtin_shift(char **argv)
11950{
11951 int n = 1;
11952 argv = skip_dash_dash(argv);
11953 if (argv[0]) {
11954 n = bb_strtou(argv[0], NULL, 10);
11955 if (errno || n < 0) {
11956 /* shared string with ash.c */
11957 bb_error_msg("Illegal number: %s", argv[0]);
11958 /*
11959 * ash aborts in this case.
11960 * bash prints error message and set $? to 1.
11961 * Interestingly, for "shift 99999" bash does not
11962 * print error message, but does set $? to 1
11963 * (and does no shifting at all).
11964 */
11965 }
11966 }
11967 if (n >= 0 && n < G.global_argc) {
11968 if (G_global_args_malloced) {
11969 int m = 1;
11970 while (m <= n)
11971 free(G.global_argv[m++]);
11972 }
11973 G.global_argc -= n;
11974 memmove(&G.global_argv[1], &G.global_argv[n+1],
11975 G.global_argc * sizeof(G.global_argv[0]));
11976 return EXIT_SUCCESS;
11977 }
11978 return EXIT_FAILURE;
11979}
11980
11981#if ENABLE_HUSH_GETOPTS
11982static int FAST_FUNC builtin_getopts(char **argv)
11983{
11984/* http://pubs.opengroup.org/onlinepubs/9699919799/utilities/getopts.html
11985
11986TODO:
11987If a required argument is not found, and getopts is not silent,
11988a question mark (?) is placed in VAR, OPTARG is unset, and a
11989diagnostic message is printed. If getopts is silent, then a
11990colon (:) is placed in VAR and OPTARG is set to the option
11991character found.
11992
11993Test that VAR is a valid variable name?
11994
11995"Whenever the shell is invoked, OPTIND shall be initialized to 1"
11996*/
11997 char cbuf[2];
11998 const char *cp, *optstring, *var;
11999 int c, n, exitcode, my_opterr;
12000 unsigned count;
12001
12002 optstring = *++argv;
12003 if (!optstring || !(var = *++argv)) {
12004 bb_simple_error_msg("usage: getopts OPTSTRING VAR [ARGS]");
12005 return EXIT_FAILURE;
12006 }
12007
12008 if (argv[1])
12009 argv[0] = G.global_argv[0]; /* for error messages in getopt() */
12010 else
12011 argv = G.global_argv;
12012 cbuf[1] = '\0';
12013
12014 my_opterr = 0;
12015 if (optstring[0] != ':') {
12016 cp = get_local_var_value("OPTERR");
12017 /* 0 if "OPTERR=0", 1 otherwise */
12018 my_opterr = (!cp || NOT_LONE_CHAR(cp, '0'));
12019 }
12020
12021 /* getopts stops on first non-option. Add "+" to force that */
12022 /*if (optstring[0] != '+')*/ {
12023 char *s = alloca(strlen(optstring) + 2);
12024 sprintf(s, "+%s", optstring);
12025 optstring = s;
12026 }
12027
12028 /* Naively, now we should just
12029 * cp = get_local_var_value("OPTIND");
12030 * optind = cp ? atoi(cp) : 0;
12031 * optarg = NULL;
12032 * opterr = my_opterr;
12033 * c = getopt(string_array_len(argv), argv, optstring);
12034 * and be done? Not so fast...
12035 * Unlike normal getopt() usage in C programs, here
12036 * each successive call will (usually) have the same argv[] CONTENTS,
12037 * but not the ADDRESSES. Worse yet, it's possible that between
12038 * invocations of "getopts", there will be calls to shell builtins
12039 * which use getopt() internally. Example:
12040 * while getopts "abc" RES -a -bc -abc de; do
12041 * unset -ff func
12042 * done
12043 * This would not work correctly: getopt() call inside "unset"
12044 * modifies internal libc state which is tracking position in
12045 * multi-option strings ("-abc"). At best, it can skip options
12046 * or return the same option infinitely. With glibc implementation
12047 * of getopt(), it would use outright invalid pointers and return
12048 * garbage even _without_ "unset" mangling internal state.
12049 *
12050 * We resort to resetting getopt() state and calling it N times,
12051 * until we get Nth result (or failure).
12052 * (N == G.getopt_count is reset to 0 whenever OPTIND is [un]set).
12053 */
12054 GETOPT_RESET();
12055 count = 0;
12056 n = string_array_len(argv);
12057 do {
12058 optarg = NULL;
12059 opterr = (count < G.getopt_count) ? 0 : my_opterr;
12060 c = getopt(n, argv, optstring);
12061 if (c < 0)
12062 break;
12063 count++;
12064 } while (count <= G.getopt_count);
12065
12066 /* Set OPTIND. Prevent resetting of the magic counter! */
12067 set_local_var_from_halves("OPTIND", utoa(optind));
12068 G.getopt_count = count; /* "next time, give me N+1'th result" */
12069 GETOPT_RESET(); /* just in case */
12070
12071 /* Set OPTARG */
12072 /* Always set or unset, never left as-is, even on exit/error:
12073 * "If no option was found, or if the option that was found
12074 * does not have an option-argument, OPTARG shall be unset."
12075 */
12076 cp = optarg;
12077 if (c == '?') {
12078 /* If ":optstring" and unknown option is seen,
12079 * it is stored to OPTARG.
12080 */
12081 if (optstring[1] == ':') {
12082 cbuf[0] = optopt;
12083 cp = cbuf;
12084 }
12085 }
12086 if (cp)
12087 set_local_var_from_halves("OPTARG", cp);
12088 else
12089 unset_local_var("OPTARG");
12090
12091 /* Convert -1 to "?" */
12092 exitcode = EXIT_SUCCESS;
12093 if (c < 0) { /* -1: end of options */
12094 exitcode = EXIT_FAILURE;
12095 c = '?';
12096 }
12097
12098 /* Set VAR */
12099 cbuf[0] = c;
12100 set_local_var_from_halves(var, cbuf);
12101
12102 return exitcode;
12103}
12104#endif
12105
12106static int FAST_FUNC builtin_source(char **argv)
12107{
12108 char *arg_path, *filename;
12109 HFILE *input;
12110 save_arg_t sv;
12111 char *args_need_save;
12112#if ENABLE_HUSH_FUNCTIONS
12113 smallint sv_flg;
12114#endif
12115
12116 argv = skip_dash_dash(argv);
12117 filename = argv[0];
12118 if (!filename) {
12119 /* bash says: "bash: .: filename argument required" */
12120 return 2; /* bash compat */
12121 }
12122 arg_path = NULL;
12123 if (!strchr(filename, '/')) {
12124 arg_path = find_in_path(filename);
12125 if (arg_path)
12126 filename = arg_path;
12127 else if (!ENABLE_HUSH_BASH_SOURCE_CURDIR) {
12128 errno = ENOENT;
12129 bb_simple_perror_msg(filename);
12130 return EXIT_FAILURE;
12131 }
12132 }
12133 input = hfopen(filename);
12134 free(arg_path);
12135 if (!input) {
12136 bb_perror_msg("%s", filename);
12137 /* POSIX: non-interactive shell should abort here,
12138 * not merely fail. So far no one complained :)
12139 */
12140 return EXIT_FAILURE;
12141 }
12142
12143#if ENABLE_HUSH_FUNCTIONS
12144 sv_flg = G_flag_return_in_progress;
12145 /* "we are inside sourced file, ok to use return" */
12146 G_flag_return_in_progress = -1;
12147#endif
12148 args_need_save = argv[1]; /* used as a boolean variable */
12149 if (args_need_save)
12150 save_and_replace_G_args(&sv, argv);
12151
12152 /* "false; . ./empty_line; echo Zero:$?" should print 0 */
12153 G.last_exitcode = 0;
12154 parse_and_run_file(input);
12155 hfclose(input);
12156
12157 if (args_need_save) /* can't use argv[1] instead: "shift" can mangle it */
12158 restore_G_args(&sv, argv);
12159#if ENABLE_HUSH_FUNCTIONS
12160 G_flag_return_in_progress = sv_flg;
12161#endif
12162
12163 return G.last_exitcode;
12164}
12165
12166#if ENABLE_HUSH_TRAP
12167static int FAST_FUNC builtin_trap(char **argv)
12168{
12169 int sig;
12170 char *new_cmd;
12171
12172 if (!G_traps)
12173 G_traps = xzalloc(sizeof(G_traps[0]) * NSIG);
12174
12175 argv++;
12176 if (!*argv) {
12177 int i;
12178 /* No args: print all trapped */
12179 for (i = 0; i < NSIG; ++i) {
12180 if (G_traps[i]) {
12181 printf("trap -- ");
12182 print_escaped(G_traps[i]);
12183 /* note: bash adds "SIG", but only if invoked
12184 * as "bash". If called as "sh", or if set -o posix,
12185 * then it prints short signal names.
12186 * We are printing short names: */
12187 printf(" %s\n", get_signame(i));
12188 }
12189 }
12190 /*fflush_all(); - done after each builtin anyway */
12191 return EXIT_SUCCESS;
12192 }
12193
12194 new_cmd = NULL;
12195 /* If first arg is a number: reset all specified signals */
12196 sig = bb_strtou(*argv, NULL, 10);
12197 if (errno == 0) {
12198 int ret;
12199 process_sig_list:
12200 ret = EXIT_SUCCESS;
12201 while (*argv) {
12202 sighandler_t handler;
12203
12204 sig = get_signum(*argv++);
12205 if (sig < 0) {
12206 ret = EXIT_FAILURE;
12207 /* Mimic bash message exactly */
12208 bb_error_msg("trap: %s: invalid signal specification", argv[-1]);
12209 continue;
12210 }
12211
12212 free(G_traps[sig]);
12213 G_traps[sig] = xstrdup(new_cmd);
12214
12215 debug_printf("trap: setting SIG%s (%i) to '%s'\n",
12216 get_signame(sig), sig, G_traps[sig]);
12217
12218 /* There is no signal for 0 (EXIT) */
12219 if (sig == 0)
12220 continue;
12221
12222 if (new_cmd)
12223 handler = (new_cmd[0] ? record_pending_signo : SIG_IGN);
12224 else
12225 /* We are removing trap handler */
12226 handler = pick_sighandler(sig);
12227 install_sighandler(sig, handler);
12228 }
12229 return ret;
12230 }
12231
12232 if (!argv[1]) { /* no second arg */
12233 bb_simple_error_msg("trap: invalid arguments");
12234 return EXIT_FAILURE;
12235 }
12236
12237 /* First arg is "-": reset all specified to default */
12238 /* First arg is "--": skip it, the rest is "handler SIGs..." */
12239 /* Everything else: set arg as signal handler
12240 * (includes "" case, which ignores signal) */
12241 if (argv[0][0] == '-') {
12242 if (argv[0][1] == '\0') { /* "-" */
12243 /* new_cmd remains NULL: "reset these sigs" */
12244 goto reset_traps;
12245 }
12246 if (argv[0][1] == '-' && argv[0][2] == '\0') { /* "--" */
12247 argv++;
12248 }
12249 /* else: "-something", no special meaning */
12250 }
12251 new_cmd = *argv;
12252 reset_traps:
12253 argv++;
12254 goto process_sig_list;
12255}
12256#endif
12257
12258#if ENABLE_HUSH_JOB
12259static struct pipe *parse_jobspec(const char *str)
12260{
12261 struct pipe *pi;
12262 unsigned jobnum;
12263
12264 if (sscanf(str, "%%%u", &jobnum) != 1) {
12265 if (str[0] != '%'
12266 || (str[1] != '%' && str[1] != '+' && str[1] != '\0')
12267 ) {
12268 bb_error_msg("bad argument '%s'", str);
12269 return NULL;
12270 }
12271 /* It is "%%", "%+" or "%" - current job */
12272 jobnum = G.last_jobid;
12273 if (jobnum == 0) {
12274 bb_simple_error_msg("no current job");
12275 return NULL;
12276 }
12277 }
12278 for (pi = G.job_list; pi; pi = pi->next) {
12279 if (pi->jobid == jobnum) {
12280 return pi;
12281 }
12282 }
12283 bb_error_msg("%u: no such job", jobnum);
12284 return NULL;
12285}
12286
12287static int FAST_FUNC builtin_jobs(char **argv UNUSED_PARAM)
12288{
12289 struct pipe *job;
12290 const char *status_string;
12291
12292 checkjobs(NULL, 0 /*(no pid to wait for)*/);
12293 for (job = G.job_list; job; job = job->next) {
12294 if (job->alive_cmds == job->stopped_cmds)
12295 status_string = "Stopped";
12296 else
12297 status_string = "Running";
12298
12299 printf(JOB_STATUS_FORMAT, job->jobid, status_string, job->cmdtext);
12300 }
12301
12302 clean_up_last_dead_job();
12303
12304 return EXIT_SUCCESS;
12305}
12306
12307/* built-in 'fg' and 'bg' handler */
12308static int FAST_FUNC builtin_fg_bg(char **argv)
12309{
12310 int i;
12311 struct pipe *pi;
12312
12313 if (!G_interactive_fd)
12314 return EXIT_FAILURE;
12315
12316 /* If they gave us no args, assume they want the last backgrounded task */
12317 if (!argv[1]) {
12318 for (pi = G.job_list; pi; pi = pi->next) {
12319 if (pi->jobid == G.last_jobid) {
12320 goto found;
12321 }
12322 }
12323 bb_error_msg("%s: no current job", argv[0]);
12324 return EXIT_FAILURE;
12325 }
12326
12327 pi = parse_jobspec(argv[1]);
12328 if (!pi)
12329 return EXIT_FAILURE;
12330 found:
12331 /* TODO: bash prints a string representation
12332 * of job being foregrounded (like "sleep 1 | cat") */
12333 if (argv[0][0] == 'f' && G_saved_tty_pgrp) {
12334 /* Put the job into the foreground. */
12335 tcsetpgrp(G_interactive_fd, pi->pgrp);
12336 }
12337
12338 /* Restart the processes in the job */
12339 debug_printf_jobs("reviving %d procs, pgrp %d\n", pi->num_cmds, pi->pgrp);
12340 for (i = 0; i < pi->num_cmds; i++) {
12341 debug_printf_jobs("reviving pid %d\n", pi->cmds[i].pid);
12342 }
12343 pi->stopped_cmds = 0;
12344
12345 i = kill(- pi->pgrp, SIGCONT);
12346 if (i < 0) {
12347 if (errno == ESRCH) {
12348 delete_finished_job(pi);
12349 return EXIT_SUCCESS;
12350 }
12351 bb_simple_perror_msg("kill (SIGCONT)");
12352 }
12353
12354 if (argv[0][0] == 'f') {
12355 remove_job_from_table(pi); /* FG job shouldn't be in job table */
12356 return checkjobs_and_fg_shell(pi);
12357 }
12358 return EXIT_SUCCESS;
12359}
12360#endif
12361
12362#if ENABLE_HUSH_KILL
12363static int FAST_FUNC builtin_kill(char **argv)
12364{
12365 int ret = 0;
12366
12367# if ENABLE_HUSH_JOB
12368 if (argv[1] && strcmp(argv[1], "-l") != 0) {
12369 int i = 1;
12370
12371 do {
12372 struct pipe *pi;
12373 char *dst;
12374 int j, n;
12375
12376 if (argv[i][0] != '%')
12377 continue;
12378 /*
12379 * "kill %N" - job kill
12380 * Converting to pgrp / pid kill
12381 */
12382 pi = parse_jobspec(argv[i]);
12383 if (!pi) {
12384 /* Eat bad jobspec */
12385 j = i;
12386 do {
12387 j++;
12388 argv[j - 1] = argv[j];
12389 } while (argv[j]);
12390 ret = 1;
12391 i--;
12392 continue;
12393 }
12394 /*
12395 * In jobs started under job control, we signal
12396 * entire process group by kill -PGRP_ID.
12397 * This happens, f.e., in interactive shell.
12398 *
12399 * Otherwise, we signal each child via
12400 * kill PID1 PID2 PID3.
12401 * Testcases:
12402 * sh -c 'sleep 1|sleep 1 & kill %1'
12403 * sh -c 'true|sleep 2 & sleep 1; kill %1'
12404 * sh -c 'true|sleep 1 & sleep 2; kill %1'
12405 */
12406 n = G_interactive_fd ? 1 : pi->num_cmds;
12407 dst = alloca(n * sizeof(int)*4);
12408 argv[i] = dst;
12409 if (G_interactive_fd)
12410 dst += sprintf(dst, " -%u", (int)pi->pgrp);
12411 else for (j = 0; j < n; j++) {
12412 struct command *cmd = &pi->cmds[j];
12413 /* Skip exited members of the job */
12414 if (cmd->pid == 0)
12415 continue;
12416 /*
12417 * kill_main has matching code to expect
12418 * leading space. Needed to not confuse
12419 * negative pids with "kill -SIGNAL_NO" syntax
12420 */
12421 dst += sprintf(dst, " %u", (int)cmd->pid);
12422 }
12423 *dst = '\0';
12424 } while (argv[++i]);
12425 }
12426# endif
12427
12428 if (argv[1] || ret == 0) {
12429 ret = run_applet_main(argv, kill_main);
12430 }
12431 /* else: ret = 1, "kill %bad_jobspec" case */
12432 return ret;
12433}
12434#endif
12435
12436#if ENABLE_HUSH_WAIT
12437/* http://www.opengroup.org/onlinepubs/9699919799/utilities/wait.html */
12438# if !ENABLE_HUSH_JOB
12439# define wait_for_child_or_signal(pipe,pid) wait_for_child_or_signal(pid)
12440# endif
12441static int wait_for_child_or_signal(struct pipe *waitfor_pipe, pid_t waitfor_pid)
12442{
12443 int ret = 0;
12444 for (;;) {
12445 int sig;
12446 sigset_t oldset;
12447
12448 if (!sigisemptyset(&G.pending_set))
12449 goto check_sig;
12450
12451 /* waitpid is not interruptible by SA_RESTARTed
12452 * signals which we use. Thus, this ugly dance:
12453 */
12454
12455 /* Make sure possible SIGCHLD is stored in kernel's
12456 * pending signal mask before we call waitpid.
12457 * Or else we may race with SIGCHLD, lose it,
12458 * and get stuck in sigsuspend...
12459 */
12460 sigfillset(&oldset); /* block all signals, remember old set */
12461 sigprocmask2(SIG_SETMASK, &oldset);
12462
12463 if (!sigisemptyset(&G.pending_set)) {
12464 /* Crap! we raced with some signal! */
12465 goto restore;
12466 }
12467
12468 /*errno = 0; - checkjobs does this */
12469/* Can't pass waitfor_pipe into checkjobs(): it won't be interruptible */
12470 ret = checkjobs(NULL, waitfor_pid); /* waitpid(WNOHANG) inside */
12471 debug_printf_exec("checkjobs:%d\n", ret);
12472# if ENABLE_HUSH_JOB
12473 if (waitfor_pipe) {
12474 int rcode = job_exited_or_stopped(waitfor_pipe);
12475 debug_printf_exec("job_exited_or_stopped:%d\n", rcode);
12476 if (rcode >= 0) {
12477 ret = rcode;
12478 sigprocmask(SIG_SETMASK, &oldset, NULL);
12479 break;
12480 }
12481 }
12482# endif
12483 /* if ECHILD, there are no children (ret is -1 or 0) */
12484 /* if ret == 0, no children changed state */
12485 /* if ret != 0, it's exitcode+1 of exited waitfor_pid child */
12486 if (errno == ECHILD || ret) {
12487 ret--;
12488 if (ret < 0) /* if ECHILD, may need to fix "ret" */
12489 ret = 0;
12490# if ENABLE_HUSH_BASH_COMPAT
12491 if (waitfor_pid == -1 && errno == ECHILD) {
12492 /* exitcode of "wait -n" with no children is 127, not 0 */
12493 ret = 127;
12494 }
12495# endif
12496 sigprocmask(SIG_SETMASK, &oldset, NULL);
12497 break;
12498 }
12499 /* Wait for SIGCHLD or any other signal */
12500 /* It is vitally important for sigsuspend that SIGCHLD has non-DFL handler! */
12501 /* Note: sigsuspend invokes signal handler */
12502 sigsuspend(&oldset);
12503 /* ^^^ add "sigdelset(&oldset, SIGCHLD)" before sigsuspend
12504 * to make sure SIGCHLD is not masked off?
12505 * It was reported that this:
12506 * fn() { : | return; }
12507 * shopt -s lastpipe
12508 * fn
12509 * exec hush SCRIPT
12510 * under bash 4.4.23 runs SCRIPT with SIGCHLD masked,
12511 * making "wait" commands in SCRIPT block forever.
12512 */
12513 restore:
12514 sigprocmask(SIG_SETMASK, &oldset, NULL);
12515 check_sig:
12516 /* So, did we get a signal? */
12517 sig = check_and_run_traps();
12518 if (sig /*&& sig != SIGCHLD - always true */) {
12519 /* Do this for any (non-ignored) signal, not only for ^C */
12520 ret = 128 | sig;
12521 break;
12522 }
12523 /* SIGCHLD, or no signal, or ignored one, such as SIGQUIT. Repeat */
12524 }
12525 return ret;
12526}
12527
12528static int FAST_FUNC builtin_wait(char **argv)
12529{
12530 int ret;
12531 int status;
12532
12533 argv = skip_dash_dash(argv);
12534# if ENABLE_HUSH_BASH_COMPAT
12535 if (argv[0] && strcmp(argv[0], "-n") == 0) {
12536 /* wait -n */
12537 /* (bash accepts "wait -n PID" too and ignores PID) */
12538 G.dead_job_exitcode = -1;
12539 return wait_for_child_or_signal(NULL, -1 /*no job, wait for one job*/);
12540 }
12541# endif
12542 if (argv[0] == NULL) {
12543 /* Don't care about wait results */
12544 /* Note 1: must wait until there are no more children */
12545 /* Note 2: must be interruptible */
12546 /* Examples:
12547 * $ sleep 3 & sleep 6 & wait
12548 * [1] 30934 sleep 3
12549 * [2] 30935 sleep 6
12550 * [1] Done sleep 3
12551 * [2] Done sleep 6
12552 * $ sleep 3 & sleep 6 & wait
12553 * [1] 30936 sleep 3
12554 * [2] 30937 sleep 6
12555 * [1] Done sleep 3
12556 * ^C <-- after ~4 sec from keyboard
12557 * $
12558 */
12559 return wait_for_child_or_signal(NULL, 0 /*no job and no pid to wait for*/);
12560 }
12561
12562 do {
12563 pid_t pid = bb_strtou(*argv, NULL, 10);
12564 if (errno || pid <= 0) {
12565# if ENABLE_HUSH_JOB
12566 if (argv[0][0] == '%') {
12567 struct pipe *wait_pipe;
12568 ret = 127; /* bash compat for bad jobspecs */
12569 wait_pipe = parse_jobspec(*argv);
12570 if (wait_pipe) {
12571 ret = job_exited_or_stopped(wait_pipe);
12572 if (ret < 0) {
12573 ret = wait_for_child_or_signal(wait_pipe, 0);
12574 } else {
12575 /* waiting on "last dead job" removes it */
12576 clean_up_last_dead_job();
12577 }
12578 }
12579 /* else: parse_jobspec() already emitted error msg */
12580 continue;
12581 }
12582# endif
12583 /* mimic bash message */
12584 bb_error_msg("wait: '%s': not a pid or valid job spec", *argv);
12585 ret = EXIT_FAILURE;
12586 continue; /* bash checks all argv[] */
12587 }
12588
12589 /* Do we have such child? */
12590 ret = waitpid(pid, &status, WNOHANG);
12591 if (ret < 0) {
12592 /* No */
12593 ret = 127;
12594 if (errno == ECHILD) {
12595 if (pid == G.last_bg_pid) {
12596 /* "wait $!" but last bg task has already exited. Try:
12597 * (sleep 1; exit 3) & sleep 2; echo $?; wait $!; echo $?
12598 * In bash it prints exitcode 0, then 3.
12599 * In dash, it is 127.
12600 */
12601 ret = G.last_bg_pid_exitcode;
12602 } else {
12603 /* Example: "wait 1". mimic bash message */
12604 bb_error_msg("wait: pid %u is not a child of this shell", (unsigned)pid);
12605 }
12606 } else {
12607 /* ??? */
12608 bb_perror_msg("wait %s", *argv);
12609 }
12610 continue; /* bash checks all argv[] */
12611 }
12612 if (ret == 0) {
12613 /* Yes, and it still runs */
12614 ret = wait_for_child_or_signal(NULL, pid);
12615 } else {
12616 /* Yes, and it just exited */
12617 process_wait_result(NULL, pid, status);
12618 ret = WEXITSTATUS(status);
12619 if (WIFSIGNALED(status))
12620 ret = 128 | WTERMSIG(status);
12621 }
12622 } while (*++argv);
12623
12624 return ret;
12625}
12626#endif
12627
12628#if ENABLE_HUSH_LOOPS || ENABLE_HUSH_FUNCTIONS
12629static unsigned parse_numeric_argv1(char **argv, unsigned def, unsigned def_min)
12630{
12631 if (argv[1]) {
12632 def = bb_strtou(argv[1], NULL, 10);
12633 if (errno || def < def_min || argv[2]) {
12634 bb_error_msg("%s: bad arguments", argv[0]);
12635 def = UINT_MAX;
12636 }
12637 }
12638 return def;
12639}
12640#endif
12641
12642#if ENABLE_HUSH_LOOPS
12643static int FAST_FUNC builtin_break(char **argv)
12644{
12645 unsigned depth;
12646 if (G.depth_of_loop == 0) {
12647 bb_error_msg("%s: only meaningful in a loop", argv[0]);
12648 /* if we came from builtin_continue(), need to undo "= 1" */
12649 G.flag_break_continue = 0;
12650 return EXIT_SUCCESS; /* bash compat */
12651 }
12652 G.flag_break_continue++; /* BC_BREAK = 1, or BC_CONTINUE = 2 */
12653
12654 G.depth_break_continue = depth = parse_numeric_argv1(argv, 1, 1);
12655 if (depth == UINT_MAX)
12656 G.flag_break_continue = BC_BREAK;
12657 if (G.depth_of_loop < depth)
12658 G.depth_break_continue = G.depth_of_loop;
12659
12660 return EXIT_SUCCESS;
12661}
12662
12663static int FAST_FUNC builtin_continue(char **argv)
12664{
12665 G.flag_break_continue = 1; /* BC_CONTINUE = 2 = 1+1 */
12666 return builtin_break(argv);
12667}
12668#endif
12669
12670#if ENABLE_HUSH_FUNCTIONS
12671static int FAST_FUNC builtin_return(char **argv)
12672{
12673 int rc;
12674
12675 if (G_flag_return_in_progress != -1) {
12676 bb_error_msg("%s: not in a function or sourced script", argv[0]);
12677 return EXIT_FAILURE; /* bash compat */
12678 }
12679
12680 G_flag_return_in_progress = 1;
12681
12682 /* bash:
12683 * out of range: wraps around at 256, does not error out
12684 * non-numeric param:
12685 * f() { false; return qwe; }; f; echo $?
12686 * bash: return: qwe: numeric argument required <== we do this
12687 * 255 <== we also do this
12688 */
12689 rc = parse_numeric_argv1(argv, G.last_exitcode, 0);
12690# if ENABLE_HUSH_TRAP
12691 if (argv[1]) { /* "return ARG" inside a running trap sets $? */
12692 debug_printf_exec("G.return_exitcode=%d\n", rc);
12693 G.return_exitcode = rc;
12694 }
12695# endif
12696 return rc;
12697}
12698#endif
12699
12700#if ENABLE_HUSH_TIMES
12701static int FAST_FUNC builtin_times(char **argv UNUSED_PARAM)
12702{
12703 static const uint8_t times_tbl[] ALIGN1 = {
12704 ' ', offsetof(struct tms, tms_utime),
12705 '\n', offsetof(struct tms, tms_stime),
12706 ' ', offsetof(struct tms, tms_cutime),
12707 '\n', offsetof(struct tms, tms_cstime),
12708 0
12709 };
12710 const uint8_t *p;
12711 unsigned clk_tck;
12712 struct tms buf;
12713
12714 clk_tck = bb_clk_tck();
12715
12716 times(&buf);
12717 p = times_tbl;
12718 do {
12719 unsigned sec, frac;
12720 unsigned long t;
12721 t = *(clock_t *)(((char *) &buf) + p[1]);
12722 sec = t / clk_tck;
12723 frac = t % clk_tck;
12724 printf("%um%u.%03us%c",
12725 sec / 60, sec % 60,
12726 (frac * 1000) / clk_tck,
12727 p[0]);
12728 p += 2;
12729 } while (*p);
12730
12731 return EXIT_SUCCESS;
12732}
12733#endif
12734
12735#if ENABLE_HUSH_MEMLEAK
12736static int FAST_FUNC builtin_memleak(char **argv UNUSED_PARAM)
12737{
12738 void *p;
12739 unsigned long l;
12740
12741# ifdef M_TRIM_THRESHOLD
12742 /* Optional. Reduces probability of false positives */
12743 malloc_trim(0);
12744# endif
12745 /* Crude attempt to find where "free memory" starts,
12746 * sans fragmentation. */
12747 p = malloc(240);
12748 l = (unsigned long)p;
12749 free(p);
12750 p = malloc(3400);
12751 if (l < (unsigned long)p) l = (unsigned long)p;
12752 free(p);
12753
12754
12755# if 0 /* debug */
12756 {
12757 struct mallinfo mi = mallinfo();
12758 printf("top alloc:0x%lx malloced:%d+%d=%d\n", l,
12759 mi.arena, mi.hblkhd, mi.arena + mi.hblkhd);
12760 }
12761# endif
12762
12763 if (!G.memleak_value)
12764 G.memleak_value = l;
12765
12766 l -= G.memleak_value;
12767 if ((long)l < 0)
12768 l = 0;
12769 l /= 1024;
12770 if (l > 127)
12771 l = 127;
12772
12773 /* Exitcode is "how many kilobytes we leaked since 1st call" */
12774 return l;
12775}
12776#endif
Francis Laniel8197f012023-12-22 22:02:28 +010012777#endif /* !__U_BOOT__ */