Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
freebsd
GitHub Repository: freebsd/freebsd-src
Path: blob/main/contrib/llvm-project/libcxx/src/filesystem/path.cpp
35231 views
1
//===----------------------------------------------------------------------===//
2
//
3
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4
// See https://llvm.org/LICENSE.txt for license information.
5
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6
//
7
//===----------------------------------------------------------------------===//
8
9
#include <__config>
10
#include <filesystem>
11
#include <vector>
12
13
#include "error.h"
14
#include "path_parser.h"
15
16
_LIBCPP_BEGIN_NAMESPACE_FILESYSTEM
17
18
using detail::ErrorHandler;
19
using parser::createView;
20
using parser::PathParser;
21
using parser::string_view_t;
22
23
///////////////////////////////////////////////////////////////////////////////
24
// path definitions
25
///////////////////////////////////////////////////////////////////////////////
26
27
constexpr path::value_type path::preferred_separator;
28
29
path& path::replace_extension(path const& replacement) {
30
path p = extension();
31
if (not p.empty()) {
32
__pn_.erase(__pn_.size() - p.native().size());
33
}
34
if (!replacement.empty()) {
35
if (replacement.native()[0] != '.') {
36
__pn_ += PATHSTR(".");
37
}
38
__pn_.append(replacement.__pn_);
39
}
40
return *this;
41
}
42
43
///////////////////////////////////////////////////////////////////////////////
44
// path.decompose
45
46
string_view_t path::__root_name() const {
47
auto PP = PathParser::CreateBegin(__pn_);
48
if (PP.State_ == PathParser::PS_InRootName)
49
return *PP;
50
return {};
51
}
52
53
string_view_t path::__root_directory() const {
54
auto PP = PathParser::CreateBegin(__pn_);
55
if (PP.State_ == PathParser::PS_InRootName)
56
++PP;
57
if (PP.State_ == PathParser::PS_InRootDir)
58
return *PP;
59
return {};
60
}
61
62
string_view_t path::__root_path_raw() const {
63
auto PP = PathParser::CreateBegin(__pn_);
64
if (PP.State_ == PathParser::PS_InRootName) {
65
auto NextCh = PP.peek();
66
if (NextCh && isSeparator(*NextCh)) {
67
++PP;
68
return createView(__pn_.data(), &PP.RawEntry.back());
69
}
70
return PP.RawEntry;
71
}
72
if (PP.State_ == PathParser::PS_InRootDir)
73
return *PP;
74
return {};
75
}
76
77
static bool ConsumeRootName(PathParser* PP) {
78
static_assert(PathParser::PS_BeforeBegin == 1 && PathParser::PS_InRootName == 2, "Values for enums are incorrect");
79
while (PP->State_ <= PathParser::PS_InRootName)
80
++(*PP);
81
return PP->State_ == PathParser::PS_AtEnd;
82
}
83
84
static bool ConsumeRootDir(PathParser* PP) {
85
static_assert(PathParser::PS_BeforeBegin == 1 && PathParser::PS_InRootName == 2 && PathParser::PS_InRootDir == 3,
86
"Values for enums are incorrect");
87
while (PP->State_ <= PathParser::PS_InRootDir)
88
++(*PP);
89
return PP->State_ == PathParser::PS_AtEnd;
90
}
91
92
string_view_t path::__relative_path() const {
93
auto PP = PathParser::CreateBegin(__pn_);
94
if (ConsumeRootDir(&PP))
95
return {};
96
return createView(PP.RawEntry.data(), &__pn_.back());
97
}
98
99
string_view_t path::__parent_path() const {
100
if (empty())
101
return {};
102
// Determine if we have a root path but not a relative path. In that case
103
// return *this.
104
{
105
auto PP = PathParser::CreateBegin(__pn_);
106
if (ConsumeRootDir(&PP))
107
return __pn_;
108
}
109
// Otherwise remove a single element from the end of the path, and return
110
// a string representing that path
111
{
112
auto PP = PathParser::CreateEnd(__pn_);
113
--PP;
114
if (PP.RawEntry.data() == __pn_.data())
115
return {};
116
--PP;
117
return createView(__pn_.data(), &PP.RawEntry.back());
118
}
119
}
120
121
string_view_t path::__filename() const {
122
if (empty())
123
return {};
124
{
125
PathParser PP = PathParser::CreateBegin(__pn_);
126
if (ConsumeRootDir(&PP))
127
return {};
128
}
129
return *(--PathParser::CreateEnd(__pn_));
130
}
131
132
string_view_t path::__stem() const { return parser::separate_filename(__filename()).first; }
133
134
string_view_t path::__extension() const { return parser::separate_filename(__filename()).second; }
135
136
////////////////////////////////////////////////////////////////////////////
137
// path.gen
138
139
enum PathPartKind : unsigned char { PK_None, PK_RootSep, PK_Filename, PK_Dot, PK_DotDot, PK_TrailingSep };
140
141
static PathPartKind ClassifyPathPart(string_view_t Part) {
142
if (Part.empty())
143
return PK_TrailingSep;
144
if (Part == PATHSTR("."))
145
return PK_Dot;
146
if (Part == PATHSTR(".."))
147
return PK_DotDot;
148
if (Part == PATHSTR("/"))
149
return PK_RootSep;
150
#if defined(_LIBCPP_WIN32API)
151
if (Part == PATHSTR("\\"))
152
return PK_RootSep;
153
#endif
154
return PK_Filename;
155
}
156
157
path path::lexically_normal() const {
158
if (__pn_.empty())
159
return *this;
160
161
using PartKindPair = pair<string_view_t, PathPartKind>;
162
vector<PartKindPair> Parts;
163
// Guess as to how many elements the path has to avoid reallocating.
164
Parts.reserve(32);
165
166
// Track the total size of the parts as we collect them. This allows the
167
// resulting path to reserve the correct amount of memory.
168
size_t NewPathSize = 0;
169
auto AddPart = [&](PathPartKind K, string_view_t P) {
170
NewPathSize += P.size();
171
Parts.emplace_back(P, K);
172
};
173
auto LastPartKind = [&]() {
174
if (Parts.empty())
175
return PK_None;
176
return Parts.back().second;
177
};
178
179
bool MaybeNeedTrailingSep = false;
180
// Build a stack containing the remaining elements of the path, popping off
181
// elements which occur before a '..' entry.
182
for (auto PP = PathParser::CreateBegin(__pn_); PP; ++PP) {
183
auto Part = *PP;
184
PathPartKind Kind = ClassifyPathPart(Part);
185
switch (Kind) {
186
case PK_Filename:
187
case PK_RootSep: {
188
// Add all non-dot and non-dot-dot elements to the stack of elements.
189
AddPart(Kind, Part);
190
MaybeNeedTrailingSep = false;
191
break;
192
}
193
case PK_DotDot: {
194
// Only push a ".." element if there are no elements preceding the "..",
195
// or if the preceding element is itself "..".
196
auto LastKind = LastPartKind();
197
if (LastKind == PK_Filename) {
198
NewPathSize -= Parts.back().first.size();
199
Parts.pop_back();
200
} else if (LastKind != PK_RootSep)
201
AddPart(PK_DotDot, PATHSTR(".."));
202
MaybeNeedTrailingSep = LastKind == PK_Filename;
203
break;
204
}
205
case PK_Dot:
206
case PK_TrailingSep: {
207
MaybeNeedTrailingSep = true;
208
break;
209
}
210
case PK_None:
211
__libcpp_unreachable();
212
}
213
}
214
// [fs.path.generic]p6.8: If the path is empty, add a dot.
215
if (Parts.empty())
216
return PATHSTR(".");
217
218
// [fs.path.generic]p6.7: If the last filename is dot-dot, remove any
219
// trailing directory-separator.
220
bool NeedTrailingSep = MaybeNeedTrailingSep && LastPartKind() == PK_Filename;
221
222
path Result;
223
Result.__pn_.reserve(Parts.size() + NewPathSize + NeedTrailingSep);
224
for (auto& PK : Parts)
225
Result /= PK.first;
226
227
if (NeedTrailingSep)
228
Result /= PATHSTR("");
229
230
Result.make_preferred();
231
return Result;
232
}
233
234
static int DetermineLexicalElementCount(PathParser PP) {
235
int Count = 0;
236
for (; PP; ++PP) {
237
auto Elem = *PP;
238
if (Elem == PATHSTR(".."))
239
--Count;
240
else if (Elem != PATHSTR(".") && Elem != PATHSTR(""))
241
++Count;
242
}
243
return Count;
244
}
245
246
path path::lexically_relative(const path& base) const {
247
{ // perform root-name/root-directory mismatch checks
248
auto PP = PathParser::CreateBegin(__pn_);
249
auto PPBase = PathParser::CreateBegin(base.__pn_);
250
auto CheckIterMismatchAtBase = [&]() {
251
return PP.State_ != PPBase.State_ && (PP.inRootPath() || PPBase.inRootPath());
252
};
253
if (PP.inRootName() && PPBase.inRootName()) {
254
if (*PP != *PPBase)
255
return {};
256
} else if (CheckIterMismatchAtBase())
257
return {};
258
259
if (PP.inRootPath())
260
++PP;
261
if (PPBase.inRootPath())
262
++PPBase;
263
if (CheckIterMismatchAtBase())
264
return {};
265
}
266
267
// Find the first mismatching element
268
auto PP = PathParser::CreateBegin(__pn_);
269
auto PPBase = PathParser::CreateBegin(base.__pn_);
270
while (PP && PPBase && PP.State_ == PPBase.State_ && *PP == *PPBase) {
271
++PP;
272
++PPBase;
273
}
274
275
// If there is no mismatch, return ".".
276
if (!PP && !PPBase)
277
return ".";
278
279
// Otherwise, determine the number of elements, 'n', which are not dot or
280
// dot-dot minus the number of dot-dot elements.
281
int ElemCount = DetermineLexicalElementCount(PPBase);
282
if (ElemCount < 0)
283
return {};
284
285
// if n == 0 and (a == end() || a->empty()), returns path("."); otherwise
286
if (ElemCount == 0 && (PP.atEnd() || *PP == PATHSTR("")))
287
return PATHSTR(".");
288
289
// return a path constructed with 'n' dot-dot elements, followed by the
290
// elements of '*this' after the mismatch.
291
path Result;
292
// FIXME: Reserve enough room in Result that it won't have to re-allocate.
293
while (ElemCount--)
294
Result /= PATHSTR("..");
295
for (; PP; ++PP)
296
Result /= *PP;
297
return Result;
298
}
299
300
////////////////////////////////////////////////////////////////////////////
301
// path.comparisons
302
static int CompareRootName(PathParser* LHS, PathParser* RHS) {
303
if (!LHS->inRootName() && !RHS->inRootName())
304
return 0;
305
306
auto GetRootName = [](PathParser* Parser) -> string_view_t { return Parser->inRootName() ? **Parser : PATHSTR(""); };
307
int res = GetRootName(LHS).compare(GetRootName(RHS));
308
ConsumeRootName(LHS);
309
ConsumeRootName(RHS);
310
return res;
311
}
312
313
static int CompareRootDir(PathParser* LHS, PathParser* RHS) {
314
if (!LHS->inRootDir() && RHS->inRootDir())
315
return -1;
316
else if (LHS->inRootDir() && !RHS->inRootDir())
317
return 1;
318
else {
319
ConsumeRootDir(LHS);
320
ConsumeRootDir(RHS);
321
return 0;
322
}
323
}
324
325
static int CompareRelative(PathParser* LHSPtr, PathParser* RHSPtr) {
326
auto& LHS = *LHSPtr;
327
auto& RHS = *RHSPtr;
328
329
int res;
330
while (LHS && RHS) {
331
if ((res = (*LHS).compare(*RHS)) != 0)
332
return res;
333
++LHS;
334
++RHS;
335
}
336
return 0;
337
}
338
339
static int CompareEndState(PathParser* LHS, PathParser* RHS) {
340
if (LHS->atEnd() && !RHS->atEnd())
341
return -1;
342
else if (!LHS->atEnd() && RHS->atEnd())
343
return 1;
344
return 0;
345
}
346
347
int path::__compare(string_view_t __s) const {
348
auto LHS = PathParser::CreateBegin(__pn_);
349
auto RHS = PathParser::CreateBegin(__s);
350
int res;
351
352
if ((res = CompareRootName(&LHS, &RHS)) != 0)
353
return res;
354
355
if ((res = CompareRootDir(&LHS, &RHS)) != 0)
356
return res;
357
358
if ((res = CompareRelative(&LHS, &RHS)) != 0)
359
return res;
360
361
return CompareEndState(&LHS, &RHS);
362
}
363
364
////////////////////////////////////////////////////////////////////////////
365
// path.nonmembers
366
size_t hash_value(const path& __p) noexcept {
367
auto PP = PathParser::CreateBegin(__p.native());
368
size_t hash_value = 0;
369
hash<string_view_t> hasher;
370
while (PP) {
371
hash_value = __hash_combine(hash_value, hasher(*PP));
372
++PP;
373
}
374
return hash_value;
375
}
376
377
////////////////////////////////////////////////////////////////////////////
378
// path.itr
379
path::iterator path::begin() const {
380
auto PP = PathParser::CreateBegin(__pn_);
381
iterator it;
382
it.__path_ptr_ = this;
383
it.__state_ = static_cast<path::iterator::_ParserState>(PP.State_);
384
it.__entry_ = PP.RawEntry;
385
it.__stashed_elem_.__assign_view(*PP);
386
return it;
387
}
388
389
path::iterator path::end() const {
390
iterator it{};
391
it.__state_ = path::iterator::_AtEnd;
392
it.__path_ptr_ = this;
393
return it;
394
}
395
396
path::iterator& path::iterator::__increment() {
397
PathParser PP(__path_ptr_->native(), __entry_, __state_);
398
++PP;
399
__state_ = static_cast<_ParserState>(PP.State_);
400
__entry_ = PP.RawEntry;
401
__stashed_elem_.__assign_view(*PP);
402
return *this;
403
}
404
405
path::iterator& path::iterator::__decrement() {
406
PathParser PP(__path_ptr_->native(), __entry_, __state_);
407
--PP;
408
__state_ = static_cast<_ParserState>(PP.State_);
409
__entry_ = PP.RawEntry;
410
__stashed_elem_.__assign_view(*PP);
411
return *this;
412
}
413
414
#if defined(_LIBCPP_WIN32API)
415
////////////////////////////////////////////////////////////////////////////
416
// Windows path conversions
417
size_t __wide_to_char(const wstring& str, char* out, size_t outlen) {
418
if (str.empty())
419
return 0;
420
ErrorHandler<size_t> err("__wide_to_char", nullptr);
421
UINT codepage = AreFileApisANSI() ? CP_ACP : CP_OEMCP;
422
BOOL used_default = FALSE;
423
int ret = WideCharToMultiByte(codepage, 0, str.data(), str.size(), out, outlen, nullptr, &used_default);
424
if (ret <= 0 || used_default)
425
return err.report(errc::illegal_byte_sequence);
426
return ret;
427
}
428
429
size_t __char_to_wide(const string& str, wchar_t* out, size_t outlen) {
430
if (str.empty())
431
return 0;
432
ErrorHandler<size_t> err("__char_to_wide", nullptr);
433
UINT codepage = AreFileApisANSI() ? CP_ACP : CP_OEMCP;
434
int ret = MultiByteToWideChar(codepage, MB_ERR_INVALID_CHARS, str.data(), str.size(), out, outlen);
435
if (ret <= 0)
436
return err.report(errc::illegal_byte_sequence);
437
return ret;
438
}
439
#endif
440
441
_LIBCPP_END_NAMESPACE_FILESYSTEM
442
443