dir_walk.c 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154
  1. #include "dir_walk.h"
  2. #include <m-list.h>
  3. LIST_DEF(DirIndexList, uint32_t);
  4. struct DirWalk {
  5. File* file;
  6. FuriString* path;
  7. DirIndexList_t index_list;
  8. uint32_t current_index;
  9. bool recursive;
  10. DirWalkFilterCb filter_cb;
  11. void* filter_context;
  12. };
  13. DirWalk* dir_walk_alloc(Storage* storage) {
  14. DirWalk* dir_walk = malloc(sizeof(DirWalk));
  15. dir_walk->path = furi_string_alloc();
  16. dir_walk->file = storage_file_alloc(storage);
  17. DirIndexList_init(dir_walk->index_list);
  18. dir_walk->recursive = true;
  19. dir_walk->filter_cb = NULL;
  20. return dir_walk;
  21. }
  22. void dir_walk_free(DirWalk* dir_walk) {
  23. storage_file_free(dir_walk->file);
  24. furi_string_free(dir_walk->path);
  25. DirIndexList_clear(dir_walk->index_list);
  26. free(dir_walk);
  27. }
  28. void dir_walk_set_recursive(DirWalk* dir_walk, bool recursive) {
  29. dir_walk->recursive = recursive;
  30. }
  31. void dir_walk_set_filter_cb(DirWalk* dir_walk, DirWalkFilterCb cb, void* context) {
  32. dir_walk->filter_cb = cb;
  33. dir_walk->filter_context = context;
  34. }
  35. bool dir_walk_open(DirWalk* dir_walk, const char* path) {
  36. furi_string_set(dir_walk->path, path);
  37. dir_walk->current_index = 0;
  38. return storage_dir_open(dir_walk->file, path);
  39. }
  40. static bool dir_walk_filter(DirWalk* dir_walk, const char* name, FileInfo* fileinfo) {
  41. if(dir_walk->filter_cb) {
  42. return dir_walk->filter_cb(name, fileinfo, dir_walk->filter_context);
  43. } else {
  44. return true;
  45. }
  46. }
  47. static DirWalkResult
  48. dir_walk_iter(DirWalk* dir_walk, FuriString* return_path, FileInfo* fileinfo) {
  49. DirWalkResult result = DirWalkError;
  50. char* name = malloc(256); // FIXME: remove magic number
  51. FileInfo info;
  52. bool end = false;
  53. while(!end) {
  54. storage_dir_read(dir_walk->file, &info, name, 255);
  55. if(storage_file_get_error(dir_walk->file) == FSE_OK) {
  56. result = DirWalkOK;
  57. dir_walk->current_index++;
  58. if(dir_walk_filter(dir_walk, name, &info)) {
  59. if(return_path != NULL) {
  60. furi_string_printf(
  61. return_path, "%s/%s", furi_string_get_cstr(dir_walk->path), name);
  62. }
  63. if(fileinfo != NULL) {
  64. memcpy(fileinfo, &info, sizeof(FileInfo));
  65. }
  66. end = true;
  67. }
  68. if((info.flags & FSF_DIRECTORY) && dir_walk->recursive) {
  69. // step into
  70. DirIndexList_push_back(dir_walk->index_list, dir_walk->current_index);
  71. dir_walk->current_index = 0;
  72. storage_dir_close(dir_walk->file);
  73. furi_string_cat_printf(dir_walk->path, "/%s", name);
  74. storage_dir_open(dir_walk->file, furi_string_get_cstr(dir_walk->path));
  75. }
  76. } else if(storage_file_get_error(dir_walk->file) == FSE_NOT_EXIST) {
  77. if(DirIndexList_size(dir_walk->index_list) == 0) {
  78. // last
  79. result = DirWalkLast;
  80. end = true;
  81. } else {
  82. // step out
  83. uint32_t index;
  84. DirIndexList_pop_back(&index, dir_walk->index_list);
  85. dir_walk->current_index = 0;
  86. storage_dir_close(dir_walk->file);
  87. size_t last_char = furi_string_search_rchar(dir_walk->path, '/');
  88. if(last_char != FURI_STRING_FAILURE) {
  89. furi_string_left(dir_walk->path, last_char);
  90. }
  91. storage_dir_open(dir_walk->file, furi_string_get_cstr(dir_walk->path));
  92. // rewind
  93. while(true) {
  94. if(index == dir_walk->current_index) {
  95. result = DirWalkOK;
  96. break;
  97. }
  98. if(!storage_dir_read(dir_walk->file, &info, name, 255)) {
  99. result = DirWalkError;
  100. end = true;
  101. break;
  102. }
  103. dir_walk->current_index++;
  104. }
  105. }
  106. } else {
  107. result = DirWalkError;
  108. end = true;
  109. }
  110. }
  111. free(name);
  112. return result;
  113. }
  114. FS_Error dir_walk_get_error(DirWalk* dir_walk) {
  115. return storage_file_get_error(dir_walk->file);
  116. }
  117. DirWalkResult dir_walk_read(DirWalk* dir_walk, FuriString* return_path, FileInfo* fileinfo) {
  118. return dir_walk_iter(dir_walk, return_path, fileinfo);
  119. }
  120. void dir_walk_close(DirWalk* dir_walk) {
  121. if(storage_file_is_open(dir_walk->file)) {
  122. storage_dir_close(dir_walk->file);
  123. }
  124. DirIndexList_reset(dir_walk->index_list);
  125. furi_string_reset(dir_walk->path);
  126. dir_walk->current_index = 0;
  127. }