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