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 | |
44 | namespace fs = boost::filesystem; |
45 | |
46 | using boost::filesystem::path; |
47 | |
48 | //--------------------------------------------------------------------------------------// |
49 | // // |
50 | // class path helpers // |
51 | // // |
52 | //--------------------------------------------------------------------------------------// |
53 | |
54 | namespace { |
55 | //------------------------------------------------------------------------------------// |
56 | // miscellaneous class path helpers // |
57 | //------------------------------------------------------------------------------------// |
58 | |
59 | typedef path::value_type value_type; |
60 | typedef path::string_type string_type; |
61 | typedef string_type::size_type size_type; |
62 | |
63 | #ifdef BOOST_WINDOWS_API |
64 | |
65 | const wchar_t dot_path_literal[] = L"." ; |
66 | const wchar_t dot_dot_path_literal[] = L".." ; |
67 | const wchar_t separators[] = L"/\\" ; |
68 | using boost::filesystem::detail::colon; |
69 | using boost::filesystem::detail::questionmark; |
70 | |
71 | inline bool is_alnum(wchar_t c) |
72 | { |
73 | return boost::filesystem::detail::is_letter(c) || (c >= L'0' && c <= L'9'); |
74 | } |
75 | |
76 | inline 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 |
93 | inline 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 | |
107 | const char dot_path_literal[] = "." ; |
108 | const char dot_dot_path_literal[] = ".." ; |
109 | const 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 |
112 | inline 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 |
124 | bool 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. |
127 | size_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. |
131 | size_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 |
134 | void 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 |
137 | inline 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 | |
150 | namespace boost { |
151 | namespace filesystem { |
152 | namespace 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. |
156 | inline 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 | |
168 | BOOST_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 | |
266 | BOOST_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 | |
361 | BOOST_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 | |
407 | BOOST_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 | |
453 | BOOST_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 | |
458 | BOOST_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 | |
477 | BOOST_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 | |
496 | BOOST_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 | |
558 | BOOST_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 | |
579 | BOOST_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 | |
600 | BOOST_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 | |
605 | BOOST_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 | |
612 | BOOST_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 | |
629 | BOOST_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 | |
645 | BOOST_FILESYSTEM_DECL void path_algorithms::remove_filename_v3(path& p) |
646 | { |
647 | p.remove_filename_and_trailing_separators(); |
648 | } |
649 | |
650 | BOOST_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 | |
656 | BOOST_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 | |
671 | BOOST_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 | |
688 | BOOST_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 | |
695 | BOOST_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 | |
707 | BOOST_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 | |
716 | BOOST_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 | |
741 | BOOST_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 | |
778 | BOOST_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 | |
808 | BOOST_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 | |
816 | BOOST_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 | |
828 | BOOST_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 | |
840 | BOOST_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 | |
849 | BOOST_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 | |
881 | BOOST_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 | |
888 | BOOST_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 | |
895 | BOOST_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 | |
904 | BOOST_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 | |
944 | namespace { |
945 | |
946 | // is_root_separator ---------------------------------------------------------------// |
947 | |
948 | // pos is position of the separator |
949 | inline 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. |
963 | inline 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 |
983 | size_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 | |
1069 | find_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. |
1087 | void 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 | |
1121 | namespace boost { |
1122 | namespace filesystem { |
1123 | namespace detail { |
1124 | |
1125 | BOOST_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 | |
1179 | BOOST_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 | |
1240 | BOOST_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 | |
1300 | BOOST_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 | |
1364 | BOOST_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 | |
1384 | BOOST_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 | |
1395 | namespace { |
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 | |
1417 | std::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 | |
1451 | std::locale* g_path_locale = nullptr; |
1452 | |
1453 | void 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. |
1462 | inline 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 | |
1492 | inline 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 | |
1508 | const boost::filesystem::path* g_dot_path = nullptr; |
1509 | const boost::filesystem::path* g_dot_dot_path = nullptr; |
1510 | |
1511 | inline void schedule_path_locale_cleanup() noexcept |
1512 | { |
1513 | } |
1514 | |
1515 | inline 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 | |
1537 | inline 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 | |
1559 | void __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 | |
1569 | BOOST_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 |
1583 | extern 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") |
1591 | BOOST_ATTRIBUTE_UNUSED BOOST_FILESYSTEM_ATTRIBUTE_RETAIN |
1592 | extern 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 |
1602 | struct 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 | }; |
1608 | BOOST_ATTRIBUTE_UNUSED |
1609 | static const globals_retainer g_globals_retainer; |
1610 | #endif // defined(BOOST_FILESYSTEM_NO_ATTRIBUTE_RETAIN) |
1611 | |
1612 | #else // defined(_MSC_VER) |
1613 | |
1614 | struct 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 | |
1625 | BOOST_FILESYSTEM_INIT_PRIORITY(BOOST_FILESYSTEM_PATH_GLOBALS_INIT_PRIORITY) BOOST_ATTRIBUTE_UNUSED |
1626 | const path_locale_deleter g_path_locale_deleter = {}; |
1627 | BOOST_FILESYSTEM_INIT_PRIORITY(BOOST_FILESYSTEM_PATH_GLOBALS_INIT_PRIORITY) |
1628 | const boost::filesystem::path g_dot_path(dot_path_literal); |
1629 | BOOST_FILESYSTEM_INIT_PRIORITY(BOOST_FILESYSTEM_PATH_GLOBALS_INIT_PRIORITY) |
1630 | const boost::filesystem::path g_dot_dot_path(dot_dot_path_literal); |
1631 | |
1632 | inline void schedule_path_locale_cleanup() noexcept |
1633 | { |
1634 | } |
1635 | |
1636 | inline boost::filesystem::path const& get_dot_path() |
1637 | { |
1638 | return g_dot_path; |
1639 | } |
1640 | |
1641 | inline 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 | |
1648 | inline void schedule_path_locale_cleanup() noexcept |
1649 | { |
1650 | BOOST_ATTRIBUTE_UNUSED static const path_locale_deleter g_path_locale_deleter; |
1651 | } |
1652 | |
1653 | inline 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 | |
1659 | inline 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 | |
1675 | namespace boost { |
1676 | namespace filesystem { |
1677 | |
1678 | BOOST_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 | |
1686 | BOOST_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 | |
1703 | namespace detail { |
1704 | |
1705 | BOOST_FILESYSTEM_DECL path const& dot_path() |
1706 | { |
1707 | return get_dot_path(); |
1708 | } |
1709 | |
1710 | BOOST_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 | |