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