]> git.tdb.fi Git - libs/core.git/blob - source/fs/path.cpp
Merge branch 'fs-master'
[libs/core.git] / source / fs / path.cpp
1 #include <msp/core/except.h>
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 unsigned Path::size() const
32 {
33         if(path.empty())
34                 return 0;
35         if(path.size()==1 && path[0]==DIRSEP)
36                 return 1;
37
38         unsigned count = 1;
39         for(string::const_iterator i=path.begin(); i!=path.end(); ++i)
40                 if(*i==DIRSEP) ++count;
41         return count;
42 }
43
44 bool Path::is_absolute() const
45 {
46 #ifdef WIN32
47         if(is_windows_drive((*this)[0]))
48                 return true;
49 #endif
50         if(path[0]==DIRSEP)
51                 return true;
52         return false;
53 }
54
55 Path Path::subpath(unsigned start, unsigned count) const
56 {
57         Path result;
58         Iterator 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                 path = p.path;
80         else
81         {
82                 for(Iterator i=p.begin(); i!=p.end(); ++i)
83                         add_component(*i);
84         }
85         return *this;
86 }
87
88 string Path::operator[](int n) const
89 {
90         if(n>=0)
91         {
92                 for(Iterator i=begin(); i!=end(); ++i, --n)
93                         if(!n)
94                                 return *i;
95         }
96         else
97         {
98                 for(Iterator i=end(); i!=begin();)
99                 {
100                         --i;
101                         if(!++n)
102                                 return *i;
103                 }
104         }
105
106         throw InvalidParameterValue("Path component index out of range");
107 }
108
109 bool Path::operator==(const Path &p) const
110 {
111 #ifdef WIN32
112         return !strcasecmp(path, p.path);
113 #else
114         return path==p.path;
115 #endif
116 }
117
118 Path::Iterator Path::begin() const
119 {
120         return Iterator(*this);
121 }
122
123 Path::Iterator Path::end() const
124 {
125         Iterator i(*this);
126         i.start=i.end = std::string::npos;
127         return i;
128 }
129
130 void Path::init(const string &p)
131 {
132         string::size_type start = 0;
133         if(p[0]=='/' || p[0]=='\\')
134                 add_component(string(1, DIRSEP));
135         while(1)
136         {
137                 string::size_type slash = p.find_first_of("/\\", start);
138                 if(slash>start)
139                         add_component(p.substr(start, slash-start));
140                 if(slash==string::npos)
141                         break;
142                 start = slash+1;
143         }
144 }
145
146 void Path::add_component(const string &comp)
147 {
148         if(comp.empty())
149                 ;
150         else if(comp.size()==1 && comp[0]==DIRSEP)
151         {
152                 // Replace the path with the root directory
153 #ifdef WIN32
154                 unsigned slash = path.find(DIRSEP);
155                 if(is_windows_drive(path.substr(0, slash)))
156                         path = path.substr(0, 2);
157                 else
158 #endif
159                 path = comp;
160         }
161 #ifdef WIN32
162         else if(is_windows_drive(comp))
163                 path = comp;
164 #endif
165         else if(comp=="..")
166         {
167                 if(path.empty() || path==".")
168                         path = comp;
169                 // .. in root directory is a no-op
170                 else if(path.size()==1 && path[0]==DIRSEP)
171                         ;
172 #ifdef WIN32
173                 else if(is_windows_drive(path))
174                         ;
175 #endif
176                 else
177                 {
178                         string::size_type slash = path.rfind(DIRSEP);
179                         string::size_type start = (slash==string::npos ? 0 : slash+1);
180                         if(!path.compare(start, string::npos, ".."))
181                         {
182                                 // If the last component already is a .., add another
183                                 path += DIRSEP;
184                                 path += comp;
185                         }
186                         else if(slash==string::npos)
187                                 path = ".";
188                         else
189                         {
190                                 if(slash==0)
191                                         slash = 1;
192                                 // Otherwise, erase the last component
193                                 path.erase(slash, string::npos);
194                         }
195                 }
196         }
197         else if(comp!="." || path.empty())
198         {
199                 if(comp!="." && path.empty())
200                         path = ".";
201                 if(path.size()>1 || (path.size()==1 && path[0]!=DIRSEP))
202                         path += DIRSEP;
203                 path += comp;
204         }
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