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.37.0.git23da5c4b716b"
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... */
47 typedef signed char smallint;
48 typedef unsigned char smalluint;
49 #else
50 /* for arches where byte accesses generate larger code: */
51 typedef int smallint;
52 typedef 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 
xfunc_die(void)85 static void __noreturn xfunc_die(void)
86 {
87 	panic("HUSH died!");
88 }
89 
90 #define bb_error_msg_and_die(format, ...) do { \
91 panic("HUSH: " format, __VA_ARGS__); \
92 } while (0);
93 
94 #define bb_simple_error_msg_and_die(msg) do { \
95 panic_str("HUSH: " msg); \
96 } while (0);
97 
98 /* fdprintf() is used for debug output. */
fdprintf(int fd,const char * format,...)99 static 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 
bb_verror_msg(const char * s,va_list p,const char * strerr)113 static 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 
bb_error_msg(const char * s,...)119 static 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 
bb_simple_error_msg(const char * s)128 static void bb_simple_error_msg(const char *s)
129 {
130 	bb_error_msg("%s", s);
131 }
132 
xmalloc(size_t size)133 static 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 
xzalloc(size_t size)141 static void *xzalloc(size_t size)
142 {
143 	void *p = xmalloc(size);
144 	memset(p, 0, size);
145 	return p;
146 }
147 
xrealloc(void * ptr,size_t size)148 static 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 
xmemdup(const void * s,int n)156 static 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 
mempcpy(void * dest,const void * src,size_t len)164 static 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. */
overlapping_strcpy(char * dst,const char * src)170 static 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 
skip_whitespace(const char * s)184 static 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 
skip_non_whitespace(const char * s)199 static 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 
endofname(const char * name)210 static 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  */
list_size(char ** list)229 static 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 
varcmp(const char * p,const char * q)238 static 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';
252 out:
253 	return c - d;
254 }
255 
256 struct in_str;
257 static int u_boot_cli_readline(struct in_str *i);
258 
259 struct in_str;
260 static int u_boot_cli_readline(struct in_str *i);
261 
262 /*
263  * BusyBox globals which are needed for hush.
264  */
265 static uint8_t xfunc_error_retval;
266 
267 static 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 
u_boot_hush_start_modern(void)291 int u_boot_hush_start_modern(void)
292 {
293 	INIT_G();
294 	return 0;
295 }
296 
u_boot_cli_readline(struct in_str * i)297 static 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