zephyr/subsys/shell/shell_utils.c
Peter Bigot a538dcd8f8 shell: refactor device_name_get implementation
Several shell modules use cloned code to iterate over all devices and
identify the nth instance that meets some criteria.  The code was
repetitive and included various errors.  Abstract to a helper function
that performs the check consistently.

Signed-off-by: Peter Bigot <peter.bigot@nordicsemi.no>
2020-06-23 13:27:14 +02:00

489 lines
10 KiB
C

/*
* Copyright (c) 2018 Nordic Semiconductor ASA
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <ctype.h>
#include <device.h>
#include "shell_utils.h"
#include "shell_wildcard.h"
extern const struct shell_cmd_entry __shell_root_cmds_start[];
extern const struct shell_cmd_entry __shell_root_cmds_end[];
static inline const struct shell_cmd_entry *shell_root_cmd_get(uint32_t id)
{
return &__shell_root_cmds_start[id];
}
/* Calculates relative line number of given position in buffer */
static uint32_t line_num_with_buffer_offset_get(struct shell_multiline_cons *cons,
uint16_t buffer_pos)
{
return ((buffer_pos + cons->name_len) / cons->terminal_wid);
}
/* Calculates column number of given position in buffer */
static uint32_t col_num_with_buffer_offset_get(struct shell_multiline_cons *cons,
uint16_t buffer_pos)
{
/* columns are counted from 1 */
return (1 + ((buffer_pos + cons->name_len) % cons->terminal_wid));
}
int32_t column_span_with_buffer_offsets_get(struct shell_multiline_cons *cons,
uint16_t offset1,
uint16_t offset2)
{
return col_num_with_buffer_offset_get(cons, offset2)
- col_num_with_buffer_offset_get(cons, offset1);
}
int32_t row_span_with_buffer_offsets_get(struct shell_multiline_cons *cons,
uint16_t offset1,
uint16_t offset2)
{
return line_num_with_buffer_offset_get(cons, offset2)
- line_num_with_buffer_offset_get(cons, offset1);
}
void shell_multiline_data_calc(struct shell_multiline_cons *cons,
uint16_t buff_pos, uint16_t buff_len)
{
/* Current cursor position in command.
* +1 -> because home position is (1, 1)
*/
cons->cur_x = (buff_pos + cons->name_len) % cons->terminal_wid + 1;
cons->cur_y = (buff_pos + cons->name_len) / cons->terminal_wid + 1;
/* Extreme position when cursor is at the end of command. */
cons->cur_y_end = (buff_len + cons->name_len) / cons->terminal_wid + 1;
cons->cur_x_end = (buff_len + cons->name_len) % cons->terminal_wid + 1;
}
static char make_argv(char **ppcmd, uint8_t c)
{
char *cmd = *ppcmd;
char quote = 0;
while (1) {
c = *cmd;
if (c == '\0') {
break;
}
if (!quote) {
switch (c) {
case '\\':
memmove(cmd, cmd + 1,
shell_strlen(cmd));
cmd += 1;
continue;
case '\'':
case '\"':
memmove(cmd, cmd + 1,
shell_strlen(cmd));
quote = c;
continue;
default:
break;
}
}
if (quote == c) {
memmove(cmd, cmd + 1, shell_strlen(cmd));
quote = 0;
continue;
}
if (quote && c == '\\') {
char t = *(cmd + 1);
if (t == quote) {
memmove(cmd, cmd + 1,
shell_strlen(cmd));
cmd += 1;
continue;
}
if (t == '0') {
uint8_t i;
uint8_t v = 0U;
for (i = 2U; i < (2 + 3); i++) {
t = *(cmd + i);
if (t >= '0' && t <= '7') {
v = (v << 3) | (t - '0');
} else {
break;
}
}
if (i > 2) {
memmove(cmd, cmd + (i - 1),
shell_strlen(cmd) - (i - 2));
*cmd++ = v;
continue;
}
}
if (t == 'x') {
uint8_t i;
uint8_t v = 0U;
for (i = 2U; i < (2 + 2); i++) {
t = *(cmd + i);
if (t >= '0' && t <= '9') {
v = (v << 4) | (t - '0');
} else if ((t >= 'a') &&
(t <= 'f')) {
v = (v << 4) | (t - 'a' + 10);
} else if ((t >= 'A') && (t <= 'F')) {
v = (v << 4) | (t - 'A' + 10);
} else {
break;
}
}
if (i > 2) {
memmove(cmd, cmd + (i - 1),
shell_strlen(cmd) - (i - 2));
*cmd++ = v;
continue;
}
}
}
if (!quote && isspace((int) c)) {
break;
}
cmd += 1;
}
*ppcmd = cmd;
return quote;
}
char shell_make_argv(size_t *argc, const char **argv, char *cmd, uint8_t max_argc)
{
char quote = 0;
char c;
*argc = 0;
do {
c = *cmd;
if (c == '\0') {
break;
}
if (isspace((int) c)) {
*cmd++ = '\0';
continue;
}
argv[(*argc)++] = cmd;
if (*argc == max_argc) {
break;
}
quote = make_argv(&cmd, c);
} while (true);
argv[*argc] = 0;
return quote;
}
void shell_pattern_remove(char *buff, uint16_t *buff_len, const char *pattern)
{
char *pattern_addr = strstr(buff, pattern);
uint16_t shift;
uint16_t pattern_len = shell_strlen(pattern);
if (!pattern_addr) {
return;
}
if (pattern_addr > buff) {
if (*(pattern_addr - 1) == ' ') {
pattern_len++; /* space needs to be removed as well */
pattern_addr--; /* set pointer to space */
}
}
shift = shell_strlen(pattern_addr) - pattern_len + 1; /* +1 for EOS */
*buff_len -= pattern_len;
memmove(pattern_addr, pattern_addr + pattern_len, shift);
}
static inline uint32_t shell_root_cmd_count(void)
{
return ((uint8_t *)__shell_root_cmds_end -
(uint8_t *)__shell_root_cmds_start)/
sizeof(struct shell_cmd_entry);
}
/* Function returning pointer to parent command matching requested syntax. */
const struct shell_static_entry *shell_root_cmd_find(const char *syntax)
{
const size_t cmd_count = shell_root_cmd_count();
const struct shell_cmd_entry *cmd;
for (size_t cmd_idx = 0; cmd_idx < cmd_count; ++cmd_idx) {
cmd = shell_root_cmd_get(cmd_idx);
if (strcmp(syntax, cmd->u.entry->syntax) == 0) {
return cmd->u.entry;
}
}
return NULL;
}
const struct shell_static_entry *shell_cmd_get(
const struct shell_static_entry *parent,
size_t idx,
struct shell_static_entry *dloc)
{
__ASSERT_NO_MSG(dloc != NULL);
const struct shell_static_entry *res = NULL;
if (parent == NULL) {
return (idx < shell_root_cmd_count()) ?
shell_root_cmd_get(idx)->u.entry : NULL;
}
if (parent->subcmd) {
if (parent->subcmd->is_dynamic) {
parent->subcmd->u.dynamic_get(idx, dloc);
if (dloc->syntax != NULL) {
res = dloc;
}
} else {
if (parent->subcmd->u.entry[idx].syntax != NULL) {
res = &parent->subcmd->u.entry[idx];
}
}
}
return res;
}
/* Function returns pointer to a command matching given pattern.
*
* @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 parent commands.
* @param cmd_str Command pattern to be found.
* @param dloc Shell static command descriptor.
*
* @return Pointer to found command.
*/
const struct shell_static_entry *shell_find_cmd(
const struct shell_static_entry *parent,
const char *cmd_str,
struct shell_static_entry *dloc)
{
const struct shell_static_entry *entry;
size_t idx = 0;
while ((entry = shell_cmd_get(parent, idx++, dloc)) != NULL) {
if (strcmp(cmd_str, entry->syntax) == 0) {
return entry;
}
};
return NULL;
}
const struct shell_static_entry *shell_get_last_command(
const struct shell_static_entry *entry,
size_t argc,
const char *argv[],
size_t *match_arg,
struct shell_static_entry *dloc,
bool only_static)
{
const struct shell_static_entry *prev_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;
}
}
prev_entry = entry;
entry = shell_find_cmd(entry, argv[*match_arg], dloc);
if (entry) {
(*match_arg)++;
} else {
entry = prev_entry;
break;
}
if (only_static && (entry == dloc)) {
(*match_arg)--;
return NULL;
}
}
return entry;
}
int shell_set_root_cmd(const char *cmd)
{
const struct shell_static_entry *entry;
entry = cmd ? shell_root_cmd_find(cmd) : NULL;
if (cmd && (entry == NULL)) {
return -EINVAL;
}
Z_STRUCT_SECTION_FOREACH(shell, sh) {
sh->ctx->selected_cmd = entry;
}
return 0;
}
int shell_command_add(char *buff, uint16_t *buff_len,
const char *new_cmd, const char *pattern)
{
uint16_t cmd_len = shell_strlen(new_cmd);
char *cmd_source_addr;
uint16_t shift;
/* +1 for space */
if ((*buff_len + cmd_len + 1) > CONFIG_SHELL_CMD_BUFF_SIZE) {
return -ENOMEM;
}
cmd_source_addr = strstr(buff, pattern);
if (!cmd_source_addr) {
return -EINVAL;
}
shift = shell_strlen(cmd_source_addr);
/* make place for new command: + 1 for space + 1 for EOS */
memmove(cmd_source_addr + cmd_len + 1, cmd_source_addr, shift + 1);
memcpy(cmd_source_addr, new_cmd, cmd_len);
cmd_source_addr[cmd_len] = ' ';
*buff_len += cmd_len + 1; /* + 1 for space */
return 0;
}
void shell_spaces_trim(char *str)
{
uint16_t len = shell_strlen(str);
uint16_t shift = 0U;
if (!str) {
return;
}
for (uint16_t i = 0; i < len - 1; i++) {
if (isspace((int)str[i])) {
for (uint16_t j = i + 1; j < len; j++) {
if (isspace((int)str[j])) {
shift++;
continue;
}
if (shift > 0) {
/* +1 for EOS */
memmove(&str[i + 1],
&str[j],
len - j + 1);
len -= shift;
shift = 0U;
}
break;
}
}
}
}
/** @brief Remove white chars from beginning and end of command buffer.
*
*/
static void buffer_trim(char *buff, uint16_t *buff_len)
{
uint16_t i = 0U;
/* no command in the buffer */
if (buff[0] == '\0') {
return;
}
while (isspace((int) buff[*buff_len - 1U])) {
*buff_len -= 1U;
if (*buff_len == 0U) {
buff[0] = '\0';
return;
}
}
buff[*buff_len] = '\0';
/* Counting whitespace characters starting from beginning of the
* command.
*/
while (isspace((int) buff[i++])) {
}
/* Removing counted whitespace characters. */
if (--i > 0) {
memmove(buff, buff + i, (*buff_len + 1U) - i); /* +1 for '\0' */
*buff_len = *buff_len - i;
}
}
void shell_cmd_trim(const struct shell *shell)
{
buffer_trim(shell->ctx->cmd_buff, &shell->ctx->cmd_buff_len);
shell->ctx->cmd_buff_pos = shell->ctx->cmd_buff_len;
}
struct device *shell_device_lookup(size_t idx,
const char *prefix)
{
size_t match_idx = 0;
struct device *dev;
size_t len = z_device_get_all_static(&dev);
struct device *dev_end = dev + len;
while (dev < dev_end) {
if ((dev->driver_api != NULL)
&& (dev->name != NULL)
&& (strlen(dev->name) != 0)
&& ((prefix == NULL)
|| (strncmp(prefix, dev->name,
strlen(prefix)) == 0))) {
if (match_idx == idx) {
return dev;
}
++match_idx;
}
++dev;
}
return NULL;
}