NOW I do it right: #woxblox#
[divverent/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 {
32   unsigned int depth = 0;
33   while(path != 0 && path[0] != '\0')
34   {
35     path = strchr(path, '/');
36     if(path != 0)
37     {
38       ++path;
39     }
40     ++depth;
41   }
42   return depth;
43 }
44
45 /// \brief A generic unix-style file-system which maps paths to files and directories.
46 /// Provides average O(log n) find and insert methods.
47 /// \param file_type The data type which represents a file.
48 template<typename file_type>
49 class GenericFileSystem
50 {
51   class Path
52   {
53     CopiedString m_path;
54     unsigned int m_depth;
55   public:
56     Path(const char* path)
57       : m_path(path), m_depth(path_get_depth(c_str()))
58     {
59     }
60     Path(StringRange range)
61       : m_path(range), m_depth(path_get_depth(c_str()))
62     {
63     }
64     bool operator<(const Path& other) const
65     {
66       return string_less_nocase(c_str(), other.c_str());
67     }
68     unsigned int depth() const
69     {
70       return m_depth;
71     }
72     const char* c_str() const
73     {
74       return m_path.c_str();
75     }
76   };
77
78   class Entry
79   {
80     file_type* m_file;
81   public:
82     Entry() : m_file(0)
83     {
84     }
85     Entry(file_type* file) : m_file(file)
86     {
87     }
88     file_type* file() const
89     {
90       return m_file;
91     }
92     bool is_directory() const
93     {
94       return file() == 0;
95     }
96   };
97
98   typedef std::map<Path, Entry> Entries;
99   Entries m_entries;
100
101 public:
102   typedef typename Entries::iterator iterator;
103   typedef typename Entries::value_type value_type;
104   typedef Entry entry_type;
105
106   iterator begin()
107   {
108     return m_entries.begin();
109   }
110   iterator end()
111   {
112     return m_entries.end();
113   }
114
115   /// \brief Returns the file at \p path.
116   /// Creates all directories below \p path if they do not exist.
117   /// O(log n) on average.
118   entry_type& operator[](const Path& path)
119   {
120     {
121       const char* end = path_remove_directory(path.c_str());
122       while(end[0] != '\0')
123       {
124         Path dir(StringRange(path.c_str(), end));
125         m_entries.insert(value_type(dir, Entry(0)));
126         end = path_remove_directory(end);
127       }
128     }
129
130     return m_entries[path];
131   }
132
133   /// \brief Returns the file at \p path or end() if not found.
134   iterator find(const Path& path)
135   {
136     return m_entries.find(path);
137   }
138
139   iterator begin(const char* root)
140   {
141     if(root[0] == '\0')
142     {
143       return m_entries.begin();
144     }
145     iterator i = m_entries.find(root);
146     if(i == m_entries.end())
147     {
148       return i;
149     }
150     return ++i;
151   }
152
153   /// \brief Performs a depth-first traversal of the file-system subtree rooted at \p root.
154   /// Traverses the entire tree if \p root is "".
155   /// Calls \p visitor.file() with the path to each file relative to the filesystem root.
156   /// Calls \p visitor.directory() with the path to each directory relative to the filesystem root.
157   template<typename visitor_type>
158   void traverse(visitor_type visitor, const char* root)
159   {
160     unsigned int start_depth = path_get_depth(root);
161     unsigned int skip_depth = 0;
162     for(iterator i = begin(root); i != end() && i->first.depth() > start_depth; ++i)
163     {
164       if(i->first.depth() == skip_depth)
165       {
166         skip_depth = 0;
167       }
168       if(skip_depth == 0)
169       {
170         if(!i->second.is_directory())
171         {
172           visitor.file(i->first.c_str());
173         }
174         else if(visitor.directory(i->first.c_str(), i->first.depth() - start_depth))
175         {
176           skip_depth = i->first.depth();
177         }
178       }
179     }
180   }
181 };
182
183 #endif