1// filesystem path.cpp ------------------------------------------------------------- //
2
3// Copyright Beman Dawes 2008
4// Copyright Andrey Semashev 2021-2024
5
6// Distributed under the Boost Software License, Version 1.0.
7// See http://www.boost.org/LICENSE_1_0.txt
8
9// Library home page: http://www.boost.org/libs/filesystem
10
11#include "platform_config.hpp"
12
13#include <boost/filesystem/config.hpp>
14#include <boost/filesystem/path.hpp>
15#include <boost/filesystem/detail/path_traits.hpp> // codecvt_error_category()
16#include <boost/system/error_category.hpp> // for BOOST_SYSTEM_HAS_CONSTEXPR
17#include <boost/assert.hpp>
18#include <algorithm>
19#include <iterator>
20#include <utility>
21#include <string>
22#include <cstddef>
23#include <cstring>
24#include <cstdlib> // std::atexit
25
26#ifdef BOOST_WINDOWS_API
27#include "windows_file_codecvt.hpp"
28#include "windows_tools.hpp"
29#include <windows.h>
30#elif defined(macintosh) || defined(__APPLE__) || defined(__APPLE_CC__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__HAIKU__)
31#include <boost/filesystem/detail/utf8_codecvt_facet.hpp>
32#endif
33
34#ifdef BOOST_FILESYSTEM_DEBUG
35#include <iostream>
36#include <iomanip>
37#endif
38
39#include "atomic_tools.hpp"
40#include "private_config.hpp"
41
42#include <boost/filesystem/detail/header.hpp> // must be the last #include
43
44namespace fs = boost::filesystem;
45
46using boost::filesystem::path;
47
48//--------------------------------------------------------------------------------------//
49// //
50// class path helpers //
51// //
52//--------------------------------------------------------------------------------------//
53
54namespace {
55//------------------------------------------------------------------------------------//
56// miscellaneous class path helpers //
57//------------------------------------------------------------------------------------//
58
59typedef path::value_type value_type;
60typedef path::string_type string_type;
61typedef string_type::size_type size_type;
62
63#ifdef BOOST_WINDOWS_API
64
65const wchar_t dot_path_literal[] = L".";
66const wchar_t dot_dot_path_literal[] = L"..";
67const wchar_t separators[] = L"/\\";
68using boost::filesystem::detail::colon;
69using boost::filesystem::detail::questionmark;
70
71inline bool is_alnum(wchar_t c)
72{
73 return boost::filesystem::detail::is_letter(c) || (c >= L'0' && c <= L'9');
74}
75
76inline bool is_device_name_char(wchar_t c)
77{
78 // https://googleprojectzero.blogspot.com/2016/02/the-definitive-guide-on-win32-to-nt.html
79 // Device names are:
80 //
81 // - PRN
82 // - AUX
83 // - NUL
84 // - CON
85 // - LPT[1-9]
86 // - COM[1-9]
87 // - CONIN$
88 // - CONOUT$
89 return is_alnum(c) || c == L'$';
90}
91
92//! Returns position of the first directory separator in the \a size initial characters of \a p, or \a size if not found
93inline size_type find_separator(const wchar_t* p, size_type size) noexcept
94{
95 size_type pos = 0u;
96 for (; pos < size; ++pos)
97 {
98 const wchar_t c = p[pos];
99 if (boost::filesystem::detail::is_directory_separator(c))
100 break;
101 }
102 return pos;
103}
104
105#else // BOOST_WINDOWS_API
106
107const char dot_path_literal[] = ".";
108const char dot_dot_path_literal[] = "..";
109const char separators[] = "/";
110
111//! Returns position of the first directory separator in the \a size initial characters of \a p, or \a size if not found
112inline size_type find_separator(const char* p, size_type size) noexcept
113{
114 const char* sep = static_cast< const char* >(std::memchr(s: p, c: '/', n: size));
115 size_type pos = size;
116 if (BOOST_LIKELY(!!sep))
117 pos = sep - p;
118 return pos;
119}
120
121#endif // BOOST_WINDOWS_API
122
123// pos is position of the separator
124bool is_root_separator(string_type const& str, size_type root_dir_pos, size_type pos);
125
126// Returns: Size of the filename element that ends at end_pos (which is past-the-end position). 0 if no filename found.
127size_type find_filename_size(string_type const& str, size_type root_name_size, size_type end_pos);
128
129// Returns: starting position of root directory or size if not found. Sets root_name_size to length
130// of the root name if the characters before the returned position (if any) are considered a root name.
131size_type find_root_directory_start(const value_type* path, size_type size, size_type& root_name_size);
132
133// Finds position and size of the first element of the path
134void first_element(string_type const& src, size_type& element_pos, size_type& element_size, size_type size);
135
136// Finds position and size of the first element of the path
137inline void first_element(string_type const& src, size_type& element_pos, size_type& element_size)
138{
139 first_element(src, element_pos, element_size, size: src.size());
140}
141
142} // unnamed namespace
143
144//--------------------------------------------------------------------------------------//
145// //
146// class path implementation //
147// //
148//--------------------------------------------------------------------------------------//
149
150namespace boost {
151namespace filesystem {
152namespace detail {
153
154// C++14 provides a mismatch algorithm with four iterator arguments(), but earlier
155// standard libraries didn't, so provide this needed functionality.
156inline std::pair< path::iterator, path::iterator > mismatch(path::iterator it1, path::iterator it1end, path::iterator it2, path::iterator it2end)
157{
158 for (; it1 != it1end && it2 != it2end && path_algorithms::compare_v4(left: *it1, right: *it2) == 0;)
159 {
160 path_algorithms::increment_v4(it&: it1);
161 path_algorithms::increment_v4(it&: it2);
162 }
163 return std::make_pair(x&: it1, y&: it2);
164}
165
166// normal --------------------------------------------------------------------------//
167
168BOOST_FILESYSTEM_DECL path path_algorithms::lexically_normal_v3(path const& p)
169{
170 const value_type* const pathname = p.m_pathname.c_str();
171 const size_type pathname_size = p.m_pathname.size();
172 size_type root_name_size = 0;
173 size_type root_dir_pos = find_root_directory_start(path: pathname, size: pathname_size, root_name_size);
174 path normal(pathname, pathname + root_name_size);
175
176#if defined(BOOST_WINDOWS_API)
177 for (size_type i = 0; i < root_name_size; ++i)
178 {
179 if (normal.m_pathname[i] == path::separator)
180 normal.m_pathname[i] = path::preferred_separator;
181 }
182#endif
183
184 size_type root_path_size = root_name_size;
185 if (root_dir_pos < pathname_size)
186 {
187 root_path_size = root_dir_pos + 1;
188 normal.m_pathname.push_back(c: path::preferred_separator);
189 }
190
191 size_type i = root_path_size;
192
193 // Skip redundant directory separators after the root directory
194 while (i < pathname_size && detail::is_directory_separator(c: pathname[i]))
195 ++i;
196
197 if (i < pathname_size)
198 {
199 bool last_element_was_dot = false;
200 while (true)
201 {
202 {
203 const size_type start_pos = i;
204
205 // Find next separator
206 i += find_separator(p: pathname + i, size: pathname_size - i);
207
208 const size_type size = i - start_pos;
209
210 // Skip dot elements
211 if (size == 1u && pathname[start_pos] == path::dot)
212 {
213 last_element_was_dot = true;
214 goto skip_append;
215 }
216
217 last_element_was_dot = false;
218
219 // Process dot dot elements
220 if (size == 2u && pathname[start_pos] == path::dot && pathname[start_pos + 1] == path::dot && normal.m_pathname.size() > root_path_size)
221 {
222 // Don't remove previous dot dot elements
223 const size_type normal_size = normal.m_pathname.size();
224 size_type filename_size = find_filename_size(str: normal.m_pathname, root_name_size: root_path_size, end_pos: normal_size);
225 size_type pos = normal_size - filename_size;
226 if (filename_size != 2u || normal.m_pathname[pos] != path::dot || normal.m_pathname[pos + 1] != path::dot)
227 {
228 if (pos > root_path_size && detail::is_directory_separator(c: normal.m_pathname[pos - 1]))
229 --pos;
230 normal.m_pathname.erase(first: normal.m_pathname.begin() + pos , last: normal.m_pathname.end());
231 goto skip_append;
232 }
233 }
234
235 // Append the element
236 path_algorithms::append_separator_if_needed(p&: normal);
237 normal.m_pathname.append(s: pathname + start_pos, n: size);
238 }
239
240 skip_append:
241 if (i == pathname_size)
242 break;
243
244 // Skip directory separators, including duplicates
245 while (i < pathname_size && detail::is_directory_separator(c: pathname[i]))
246 ++i;
247
248 if (i == pathname_size)
249 {
250 // If a path ends with a separator, add a trailing dot element
251 goto append_trailing_dot;
252 }
253 }
254
255 if (normal.empty() || last_element_was_dot)
256 {
257 append_trailing_dot:
258 path_algorithms::append_separator_if_needed(p&: normal);
259 normal.m_pathname.push_back(c: path::dot);
260 }
261 }
262
263 return normal;
264}
265
266BOOST_FILESYSTEM_DECL path path_algorithms::lexically_normal_v4(path const& p)
267{
268 const value_type* const pathname = p.m_pathname.c_str();
269 const size_type pathname_size = p.m_pathname.size();
270 size_type root_name_size = 0;
271 size_type root_dir_pos = find_root_directory_start(path: pathname, size: pathname_size, root_name_size);
272 path normal(pathname, pathname + root_name_size);
273
274 size_type root_path_size = root_name_size;
275 if (root_dir_pos < pathname_size)
276 {
277 root_path_size = root_dir_pos + 1;
278 normal.m_pathname.push_back(c: path::preferred_separator);
279 }
280
281 size_type i = root_path_size;
282
283 // Skip redundant directory separators after the root directory
284 while (i < pathname_size && detail::is_directory_separator(c: pathname[i]))
285 ++i;
286
287 if (i < pathname_size)
288 {
289 while (true)
290 {
291 bool last_element_was_dot = false;
292 {
293 const size_type start_pos = i;
294
295 // Find next separator
296 i += find_separator(p: pathname + i, size: pathname_size - i);
297
298 const size_type size = i - start_pos;
299
300 // Skip dot elements
301 if (size == 1u && pathname[start_pos] == path::dot)
302 {
303 last_element_was_dot = true;
304 goto skip_append;
305 }
306
307 // Process dot dot elements
308 if (size == 2u && pathname[start_pos] == path::dot && pathname[start_pos + 1] == path::dot && normal.m_pathname.size() > root_path_size)
309 {
310 // Don't remove previous dot dot elements
311 const size_type normal_size = normal.m_pathname.size();
312 size_type filename_size = find_filename_size(str: normal.m_pathname, root_name_size: root_path_size, end_pos: normal_size);
313 size_type pos = normal_size - filename_size;
314 if (filename_size != 2u || normal.m_pathname[pos] != path::dot || normal.m_pathname[pos + 1] != path::dot)
315 {
316 if (pos > root_path_size && detail::is_directory_separator(c: normal.m_pathname[pos - 1]))
317 --pos;
318 normal.m_pathname.erase(first: normal.m_pathname.begin() + pos, last: normal.m_pathname.end());
319 goto skip_append;
320 }
321 }
322
323 // Append the element
324 path_algorithms::append_separator_if_needed(p&: normal);
325 normal.m_pathname.append(s: pathname + start_pos, n: size);
326 }
327
328 skip_append:
329 if (i == pathname_size)
330 {
331 // If a path ends with a trailing dot after a directory element, add a trailing separator
332 if (last_element_was_dot && !normal.empty() && !normal.filename_is_dot_dot())
333 path_algorithms::append_separator_if_needed(p&: normal);
334
335 break;
336 }
337
338 // Skip directory separators, including duplicates
339 while (i < pathname_size && detail::is_directory_separator(c: pathname[i]))
340 ++i;
341
342 if (i == pathname_size)
343 {
344 // If a path ends with a separator, add a trailing separator
345 if (!normal.empty() && !normal.filename_is_dot_dot())
346 path_algorithms::append_separator_if_needed(p&: normal);
347 break;
348 }
349 }
350
351 // If the original path was not empty and normalized ended up being empty, make it a dot
352 if (normal.empty())
353 normal.m_pathname.push_back(c: path::dot);
354 }
355
356 return normal;
357}
358
359// generic_path ---------------------------------------------------------------------//
360
361BOOST_FILESYSTEM_DECL path path_algorithms::generic_path_v3(path const& p)
362{
363 path tmp;
364 const size_type pathname_size = p.m_pathname.size();
365 tmp.m_pathname.reserve(res_arg: pathname_size);
366
367 const value_type* const pathname = p.m_pathname.c_str();
368
369 // Don't remove duplicate slashes from the root name
370 size_type root_name_size = 0u;
371 size_type root_dir_pos = find_root_directory_start(path: pathname, size: pathname_size, root_name_size);
372 if (root_name_size > 0u)
373 {
374 tmp.m_pathname.append(s: pathname, n: root_name_size);
375#if defined(BOOST_WINDOWS_API)
376 std::replace(tmp.m_pathname.begin(), tmp.m_pathname.end(), L'\\', L'/');
377#endif
378 }
379
380 size_type pos = root_name_size;
381 if (root_dir_pos < pathname_size)
382 {
383 tmp.m_pathname.push_back(c: path::separator);
384 pos = root_dir_pos + 1u;
385 }
386
387 while (pos < pathname_size)
388 {
389 size_type element_size = find_separator(p: pathname + pos, size: pathname_size - pos);
390 if (element_size > 0u)
391 {
392 tmp.m_pathname.append(s: pathname + pos, n: element_size);
393
394 pos += element_size;
395 if (pos >= pathname_size)
396 break;
397
398 tmp.m_pathname.push_back(c: path::separator);
399 }
400
401 ++pos;
402 }
403
404 return tmp;
405}
406
407BOOST_FILESYSTEM_DECL path path_algorithms::generic_path_v4(path const& p)
408{
409 path tmp;
410 const size_type pathname_size = p.m_pathname.size();
411 tmp.m_pathname.reserve(res_arg: pathname_size);
412
413 const value_type* const pathname = p.m_pathname.c_str();
414
415 // Treat root name specially as it may contain backslashes, duplicate ones too,
416 // in case of UNC paths and Windows-specific prefixes.
417 size_type root_name_size = 0u;
418 size_type root_dir_pos = find_root_directory_start(path: pathname, size: pathname_size, root_name_size);
419 if (root_name_size > 0u)
420 tmp.m_pathname.append(s: pathname, n: root_name_size);
421
422 size_type pos = root_name_size;
423 if (root_dir_pos < pathname_size)
424 {
425 tmp.m_pathname.push_back(c: path::separator);
426 pos = root_dir_pos + 1u;
427 }
428
429 while (pos < pathname_size)
430 {
431 size_type element_size = find_separator(p: pathname + pos, size: pathname_size - pos);
432 if (element_size > 0u)
433 {
434 tmp.m_pathname.append(s: pathname + pos, n: element_size);
435
436 pos += element_size;
437 if (pos >= pathname_size)
438 break;
439
440 tmp.m_pathname.push_back(c: path::separator);
441 }
442
443 ++pos;
444 }
445
446 return tmp;
447}
448
449#if defined(BOOST_WINDOWS_API)
450
451// make_preferred -------------------------------------------------------------------//
452
453BOOST_FILESYSTEM_DECL void path_algorithms::make_preferred_v3(path& p)
454{
455 std::replace(p.m_pathname.begin(), p.m_pathname.end(), L'/', L'\\');
456}
457
458BOOST_FILESYSTEM_DECL void path_algorithms::make_preferred_v4(path& p)
459{
460 const size_type pathname_size = p.m_pathname.size();
461 if (pathname_size > 0u)
462 {
463 value_type* const pathname = &p.m_pathname[0];
464
465 // Avoid converting slashes in the root name
466 size_type root_name_size = 0u;
467 find_root_directory_start(pathname, pathname_size, root_name_size);
468
469 std::replace(pathname + root_name_size, pathname + pathname_size, L'/', L'\\');
470 }
471}
472
473#endif // defined(BOOST_WINDOWS_API)
474
475// append --------------------------------------------------------------------------//
476
477BOOST_FILESYSTEM_DECL void path_algorithms::append_v3(path& p, const value_type* begin, const value_type* end)
478{
479 if (begin != end)
480 {
481 if (BOOST_LIKELY(begin < p.m_pathname.data() || begin >= (p.m_pathname.data() + p.m_pathname.size())))
482 {
483 if (!detail::is_directory_separator(c: *begin))
484 path_algorithms::append_separator_if_needed(p);
485 p.m_pathname.append(first: begin, last: end);
486 }
487 else
488 {
489 // overlapping source
490 string_type rhs(begin, end);
491 path_algorithms::append_v3(p, begin: rhs.data(), end: rhs.data() + rhs.size());
492 }
493 }
494}
495
496BOOST_FILESYSTEM_DECL void path_algorithms::append_v4(path& p, const value_type* begin, const value_type* end)
497{
498 if (begin != end)
499 {
500 if (BOOST_LIKELY(begin < p.m_pathname.data() || begin >= (p.m_pathname.data() + p.m_pathname.size())))
501 {
502 const size_type that_size = end - begin;
503 size_type that_root_name_size = 0;
504 size_type that_root_dir_pos = find_root_directory_start(path: begin, size: that_size, root_name_size&: that_root_name_size);
505
506 // if (p.is_absolute())
507 if
508 (
509#if defined(BOOST_WINDOWS_API)
510 that_root_name_size > 0 &&
511#endif
512 that_root_dir_pos < that_size
513 )
514 {
515 return_assign:
516 p.assign(begin, end);
517 return;
518 }
519
520 size_type this_root_name_size = 0;
521 find_root_directory_start(path: p.m_pathname.c_str(), size: p.m_pathname.size(), root_name_size&: this_root_name_size);
522
523 if
524 (
525 that_root_name_size > 0 &&
526 (that_root_name_size != this_root_name_size || std::memcmp(s1: p.m_pathname.c_str(), s2: begin, n: this_root_name_size * sizeof(value_type)) != 0)
527 )
528 {
529 goto return_assign;
530 }
531
532 if (that_root_dir_pos < that_size)
533 {
534 // Remove root directory (if any) and relative path to replace with those from p
535 p.m_pathname.erase(first: p.m_pathname.begin() + this_root_name_size, last: p.m_pathname.end());
536 }
537
538 const value_type* const that_path = begin + that_root_name_size;
539 if (!detail::is_directory_separator(c: *that_path))
540 path_algorithms::append_separator_if_needed(p);
541 p.m_pathname.append(first: that_path, last: end);
542 }
543 else
544 {
545 // overlapping source
546 string_type rhs(begin, end);
547 path_algorithms::append_v4(p, begin: rhs.data(), end: rhs.data() + rhs.size());
548 }
549 }
550 else if (path_algorithms::has_filename_v4(p))
551 {
552 p.m_pathname.push_back(c: path::preferred_separator);
553 }
554}
555
556// compare -------------------------------------------------------------------------//
557
558BOOST_FILESYSTEM_DECL int path_algorithms::lex_compare_v3
559(
560 path_detail::path_iterator first1, path_detail::path_iterator const& last1,
561 path_detail::path_iterator first2, path_detail::path_iterator const& last2
562)
563{
564 for (; first1 != last1 && first2 != last2;)
565 {
566 if (first1->native() < first2->native())
567 return -1;
568 if (first2->native() < first1->native())
569 return 1;
570 BOOST_ASSERT(first2->native() == first1->native());
571 path_algorithms::increment_v3(it&: first1);
572 path_algorithms::increment_v3(it&: first2);
573 }
574 if (first1 == last1 && first2 == last2)
575 return 0;
576 return first1 == last1 ? -1 : 1;
577}
578
579BOOST_FILESYSTEM_DECL int path_algorithms::lex_compare_v4
580(
581 path_detail::path_iterator first1, path_detail::path_iterator const& last1,
582 path_detail::path_iterator first2, path_detail::path_iterator const& last2
583)
584{
585 for (; first1 != last1 && first2 != last2;)
586 {
587 if (first1->native() < first2->native())
588 return -1;
589 if (first2->native() < first1->native())
590 return 1;
591 BOOST_ASSERT(first2->native() == first1->native());
592 path_algorithms::increment_v4(it&: first1);
593 path_algorithms::increment_v4(it&: first2);
594 }
595 if (first1 == last1 && first2 == last2)
596 return 0;
597 return first1 == last1 ? -1 : 1;
598}
599
600BOOST_FILESYSTEM_DECL int path_algorithms::compare_v3(path const& left, path const& right)
601{
602 return path_algorithms::lex_compare_v3(first1: left.begin(), last1: left.end(), first2: right.begin(), last2: right.end());
603}
604
605BOOST_FILESYSTEM_DECL int path_algorithms::compare_v4(path const& left, path const& right)
606{
607 return path_algorithms::lex_compare_v4(first1: left.begin(), last1: left.end(), first2: right.begin(), last2: right.end());
608}
609
610// append_separator_if_needed ------------------------------------------------------//
611
612BOOST_FILESYSTEM_DECL path_algorithms::string_type::size_type path_algorithms::append_separator_if_needed(path& p)
613{
614 if (!p.m_pathname.empty() &&
615#ifdef BOOST_WINDOWS_API
616 *(p.m_pathname.end() - 1) != colon &&
617#endif
618 !detail::is_directory_separator(c: *(p.m_pathname.end() - 1)))
619 {
620 string_type::size_type tmp(p.m_pathname.size());
621 p.m_pathname.push_back(c: path::preferred_separator);
622 return tmp;
623 }
624 return 0;
625}
626
627// erase_redundant_separator -------------------------------------------------------//
628
629BOOST_FILESYSTEM_DECL void path_algorithms::erase_redundant_separator(path& p, string_type::size_type sep_pos)
630{
631 if (sep_pos // a separator was added
632 && sep_pos < p.m_pathname.size() // and something was appended
633 && (p.m_pathname[sep_pos + 1] == path::separator // and it was also separator
634#ifdef BOOST_WINDOWS_API
635 || p.m_pathname[sep_pos + 1] == path::preferred_separator // or preferred_separator
636#endif
637 ))
638 {
639 p.m_pathname.erase(position: p.m_pathname.begin() + sep_pos); // erase the added separator
640 }
641}
642
643// modifiers -----------------------------------------------------------------------//
644
645BOOST_FILESYSTEM_DECL void path_algorithms::remove_filename_v3(path& p)
646{
647 p.remove_filename_and_trailing_separators();
648}
649
650BOOST_FILESYSTEM_DECL void path_algorithms::remove_filename_v4(path& p)
651{
652 size_type filename_size = path_algorithms::find_filename_v4_size(p);
653 p.m_pathname.erase(first: p.m_pathname.begin() + (p.m_pathname.size() - filename_size), last: p.m_pathname.end());
654}
655
656BOOST_FILESYSTEM_DECL void path_algorithms::replace_extension_v3(path& p, path const& new_extension)
657{
658 // erase existing extension, including the dot, if any
659 size_type ext_pos = p.m_pathname.size() - path_algorithms::extension_v3(p).m_pathname.size();
660 p.m_pathname.erase(first: p.m_pathname.begin() + ext_pos, last: p.m_pathname.end());
661
662 if (!new_extension.empty())
663 {
664 // append new_extension, adding the dot if necessary
665 if (new_extension.m_pathname[0] != path::dot)
666 p.m_pathname.push_back(c: path::dot);
667 p.m_pathname.append(str: new_extension.m_pathname);
668 }
669}
670
671BOOST_FILESYSTEM_DECL void path_algorithms::replace_extension_v4(path& p, path const& new_extension)
672{
673 // erase existing extension, including the dot, if any
674 size_type ext_pos = p.m_pathname.size() - path_algorithms::find_extension_v4_size(p);
675 p.m_pathname.erase(first: p.m_pathname.begin() + ext_pos, last: p.m_pathname.end());
676
677 if (!new_extension.empty())
678 {
679 // append new_extension, adding the dot if necessary
680 if (new_extension.m_pathname[0] != path::dot)
681 p.m_pathname.push_back(c: path::dot);
682 p.m_pathname.append(str: new_extension.m_pathname);
683 }
684}
685
686// decomposition -------------------------------------------------------------------//
687
688BOOST_FILESYSTEM_DECL size_type path_algorithms::find_root_name_size(path const& p)
689{
690 size_type root_name_size = 0;
691 find_root_directory_start(path: p.m_pathname.c_str(), size: p.m_pathname.size(), root_name_size);
692 return root_name_size;
693}
694
695BOOST_FILESYSTEM_DECL size_type path_algorithms::find_root_path_size(path const& p)
696{
697 size_type root_name_size = 0;
698 size_type root_dir_pos = find_root_directory_start(path: p.m_pathname.c_str(), size: p.m_pathname.size(), root_name_size);
699
700 size_type size = root_name_size;
701 if (root_dir_pos < p.m_pathname.size())
702 size = root_dir_pos + 1;
703
704 return size;
705}
706
707BOOST_FILESYSTEM_DECL path_algorithms::substring path_algorithms::find_root_directory(path const& p)
708{
709 substring root_dir;
710 size_type root_name_size = 0;
711 root_dir.pos = find_root_directory_start(path: p.m_pathname.c_str(), size: p.m_pathname.size(), root_name_size);
712 root_dir.size = static_cast< std::size_t >(root_dir.pos < p.m_pathname.size());
713 return root_dir;
714}
715
716BOOST_FILESYSTEM_DECL path_algorithms::substring path_algorithms::find_relative_path(path const& p)
717{
718 size_type root_name_size = 0;
719 size_type root_dir_pos = find_root_directory_start(path: p.m_pathname.c_str(), size: p.m_pathname.size(), root_name_size);
720
721 // Skip root name, root directory and any duplicate separators
722 size_type size = root_name_size;
723 if (root_dir_pos < p.m_pathname.size())
724 {
725 size = root_dir_pos + 1;
726
727 for (size_type n = p.m_pathname.size(); size < n; ++size)
728 {
729 if (!detail::is_directory_separator(c: p.m_pathname[size]))
730 break;
731 }
732 }
733
734 substring rel_path;
735 rel_path.pos = size;
736 rel_path.size = p.m_pathname.size() - size;
737
738 return rel_path;
739}
740
741BOOST_FILESYSTEM_DECL path_algorithms::string_type::size_type path_algorithms::find_parent_path_size(path const& p)
742{
743 const size_type size = p.m_pathname.size();
744 size_type root_name_size = 0;
745 size_type root_dir_pos = find_root_directory_start(path: p.m_pathname.c_str(), size, root_name_size);
746
747 size_type filename_size = find_filename_size(str: p.m_pathname, root_name_size, end_pos: size);
748 size_type end_pos = size - filename_size;
749 while (true)
750 {
751 if (end_pos <= root_name_size)
752 {
753 // Keep the root name as the parent path if there was a filename
754 if (filename_size == 0)
755 end_pos = 0u;
756 break;
757 }
758
759 --end_pos;
760
761 if (!detail::is_directory_separator(c: p.m_pathname[end_pos]))
762 {
763 ++end_pos;
764 break;
765 }
766
767 if (end_pos == root_dir_pos)
768 {
769 // Keep the trailing root directory if there was a filename
770 end_pos += filename_size > 0;
771 break;
772 }
773 }
774
775 return end_pos;
776}
777
778BOOST_FILESYSTEM_DECL path path_algorithms::filename_v3(path const& p)
779{
780 const size_type size = p.m_pathname.size();
781 size_type root_name_size = 0;
782 size_type root_dir_pos = find_root_directory_start(path: p.m_pathname.c_str(), size, root_name_size);
783 size_type filename_size, pos;
784 if (root_dir_pos < size && detail::is_directory_separator(c: p.m_pathname[size - 1]) && is_root_separator(str: p.m_pathname, root_dir_pos, pos: size - 1))
785 {
786 // Return root directory
787 pos = root_dir_pos;
788 filename_size = 1u;
789 }
790 else if (root_name_size == size)
791 {
792 // Return root name
793 pos = 0u;
794 filename_size = root_name_size;
795 }
796 else
797 {
798 filename_size = find_filename_size(str: p.m_pathname, root_name_size, end_pos: size);
799 pos = size - filename_size;
800 if (filename_size == 0u && pos > root_name_size && detail::is_directory_separator(c: p.m_pathname[pos - 1]) && !is_root_separator(str: p.m_pathname, root_dir_pos, pos: pos - 1))
801 return detail::dot_path();
802 }
803
804 const value_type* ptr = p.m_pathname.c_str() + pos;
805 return path(ptr, ptr + filename_size);
806}
807
808BOOST_FILESYSTEM_DECL path_algorithms::string_type::size_type path_algorithms::find_filename_v4_size(path const& p)
809{
810 const size_type size = p.m_pathname.size();
811 size_type root_name_size = 0;
812 find_root_directory_start(path: p.m_pathname.c_str(), size, root_name_size);
813 return find_filename_size(str: p.m_pathname, root_name_size, end_pos: size);
814}
815
816BOOST_FILESYSTEM_DECL path path_algorithms::stem_v3(path const& p)
817{
818 path name(path_algorithms::filename_v3(p));
819 if (path_algorithms::compare_v4(left: name, right: detail::dot_path()) != 0 && path_algorithms::compare_v4(left: name, right: detail::dot_dot_path()) != 0)
820 {
821 size_type pos = name.m_pathname.rfind(c: path::dot);
822 if (pos != string_type::npos)
823 name.m_pathname.erase(first: name.m_pathname.begin() + pos, last: name.m_pathname.end());
824 }
825 return name;
826}
827
828BOOST_FILESYSTEM_DECL path path_algorithms::stem_v4(path const& p)
829{
830 path name(path_algorithms::filename_v4(p));
831 if (path_algorithms::compare_v4(left: name, right: detail::dot_path()) != 0 && path_algorithms::compare_v4(left: name, right: detail::dot_dot_path()) != 0)
832 {
833 size_type pos = name.m_pathname.rfind(c: path::dot);
834 if (pos != 0 && pos != string_type::npos)
835 name.m_pathname.erase(first: name.m_pathname.begin() + pos, last: name.m_pathname.end());
836 }
837 return name;
838}
839
840BOOST_FILESYSTEM_DECL path path_algorithms::extension_v3(path const& p)
841{
842 path name(path_algorithms::filename_v3(p));
843 if (path_algorithms::compare_v4(left: name, right: detail::dot_path()) == 0 || path_algorithms::compare_v4(left: name, right: detail::dot_dot_path()) == 0)
844 return path();
845 size_type pos(name.m_pathname.rfind(c: path::dot));
846 return pos == string_type::npos ? path() : path(name.m_pathname.c_str() + pos);
847}
848
849BOOST_FILESYSTEM_DECL path_algorithms::string_type::size_type path_algorithms::find_extension_v4_size(path const& p)
850{
851 const size_type size = p.m_pathname.size();
852 size_type root_name_size = 0;
853 find_root_directory_start(path: p.m_pathname.c_str(), size, root_name_size);
854 size_type filename_size = find_filename_size(str: p.m_pathname, root_name_size, end_pos: size);
855 size_type filename_pos = size - filename_size;
856 if
857 (
858 filename_size > 0u &&
859 // Check for "." and ".." filenames
860 !(p.m_pathname[filename_pos] == path::dot &&
861 (filename_size == 1u || (filename_size == 2u && p.m_pathname[filename_pos + 1u] == path::dot)))
862 )
863 {
864 size_type ext_pos = size;
865 while (ext_pos > filename_pos)
866 {
867 --ext_pos;
868 if (p.m_pathname[ext_pos] == path::dot)
869 break;
870 }
871
872 if (ext_pos > filename_pos)
873 return size - ext_pos;
874 }
875
876 return 0u;
877}
878
879} // namespace detail
880
881BOOST_FILESYSTEM_DECL path& path::remove_filename_and_trailing_separators()
882{
883 size_type end_pos = detail::path_algorithms::find_parent_path_size(p: *this);
884 m_pathname.erase(first: m_pathname.begin() + end_pos, last: m_pathname.end());
885 return *this;
886}
887
888BOOST_FILESYSTEM_DECL path& path::remove_trailing_separator()
889{
890 if (!m_pathname.empty() && detail::is_directory_separator(c: m_pathname[m_pathname.size() - 1]))
891 m_pathname.erase(position: m_pathname.end() - 1);
892 return *this;
893}
894
895BOOST_FILESYSTEM_DECL path& path::replace_filename(path const& replacement)
896{
897 detail::path_algorithms::remove_filename_v4(p&: *this);
898 detail::path_algorithms::append_v4(p&: *this, begin: replacement.m_pathname.data(), end: replacement.m_pathname.data() + replacement.m_pathname.size());
899 return *this;
900}
901
902// lexical operations --------------------------------------------------------------//
903
904BOOST_FILESYSTEM_DECL path path::lexically_relative(path const& base) const
905{
906 path::iterator b = begin(), e = end(), base_b = base.begin(), base_e = base.end();
907 std::pair< path::iterator, path::iterator > mm = detail::mismatch(it1: b, it1end: e, it2: base_b, it2end: base_e);
908 if (mm.first == b && mm.second == base_b)
909 return path();
910 if (mm.first == e && mm.second == base_e)
911 return detail::dot_path();
912
913 std::ptrdiff_t n = 0;
914 for (; mm.second != base_e; detail::path_algorithms::increment_v4(it&: mm.second))
915 {
916 path const& p = *mm.second;
917 if (detail::path_algorithms::compare_v4(left: p, right: detail::dot_dot_path()) == 0)
918 --n;
919 else if (!p.empty() && detail::path_algorithms::compare_v4(left: p, right: detail::dot_path()) != 0)
920 ++n;
921 }
922 if (n < 0)
923 return path();
924 if (n == 0 && (mm.first == e || mm.first->empty()))
925 return detail::dot_path();
926
927 path tmp;
928 for (; n > 0; --n)
929 detail::path_algorithms::append_v4(left&: tmp, right: detail::dot_dot_path());
930 for (; mm.first != e; detail::path_algorithms::increment_v4(it&: mm.first))
931 detail::path_algorithms::append_v4(left&: tmp, right: *mm.first);
932 return tmp;
933}
934
935} // namespace filesystem
936} // namespace boost
937
938//--------------------------------------------------------------------------------------//
939// //
940// class path helpers implementation //
941// //
942//--------------------------------------------------------------------------------------//
943
944namespace {
945
946// is_root_separator ---------------------------------------------------------------//
947
948// pos is position of the separator
949inline bool is_root_separator(string_type const& str, size_type root_dir_pos, size_type pos)
950{
951 BOOST_ASSERT_MSG(pos < str.size() && fs::detail::is_directory_separator(str[pos]), "precondition violation");
952
953 // root_dir_pos points at the leftmost separator, we need to skip any duplicate separators right of root dir
954 while (pos > root_dir_pos && fs::detail::is_directory_separator(c: str[pos - 1]))
955 --pos;
956
957 return pos == root_dir_pos;
958}
959
960// find_filename_size --------------------------------------------------------------//
961
962// Returns: Size of the filename element that ends at end_pos (which is past-the-end position). 0 if no filename found.
963inline size_type find_filename_size(string_type const& str, size_type root_name_size, size_type end_pos)
964{
965 size_type pos = end_pos;
966 while (pos > root_name_size)
967 {
968 --pos;
969
970 if (fs::detail::is_directory_separator(c: str[pos]))
971 {
972 ++pos; // filename starts past the separator
973 break;
974 }
975 }
976
977 return end_pos - pos;
978}
979
980// find_root_directory_start -------------------------------------------------------//
981
982// Returns: starting position of root directory or size if not found
983size_type find_root_directory_start(const value_type* path, size_type size, size_type& root_name_size)
984{
985 root_name_size = 0;
986 if (size == 0)
987 return 0;
988
989 bool parsing_root_name = false;
990 size_type pos = 0;
991
992 // case "//", possibly followed by more characters
993 if (fs::detail::is_directory_separator(c: path[0]))
994 {
995 if (size >= 2 && fs::detail::is_directory_separator(c: path[1]))
996 {
997 if (size == 2)
998 {
999 // The whole path is just a pair of separators
1000 root_name_size = 2;
1001 return 2;
1002 }
1003#ifdef BOOST_WINDOWS_API
1004 // https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file
1005 // cases "\\?\" and "\\.\"
1006 else if (size >= 4 && (path[2] == questionmark || path[2] == fs::path::dot) && fs::detail::is_directory_separator(path[3]))
1007 {
1008 parsing_root_name = true;
1009 pos += 4;
1010 }
1011#endif
1012 else if (fs::detail::is_directory_separator(c: path[2]))
1013 {
1014 // The path starts with three directory separators, which is interpreted as a root directory followed by redundant separators
1015 return 0;
1016 }
1017 else
1018 {
1019 // case "//net {/}"
1020 parsing_root_name = true;
1021 pos += 2;
1022 goto find_next_separator;
1023 }
1024 }
1025#ifdef BOOST_WINDOWS_API
1026 // https://stackoverflow.com/questions/23041983/path-prefixes-and
1027 // case "\??\" (NT path prefix)
1028 else if (size >= 4 && path[1] == questionmark && path[2] == questionmark && fs::detail::is_directory_separator(path[3]))
1029 {
1030 parsing_root_name = true;
1031 pos += 4;
1032 }
1033#endif
1034 else
1035 {
1036 // The path starts with a separator, possibly followed by a non-separator character
1037 return 0;
1038 }
1039 }
1040
1041#ifdef BOOST_WINDOWS_API
1042 // case "c:" or "prn:"
1043 // Note: There is ambiguity in a "c:x" path interpretation. It could either mean a file "x" located at the current directory for drive C:,
1044 // or an alternative stream "x" of a file "c". Windows API resolve this as the former, and so do we.
1045 if ((size - pos) >= 2 && fs::detail::is_letter(path[pos]))
1046 {
1047 size_type i = pos + 1;
1048 for (; i < size; ++i)
1049 {
1050 if (!is_device_name_char(path[i]))
1051 break;
1052 }
1053
1054 if (i < size && path[i] == colon)
1055 {
1056 pos = i + 1;
1057 root_name_size = pos;
1058 parsing_root_name = false;
1059
1060 if (pos < size && fs::detail::is_directory_separator(path[pos]))
1061 return pos;
1062 }
1063 }
1064#endif
1065
1066 if (!parsing_root_name)
1067 return size;
1068
1069find_next_separator:
1070 pos += find_separator(p: path + pos, size: size - pos);
1071 if (parsing_root_name)
1072 root_name_size = pos;
1073
1074 return pos;
1075}
1076
1077//--------------------------------------------------------------------------------------//
1078// //
1079// class path::iterator implementation //
1080// //
1081//--------------------------------------------------------------------------------------//
1082
1083// first_element ----------------------------------------------------------------------//
1084
1085// sets pos and len of first element, excluding extra separators
1086// if src.empty(), sets pos,len, to 0,0.
1087void first_element(string_type const& src, size_type& element_pos, size_type& element_size, size_type size)
1088{
1089 element_pos = 0;
1090 element_size = 0;
1091 if (src.empty())
1092 return;
1093
1094 size_type root_name_size = 0;
1095 size_type root_dir_pos = find_root_directory_start(path: src.c_str(), size, root_name_size);
1096
1097 // First element is the root name, if there is one
1098 if (root_name_size > 0)
1099 {
1100 element_size = root_name_size;
1101 return;
1102 }
1103
1104 // Otherwise, the root directory
1105 if (root_dir_pos < size)
1106 {
1107 element_pos = root_dir_pos;
1108 element_size = 1u;
1109 return;
1110 }
1111
1112 // Otherwise, the first filename or directory name in a relative path
1113 size_type end_pos = src.find_first_of(s: separators);
1114 if (end_pos == string_type::npos)
1115 end_pos = src.size();
1116 element_size = end_pos;
1117}
1118
1119} // unnamed namespace
1120
1121namespace boost {
1122namespace filesystem {
1123namespace detail {
1124
1125BOOST_FILESYSTEM_DECL void path_algorithms::increment_v3(path_detail::path_iterator& it)
1126{
1127 const size_type size = it.m_path_ptr->m_pathname.size();
1128 BOOST_ASSERT_MSG(it.m_pos < size, "path::iterator increment past end()");
1129
1130 // increment to position past current element; if current element is implicit dot,
1131 // this will cause m_pos to represent the end iterator
1132 it.m_pos += it.m_element.m_pathname.size();
1133
1134 // if the end is reached, we are done
1135 if (it.m_pos >= size)
1136 {
1137 BOOST_ASSERT_MSG(it.m_pos == size, "path::iterator increment after the referenced path was modified");
1138 it.m_element.clear(); // aids debugging
1139 return;
1140 }
1141
1142 // process separator (Windows drive spec is only case not a separator)
1143 if (detail::is_directory_separator(c: it.m_path_ptr->m_pathname[it.m_pos]))
1144 {
1145 size_type root_name_size = 0;
1146 size_type root_dir_pos = find_root_directory_start(path: it.m_path_ptr->m_pathname.c_str(), size, root_name_size);
1147
1148 // detect root directory and set iterator value to the separator if it is
1149 if (it.m_pos == root_dir_pos && it.m_element.m_pathname.size() == root_name_size)
1150 {
1151 it.m_element.m_pathname = path::separator; // generic format; see docs
1152 return;
1153 }
1154
1155 // skip separators until m_pos points to the start of the next element
1156 while (it.m_pos != size && detail::is_directory_separator(c: it.m_path_ptr->m_pathname[it.m_pos]))
1157 {
1158 ++it.m_pos;
1159 }
1160
1161 // detect trailing separator, and treat it as ".", per POSIX spec
1162 if (it.m_pos == size &&
1163 !is_root_separator(str: it.m_path_ptr->m_pathname, root_dir_pos, pos: it.m_pos - 1))
1164 {
1165 --it.m_pos;
1166 it.m_element = detail::dot_path();
1167 return;
1168 }
1169 }
1170
1171 // get m_element
1172 size_type end_pos = it.m_path_ptr->m_pathname.find_first_of(s: separators, pos: it.m_pos);
1173 if (end_pos == string_type::npos)
1174 end_pos = size;
1175 const path::value_type* p = it.m_path_ptr->m_pathname.c_str();
1176 it.m_element.m_pathname.assign(first: p + it.m_pos, last: p + end_pos);
1177}
1178
1179BOOST_FILESYSTEM_DECL void path_algorithms::increment_v4(path_detail::path_iterator& it)
1180{
1181 const size_type size = it.m_path_ptr->m_pathname.size();
1182 BOOST_ASSERT_MSG(it.m_pos <= size, "path::iterator increment past end()");
1183
1184 if (it.m_element.m_pathname.empty() && (it.m_pos + 1) == size && detail::is_directory_separator(c: it.m_path_ptr->m_pathname[it.m_pos]))
1185 {
1186 // The iterator was pointing to the last empty element of the path; set to end.
1187 it.m_pos = size;
1188 return;
1189 }
1190
1191 // increment to position past current element; if current element is implicit dot,
1192 // this will cause m_pos to represent the end iterator
1193 it.m_pos += it.m_element.m_pathname.size();
1194
1195 // if the end is reached, we are done
1196 if (it.m_pos >= size)
1197 {
1198 BOOST_ASSERT_MSG(it.m_pos == size, "path::iterator increment after the referenced path was modified");
1199 it.m_element.clear(); // aids debugging
1200 return;
1201 }
1202
1203 // process separator (Windows drive spec is only case not a separator)
1204 if (detail::is_directory_separator(c: it.m_path_ptr->m_pathname[it.m_pos]))
1205 {
1206 size_type root_name_size = 0;
1207 size_type root_dir_pos = find_root_directory_start(path: it.m_path_ptr->m_pathname.c_str(), size, root_name_size);
1208
1209 // detect root directory and set iterator value to the separator if it is
1210 if (it.m_pos == root_dir_pos && it.m_element.m_pathname.size() == root_name_size)
1211 {
1212 it.m_element.m_pathname = path::separator; // generic format; see docs
1213 return;
1214 }
1215
1216 // skip separators until m_pos points to the start of the next element
1217 while (it.m_pos != size && detail::is_directory_separator(c: it.m_path_ptr->m_pathname[it.m_pos]))
1218 {
1219 ++it.m_pos;
1220 }
1221
1222 // detect trailing separator
1223 if (it.m_pos == size &&
1224 !is_root_separator(str: it.m_path_ptr->m_pathname, root_dir_pos, pos: it.m_pos - 1))
1225 {
1226 --it.m_pos;
1227 it.m_element.m_pathname.clear();
1228 return;
1229 }
1230 }
1231
1232 // get m_element
1233 size_type end_pos = it.m_path_ptr->m_pathname.find_first_of(s: separators, pos: it.m_pos);
1234 if (end_pos == string_type::npos)
1235 end_pos = size;
1236 const path::value_type* p = it.m_path_ptr->m_pathname.c_str();
1237 it.m_element.m_pathname.assign(first: p + it.m_pos, last: p + end_pos);
1238}
1239
1240BOOST_FILESYSTEM_DECL void path_algorithms::decrement_v3(path_detail::path_iterator& it)
1241{
1242 const size_type size = it.m_path_ptr->m_pathname.size();
1243 BOOST_ASSERT_MSG(it.m_pos > 0, "path::iterator decrement past begin()");
1244 BOOST_ASSERT_MSG(it.m_pos <= size, "path::iterator decrement after the referenced path was modified");
1245
1246 size_type root_name_size = 0;
1247 size_type root_dir_pos = find_root_directory_start(path: it.m_path_ptr->m_pathname.c_str(), size, root_name_size);
1248
1249 if (root_dir_pos < size && it.m_pos == root_dir_pos)
1250 {
1251 // Was pointing at root directory, decrement to root name
1252 set_to_root_name:
1253 it.m_pos = 0u;
1254 const path::value_type* p = it.m_path_ptr->m_pathname.c_str();
1255 it.m_element.m_pathname.assign(first: p, last: p + root_name_size);
1256 return;
1257 }
1258
1259 // if at end and there was a trailing non-root '/', return "."
1260 if (it.m_pos == size &&
1261 size > 1 &&
1262 detail::is_directory_separator(c: it.m_path_ptr->m_pathname[it.m_pos - 1]) &&
1263 !is_root_separator(str: it.m_path_ptr->m_pathname, root_dir_pos, pos: it.m_pos - 1))
1264 {
1265 --it.m_pos;
1266 it.m_element = detail::dot_path();
1267 return;
1268 }
1269
1270 // skip separators unless root directory
1271 size_type end_pos = it.m_pos;
1272 while (end_pos > root_name_size)
1273 {
1274 --end_pos;
1275
1276 if (end_pos == root_dir_pos)
1277 {
1278 // Decremented to the root directory
1279 it.m_pos = end_pos;
1280 it.m_element.m_pathname = path::separator; // generic format; see docs
1281 return;
1282 }
1283
1284 if (!detail::is_directory_separator(c: it.m_path_ptr->m_pathname[end_pos]))
1285 {
1286 ++end_pos;
1287 break;
1288 }
1289 }
1290
1291 if (end_pos <= root_name_size)
1292 goto set_to_root_name;
1293
1294 size_type filename_size = find_filename_size(str: it.m_path_ptr->m_pathname, root_name_size, end_pos);
1295 it.m_pos = end_pos - filename_size;
1296 const path::value_type* p = it.m_path_ptr->m_pathname.c_str();
1297 it.m_element.m_pathname.assign(first: p + it.m_pos, last: p + end_pos);
1298}
1299
1300BOOST_FILESYSTEM_DECL void path_algorithms::decrement_v4(path_detail::path_iterator& it)
1301{
1302 const size_type size = it.m_path_ptr->m_pathname.size();
1303 BOOST_ASSERT_MSG(it.m_pos > 0, "path::iterator decrement past begin()");
1304 BOOST_ASSERT_MSG(it.m_pos <= size, "path::iterator decrement after the referenced path was modified");
1305
1306 size_type root_name_size = 0;
1307 size_type root_dir_pos = find_root_directory_start(path: it.m_path_ptr->m_pathname.c_str(), size, root_name_size);
1308
1309 if (root_dir_pos < size && it.m_pos == root_dir_pos)
1310 {
1311 // Was pointing at root directory, decrement to root name
1312 set_to_root_name:
1313 it.m_pos = 0u;
1314 const path::value_type* p = it.m_path_ptr->m_pathname.c_str();
1315 it.m_element.m_pathname.assign(first: p, last: p + root_name_size);
1316 return;
1317 }
1318
1319 // if at end and there was a trailing '/', return ""
1320 if (it.m_pos == size &&
1321 size > 1 &&
1322 detail::is_directory_separator(c: it.m_path_ptr->m_pathname[it.m_pos - 1]) &&
1323 !is_root_separator(str: it.m_path_ptr->m_pathname, root_dir_pos, pos: it.m_pos - 1))
1324 {
1325 --it.m_pos;
1326 it.m_element.m_pathname.clear();
1327 return;
1328 }
1329
1330 // skip separators unless root directory
1331 size_type end_pos = it.m_pos;
1332 while (end_pos > root_name_size)
1333 {
1334 --end_pos;
1335
1336 if (end_pos == root_dir_pos)
1337 {
1338 // Decremented to the root directory
1339 it.m_pos = end_pos;
1340 it.m_element.m_pathname = path::separator; // generic format; see docs
1341 return;
1342 }
1343
1344 if (!detail::is_directory_separator(c: it.m_path_ptr->m_pathname[end_pos]))
1345 {
1346 ++end_pos;
1347 break;
1348 }
1349 }
1350
1351 if (end_pos <= root_name_size)
1352 goto set_to_root_name;
1353
1354 size_type filename_size = find_filename_size(str: it.m_path_ptr->m_pathname, root_name_size, end_pos);
1355 it.m_pos = end_pos - filename_size;
1356 const path::value_type* p = it.m_path_ptr->m_pathname.c_str();
1357 it.m_element.m_pathname.assign(first: p + it.m_pos, last: p + end_pos);
1358}
1359
1360} // namespace detail
1361
1362// path iterators ------------------------------------------------------------------//
1363
1364BOOST_FILESYSTEM_DECL path::iterator path::begin() const
1365{
1366 iterator itr;
1367 itr.m_path_ptr = this;
1368
1369 size_type element_size;
1370 first_element(src: m_pathname, element_pos&: itr.m_pos, element_size);
1371
1372 if (element_size > 0)
1373 {
1374 itr.m_element = m_pathname.substr(pos: itr.m_pos, n: element_size);
1375#ifdef BOOST_WINDOWS_API
1376 if (itr.m_element.m_pathname.size() == 1u && itr.m_element.m_pathname[0] == path::preferred_separator)
1377 itr.m_element.m_pathname[0] = path::separator;
1378#endif
1379 }
1380
1381 return itr;
1382}
1383
1384BOOST_FILESYSTEM_DECL path::iterator path::end() const
1385{
1386 iterator itr;
1387 itr.m_path_ptr = this;
1388 itr.m_pos = m_pathname.size();
1389 return itr;
1390}
1391
1392} // namespace filesystem
1393} // namespace boost
1394
1395namespace {
1396
1397//------------------------------------------------------------------------------------//
1398// locale helpers //
1399//------------------------------------------------------------------------------------//
1400
1401// Prior versions of these locale and codecvt implementations tried to take advantage
1402// of static initialization where possible, kept a local copy of the current codecvt
1403// facet (to avoid codecvt() having to call use_facet()), and was not multi-threading
1404// safe (again for efficiency).
1405//
1406// This was error prone, and required different implementation techniques depending
1407// on the compiler and also whether static or dynamic linking was used. Furthermore,
1408// users could not easily provide their multi-threading safe wrappers because the
1409// path interface requires the implementation itself to call codecvt() to obtain the
1410// default facet, and the initialization of the static within path_locale() could race.
1411//
1412// The code below is portable to all platforms, is much simpler, and hopefully will be
1413// much more robust. Timing tests (on Windows, using a Visual C++ release build)
1414// indicated the current code is roughly 9% slower than the previous code, and that
1415// seems a small price to pay for better code that is easier to use.
1416
1417std::locale default_locale()
1418{
1419#if defined(BOOST_WINDOWS_API)
1420 std::locale global_loc = std::locale();
1421 return std::locale(global_loc, new boost::filesystem::detail::windows_file_codecvt());
1422#elif defined(macintosh) || defined(__APPLE__) || defined(__APPLE_CC__) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__HAIKU__)
1423 // "All BSD system functions expect their string parameters to be in UTF-8 encoding
1424 // and nothing else." See
1425 // http://developer.apple.com/mac/library/documentation/MacOSX/Conceptual/BPInternational/Articles/FileEncodings.html
1426 //
1427 // "The kernel will reject any filename that is not a valid UTF-8 string, and it will
1428 // even be normalized (to Unicode NFD) before stored on disk, at least when using HFS.
1429 // The right way to deal with it would be to always convert the filename to UTF-8
1430 // before trying to open/create a file." See
1431 // http://lists.apple.com/archives/unix-porting/2007/Sep/msg00023.html
1432 //
1433 // "How a file name looks at the API level depends on the API. Current Carbon APIs
1434 // handle file names as an array of UTF-16 characters; POSIX ones handle them as an
1435 // array of UTF-8, which is why UTF-8 works well in Terminal. How it's stored on disk
1436 // depends on the disk format; HFS+ uses UTF-16, but that's not important in most
1437 // cases." See
1438 // http://lists.apple.com/archives/applescript-users/2002/Sep/msg00319.html
1439 //
1440 // Many thanks to Peter Dimov for digging out the above references!
1441
1442 std::locale global_loc = std::locale();
1443 return std::locale(global_loc, new boost::filesystem::detail::utf8_codecvt_facet());
1444#else // Other POSIX
1445 // ISO C calls std::locale("") "the locale-specific native environment", and this
1446 // locale is the default for many POSIX-based operating systems such as Linux.
1447 return std::locale("");
1448#endif
1449}
1450
1451std::locale* g_path_locale = nullptr;
1452
1453void schedule_path_locale_cleanup() noexcept;
1454
1455// std::locale("") construction, needed on non-Apple POSIX systems, can throw
1456// (if environmental variables LC_MESSAGES or LANG are wrong, for example), so
1457// get_path_locale() provides lazy initialization to ensure that any
1458// exceptions occur after main() starts and so can be caught. Furthermore,
1459// g_path_locale is only initialized if path::codecvt() or path::imbue() are themselves
1460// actually called, ensuring that an exception will only be thrown if std::locale("")
1461// is really needed.
1462inline std::locale& get_path_locale()
1463{
1464#if !defined(BOOST_FILESYSTEM_SINGLE_THREADED)
1465 atomic_ns::atomic_ref< std::locale* > a(g_path_locale);
1466 std::locale* p = a.load(order: atomic_ns::memory_order_acquire);
1467 if (BOOST_UNLIKELY(!p))
1468 {
1469 std::locale* new_p = new std::locale(default_locale());
1470 if (a.compare_exchange_strong(expected&: p, desired: new_p, success_order: atomic_ns::memory_order_acq_rel, failure_order: atomic_ns::memory_order_acquire))
1471 {
1472 p = new_p;
1473 schedule_path_locale_cleanup();
1474 }
1475 else
1476 {
1477 delete new_p;
1478 }
1479 }
1480 return *p;
1481#else // !defined(BOOST_FILESYSTEM_SINGLE_THREADED)
1482 std::locale* p = g_path_locale;
1483 if (BOOST_UNLIKELY(!p))
1484 {
1485 g_path_locale = p = new std::locale(default_locale());
1486 schedule_path_locale_cleanup();
1487 }
1488 return *p;
1489#endif // !defined(BOOST_FILESYSTEM_SINGLE_THREADED)
1490}
1491
1492inline std::locale* replace_path_locale(std::locale const& loc)
1493{
1494 std::locale* new_p = new std::locale(loc);
1495#if !defined(BOOST_FILESYSTEM_SINGLE_THREADED)
1496 std::locale* p = atomic_ns::atomic_ref< std::locale* >(g_path_locale).exchange(v: new_p, order: atomic_ns::memory_order_acq_rel);
1497#else
1498 std::locale* p = g_path_locale;
1499 g_path_locale = new_p;
1500#endif
1501 if (!p)
1502 schedule_path_locale_cleanup();
1503 return p;
1504}
1505
1506#if defined(_MSC_VER)
1507
1508const boost::filesystem::path* g_dot_path = nullptr;
1509const boost::filesystem::path* g_dot_dot_path = nullptr;
1510
1511inline void schedule_path_locale_cleanup() noexcept
1512{
1513}
1514
1515inline boost::filesystem::path const& get_dot_path()
1516{
1517#if !defined(BOOST_FILESYSTEM_SINGLE_THREADED)
1518 atomic_ns::atomic_ref< const boost::filesystem::path* > a(g_dot_path);
1519 const boost::filesystem::path* p = a.load(atomic_ns::memory_order_acquire);
1520 if (BOOST_UNLIKELY(!p))
1521 {
1522 const boost::filesystem::path* new_p = new boost::filesystem::path(dot_path_literal);
1523 if (a.compare_exchange_strong(p, new_p, atomic_ns::memory_order_acq_rel, atomic_ns::memory_order_acquire))
1524 p = new_p;
1525 else
1526 delete new_p;
1527 }
1528 return *p;
1529#else // !defined(BOOST_FILESYSTEM_SINGLE_THREADED)
1530 const boost::filesystem::path* p = g_dot_path;
1531 if (BOOST_UNLIKELY(!p))
1532 g_dot_path = p = new boost::filesystem::path(dot_path_literal);
1533 return *p;
1534#endif // !defined(BOOST_FILESYSTEM_SINGLE_THREADED)
1535}
1536
1537inline boost::filesystem::path const& get_dot_dot_path()
1538{
1539#if !defined(BOOST_FILESYSTEM_SINGLE_THREADED)
1540 atomic_ns::atomic_ref< const boost::filesystem::path* > a(g_dot_dot_path);
1541 const boost::filesystem::path* p = a.load(atomic_ns::memory_order_acquire);
1542 if (BOOST_UNLIKELY(!p))
1543 {
1544 const boost::filesystem::path* new_p = new boost::filesystem::path(dot_dot_path_literal);
1545 if (a.compare_exchange_strong(p, new_p, atomic_ns::memory_order_acq_rel, atomic_ns::memory_order_acquire))
1546 p = new_p;
1547 else
1548 delete new_p;
1549 }
1550 return *p;
1551#else // !defined(BOOST_FILESYSTEM_SINGLE_THREADED)
1552 const boost::filesystem::path* p = g_dot_dot_path;
1553 if (BOOST_UNLIKELY(!p))
1554 g_dot_dot_path = p = new boost::filesystem::path(dot_dot_path_literal);
1555 return *p;
1556#endif // !defined(BOOST_FILESYSTEM_SINGLE_THREADED)
1557}
1558
1559void __cdecl destroy_path_globals()
1560{
1561 delete g_dot_dot_path;
1562 g_dot_dot_path = nullptr;
1563 delete g_dot_path;
1564 g_dot_path = nullptr;
1565 delete g_path_locale;
1566 g_path_locale = nullptr;
1567}
1568
1569BOOST_FILESYSTEM_INIT_FUNC init_path_globals()
1570{
1571#if !defined(BOOST_SYSTEM_HAS_CONSTEXPR)
1572 // codecvt_error_category needs to be called early to dynamic-initialize the error category instance
1573 boost::filesystem::codecvt_error_category();
1574#endif
1575 std::atexit(&destroy_path_globals);
1576 return BOOST_FILESYSTEM_INITRETSUCCESS_V;
1577}
1578
1579#if _MSC_VER >= 1400
1580
1581#pragma section(".CRT$XCM", long, read)
1582__declspec(allocate(".CRT$XCM")) BOOST_ATTRIBUTE_UNUSED BOOST_FILESYSTEM_ATTRIBUTE_RETAIN
1583extern const init_func_ptr_t p_init_path_globals = &init_path_globals;
1584
1585#else // _MSC_VER >= 1400
1586
1587#if (_MSC_VER >= 1300) // 1300 == VC++ 7.0
1588#pragma data_seg(push, old_seg)
1589#endif
1590#pragma data_seg(".CRT$XCM")
1591BOOST_ATTRIBUTE_UNUSED BOOST_FILESYSTEM_ATTRIBUTE_RETAIN
1592extern const init_func_ptr_t p_init_path_globals = &init_path_globals;
1593#pragma data_seg()
1594#if (_MSC_VER >= 1300) // 1300 == VC++ 7.0
1595#pragma data_seg(pop, old_seg)
1596#endif
1597
1598#endif // _MSC_VER >= 1400
1599
1600#if defined(BOOST_FILESYSTEM_NO_ATTRIBUTE_RETAIN)
1601//! Makes sure the global initializer pointers are referenced and not removed by linker
1602struct globals_retainer
1603{
1604 const init_func_ptr_t* volatile m_p_init_path_globals;
1605
1606 globals_retainer() { m_p_init_path_globals = &p_init_path_globals; }
1607};
1608BOOST_ATTRIBUTE_UNUSED
1609static const globals_retainer g_globals_retainer;
1610#endif // defined(BOOST_FILESYSTEM_NO_ATTRIBUTE_RETAIN)
1611
1612#else // defined(_MSC_VER)
1613
1614struct path_locale_deleter
1615{
1616 ~path_locale_deleter()
1617 {
1618 delete g_path_locale;
1619 g_path_locale = nullptr;
1620 }
1621};
1622
1623#if defined(BOOST_FILESYSTEM_HAS_INIT_PRIORITY)
1624
1625BOOST_FILESYSTEM_INIT_PRIORITY(BOOST_FILESYSTEM_PATH_GLOBALS_INIT_PRIORITY) BOOST_ATTRIBUTE_UNUSED
1626const path_locale_deleter g_path_locale_deleter = {};
1627BOOST_FILESYSTEM_INIT_PRIORITY(BOOST_FILESYSTEM_PATH_GLOBALS_INIT_PRIORITY)
1628const boost::filesystem::path g_dot_path(dot_path_literal);
1629BOOST_FILESYSTEM_INIT_PRIORITY(BOOST_FILESYSTEM_PATH_GLOBALS_INIT_PRIORITY)
1630const boost::filesystem::path g_dot_dot_path(dot_dot_path_literal);
1631
1632inline void schedule_path_locale_cleanup() noexcept
1633{
1634}
1635
1636inline boost::filesystem::path const& get_dot_path()
1637{
1638 return g_dot_path;
1639}
1640
1641inline boost::filesystem::path const& get_dot_dot_path()
1642{
1643 return g_dot_dot_path;
1644}
1645
1646#else // defined(BOOST_FILESYSTEM_HAS_INIT_PRIORITY)
1647
1648inline void schedule_path_locale_cleanup() noexcept
1649{
1650 BOOST_ATTRIBUTE_UNUSED static const path_locale_deleter g_path_locale_deleter;
1651}
1652
1653inline boost::filesystem::path const& get_dot_path()
1654{
1655 static const boost::filesystem::path g_dot_path(dot_path_literal);
1656 return g_dot_path;
1657}
1658
1659inline boost::filesystem::path const& get_dot_dot_path()
1660{
1661 static const boost::filesystem::path g_dot_dot_path(dot_dot_path_literal);
1662 return g_dot_dot_path;
1663}
1664
1665#endif // defined(BOOST_FILESYSTEM_HAS_INIT_PRIORITY)
1666
1667#endif // defined(_MSC_VER)
1668
1669} // unnamed namespace
1670
1671//--------------------------------------------------------------------------------------//
1672// path::codecvt() and path::imbue() implementation //
1673//--------------------------------------------------------------------------------------//
1674
1675namespace boost {
1676namespace filesystem {
1677
1678BOOST_FILESYSTEM_DECL path::codecvt_type const& path::codecvt()
1679{
1680#ifdef BOOST_FILESYSTEM_DEBUG
1681 std::cout << "***** path::codecvt() called" << std::endl;
1682#endif
1683 return std::use_facet< std::codecvt< wchar_t, char, std::mbstate_t > >(loc: get_path_locale());
1684}
1685
1686BOOST_FILESYSTEM_DECL std::locale path::imbue(std::locale const& loc)
1687{
1688#ifdef BOOST_FILESYSTEM_DEBUG
1689 std::cout << "***** path::imbue() called" << std::endl;
1690#endif
1691 std::locale* p = replace_path_locale(loc);
1692 if (BOOST_LIKELY(p != nullptr))
1693 {
1694 // Note: copying/moving std::locale does not throw
1695 std::locale temp(std::move(*p));
1696 delete p;
1697 return temp;
1698 }
1699
1700 return default_locale();
1701}
1702
1703namespace detail {
1704
1705BOOST_FILESYSTEM_DECL path const& dot_path()
1706{
1707 return get_dot_path();
1708}
1709
1710BOOST_FILESYSTEM_DECL path const& dot_dot_path()
1711{
1712 return get_dot_dot_path();
1713}
1714
1715} // namespace detail
1716} // namespace filesystem
1717} // namespace boost
1718
1719#include <boost/filesystem/detail/footer.hpp>
1720

source code of boost/libs/filesystem/src/path.cpp