1// filesystem path.cpp ------------------------------------------------------------- //
2
3// Copyright Beman Dawes 2008
4
5// Distributed under the Boost Software License, Version 1.0.
6// See http://www.boost.org/LICENSE_1_0.txt
7
8// Library home page: http://www.boost.org/libs/filesystem
9
10// Old standard library configurations, particularly MingGW, don't support wide strings.
11// Report this with an explicit error message.
12#include <boost/config.hpp>
13# if defined( BOOST_NO_STD_WSTRING )
14# error Configuration not supported: Boost.Filesystem V3 and later requires std::wstring support
15# endif
16
17// define BOOST_FILESYSTEM_SOURCE so that <boost/system/config.hpp> knows
18// the library is being built (possibly exporting rather than importing code)
19#define BOOST_FILESYSTEM_SOURCE
20
21#ifndef BOOST_SYSTEM_NO_DEPRECATED
22# define BOOST_SYSTEM_NO_DEPRECATED
23#endif
24
25#include <boost/filesystem/config.hpp>
26#include <boost/filesystem/path.hpp>
27#include <boost/filesystem/operations.hpp> // for filesystem_error
28#include <boost/scoped_array.hpp>
29#include <boost/system/error_code.hpp>
30#include <boost/assert.hpp>
31#include <algorithm>
32#include <iterator>
33#include <cstddef>
34#include <cstring>
35#include <cassert>
36
37#ifdef BOOST_WINDOWS_API
38# include "windows_file_codecvt.hpp"
39# include <windows.h>
40#elif defined(macintosh) || defined(__APPLE__) || defined(__APPLE_CC__) \
41 || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__HAIKU__)
42# include <boost/filesystem/detail/utf8_codecvt_facet.hpp>
43#endif
44
45#ifdef BOOST_FILESYSTEM_DEBUG
46# include <iostream>
47# include <iomanip>
48#endif
49
50namespace fs = boost::filesystem;
51
52using boost::filesystem::path;
53
54using std::string;
55using std::wstring;
56
57using boost::system::error_code;
58
59//--------------------------------------------------------------------------------------//
60// //
61// class path helpers //
62// //
63//--------------------------------------------------------------------------------------//
64
65namespace
66{
67 //------------------------------------------------------------------------------------//
68 // miscellaneous class path helpers //
69 //------------------------------------------------------------------------------------//
70
71 typedef path::value_type value_type;
72 typedef path::string_type string_type;
73 typedef string_type::size_type size_type;
74
75# ifdef BOOST_WINDOWS_API
76
77 const wchar_t* const separators = L"/\\";
78 const wchar_t* separator_string = L"/";
79 const wchar_t* preferred_separator_string = L"\\";
80 const wchar_t colon = L':';
81 const wchar_t questionmark = L'?';
82
83 inline bool is_letter(wchar_t c)
84 {
85 return (c >= L'a' && c <=L'z') || (c >= L'A' && c <=L'Z');
86 }
87
88# else
89
90 const char* const separators = "/";
91 const char* separator_string = "/";
92 const char* preferred_separator_string = "/";
93
94# endif
95
96 bool is_root_separator(const string_type& str, size_type pos);
97 // pos is position of the separator
98
99 size_type filename_pos(const string_type& str,
100 size_type end_pos); // end_pos is past-the-end position
101 // Returns: 0 if str itself is filename (or empty)
102
103 size_type root_directory_start(const string_type& path, size_type size);
104 // Returns: npos if no root_directory found
105
106 void first_element(
107 const string_type& src,
108 size_type& element_pos,
109 size_type& element_size,
110# if !BOOST_WORKAROUND(BOOST_MSVC, <= 1310) // VC++ 7.1
111 size_type size = string_type::npos
112# else
113 size_type size = -1
114# endif
115 );
116
117} // unnamed namespace
118
119//--------------------------------------------------------------------------------------//
120// //
121// class path implementation //
122// //
123//--------------------------------------------------------------------------------------//
124
125namespace boost
126{
127namespace filesystem
128{
129
130 BOOST_FILESYSTEM_DECL path& path::operator/=(const path& p)
131 {
132 if (p.empty())
133 return *this;
134 if (this == &p) // self-append
135 {
136 path rhs(p);
137 if (!detail::is_directory_separator(rhs.m_pathname[0]))
138 m_append_separator_if_needed();
139 m_pathname += rhs.m_pathname;
140 }
141 else
142 {
143 if (!detail::is_directory_separator(*p.m_pathname.begin()))
144 m_append_separator_if_needed();
145 m_pathname += p.m_pathname;
146 }
147 return *this;
148 }
149
150 BOOST_FILESYSTEM_DECL path& path::operator/=(const value_type* ptr)
151 {
152 if (!*ptr)
153 return *this;
154 if (ptr >= m_pathname.data()
155 && ptr < m_pathname.data() + m_pathname.size()) // overlapping source
156 {
157 path rhs(ptr);
158 if (!detail::is_directory_separator(rhs.m_pathname[0]))
159 m_append_separator_if_needed();
160 m_pathname += rhs.m_pathname;
161 }
162 else
163 {
164 if (!detail::is_directory_separator(*ptr))
165 m_append_separator_if_needed();
166 m_pathname += ptr;
167 }
168 return *this;
169 }
170
171# ifdef BOOST_WINDOWS_API
172
173 BOOST_FILESYSTEM_DECL path path::generic_path() const
174 {
175 path tmp(*this);
176 std::replace(tmp.m_pathname.begin(), tmp.m_pathname.end(), L'\\', L'/');
177 return tmp;
178 }
179
180# endif // BOOST_WINDOWS_API
181
182 BOOST_FILESYSTEM_DECL int path::compare(const path& p) const BOOST_NOEXCEPT
183 {
184 return detail::lex_compare(begin(), end(), p.begin(), p.end());
185 }
186
187 // m_append_separator_if_needed ----------------------------------------------------//
188
189 BOOST_FILESYSTEM_DECL path::string_type::size_type path::m_append_separator_if_needed()
190 {
191 if (!m_pathname.empty() &&
192# ifdef BOOST_WINDOWS_API
193 *(m_pathname.end()-1) != colon &&
194# endif
195 !detail::is_directory_separator(*(m_pathname.end()-1)))
196 {
197 string_type::size_type tmp(m_pathname.size());
198 m_pathname += preferred_separator;
199 return tmp;
200 }
201 return 0;
202 }
203
204 // m_erase_redundant_separator -----------------------------------------------------//
205
206 BOOST_FILESYSTEM_DECL void path::m_erase_redundant_separator(string_type::size_type sep_pos)
207 {
208 if (sep_pos // a separator was added
209 && sep_pos < m_pathname.size() // and something was appended
210 && (m_pathname[sep_pos+1] == separator // and it was also separator
211# ifdef BOOST_WINDOWS_API
212 || m_pathname[sep_pos+1] == preferred_separator // or preferred_separator
213# endif
214)) { m_pathname.erase(sep_pos, 1); } // erase the added separator
215 }
216
217 // modifiers -----------------------------------------------------------------------//
218
219# ifdef BOOST_WINDOWS_API
220 BOOST_FILESYSTEM_DECL path& path::make_preferred()
221 {
222 std::replace(m_pathname.begin(), m_pathname.end(), L'/', L'\\');
223 return *this;
224 }
225# endif
226
227 BOOST_FILESYSTEM_DECL path& path::remove_filename()
228 {
229 m_pathname.erase(m_parent_path_end());
230 return *this;
231 }
232
233 BOOST_FILESYSTEM_DECL path& path::remove_trailing_separator()
234 {
235 if (!m_pathname.empty()
236 && detail::is_directory_separator(m_pathname[m_pathname.size() - 1]))
237 m_pathname.erase(m_pathname.size() - 1);
238 return *this;
239 }
240
241 BOOST_FILESYSTEM_DECL path& path::replace_extension(const path& new_extension)
242 {
243 // erase existing extension, including the dot, if any
244 m_pathname.erase(m_pathname.size()-extension().m_pathname.size());
245
246 if (!new_extension.empty())
247 {
248 // append new_extension, adding the dot if necessary
249 if (new_extension.m_pathname[0] != dot)
250 m_pathname.push_back(dot);
251 m_pathname.append(new_extension.m_pathname);
252 }
253
254 return *this;
255 }
256
257 // decomposition -------------------------------------------------------------------//
258
259 BOOST_FILESYSTEM_DECL path path::root_path() const
260 {
261 path temp(root_name());
262 if (!root_directory().empty()) temp.m_pathname += root_directory().c_str();
263 return temp;
264 }
265
266 BOOST_FILESYSTEM_DECL path path::root_name() const
267 {
268 iterator itr(begin());
269
270 return (itr.m_pos != m_pathname.size()
271 && (
272 (itr.m_element.m_pathname.size() > 1
273 && detail::is_directory_separator(itr.m_element.m_pathname[0])
274 && detail::is_directory_separator(itr.m_element.m_pathname[1]))
275# ifdef BOOST_WINDOWS_API
276 || itr.m_element.m_pathname[itr.m_element.m_pathname.size()-1] == colon
277# endif
278 ))
279 ? itr.m_element
280 : path();
281 }
282
283 BOOST_FILESYSTEM_DECL path path::root_directory() const
284 {
285 size_type pos(root_directory_start(m_pathname, m_pathname.size()));
286
287 return pos == string_type::npos
288 ? path()
289 : path(m_pathname.c_str() + pos, m_pathname.c_str() + pos + 1);
290 }
291
292 BOOST_FILESYSTEM_DECL path path::relative_path() const
293 {
294 iterator itr(begin());
295
296 for (; itr.m_pos != m_pathname.size()
297 && (detail::is_directory_separator(itr.m_element.m_pathname[0])
298# ifdef BOOST_WINDOWS_API
299 || itr.m_element.m_pathname[itr.m_element.m_pathname.size()-1] == colon
300# endif
301 ); ++itr) {}
302
303 return path(m_pathname.c_str() + itr.m_pos);
304 }
305
306 BOOST_FILESYSTEM_DECL string_type::size_type path::m_parent_path_end() const
307 {
308 size_type end_pos(filename_pos(m_pathname, m_pathname.size()));
309
310 bool filename_was_separator(m_pathname.size()
311 && detail::is_directory_separator(m_pathname[end_pos]));
312
313 // skip separators unless root directory
314 size_type root_dir_pos(root_directory_start(m_pathname, end_pos));
315 for (;
316 end_pos > 0
317 && (end_pos-1) != root_dir_pos
318 && detail::is_directory_separator(m_pathname[end_pos-1])
319 ;
320 --end_pos) {}
321
322 return (end_pos == 1 && root_dir_pos == 0 && filename_was_separator)
323 ? string_type::npos
324 : end_pos;
325 }
326
327 BOOST_FILESYSTEM_DECL path path::parent_path() const
328 {
329 size_type end_pos(m_parent_path_end());
330 return end_pos == string_type::npos
331 ? path()
332 : path(m_pathname.c_str(), m_pathname.c_str() + end_pos);
333 }
334
335 BOOST_FILESYSTEM_DECL path path::filename() const
336 {
337 size_type pos(filename_pos(m_pathname, m_pathname.size()));
338 return (m_pathname.size()
339 && pos
340 && detail::is_directory_separator(m_pathname[pos])
341 && !is_root_separator(m_pathname, pos))
342 ? detail::dot_path()
343 : path(m_pathname.c_str() + pos);
344 }
345
346 BOOST_FILESYSTEM_DECL path path::stem() const
347 {
348 path name(filename());
349 if (name == detail::dot_path() || name == detail::dot_dot_path()) return name;
350 size_type pos(name.m_pathname.rfind(dot));
351 return pos == string_type::npos
352 ? name
353 : path(name.m_pathname.c_str(), name.m_pathname.c_str() + pos);
354 }
355
356 BOOST_FILESYSTEM_DECL path path::extension() const
357 {
358 path name(filename());
359 if (name == detail::dot_path() || name == detail::dot_dot_path()) return path();
360 size_type pos(name.m_pathname.rfind(dot));
361 return pos == string_type::npos
362 ? path()
363 : path(name.m_pathname.c_str() + pos);
364 }
365
366 // lexical operations --------------------------------------------------------------//
367
368 namespace detail
369 {
370 // C++14 provides a mismatch algorithm with four iterator arguments(), but earlier
371 // standard libraries didn't, so provide this needed functionality.
372 inline
373 std::pair<path::iterator, path::iterator> mismatch(path::iterator it1,
374 path::iterator it1end, path::iterator it2, path::iterator it2end)
375 {
376 for (; it1 != it1end && it2 != it2end && *it1 == *it2;)
377 {
378 ++it1;
379 ++it2;
380 }
381 return std::make_pair(it1, it2);
382 }
383 }
384
385 BOOST_FILESYSTEM_DECL path path::lexically_relative(const path& base) const
386 {
387 std::pair<path::iterator, path::iterator> mm
388 = detail::mismatch(begin(), end(), base.begin(), base.end());
389 if (mm.first == begin() && mm.second == base.begin())
390 return path();
391 if (mm.first == end() && mm.second == base.end())
392 return detail::dot_path();
393 path tmp;
394 for (; mm.second != base.end(); ++mm.second)
395 tmp /= detail::dot_dot_path();
396 for (; mm.first != end(); ++mm.first)
397 tmp /= *mm.first;
398 return tmp;
399 }
400
401 // normal --------------------------------------------------------------------------//
402
403 BOOST_FILESYSTEM_DECL path path::lexically_normal() const
404 {
405 if (m_pathname.empty())
406 return *this;
407
408 path temp;
409 iterator start(begin());
410 iterator last(end());
411 iterator stop(last--);
412 for (iterator itr(start); itr != stop; ++itr)
413 {
414 // ignore "." except at start and last
415 if (itr->native().size() == 1
416 && (itr->native())[0] == dot
417 && itr != start
418 && itr != last) continue;
419
420 // ignore a name and following ".."
421 if (!temp.empty()
422 && itr->native().size() == 2
423 && (itr->native())[0] == dot
424 && (itr->native())[1] == dot) // dot dot
425 {
426 string_type lf(temp.filename().native());
427 if (lf.size() > 0
428 && (lf.size() != 1
429 || (lf[0] != dot
430 && lf[0] != separator))
431 && (lf.size() != 2
432 || (lf[0] != dot
433 && lf[1] != dot
434# ifdef BOOST_WINDOWS_API
435 && lf[1] != colon
436# endif
437 )
438 )
439 )
440 {
441 temp.remove_filename();
442 //// if not root directory, must also remove "/" if any
443 //if (temp.native().size() > 0
444 // && temp.native()[temp.native().size()-1]
445 // == separator)
446 //{
447 // string_type::size_type rds(
448 // root_directory_start(temp.native(), temp.native().size()));
449 // if (rds == string_type::npos
450 // || rds != temp.native().size()-1)
451 // {
452 // temp.m_pathname.erase(temp.native().size()-1);
453 // }
454 //}
455
456 iterator next(itr);
457 if (temp.empty() && ++next != stop
458 && next == last && *last == detail::dot_path())
459 {
460 temp /= detail::dot_path();
461 }
462 continue;
463 }
464 }
465
466 temp /= *itr;
467 };
468
469 if (temp.empty())
470 temp /= detail::dot_path();
471 return temp;
472 }
473
474} // namespace filesystem
475} // namespace boost
476
477//--------------------------------------------------------------------------------------//
478// //
479// class path helpers implementation //
480// //
481//--------------------------------------------------------------------------------------//
482
483namespace
484{
485
486 // is_root_separator ---------------------------------------------------------------//
487
488 bool is_root_separator(const string_type & str, size_type pos)
489 // pos is position of the separator
490 {
491 BOOST_ASSERT_MSG(!str.empty() && fs::detail::is_directory_separator(str[pos]),
492 "precondition violation");
493
494 // subsequent logic expects pos to be for leftmost slash of a set
495 while (pos > 0 && fs::detail::is_directory_separator(str[pos-1]))
496 --pos;
497
498 // "/" [...]
499 if (pos == 0)
500 return true;
501
502# ifdef BOOST_WINDOWS_API
503 // "c:/" [...]
504 if (pos == 2 && is_letter(str[0]) && str[1] == colon)
505 return true;
506# endif
507
508 // "//" name "/"
509 if (pos < 3 || !fs::detail::is_directory_separator(str[0])
510 || !fs::detail::is_directory_separator(str[1]))
511 return false;
512
513 return str.find_first_of(separators, 2) == pos;
514 }
515
516 // filename_pos --------------------------------------------------------------------//
517
518 size_type filename_pos(const string_type & str,
519 size_type end_pos) // end_pos is past-the-end position
520 // return 0 if str itself is filename (or empty)
521 {
522 // case: "//"
523 if (end_pos == 2
524 && fs::detail::is_directory_separator(str[0])
525 && fs::detail::is_directory_separator(str[1])) return 0;
526
527 // case: ends in "/"
528 if (end_pos && fs::detail::is_directory_separator(str[end_pos-1]))
529 return end_pos-1;
530
531 // set pos to start of last element
532 size_type pos(str.find_last_of(separators, end_pos-1));
533
534# ifdef BOOST_WINDOWS_API
535 if (pos == string_type::npos && end_pos > 1)
536 pos = str.find_last_of(colon, end_pos-2);
537# endif
538
539 return (pos == string_type::npos // path itself must be a filename (or empty)
540 || (pos == 1 && fs::detail::is_directory_separator(str[0]))) // or net
541 ? 0 // so filename is entire string
542 : pos + 1; // or starts after delimiter
543 }
544
545 // root_directory_start ------------------------------------------------------------//
546
547 size_type root_directory_start(const string_type & path, size_type size)
548 // return npos if no root_directory found
549 {
550
551# ifdef BOOST_WINDOWS_API
552 // case "c:/"
553 if (size > 2
554 && path[1] == colon
555 && fs::detail::is_directory_separator(path[2])) return 2;
556# endif
557
558 // case "//"
559 if (size == 2
560 && fs::detail::is_directory_separator(path[0])
561 && fs::detail::is_directory_separator(path[1])) return string_type::npos;
562
563# ifdef BOOST_WINDOWS_API
564 // case "\\?\"
565 if (size > 4
566 && fs::detail::is_directory_separator(path[0])
567 && fs::detail::is_directory_separator(path[1])
568 && path[2] == questionmark
569 && fs::detail::is_directory_separator(path[3]))
570 {
571 string_type::size_type pos(path.find_first_of(separators, 4));
572 return pos < size ? pos : string_type::npos;
573 }
574# endif
575
576 // case "//net {/}"
577 if (size > 3
578 && fs::detail::is_directory_separator(path[0])
579 && fs::detail::is_directory_separator(path[1])
580 && !fs::detail::is_directory_separator(path[2]))
581 {
582 string_type::size_type pos(path.find_first_of(separators, 2));
583 return pos < size ? pos : string_type::npos;
584 }
585
586 // case "/"
587 if (size > 0 && fs::detail::is_directory_separator(path[0])) return 0;
588
589 return string_type::npos;
590 }
591
592 // first_element --------------------------------------------------------------------//
593 // sets pos and len of first element, excluding extra separators
594 // if src.empty(), sets pos,len, to 0,0.
595
596 void first_element(
597 const string_type & src,
598 size_type & element_pos,
599 size_type & element_size,
600 size_type size
601)
602 {
603 if (size == string_type::npos) size = src.size();
604 element_pos = 0;
605 element_size = 0;
606 if (src.empty()) return;
607
608 string_type::size_type cur(0);
609
610 // deal with // [network]
611 if (size >= 2 && fs::detail::is_directory_separator(src[0])
612 && fs::detail::is_directory_separator(src[1])
613 && (size == 2
614 || !fs::detail::is_directory_separator(src[2])))
615 {
616 cur += 2;
617 element_size += 2;
618 }
619
620 // leading (not non-network) separator
621 else if (fs::detail::is_directory_separator(src[0]))
622 {
623 ++element_size;
624 // bypass extra leading separators
625 while (cur+1 < size
626 && fs::detail::is_directory_separator(src[cur+1]))
627 {
628 ++cur;
629 ++element_pos;
630 }
631 return;
632 }
633
634 // at this point, we have either a plain name, a network name,
635 // or (on Windows only) a device name
636
637 // find the end
638 while (cur < size
639# ifdef BOOST_WINDOWS_API
640 && src[cur] != colon
641# endif
642 && !fs::detail::is_directory_separator(src[cur]))
643 {
644 ++cur;
645 ++element_size;
646 }
647
648# ifdef BOOST_WINDOWS_API
649 if (cur == size) return;
650 // include device delimiter
651 if (src[cur] == colon)
652 { ++element_size; }
653# endif
654
655 return;
656 }
657
658} // unnamed namespace
659
660
661namespace boost
662{
663namespace filesystem
664{
665 namespace detail
666 {
667 BOOST_FILESYSTEM_DECL
668 int lex_compare(path::iterator first1, path::iterator last1,
669 path::iterator first2, path::iterator last2)
670 {
671 for (; first1 != last1 && first2 != last2;)
672 {
673 if (first1->native() < first2->native()) return -1;
674 if (first2->native() < first1->native()) return 1;
675 BOOST_ASSERT(first2->native() == first1->native());
676 ++first1;
677 ++first2;
678 }
679 if (first1 == last1 && first2 == last2)
680 return 0;
681 return first1 == last1 ? -1 : 1;
682 }
683
684 BOOST_FILESYSTEM_DECL
685 const path& dot_path()
686 {
687# ifdef BOOST_WINDOWS_API
688 static const fs::path dot_pth(L".");
689# else
690 static const fs::path dot_pth(".");
691# endif
692 return dot_pth;
693 }
694
695 BOOST_FILESYSTEM_DECL
696 const path& dot_dot_path()
697 {
698# ifdef BOOST_WINDOWS_API
699 static const fs::path dot_dot(L"..");
700# else
701 static const fs::path dot_dot("..");
702# endif
703 return dot_dot;
704 }
705 }
706
707//--------------------------------------------------------------------------------------//
708// //
709// class path::iterator implementation //
710// //
711//--------------------------------------------------------------------------------------//
712
713 BOOST_FILESYSTEM_DECL path::iterator path::begin() const
714 {
715 iterator itr;
716 itr.m_path_ptr = this;
717 size_type element_size;
718 first_element(m_pathname, itr.m_pos, element_size);
719 itr.m_element = m_pathname.substr(itr.m_pos, element_size);
720 if (itr.m_element.m_pathname == preferred_separator_string)
721 itr.m_element.m_pathname = separator_string; // needed for Windows, harmless on POSIX
722 return itr;
723 }
724
725 BOOST_FILESYSTEM_DECL path::iterator path::end() const
726 {
727 iterator itr;
728 itr.m_path_ptr = this;
729 itr.m_pos = m_pathname.size();
730 return itr;
731 }
732
733 BOOST_FILESYSTEM_DECL void path::m_path_iterator_increment(path::iterator & it)
734 {
735 BOOST_ASSERT_MSG(it.m_pos < it.m_path_ptr->m_pathname.size(),
736 "path::basic_iterator increment past end()");
737
738 // increment to position past current element; if current element is implicit dot,
739 // this will cause it.m_pos to represent the end iterator
740 it.m_pos += it.m_element.m_pathname.size();
741
742 // if the end is reached, we are done
743 if (it.m_pos == it.m_path_ptr->m_pathname.size())
744 {
745 it.m_element.clear(); // aids debugging, may release unneeded memory
746 return;
747 }
748
749 // both POSIX and Windows treat paths that begin with exactly two separators specially
750 bool was_net(it.m_element.m_pathname.size() > 2
751 && detail::is_directory_separator(it.m_element.m_pathname[0])
752 && detail::is_directory_separator(it.m_element.m_pathname[1])
753 && !detail::is_directory_separator(it.m_element.m_pathname[2]));
754
755 // process separator (Windows drive spec is only case not a separator)
756 if (detail::is_directory_separator(it.m_path_ptr->m_pathname[it.m_pos]))
757 {
758 // detect root directory
759 if (was_net
760# ifdef BOOST_WINDOWS_API
761 // case "c:/"
762 || it.m_element.m_pathname[it.m_element.m_pathname.size()-1] == colon
763# endif
764 )
765 {
766 it.m_element.m_pathname = separator; // generic format; see docs
767 return;
768 }
769
770 // skip separators until it.m_pos points to the start of the next element
771 while (it.m_pos != it.m_path_ptr->m_pathname.size()
772 && detail::is_directory_separator(it.m_path_ptr->m_pathname[it.m_pos]))
773 { ++it.m_pos; }
774
775 // detect trailing separator, and treat it as ".", per POSIX spec
776 if (it.m_pos == it.m_path_ptr->m_pathname.size()
777 && !is_root_separator(it.m_path_ptr->m_pathname, it.m_pos-1))
778 {
779 --it.m_pos;
780 it.m_element = detail::dot_path();
781 return;
782 }
783 }
784
785 // get m_element
786 size_type end_pos(it.m_path_ptr->m_pathname.find_first_of(separators, it.m_pos));
787 if (end_pos == string_type::npos)
788 end_pos = it.m_path_ptr->m_pathname.size();
789 it.m_element = it.m_path_ptr->m_pathname.substr(it.m_pos, end_pos - it.m_pos);
790 }
791
792 BOOST_FILESYSTEM_DECL void path::m_path_iterator_decrement(path::iterator & it)
793 {
794 BOOST_ASSERT_MSG(it.m_pos, "path::iterator decrement past begin()");
795
796 size_type end_pos(it.m_pos);
797
798 // if at end and there was a trailing non-root '/', return "."
799 if (it.m_pos == it.m_path_ptr->m_pathname.size()
800 && it.m_path_ptr->m_pathname.size() > 1
801 && detail::is_directory_separator(it.m_path_ptr->m_pathname[it.m_pos-1])
802 && !is_root_separator(it.m_path_ptr->m_pathname, it.m_pos-1)
803 )
804 {
805 --it.m_pos;
806 it.m_element = detail::dot_path();
807 return;
808 }
809
810 size_type root_dir_pos(root_directory_start(it.m_path_ptr->m_pathname, end_pos));
811
812 // skip separators unless root directory
813 for (
814 ;
815 end_pos > 0
816 && (end_pos-1) != root_dir_pos
817 && detail::is_directory_separator(it.m_path_ptr->m_pathname[end_pos-1])
818 ;
819 --end_pos) {}
820
821 it.m_pos = filename_pos(it.m_path_ptr->m_pathname, end_pos);
822 it.m_element = it.m_path_ptr->m_pathname.substr(it.m_pos, end_pos - it.m_pos);
823 if (it.m_element.m_pathname == preferred_separator_string) // needed for Windows, harmless on POSIX
824 it.m_element.m_pathname = separator_string; // generic format; see docs
825 }
826
827} // namespace filesystem
828} // namespace boost
829
830namespace
831{
832
833 //------------------------------------------------------------------------------------//
834 // locale helpers //
835 //------------------------------------------------------------------------------------//
836
837 // Prior versions of these locale and codecvt implementations tried to take advantage
838 // of static initialization where possible, kept a local copy of the current codecvt
839 // facet (to avoid codecvt() having to call use_facet()), and was not multi-threading
840 // safe (again for efficiency).
841 //
842 // This was error prone, and required different implementation techniques depending
843 // on the compiler and also whether static or dynamic linking was used. Furthermore,
844 // users could not easily provide their multi-threading safe wrappers because the
845 // path interface requires the implementation itself to call codecvt() to obtain the
846 // default facet, and the initialization of the static within path_locale() could race.
847 //
848 // The code below is portable to all platforms, is much simpler, and hopefully will be
849 // much more robust. Timing tests (on Windows, using a Visual C++ release build)
850 // indicated the current code is roughly 9% slower than the previous code, and that
851 // seems a small price to pay for better code that is easier to use.
852
853 std::locale default_locale()
854 {
855# if defined(BOOST_WINDOWS_API)
856 std::locale global_loc = std::locale();
857 return std::locale(global_loc, new windows_file_codecvt);
858# elif defined(macintosh) || defined(__APPLE__) || defined(__APPLE_CC__) \
859 || defined(__FreeBSD__) || defined(__OpenBSD__) || defined(__HAIKU__)
860 // "All BSD system functions expect their string parameters to be in UTF-8 encoding
861 // and nothing else." See
862 // http://developer.apple.com/mac/library/documentation/MacOSX/Conceptual/BPInternational/Articles/FileEncodings.html
863 //
864 // "The kernel will reject any filename that is not a valid UTF-8 string, and it will
865 // even be normalized (to Unicode NFD) before stored on disk, at least when using HFS.
866 // The right way to deal with it would be to always convert the filename to UTF-8
867 // before trying to open/create a file." See
868 // http://lists.apple.com/archives/unix-porting/2007/Sep/msg00023.html
869 //
870 // "How a file name looks at the API level depends on the API. Current Carbon APIs
871 // handle file names as an array of UTF-16 characters; POSIX ones handle them as an
872 // array of UTF-8, which is why UTF-8 works well in Terminal. How it's stored on disk
873 // depends on the disk format; HFS+ uses UTF-16, but that's not important in most
874 // cases." See
875 // http://lists.apple.com/archives/applescript-users/2002/Sep/msg00319.html
876 //
877 // Many thanks to Peter Dimov for digging out the above references!
878
879 std::locale global_loc = std::locale();
880 return std::locale(global_loc, new boost::filesystem::detail::utf8_codecvt_facet);
881# else // Other POSIX
882 // ISO C calls std::locale("") "the locale-specific native environment", and this
883 // locale is the default for many POSIX-based operating systems such as Linux.
884 return std::locale("");
885# endif
886 }
887
888 std::locale& path_locale()
889 // std::locale("") construction, needed on non-Apple POSIX systems, can throw
890 // (if environmental variables LC_MESSAGES or LANG are wrong, for example), so
891 // path_locale() provides lazy initialization via a local static to ensure that any
892 // exceptions occur after main() starts and so can be caught. Furthermore,
893 // path_locale() is only called if path::codecvt() or path::imbue() are themselves
894 // actually called, ensuring that an exception will only be thrown if std::locale("")
895 // is really needed.
896 {
897 // [locale] paragraph 6: Once a facet reference is obtained from a locale object by
898 // calling use_facet<>, that reference remains usable, and the results from member
899 // functions of it may be cached and re-used, as long as some locale object refers
900 // to that facet.
901 static std::locale loc(default_locale());
902#ifdef BOOST_FILESYSTEM_DEBUG
903 std::cout << "***** path_locale() called" << std::endl;
904#endif
905 return loc;
906 }
907} // unnamed namespace
908
909//--------------------------------------------------------------------------------------//
910// path::codecvt() and path::imbue() implementation //
911//--------------------------------------------------------------------------------------//
912
913namespace boost
914{
915namespace filesystem
916{
917 // See comments above
918
919 BOOST_FILESYSTEM_DECL const path::codecvt_type& path::codecvt()
920 {
921#ifdef BOOST_FILESYSTEM_DEBUG
922 std::cout << "***** path::codecvt() called" << std::endl;
923#endif
924 BOOST_ASSERT_MSG(&path_locale(), "boost::filesystem::path locale initialization error");
925
926 return std::use_facet<std::codecvt<wchar_t, char, std::mbstate_t> >(path_locale());
927 }
928
929 BOOST_FILESYSTEM_DECL std::locale path::imbue(const std::locale& loc)
930 {
931#ifdef BOOST_FILESYSTEM_DEBUG
932 std::cout << "***** path::imbue() called" << std::endl;
933#endif
934 std::locale temp(path_locale());
935 path_locale() = loc;
936 return temp;
937 }
938
939} // namespace filesystem
940} // namespace boost
941