#if !defined(INCLUDED_FS_FILESYSTEM_H) #define INCLUDED_FS_FILESYSTEM_H #include "string/string.h" #include "os/path.h" #include inline unsigned int path_get_depth(const char* path) { unsigned int depth = 0; while(path != 0 && path[0] != '\0') { path = strchr(path, '/'); if(path != 0) { ++path; } ++depth; } return depth; } /// \brief A generic unix-style file-system which maps paths to files and directories. /// Provides average O(log n) find and insert methods. /// \param file_type The data type which represents a file. template class GenericFileSystem { class Path { CopiedString m_path; unsigned int m_depth; public: Path(const char* path) : m_path(path), m_depth(path_get_depth(c_str())) { } Path(const char* start, const char* finish) : m_path(start, finish), m_depth(path_get_depth(c_str())) { } bool operator<(const Path& other) const { return string_less_nocase(c_str(), other.c_str()); } unsigned int depth() const { return m_depth; } const char* c_str() const { return m_path.c_str(); } }; class Entry { file_type* m_file; public: Entry(file_type* file) : m_file(file) { } file_type* file() const { return m_file; } bool is_directory() const { return file() == 0; } }; typedef std::map Entries; Entries m_entries; public: typedef typename Entries::iterator iterator; typedef typename Entries::value_type value_type; iterator begin() { return m_entries.begin(); } iterator end() { return m_entries.end(); } /// \brief Adds the file \p entry at \p path. /// Creates all directories below \p path if they do not exist. /// O(log n) on average. void insert(const Path& path, const Entry& entry) { { const char* end = path_remove_directory(path.c_str()); while(end[0] != '\0') { Path dir(path.c_str(), end); m_entries.insert(value_type(dir, Entry(0))); end = path_remove_directory(end); } } m_entries.insert(value_type(path, entry)); } /// \brief Returns the file at \p path or end() if not found. iterator find(const Path& path) { return m_entries.find(path); } iterator begin(const char* root) { if(root[0] == '\0') { return m_entries.begin(); } iterator i = m_entries.find(root); if(i == m_entries.end()) { return i; } return ++i; } /// \brief Performs a depth-first traversal of the file-system subtree rooted at \p root. /// Traverses the entire tree if \p root is "". /// Calls \p visitor.file() with the path to each file relative to the filesystem root. /// Calls \p visitor.directory() with the path to each directory relative to the filesystem root. template void traverse(visitor_type visitor, const char* root) { unsigned int start_depth = path_get_depth(root); unsigned int skip_depth = 0; for(iterator i = begin(root); i != end() && i->first.depth() > start_depth; ++i) { if(i->first.depth() == skip_depth) { skip_depth = 0; } if(skip_depth == 0) { if(!i->second.is_directory()) { visitor.file(i->first.c_str()); } else if(visitor.directory(i->first.c_str(), i->first.depth() - start_depth)) { skip_depth = i->first.depth(); } } } } }; #endif