xref: /nrf52832-nimble/packages/NimBLE-latest/porting/npl/rtthread/src/npl_shell.c (revision 042d53a763ad75cb1465103098bb88c245d95138)
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