| /* |
| * 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; |
| } |