From c54a63458ab72d1de2eb8e9f7d7597e67f463550 Mon Sep 17 00:00:00 2001 From: yiyi <1692157237@qq.com> Date: Mon, 1 Dec 2025 13:30:03 +0800 Subject: [PATCH 1/3] Reconstruct the shell, manage historical commands using list, and add the ctrl+z/y functions --- components/finsh/Kconfig | 67 +- components/finsh/shell.c | 2144 +++++++++++++++++++++++++------------- components/finsh/shell.h | 202 ++-- 3 files changed, 1587 insertions(+), 826 deletions(-) diff --git a/components/finsh/Kconfig b/components/finsh/Kconfig index 67a50a28d7e..0e8be759d30 100644 --- a/components/finsh/Kconfig +++ b/components/finsh/Kconfig @@ -29,24 +29,28 @@ if RT_USING_MSH config FINSH_USING_HISTORY bool "Enable command history feature" default y - - if FINSH_USING_HISTORY - config FINSH_HISTORY_LINES - int "The command history line number" - default 5 - endif + if FINSH_USING_HISTORY + config FINSH_HISTORY_LINES + int "The command history line number" + default 10 + endif + + config FINSH_USING_SNAPSHOT + bool "Enable command Ctrl+Z/Ctrl+Y snapshot feature" + default n + if FINSH_USING_SNAPSHOT + config FINSH_SNAPSHOT_DEPTH + int "The command snapshot depth" + default 10 + endif config FINSH_USING_WORD_OPERATION - bool "Enable word-based cursor operations" + bool "Enable Ctrl+W/Backspace/Ctrl+Left/Right word-based cursor operations" default n - help - Enable Ctrl+Backspace to delete words and Ctrl+Arrow to move cursor by word - - config FINSH_USING_FUNC_EXT - bool "Enable function extension home end ins del" + + config FINSH_USING_EXTEND_FEATURE + bool "Enable insert/delete/home/end extend feature" default n - help - Enable function extension home end ins del. config FINSH_USING_SYMTAB bool "Using symbol table for commands" @@ -67,24 +71,31 @@ if RT_USING_MSH config FINSH_ECHO_DISABLE_DEFAULT bool "Disable the echo mode in default" default n + + config FINSH_PROMPT_ENABLE_DEFAULT + bool "Enable the prompt mode in default" + default y + + config FINSH_PROMPT_WORD_DEFAULT + string "The default prompt word" + default "msh " config FINSH_USING_AUTH bool "shell support authentication" default n - - if FINSH_USING_AUTH - config FINSH_DEFAULT_PASSWORD - string "The default password for shell authentication" - default "rtthread" - - config FINSH_PASSWORD_MIN - int "The password min length" - default 6 - - config FINSH_PASSWORD_MAX - int "The password max length" - default RT_NAME_MAX - endif + if FINSH_USING_AUTH + config FINSH_DEFAULT_PASSWORD + string "The default password for shell authentication" + default "rtthread" + + config FINSH_PASSWORD_MIN + int "The password min length" + default 6 + + config FINSH_PASSWORD_MAX + int "The password max length" + default RT_NAME_MAX + endif config FINSH_ARG_MAX int "The number of arguments for a shell command" diff --git a/components/finsh/shell.c b/components/finsh/shell.c index c85696ba8ac..0352963fb6a 100644 --- a/components/finsh/shell.c +++ b/components/finsh/shell.c @@ -16,6 +16,27 @@ * initialization when use GNU GCC compiler. * 2016-11-26 armink add password authentication * 2018-07-02 aozima add custom prompt support. + * 2025-09-22 yiyi reconstruct the shell, and the functions after reconstruction are as follows: + * #ifdef FINSH_USING_HISTORY + * 1. 'Up/Down' to history command. + * #endif + * 2. 'Left/Right' to move cursor. + * #ifdef FINSH_USING_WORD_OPERATION + * 3. 'Ctrl+Left/Right' to jump word by word. + * 4. 'Ctrl+W/Backspace' to delete word. + * #endif + * #ifdef FINSH_USING_SNAPSHOT + * 5. 'Ctrl+Z' to pop snapshot. + * 6. 'Ctrl+Y' to rollback snapshot. + * #endif + * #ifdef FINSH_USING_EXTEND_FEATURE + * 7. 'Home/End' to move cursor to the beginning/end of the command. + * 8. 'Delete' to delete character backward. + * 9. 'Insert' to toggle insert mode. + * #endif + * 10. 'Tab' to auto complete. + * 11. 'Backspace' to delete character forward + * 12. 'Enter' to execute command. */ #include @@ -39,928 +60,1560 @@ /* finsh thread */ #ifndef RT_USING_HEAP - static struct rt_thread finsh_thread; - rt_align(RT_ALIGN_SIZE) - static char finsh_thread_stack[FINSH_THREAD_STACK_SIZE]; - struct finsh_shell _shell; -#endif +static struct rt_thread finsh_thread = { 0 }; +rt_align(RT_ALIGN_SIZE) static char finsh_thread_stack[FINSH_THREAD_STACK_SIZE] = { 0 }; +static struct finsh_shell _shell = { 0 }; +#endif /* !RT_USING_HEAP */ /* finsh symtab */ #ifdef FINSH_USING_SYMTAB - struct finsh_syscall *_syscall_table_begin = NULL; - struct finsh_syscall *_syscall_table_end = NULL; -#endif +struct finsh_syscall *_syscall_table_begin = NULL; +struct finsh_syscall *_syscall_table_end = NULL; +#endif /* FINSH_USING_SYMTAB */ -struct finsh_shell *shell; -static char *finsh_prompt_custom = RT_NULL; +static struct finsh_shell *shell = NULL; +static char finsh_prompt[RT_CONSOLEBUF_SIZE + 1] = { 0 }; +static size_t finsh_prompt_length = 0; #if defined(_MSC_VER) || (defined(__GNUC__) && defined(__x86_64__)) struct finsh_syscall *finsh_syscall_next(struct finsh_syscall *call) { - unsigned int *ptr; - ptr = (unsigned int *)(call + 1); - while ((*ptr == 0) && ((unsigned int *)ptr < (unsigned int *) _syscall_table_end)) - ptr ++; + rt_ubase_t *ptr; + ptr = (rt_ubase_t *)(call + 1); + while ((!(*ptr)) && ((rt_ubase_t *)ptr < (rt_ubase_t *)_syscall_table_end)) + ptr++; return (struct finsh_syscall *)ptr; } - #endif /* defined(_MSC_VER) || (defined(__GNUC__) && defined(__x86_64__)) */ -#ifdef RT_USING_HEAP -int finsh_set_prompt(const char *prompt) +#ifdef RT_USING_HOOK +static void (*_finsh_thread_entry_hook)(void); + +/** + * @brief Set the hook function to be called at the entry of the finsh thread. + * + * This function assigns a user-defined hook function that will be executed + * when the finsh thread starts. It allows users to perform custom initialization + * or actions at the beginning of the finsh thread execution. + * + * @param hook Pointer to the hook function to be set. + */ +void finsh_thread_entry_sethook(void (*hook)(void)) { - if (finsh_prompt_custom) - { - rt_free(finsh_prompt_custom); - finsh_prompt_custom = RT_NULL; - } + _finsh_thread_entry_hook = hook; +} +#endif /* RT_USING_HOOK */ - /* strdup */ - if (prompt) +/** + * @brief Set the shell prompt string. + * + * This function sets the prompt string for the FinSH shell. It copies the given prompt + * to the internal prompt buffer, ensuring it does not exceed the maximum buffer size. + * If the input prompt is NULL, it prints an error message and returns an error code. + * + * @param prompt The new prompt string to set. + * + * @return 0 on success, -EINVAL if the prompt is NULL. + */ +int finsh_set_prompt_word(const char *prompt) +{ + if (!prompt) { - finsh_prompt_custom = (char *)rt_malloc(rt_strlen(prompt) + 1); - if (finsh_prompt_custom) - { - rt_strcpy(finsh_prompt_custom, prompt); - } + rt_kprintf("Invalid prompt!\n"); + return -EINVAL; } + rt_memset(finsh_prompt, 0, sizeof(finsh_prompt)); + rt_strncpy(finsh_prompt, prompt, RT_CONSOLEBUF_SIZE); + finsh_prompt_length = rt_strlen(finsh_prompt); + return 0; } -#endif /* RT_USING_HEAP */ -#define _MSH_PROMPT "msh " - -const char *finsh_get_prompt(void) +/** + * @brief Get the current shell prompt string. + * + * This function returns the current prompt string for the FinSH shell. + * If the prompt mode is disabled, it returns an empty string. + * If the prompt string is not set, it sets the default prompt ("msh "). + * When POSIX and workdir support are enabled, it appends the current working directory to the prompt. + * Finally, it appends a '>' character to the prompt if there is enough space. + * + * @return Pointer to the prompt string. + */ +const char *finsh_get_prompt_word(void) { - static char finsh_prompt[RT_CONSOLEBUF_SIZE + 1] = {0}; + size_t len; + + RT_ASSERT(shell != NULL); /* check prompt mode */ - if (!shell->prompt_mode) + if (!shell->is_prompt) { - finsh_prompt[0] = '\0'; - return finsh_prompt; + finsh_prompt[RT_CONSOLEBUF_SIZE] = 0; + return &finsh_prompt[RT_CONSOLEBUF_SIZE]; } - if (finsh_prompt_custom) - { - rt_strncpy(finsh_prompt, finsh_prompt_custom, sizeof(finsh_prompt) - 1); - } - else - { - rt_strcpy(finsh_prompt, _MSH_PROMPT); - } + if (!finsh_prompt[0]) + finsh_set_prompt_word(FINSH_PROMPT_WORD_DEFAULT); + len = finsh_prompt_length; #if defined(DFS_USING_POSIX) && defined(DFS_USING_WORKDIR) /* get current working directory */ - getcwd(&finsh_prompt[rt_strlen(finsh_prompt)], RT_CONSOLEBUF_SIZE - rt_strlen(finsh_prompt)); + getcwd(&finsh_prompt[len], RT_CONSOLEBUF_SIZE - len); #endif - - if (rt_strlen(finsh_prompt) + 2 < RT_CONSOLEBUF_SIZE) + len = rt_strlen(finsh_prompt); + if ((len + 2) < RT_CONSOLEBUF_SIZE) { - finsh_prompt[rt_strlen(finsh_prompt)] = '>'; - finsh_prompt[rt_strlen(finsh_prompt) + 1] = '\0'; + finsh_prompt[len++] = '>'; + finsh_prompt[len++] = 0; } return finsh_prompt; } /** - * @ingroup group_finsh - * - * This function get the prompt mode of finsh shell. - * - * @return prompt the prompt mode, 0 disable prompt mode, other values enable prompt mode. - */ -rt_uint32_t finsh_get_prompt_mode(void) + * @brief Set the prompt mode of the FinSH shell. + * + * This function enables or disables the prompt mode for the FinSH shell. + * If the parameter is 0, the prompt mode is disabled; any other value enables the prompt mode. + * + * @param prompt The prompt mode flag (0 to disable, non-zero to enable). + */ +void finsh_set_prompt(rt_bool_t prompt) { - RT_ASSERT(shell != RT_NULL); - return shell->prompt_mode; + RT_ASSERT(shell != NULL); + shell->is_prompt = prompt; } /** - * @ingroup group_finsh - * - * This function set the prompt mode of finsh shell. - * - * The parameter 0 disable prompt mode, other values enable prompt mode. - * - * @param prompt_mode the prompt mode - */ -void finsh_set_prompt_mode(rt_uint32_t prompt_mode) + * @brief Get the prompt mode of the FinSH shell. + * + * This function returns the current prompt mode of the FinSH shell. + * If the prompt mode is enabled, it returns a non-zero value; if disabled, it returns 0. + * + * @return The prompt mode status: 0 means prompt mode is disabled, non-zero means enabled. + */ +rt_bool_t finsh_get_prompt(void) { - RT_ASSERT(shell != RT_NULL); - shell->prompt_mode = prompt_mode; -} - -int finsh_getchar(void) -{ -#ifdef RT_USING_DEVICE - char ch = 0; -#ifdef RT_USING_POSIX_STDIO - if(read(rt_posix_stdio_get_console(), &ch, 1) > 0) - { - return ch; - } - else - { - return -1; /* EOF */ - } -#else - rt_device_t device; - - RT_ASSERT(shell != RT_NULL); - - device = shell->device; - if (device == RT_NULL) - { - return -1; /* EOF */ - } - - while (rt_device_read(device, -1, &ch, 1) != 1) - { - rt_sem_take(&shell->rx_sem, RT_WAITING_FOREVER); - if (shell->device != device) - { - device = shell->device; - if (device == RT_NULL) - { - return -1; - } - } - } - return ch; -#endif /* RT_USING_POSIX_STDIO */ -#else - extern signed char rt_hw_console_getchar(void); - return rt_hw_console_getchar(); -#endif /* RT_USING_DEVICE */ + RT_ASSERT(shell != NULL); + return shell->is_prompt; } #if !defined(RT_USING_POSIX_STDIO) && defined(RT_USING_DEVICE) static rt_err_t finsh_rx_ind(rt_device_t dev, rt_size_t size) { - RT_ASSERT(shell != RT_NULL); + RT_ASSERT(shell != NULL); - /* release semaphore to let finsh thread rx data */ - rt_sem_release(&shell->rx_sem); + if (size) + rt_sem_release(&shell->rx_notice); - return RT_EOK; + return 0; } /** - * @ingroup group_finsh - * - * This function sets the input device of finsh shell. - * - * @param device_name the name of new input device. - */ + * @brief Set the input device for the FinSH shell. + * + * This function changes the input device used by the FinSH shell to the device specified by device_name. + * It first finds the device by name, then opens it with the appropriate flags. If the device is already + * the current input device, the function returns immediately. If a previous device was set, it will be closed + * and its receive indication callback cleared. The new device will have its receive indication callback set + * to finsh_rx_ind, and the shell's device pointer will be updated. + * + * @param device_name The name of the new input device to be used by the shell. + */ void finsh_set_device(const char *device_name) { - rt_device_t dev = RT_NULL; - - RT_ASSERT(shell != RT_NULL); - dev = rt_device_find(device_name); - if (dev == RT_NULL) + struct rt_device *device; + uint16_t oflag; + int ret; +#ifdef RT_USING_SERIAL_V2 + int rxto; +#endif /* RT_USING_SERIAL_V2 */ + + RT_ASSERT(shell != NULL); + RT_ASSERT(device_name != NULL); + + device = rt_device_find(device_name); + if (!device) { - rt_kprintf("finsh: can not find device: %s\n", device_name); + rt_kprintf("Finsh: can not find device: %s!\n", device_name); return; } - /* check whether it's a same device */ - if (dev == shell->device) return; - /* open this device and set the new device in finsh shell */ - if (rt_device_open(dev, RT_DEVICE_OFLAG_RDWR | RT_DEVICE_FLAG_INT_RX | \ - RT_DEVICE_FLAG_STREAM) == RT_EOK) + if (device == shell->device) + return; + +#ifdef RT_USING_SERIAL_V2 + oflag = RT_DEVICE_FLAG_RX_BLOCKING | RT_DEVICE_FLAG_TX_BLOCKING; +#else + oflag = RT_DEVICE_FLAG_INT_RX; +#endif /* RT_USING_SERIAL_V2 */ + oflag |= RT_DEVICE_OFLAG_RDWR | RT_DEVICE_FLAG_STREAM; + ret = rt_device_open(device, oflag); + if (ret) { - if (shell->device != RT_NULL) - { - /* close old finsh device */ - rt_device_close(shell->device); - rt_device_set_rx_indicate(shell->device, RT_NULL); - } + rt_kprintf("Finsh: open device: %s failed!\n", device_name); + return; + } - /* clear line buffer before switch to new device */ - rt_memset(shell->line, 0, sizeof(shell->line)); - shell->line_curpos = shell->line_position = 0; +#ifdef RT_USING_SERIAL_V2 + rxto = RT_WAITING_NO; + rt_device_control(device, RT_SERIAL_CTRL_SET_RX_TIMEOUT, (void *)&rxto); +#endif /* RT_USING_SERIAL_V2 */ - shell->device = dev; - rt_device_set_rx_indicate(dev, finsh_rx_ind); + if (shell->device) + { + rt_device_close(shell->device); + rt_device_set_rx_indicate(shell->device, NULL); } + + rt_device_set_rx_indicate(device, finsh_rx_ind); + shell->device = device; } /** - * @ingroup group_finsh - * - * This function returns current finsh shell input device. - * - * @return the finsh shell input device name is returned. - */ -const char *finsh_get_device() + * Get the name of the current input device used by the finsh shell. + * + * @return The name of the device as a string. + */ +const char *finsh_get_device(void) { - RT_ASSERT(shell != RT_NULL); + RT_ASSERT(shell != NULL); return shell->device->parent.name; } #endif /* !defined(RT_USING_POSIX_STDIO) && defined(RT_USING_DEVICE) */ /** - * @ingroup group_finsh - * - * This function set the echo mode of finsh shell. - * - * FINSH_OPTION_ECHO=0x01 is echo mode, other values are none-echo mode. - * - * @param echo the echo mode - */ -void finsh_set_echo(rt_uint32_t echo) + * Set the echo mode of the finsh shell. + * + * When echo mode is enabled, the shell will display user input characters. + * When disabled, user input will not be echoed to the terminal. + * + * @param echo RT_TRUE to enable echo, RT_FALSE to disable echo. + */ +void finsh_set_echo(rt_bool_t echo) { - RT_ASSERT(shell != RT_NULL); - shell->echo_mode = (rt_uint8_t)echo; + RT_ASSERT(shell != NULL); + shell->is_echo = echo; } /** - * @ingroup group_finsh - * - * This function gets the echo mode of finsh shell. - * - * @return the echo mode - */ -rt_uint32_t finsh_get_echo() + * @brief Get the current echo mode status of the finsh shell. + * + * This function returns whether the shell is currently in echo mode. + * When echo mode is enabled, user input will be displayed on the terminal. + * + * @return RT_TRUE if echo mode is enabled, RT_FALSE otherwise. + */ +rt_bool_t finsh_get_echo(void) { - RT_ASSERT(shell != RT_NULL); - - return shell->echo_mode; + RT_ASSERT(shell != NULL); + return shell->is_echo; } #ifdef FINSH_USING_AUTH /** - * set a new password for finsh - * - * @param password new password - * - * @return result, RT_EOK on OK, -RT_ERROR on the new password length is less than - * FINSH_PASSWORD_MIN or greater than FINSH_PASSWORD_MAX - */ -rt_err_t finsh_set_password(const char *password) + * @brief Set a new password for the finsh shell. + * + * This function sets a new password for the finsh shell. It first checks whether the input password + * is valid (not NULL and its length is within the allowed range). If the password is valid, it copies + * the new password into the shell's password buffer in a critical section to ensure thread safety. + * + * @param password The new password string to set. + * + * @return 0 on success, -EINVAL if the password is NULL or its length is invalid. + */ +int finsh_set_password(const char *password) { + size_t len; rt_base_t level; - rt_size_t pw_len = rt_strlen(password); - if (pw_len < FINSH_PASSWORD_MIN || pw_len > FINSH_PASSWORD_MAX) - return -RT_ERROR; + RT_ASSERT(shell != NULL); + + if (!password) + { + rt_kprintf("Password is NULL!\n"); + return -EINVAL; + } + + len = rt_strlen(password); + if (len > FINSH_PASSWORD_MAX || len < FINSH_PASSWORD_MIN) + { + rt_kprintf("Password length is less than FINSH_PASSWORD_MIN(%d) or greater than FINSH_PASSWORD_MAX(%d)!\n", FINSH_PASSWORD_MIN, FINSH_PASSWORD_MAX); + return -EINVAL; + } level = rt_hw_interrupt_disable(); - rt_strncpy(shell->password, password, FINSH_PASSWORD_MAX); + rt_strncpy(shell->password, password, len); + shell->password[len] = 0; rt_hw_interrupt_enable(level); - return RT_EOK; + return 0; } /** - * get the finsh password - * - * @return password - */ + * @brief Get the current password of the finsh shell. + * + * This function returns a pointer to the password string currently used by the finsh shell + * for authentication purposes. + * + * @return Pointer to the password string. + */ const char *finsh_get_password(void) { + RT_ASSERT(shell != NULL); return shell->password; } -static void finsh_wait_auth(void) +static void finsh_wait_input_password(char *password) { - int ch; - rt_bool_t input_finish = RT_FALSE; - char password[FINSH_PASSWORD_MAX] = { 0 }; - rt_size_t cur_pos = 0; - /* password not set */ - if (rt_strlen(finsh_get_password()) == 0) return; + char ch; + size_t cursor = 0; while (1) { - rt_kprintf("Password for login: "); - while (!input_finish) + /* read one character from device */ + ch = finsh_getchar(); + if (ch < 0) + continue; + + if (ch >= ' ' && ch <= '~' && cursor < FINSH_PASSWORD_MAX) { - while (1) - { - /* read one character from device */ - ch = (int)finsh_getchar(); - if (ch < 0) - { - continue; - } - - if (ch >= ' ' && ch <= '~' && cur_pos < FINSH_PASSWORD_MAX) - { - /* change the printable characters to '*' */ - rt_kprintf("*"); - password[cur_pos++] = ch; - } - else if (ch == '\b' && cur_pos > 0) - { - /* backspace */ - cur_pos--; - password[cur_pos] = '\0'; - rt_kprintf("\b \b"); - } - else if (ch == '\r' || ch == '\n') - { - rt_kprintf("\n"); - input_finish = RT_TRUE; - break; - } - } + rt_kprintf("*"); + password[cursor++] = ch; } - if (!rt_strncmp(shell->password, password, FINSH_PASSWORD_MAX)) return; - else + else if (ch == '\b' && cursor > 0) { - /* authentication failed, delay 2S for retry */ - rt_thread_delay(2 * RT_TICK_PER_SECOND); - rt_kprintf("Sorry, try again.\n"); - cur_pos = 0; - input_finish = RT_FALSE; - rt_memset(password, '\0', FINSH_PASSWORD_MAX); + password[--cursor] = 0; + rt_kprintf("\b \b"); } + else if (ch == '\r' || ch == '\n') + break; } + + rt_kprintf("\n"); } -#endif /* FINSH_USING_AUTH */ -static void shell_auto_complete(char *prefix) +static void finsh_password_auth(void) { - rt_kprintf("\n"); - msh_auto_complete(prefix); + char password[FINSH_PASSWORD_MAX] = { 0 }; -#ifdef FINSH_USING_OPTION_COMPLETION - msh_opt_auto_complete(prefix); -#endif + /* password not set */ + if (!rt_strlen(finsh_get_password())) + return; + + while (1) + { + rt_kprintf("Password for login: "); + finsh_wait_input_password(password); + + if (!rt_strncmp(shell->password, password, FINSH_PASSWORD_MAX)) + break; - rt_kprintf("%s%s", FINSH_PROMPT, prefix); + /* authentication failed, delay 2S for retry */ + rt_thread_delay(2 * RT_TICK_PER_SECOND); + rt_kprintf("Sorry, password is incorrect, try again.\n"); + rt_memset(password, 0, FINSH_PASSWORD_MAX); + } } +#endif /* FINSH_USING_AUTH */ #ifdef FINSH_USING_HISTORY -static rt_bool_t shell_handle_history(struct finsh_shell *shell) +static struct finsh_history *finsh_history_alloc(char *cmd, size_t cmd_length) { -#if defined(_WIN32) + struct finsh_history *history; + +#ifndef RT_USING_HEAP int i; - rt_kprintf("\r"); + for (i = 0; i < FINSH_HISTORY_LINES; i++) + { + /* Use list.next and list.prev to check if the current historical record is being used */ + if (shell->history[i].list.next && shell->history[i].list.prev) + continue; + history = &shell->history[i]; + break; + } + if (i >= FINSH_HISTORY_LINES) + { + rt_kprintf("No available historical record buffer zone!\n"); + return NULL; + } +#else + history = (struct finsh_history *)rt_malloc(sizeof(*history)); + if (!history) + { + rt_kprintf("Failed to allocate memory for history!\n"); + return NULL; + } - for (i = 0; i <= 60; i++) - putchar(' '); - rt_kprintf("\r"); + history->cmd = (char *)rt_calloc(1, cmd_length + 1); + if (!history->cmd) + { + rt_free(history); + rt_kprintf("Failed to allocate memory for history command!\n"); + return NULL; + } +#endif /* !RT_USING_HEAP */ -#else - rt_kprintf("\033[2K\r"); -#endif - rt_kprintf("%s%s", FINSH_PROMPT, shell->line); - return RT_FALSE; + if (cmd) + rt_strncpy(history->cmd, cmd, cmd_length); + history->cmd[cmd_length] = 0; /* add null terminator */ + + return history; } -static void shell_push_history(struct finsh_shell *shell) +static void finsh_history_free(struct finsh_history *history) { - if (shell->line_position != 0) - { - /* push history */ - if (shell->history_count >= FINSH_HISTORY_LINES) - { - /* if current cmd is same as last cmd, don't push */ - if (rt_memcmp(&shell->cmd_history[FINSH_HISTORY_LINES - 1], shell->line, FINSH_CMD_SIZE)) - { - /* move history */ - int index; - for (index = 0; index < FINSH_HISTORY_LINES - 1; index ++) - { - rt_memcpy(&shell->cmd_history[index][0], - &shell->cmd_history[index + 1][0], FINSH_CMD_SIZE); - } - rt_memset(&shell->cmd_history[index][0], 0, FINSH_CMD_SIZE); - rt_memcpy(&shell->cmd_history[index][0], shell->line, shell->line_position); - - /* it's the maximum history */ - shell->history_count = FINSH_HISTORY_LINES; - } - } - else - { - /* if current cmd is same as last cmd, don't push */ - if (shell->history_count == 0 || rt_memcmp(&shell->cmd_history[shell->history_count - 1], shell->line, FINSH_CMD_SIZE)) - { - shell->current_history = shell->history_count; - rt_memset(&shell->cmd_history[shell->history_count][0], 0, FINSH_CMD_SIZE); - rt_memcpy(&shell->cmd_history[shell->history_count][0], shell->line, shell->line_position); +#ifdef RT_USING_HEAP + if (!history || !history->cmd) +#else + if (!history) +#endif /* RT_USING_HEAP */ + return; - /* increase count and set current history position */ - shell->history_count ++; - } - } - } - shell->current_history = shell->history_count; +#ifdef RT_USING_HEAP + rt_memset(history->cmd, 0, rt_strlen(history->cmd)); + rt_free(history->cmd); +#endif /* RT_USING_HEAP */ + rt_memset(history, 0, sizeof(*history)); +#ifdef RT_USING_HEAP + rt_free(history); +#endif /* RT_USING_HEAP */ } -#endif -#if defined(FINSH_USING_WORD_OPERATION) -static int find_prev_word_start(const char *line, int curpos) +static struct finsh_history *finsh_history_realloc(struct finsh_history *history, char *cmd, size_t cmd_length) { - if (curpos <= 0) return 0; + if (!history) + return finsh_history_alloc(cmd, cmd_length); - /* Skip whitespace */ - while (--curpos > 0 && (line[curpos] == ' ' || line[curpos] == '\t')); +#ifdef RT_USING_HEAP + if (cmd_length > rt_strlen(history->cmd)) + history->cmd = (char *)rt_realloc(history->cmd, cmd_length + 1); + if (!history->cmd) + { + rt_free(history); + rt_kprintf("Failed to allocate memory for history command!\n"); + return NULL; + } +#endif /* RT_USING_HEAP */ - /* Find word start */ - while (curpos > 0 && !(line[curpos] == ' ' || line[curpos] == '\t')) - curpos--; + if (cmd) + rt_strncpy(history->cmd, cmd, cmd_length); + history->cmd[cmd_length] = 0; /* add null terminator */ - return (curpos <= 0) ? 0 : curpos + 1; + return history; } +#endif /* FINSH_USING_HISTORY */ -static int find_next_word_end(const char *line, int curpos, int max) +#ifdef FINSH_USING_SNAPSHOT +static struct finsh_snapshot *finsh_snapshot_alloc(char *cmd, size_t cmd_length, size_t cmd_cursor) { - if (curpos >= max) return max; + struct finsh_snapshot *snap; + +#ifndef RT_USING_HEAP + int i; + for (i = 0; i < FINSH_SNAPSHOT_DEPTH; i++) + { + if (shell->snapshot[i].list.next && shell->snapshot[i].list.prev) + continue; + snap = &shell->snapshot[i]; + break; + } + if (i >= FINSH_SNAPSHOT_DEPTH) + { + rt_kprintf("No available snapshot buffer zone!\n"); + return NULL; + } +#else + snap = (struct finsh_snapshot *)rt_malloc(sizeof(*snap)); + if (!snap) + { + rt_kprintf("Failed to allocate memory for snapshot!\n"); + return NULL; + } - /* Skip to next word */ - while (curpos < max && (line[curpos] == ' ' || line[curpos] == '\t')) - curpos++; + snap->cmd = (char *)rt_calloc(1, cmd_length + 1); + if (!snap->cmd) + { + rt_free(snap); + rt_kprintf("Failed to allocate memory for snapshot command!\n"); + return NULL; + } +#endif /* !RT_USING_HEAP */ - /* Find word end */ - while (curpos < max && !(line[curpos] == ' ' || line[curpos] == '\t')) - curpos++; + if (cmd) + rt_strncpy(snap->cmd, cmd, cmd_length); + snap->cmd_cursor = cmd_cursor; + snap->cmd_length = cmd_length; + snap->cmd[cmd_length] = 0; /* add null terminator */ - return curpos; + return snap; } -#endif /* defined(FINSH_USING_WORD_OPERATION) */ -#ifdef RT_USING_HOOK -static void (*_finsh_thread_entry_hook)(void); +static void finsh_snapshot_free(struct finsh_snapshot *snap) +{ +#ifdef RT_USING_HEAP + if (!snap || !snap->cmd) +#else + if (!snap) +#endif /* RT_USING_HEAP */ + return; -/** - * @ingroup group_finsh - * - * @brief This function set a hook function at the entry of finsh thread - * - * @param hook the function point to be called - */ -void finsh_thread_entry_sethook(void (*hook)(void)) +#ifdef RT_USING_HEAP + rt_memset(snap->cmd, 0, snap->cmd_length); + rt_free(snap->cmd); +#endif /* RT_USING_HEAP */ + rt_memset(snap, 0, sizeof(*snap)); +#ifdef RT_USING_HEAP + rt_free(snap); +#endif /* RT_USING_HEAP */ +} + +static struct finsh_snapshot *finsh_snapshot_realloc(struct finsh_snapshot *snap, char *cmd, size_t cmd_length, size_t cmd_cursor) { - _finsh_thread_entry_hook = hook; + if (!snap) + return finsh_snapshot_alloc(cmd, cmd_length, cmd_cursor); + +#ifdef RT_USING_HEAP + if (cmd_length > rt_strlen(snap->cmd)) + snap->cmd = (char *)rt_realloc(snap->cmd, cmd_length + 1); + if (!snap->cmd) + { + rt_free(snap); + rt_kprintf("Failed to allocate memory for snapshot command!\n"); + return NULL; + } +#endif /* RT_USING_HEAP */ + + if (cmd) + rt_strncpy(snap->cmd, cmd, cmd_length); + snap->cmd_cursor = cmd_cursor; + snap->cmd_length = cmd_length; + snap->cmd[cmd_length] = 0; /* add null terminator */ + + return snap; } -#endif /* RT_USING_HOOK */ +#endif /* FINSH_USING_SNAPSHOT */ -static void finsh_thread_entry(void *parameter) +static int finsh_shell_init(void) { - int ch; +#if !defined(RT_USING_POSIX_STDIO) && defined(RT_USING_DEVICE) + struct rt_device *console; +#endif /* !defined(RT_USING_POSIX_STDIO) && defined(RT_USING_DEVICE) */ + int ret; - RT_OBJECT_HOOK_CALL(_finsh_thread_entry_hook, ()); + RT_ASSERT(shell != NULL); + + ret = rt_sem_init(&shell->rx_notice, "shrx", 0, RT_IPC_FLAG_FIFO); + if (ret) + { + rt_kprintf("Failed to initialize shell_rx_notice semaphore!\n"); + return -EIO; + } + + shell->input_state = FINSH_INPUT_STATE_NORMAL; + +#ifdef FINSH_USING_HISTORY + rt_list_init(&shell->history_list); + shell->cur_history = &shell->history_list; + shell->is_push_last_history = RT_FALSE; +#ifndef RT_USING_HEAP + rt_memset(shell->history, 0, sizeof(shell->history)); +#endif /* !RT_USING_HEAP */ +#endif /* FINSH_USING_HISTORY */ + + rt_memset(shell->cmd, 0, sizeof(shell->cmd)); + shell->cmd_length = 0; + shell->cmd_cursor = 0; + + shell->extend_key = 0; + shell->is_insert = RT_FALSE; + shell->is_ctrl = RT_FALSE; - /* normal is echo mode */ #ifndef FINSH_ECHO_DISABLE_DEFAULT - shell->echo_mode = 1; + shell->is_echo = RT_TRUE; #else - shell->echo_mode = 0; -#endif + shell->is_echo = RT_FALSE; +#endif /* !FINSH_ECHO_DISABLE_DEFAULT */ + +#ifdef FINSH_PROMPT_ENABLE_DEFAULT + shell->is_prompt = RT_TRUE; +#else + shell->is_prompt = RT_FALSE; +#endif /* FINSH_PROMPT_ENABLE_DEFAULT */ + +#ifdef FINSH_USING_SNAPSHOT + rt_list_init(&shell->snapshot_list); + shell->cur_snapshot = &shell->snapshot_list; + shell->is_push_last_snapshot = RT_FALSE; + shell->snapshot_state = FINSH_SNAPSHOT_STATE_NONE; +#ifndef RT_USING_HEAP + rt_memset(shell->snapshot, 0, sizeof(shell->snapshot)); +#endif /* !RT_USING_HEAP */ +#endif /* FINSH_USING_SNAPSHOT */ + +#ifdef FINSH_USING_AUTH + rt_memset(shell->password, 0, sizeof(shell->password)); +#endif /* FINSH_USING_AUTH */ #if !defined(RT_USING_POSIX_STDIO) && defined(RT_USING_DEVICE) - /* set console device as shell device */ - if (shell->device == RT_NULL) + if (!shell->device) { - rt_device_t console = rt_console_get_device(); + console = rt_console_get_device(); if (console) - { finsh_set_device(console->parent.name); - } + } + if (!shell->device) + { + rt_sem_detach(&shell->rx_notice); + rt_kprintf("Finsh: can not find device!\n"); + return -ENODEV; } #endif /* !defined(RT_USING_POSIX_STDIO) && defined(RT_USING_DEVICE) */ -#ifdef FINSH_USING_AUTH - /* set the default password when the password isn't setting */ - if (rt_strlen(finsh_get_password()) == 0) + return 0; +} + +static void finsh_shell_deinit(void) +{ +#ifdef FINSH_USING_HISTORY + struct finsh_history *history, *n; +#endif /* AT_BYPASS_USING_HISTORY */ +#ifdef FINSH_USING_SNAPSHOT + struct finsh_snapshot *snap, *s; +#endif /* AT_BYPASS_USING_SNAPSHOT */ + + RT_ASSERT(shell != NULL); + +#ifdef FINSH_USING_HISTORY + rt_list_for_each_entry_safe(history, n, &shell->history_list, list) { - if (finsh_set_password(FINSH_DEFAULT_PASSWORD) != RT_EOK) - { - rt_kprintf("Finsh password set failed.\n"); - } + rt_list_remove(&history->list); + finsh_history_free(history); } - /* waiting authenticate success */ - finsh_wait_auth(); -#endif - - rt_kprintf(FINSH_PROMPT); +#endif /* FINSH_USING_HISTORY */ - while (1) +#ifdef FINSH_USING_SNAPSHOT + rt_list_for_each_entry_safe(snap, s, &shell->snapshot_list, list) { - ch = (int)finsh_getchar(); - if (ch < 0) - { - continue; - } + rt_list_remove(&snap->list); + finsh_snapshot_free(snap); + } +#endif /* FINSH_USING_SNAPSHOT */ - /* - * handle control key - * up key : 0x1b 0x5b 0x41 - * down key: 0x1b 0x5b 0x42 - * right key:0x1b 0x5b 0x43 - * left key: 0x1b 0x5b 0x44 - * home : 0x1b 0x5b 0x31 0x7E - * insert : 0x1b 0x5b 0x32 0x7E - * del : 0x1b 0x5b 0x33 0x7E - * end : 0x1b 0x5b 0x34 0x7E - */ - if (ch == 0x1b) - { - shell->stat = WAIT_SPEC_KEY; - continue; - } - else if (shell->stat == WAIT_SPEC_KEY) - { - if (ch == 0x5b || ch == 0x41 || ch == 0x42 || ch == 0x43 || ch == 0x44) - { - shell->stat = WAIT_FUNC_KEY; - continue; - } +#if !defined(RT_USING_POSIX_STDIO) && defined(RT_USING_DEVICE) + if (shell->device) + { + rt_device_close(shell->device); + rt_device_set_rx_indicate(shell->device, NULL); + } + shell->device = NULL; +#endif /* !defined(RT_USING_POSIX_STDIO) && defined(RT_USING_DEVICE) */ - shell->stat = WAIT_NORMAL; - } - else if (shell->stat == WAIT_FUNC_KEY) - { - shell->stat = WAIT_NORMAL; + rt_sem_detach(&shell->rx_notice); +} - if (ch == 0x41) /* up key */ - { -#ifdef FINSH_USING_HISTORY - /* prev history */ - if (shell->current_history > 0) - shell->current_history --; - else - { - shell->current_history = 0; - continue; - } - - /* copy the history command */ - rt_memcpy(shell->line, &shell->cmd_history[shell->current_history][0], - FINSH_CMD_SIZE); - shell->line_curpos = shell->line_position = (rt_uint16_t)rt_strlen(shell->line); - shell_handle_history(shell); -#endif - continue; - } - else if (ch == 0x42) /* down key */ - { -#ifdef FINSH_USING_HISTORY - /* next history */ - if (shell->current_history < shell->history_count - 1) - shell->current_history ++; - else - { - /* set to the end of history */ - if (shell->history_count != 0) - shell->current_history = shell->history_count - 1; - else - continue; - } - - rt_memcpy(shell->line, &shell->cmd_history[shell->current_history][0], - FINSH_CMD_SIZE); - shell->line_curpos = shell->line_position = (rt_uint16_t)rt_strlen(shell->line); - shell_handle_history(shell); -#endif - continue; - } - else if (ch == 0x44) /* left key */ - { - if (shell->line_curpos) - { - rt_kprintf("\b"); - shell->line_curpos --; - } +/** + * @brief Get a character from the shell input. + * + * This function reads a single character from the shell input source. + * - If RT_USING_DEVICE is defined, it reads from the configured device. + * - If RT_USING_POSIX_STDIO is defined, it reads from the POSIX console file descriptor. + * - Otherwise, it reads from the shell's device using rt_device_read. If no data is available, + * it waits on the rx_notice semaphore until data arrives. If the device is unavailable, it returns -1 (EOF). + * - If RT_USING_DEVICE is not defined, it reads from the hardware console using rt_hw_console_getchar. + * + * @return The character read on success, or -1 on failure or EOF. + */ +char finsh_getchar(void) +{ +#ifdef RT_USING_DEVICE + char ch; + int ret; +#ifdef RT_USING_POSIX_STDIO + return (read(rt_posix_stdio_get_console(), &ch, 1) > 0) ? ch : -1; +#else + RT_ASSERT(shell != NULL); - continue; - } - else if (ch == 0x43) /* right key */ - { - if (shell->line_curpos < shell->line_position) - { - rt_kprintf("%c", shell->line[shell->line_curpos]); - shell->line_curpos ++; - } + if (!shell->device) + return -1; /* EOF */ - continue; - } -#if defined(FINSH_USING_WORD_OPERATION) - /* Add Ctrl+Left/Right handling */ - else if (ch == '1') - { - /* Read modifier sequence [1;5D/C] */ - int next_ch = finsh_getchar(); - if (next_ch == ';') - { - next_ch = finsh_getchar(); - if (next_ch == '5') - { - next_ch = finsh_getchar(); - if (next_ch == 'D') /* Ctrl+Left */ - { - int new_pos = find_prev_word_start(shell->line, shell->line_curpos); - if (new_pos != shell->line_curpos) - { - rt_kprintf("\033[%dD", shell->line_curpos - new_pos); - shell->line_curpos = new_pos; - } - continue; - } - else if (next_ch == 'C') /* Ctrl+Right */ - { - int new_pos = find_next_word_end(shell->line, shell->line_curpos, shell->line_position); - if (new_pos != shell->line_curpos) - { - rt_kprintf("\033[%dC", new_pos - shell->line_curpos); - shell->line_curpos = new_pos; - } - continue; - } - } - } - } -#endif /*defined(FINSH_USING_WORD_OPERATION) */ -#if defined(FINSH_USING_FUNC_EXT) - else if (ch >= 0x31 && ch <= 0x34) /* home(0x31), insert(0x32), del(0x33), end(0x34) */ - { - shell->stat = WAIT_EXT_KEY; - shell->line[shell->line_position + 1] = ch; /* store the key code */ - continue; - } + ch = 0; + while (rt_device_read(shell->device, -1, &ch, 1) != 1) + { + ret = rt_sem_take(&shell->rx_notice, RT_WAITING_FOREVER); + if (ret || !shell->device) + return -1; + } - } - else if (shell->stat == WAIT_EXT_KEY) - { - shell->stat = WAIT_NORMAL; + return ch; +#endif /* RT_USING_POSIX_STDIO */ +#else + extern signed char rt_hw_console_getchar(void); + return rt_hw_console_getchar(); +#endif /* RT_USING_DEVICE */ +} + +/* clang-format off */ + #define finsh_printf(fmt, ...) do { if (shell->is_echo) rt_kprintf(fmt, ##__VA_ARGS__); } while (0) + #define finsh_puts(str) do { if (shell->is_echo) rt_kputs(str); } while (0) + #define finsh_putc(ch) do { if (shell->is_echo) rt_kprintf("%c", ch); } while (0) +/* clang-format on */ - if (ch == 0x7E) /* extended key terminator */ +#ifdef FINSH_USING_SNAPSHOT +rt_inline int _is_snapshot_last(rt_list_t *node) +{ + return node == shell->snapshot_list.next; +} + +static void finsh_push_snapshot(void) +{ + struct finsh_snapshot *snap, *pos, *n; + rt_list_t *node; + uint32_t len; + + /* if current snapshot is not the last of the snapshot list, use the current snapshot */ + node = !_is_snapshot_last(shell->cur_snapshot) ? shell->cur_snapshot : NULL; + if (node) + snap = rt_list_entry(node, struct finsh_snapshot, list); + else + { + len = rt_list_len(&shell->snapshot_list); + if (len > 0) + { + node = shell->cur_snapshot; + snap = rt_list_entry(node, struct finsh_snapshot, list); + if (snap->cmd_length == shell->cmd_length && !rt_strncmp(snap->cmd, shell->cmd, snap->cmd_length)) { - rt_uint8_t key_code = shell->line[shell->line_position + 1]; - - if (key_code == 0x31) /* home key */ - { - /* move cursor to beginning of line */ - while (shell->line_curpos > 0) - { - rt_kprintf("\b"); - shell->line_curpos--; - } - } - else if (key_code == 0x32) /* insert key */ - { - /* toggle insert mode */ - shell->overwrite_mode = !shell->overwrite_mode; - } - else if (key_code == 0x33) /* del key */ - { - /* delete character at current cursor position */ - if (shell->line_curpos < shell->line_position) - { - int i; - shell->line_position--; - rt_memmove(&shell->line[shell->line_curpos], - &shell->line[shell->line_curpos + 1], - shell->line_position - shell->line_curpos); - - shell->line[shell->line_position] = 0; - - rt_kprintf("%s ", &shell->line[shell->line_curpos]); - - /* move cursor back to original position */ - for (i = shell->line_curpos; i <= shell->line_position; i++) - rt_kprintf("\b"); - } - } - else if (key_code == 0x34) /* end key */ - { - /* move cursor to end of line */ - while (shell->line_curpos < shell->line_position) - { - rt_kprintf("%c", shell->line[shell->line_curpos]); - shell->line_curpos++; - } - } - continue; + shell->is_push_last_snapshot = RT_FALSE; + return; } -#endif /*defined(FINSH_USING_FUNC_EXT) */ } - /* received null or error */ - if (ch == '\0' || ch == 0xFF) continue; - /* handle tab key */ - else if (ch == '\t') + if (len < FINSH_SNAPSHOT_DEPTH) + snap = NULL; + else { - int i; - /* move the cursor to the beginning of line */ - for (i = 0; i < shell->line_curpos; i++) - rt_kprintf("\b"); + node = shell->snapshot_list.prev; + snap = rt_list_entry(node, struct finsh_snapshot, list); + rt_list_remove(&snap->list); + } + } - /* auto complete */ - shell_auto_complete(&shell->line[0]); - /* re-calculate position */ - shell->line_curpos = shell->line_position = (rt_uint16_t)rt_strlen(shell->line); + snap = finsh_snapshot_realloc(snap, shell->cmd, shell->cmd_length, shell->cmd_cursor); + if (!snap) + { + rt_kprintf("Failed to allocate memory for snapshot!\n"); + return; + } - continue; - } - /* handle backspace key */ - else if (ch == 0x7f || ch == 0x08) + /* if current snapshot is not the top of the snapshot list, remove the snapshot list */ + if (!_is_snapshot_last(shell->cur_snapshot)) + { + rt_list_for_each_entry_safe(pos, n, &shell->snapshot_list, list) { - /* note that shell->line_curpos >= 0 */ - if (shell->line_curpos == 0) - continue; + rt_list_remove(&pos->list); + if (&pos->list == shell->cur_snapshot) + break; + finsh_snapshot_free(pos); + } + } - shell->line_position--; - shell->line_curpos--; + rt_list_insert_after(&shell->snapshot_list, &snap->list); + /* set the current snapshot to the new snapshot, the "cur_snapshot" always starts from the first snapshot. */ + shell->cur_snapshot = &snap->list; + shell->is_push_last_snapshot = RT_FALSE; +} +#endif /* FINSH_USING_SNAPSHOT */ - if (shell->line_position > shell->line_curpos) - { - int i; +static void finsh_handle_backspace_key(void) +{ + size_t cursor; + int i; + +#ifdef FINSH_USING_SNAPSHOT + if (shell->cmd_length > 0 && shell->snapshot_state != FINSH_SNAPSHOT_STATE_BACKSPACE) + { + finsh_push_snapshot(); + shell->snapshot_state = FINSH_SNAPSHOT_STATE_BACKSPACE; + } +#endif /* FINSH_USING_SNAPSHOT */ - rt_memmove(&shell->line[shell->line_curpos], - &shell->line[shell->line_curpos + 1], - shell->line_position - shell->line_curpos); - shell->line[shell->line_position] = 0; + /* if cursor is at the beginning of the command, do nothing */ + if (shell->cmd_cursor == 0) + return; - rt_kprintf("\b%s \b", &shell->line[shell->line_curpos]); + /* move index to the previous position */ + shell->cmd_length--; + shell->cmd_cursor--; - /* move the cursor to the origin position */ - for (i = shell->line_curpos; i <= shell->line_position; i++) - rt_kprintf("\b"); - } - else - { - rt_kprintf("\b \b"); - shell->line[shell->line_position] = 0; - } + if (shell->cmd_cursor >= shell->cmd_length) + { + finsh_puts("\b \b"); + shell->cmd[shell->cmd_length] = 0; + return; + } - continue; - } -#if defined(FINSH_USING_WORD_OPERATION) - /* Add Ctrl+Backspace handling */ - else if (ch == 0x17) /* Ctrl+Backspace (typically ^W) */ + cursor = shell->cmd_cursor; + rt_memmove(&shell->cmd[cursor], &shell->cmd[cursor + 1], shell->cmd_length - cursor); + shell->cmd[shell->cmd_length] = 0; + + finsh_printf("\b%s \b", &shell->cmd[cursor]); + + /* move the cursor to the origin position */ + for (i = cursor; i < shell->cmd_length; i++) + finsh_putc('\b'); +} + +static void finsh_auto_complete(void) +{ + finsh_putc("\n"); + msh_auto_complete(shell->cmd); +#ifdef FINSH_USING_OPTION_COMPLETION + msh_opt_auto_complete(shell->cmd); +#endif /* FINSH_USING_OPTION_COMPLETION */ + finsh_printf("%s%s", FINSH_PROMPT, shell->cmd); + + /* re-calculate position */ + shell->cmd_cursor = strlen(shell->cmd); + shell->cmd_length = shell->cmd_cursor; +} + +#if defined(FINSH_USING_WORD_OPERATION) || defined(FINSH_USING_SNAPSHOT) || defined(FINSH_USING_HISTORY) +static void finsh_render_line(void) +{ +#ifdef _WIN32 + int i; + + finsh_putc('\r'); + for (i = 0; i <= 60; i++) + finsh_putc(' '); + finsh_putc('\r'); +#else /* _WIN32 */ + finsh_puts("\033[2K\r"); +#endif /* _WIN32 */ + finsh_printf("%s%s", FINSH_PROMPT, shell->cmd); + + if (shell->cmd_cursor < shell->cmd_length) + finsh_printf("\033[%dD", (int)(shell->cmd_length - shell->cmd_cursor)); +} +#endif /* FINSH_USING_WORD_OPERATION || FINSH_USING_SNAPSHOT || FINSH_USING_HISTORY */ + +#ifdef FINSH_USING_WORD_OPERATION // 123 456 789 +rt_inline int _is_word_char(char ch) +{ + return (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9') || ch == '_'; +} + +static void finsh_handle_ctrl_backspace_key(void) +{ + size_t cursor, start_cursor, delete_count; + rt_bool_t is_word_char; + + /* if cursor is at the beginning, do nothing */ + if (shell->cmd_cursor == 0) + return; + + start_cursor = shell->cmd_cursor; + cursor = shell->cmd_cursor; + while (cursor > 0 && shell->cmd[cursor - 1] == ' ') + cursor--; + is_word_char = cursor > 0 && _is_word_char(shell->cmd[cursor - 1]); + + /* find the start of the word to delete */ + while (cursor > 0 && is_word_char && _is_word_char(shell->cmd[cursor - 1])) + cursor--; + while (cursor > 0 && !is_word_char && !_is_word_char(shell->cmd[cursor - 1])) + cursor--; + + /* calculate how many characters to delete */ + delete_count = start_cursor - cursor; + if (delete_count == 0) + return; + +#ifdef FINSH_USING_SNAPSHOT + finsh_push_snapshot(); +#endif /* FINSH_USING_SNAPSHOT */ + + /* move the remaining characters to fill the gap */ + rt_memmove(&shell->cmd[cursor], &shell->cmd[start_cursor], shell->cmd_length - start_cursor); + shell->cmd_length -= delete_count; + rt_memset(&shell->cmd[shell->cmd_length], 0, delete_count); + shell->cmd_cursor = cursor; + + finsh_render_line(); +} +#endif /* FINSH_USING_WORD_OPERATION */ + +#ifdef FINSH_USING_SNAPSHOT +rt_inline int _is_snapshot_tail(rt_list_t *node) +{ + return node == shell->snapshot_list.prev; +} + +static void finsh_pop_snapshot(void) +{ + struct finsh_snapshot *snap; + rt_list_t *node; + + if (rt_list_isempty(&shell->snapshot_list)) + return; + + if (!shell->is_push_last_snapshot) + { + finsh_push_snapshot(); + shell->is_push_last_snapshot = RT_TRUE; + } + + if (_is_snapshot_tail(shell->cur_snapshot)) + return; + + node = shell->cur_snapshot->next; + snap = rt_list_entry(node, struct finsh_snapshot, list); + rt_memset(shell->cmd, 0, sizeof(shell->cmd)); + rt_strncpy(shell->cmd, snap->cmd, snap->cmd_length); + shell->cmd_length = snap->cmd_length; + shell->cmd_cursor = snap->cmd_cursor; + + finsh_render_line(); + shell->cur_snapshot = node; +} + +rt_inline int _is_snapshot_head(rt_list_t *node) +{ + return node == &shell->snapshot_list; +} + +static void finsh_rollback_snapshot(void) +{ + struct finsh_snapshot *snap; + rt_list_t *node; + + if (rt_list_isempty(&shell->snapshot_list)) + return; + + node = shell->cur_snapshot->prev; + if (_is_snapshot_head(node)) + return; + + snap = rt_list_entry(node, struct finsh_snapshot, list); + rt_memset(shell->cmd, 0, sizeof(shell->cmd)); + rt_strncpy(shell->cmd, snap->cmd, snap->cmd_length); + shell->cmd_length = snap->cmd_length; + shell->cmd_cursor = snap->cmd_cursor; + + finsh_render_line(); + shell->cur_snapshot = node; +} +#endif /* FINSH_USING_SNAPSHOT */ + +#ifdef FINSH_USING_HISTORY +rt_inline int _is_history_head(rt_list_t *node) +{ + return node == &shell->history_list; +} + +rt_inline int _is_history_last(rt_list_t *node) +{ + return node == shell->history_list.next; +} + +static void finsh_push_history(void) +{ + struct finsh_history *history; + rt_list_t *node; + uint32_t len; + + /* if command is empty, do nothing */ + if (shell->cmd_length == 0) + return; + + /* if current history is not the head of the history list, check if the command is the same as the current history */ + if (!_is_history_head(shell->cur_history)) + { + node = shell->cur_history; + history = rt_list_entry(node, struct finsh_history, list); + len = rt_strlen(history->cmd); + if (len == shell->cmd_length && !rt_strncmp(history->cmd, shell->cmd, len)) { - if (shell->line_curpos == 0) continue; - - int start = find_prev_word_start(shell->line, shell->line_curpos); - int del_count = shell->line_curpos - start; - int new_len = shell->line_position - del_count; - - /* Delete characters and properly add RT_NULL termination */ - rt_memmove(&shell->line[start], - &shell->line[start + del_count], - new_len - start + 1); - - /* Clear residual data */ - rt_memset(&shell->line[new_len], 0, shell->line_position - new_len); - - /* Update positions */ - shell->line_position = new_len; - shell->line_curpos = start; - - /* Redraw the affected line section */ - rt_kprintf("\033[%dD", del_count); - /* Rewrite the remaining content */ - rt_kprintf("%.*s", shell->line_position - start, &shell->line[start]); - /* Clear trailing artifacts */ - rt_kprintf("\033[K"); - if (shell->line_position > start) + if (!_is_history_last(node)) { - /* Reset cursor */ - rt_kprintf("\033[%dD", shell->line_position - start); + rt_list_remove(&history->list); + rt_list_insert_after(&shell->history_list, &history->list); + shell->cur_history = &history->list; } - - continue; + shell->is_push_last_history = RT_TRUE; + return; } -#endif /*defined(FINSH_USING_WORD_OPERATION) */ - /* handle end of line, break */ - if (ch == '\r' || ch == '\n') - { + } + + /* if the history list is not full, allocate a new history */ + len = rt_list_len(&shell->history_list); + if (len < FINSH_HISTORY_LINES) + history = NULL; + else + { + /* if the history list is full, remove the last history */ + node = shell->history_list.prev; + history = rt_list_entry(node, struct finsh_history, list); + rt_list_remove(&history->list); + } + + history = finsh_history_realloc(history, shell->cmd, shell->cmd_length); + if (!history) + { + rt_kprintf("Failed to allocate memory for new history!\n"); + return; + } + + /* insert the new history to the head of the history list */ + rt_list_insert_after(&shell->history_list, &history->list); + + shell->cur_history = &history->list; + shell->is_push_last_history = RT_TRUE; +} +#endif /* FINSH_USING_HISTORY */ + +static void finsh_handle_enter_key(void) +{ +#ifdef FINSH_USING_SNAPSHOT + struct finsh_snapshot *snap, *n; +#endif /* FINSH_USING_SNAPSHOT */ + #ifdef FINSH_USING_HISTORY - shell_push_history(shell); -#endif - if (shell->echo_mode) - rt_kprintf("\n"); - msh_exec(shell->line, shell->line_position); + finsh_push_history(); +#endif /* FINSH_USING_HISTORY */ - rt_kprintf(FINSH_PROMPT); - rt_memset(shell->line, 0, sizeof(shell->line)); - shell->line_curpos = shell->line_position = 0; - continue; - } + rt_kprintf("\n"); + msh_exec(shell->cmd, shell->cmd_length); + rt_kprintf(FINSH_PROMPT); + rt_memset(shell->cmd, 0, sizeof(shell->cmd)); + shell->cmd_cursor = 0; + shell->cmd_length = 0; - /* it's a large line, discard it */ - if (shell->line_position >= FINSH_CMD_SIZE) - shell->line_position = 0; +#ifdef FINSH_USING_SNAPSHOT + rt_list_for_each_entry_safe(snap, n, &shell->snapshot_list, list) + { + rt_list_remove(&snap->list); + finsh_snapshot_free(snap); + } + rt_list_init(&shell->snapshot_list); + shell->cur_snapshot = &shell->snapshot_list; + shell->snapshot_state = FINSH_SNAPSHOT_STATE_NONE; +#endif /* FINSH_USING_SNAPSHOT */ +} - /* normal character */ - if (shell->line_curpos < shell->line_position) +rt_inline void _finsh_add_key_by_insert(char ch) +{ + size_t cursor; + + cursor = shell->cmd_cursor; + shell->cmd[cursor] = ch; + finsh_putc(ch); + + shell->cmd_cursor++; +} + +rt_inline void _finsh_add_key_by_normal(char ch) +{ + size_t cursor; + int i; + + cursor = shell->cmd_cursor; + rt_memmove(&shell->cmd[cursor + 1], &shell->cmd[cursor], shell->cmd_length - cursor); + shell->cmd[cursor] = ch; + + finsh_puts(&shell->cmd[cursor]); + for (i = cursor; i < shell->cmd_length; i++) + finsh_putc('\b'); + + shell->cmd_cursor++; + shell->cmd_length++; +} + +static void finsh_handle_normal_key(char ch) +{ + switch (ch) + { + case 0x1B: /* ESC key */ + shell->input_state = FINSH_INPUT_STATE_SPECIFIED_KEY; + return; + case 0x08: /* backspace key */ + case 0x7F: + finsh_handle_backspace_key(); + return; + case 0x09: /* tab key */ + finsh_auto_complete(); + return; + case 0x17: /* Ctrl+W/Backspace delete word */ +#ifdef FINSH_USING_WORD_OPERATION + finsh_handle_ctrl_backspace_key(); +#endif /* FINSH_USING_WORD_OPERATION */ + return; + case 0x1A: /* Ctrl+Z pop snapshot */ +#ifdef FINSH_USING_SNAPSHOT + finsh_pop_snapshot(); +#endif /* FINSH_USING_SNAPSHOT */ + return; + case 0x19: /* Ctrl+Y rollback snapshot */ +#ifdef FINSH_USING_SNAPSHOT + finsh_rollback_snapshot(); +#endif /* FINSH_USING_SNAPSHOT */ + return; + case '\r': /* enter key */ + case '\n': /* newline key */ + finsh_handle_enter_key(); + return; + case ' ': /* space key */ +#ifdef FINSH_USING_SNAPSHOT + if (!shell->is_space_snapshot) { - int i; -#if defined(FINSH_USING_FUNC_EXT) - if (shell->overwrite_mode) /* overwrite mode */ - { - /* directly overwrite the character */ - shell->line[shell->line_curpos] = ch; - if (shell->echo_mode) - rt_kprintf("%c", ch); - shell->line_curpos++; - } - else /* insert mode */ -#endif /*defined(FINSH_USING_FUNC_EXT)*/ - { - shell->line_position++; - /* move existing characters to the right */ - rt_memmove(&shell->line[shell->line_curpos + 1], - &shell->line[shell->line_curpos], - shell->line_position - shell->line_curpos); - shell->line[shell->line_curpos] = ch; - - if (shell->echo_mode) - { - rt_kprintf("%s", &shell->line[shell->line_curpos]); - /* move cursor back to correct position */ - for (i = shell->line_curpos + 1; i < shell->line_position; i++) - rt_kprintf("\b"); - } - shell->line_curpos++; - } + shell->snapshot_state = FINSH_SNAPSHOT_STATE_SPACE; + shell->is_space_snapshot = RT_TRUE; } - else +#endif /* FINSH_USING_SNAPSHOT */ + break; + default: + if (ch > 0x7E || ch < 0x21) { - /* append character at end of line */ - shell->line[shell->line_position] = ch; - if (shell->echo_mode) - rt_kprintf("%c", ch); - shell->line_position++; - shell->line_curpos++; + rt_kprintf("Invalid character: %d!\n", ch); + return; } +#ifdef FINSH_USING_SNAPSHOT + shell->is_space_snapshot = RT_FALSE; +#endif /* FINSH_USING_SNAPSHOT */ + break; + } + +#ifdef FINSH_USING_SNAPSHOT + if (shell->snapshot_state != FINSH_SNAPSHOT_STATE_NORMAL) + { + finsh_push_snapshot(); + shell->snapshot_state = FINSH_SNAPSHOT_STATE_NORMAL; + } +#endif /* FINSH_USING_SNAPSHOT */ + + if (shell->cmd_length >= FINSH_CMD_SIZE) + { + shell->cmd_length = 0; + shell->cmd_cursor = 0; + } + + if (shell->cmd_cursor >= shell->cmd_length) + { + shell->cmd[shell->cmd_length] = ch; + finsh_putc(ch); + shell->cmd_length++; + shell->cmd_cursor++; + } + else + shell->is_insert ? _finsh_add_key_by_insert(ch) : _finsh_add_key_by_normal(ch); +} + +rt_inline void _finsh_handle_specified_key(char ch) +{ +#ifdef FINSH_USING_SNAPSHOT + if (shell->cmd_length > 0 && shell->snapshot_state != FINSH_SNAPSHOT_STATE_ESC) + { + finsh_push_snapshot(); + shell->snapshot_state = FINSH_SNAPSHOT_STATE_ESC; + } +#endif /* FINSH_USING_SNAPSHOT */ + shell->input_state = ch == 0x5B ? FINSH_INPUT_STATE_FUNCTION_KEY : FINSH_INPUT_STATE_NORMAL; +} + +#ifdef FINSH_USING_HISTORY +static void finsh_pop_history(int ch) +{ + struct finsh_history *history; + rt_list_t *node; + + if (rt_list_isempty(&shell->history_list)) + return; + + node = shell->cur_history; + if (!shell->is_push_last_history) + node = ch == 0x41 ? node->next : node->prev; + else + shell->is_push_last_history = RT_FALSE; + + if (_is_history_head(node)) + node = shell->cur_history; + else + shell->cur_history = node; + + history = rt_list_entry(node, struct finsh_history, list); + shell->cmd_length = rt_strlen(history->cmd); + shell->cmd_cursor = shell->cmd_length; + rt_memset(shell->cmd, 0, sizeof(shell->cmd)); + rt_memcpy(shell->cmd, history->cmd, shell->cmd_length); + + finsh_render_line(); +} +#endif /* FINSH_USING_HISTORY */ + +rt_inline void _finsh_handle_left_key(void) +{ + if (shell->cmd_cursor > 0) + { + finsh_putc('\b'); + shell->cmd_cursor--; + } +} + +rt_inline void _finsh_handle_right_key(void) +{ + if (shell->cmd_cursor < shell->cmd_length) + { + finsh_putc(shell->cmd[shell->cmd_cursor]); + shell->cmd_cursor++; + } +} + +#ifdef FINSH_USING_EXTEND_FEATURE +rt_inline void _finsh_handle_home_key(void) +{ + int i; + + for (i = 0; i < shell->cmd_cursor; i++) + finsh_putc('\b'); + shell->cmd_cursor = 0; +} + +rt_inline void _finsh_handle_end_key(void) +{ + int i; + + for (i = shell->cmd_cursor; i < shell->cmd_length; i++) + finsh_putc(shell->cmd[i]); + shell->cmd_cursor = shell->cmd_length; +} +#endif /* FINSH_USING_EXTEND_FEATURE */ + +static void finsh_handle_function_key(char ch) +{ + /* if the key is the extend key, set the input state to extend key */ + if (ch >= 0x31 && ch <= 0x34) + { + shell->extend_key = ch; + shell->input_state = FINSH_INPUT_STATE_EXTEND_KEY; + return; + } - ch = 0; - if (shell->line_position >= FINSH_CMD_SIZE) + switch (ch) + { + case 0x41: /* up key */ + case 0x42: /* down key */ +#ifdef FINSH_USING_HISTORY + finsh_pop_history(ch); +#endif /* FINSH_USING_HISTORY */ + break; + case 0x44: /* left key */ + _finsh_handle_left_key(); + break; + case 0x43: /* right key */ + _finsh_handle_right_key(); + break; + case 0x46: /* end key */ +#ifdef FINSH_USING_EXTEND_FEATURE + _finsh_handle_end_key(); +#endif /* FINSH_USING_EXTEND_FEATURE */ + break; + case 0x48: /* home key */ +#ifdef FINSH_USING_EXTEND_FEATURE + _finsh_handle_home_key(); +#endif /* FINSH_USING_EXTEND_FEATURE */ + break; + default: + rt_kprintf("Invalid function key: %d!\n", ch); + break; + } + + shell->input_state = FINSH_INPUT_STATE_NORMAL; +} + +#ifdef FINSH_USING_EXTEND_FEATURE +static void finsh_handle_delete_key(void) +{ + size_t cursor; + int i; + + /* if cursor is at the end of the command, do nothing */ + if (shell->cmd_cursor >= shell->cmd_length) + return; + + /* move index to the previous position */ + shell->cmd_length--; + + cursor = shell->cmd_cursor; + rt_memmove(&shell->cmd[cursor], &shell->cmd[cursor + 1], shell->cmd_length - cursor); + shell->cmd[shell->cmd_length] = 0; + + finsh_printf("%s \b", &shell->cmd[cursor]); + + /* move the cursor to the origin position */ + for (i = cursor; i < shell->cmd_length; i++) + finsh_putc('\b'); +} +#endif /* FINSH_USING_EXTEND_FEATURE */ + +static void finsh_handle_extend_key(char ch) +{ + /* if not the end of the key, do nothing */ + if (ch != 0x7E) + { + shell->extend_key = 0; + shell->input_state = ch == 0x3B ? FINSH_INPUT_STATE_CTRL_SEQUENCE : FINSH_INPUT_STATE_NORMAL; + return; + } + +#ifdef FINSH_USING_EXTEND_FEATURE + switch (shell->extend_key) + { + case 0x32: /* insert key */ + shell->is_insert = !shell->is_insert; + break; + case 0x33: /* delete key */ + finsh_handle_delete_key(); + break; + case 0x31: /* home key */ + _finsh_handle_home_key(); + break; + case 0x34: /* end key */ + _finsh_handle_end_key(); + break; + default: + rt_kprintf("Invalid extend key: %d!\n", shell->extend_key); + break; + } +#endif /* FINSH_USING_EXTEND_FEATURE */ + + /* reset extend key */ + shell->extend_key = 0; + shell->input_state = FINSH_INPUT_STATE_NORMAL; +} + +#ifdef FINSH_USING_WORD_OPERATION +static void finsh_handle_ctrl_left_key(void) +{ + size_t cursor; + rt_bool_t is_word_char; + + /* if already at the beginning, do nothing */ + if (shell->cmd_cursor == 0) + return; + + cursor = shell->cmd_cursor; + while (cursor > 0 && shell->cmd[cursor - 1] == ' ') + cursor--; + is_word_char = cursor > 0 && _is_word_char(shell->cmd[cursor - 1]); + + while (cursor > 0 && is_word_char && _is_word_char(shell->cmd[cursor - 1])) + cursor--; + while (cursor > 0 && !is_word_char && !_is_word_char(shell->cmd[cursor - 1])) + cursor--; + + /* calculate steps to move and move cursor */ + finsh_printf("\033[%dD", shell->cmd_cursor - cursor); + + shell->cmd_cursor = cursor; +} + +static void finsh_handle_ctrl_right_key(void) +{ + size_t cursor; + rt_bool_t is_word_char; + + /* if already at the end, do nothing */ + if (cursor >= shell->cmd_length) + return; + + cursor = shell->cmd_cursor; + while (cursor < shell->cmd_length && shell->cmd[cursor] == ' ') + cursor++; + is_word_char = cursor < shell->cmd_length && _is_word_char(shell->cmd[cursor]); + + while (cursor < shell->cmd_length && is_word_char && _is_word_char(shell->cmd[cursor])) + cursor++; + while (cursor < shell->cmd_length && !is_word_char && !_is_word_char(shell->cmd[cursor])) + cursor++; + + /* display moved characters */ + finsh_printf("\033[%dC", cursor - shell->cmd_cursor); + + shell->cmd_cursor = cursor; +} +#endif /* FINSH_USING_WORD_OPERATION */ + +static void finsh_handle_ctrl_sequence(char ch) +{ + if (!shell->is_ctrl) + { + /* if the key is the ctrl key, set the input state to ctrl sequence */ + if (ch == 0x35) + shell->is_ctrl = RT_TRUE; + else + shell->input_state = FINSH_INPUT_STATE_NORMAL; + return; + } + +#ifdef FINSH_USING_WORD_OPERATION + switch (ch) + { + case 0x44: /* Ctrl+left key */ + finsh_handle_ctrl_left_key(); + break; + case 0x43: /* Ctrl+right key */ + finsh_handle_ctrl_right_key(); + break; + case 0x7F: /* Ctrl+W/Backspace delete word */ + finsh_handle_ctrl_backspace_key(); + break; + default: + rt_kprintf("Invalid Ctrl sequence: %d!\n", ch); + break; + } +#endif /* FINSH_USING_WORD_OPERATION */ + + shell->is_ctrl = RT_FALSE; + shell->input_state = FINSH_INPUT_STATE_NORMAL; +} + +static void finsh_thread_entry(void *parameter) +{ + char ch; + + RT_OBJECT_HOOK_CALL(_finsh_thread_entry_hook, ()); + + if (finsh_shell_init()) + return; + +#ifdef FINSH_USING_AUTH + /* set the default password when the password isn't setting */ + if (!rt_strlen(finsh_get_password())) + finsh_set_password(FINSH_DEFAULT_PASSWORD); + /* waiting authenticate success */ + finsh_password_auth(); +#endif /* FINSH_USING_AUTH */ + + rt_kprintf(FINSH_PROMPT); + while (1) + { + ch = finsh_getchar(); + if (ch <= 0) + continue; + + /* + * handle control key + * up key: 0x1B 0x5B 0x41 + * down key: 0x1B 0x5B 0x42 + * right key: 0x1B 0x5B 0x43 + * left key: 0x1B 0x5B 0x44 + * end key: 0x1B 0x5B 0x46 + * home key: 0x1B 0x5B 0x48 + * + * handle extend key + * home key: 0x1B 0x5B 0x31 0x7E + * insert key: 0x1B 0x5B 0x32 0x7E + * delete key: 0x1B 0x5B 0x33 0x7E + * end key: 0x1B 0x5B 0x34 0x7E + * + * handle Ctrl sequence + * Ctrl+left: 0x1B 0x5B 0x31 0x3B 0x35 0x44 (jump to previous word) + * Ctrl+right: 0x1B 0x5B 0x31 0x3B 0x35 0x43 (jump to next word) + * Ctrl+backspace/w:0x1B 0x5B 0x31 0x3B 0x35 0x7F (delete word) + */ + + switch (shell->input_state) { - /* clear command line */ - shell->line_position = 0; - shell->line_curpos = 0; + case FINSH_INPUT_STATE_NORMAL: + finsh_handle_normal_key(ch); + break; + case FINSH_INPUT_STATE_SPECIFIED_KEY: + _finsh_handle_specified_key(ch); + break; + case FINSH_INPUT_STATE_FUNCTION_KEY: + finsh_handle_function_key(ch); + break; + case FINSH_INPUT_STATE_EXTEND_KEY: + finsh_handle_extend_key(ch); + break; + case FINSH_INPUT_STATE_CTRL_SEQUENCE: + finsh_handle_ctrl_sequence(ch); + break; + default: + rt_kprintf("Invalid input state: %d", shell->input_state); + break; } - } /* end of device read */ + } } static void finsh_system_function_init(const void *begin, const void *end) { - _syscall_table_begin = (struct finsh_syscall *) begin; - _syscall_table_end = (struct finsh_syscall *) end; + _syscall_table_begin = (struct finsh_syscall *)begin; + _syscall_table_end = (struct finsh_syscall *)end; } -#if defined(__ICCARM__) || defined(__ICCRX__) /* for IAR compiler */ +#if defined(__ICCARM__) || defined(__ICCRX__) /* for IAR compiler */ #ifdef FINSH_USING_SYMTAB - #pragma section="FSymTab" -#endif +#pragma section = "FSymTab" +#endif /* FINSH_USING_SYMTAB */ #elif defined(__ADSPBLACKFIN__) /* for VisaulDSP++ Compiler*/ #ifdef FINSH_USING_SYMTAB - extern "asm" int __fsymtab_start; - extern "asm" int __fsymtab_end; -#endif +extern "asm" int __fsymtab_start; +extern "asm" int __fsymtab_end; +#endif /* FINSH_USING_SYMTAB */ #elif defined(_MSC_VER) #pragma section("FSymTab$a", read) const char __fsym_begin_name[] = "__start"; const char __fsym_begin_desc[] = "begin of finsh"; -__declspec(allocate("FSymTab$a")) const struct finsh_syscall __fsym_begin = -{ +__declspec(allocate("FSymTab$a")) const struct finsh_syscall __fsym_begin = { __fsym_begin_name, __fsym_begin_desc, - NULL + NULL, }; #pragma section("FSymTab$z", read) const char __fsym_end_name[] = "__end"; const char __fsym_end_desc[] = "end of finsh"; -__declspec(allocate("FSymTab$z")) const struct finsh_syscall __fsym_end = -{ +__declspec(allocate("FSymTab$z")) const struct finsh_syscall __fsym_end = { __fsym_end_name, __fsym_end_desc, - NULL + NULL, }; -#endif +#endif /* _MSC_VER */ -/* - * @ingroup group_finsh - * - * This function will initialize finsh shell - */ +/** + * @brief Initialize the finsh shell system. + * + * This function initializes the finsh shell, including allocating memory for the shell structure, + * setting up the system call table if symbol table support is enabled, and creating or initializing + * the shell thread. It supports different compilers and memory allocation strategies (heap or static). + * If the shell is already initialized, it will not re-initialize. + * + * @return 0 on success, negative error code on failure. + */ int finsh_system_init(void) { - rt_err_t result = RT_EOK; rt_thread_t tid; + int ret; + + if (shell) + { + rt_kprintf("Finsh shell already initialized.\n"); + return 0; + } #ifdef FINSH_USING_SYMTAB -#ifdef __ARMCC_VERSION /* ARM C Compiler */ +#ifdef __ARMCC_VERSION /* ARM C Compiler */ extern const int FSymTab$$Base; extern const int FSymTab$$Limit; finsh_system_function_init(&FSymTab$$Base, &FSymTab$$Limit); -#elif defined (__ICCARM__) || defined(__ICCRX__) /* for IAR Compiler */ - finsh_system_function_init(__section_begin("FSymTab"), - __section_end("FSymTab")); -#elif defined (__GNUC__) || defined(__TI_COMPILER_VERSION__) || defined(__TASKING__) +#elif defined(__ICCARM__) || defined(__ICCRX__) /* for IAR Compiler */ + finsh_system_function_init(__section_begin("FSymTab"), __section_end("FSymTab")); +#elif defined(__GNUC__) || defined(__TI_COMPILER_VERSION__) || defined(__TASKING__) /* GNU GCC Compiler and TI CCS */ extern const int __fsymtab_start; extern const int __fsymtab_end; @@ -968,55 +1621,82 @@ int finsh_system_init(void) #elif defined(__ADSPBLACKFIN__) /* for VisualDSP++ Compiler */ finsh_system_function_init(&__fsymtab_start, &__fsymtab_end); #elif defined(_MSC_VER) - unsigned int *ptr_begin, *ptr_end; + rt_ubase_t *ptr_begin, *ptr_end; - if (shell) - { - rt_kprintf("finsh shell already init.\n"); - return RT_EOK; - } + ptr_begin = (rt_ubase_t *)&__fsym_begin; + ptr_begin += (sizeof(struct finsh_syscall) / sizeof(rt_ubase_t)); + while (*ptr_begin == 0) + ptr_begin++; - ptr_begin = (unsigned int *)&__fsym_begin; - ptr_begin += (sizeof(struct finsh_syscall) / sizeof(unsigned int)); - while (*ptr_begin == 0) ptr_begin ++; - - ptr_end = (unsigned int *) &__fsym_end; - ptr_end --; - while (*ptr_end == 0) ptr_end --; + ptr_end = (rt_ubase_t *)&__fsym_end; + ptr_end--; + while (*ptr_end == 0) + ptr_end--; finsh_system_function_init(ptr_begin, ptr_end); -#endif -#endif +#endif /* _MSC_VER */ +#endif /* FINSH_USING_SYMTAB */ #ifdef RT_USING_HEAP - /* create or set shell structure */ - shell = (struct finsh_shell *)rt_calloc(1, sizeof(struct finsh_shell)); - if (shell == RT_NULL) + shell = (struct finsh_shell *)rt_calloc(1, sizeof(*shell)); + if (!shell) + { + rt_kprintf("No memory for shell!\n"); + return -ENOMEM; + } + tid = rt_thread_create(FINSH_THREAD_NAME, finsh_thread_entry, NULL, FINSH_THREAD_STACK_SIZE, FINSH_THREAD_PRIORITY, 10); + if (!tid) { - rt_kprintf("no memory for shell\n"); - return -1; + rt_free(shell); + rt_kprintf("Create finsh thread failed!\n"); + return -ENOMEM; } - tid = rt_thread_create(FINSH_THREAD_NAME, - finsh_thread_entry, RT_NULL, - FINSH_THREAD_STACK_SIZE, FINSH_THREAD_PRIORITY, 10); #else + ret = rt_thread_init(&finsh_thread, FINSH_THREAD_NAME, finsh_thread_entry, NULL, &finsh_thread_stack[0], sizeof(finsh_thread_stack), FINSH_THREAD_PRIORITY, 10); + if (ret) + { + rt_kprintf("Init finsh thread failed!\n"); + return ret; + } shell = &_shell; tid = &finsh_thread; - result = rt_thread_init(&finsh_thread, - FINSH_THREAD_NAME, - finsh_thread_entry, RT_NULL, - &finsh_thread_stack[0], sizeof(finsh_thread_stack), - FINSH_THREAD_PRIORITY, 10); #endif /* RT_USING_HEAP */ - rt_sem_init(&(shell->rx_sem), "shrx", 0, 0); - finsh_set_prompt_mode(1); + return rt_thread_startup(tid); +} +INIT_APP_EXPORT(finsh_system_init); + +/** + * @brief Deinitialize the FinSH shell system. + * + * This function performs a complete deinitialization of the FinSH shell system. + * It first deinitializes the shell's internal state and resources, then closes + * and detaches the associated device if it exists. The function also finds and + * deletes the shell thread, and finally clears the shell structure memory. + * + * @return 0 on success. + */ +int finsh_system_deinit(void) +{ + rt_thread_t tid; + + RT_ASSERT(shell != NULL); + + finsh_shell_deinit(); + + tid = rt_thread_find(FINSH_THREAD_NAME); + if (tid) + { +#ifdef RT_USING_HEAP + rt_thread_delete(tid); +#else + rt_thread_detach(tid); +#endif /* RT_USING_HEAP */ + } + + rt_memset(shell, 0, sizeof(*shell)); - if (tid != NULL && result == RT_EOK) - rt_thread_startup(tid); return 0; } -INIT_APP_EXPORT(finsh_system_init); #endif /* RT_USING_FINSH */ - diff --git a/components/finsh/shell.h b/components/finsh/shell.h index 422c17e165a..494338653a1 100644 --- a/components/finsh/shell.h +++ b/components/finsh/shell.h @@ -5,7 +5,8 @@ * * Change Logs: * Date Author Notes - * 2011-06-02 Bernard Add finsh_get_prompt function declaration + * 2011-06-02 Bernard Add finsh_get_prompt function declaration. + * 2025-09-22 yiyi reconstruct the shell. */ #ifndef __SHELL_H__ @@ -14,97 +15,166 @@ #include #include "finsh.h" +#ifndef FINSH_THREAD_NAME +#define FINSH_THREAD_NAME "tshell" +#endif /* !FINSH_THREAD_NAME */ + #ifndef FINSH_THREAD_PRIORITY - #define FINSH_THREAD_PRIORITY 20 -#endif -#ifndef FINSH_THREAD_STACK_SIZE - #define FINSH_THREAD_STACK_SIZE 2048 -#endif -#ifndef FINSH_CMD_SIZE - #define FINSH_CMD_SIZE 80 -#endif +#define FINSH_THREAD_PRIORITY 20 +#endif /* !FINSH_THREAD_PRIORITY */ -#define FINSH_OPTION_ECHO 0x01 +#ifndef FINSH_THREAD_STACK_SIZE +#define FINSH_THREAD_STACK_SIZE 2048 +#endif /* !FINSH_THREAD_STACK_SIZE */ -#define FINSH_PROMPT finsh_get_prompt() -const char *finsh_get_prompt(void); -int finsh_set_prompt(const char *prompt); +#ifndef FINSH_CMD_SIZE +#define FINSH_CMD_SIZE 80 +#endif /* !FINSH_CMD_SIZE */ #ifdef FINSH_USING_HISTORY - #ifndef FINSH_HISTORY_LINES - #define FINSH_HISTORY_LINES 5 - #endif -#endif +#ifndef FINSH_HISTORY_LINES +#define FINSH_HISTORY_LINES 5 +#endif /* !FINSH_HISTORY_LINES */ +#endif /* FINSH_USING_HISTORY */ #ifdef FINSH_USING_AUTH - #ifndef FINSH_PASSWORD_MAX - #define FINSH_PASSWORD_MAX RT_NAME_MAX - #endif - #ifndef FINSH_PASSWORD_MIN - #define FINSH_PASSWORD_MIN 6 - #endif - #ifndef FINSH_DEFAULT_PASSWORD - #define FINSH_DEFAULT_PASSWORD "rtthread" - #endif +#ifndef FINSH_PASSWORD_MAX +#define FINSH_PASSWORD_MAX RT_NAME_MAX +#endif /* !FINSH_PASSWORD_MAX */ +#ifndef FINSH_PASSWORD_MIN +#define FINSH_PASSWORD_MIN 6 +#endif /* !FINSH_PASSWORD_MIN */ +#ifndef FINSH_DEFAULT_PASSWORD +#define FINSH_DEFAULT_PASSWORD "rtthread" +#endif /* !FINSH_DEFAULT_PASSWORD */ #endif /* FINSH_USING_AUTH */ -#ifndef FINSH_THREAD_NAME - #define FINSH_THREAD_NAME "tshell" -#endif +#ifdef FINSH_USING_SNAPSHOT +#ifndef FINSH_SNAPSHOT_DEPTH +#define FINSH_SNAPSHOT_DEPTH 32 +#endif /* !FINSH_SNAPSHOT_DEPTH */ +#endif /* FINSH_USING_SNAPSHOT */ + +#ifndef FINSH_PROMPT_WORD_DEFAULT +#define FINSH_PROMPT_WORD_DEFAULT "msh " +#endif /* !FINSH_PROMPT_WORD_DEFAULT */ -enum input_stat +enum finsh_input_state { - WAIT_NORMAL, - WAIT_SPEC_KEY, - WAIT_FUNC_KEY, - WAIT_EXT_KEY, + FINSH_INPUT_STATE_NORMAL, + FINSH_INPUT_STATE_SPECIFIED_KEY, + FINSH_INPUT_STATE_FUNCTION_KEY, + FINSH_INPUT_STATE_EXTEND_KEY, + FINSH_INPUT_STATE_CTRL_SEQUENCE, }; -struct finsh_shell -{ - struct rt_semaphore rx_sem; - enum input_stat stat; - - rt_uint8_t echo_mode: 1; - rt_uint8_t prompt_mode: 1; - rt_uint8_t overwrite_mode: 1; #ifdef FINSH_USING_HISTORY - rt_uint16_t current_history; - rt_uint16_t history_count; +struct finsh_history +{ +#ifndef RT_USING_HEAP + char cmd[FINSH_CMD_SIZE + 1]; +#else + char *cmd; /* cmd is the command string */ +#endif /* !RT_USING_HEAP */ + rt_list_t list; +}; +#endif /* FINSH_USING_HISTORY */ - char cmd_history[FINSH_HISTORY_LINES][FINSH_CMD_SIZE]; -#endif +#ifdef FINSH_USING_SNAPSHOT +enum finsh_snapshot_state +{ + FINSH_SNAPSHOT_STATE_NONE, + FINSH_SNAPSHOT_STATE_NORMAL, + FINSH_SNAPSHOT_STATE_ESC, + FINSH_SNAPSHOT_STATE_BACKSPACE, + FINSH_SNAPSHOT_STATE_SPACE, +}; - char line[FINSH_CMD_SIZE + 1]; - rt_uint16_t line_position; - rt_uint16_t line_curpos; +struct finsh_snapshot +{ +#ifndef RT_USING_HEAP + char cmd[FINSH_CMD_SIZE + 1]; +#else + char *cmd; /* cmd is the command string */ +#endif /* !RT_USING_HEAP */ + size_t cmd_length; + size_t cmd_cursor; + rt_list_t list; +}; +#endif /* FINSH_USING_SNAPSHOT */ -#if !defined(RT_USING_POSIX_STDIO) && defined(RT_USING_DEVICE) - rt_device_t device; -#endif +struct finsh_shell +{ + struct rt_semaphore rx_notice; + enum finsh_input_state input_state; + +#ifdef FINSH_USING_HISTORY + rt_list_t history_list; + rt_list_t *cur_history; + rt_bool_t is_push_last_history; +#ifndef RT_USING_HEAP + struct finsh_history history[FINSH_HISTORY_LINES]; +#endif /* !RT_USING_HEAP */ +#endif /* FINSH_USING_HISTORY */ + + char cmd[FINSH_CMD_SIZE + 1]; + size_t cmd_length; + size_t cmd_cursor; + + char extend_key; /* extend key is the key after the function key */ + rt_bool_t is_insert; /* insert mode is the mode to insert character at the cursor position */ + rt_bool_t is_ctrl; /* ctrl mode is the mode to handle ctrl sequence */ + + rt_bool_t is_echo; /* echo mode is the mode to echo the command */ + rt_bool_t is_prompt; /* prompt mode is the mode to prompt the command */ + +#ifdef FINSH_USING_SNAPSHOT + rt_list_t snapshot_list; + rt_list_t *cur_snapshot; + rt_bool_t is_push_last_snapshot; + enum finsh_snapshot_state snapshot_state; + rt_bool_t is_space_snapshot; +#ifndef RT_USING_HEAP + struct finsh_snapshot snapshot[FINSH_SNAPSHOT_DEPTH]; +#endif /* !RT_USING_HEAP */ +#endif /* FINSH_USING_SNAPSHOT */ #ifdef FINSH_USING_AUTH char password[FINSH_PASSWORD_MAX]; -#endif -}; +#endif /* FINSH_USING_AUTH */ -void finsh_set_echo(rt_uint32_t echo); -rt_uint32_t finsh_get_echo(void); +#if !defined(RT_USING_POSIX_STDIO) && defined(RT_USING_DEVICE) + struct rt_device *device; +#endif /* !defined(RT_USING_POSIX_STDIO) && defined(RT_USING_DEVICE) */ +}; int finsh_system_init(void); -const char *finsh_get_device(void); -int finsh_getchar(void); +int finsh_system_deinit(void); -rt_uint32_t finsh_get_prompt_mode(void); -void finsh_set_prompt_mode(rt_uint32_t prompt_mode); - -#ifdef FINSH_USING_AUTH - rt_err_t finsh_set_password(const char *password); - const char *finsh_get_password(void); -#endif +char finsh_getchar(void); #ifdef RT_USING_HOOK void finsh_thread_entry_sethook(void (*hook)(void)); #endif /* RT_USING_HOOK */ -#endif +int finsh_set_prompt_word(const char *prompt); +const char *finsh_get_prompt_word(void); +#define FINSH_PROMPT finsh_get_prompt_word() + +void finsh_set_prompt(rt_bool_t prompt); +rt_bool_t finsh_get_prompt(void); + +#if !defined(RT_USING_POSIX_STDIO) && defined(RT_USING_DEVICE) +void finsh_set_device(const char *device_name); +const char *finsh_get_device(void); +#endif /* !defined(RT_USING_POSIX_STDIO) && defined(RT_USING_DEVICE) */ + +void finsh_set_echo(rt_bool_t echo); +rt_bool_t finsh_get_echo(void); + +#ifdef FINSH_USING_AUTH +int finsh_set_password(const char *password); +const char *finsh_get_password(void); +#endif /* FINSH_USING_AUTH */ + +#endif /* __SHELL_H__ */ From 3bbdd61f316fcea10d0e266503210be49e76c5f7 Mon Sep 17 00:00:00 2001 From: youzuwei <1692157237@qq.com> Date: Mon, 1 Dec 2025 21:26:59 +0800 Subject: [PATCH 2/3] Fix return values in shell.c to use RT_ prefixed error codes and clean up comments for consistency. --- components/finsh/shell.c | 43 +++++++++++++++++++++------------------- 1 file changed, 23 insertions(+), 20 deletions(-) diff --git a/components/finsh/shell.c b/components/finsh/shell.c index 0352963fb6a..e0a47d4e536 100644 --- a/components/finsh/shell.c +++ b/components/finsh/shell.c @@ -35,7 +35,7 @@ * 9. 'Insert' to toggle insert mode. * #endif * 10. 'Tab' to auto complete. - * 11. 'Backspace' to delete character forward + * 11. 'Backspace' to delete character forward * 12. 'Enter' to execute command. */ @@ -114,14 +114,14 @@ void finsh_thread_entry_sethook(void (*hook)(void)) * * @param prompt The new prompt string to set. * - * @return 0 on success, -EINVAL if the prompt is NULL. + * @return 0 on success, -RT_EINVAL if the prompt is NULL. */ int finsh_set_prompt_word(const char *prompt) { if (!prompt) { rt_kprintf("Invalid prompt!\n"); - return -EINVAL; + return -RT_EINVAL; } rt_memset(finsh_prompt, 0, sizeof(finsh_prompt)); @@ -323,7 +323,7 @@ rt_bool_t finsh_get_echo(void) * * @param password The new password string to set. * - * @return 0 on success, -EINVAL if the password is NULL or its length is invalid. + * @return 0 on success, -RT_EINVAL if the password is NULL or its length is invalid. */ int finsh_set_password(const char *password) { @@ -335,14 +335,14 @@ int finsh_set_password(const char *password) if (!password) { rt_kprintf("Password is NULL!\n"); - return -EINVAL; + return -RT_EINVAL; } len = rt_strlen(password); if (len > FINSH_PASSWORD_MAX || len < FINSH_PASSWORD_MIN) { rt_kprintf("Password length is less than FINSH_PASSWORD_MIN(%d) or greater than FINSH_PASSWORD_MAX(%d)!\n", FINSH_PASSWORD_MIN, FINSH_PASSWORD_MAX); - return -EINVAL; + return -RT_EINVAL; } level = rt_hw_interrupt_disable(); @@ -438,14 +438,14 @@ static struct finsh_history *finsh_history_alloc(char *cmd, size_t cmd_length) if (i >= FINSH_HISTORY_LINES) { rt_kprintf("No available historical record buffer zone!\n"); - return NULL; + return RT_NULL; } #else history = (struct finsh_history *)rt_malloc(sizeof(*history)); if (!history) { rt_kprintf("Failed to allocate memory for history!\n"); - return NULL; + return RT_NULL; } history->cmd = (char *)rt_calloc(1, cmd_length + 1); @@ -453,7 +453,7 @@ static struct finsh_history *finsh_history_alloc(char *cmd, size_t cmd_length) { rt_free(history); rt_kprintf("Failed to allocate memory for history command!\n"); - return NULL; + return RT_NULL; } #endif /* !RT_USING_HEAP */ @@ -495,7 +495,7 @@ static struct finsh_history *finsh_history_realloc(struct finsh_history *history { rt_free(history); rt_kprintf("Failed to allocate memory for history command!\n"); - return NULL; + return RT_NULL; } #endif /* RT_USING_HEAP */ @@ -524,14 +524,14 @@ static struct finsh_snapshot *finsh_snapshot_alloc(char *cmd, size_t cmd_length, if (i >= FINSH_SNAPSHOT_DEPTH) { rt_kprintf("No available snapshot buffer zone!\n"); - return NULL; + return RT_NULL; } #else snap = (struct finsh_snapshot *)rt_malloc(sizeof(*snap)); if (!snap) { rt_kprintf("Failed to allocate memory for snapshot!\n"); - return NULL; + return RT_NULL; } snap->cmd = (char *)rt_calloc(1, cmd_length + 1); @@ -539,7 +539,7 @@ static struct finsh_snapshot *finsh_snapshot_alloc(char *cmd, size_t cmd_length, { rt_free(snap); rt_kprintf("Failed to allocate memory for snapshot command!\n"); - return NULL; + return RT_NULL; } #endif /* !RT_USING_HEAP */ @@ -583,7 +583,7 @@ static struct finsh_snapshot *finsh_snapshot_realloc(struct finsh_snapshot *snap { rt_free(snap); rt_kprintf("Failed to allocate memory for snapshot command!\n"); - return NULL; + return RT_NULL; } #endif /* RT_USING_HEAP */ @@ -610,7 +610,7 @@ static int finsh_shell_init(void) if (ret) { rt_kprintf("Failed to initialize shell_rx_notice semaphore!\n"); - return -EIO; + return -RT_EIO; } shell->input_state = FINSH_INPUT_STATE_NORMAL; @@ -669,7 +669,7 @@ static int finsh_shell_init(void) { rt_sem_detach(&shell->rx_notice); rt_kprintf("Finsh: can not find device!\n"); - return -ENODEV; + return -RT_ENOENT; } #endif /* !defined(RT_USING_POSIX_STDIO) && defined(RT_USING_DEVICE) */ @@ -731,10 +731,11 @@ char finsh_getchar(void) { #ifdef RT_USING_DEVICE char ch; - int ret; #ifdef RT_USING_POSIX_STDIO return (read(rt_posix_stdio_get_console(), &ch, 1) > 0) ? ch : -1; #else + int ret; + RT_ASSERT(shell != NULL); if (!shell->device) @@ -1597,8 +1598,11 @@ __declspec(allocate("FSymTab$z")) const struct finsh_syscall __fsym_end = { */ int finsh_system_init(void) { + rt_ubase_t *ptr_begin, *ptr_end; rt_thread_t tid; +#ifndef RT_USING_HEAP int ret; +#endif /* RT_USING_HEAP */ if (shell) { @@ -1621,7 +1625,6 @@ int finsh_system_init(void) #elif defined(__ADSPBLACKFIN__) /* for VisualDSP++ Compiler */ finsh_system_function_init(&__fsymtab_start, &__fsymtab_end); #elif defined(_MSC_VER) - rt_ubase_t *ptr_begin, *ptr_end; ptr_begin = (rt_ubase_t *)&__fsym_begin; ptr_begin += (sizeof(struct finsh_syscall) / sizeof(rt_ubase_t)); @@ -1642,14 +1645,14 @@ int finsh_system_init(void) if (!shell) { rt_kprintf("No memory for shell!\n"); - return -ENOMEM; + return -RT_ENOMEM; } tid = rt_thread_create(FINSH_THREAD_NAME, finsh_thread_entry, NULL, FINSH_THREAD_STACK_SIZE, FINSH_THREAD_PRIORITY, 10); if (!tid) { rt_free(shell); rt_kprintf("Create finsh thread failed!\n"); - return -ENOMEM; + return -RT_ENOMEM; } #else ret = rt_thread_init(&finsh_thread, FINSH_THREAD_NAME, finsh_thread_entry, NULL, &finsh_thread_stack[0], sizeof(finsh_thread_stack), FINSH_THREAD_PRIORITY, 10); From e54ec9b34feeb70e8f650aa7ff4a6283c9e97aff Mon Sep 17 00:00:00 2001 From: youzuwei <1692157237@qq.com> Date: Mon, 1 Dec 2025 22:22:35 +0800 Subject: [PATCH 3/3] Refactor shell.c and shell.h to replace NULL with RT_NULL, update size_t to rt_size_t. --- components/finsh/shell.c | 114 +++++++++++++++++++-------------------- components/finsh/shell.h | 10 ++-- 2 files changed, 61 insertions(+), 63 deletions(-) diff --git a/components/finsh/shell.c b/components/finsh/shell.c index e0a47d4e536..3d9e080d5f9 100644 --- a/components/finsh/shell.c +++ b/components/finsh/shell.c @@ -67,13 +67,13 @@ static struct finsh_shell _shell = { 0 }; /* finsh symtab */ #ifdef FINSH_USING_SYMTAB -struct finsh_syscall *_syscall_table_begin = NULL; -struct finsh_syscall *_syscall_table_end = NULL; +struct finsh_syscall *_syscall_table_begin = RT_NULL; +struct finsh_syscall *_syscall_table_end = RT_NULL; #endif /* FINSH_USING_SYMTAB */ -static struct finsh_shell *shell = NULL; +static struct finsh_shell *shell = RT_NULL; static char finsh_prompt[RT_CONSOLEBUF_SIZE + 1] = { 0 }; -static size_t finsh_prompt_length = 0; +static rt_size_t finsh_prompt_length = 0; #if defined(_MSC_VER) || (defined(__GNUC__) && defined(__x86_64__)) struct finsh_syscall *finsh_syscall_next(struct finsh_syscall *call) @@ -110,11 +110,11 @@ void finsh_thread_entry_sethook(void (*hook)(void)) * * This function sets the prompt string for the FinSH shell. It copies the given prompt * to the internal prompt buffer, ensuring it does not exceed the maximum buffer size. - * If the input prompt is NULL, it prints an error message and returns an error code. + * If the input prompt is RT_NULL, it prints an error message and returns an error code. * * @param prompt The new prompt string to set. * - * @return 0 on success, -RT_EINVAL if the prompt is NULL. + * @return 0 on success, -RT_EINVAL if the prompt is RT_NULL. */ int finsh_set_prompt_word(const char *prompt) { @@ -144,9 +144,9 @@ int finsh_set_prompt_word(const char *prompt) */ const char *finsh_get_prompt_word(void) { - size_t len; + rt_size_t len; - RT_ASSERT(shell != NULL); + RT_ASSERT(shell != RT_NULL); /* check prompt mode */ if (!shell->is_prompt) @@ -183,7 +183,7 @@ const char *finsh_get_prompt_word(void) */ void finsh_set_prompt(rt_bool_t prompt) { - RT_ASSERT(shell != NULL); + RT_ASSERT(shell != RT_NULL); shell->is_prompt = prompt; } @@ -197,14 +197,14 @@ void finsh_set_prompt(rt_bool_t prompt) */ rt_bool_t finsh_get_prompt(void) { - RT_ASSERT(shell != NULL); + RT_ASSERT(shell != RT_NULL); return shell->is_prompt; } #if !defined(RT_USING_POSIX_STDIO) && defined(RT_USING_DEVICE) static rt_err_t finsh_rx_ind(rt_device_t dev, rt_size_t size) { - RT_ASSERT(shell != NULL); + RT_ASSERT(shell != RT_NULL); if (size) rt_sem_release(&shell->rx_notice); @@ -226,14 +226,14 @@ static rt_err_t finsh_rx_ind(rt_device_t dev, rt_size_t size) void finsh_set_device(const char *device_name) { struct rt_device *device; - uint16_t oflag; + rt_uint16_t oflag; int ret; #ifdef RT_USING_SERIAL_V2 int rxto; #endif /* RT_USING_SERIAL_V2 */ - RT_ASSERT(shell != NULL); - RT_ASSERT(device_name != NULL); + RT_ASSERT(shell != RT_NULL); + RT_ASSERT(device_name != RT_NULL); device = rt_device_find(device_name); if (!device) @@ -266,7 +266,7 @@ void finsh_set_device(const char *device_name) if (shell->device) { rt_device_close(shell->device); - rt_device_set_rx_indicate(shell->device, NULL); + rt_device_set_rx_indicate(shell->device, RT_NULL); } rt_device_set_rx_indicate(device, finsh_rx_ind); @@ -280,7 +280,7 @@ void finsh_set_device(const char *device_name) */ const char *finsh_get_device(void) { - RT_ASSERT(shell != NULL); + RT_ASSERT(shell != RT_NULL); return shell->device->parent.name; } #endif /* !defined(RT_USING_POSIX_STDIO) && defined(RT_USING_DEVICE) */ @@ -295,7 +295,7 @@ const char *finsh_get_device(void) */ void finsh_set_echo(rt_bool_t echo) { - RT_ASSERT(shell != NULL); + RT_ASSERT(shell != RT_NULL); shell->is_echo = echo; } @@ -309,7 +309,7 @@ void finsh_set_echo(rt_bool_t echo) */ rt_bool_t finsh_get_echo(void) { - RT_ASSERT(shell != NULL); + RT_ASSERT(shell != RT_NULL); return shell->is_echo; } @@ -318,23 +318,23 @@ rt_bool_t finsh_get_echo(void) * @brief Set a new password for the finsh shell. * * This function sets a new password for the finsh shell. It first checks whether the input password - * is valid (not NULL and its length is within the allowed range). If the password is valid, it copies + * is valid (not RT_NULL and its length is within the allowed range). If the password is valid, it copies * the new password into the shell's password buffer in a critical section to ensure thread safety. * * @param password The new password string to set. * - * @return 0 on success, -RT_EINVAL if the password is NULL or its length is invalid. + * @return 0 on success, -RT_EINVAL if the password is RT_NULL or its length is invalid. */ int finsh_set_password(const char *password) { - size_t len; + rt_size_t len; rt_base_t level; - RT_ASSERT(shell != NULL); + RT_ASSERT(shell != RT_NULL); if (!password) { - rt_kprintf("Password is NULL!\n"); + rt_kprintf("Password is RT_NULL!\n"); return -RT_EINVAL; } @@ -363,14 +363,14 @@ int finsh_set_password(const char *password) */ const char *finsh_get_password(void) { - RT_ASSERT(shell != NULL); + RT_ASSERT(shell != RT_NULL); return shell->password; } static void finsh_wait_input_password(char *password) { char ch; - size_t cursor = 0; + rt_size_t cursor = 0; while (1) { @@ -421,7 +421,7 @@ static void finsh_password_auth(void) #endif /* FINSH_USING_AUTH */ #ifdef FINSH_USING_HISTORY -static struct finsh_history *finsh_history_alloc(char *cmd, size_t cmd_length) +static struct finsh_history *finsh_history_alloc(char *cmd, rt_size_t cmd_length) { struct finsh_history *history; @@ -483,7 +483,7 @@ static void finsh_history_free(struct finsh_history *history) #endif /* RT_USING_HEAP */ } -static struct finsh_history *finsh_history_realloc(struct finsh_history *history, char *cmd, size_t cmd_length) +static struct finsh_history *finsh_history_realloc(struct finsh_history *history, char *cmd, rt_size_t cmd_length) { if (!history) return finsh_history_alloc(cmd, cmd_length); @@ -508,7 +508,7 @@ static struct finsh_history *finsh_history_realloc(struct finsh_history *history #endif /* FINSH_USING_HISTORY */ #ifdef FINSH_USING_SNAPSHOT -static struct finsh_snapshot *finsh_snapshot_alloc(char *cmd, size_t cmd_length, size_t cmd_cursor) +static struct finsh_snapshot *finsh_snapshot_alloc(char *cmd, rt_size_t cmd_length, rt_size_t cmd_cursor) { struct finsh_snapshot *snap; @@ -571,7 +571,7 @@ static void finsh_snapshot_free(struct finsh_snapshot *snap) #endif /* RT_USING_HEAP */ } -static struct finsh_snapshot *finsh_snapshot_realloc(struct finsh_snapshot *snap, char *cmd, size_t cmd_length, size_t cmd_cursor) +static struct finsh_snapshot *finsh_snapshot_realloc(struct finsh_snapshot *snap, char *cmd, rt_size_t cmd_length, rt_size_t cmd_cursor) { if (!snap) return finsh_snapshot_alloc(cmd, cmd_length, cmd_cursor); @@ -604,7 +604,7 @@ static int finsh_shell_init(void) #endif /* !defined(RT_USING_POSIX_STDIO) && defined(RT_USING_DEVICE) */ int ret; - RT_ASSERT(shell != NULL); + RT_ASSERT(shell != RT_NULL); ret = rt_sem_init(&shell->rx_notice, "shrx", 0, RT_IPC_FLAG_FIFO); if (ret) @@ -685,7 +685,7 @@ static void finsh_shell_deinit(void) struct finsh_snapshot *snap, *s; #endif /* AT_BYPASS_USING_SNAPSHOT */ - RT_ASSERT(shell != NULL); + RT_ASSERT(shell != RT_NULL); #ifdef FINSH_USING_HISTORY rt_list_for_each_entry_safe(history, n, &shell->history_list, list) @@ -707,9 +707,9 @@ static void finsh_shell_deinit(void) if (shell->device) { rt_device_close(shell->device); - rt_device_set_rx_indicate(shell->device, NULL); + rt_device_set_rx_indicate(shell->device, RT_NULL); } - shell->device = NULL; + shell->device = RT_NULL; #endif /* !defined(RT_USING_POSIX_STDIO) && defined(RT_USING_DEVICE) */ rt_sem_detach(&shell->rx_notice); @@ -736,7 +736,7 @@ char finsh_getchar(void) #else int ret; - RT_ASSERT(shell != NULL); + RT_ASSERT(shell != RT_NULL); if (!shell->device) return -1; /* EOF */ @@ -773,10 +773,10 @@ static void finsh_push_snapshot(void) { struct finsh_snapshot *snap, *pos, *n; rt_list_t *node; - uint32_t len; + rt_uint32_t len; /* if current snapshot is not the last of the snapshot list, use the current snapshot */ - node = !_is_snapshot_last(shell->cur_snapshot) ? shell->cur_snapshot : NULL; + node = !_is_snapshot_last(shell->cur_snapshot) ? shell->cur_snapshot : RT_NULL; if (node) snap = rt_list_entry(node, struct finsh_snapshot, list); else @@ -794,7 +794,7 @@ static void finsh_push_snapshot(void) } if (len < FINSH_SNAPSHOT_DEPTH) - snap = NULL; + snap = RT_NULL; else { node = shell->snapshot_list.prev; @@ -831,7 +831,7 @@ static void finsh_push_snapshot(void) static void finsh_handle_backspace_key(void) { - size_t cursor; + rt_size_t cursor; int i; #ifdef FINSH_USING_SNAPSHOT @@ -870,7 +870,7 @@ static void finsh_handle_backspace_key(void) static void finsh_auto_complete(void) { - finsh_putc("\n"); + finsh_putc('\n'); msh_auto_complete(shell->cmd); #ifdef FINSH_USING_OPTION_COMPLETION msh_opt_auto_complete(shell->cmd); @@ -878,7 +878,7 @@ static void finsh_auto_complete(void) finsh_printf("%s%s", FINSH_PROMPT, shell->cmd); /* re-calculate position */ - shell->cmd_cursor = strlen(shell->cmd); + shell->cmd_cursor = rt_strlen(shell->cmd); shell->cmd_length = shell->cmd_cursor; } @@ -893,7 +893,7 @@ static void finsh_render_line(void) finsh_putc(' '); finsh_putc('\r'); #else /* _WIN32 */ - finsh_puts("\033[2K\r"); + finsh_puts("\033[2K\r"); // 2K: clear the line, \r: return to the beginning of the line #endif /* _WIN32 */ finsh_printf("%s%s", FINSH_PROMPT, shell->cmd); @@ -910,7 +910,7 @@ rt_inline int _is_word_char(char ch) static void finsh_handle_ctrl_backspace_key(void) { - size_t cursor, start_cursor, delete_count; + rt_size_t cursor, start_cursor, delete_count; rt_bool_t is_word_char; /* if cursor is at the beginning, do nothing */ @@ -1025,7 +1025,7 @@ static void finsh_push_history(void) { struct finsh_history *history; rt_list_t *node; - uint32_t len; + rt_uint32_t len; /* if command is empty, do nothing */ if (shell->cmd_length == 0) @@ -1053,7 +1053,7 @@ static void finsh_push_history(void) /* if the history list is not full, allocate a new history */ len = rt_list_len(&shell->history_list); if (len < FINSH_HISTORY_LINES) - history = NULL; + history = RT_NULL; else { /* if the history list is full, remove the last history */ @@ -1108,7 +1108,7 @@ static void finsh_handle_enter_key(void) rt_inline void _finsh_add_key_by_insert(char ch) { - size_t cursor; + rt_size_t cursor; cursor = shell->cmd_cursor; shell->cmd[cursor] = ch; @@ -1119,7 +1119,7 @@ rt_inline void _finsh_add_key_by_insert(char ch) rt_inline void _finsh_add_key_by_normal(char ch) { - size_t cursor; + rt_size_t cursor; int i; cursor = shell->cmd_cursor; @@ -1338,7 +1338,7 @@ static void finsh_handle_function_key(char ch) #ifdef FINSH_USING_EXTEND_FEATURE static void finsh_handle_delete_key(void) { - size_t cursor; + rt_size_t cursor; int i; /* if cursor is at the end of the command, do nothing */ @@ -1399,7 +1399,7 @@ static void finsh_handle_extend_key(char ch) #ifdef FINSH_USING_WORD_OPERATION static void finsh_handle_ctrl_left_key(void) { - size_t cursor; + rt_size_t cursor; rt_bool_t is_word_char; /* if already at the beginning, do nothing */ @@ -1424,7 +1424,7 @@ static void finsh_handle_ctrl_left_key(void) static void finsh_handle_ctrl_right_key(void) { - size_t cursor; + rt_size_t cursor; rt_bool_t is_word_char; /* if already at the end, do nothing */ @@ -1573,7 +1573,7 @@ const char __fsym_begin_desc[] = "begin of finsh"; __declspec(allocate("FSymTab$a")) const struct finsh_syscall __fsym_begin = { __fsym_begin_name, __fsym_begin_desc, - NULL, + RT_NULL, }; #pragma section("FSymTab$z", read) @@ -1582,7 +1582,7 @@ const char __fsym_end_desc[] = "end of finsh"; __declspec(allocate("FSymTab$z")) const struct finsh_syscall __fsym_end = { __fsym_end_name, __fsym_end_desc, - NULL, + RT_NULL, }; #endif /* _MSC_VER */ @@ -1598,11 +1598,7 @@ __declspec(allocate("FSymTab$z")) const struct finsh_syscall __fsym_end = { */ int finsh_system_init(void) { - rt_ubase_t *ptr_begin, *ptr_end; rt_thread_t tid; -#ifndef RT_USING_HEAP - int ret; -#endif /* RT_USING_HEAP */ if (shell) { @@ -1625,6 +1621,7 @@ int finsh_system_init(void) #elif defined(__ADSPBLACKFIN__) /* for VisualDSP++ Compiler */ finsh_system_function_init(&__fsymtab_start, &__fsymtab_end); #elif defined(_MSC_VER) + rt_ubase_t *ptr_begin, *ptr_end; ptr_begin = (rt_ubase_t *)&__fsym_begin; ptr_begin += (sizeof(struct finsh_syscall) / sizeof(rt_ubase_t)); @@ -1647,7 +1644,7 @@ int finsh_system_init(void) rt_kprintf("No memory for shell!\n"); return -RT_ENOMEM; } - tid = rt_thread_create(FINSH_THREAD_NAME, finsh_thread_entry, NULL, FINSH_THREAD_STACK_SIZE, FINSH_THREAD_PRIORITY, 10); + tid = rt_thread_create(FINSH_THREAD_NAME, finsh_thread_entry, RT_NULL, FINSH_THREAD_STACK_SIZE, FINSH_THREAD_PRIORITY, 10); if (!tid) { rt_free(shell); @@ -1655,7 +1652,8 @@ int finsh_system_init(void) return -RT_ENOMEM; } #else - ret = rt_thread_init(&finsh_thread, FINSH_THREAD_NAME, finsh_thread_entry, NULL, &finsh_thread_stack[0], sizeof(finsh_thread_stack), FINSH_THREAD_PRIORITY, 10); + int ret; + ret = rt_thread_init(&finsh_thread, FINSH_THREAD_NAME, finsh_thread_entry, RT_NULL, &finsh_thread_stack[0], sizeof(finsh_thread_stack), FINSH_THREAD_PRIORITY, 10); if (ret) { rt_kprintf("Init finsh thread failed!\n"); @@ -1683,7 +1681,7 @@ int finsh_system_deinit(void) { rt_thread_t tid; - RT_ASSERT(shell != NULL); + RT_ASSERT(shell != RT_NULL); finsh_shell_deinit(); diff --git a/components/finsh/shell.h b/components/finsh/shell.h index 494338653a1..3fbe0efb74c 100644 --- a/components/finsh/shell.h +++ b/components/finsh/shell.h @@ -12,7 +12,7 @@ #ifndef __SHELL_H__ #define __SHELL_H__ -#include +#include #include "finsh.h" #ifndef FINSH_THREAD_NAME @@ -97,8 +97,8 @@ struct finsh_snapshot #else char *cmd; /* cmd is the command string */ #endif /* !RT_USING_HEAP */ - size_t cmd_length; - size_t cmd_cursor; + rt_size_t cmd_length; + rt_size_t cmd_cursor; rt_list_t list; }; #endif /* FINSH_USING_SNAPSHOT */ @@ -118,8 +118,8 @@ struct finsh_shell #endif /* FINSH_USING_HISTORY */ char cmd[FINSH_CMD_SIZE + 1]; - size_t cmd_length; - size_t cmd_cursor; + rt_size_t cmd_length; + rt_size_t cmd_cursor; char extend_key; /* extend key is the key after the function key */ rt_bool_t is_insert; /* insert mode is the mode to insert character at the cursor position */