Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/kyua/utils/fs/path.cpp
48081 views
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
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.
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*
100
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&
110
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
122
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
138
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
155
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
164
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
176
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
191
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
216
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
233
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
248
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
268
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
284
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&
300
fs::operator<<(std::ostream& os, const fs::path& p)
301
{
302
return (os << p.str());
303
}
304
305