1 // Class experimental::filesystem::path -*- C++ -*-
3 // Copyright (C) 2014-2018 Free Software Foundation, Inc.
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)
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
29 #include <experimental/filesystem>
31 namespace fs
= std::experimental::filesystem
;
34 fs::filesystem_error::~filesystem_error() = default;
36 constexpr path::value_type
path::preferred_separator
[[gnu::used
]];
39 path::remove_filename()
41 if (_M_type
== _Type::_Multi
)
43 if (!_M_cmpts
.empty())
45 auto cmpt
= std::prev(_M_cmpts
.end());
46 _M_pathname
.erase(cmpt
->_M_pos
);
57 path::replace_filename(const path
& replacement
)
60 operator/=(replacement
);
64 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
65 const fs::path::value_type dot
= L
'.';
67 const fs::path::value_type dot
= '.';
71 path::replace_extension(const path
& replacement
)
73 auto ext
= _M_find_extension();
74 if (ext
.first
&& ext
.second
!= string_type::npos
)
76 if (ext
.first
== &_M_pathname
)
77 _M_pathname
.erase(ext
.second
);
80 const auto& back
= _M_cmpts
.back();
81 if (ext
.first
!= &back
._M_pathname
)
82 _GLIBCXX_THROW_OR_ABORT(
83 std::logic_error("path::replace_extension failed"));
84 _M_pathname
.erase(back
._M_pos
+ ext
.second
);
87 if (!replacement
.empty() && replacement
.native()[0] != dot
)
89 _M_pathname
+= replacement
.native();
96 template<typename Iter1
, typename Iter2
>
97 int do_compare(Iter1 begin1
, Iter1 end1
, Iter2 begin2
, Iter2 end2
)
100 while (begin1
!= end1
&& begin2
!= end2
)
102 if (begin1
->native() < begin2
->native())
104 if (begin1
->native() > begin2
->native())
121 path::compare(const path
& p
) const noexcept
126 const string_type
& native() const noexcept
{ return ptr
->native(); }
129 if (_M_type
== _Type::_Multi
&& p
._M_type
== _Type::_Multi
)
130 return do_compare(_M_cmpts
.begin(), _M_cmpts
.end(),
131 p
._M_cmpts
.begin(), p
._M_cmpts
.end());
132 else if (_M_type
== _Type::_Multi
)
134 CmptRef c
[1] = { { &p
} };
135 return do_compare(_M_cmpts
.begin(), _M_cmpts
.end(), c
, c
+1);
137 else if (p
._M_type
== _Type::_Multi
)
139 CmptRef c
[1] = { { this } };
140 return do_compare(c
, c
+1, p
._M_cmpts
.begin(), p
._M_cmpts
.end());
143 return _M_pathname
.compare(p
._M_pathname
);
147 path::root_name() const
150 if (_M_type
== _Type::_Root_name
)
152 else if (_M_cmpts
.size() && _M_cmpts
.begin()->_M_type
== _Type::_Root_name
)
153 __ret
= *_M_cmpts
.begin();
158 path::root_directory() const
161 if (_M_type
== _Type::_Root_dir
)
163 else if (!_M_cmpts
.empty())
165 auto __it
= _M_cmpts
.begin();
166 if (__it
->_M_type
== _Type::_Root_name
)
168 if (__it
!= _M_cmpts
.end() && __it
->_M_type
== _Type::_Root_dir
)
176 path::root_path() const
179 if (_M_type
== _Type::_Root_name
|| _M_type
== _Type::_Root_dir
)
181 else if (!_M_cmpts
.empty())
183 auto __it
= _M_cmpts
.begin();
184 if (__it
->_M_type
== _Type::_Root_name
)
187 if (__it
!= _M_cmpts
.end() && __it
->_M_type
== _Type::_Root_dir
)
189 __ret
._M_pathname
+= preferred_separator
;
190 __ret
._M_split_cmpts();
193 else if (__it
->_M_type
== _Type::_Root_dir
)
200 path::relative_path() const
203 if (_M_type
== _Type::_Filename
)
205 else if (!_M_cmpts
.empty())
207 auto __it
= _M_cmpts
.begin();
208 if (__it
->_M_type
== _Type::_Root_name
)
210 if (__it
!= _M_cmpts
.end() && __it
->_M_type
== _Type::_Root_dir
)
212 if (__it
!= _M_cmpts
.end())
213 __ret
.assign(_M_pathname
.substr(__it
->_M_pos
));
219 path::parent_path() const
222 if (_M_cmpts
.size() < 2)
224 for (auto __it
= _M_cmpts
.begin(), __end
= std::prev(_M_cmpts
.end());
225 __it
!= __end
; ++__it
)
233 path::has_root_name() const
235 if (_M_type
== _Type::_Root_name
)
237 if (!_M_cmpts
.empty() && _M_cmpts
.begin()->_M_type
== _Type::_Root_name
)
243 path::has_root_directory() const
245 if (_M_type
== _Type::_Root_dir
)
247 if (!_M_cmpts
.empty())
249 auto __it
= _M_cmpts
.begin();
250 if (__it
->_M_type
== _Type::_Root_name
)
252 if (__it
!= _M_cmpts
.end() && __it
->_M_type
== _Type::_Root_dir
)
259 path::has_root_path() const
261 if (_M_type
== _Type::_Root_name
|| _M_type
== _Type::_Root_dir
)
263 if (!_M_cmpts
.empty())
265 auto __type
= _M_cmpts
.front()._M_type
;
266 if (__type
== _Type::_Root_name
|| __type
== _Type::_Root_dir
)
273 path::has_relative_path() const
275 if (_M_type
== _Type::_Filename
)
277 if (!_M_cmpts
.empty())
279 auto __it
= _M_cmpts
.begin();
280 if (__it
->_M_type
== _Type::_Root_name
)
282 if (__it
!= _M_cmpts
.end() && __it
->_M_type
== _Type::_Root_dir
)
284 if (__it
!= _M_cmpts
.end())
292 path::has_parent_path() const
294 return _M_cmpts
.size() > 1;
298 path::has_filename() const
303 std::pair
<const path::string_type
*, std::size_t>
304 path::_M_find_extension() const
306 const string_type
* s
= nullptr;
308 if (_M_type
!= _Type::_Multi
)
310 else if (!_M_cmpts
.empty())
312 const auto& c
= _M_cmpts
.back();
313 if (c
._M_type
== _Type::_Filename
)
319 if (auto sz
= s
->size())
321 if (sz
<= 2 && (*s
)[0] == dot
)
323 if (sz
== 1 || (*s
)[1] == dot
) // filename is "." or ".."
324 return { s
, string_type::npos
};
326 return { s
, 0 }; // filename is like ".?"
328 return { s
, s
->rfind(dot
) };
335 path::_M_split_cmpts()
337 _M_type
= _Type::_Multi
;
340 if (_M_pathname
.empty())
344 const size_t len
= _M_pathname
.size();
346 // look for root name or root directory
347 if (_S_is_dir_sep(_M_pathname
[0]))
349 // look for root name, such as "//" or "//foo"
350 if (len
> 1 && _M_pathname
[1] == _M_pathname
[0])
354 // entire path is just "//"
355 _M_type
= _Type::_Root_name
;
359 if (!_S_is_dir_sep(_M_pathname
[2]))
361 // got root name, find its end
363 while (pos
< len
&& !_S_is_dir_sep(_M_pathname
[pos
]))
365 _M_add_root_name(pos
);
366 if (pos
< len
) // also got root directory
367 _M_add_root_dir(pos
);
371 // got something like "///foo" which is just a root directory
372 // composed of multiple redundant directory separators
376 else // got root directory
380 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
381 else if (len
> 1 && _M_pathname
[1] == L
':')
383 // got disk designator
385 if (len
> 2 && _S_is_dir_sep(_M_pathname
[2]))
394 if (_S_is_dir_sep(_M_pathname
[pos
]))
397 _M_add_filename(back
, pos
- back
);
405 _M_add_filename(back
, pos
- back
);
406 else if (_S_is_dir_sep(_M_pathname
.back()))
409 // "Dot, if one or more trailing non-root slash characters are present."
410 if (_M_cmpts
.back()._M_type
== _Type::_Filename
)
412 const auto& last
= _M_cmpts
.back();
413 pos
= last
._M_pos
+ last
._M_pathname
.size();
414 _M_cmpts
.emplace_back(string_type(1, dot
), _Type::_Filename
, pos
);
422 path::_M_add_root_name(size_t n
)
424 _M_cmpts
.emplace_back(_M_pathname
.substr(0, n
), _Type::_Root_name
, 0);
428 path::_M_add_root_dir(size_t pos
)
430 _M_cmpts
.emplace_back(_M_pathname
.substr(pos
, 1), _Type::_Root_dir
, pos
);
434 path::_M_add_filename(size_t pos
, size_t n
)
436 _M_cmpts
.emplace_back(_M_pathname
.substr(pos
, n
), _Type::_Filename
, pos
);
442 if (_M_cmpts
.size() == 1)
444 _M_type
= _M_cmpts
.front()._M_type
;
450 path::_S_convert_loc(const char* __first
, const char* __last
,
451 const std::locale
& __loc
)
453 #if _GLIBCXX_USE_WCHAR_T
454 auto& __cvt
= std::use_facet
<codecvt
<wchar_t, char, mbstate_t>>(__loc
);
455 basic_string
<wchar_t> __ws
;
456 if (!__str_codecvt_in(__first
, __last
, __ws
, __cvt
))
457 _GLIBCXX_THROW_OR_ABORT(filesystem_error(
458 "Cannot convert character sequence",
459 std::make_error_code(errc::illegal_byte_sequence
)));
460 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
463 return _Cvt
<wchar_t>::_S_convert(__ws
.data(), __ws
.data() + __ws
.size());
466 return {__first
, __last
};
471 fs::hash_value(const path
& p
) noexcept
474 // "If for two paths, p1 == p2 then hash_value(p1) == hash_value(p2)."
475 // Equality works as if by traversing the range [begin(), end()), meaning
476 // e.g. path("a//b") == path("a/b"), so we cannot simply hash _M_pathname
477 // but need to iterate over individual elements. Use the hash_combine from
478 // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3876.pdf
480 for (const auto& x
: p
)
482 seed
^= std::hash
<path::string_type
>()(x
.native()) + 0x9e3779b9
483 + (seed
<<6) + (seed
>>2);
488 #include <experimental/string_view>
491 fs::filesystem_error::_M_gen_what()
493 const std::string pstr1
= _M_path1
.u8string();
494 const std::string pstr2
= _M_path2
.u8string();
495 experimental::string_view s
= this->system_error::what();
496 const size_t len
= 18 + s
.length()
497 + (pstr1
.length() ? pstr1
.length() + 3 : 0)
498 + (pstr2
.length() ? pstr2
.length() + 3 : 0);
501 w
= "filesystem error: ";
502 w
.append(s
.data(), s
.length());