xref: /nrf52832-nimble/rt-thread/components/finsh/msh.c (revision 104654410c56c573564690304ae786df310c91fc)
1 /*
2  * Copyright (c) 2006-2018, RT-Thread Development Team
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  *
6  * Change Logs:
7  * Date           Author       Notes
8  * 2013-03-30     Bernard      the first verion for finsh
9  * 2014-01-03     Bernard      msh can execute module.
10  * 2017-07-19     Aubr.Cool    limit argc to RT_FINSH_ARG_MAX
11  */
12 #include <rtthread.h>
13 
14 #ifdef FINSH_USING_MSH
15 
16 #include "msh.h"
17 #include <finsh.h>
18 #include <shell.h>
19 
20 #ifdef RT_USING_DFS
21 #include <dfs_posix.h>
22 #endif
23 
24 #ifdef RT_USING_MODULE
25 #include <dlmodule.h>
26 #endif
27 
28 #ifndef FINSH_ARG_MAX
29 #define FINSH_ARG_MAX    8
30 #endif
31 
32 typedef int (*cmd_function_t)(int argc, char **argv);
33 
34 #ifdef FINSH_USING_MSH
35 #ifdef FINSH_USING_MSH_ONLY
msh_is_used(void)36 rt_bool_t msh_is_used(void)
37 {
38     return RT_TRUE;
39 }
40 #else
41 #ifdef FINSH_USING_MSH_DEFAULT
42 static rt_bool_t __msh_state = RT_TRUE;
43 #else
44 static rt_bool_t __msh_state = RT_FALSE;
45 #endif
msh_is_used(void)46 rt_bool_t msh_is_used(void)
47 {
48     return __msh_state;
49 }
50 
msh_exit(int argc,char ** argv)51 static int msh_exit(int argc, char **argv)
52 {
53     /* return to finsh shell mode */
54     __msh_state = RT_FALSE;
55     return 0;
56 }
57 FINSH_FUNCTION_EXPORT_ALIAS(msh_exit, __cmd_exit, return to RT-Thread shell mode.);
58 
msh_enter(void)59 static int msh_enter(void)
60 {
61     /* enter module shell mode */
62     __msh_state = RT_TRUE;
63     return 0;
64 }
65 FINSH_FUNCTION_EXPORT_ALIAS(msh_enter, msh, use module shell);
66 #endif
67 
msh_help(int argc,char ** argv)68 int msh_help(int argc, char **argv)
69 {
70     rt_kprintf("RT-Thread shell commands:\n");
71     {
72         struct finsh_syscall *index;
73 
74         for (index = _syscall_table_begin;
75                 index < _syscall_table_end;
76                 FINSH_NEXT_SYSCALL(index))
77         {
78             if (strncmp(index->name, "__cmd_", 6) != 0) continue;
79 #if defined(FINSH_USING_DESCRIPTION) && defined(FINSH_USING_SYMTAB)
80             rt_kprintf("%-16s - %s\n", &index->name[6], index->desc);
81 #else
82             rt_kprintf("%s ", &index->name[6]);
83 #endif
84         }
85     }
86     rt_kprintf("\n");
87 
88     return 0;
89 }
90 FINSH_FUNCTION_EXPORT_ALIAS(msh_help, __cmd_help, RT-Thread shell help.);
91 
msh_split(char * cmd,rt_size_t length,char * argv[FINSH_ARG_MAX])92 static int msh_split(char *cmd, rt_size_t length, char *argv[FINSH_ARG_MAX])
93 {
94     char *ptr;
95     rt_size_t position;
96     rt_size_t argc;
97     rt_size_t i;
98 
99     ptr = cmd;
100     position = 0; argc = 0;
101 
102     while (position < length)
103     {
104         /* strip bank and tab */
105         while ((*ptr == ' ' || *ptr == '\t') && position < length)
106         {
107             *ptr = '\0';
108             ptr ++; position ++;
109         }
110 
111         if(argc >= FINSH_ARG_MAX)
112         {
113             rt_kprintf("Too many args ! We only Use:\n");
114             for(i = 0; i < argc; i++)
115             {
116                 rt_kprintf("%s ", argv[i]);
117             }
118             rt_kprintf("\n");
119             break;
120         }
121 
122         if (position >= length) break;
123 
124         /* handle string */
125         if (*ptr == '"')
126         {
127             ptr ++; position ++;
128             argv[argc] = ptr; argc ++;
129 
130             /* skip this string */
131             while (*ptr != '"' && position < length)
132             {
133                 if (*ptr == '\\')
134                 {
135                     if (*(ptr + 1) == '"')
136                     {
137                         ptr ++; position ++;
138                     }
139                 }
140                 ptr ++; position ++;
141             }
142             if (position >= length) break;
143 
144             /* skip '"' */
145             *ptr = '\0'; ptr ++; position ++;
146         }
147         else
148         {
149             argv[argc] = ptr;
150             argc ++;
151             while ((*ptr != ' ' && *ptr != '\t') && position < length)
152             {
153                 ptr ++; position ++;
154             }
155             if (position >= length) break;
156         }
157     }
158 
159     return argc;
160 }
161 
msh_get_cmd(char * cmd,int size)162 static cmd_function_t msh_get_cmd(char *cmd, int size)
163 {
164     struct finsh_syscall *index;
165     cmd_function_t cmd_func = RT_NULL;
166 
167     for (index = _syscall_table_begin;
168             index < _syscall_table_end;
169             FINSH_NEXT_SYSCALL(index))
170     {
171         if (strncmp(index->name, "__cmd_", 6) != 0) continue;
172 
173         if (strncmp(&index->name[6], cmd, size) == 0 &&
174                 index->name[6 + size] == '\0')
175         {
176             cmd_func = (cmd_function_t)index->func;
177             break;
178         }
179     }
180 
181     return cmd_func;
182 }
183 
184 #if defined(RT_USING_MODULE) && defined(RT_USING_DFS)
185 /* Return 0 on module executed. Other value indicate error.
186  */
msh_exec_module(const char * cmd_line,int size)187 int msh_exec_module(const char *cmd_line, int size)
188 {
189     int ret;
190     int fd = -1;
191     char *pg_name;
192     int length, cmd_length = 0;
193 
194     if (size == 0)
195         return -RT_ERROR;
196     /* get the length of command0 */
197     while ((cmd_line[cmd_length] != ' ' && cmd_line[cmd_length] != '\t') && cmd_length < size)
198         cmd_length ++;
199 
200     /* get name length */
201     length = cmd_length + 32;
202 
203     /* allocate program name memory */
204     pg_name = (char *) rt_malloc(length);
205     if (pg_name == RT_NULL)
206         return -RT_ENOMEM;
207 
208     /* copy command0 */
209     memcpy(pg_name, cmd_line, cmd_length);
210     pg_name[cmd_length] = '\0';
211 
212     if (strstr(pg_name, ".mo") != RT_NULL || strstr(pg_name, ".MO") != RT_NULL)
213     {
214         /* try to open program */
215         fd = open(pg_name, O_RDONLY, 0);
216 
217         /* search in /bin path */
218         if (fd < 0)
219         {
220             rt_snprintf(pg_name, length - 1, "/bin/%.*s", cmd_length, cmd_line);
221             fd = open(pg_name, O_RDONLY, 0);
222         }
223     }
224     else
225     {
226         /* add .mo and open program */
227 
228         /* try to open program */
229         strcat(pg_name, ".mo");
230         fd = open(pg_name, O_RDONLY, 0);
231 
232         /* search in /bin path */
233         if (fd < 0)
234         {
235             rt_snprintf(pg_name, length - 1, "/bin/%.*s.mo", cmd_length, cmd_line);
236             fd = open(pg_name, O_RDONLY, 0);
237         }
238     }
239 
240     if (fd >= 0)
241     {
242         /* found program */
243         close(fd);
244         dlmodule_exec(pg_name, cmd_line, size);
245         ret = 0;
246     }
247     else
248     {
249         ret = -1;
250     }
251 
252     rt_free(pg_name);
253     return ret;
254 }
255 
system(const char * command)256 int system(const char *command)
257 {
258     int ret = -RT_ENOMEM;
259     char *cmd = rt_strdup(command);
260 
261     if (cmd)
262     {
263         ret = msh_exec(cmd, rt_strlen(cmd));
264         rt_free(cmd);
265     }
266 
267     return ret;
268 }
269 RTM_EXPORT(system);
270 #endif
271 
_msh_exec_cmd(char * cmd,rt_size_t length,int * retp)272 static int _msh_exec_cmd(char *cmd, rt_size_t length, int *retp)
273 {
274     int argc;
275     rt_size_t cmd0_size = 0;
276     cmd_function_t cmd_func;
277     char *argv[FINSH_ARG_MAX];
278 
279     RT_ASSERT(cmd);
280     RT_ASSERT(retp);
281 
282     /* find the size of first command */
283     while ((cmd[cmd0_size] != ' ' && cmd[cmd0_size] != '\t') && cmd0_size < length)
284         cmd0_size ++;
285     if (cmd0_size == 0)
286         return -RT_ERROR;
287 
288     cmd_func = msh_get_cmd(cmd, cmd0_size);
289     if (cmd_func == RT_NULL)
290         return -RT_ERROR;
291 
292     /* split arguments */
293     memset(argv, 0x00, sizeof(argv));
294     argc = msh_split(cmd, length, argv);
295     if (argc == 0)
296         return -RT_ERROR;
297 
298     /* exec this command */
299     *retp = cmd_func(argc, argv);
300     return 0;
301 }
302 
303 #if defined(RT_USING_LWP) && defined(RT_USING_DFS)
_msh_exec_lwp(char * cmd,rt_size_t length)304 static int _msh_exec_lwp(char *cmd, rt_size_t length)
305 {
306     int argc;
307     int cmd0_size = 0;
308     char *argv[FINSH_ARG_MAX];
309     int fd = -1;
310     char *pg_name;
311 
312     extern int exec(char*, int, char**);
313 
314     /* find the size of first command */
315     while ((cmd[cmd0_size] != ' ' && cmd[cmd0_size] != '\t') && cmd0_size < length)
316         cmd0_size ++;
317     if (cmd0_size == 0)
318         return -1;
319 
320     /* split arguments */
321     rt_memset(argv, 0x00, sizeof(argv));
322     argc = msh_split(cmd, length, argv);
323     if (argc == 0)
324         return -1;
325 
326     pg_name = argv[0];
327     /* try to open program */
328     fd = open(pg_name, O_RDONLY, 0);
329 
330     if (fd < 0)
331         return -1;
332 
333     /* found program */
334     close(fd);
335     exec(pg_name, argc, argv);
336 
337     return 0;
338 }
339 #endif
340 
msh_exec(char * cmd,rt_size_t length)341 int msh_exec(char *cmd, rt_size_t length)
342 {
343     int cmd_ret;
344 
345     /* strim the beginning of command */
346     while (*cmd  == ' ' || *cmd == '\t')
347     {
348         cmd++;
349         length--;
350     }
351 
352     if (length == 0)
353         return 0;
354 
355     /* Exec sequence:
356      * 1. built-in command
357      * 2. module(if enabled)
358      */
359     if (_msh_exec_cmd(cmd, length, &cmd_ret) == 0)
360     {
361         return cmd_ret;
362     }
363 #ifdef RT_USING_DFS
364 #ifdef DFS_USING_WORKDIR
365     if (msh_exec_script(cmd, length) == 0)
366     {
367         return 0;
368     }
369 #endif
370 
371 #ifdef RT_USING_MODULE
372     if (msh_exec_module(cmd, length) == 0)
373     {
374         return 0;
375     }
376 #endif
377 
378 #ifdef RT_USING_LWP
379     if (_msh_exec_lwp(cmd, length) == 0)
380     {
381         return 0;
382     }
383 #endif
384 #endif
385 
386     /* truncate the cmd at the first space. */
387     {
388         char *tcmd;
389         tcmd = cmd;
390         while (*tcmd != ' ' && *tcmd != '\0')
391         {
392             tcmd++;
393         }
394         *tcmd = '\0';
395     }
396     rt_kprintf("%s: command not found.\n", cmd);
397     return -1;
398 }
399 
str_common(const char * str1,const char * str2)400 static int str_common(const char *str1, const char *str2)
401 {
402     const char *str = str1;
403 
404     while ((*str != 0) && (*str2 != 0) && (*str == *str2))
405     {
406         str ++;
407         str2 ++;
408     }
409 
410     return (str - str1);
411 }
412 
413 #ifdef RT_USING_DFS
msh_auto_complete_path(char * path)414 void msh_auto_complete_path(char *path)
415 {
416     DIR *dir = RT_NULL;
417     struct dirent *dirent = RT_NULL;
418     char *full_path, *ptr, *index;
419 
420     if (!path)
421         return;
422 
423     full_path = (char *)rt_malloc(256);
424     if (full_path == RT_NULL) return; /* out of memory */
425 
426     if (*path != '/')
427     {
428         getcwd(full_path, 256);
429         if (full_path[rt_strlen(full_path) - 1]  != '/')
430             strcat(full_path, "/");
431     }
432     else *full_path = '\0';
433 
434     index = RT_NULL;
435     ptr = path;
436     for (;;)
437     {
438         if (*ptr == '/') index = ptr + 1;
439         if (!*ptr) break;
440 
441         ptr ++;
442     }
443     if (index == RT_NULL) index = path;
444 
445     if (index != RT_NULL)
446     {
447         char *dest = index;
448 
449         /* fill the parent path */
450         ptr = full_path;
451         while (*ptr) ptr ++;
452 
453         for (index = path; index != dest;)
454             *ptr++ = *index++;
455         *ptr = '\0';
456 
457         dir = opendir(full_path);
458         if (dir == RT_NULL) /* open directory failed! */
459         {
460             rt_free(full_path);
461             return;
462         }
463 
464         /* restore the index position */
465         index = dest;
466     }
467 
468     /* auto complete the file or directory name */
469     if (*index == '\0') /* display all of files and directories */
470     {
471         for (;;)
472         {
473             dirent = readdir(dir);
474             if (dirent == RT_NULL) break;
475 
476             rt_kprintf("%s\n", dirent->d_name);
477         }
478     }
479     else
480     {
481         rt_size_t length, min_length;
482 
483         min_length = 0;
484         for (;;)
485         {
486             dirent = readdir(dir);
487             if (dirent == RT_NULL) break;
488 
489             /* matched the prefix string */
490             if (strncmp(index, dirent->d_name, rt_strlen(index)) == 0)
491             {
492                 if (min_length == 0)
493                 {
494                     min_length = rt_strlen(dirent->d_name);
495                     /* save dirent name */
496                     strcpy(full_path, dirent->d_name);
497                 }
498 
499                 length = str_common(dirent->d_name, full_path);
500 
501                 if (length < min_length)
502                 {
503                     min_length = length;
504                 }
505             }
506         }
507 
508         if (min_length)
509         {
510             if (min_length < rt_strlen(full_path))
511             {
512                 /* list the candidate */
513                 rewinddir(dir);
514 
515                 for (;;)
516                 {
517                     dirent = readdir(dir);
518                     if (dirent == RT_NULL) break;
519 
520                     if (strncmp(index, dirent->d_name, rt_strlen(index)) == 0)
521                         rt_kprintf("%s\n", dirent->d_name);
522                 }
523             }
524 
525             length = index - path;
526             memcpy(index, full_path, min_length);
527             path[length + min_length] = '\0';
528         }
529     }
530 
531     closedir(dir);
532     rt_free(full_path);
533 }
534 #endif
535 
msh_auto_complete(char * prefix)536 void msh_auto_complete(char *prefix)
537 {
538     int length, min_length;
539     const char *name_ptr, *cmd_name;
540     struct finsh_syscall *index;
541 
542     min_length = 0;
543     name_ptr = RT_NULL;
544 
545     if (*prefix == '\0')
546     {
547         msh_help(0, RT_NULL);
548         return;
549     }
550 
551 #ifdef RT_USING_DFS
552     /* check whether a spare in the command */
553     {
554         char *ptr;
555 
556         ptr = prefix + rt_strlen(prefix);
557         while (ptr != prefix)
558         {
559             if (*ptr == ' ')
560             {
561                 msh_auto_complete_path(ptr + 1);
562                 break;
563             }
564 
565             ptr --;
566         }
567 #ifdef RT_USING_MODULE
568         /* There is a chance that the user want to run the module directly. So
569          * try to complete the file names. If the completed path is not a
570          * module, the system won't crash anyway. */
571         if (ptr == prefix)
572         {
573             msh_auto_complete_path(ptr);
574         }
575 #endif
576     }
577 #endif
578 
579     /* checks in internal command */
580     {
581         for (index = _syscall_table_begin; index < _syscall_table_end; FINSH_NEXT_SYSCALL(index))
582         {
583             /* skip finsh shell function */
584             if (strncmp(index->name, "__cmd_", 6) != 0) continue;
585 
586             cmd_name = (const char *) &index->name[6];
587             if (strncmp(prefix, cmd_name, strlen(prefix)) == 0)
588             {
589                 if (min_length == 0)
590                 {
591                     /* set name_ptr */
592                     name_ptr = cmd_name;
593                     /* set initial length */
594                     min_length = strlen(name_ptr);
595                 }
596 
597                 length = str_common(name_ptr, cmd_name);
598                 if (length < min_length)
599                     min_length = length;
600 
601                 rt_kprintf("%s\n", cmd_name);
602             }
603         }
604     }
605 
606     /* auto complete string */
607     if (name_ptr != NULL)
608     {
609         rt_strncpy(prefix, name_ptr, min_length);
610     }
611 
612     return ;
613 }
614 #endif
615 
616 #endif /* FINSH_USING_MSH */
617