--- /dev/null
+/*
+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 <map>
+
+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<typename file_type>
+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<Path, Entry> 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<typename visitor_type>
+ 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