1 /*
2 * SPDX-License-Identifier: Apache-2.0
3 *
4 * Date Author Notes
5 * 2019-02-14 ZeroFree first implementation
6 */
7
8 #include <string.h>
9 #include "nimble/nimble_npl.h"
10 #include "nimble/npl_shell.h"
11
12 #include <rtthread.h>
13
14 #define SHELL_PROMPT "shell"
15 #define SHELL_MAX_MODULES 3
16 #define SHELL_PROMPT_SUFFIX "> "
17
18 static const char *get_command_and_module(char *argv[], int *module);
19 static int set_default_module(const char *name);
20 static void print_prompt(void);
21 static int get_destination_module(const char *module_str, int len);
22 static int show_cmd_help(char *argv[]);
23
24 static struct shell_module shell_modules[SHELL_MAX_MODULES];
25 static size_t num_of_shell_entities;
26
27 static const char *prompt;
28 static int default_module = -1;
29
30 static shell_cmd_func_t app_cmd_handler;
31 static shell_prompt_function_t app_prompt_handler;
32
console_write(const char * str,int cnt)33 void console_write(const char *str, int cnt)
34 {
35 rt_device_write(rt_console_get_device(), 0, str, cnt);
36 }
37
shell_register(const char * module_name,const struct shell_cmd * commands)38 int shell_register(const char *module_name, const struct shell_cmd *commands)
39 {
40 if (num_of_shell_entities >= SHELL_MAX_MODULES)
41 {
42 console_printf("Max number of modules reached\n");
43 return -1;
44 }
45
46 shell_modules[num_of_shell_entities].name = module_name;
47 shell_modules[num_of_shell_entities].commands = commands;
48 ++num_of_shell_entities;
49
50 return 0;
51 }
52
shell_register_default_module(const char * name)53 void shell_register_default_module(const char *name)
54 {
55 int result = set_default_module(name);
56
57 if (result != -1)
58 {
59 console_printf("\n");
60 print_prompt();
61 }
62 }
63
print_modules(void)64 static void print_modules(void)
65 {
66 int module;
67
68 for (module = 0; module < num_of_shell_entities; module++)
69 {
70 console_printf("%s\n", shell_modules[module].name);
71 }
72 }
73
print_module_commands(const int module)74 static void print_module_commands(const int module)
75 {
76 const struct shell_module *shell_module = &shell_modules[module];
77 int i;
78
79 console_printf("help\n");
80
81 for (i = 0; shell_module->commands[i].sc_cmd; i++)
82 {
83 console_printf("%-30s", shell_module->commands[i].sc_cmd);
84 if (shell_module->commands[i].help &&
85 shell_module->commands[i].help->summary)
86 {
87 console_printf("%s", shell_module->commands[i].help->summary);
88 }
89 console_printf("\n");
90 }
91 }
92
show_help(int argc,char * argv[])93 static int show_help(int argc, char *argv[])
94 {
95 int module;
96
97 /* help per command */
98 if ((argc > 2) || ((default_module != -1) && (argc == 2)))
99 {
100 return show_cmd_help(&argv[1]);
101 }
102
103 /* help per module */
104 if ((argc == 2) || ((default_module != -1) && (argc == 1)))
105 {
106 if (default_module == -1)
107 {
108 module = get_destination_module(argv[1], -1);
109 if (module == -1)
110 {
111 console_printf("Illegal module %s\n", argv[1]);
112 return 0;
113 }
114 }
115 else
116 {
117 module = default_module;
118 }
119
120 print_module_commands(module);
121 }
122 else /* help for all entities */
123 {
124 console_printf("Available modules:\n");
125 print_modules();
126 console_printf("To select a module, enter 'select <module name>'.\n");
127 }
128
129 return 0;
130 }
131
set_default_module(const char * name)132 static int set_default_module(const char *name)
133 {
134 int module;
135
136 module = get_destination_module(name, -1);
137
138 if (module == -1)
139 {
140 console_printf("Illegal module %s, default is not changed\n", name);
141 return -1;
142 }
143
144 default_module = module;
145
146 return 0;
147 }
148
select_module(int argc,char * argv[])149 static int select_module(int argc, char *argv[])
150 {
151 if (argc == 1)
152 {
153 default_module = -1;
154 }
155 else
156 {
157 set_default_module(argv[1]);
158 }
159
160 return 0;
161 }
162
line2argv(char * str,char * argv[],size_t size)163 static size_t line2argv(char *str, char *argv[], size_t size)
164 {
165 size_t argc = 0;
166
167 if (!strlen(str))
168 {
169 return 0;
170 }
171
172 while (*str && *str == ' ')
173 {
174 str++;
175 }
176
177 if (!*str)
178 {
179 return 0;
180 }
181
182 argv[argc++] = str;
183
184 while ((str = strchr(str, ' ')))
185 {
186 *str++ = '\0';
187
188 while (*str && *str == ' ')
189 {
190 str++;
191 }
192
193 if (!*str)
194 {
195 break;
196 }
197
198 argv[argc++] = str;
199
200 if (argc == size)
201 {
202 console_printf("Too many parameters (max %zu)\n", size - 1);
203 return 0;
204 }
205 }
206
207 /* keep it POSIX style where argv[argc] is required to be NULL */
208 argv[argc] = NULL;
209
210 return argc;
211 }
212
get_cb(int argc,char * argv[])213 static shell_cmd_func_t get_cb(int argc, char *argv[])
214 {
215 const char *first_string = argv[0];
216 int module = -1;
217 const struct shell_module *shell_module;
218 const char *command;
219 int i;
220
221 if (!first_string || first_string[0] == '\0')
222 {
223 console_printf("Illegal parameter\n");
224 return NULL;
225 }
226
227 if (!strcmp(first_string, "help"))
228 {
229 return show_help;
230 }
231
232 if (!strcmp(first_string, "select"))
233 {
234 return select_module;
235 }
236
237 if ((argc == 1) && (default_module == -1))
238 {
239 console_printf("Missing parameter\n");
240 return NULL;
241 }
242
243 command = get_command_and_module(argv, &module);
244 if ((module == -1) || (command == NULL))
245 {
246 return NULL;
247 }
248
249 shell_module = &shell_modules[module];
250 for (i = 0; shell_module->commands[i].sc_cmd; i++)
251 {
252 if (!strcmp(command, shell_module->commands[i].sc_cmd))
253 {
254 return shell_module->commands[i].sc_cmd_func;
255 }
256 }
257
258 return NULL;
259 }
260
261
get_prompt(void)262 static const char *get_prompt(void)
263 {
264 const char *str;
265
266 if (app_prompt_handler)
267 {
268
269 str = app_prompt_handler();
270 if (str)
271 {
272 return str;
273 }
274 }
275
276 if (default_module != -1)
277 {
278 return shell_modules[default_module].name;
279 }
280
281 return prompt;
282 }
283
print_prompt(void)284 static void print_prompt(void)
285 {
286 console_printf("%s%s", get_prompt(), SHELL_PROMPT_SUFFIX);
287 }
288
get_destination_module(const char * module_str,int len)289 static int get_destination_module(const char *module_str, int len)
290 {
291 int i;
292
293 for (i = 0; i < num_of_shell_entities; i++)
294 {
295 if (len < 0)
296 {
297 if (!strcmp(module_str, shell_modules[i].name))
298 {
299 return i;
300 }
301 }
302 else
303 {
304 if (!strncmp(module_str, shell_modules[i].name, len))
305 {
306 return i;
307 }
308 }
309 }
310
311 return -1;
312 }
313
314 /* For a specific command: argv[0] = module name, argv[1] = command name
315 * If a default module was selected: argv[0] = command name
316 */
get_command_and_module(char * argv[],int * module)317 static const char *get_command_and_module(char *argv[], int *module)
318 {
319 *module = -1;
320
321 if (!argv[0])
322 {
323 console_printf("Unrecognized command\n");
324 return NULL;
325 }
326
327 if (default_module == -1)
328 {
329 if (!argv[1] || argv[1][0] == '\0')
330 {
331 console_printf("Unrecognized command: %s\n", argv[0]);
332 return NULL;
333 }
334
335 *module = get_destination_module(argv[0], -1);
336 if (*module == -1)
337 {
338 console_printf("Illegal module %s\n", argv[0]);
339 return NULL;
340 }
341
342 return argv[1];
343 }
344
345 *module = default_module;
346 return argv[0];
347 }
348
print_command_params(const int module,const int command)349 static void print_command_params(const int module, const int command)
350 {
351 const struct shell_module *shell_module = &shell_modules[module];
352 const struct shell_cmd *shell_cmd = &shell_module->commands[command];
353 int i;
354
355 if (!(shell_cmd->help && shell_cmd->help->params))
356 {
357 return;
358 }
359
360 for (i = 0; shell_cmd->help->params[i].param_name; i++)
361 {
362 console_printf("%-30s%s\n", shell_cmd->help->params[i].param_name,
363 shell_cmd->help->params[i].help);
364 }
365 }
366
show_cmd_help(char * argv[])367 static int show_cmd_help(char *argv[])
368 {
369 const char *command = NULL;
370 int module = -1;
371 const struct shell_module *shell_module = NULL;
372 const struct shell_cmd *cmd;
373 int i;
374
375 command = get_command_and_module(argv, &module);
376 if ((module == -1) || (command == NULL))
377 {
378 return 0;
379 }
380
381 shell_module = &shell_modules[module];
382 for (i = 0; shell_module->commands[i].sc_cmd; i++)
383 {
384 cmd = &shell_module->commands[i];
385
386 if (!strcmp(command, cmd->sc_cmd))
387 {
388
389 if (!cmd->help || (!cmd->help->summary &&
390 !cmd->help->usage &&
391 !cmd->help->params))
392 {
393 console_printf("(no help available)\n");
394 return 0;
395 }
396
397 if (cmd->help->summary)
398 {
399 console_printf("Summary:\n");
400 console_printf("%s\n", cmd->help->summary);
401 }
402
403 if (cmd->help->usage)
404 {
405 console_printf("Usage:\n");
406 console_printf("%s\n", cmd->help->usage);
407 }
408
409 if (cmd->help->params)
410 {
411 console_printf("Parameters:\n");
412 print_command_params(module, i);
413 }
414
415 return 0;
416 }
417 }
418
419 console_printf("Unrecognized command: %s\n", argv[0]);
420 return 0;
421 }
422
shell_process_command(char * line)423 void shell_process_command(char *line)
424 {
425 char *argv[FINSH_CMD_SIZE + 1];
426 shell_cmd_func_t sc_cmd_func;
427 size_t argc_offset = 0;
428 size_t argc;
429
430 argc = line2argv(line, argv, FINSH_CMD_SIZE + 1);
431 if (!argc)
432 {
433 print_prompt();
434 return;
435 }
436
437 sc_cmd_func = get_cb(argc, argv);
438 if (!sc_cmd_func)
439 {
440 if (app_cmd_handler != NULL)
441 {
442 sc_cmd_func = app_cmd_handler;
443 }
444 else
445 {
446 console_printf("Unrecognized command: %s\n", argv[0]);
447 console_printf("Type 'help' for list of available commands\n");
448 print_prompt();
449 return;
450 }
451 }
452
453 /* Allow invoking a cmd with module name as a prefix; a command should
454 * not know how it was invoked (with or without prefix)
455 */
456 if (default_module == -1 && sc_cmd_func != select_module &&
457 sc_cmd_func != show_help)
458 {
459 argc_offset = 1;
460 }
461
462 /* Execute callback with arguments */
463 if (sc_cmd_func(argc - argc_offset, &argv[argc_offset]) < 0)
464 {
465 show_cmd_help(argv);
466 }
467
468 print_prompt();
469 }
470