1
// filesystem path.cpp ------------------------------------------------------------- //
3
// Copyright Beman Dawes 2008
5
// Distributed under the Boost Software License, Version 1.0.
6
// See http://www.boost.org/LICENSE_1_0.txt
8
// Library home page: http://www.boost.org/libs/filesystem
10
#include <boost/config.hpp>
11
#if !defined( BOOST_NO_STD_WSTRING )
12
// Boost.Filesystem V3 and later requires std::wstring support.
13
// During the transition to V3, libraries are compiled with both V2 and V3 sources.
14
// On old compilers that don't support V3 anyhow, we just skip everything so the compile
15
// will succeed and the library can be built.
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
21
#ifndef BOOST_SYSTEM_NO_DEPRECATED
22
# define BOOST_SYSTEM_NO_DEPRECATED
25
#include <boost/filesystem/v3/config.hpp>
26
#include <boost/filesystem/v3/path.hpp>
27
#include <boost/scoped_array.hpp>
28
#include <boost/system/error_code.hpp>
29
#include <boost/assert.hpp>
34
#ifdef BOOST_WINDOWS_API
35
# include "windows_file_codecvt.hpp"
37
#elif defined(macintosh) || defined(__APPLE__) || defined(__APPLE_CC__)
38
# include <boost/filesystem/detail/utf8_codecvt_facet.hpp>
41
#ifdef BOOST_FILESYSTEM_DEBUG
46
namespace fs = boost::filesystem3;
48
using boost::filesystem3::path;
53
using boost::system::error_code;
55
#ifndef BOOST_FILESYSTEM_CODECVT_BUF_SIZE
56
# define BOOST_FILESYSTEM_CODECVT_BUF_SIZE 256
59
//--------------------------------------------------------------------------------------//
61
// class path helpers //
63
//--------------------------------------------------------------------------------------//
67
//------------------------------------------------------------------------------------//
68
// miscellaneous class path helpers //
69
//------------------------------------------------------------------------------------//
71
typedef path::value_type value_type;
72
typedef path::string_type string_type;
73
typedef string_type::size_type size_type;
75
const std::size_t default_codecvt_buf_size = BOOST_FILESYSTEM_CODECVT_BUF_SIZE;
77
# ifdef BOOST_WINDOWS_API
79
const wchar_t separator = L'/';
80
const wchar_t preferred_separator = L'\\';
81
const wchar_t* const separators = L"/\\";
82
const wchar_t* separator_string = L"/";
83
const wchar_t* preferred_separator_string = L"\\";
84
const wchar_t colon = L':';
85
const wchar_t dot = L'.';
86
const fs::path dot_path(L".");
87
const fs::path dot_dot_path(L"..");
91
const char separator = '/';
92
const char preferred_separator = '/';
93
const char* const separators = "/";
94
const char* separator_string = "/";
95
const char* preferred_separator_string = "/";
96
const char colon = ':';
98
const fs::path dot_path(".");
99
const fs::path dot_dot_path("..");
103
inline bool is_separator(fs::path::value_type c)
105
return c == separator
106
# ifdef BOOST_WINDOWS_API
107
|| c == preferred_separator
112
bool is_non_root_separator(const string_type& str, size_type pos);
113
// pos is position of the separator
115
size_type filename_pos(const string_type& str,
116
size_type end_pos); // end_pos is past-the-end position
117
// Returns: 0 if str itself is filename (or empty)
119
size_type root_directory_start(const string_type& path, size_type size);
120
// Returns: npos if no root_directory found
123
const string_type& src,
124
size_type& element_pos,
125
size_type& element_size,
126
# if !BOOST_WORKAROUND(BOOST_MSVC, <= 1310) // VC++ 7.1
127
size_type size = string_type::npos
133
} // unnamed namespace
135
//--------------------------------------------------------------------------------------//
137
// class path implementation //
139
//--------------------------------------------------------------------------------------//
143
namespace filesystem3
146
path & path::operator/=(const path & p)
150
if (!is_separator(*p.m_pathname.begin()))
151
m_append_separator_if_needed();
152
m_pathname += p.m_pathname;
156
# ifdef BOOST_WINDOWS_API
158
void path::m_portable()
160
for (string_type::iterator it = m_pathname.begin();
161
it != m_pathname.end(); ++it)
168
const std::string path::generic_string(const codecvt_type& cvt) const
172
return tmp.string(cvt);
175
const std::wstring path::generic_wstring() const
179
return tmp.wstring();
182
# endif // BOOST_WINDOWS_API
184
// m_append_separator_if_needed ----------------------------------------------------//
186
path::string_type::size_type path::m_append_separator_if_needed()
188
if (!m_pathname.empty() &&
189
# ifdef BOOST_WINDOWS_API
190
*(m_pathname.end()-1) != colon &&
192
!is_separator(*(m_pathname.end()-1)))
194
string_type::size_type tmp(m_pathname.size());
195
m_pathname += preferred_separator;
201
// m_erase_redundant_separator -----------------------------------------------------//
203
void path::m_erase_redundant_separator(string_type::size_type sep_pos)
205
if (sep_pos // a separator was added
206
&& sep_pos < m_pathname.size() // and something was appended
207
&& (m_pathname[sep_pos+1] == separator // and it was also separator
208
# ifdef BOOST_WINDOWS_API
209
|| m_pathname[sep_pos+1] == preferred_separator // or preferred_separator
211
)) { m_pathname.erase(sep_pos, 1); } // erase the added separator
214
// modifiers -----------------------------------------------------------------------//
216
# ifdef BOOST_WINDOWS_API
217
path & path::make_preferred()
219
for (string_type::iterator it = m_pathname.begin();
220
it != m_pathname.end(); ++it)
229
path& path::remove_filename()
231
m_pathname.erase(m_parent_path_end());
235
path & path::replace_extension(const path & source)
237
// erase existing extension if any
238
size_type pos(m_pathname.rfind(dot));
239
if (pos != string_type::npos)
240
m_pathname.erase(pos);
242
// append source extension if any
243
pos = source.m_pathname.rfind(dot);
244
if (pos != string_type::npos)
245
m_pathname += source.c_str() + pos;
250
// decomposition -------------------------------------------------------------------//
252
path path::root_path() const
254
path temp(root_name());
255
if (!root_directory().empty()) temp.m_pathname += root_directory().c_str();
259
path path::root_name() const
261
iterator itr(begin());
263
return (itr.m_pos != m_pathname.size()
265
(itr.m_element.m_pathname.size() > 1
266
&& is_separator(itr.m_element.m_pathname[0])
267
&& is_separator(itr.m_element.m_pathname[1])
269
# ifdef BOOST_WINDOWS_API
270
|| itr.m_element.m_pathname[itr.m_element.m_pathname.size()-1] == colon
277
path path::root_directory() const
279
size_type pos(root_directory_start(m_pathname, m_pathname.size()));
281
return pos == string_type::npos
283
: path(m_pathname.c_str() + pos, m_pathname.c_str() + pos + 1);
286
path path::relative_path() const
288
iterator itr(begin());
290
for (; itr.m_pos != m_pathname.size()
291
&& (is_separator(itr.m_element.m_pathname[0])
292
# ifdef BOOST_WINDOWS_API
293
|| itr.m_element.m_pathname[itr.m_element.m_pathname.size()-1] == colon
297
return path(m_pathname.c_str() + itr.m_pos);
300
string_type::size_type path::m_parent_path_end() const
302
size_type end_pos(filename_pos(m_pathname, m_pathname.size()));
304
bool filename_was_separator(m_pathname.size()
305
&& is_separator(m_pathname[end_pos]));
307
// skip separators unless root directory
308
size_type root_dir_pos(root_directory_start(m_pathname, end_pos));
311
&& (end_pos-1) != root_dir_pos
312
&& is_separator(m_pathname[end_pos-1])
316
return (end_pos == 1 && root_dir_pos == 0 && filename_was_separator)
321
path path::parent_path() const
323
size_type end_pos(m_parent_path_end());
324
return end_pos == string_type::npos
326
: path(m_pathname.c_str(), m_pathname.c_str() + end_pos);
329
path path::filename() const
331
size_type pos(filename_pos(m_pathname, m_pathname.size()));
332
return (m_pathname.size()
334
&& is_separator(m_pathname[pos])
335
&& is_non_root_separator(m_pathname, pos))
337
: path(m_pathname.c_str() + pos);
340
path path::stem() const
342
path name(filename());
343
if (name == dot_path || name == dot_dot_path) return name;
344
size_type pos(name.m_pathname.rfind(dot));
345
return pos == string_type::npos
347
: path(name.m_pathname.c_str(), name.m_pathname.c_str() + pos);
350
path path::extension() const
352
path name(filename());
353
if (name == dot_path || name == dot_dot_path) return path();
354
size_type pos(name.m_pathname.rfind(dot));
355
return pos == string_type::npos
357
: path(name.m_pathname.c_str() + pos);
360
// m_normalize ----------------------------------------------------------------------//
362
path& path::m_normalize()
364
if (m_pathname.empty()) return *this;
367
iterator start(begin());
368
iterator last(end());
369
iterator stop(last--);
370
for (iterator itr(start); itr != stop; ++itr)
372
// ignore "." except at start and last
373
if (itr->native().size() == 1
374
&& (itr->native())[0] == dot
376
&& itr != last) continue;
378
// ignore a name and following ".."
380
&& itr->native().size() == 2
381
&& (itr->native())[0] == dot
382
&& (itr->native())[1] == dot) // dot dot
384
string_type lf(temp.filename().native());
388
&& lf[0] != separator))
392
# ifdef BOOST_WINDOWS_API
399
temp.remove_filename();
400
// if not root directory, must also remove "/" if any
401
if (temp.m_pathname.size() > 0
402
&& temp.m_pathname[temp.m_pathname.size()-1]
405
string_type::size_type rds(
406
root_directory_start(temp.m_pathname, temp.m_pathname.size()));
407
if (rds == string_type::npos
408
|| rds != temp.m_pathname.size()-1)
409
{ temp.m_pathname.erase(temp.m_pathname.size()-1); }
413
if (temp.empty() && ++next != stop
414
&& next == last && *last == dot_path) temp /= dot_path;
422
if (temp.empty()) temp /= dot_path;
423
m_pathname = temp.m_pathname;
427
} // namespace filesystem3
430
//--------------------------------------------------------------------------------------//
432
// class path helpers implementation //
434
//--------------------------------------------------------------------------------------//
439
// is_non_root_separator -------------------------------------------------//
441
bool is_non_root_separator(const string_type & str, size_type pos)
442
// pos is position of the separator
444
BOOST_ASSERT(!str.empty() && is_separator(str[pos])
445
&& "precondition violation");
447
// subsequent logic expects pos to be for leftmost slash of a set
448
while (pos > 0 && is_separator(str[pos-1]))
452
&& (pos <= 2 || !is_separator(str[1])
453
|| str.find_first_of(separators, 2) != pos)
454
# ifdef BOOST_WINDOWS_API
455
&& (pos !=2 || str[1] != colon)
460
// filename_pos --------------------------------------------------------------------//
462
size_type filename_pos(const string_type & str,
463
size_type end_pos) // end_pos is past-the-end position
464
// return 0 if str itself is filename (or empty)
468
&& is_separator(str[0])
469
&& is_separator(str[1])) return 0;
472
if (end_pos && is_separator(str[end_pos-1]))
475
// set pos to start of last element
476
size_type pos(str.find_last_of(separators, end_pos-1));
478
# ifdef BOOST_WINDOWS_API
479
if (pos == string_type::npos)
480
pos = str.find_last_of(colon, end_pos-2);
483
return (pos == string_type::npos // path itself must be a filename (or empty)
484
|| (pos == 1 && is_separator(str[0]))) // or net
485
? 0 // so filename is entire string
486
: pos + 1; // or starts after delimiter
489
// root_directory_start ------------------------------------------------------------//
491
size_type root_directory_start(const string_type & path, size_type size)
492
// return npos if no root_directory found
495
# ifdef BOOST_WINDOWS_API
499
&& is_separator(path[2])) return 2;
504
&& is_separator(path[0])
505
&& is_separator(path[1])) return string_type::npos;
509
&& is_separator(path[0])
510
&& is_separator(path[1])
511
&& !is_separator(path[2]))
513
string_type::size_type pos(path.find_first_of(separators, 2));
514
return pos < size ? pos : string_type::npos;
518
if (size > 0 && is_separator(path[0])) return 0;
520
return string_type::npos;
523
// first_element --------------------------------------------------------------------//
524
// sets pos and len of first element, excluding extra separators
525
// if src.empty(), sets pos,len, to 0,0.
528
const string_type & src,
529
size_type & element_pos,
530
size_type & element_size,
534
if (size == string_type::npos) size = src.size();
537
if (src.empty()) return;
539
string_type::size_type cur(0);
541
// deal with // [network]
542
if (size >= 2 && is_separator(src[0])
543
&& is_separator(src[1])
545
|| !is_separator(src[2])))
551
// leading (not non-network) separator
552
else if (is_separator(src[0]))
555
// bypass extra leading separators
557
&& is_separator(src[cur+1]))
565
// at this point, we have either a plain name, a network name,
566
// or (on Windows only) a device name
570
# ifdef BOOST_WINDOWS_API
573
&& !is_separator(src[cur]))
579
# ifdef BOOST_WINDOWS_API
580
if (cur == size) return;
581
// include device delimiter
582
if (src[cur] == colon)
589
} // unnammed namespace
591
//--------------------------------------------------------------------------------------//
593
// class path::iterator implementation //
595
//--------------------------------------------------------------------------------------//
599
namespace filesystem3
602
path::iterator path::begin() const
605
itr.m_path_ptr = this;
606
size_type element_size;
607
first_element(m_pathname, itr.m_pos, element_size);
608
itr.m_element = m_pathname.substr(itr.m_pos, element_size);
609
if (itr.m_element.m_pathname == preferred_separator_string)
610
itr.m_element.m_pathname = separator_string; // needed for Windows, harmless on POSIX
614
path::iterator path::end() const
617
itr.m_path_ptr = this;
618
itr.m_pos = m_pathname.size();
622
void path::m_path_iterator_increment(path::iterator & it)
624
BOOST_ASSERT(it.m_pos < it.m_path_ptr->m_pathname.size() && "path::basic_iterator increment past end()");
626
// increment to position past current element
627
it.m_pos += it.m_element.m_pathname.size();
629
// if end reached, create end basic_iterator
630
if (it.m_pos == it.m_path_ptr->m_pathname.size())
632
it.m_element.clear();
636
// both POSIX and Windows treat paths that begin with exactly two separators specially
637
bool was_net(it.m_element.m_pathname.size() > 2
638
&& is_separator(it.m_element.m_pathname[0])
639
&& is_separator(it.m_element.m_pathname[1])
640
&& !is_separator(it.m_element.m_pathname[2]));
642
// process separator (Windows drive spec is only case not a separator)
643
if (is_separator(it.m_path_ptr->m_pathname[it.m_pos]))
645
// detect root directory
647
# ifdef BOOST_WINDOWS_API
649
|| it.m_element.m_pathname[it.m_element.m_pathname.size()-1] == colon
653
it.m_element.m_pathname = separator;
658
while (it.m_pos != it.m_path_ptr->m_pathname.size()
659
&& is_separator(it.m_path_ptr->m_pathname[it.m_pos]))
662
// detect trailing separator, and treat it as ".", per POSIX spec
663
if (it.m_pos == it.m_path_ptr->m_pathname.size()
664
&& is_non_root_separator(it.m_path_ptr->m_pathname, it.m_pos-1))
667
it.m_element = dot_path;
673
size_type end_pos(it.m_path_ptr->m_pathname.find_first_of(separators, it.m_pos));
674
if (end_pos == string_type::npos) end_pos = it.m_path_ptr->m_pathname.size();
675
it.m_element = it.m_path_ptr->m_pathname.substr(it.m_pos, end_pos - it.m_pos);
678
void path::m_path_iterator_decrement(path::iterator & it)
680
BOOST_ASSERT(it.m_pos && "path::iterator decrement past begin()");
682
size_type end_pos(it.m_pos);
684
// if at end and there was a trailing non-root '/', return "."
685
if (it.m_pos == it.m_path_ptr->m_pathname.size()
686
&& it.m_path_ptr->m_pathname.size() > 1
687
&& is_separator(it.m_path_ptr->m_pathname[it.m_pos-1])
688
&& is_non_root_separator(it.m_path_ptr->m_pathname, it.m_pos-1)
692
it.m_element = dot_path;
696
size_type root_dir_pos(root_directory_start(it.m_path_ptr->m_pathname, end_pos));
698
// skip separators unless root directory
702
&& (end_pos-1) != root_dir_pos
703
&& is_separator(it.m_path_ptr->m_pathname[end_pos-1])
707
it.m_pos = filename_pos(it.m_path_ptr->m_pathname, end_pos);
708
it.m_element = it.m_path_ptr->m_pathname.substr(it.m_pos, end_pos - it.m_pos);
709
if (it.m_element.m_pathname == preferred_separator_string)
710
it.m_element.m_pathname = separator_string; // needed for Windows, harmless on POSIX
713
} // namespace filesystem3
716
//--------------------------------------------------------------------------------------//
720
//--------------------------------------------------------------------------------------//
725
//------------------------------------------------------------------------------------//
727
//------------------------------------------------------------------------------------//
729
// std::locale construction can throw (if LC_MESSAGES is wrong, for example),
730
// so a static at function scope is used to ensure that exceptions can be
731
// caught. (A previous version was at namespace scope, so initialization
732
// occurred before main(), preventing exceptions from being caught.)
734
std::locale default_locale()
736
# ifdef BOOST_WINDOWS_API
737
std::locale global_loc = std::locale();
738
std::locale loc(global_loc, new windows_file_codecvt);
741
# elif defined(macintosh) || defined(__APPLE__) || defined(__APPLE_CC__)
742
// "All BSD system functions expect their string parameters to be in UTF-8 encoding
743
// and nothing else." http://developer.apple.com/mac/library/documentation/MacOSX/Conceptual/BPInternational/Articles/FileEncodings.html
745
// "The kernel will reject any filename that is not a valid UTF-8 string, and it will
746
// even be normalized (to Unicode NFD) before stored on disk, at least when using HFS.
747
// The right way to deal with it would be to always convert the filename to UTF-8
748
// before trying to open/create a file." http://lists.apple.com/archives/unix-porting/2007/Sep/msg00023.html
750
// "How a file name looks at the API level depends on the API. Current Carbon APIs
751
// handle file names as an array of UTF-16 characters; POSIX ones handle them as an
752
// array of UTF-8, which is why UTF-8 works well in Terminal. How it's stored on disk
753
// depends on the disk format; HFS+ uses UTF-16, but that's not important in most
754
// cases." http://lists.apple.com/archives/applescript-users/2002/Sep/msg00319.html
756
// Many thanks to Peter Dimov for digging out the above references!
757
std::locale global_loc = std::locale();
758
std::locale loc(global_loc, new boost::filesystem::detail::utf8_codecvt_facet);
762
// ISO C calls this "the locale-specific native environment":
763
return std::locale("");
768
std::locale & path_locale()
770
static std::locale loc(default_locale());
774
} // unnamed namespace
776
//--------------------------------------------------------------------------------------//
777
// path::imbue implementation //
778
//--------------------------------------------------------------------------------------//
782
namespace filesystem3
785
const path::codecvt_type *&
786
path::wchar_t_codecvt_facet()
788
static const std::codecvt<wchar_t, char, std::mbstate_t> *
790
&std::use_facet<std::codecvt<wchar_t, char, std::mbstate_t> >
795
std::locale path::imbue(const std::locale & loc)
797
std::locale temp(path_locale());
799
wchar_t_codecvt_facet() = &std::use_facet
800
<std::codecvt<wchar_t, char, std::mbstate_t> >(path_locale());
804
} // namespace filesystem3
807
#endif // no wide character support