#pragma once class DirHelper { static constexpr const char* NAME = "FAT32_DirH"; public: enum class CreateType { NONE, DIR, FILE }; /** * get (or allocate, if needed) a new DirEntry with the given name absolute name * if the entry is allocated, it is UNTYPED * name: e.g. /path1/path2/path3 or /path1/path2/file.txt */ static DirHandle getOrCreate(FS& fs, ClusterNr dirStartCluster, const std::string& absName, CreateType ct) { // determine the local name Split s = split(absName); { // search for a matching existing entry DirIterator di(fs, dirStartCluster); while(true) { DirHandle h = di.nextUsable(); if (!h.isValid()) {break;} // found a matching entry const std::string n = h.getName(); if (n == s.first) { // are we done yet? if (s.hasMore()) { const ClusterNr subDirStartCluster = h.getFirstCluster(); return getOrCreate(fs, subDirStartCluster, s.next, ct); } else { return h; } } } } // no matching entry found if (ct == CreateType::NONE) {return DirHandle::invalid();} { // allocate a new DirEntry within the current directory DirIterator di(fs, dirStartCluster); DirHandle h = di.nextFree(); h.setName(s.first); h.setSize(0); h.setFirstCluster(0); // if this is a sub-directory, allocate a new cluster for it if (s.hasMore() || ct == CreateType::DIR) { // allocate a cluster where the directory contents are to be stored ClusterNr freeCluster = fs.allocFreeCluster(0); h.setFirstCluster(freeCluster); h.setDirectory(); fs.write(h); // ensure the new cluster is initialized with all zeros (this is also equal to an immediate END_OF_DIRECTORY marker in the beginning fs.zeroOutCluster(freeCluster); DirIterator di2(fs, freeCluster); // create the "." entry (pointer to current directory) DirHandle h2 = di2.next(); h2.setDot(freeCluster); fs.write(h2); // create the ".." entry (pointer to parent directory) DirHandle h3 = di2.next(); h3.setDotDot(dirStartCluster); fs.write(h3); // more path to process? if (s.hasMore()) { return getOrCreate(fs, freeCluster, s.next, ct); } else { return h; } } else { fs.write(h); return h; } } } private: struct Split { std::string first; std::string next; Split(const std::string& first, const std::string& next) : first(first), next(next) {} bool hasMore() const {return !next.empty();} }; /** split "/path1/path2/path3/file.txt" into "path1" and "path2/path3/file.txt" */ static Split split(std::string absPath) { if (absPath[0] == '/') { absPath = absPath.substr(1); } std::size_t pos = absPath.find('/'); if (pos == std::string::npos) { return Split(absPath, ""); } else { return Split(absPath.substr(0, pos), absPath.substr(pos)); } } };