1 // Copyright 2010 Google Inc.
2 // All rights reserved.
4 // Redistribution and use in source and binary forms, with or without
5 // modification, are permitted provided that the following conditions are
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.
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.
29 #include "utils/fs/path.hpp"
31 #include "utils/fs/exceptions.hpp"
32 #include "utils/fs/operations.hpp"
33 #include "utils/sanity.hpp"
35 namespace fs
= utils::fs
;
41 /// Normalizes an input string to a valid path.
43 /// A normalized path cannot have empty components; i.e. there can be at most
44 /// one consecutive separator (/).
46 /// \param in The string to normalize.
48 /// \return The normalized string, representing a path.
50 /// \throw utils::fs::invalid_path_error If the path is empty.
52 normalize(const std::string
& in
)
55 throw fs::invalid_path_error(in
, "Cannot be empty");
59 std::string::size_type pos
= 0;
61 const std::string::size_type next_pos
= in
.find('/', pos
);
63 const std::string component
= in
.substr(pos
, next_pos
- pos
);
64 if (!component
.empty()) {
67 else if (component
!= ".")
68 out
+= "/" + component
;
71 if (next_pos
== std::string::npos
)
75 } while (pos
!= std::string::npos
);
77 return out
.empty() ? "/" : out
;
81 } // anonymous namespace
84 /// Creates a new path object from a textual representation of a path.
86 /// \param text A valid representation of a path in textual form.
88 /// \throw utils::fs::invalid_path_error If the input text does not represent a
90 fs::path::path(const std::string
& text
) :
91 _repr(normalize(text
))
96 /// Gets a view of the path as an array of characters.
98 fs::path::c_str(void) const
100 return _repr
.c_str();
104 /// Gets a view of the path as a std::string.
106 fs::path::str(void) const
112 /// Gets the branch path (directory name) of the path.
114 /// The branch path of a path with just one component (no separators) is ".".
116 /// \return A new path representing the branch path.
118 fs::path::branch_path(void) const
120 const std::string::size_type end_pos
= _repr
.rfind('/');
121 if (end_pos
== std::string::npos
)
122 return fs::path(".");
123 else if (end_pos
== 0)
124 return fs::path("/");
126 return fs::path(_repr
.substr(0, end_pos
));
130 /// Gets the leaf name (base name) of the path.
132 /// \return A new string representing the leaf name.
134 fs::path::leaf_name(void) const
136 const std::string::size_type beg_pos
= _repr
.rfind('/');
138 if (beg_pos
== std::string::npos
)
141 return _repr
.substr(beg_pos
+ 1);
145 /// Converts a relative path in the current directory to an absolute path.
147 /// \pre The path is relative.
149 /// \return The absolute representation of the relative path.
151 fs::path::to_absolute(void) const
154 return fs::current_path() / *this;
158 /// Checks whether the path is absolute.
160 fs::path::is_absolute(void) const
162 return _repr
[0] == '/';
166 /// Checks whether the path is a parent of another path.
168 /// A path is considered to be a parent of itself.
170 /// \return True if this path is a parent of p.
172 fs::path::is_parent_of(path p
) const
178 } while (p
!= fs::path(".") && p
!= fs::path("/"));
183 /// Counts the number of components in the path.
185 /// \return The number of components.
187 fs::path::ncomponents(void) const
193 for (std::string::const_iterator iter
= _repr
.begin();
194 iter
!= _repr
.end(); ++iter
) {
203 /// Less-than comparator for paths.
205 /// This is provided to make identifiers useful as map keys.
207 /// \param p The path to compare to.
209 /// \return True if this identifier sorts before the other identifier; false
212 fs::path::operator<(const fs::path
& p
) const
214 return _repr
< p
._repr
;
218 /// Compares two paths for equality.
220 /// Given that the paths are internally normalized, input paths such as
221 /// ///foo/bar and /foo///bar are exactly the same. However, this does NOT
222 /// check for true equality: i.e. this does not access the file system to check
223 /// if the paths actually point to the same object my means of links.
225 /// \param p The path to compare to.
227 /// \returns A boolean indicating whether the paths are equal.
229 fs::path::operator==(const fs::path
& p
) const
231 return _repr
== p
._repr
;
235 /// Compares two paths for inequality.
237 /// See the description of operator==() for more details on the comparison
240 /// \param p The path to compare to.
242 /// \returns A boolean indicating whether the paths are different.
244 fs::path::operator!=(const fs::path
& p
) const
246 return _repr
!= p
._repr
;
250 /// Concatenates this path with one or more components.
252 /// \param components The new components to concatenate to the path. These are
253 /// normalized because, in general, they may come from user input. These
254 /// components cannot represent an absolute path.
256 /// \return A new path containing the concatenation of this path and the
257 /// provided components.
259 /// \throw utils::fs::invalid_path_error If components does not represent a
261 /// \throw utils::fs::join_error If the join operation is invalid because the
262 /// two paths are incompatible.
264 fs::path::operator/(const std::string
& components
) const
266 return (*this) / fs::path(components
);
270 /// Concatenates this path with another path.
272 /// \param rest The path to concatenate to this one. Cannot be absolute.
274 /// \return A new path containing the concatenation of this path and the other
277 /// \throw utils::fs::join_error If the join operation is invalid because the
278 /// two paths are incompatible.
280 fs::path::operator/(const fs::path
& rest
) const
282 if (rest
.is_absolute())
283 throw fs::join_error(_repr
, rest
._repr
,
284 "Cannot concatenate a path to an absolute path");
285 return fs::path(_repr
+ '/' + rest
._repr
);
289 /// Formats a path for insertion on a stream.
291 /// \param os The output stream.
292 /// \param p The path to inject to the stream.
294 /// \return The output stream os.
296 fs::operator<<(std::ostream
& os
, const fs::path
& p
)
298 return (os
<< p
.str());