/* * Copyright (c) 2018 Nordic Semiconductor ASA * * SPDX-License-Identifier: Apache-2.0 */ #include #include #include "shell_wildcard.h" #include "shell_utils.h" static void subcmd_get(const struct shell_cmd_entry *cmd, 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); if (cmd == NULL) { *entry = NULL; return; } if (cmd->is_dynamic) { cmd->u.dynamic_get(idx, d_entry); *entry = (d_entry->syntax != NULL) ? d_entry : NULL; } else { *entry = (cmd->u.entry[idx].syntax != NULL) ? &cmd->u.entry[idx] : NULL; } } static enum shell_wildcard_status command_add(char *buff, u16_t *buff_len, char const *cmd, char const *pattern) { u16_t cmd_len = shell_strlen(cmd); char *completion_addr; u16_t shift; /* +1 for space */ if ((*buff_len + cmd_len + 1) > CONFIG_SHELL_CMD_BUFF_SIZE) { return SHELL_WILDCARD_CMD_MISSING_SPACE; } completion_addr = strstr(buff, pattern); if (!completion_addr) { return SHELL_WILDCARD_CMD_NO_MATCH_FOUND; } shift = shell_strlen(completion_addr); /* make place for new command: + 1 for space + 1 for EOS */ memmove(completion_addr + cmd_len + 1, completion_addr, shift + 1); memcpy(completion_addr, cmd, cmd_len); /* adding space to not brake next command in the buffer */ completion_addr[cmd_len] = ' '; *buff_len += cmd_len + 1; /* + 1 for space */ return SHELL_WILDCARD_CMD_ADDED; } /** * @internal @brief Function for searching and adding commands to the temporary * shell buffer matching to wildcard pattern. * * Function will search commands tree for commands matching wildcard pattern * stored in argv[cmd_lvl]. When match is found wildcard pattern will be * replaced by matching commands. If there is no space in the buffer to add all * matching commands function will add as many as possible. Next it will * continue to search for next wildcard pattern and it will try to add matching * commands. * * * This function is internal to shell module and shall be not called directly. * * @param[in/out] shell Pointer to the CLI instance. * @param[in] cmd Pointer to command which will be processed * @param[in] pattern Pointer to wildcard pattern. * * @retval WILDCARD_CMD_ADDED All matching commands added to the buffer. * @retval WILDCARD_CMD_ADDED_MISSING_SPACE Not all matching commands added * because CONFIG_SHELL_CMD_BUFF_SIZE * is too small. * @retval WILDCARD_CMD_NO_MATCH_FOUND No matching command found. */ static enum shell_wildcard_status commands_expand(const struct shell *shell, const struct shell_cmd_entry *cmd, const char *pattern) { enum shell_wildcard_status ret_val = SHELL_WILDCARD_CMD_NO_MATCH_FOUND; struct shell_static_entry const *p_static_entry = NULL; struct shell_static_entry static_entry; size_t cmd_idx = 0; size_t cnt = 0; do { subcmd_get(cmd, cmd_idx++, &p_static_entry, &static_entry); if (!p_static_entry) { break; } if (fnmatch(pattern, p_static_entry->syntax, 0) == 0) { ret_val = command_add(shell->ctx->temp_buff, &shell->ctx->cmd_tmp_buff_len, p_static_entry->syntax, pattern); if (ret_val == SHELL_WILDCARD_CMD_MISSING_SPACE) { shell_fprintf(shell, SHELL_WARNING, "Command buffer is too short to" " expand all commands matching" " wildcard pattern: %s\r\n", pattern); break; } else if (ret_val != SHELL_WILDCARD_CMD_ADDED) { break; } cnt++; } } while (cmd_idx); if (cnt > 0) { shell_pattern_remove(shell->ctx->temp_buff, &shell->ctx->cmd_tmp_buff_len, pattern); } return ret_val; } bool shell_wildcard_character_exist(const char *str) { size_t str_len = shell_strlen(str); for (size_t i = 0; i < str_len; i++) { if ((str[i] == '?') || (str[i] == '*')) { return true; } } return false; } void shell_wildcard_prepare(const struct shell *shell) { /* Wildcard can be correctly handled under following conditions: * - wildcard command does not have a handler * - wildcard command is on the deepest commands level * - other commands on the same level as wildcard command shall also not * have a handler * * Algorithm: * 1. Command buffer: ctx->cmd_buff is copied to temporary buffer: * ctx->temp_buff. * 2. Algorithm goes through command buffer to find handlers and * subcommands. * 3. If algorithm will find a wildcard character it switches to * Temporary buffer. * 4. In the Temporary buffer command containing wildcard character is * replaced by matching command(s). * 5. Algorithm switch back to Command buffer and analyzes next command. * 6. When all arguments are analyzed from Command buffer, Temporary * buffer with all expanded commands is copied to Command buffer. * 7. Deepest found handler is executed and all lower level commands, * including expanded commands, are passed as arguments. */ memset(shell->ctx->temp_buff, 0, sizeof(shell->ctx->temp_buff)); memcpy(shell->ctx->temp_buff, shell->ctx->cmd_buff, shell->ctx->cmd_buff_len); /* Function shell_spaces_trim must be used instead of shell_make_argv. * At this point it is important to keep temp_buff as one string. * It will allow to find wildcard commands easily with strstr function. */ shell_spaces_trim(shell->ctx->temp_buff); /* +1 for EOS*/ shell->ctx->cmd_tmp_buff_len = shell_strlen(shell->ctx->temp_buff) + 1; } enum shell_wildcard_status shell_wildcard_process(const struct shell *shell, const struct shell_cmd_entry *cmd, const char *pattern) { enum shell_wildcard_status ret_val = SHELL_WILDCARD_NOT_FOUND; if (cmd == NULL) { return ret_val; } if (!shell_wildcard_character_exist(pattern)) { return ret_val; } /* Function will search commands tree for commands matching wildcard * pattern stored in argv[cmd_lvl]. When match is found wildcard pattern * will be replaced by matching commands. If there is no space in the * buffer to add all matching commands function will add as many as * possible. Next it will continue to search for next wildcard pattern * and it will try to add matching commands. */ ret_val = commands_expand(shell, cmd, pattern); return ret_val; } void shell_wildcard_finalize(const struct shell *shell) { memcpy(shell->ctx->cmd_buff, shell->ctx->temp_buff, shell->ctx->cmd_tmp_buff_len); shell->ctx->cmd_buff_len = shell->ctx->cmd_tmp_buff_len; }