msh_file.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706
  1. /*
  2. * Copyright (c) 2006-2021, RT-Thread Development Team
  3. *
  4. * SPDX-License-Identifier: Apache-2.0
  5. *
  6. * Change Logs:
  7. * Date Author Notes
  8. * 2015-09-25 Bernard the first verion for FinSH
  9. * 2021-06-09 Meco Man implement tail command
  10. */
  11. #include <rtthread.h>
  12. #if defined(RT_USING_FINSH) && defined(RT_USING_DFS)
  13. #include <finsh.h>
  14. #include "msh.h"
  15. #include <dfs_posix.h>
  16. static int msh_readline(int fd, char *line_buf, int size)
  17. {
  18. char ch;
  19. int index = 0;
  20. do
  21. {
  22. if (read(fd, &ch, 1) != 1)
  23. {
  24. /* nothing in this file */
  25. return 0;
  26. }
  27. }
  28. while (ch == '\n' || ch == '\r');
  29. /* set the first character */
  30. line_buf[index ++] = ch;
  31. while (index < size)
  32. {
  33. if (read(fd, &ch, 1) == 1)
  34. {
  35. if (ch == '\n' || ch == '\r')
  36. {
  37. line_buf[index] = '\0';
  38. break;
  39. }
  40. line_buf[index++] = ch;
  41. }
  42. else
  43. {
  44. line_buf[index] = '\0';
  45. break;
  46. }
  47. }
  48. return index;
  49. }
  50. int msh_exec_script(const char *cmd_line, int size)
  51. {
  52. int ret;
  53. int fd = -1;
  54. char *pg_name;
  55. int length, cmd_length = 0;
  56. if (size == 0) return -RT_ERROR;
  57. /* get the length of command0 */
  58. while ((cmd_line[cmd_length] != ' ' && cmd_line[cmd_length] != '\t') && cmd_length < size)
  59. cmd_length ++;
  60. /* get name length */
  61. length = cmd_length + 32;
  62. /* allocate program name memory */
  63. pg_name = (char *) rt_malloc(length);
  64. if (pg_name == RT_NULL) return -RT_ENOMEM;
  65. /* copy command0 */
  66. memcpy(pg_name, cmd_line, cmd_length);
  67. pg_name[cmd_length] = '\0';
  68. if (strstr(pg_name, ".sh") != RT_NULL || strstr(pg_name, ".SH") != RT_NULL)
  69. {
  70. /* try to open program */
  71. fd = open(pg_name, O_RDONLY, 0);
  72. /* search in /bin path */
  73. if (fd < 0)
  74. {
  75. rt_snprintf(pg_name, length - 1, "/bin/%.*s", cmd_length, cmd_line);
  76. fd = open(pg_name, O_RDONLY, 0);
  77. }
  78. }
  79. rt_free(pg_name);
  80. if (fd >= 0)
  81. {
  82. /* found script */
  83. char *line_buf;
  84. int length;
  85. line_buf = (char *) rt_malloc(RT_CONSOLEBUF_SIZE);
  86. if (line_buf == RT_NULL)
  87. {
  88. close(fd);
  89. return -RT_ENOMEM;
  90. }
  91. /* read line by line and then exec it */
  92. do
  93. {
  94. length = msh_readline(fd, line_buf, RT_CONSOLEBUF_SIZE);
  95. if (length > 0)
  96. {
  97. char ch = '\0';
  98. int index;
  99. for (index = 0; index < length; index ++)
  100. {
  101. ch = line_buf[index];
  102. if (ch == ' ' || ch == '\t') continue;
  103. else break;
  104. }
  105. if (ch != '#') /* not a comment */
  106. msh_exec(line_buf, length);
  107. }
  108. }
  109. while (length > 0);
  110. close(fd);
  111. rt_free(line_buf);
  112. ret = 0;
  113. }
  114. else
  115. {
  116. ret = -1;
  117. }
  118. return ret;
  119. }
  120. #ifdef DFS_USING_WORKDIR
  121. extern char working_directory[];
  122. #endif
  123. static int cmd_ls(int argc, char **argv)
  124. {
  125. extern void ls(const char *pathname);
  126. if (argc == 1)
  127. {
  128. #ifdef DFS_USING_WORKDIR
  129. ls(working_directory);
  130. #else
  131. ls("/");
  132. #endif
  133. }
  134. else
  135. {
  136. ls(argv[1]);
  137. }
  138. return 0;
  139. }
  140. MSH_CMD_EXPORT_ALIAS(cmd_ls, ls, List information about the FILEs.);
  141. static int cmd_cp(int argc, char **argv)
  142. {
  143. void copy(const char *src, const char *dst);
  144. if (argc != 3)
  145. {
  146. rt_kprintf("Usage: cp SOURCE DEST\n");
  147. rt_kprintf("Copy SOURCE to DEST.\n");
  148. }
  149. else
  150. {
  151. copy(argv[1], argv[2]);
  152. }
  153. return 0;
  154. }
  155. MSH_CMD_EXPORT_ALIAS(cmd_cp, cp, Copy SOURCE to DEST.);
  156. static int cmd_mv(int argc, char **argv)
  157. {
  158. if (argc != 3)
  159. {
  160. rt_kprintf("Usage: mv SOURCE DEST\n");
  161. rt_kprintf("Rename SOURCE to DEST, or move SOURCE(s) to DIRECTORY.\n");
  162. }
  163. else
  164. {
  165. int fd;
  166. char *dest = RT_NULL;
  167. rt_kprintf("%s => %s\n", argv[1], argv[2]);
  168. fd = open(argv[2], O_DIRECTORY, 0);
  169. if (fd >= 0)
  170. {
  171. char *src;
  172. close(fd);
  173. /* it's a directory */
  174. dest = (char *)rt_malloc(DFS_PATH_MAX);
  175. if (dest == RT_NULL)
  176. {
  177. rt_kprintf("out of memory\n");
  178. return -RT_ENOMEM;
  179. }
  180. src = argv[1] + rt_strlen(argv[1]);
  181. while (src != argv[1])
  182. {
  183. if (*src == '/') break;
  184. src --;
  185. }
  186. rt_snprintf(dest, DFS_PATH_MAX - 1, "%s/%s", argv[2], src);
  187. }
  188. else
  189. {
  190. fd = open(argv[2], O_RDONLY, 0);
  191. if (fd >= 0)
  192. {
  193. close(fd);
  194. unlink(argv[2]);
  195. }
  196. dest = argv[2];
  197. }
  198. rename(argv[1], dest);
  199. if (dest != RT_NULL && dest != argv[2]) rt_free(dest);
  200. }
  201. return 0;
  202. }
  203. MSH_CMD_EXPORT_ALIAS(cmd_mv, mv, Rename SOURCE to DEST.);
  204. static int cmd_cat(int argc, char **argv)
  205. {
  206. int index;
  207. extern void cat(const char *filename);
  208. if (argc == 1)
  209. {
  210. rt_kprintf("Usage: cat [FILE]...\n");
  211. rt_kprintf("Concatenate FILE(s)\n");
  212. return 0;
  213. }
  214. for (index = 1; index < argc; index ++)
  215. {
  216. cat(argv[index]);
  217. }
  218. return 0;
  219. }
  220. MSH_CMD_EXPORT_ALIAS(cmd_cat, cat, Concatenate FILE(s));
  221. static void directory_delete_for_msh(const char *pathname, char f, char v)
  222. {
  223. DIR *dir = NULL;
  224. struct dirent *dirent = NULL;
  225. char *full_path;
  226. if (pathname == RT_NULL)
  227. return;
  228. full_path = (char *)rt_malloc(DFS_PATH_MAX);
  229. if (full_path == RT_NULL)
  230. return;
  231. dir = opendir(pathname);
  232. if (dir == RT_NULL)
  233. {
  234. if (f == 0)
  235. {
  236. rt_kprintf("cannot remove '%s'\n", pathname);
  237. }
  238. rt_free(full_path);
  239. return;
  240. }
  241. while (1)
  242. {
  243. dirent = readdir(dir);
  244. if (dirent == RT_NULL)
  245. break;
  246. if (rt_strcmp(".", dirent->d_name) != 0 &&
  247. rt_strcmp("..", dirent->d_name) != 0)
  248. {
  249. rt_sprintf(full_path, "%s/%s", pathname, dirent->d_name);
  250. if (dirent->d_type == DT_REG)
  251. {
  252. if (unlink(full_path) != 0)
  253. {
  254. if (f == 0)
  255. rt_kprintf("cannot remove '%s'\n", full_path);
  256. }
  257. else if (v)
  258. {
  259. rt_kprintf("removed '%s'\n", full_path);
  260. }
  261. }
  262. else if (dirent->d_type == DT_DIR)
  263. {
  264. directory_delete_for_msh(full_path, f, v);
  265. }
  266. }
  267. }
  268. closedir(dir);
  269. rt_free(full_path);
  270. if (unlink(pathname) != 0)
  271. {
  272. if (f == 0)
  273. rt_kprintf("cannot remove '%s'\n", pathname);
  274. }
  275. else if (v)
  276. {
  277. rt_kprintf("removed directory '%s'\n", pathname);
  278. }
  279. }
  280. static int cmd_rm(int argc, char **argv)
  281. {
  282. int index, n;
  283. char f = 0, r = 0, v = 0;
  284. if (argc == 1)
  285. {
  286. rt_kprintf("Usage: rm option(s) FILE...\n");
  287. rt_kprintf("Remove (unlink) the FILE(s).\n");
  288. return 0;
  289. }
  290. if (argv[1][0] == '-')
  291. {
  292. for (n = 0; argv[1][n]; n++)
  293. {
  294. switch (argv[1][n])
  295. {
  296. case 'f':
  297. f = 1;
  298. break;
  299. case 'r':
  300. r = 1;
  301. break;
  302. case 'v':
  303. v = 1;
  304. break;
  305. case '-':
  306. break;
  307. default:
  308. rt_kprintf("Error: Bad option: %c\n", argv[1][n]);
  309. return 0;
  310. }
  311. }
  312. argc -= 1;
  313. argv = argv + 1;
  314. }
  315. for (index = 1; index < argc; index ++)
  316. {
  317. struct stat s;
  318. if (stat(argv[index], &s) == 0)
  319. {
  320. if (s.st_mode & S_IFDIR)
  321. {
  322. if (r == 0)
  323. rt_kprintf("cannot remove '%s': Is a directory\n", argv[index]);
  324. else
  325. directory_delete_for_msh(argv[index], f, v);
  326. }
  327. else if (s.st_mode & S_IFREG)
  328. {
  329. if (unlink(argv[index]) != 0)
  330. {
  331. if (f == 0)
  332. rt_kprintf("cannot remove '%s'\n", argv[index]);
  333. }
  334. else if (v)
  335. {
  336. rt_kprintf("removed '%s'\n", argv[index]);
  337. }
  338. }
  339. }
  340. else if (f == 0)
  341. {
  342. rt_kprintf("cannot remove '%s': No such file or directory\n", argv[index]);
  343. }
  344. }
  345. return 0;
  346. }
  347. MSH_CMD_EXPORT_ALIAS(cmd_rm, rm, Remove(unlink) the FILE(s).);
  348. #ifdef DFS_USING_WORKDIR
  349. static int cmd_cd(int argc, char **argv)
  350. {
  351. if (argc == 1)
  352. {
  353. rt_kprintf("%s\n", working_directory);
  354. }
  355. else if (argc == 2)
  356. {
  357. if (chdir(argv[1]) != 0)
  358. {
  359. rt_kprintf("No such directory: %s\n", argv[1]);
  360. }
  361. }
  362. return 0;
  363. }
  364. MSH_CMD_EXPORT_ALIAS(cmd_cd, cd, Change the shell working directory.);
  365. static int cmd_pwd(int argc, char **argv)
  366. {
  367. rt_kprintf("%s\n", working_directory);
  368. return 0;
  369. }
  370. MSH_CMD_EXPORT_ALIAS(cmd_pwd, pwd, Print the name of the current working directory.);
  371. #endif
  372. static int cmd_mkdir(int argc, char **argv)
  373. {
  374. if (argc == 1)
  375. {
  376. rt_kprintf("Usage: mkdir [OPTION] DIRECTORY\n");
  377. rt_kprintf("Create the DIRECTORY, if they do not already exist.\n");
  378. }
  379. else
  380. {
  381. mkdir(argv[1], 0);
  382. }
  383. return 0;
  384. }
  385. MSH_CMD_EXPORT_ALIAS(cmd_mkdir, mkdir, Create the DIRECTORY.);
  386. static int cmd_mkfs(int argc, char **argv)
  387. {
  388. int result = 0;
  389. char *type = "elm"; /* use the default file system type as 'fatfs' */
  390. if (argc == 2)
  391. {
  392. result = dfs_mkfs(type, argv[1]);
  393. }
  394. else if (argc == 4)
  395. {
  396. if (strcmp(argv[1], "-t") == 0)
  397. {
  398. type = argv[2];
  399. result = dfs_mkfs(type, argv[3]);
  400. }
  401. }
  402. else
  403. {
  404. rt_kprintf("Usage: mkfs [-t type] device\n");
  405. return 0;
  406. }
  407. if (result != RT_EOK)
  408. {
  409. rt_kprintf("mkfs failed, result=%d\n", result);
  410. }
  411. return 0;
  412. }
  413. MSH_CMD_EXPORT_ALIAS(cmd_mkfs, mkfs, format disk with file system);
  414. extern struct dfs_filesystem filesystem_table[];
  415. static int cmd_mount(int argc, char **argv)
  416. {
  417. if (argc == 1)
  418. {
  419. struct dfs_filesystem *iter;
  420. /* display the mount history */
  421. rt_kprintf("filesystem device mountpoint\n");
  422. rt_kprintf("---------- ------ ----------\n");
  423. for (iter = &filesystem_table[0];
  424. iter < &filesystem_table[DFS_FILESYSTEMS_MAX]; iter++)
  425. {
  426. if ((iter != NULL) && (iter->path != NULL))
  427. {
  428. rt_kprintf("%-10s %-6s %-s\n",
  429. iter->ops->name, iter->dev_id->parent.name, iter->path);
  430. }
  431. }
  432. return 0;
  433. }
  434. else if (argc == 4)
  435. {
  436. char *device = argv[1];
  437. char *path = argv[2];
  438. char *fstype = argv[3];
  439. /* mount a filesystem to the specified directory */
  440. rt_kprintf("mount device %s(%s) onto %s ... ", device, fstype, path);
  441. if (dfs_mount(device, path, fstype, 0, 0) == 0)
  442. {
  443. rt_kprintf("succeed!\n");
  444. return 0;
  445. }
  446. else
  447. {
  448. rt_kprintf("failed!\n");
  449. return -1;
  450. }
  451. }
  452. else
  453. {
  454. rt_kprintf("Usage: mount <device> <mountpoint> <fstype>.\n");
  455. return -1;
  456. }
  457. }
  458. MSH_CMD_EXPORT_ALIAS(cmd_mount, mount, mount <device> <mountpoint> <fstype>);
  459. /* unmount the filesystem from the specified mountpoint */
  460. static int cmd_umount(int argc, char **argv)
  461. {
  462. char *path = argv[1];
  463. if (argc != 2)
  464. {
  465. rt_kprintf("Usage: unmount <mountpoint>.\n");
  466. return -1;
  467. }
  468. rt_kprintf("unmount %s ... ", path);
  469. if (dfs_unmount(path) < 0)
  470. {
  471. rt_kprintf("failed!\n");
  472. return -1;
  473. }
  474. else
  475. {
  476. rt_kprintf("succeed!\n");
  477. return 0;
  478. }
  479. }
  480. MSH_CMD_EXPORT_ALIAS(cmd_umount, umount, Unmount device from file system);
  481. extern int df(const char *path);
  482. static int cmd_df(int argc, char **argv)
  483. {
  484. if (argc != 2)
  485. {
  486. df("/");
  487. }
  488. else
  489. {
  490. if ((strcmp(argv[1], "--help") == 0) || (strcmp(argv[1], "-h") == 0))
  491. {
  492. rt_kprintf("df [path]\n");
  493. }
  494. else
  495. {
  496. df(argv[1]);
  497. }
  498. }
  499. return 0;
  500. }
  501. MSH_CMD_EXPORT_ALIAS(cmd_df, df, disk free);
  502. static int cmd_echo(int argc, char **argv)
  503. {
  504. if (argc == 2)
  505. {
  506. rt_kprintf("%s\n", argv[1]);
  507. }
  508. else if (argc == 3)
  509. {
  510. int fd;
  511. fd = open(argv[2], O_RDWR | O_APPEND | O_CREAT, 0);
  512. if (fd >= 0)
  513. {
  514. write(fd, argv[1], strlen(argv[1]));
  515. close(fd);
  516. }
  517. else
  518. {
  519. rt_kprintf("open file:%s failed!\n", argv[2]);
  520. }
  521. }
  522. else
  523. {
  524. rt_kprintf("Usage: echo \"string\" [filename]\n");
  525. }
  526. return 0;
  527. }
  528. MSH_CMD_EXPORT_ALIAS(cmd_echo, echo, echo string to file);
  529. static int cmd_tail(int argc, char **argv)
  530. {
  531. int fd;
  532. char c = RT_NULL;
  533. char *file_name = RT_NULL;
  534. rt_uint32_t total_lines = 0;
  535. rt_uint32_t target_line = 0;
  536. rt_uint32_t current_line = 0;
  537. rt_uint32_t required_lines = 0;
  538. rt_uint32_t after_xxx_line = 0;
  539. if (argc < 2)
  540. {
  541. rt_kprintf("Usage: tail [-n numbers] <filename>\n");
  542. return -1;
  543. }
  544. else if (argc == 2)
  545. {
  546. required_lines = 10; /* default: 10 lines from tail */
  547. file_name = argv[1];
  548. }
  549. else if (rt_strcmp(argv[1], "-n") == 0)
  550. {
  551. if (argv[2][0] != '+')
  552. {
  553. required_lines = atoi(argv[2]);
  554. }
  555. else
  556. {
  557. after_xxx_line = atoi(&argv[2][1]); /* eg: +100, to get the 100 */
  558. }
  559. file_name = argv[3];
  560. }
  561. else
  562. {
  563. rt_kprintf("Usage: tail [-n numbers] <filename>\n");
  564. return -1;
  565. }
  566. fd = open(file_name, O_RDONLY);
  567. if (fd < 0)
  568. {
  569. rt_kprintf("File doesn't exist\n");
  570. return -1;
  571. }
  572. while ((read(fd, &c, sizeof(char))) > 0)
  573. {
  574. if (c == '\n')
  575. {
  576. total_lines++;
  577. }
  578. }
  579. rt_kprintf("\nTotal Number of lines:%d\n", total_lines);
  580. if (after_xxx_line != 0)
  581. {
  582. if (total_lines > after_xxx_line)
  583. {
  584. required_lines = total_lines - after_xxx_line;
  585. }
  586. else
  587. {
  588. rt_kprintf("\nError:Required lines are more than total number of lines\n");
  589. close(fd);
  590. return -1;
  591. }
  592. }
  593. if (required_lines > total_lines)
  594. {
  595. rt_kprintf("\nError:Required lines are more than total number of lines\n");
  596. close(fd);
  597. return -1;
  598. }
  599. rt_kprintf("Required Number of lines:%d\n", required_lines);
  600. target_line = total_lines - required_lines;
  601. lseek(fd, 0, SEEK_SET); /* back to head */
  602. while ((read(fd, &c, sizeof(char))) > 0)
  603. {
  604. if (c == '\n')
  605. {
  606. current_line++;
  607. }
  608. if (current_line > target_line)
  609. {
  610. rt_kprintf("%c", c);
  611. }
  612. }
  613. rt_kprintf("\n");
  614. close(fd);
  615. return 0;
  616. }
  617. MSH_CMD_EXPORT_ALIAS(cmd_tail, tail, print the last N - lines data of the given file);
  618. #endif /* defined(RT_USING_FINSH) && defined(RT_USING_DFS) */