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 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 46 rt_bool_t msh_is_used(void) 47 { 48 return __msh_state; 49 } 50 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 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 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 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 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 */ 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 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 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) 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 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 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 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 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