]> git.tdb.fi Git - libs/core.git/blob - source/fs/path.cpp
Very minor refactoring
[libs/core.git] / source / fs / path.cpp
1 #include <stdexcept>
2 #include <msp/strings/utils.h>
3 #include "path.h"
4 #include "utils.h"
5
6 using namespace std;
7
8 namespace {
9
10 #ifdef WIN32
11 inline bool is_windows_drive(const std::string &p)
12 { return (p.size()==2 && ((p[0]>='A' && p[0]<='Z') || (p[0]>='a' && p[0]<='z')) && p[1]==':'); }
13 #endif
14
15 }
16
17 namespace Msp {
18 namespace FS {
19
20 Path::Path()
21 { }
22
23 Path::Path(const string &p)
24 {
25         init(p);
26 }
27
28 Path::Path(const char *p)
29 {
30         init(p);
31 }
32
33 void Path::init(const string &p)
34 {
35         string::size_type start = 0;
36         if(p[0]=='/' || p[0]=='\\')
37                 add_component(string(1, DIRSEP));
38         while(1)
39         {
40                 string::size_type slash = p.find_first_of("/\\", start);
41                 if(slash>start)
42                         add_component(p.substr(start, slash-start));
43                 if(slash==string::npos)
44                         break;
45                 start = slash+1;
46         }
47 }
48
49 unsigned Path::size() const
50 {
51         if(path.empty())
52                 return 0;
53         if(path.size()==1 && path[0]==DIRSEP)
54                 return 1;
55
56         unsigned count = 1;
57         for(string::const_iterator i=path.begin(); i!=path.end(); ++i)
58                 if(*i==DIRSEP) ++count;
59         return count;
60 }
61
62 bool Path::is_absolute() const
63 {
64 #ifdef WIN32
65         if(is_windows_drive((*this)[0]))
66                 return true;
67 #endif
68         return path[0]==DIRSEP;
69 }
70
71 Path Path::subpath(unsigned start, unsigned count) const
72 {
73         Path result;
74         Iterator i = begin();
75         for(unsigned j=0; (j<start && i!=end()); ++j)
76                 ++i;
77         for(unsigned j=0; (j<count && i!=end()); ++j)
78         {
79                 result /= *i;
80                 ++i;
81         }
82         return result;
83 }
84
85 Path Path::operator/(const Path &p) const
86 {
87         Path a = *this;
88         a /= p;
89         return a;
90 }
91
92 Path &Path::operator/=(const Path &p)
93 {
94         if(p.is_absolute())
95                 path = p.path;
96         else
97         {
98                 for(Iterator i=p.begin(); i!=p.end(); ++i)
99                         add_component(*i);
100         }
101         return *this;
102 }
103
104 void Path::add_component(const string &comp)
105 {
106         if(comp.empty())
107                 ;
108         else if(comp.size()==1 && comp[0]==DIRSEP)
109         {
110                 // Replace the path with the root directory
111 #ifdef WIN32
112                 unsigned slash = path.find(DIRSEP);
113                 if(is_windows_drive(path.substr(0, slash)))
114                         path = path.substr(0, 2);
115                 else
116 #endif
117                 path = comp;
118         }
119 #ifdef WIN32
120         else if(is_windows_drive(comp))
121                 path = comp;
122 #endif
123         else if(comp=="..")
124         {
125                 if(path.empty() || path==".")
126                         path = comp;
127                 // .. in root directory is a no-op
128                 else if(path.size()==1 && path[0]==DIRSEP)
129                         ;
130 #ifdef WIN32
131                 else if(is_windows_drive(path))
132                         ;
133 #endif
134                 else
135                 {
136                         string::size_type slash = path.rfind(DIRSEP);
137                         string::size_type start = (slash==string::npos ? 0 : slash+1);
138                         if(!path.compare(start, string::npos, ".."))
139                         {
140                                 // If the last component already is a .., add another
141                                 path += DIRSEP;
142                                 path += comp;
143                         }
144                         else if(slash==string::npos)
145                                 path = ".";
146                         else
147                         {
148                                 if(slash==0)
149                                         slash = 1;
150                                 // Otherwise, erase the last component
151                                 path.erase(slash, string::npos);
152                         }
153                 }
154         }
155         else if(comp!="." || path.empty())
156         {
157                 if(comp!="." && path.empty())
158                         path = ".";
159                 if(path.size()>1 || (path.size()==1 && path[0]!=DIRSEP))
160                         path += DIRSEP;
161                 path += comp;
162         }
163 }
164
165 string Path::operator[](int n) const
166 {
167         if(n>=0)
168         {
169                 for(Iterator i=begin(); i!=end(); ++i, --n)
170                         if(!n)
171                                 return *i;
172         }
173         else
174         {
175                 for(Iterator i=end(); i!=begin();)
176                 {
177                         --i;
178                         if(!++n)
179                                 return *i;
180                 }
181         }
182
183         throw invalid_argument("Path::operator[]");
184 }
185
186 bool Path::operator==(const Path &other) const
187 {
188 #ifdef WIN32
189         return strcasecmp(path, other.path)==0;
190 #else
191         return path==other.path;
192 #endif
193 }
194
195 bool Path::operator<(const Path &other) const
196 {
197 #ifdef WIN32
198         return strcasecmp(path, other.path)<0;
199 #else
200         return path<other.path;
201 #endif
202 }
203
204 bool Path::operator>(const Path &other) const
205 {
206 #ifdef WIN32
207         return strcasecmp(path, other.path)>0;
208 #else
209         return path>other.path;
210 #endif
211 }
212
213 Path::Iterator Path::begin() const
214 {
215         return Iterator(*this);
216 }
217
218 Path::Iterator Path::end() const
219 {
220         Iterator i(*this);
221         i.start = i.end = std::string::npos;
222         return i;
223 }
224
225
226 Path::Iterator::Iterator(const Path &p):
227         path(p),
228         start(0)
229 {
230         if(path.path.empty())
231                 start = end = string::npos;
232         else if(path.path[0]==DIRSEP)
233                 end = 1;
234 #ifdef WIN32
235         else if(path.path.size()>2 && path.path[2]==DIRSEP && is_windows_drive(path.path.substr(0, 2)))
236                 end = 2;
237 #endif
238         else
239                 end = path.path.find(DIRSEP);
240 }
241
242 Path::Iterator &Path::Iterator::operator++()
243 {
244         start = end;
245         if(start>=path.path.size())
246                 return *this;
247         if(path.path[start]==DIRSEP)
248                 ++start;
249         end = path.path.find(DIRSEP, start);
250         return *this;
251 }
252
253 Path::Iterator &Path::Iterator::operator--()
254 {
255         if(start==0)
256                 return *this;
257
258         end = start;
259         if(end>1 && end<path.path.size() && path.path[end]!=DIRSEP)
260                 --end;
261
262         start = path.path.rfind(DIRSEP, end-1);
263         if(start==string::npos)
264                 start = 0;
265         else if(start<end-1)
266                 ++start;
267
268         return *this;
269 }
270
271 string Path::Iterator::operator*() const
272 {
273         if(start>=path.path.size())
274                 return string();
275         if(start==end)
276                 return string();
277         return path.path.substr(start, end-start);
278 }
279
280 } // namespace FS
281 } // namespace Msp