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