| 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 | |
| 50 | namespace fs = boost::filesystem; |
| 51 | |
| 52 | using boost::filesystem::path; |
| 53 | |
| 54 | using std::string; |
| 55 | using std::wstring; |
| 56 | |
| 57 | using boost::system::error_code; |
| 58 | |
| 59 | //--------------------------------------------------------------------------------------// |
| 60 | // // |
| 61 | // class path helpers // |
| 62 | // // |
| 63 | //--------------------------------------------------------------------------------------// |
| 64 | |
| 65 | namespace |
| 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 | |
| 125 | namespace boost |
| 126 | { |
| 127 | namespace 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 | |
| 483 | namespace |
| 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 | |
| 661 | namespace boost |
| 662 | { |
| 663 | namespace 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 | |
| 830 | namespace |
| 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 | |
| 913 | namespace boost |
| 914 | { |
| 915 | namespace 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 | |