1 // Copyright 2010 The Kyua Authors.
2 // All rights reserved.
3 //
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
6 // met:
7 //
8 // * Redistributions of source code must retain the above copyright
9 // notice, this list of conditions and the following disclaimer.
10 // * Redistributions in binary form must reproduce the above copyright
11 // notice, this list of conditions and the following disclaimer in the
12 // documentation and/or other materials provided with the distribution.
13 // * Neither the name of Google Inc. nor the names of its contributors
14 // may be used to endorse or promote products derived from this software
15 // without specific prior written permission.
16 //
17 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20 // A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21 // OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22 // SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23 // LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25 // THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26 // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27 // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
29 #include "utils/fs/path.hpp"
30
31 #include "utils/fs/exceptions.hpp"
32 #include "utils/fs/operations.hpp"
33 #include "utils/sanity.hpp"
34
35 namespace fs = utils::fs;
36
37
38 namespace {
39
40
41 /// Normalizes an input string to a valid path.
42 ///
43 /// A normalized path cannot have empty components; i.e. there can be at most
44 /// one consecutive separator (/).
45 ///
46 /// \param in The string to normalize.
47 ///
48 /// \return The normalized string, representing a path.
49 ///
50 /// \throw utils::fs::invalid_path_error If the path is empty.
51 static std::string
normalize(const std::string & in)52 normalize(const std::string& in)
53 {
54 if (in.empty())
55 throw fs::invalid_path_error(in, "Cannot be empty");
56
57 std::string out;
58
59 std::string::size_type pos = 0;
60 do {
61 const std::string::size_type next_pos = in.find('/', pos);
62
63 const std::string component = in.substr(pos, next_pos - pos);
64 if (!component.empty()) {
65 if (pos == 0)
66 out += component;
67 else if (component != ".")
68 out += "/" + component;
69 }
70
71 if (next_pos == std::string::npos)
72 pos = next_pos;
73 else
74 pos = next_pos + 1;
75 } while (pos != std::string::npos);
76
77 return out.empty() ? "/" : out;
78 }
79
80
81 } // anonymous namespace
82
83
84 /// Creates a new path object from a textual representation of a path.
85 ///
86 /// \param text A valid representation of a path in textual form.
87 ///
88 /// \throw utils::fs::invalid_path_error If the input text does not represent a
89 /// valid path.
path(const std::string & text)90 fs::path::path(const std::string& text) :
91 _repr(normalize(text))
92 {
93 }
94
95
96 /// Gets a view of the path as an array of characters.
97 ///
98 /// \return A \code const char* \endcode representation for the object.
99 const char*
c_str(void) const100 fs::path::c_str(void) const
101 {
102 return _repr.c_str();
103 }
104
105
106 /// Gets a view of the path as a std::string.
107 ///
108 /// \return A \code std::string& \endcode representation for the object.
109 const std::string&
str(void) const110 fs::path::str(void) const
111 {
112 return _repr;
113 }
114
115
116 /// Gets the branch path (directory name) of the path.
117 ///
118 /// The branch path of a path with just one component (no separators) is ".".
119 ///
120 /// \return A new path representing the branch path.
121 fs::path
branch_path(void) const122 fs::path::branch_path(void) const
123 {
124 const std::string::size_type end_pos = _repr.rfind('/');
125 if (end_pos == std::string::npos)
126 return fs::path(".");
127 else if (end_pos == 0)
128 return fs::path("/");
129 else
130 return fs::path(_repr.substr(0, end_pos));
131 }
132
133
134 /// Gets the leaf name (base name) of the path.
135 ///
136 /// \return A new string representing the leaf name.
137 std::string
leaf_name(void) const138 fs::path::leaf_name(void) const
139 {
140 const std::string::size_type beg_pos = _repr.rfind('/');
141
142 if (beg_pos == std::string::npos)
143 return _repr;
144 else
145 return _repr.substr(beg_pos + 1);
146 }
147
148
149 /// Converts a relative path in the current directory to an absolute path.
150 ///
151 /// \pre The path is relative.
152 ///
153 /// \return The absolute representation of the relative path.
154 fs::path
to_absolute(void) const155 fs::path::to_absolute(void) const
156 {
157 PRE(!is_absolute());
158 return fs::current_path() / *this;
159 }
160
161
162 /// \return True if the representation of the path is absolute.
163 bool
is_absolute(void) const164 fs::path::is_absolute(void) const
165 {
166 return _repr[0] == '/';
167 }
168
169
170 /// Checks whether the path is a parent of another path.
171 ///
172 /// A path is considered to be a parent of itself.
173 ///
174 /// \return True if this path is a parent of p.
175 bool
is_parent_of(path p) const176 fs::path::is_parent_of(path p) const
177 {
178 do {
179 if ((*this) == p)
180 return true;
181 p = p.branch_path();
182 } while (p != fs::path(".") && p != fs::path("/"));
183 return false;
184 }
185
186
187 /// Counts the number of components in the path.
188 ///
189 /// \return The number of components.
190 int
ncomponents(void) const191 fs::path::ncomponents(void) const
192 {
193 int count = 0;
194 if (_repr == "/")
195 return 1;
196 else {
197 for (std::string::const_iterator iter = _repr.begin();
198 iter != _repr.end(); ++iter) {
199 if (*iter == '/')
200 count++;
201 }
202 return count + 1;
203 }
204 }
205
206
207 /// Less-than comparator for paths.
208 ///
209 /// This is provided to make identifiers useful as map keys.
210 ///
211 /// \param p The path to compare to.
212 ///
213 /// \return True if this identifier sorts before the other identifier; false
214 /// otherwise.
215 bool
operator <(const fs::path & p) const216 fs::path::operator<(const fs::path& p) const
217 {
218 return _repr < p._repr;
219 }
220
221
222 /// Compares two paths for equality.
223 ///
224 /// Given that the paths are internally normalized, input paths such as
225 /// ///foo/bar and /foo///bar are exactly the same. However, this does NOT
226 /// check for true equality: i.e. this does not access the file system to check
227 /// if the paths actually point to the same object my means of links.
228 ///
229 /// \param p The path to compare to.
230 ///
231 /// \returns A boolean indicating whether the paths are equal.
232 bool
operator ==(const fs::path & p) const233 fs::path::operator==(const fs::path& p) const
234 {
235 return _repr == p._repr;
236 }
237
238
239 /// Compares two paths for inequality.
240 ///
241 /// See the description of operator==() for more details on the comparison
242 /// performed.
243 ///
244 /// \param p The path to compare to.
245 ///
246 /// \returns A boolean indicating whether the paths are different.
247 bool
operator !=(const fs::path & p) const248 fs::path::operator!=(const fs::path& p) const
249 {
250 return _repr != p._repr;
251 }
252
253
254 /// Concatenates this path with one or more components.
255 ///
256 /// \param components The new components to concatenate to the path. These are
257 /// normalized because, in general, they may come from user input. These
258 /// components cannot represent an absolute path.
259 ///
260 /// \return A new path containing the concatenation of this path and the
261 /// provided components.
262 ///
263 /// \throw utils::fs::invalid_path_error If components does not represent a
264 /// valid path.
265 /// \throw utils::fs::join_error If the join operation is invalid because the
266 /// two paths are incompatible.
267 fs::path
operator /(const std::string & components) const268 fs::path::operator/(const std::string& components) const
269 {
270 return (*this) / fs::path(components);
271 }
272
273
274 /// Concatenates this path with another path.
275 ///
276 /// \param rest The path to concatenate to this one. Cannot be absolute.
277 ///
278 /// \return A new path containing the concatenation of this path and the other
279 /// path.
280 ///
281 /// \throw utils::fs::join_error If the join operation is invalid because the
282 /// two paths are incompatible.
283 fs::path
operator /(const fs::path & rest) const284 fs::path::operator/(const fs::path& rest) const
285 {
286 if (rest.is_absolute())
287 throw fs::join_error(_repr, rest._repr,
288 "Cannot concatenate a path to an absolute path");
289 return fs::path(_repr + '/' + rest._repr);
290 }
291
292
293 /// Formats a path for insertion on a stream.
294 ///
295 /// \param os The output stream.
296 /// \param p The path to inject to the stream.
297 ///
298 /// \return The output stream os.
299 std::ostream&
operator <<(std::ostream & os,const fs::path & p)300 fs::operator<<(std::ostream& os, const fs::path& p)
301 {
302 return (os << p.str());
303 }
304