1 /*
2 * Copyright (c) 2018 Nordic Semiconductor ASA
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <shell/shell.h>
8 #include <ctype.h>
9 #include <stdio.h>
10 #include <stdlib.h>
11
12 #define MAX_CMD_CNT (20u)
13 #define MAX_CMD_LEN (33u)
14
15 /* buffer holding dynamicly created user commands */
16 static char dynamic_cmd_buffer[MAX_CMD_CNT][MAX_CMD_LEN];
17 /* commands counter */
18 static uint8_t dynamic_cmd_cnt;
19
20 typedef int cmp_t(const void *, const void *);
21 extern void qsort(void *a, size_t n, size_t es, cmp_t *cmp);
22
23 /* function required by qsort */
string_cmp(const void * p_a,const void * p_b)24 static int string_cmp(const void *p_a, const void *p_b)
25 {
26 return strcmp((const char *)p_a, (const char *)p_b);
27 }
28
cmd_dynamic_add(const struct shell * shell,size_t argc,char ** argv)29 static int cmd_dynamic_add(const struct shell *shell,
30 size_t argc, char **argv)
31 {
32 uint16_t cmd_len;
33 uint8_t idx;
34
35 ARG_UNUSED(argc);
36
37 if (dynamic_cmd_cnt >= MAX_CMD_CNT) {
38 shell_error(shell, "command limit reached");
39 return -ENOEXEC;
40 }
41
42 cmd_len = strlen(argv[1]);
43
44 if (cmd_len >= MAX_CMD_LEN) {
45 shell_error(shell, "too long command");
46 return -ENOEXEC;
47 }
48
49 for (idx = 0U; idx < cmd_len; idx++) {
50 if (!isalnum((int)(argv[1][idx]))) {
51 shell_error(shell,
52 "bad command name - please use only"
53 " alphanumerical characters");
54 return -ENOEXEC;
55 }
56 }
57
58 for (idx = 0U; idx < MAX_CMD_CNT; idx++) {
59 if (!strcmp(dynamic_cmd_buffer[idx], argv[1])) {
60 shell_error(shell, "duplicated command");
61 return -ENOEXEC;
62 }
63 }
64
65 sprintf(dynamic_cmd_buffer[dynamic_cmd_cnt++], "%s", argv[1]);
66
67 qsort(dynamic_cmd_buffer, dynamic_cmd_cnt,
68 sizeof(dynamic_cmd_buffer[0]), string_cmp);
69
70 shell_print(shell, "command added successfully");
71
72 return 0;
73 }
74
cmd_dynamic_execute(const struct shell * shell,size_t argc,char ** argv)75 static int cmd_dynamic_execute(const struct shell *shell,
76 size_t argc, char **argv)
77 {
78 ARG_UNUSED(argc);
79 ARG_UNUSED(argv);
80
81 for (uint8_t idx = 0; idx < dynamic_cmd_cnt; idx++) {
82 if (!strcmp(dynamic_cmd_buffer[idx], argv[1])) {
83 shell_print(shell, "dynamic command: %s", argv[1]);
84 return 0;
85 }
86 }
87
88 shell_error(shell, "%s: uknown parameter: %s", argv[0], argv[1]);
89
90 return -ENOEXEC;
91 }
92
cmd_dynamic_remove(const struct shell * shell,size_t argc,char ** argv)93 static int cmd_dynamic_remove(const struct shell *shell, size_t argc,
94 char **argv)
95 {
96 ARG_UNUSED(argc);
97 ARG_UNUSED(argv);
98
99 for (uint8_t idx = 0; idx < dynamic_cmd_cnt; idx++) {
100 if (!strcmp(dynamic_cmd_buffer[idx], argv[1])) {
101 if (idx == MAX_CMD_CNT - 1) {
102 dynamic_cmd_buffer[idx][0] = '\0';
103 } else {
104 memmove(dynamic_cmd_buffer[idx],
105 dynamic_cmd_buffer[idx + 1],
106 sizeof(dynamic_cmd_buffer[idx]) *
107 (dynamic_cmd_cnt - idx));
108 }
109
110 --dynamic_cmd_cnt;
111 shell_print(shell, "command removed successfully");
112 return 0;
113 }
114 }
115 shell_error(shell, "did not find command: %s", argv[1]);
116
117 return -ENOEXEC;
118 }
119
cmd_dynamic_show(const struct shell * shell,size_t argc,char ** argv)120 static int cmd_dynamic_show(const struct shell *shell,
121 size_t argc, char **argv)
122 {
123 ARG_UNUSED(argc);
124 ARG_UNUSED(argv);
125
126 if (dynamic_cmd_cnt == 0U) {
127 shell_warn(shell, "Please add some commands first.");
128 return -ENOEXEC;
129 }
130
131 shell_print(shell, "Dynamic command list:");
132
133 for (uint8_t i = 0; i < dynamic_cmd_cnt; i++) {
134 shell_print(shell, "[%3d] %s", i, dynamic_cmd_buffer[i]);
135 }
136
137 return 0;
138 }
139
140 /* dynamic command creation */
dynamic_cmd_get(size_t idx,struct shell_static_entry * entry)141 static void dynamic_cmd_get(size_t idx, struct shell_static_entry *entry)
142 {
143 if (idx < dynamic_cmd_cnt) {
144 /* m_dynamic_cmd_buffer must be sorted alphabetically to ensure
145 * correct CLI completion
146 */
147 entry->syntax = dynamic_cmd_buffer[idx];
148 entry->handler = NULL;
149 entry->subcmd = NULL;
150 entry->help = "Show dynamic command name.";
151 } else {
152 /* if there are no more dynamic commands available syntax
153 * must be set to NULL.
154 */
155 entry->syntax = NULL;
156 }
157 }
158
159 SHELL_DYNAMIC_CMD_CREATE(m_sub_dynamic_set, dynamic_cmd_get);
160 SHELL_STATIC_SUBCMD_SET_CREATE(m_sub_dynamic,
161 SHELL_CMD_ARG(add, NULL,
162 "Add a new dynamic command.\nExample usage: [ dynamic add test "
163 "] will add a dynamic command 'test'.\nIn this example, command"
164 " name length is limited to 32 chars. You can add up to 20"
165 " commands. Commands are automatically sorted to ensure correct"
166 " shell completion.",
167 cmd_dynamic_add, 2, 0),
168 SHELL_CMD_ARG(execute, &m_sub_dynamic_set,
169 "Execute a command.", cmd_dynamic_execute, 2, 0),
170 SHELL_CMD_ARG(remove, &m_sub_dynamic_set,
171 "Remove a command.", cmd_dynamic_remove, 2, 0),
172 SHELL_CMD_ARG(show, NULL,
173 "Show all added dynamic commands.", cmd_dynamic_show, 1, 0),
174 SHELL_SUBCMD_SET_END
175 );
176
177 SHELL_CMD_REGISTER(dynamic, &m_sub_dynamic,
178 "Demonstrate dynamic command usage.", NULL);
179