mirror of git://gcc.gnu.org/git/gcc.git
824 lines
20 KiB
C++
824 lines
20 KiB
C++
// Class filesystem::path -*- C++ -*-
|
|
|
|
// Copyright (C) 2014-2018 Free Software Foundation, Inc.
|
|
//
|
|
// This file is part of the GNU ISO C++ Library. This library is free
|
|
// software; you can redistribute it and/or modify it under the
|
|
// terms of the GNU General Public License as published by the
|
|
// Free Software Foundation; either version 3, or (at your option)
|
|
// any later version.
|
|
|
|
// This library is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
|
|
// Under Section 7 of GPL version 3, you are granted additional
|
|
// permissions described in the GCC Runtime Library Exception, version
|
|
// 3.1, as published by the Free Software Foundation.
|
|
|
|
// You should have received a copy of the GNU General Public License and
|
|
// a copy of the GCC Runtime Library Exception along with this program;
|
|
// see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
|
|
// <http://www.gnu.org/licenses/>.
|
|
|
|
#ifndef _GLIBCXX_USE_CXX11_ABI
|
|
# define _GLIBCXX_USE_CXX11_ABI 1
|
|
#endif
|
|
|
|
#include <filesystem>
|
|
#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
|
|
# include <algorithm>
|
|
#endif
|
|
|
|
namespace fs = std::filesystem;
|
|
using fs::path;
|
|
|
|
constexpr path::value_type path::preferred_separator;
|
|
|
|
#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
|
|
path&
|
|
path::operator/=(const path& __p)
|
|
{
|
|
if (__p.is_absolute()
|
|
|| (__p.has_root_name() && __p.root_name() != root_name()))
|
|
return operator=(__p);
|
|
|
|
basic_string_view<value_type> __lhs = _M_pathname;
|
|
bool __add_sep = false;
|
|
|
|
if (__p.has_root_directory())
|
|
{
|
|
// Remove any root directory and relative path
|
|
if (_M_type != _Type::_Root_name)
|
|
{
|
|
if (!_M_cmpts.empty()
|
|
&& _M_cmpts.front()._M_type == _Type::_Root_name)
|
|
__lhs = _M_cmpts.front()._M_pathname;
|
|
else
|
|
__lhs = {};
|
|
}
|
|
}
|
|
else if (has_filename() || (!has_root_directory() && is_absolute()))
|
|
__add_sep = true;
|
|
|
|
basic_string_view<value_type> __rhs = __p._M_pathname;
|
|
// Omit any root-name from the generic format pathname:
|
|
if (__p._M_type == _Type::_Root_name)
|
|
__rhs = {};
|
|
else if (!__p._M_cmpts.empty()
|
|
&& __p._M_cmpts.front()._M_type == _Type::_Root_name)
|
|
__rhs.remove_prefix(__p._M_cmpts.front()._M_pathname.size());
|
|
|
|
const size_t __len = __lhs.size() + (int)__add_sep + __rhs.size();
|
|
const size_t __maxcmpts = _M_cmpts.size() + __p._M_cmpts.size();
|
|
if (_M_pathname.capacity() < __len || _M_cmpts.capacity() < __maxcmpts)
|
|
{
|
|
// Construct new path and swap (strong exception-safety guarantee).
|
|
string_type __tmp;
|
|
__tmp.reserve(__len);
|
|
__tmp = __lhs;
|
|
if (__add_sep)
|
|
__tmp += preferred_separator;
|
|
__tmp += __rhs;
|
|
path __newp = std::move(__tmp);
|
|
swap(__newp);
|
|
}
|
|
else
|
|
{
|
|
_M_pathname = __lhs;
|
|
if (__add_sep)
|
|
_M_pathname += preferred_separator;
|
|
_M_pathname += __rhs;
|
|
_M_split_cmpts();
|
|
}
|
|
return *this;
|
|
}
|
|
#endif
|
|
|
|
path&
|
|
path::remove_filename()
|
|
{
|
|
if (_M_type == _Type::_Multi)
|
|
{
|
|
if (!_M_cmpts.empty())
|
|
{
|
|
auto cmpt = std::prev(_M_cmpts.end());
|
|
if (cmpt->_M_type == _Type::_Filename && !cmpt->empty())
|
|
{
|
|
_M_pathname.erase(cmpt->_M_pos);
|
|
auto prev = std::prev(cmpt);
|
|
if (prev->_M_type == _Type::_Root_dir
|
|
|| prev->_M_type == _Type::_Root_name)
|
|
{
|
|
_M_cmpts.erase(cmpt);
|
|
_M_trim();
|
|
}
|
|
else
|
|
cmpt->clear();
|
|
}
|
|
}
|
|
}
|
|
else if (_M_type == _Type::_Filename)
|
|
clear();
|
|
return *this;
|
|
}
|
|
|
|
path&
|
|
path::replace_filename(const path& replacement)
|
|
{
|
|
remove_filename();
|
|
operator/=(replacement);
|
|
return *this;
|
|
}
|
|
|
|
#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
|
|
const fs::path::value_type dot = L'.';
|
|
#else
|
|
const fs::path::value_type dot = '.';
|
|
#endif
|
|
|
|
path&
|
|
path::replace_extension(const path& replacement)
|
|
{
|
|
auto ext = _M_find_extension();
|
|
// Any existing extension() is removed
|
|
if (ext.first && ext.second != string_type::npos)
|
|
{
|
|
if (ext.first == &_M_pathname)
|
|
_M_pathname.erase(ext.second);
|
|
else
|
|
{
|
|
const auto& back = _M_cmpts.back();
|
|
if (ext.first != &back._M_pathname)
|
|
_GLIBCXX_THROW_OR_ABORT(
|
|
std::logic_error("path::replace_extension failed"));
|
|
_M_pathname.erase(back._M_pos + ext.second);
|
|
}
|
|
}
|
|
// If replacement is not empty and does not begin with a dot character,
|
|
// a dot character is appended
|
|
if (!replacement.empty() && replacement.native()[0] != dot)
|
|
_M_pathname += dot;
|
|
operator+=(replacement);
|
|
return *this;
|
|
}
|
|
|
|
namespace
|
|
{
|
|
template<typename Iter1, typename Iter2>
|
|
int do_compare(Iter1 begin1, Iter1 end1, Iter2 begin2, Iter2 end2)
|
|
{
|
|
int cmpt = 1;
|
|
while (begin1 != end1 && begin2 != end2)
|
|
{
|
|
if (begin1->native() < begin2->native())
|
|
return -cmpt;
|
|
if (begin1->native() > begin2->native())
|
|
return +cmpt;
|
|
++begin1;
|
|
++begin2;
|
|
++cmpt;
|
|
}
|
|
if (begin1 == end1)
|
|
{
|
|
if (begin2 == end2)
|
|
return 0;
|
|
return -cmpt;
|
|
}
|
|
return +cmpt;
|
|
}
|
|
}
|
|
|
|
int
|
|
path::compare(const path& p) const noexcept
|
|
{
|
|
struct CmptRef
|
|
{
|
|
const path* ptr;
|
|
const string_type& native() const noexcept { return ptr->native(); }
|
|
};
|
|
|
|
if (empty() && p.empty())
|
|
return 0;
|
|
else if (_M_type == _Type::_Multi && p._M_type == _Type::_Multi)
|
|
return do_compare(_M_cmpts.begin(), _M_cmpts.end(),
|
|
p._M_cmpts.begin(), p._M_cmpts.end());
|
|
else if (_M_type == _Type::_Multi)
|
|
{
|
|
CmptRef c[1] = { { &p } };
|
|
return do_compare(_M_cmpts.begin(), _M_cmpts.end(), c, c+1);
|
|
}
|
|
else if (p._M_type == _Type::_Multi)
|
|
{
|
|
CmptRef c[1] = { { this } };
|
|
return do_compare(c, c+1, p._M_cmpts.begin(), p._M_cmpts.end());
|
|
}
|
|
else
|
|
return _M_pathname.compare(p._M_pathname);
|
|
}
|
|
|
|
path
|
|
path::root_name() const
|
|
{
|
|
path __ret;
|
|
if (_M_type == _Type::_Root_name)
|
|
__ret = *this;
|
|
else if (_M_cmpts.size() && _M_cmpts.begin()->_M_type == _Type::_Root_name)
|
|
__ret = *_M_cmpts.begin();
|
|
return __ret;
|
|
}
|
|
|
|
path
|
|
path::root_directory() const
|
|
{
|
|
path __ret;
|
|
if (_M_type == _Type::_Root_dir)
|
|
{
|
|
__ret._M_type = _Type::_Root_dir;
|
|
__ret._M_pathname.assign(1, preferred_separator);
|
|
}
|
|
else if (!_M_cmpts.empty())
|
|
{
|
|
auto __it = _M_cmpts.begin();
|
|
if (__it->_M_type == _Type::_Root_name)
|
|
++__it;
|
|
if (__it != _M_cmpts.end() && __it->_M_type == _Type::_Root_dir)
|
|
__ret = *__it;
|
|
}
|
|
return __ret;
|
|
}
|
|
|
|
path
|
|
path::root_path() const
|
|
{
|
|
path __ret;
|
|
if (_M_type == _Type::_Root_name)
|
|
__ret = *this;
|
|
else if (_M_type == _Type::_Root_dir)
|
|
{
|
|
__ret._M_pathname.assign(1, preferred_separator);
|
|
__ret._M_type = _Type::_Root_dir;
|
|
}
|
|
else if (!_M_cmpts.empty())
|
|
{
|
|
auto __it = _M_cmpts.begin();
|
|
if (__it->_M_type == _Type::_Root_name)
|
|
{
|
|
__ret = *__it++;
|
|
if (__it != _M_cmpts.end() && __it->_M_type == _Type::_Root_dir)
|
|
__ret /= *__it;
|
|
}
|
|
else if (__it->_M_type == _Type::_Root_dir)
|
|
__ret = *__it;
|
|
}
|
|
return __ret;
|
|
}
|
|
|
|
path
|
|
path::relative_path() const
|
|
{
|
|
path __ret;
|
|
if (_M_type == _Type::_Filename)
|
|
__ret = *this;
|
|
else if (!_M_cmpts.empty())
|
|
{
|
|
auto __it = _M_cmpts.begin();
|
|
if (__it->_M_type == _Type::_Root_name)
|
|
++__it;
|
|
if (__it != _M_cmpts.end() && __it->_M_type == _Type::_Root_dir)
|
|
++__it;
|
|
if (__it != _M_cmpts.end())
|
|
__ret.assign(_M_pathname.substr(__it->_M_pos));
|
|
}
|
|
return __ret;
|
|
}
|
|
|
|
path
|
|
path::parent_path() const
|
|
{
|
|
path __ret;
|
|
if (!has_relative_path())
|
|
__ret = *this;
|
|
else if (_M_cmpts.size() >= 2)
|
|
{
|
|
for (auto __it = _M_cmpts.begin(), __end = std::prev(_M_cmpts.end());
|
|
__it != __end; ++__it)
|
|
{
|
|
__ret /= *__it;
|
|
}
|
|
}
|
|
return __ret;
|
|
}
|
|
|
|
bool
|
|
path::has_root_name() const
|
|
{
|
|
if (_M_type == _Type::_Root_name)
|
|
return true;
|
|
if (!_M_cmpts.empty() && _M_cmpts.begin()->_M_type == _Type::_Root_name)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
bool
|
|
path::has_root_directory() const
|
|
{
|
|
if (_M_type == _Type::_Root_dir)
|
|
return true;
|
|
if (!_M_cmpts.empty())
|
|
{
|
|
auto __it = _M_cmpts.begin();
|
|
if (__it->_M_type == _Type::_Root_name)
|
|
++__it;
|
|
if (__it != _M_cmpts.end() && __it->_M_type == _Type::_Root_dir)
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool
|
|
path::has_root_path() const
|
|
{
|
|
if (_M_type == _Type::_Root_name || _M_type == _Type::_Root_dir)
|
|
return true;
|
|
if (!_M_cmpts.empty())
|
|
{
|
|
auto __type = _M_cmpts.front()._M_type;
|
|
if (__type == _Type::_Root_name || __type == _Type::_Root_dir)
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool
|
|
path::has_relative_path() const
|
|
{
|
|
if (_M_type == _Type::_Filename && !_M_pathname.empty())
|
|
return true;
|
|
if (!_M_cmpts.empty())
|
|
{
|
|
auto __it = _M_cmpts.begin();
|
|
if (__it->_M_type == _Type::_Root_name)
|
|
++__it;
|
|
if (__it != _M_cmpts.end() && __it->_M_type == _Type::_Root_dir)
|
|
++__it;
|
|
if (__it != _M_cmpts.end() && !__it->_M_pathname.empty())
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
bool
|
|
path::has_parent_path() const
|
|
{
|
|
if (!has_relative_path())
|
|
return !empty();
|
|
return _M_cmpts.size() >= 2;
|
|
}
|
|
|
|
bool
|
|
path::has_filename() const
|
|
{
|
|
if (empty())
|
|
return false;
|
|
if (_M_type == _Type::_Filename)
|
|
return !_M_pathname.empty();
|
|
if (_M_type == _Type::_Multi)
|
|
{
|
|
if (_M_pathname.back() == preferred_separator)
|
|
return false;
|
|
return _M_cmpts.back().has_filename();
|
|
}
|
|
return false;
|
|
}
|
|
|
|
namespace
|
|
{
|
|
inline bool is_dot(fs::path::value_type c) { return c == dot; }
|
|
|
|
inline bool is_dot(const fs::path& path)
|
|
{
|
|
const auto& filename = path.native();
|
|
return filename.size() == 1 && is_dot(filename[0]);
|
|
}
|
|
|
|
inline bool is_dotdot(const fs::path& path)
|
|
{
|
|
const auto& filename = path.native();
|
|
return filename.size() == 2 && is_dot(filename[0]) && is_dot(filename[1]);
|
|
}
|
|
} // namespace
|
|
|
|
path
|
|
path::lexically_normal() const
|
|
{
|
|
/*
|
|
C++17 [fs.path.generic] p6
|
|
- If the path is empty, stop.
|
|
- Replace each slash character in the root-name with a preferred-separator.
|
|
- Replace each directory-separator with a preferred-separator.
|
|
- Remove each dot filename and any immediately following directory-separator.
|
|
- As long as any appear, remove a non-dot-dot filename immediately followed
|
|
by a directory-separator and a dot-dot filename, along with any immediately
|
|
following directory-separator.
|
|
- If there is a root-directory, remove all dot-dot filenames and any
|
|
directory-separators immediately following them.
|
|
- If the last filename is dot-dot, remove any trailing directory-separator.
|
|
- If the path is empty, add a dot.
|
|
*/
|
|
path ret;
|
|
// If the path is empty, stop.
|
|
if (empty())
|
|
return ret;
|
|
for (auto& p : *this)
|
|
{
|
|
#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
|
|
// Replace each slash character in the root-name
|
|
if (p._M_type == _Type::_Root_name || p._M_type == _Type::_Root_dir)
|
|
{
|
|
string_type s = p.native();
|
|
std::replace(s.begin(), s.end(), L'/', L'\\');
|
|
ret /= s;
|
|
continue;
|
|
}
|
|
#endif
|
|
if (is_dotdot(p))
|
|
{
|
|
if (ret.has_filename())
|
|
{
|
|
// remove a non-dot-dot filename immediately followed by /..
|
|
if (!is_dotdot(ret.filename()))
|
|
ret.remove_filename();
|
|
else
|
|
ret /= p;
|
|
}
|
|
else if (!ret.has_relative_path())
|
|
{
|
|
// remove a dot-dot filename immediately after root-directory
|
|
if (!ret.has_root_directory())
|
|
ret /= p;
|
|
}
|
|
else
|
|
{
|
|
// Got a path with a relative path (i.e. at least one non-root
|
|
// element) and no filename at the end (i.e. empty last element),
|
|
// so must have a trailing slash. See what is before it.
|
|
auto elem = std::prev(ret.end(), 2);
|
|
if (elem->has_filename() && !is_dotdot(*elem))
|
|
{
|
|
// Remove the filename before the trailing slash
|
|
// (equiv. to ret = ret.parent_path().remove_filename())
|
|
|
|
if (elem == ret.begin())
|
|
ret.clear();
|
|
else
|
|
{
|
|
ret._M_pathname.erase(elem._M_cur->_M_pos);
|
|
// Do we still have a trailing slash?
|
|
if (std::prev(elem)->_M_type == _Type::_Filename)
|
|
ret._M_cmpts.erase(elem._M_cur);
|
|
else
|
|
ret._M_cmpts.erase(elem._M_cur, ret._M_cmpts.end());
|
|
}
|
|
}
|
|
else // ???
|
|
ret /= p;
|
|
}
|
|
}
|
|
else if (is_dot(p))
|
|
ret /= path();
|
|
else
|
|
ret /= p;
|
|
}
|
|
|
|
if (ret._M_cmpts.size() >= 2)
|
|
{
|
|
auto back = std::prev(ret.end());
|
|
// If the last filename is dot-dot, ...
|
|
if (back->empty() && is_dotdot(*std::prev(back)))
|
|
// ... remove any trailing directory-separator.
|
|
ret = ret.parent_path();
|
|
}
|
|
// If the path is empty, add a dot.
|
|
else if (ret.empty())
|
|
ret = ".";
|
|
|
|
return ret;
|
|
}
|
|
|
|
path
|
|
path::lexically_relative(const path& base) const
|
|
{
|
|
path ret;
|
|
if (root_name() != base.root_name())
|
|
return ret;
|
|
if (is_absolute() != base.is_absolute())
|
|
return ret;
|
|
if (!has_root_directory() && base.has_root_directory())
|
|
return ret;
|
|
auto [a, b] = std::mismatch(begin(), end(), base.begin(), base.end());
|
|
if (a == end() && b == base.end())
|
|
ret = ".";
|
|
else
|
|
{
|
|
int n = 0;
|
|
for (; b != base.end(); ++b)
|
|
{
|
|
const path& p = *b;
|
|
if (is_dotdot(p))
|
|
--n;
|
|
else if (!p.empty() && !is_dot(p))
|
|
++n;
|
|
}
|
|
if (n == 0 && (a == end() || a->empty()))
|
|
ret = ".";
|
|
else if (n >= 0)
|
|
{
|
|
const path dotdot("..");
|
|
while (n--)
|
|
ret /= dotdot;
|
|
for (; a != end(); ++a)
|
|
ret /= *a;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
path
|
|
path::lexically_proximate(const path& base) const
|
|
{
|
|
path rel = lexically_relative(base);
|
|
if (rel.empty())
|
|
rel = *this;
|
|
return rel;
|
|
}
|
|
|
|
std::pair<const path::string_type*, std::size_t>
|
|
path::_M_find_extension() const
|
|
{
|
|
const string_type* s = nullptr;
|
|
|
|
if (_M_type == _Type::_Filename)
|
|
s = &_M_pathname;
|
|
else if (_M_type == _Type::_Multi && !_M_cmpts.empty())
|
|
{
|
|
const auto& c = _M_cmpts.back();
|
|
if (c._M_type == _Type::_Filename)
|
|
s = &c._M_pathname;
|
|
}
|
|
|
|
if (s)
|
|
{
|
|
if (auto sz = s->size())
|
|
{
|
|
if (sz <= 2 && (*s)[0] == dot)
|
|
return { s, string_type::npos };
|
|
const auto pos = s->rfind(dot);
|
|
return { s, pos ? pos : string_type::npos };
|
|
}
|
|
}
|
|
return {};
|
|
}
|
|
|
|
void
|
|
path::_M_split_cmpts()
|
|
{
|
|
_M_cmpts.clear();
|
|
if (_M_pathname.empty())
|
|
{
|
|
_M_type = _Type::_Filename;
|
|
return;
|
|
}
|
|
_M_type = _Type::_Multi;
|
|
|
|
size_t pos = 0;
|
|
const size_t len = _M_pathname.size();
|
|
|
|
// look for root name or root directory
|
|
if (_S_is_dir_sep(_M_pathname[0]))
|
|
{
|
|
#ifdef __CYGWIN__
|
|
// look for root name, such as "//foo"
|
|
if (len > 2 && _M_pathname[1] == _M_pathname[0])
|
|
{
|
|
if (!_S_is_dir_sep(_M_pathname[2]))
|
|
{
|
|
// got root name, find its end
|
|
pos = 3;
|
|
while (pos < len && !_S_is_dir_sep(_M_pathname[pos]))
|
|
++pos;
|
|
_M_add_root_name(pos);
|
|
if (pos < len) // also got root directory
|
|
_M_add_root_dir(pos);
|
|
}
|
|
else
|
|
{
|
|
// got something like "///foo" which is just a root directory
|
|
// composed of multiple redundant directory separators
|
|
_M_add_root_dir(0);
|
|
}
|
|
}
|
|
else
|
|
#endif
|
|
{
|
|
// got root directory
|
|
if (_M_pathname.find_first_not_of('/') == string_type::npos)
|
|
{
|
|
// entire path is just slashes
|
|
_M_type = _Type::_Root_dir;
|
|
return;
|
|
}
|
|
_M_add_root_dir(0);
|
|
++pos;
|
|
}
|
|
}
|
|
#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
|
|
else if (len > 1 && _M_pathname[1] == L':')
|
|
{
|
|
// got disk designator
|
|
_M_add_root_name(2);
|
|
if (len > 2 && _S_is_dir_sep(_M_pathname[2]))
|
|
_M_add_root_dir(2);
|
|
pos = 2;
|
|
}
|
|
#endif
|
|
|
|
size_t back = pos;
|
|
while (pos < len)
|
|
{
|
|
if (_S_is_dir_sep(_M_pathname[pos]))
|
|
{
|
|
if (back != pos)
|
|
_M_add_filename(back, pos - back);
|
|
back = ++pos;
|
|
}
|
|
else
|
|
++pos;
|
|
}
|
|
|
|
if (back != pos)
|
|
_M_add_filename(back, pos - back);
|
|
else if (_S_is_dir_sep(_M_pathname.back()))
|
|
{
|
|
// [fs.path.itr]/4
|
|
// An empty element, if trailing non-root directory-separator present.
|
|
if (_M_cmpts.back()._M_type == _Type::_Filename)
|
|
{
|
|
pos = _M_pathname.size();
|
|
_M_cmpts.emplace_back(string_type(), _Type::_Filename, pos);
|
|
}
|
|
}
|
|
|
|
_M_trim();
|
|
}
|
|
|
|
void
|
|
path::_M_add_root_name(size_t n)
|
|
{
|
|
_M_cmpts.emplace_back(_M_pathname.substr(0, n), _Type::_Root_name, 0);
|
|
}
|
|
|
|
void
|
|
path::_M_add_root_dir(size_t pos)
|
|
{
|
|
_M_cmpts.emplace_back(_M_pathname.substr(pos, 1), _Type::_Root_dir, pos);
|
|
}
|
|
|
|
void
|
|
path::_M_add_filename(size_t pos, size_t n)
|
|
{
|
|
_M_cmpts.emplace_back(_M_pathname.substr(pos, n), _Type::_Filename, pos);
|
|
}
|
|
|
|
void
|
|
path::_M_trim()
|
|
{
|
|
if (_M_cmpts.size() == 1)
|
|
{
|
|
_M_type = _M_cmpts.front()._M_type;
|
|
_M_cmpts.clear();
|
|
}
|
|
}
|
|
|
|
path::string_type
|
|
path::_S_convert_loc(const char* __first, const char* __last,
|
|
const std::locale& __loc)
|
|
{
|
|
#if _GLIBCXX_USE_WCHAR_T
|
|
auto& __cvt = std::use_facet<codecvt<wchar_t, char, mbstate_t>>(__loc);
|
|
basic_string<wchar_t> __ws;
|
|
if (!__str_codecvt_in(__first, __last, __ws, __cvt))
|
|
_GLIBCXX_THROW_OR_ABORT(filesystem_error(
|
|
"Cannot convert character sequence",
|
|
std::make_error_code(errc::illegal_byte_sequence)));
|
|
#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
|
|
return __ws;
|
|
#else
|
|
return _Cvt<wchar_t>::_S_convert(__ws.data(), __ws.data() + __ws.size());
|
|
#endif
|
|
#else
|
|
return {__first, __last};
|
|
#endif
|
|
}
|
|
|
|
std::size_t
|
|
fs::hash_value(const path& p) noexcept
|
|
{
|
|
// [path.non-member]
|
|
// "If for two paths, p1 == p2 then hash_value(p1) == hash_value(p2)."
|
|
// Equality works as if by traversing the range [begin(), end()), meaning
|
|
// e.g. path("a//b") == path("a/b"), so we cannot simply hash _M_pathname
|
|
// but need to iterate over individual elements. Use the hash_combine from
|
|
// http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3876.pdf
|
|
size_t seed = 0;
|
|
for (const auto& x : p)
|
|
{
|
|
seed ^= std::hash<path::string_type>()(x.native()) + 0x9e3779b9
|
|
+ (seed<<6) + (seed>>2);
|
|
}
|
|
return seed;
|
|
}
|
|
|
|
struct fs::filesystem_error::_Impl
|
|
{
|
|
_Impl(const string& what_arg, const path& p1, const path& p2)
|
|
: path1(p1), path2(p2), what(make_what(what_arg, &p1, &p2))
|
|
{ }
|
|
|
|
_Impl(const string& what_arg, const path& p1)
|
|
: path1(p1), path2(), what(make_what(what_arg, &p1, nullptr))
|
|
{ }
|
|
|
|
_Impl(const string& what_arg)
|
|
: what(make_what(what_arg, nullptr, nullptr))
|
|
{ }
|
|
|
|
static std::string
|
|
make_what(const std::string& s, const path* p1, const path* p2)
|
|
{
|
|
const std::string pstr1 = p1 ? p1->u8string() : std::string{};
|
|
const std::string pstr2 = p2 ? p2->u8string() : std::string{};
|
|
const size_t len = 18 + s.length()
|
|
+ (pstr1.length() ? pstr1.length() + 3 : 0)
|
|
+ (pstr2.length() ? pstr2.length() + 3 : 0);
|
|
std::string w;
|
|
w.reserve(len);
|
|
w = "filesystem error: ";
|
|
w += s;
|
|
if (p1)
|
|
{
|
|
w += " [";
|
|
w += pstr1;
|
|
w += ']';
|
|
if (p2)
|
|
{
|
|
w += " [";
|
|
w += pstr2;
|
|
w += ']';
|
|
}
|
|
}
|
|
return w;
|
|
}
|
|
|
|
path path1;
|
|
path path2;
|
|
std::string what;
|
|
};
|
|
|
|
template class std::__shared_ptr<const fs::filesystem_error::_Impl>;
|
|
|
|
fs::filesystem_error::
|
|
filesystem_error(const string& what_arg, error_code ec)
|
|
: system_error(ec, what_arg),
|
|
_M_impl(std::__make_shared<_Impl>(what_arg))
|
|
{ }
|
|
|
|
fs::filesystem_error::
|
|
filesystem_error(const string& what_arg, const path& p1, error_code ec)
|
|
: system_error(ec, what_arg),
|
|
_M_impl(std::__make_shared<_Impl>(what_arg, p1))
|
|
{ }
|
|
|
|
fs::filesystem_error::
|
|
filesystem_error(const string& what_arg, const path& p1, const path& p2,
|
|
error_code ec)
|
|
: system_error(ec, what_arg),
|
|
_M_impl(std::__make_shared<_Impl>(what_arg, p1, p2))
|
|
{ }
|
|
|
|
fs::filesystem_error::~filesystem_error() = default;
|
|
|
|
const fs::path&
|
|
fs::filesystem_error::path1() const noexcept
|
|
{ return _M_impl->path1; }
|
|
|
|
const fs::path&
|
|
fs::filesystem_error::path2() const noexcept
|
|
{ return _M_impl->path2; }
|
|
|
|
const char*
|
|
fs::filesystem_error::what() const noexcept
|
|
{ return _M_impl->what.c_str(); }
|