]> git.tdb.fi Git - libs/core.git/blob - source/path.cpp
Rename the library as mspfs
[libs/core.git] / source / path.cpp
1 /* $Id$
2
3 This file is part of libmspfs
4 Copyright © 2006-2008  Mikko Rasa, Mikkosoft Productions
5 Distributed under the LGPL
6 */
7
8 #include <msp/strings/utils.h>
9 #include "path.h"
10 #include "utils.h"
11
12 using namespace std;
13
14 namespace Msp {
15
16 /**
17 Returns the number of components in the path.
18 */
19 unsigned Path::size() const
20 {
21         if(path.empty())
22                 return 0;
23         if(path.size()==1 && path[0]==DIRCHAR)
24                 return 1;
25
26         unsigned count=1;
27         for(string::const_iterator i=path.begin(); i!=path.end(); ++i)
28                 if(*i==DIRCHAR) ++count;
29         return count;
30 }
31
32 bool Path::is_absolute() const
33 {
34 #ifdef WIN32
35         if(is_windows_drive((*this)[0]))
36                 return true;
37 #endif
38         if(path[0]==DIRCHAR)
39                 return true;
40         return false;
41 }
42
43 /**
44 Extracts the given component range from the path.
45 */
46 Path Path::subpath(unsigned start, unsigned count) const
47 {
48         Path result;
49         Iterator i=begin();
50         for(unsigned j=0; (j<start && i!=end()); ++j)
51                 ++i;
52         for(unsigned j=0; (j<count && i!=end()); ++j)
53         {
54                 result/=*i;
55                 ++i;
56         }
57         return result;
58 }
59
60 /**
61 Attaches another path to the end of this one.  An absolute path replaces the
62 existing data.  ".." elements annihilate the last component and "." elements
63 are ignored.
64 */
65 Path &Path::operator/=(const Path &p)
66 {
67         if(p.is_absolute())
68                 path=p.path;
69         else
70         {
71                 for(Iterator i=p.begin(); i!=p.end(); ++i)
72                         add_component(*i);
73         }
74         return *this;
75 }
76
77 /**
78 Returns the path component at the specified index.  Negative indices count from
79 the end of the path.
80 */
81 string Path::operator[](int n) const
82 {
83         if(n>=0)
84         {
85                 for(Iterator i=begin(); i!=end(); ++i, --n)
86                         if(!n) return *i;
87         }
88         else
89         {
90                 for(Iterator i=--end();; --i)
91                 {
92                         if(!++n) return *i;
93                         if(i==begin()) break;
94                 }
95         }
96
97         return "";
98 }
99
100 bool Path::operator==(const Path &p) const
101 {
102 #ifdef WIN32
103         return !strcasecmp(path, p.path);
104 #else
105         return path==p.path;
106 #endif
107 }
108
109 void Path::init(const string &p)
110 {
111         unsigned start=0;
112         if(p[0]=='/' || p[0]=='\\')
113                 add_component(string(1, DIRCHAR));
114         while(1)
115         {
116                 unsigned slash=p.find_first_of("/\\", start);
117                 if(slash>start)
118                         add_component(p.substr(start, slash-start));
119                 if(slash==string::npos)
120                         break;
121                 start=slash+1;
122         }
123 }
124
125 /**
126 Adds a single component to the path, emulating the cd command.
127 */
128 void Path::add_component(const string &comp)
129 {
130         if(comp.empty())
131                 ;
132         else if(comp.size()==1 && comp[0]==DIRCHAR)
133         {
134                 // Replace the path with the root directory
135 #ifdef WIN32
136                 unsigned slash=path.find(DIRCHAR);
137                 if(is_windows_drive(path.substr(0, slash)))
138                         path=path.substr(0, 2);
139                 else
140 #endif
141                 path=comp;
142         }
143 #ifdef WIN32
144         else if(is_windows_drive(comp))
145                 path=comp;
146 #endif
147         else if(comp=="..")
148         {
149                 if(path.empty())
150                         path=comp;
151                 // .. in root directory is a no-op
152                 else if(path.size()==1 && path[0]==DIRCHAR)
153                         ;
154 #ifdef WIN32
155                 else if(is_windows_drive(path))
156                         ;
157 #endif
158                 else
159                 {
160                         unsigned slash=path.rfind(DIRCHAR);
161                         unsigned start=(slash==string::npos)?0:slash+1;
162                         if(!path.compare(start, string::npos, ".."))
163                         {
164                                 // If the last component already is a .., add another
165                                 path+=DIRCHAR;
166                                 path+=comp;
167                         }
168                         else
169                                 // Otherwise, erase the last component
170                                 path.erase(slash, string::npos);
171                 }
172         }
173         else if(comp!="." || path.empty())
174         {
175                 if(path==".")
176                         path="";
177                 if(path.size()>1 || (path.size()==1 && path[0]!=DIRCHAR))
178                         path+=DIRCHAR;
179                 path+=comp;
180         }
181 }
182
183 Path::Iterator::Iterator(const Path &p):
184         path(p),
185         start(0)
186 {
187         if(path.path.empty())
188                 start=end=string::npos;
189         else if(path.path[0]==DIRCHAR)
190                 end=1;
191 #ifdef WIN32
192         else if(path.path.size()>2 && path.path[2]==DIRCHAR && is_windows_drive(path.path.substr(0, 2)))
193                 end=2;
194 #endif
195         else
196                 end=path.path.find(DIRCHAR);
197 }
198
199 Path::Iterator &Path::Iterator::operator++()
200 {
201         start=end;
202         if(start>=path.path.size()) return *this;
203         if(path.path[start]==DIRCHAR) ++start;
204         end=path.path.find(DIRCHAR, start);
205         return *this;
206 }
207
208 Path::Iterator &Path::Iterator::operator--()
209 {
210         end=start;
211         if(end==0) return *this;
212         if(end>1 && end<path.path.size() && path.path[end]!=DIRCHAR) --end;
213         start=path.path.rfind(DIRCHAR, end-1);
214         if(start==string::npos)
215                 start=0;
216         else if(start<end-1)
217                 ++start;
218         return *this;
219 }
220
221 string Path::Iterator::operator*() const
222 {
223         if(start>=path.path.size()) return "";
224         if(start==end) return "";
225         return path.path.substr(start, end-start);
226 }
227
228 } // namespace Msp