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