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