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