/* 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