Arduino FatFS
Loading...
Searching...
No Matches
filesystem.h
1// Minimal std::filesystem-like shim on top of Arduino-FatFs
2// This header provides a directory iterator to ease porting code that expects
3// filesystem helpers. It is not a drop-in replacement for std::filesystem.
4// Uses FatFs API directly (f_opendir, f_readdir, etc.) instead of SD wrapper.
5
6#pragma once
7
8#include <string>
9#include "fatfs.h"
10
11namespace fatfs {
12
13
19 std::string path;
20 bool is_directory;
21 uint64_t size; // File size in bytes (0 for directories)
22};
23
24// Get the global FatFs instance from SD object
25inline fatfs::FatFs* get_fatfs() { return SD.getFatFs(); }
26
43 public:
44 directory_iterator() : end_flag(true) {}
45 explicit directory_iterator(const std::string& rootPath)
46 : root(rootPath), end_flag(false) {
47 auto fs = get_fatfs();
48 if (!fs) {
49 end_flag = true;
50 return;
51 }
52 this->fs = fs;
53
54 // Open directory using FatFs API
55 FRESULT res = fs->f_opendir(&dir, root.empty() ? "/" : root.c_str());
56 if (res != FR_OK) {
57 end_flag = true;
58 } else {
59 advance();
60 }
61 }
62
64 if (!end_flag && fs) {
65 fs->f_closedir(&dir);
66 }
67 }
68
69 directory_iterator& operator++() {
70 advance();
71 return *this;
72 }
73
74 directory_entry operator*() const {
76 e.path = root;
77 if (!root.empty() && root.back() != '/') e.path += "/";
78 e.path += info.fname;
79 e.is_directory = (info.fattrib & AM_DIR) != 0;
80 e.size = info.fsize;
81 return e;
82 }
83
84 bool operator!=(const directory_iterator& other) const {
85 return end_flag != other.end_flag;
86 }
87
88 static directory_iterator end() { return directory_iterator(); }
89
90 private:
91 void advance() {
92 if (!fs) {
93 end_flag = true;
94 return;
95 }
96
97 // Read next directory entry
98 FRESULT res = fs->f_readdir(&dir, &info);
99 if (res != FR_OK || info.fname[0] == 0) {
100 // Error or no more entries
101 fs->f_closedir(&dir);
102 end_flag = true;
103 }
104 }
105
106 fatfs::FatFs* fs = nullptr;
107 DIR dir;
108 FILINFO info;
109 std::string root;
110 bool end_flag{true};
111};
112
136 public:
137 recursive_directory_iterator() : end_flag(true) {}
138 explicit recursive_directory_iterator(const std::string& rootPath)
139 : end_flag(false) {
140 auto fs = get_fatfs();
141 if (!fs) {
142 end_flag = true;
143 return;
144 }
145 this->fs = fs;
146
147 // Start with root directory
148 if (!push_directory(rootPath.empty() ? "/" : rootPath)) {
149 end_flag = true;
150 } else {
151 advance();
152 }
153 }
154
156 // Close all open directories
157 while (!stack.empty()) {
158 if (fs) fs->f_closedir(&stack.back().dir);
159 stack.pop_back();
160 }
161 }
162
163 recursive_directory_iterator& operator++() {
164 advance();
165 return *this;
166 }
167
168 directory_entry operator*() const { return current_entry; }
169
170 bool operator!=(const recursive_directory_iterator& other) const {
171 return end_flag != other.end_flag;
172 }
173
174 static recursive_directory_iterator end() {
176 }
177
178 private:
179 struct DirLevel {
180 DIR dir;
181 std::string path;
182 };
183
184 bool push_directory(const std::string& path) {
185 if (!fs) return false;
186
187 DirLevel level;
188 level.path = path;
189 FRESULT res = fs->f_opendir(&level.dir, path.c_str());
190 if (res == FR_OK) {
191 stack.push_back(level);
192 return true;
193 }
194 return false;
195 }
196
197 void advance() {
198 if (!fs || stack.empty()) {
199 end_flag = true;
200 return;
201 }
202
203 FILINFO info;
204
205 while (!stack.empty()) {
206 auto& level = stack.back();
207
208 // Read next entry in current directory
209 FRESULT res = fs->f_readdir(&level.dir, &info);
210
211 if (res != FR_OK || info.fname[0] == 0) {
212 // No more entries in this directory, pop and continue with parent
213 fs->f_closedir(&level.dir);
214 stack.pop_back();
215 continue;
216 }
217
218 // Skip . and .. entries
219 if (strcmp(info.fname, ".") == 0 || strcmp(info.fname, "..") == 0) {
220 continue;
221 }
222
223 // Build full path for current entry
224 current_entry.path = level.path;
225 if (!current_entry.path.empty() && current_entry.path.back() != '/') {
226 current_entry.path += "/";
227 }
228 current_entry.path += info.fname;
229 current_entry.is_directory = (info.fattrib & AM_DIR) != 0;
230 current_entry.size = info.fsize;
231
232 // If this is a directory, push it onto the stack for depth-first
233 // traversal
234 if (current_entry.is_directory) {
235 push_directory(current_entry.path);
236 }
237
238 // Return current entry (directory or file)
239 return;
240 }
241
242 // Stack is empty, we're done
243 end_flag = true;
244 }
245
246 fatfs::FatFs* fs = nullptr;
247 std::vector<DirLevel> stack;
248 directory_entry current_entry;
249 bool end_flag{true};
250};
251
252} // namespace fatfs_fs
API for FatFS See http://elm-chan.org/fsw/ff/00index_e.html.
Definition ff.h:34
FRESULT f_readdir(DIR *dp, FILINFO *fno)
Definition ff-inc.h:4543
FRESULT f_opendir(DIR *dp, const TCHAR *path)
Definition ff-inc.h:4449
FRESULT f_closedir(DIR *dp)
Definition ff-inc.h:4515
Iterator for non-recursive directory traversal.
Definition filesystem.h:42
Iterator for recursive directory tree traversal.
Definition filesystem.h:135
Definition ffdef.h:200
Definition ffdef.h:218
Representation of a directory entry.
Definition filesystem.h:18