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