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