219 lines
6.5 KiB
C
219 lines
6.5 KiB
C
/*
|
|
* Copyright (c) 2018 Nordic Semiconductor ASA
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include <string.h>
|
|
#include <fnmatch.h>
|
|
#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;
|
|
}
|