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