pr86900.C: XFAIL AIX.
[official-gcc.git] / libstdc++-v3 / src / filesystem / std-path.cc
blob06d882cf4de9191e37b8f79bd4bed1d025f85818
1 // Class filesystem::path -*- C++ -*-
3 // Copyright (C) 2014-2018 Free Software Foundation, Inc.
4 //
5 // This file is part of the GNU ISO C++ Library. This library is free
6 // software; you can redistribute it and/or modify it under the
7 // terms of the GNU General Public License as published by the
8 // Free Software Foundation; either version 3, or (at your option)
9 // any later version.
11 // This library is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // Under Section 7 of GPL version 3, you are granted additional
17 // permissions described in the GCC Runtime Library Exception, version
18 // 3.1, as published by the Free Software Foundation.
20 // You should have received a copy of the GNU General Public License and
21 // a copy of the GCC Runtime Library Exception along with this program;
22 // see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
23 // <http://www.gnu.org/licenses/>.
25 #ifndef _GLIBCXX_USE_CXX11_ABI
26 # define _GLIBCXX_USE_CXX11_ABI 1
27 #endif
29 #include <filesystem>
30 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
31 # include <algorithm>
32 #endif
34 namespace fs = std::filesystem;
35 using fs::path;
37 constexpr path::value_type path::preferred_separator;
39 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
40 path&
41 path::operator/=(const path& __p)
43 if (__p.is_absolute()
44 || (__p.has_root_name() && __p.root_name() != root_name()))
45 return operator=(__p);
47 basic_string_view<value_type> __lhs = _M_pathname;
48 bool __add_sep = false;
50 if (__p.has_root_directory())
52 // Remove any root directory and relative path
53 if (_M_type != _Type::_Root_name)
55 if (!_M_cmpts.empty()
56 && _M_cmpts.front()._M_type == _Type::_Root_name)
57 __lhs = _M_cmpts.front()._M_pathname;
58 else
59 __lhs = {};
62 else if (has_filename() || (!has_root_directory() && is_absolute()))
63 __add_sep = true;
65 basic_string_view<value_type> __rhs = __p._M_pathname;
66 // Omit any root-name from the generic format pathname:
67 if (__p._M_type == _Type::_Root_name)
68 __rhs = {};
69 else if (!__p._M_cmpts.empty()
70 && __p._M_cmpts.front()._M_type == _Type::_Root_name)
71 __rhs.remove_prefix(__p._M_cmpts.front()._M_pathname.size());
73 const size_t __len = __lhs.size() + (int)__add_sep + __rhs.size();
74 const size_t __maxcmpts = _M_cmpts.size() + __p._M_cmpts.size();
75 if (_M_pathname.capacity() < __len || _M_cmpts.capacity() < __maxcmpts)
77 // Construct new path and swap (strong exception-safety guarantee).
78 string_type __tmp;
79 __tmp.reserve(__len);
80 __tmp = __lhs;
81 if (__add_sep)
82 __tmp += preferred_separator;
83 __tmp += __rhs;
84 path __newp = std::move(__tmp);
85 swap(__newp);
87 else
89 _M_pathname = __lhs;
90 if (__add_sep)
91 _M_pathname += preferred_separator;
92 _M_pathname += __rhs;
93 _M_split_cmpts();
95 return *this;
97 #endif
99 path&
100 path::remove_filename()
102 if (_M_type == _Type::_Multi)
104 if (!_M_cmpts.empty())
106 auto cmpt = std::prev(_M_cmpts.end());
107 if (cmpt->_M_type == _Type::_Filename && !cmpt->empty())
109 _M_pathname.erase(cmpt->_M_pos);
110 auto prev = std::prev(cmpt);
111 if (prev->_M_type == _Type::_Root_dir
112 || prev->_M_type == _Type::_Root_name)
114 _M_cmpts.erase(cmpt);
115 _M_trim();
117 else
118 cmpt->clear();
122 else if (_M_type == _Type::_Filename)
123 clear();
124 return *this;
127 path&
128 path::replace_filename(const path& replacement)
130 remove_filename();
131 operator/=(replacement);
132 return *this;
135 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
136 const fs::path::value_type dot = L'.';
137 #else
138 const fs::path::value_type dot = '.';
139 #endif
141 path&
142 path::replace_extension(const path& replacement)
144 auto ext = _M_find_extension();
145 // Any existing extension() is removed
146 if (ext.first && ext.second != string_type::npos)
148 if (ext.first == &_M_pathname)
149 _M_pathname.erase(ext.second);
150 else
152 const auto& back = _M_cmpts.back();
153 if (ext.first != &back._M_pathname)
154 _GLIBCXX_THROW_OR_ABORT(
155 std::logic_error("path::replace_extension failed"));
156 _M_pathname.erase(back._M_pos + ext.second);
159 // If replacement is not empty and does not begin with a dot character,
160 // a dot character is appended
161 if (!replacement.empty() && replacement.native()[0] != dot)
162 _M_pathname += dot;
163 operator+=(replacement);
164 return *this;
167 namespace
169 template<typename Iter1, typename Iter2>
170 int do_compare(Iter1 begin1, Iter1 end1, Iter2 begin2, Iter2 end2)
172 int cmpt = 1;
173 while (begin1 != end1 && begin2 != end2)
175 if (begin1->native() < begin2->native())
176 return -cmpt;
177 if (begin1->native() > begin2->native())
178 return +cmpt;
179 ++begin1;
180 ++begin2;
181 ++cmpt;
183 if (begin1 == end1)
185 if (begin2 == end2)
186 return 0;
187 return -cmpt;
189 return +cmpt;
194 path::compare(const path& p) const noexcept
196 struct CmptRef
198 const path* ptr;
199 const string_type& native() const noexcept { return ptr->native(); }
202 if (empty() && p.empty())
203 return 0;
204 else if (_M_type == _Type::_Multi && p._M_type == _Type::_Multi)
205 return do_compare(_M_cmpts.begin(), _M_cmpts.end(),
206 p._M_cmpts.begin(), p._M_cmpts.end());
207 else if (_M_type == _Type::_Multi)
209 CmptRef c[1] = { { &p } };
210 return do_compare(_M_cmpts.begin(), _M_cmpts.end(), c, c+1);
212 else if (p._M_type == _Type::_Multi)
214 CmptRef c[1] = { { this } };
215 return do_compare(c, c+1, p._M_cmpts.begin(), p._M_cmpts.end());
217 else
218 return _M_pathname.compare(p._M_pathname);
221 path
222 path::root_name() const
224 path __ret;
225 if (_M_type == _Type::_Root_name)
226 __ret = *this;
227 else if (_M_cmpts.size() && _M_cmpts.begin()->_M_type == _Type::_Root_name)
228 __ret = *_M_cmpts.begin();
229 return __ret;
232 path
233 path::root_directory() const
235 path __ret;
236 if (_M_type == _Type::_Root_dir)
238 __ret._M_type = _Type::_Root_dir;
239 __ret._M_pathname.assign(1, preferred_separator);
241 else if (!_M_cmpts.empty())
243 auto __it = _M_cmpts.begin();
244 if (__it->_M_type == _Type::_Root_name)
245 ++__it;
246 if (__it != _M_cmpts.end() && __it->_M_type == _Type::_Root_dir)
247 __ret = *__it;
249 return __ret;
252 path
253 path::root_path() const
255 path __ret;
256 if (_M_type == _Type::_Root_name)
257 __ret = *this;
258 else if (_M_type == _Type::_Root_dir)
260 __ret._M_pathname.assign(1, preferred_separator);
261 __ret._M_type = _Type::_Root_dir;
263 else if (!_M_cmpts.empty())
265 auto __it = _M_cmpts.begin();
266 if (__it->_M_type == _Type::_Root_name)
268 __ret = *__it++;
269 if (__it != _M_cmpts.end() && __it->_M_type == _Type::_Root_dir)
270 __ret /= *__it;
272 else if (__it->_M_type == _Type::_Root_dir)
273 __ret = *__it;
275 return __ret;
278 path
279 path::relative_path() const
281 path __ret;
282 if (_M_type == _Type::_Filename)
283 __ret = *this;
284 else if (!_M_cmpts.empty())
286 auto __it = _M_cmpts.begin();
287 if (__it->_M_type == _Type::_Root_name)
288 ++__it;
289 if (__it != _M_cmpts.end() && __it->_M_type == _Type::_Root_dir)
290 ++__it;
291 if (__it != _M_cmpts.end())
292 __ret.assign(_M_pathname.substr(__it->_M_pos));
294 return __ret;
297 path
298 path::parent_path() const
300 path __ret;
301 if (!has_relative_path())
302 __ret = *this;
303 else if (_M_cmpts.size() >= 2)
305 for (auto __it = _M_cmpts.begin(), __end = std::prev(_M_cmpts.end());
306 __it != __end; ++__it)
308 __ret /= *__it;
311 return __ret;
314 bool
315 path::has_root_name() const
317 if (_M_type == _Type::_Root_name)
318 return true;
319 if (!_M_cmpts.empty() && _M_cmpts.begin()->_M_type == _Type::_Root_name)
320 return true;
321 return false;
324 bool
325 path::has_root_directory() const
327 if (_M_type == _Type::_Root_dir)
328 return true;
329 if (!_M_cmpts.empty())
331 auto __it = _M_cmpts.begin();
332 if (__it->_M_type == _Type::_Root_name)
333 ++__it;
334 if (__it != _M_cmpts.end() && __it->_M_type == _Type::_Root_dir)
335 return true;
337 return false;
340 bool
341 path::has_root_path() const
343 if (_M_type == _Type::_Root_name || _M_type == _Type::_Root_dir)
344 return true;
345 if (!_M_cmpts.empty())
347 auto __type = _M_cmpts.front()._M_type;
348 if (__type == _Type::_Root_name || __type == _Type::_Root_dir)
349 return true;
351 return false;
354 bool
355 path::has_relative_path() const
357 if (_M_type == _Type::_Filename && !_M_pathname.empty())
358 return true;
359 if (!_M_cmpts.empty())
361 auto __it = _M_cmpts.begin();
362 if (__it->_M_type == _Type::_Root_name)
363 ++__it;
364 if (__it != _M_cmpts.end() && __it->_M_type == _Type::_Root_dir)
365 ++__it;
366 if (__it != _M_cmpts.end() && !__it->_M_pathname.empty())
367 return true;
369 return false;
373 bool
374 path::has_parent_path() const
376 if (!has_relative_path())
377 return !empty();
378 return _M_cmpts.size() >= 2;
381 bool
382 path::has_filename() const
384 if (empty())
385 return false;
386 if (_M_type == _Type::_Filename)
387 return !_M_pathname.empty();
388 if (_M_type == _Type::_Multi)
390 if (_M_pathname.back() == preferred_separator)
391 return false;
392 return _M_cmpts.back().has_filename();
394 return false;
397 namespace
399 inline bool is_dot(fs::path::value_type c) { return c == dot; }
401 inline bool is_dot(const fs::path& path)
403 const auto& filename = path.native();
404 return filename.size() == 1 && is_dot(filename[0]);
407 inline bool is_dotdot(const fs::path& path)
409 const auto& filename = path.native();
410 return filename.size() == 2 && is_dot(filename[0]) && is_dot(filename[1]);
412 } // namespace
414 path
415 path::lexically_normal() const
418 C++17 [fs.path.generic] p6
419 - If the path is empty, stop.
420 - Replace each slash character in the root-name with a preferred-separator.
421 - Replace each directory-separator with a preferred-separator.
422 - Remove each dot filename and any immediately following directory-separator.
423 - As long as any appear, remove a non-dot-dot filename immediately followed
424 by a directory-separator and a dot-dot filename, along with any immediately
425 following directory-separator.
426 - If there is a root-directory, remove all dot-dot filenames and any
427 directory-separators immediately following them.
428 - If the last filename is dot-dot, remove any trailing directory-separator.
429 - If the path is empty, add a dot.
431 path ret;
432 // If the path is empty, stop.
433 if (empty())
434 return ret;
435 for (auto& p : *this)
437 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
438 // Replace each slash character in the root-name
439 if (p._M_type == _Type::_Root_name || p._M_type == _Type::_Root_dir)
441 string_type s = p.native();
442 std::replace(s.begin(), s.end(), L'/', L'\\');
443 ret /= s;
444 continue;
446 #endif
447 if (is_dotdot(p))
449 if (ret.has_filename())
451 // remove a non-dot-dot filename immediately followed by /..
452 if (!is_dotdot(ret.filename()))
453 ret.remove_filename();
454 else
455 ret /= p;
457 else if (!ret.has_relative_path())
459 // remove a dot-dot filename immediately after root-directory
460 if (!ret.has_root_directory())
461 ret /= p;
463 else
465 // Got a path with a relative path (i.e. at least one non-root
466 // element) and no filename at the end (i.e. empty last element),
467 // so must have a trailing slash. See what is before it.
468 auto elem = std::prev(ret.end(), 2);
469 if (elem->has_filename() && !is_dotdot(*elem))
471 // Remove the filename before the trailing slash
472 // (equiv. to ret = ret.parent_path().remove_filename())
474 if (elem == ret.begin())
475 ret.clear();
476 else
478 ret._M_pathname.erase(elem._M_cur->_M_pos);
479 // Do we still have a trailing slash?
480 if (std::prev(elem)->_M_type == _Type::_Filename)
481 ret._M_cmpts.erase(elem._M_cur);
482 else
483 ret._M_cmpts.erase(elem._M_cur, ret._M_cmpts.end());
486 else // ???
487 ret /= p;
490 else if (is_dot(p))
491 ret /= path();
492 else
493 ret /= p;
496 if (ret._M_cmpts.size() >= 2)
498 auto back = std::prev(ret.end());
499 // If the last filename is dot-dot, ...
500 if (back->empty() && is_dotdot(*std::prev(back)))
501 // ... remove any trailing directory-separator.
502 ret = ret.parent_path();
504 // If the path is empty, add a dot.
505 else if (ret.empty())
506 ret = ".";
508 return ret;
511 path
512 path::lexically_relative(const path& base) const
514 path ret;
515 if (root_name() != base.root_name())
516 return ret;
517 if (is_absolute() != base.is_absolute())
518 return ret;
519 if (!has_root_directory() && base.has_root_directory())
520 return ret;
521 auto [a, b] = std::mismatch(begin(), end(), base.begin(), base.end());
522 if (a == end() && b == base.end())
523 ret = ".";
524 else
526 int n = 0;
527 for (; b != base.end(); ++b)
529 const path& p = *b;
530 if (is_dotdot(p))
531 --n;
532 else if (!p.empty() && !is_dot(p))
533 ++n;
535 if (n == 0 && (a == end() || a->empty()))
536 ret = ".";
537 else if (n >= 0)
539 const path dotdot("..");
540 while (n--)
541 ret /= dotdot;
542 for (; a != end(); ++a)
543 ret /= *a;
546 return ret;
549 path
550 path::lexically_proximate(const path& base) const
552 path rel = lexically_relative(base);
553 if (rel.empty())
554 rel = *this;
555 return rel;
558 std::pair<const path::string_type*, std::size_t>
559 path::_M_find_extension() const
561 const string_type* s = nullptr;
563 if (_M_type == _Type::_Filename)
564 s = &_M_pathname;
565 else if (_M_type == _Type::_Multi && !_M_cmpts.empty())
567 const auto& c = _M_cmpts.back();
568 if (c._M_type == _Type::_Filename)
569 s = &c._M_pathname;
572 if (s)
574 if (auto sz = s->size())
576 if (sz <= 2 && (*s)[0] == dot)
577 return { s, string_type::npos };
578 const auto pos = s->rfind(dot);
579 return { s, pos ? pos : string_type::npos };
582 return {};
585 void
586 path::_M_split_cmpts()
588 _M_cmpts.clear();
589 if (_M_pathname.empty())
591 _M_type = _Type::_Filename;
592 return;
594 _M_type = _Type::_Multi;
596 size_t pos = 0;
597 const size_t len = _M_pathname.size();
599 // look for root name or root directory
600 if (_S_is_dir_sep(_M_pathname[0]))
602 #ifdef __CYGWIN__
603 // look for root name, such as "//foo"
604 if (len > 2 && _M_pathname[1] == _M_pathname[0])
606 if (!_S_is_dir_sep(_M_pathname[2]))
608 // got root name, find its end
609 pos = 3;
610 while (pos < len && !_S_is_dir_sep(_M_pathname[pos]))
611 ++pos;
612 _M_add_root_name(pos);
613 if (pos < len) // also got root directory
614 _M_add_root_dir(pos);
616 else
618 // got something like "///foo" which is just a root directory
619 // composed of multiple redundant directory separators
620 _M_add_root_dir(0);
623 else
624 #endif
626 // got root directory
627 if (_M_pathname.find_first_not_of('/') == string_type::npos)
629 // entire path is just slashes
630 _M_type = _Type::_Root_dir;
631 return;
633 _M_add_root_dir(0);
634 ++pos;
637 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
638 else if (len > 1 && _M_pathname[1] == L':')
640 // got disk designator
641 _M_add_root_name(2);
642 if (len > 2 && _S_is_dir_sep(_M_pathname[2]))
643 _M_add_root_dir(2);
644 pos = 2;
646 #endif
648 size_t back = pos;
649 while (pos < len)
651 if (_S_is_dir_sep(_M_pathname[pos]))
653 if (back != pos)
654 _M_add_filename(back, pos - back);
655 back = ++pos;
657 else
658 ++pos;
661 if (back != pos)
662 _M_add_filename(back, pos - back);
663 else if (_S_is_dir_sep(_M_pathname.back()))
665 // [fs.path.itr]/4
666 // An empty element, if trailing non-root directory-separator present.
667 if (_M_cmpts.back()._M_type == _Type::_Filename)
669 pos = _M_pathname.size();
670 _M_cmpts.emplace_back(string_type(), _Type::_Filename, pos);
674 _M_trim();
677 void
678 path::_M_add_root_name(size_t n)
680 _M_cmpts.emplace_back(_M_pathname.substr(0, n), _Type::_Root_name, 0);
683 void
684 path::_M_add_root_dir(size_t pos)
686 _M_cmpts.emplace_back(_M_pathname.substr(pos, 1), _Type::_Root_dir, pos);
689 void
690 path::_M_add_filename(size_t pos, size_t n)
692 _M_cmpts.emplace_back(_M_pathname.substr(pos, n), _Type::_Filename, pos);
695 void
696 path::_M_trim()
698 if (_M_cmpts.size() == 1)
700 _M_type = _M_cmpts.front()._M_type;
701 _M_cmpts.clear();
705 path::string_type
706 path::_S_convert_loc(const char* __first, const char* __last,
707 const std::locale& __loc)
709 #if _GLIBCXX_USE_WCHAR_T
710 auto& __cvt = std::use_facet<codecvt<wchar_t, char, mbstate_t>>(__loc);
711 basic_string<wchar_t> __ws;
712 if (!__str_codecvt_in(__first, __last, __ws, __cvt))
713 _GLIBCXX_THROW_OR_ABORT(filesystem_error(
714 "Cannot convert character sequence",
715 std::make_error_code(errc::illegal_byte_sequence)));
716 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
717 return __ws;
718 #else
719 return _Cvt<wchar_t>::_S_convert(__ws.data(), __ws.data() + __ws.size());
720 #endif
721 #else
722 return {__first, __last};
723 #endif
726 std::size_t
727 fs::hash_value(const path& p) noexcept
729 // [path.non-member]
730 // "If for two paths, p1 == p2 then hash_value(p1) == hash_value(p2)."
731 // Equality works as if by traversing the range [begin(), end()), meaning
732 // e.g. path("a//b") == path("a/b"), so we cannot simply hash _M_pathname
733 // but need to iterate over individual elements. Use the hash_combine from
734 // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3876.pdf
735 size_t seed = 0;
736 for (const auto& x : p)
738 seed ^= std::hash<path::string_type>()(x.native()) + 0x9e3779b9
739 + (seed<<6) + (seed>>2);
741 return seed;
744 struct fs::filesystem_error::_Impl
746 _Impl(const string& what_arg, const path& p1, const path& p2)
747 : path1(p1), path2(p2), what(make_what(what_arg, &p1, &p2))
750 _Impl(const string& what_arg, const path& p1)
751 : path1(p1), path2(), what(make_what(what_arg, &p1, nullptr))
754 _Impl(const string& what_arg)
755 : what(make_what(what_arg, nullptr, nullptr))
758 static std::string
759 make_what(const std::string& s, const path* p1, const path* p2)
761 const std::string pstr1 = p1 ? p1->u8string() : std::string{};
762 const std::string pstr2 = p2 ? p2->u8string() : std::string{};
763 const size_t len = 18 + s.length()
764 + (pstr1.length() ? pstr1.length() + 3 : 0)
765 + (pstr2.length() ? pstr2.length() + 3 : 0);
766 std::string w;
767 w.reserve(len);
768 w = "filesystem error: ";
769 w += s;
770 if (p1)
772 w += " [";
773 w += pstr1;
774 w += ']';
775 if (p2)
777 w += " [";
778 w += pstr2;
779 w += ']';
782 return w;
785 path path1;
786 path path2;
787 std::string what;
790 template class std::__shared_ptr<const fs::filesystem_error::_Impl>;
792 fs::filesystem_error::
793 filesystem_error(const string& what_arg, error_code ec)
794 : system_error(ec, what_arg),
795 _M_impl(std::__make_shared<_Impl>(what_arg))
798 fs::filesystem_error::
799 filesystem_error(const string& what_arg, const path& p1, error_code ec)
800 : system_error(ec, what_arg),
801 _M_impl(std::__make_shared<_Impl>(what_arg, p1))
804 fs::filesystem_error::
805 filesystem_error(const string& what_arg, const path& p1, const path& p2,
806 error_code ec)
807 : system_error(ec, what_arg),
808 _M_impl(std::__make_shared<_Impl>(what_arg, p1, p2))
811 fs::filesystem_error::~filesystem_error() = default;
813 const fs::path&
814 fs::filesystem_error::path1() const noexcept
815 { return _M_impl->path1; }
817 const fs::path&
818 fs::filesystem_error::path2() const noexcept
819 { return _M_impl->path2; }
821 const char*
822 fs::filesystem_error::what() const noexcept
823 { return _M_impl->what.c_str(); }