2 Copyright (C) 2001-2006, William Joseph.
5 This file is part of GtkRadiant.
7 GtkRadiant is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 GtkRadiant is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with GtkRadiant; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
22 #if !defined( INCLUDED_FS_FILESYSTEM_H )
23 #define INCLUDED_FS_FILESYSTEM_H
25 #include "string/string.h"
30 inline unsigned int path_get_depth( const char* path ){
31 unsigned int depth = 0;
32 while ( path != 0 && path[0] != '\0' )
34 path = strchr( path, '/' );
43 /// \brief A generic unix-style file-system which maps paths to files and directories.
44 /// Provides average O(log n) find and insert methods.
45 /// \param file_type The data type which represents a file.
46 template<typename file_type>
47 class GenericFileSystem
54 Path( const char* path )
55 : m_path( path ), m_depth( path_get_depth( c_str() ) ){
57 Path( StringRange range )
58 : m_path( range ), m_depth( path_get_depth( c_str() ) ){
60 bool operator<( const Path& other ) const {
61 return string_less_nocase( c_str(), other.c_str() );
63 unsigned int depth() const {
66 const char* c_str() const {
67 return m_path.c_str();
75 Entry() : m_file( 0 ){
77 Entry( file_type* file ) : m_file( file ){
79 file_type* file() const {
82 bool is_directory() const {
87 typedef std::map<Path, Entry> Entries;
91 typedef typename Entries::iterator iterator;
92 typedef typename Entries::value_type value_type;
93 typedef Entry entry_type;
96 return m_entries.begin();
99 return m_entries.end();
102 /// \brief Returns the file at \p path.
103 /// Creates all directories below \p path if they do not exist.
104 /// O(log n) on average.
105 entry_type& operator[]( const Path& path ){
107 const char* end = path_remove_directory( path.c_str() );
108 while ( end[0] != '\0' )
110 Path dir( StringRange( path.c_str(), end ) );
111 m_entries.insert( value_type( dir, Entry( 0 ) ) );
112 end = path_remove_directory( end );
116 return m_entries[path];
119 /// \brief Returns the file at \p path or end() if not found.
120 iterator find( const Path& path ){
121 return m_entries.find( path );
124 iterator begin( const char* root ){
125 if ( root[0] == '\0' ) {
126 return m_entries.begin();
128 iterator i = m_entries.find( root );
129 if ( i == m_entries.end() ) {
135 /// \brief Performs a depth-first traversal of the file-system subtree rooted at \p root.
136 /// Traverses the entire tree if \p root is "".
137 /// Calls \p visitor.file() with the path to each file relative to the filesystem root.
138 /// Calls \p visitor.directory() with the path to each directory relative to the filesystem root.
139 template<typename visitor_type>
140 void traverse( visitor_type visitor, const char* root ){
141 unsigned int start_depth = path_get_depth( root );
142 unsigned int skip_depth = 0;
143 for ( iterator i = begin( root ); i != end() && i->first.depth() > start_depth; ++i )
145 if ( i->first.depth() == skip_depth ) {
148 if ( skip_depth == 0 ) {
149 if ( !i->second.is_directory() ) {
150 visitor.file( i->first.c_str() );
152 else if ( visitor.directory( i->first.c_str(), i->first.depth() - start_depth ) ) {
153 skip_depth = i->first.depth();