Style fix: add whitespace
[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 inline bool is_windows_drive(const std::string &p)
11 { return (p.size()==2 && ((p[0]>='A' && p[0]<='Z') || (p[0]>='a' && p[0]<='z')) && p[1]==':'); }
12
13 }
14
15 namespace Msp {
16 namespace FS {
17
18 Path::Path()
19 { }
20
21 Path::Path(const string &p)
22 {
23         init(p);
24 }
25
26 Path::Path(const char *p)
27 {
28         init(p);
29 }
30
31 void Path::init(const string &p)
32 {
33         string::size_type start = 0;
34         if(p[0]=='/' || p[0]=='\\')
35                 add_component(string(1, DIRSEP));
36         while(1)
37         {
38                 string::size_type slash = p.find_first_of("/\\", start);
39                 if(slash>start)
40                         add_component(p.substr(start, slash-start));
41                 if(slash==string::npos)
42                         break;
43                 start = slash+1;
44         }
45 }
46
47 unsigned Path::size() const
48 {
49         if(path.empty())
50                 return 0;
51         if(path.size()==1 && path[0]==DIRSEP)
52                 return 1;
53
54         unsigned count = 1;
55         for(string::const_iterator i=path.begin(); i!=path.end(); ++i)
56                 if(*i==DIRSEP) ++count;
57         return count;
58 }
59
60 bool Path::is_absolute() const
61 {
62 #ifdef WIN32
63         if(is_windows_drive((*this)[0]))
64                 return true;
65 #endif
66         if(path[0]==DIRSEP)
67                 return true;
68         return false;
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 &p) const
187 {
188 #ifdef WIN32
189         return !strcasecmp(path, p.path);
190 #else
191         return path==p.path;
192 #endif
193 }
194
195 Path::Iterator Path::begin() const
196 {
197         return Iterator(*this);
198 }
199
200 Path::Iterator Path::end() const
201 {
202         Iterator i(*this);
203         i.start = i.end = std::string::npos;
204         return i;
205 }
206
207
208 Path::Iterator::Iterator(const Path &p):
209         path(p),
210         start(0)
211 {
212         if(path.path.empty())
213                 start = end = string::npos;
214         else if(path.path[0]==DIRSEP)
215                 end = 1;
216 #ifdef WIN32
217         else if(path.path.size()>2 && path.path[2]==DIRSEP && is_windows_drive(path.path.substr(0, 2)))
218                 end = 2;
219 #endif
220         else
221                 end = path.path.find(DIRSEP);
222 }
223
224 Path::Iterator &Path::Iterator::operator++()
225 {
226         start = end;
227         if(start>=path.path.size())
228                 return *this;
229         if(path.path[start]==DIRSEP)
230                 ++start;
231         end = path.path.find(DIRSEP, start);
232         return *this;
233 }
234
235 Path::Iterator &Path::Iterator::operator--()
236 {
237         if(start==0)
238                 return *this;
239
240         end = start;
241         if(end>1 && end<path.path.size() && path.path[end]!=DIRSEP)
242                 --end;
243
244         start = path.path.rfind(DIRSEP, end-1);
245         if(start==string::npos)
246                 start = 0;
247         else if(start<end-1)
248                 ++start;
249
250         return *this;
251 }
252
253 string Path::Iterator::operator*() const
254 {
255         if(start>=path.path.size())
256                 return string();
257         if(start==end)
258                 return string();
259         return path.path.substr(start, end-start);
260 }
261
262 } // namespace FS
263 } // namespace Msp