1 /*
2  * Copyright (c) 2006-2022, 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 #include <string.h>
14 #include <errno.h>
15 
16 #ifdef RT_USING_FINSH
17 
18 #ifndef FINSH_ARG_MAX
19 #define FINSH_ARG_MAX    8
20 #endif /* FINSH_ARG_MAX */
21 
22 #include "msh.h"
23 #include "shell.h"
24 #ifdef DFS_USING_POSIX
25 #include <dfs_file.h>
26 #include <unistd.h>
27 #include <fcntl.h>
28 #endif /* DFS_USING_POSIX */
29 #ifdef RT_USING_MODULE
30 #include <dlmodule.h>
31 #endif /* RT_USING_MODULE */
32 
33 typedef int (*cmd_function_t)(int argc, char **argv);
34 
msh_help(int argc,char ** argv)35 static int msh_help(int argc, char **argv)
36 {
37     rt_kprintf("RT-Thread shell commands:\n");
38     {
39         struct finsh_syscall *index;
40 
41         for (index = _syscall_table_begin;
42                 index < _syscall_table_end;
43                 FINSH_NEXT_SYSCALL(index))
44         {
45 #if defined(FINSH_USING_DESCRIPTION) && defined(FINSH_USING_SYMTAB)
46             rt_kprintf("%-16s - %s\n", index->name, index->desc);
47 #else
48             rt_kprintf("%s ", index->name);
49 #endif
50         }
51     }
52     rt_kprintf("\n");
53 
54     return 0;
55 }
56 MSH_CMD_EXPORT_ALIAS(msh_help, help, RT-Thread shell help);
57 
58 #ifdef MSH_USING_BUILT_IN_COMMANDS
cmd_ps(int argc,char ** argv)59 static int cmd_ps(int argc, char **argv)
60 {
61     extern long list_thread(void);
62     extern int list_module(void);
63 
64 #ifdef RT_USING_MODULE
65     if ((argc == 2) && (strcmp(argv[1], "-m") == 0))
66         list_module();
67     else
68 #endif
69     list_thread();
70     return 0;
71 }
72 MSH_CMD_EXPORT_ALIAS(cmd_ps, ps, List threads in the system);
73 
74 #ifdef RT_USING_HEAP
cmd_free(int argc,char ** argv)75 static int cmd_free(int argc, char **argv)
76 {
77 #ifdef RT_USING_MEMHEAP_AS_HEAP
78     extern void list_memheap(void);
79     list_memheap();
80 #else
81     rt_size_t total = 0, used = 0, max_used = 0;
82 
83     rt_memory_info(&total, &used, &max_used);
84     rt_kprintf("total    : %d\n", total);
85     rt_kprintf("used     : %d\n", used);
86     rt_kprintf("maximum  : %d\n", max_used);
87     rt_kprintf("available: %d\n", total - used);
88 #endif
89     return 0;
90 }
91 MSH_CMD_EXPORT_ALIAS(cmd_free, free, Show the memory usage in the system);
92 #endif /* RT_USING_HEAP */
93 
94 #if RT_CPUS_NR > 1
cmd_bind(int argc,char ** argv)95 static int cmd_bind(int argc, char **argv)
96 {
97     rt_err_t result;
98     rt_ubase_t thread_id;
99     rt_ubase_t core_id;
100     rt_thread_t thread;
101     char *endptr;
102 
103     if (argc != 3)
104     {
105         rt_kprintf("Usage: bind <thread_id> <core_id>\n");
106         return 0;
107     }
108 
109     /* Parse thread_id */
110     thread_id = (rt_ubase_t)strtoul(argv[1], &endptr, 0);
111     if (*endptr != '\0')
112     {
113         rt_kprintf("Error: Invalid thread ID '%s'\n", argv[1]);
114         return 0;
115     }
116 
117     /* Parse core_id */
118     core_id = (rt_uint8_t)strtoul(argv[2], &endptr, 0);
119     if (*endptr != '\0')
120     {
121         rt_kprintf("Error: Invalid core ID '%s'\n", argv[2]);
122         return 0;
123     }
124 
125     thread = (rt_thread_t)thread_id;
126 
127     if (rt_object_get_type(&thread->parent) != RT_Object_Class_Thread)
128     {
129         rt_kprintf("Error: Invalid thread ID %#lx\n", thread_id);
130         return 0;
131     }
132 
133     result = rt_thread_control(thread, RT_THREAD_CTRL_BIND_CPU, (void *)core_id);
134     if (result == RT_EOK)
135     {
136         rt_kprintf("Thread 0x%lx bound to core %d successfully\n",
137             thread_id, core_id);
138     }
139     else
140     {
141         rt_kprintf("Failed to bind thread 0x%lx to core %d\n",
142             thread_id, core_id);
143     }
144     return 0;
145 }
146 MSH_CMD_EXPORT_ALIAS(cmd_bind, bind, Binding thread to core);
147 #endif /* RT_CPUS_NR > 1 */
148 
149 #endif /* MSH_USING_BUILT_IN_COMMANDS */
150 
msh_split(char * cmd,rt_size_t length,char * argv[FINSH_ARG_MAX])151 static int msh_split(char *cmd, rt_size_t length, char *argv[FINSH_ARG_MAX])
152 {
153     char *ptr;
154     rt_size_t position;
155     rt_size_t argc;
156     rt_size_t i;
157 
158     ptr = cmd;
159     position = 0;
160     argc = 0;
161 
162     while (position < length)
163     {
164         /* strip bank and tab */
165         while ((*ptr == ' ' || *ptr == '\t') && position < length)
166         {
167             *ptr = '\0';
168             ptr ++;
169             position ++;
170         }
171 
172         if (argc >= FINSH_ARG_MAX)
173         {
174             rt_kprintf("Too many args ! We only Use:\n");
175             for (i = 0; i < argc; i++)
176             {
177                 rt_kprintf("%s ", argv[i]);
178             }
179             rt_kprintf("\n");
180             break;
181         }
182 
183         if (position >= length) break;
184 
185         /* handle string */
186         if (*ptr == '"')
187         {
188             ptr ++;
189             position ++;
190             argv[argc] = ptr;
191             argc ++;
192 
193             /* skip this string */
194             while (*ptr != '"' && position < length)
195             {
196                 if (*ptr == '\\')
197                 {
198                     if (*(ptr + 1) == '"')
199                     {
200                         ptr ++;
201                         position ++;
202                     }
203                 }
204                 ptr ++;
205                 position ++;
206             }
207             if (position >= length) break;
208 
209             /* skip '"' */
210             *ptr = '\0';
211             ptr ++;
212             position ++;
213         }
214         else
215         {
216             argv[argc] = ptr;
217             argc ++;
218             while ((*ptr != ' ' && *ptr != '\t') && position < length)
219             {
220                 ptr ++;
221                 position ++;
222             }
223             if (position >= length) break;
224         }
225     }
226 
227     return argc;
228 }
229 
msh_get_cmd(char * cmd,int size)230 static cmd_function_t msh_get_cmd(char *cmd, int size)
231 {
232     struct finsh_syscall *index;
233     cmd_function_t cmd_func = RT_NULL;
234 
235     for (index = _syscall_table_begin;
236             index < _syscall_table_end;
237             FINSH_NEXT_SYSCALL(index))
238     {
239         if (strncmp(index->name, cmd, size) == 0 &&
240                 index->name[size] == '\0')
241         {
242             cmd_func = (cmd_function_t)index->func;
243             break;
244         }
245     }
246 
247     return cmd_func;
248 }
249 
250 #if defined(RT_USING_MODULE) && defined(DFS_USING_POSIX)
251 /* Return 0 on module executed. Other value indicate error.
252  */
msh_exec_module(const char * cmd_line,int size)253 int msh_exec_module(const char *cmd_line, int size)
254 {
255     int ret;
256     int fd = -1;
257     char *pg_name;
258     int length, cmd_length = 0;
259 
260     if (size == 0)
261         return -RT_ERROR;
262     /* get the length of command0 */
263     while ((cmd_line[cmd_length] != ' ' && cmd_line[cmd_length] != '\t') && cmd_length < size)
264         cmd_length ++;
265 
266     /* get name length */
267     length = cmd_length + 32;
268 
269     /* allocate program name memory */
270     pg_name = (char *) rt_malloc(length + 3);
271     if (pg_name == RT_NULL)
272         return -RT_ENOMEM;
273 
274     /* copy command0 */
275     rt_memcpy(pg_name, cmd_line, cmd_length);
276     pg_name[cmd_length] = '\0';
277 
278     if (strstr(pg_name, ".mo") != RT_NULL || strstr(pg_name, ".MO") != RT_NULL)
279     {
280         /* try to open program */
281         fd = open(pg_name, O_RDONLY, 0);
282 
283         /* search in /bin path */
284         if (fd < 0)
285         {
286             rt_snprintf(pg_name, length - 1, "/bin/%.*s", cmd_length, cmd_line);
287             fd = open(pg_name, O_RDONLY, 0);
288         }
289     }
290     else
291     {
292         /* add .mo and open program */
293 
294         /* try to open program */
295         strcat(pg_name, ".mo");
296         fd = open(pg_name, O_RDONLY, 0);
297 
298         /* search in /bin path */
299         if (fd < 0)
300         {
301             rt_snprintf(pg_name, length - 1, "/bin/%.*s.mo", cmd_length, cmd_line);
302             fd = open(pg_name, O_RDONLY, 0);
303         }
304     }
305 
306     if (fd >= 0)
307     {
308         /* found program */
309         close(fd);
310         dlmodule_exec(pg_name, cmd_line, size);
311         ret = 0;
312     }
313     else
314     {
315         ret = -1;
316     }
317 
318     rt_free(pg_name);
319     return ret;
320 }
321 #endif
322 
_msh_exec_cmd(char * cmd,rt_size_t length,int * retp)323 static int _msh_exec_cmd(char *cmd, rt_size_t length, int *retp)
324 {
325     int argc;
326     rt_size_t cmd0_size = 0;
327     cmd_function_t cmd_func;
328     char *argv[FINSH_ARG_MAX];
329 
330     RT_ASSERT(cmd);
331     RT_ASSERT(retp);
332 
333     /* find the size of first command */
334     while (cmd0_size < length && (cmd[cmd0_size] != ' ' && cmd[cmd0_size] != '\t'))
335         cmd0_size ++;
336     if (cmd0_size == 0)
337         return -RT_ERROR;
338 
339     cmd_func = msh_get_cmd(cmd, cmd0_size);
340     if (cmd_func == RT_NULL)
341         return -RT_ERROR;
342 
343     /* split arguments */
344     rt_memset(argv, 0x00, sizeof(argv));
345     argc = msh_split(cmd, length, argv);
346     if (argc == 0)
347         return -RT_ERROR;
348 
349     /* exec this command */
350     *retp = cmd_func(argc, argv);
351     return 0;
352 }
353 
354 #if defined(RT_USING_SMART) && defined(DFS_USING_POSIX)
355 #include <lwp.h>
356 /* check whether a file of the given path exits */
_msh_lwp_cmd_exists(const char * path)357 static rt_bool_t _msh_lwp_cmd_exists(const char *path)
358 {
359     int fd = -1;
360     fd = open(path, O_RDONLY, 0);
361     if (fd < 0)
362     {
363         return RT_FALSE;
364     }
365     close(fd);
366     return RT_TRUE;
367 }
368 
369 /*
370  * search for the file named "pg_name" or "pg_name.elf" at the given directory,
371  * and return its path. return NULL when not found.
372  */
_msh_exec_search_path(const char * path,const char * pg_name)373 static char *_msh_exec_search_path(const char *path, const char *pg_name)
374 {
375     char *path_buffer = RT_NULL;
376     ssize_t pg_len = strlen(pg_name);
377     ssize_t base_len = 0;
378 
379     if (path)
380     {
381         base_len = strlen(path);
382     }
383 
384     path_buffer = rt_malloc(base_len + pg_len + 6);
385     if (path_buffer == RT_NULL)
386     {
387         return RT_NULL; /* no mem */
388     }
389 
390     if (base_len > 0)
391     {
392         memcpy(path_buffer, path, base_len);
393         path_buffer[base_len] = '/';
394         path_buffer[base_len + 1] = '\0';
395     }
396     else
397     {
398         *path_buffer = '\0';
399     }
400     strcat(path_buffer, pg_name);
401 
402     if (_msh_lwp_cmd_exists(path_buffer))
403     {
404         return path_buffer;
405     }
406 
407     if (strstr(path_buffer, ".elf") != NULL)
408     {
409         goto not_found;
410     }
411 
412     strcat(path_buffer, ".elf");
413     if (_msh_lwp_cmd_exists(path_buffer))
414     {
415         return path_buffer;
416     }
417 
418 not_found:
419     rt_free(path_buffer);
420     return RT_NULL;
421 }
422 
423 /*
424  * search for the file named "pg_name" or "pg_name.elf" at each env path,
425  * and return its path. return NULL when not found.
426  */
_msh_exec_search_env(const char * pg_name)427 static char *_msh_exec_search_env(const char *pg_name)
428 {
429     char *result = RT_NULL;
430     char *exec_path = RT_NULL;
431     char *search_path = RT_NULL;
432     char *pos = RT_NULL;
433     char tmp_ch = '\0';
434 
435     if (!(exec_path = getenv("PATH")))
436     {
437         return RT_NULL;
438     }
439 
440     /* exec path may need to be modified */
441     if (!(exec_path = strdup(exec_path)))
442     {
443         return RT_NULL;
444     }
445 
446     pos = exec_path;
447     search_path = exec_path;
448 
449     /* walk through the entire exec_path until finding the program wanted
450        or hitting its end */
451     while (1)
452     {
453         /* env paths are seperated by ':' */
454         if (*pos == ':' || *pos == '\0')
455         {
456             tmp_ch = *pos;
457             *pos = '\0';
458 
459             result = _msh_exec_search_path(search_path, pg_name);
460             if (result || tmp_ch == '\0')
461             {
462                 goto ret;
463             }
464 
465             pos++;
466             search_path = pos;
467             continue;
468         }
469 
470         pos++;
471     }
472 
473     /* release the duplicated exec_path and return */
474 ret:
475     rt_free(exec_path);
476     return result;
477 }
478 
_msh_exec_lwp(int debug,char * cmd,rt_size_t length)479 int _msh_exec_lwp(int debug, char *cmd, rt_size_t length)
480 {
481     int argc;
482     int cmd0_size = 0;
483     char *argv[FINSH_ARG_MAX];
484     char *pg_name;
485     int ret;
486 
487     /* find the size of first command */
488     while ((cmd[cmd0_size] != ' ' && cmd[cmd0_size] != '\t') && cmd0_size < length)
489         cmd0_size ++;
490     if (cmd0_size == 0)
491         return -1;
492 
493     /* split arguments */
494     rt_memset(argv, 0x00, sizeof(argv));
495     argc = msh_split(cmd, length, argv);
496     if (argc == 0)
497         return -1;
498 
499     /* try to find program in working directory */
500     pg_name = _msh_exec_search_path("", argv[0]);
501     if (pg_name)
502     {
503         goto found_program;
504     }
505 
506     /* only check these paths when the first argument doesn't contain path
507        seperator */
508     if (strstr(argv[0], "/"))
509     {
510         return -1;
511     }
512 
513     /* try to find program in /bin */
514     pg_name = _msh_exec_search_path("/bin", argv[0]);
515     if (pg_name)
516     {
517         goto found_program;
518     }
519 
520     /* try to find program in dirs registered to env path */
521     pg_name = _msh_exec_search_env(argv[0]);
522     if (pg_name)
523     {
524         goto found_program;
525     }
526 
527     /* not found in anywhere */
528     return -1;
529 
530     /* found program */
531 found_program:
532     ret = exec(pg_name, debug, argc, argv);
533     rt_free(pg_name);
534 
535     return ret;
536 }
537 #endif
538 
539 
msh_exec(char * cmd,rt_size_t length)540 int msh_exec(char *cmd, rt_size_t length)
541 {
542     int cmd_ret = 0;
543 
544     /* strim the beginning of command */
545     while ((length > 0) && (*cmd  == ' ' || *cmd == '\t'))
546     {
547         cmd++;
548         length--;
549     }
550 
551     if (length == 0)
552         return 0;
553 
554     /* Exec sequence:
555      * 1. built-in command
556      * 2. module(if enabled)
557      */
558     if (_msh_exec_cmd(cmd, length, &cmd_ret) == 0)
559     {
560         if(cmd_ret < 0)
561         {
562             rt_kprintf("%s: command failed %d.\n", cmd, cmd_ret);
563         }
564         return cmd_ret;
565     }
566 #ifdef DFS_USING_POSIX
567 #ifdef DFS_USING_WORKDIR
568     if (msh_exec_script(cmd, length) == 0)
569     {
570         return 0;
571     }
572 #endif
573 
574 #ifdef RT_USING_MODULE
575     if (msh_exec_module(cmd, length) == 0)
576     {
577         return 0;
578     }
579 #endif /* RT_USING_MODULE */
580 
581 #ifdef RT_USING_SMART
582     /* exec from msh_exec , debug = 0*/
583     /* _msh_exec_lwp return is pid , <= 0 means failed */
584     cmd_ret = _msh_exec_lwp(0, cmd, length);
585     if (cmd_ret > 0)
586     {
587         return 0;
588     }
589 #endif /* RT_USING_SMART */
590 #endif /* DFS_USING_POSIX */
591 
592     /* truncate the cmd at the first space. */
593     {
594         char *tcmd;
595         tcmd = cmd;
596         while (*tcmd != ' ' && *tcmd != '\0')
597         {
598             tcmd++;
599         }
600         *tcmd = '\0';
601     }
602 #ifdef RT_USING_SMART
603     if (cmd_ret == -EACCES)
604     {
605         rt_kprintf("%s: Permission denied.\n", cmd);
606     }
607     else
608 #endif
609     {
610         rt_kprintf("%s: command not found.\n", cmd);
611     }
612     return -1;
613 }
614 
str_common(const char * str1,const char * str2)615 static int str_common(const char *str1, const char *str2)
616 {
617     const char *str = str1;
618 
619     while ((*str != 0) && (*str2 != 0) && (*str == *str2))
620     {
621         str ++;
622         str2 ++;
623     }
624 
625     return (str - str1);
626 }
627 
628 #ifdef DFS_USING_POSIX
msh_auto_complete_path(char * path)629 void msh_auto_complete_path(char *path)
630 {
631     DIR *dir = RT_NULL;
632     struct dirent *dirent = RT_NULL;
633     char *full_path, *ptr, *index;
634 
635     if (!path)
636         return;
637 
638     full_path = (char *)rt_malloc(256);
639     if (full_path == RT_NULL) return; /* out of memory */
640 
641     if (*path != '/')
642     {
643         getcwd(full_path, 256);
644         if (full_path[rt_strlen(full_path) - 1]  != '/')
645             strcat(full_path, "/");
646     }
647     else *full_path = '\0';
648 
649     index = RT_NULL;
650     ptr = path;
651     for (;;)
652     {
653         if (*ptr == '/') index = ptr + 1;
654         if (!*ptr) break;
655 
656         ptr ++;
657     }
658     if (index == RT_NULL) index = path;
659 
660     if (index != RT_NULL)
661     {
662         char *dest = index;
663 
664         /* fill the parent path */
665         ptr = full_path;
666         while (*ptr) ptr ++;
667 
668         for (index = path; index != dest;)
669             *ptr++ = *index++;
670         *ptr = '\0';
671 
672         dir = opendir(full_path);
673         if (dir == RT_NULL) /* open directory failed! */
674         {
675             rt_free(full_path);
676             return;
677         }
678 
679         /* restore the index position */
680         index = dest;
681     }
682 
683     /* auto complete the file or directory name */
684     if (*index == '\0') /* display all of files and directories */
685     {
686         for (;;)
687         {
688             dirent = readdir(dir);
689             if (dirent == RT_NULL) break;
690 
691             rt_kprintf("%s\n", dirent->d_name);
692         }
693     }
694     else
695     {
696         int multi = 0;
697         rt_size_t length, min_length;
698 
699         min_length = 0;
700         for (;;)
701         {
702             dirent = readdir(dir);
703             if (dirent == RT_NULL) break;
704 
705             /* matched the prefix string */
706             if (strncmp(index, dirent->d_name, rt_strlen(index)) == 0)
707             {
708                 multi ++;
709                 if (min_length == 0)
710                 {
711                     min_length = rt_strlen(dirent->d_name);
712                     /* save dirent name */
713                     strcpy(full_path, dirent->d_name);
714                 }
715 
716                 length = str_common(dirent->d_name, full_path);
717 
718                 if (length < min_length)
719                 {
720                     min_length = length;
721                 }
722             }
723         }
724 
725         if (min_length)
726         {
727             if (multi > 1)
728             {
729                 /* list the candidate */
730                 rewinddir(dir);
731 
732                 for (;;)
733                 {
734                     dirent = readdir(dir);
735                     if (dirent == RT_NULL) break;
736 
737                     if (strncmp(index, dirent->d_name, rt_strlen(index)) == 0)
738                         rt_kprintf("%s\n", dirent->d_name);
739                 }
740             }
741 
742             length = index - path;
743             rt_memcpy(index, full_path, min_length);
744             path[length + min_length] = '\0';
745 
746             /* try to locate folder */
747             if (multi == 1)
748             {
749                 struct stat buffer = {0};
750                 if ((stat(path, &buffer) == 0))
751                 {
752                     if (S_ISDIR(buffer.st_mode))
753                     {
754                         strcat(path, "/");
755                     }
756                     else if (S_ISLNK(buffer.st_mode))
757                     {
758                         DIR *link_dir = opendir(path);
759                         if (link_dir)
760                         {
761                             closedir(link_dir);
762                             strcat(path, "/");
763                         }
764                     }
765                 }
766             }
767         }
768     }
769 
770     closedir(dir);
771     rt_free(full_path);
772 }
773 #endif /* DFS_USING_POSIX */
774 
msh_auto_complete(char * prefix)775 void msh_auto_complete(char *prefix)
776 {
777     int length, min_length;
778     const char *name_ptr, *cmd_name;
779     struct finsh_syscall *index;
780 
781     min_length = 0;
782     name_ptr = RT_NULL;
783 
784     if (*prefix == '\0')
785     {
786         msh_help(0, RT_NULL);
787         return;
788     }
789 
790 #ifdef DFS_USING_POSIX
791     /* check whether a spare in the command */
792     {
793         char *ptr;
794 
795         ptr = prefix + rt_strlen(prefix);
796         while (ptr != prefix)
797         {
798             if (*ptr == ' ')
799             {
800                 msh_auto_complete_path(ptr + 1);
801                 break;
802             }
803 
804             ptr --;
805         }
806 #if defined(RT_USING_MODULE) || defined(RT_USING_SMART)
807         /* There is a chance that the user want to run the module directly. So
808          * try to complete the file names. If the completed path is not a
809          * module, the system won't crash anyway. */
810         if (ptr == prefix)
811         {
812             msh_auto_complete_path(ptr);
813         }
814 #endif /* RT_USING_MODULE */
815     }
816 #endif /* DFS_USING_POSIX */
817 
818     /* checks in internal command */
819     {
820         for (index = _syscall_table_begin; index < _syscall_table_end; FINSH_NEXT_SYSCALL(index))
821         {
822             /* skip finsh shell function */
823             cmd_name = (const char *) index->name;
824             if (strncmp(prefix, cmd_name, strlen(prefix)) == 0)
825             {
826                 if (min_length == 0)
827                 {
828                     /* set name_ptr */
829                     name_ptr = cmd_name;
830                     /* set initial length */
831                     min_length = strlen(name_ptr);
832                 }
833 
834                 length = str_common(name_ptr, cmd_name);
835                 if (length < min_length)
836                     min_length = length;
837 
838                 rt_kprintf("%s\n", cmd_name);
839             }
840         }
841     }
842 
843     /* auto complete string */
844     if (name_ptr != NULL)
845     {
846         rt_strncpy(prefix, name_ptr, min_length);
847     }
848 
849     return ;
850 }
851 
852 #ifdef FINSH_USING_OPTION_COMPLETION
msh_get_cmd_opt(char * opt_str)853 static msh_cmd_opt_t *msh_get_cmd_opt(char *opt_str)
854 {
855     struct finsh_syscall *index;
856     msh_cmd_opt_t *opt = RT_NULL;
857     char *ptr;
858     int len;
859 
860     ptr = strchr(opt_str, ' ');
861     if (ptr)
862     {
863         len = ptr - opt_str;
864     }
865     else
866     {
867         len = strlen(opt_str);
868     }
869 
870     for (index = _syscall_table_begin;
871             index < _syscall_table_end;
872             FINSH_NEXT_SYSCALL(index))
873     {
874         if (strncmp(index->name, opt_str, len) == 0 && index->name[len] == '\0')
875         {
876             opt = index->opt;
877             break;
878         }
879     }
880 
881     return opt;
882 }
883 
msh_get_argc(char * prefix,char ** last_argv)884 static int msh_get_argc(char *prefix, char **last_argv)
885 {
886     int argc = 0;
887     char *ch = prefix;
888 
889     while (*ch)
890     {
891         if ((*ch == ' ') && *(ch + 1) && (*(ch + 1) != ' '))
892         {
893             *last_argv = ch + 1;
894             argc++;
895         }
896         ch++;
897     }
898 
899     return argc;
900 }
901 
msh_opt_complete(char * opts_str,struct msh_cmd_opt * cmd_opt)902 static void msh_opt_complete(char *opts_str, struct msh_cmd_opt *cmd_opt)
903 {
904     struct msh_cmd_opt *opt = cmd_opt;
905     const char *name_ptr = RT_NULL;
906     int min_length = 0, length, opts_str_len;
907 
908     opts_str_len = strlen(opts_str);
909 
910     for (opt = cmd_opt; opt->id; opt++)
911     {
912         if (!strncmp(opt->name, opts_str, opts_str_len))
913         {
914             if (min_length == 0)
915             {
916                 /* set name_ptr */
917                 name_ptr = opt->name;
918                 /* set initial length */
919                 min_length = strlen(name_ptr);
920             }
921 
922             length = str_common(name_ptr, opt->name);
923             if (length < min_length)
924             {
925                 min_length = length;
926             }
927 
928             rt_kprintf("%s\n", opt->name);
929         }
930     }
931     rt_kprintf("\n");
932 
933     if (name_ptr != NULL)
934     {
935         strncpy(opts_str, name_ptr, min_length);
936     }
937 }
938 
msh_opt_help(msh_cmd_opt_t * cmd_opt)939 static void msh_opt_help(msh_cmd_opt_t *cmd_opt)
940 {
941     msh_cmd_opt_t *opt = cmd_opt;
942 
943     for (; opt->id; opt++)
944     {
945         rt_kprintf("%-16s - %s\n", opt->name, opt->des);
946     }
947     rt_kprintf("\n");
948 }
949 
msh_opt_auto_complete(char * prefix)950 void msh_opt_auto_complete(char *prefix)
951 {
952     int argc;
953     char *opt_str = RT_NULL;
954     msh_cmd_opt_t *opt = RT_NULL;
955 
956     argc = msh_get_argc(prefix, &opt_str);
957     if (argc)
958     {
959         opt = msh_get_cmd_opt(prefix);
960     }
961     else if (!msh_get_cmd(prefix, strlen(prefix)) && (' ' == prefix[strlen(prefix) - 1]))
962     {
963         opt = msh_get_cmd_opt(prefix);
964     }
965 
966     if (opt && opt->id)
967     {
968         switch (argc)
969         {
970         case 0:
971             msh_opt_help(opt);
972             break;
973 
974         case 1:
975             msh_opt_complete(opt_str, opt);
976             break;
977 
978         default:
979             break;
980         }
981     }
982 }
983 
msh_cmd_opt_id_get(int argc,char * argv[],void * options)984 int msh_cmd_opt_id_get(int argc, char *argv[], void *options)
985 {
986     msh_cmd_opt_t *opt = (msh_cmd_opt_t *) options;
987     int opt_id;
988 
989     for (opt_id = 0; (argc >= 2) && opt && opt->id; opt++)
990     {
991         if (!strcmp(opt->name, argv[1]))
992         {
993             opt_id = opt->id;
994             break;
995         }
996     }
997 
998     return opt_id;
999 }
1000 
msh_opt_list_dump(void * options)1001 void msh_opt_list_dump(void *options)
1002 {
1003     msh_cmd_opt_t *opt = (msh_cmd_opt_t *) options;
1004 
1005     for (; opt && opt->id; opt++)
1006     {
1007         rt_kprintf("    %-16s - %s\n", opt->name, opt->des);
1008     }
1009 }
1010 #endif /* FINSH_USING_OPTION_COMPLETION */
1011 #endif /* RT_USING_FINSH */
1012