| // SPDX-License-Identifier: GPL-2.0+ | |
| /* | |
| * This file defines the compilation unit for the new hush shell version. The | |
| * actual implementation from upstream BusyBox can be found in | |
| * `cli_hush_upstream.c` which is included at the end of this file. | |
| * | |
| * This "wrapper" technique is used to keep the changes to the upstream version | |
| * as minmal as possible. Instead, all defines and redefines necessary are done | |
| * here, outside the upstream sources. This will hopefully make upgrades to | |
| * newer revisions much easier. | |
| * | |
| * Copyright (c) 2021, Harald Seiler, DENX Software Engineering, hws@denx.de | |
| */ | |
| #include <env.h> | |
| #include <malloc.h> /* malloc, free, realloc*/ | |
| #include <linux/ctype.h> /* isalpha, isdigit */ | |
| #include <console.h> | |
| #include <bootretry.h> | |
| #include <cli.h> | |
| #include <cli_hush.h> | |
| #include <command.h> /* find_cmd */ | |
| #include <asm/global_data.h> | |
| /* | |
| * BusyBox Version: UPDATE THIS WHEN PULLING NEW UPSTREAM REVISION! | |
| */ | |
| #define BB_VER "1.37.0.git23da5c4b716b" | |
| /* | |
| * Define hush features by the names used upstream. | |
| */ | |
| #define ENABLE_HUSH_INTERACTIVE 1 | |
| #define ENABLE_FEATURE_EDITING 1 | |
| #define ENABLE_HUSH_IF 1 | |
| #define ENABLE_HUSH_LOOPS 1 | |
| /* No MMU in U-Boot */ | |
| #define BB_MMU 0 | |
| #define USE_FOR_NOMMU(...) __VA_ARGS__ | |
| #define USE_FOR_MMU(...) | |
| /* | |
| * Size-saving "small" ints (arch-dependent) | |
| */ | |
| #if CONFIG_IS_ENABLED(X86) || CONFIG_IS_ENABLED(X86_64) || CONFIG_IS_ENABLED(MIPS) | |
| /* add other arches which benefit from this... */ | |
| typedef signed char smallint; | |
| typedef unsigned char smalluint; | |
| #else | |
| /* for arches where byte accesses generate larger code: */ | |
| typedef int smallint; | |
| typedef unsigned smalluint; | |
| #endif | |
| /* | |
| * Alignment defines used by BusyBox. | |
| */ | |
| #define ALIGN1 __attribute__((aligned(1))) | |
| #define ALIGN2 __attribute__((aligned(2))) | |
| #define ALIGN4 __attribute__((aligned(4))) | |
| #define ALIGN8 __attribute__((aligned(8))) | |
| #define ALIGN_PTR __attribute__((aligned(sizeof(void*)))) | |
| /* | |
| * Miscellaneous compiler/platform defines. | |
| */ | |
| #define FAST_FUNC /* not used in U-Boot */ | |
| #define UNUSED_PARAM __always_unused | |
| #define ALWAYS_INLINE __always_inline | |
| #define NOINLINE noinline | |
| /* | |
| * Defines to provide equivalents to what libc/BusyBox defines. | |
| */ | |
| #define EOF (-1) | |
| #define EXIT_SUCCESS 0 | |
| #define EXIT_FAILURE 1 | |
| /* | |
| * Stubs to provide libc/BusyBox functions based on U-Boot equivalents where it | |
| * makes sense. | |
| */ | |
| #define utoa simple_itoa | |
| static void __noreturn xfunc_die(void) | |
| { | |
| panic("HUSH died!"); | |
| } | |
| #define bb_error_msg_and_die(format, ...) do { \ | |
| panic("HUSH: " format, __VA_ARGS__); \ | |
| } while (0); | |
| #define bb_simple_error_msg_and_die(msg) do { \ | |
| panic_str("HUSH: " msg); \ | |
| } while (0); | |
| /* fdprintf() is used for debug output. */ | |
| static int __maybe_unused fdprintf(int fd, const char *format, ...) | |
| { | |
| va_list args; | |
| uint i; | |
| assert(fd == 2); | |
| va_start(args, format); | |
| i = vprintf(format, args); | |
| va_end(args); | |
| return i; | |
| } | |
| static void bb_verror_msg(const char *s, va_list p, const char* strerr) | |
| { | |
| /* TODO: what to do with strerr arg? */ | |
| vprintf(s, p); | |
| } | |
| static void bb_error_msg(const char *s, ...) | |
| { | |
| va_list p; | |
| va_start(p, s); | |
| bb_verror_msg(s, p, NULL); | |
| va_end(p); | |
| } | |
| static void bb_simple_error_msg(const char *s) | |
| { | |
| bb_error_msg("%s", s); | |
| } | |
| static void *xmalloc(size_t size) | |
| { | |
| void *p = NULL; | |
| if (!(p = malloc(size))) | |
| panic("out of memory"); | |
| return p; | |
| } | |
| static void *xzalloc(size_t size) | |
| { | |
| void *p = xmalloc(size); | |
| memset(p, 0, size); | |
| return p; | |
| } | |
| static void *xrealloc(void *ptr, size_t size) | |
| { | |
| void *p = NULL; | |
| if (!(p = realloc(ptr, size))) | |
| panic("out of memory"); | |
| return p; | |
| } | |
| static void *xmemdup(const void *s, int n) | |
| { | |
| return memcpy(xmalloc(n), s, n); | |
| } | |
| #define xstrdup strdup | |
| #define xstrndup strndup | |
| static void *mempcpy(void *dest, const void *src, size_t len) | |
| { | |
| return memcpy(dest, src, len) + len; | |
| } | |
| /* Like strcpy but can copy overlapping strings. */ | |
| static void overlapping_strcpy(char *dst, const char *src) | |
| { | |
| /* | |
| * Cheap optimization for dst == src case - | |
| * better to have it here than in many callers. | |
| */ | |
| if (dst != src) { | |
| while ((*dst = *src) != '\0') { | |
| dst++; | |
| src++; | |
| } | |
| } | |
| } | |
| static char* skip_whitespace(const char *s) | |
| { | |
| /* | |
| * In POSIX/C locale (the only locale we care about: do we REALLY want | |
| * to allow Unicode whitespace in, say, .conf files? nuts!) | |
| * isspace is only these chars: "\t\n\v\f\r" and space. | |
| * "\t\n\v\f\r" happen to have ASCII codes 9,10,11,12,13. | |
| * Use that. | |
| */ | |
| while (*s == ' ' || (unsigned char)(*s - 9) <= (13 - 9)) | |
| s++; | |
| return (char *) s; | |
| } | |
| static char* skip_non_whitespace(const char *s) | |
| { | |
| while (*s != '\0' && *s != ' ' && (unsigned char)(*s - 9) > (13 - 9)) | |
| s++; | |
| return (char *) s; | |
| } | |
| #define is_name(c) ((c) == '_' || isalpha((unsigned char)(c))) | |
| #define is_in_name(c) ((c) == '_' || isalnum((unsigned char)(c))) | |
| static const char* endofname(const char *name) | |
| { | |
| if (!is_name(*name)) | |
| return name; | |
| while (*++name) { | |
| if (!is_in_name(*name)) | |
| break; | |
| } | |
| return name; | |
| } | |
| /** | |
| * list_size() - returns the number of elements in char ** before NULL. | |
| * | |
| * Argument must contain NULL to signalize its end. | |
| * | |
| * @list The list to count the number of element. | |
| * @return The number of element in list. | |
| */ | |
| static size_t list_size(char **list) | |
| { | |
| size_t size; | |
| for (size = 0; list[size] != NULL; size++); | |
| return size; | |
| } | |
| static int varcmp(const char *p, const char *q) | |
| { | |
| int c, d; | |
| while ((c = *p) == (d = *q)) { | |
| if (c == '\0' || c == '=') | |
| goto out; | |
| p++; | |
| q++; | |
| } | |
| if (c == '=') | |
| c = '\0'; | |
| if (d == '=') | |
| d = '\0'; | |
| out: | |
| return c - d; | |
| } | |
| struct in_str; | |
| static int u_boot_cli_readline(struct in_str *i); | |
| struct in_str; | |
| static int u_boot_cli_readline(struct in_str *i); | |
| /* | |
| * BusyBox globals which are needed for hush. | |
| */ | |
| static uint8_t xfunc_error_retval; | |
| static const char defifsvar[] __aligned(1) = "IFS= \t\n"; | |
| #define defifs (defifsvar + 4) | |
| /* This define is used to check if exit command was called. */ | |
| #define EXIT_RET_CODE -2 | |
| /* | |
| * This define is used for changes that need be done directly in the upstream | |
| * sources still. Ideally, its use should be minimized as much as possible. | |
| */ | |
| #define __U_BOOT__ | |
| /* | |
| * | |
| * +-- Include of the upstream sources --+ * | |
| * V V | |
| */ | |
| #include "cli_hush_upstream.c" | |
| /* | |
| * A A | |
| * +-- Include of the upstream sources --+ * | |
| * | |
| */ | |
| int u_boot_hush_start_modern(void) | |
| { | |
| INIT_G(); | |
| return 0; | |
| } | |
| static int u_boot_cli_readline(struct in_str *i) | |
| { | |
| char *prompt; | |
| char __maybe_unused *ps_prompt = NULL; | |
| if (!G.promptmode) | |
| prompt = CONFIG_SYS_PROMPT; | |
| #ifdef CONFIG_SYS_PROMPT_HUSH_PS2 | |
| else | |
| prompt = CONFIG_SYS_PROMPT_HUSH_PS2; | |
| #else | |
| /* TODO: default value? */ | |
| #error "SYS_PROMPT_HUSH_PS2 is not defined!" | |
| #endif | |
| if (CONFIG_IS_ENABLED(CMDLINE_PS_SUPPORT)) { | |
| if (!G.promptmode) | |
| ps_prompt = env_get("PS1"); | |
| else | |
| ps_prompt = env_get("PS2"); | |
| if (ps_prompt) | |
| prompt = ps_prompt; | |
| } | |
| return cli_readline(prompt); | |
| } |