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