1// SPDX-License-Identifier: GPL-2.0+
2/*
3 * This file defines the compilation unit for the new hush shell version.  The
4 * actual implementation from upstream BusyBox can be found in
5 * `cli_hush_upstream.c` which is included at the end of this file.
6 *
7 * This "wrapper" technique is used to keep the changes to the upstream version
8 * as minmal as possible.  Instead, all defines and redefines necessary are done
9 * here, outside the upstream sources.  This will hopefully make upgrades to
10 * newer revisions much easier.
11 *
12 * Copyright (c) 2021, Harald Seiler, DENX Software Engineering, hws@denx.de
13 */
14
15#include <env.h>
16#include <malloc.h>         /* malloc, free, realloc*/
17#include <linux/ctype.h>    /* isalpha, isdigit */
18#include <console.h>
19#include <bootretry.h>
20#include <cli.h>
21#include <cli_hush.h>
22#include <command.h>        /* find_cmd */
23#include <asm/global_data.h>
24
25/*
26 * BusyBox Version: UPDATE THIS WHEN PULLING NEW UPSTREAM REVISION!
27 */
28#define BB_VER			"1.35.0.git7d1c7d833785"
29
30/*
31 * Define hush features by the names used upstream.
32 */
33#define ENABLE_HUSH_INTERACTIVE	1
34#define ENABLE_FEATURE_EDITING	1
35#define ENABLE_HUSH_IF		1
36#define ENABLE_HUSH_LOOPS	1
37/* No MMU in U-Boot */
38#define BB_MMU			0
39#define USE_FOR_NOMMU(...)	__VA_ARGS__
40#define USE_FOR_MMU(...)
41
42/*
43 * Size-saving "small" ints (arch-dependent)
44 */
45#if CONFIG_IS_ENABLED(X86) || CONFIG_IS_ENABLED(X86_64) || CONFIG_IS_ENABLED(MIPS)
46/* add other arches which benefit from this... */
47typedef signed char smallint;
48typedef unsigned char smalluint;
49#else
50/* for arches where byte accesses generate larger code: */
51typedef int smallint;
52typedef unsigned smalluint;
53#endif
54
55/*
56 * Alignment defines used by BusyBox.
57 */
58#define ALIGN1			__attribute__((aligned(1)))
59#define ALIGN2			__attribute__((aligned(2)))
60#define ALIGN4			__attribute__((aligned(4)))
61#define ALIGN8			__attribute__((aligned(8)))
62#define ALIGN_PTR		__attribute__((aligned(sizeof(void*))))
63
64/*
65 * Miscellaneous compiler/platform defines.
66 */
67#define FAST_FUNC /* not used in U-Boot */
68#define UNUSED_PARAM		__always_unused
69#define ALWAYS_INLINE		__always_inline
70#define NOINLINE		noinline
71
72/*
73 * Defines to provide equivalents to what libc/BusyBox defines.
74 */
75#define EOF			(-1)
76#define EXIT_SUCCESS		0
77#define EXIT_FAILURE		1
78
79/*
80 * Stubs to provide libc/BusyBox functions based on U-Boot equivalents where it
81 * makes sense.
82 */
83#define utoa			simple_itoa
84
85static void __noreturn xfunc_die(void)
86{
87	panic("HUSH died!");
88}
89
90#define bb_error_msg_and_die(format, ...) do { \
91panic("HUSH: " format, __VA_ARGS__); \
92} while (0);
93
94#define bb_simple_error_msg_and_die(msg) do { \
95panic_str("HUSH: " msg); \
96} while (0);
97
98/* fdprintf() is used for debug output. */
99static int __maybe_unused fdprintf(int fd, const char *format, ...)
100{
101	va_list args;
102	uint i;
103
104	assert(fd == 2);
105
106	va_start(args, format);
107	i = vprintf(format, args);
108	va_end(args);
109
110	return i;
111}
112
113static void bb_verror_msg(const char *s, va_list p, const char* strerr)
114{
115	/* TODO: what to do with strerr arg? */
116	vprintf(s, p);
117}
118
119static void bb_error_msg(const char *s, ...)
120{
121	va_list p;
122
123	va_start(p, s);
124	bb_verror_msg(s, p, NULL);
125	va_end(p);
126}
127
128static void bb_simple_error_msg(const char *s)
129{
130	bb_error_msg("%s", s);
131}
132
133static void *xmalloc(size_t size)
134{
135	void *p = NULL;
136	if (!(p = malloc(size)))
137		panic("out of memory");
138	return p;
139}
140
141static void *xzalloc(size_t size)
142{
143	void *p = xmalloc(size);
144	memset(p, 0, size);
145	return p;
146}
147
148static void *xrealloc(void *ptr, size_t size)
149{
150	void *p = NULL;
151	if (!(p = realloc(ptr, size)))
152		panic("out of memory");
153	return p;
154}
155
156static void *xmemdup(const void *s, int n)
157{
158	return memcpy(xmalloc(n), s, n);
159}
160
161#define xstrdup		strdup
162#define xstrndup	strndup
163
164static void *mempcpy(void *dest, const void *src, size_t len)
165{
166	return memcpy(dest, src, len) + len;
167}
168
169/* Like strcpy but can copy overlapping strings. */
170static void overlapping_strcpy(char *dst, const char *src)
171{
172	/*
173	 * Cheap optimization for dst == src case -
174	 * better to have it here than in many callers.
175	 */
176	if (dst != src) {
177		while ((*dst = *src) != '\0') {
178			dst++;
179			src++;
180		}
181	}
182}
183
184static char* skip_whitespace(const char *s)
185{
186	/*
187	 * In POSIX/C locale (the only locale we care about: do we REALLY want
188	 * to allow Unicode whitespace in, say, .conf files? nuts!)
189	 * isspace is only these chars: "\t\n\v\f\r" and space.
190	 * "\t\n\v\f\r" happen to have ASCII codes 9,10,11,12,13.
191	 * Use that.
192	 */
193	while (*s == ' ' || (unsigned char)(*s - 9) <= (13 - 9))
194		s++;
195
196	return (char *) s;
197}
198
199static char* skip_non_whitespace(const char *s)
200{
201	while (*s != '\0' && *s != ' ' && (unsigned char)(*s - 9) > (13 - 9))
202		s++;
203
204	return (char *) s;
205}
206
207#define is_name(c)	((c) == '_' || isalpha((unsigned char)(c)))
208#define is_in_name(c)	((c) == '_' || isalnum((unsigned char)(c)))
209
210static const char* endofname(const char *name)
211{
212	if (!is_name(*name))
213		return name;
214	while (*++name) {
215		if (!is_in_name(*name))
216			break;
217	}
218	return name;
219}
220
221/**
222 * list_size() - returns the number of elements in char ** before NULL.
223 *
224 * Argument must contain NULL to signalize its end.
225 *
226 * @list The list to count the number of element.
227 * @return The number of element in list.
228 */
229static size_t list_size(char **list)
230{
231	size_t size;
232
233	for (size = 0; list[size] != NULL; size++);
234
235	return size;
236}
237
238static int varcmp(const char *p, const char *q)
239{
240	int c, d;
241
242	while ((c = *p) == (d = *q)) {
243		if (c == '\0' || c == '=')
244			goto out;
245		p++;
246		q++;
247	}
248	if (c == '=')
249		c = '\0';
250	if (d == '=')
251		d = '\0';
252out:
253	return c - d;
254}
255
256struct in_str;
257static int u_boot_cli_readline(struct in_str *i);
258
259struct in_str;
260static int u_boot_cli_readline(struct in_str *i);
261
262/*
263 * BusyBox globals which are needed for hush.
264 */
265static uint8_t xfunc_error_retval;
266
267static const char defifsvar[] __aligned(1) = "IFS= \t\n";
268#define defifs (defifsvar + 4)
269
270/* This define is used to check if exit command was called. */
271#define EXIT_RET_CODE -2
272
273/*
274 * This define is used for changes that need be done directly in the upstream
275 * sources still. Ideally, its use should be minimized as much as possible.
276 */
277#define __U_BOOT__
278
279/*
280 *
281 * +-- Include of the upstream sources --+ *
282 * V                                     V
283 */
284#include "cli_hush_upstream.c"
285/*
286 * A                                     A
287 * +-- Include of the upstream sources --+ *
288 *
289 */
290
291int u_boot_hush_start_modern(void)
292{
293	INIT_G();
294	return 0;
295}
296
297static int u_boot_cli_readline(struct in_str *i)
298{
299	char *prompt;
300	char __maybe_unused *ps_prompt = NULL;
301
302	if (!G.promptmode)
303		prompt = CONFIG_SYS_PROMPT;
304#ifdef CONFIG_SYS_PROMPT_HUSH_PS2
305	else
306		prompt = CONFIG_SYS_PROMPT_HUSH_PS2;
307#else
308	/* TODO: default value? */
309	#error "SYS_PROMPT_HUSH_PS2 is not defined!"
310#endif
311
312	if (CONFIG_IS_ENABLED(CMDLINE_PS_SUPPORT)) {
313		if (!G.promptmode)
314			ps_prompt = env_get("PS1");
315		else
316			ps_prompt = env_get("PS2");
317
318		if (ps_prompt)
319			prompt = ps_prompt;
320	}
321
322	return cli_readline(prompt);
323}
324