blob: daf2c2bba79f22b0e62ec9752b05ffb8d3777b3e [file] [log] [blame]
Eric Fiselierc7979582016-06-17 19:46:40 +00001//===--------------------- filesystem/path.cpp ----------------------------===//
2//
3// The LLVM Compiler Infrastructure
4//
5// This file is dual licensed under the MIT and the University of Illinois Open
6// Source Licenses. See LICENSE.TXT for details.
7//
8//===----------------------------------------------------------------------===//
9#include "experimental/filesystem"
Eric Fiselier0fdab5e2016-07-23 03:10:56 +000010#include "string_view"
Eric Fiselierc7979582016-06-17 19:46:40 +000011#include "utility"
Eric Fiselier0fdab5e2016-07-23 03:10:56 +000012
Eric Fiselierc7979582016-06-17 19:46:40 +000013namespace { namespace parser
14{
Eric Fiselier1467a192016-10-30 23:30:38 +000015using namespace std;
16using namespace std::experimental::filesystem;
Eric Fiselierc7979582016-06-17 19:46:40 +000017
Eric Fiselier1467a192016-10-30 23:30:38 +000018using string_view_t = path::__string_view;
Eric Fiselier0fdab5e2016-07-23 03:10:56 +000019using string_view_pair = pair<string_view_t, string_view_t>;
Eric Fiselier1467a192016-10-30 23:30:38 +000020using PosPtr = path::value_type const*;
Eric Fiselierc7979582016-06-17 19:46:40 +000021
Eric Fiselier1467a192016-10-30 23:30:38 +000022struct PathParser {
23 enum ParserState : unsigned char {
24 // Zero is a special sentinel value used by default constructed iterators.
25 PS_BeforeBegin = 1,
26 PS_InRootName,
27 PS_InRootDir,
28 PS_InFilenames,
29 PS_InTrailingSep,
30 PS_AtEnd
31 };
Eric Fiselierc7979582016-06-17 19:46:40 +000032
Eric Fiselier1467a192016-10-30 23:30:38 +000033 const string_view_t Path;
34 string_view_t RawEntry;
35 ParserState State;
Eric Fiselierc7979582016-06-17 19:46:40 +000036
Eric Fiselier1467a192016-10-30 23:30:38 +000037private:
38 PathParser(string_view_t P, ParserState State) noexcept
39 : Path(P), State(State) {}
Eric Fiselierc7979582016-06-17 19:46:40 +000040
Eric Fiselier1467a192016-10-30 23:30:38 +000041public:
42 PathParser(string_view_t P, string_view_t E, unsigned char S)
43 : Path(P), RawEntry(E), State(static_cast<ParserState>(S)) {
Eric Fiselieref915d32016-10-30 23:53:50 +000044 // S cannot be '0' or PS_BeforeBegin.
Eric Fiselier1467a192016-10-30 23:30:38 +000045 }
Eric Fiselierc7979582016-06-17 19:46:40 +000046
Eric Fiselier1467a192016-10-30 23:30:38 +000047 static PathParser CreateBegin(string_view_t P) noexcept {
48 PathParser PP(P, PS_BeforeBegin);
49 PP.increment();
50 return PP;
51 }
Eric Fiselierc7979582016-06-17 19:46:40 +000052
Eric Fiselier1467a192016-10-30 23:30:38 +000053 static PathParser CreateEnd(string_view_t P) noexcept {
54 PathParser PP(P, PS_AtEnd);
55 return PP;
56 }
Eric Fiselierc7979582016-06-17 19:46:40 +000057
Eric Fiselier1467a192016-10-30 23:30:38 +000058 PosPtr peek() const noexcept {
59 auto End = &Path.back() + 1;
60 auto TkEnd = getNextTokenStartPos();
61 return TkEnd == End ? nullptr : TkEnd;
62 }
Eric Fiselierc7979582016-06-17 19:46:40 +000063
Eric Fiselier1467a192016-10-30 23:30:38 +000064 void increment() noexcept {
65 const PosPtr End = &Path.back() + 1;
66 const PosPtr Start = getNextTokenStartPos();
67 if (Start == End)
68 return makeState(PS_AtEnd);
Eric Fiselierc7979582016-06-17 19:46:40 +000069
Eric Fiselier1467a192016-10-30 23:30:38 +000070 switch (State) {
71 case PS_BeforeBegin: {
72 PosPtr TkEnd = consumeSeparator(Start, End);
73 // If we consumed exactly two separators we have a root name.
74 if (TkEnd && TkEnd == Start + 2) {
75 // FIXME Do we need to consume a name or is '//' a root name on its own?
76 // what about '//.', '//..', '//...'?
77 auto NameEnd = consumeName(TkEnd, End);
78 if (NameEnd)
79 TkEnd = NameEnd;
80 return makeState(PS_InRootName, Start, TkEnd);
81 }
82 else if (TkEnd)
83 return makeState(PS_InRootDir, Start, TkEnd);
84 else
85 return makeState(PS_InFilenames, Start, consumeName(Start, End));
Eric Fiselierc7979582016-06-17 19:46:40 +000086 }
Eric Fiselierc7979582016-06-17 19:46:40 +000087
Eric Fiselier1467a192016-10-30 23:30:38 +000088 case PS_InRootName:
89 return makeState(PS_InRootDir, Start, consumeSeparator(Start, End));
90 case PS_InRootDir:
91 return makeState(PS_InFilenames, Start, consumeName(Start, End));
Eric Fiselierc7979582016-06-17 19:46:40 +000092
Eric Fiselier1467a192016-10-30 23:30:38 +000093 case PS_InFilenames: {
94 PosPtr SepEnd = consumeSeparator(Start, End);
Eric Fiselier1467a192016-10-30 23:30:38 +000095 if (SepEnd != End) {
96 PosPtr TkEnd = consumeName(SepEnd, End);
97 if (TkEnd)
98 return makeState(PS_InFilenames, SepEnd, TkEnd);
99 }
100 return makeState(PS_InTrailingSep, Start, SepEnd);
101 }
102
103 case PS_InTrailingSep:
104 return makeState(PS_AtEnd);
105
106 case PS_AtEnd:
107 _LIBCPP_UNREACHABLE();
108 }
109 }
110
111 void decrement() noexcept {
112 const PosPtr REnd = &Path.front() - 1;
113 const PosPtr RStart = getCurrentTokenStartPos() - 1;
Eric Fiselier1467a192016-10-30 23:30:38 +0000114
115 switch (State) {
116 case PS_AtEnd: {
117 // Try to consume a trailing separator or root directory first.
118 if (PosPtr SepEnd = consumeSeparator(RStart, REnd)) {
119 if (SepEnd == REnd)
120 return makeState((RStart == REnd + 2) ? PS_InRootName : PS_InRootDir,
121 Path.data(), RStart + 1);
122 // Check if we're seeing the root directory separator
123 auto PP = CreateBegin(Path);
124 bool InRootDir = PP.State == PS_InRootName &&
125 &PP.RawEntry.back() == SepEnd;
126 return makeState(InRootDir ? PS_InRootDir : PS_InTrailingSep,
127 SepEnd + 1, RStart + 1);
128 } else {
129 PosPtr TkStart = consumeName(RStart, REnd);
Eric Fiselier1467a192016-10-30 23:30:38 +0000130 if (TkStart == REnd + 2 && consumeSeparator(TkStart, REnd) == REnd)
131 return makeState(PS_InRootName, Path.data(), RStart + 1);
132 else
133 return makeState(PS_InFilenames, TkStart + 1, RStart + 1);
134 }
135 }
136 case PS_InTrailingSep:
137 return makeState(PS_InFilenames, consumeName(RStart, REnd) + 1, RStart + 1);
138 case PS_InFilenames: {
139 PosPtr SepEnd = consumeSeparator(RStart, REnd);
140 if (SepEnd == REnd)
141 return makeState((RStart == REnd + 2) ? PS_InRootName : PS_InRootDir,
142 Path.data(), RStart + 1);
143 PosPtr TkEnd = consumeName(SepEnd, REnd);
144 if (TkEnd == REnd + 2 && consumeSeparator(TkEnd, REnd) == REnd)
145 return makeState(PS_InRootDir, SepEnd + 1, RStart + 1);
146 return makeState(PS_InFilenames, TkEnd + 1, SepEnd + 1);
147 }
148 case PS_InRootDir:
149 return makeState(PS_InRootName, Path.data(), RStart + 1);
150 case PS_InRootName:
151 case PS_BeforeBegin:
152 _LIBCPP_UNREACHABLE();
153 }
154 }
155
156 /// \brief Return a view with the "preferred representation" of the current
157 /// element. For example trailing separators are represented as a '.'
158 string_view_t operator*() const noexcept {
159 switch (State) {
160 case PS_BeforeBegin:
161 case PS_AtEnd:
162 return "";
163 case PS_InRootDir:
164 return "/";
165 case PS_InTrailingSep:
166 return ".";
167 case PS_InRootName:
168 case PS_InFilenames:
169 return RawEntry;
170 }
171 _LIBCPP_UNREACHABLE();
172 }
173
174 explicit operator bool() const noexcept {
175 return State != PS_BeforeBegin && State != PS_AtEnd;
176 }
177
178 PathParser& operator++() noexcept {
179 increment();
180 return *this;
181 }
182
183 PathParser& operator--() noexcept {
184 decrement();
185 return *this;
186 }
187
188private:
189 void makeState(ParserState NewState, PosPtr Start, PosPtr End) noexcept {
Eric Fiselier1467a192016-10-30 23:30:38 +0000190 State = NewState;
Eric Fiselier1467a192016-10-30 23:30:38 +0000191 RawEntry = string_view_t(Start, End - Start);
192 }
193 void makeState(ParserState NewState) noexcept {
Eric Fiselier1467a192016-10-30 23:30:38 +0000194 State = NewState;
195 RawEntry = {};
196 }
197
198 /// \brief Return a pointer to the first character after the currently
199 /// lexed element.
200 PosPtr getNextTokenStartPos() const noexcept {
201 switch (State) {
202 case PS_BeforeBegin:
203 return &Path.front();
204 case PS_InRootName:
205 case PS_InRootDir:
206 case PS_InFilenames:
207 return &RawEntry.back() + 1;
208 case PS_InTrailingSep:
209 case PS_AtEnd:
210 return &Path.back() + 1;
211 }
212 _LIBCPP_UNREACHABLE();
213 }
214
215 /// \brief Return a pointer to the first character in the currently lexed
216 /// element.
217 PosPtr getCurrentTokenStartPos() const noexcept {
218 switch (State) {
219 case PS_BeforeBegin:
220 case PS_InRootName:
221 return &Path.front();
222 case PS_InRootDir:
223 case PS_InFilenames:
224 case PS_InTrailingSep:
225 return &RawEntry.front();
226 case PS_AtEnd:
227 return &Path.back() + 1;
228 }
229 _LIBCPP_UNREACHABLE();
230 }
231
232 PosPtr consumeSeparator(PosPtr P, PosPtr End) const noexcept {
233 if (P == End || *P != '/')
234 return nullptr;
235 const int Inc = P < End ? 1 : -1;
236 P += Inc;
237 while (P != End && *P == '/')
238 P += Inc;
239 return P;
240 }
241
242 PosPtr consumeName(PosPtr P, PosPtr End) const noexcept {
243 if (P == End || *P == '/')
244 return nullptr;
245 const int Inc = P < End ? 1 : -1;
246 P += Inc;
247 while (P != End && *P != '/')
248 P += Inc;
249 return P;
250 }
251};
Eric Fiselierc7979582016-06-17 19:46:40 +0000252
Eric Fiselier0fdab5e2016-07-23 03:10:56 +0000253string_view_pair separate_filename(string_view_t const & s) {
Eric Fiselierc7979582016-06-17 19:46:40 +0000254 if (s == "." || s == ".." || s.empty()) return string_view_pair{s, ""};
255 auto pos = s.find_last_of('.');
Eric Fiselier0fdab5e2016-07-23 03:10:56 +0000256 if (pos == string_view_t::npos) return string_view_pair{s, string_view{}};
Eric Fiselierc7979582016-06-17 19:46:40 +0000257 return string_view_pair{s.substr(0, pos), s.substr(pos)};
258}
259
Eric Fiselier1467a192016-10-30 23:30:38 +0000260string_view_t createView(PosPtr S, PosPtr E) noexcept {
261 return {S, static_cast<size_t>(E - S) + 1};
Eric Fiselierc7979582016-06-17 19:46:40 +0000262}
263
264}} // namespace parser
265
Eric Fiselier1467a192016-10-30 23:30:38 +0000266_LIBCPP_BEGIN_NAMESPACE_EXPERIMENTAL_FILESYSTEM
Eric Fiselierc7979582016-06-17 19:46:40 +0000267
Eric Fiselier1467a192016-10-30 23:30:38 +0000268using parser::string_view_t;
269using parser::string_view_pair;
270using parser::PathParser;
271using parser::createView;
Eric Fiselierc7979582016-06-17 19:46:40 +0000272
Eric Fiselierc7979582016-06-17 19:46:40 +0000273///////////////////////////////////////////////////////////////////////////////
274// path definitions
275///////////////////////////////////////////////////////////////////////////////
276
Eric Fiselier1467a192016-10-30 23:30:38 +0000277constexpr path::value_type path::preferred_separator;
278
Eric Fiselierc7979582016-06-17 19:46:40 +0000279path & path::replace_extension(path const & replacement)
280{
281 path p = extension();
282 if (not p.empty()) {
283 __pn_.erase(__pn_.size() - p.native().size());
284 }
285 if (!replacement.empty()) {
286 if (replacement.native()[0] != '.') {
287 __pn_ += ".";
288 }
289 __pn_.append(replacement.__pn_);
290 }
291 return *this;
292}
293
294///////////////////////////////////////////////////////////////////////////////
295// path.decompose
296
Eric Fiselier0fdab5e2016-07-23 03:10:56 +0000297string_view_t path::__root_name() const
Eric Fiselierc7979582016-06-17 19:46:40 +0000298{
Eric Fiselier1467a192016-10-30 23:30:38 +0000299 auto PP = PathParser::CreateBegin(__pn_);
300 if (PP.State == PathParser::PS_InRootName)
301 return *PP;
302 return {};
Eric Fiselierc7979582016-06-17 19:46:40 +0000303}
304
Eric Fiselier0fdab5e2016-07-23 03:10:56 +0000305string_view_t path::__root_directory() const
Eric Fiselierc7979582016-06-17 19:46:40 +0000306{
Eric Fiselier1467a192016-10-30 23:30:38 +0000307 auto PP = PathParser::CreateBegin(__pn_);
308 if (PP.State == PathParser::PS_InRootName)
309 ++PP;
310 if (PP.State == PathParser::PS_InRootDir)
311 return *PP;
312 return {};
Eric Fiselierc7979582016-06-17 19:46:40 +0000313}
314
Eric Fiselier87ee8a02016-10-15 22:37:42 +0000315string_view_t path::__root_path_raw() const
316{
Eric Fiselier1467a192016-10-30 23:30:38 +0000317 auto PP = PathParser::CreateBegin(__pn_);
318 if (PP.State == PathParser::PS_InRootName) {
319 auto NextCh = PP.peek();
320 if (NextCh && *NextCh == '/') {
321 ++PP;
Eric Fiselier1467a192016-10-30 23:30:38 +0000322 return createView(__pn_.data(), &PP.RawEntry.back());
323 }
324 return PP.RawEntry;
325 }
326 if (PP.State == PathParser::PS_InRootDir)
327 return *PP;
Eric Fiselier87ee8a02016-10-15 22:37:42 +0000328 return {};
329}
330
Eric Fiselier0fdab5e2016-07-23 03:10:56 +0000331string_view_t path::__relative_path() const
Eric Fiselierc7979582016-06-17 19:46:40 +0000332{
Eric Fiselier1467a192016-10-30 23:30:38 +0000333 auto PP = PathParser::CreateBegin(__pn_);
334 while (PP.State <= PathParser::PS_InRootDir)
335 ++PP;
336 if (PP.State == PathParser::PS_AtEnd)
337 return {};
338 return createView(PP.RawEntry.data(), &__pn_.back());
Eric Fiselierc7979582016-06-17 19:46:40 +0000339}
340
Eric Fiselier0fdab5e2016-07-23 03:10:56 +0000341string_view_t path::__parent_path() const
Eric Fiselierc7979582016-06-17 19:46:40 +0000342{
Eric Fiselier1467a192016-10-30 23:30:38 +0000343 if (empty())
344 return {};
345 auto PP = PathParser::CreateEnd(__pn_);
346 --PP;
347 if (PP.RawEntry.data() == __pn_.data())
348 return {};
349 --PP;
350 return createView(__pn_.data(), &PP.RawEntry.back());
Eric Fiselierc7979582016-06-17 19:46:40 +0000351}
352
Eric Fiselier0fdab5e2016-07-23 03:10:56 +0000353string_view_t path::__filename() const
Eric Fiselierc7979582016-06-17 19:46:40 +0000354{
Eric Fiselier1467a192016-10-30 23:30:38 +0000355 if (empty()) return {};
356 return *(--PathParser::CreateEnd(__pn_));
Eric Fiselierc7979582016-06-17 19:46:40 +0000357}
358
Eric Fiselier0fdab5e2016-07-23 03:10:56 +0000359string_view_t path::__stem() const
Eric Fiselierc7979582016-06-17 19:46:40 +0000360{
361 return parser::separate_filename(__filename()).first;
362}
363
Eric Fiselier0fdab5e2016-07-23 03:10:56 +0000364string_view_t path::__extension() const
Eric Fiselierc7979582016-06-17 19:46:40 +0000365{
366 return parser::separate_filename(__filename()).second;
367}
368
369////////////////////////////////////////////////////////////////////////////
370// path.comparisons
Eric Fiselier0fdab5e2016-07-23 03:10:56 +0000371int path::__compare(string_view_t __s) const {
Eric Fiselier1467a192016-10-30 23:30:38 +0000372 auto PP = PathParser::CreateBegin(__pn_);
373 auto PP2 = PathParser::CreateBegin(__s);
374 while (PP && PP2) {
375 int res = (*PP).compare(*PP2);
Eric Fiselierc7979582016-06-17 19:46:40 +0000376 if (res != 0) return res;
Eric Fiselier1467a192016-10-30 23:30:38 +0000377 ++PP; ++PP2;
Eric Fiselierc7979582016-06-17 19:46:40 +0000378 }
Eric Fiselier1467a192016-10-30 23:30:38 +0000379 if (PP.State == PP2.State && PP.State == PathParser::PS_AtEnd)
Eric Fiselierc7979582016-06-17 19:46:40 +0000380 return 0;
Eric Fiselier1467a192016-10-30 23:30:38 +0000381 if (PP.State == PathParser::PS_AtEnd)
Eric Fiselierc7979582016-06-17 19:46:40 +0000382 return -1;
383 return 1;
384}
385
386////////////////////////////////////////////////////////////////////////////
387// path.nonmembers
Eric Fiselier1467a192016-10-30 23:30:38 +0000388size_t hash_value(const path& __p) noexcept {
389 auto PP = PathParser::CreateBegin(__p.native());
Eric Fiselier918f32f2016-12-02 23:38:31 +0000390 size_t hash_value = 0;
Eric Fiselierc7979582016-06-17 19:46:40 +0000391 std::hash<string_view> hasher;
Eric Fiselier1467a192016-10-30 23:30:38 +0000392 while (PP) {
Eric Fiselier918f32f2016-12-02 23:38:31 +0000393 hash_value = __hash_combine(hash_value, hasher(*PP));
Eric Fiselier1467a192016-10-30 23:30:38 +0000394 ++PP;
Eric Fiselierc7979582016-06-17 19:46:40 +0000395 }
Eric Fiselier918f32f2016-12-02 23:38:31 +0000396 return hash_value;
Eric Fiselierc7979582016-06-17 19:46:40 +0000397}
398
399////////////////////////////////////////////////////////////////////////////
400// path.itr
401path::iterator path::begin() const
402{
Eric Fiselier1467a192016-10-30 23:30:38 +0000403 auto PP = PathParser::CreateBegin(__pn_);
Eric Fiselierc7979582016-06-17 19:46:40 +0000404 iterator it;
405 it.__path_ptr_ = this;
Eric Fiselier1467a192016-10-30 23:30:38 +0000406 it.__state_ = PP.State;
407 it.__entry_ = PP.RawEntry;
408 it.__stashed_elem_.__assign_view(*PP);
Eric Fiselierc7979582016-06-17 19:46:40 +0000409 return it;
410}
411
412path::iterator path::end() const
413{
414 iterator it{};
Eric Fiselier1467a192016-10-30 23:30:38 +0000415 it.__state_ = PathParser::PS_AtEnd;
Eric Fiselierc7979582016-06-17 19:46:40 +0000416 it.__path_ptr_ = this;
Eric Fiselierc7979582016-06-17 19:46:40 +0000417 return it;
418}
419
420path::iterator& path::iterator::__increment() {
Eric Fiselier1467a192016-10-30 23:30:38 +0000421 static_assert(__at_end == PathParser::PS_AtEnd, "");
422 PathParser PP(__path_ptr_->native(), __entry_, __state_);
423 ++PP;
424 __state_ = PP.State;
425 __entry_ = PP.RawEntry;
426 __stashed_elem_.__assign_view(*PP);
Eric Fiselierc7979582016-06-17 19:46:40 +0000427 return *this;
428}
429
430path::iterator& path::iterator::__decrement() {
Eric Fiselier1467a192016-10-30 23:30:38 +0000431 PathParser PP(__path_ptr_->native(), __entry_, __state_);
432 --PP;
433 __state_ = PP.State;
434 __entry_ = PP.RawEntry;
435 __stashed_elem_.__assign_view(*PP);
Eric Fiselierc7979582016-06-17 19:46:40 +0000436 return *this;
437}
438
439_LIBCPP_END_NAMESPACE_EXPERIMENTAL_FILESYSTEM