pr86900.C: XFAIL AIX.
[official-gcc.git] / libstdc++-v3 / src / filesystem / path.cc
blob63da684cf0a8032ad55d4a25d509807905a7e479
1 // Class experimental::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 <experimental/filesystem>
31 namespace fs = std::experimental::filesystem;
32 using fs::path;
34 fs::filesystem_error::~filesystem_error() = default;
36 constexpr path::value_type path::preferred_separator [[gnu::used]];
38 path&
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);
47 _M_cmpts.erase(cmpt);
48 _M_trim();
51 else
52 clear();
53 return *this;
56 path&
57 path::replace_filename(const path& replacement)
59 remove_filename();
60 operator/=(replacement);
61 return *this;
64 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
65 const fs::path::value_type dot = L'.';
66 #else
67 const fs::path::value_type dot = '.';
68 #endif
70 path&
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);
78 else
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)
88 _M_pathname += dot;
89 _M_pathname += replacement.native();
90 _M_split_cmpts();
91 return *this;
94 namespace
96 template<typename Iter1, typename Iter2>
97 int do_compare(Iter1 begin1, Iter1 end1, Iter2 begin2, Iter2 end2)
99 int cmpt = 1;
100 while (begin1 != end1 && begin2 != end2)
102 if (begin1->native() < begin2->native())
103 return -cmpt;
104 if (begin1->native() > begin2->native())
105 return +cmpt;
106 ++begin1;
107 ++begin2;
108 ++cmpt;
110 if (begin1 == end1)
112 if (begin2 == end2)
113 return 0;
114 return -cmpt;
116 return +cmpt;
121 path::compare(const path& p) const noexcept
123 struct CmptRef
125 const path* ptr;
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());
142 else
143 return _M_pathname.compare(p._M_pathname);
146 path
147 path::root_name() const
149 path __ret;
150 if (_M_type == _Type::_Root_name)
151 __ret = *this;
152 else if (_M_cmpts.size() && _M_cmpts.begin()->_M_type == _Type::_Root_name)
153 __ret = *_M_cmpts.begin();
154 return __ret;
157 path
158 path::root_directory() const
160 path __ret;
161 if (_M_type == _Type::_Root_dir)
162 __ret = *this;
163 else if (!_M_cmpts.empty())
165 auto __it = _M_cmpts.begin();
166 if (__it->_M_type == _Type::_Root_name)
167 ++__it;
168 if (__it != _M_cmpts.end() && __it->_M_type == _Type::_Root_dir)
169 __ret = *__it;
171 return __ret;
175 path
176 path::root_path() const
178 path __ret;
179 if (_M_type == _Type::_Root_name || _M_type == _Type::_Root_dir)
180 __ret = *this;
181 else if (!_M_cmpts.empty())
183 auto __it = _M_cmpts.begin();
184 if (__it->_M_type == _Type::_Root_name)
186 __ret = *__it++;
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)
194 __ret = *__it;
196 return __ret;
199 path
200 path::relative_path() const
202 path __ret;
203 if (_M_type == _Type::_Filename)
204 __ret = *this;
205 else if (!_M_cmpts.empty())
207 auto __it = _M_cmpts.begin();
208 if (__it->_M_type == _Type::_Root_name)
209 ++__it;
210 if (__it != _M_cmpts.end() && __it->_M_type == _Type::_Root_dir)
211 ++__it;
212 if (__it != _M_cmpts.end())
213 __ret.assign(_M_pathname.substr(__it->_M_pos));
215 return __ret;
218 path
219 path::parent_path() const
221 path __ret;
222 if (_M_cmpts.size() < 2)
223 return __ret;
224 for (auto __it = _M_cmpts.begin(), __end = std::prev(_M_cmpts.end());
225 __it != __end; ++__it)
227 __ret /= *__it;
229 return __ret;
232 bool
233 path::has_root_name() const
235 if (_M_type == _Type::_Root_name)
236 return true;
237 if (!_M_cmpts.empty() && _M_cmpts.begin()->_M_type == _Type::_Root_name)
238 return true;
239 return false;
242 bool
243 path::has_root_directory() const
245 if (_M_type == _Type::_Root_dir)
246 return true;
247 if (!_M_cmpts.empty())
249 auto __it = _M_cmpts.begin();
250 if (__it->_M_type == _Type::_Root_name)
251 ++__it;
252 if (__it != _M_cmpts.end() && __it->_M_type == _Type::_Root_dir)
253 return true;
255 return false;
258 bool
259 path::has_root_path() const
261 if (_M_type == _Type::_Root_name || _M_type == _Type::_Root_dir)
262 return true;
263 if (!_M_cmpts.empty())
265 auto __type = _M_cmpts.front()._M_type;
266 if (__type == _Type::_Root_name || __type == _Type::_Root_dir)
267 return true;
269 return false;
272 bool
273 path::has_relative_path() const
275 if (_M_type == _Type::_Filename)
276 return true;
277 if (!_M_cmpts.empty())
279 auto __it = _M_cmpts.begin();
280 if (__it->_M_type == _Type::_Root_name)
281 ++__it;
282 if (__it != _M_cmpts.end() && __it->_M_type == _Type::_Root_dir)
283 ++__it;
284 if (__it != _M_cmpts.end())
285 return true;
287 return false;
291 bool
292 path::has_parent_path() const
294 return _M_cmpts.size() > 1;
297 bool
298 path::has_filename() const
300 return !empty();
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)
309 s = &_M_pathname;
310 else if (!_M_cmpts.empty())
312 const auto& c = _M_cmpts.back();
313 if (c._M_type == _Type::_Filename)
314 s = &c._M_pathname;
317 if (s)
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 };
325 else
326 return { s, 0 }; // filename is like ".?"
328 return { s, s->rfind(dot) };
331 return {};
334 void
335 path::_M_split_cmpts()
337 _M_type = _Type::_Multi;
338 _M_cmpts.clear();
340 if (_M_pathname.empty())
341 return;
343 size_t pos = 0;
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])
352 if (len == 2)
354 // entire path is just "//"
355 _M_type = _Type::_Root_name;
356 return;
359 if (!_S_is_dir_sep(_M_pathname[2]))
361 // got root name, find its end
362 pos = 3;
363 while (pos < len && !_S_is_dir_sep(_M_pathname[pos]))
364 ++pos;
365 _M_add_root_name(pos);
366 if (pos < len) // also got root directory
367 _M_add_root_dir(pos);
369 else
371 // got something like "///foo" which is just a root directory
372 // composed of multiple redundant directory separators
373 _M_add_root_dir(0);
376 else // got root directory
377 _M_add_root_dir(0);
378 ++pos;
380 #ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
381 else if (len > 1 && _M_pathname[1] == L':')
383 // got disk designator
384 _M_add_root_name(2);
385 if (len > 2 && _S_is_dir_sep(_M_pathname[2]))
386 _M_add_root_dir(2);
387 pos = 2;
389 #endif
391 size_t back = pos;
392 while (pos < len)
394 if (_S_is_dir_sep(_M_pathname[pos]))
396 if (back != pos)
397 _M_add_filename(back, pos - back);
398 back = ++pos;
400 else
401 ++pos;
404 if (back != pos)
405 _M_add_filename(back, pos - back);
406 else if (_S_is_dir_sep(_M_pathname.back()))
408 // [path.itr]/8
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);
418 _M_trim();
421 void
422 path::_M_add_root_name(size_t n)
424 _M_cmpts.emplace_back(_M_pathname.substr(0, n), _Type::_Root_name, 0);
427 void
428 path::_M_add_root_dir(size_t pos)
430 _M_cmpts.emplace_back(_M_pathname.substr(pos, 1), _Type::_Root_dir, pos);
433 void
434 path::_M_add_filename(size_t pos, size_t n)
436 _M_cmpts.emplace_back(_M_pathname.substr(pos, n), _Type::_Filename, pos);
439 void
440 path::_M_trim()
442 if (_M_cmpts.size() == 1)
444 _M_type = _M_cmpts.front()._M_type;
445 _M_cmpts.clear();
449 path::string_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
461 return __ws;
462 #else
463 return _Cvt<wchar_t>::_S_convert(__ws.data(), __ws.data() + __ws.size());
464 #endif
465 #else
466 return {__first, __last};
467 #endif
470 std::size_t
471 fs::hash_value(const path& p) noexcept
473 // [path.non-member]
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
479 size_t seed = 0;
480 for (const auto& x : p)
482 seed ^= std::hash<path::string_type>()(x.native()) + 0x9e3779b9
483 + (seed<<6) + (seed>>2);
485 return seed;
488 #include <experimental/string_view>
490 std::string
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);
499 std::string w;
500 w.reserve(len);
501 w = "filesystem error: ";
502 w.append(s.data(), s.length());
503 if (!pstr1.empty())
505 w += " [";
506 w += pstr1;
507 w += ']';
509 if (!pstr1.empty())
511 w += " [";
512 w += pstr2;
513 w += ']';
515 return w;