]> git.xonotic.org Git - xonotic/netradiant.git/blob - libs/fs_filesystem.h
Merge branch 'master' into divVerent/farplanedist-sky-fix
[xonotic/netradiant.git] / libs / fs_filesystem.h
1 /*
2    Copyright (C) 2001-2006, William Joseph.
3    All Rights Reserved.
4
5    This file is part of GtkRadiant.
6
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.
11
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.
16
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
20  */
21
22 #if !defined( INCLUDED_FS_FILESYSTEM_H )
23 #define INCLUDED_FS_FILESYSTEM_H
24
25 #include "string/string.h"
26 #include "os/path.h"
27
28 #include <map>
29
30 inline unsigned int path_get_depth( const char* path ){
31         unsigned int depth = 0;
32         while ( path != 0 && path[0] != '\0' )
33         {
34                 path = strchr( path, '/' );
35                 if ( path != 0 ) {
36                         ++path;
37                 }
38                 ++depth;
39         }
40         return depth;
41 }
42
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
48 {
49 class Path
50 {
51 CopiedString m_path;
52 unsigned int m_depth;
53 public:
54 Path( const char* path )
55         : m_path( path ), m_depth( path_get_depth( c_str() ) ){
56 }
57 Path( StringRange range )
58         : m_path( range ), m_depth( path_get_depth( c_str() ) ){
59 }
60 bool operator<( const Path& other ) const {
61         return string_less_nocase( c_str(), other.c_str() );
62 }
63 unsigned int depth() const {
64         return m_depth;
65 }
66 const char* c_str() const {
67         return m_path.c_str();
68 }
69 };
70
71 class Entry
72 {
73 file_type* m_file;
74 public:
75 Entry() : m_file( 0 ){
76 }
77 Entry( file_type* file ) : m_file( file ){
78 }
79 file_type* file() const {
80         return m_file;
81 }
82 bool is_directory() const {
83         return file() == 0;
84 }
85 };
86
87 typedef std::map<Path, Entry> Entries;
88 Entries m_entries;
89
90 public:
91 typedef typename Entries::iterator iterator;
92 typedef typename Entries::value_type value_type;
93 typedef Entry entry_type;
94
95 iterator begin(){
96         return m_entries.begin();
97 }
98 iterator end(){
99         return m_entries.end();
100 }
101
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 ){
106         {
107                 const char* end = path_remove_directory( path.c_str() );
108                 while ( end[0] != '\0' )
109                 {
110                         Path dir( StringRange( path.c_str(), end ) );
111                         m_entries.insert( value_type( dir, Entry( 0 ) ) );
112                         end = path_remove_directory( end );
113                 }
114         }
115
116         return m_entries[path];
117 }
118
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 );
122 }
123
124 iterator begin( const char* root ){
125         if ( root[0] == '\0' ) {
126                 return m_entries.begin();
127         }
128         iterator i = m_entries.find( root );
129         if ( i == m_entries.end() ) {
130                 return i;
131         }
132         return ++i;
133 }
134
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 )
144         {
145                 if ( i->first.depth() == skip_depth ) {
146                         skip_depth = 0;
147                 }
148                 if ( skip_depth == 0 ) {
149                         if ( !i->second.is_directory() ) {
150                                 visitor.file( i->first.c_str() );
151                         }
152                         else if ( visitor.directory( i->first.c_str(), i->first.depth() - start_depth ) ) {
153                                 skip_depth = i->first.depth();
154                         }
155                 }
156         }
157 }
158 };
159
160 #endif