blob: 70284ab654af40f8df040cabdbb5635e1a8486ea [file] [log] [blame]
Eric Fiselier6e9a6942016-06-17 19:46:40 +00001//===--------------------- filesystem/ops.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
10#include "experimental/filesystem"
11#include "iterator"
12#include "fstream"
Eric Fiselier6e9a6942016-06-17 19:46:40 +000013#include "random" /* for unique_path */
Eric Fiselier1e34c762018-04-02 23:03:41 +000014#include "string_view"
15#include "type_traits"
16#include "vector"
Eric Fiselier6e9a6942016-06-17 19:46:40 +000017#include "cstdlib"
18#include "climits"
19
Eric Fiselierb2e93372017-07-08 04:18:41 +000020#include "filesystem_time_helper.h"
21
Eric Fiselier6e9a6942016-06-17 19:46:40 +000022#include <unistd.h>
23#include <sys/stat.h>
24#include <sys/statvfs.h>
25#include <fcntl.h> /* values for fchmodat */
Nico Weber68b20ca2018-02-06 19:17:41 +000026
27#if (__APPLE__)
28#if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__)
29#if __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ >= 101300
30#define _LIBCXX_USE_UTIMENSAT
31#endif
32#elif defined(__ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__)
33#if __ENVIRONMENT_IPHONE_OS_VERSION_MIN_REQUIRED__ >= 110000
34#define _LIBCXX_USE_UTIMENSAT
35#endif
36#elif defined(__ENVIRONMENT_TV_OS_VERSION_MIN_REQUIRED__)
37#if __ENVIRONMENT_TV_OS_VERSION_MIN_REQUIRED__ >= 110000
38#define _LIBCXX_USE_UTIMENSAT
39#endif
40#elif defined(__ENVIRONMENT_WATCH_OS_VERSION_MIN_REQUIRED__)
41#if __ENVIRONMENT_WATCH_OS_VERSION_MIN_REQUIRED__ >= 40000
42#define _LIBCXX_USE_UTIMENSAT
43#endif
44#endif // __ENVIRONMENT_.*_VERSION_MIN_REQUIRED__
45#else
46// We can use the presence of UTIME_OMIT to detect platforms that provide
47// utimensat.
48#if defined(UTIME_OMIT)
49#define _LIBCXX_USE_UTIMENSAT
50#endif
51#endif // __APPLE__
52
53#if !defined(_LIBCXX_USE_UTIMENSAT)
Eric Fiselier6e9a6942016-06-17 19:46:40 +000054#include <sys/time.h> // for ::utimes as used in __last_write_time
55#endif
56
57_LIBCPP_BEGIN_NAMESPACE_EXPERIMENTAL_FILESYSTEM
58
59filesystem_error::~filesystem_error() {}
60
61
Eric Fiselier1e34c762018-04-02 23:03:41 +000062namespace { namespace parser
63{
64
65using string_view_t = path::__string_view;
66using string_view_pair = pair<string_view_t, string_view_t>;
67using PosPtr = path::value_type const*;
68
69struct PathParser {
70 enum ParserState : unsigned char {
71 // Zero is a special sentinel value used by default constructed iterators.
72 PS_BeforeBegin = 1,
73 PS_InRootName,
74 PS_InRootDir,
75 PS_InFilenames,
76 PS_InTrailingSep,
77 PS_AtEnd
78 };
79
80 const string_view_t Path;
81 string_view_t RawEntry;
82 ParserState State;
83
84private:
85 PathParser(string_view_t P, ParserState State) noexcept
86 : Path(P), State(State) {}
87
88public:
89 PathParser(string_view_t P, string_view_t E, unsigned char S)
90 : Path(P), RawEntry(E), State(static_cast<ParserState>(S)) {
91 // S cannot be '0' or PS_BeforeBegin.
92 }
93
94 static PathParser CreateBegin(string_view_t P) noexcept {
95 PathParser PP(P, PS_BeforeBegin);
96 PP.increment();
97 return PP;
98 }
99
100 static PathParser CreateEnd(string_view_t P) noexcept {
101 PathParser PP(P, PS_AtEnd);
102 return PP;
103 }
104
105 PosPtr peek() const noexcept {
106 auto TkEnd = getNextTokenStartPos();
107 auto End = getAfterBack();
108 return TkEnd == End ? nullptr : TkEnd;
109 }
110
111 void increment() noexcept {
112 const PosPtr End = getAfterBack();
113 const PosPtr Start = getNextTokenStartPos();
114 if (Start == End)
115 return makeState(PS_AtEnd);
116
117 switch (State) {
118 case PS_BeforeBegin: {
119 PosPtr TkEnd = consumeSeparator(Start, End);
120 if (TkEnd)
121 return makeState(PS_InRootDir, Start, TkEnd);
122 else
123 return makeState(PS_InFilenames, Start, consumeName(Start, End));
124 }
125 case PS_InRootDir:
126 return makeState(PS_InFilenames, Start, consumeName(Start, End));
127
128 case PS_InFilenames: {
129 PosPtr SepEnd = consumeSeparator(Start, End);
130 if (SepEnd != End) {
131 PosPtr TkEnd = consumeName(SepEnd, End);
132 if (TkEnd)
133 return makeState(PS_InFilenames, SepEnd, TkEnd);
134 }
135 return makeState(PS_InTrailingSep, Start, SepEnd);
136 }
137
138 case PS_InTrailingSep:
139 return makeState(PS_AtEnd);
140
141 case PS_InRootName:
142 case PS_AtEnd:
143 _LIBCPP_UNREACHABLE();
144 }
145 }
146
147 void decrement() noexcept {
148 const PosPtr REnd = getBeforeFront();
149 const PosPtr RStart = getCurrentTokenStartPos() - 1;
150 if (RStart == REnd) // we're decrementing the begin
151 return makeState(PS_BeforeBegin);
152
153 switch (State) {
154 case PS_AtEnd: {
155 // Try to consume a trailing separator or root directory first.
156 if (PosPtr SepEnd = consumeSeparator(RStart, REnd)) {
157 if (SepEnd == REnd)
158 return makeState(PS_InRootDir, Path.data(), RStart + 1);
159 return makeState(PS_InTrailingSep, SepEnd + 1, RStart + 1);
160 } else {
161 PosPtr TkStart = consumeName(RStart, REnd);
162 return makeState(PS_InFilenames, TkStart + 1, RStart + 1);
163 }
164 }
165 case PS_InTrailingSep:
166 return makeState(PS_InFilenames, consumeName(RStart, REnd) + 1, RStart + 1);
167 case PS_InFilenames: {
168 PosPtr SepEnd = consumeSeparator(RStart, REnd);
169 if (SepEnd == REnd)
170 return makeState(PS_InRootDir, Path.data(), RStart + 1);
171 PosPtr TkEnd = consumeName(SepEnd, REnd);
172 return makeState(PS_InFilenames, TkEnd + 1, SepEnd + 1);
173 }
174 case PS_InRootDir:
175 // return makeState(PS_InRootName, Path.data(), RStart + 1);
176 case PS_InRootName:
177 case PS_BeforeBegin:
178 _LIBCPP_UNREACHABLE();
179 }
180 }
181
182 /// \brief Return a view with the "preferred representation" of the current
183 /// element. For example trailing separators are represented as a '.'
184 string_view_t operator*() const noexcept {
185 switch (State) {
186 case PS_BeforeBegin:
187 case PS_AtEnd:
188 return "";
189 case PS_InRootDir:
190 return "/";
191 case PS_InTrailingSep:
192 return "";
193 case PS_InRootName:
194 case PS_InFilenames:
195 return RawEntry;
196 }
197 _LIBCPP_UNREACHABLE();
198 }
199
200 explicit operator bool() const noexcept {
201 return State != PS_BeforeBegin && State != PS_AtEnd;
202 }
203
204 PathParser& operator++() noexcept {
205 increment();
206 return *this;
207 }
208
209 PathParser& operator--() noexcept {
210 decrement();
211 return *this;
212 }
213
214 bool inRootPath() const noexcept {
215 return State == PS_InRootDir || State == PS_InRootName;
216 }
217
218private:
219 void makeState(ParserState NewState, PosPtr Start, PosPtr End) noexcept {
220 State = NewState;
221 RawEntry = string_view_t(Start, End - Start);
222 }
223 void makeState(ParserState NewState) noexcept {
224 State = NewState;
225 RawEntry = {};
226 }
227
228 PosPtr getAfterBack() const noexcept {
229 return Path.data() + Path.size();
230 }
231
232 PosPtr getBeforeFront() const noexcept {
233 return Path.data() - 1;
234 }
235
236 /// \brief Return a pointer to the first character after the currently
237 /// lexed element.
238 PosPtr getNextTokenStartPos() const noexcept {
239 switch (State) {
240 case PS_BeforeBegin:
241 return Path.data();
242 case PS_InRootName:
243 case PS_InRootDir:
244 case PS_InFilenames:
245 return &RawEntry.back() + 1;
246 case PS_InTrailingSep:
247 case PS_AtEnd:
248 return getAfterBack();
249 }
250 _LIBCPP_UNREACHABLE();
251 }
252
253 /// \brief Return a pointer to the first character in the currently lexed
254 /// element.
255 PosPtr getCurrentTokenStartPos() const noexcept {
256 switch (State) {
257 case PS_BeforeBegin:
258 case PS_InRootName:
259 return &Path.front();
260 case PS_InRootDir:
261 case PS_InFilenames:
262 case PS_InTrailingSep:
263 return &RawEntry.front();
264 case PS_AtEnd:
265 return &Path.back() + 1;
266 }
267 _LIBCPP_UNREACHABLE();
268 }
269
270 PosPtr consumeSeparator(PosPtr P, PosPtr End) const noexcept {
271 if (P == End || *P != '/')
272 return nullptr;
273 const int Inc = P < End ? 1 : -1;
274 P += Inc;
275 while (P != End && *P == '/')
276 P += Inc;
277 return P;
278 }
279
280 PosPtr consumeName(PosPtr P, PosPtr End) const noexcept {
281 if (P == End || *P == '/')
282 return nullptr;
283 const int Inc = P < End ? 1 : -1;
284 P += Inc;
285 while (P != End && *P != '/')
286 P += Inc;
287 return P;
288 }
289};
290
291string_view_pair separate_filename(string_view_t const & s) {
292 if (s == "." || s == ".." || s.empty()) return string_view_pair{s, ""};
293 auto pos = s.find_last_of('.');
294 if (pos == string_view_t::npos || pos == 0)
295 return string_view_pair{s, string_view_t{}};
296 return string_view_pair{s.substr(0, pos), s.substr(pos)};
297}
298
299string_view_t createView(PosPtr S, PosPtr E) noexcept {
300 return {S, static_cast<size_t>(E - S) + 1};
301}
302
303}} // namespace parser
304
305
Eric Fiselier6e9a6942016-06-17 19:46:40 +0000306// POSIX HELPERS
307
308namespace detail { namespace {
309
310using value_type = path::value_type;
311using string_type = path::string_type;
312
Eric Fiselier6e9a6942016-06-17 19:46:40 +0000313inline std::error_code capture_errno() {
Saleem Abdulrasool166d27e2017-02-07 02:46:59 +0000314 _LIBCPP_ASSERT(errno, "Expected errno to be non-zero");
315 return std::error_code(errno, std::generic_category());
Eric Fiselier6e9a6942016-06-17 19:46:40 +0000316}
317
318void set_or_throw(std::error_code const& m_ec, std::error_code* ec,
319 const char* msg, path const& p = {}, path const& p2 = {})
320{
321 if (ec) {
322 *ec = m_ec;
323 } else {
324 string msg_s("std::experimental::filesystem::");
325 msg_s += msg;
Marshall Clowe7acb0e2016-08-25 17:47:09 +0000326 __throw_filesystem_error(msg_s, p, p2, m_ec);
Eric Fiselier6e9a6942016-06-17 19:46:40 +0000327 }
328}
329
330void set_or_throw(std::error_code* ec, const char* msg,
331 path const& p = {}, path const& p2 = {})
332{
333 return set_or_throw(capture_errno(), ec, msg, p, p2);
334}
335
336perms posix_get_perms(const struct ::stat & st) noexcept {
337 return static_cast<perms>(st.st_mode) & perms::mask;
338}
339
340::mode_t posix_convert_perms(perms prms) {
341 return static_cast< ::mode_t>(prms & perms::mask);
342}
343
344file_status create_file_status(std::error_code& m_ec, path const& p,
345 struct ::stat& path_stat,
346 std::error_code* ec)
347{
348 if (ec) *ec = m_ec;
349 if (m_ec && (m_ec.value() == ENOENT || m_ec.value() == ENOTDIR)) {
350 return file_status(file_type::not_found);
351 }
352 else if (m_ec) {
353 set_or_throw(m_ec, ec, "posix_stat", p);
354 return file_status(file_type::none);
355 }
356 // else
357
358 file_status fs_tmp;
359 auto const mode = path_stat.st_mode;
360 if (S_ISLNK(mode)) fs_tmp.type(file_type::symlink);
361 else if (S_ISREG(mode)) fs_tmp.type(file_type::regular);
362 else if (S_ISDIR(mode)) fs_tmp.type(file_type::directory);
363 else if (S_ISBLK(mode)) fs_tmp.type(file_type::block);
364 else if (S_ISCHR(mode)) fs_tmp.type(file_type::character);
365 else if (S_ISFIFO(mode)) fs_tmp.type(file_type::fifo);
366 else if (S_ISSOCK(mode)) fs_tmp.type(file_type::socket);
367 else fs_tmp.type(file_type::unknown);
368
369 fs_tmp.permissions(detail::posix_get_perms(path_stat));
370 return fs_tmp;
371}
372
373file_status posix_stat(path const & p, struct ::stat& path_stat,
374 std::error_code* ec)
375{
376 std::error_code m_ec;
377 if (::stat(p.c_str(), &path_stat) == -1)
378 m_ec = detail::capture_errno();
379 return create_file_status(m_ec, p, path_stat, ec);
380}
381
382file_status posix_stat(path const & p, std::error_code* ec) {
383 struct ::stat path_stat;
384 return posix_stat(p, path_stat, ec);
385}
386
387file_status posix_lstat(path const & p, struct ::stat & path_stat,
388 std::error_code* ec)
389{
390 std::error_code m_ec;
391 if (::lstat(p.c_str(), &path_stat) == -1)
392 m_ec = detail::capture_errno();
393 return create_file_status(m_ec, p, path_stat, ec);
394}
395
396file_status posix_lstat(path const & p, std::error_code* ec) {
397 struct ::stat path_stat;
398 return posix_lstat(p, path_stat, ec);
399}
400
401bool stat_equivalent(struct ::stat& st1, struct ::stat& st2) {
402 return (st1.st_dev == st2.st_dev && st1.st_ino == st2.st_ino);
403}
404
405// DETAIL::MISC
406
407
408bool copy_file_impl(const path& from, const path& to, perms from_perms,
409 std::error_code *ec)
410{
411 std::ifstream in(from.c_str(), std::ios::binary);
412 std::ofstream out(to.c_str(), std::ios::binary);
413
414 if (in.good() && out.good()) {
415 using InIt = std::istreambuf_iterator<char>;
416 using OutIt = std::ostreambuf_iterator<char>;
417 InIt bin(in);
418 InIt ein;
419 OutIt bout(out);
420 std::copy(bin, ein, bout);
421 }
422 if (out.fail() || in.fail()) {
423 set_or_throw(make_error_code(errc::operation_not_permitted),
424 ec, "copy_file", from, to);
425 return false;
426 }
Eric Fiselierf2c93732018-03-26 06:23:55 +0000427 __permissions(to, from_perms, perm_options::replace, ec);
Eric Fiselier6e9a6942016-06-17 19:46:40 +0000428 // TODO what if permissions fails?
429 return true;
430}
431
432}} // end namespace detail
433
434using detail::set_or_throw;
Eric Fiselier1e34c762018-04-02 23:03:41 +0000435using parser::string_view_t;
436using parser::PathParser;
437using parser::createView;
Eric Fiselier6e9a6942016-06-17 19:46:40 +0000438
Eric Fiselier1e34c762018-04-02 23:03:41 +0000439static path __do_absolute(const path& p, path *cwd, std::error_code *ec) {
440 if (ec) ec->clear();
441 if (p.is_absolute())
442 return p;
443 *cwd = __current_path(ec);
444 if (ec && *ec)
445 return {};
446 return (*cwd) / p;
447}
448
449path __absolute(const path& p, std::error_code *ec) {
450 path cwd;
451 return __do_absolute(p, &cwd, ec);
452}
453
454path __canonical(path const & orig_p, std::error_code *ec)
Eric Fiselier6e9a6942016-06-17 19:46:40 +0000455{
Eric Fiselier1e34c762018-04-02 23:03:41 +0000456 path cwd;
457 path p = __do_absolute(orig_p, &cwd, ec);
Eric Fiselier6e9a6942016-06-17 19:46:40 +0000458 char buff[PATH_MAX + 1];
459 char *ret;
460 if ((ret = ::realpath(p.c_str(), buff)) == nullptr) {
Eric Fiselier1e34c762018-04-02 23:03:41 +0000461 set_or_throw(ec, "canonical", orig_p, cwd);
Eric Fiselier6e9a6942016-06-17 19:46:40 +0000462 return {};
463 }
464 if (ec) ec->clear();
465 return {ret};
466}
467
468void __copy(const path& from, const path& to, copy_options options,
469 std::error_code *ec)
470{
471 const bool sym_status = bool(options &
472 (copy_options::create_symlinks | copy_options::skip_symlinks));
473
474 const bool sym_status2 = bool(options &
475 copy_options::copy_symlinks);
476
Marshall Clow4aee06b2017-08-02 20:29:26 +0000477 std::error_code m_ec1;
Eric Fiselier6e9a6942016-06-17 19:46:40 +0000478 struct ::stat f_st = {};
479 const file_status f = sym_status || sym_status2
Marshall Clow4aee06b2017-08-02 20:29:26 +0000480 ? detail::posix_lstat(from, f_st, &m_ec1)
481 : detail::posix_stat(from, f_st, &m_ec1);
482 if (m_ec1)
483 return set_or_throw(m_ec1, ec, "copy", from, to);
Eric Fiselier6e9a6942016-06-17 19:46:40 +0000484
485 struct ::stat t_st = {};
Marshall Clow4aee06b2017-08-02 20:29:26 +0000486 const file_status t = sym_status ? detail::posix_lstat(to, t_st, &m_ec1)
487 : detail::posix_stat(to, t_st, &m_ec1);
Eric Fiselier6e9a6942016-06-17 19:46:40 +0000488
489 if (not status_known(t))
Marshall Clow4aee06b2017-08-02 20:29:26 +0000490 return set_or_throw(m_ec1, ec, "copy", from, to);
Eric Fiselier6e9a6942016-06-17 19:46:40 +0000491
492 if (!exists(f) || is_other(f) || is_other(t)
493 || (is_directory(f) && is_regular_file(t))
494 || detail::stat_equivalent(f_st, t_st))
495 {
496 return set_or_throw(make_error_code(errc::function_not_supported),
497 ec, "copy", from, to);
498 }
499
500 if (ec) ec->clear();
501
502 if (is_symlink(f)) {
503 if (bool(copy_options::skip_symlinks & options)) {
504 // do nothing
505 } else if (not exists(t)) {
506 __copy_symlink(from, to, ec);
507 } else {
508 set_or_throw(make_error_code(errc::file_exists),
509 ec, "copy", from, to);
510 }
511 return;
512 }
513 else if (is_regular_file(f)) {
514 if (bool(copy_options::directories_only & options)) {
515 // do nothing
516 }
517 else if (bool(copy_options::create_symlinks & options)) {
518 __create_symlink(from, to, ec);
519 }
520 else if (bool(copy_options::create_hard_links & options)) {
521 __create_hard_link(from, to, ec);
522 }
523 else if (is_directory(t)) {
524 __copy_file(from, to / from.filename(), options, ec);
525 } else {
526 __copy_file(from, to, options, ec);
527 }
528 return;
529 }
Eric Fiselier451f34d2016-10-16 00:29:22 +0000530 else if (is_directory(f) && bool(copy_options::create_symlinks & options)) {
531 return set_or_throw(make_error_code(errc::is_a_directory), ec, "copy");
532 }
Eric Fiselier508f2082016-10-11 22:18:09 +0000533 else if (is_directory(f) && (bool(copy_options::recursive & options) ||
534 copy_options::none == options)) {
Eric Fiselier6e9a6942016-06-17 19:46:40 +0000535
536 if (!exists(t)) {
537 // create directory to with attributes from 'from'.
538 __create_directory(to, from, ec);
539 if (ec && *ec) { return; }
540 }
541 directory_iterator it = ec ? directory_iterator(from, *ec)
542 : directory_iterator(from);
543 if (ec && *ec) { return; }
Marshall Clow4aee06b2017-08-02 20:29:26 +0000544 std::error_code m_ec2;
545 for (; it != directory_iterator(); it.increment(m_ec2)) {
546 if (m_ec2) return set_or_throw(m_ec2, ec, "copy", from, to);
Eric Fiselier6e9a6942016-06-17 19:46:40 +0000547 __copy(it->path(), to / it->path().filename(),
548 options | copy_options::__in_recursive_copy, ec);
549 if (ec && *ec) { return; }
550 }
551 }
552}
553
554
555bool __copy_file(const path& from, const path& to, copy_options options,
556 std::error_code *ec)
557{
Eric Fiselieraf1fd7c2018-02-04 02:43:32 +0000558 using StatT = struct ::stat;
559 if (ec)
560 ec->clear();
Eric Fiselier6e9a6942016-06-17 19:46:40 +0000561
562 std::error_code m_ec;
Eric Fiselieraf1fd7c2018-02-04 02:43:32 +0000563 StatT from_stat;
564 auto from_st = detail::posix_stat(from, from_stat, &m_ec);
Eric Fiselier6e9a6942016-06-17 19:46:40 +0000565 if (not is_regular_file(from_st)) {
Eric Fiselieraf1fd7c2018-02-04 02:43:32 +0000566 if (not m_ec)
567 m_ec = make_error_code(errc::not_supported);
568 set_or_throw(m_ec, ec, "copy_file", from, to);
569 return false;
Eric Fiselier6e9a6942016-06-17 19:46:40 +0000570 }
571
Eric Fiselieraf1fd7c2018-02-04 02:43:32 +0000572 StatT to_stat;
573 auto to_st = detail::posix_stat(to, to_stat, &m_ec);
Eric Fiselier6e9a6942016-06-17 19:46:40 +0000574 if (!status_known(to_st)) {
575 set_or_throw(m_ec, ec, "copy_file", from, to);
576 return false;
577 }
578
579 const bool to_exists = exists(to_st);
Eric Fiselier1e1bbc72016-10-16 00:47:59 +0000580 if (to_exists && !is_regular_file(to_st)) {
581 set_or_throw(make_error_code(errc::not_supported), ec, "copy_file", from, to);
582 return false;
583 }
Eric Fiselieraf1fd7c2018-02-04 02:43:32 +0000584 if (to_exists && detail::stat_equivalent(from_stat, to_stat)) {
585 set_or_throw(make_error_code(errc::file_exists), ec, "copy_file", from,
586 to);
587 return false;
588 }
Eric Fiselier6e9a6942016-06-17 19:46:40 +0000589 if (to_exists && bool(copy_options::skip_existing & options)) {
590 return false;
591 }
592 else if (to_exists && bool(copy_options::update_existing & options)) {
593 auto from_time = __last_write_time(from, ec);
594 if (ec && *ec) { return false; }
595 auto to_time = __last_write_time(to, ec);
596 if (ec && *ec) { return false; }
597 if (from_time <= to_time) {
598 return false;
599 }
600 return detail::copy_file_impl(from, to, from_st.permissions(), ec);
601 }
602 else if (!to_exists || bool(copy_options::overwrite_existing & options)) {
603 return detail::copy_file_impl(from, to, from_st.permissions(), ec);
604 }
605 else {
Eric Fiselieraf1fd7c2018-02-04 02:43:32 +0000606 set_or_throw(make_error_code(errc::file_exists), ec, "copy_file", from,
607 to);
608 return false;
Eric Fiselier6e9a6942016-06-17 19:46:40 +0000609 }
Eric Fiselier1e1bbc72016-10-16 00:47:59 +0000610
611 _LIBCPP_UNREACHABLE();
Eric Fiselier6e9a6942016-06-17 19:46:40 +0000612}
613
614void __copy_symlink(const path& existing_symlink, const path& new_symlink,
615 std::error_code *ec)
616{
617 const path real_path(__read_symlink(existing_symlink, ec));
618 if (ec && *ec) { return; }
619 // NOTE: proposal says you should detect if you should call
620 // create_symlink or create_directory_symlink. I don't think this
621 // is needed with POSIX
622 __create_symlink(real_path, new_symlink, ec);
623}
624
625
626bool __create_directories(const path& p, std::error_code *ec)
627{
628 std::error_code m_ec;
629 auto const st = detail::posix_stat(p, &m_ec);
630 if (!status_known(st)) {
631 set_or_throw(m_ec, ec, "create_directories", p);
632 return false;
633 }
634 else if (is_directory(st)) {
635 if (ec) ec->clear();
636 return false;
637 }
638 else if (exists(st)) {
639 set_or_throw(make_error_code(errc::file_exists),
640 ec, "create_directories", p);
641 return false;
642 }
643
644 const path parent = p.parent_path();
645 if (!parent.empty()) {
646 const file_status parent_st = status(parent, m_ec);
647 if (not status_known(parent_st)) {
648 set_or_throw(m_ec, ec, "create_directories", p);
649 return false;
650 }
651 if (not exists(parent_st)) {
652 __create_directories(parent, ec);
653 if (ec && *ec) { return false; }
654 }
655 }
656 return __create_directory(p, ec);
657}
658
659bool __create_directory(const path& p, std::error_code *ec)
660{
661 if (ec) ec->clear();
662 if (::mkdir(p.c_str(), static_cast<int>(perms::all)) == 0)
663 return true;
664 if (errno != EEXIST || !is_directory(p))
665 set_or_throw(ec, "create_directory", p);
666 return false;
667}
668
669bool __create_directory(path const & p, path const & attributes,
670 std::error_code *ec)
671{
672 struct ::stat attr_stat;
673 std::error_code mec;
674 auto st = detail::posix_stat(attributes, attr_stat, &mec);
675 if (!status_known(st)) {
676 set_or_throw(mec, ec, "create_directory", p, attributes);
677 return false;
678 }
679 if (ec) ec->clear();
680 if (::mkdir(p.c_str(), attr_stat.st_mode) == 0)
681 return true;
682 if (errno != EEXIST || !is_directory(p))
683 set_or_throw(ec, "create_directory", p, attributes);
684 return false;
685}
686
687void __create_directory_symlink(path const & from, path const & to,
688 std::error_code *ec){
689 if (::symlink(from.c_str(), to.c_str()) != 0)
690 set_or_throw(ec, "create_directory_symlink", from, to);
691 else if (ec)
692 ec->clear();
693}
694
695void __create_hard_link(const path& from, const path& to, std::error_code *ec){
696 if (::link(from.c_str(), to.c_str()) == -1)
697 set_or_throw(ec, "create_hard_link", from, to);
698 else if (ec)
699 ec->clear();
700}
701
702void __create_symlink(path const & from, path const & to, std::error_code *ec) {
703
704 if (::symlink(from.c_str(), to.c_str()) == -1)
705 set_or_throw(ec, "create_symlink", from, to);
706 else if (ec)
707 ec->clear();
708}
709
710path __current_path(std::error_code *ec) {
711 auto size = ::pathconf(".", _PC_PATH_MAX);
712 _LIBCPP_ASSERT(size >= 0, "pathconf returned a 0 as max size");
713
714 auto buff = std::unique_ptr<char[]>(new char[size + 1]);
715 char* ret;
716 if ((ret = ::getcwd(buff.get(), static_cast<size_t>(size))) == nullptr) {
717 set_or_throw(ec, "current_path");
718 return {};
719 }
720 if (ec) ec->clear();
721 return {buff.get()};
722}
723
724void __current_path(const path& p, std::error_code *ec) {
725 if (::chdir(p.c_str()) == -1)
726 set_or_throw(ec, "current_path", p);
727 else if (ec)
728 ec->clear();
729}
730
731bool __equivalent(const path& p1, const path& p2, std::error_code *ec)
732{
Eric Fiselier3288eac2017-07-05 03:37:05 +0000733 auto make_unsupported_error = [&]() {
734 set_or_throw(make_error_code(errc::not_supported), ec,
735 "equivalent", p1, p2);
736 return false;
737 };
Eric Fiselier6e9a6942016-06-17 19:46:40 +0000738 std::error_code ec1, ec2;
739 struct ::stat st1 = {};
740 struct ::stat st2 = {};
741 auto s1 = detail::posix_stat(p1.native(), st1, &ec1);
Eric Fiselier3288eac2017-07-05 03:37:05 +0000742 if (!exists(s1))
743 return make_unsupported_error();
Eric Fiselier6e9a6942016-06-17 19:46:40 +0000744 auto s2 = detail::posix_stat(p2.native(), st2, &ec2);
Eric Fiselier3288eac2017-07-05 03:37:05 +0000745 if (!exists(s2))
746 return make_unsupported_error();
Eric Fiselier6e9a6942016-06-17 19:46:40 +0000747 if (ec) ec->clear();
Eric Fiselieraf1fd7c2018-02-04 02:43:32 +0000748 return detail::stat_equivalent(st1, st2);
Eric Fiselier6e9a6942016-06-17 19:46:40 +0000749}
750
751
752std::uintmax_t __file_size(const path& p, std::error_code *ec)
753{
754 std::error_code m_ec;
755 struct ::stat st;
756 file_status fst = detail::posix_stat(p, st, &m_ec);
757 if (!exists(fst) || !is_regular_file(fst)) {
758 if (!m_ec)
759 m_ec = make_error_code(errc::not_supported);
760 set_or_throw(m_ec, ec, "file_size", p);
761 return static_cast<uintmax_t>(-1);
762 }
763 // is_regular_file(p) == true
764 if (ec) ec->clear();
765 return static_cast<std::uintmax_t>(st.st_size);
766}
767
768std::uintmax_t __hard_link_count(const path& p, std::error_code *ec)
769{
770 std::error_code m_ec;
771 struct ::stat st;
772 detail::posix_stat(p, st, &m_ec);
773 if (m_ec) {
774 set_or_throw(m_ec, ec, "hard_link_count", p);
775 return static_cast<std::uintmax_t>(-1);
776 }
777 if (ec) ec->clear();
778 return static_cast<std::uintmax_t>(st.st_nlink);
779}
780
781
782bool __fs_is_empty(const path& p, std::error_code *ec)
783{
784 if (ec) ec->clear();
785 std::error_code m_ec;
786 struct ::stat pst;
787 auto st = detail::posix_stat(p, pst, &m_ec);
Eric Fiselier25dc5bd2016-10-15 23:05:04 +0000788 if (m_ec) {
789 set_or_throw(m_ec, ec, "is_empty", p);
790 return false;
791 }
792 else if (!is_directory(st) && !is_regular_file(st)) {
793 m_ec = make_error_code(errc::not_supported);
794 set_or_throw(m_ec, ec, "is_empty");
795 return false;
796 }
797 else if (is_directory(st)) {
798 auto it = ec ? directory_iterator(p, *ec) : directory_iterator(p);
799 if (ec && *ec)
800 return false;
801 return it == directory_iterator{};
802 }
Eric Fiselier6e9a6942016-06-17 19:46:40 +0000803 else if (is_regular_file(st))
804 return static_cast<std::uintmax_t>(pst.st_size) == 0;
Eric Fiselier25dc5bd2016-10-15 23:05:04 +0000805
806 _LIBCPP_UNREACHABLE();
Eric Fiselier6e9a6942016-06-17 19:46:40 +0000807}
808
809
Eric Fiselier70f7afe2016-06-19 02:04:49 +0000810namespace detail { namespace {
811
Eric Fiselier50ca6f72017-05-05 20:39:03 +0000812using TimeSpec = struct timespec;
813using StatT = struct stat;
Eric Fiselierfd0e7612016-10-10 04:22:58 +0000814
815#if defined(__APPLE__)
816TimeSpec extract_mtime(StatT const& st) { return st.st_mtimespec; }
Don Hinton14d7b692017-12-25 05:33:42 +0000817__attribute__((unused)) // Suppress warning
Eric Fiselierfd0e7612016-10-10 04:22:58 +0000818TimeSpec extract_atime(StatT const& st) { return st.st_atimespec; }
819#else
820TimeSpec extract_mtime(StatT const& st) { return st.st_mtim; }
821__attribute__((unused)) // Suppress warning
822TimeSpec extract_atime(StatT const& st) { return st.st_atim; }
823#endif
824
Eric Fiselier70f7afe2016-06-19 02:04:49 +0000825}} // end namespace detail
826
Eric Fiselierb2e93372017-07-08 04:18:41 +0000827using FSTime = fs_time_util<file_time_type, time_t, struct timespec>;
828
Eric Fiselier6e9a6942016-06-17 19:46:40 +0000829file_time_type __last_write_time(const path& p, std::error_code *ec)
830{
Eric Fiselierd5fc5ca2016-09-28 21:16:58 +0000831 using namespace ::std::chrono;
Eric Fiselier6e9a6942016-06-17 19:46:40 +0000832 std::error_code m_ec;
833 struct ::stat st;
834 detail::posix_stat(p, st, &m_ec);
835 if (m_ec) {
836 set_or_throw(m_ec, ec, "last_write_time", p);
837 return file_time_type::min();
838 }
Eric Fiselierc0f860c2016-09-29 01:01:26 +0000839 if (ec) ec->clear();
840 auto ts = detail::extract_mtime(st);
Eric Fiselierb2e93372017-07-08 04:18:41 +0000841 if (!FSTime::is_representable(ts)) {
Eric Fiselierd5fc5ca2016-09-28 21:16:58 +0000842 set_or_throw(error_code(EOVERFLOW, generic_category()), ec,
843 "last_write_time", p);
844 return file_time_type::min();
845 }
Eric Fiselierb2e93372017-07-08 04:18:41 +0000846 return FSTime::convert_timespec(ts);
Eric Fiselier6e9a6942016-06-17 19:46:40 +0000847}
848
Eric Fiselier6e9a6942016-06-17 19:46:40 +0000849void __last_write_time(const path& p, file_time_type new_time,
850 std::error_code *ec)
851{
852 using namespace std::chrono;
853 std::error_code m_ec;
854
Nico Weber68b20ca2018-02-06 19:17:41 +0000855#if !defined(_LIBCXX_USE_UTIMENSAT)
Eric Fiselier6e9a6942016-06-17 19:46:40 +0000856 // This implementation has a race condition between determining the
857 // last access time and attempting to set it to the same value using
858 // ::utimes
Eric Fiselier6e9a6942016-06-17 19:46:40 +0000859 struct ::stat st;
860 file_status fst = detail::posix_stat(p, st, &m_ec);
861 if (m_ec && !status_known(fst)) {
862 set_or_throw(m_ec, ec, "last_write_time", p);
863 return;
864 }
Eric Fiselierd5fc5ca2016-09-28 21:16:58 +0000865 auto atime = detail::extract_atime(st);
Eric Fiselier6e9a6942016-06-17 19:46:40 +0000866 struct ::timeval tbuf[2];
Eric Fiselierd5fc5ca2016-09-28 21:16:58 +0000867 tbuf[0].tv_sec = atime.tv_sec;
868 tbuf[0].tv_usec = duration_cast<microseconds>(nanoseconds(atime.tv_nsec)).count();
Eric Fiselierb2e93372017-07-08 04:18:41 +0000869 const bool overflowed = !FSTime::set_times_checked<microseconds>(
Eric Fiselier70f7afe2016-06-19 02:04:49 +0000870 &tbuf[1].tv_sec, &tbuf[1].tv_usec, new_time);
871
872 if (overflowed) {
873 set_or_throw(make_error_code(errc::invalid_argument), ec,
874 "last_write_time", p);
875 return;
876 }
Eric Fiselier6e9a6942016-06-17 19:46:40 +0000877 if (::utimes(p.c_str(), tbuf) == -1) {
878 m_ec = detail::capture_errno();
879 }
880#else
Eric Fiselier6e9a6942016-06-17 19:46:40 +0000881 struct ::timespec tbuf[2];
882 tbuf[0].tv_sec = 0;
883 tbuf[0].tv_nsec = UTIME_OMIT;
Eric Fiselier70f7afe2016-06-19 02:04:49 +0000884
Eric Fiselierb2e93372017-07-08 04:18:41 +0000885 const bool overflowed = !FSTime::set_times_checked<nanoseconds>(
Eric Fiselier70f7afe2016-06-19 02:04:49 +0000886 &tbuf[1].tv_sec, &tbuf[1].tv_nsec, new_time);
887 if (overflowed) {
888 set_or_throw(make_error_code(errc::invalid_argument),
889 ec, "last_write_time", p);
890 return;
891 }
Eric Fiselier6e9a6942016-06-17 19:46:40 +0000892 if (::utimensat(AT_FDCWD, p.c_str(), tbuf, 0) == -1) {
893 m_ec = detail::capture_errno();
894 }
895#endif
896 if (m_ec)
897 set_or_throw(m_ec, ec, "last_write_time", p);
898 else if (ec)
899 ec->clear();
900}
901
902
Eric Fiselierf2c93732018-03-26 06:23:55 +0000903void __permissions(const path& p, perms prms, perm_options opts,
904 std::error_code *ec)
Eric Fiselier6e9a6942016-06-17 19:46:40 +0000905{
Eric Fiselierf2c93732018-03-26 06:23:55 +0000906 auto has_opt = [&](perm_options o) { return bool(o & opts); };
907 const bool resolve_symlinks = !has_opt(perm_options::nofollow);
908 const bool add_perms = has_opt(perm_options::add);
909 const bool remove_perms = has_opt(perm_options::remove);
910 _LIBCPP_ASSERT(
911 (add_perms + remove_perms + has_opt(perm_options::replace)) == 1,
912 "One and only one of the perm_options constants replace, add, or remove "
913 "is present in opts");
Eric Fiselier6e9a6942016-06-17 19:46:40 +0000914
Eric Fiselier55e084d2016-06-22 07:57:38 +0000915 bool set_sym_perms = false;
916 prms &= perms::mask;
917 if (!resolve_symlinks || (add_perms || remove_perms)) {
918 std::error_code m_ec;
919 file_status st = resolve_symlinks ? detail::posix_stat(p, &m_ec)
920 : detail::posix_lstat(p, &m_ec);
921 set_sym_perms = is_symlink(st);
Eric Fiselier6e9a6942016-06-17 19:46:40 +0000922 if (m_ec) return set_or_throw(m_ec, ec, "permissions", p);
Eric Fiselier55e084d2016-06-22 07:57:38 +0000923 _LIBCPP_ASSERT(st.permissions() != perms::unknown,
924 "Permissions unexpectedly unknown");
925 if (add_perms)
926 prms |= st.permissions();
927 else if (remove_perms)
928 prms = st.permissions() & ~prms;
Eric Fiselier6e9a6942016-06-17 19:46:40 +0000929 }
Eric Fiselier55e084d2016-06-22 07:57:38 +0000930 const auto real_perms = detail::posix_convert_perms(prms);
Eric Fiselier6e9a6942016-06-17 19:46:40 +0000931
932# if defined(AT_SYMLINK_NOFOLLOW) && defined(AT_FDCWD)
933 const int flags = set_sym_perms ? AT_SYMLINK_NOFOLLOW : 0;
934 if (::fchmodat(AT_FDCWD, p.c_str(), real_perms, flags) == -1) {
Eric Fiselier7c96ddb2016-06-21 22:42:42 +0000935 return set_or_throw(ec, "permissions", p);
936 }
Eric Fiselier6e9a6942016-06-17 19:46:40 +0000937# else
938 if (set_sym_perms)
939 return set_or_throw(make_error_code(errc::operation_not_supported),
940 ec, "permissions", p);
941 if (::chmod(p.c_str(), real_perms) == -1) {
Eric Fiselier6e9a6942016-06-17 19:46:40 +0000942 return set_or_throw(ec, "permissions", p);
943 }
Eric Fiselier7c96ddb2016-06-21 22:42:42 +0000944# endif
Eric Fiselier6e9a6942016-06-17 19:46:40 +0000945 if (ec) ec->clear();
946}
947
948
949path __read_symlink(const path& p, std::error_code *ec) {
950 char buff[PATH_MAX + 1];
951 std::error_code m_ec;
952 ::ssize_t ret;
953 if ((ret = ::readlink(p.c_str(), buff, PATH_MAX)) == -1) {
954 set_or_throw(ec, "read_symlink", p);
955 return {};
956 }
957 _LIBCPP_ASSERT(ret <= PATH_MAX, "TODO");
958 _LIBCPP_ASSERT(ret > 0, "TODO");
959 if (ec) ec->clear();
960 buff[ret] = 0;
961 return {buff};
962}
963
964
965bool __remove(const path& p, std::error_code *ec) {
966 if (ec) ec->clear();
Ekaterina Vaartis45d58932018-01-11 17:04:29 +0000967
Eric Fiselier6e9a6942016-06-17 19:46:40 +0000968 if (::remove(p.c_str()) == -1) {
Ekaterina Vaartis45d58932018-01-11 17:04:29 +0000969 if (errno != ENOENT)
970 set_or_throw(ec, "remove", p);
Eric Fiselier6e9a6942016-06-17 19:46:40 +0000971 return false;
972 }
973 return true;
974}
975
976namespace {
977
978std::uintmax_t remove_all_impl(path const & p, std::error_code& ec)
979{
980 const auto npos = static_cast<std::uintmax_t>(-1);
981 const file_status st = __symlink_status(p, &ec);
982 if (ec) return npos;
983 std::uintmax_t count = 1;
984 if (is_directory(st)) {
985 for (directory_iterator it(p, ec); !ec && it != directory_iterator();
986 it.increment(ec)) {
987 auto other_count = remove_all_impl(it->path(), ec);
988 if (ec) return npos;
989 count += other_count;
990 }
991 if (ec) return npos;
992 }
993 if (!__remove(p, &ec)) return npos;
994 return count;
995}
996
997} // end namespace
998
999std::uintmax_t __remove_all(const path& p, std::error_code *ec) {
Ekaterina Vaartis45d58932018-01-11 17:04:29 +00001000 if (ec) ec->clear();
1001
Eric Fiselier6e9a6942016-06-17 19:46:40 +00001002 std::error_code mec;
1003 auto count = remove_all_impl(p, mec);
1004 if (mec) {
Ekaterina Vaartis45d58932018-01-11 17:04:29 +00001005 if (mec == errc::no_such_file_or_directory) {
1006 return 0;
1007 } else {
1008 set_or_throw(mec, ec, "remove_all", p);
1009 return static_cast<std::uintmax_t>(-1);
1010 }
Eric Fiselier6e9a6942016-06-17 19:46:40 +00001011 }
Eric Fiselier6e9a6942016-06-17 19:46:40 +00001012 return count;
1013}
1014
1015void __rename(const path& from, const path& to, std::error_code *ec) {
1016 if (::rename(from.c_str(), to.c_str()) == -1)
1017 set_or_throw(ec, "rename", from, to);
1018 else if (ec)
1019 ec->clear();
1020}
1021
1022void __resize_file(const path& p, std::uintmax_t size, std::error_code *ec) {
Eric Fiselier4b1c5602017-06-16 06:17:52 +00001023 if (::truncate(p.c_str(), static_cast<::off_t>(size)) == -1)
Eric Fiselier6e9a6942016-06-17 19:46:40 +00001024 set_or_throw(ec, "resize_file", p);
1025 else if (ec)
1026 ec->clear();
1027}
1028
1029space_info __space(const path& p, std::error_code *ec) {
1030 space_info si;
Eric Fiselier591e18f2016-06-18 02:11:48 +00001031 struct statvfs m_svfs = {};
Eric Fiselier6e9a6942016-06-17 19:46:40 +00001032 if (::statvfs(p.c_str(), &m_svfs) == -1) {
1033 set_or_throw(ec, "space", p);
1034 si.capacity = si.free = si.available =
1035 static_cast<std::uintmax_t>(-1);
1036 return si;
1037 }
1038 if (ec) ec->clear();
1039 // Multiply with overflow checking.
Eric Fiselier591e18f2016-06-18 02:11:48 +00001040 auto do_mult = [&](std::uintmax_t& out, std::uintmax_t other) {
Eric Fiselier6e9a6942016-06-17 19:46:40 +00001041 out = other * m_svfs.f_frsize;
Eric Fiselierfbdbb362016-09-27 02:13:27 +00001042 if (other == 0 || out / other != m_svfs.f_frsize)
Eric Fiselier6e9a6942016-06-17 19:46:40 +00001043 out = static_cast<std::uintmax_t>(-1);
1044 };
1045 do_mult(si.capacity, m_svfs.f_blocks);
1046 do_mult(si.free, m_svfs.f_bfree);
1047 do_mult(si.available, m_svfs.f_bavail);
1048 return si;
1049}
1050
1051file_status __status(const path& p, std::error_code *ec) {
1052 return detail::posix_stat(p, ec);
1053}
1054
1055file_status __symlink_status(const path& p, std::error_code *ec) {
1056 return detail::posix_lstat(p, ec);
1057}
1058
Saleem Abdulrasool75668692017-02-05 17:21:52 +00001059path __temp_directory_path(std::error_code* ec) {
1060 const char* env_paths[] = {"TMPDIR", "TMP", "TEMP", "TEMPDIR"};
1061 const char* ret = nullptr;
1062
1063 for (auto& ep : env_paths)
1064 if ((ret = std::getenv(ep)))
1065 break;
1066 if (ret == nullptr)
1067 ret = "/tmp";
1068
1069 path p(ret);
1070 std::error_code m_ec;
1071 if (!exists(p, m_ec) || !is_directory(p, m_ec)) {
Eric Fiselier6e9a6942016-06-17 19:46:40 +00001072 if (!m_ec || m_ec == make_error_code(errc::no_such_file_or_directory))
Saleem Abdulrasool75668692017-02-05 17:21:52 +00001073 m_ec = make_error_code(errc::not_a_directory);
Eric Fiselier6e9a6942016-06-17 19:46:40 +00001074 set_or_throw(m_ec, ec, "temp_directory_path");
1075 return {};
Saleem Abdulrasool75668692017-02-05 17:21:52 +00001076 }
1077
1078 if (ec)
1079 ec->clear();
1080 return p;
Eric Fiselier6e9a6942016-06-17 19:46:40 +00001081}
1082
Eric Fiselier6e9a6942016-06-17 19:46:40 +00001083
Eric Fiselier1e34c762018-04-02 23:03:41 +00001084path __weakly_canonical(const path& p, std::error_code *ec) {
1085 if (p.empty())
1086 return __canonical("", ec);
Eric Fiselier6e9a6942016-06-17 19:46:40 +00001087
Eric Fiselier1e34c762018-04-02 23:03:41 +00001088 path result;
1089 path tmp;
1090 tmp.__reserve(p.native().size());
1091 auto PP = PathParser::CreateEnd(p.native());
1092 --PP;
1093 std::vector<string_view_t> DNEParts;
Eric Fiselier6e9a6942016-06-17 19:46:40 +00001094
Eric Fiselier1e34c762018-04-02 23:03:41 +00001095 while (PP.State != PathParser::PS_BeforeBegin) {
1096 tmp.assign(createView(p.native().data(), &PP.RawEntry.back()));
1097 std::error_code m_ec;
1098 file_status st = __status(tmp, &m_ec);
1099 if (!status_known(st)) {
1100 set_or_throw(m_ec, ec, "weakly_canonical", p);
1101 return {};
1102 } else if (exists(st)) {
1103 result = __canonical(tmp, ec);
1104 break;
Eric Fiselier6e9a6942016-06-17 19:46:40 +00001105 }
Eric Fiselier1e34c762018-04-02 23:03:41 +00001106 DNEParts.push_back(*PP);
1107 --PP;
1108 }
1109 if (PP.State == PathParser::PS_BeforeBegin)
1110 result = __canonical("", ec);
1111 if (ec) ec->clear();
1112 if (DNEParts.empty())
1113 return result;
1114 for (auto It=DNEParts.rbegin(); It != DNEParts.rend(); ++It)
1115 result /= *It;
1116 return result.lexically_normal();
Eric Fiselier6e9a6942016-06-17 19:46:40 +00001117}
1118
Eric Fiselier1e34c762018-04-02 23:03:41 +00001119
1120
1121///////////////////////////////////////////////////////////////////////////////
1122// path definitions
1123///////////////////////////////////////////////////////////////////////////////
1124
1125constexpr path::value_type path::preferred_separator;
1126
1127path & path::replace_extension(path const & replacement)
1128{
1129 path p = extension();
1130 if (not p.empty()) {
1131 __pn_.erase(__pn_.size() - p.native().size());
1132 }
1133 if (!replacement.empty()) {
1134 if (replacement.native()[0] != '.') {
1135 __pn_ += ".";
1136 }
1137 __pn_.append(replacement.__pn_);
1138 }
1139 return *this;
1140}
1141
1142///////////////////////////////////////////////////////////////////////////////
1143// path.decompose
1144
1145string_view_t path::__root_name() const
1146{
1147 auto PP = PathParser::CreateBegin(__pn_);
1148 if (PP.State == PathParser::PS_InRootName)
1149 return *PP;
1150 return {};
1151}
1152
1153string_view_t path::__root_directory() const
1154{
1155 auto PP = PathParser::CreateBegin(__pn_);
1156 if (PP.State == PathParser::PS_InRootName)
1157 ++PP;
1158 if (PP.State == PathParser::PS_InRootDir)
1159 return *PP;
1160 return {};
1161}
1162
1163string_view_t path::__root_path_raw() const
1164{
1165 auto PP = PathParser::CreateBegin(__pn_);
1166 if (PP.State == PathParser::PS_InRootName) {
1167 auto NextCh = PP.peek();
1168 if (NextCh && *NextCh == '/') {
1169 ++PP;
1170 return createView(__pn_.data(), &PP.RawEntry.back());
1171 }
1172 return PP.RawEntry;
1173 }
1174 if (PP.State == PathParser::PS_InRootDir)
1175 return *PP;
1176 return {};
1177}
1178
1179static bool ConsumeRootDir(PathParser* PP) {
1180 while (PP->State <= PathParser::PS_InRootDir)
1181 ++(*PP);
1182 return PP->State == PathParser::PS_AtEnd;
1183}
1184
1185string_view_t path::__relative_path() const
1186{
1187 auto PP = PathParser::CreateBegin(__pn_);
1188 if (ConsumeRootDir(&PP))
1189 return {};
1190 return createView(PP.RawEntry.data(), &__pn_.back());
1191}
1192
1193string_view_t path::__parent_path() const
1194{
1195 if (empty())
1196 return {};
1197 // Determine if we have a root path but not a relative path. In that case
1198 // return *this.
1199 {
1200 auto PP = PathParser::CreateBegin(__pn_);
1201 if (ConsumeRootDir(&PP))
1202 return __pn_;
1203 }
1204 // Otherwise remove a single element from the end of the path, and return
1205 // a string representing that path
1206 {
1207 auto PP = PathParser::CreateEnd(__pn_);
1208 --PP;
1209 if (PP.RawEntry.data() == __pn_.data())
1210 return {};
1211 --PP;
1212 return createView(__pn_.data(), &PP.RawEntry.back());
1213 }
1214}
1215
1216string_view_t path::__filename() const
1217{
1218 if (empty()) return {};
1219 {
1220 PathParser PP = PathParser::CreateBegin(__pn_);
1221 if (ConsumeRootDir(&PP))
1222 return {};
1223 }
1224 return *(--PathParser::CreateEnd(__pn_));
1225}
1226
1227string_view_t path::__stem() const
1228{
1229 return parser::separate_filename(__filename()).first;
1230}
1231
1232string_view_t path::__extension() const
1233{
1234 return parser::separate_filename(__filename()).second;
1235}
1236
1237////////////////////////////////////////////////////////////////////////////
1238// path.gen
1239
1240
1241enum PathPartKind : unsigned char {
1242 PK_None,
1243 PK_RootSep,
1244 PK_Filename,
1245 PK_Dot,
1246 PK_DotDot,
1247 PK_TrailingSep
1248};
1249
1250static PathPartKind ClassifyPathPart(string_view_t Part) {
1251 if (Part.empty())
1252 return PK_TrailingSep;
1253 if (Part == ".")
1254 return PK_Dot;
1255 if (Part == "..")
1256 return PK_DotDot;
1257 if (Part == "/")
1258 return PK_RootSep;
1259 return PK_Filename;
1260}
1261
1262path path::lexically_normal() const {
1263 if (__pn_.empty())
1264 return *this;
1265
1266 using PartKindPair = std::pair<string_view_t, PathPartKind>;
1267 std::vector<PartKindPair> Parts;
1268 // Guess as to how many elements the path has to avoid reallocating.
1269 Parts.reserve(32);
1270
1271 // Track the total size of the parts as we collect them. This allows the
1272 // resulting path to reserve the correct amount of memory.
1273 size_t NewPathSize = 0;
1274 auto AddPart = [&](PathPartKind K, string_view_t P) {
1275 NewPathSize += P.size();
1276 Parts.emplace_back(P, K);
1277 };
1278 auto LastPartKind = [&]() {
1279 if (Parts.empty())
1280 return PK_None;
1281 return Parts.back().second;
1282 };
1283
1284 bool MaybeNeedTrailingSep = false;
1285 // Build a stack containing the remaining elements of the path, popping off
1286 // elements which occur before a '..' entry.
1287 for (auto PP = PathParser::CreateBegin(__pn_); PP; ++PP) {
1288 auto Part = *PP;
1289 PathPartKind Kind = ClassifyPathPart(Part);
1290 switch (Kind) {
1291 case PK_Filename:
1292 case PK_RootSep: {
1293 // Add all non-dot and non-dot-dot elements to the stack of elements.
1294 AddPart(Kind, Part);
1295 MaybeNeedTrailingSep = false;
1296 break;
1297 }
1298 case PK_DotDot: {
1299 // Only push a ".." element if there are no elements preceding the "..",
1300 // or if the preceding element is itself "..".
1301 auto LastKind = LastPartKind();
1302 if (LastKind == PK_Filename) {
1303 NewPathSize -= Parts.back().first.size();
1304 Parts.pop_back();
1305 } else if (LastKind != PK_RootSep)
1306 AddPart(PK_DotDot, "..");
1307 MaybeNeedTrailingSep = LastKind == PK_Filename;
1308 break;
1309 }
1310 case PK_Dot:
1311 case PK_TrailingSep: {
1312 MaybeNeedTrailingSep = true;
1313 break;
1314 }
1315 case PK_None:
1316 _LIBCPP_UNREACHABLE();
1317 }
1318 }
1319 // [fs.path.generic]p6.8: If the path is empty, add a dot.
1320 if (Parts.empty())
1321 return ".";
1322
1323 // [fs.path.generic]p6.7: If the last filename is dot-dot, remove any
1324 // trailing directory-separator.
1325 bool NeedTrailingSep = MaybeNeedTrailingSep && LastPartKind() == PK_Filename;
1326
1327 path Result;
1328 Result.__pn_.reserve(Parts.size() + NewPathSize + NeedTrailingSep);
1329 for (auto &PK : Parts)
1330 Result /= PK.first;
1331
1332 if (NeedTrailingSep)
1333 Result /= "";
1334
1335 return Result;
1336}
1337
1338static int DetermineLexicalElementCount(PathParser PP) {
1339 int Count = 0;
1340 for (; PP; ++PP) {
1341 auto Elem = *PP;
1342 if (Elem == "..")
1343 --Count;
1344 else if (Elem != ".")
1345 ++Count;
1346 }
1347 return Count;
1348}
1349
1350path path::lexically_relative(const path& base) const {
1351 { // perform root-name/root-directory mismatch checks
1352 auto PP = PathParser::CreateBegin(__pn_);
1353 auto PPBase = PathParser::CreateBegin(base.__pn_);
1354 auto CheckIterMismatchAtBase = [&]() {
1355 return PP.State != PPBase.State && (
1356 PP.inRootPath() || PPBase.inRootPath());
1357 };
1358 if (PP.State == PathParser::PS_InRootName &&
1359 PPBase.State == PathParser::PS_InRootName) {
1360 if (*PP != *PPBase)
1361 return {};
1362 } else if (CheckIterMismatchAtBase())
1363 return {};
1364
1365 if (PP.inRootPath()) ++PP;
1366 if (PPBase.inRootPath()) ++PPBase;
1367 if (CheckIterMismatchAtBase())
1368 return {};
1369 }
1370
1371 // Find the first mismatching element
1372 auto PP = PathParser::CreateBegin(__pn_);
1373 auto PPBase = PathParser::CreateBegin(base.__pn_);
1374 while (PP && PPBase && PP.State == PPBase.State &&
1375 *PP == *PPBase) {
1376 ++PP;
1377 ++PPBase;
1378 }
1379
1380 // If there is no mismatch, return ".".
1381 if (!PP && !PPBase)
1382 return ".";
1383
1384 // Otherwise, determine the number of elements, 'n', which are not dot or
1385 // dot-dot minus the number of dot-dot elements.
1386 int ElemCount = DetermineLexicalElementCount(PPBase);
1387 if (ElemCount < 0)
1388 return {};
1389
1390 // return a path constructed with 'n' dot-dot elements, followed by the the
1391 // elements of '*this' after the mismatch.
1392 path Result;
1393 // FIXME: Reserve enough room in Result that it won't have to re-allocate.
1394 while (ElemCount--)
1395 Result /= "..";
1396 for (; PP; ++PP)
1397 Result /= *PP;
1398 return Result;
1399}
1400
1401////////////////////////////////////////////////////////////////////////////
1402// path.comparisons
1403int path::__compare(string_view_t __s) const {
1404 auto PP = PathParser::CreateBegin(__pn_);
1405 auto PP2 = PathParser::CreateBegin(__s);
1406 while (PP && PP2) {
1407 int res = (*PP).compare(*PP2);
1408 if (res != 0) return res;
1409 ++PP; ++PP2;
1410 }
1411 if (PP.State == PP2.State && !PP)
1412 return 0;
1413 if (!PP)
1414 return -1;
1415 return 1;
1416}
1417
1418////////////////////////////////////////////////////////////////////////////
1419// path.nonmembers
1420size_t hash_value(const path& __p) noexcept {
1421 auto PP = PathParser::CreateBegin(__p.native());
1422 size_t hash_value = 0;
1423 std::hash<string_view_t> hasher;
1424 while (PP) {
1425 hash_value = __hash_combine(hash_value, hasher(*PP));
1426 ++PP;
1427 }
1428 return hash_value;
1429}
1430
1431////////////////////////////////////////////////////////////////////////////
1432// path.itr
1433path::iterator path::begin() const
1434{
1435 auto PP = PathParser::CreateBegin(__pn_);
1436 iterator it;
1437 it.__path_ptr_ = this;
1438 it.__state_ = PP.State;
1439 it.__entry_ = PP.RawEntry;
1440 it.__stashed_elem_.__assign_view(*PP);
1441 return it;
1442}
1443
1444path::iterator path::end() const
1445{
1446 iterator it{};
1447 it.__state_ = PathParser::PS_AtEnd;
1448 it.__path_ptr_ = this;
1449 return it;
1450}
1451
1452path::iterator& path::iterator::__increment() {
1453 static_assert(__at_end == PathParser::PS_AtEnd, "");
1454 PathParser PP(__path_ptr_->native(), __entry_, __state_);
1455 ++PP;
1456 __state_ = PP.State;
1457 __entry_ = PP.RawEntry;
1458 __stashed_elem_.__assign_view(*PP);
1459 return *this;
1460}
1461
1462path::iterator& path::iterator::__decrement() {
1463 PathParser PP(__path_ptr_->native(), __entry_, __state_);
1464 --PP;
1465 __state_ = PP.State;
1466 __entry_ = PP.RawEntry;
1467 __stashed_elem_.__assign_view(*PP);
1468 return *this;
1469}
1470
1471
Eric Fiselier6e9a6942016-06-17 19:46:40 +00001472_LIBCPP_END_NAMESPACE_EXPERIMENTAL_FILESYSTEM