467 lines
9.7 KiB
C
467 lines
9.7 KiB
C
/*
|
|
* Copyright (c) 2018 Nordic Semiconductor ASA
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
#include <ctype.h>
|
|
#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];
|
|
|
|
static inline const struct shell_cmd_entry *shell_root_cmd_get(u32_t id)
|
|
{
|
|
return &__shell_root_cmds_start[id];
|
|
}
|
|
|
|
/* Calculates relative line number of given position in buffer */
|
|
static u32_t line_num_with_buffer_offset_get(struct shell_multiline_cons *cons,
|
|
u16_t buffer_pos)
|
|
{
|
|
return ((buffer_pos + cons->name_len) / cons->terminal_wid);
|
|
}
|
|
|
|
/* Calculates column number of given position in buffer */
|
|
static u32_t col_num_with_buffer_offset_get(struct shell_multiline_cons *cons,
|
|
u16_t buffer_pos)
|
|
{
|
|
/* columns are counted from 1 */
|
|
return (1 + ((buffer_pos + cons->name_len) % cons->terminal_wid));
|
|
}
|
|
|
|
s32_t column_span_with_buffer_offsets_get(struct shell_multiline_cons *cons,
|
|
u16_t offset1,
|
|
u16_t offset2)
|
|
{
|
|
return col_num_with_buffer_offset_get(cons, offset2)
|
|
- col_num_with_buffer_offset_get(cons, offset1);
|
|
}
|
|
|
|
s32_t row_span_with_buffer_offsets_get(struct shell_multiline_cons *cons,
|
|
u16_t offset1,
|
|
u16_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,
|
|
u16_t buff_pos, u16_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, u8_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') {
|
|
u8_t i;
|
|
u8_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') {
|
|
u8_t i;
|
|
u8_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, char **argv, char *cmd, u8_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;
|
|
quote = make_argv(&cmd, c);
|
|
} while (*argc < max_argc);
|
|
|
|
argv[*argc] = 0;
|
|
|
|
return quote;
|
|
}
|
|
|
|
void shell_pattern_remove(char *buff, u16_t *buff_len, const char *pattern)
|
|
{
|
|
char *pattern_addr = strstr(buff, pattern);
|
|
u16_t shift;
|
|
u16_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 u32_t shell_root_cmd_count(void)
|
|
{
|
|
return ((u8_t *)__shell_root_cmds_end -
|
|
(u8_t *)__shell_root_cmds_start)/
|
|
sizeof(struct shell_cmd_entry);
|
|
}
|
|
|
|
/* Function returning pointer to root 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;
|
|
}
|
|
|
|
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 (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;
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (command == NULL) {
|
|
return;
|
|
}
|
|
|
|
if (command->is_dynamic) {
|
|
command->u.dynamic_get(idx, d_entry);
|
|
if (d_entry->syntax != NULL) {
|
|
*entry = d_entry;
|
|
}
|
|
} else {
|
|
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)
|
|
{
|
|
u16_t cmd_len = shell_strlen(new_cmd);
|
|
char *cmd_source_addr;
|
|
u16_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)
|
|
{
|
|
u16_t len = shell_strlen(str);
|
|
u16_t shift = 0U;
|
|
|
|
if (!str) {
|
|
return;
|
|
}
|
|
|
|
for (u16_t i = 0; i < len - 1; i++) {
|
|
if (isspace((int)str[i])) {
|
|
for (u16_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 - shift + 1);
|
|
len -= shift;
|
|
shift = 0U;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/** @brief Remove white chars from beginning and end of command buffer.
|
|
*
|
|
*/
|
|
static void buffer_trim(char *buff, u16_t *buff_len)
|
|
{
|
|
u16_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;
|
|
}
|