| /* |
| * Copyright (c) 2018 Nordic Semiconductor ASA |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #include <zephyr/shell/shell.h> |
| #include <ctype.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| |
| #define MAX_CMD_CNT (20u) |
| #define MAX_CMD_LEN (33u) |
| |
| /* buffer holding dynamically created user commands */ |
| static char dynamic_cmd_buffer[MAX_CMD_CNT][MAX_CMD_LEN]; |
| /* commands counter */ |
| static uint8_t dynamic_cmd_cnt; |
| |
| typedef int cmp_t(const void *, const void *); |
| extern void qsort(void *a, size_t n, size_t es, cmp_t *cmp); |
| |
| /* function required by qsort */ |
| static int string_cmp(const void *p_a, const void *p_b) |
| { |
| return strcmp((const char *)p_a, (const char *)p_b); |
| } |
| |
| static int cmd_dynamic_add(const struct shell *sh, |
| size_t argc, char **argv) |
| { |
| uint16_t cmd_len; |
| uint8_t idx; |
| |
| ARG_UNUSED(argc); |
| |
| if (dynamic_cmd_cnt >= MAX_CMD_CNT) { |
| shell_error(sh, "command limit reached"); |
| return -ENOEXEC; |
| } |
| |
| cmd_len = strlen(argv[1]); |
| |
| if (cmd_len >= MAX_CMD_LEN) { |
| shell_error(sh, "too long command"); |
| return -ENOEXEC; |
| } |
| |
| for (idx = 0U; idx < cmd_len; idx++) { |
| if (isalnum((int)(argv[1][idx])) == 0) { |
| shell_error(sh, |
| "bad command name - please use only" |
| " alphanumerical characters"); |
| return -ENOEXEC; |
| } |
| } |
| |
| for (idx = 0U; idx < MAX_CMD_CNT; idx++) { |
| if (!strcmp(dynamic_cmd_buffer[idx], argv[1])) { |
| shell_error(sh, "duplicated command"); |
| return -ENOEXEC; |
| } |
| } |
| |
| sprintf(dynamic_cmd_buffer[dynamic_cmd_cnt++], "%s", argv[1]); |
| |
| qsort(dynamic_cmd_buffer, dynamic_cmd_cnt, |
| sizeof(dynamic_cmd_buffer[0]), string_cmp); |
| |
| shell_print(sh, "command added successfully"); |
| |
| return 0; |
| } |
| |
| static int cmd_dynamic_execute(const struct shell *sh, |
| size_t argc, char **argv) |
| { |
| ARG_UNUSED(argc); |
| ARG_UNUSED(argv); |
| |
| for (uint8_t idx = 0; idx < dynamic_cmd_cnt; idx++) { |
| if (!strcmp(dynamic_cmd_buffer[idx], argv[1])) { |
| shell_print(sh, "dynamic command: %s", argv[1]); |
| return 0; |
| } |
| } |
| |
| shell_error(sh, "%s: unknown parameter: %s", argv[0], argv[1]); |
| |
| return -ENOEXEC; |
| } |
| |
| static int cmd_dynamic_remove(const struct shell *sh, size_t argc, |
| char **argv) |
| { |
| ARG_UNUSED(argc); |
| ARG_UNUSED(argv); |
| |
| for (uint8_t idx = 0; idx < dynamic_cmd_cnt; idx++) { |
| if (!strcmp(dynamic_cmd_buffer[idx], argv[1])) { |
| if (idx == MAX_CMD_CNT - 1) { |
| dynamic_cmd_buffer[idx][0] = '\0'; |
| } else { |
| memmove(dynamic_cmd_buffer[idx], |
| dynamic_cmd_buffer[idx + 1], |
| sizeof(dynamic_cmd_buffer[idx]) * |
| (dynamic_cmd_cnt - idx)); |
| } |
| |
| --dynamic_cmd_cnt; |
| shell_print(sh, "command removed successfully"); |
| return 0; |
| } |
| } |
| shell_error(sh, "did not find command: %s", argv[1]); |
| |
| return -ENOEXEC; |
| } |
| |
| static int cmd_dynamic_show(const struct shell *sh, |
| size_t argc, char **argv) |
| { |
| ARG_UNUSED(argc); |
| ARG_UNUSED(argv); |
| |
| if (dynamic_cmd_cnt == 0U) { |
| shell_warn(sh, "Please add some commands first."); |
| return -ENOEXEC; |
| } |
| |
| shell_print(sh, "Dynamic command list:"); |
| |
| for (uint8_t i = 0; i < dynamic_cmd_cnt; i++) { |
| shell_print(sh, "[%3d] %s", i, dynamic_cmd_buffer[i]); |
| } |
| |
| return 0; |
| } |
| |
| /* dynamic command creation */ |
| static void dynamic_cmd_get(size_t idx, struct shell_static_entry *entry) |
| { |
| if (idx < dynamic_cmd_cnt) { |
| /* m_dynamic_cmd_buffer must be sorted alphabetically to ensure |
| * correct CLI completion |
| */ |
| entry->syntax = dynamic_cmd_buffer[idx]; |
| entry->handler = NULL; |
| entry->subcmd = NULL; |
| entry->help = "Show dynamic command name."; |
| } else { |
| /* if there are no more dynamic commands available syntax |
| * must be set to NULL. |
| */ |
| entry->syntax = NULL; |
| } |
| } |
| |
| SHELL_DYNAMIC_CMD_CREATE(m_sub_dynamic_set, dynamic_cmd_get); |
| SHELL_STATIC_SUBCMD_SET_CREATE(m_sub_dynamic, |
| SHELL_CMD_ARG(add, NULL, |
| "Add a new dynamic command.\nExample usage: [ dynamic add test " |
| "] will add a dynamic command 'test'.\nIn this example, command" |
| " name length is limited to 32 chars. You can add up to 20" |
| " commands. Commands are automatically sorted to ensure correct" |
| " shell completion.", |
| cmd_dynamic_add, 2, 0), |
| SHELL_CMD_ARG(execute, &m_sub_dynamic_set, |
| "Execute a command.", cmd_dynamic_execute, 2, 0), |
| SHELL_CMD_ARG(remove, &m_sub_dynamic_set, |
| "Remove a command.", cmd_dynamic_remove, 2, 0), |
| SHELL_CMD_ARG(show, NULL, |
| "Show all added dynamic commands.", cmd_dynamic_show, 1, 0), |
| SHELL_SUBCMD_SET_END |
| ); |
| |
| SHELL_CMD_REGISTER(dynamic, &m_sub_dynamic, |
| "Demonstrate dynamic command usage.", NULL); |