131 lines
2.8 KiB
C++
131 lines
2.8 KiB
C++
#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));
|
|
}
|
|
|
|
}
|
|
|
|
};
|