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