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