]> git.ipfire.org Git - thirdparty/gcc.git/blame - libstdc++-v3/src/filesystem/path.cc
Update copyright years.
[thirdparty/gcc.git] / libstdc++-v3 / src / filesystem / path.cc
CommitLineData
641cb5a6 1// Class experimental::filesystem::path -*- C++ -*-
0ca7ba9a 2
83ffe9cd 3// Copyright (C) 2014-2023 Free Software Foundation, Inc.
0ca7ba9a
JW
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.
10
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.
15
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.
19
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/>.
24
8b756210
JW
25#ifndef _GLIBCXX_USE_CXX11_ABI
26# define _GLIBCXX_USE_CXX11_ABI 1
27#endif
28
0ca7ba9a
JW
29#include <experimental/filesystem>
30
641cb5a6
JW
31namespace fs = std::experimental::filesystem;
32using fs::path;
0ca7ba9a 33
641cb5a6 34fs::filesystem_error::~filesystem_error() = default;
5b244a78 35
079638f9 36constexpr path::value_type path::preferred_separator [[gnu::used]];
0ca7ba9a
JW
37
38path&
39path::remove_filename()
40{
41 if (_M_type == _Type::_Multi)
42 {
43 if (!_M_cmpts.empty())
44 {
a00d74c4 45 auto cmpt = std::prev(_M_cmpts.end());
0ca7ba9a
JW
46 _M_pathname.erase(cmpt->_M_pos);
47 _M_cmpts.erase(cmpt);
48 _M_trim();
49 }
50 }
51 else
52 clear();
53 return *this;
54}
55
56path&
57path::replace_filename(const path& replacement)
58{
59 remove_filename();
60 operator/=(replacement);
61 return *this;
62}
63
9534a5e6
JW
64#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
65const fs::path::value_type dot = L'.';
66#else
67const fs::path::value_type dot = '.';
68#endif
69
0ca7ba9a
JW
70path&
71path::replace_extension(const path& replacement)
72{
73 auto ext = _M_find_extension();
74 if (ext.first && ext.second != string_type::npos)
75 {
76 if (ext.first == &_M_pathname)
77 _M_pathname.erase(ext.second);
78 else
79 {
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);
85 }
86 }
9534a5e6
JW
87 if (!replacement.empty() && replacement.native()[0] != dot)
88 _M_pathname += dot;
0ca7ba9a
JW
89 _M_pathname += replacement.native();
90 _M_split_cmpts();
91 return *this;
92}
93
94namespace
95{
96 template<typename Iter1, typename Iter2>
97 int do_compare(Iter1 begin1, Iter1 end1, Iter2 begin2, Iter2 end2)
98 {
99 int cmpt = 1;
100 while (begin1 != end1 && begin2 != end2)
101 {
102 if (begin1->native() < begin2->native())
103 return -cmpt;
104 if (begin1->native() > begin2->native())
105 return +cmpt;
106 ++begin1;
107 ++begin2;
108 ++cmpt;
109 }
110 if (begin1 == end1)
111 {
112 if (begin2 == end2)
113 return 0;
114 return -cmpt;
115 }
116 return +cmpt;
117 }
118}
119
120int
121path::compare(const path& p) const noexcept
122{
4ad37627
JW
123 struct CmptRef
124 {
125 const path* ptr;
126 const string_type& native() const noexcept { return ptr->native(); }
127 };
128
0ca7ba9a
JW
129 if (_M_type == _Type::_Multi && p._M_type == _Type::_Multi)
130 return do_compare(_M_cmpts.begin(), _M_cmpts.end(),
a00d74c4 131 p._M_cmpts.begin(), p._M_cmpts.end());
0ca7ba9a
JW
132 else if (_M_type == _Type::_Multi)
133 {
4ad37627 134 CmptRef c[1] = { { &p } };
0ca7ba9a
JW
135 return do_compare(_M_cmpts.begin(), _M_cmpts.end(), c, c+1);
136 }
137 else if (p._M_type == _Type::_Multi)
138 {
4ad37627 139 CmptRef c[1] = { { this } };
0ca7ba9a
JW
140 return do_compare(c, c+1, p._M_cmpts.begin(), p._M_cmpts.end());
141 }
142 else
143 return _M_pathname.compare(p._M_pathname);
144}
145
146path
147path::root_name() const
148{
149 path __ret;
150 if (_M_type == _Type::_Root_name)
151 __ret = *this;
a00d74c4 152 else if (_M_cmpts.size() && _M_cmpts.begin()->_M_type == _Type::_Root_name)
0ca7ba9a
JW
153 __ret = *_M_cmpts.begin();
154 return __ret;
155}
156
157path
158path::root_directory() const
159{
160 path __ret;
161 if (_M_type == _Type::_Root_dir)
162 __ret = *this;
163 else if (!_M_cmpts.empty())
164 {
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;
170 }
171 return __ret;
172}
173
174
175path
176path::root_path() const
177{
178 path __ret;
179 if (_M_type == _Type::_Root_name || _M_type == _Type::_Root_dir)
180 __ret = *this;
181 else if (!_M_cmpts.empty())
182 {
183 auto __it = _M_cmpts.begin();
184 if (__it->_M_type == _Type::_Root_name)
185 {
186 __ret = *__it++;
187 if (__it != _M_cmpts.end() && __it->_M_type == _Type::_Root_dir)
188 {
189 __ret._M_pathname += preferred_separator;
190 __ret._M_split_cmpts();
191 }
192 }
193 else if (__it->_M_type == _Type::_Root_dir)
194 __ret = *__it;
195 }
196 return __ret;
197}
198
199path
200path::relative_path() const
201{
202 path __ret;
203 if (_M_type == _Type::_Filename)
204 __ret = *this;
205 else if (!_M_cmpts.empty())
206 {
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));
214 }
215 return __ret;
216}
217
218path
219path::parent_path() const
220{
221 path __ret;
222 if (_M_cmpts.size() < 2)
223 return __ret;
a00d74c4
JW
224 for (auto __it = _M_cmpts.begin(), __end = std::prev(_M_cmpts.end());
225 __it != __end; ++__it)
0ca7ba9a
JW
226 {
227 __ret /= *__it;
228 }
229 return __ret;
230}
231
232bool
233path::has_root_name() const
234{
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;
240}
241
242bool
243path::has_root_directory() const
244{
245 if (_M_type == _Type::_Root_dir)
246 return true;
247 if (!_M_cmpts.empty())
248 {
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;
254 }
255 return false;
256}
257
258bool
259path::has_root_path() const
260{
261 if (_M_type == _Type::_Root_name || _M_type == _Type::_Root_dir)
262 return true;
263 if (!_M_cmpts.empty())
264 {
265 auto __type = _M_cmpts.front()._M_type;
266 if (__type == _Type::_Root_name || __type == _Type::_Root_dir)
267 return true;
268 }
269 return false;
270}
271
272bool
273path::has_relative_path() const
274{
275 if (_M_type == _Type::_Filename)
276 return true;
277 if (!_M_cmpts.empty())
278 {
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;
286 }
287 return false;
288}
289
290
291bool
292path::has_parent_path() const
293{
294 return _M_cmpts.size() > 1;
295}
296
297bool
298path::has_filename() const
299{
300 return !empty();
301}
302
303std::pair<const path::string_type*, std::size_t>
304path::_M_find_extension() const
305{
9534a5e6 306 const string_type* s = nullptr;
0ca7ba9a
JW
307
308 if (_M_type != _Type::_Multi)
309 s = &_M_pathname;
310 else if (!_M_cmpts.empty())
311 {
312 const auto& c = _M_cmpts.back();
313 if (c._M_type == _Type::_Filename)
314 s = &c._M_pathname;
315 }
316
317 if (s)
318 {
319 if (auto sz = s->size())
320 {
9534a5e6 321 if (sz <= 2 && (*s)[0] == dot)
0ca7ba9a 322 {
9534a5e6 323 if (sz == 1 || (*s)[1] == dot) // filename is "." or ".."
0ca7ba9a
JW
324 return { s, string_type::npos };
325 else
326 return { s, 0 }; // filename is like ".?"
327 }
9534a5e6 328 return { s, s->rfind(dot) };
0ca7ba9a
JW
329 }
330 }
331 return {};
332}
333
334void
335path::_M_split_cmpts()
336{
337 _M_type = _Type::_Multi;
338 _M_cmpts.clear();
339
e9089e4f
JW
340 // Use const-reference to access _M_pathname, to avoid "leaking" COW string.
341 const auto& pathname = _M_pathname;
342
343 if (pathname.empty())
0ca7ba9a
JW
344 return;
345
441ed45c
JW
346 {
347 // Approximate count of components, to reserve space in _M_cmpts vector:
348 int count = 1;
e9089e4f 349 bool saw_sep_last = _S_is_dir_sep(pathname[0]);
441ed45c 350 bool saw_non_sep = !saw_sep_last;
e9089e4f 351 for (value_type c : pathname)
441ed45c
JW
352 {
353 if (_S_is_dir_sep(c))
354 saw_sep_last = true;
355 else if (saw_sep_last)
356 {
357 ++count;
358 saw_sep_last = false;
359 saw_non_sep = true;
360 }
361 }
362 if (saw_non_sep && saw_sep_last)
363 ++count; // empty filename after trailing slash
364 if (count > 1)
365 _M_cmpts.reserve(count);
366 }
367
0ca7ba9a 368 size_t pos = 0;
e9089e4f 369 const size_t len = pathname.size();
0ca7ba9a
JW
370
371 // look for root name or root directory
e9089e4f 372 if (_S_is_dir_sep(pathname[0]))
0ca7ba9a
JW
373 {
374 // look for root name, such as "//" or "//foo"
e9089e4f 375 if (len > 1 && pathname[1] == pathname[0])
0ca7ba9a
JW
376 {
377 if (len == 2)
378 {
379 // entire path is just "//"
380 _M_type = _Type::_Root_name;
381 return;
382 }
383
e9089e4f 384 if (!_S_is_dir_sep(pathname[2]))
0ca7ba9a
JW
385 {
386 // got root name, find its end
387 pos = 3;
e9089e4f 388 while (pos < len && !_S_is_dir_sep(pathname[pos]))
0ca7ba9a 389 ++pos;
441ed45c
JW
390 if (pos == len)
391 {
392 _M_type = _Type::_Root_name;
393 return;
394 }
0ca7ba9a 395 _M_add_root_name(pos);
441ed45c 396 _M_add_root_dir(pos);
0ca7ba9a
JW
397 }
398 else
399 {
400 // got something like "///foo" which is just a root directory
401 // composed of multiple redundant directory separators
402 _M_add_root_dir(0);
403 }
404 }
441ed45c
JW
405 else if (len == 1) // got root directory only
406 {
407 _M_type = _Type::_Root_dir;
408 return;
409 }
0ca7ba9a
JW
410 else // got root directory
411 _M_add_root_dir(0);
412 ++pos;
413 }
414#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
e9089e4f 415 else if (len > 1 && pathname[1] == L':')
0ca7ba9a
JW
416 {
417 // got disk designator
441ed45c
JW
418 if (len == 2)
419 {
420 _M_type = _Type::_Root_name;
421 return;
422 }
0ca7ba9a 423 _M_add_root_name(2);
e9089e4f 424 if (len > 2 && _S_is_dir_sep(pathname[2]))
0ca7ba9a
JW
425 _M_add_root_dir(2);
426 pos = 2;
427 }
428#endif
441ed45c
JW
429 else
430 {
431 size_t n = 1;
e9089e4f 432 for (; n < pathname.size() && !_S_is_dir_sep(pathname[n]); ++n)
441ed45c 433 { }
e9089e4f 434 if (n == pathname.size())
441ed45c
JW
435 {
436 _M_type = _Type::_Filename;
437 return;
438 }
439 }
0ca7ba9a
JW
440
441 size_t back = pos;
442 while (pos < len)
443 {
e9089e4f 444 if (_S_is_dir_sep(pathname[pos]))
0ca7ba9a
JW
445 {
446 if (back != pos)
447 _M_add_filename(back, pos - back);
448 back = ++pos;
449 }
450 else
451 ++pos;
452 }
453
454 if (back != pos)
455 _M_add_filename(back, pos - back);
e9089e4f 456 else if (_S_is_dir_sep(pathname.back()))
0ca7ba9a
JW
457 {
458 // [path.itr]/8
459 // "Dot, if one or more trailing non-root slash characters are present."
460 if (_M_cmpts.back()._M_type == _Type::_Filename)
461 {
462 const auto& last = _M_cmpts.back();
463 pos = last._M_pos + last._M_pathname.size();
9534a5e6 464 _M_cmpts.emplace_back(string_type(1, dot), _Type::_Filename, pos);
0ca7ba9a
JW
465 }
466 }
467
468 _M_trim();
469}
470
471void
472path::_M_add_root_name(size_t n)
473{
474 _M_cmpts.emplace_back(_M_pathname.substr(0, n), _Type::_Root_name, 0);
475}
476
477void
478path::_M_add_root_dir(size_t pos)
479{
480 _M_cmpts.emplace_back(_M_pathname.substr(pos, 1), _Type::_Root_dir, pos);
481}
482
483void
484path::_M_add_filename(size_t pos, size_t n)
485{
486 _M_cmpts.emplace_back(_M_pathname.substr(pos, n), _Type::_Filename, pos);
487}
488
489void
490path::_M_trim()
491{
492 if (_M_cmpts.size() == 1)
493 {
494 _M_type = _M_cmpts.front()._M_type;
495 _M_cmpts.clear();
496 }
497}
498
499path::string_type
500path::_S_convert_loc(const char* __first, const char* __last,
7fcdbdd2 501 const std::locale& __loc)
0ca7ba9a 502{
7fcdbdd2 503#if _GLIBCXX_USE_WCHAR_T
0ca7ba9a
JW
504 auto& __cvt = std::use_facet<codecvt<wchar_t, char, mbstate_t>>(__loc);
505 basic_string<wchar_t> __ws;
26b1320e 506 if (!__str_codecvt_in_all(__first, __last, __ws, __cvt))
0ca7ba9a
JW
507 _GLIBCXX_THROW_OR_ABORT(filesystem_error(
508 "Cannot convert character sequence",
509 std::make_error_code(errc::illegal_byte_sequence)));
510#ifdef _GLIBCXX_FILESYSTEM_IS_WINDOWS
511 return __ws;
512#else
513 return _Cvt<wchar_t>::_S_convert(__ws.data(), __ws.data() + __ws.size());
514#endif
7fcdbdd2
JW
515#else
516 return {__first, __last};
517#endif
0ca7ba9a
JW
518}
519
520std::size_t
641cb5a6 521fs::hash_value(const path& p) noexcept
0ca7ba9a
JW
522{
523 // [path.non-member]
524 // "If for two paths, p1 == p2 then hash_value(p1) == hash_value(p2)."
525 // Equality works as if by traversing the range [begin(), end()), meaning
526 // e.g. path("a//b") == path("a/b"), so we cannot simply hash _M_pathname
527 // but need to iterate over individual elements. Use the hash_combine from
528 // http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3876.pdf
529 size_t seed = 0;
530 for (const auto& x : p)
531 {
532 seed ^= std::hash<path::string_type>()(x.native()) + 0x9e3779b9
533 + (seed<<6) + (seed>>2);
534 }
535 return seed;
536}
641cb5a6 537
345d2d03 538#include <experimental/string_view>
641cb5a6 539
345d2d03
JW
540std::string
541fs::filesystem_error::_M_gen_what()
542{
543 const std::string pstr1 = _M_path1.u8string();
544 const std::string pstr2 = _M_path2.u8string();
545 experimental::string_view s = this->system_error::what();
546 const size_t len = 18 + s.length()
7f00fdb2 547 + (pstr1.length() || pstr2.length() ? pstr1.length() + 3 : 0)
345d2d03
JW
548 + (pstr2.length() ? pstr2.length() + 3 : 0);
549 std::string w;
550 w.reserve(len);
551 w = "filesystem error: ";
552 w.append(s.data(), s.length());
553 if (!pstr1.empty())
554 {
555 w += " [";
556 w += pstr1;
557 w += ']';
558 }
7f00fdb2 559 if (!pstr2.empty())
345d2d03 560 {
7f00fdb2
JW
561 if (pstr1.empty())
562 w += " []";
345d2d03
JW
563 w += " [";
564 w += pstr2;
565 w += ']';
566 }
567 return w;
568}