diff --git a/include/shell/shell.h b/include/shell/shell.h index bbb4c60c45e..7a75ad074da 100644 --- a/include/shell/shell.h +++ b/include/shell/shell.h @@ -555,6 +555,9 @@ struct shell_ctx { /*!< Currently executed command.*/ struct shell_static_entry active_cmd; + /* New root command. If NULL shell uses default root commands. */ + const struct shell_static_entry *selected_cmd; + /*!< VT100 color and cursor position, terminal width.*/ struct shell_vt100_ctx vt100_ctx; diff --git a/subsys/shell/Kconfig b/subsys/shell/Kconfig index 908bb0e6206..4fe7070ef65 100644 --- a/subsys/shell/Kconfig +++ b/subsys/shell/Kconfig @@ -140,6 +140,13 @@ config SHELL_CMDS_RESIZE must be called to ensure correct text display on the terminal screen. The resize command can be turned off to save code memory (~0,5k). +config SHELL_CMDS_SELECT + bool "Enable select command" + depends on SHELL_CMDS + help + This option enables select command. It can be used to set new root + command. Exit to main command tree is with alt+r. + config SHELL_LOG_BACKEND bool default y if LOG diff --git a/subsys/shell/shell.c b/subsys/shell/shell.c index 79e5eff7907..a1044e5de06 100644 --- a/subsys/shell/shell.c +++ b/subsys/shell/shell.c @@ -207,62 +207,6 @@ static void history_handle(const struct shell *shell, bool up) shell_op_cond_next_line(shell); } -static const struct shell_static_entry *find_cmd( - const struct shell_cmd_entry *cmd, - size_t lvl, - char *cmd_str, - struct shell_static_entry *d_entry) -{ - const struct shell_static_entry *entry = NULL; - size_t idx = 0; - - do { - shell_cmd_get(cmd, lvl, idx++, &entry, d_entry); - if (entry && (strcmp(cmd_str, entry->syntax) == 0)) { - return entry; - } - } while (entry); - - return NULL; -} - -/** @brief Function for getting last valid command in list of arguments. */ -static const struct shell_static_entry *get_last_command( - const struct shell *shell, - size_t argc, - char *argv[], - size_t *match_arg, - struct shell_static_entry *d_entry) -{ - const struct shell_static_entry *prev_entry = NULL; - const struct shell_cmd_entry *prev_cmd = NULL; - const struct shell_static_entry *entry = NULL; - *match_arg = SHELL_CMD_ROOT_LVL; - - while (*match_arg < argc) { - - if (IS_ENABLED(CONFIG_SHELL_WILDCARD)) { - /* ignore wildcard argument */ - if (shell_wildcard_character_exist(argv[*match_arg])) { - (*match_arg)++; - continue; - } - } - - entry = find_cmd(prev_cmd, *match_arg, argv[*match_arg], - d_entry); - if (entry) { - prev_cmd = entry->subcmd; - prev_entry = entry; - (*match_arg)++; - } else { - break; - } - } - - return entry; -} - static inline u16_t completion_space_get(const struct shell *shell) { u16_t space = (CONFIG_SHELL_CMD_BUFF_SIZE - 1) - @@ -273,7 +217,7 @@ static inline u16_t completion_space_get(const struct shell *shell) /* Prepare arguments and return number of space available for completion. */ static bool tab_prepare(const struct shell *shell, const struct shell_static_entry **cmd, - char **argv, size_t *argc, + char ***argv, size_t *argc, size_t *complete_arg_idx, struct shell_static_entry *d_entry) { @@ -290,9 +234,16 @@ static bool tab_prepare(const struct shell *shell, shell->ctx->temp_buff[shell->ctx->cmd_buff_pos] = '\0'; /* Create argument list. */ - (void)shell_make_argv(argc, argv, shell->ctx->temp_buff, + (void)shell_make_argv(argc, *argv, shell->ctx->temp_buff, CONFIG_SHELL_ARGC_MAX); + if (IS_ENABLED(CONFIG_SHELL_CMDS_SELECT) && + (strcmp("select", (*argv)[0]) == 0) && + !shell_in_select_mode(shell)) { + *argv = *argv + 1; + *argc = *argc - 1; + } + /* If last command is not completed (followed by space) it is treated * as uncompleted one. */ @@ -308,11 +259,11 @@ static bool tab_prepare(const struct shell *shell, search_argc = space ? *argc : *argc - 1; - *cmd = get_last_command(shell, search_argc, argv, complete_arg_idx, - d_entry); + *cmd = shell_get_last_command(shell, search_argc, *argv, + complete_arg_idx, d_entry, false); - /* if search_argc == 0 (empty command line) get_last_command will return - * NULL tab is allowed, otherwise not. + /* if search_argc == 0 (empty command line) shell_get_last_command will + * return NULL tab is allowed, otherwise not. */ if ((*cmd == NULL) && (search_argc != 0)) { return false; @@ -336,7 +287,8 @@ static inline bool is_completion_candidate(const char *candidate, return (strncmp(candidate, str, len) == 0) ? true : false; } -static void find_completion_candidates(const struct shell_static_entry *cmd, +static void find_completion_candidates(const struct shell *shell, + const struct shell_static_entry *cmd, const char *incompl_cmd, size_t *first_idx, size_t *cnt, u16_t *longest) @@ -354,7 +306,7 @@ static void find_completion_candidates(const struct shell_static_entry *cmd, bool is_empty; bool is_candidate; - shell_cmd_get(cmd ? cmd->subcmd : NULL, cmd ? 1 : 0, + shell_cmd_get(shell, cmd ? cmd->subcmd : NULL, cmd ? 1 : 0, idx, &candidate, &dynamic_entry); if (!candidate) { @@ -392,7 +344,7 @@ static void autocomplete(const struct shell *shell, /* shell->ctx->active_cmd can be safely used outside of command context * to save stack */ - shell_cmd_get(cmd ? cmd->subcmd : NULL, cmd ? 1 : 0, + shell_cmd_get(shell, cmd ? cmd->subcmd : NULL, cmd ? 1 : 0, subcmd_idx, &match, &shell->ctx->active_cmd); cmd_len = shell_strlen(match->syntax); @@ -457,7 +409,7 @@ static void tab_options_print(const struct shell *shell, /* shell->ctx->active_cmd can be safely used outside of command * context to save stack */ - shell_cmd_get(cmd ? cmd->subcmd : NULL, cmd ? 1 : 0, + shell_cmd_get(shell, cmd ? cmd->subcmd : NULL, cmd ? 1 : 0, idx, &match, &shell->ctx->active_cmd); idx++; is_empty = is_empty_cmd(match); @@ -474,7 +426,8 @@ static void tab_options_print(const struct shell *shell, shell_print_prompt_and_cmd(shell); } -static u16_t common_beginning_find(const struct shell_static_entry *cmd, +static u16_t common_beginning_find(const struct shell *shell, + const struct shell_static_entry *cmd, const char **str, size_t first, size_t cnt, u16_t arg_len) { @@ -485,7 +438,7 @@ static u16_t common_beginning_find(const struct shell_static_entry *cmd, __ASSERT_NO_MSG(cnt > 1); - shell_cmd_get(cmd ? cmd->subcmd : NULL, cmd ? 1 : 0, + shell_cmd_get(shell, cmd ? cmd->subcmd : NULL, cmd ? 1 : 0, first, &match, &dynamic_entry); *str = match->syntax; @@ -495,7 +448,7 @@ static u16_t common_beginning_find(const struct shell_static_entry *cmd, const struct shell_static_entry *match2; int curr_common; - shell_cmd_get(cmd ? cmd->subcmd : NULL, cmd ? 1 : 0, + shell_cmd_get(shell, cmd ? cmd->subcmd : NULL, cmd ? 1 : 0, idx++, &match2, &dynamic_entry2); if (match2 == NULL) { @@ -520,8 +473,8 @@ static void partial_autocomplete(const struct shell *shell, { const char *completion; u16_t arg_len = shell_strlen(arg); - u16_t common = common_beginning_find(cmd, &completion, - first, cnt, arg_len); + u16_t common = common_beginning_find(shell, cmd, &completion, first, + cnt, arg_len); if (common) { shell_op_completion_insert(shell, &completion[arg_len], @@ -672,16 +625,25 @@ static int execute(const struct shell *shell) } } - shell_cmd_get(p_cmd, cmd_lvl, cmd_idx++, &p_static_entry, + shell_cmd_get(shell, p_cmd, cmd_lvl, cmd_idx++, &p_static_entry, &d_entry); if ((cmd_idx == 0) || (p_static_entry == NULL)) { - if (cmd_lvl == 0) { + if (cmd_lvl == 0 && + (!shell_in_select_mode(shell) || + shell->ctx->selected_cmd->handler == NULL)) { shell_internal_fprintf(shell, SHELL_ERROR, "%s%s\n", argv[0], SHELL_MSG_CMD_NOT_FOUND); return -ENOEXEC; } + if (IS_ENABLED(CONFIG_SHELL_CMDS_SELECT) && + shell_in_select_mode(shell) && + shell->ctx->selected_cmd->handler != NULL) { + p_static_entry = shell->ctx->selected_cmd; + shell->ctx->active_cmd = *p_static_entry; + cmd_with_handler_lvl = cmd_lvl; + } break; } @@ -739,24 +701,26 @@ static int execute(const struct shell *shell) static void tab_handle(const struct shell *shell) { /* +1 reserved for NULL in function shell_make_argv */ - char *argv[CONFIG_SHELL_ARGC_MAX + 1]; + char *__argv[CONFIG_SHELL_ARGC_MAX + 1]; /* d_entry - placeholder for dynamic command */ struct shell_static_entry d_entry; const struct shell_static_entry *cmd; + char **argv = __argv; size_t first = 0; size_t arg_idx; u16_t longest; size_t argc; size_t cnt; - bool tab_possible = tab_prepare(shell, &cmd, argv, &argc, - &arg_idx, &d_entry); + bool tab_possible = tab_prepare(shell, &cmd, &argv, &argc, &arg_idx, + &d_entry); if (tab_possible == false) { return; } - find_completion_candidates(cmd, argv[arg_idx], &first, &cnt, &longest); + find_completion_candidates(shell, cmd, argv[arg_idx], &first, &cnt, + &longest); if (cnt == 1) { /* Autocompletion.*/ @@ -778,6 +742,15 @@ static void alt_metakeys_handle(const struct shell *shell, char data) shell_op_cursor_word_move(shell, -1); } else if (data == SHELL_VT100_ASCII_ALT_F) { shell_op_cursor_word_move(shell, 1); + } else if (data == SHELL_VT100_ASCII_ALT_R && + IS_ENABLED(CONFIG_SHELL_CMDS_SELECT)) { + if (shell->ctx->selected_cmd != NULL) { + shell_cmd_line_erase(shell); + shell_internal_fprintf(shell, SHELL_WARNING, + "Restored default root commands\n"); + shell->ctx->selected_cmd = NULL; + shell_print_prompt_and_cmd(shell); + } } } @@ -887,7 +860,8 @@ static void state_collect(const struct shell *shell) switch (shell->ctx->receive_state) { case SHELL_RECEIVE_DEFAULT: if (process_nl(shell, data)) { - if (!shell->ctx->cmd_buff_len) { + if (!shell->ctx->cmd_buff_len && + shell->ctx->selected_cmd == NULL) { history_mode_exit(shell); cursor_next_line_move(shell); } else { diff --git a/subsys/shell/shell_cmds.c b/subsys/shell/shell_cmds.c index 5d3babf702a..695be46d86f 100644 --- a/subsys/shell/shell_cmds.c +++ b/subsys/shell/shell_cmds.c @@ -40,6 +40,12 @@ #define SHELL_HELP_ECHO_OFF \ "Disable shell echo. Editing keys and meta-keys are not handled" +#define SHELL_HELP_SELECT "Selects new root command. In order for the " \ + "command to be selected, it must meet the criteria:\n" \ + " - it is a static command\n" \ + " - it is not preceded by a dynamic command\n" \ + "Return to the main command tree is done by pressing alt+r." + #define SHELL_HELP_SHELL "Useful, not Unix-like shell commands." #define SHELL_HELP_HELP "Prints help message." @@ -374,6 +380,27 @@ static int cmd_resize(const struct shell *shell, size_t argc, char **argv) return 0; } +static int cmd_select(const struct shell *shell, size_t argc, char **argv) +{ + const struct shell_static_entry *candidate = NULL; + struct shell_static_entry entry; + size_t matching_argc; + + argc--; + argv = argv + 1; + candidate = shell_get_last_command(shell, argc, argv, &matching_argc, + &entry, true); + + if ((candidate != NULL) && (argc == matching_argc)) { + shell->ctx->selected_cmd = candidate; + return 0; + } + + shell_error(shell, "Cannot select command"); + + return -EINVAL; +} + SHELL_STATIC_SUBCMD_SET_CREATE(m_sub_colors, SHELL_CMD_ARG(off, NULL, SHELL_HELP_COLORS_OFF, cmd_colors_off, 1, 0), SHELL_CMD_ARG(on, NULL, SHELL_HELP_COLORS_ON, cmd_colors_on, 1, 0), @@ -425,3 +452,5 @@ SHELL_COND_CMD_ARG_REGISTER(CONFIG_SHELL_HISTORY, history, NULL, SHELL_HELP_HISTORY, cmd_history, 1, 0); SHELL_COND_CMD_ARG_REGISTER(CONFIG_SHELL_CMDS_RESIZE, resize, &m_sub_resize, SHELL_HELP_RESIZE, cmd_resize, 1, 1); +SHELL_COND_CMD_ARG_REGISTER(CONFIG_SHELL_CMDS_SELECT, select, NULL, + SHELL_HELP_SELECT, cmd_select, 2, 255); diff --git a/subsys/shell/shell_help.c b/subsys/shell/shell_help.c index 3cdbf5041c2..7e127cd8f34 100644 --- a/subsys/shell/shell_help.c +++ b/subsys/shell/shell_help.c @@ -159,7 +159,7 @@ void shell_help_subcmd_print(const struct shell *shell) /* Searching for the longest subcommand to print. */ do { - shell_cmd_get(shell->ctx->active_cmd.subcmd, + shell_cmd_get(shell, shell->ctx->active_cmd.subcmd, !SHELL_CMD_ROOT_LVL, cmd_idx++, &entry, &static_entry); @@ -182,7 +182,7 @@ void shell_help_subcmd_print(const struct shell *shell) cmd_idx = 0; while (true) { - shell_cmd_get(shell->ctx->active_cmd.subcmd, + shell_cmd_get(shell, shell->ctx->active_cmd.subcmd, !SHELL_CMD_ROOT_LVL, cmd_idx++, &entry, &static_entry); diff --git a/subsys/shell/shell_utils.c b/subsys/shell/shell_utils.c index 33414db1149..ec6c0315c88 100644 --- a/subsys/shell/shell_utils.c +++ b/subsys/shell/shell_utils.c @@ -3,8 +3,9 @@ * * SPDX-License-Identifier: Apache-2.0 */ -#include "shell_utils.h" #include +#include "shell_utils.h" +#include "shell_wildcard.h" extern const struct shell_cmd_entry __shell_root_cmds_start[0]; extern const struct shell_cmd_entry __shell_root_cmds_end[0]; @@ -240,39 +241,127 @@ const struct shell_static_entry *shell_root_cmd_find(const char *syntax) return NULL; } -void shell_cmd_get(const struct shell_cmd_entry *command, size_t lvl, +void shell_cmd_get(const struct shell *shell, + const struct shell_cmd_entry *command, size_t lvl, size_t idx, const struct shell_static_entry **entry, struct shell_static_entry *d_entry) { __ASSERT_NO_MSG(entry != NULL); __ASSERT_NO_MSG(d_entry != NULL); + *entry = NULL; + if (lvl == SHELL_CMD_ROOT_LVL) { - if (idx < shell_root_cmd_count()) { + if (shell_in_select_mode(shell) && + IS_ENABLED(CONFIG_SHELL_CMDS_SELECT)) { + const struct shell_static_entry *ptr = + shell->ctx->selected_cmd; + if (ptr->subcmd->u.entry[idx].syntax != NULL) { + *entry = &ptr->subcmd->u.entry[idx]; + } + } else if (idx < shell_root_cmd_count()) { const struct shell_cmd_entry *cmd; cmd = shell_root_cmd_get(idx); *entry = cmd->u.entry; - } else { - *entry = NULL; } return; } if (command == NULL) { - *entry = NULL; return; } if (command->is_dynamic) { command->u.dynamic_get(idx, d_entry); - *entry = (d_entry->syntax != NULL) ? d_entry : NULL; + if (d_entry->syntax != NULL) { + *entry = d_entry; + } } else { - *entry = (command->u.entry[idx].syntax != NULL) ? - &command->u.entry[idx] : NULL; + if (command->u.entry[idx].syntax != NULL) { + *entry = &command->u.entry[idx]; + } } } +/* Function returns pointer to a command matching given pattern. + * + * @param shell Shell instance. + * @param cmd Pointer to commands array that will be searched. + * @param lvl Root command indicator. If set to 0 shell will search + * for pattern in root commands. + * @param cmd_str Command pattern to be found. + * @param d_entry Shell static command descriptor. + * + * @return Pointer to found command. + */ +static const struct shell_static_entry *find_cmd( + const struct shell *shell, + const struct shell_cmd_entry *cmd, + size_t lvl, + char *cmd_str, + struct shell_static_entry *d_entry) +{ + const struct shell_static_entry *entry = NULL; + size_t idx = 0; + + do { + shell_cmd_get(shell, cmd, lvl, idx++, &entry, d_entry); + if (entry && (strcmp(cmd_str, entry->syntax) == 0)) { + return entry; + } + } while (entry); + + return NULL; +} + +const struct shell_static_entry *shell_get_last_command( + const struct shell *shell, + size_t argc, + char *argv[], + size_t *match_arg, + struct shell_static_entry *d_entry, + bool only_static) +{ + const struct shell_static_entry *prev_entry = NULL; + const struct shell_static_entry *entry = NULL; + const struct shell_cmd_entry *cmd = NULL; + + *match_arg = SHELL_CMD_ROOT_LVL; + + while (*match_arg < argc) { + + if (IS_ENABLED(CONFIG_SHELL_WILDCARD)) { + /* ignore wildcard argument */ + if (shell_wildcard_character_exist(argv[*match_arg])) { + (*match_arg)++; + continue; + } + } + + entry = find_cmd(shell, cmd, *match_arg, argv[*match_arg], + d_entry); + if (entry) { + cmd = entry->subcmd; + prev_entry = entry; + (*match_arg)++; + } else { + break; + } + + if (cmd == NULL) { + return NULL; + } + + if (only_static && cmd->is_dynamic) { + (*match_arg)--; + return NULL; + } + } + + return entry; +} + int shell_command_add(char *buff, u16_t *buff_len, const char *new_cmd, const char *pattern) { diff --git a/subsys/shell/shell_utils.h b/subsys/shell/shell_utils.h index 6b96a42f08b..1ff1e6a46d2 100644 --- a/subsys/shell/shell_utils.h +++ b/subsys/shell/shell_utils.h @@ -48,19 +48,42 @@ void shell_pattern_remove(char *buff, u16_t *buff_len, const char *pattern); * It moves the pointer entry to command of static command structure. If the * command cannot be found, the function will set entry to NULL. * - * @param command Pointer to command which will be processed (no matter + * @param shell Shell instance. + * @param command Pointer to command which will be processed (no matter * the root command). - * @param lvl Level of the requested command. - * @param idx Index of the requested command. - * @param entry Pointer which points to subcommand[idx] after function + * @param lvl Level of the requested command. + * @param idx Index of the requested command. + * @param entry Pointer which points to subcommand[idx] after function * execution. - * @param st_entry Pointer to the structure where dynamic entry data can be - * stored. + * @param st_entry Pointer to the structure where dynamic entry data can + * be stored. */ -void shell_cmd_get(const struct shell_cmd_entry *command, size_t lvl, +void shell_cmd_get(const struct shell *shell, + const struct shell_cmd_entry *command, size_t lvl, size_t idx, const struct shell_static_entry **entry, struct shell_static_entry *d_entry); + +/* @internal @brief Function returns pointer to a shell's subcommands array + * for a level given by argc and matching command patter provided in argv. + * + * @param shell Shell instance. + * @param argc Number of arguments. + * @param argv Pointer to an array with arguments. + * @param match_arg Subcommand level of last matching argument. + * @param d_entry Shell static command descriptor. + * @param only_static If true search only for static commands. + * + * @return Pointer to found command. + */ +const struct shell_static_entry *shell_get_last_command( + const struct shell *shell, + size_t argc, + char *argv[], + size_t *match_arg, + struct shell_static_entry *d_entry, + bool only_static); + int shell_command_add(char *buff, u16_t *buff_len, const char *new_cmd, const char *pattern); @@ -75,6 +98,11 @@ static inline void transport_buffer_flush(const struct shell *shell) void shell_cmd_trim(const struct shell *shell); +static inline bool shell_in_select_mode(const struct shell *shell) +{ + return shell->ctx->selected_cmd == NULL ? false : true; +} + #ifdef __cplusplus } #endif diff --git a/subsys/shell/shell_vt100.h b/subsys/shell/shell_vt100.h index bb7f5059bf1..2c6a4e374cb 100644 --- a/subsys/shell/shell_vt100.h +++ b/subsys/shell/shell_vt100.h @@ -22,6 +22,7 @@ #define SHELL_VT100_ASCII_CTRL_W (0x17) #define SHELL_VT100_ASCII_ALT_B (0x62) #define SHELL_VT100_ASCII_ALT_F (0x66) +#define SHELL_VT100_ASCII_ALT_R (0x72) #define SHELL_VT100_SETNL \ { \