/* Copyright (C) 2001-2006, William Joseph. All Rights Reserved. This file is part of GtkRadiant. GtkRadiant is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. GtkRadiant is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GtkRadiant; if not, write to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #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(StringRange range) : m_path(range), 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() : m_file(0) { } 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; typedef Entry entry_type; iterator begin() { return m_entries.begin(); } iterator end() { return m_entries.end(); } /// \brief Returns the file at \p path. /// Creates all directories below \p path if they do not exist. /// O(log n) on average. entry_type& operator[](const Path& path) { { const char* end = path_remove_directory(path.c_str()); while(end[0] != '\0') { Path dir(StringRange(path.c_str(), end)); m_entries.insert(value_type(dir, Entry(0))); end = path_remove_directory(end); } } return m_entries[path]; } /// \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