boost/filesystem/path.hpp
// boost/filesystem/path.hpp -----------------------------------------------//
// Copyright Beman Dawes 2002-2005
// Copyright Vladimir Prus 2002
// Distributed under the Boost Software License, Version 1.0. (See accompanying
// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
// See library home page at http://www.boost.org/libs/filesystem
// basic_path's stem(), extension(), and replace_extension() are based on
// basename(), extension(), and change_extension() from the original
// filesystem/convenience.hpp header by Vladimir Prus.
//----------------------------------------------------------------------------//
#ifndef BOOST_FILESYSTEM_PATH_HPP
#define BOOST_FILESYSTEM_PATH_HPP
#include <boost/filesystem/config.hpp>
#include <boost/system/system_error.hpp>
#include <boost/iterator/iterator_facade.hpp>
#include <boost/throw_exception.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/type_traits/is_same.hpp>
#include <boost/static_assert.hpp>
#include <string>
#include <algorithm> // for lexicographical_compare
#include <iosfwd> // needed by basic_path inserter and extractor
#include <stdexcept>
#include <cassert>
# ifndef BOOST_FILESYSTEM_NARROW_ONLY
# include <locale>
# endif
#include <boost/config/abi_prefix.hpp> // must be the last #include
//----------------------------------------------------------------------------//
namespace boost
{
namespace BOOST_FILESYSTEM_NAMESPACE
{
template<class String, class Traits> class basic_path;
struct path_traits;
typedef basic_path< std::string, path_traits > path;
struct path_traits
{
typedef std::string internal_string_type;
typedef std::string external_string_type;
static external_string_type to_external( const path &,
const internal_string_type & src ) { return src; }
static internal_string_type to_internal(
const external_string_type & src ) { return src; }
};
# ifndef BOOST_FILESYSTEM_NARROW_ONLY
struct BOOST_FILESYSTEM_DECL wpath_traits;
typedef basic_path< std::wstring, wpath_traits > wpath;
struct BOOST_FILESYSTEM_DECL wpath_traits
{
typedef std::wstring internal_string_type;
# ifdef BOOST_WINDOWS_API
typedef std::wstring external_string_type;
static external_string_type to_external( const wpath &,
const internal_string_type & src ) { return src; }
static internal_string_type to_internal(
const external_string_type & src ) { return src; }
# else
typedef std::string external_string_type;
static external_string_type to_external( const wpath & ph,
const internal_string_type & src );
static internal_string_type to_internal(
const external_string_type & src );
# endif
static void imbue( const std::locale & loc );
static bool imbue( const std::locale & loc, const std::nothrow_t & );
};
# endif // ifndef BOOST_FILESYSTEM_NARROW_ONLY
// path traits ---------------------------------------------------------//
template<class Path> struct is_basic_path
{ BOOST_STATIC_CONSTANT( bool, value = false ); };
template<> struct is_basic_path<path>
{ BOOST_STATIC_CONSTANT( bool, value = true ); };
# ifndef BOOST_FILESYSTEM_NARROW_ONLY
template<> struct is_basic_path<wpath>
{ BOOST_STATIC_CONSTANT( bool, value = true ); };
# endif
// These only have to be specialized if Path::string_type::value_type
// is not convertible from char, although specializations may eliminate
// compiler warnings. See ticket 2543.
template<class Path> struct slash
{ BOOST_STATIC_CONSTANT( char, value = '/' ); };
template<class Path> struct dot
{ BOOST_STATIC_CONSTANT( char, value = '.' ); };
template<class Path> struct colon
{ BOOST_STATIC_CONSTANT( char, value = ':' ); };
# ifndef BOOST_FILESYSTEM_NARROW_ONLY
template<> struct slash<wpath>
{ BOOST_STATIC_CONSTANT( wchar_t, value = L'/' ); };
template<> struct dot<wpath>
{ BOOST_STATIC_CONSTANT( wchar_t, value = L'.' ); };
template<> struct colon<wpath>
{ BOOST_STATIC_CONSTANT( wchar_t, value = L':' ); };
# endif
# ifdef BOOST_WINDOWS_PATH
template<class Path> struct path_alt_separator
{ BOOST_STATIC_CONSTANT( char, value = '\\' ); };
# ifndef BOOST_FILESYSTEM_NARROW_ONLY
template<> struct path_alt_separator<wpath>
{ BOOST_STATIC_CONSTANT( wchar_t, value = L'\\' ); };
# endif
# endif
// workaround for VC++ 7.0 and earlier issues with nested classes
namespace detail
{
template<class Path>
class iterator_helper
{
public:
typedef typename Path::iterator iterator;
static void do_increment( iterator & ph );
static void do_decrement( iterator & ph );
};
}
// basic_path ----------------------------------------------------------//
template<class String, class Traits>
class basic_path
{
// invariant: m_path valid according to the portable generic path grammar
// validate template arguments
// TODO: get these working
// BOOST_STATIC_ASSERT( ::boost::is_same<String,typename Traits::internal_string_type>::value );
// BOOST_STATIC_ASSERT( ::boost::is_same<typename Traits::external_string_type,std::string>::value || ::boost::is_same<typename Traits::external_string_type,std::wstring>::value );
public:
// compiler generates copy constructor and copy assignment
typedef basic_path<String, Traits> path_type;
typedef String string_type;
typedef typename String::value_type value_type;
typedef Traits traits_type;
typedef typename Traits::external_string_type external_string_type;
// constructors/destructor
basic_path() {}
basic_path( const string_type & s ) { operator/=( s ); }
basic_path( const value_type * s ) { operator/=( s ); }
# ifndef BOOST_NO_MEMBER_TEMPLATES
template <class InputIterator>
basic_path( InputIterator first, InputIterator last )
{ append( first, last ); }
# endif
~basic_path() {}
// assignments
basic_path & operator=( const string_type & s )
{
# if BOOST_WORKAROUND(BOOST_DINKUMWARE_STDLIB, >= 310)
m_path.clear();
# else
m_path.erase( m_path.begin(), m_path.end() );
# endif
operator/=( s );
return *this;
}
basic_path & operator=( const value_type * s )
{
# if BOOST_WORKAROUND(BOOST_DINKUMWARE_STDLIB, >= 310)
m_path.clear();
# else
m_path.erase( m_path.begin(), m_path.end() );
# endif
operator/=( s );
return *this;
}
# ifndef BOOST_NO_MEMBER_TEMPLATES
template <class InputIterator>
basic_path & assign( InputIterator first, InputIterator last )
{ m_path.clear(); append( first, last ); return *this; }
# endif
// modifiers
basic_path & operator/=( const basic_path & rhs ) { return operator /=( rhs.string().c_str() ); }
basic_path & operator/=( const string_type & rhs ) { return operator /=( rhs.c_str() ); }
basic_path & operator/=( const value_type * s );
# ifndef BOOST_NO_MEMBER_TEMPLATES
template <class InputIterator>
basic_path & append( InputIterator first, InputIterator last );
# endif
void clear()
{
# if BOOST_WORKAROUND(BOOST_DINKUMWARE_STDLIB, >= 310)
m_path.clear();
# else
m_path.erase( m_path.begin(), m_path.end() );
# endif
}
void swap( basic_path & rhs )
{
m_path.swap( rhs.m_path );
# ifdef BOOST_CYGWIN_PATH
std::swap( m_cygwin_root, rhs.m_cygwin_root );
# endif
}
basic_path & remove_filename();
basic_path & replace_extension( const string_type & new_extension = string_type() );
# ifndef BOOST_FILESYSTEM_NO_DEPRECATED
basic_path & remove_leaf() { return remove_filename(); }
# endif
// observers
const string_type & string() const { return m_path; }
const string_type file_string() const;
const string_type directory_string() const { return file_string(); }
const external_string_type external_file_string() const { return Traits::to_external( *this, file_string() ); }
const external_string_type external_directory_string() const { return Traits::to_external( *this, directory_string() ); }
basic_path root_path() const;
string_type root_name() const;
string_type root_directory() const;
basic_path relative_path() const;
basic_path parent_path() const;
string_type filename() const;
string_type stem() const;
string_type extension() const;
# ifndef BOOST_FILESYSTEM_NO_DEPRECATED
string_type leaf() const { return filename(); }
basic_path branch_path() const { return parent_path(); }
bool has_leaf() const { return !m_path.empty(); }
bool has_branch_path() const { return !parent_path().empty(); }
# endif
bool empty() const { return m_path.empty(); } // name consistent with std containers
bool is_complete() const;
bool has_root_path() const;
bool has_root_name() const;
bool has_root_directory() const;
bool has_relative_path() const { return !relative_path().empty(); }
bool has_filename() const { return !m_path.empty(); }
bool has_parent_path() const { return !parent_path().empty(); }
// iterators
class iterator : public boost::iterator_facade<
iterator,
string_type const,
boost::bidirectional_traversal_tag >
{
private:
friend class boost::iterator_core_access;
friend class boost::BOOST_FILESYSTEM_NAMESPACE::basic_path<String, Traits>;
const string_type & dereference() const
{ return m_name; }
bool equal( const iterator & rhs ) const
{ return m_path_ptr == rhs.m_path_ptr && m_pos == rhs.m_pos; }
friend class boost::BOOST_FILESYSTEM_NAMESPACE::detail::iterator_helper<path_type>;
void increment()
{
boost::BOOST_FILESYSTEM_NAMESPACE::detail::iterator_helper<path_type>::do_increment(
*this );
}
void decrement()
{
boost::BOOST_FILESYSTEM_NAMESPACE::detail::iterator_helper<path_type>::do_decrement(
*this );
}
string_type m_name; // current element
const basic_path * m_path_ptr; // path being iterated over
typename string_type::size_type m_pos; // position of name in
// path_ptr->string(). The
// end() iterator is indicated by
// pos == path_ptr->m_path.size()
}; // iterator
typedef iterator const_iterator;
iterator begin() const;
iterator end() const;
private:
// Note: This is an implementation for POSIX and Windows, where there
// are only minor differences between generic and native path grammars.
// Private members might be quite different in other implementations,
// particularly where there were wide differences between portable and
// native path formats, or between file_string() and
// directory_string() formats, or simply that the implementation
// was willing expend additional memory to achieve greater speed for
// some operations at the expense of other operations.
string_type m_path; // invariant: portable path grammar
// on Windows, backslashes converted to slashes
# ifdef BOOST_CYGWIN_PATH
bool m_cygwin_root; // if present, m_path[0] was slash. note: initialization
// done by append
# endif
void m_append_separator_if_needed();
void m_append( value_type value ); // converts Windows alt_separator
// Was qualified; como433beta8 reports:
// warning #427-D: qualified name is not allowed in member declaration
friend class iterator;
friend class boost::BOOST_FILESYSTEM_NAMESPACE::detail::iterator_helper<path_type>;
// Deprecated features ease transition for existing code. Don't use these
// in new code.
# ifndef BOOST_FILESYSTEM_NO_DEPRECATED
public:
typedef bool (*name_check)( const std::string & name );
basic_path( const string_type & str, name_check ) { operator/=( str ); }
basic_path( const typename string_type::value_type * s, name_check )
{ operator/=( s );}
string_type native_file_string() const { return file_string(); }
string_type native_directory_string() const { return directory_string(); }
static bool default_name_check_writable() { return false; }
static void default_name_check( name_check ) {}
static name_check default_name_check() { return 0; }
basic_path & canonize();
basic_path & normalize();
# endif
};
// basic_path non-member functions ---------------------------------------//
template< class String, class Traits >
inline void swap( basic_path<String, Traits> & lhs,
basic_path<String, Traits> & rhs ) { lhs.swap( rhs ); }
template< class String, class Traits >
bool operator<( const basic_path<String, Traits> & lhs, const basic_path<String, Traits> & rhs )
{
return std::lexicographical_compare(
lhs.begin(), lhs.end(), rhs.begin(), rhs.end() );
}
template< class String, class Traits >
bool operator<( const typename basic_path<String, Traits>::string_type::value_type * lhs,
const basic_path<String, Traits> & rhs )
{
basic_path<String, Traits> tmp( lhs );
return std::lexicographical_compare(
tmp.begin(), tmp.end(), rhs.begin(), rhs.end() );
}
template< class String, class Traits >
bool operator<( const typename basic_path<String, Traits>::string_type & lhs,
const basic_path<String, Traits> & rhs )
{
basic_path<String, Traits> tmp( lhs );
return std::lexicographical_compare(
tmp.begin(), tmp.end(), rhs.begin(), rhs.end() );
}
template< class String, class Traits >
bool operator<( const basic_path<String, Traits> & lhs,
const typename basic_path<String, Traits>::string_type::value_type * rhs )
{
basic_path<String, Traits> tmp( rhs );
return std::lexicographical_compare(
lhs.begin(), lhs.end(), tmp.begin(), tmp.end() );
}
template< class String, class Traits >
bool operator<( const basic_path<String, Traits> & lhs,
const typename basic_path<String, Traits>::string_type & rhs )
{
basic_path<String, Traits> tmp( rhs );
return std::lexicographical_compare(
lhs.begin(), lhs.end(), tmp.begin(), tmp.end() );
}
// operator == uses string compare rather than !(lhs < rhs) && !(rhs < lhs) because
// the result is the same yet the direct string compare is much more efficient that
// lexicographical_compare, and lexicographical_compare used twice at that.
template< class String, class Traits >
inline bool operator==( const basic_path<String, Traits> & lhs, const basic_path<String, Traits> & rhs )
{
return lhs.string() == rhs.string();
}
template< class String, class Traits >
inline bool operator==( const typename basic_path<String, Traits>::string_type::value_type * lhs,
const basic_path<String, Traits> & rhs )
{
return lhs == rhs.string();
}
template< class String, class Traits >
inline bool operator==( const typename basic_path<String, Traits>::string_type & lhs,
const basic_path<String, Traits> & rhs )
{
return lhs == rhs.string();
}
template< class String, class Traits >
inline bool operator==( const basic_path<String, Traits> & lhs,
const typename basic_path<String, Traits>::string_type::value_type * rhs )
{
return lhs.string() == rhs;
}
template< class String, class Traits >
inline bool operator==( const basic_path<String, Traits> & lhs,
const typename basic_path<String, Traits>::string_type & rhs )
{
return lhs.string() == rhs;
}
template< class String, class Traits >
inline bool operator!=( const basic_path<String, Traits> & lhs,
const basic_path<String, Traits> & rhs )
{ return !(lhs == rhs); }
template< class String, class Traits >
inline bool operator!=( const typename basic_path<String,
Traits>::string_type::value_type * lhs,
const basic_path<String, Traits> & rhs )
{ return !(lhs == rhs); }
template< class String, class Traits >
inline bool operator!=( const typename basic_path<String, Traits>::string_type & lhs,
const basic_path<String, Traits> & rhs )
{ return !(lhs == rhs); }
template< class String, class Traits >
inline bool operator!=( const basic_path<String, Traits> & lhs,
const typename basic_path<String, Traits>::string_type::value_type * rhs )
{ return !(lhs == rhs); }
template< class String, class Traits >
inline bool operator!=( const basic_path<String, Traits> & lhs,
const typename basic_path<String, Traits>::string_type & rhs )
{ return !(lhs == rhs); }
template< class String, class Traits >
inline bool operator>( const basic_path<String, Traits> & lhs, const basic_path<String, Traits> & rhs ) { return rhs < lhs; }
template< class String, class Traits >
inline bool operator>( const typename basic_path<String, Traits>::string_type::value_type * lhs,
const basic_path<String, Traits> & rhs ) { return rhs < basic_path<String, Traits>(lhs); }
template< class String, class Traits >
inline bool operator>( const typename basic_path<String, Traits>::string_type & lhs,
const basic_path<String, Traits> & rhs ) { return rhs < basic_path<String, Traits>(lhs); }
template< class String, class Traits >
inline bool operator>( const basic_path<String, Traits> & lhs,
const typename basic_path<String, Traits>::string_type::value_type * rhs )
{ return basic_path<String, Traits>(rhs) < lhs; }
template< class String, class Traits >
inline bool operator>( const basic_path<String, Traits> & lhs,
const typename basic_path<String, Traits>::string_type & rhs )
{ return basic_path<String, Traits>(rhs) < lhs; }
template< class String, class Traits >
inline bool operator<=( const basic_path<String, Traits> & lhs, const basic_path<String, Traits> & rhs ) { return !(rhs < lhs); }
template< class String, class Traits >
inline bool operator<=( const typename basic_path<String, Traits>::string_type::value_type * lhs,
const basic_path<String, Traits> & rhs ) { return !(rhs < basic_path<String, Traits>(lhs)); }
template< class String, class Traits >
inline bool operator<=( const typename basic_path<String, Traits>::string_type & lhs,
const basic_path<String, Traits> & rhs ) { return !(rhs < basic_path<String, Traits>(lhs)); }
template< class String, class Traits >
inline bool operator<=( const basic_path<String, Traits> & lhs,
const typename basic_path<String, Traits>::string_type::value_type * rhs )
{ return !(basic_path<String, Traits>(rhs) < lhs); }
template< class String, class Traits >
inline bool operator<=( const basic_path<String, Traits> & lhs,
const typename basic_path<String, Traits>::string_type & rhs )
{ return !(basic_path<String, Traits>(rhs) < lhs); }
template< class String, class Traits >
inline bool operator>=( const basic_path<String, Traits> & lhs, const basic_path<String, Traits> & rhs ) { return !(lhs < rhs); }
template< class String, class Traits >
inline bool operator>=( const typename basic_path<String, Traits>::string_type::value_type * lhs,
const basic_path<String, Traits> & rhs ) { return !(lhs < basic_path<String, Traits>(rhs)); }
template< class String, class Traits >
inline bool operator>=( const typename basic_path<String, Traits>::string_type & lhs,
const basic_path<String, Traits> & rhs ) { return !(lhs < basic_path<String, Traits>(rhs)); }
template< class String, class Traits >
inline bool operator>=( const basic_path<String, Traits> & lhs,
const typename basic_path<String, Traits>::string_type::value_type * rhs )
{ return !(basic_path<String, Traits>(lhs) < rhs); }
template< class String, class Traits >
inline bool operator>=( const basic_path<String, Traits> & lhs,
const typename basic_path<String, Traits>::string_type & rhs )
{ return !(basic_path<String, Traits>(lhs) < rhs); }
// operator /
template< class String, class Traits >
inline basic_path<String, Traits> operator/(
const basic_path<String, Traits> & lhs,
const basic_path<String, Traits> & rhs )
{ return basic_path<String, Traits>( lhs ) /= rhs; }
template< class String, class Traits >
inline basic_path<String, Traits> operator/(
const basic_path<String, Traits> & lhs,
const typename String::value_type * rhs )
{ return basic_path<String, Traits>( lhs ) /=
basic_path<String, Traits>( rhs ); }
template< class String, class Traits >
inline basic_path<String, Traits> operator/(
const basic_path<String, Traits> & lhs, const String & rhs )
{ return basic_path<String, Traits>( lhs ) /=
basic_path<String, Traits>( rhs ); }
template< class String, class Traits >
inline basic_path<String, Traits> operator/(
const typename String::value_type * lhs,
const basic_path<String, Traits> & rhs )
{ return basic_path<String, Traits>( lhs ) /= rhs; }
template< class String, class Traits >
inline basic_path<String, Traits> operator/(
const String & lhs, const basic_path<String, Traits> & rhs )
{ return basic_path<String, Traits>( lhs ) /= rhs; }
// inserters and extractors --------------------------------------------//
// bypass VC++ 7.0 and earlier, and broken Borland compilers
# if !BOOST_WORKAROUND(BOOST_MSVC, <= 1300) && !BOOST_WORKAROUND(__BORLANDC__, < 0x610)
template< class Path >
std::basic_ostream< typename Path::string_type::value_type,
typename Path::string_type::traits_type > &
operator<<
( std::basic_ostream< typename Path::string_type::value_type,
typename Path::string_type::traits_type >& os, const Path & ph )
{
os << ph.string();
return os;
}
template< class Path >
std::basic_istream< typename Path::string_type::value_type,
typename Path::string_type::traits_type > &
operator>>
( std::basic_istream< typename Path::string_type::value_type,
typename Path::string_type::traits_type >& is, Path & ph )
{
typename Path::string_type str;
is >> str;
ph = str;
return is;
}
# elif BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x564))
template< class String, class Traits >
std::basic_ostream< BOOST_DEDUCED_TYPENAME String::value_type,
BOOST_DEDUCED_TYPENAME String::traits_type > &
operator<<
( std::basic_ostream< BOOST_DEDUCED_TYPENAME String::value_type,
BOOST_DEDUCED_TYPENAME String::traits_type >& os,
const basic_path< String, Traits > & ph )
{
os << ph.string();
return os;
}
template< class String, class Traits >
std::basic_istream< BOOST_DEDUCED_TYPENAME String::value_type,
BOOST_DEDUCED_TYPENAME String::traits_type > &
operator>>
( std::basic_istream< BOOST_DEDUCED_TYPENAME String::value_type,
BOOST_DEDUCED_TYPENAME String::traits_type> & is,
basic_path< String, Traits > & ph )
{
String str;
is >> str;
ph = str;
return is;
}
# endif
// basic_filesystem_error helpers --------------------------------------//
// Originally choice of implementation was done via specialization of
// basic_filesystem_error::what(). Several compilers (GCC, aCC, etc.)
// couldn't handle that, so the choice is now accomplished by overloading.
namespace detail
{
// BOOST_FILESYSTEM_DECL version works for VC++ but not GCC. Go figure!
inline
const char * what( const char * sys_err_what,
const path & path1_arg, const path & path2_arg, std::string & target )
{
try
{
if ( target.empty() )
{
target = sys_err_what;
if ( !path1_arg.empty() )
{
target += ": \"";
target += path1_arg.file_string();
target += "\"";
}
if ( !path2_arg.empty() )
{
target += ", \"";
target += path2_arg.file_string();
target += "\"";
}
}
return target.c_str();
}
catch (...)
{
return sys_err_what;
}
}
template<class Path>
const char * what( const char * sys_err_what,
const Path & /*path1_arg*/, const Path & /*path2_arg*/, std::string & /*target*/ )
{
return sys_err_what;
}
}
// basic_filesystem_error ----------------------------------------------//
template<class Path>
class basic_filesystem_error : public system::system_error
{
// see http://www.boost.org/more/error_handling.html for design rationale
public:
// compiler generates copy constructor and copy assignment
typedef Path path_type;
basic_filesystem_error( const std::string & what_arg,
system::error_code ec );
basic_filesystem_error( const std::string & what_arg,
const path_type & path1_arg, system::error_code ec );
basic_filesystem_error( const std::string & what_arg, const path_type & path1_arg,
const path_type & path2_arg, system::error_code ec );
~basic_filesystem_error() throw() {}
const path_type & path1() const
{
static const path_type empty_path;
return m_imp_ptr.get() ? m_imp_ptr->m_path1 : empty_path ;
}
const path_type & path2() const
{
static const path_type empty_path;
return m_imp_ptr.get() ? m_imp_ptr->m_path2 : empty_path ;
}
const char * what() const throw()
{
if ( !m_imp_ptr.get() )
return system::system_error::what();
return detail::what( system::system_error::what(), m_imp_ptr->m_path1,
m_imp_ptr->m_path2, m_imp_ptr->m_what );
}
private:
struct m_imp
{
path_type m_path1; // may be empty()
path_type m_path2; // may be empty()
std::string m_what; // not built until needed
};
boost::shared_ptr<m_imp> m_imp_ptr;
};
typedef basic_filesystem_error<path> filesystem_error;
# ifndef BOOST_FILESYSTEM_NARROW_ONLY
typedef basic_filesystem_error<wpath> wfilesystem_error;
# endif
// path::name_checks -----------------------------------------------------//
BOOST_FILESYSTEM_DECL bool portable_posix_name( const std::string & name );
BOOST_FILESYSTEM_DECL bool windows_name( const std::string & name );
BOOST_FILESYSTEM_DECL bool portable_name( const std::string & name );
BOOST_FILESYSTEM_DECL bool portable_directory_name( const std::string & name );
BOOST_FILESYSTEM_DECL bool portable_file_name( const std::string & name );
BOOST_FILESYSTEM_DECL bool native( const std::string & name );
inline bool no_check( const std::string & )
{ return true; }
// implementation -----------------------------------------------------------//
namespace detail
{
// is_separator helper ------------------------------------------------//
template<class Path>
inline bool is_separator( typename Path::string_type::value_type c )
{
return c == slash<Path>::value
# ifdef BOOST_WINDOWS_PATH
|| c == path_alt_separator<Path>::value
# endif
;
}
// filename_pos helper ----------------------------------------------------//
template<class String, class Traits>
typename String::size_type filename_pos(
const String & str, // precondition: portable generic path grammar
typename String::size_type end_pos ) // end_pos is past-the-end position
// return 0 if str itself is filename (or empty)
{
typedef typename
boost::BOOST_FILESYSTEM_NAMESPACE::basic_path<String, Traits> path_type;
// case: "//"
if ( end_pos == 2
&& str[0] == slash<path_type>::value
&& str[1] == slash<path_type>::value ) return 0;
// case: ends in "/"
if ( end_pos && str[end_pos-1] == slash<path_type>::value )
return end_pos-1;
// set pos to start of last element
typename String::size_type pos(
str.find_last_of( slash<path_type>::value, end_pos-1 ) );
# ifdef BOOST_WINDOWS_PATH
if ( pos == String::npos )
pos = str.find_last_of( path_alt_separator<path_type>::value, end_pos-1 );
if ( pos == String::npos )
pos = str.find_last_of( colon<path_type>::value, end_pos-2 );
# endif
return ( pos == String::npos // path itself must be a filename (or empty)
|| (pos == 1 && str[0] == slash<path_type>::value) ) // or net
? 0 // so filename is entire string
: pos + 1; // or starts after delimiter
}
// first_element helper -----------------------------------------------//
// sets pos and len of first element, excluding extra separators
// if src.empty(), sets pos,len, to 0,0.
template<class String, class Traits>
void first_element(
const String & src, // precondition: portable generic path grammar
typename String::size_type & element_pos,
typename String::size_type & element_size,
# if !BOOST_WORKAROUND( BOOST_MSVC, <= 1310 ) // VC++ 7.1
typename String::size_type size = String::npos
# else
typename String::size_type size = -1
# endif
)
{
if ( size == String::npos ) size = src.size();
element_pos = 0;
element_size = 0;
if ( src.empty() ) return;
typedef typename boost::BOOST_FILESYSTEM_NAMESPACE::basic_path<String, Traits> path_type;
typename String::size_type cur(0);
// deal with // [network]
if ( size >= 2 && src[0] == slash<path_type>::value
&& src[1] == slash<path_type>::value
&& (size == 2
|| src[2] != slash<path_type>::value) )
{
cur += 2;
element_size += 2;
}
// leading (not non-network) separator
else if ( src[0] == slash<path_type>::value )
{
++element_size;
// bypass extra leading separators
while ( cur+1 < size
&& src[cur+1] == slash<path_type>::value )
{
++cur;
++element_pos;
}
return;
}
// at this point, we have either a plain name, a network name,
// or (on Windows only) a device name
// find the end
while ( cur < size
# ifdef BOOST_WINDOWS_PATH
&& src[cur] != colon<path_type>::value
# endif
&& src[cur] != slash<path_type>::value )
{
++cur;
++element_size;
}
# ifdef BOOST_WINDOWS_PATH
if ( cur == size ) return;
// include device delimiter
if ( src[cur] == colon<path_type>::value )
{ ++element_size; }
# endif
return;
}
// root_directory_start helper ----------------------------------------//
template<class String, class Traits>
typename String::size_type root_directory_start(
const String & s, // precondition: portable generic path grammar
typename String::size_type size )
// return npos if no root_directory found
{
typedef typename boost::BOOST_FILESYSTEM_NAMESPACE::basic_path<String, Traits> path_type;
# ifdef BOOST_WINDOWS_PATH
// case "c:/"
if ( size > 2
&& s[1] == colon<path_type>::value
&& s[2] == slash<path_type>::value ) return 2;
# endif
// case "//"
if ( size == 2
&& s[0] == slash<path_type>::value
&& s[1] == slash<path_type>::value ) return String::npos;
// case "//net {/}"
if ( size > 3
&& s[0] == slash<path_type>::value
&& s[1] == slash<path_type>::value
&& s[2] != slash<path_type>::value )
{
typename String::size_type pos(
s.find( slash<path_type>::value, 2 ) );
return pos < size ? pos : String::npos;
}
// case "/"
if ( size > 0 && s[0] == slash<path_type>::value ) return 0;
return String::npos;
}
// is_non_root_slash helper -------------------------------------------//
template<class String, class Traits>
bool is_non_root_slash( const String & str,
typename String::size_type pos ) // pos is position of the slash
{
typedef typename
boost::BOOST_FILESYSTEM_NAMESPACE::basic_path<String, Traits>
path_type;
assert( !str.empty() && str[pos] == slash<path_type>::value
&& "precondition violation" );
// subsequent logic expects pos to be for leftmost slash of a set
while ( pos > 0 && str[pos-1] == slash<path_type>::value )
--pos;
return pos != 0
&& (pos <= 2 || str[1] != slash<path_type>::value
|| str.find( slash<path_type>::value, 2 ) != pos)
# ifdef BOOST_WINDOWS_PATH
&& (pos !=2 || str[1] != colon<path_type>::value)
# endif
;
}
} // namespace detail
// decomposition functions ----------------------------------------------//
template<class String, class Traits>
String basic_path<String, Traits>::filename() const
{
typename String::size_type end_pos(
detail::filename_pos<String, Traits>( m_path, m_path.size() ) );
return (m_path.size()
&& end_pos
&& m_path[end_pos] == slash<path_type>::value
&& detail::is_non_root_slash< String, Traits >(m_path, end_pos))
? String( 1, dot<path_type>::value )
: m_path.substr( end_pos );
}
template<class String, class Traits>
String basic_path<String, Traits>::stem() const
{
string_type name = filename();
typename string_type::size_type n = name.rfind(dot<path_type>::value);
return name.substr(0, n);
}
template<class String, class Traits>
String basic_path<String, Traits>::extension() const
{
string_type name = filename();
typename string_type::size_type n = name.rfind(dot<path_type>::value);
if (n != string_type::npos)
return name.substr(n);
else
return string_type();
}
template<class String, class Traits>
basic_path<String, Traits> basic_path<String, Traits>::parent_path() const
{
typename String::size_type end_pos(
detail::filename_pos<String, Traits>( m_path, m_path.size() ) );
bool filename_was_separator( m_path.size()
&& m_path[end_pos] == slash<path_type>::value );
// skip separators unless root directory
typename string_type::size_type root_dir_pos( detail::root_directory_start
<string_type, traits_type>( m_path, end_pos ) );
for ( ;
end_pos > 0
&& (end_pos-1) != root_dir_pos
&& m_path[end_pos-1] == slash<path_type>::value
;
--end_pos ) {}
return (end_pos == 1 && root_dir_pos == 0 && filename_was_separator)
? path_type()
: path_type( m_path.substr( 0, end_pos ) );
}
template<class String, class Traits>
basic_path<String, Traits> basic_path<String, Traits>::relative_path() const
{
iterator itr( begin() );
for ( ; itr.m_pos != m_path.size()
&& (itr.m_name[0] == slash<path_type>::value
# ifdef BOOST_WINDOWS_PATH
|| itr.m_name[itr.m_name.size()-1]
== colon<path_type>::value
# endif
); ++itr ) {}
return basic_path<String, Traits>( m_path.substr( itr.m_pos ) );
}
template<class String, class Traits>
String basic_path<String, Traits>::root_name() const
{
iterator itr( begin() );
return ( itr.m_pos != m_path.size()
&& (
( itr.m_name.size() > 1
&& itr.m_name[0] == slash<path_type>::value
&& itr.m_name[1] == slash<path_type>::value
)
# ifdef BOOST_WINDOWS_PATH
|| itr.m_name[itr.m_name.size()-1]
== colon<path_type>::value
# endif
) )
? *itr
: String();
}
template<class String, class Traits>
String basic_path<String, Traits>::root_directory() const
{
typename string_type::size_type start(
detail::root_directory_start<String, Traits>( m_path, m_path.size() ) );
return start == string_type::npos
? string_type()
: m_path.substr( start, 1 );
}
template<class String, class Traits>
basic_path<String, Traits> basic_path<String, Traits>::root_path() const
{
// even on POSIX, root_name() is non-empty() on network paths
return basic_path<String, Traits>( root_name() ) /= root_directory();
}
// path query functions -------------------------------------------------//
template<class String, class Traits>
inline bool basic_path<String, Traits>::is_complete() const
{
# ifdef BOOST_WINDOWS_PATH
return has_root_name() && has_root_directory();
# else
return has_root_directory();
# endif
}
template<class String, class Traits>
inline bool basic_path<String, Traits>::has_root_path() const
{
return !root_path().empty();
}
template<class String, class Traits>
inline bool basic_path<String, Traits>::has_root_name() const
{
return !root_name().empty();
}
template<class String, class Traits>
inline bool basic_path<String, Traits>::has_root_directory() const
{
return !root_directory().empty();
}
// append ---------------------------------------------------------------//
template<class String, class Traits>
void basic_path<String, Traits>::m_append_separator_if_needed()
// requires: !empty()
{
if (
# ifdef BOOST_WINDOWS_PATH
*(m_path.end()-1) != colon<path_type>::value &&
# endif
*(m_path.end()-1) != slash<path_type>::value )
{
m_path += slash<path_type>::value;
}
}
template<class String, class Traits>
void basic_path<String, Traits>::m_append( value_type value )
{
# ifdef BOOST_CYGWIN_PATH
if ( m_path.empty() ) m_cygwin_root = (value == slash<path_type>::value);
# endif
# ifdef BOOST_WINDOWS_PATH
// for BOOST_WINDOWS_PATH, convert alt_separator ('\') to separator ('/')
m_path += ( value == path_alt_separator<path_type>::value
? slash<path_type>::value
: value );
# else
m_path += value;
# endif
}
// except that it wouldn't work for BOOST_NO_MEMBER_TEMPLATES compilers,
// the append() member template could replace this code.
template<class String, class Traits>
basic_path<String, Traits> & basic_path<String, Traits>::operator /=
( const value_type * next_p )
{
// ignore escape sequence on POSIX or Windows
if ( *next_p == slash<path_type>::value
&& *(next_p+1) == slash<path_type>::value
&& *(next_p+2) == colon<path_type>::value ) next_p += 3;
// append slash<path_type>::value if needed
if ( !empty() && *next_p != 0
&& !detail::is_separator<path_type>( *next_p ) )
{ m_append_separator_if_needed(); }
for ( ; *next_p != 0; ++next_p ) m_append( *next_p );
return *this;
}
# ifndef BOOST_NO_MEMBER_TEMPLATES
template<class String, class Traits> template <class InputIterator>
basic_path<String, Traits> & basic_path<String, Traits>::append(
InputIterator first, InputIterator last )
{
// append slash<path_type>::value if needed
if ( !empty() && first != last
&& !detail::is_separator<path_type>( *first ) )
{ m_append_separator_if_needed(); }
// song-and-dance to avoid violating InputIterator requirements
// (which prohibit lookahead) in detecting a possible escape sequence
// (escape sequences are simply ignored on POSIX and Windows)
bool was_escape_sequence(true);
std::size_t append_count(0);
typename String::size_type initial_pos( m_path.size() );
for ( ; first != last && *first; ++first )
{
if ( append_count == 0 && *first != slash<path_type>::value )
was_escape_sequence = false;
if ( append_count == 1 && *first != slash<path_type>::value )
was_escape_sequence = false;
if ( append_count == 2 && *first != colon<path_type>::value )
was_escape_sequence = false;
m_append( *first );
++append_count;
}
// erase escape sequence if any
if ( was_escape_sequence && append_count >= 3 )
m_path.erase( initial_pos, 3 );
return *this;
}
# endif
# ifndef BOOST_FILESYSTEM_NO_DEPRECATED
// canonize ------------------------------------------------------------//
template<class String, class Traits>
basic_path<String, Traits> & basic_path<String, Traits>::canonize()
{
static const typename string_type::value_type dot_str[]
= { dot<path_type>::value, 0 };
if ( m_path.empty() ) return *this;
path_type temp;
for ( iterator itr( begin() ); itr != end(); ++itr )
{
temp /= *itr;
};
if ( temp.empty() ) temp /= dot_str;
m_path = temp.m_path;
return *this;
}
// normalize ------------------------------------------------------------//
template<class String, class Traits>
basic_path<String, Traits> & basic_path<String, Traits>::normalize()
{
static const typename string_type::value_type dot_str[]
= { dot<path_type>::value, 0 };
if ( m_path.empty() ) return *this;
path_type temp;
iterator start( begin() );
iterator last( end() );
iterator stop( last-- );
for ( iterator itr( start ); itr != stop; ++itr )
{
// ignore "." except at start and last
if ( itr->size() == 1
&& (*itr)[0] == dot<path_type>::value
&& itr != start
&& itr != last ) continue;
// ignore a name and following ".."
if ( !temp.empty()
&& itr->size() == 2
&& (*itr)[0] == dot<path_type>::value
&& (*itr)[1] == dot<path_type>::value ) // dot dot
{
string_type lf( temp.filename() );
if ( lf.size() > 0
&& (lf.size() != 1
|| (lf[0] != dot<path_type>::value
&& lf[0] != slash<path_type>::value))
&& (lf.size() != 2
|| (lf[0] != dot<path_type>::value
&& lf[1] != dot<path_type>::value
# ifdef BOOST_WINDOWS_PATH
&& lf[1] != colon<path_type>::value
# endif
)
)
)
{
temp.remove_filename();
// if not root directory, must also remove "/" if any
if ( temp.m_path.size() > 0
&& temp.m_path[temp.m_path.size()-1]
== slash<path_type>::value )
{
typename string_type::size_type rds(
detail::root_directory_start<String,Traits>( temp.m_path,
temp.m_path.size() ) );
if ( rds == string_type::npos
|| rds != temp.m_path.size()-1 )
{ temp.m_path.erase( temp.m_path.size()-1 ); }
}
iterator next( itr );
if ( temp.empty() && ++next != stop
&& next == last && *last == dot_str ) temp /= dot_str;
continue;
}
}
temp /= *itr;
};
if ( temp.empty() ) temp /= dot_str;
m_path = temp.m_path;
return *this;
}
# endif
// modifiers ------------------------------------------------------------//
template<class String, class Traits>
basic_path<String, Traits> & basic_path<String, Traits>::remove_filename()
{
m_path.erase(
detail::filename_pos<String, Traits>( m_path, m_path.size() ) );
return *this;
}
template<class String, class Traits>
basic_path<String, Traits> &
basic_path<String, Traits>::replace_extension( const string_type & new_ext )
{
// erase existing extension if any
string_type old_ext = extension();
if ( !old_ext.empty() )
m_path.erase( m_path.size() - old_ext.size() );
if ( !new_ext.empty() && new_ext[0] != dot<path_type>::value )
m_path += dot<path_type>::value;
m_path += new_ext;
return *this;
}
// path conversion functions --------------------------------------------//
template<class String, class Traits>
const String
basic_path<String, Traits>::file_string() const
{
# ifdef BOOST_WINDOWS_PATH
// for Windows, use the alternate separator, and bypass extra
// root separators
typename string_type::size_type root_dir_start(
detail::root_directory_start<String, Traits>( m_path, m_path.size() ) );
bool in_root( root_dir_start != string_type::npos );
String s;
for ( typename string_type::size_type pos( 0 );
pos != m_path.size(); ++pos )
{
// special case // [net]
if ( pos == 0 && m_path.size() > 1
&& m_path[0] == slash<path_type>::value
&& m_path[1] == slash<path_type>::value
&& ( m_path.size() == 2
|| !detail::is_separator<path_type>( m_path[2] )
) )
{
++pos;
s += path_alt_separator<path_type>::value;
s += path_alt_separator<path_type>::value;
continue;
}
// bypass extra root separators
if ( in_root )
{
if ( s.size() > 0
&& s[s.size()-1] == path_alt_separator<path_type>::value
&& m_path[pos] == slash<path_type>::value
) continue;
}
if ( m_path[pos] == slash<path_type>::value )
s += path_alt_separator<path_type>::value;
else
s += m_path[pos];
if ( pos > root_dir_start
&& m_path[pos] == slash<path_type>::value )
{ in_root = false; }
}
# ifdef BOOST_CYGWIN_PATH
if ( m_cygwin_root ) s[0] = slash<path_type>::value;
# endif
return s;
# else
return m_path;
# endif
}
// iterator functions ---------------------------------------------------//
template<class String, class Traits>
typename basic_path<String, Traits>::iterator basic_path<String, Traits>::begin() const
{
iterator itr;
itr.m_path_ptr = this;
typename string_type::size_type element_size;
detail::first_element<String, Traits>( m_path, itr.m_pos, element_size );
itr.m_name = m_path.substr( itr.m_pos, element_size );
return itr;
}
template<class String, class Traits>
typename basic_path<String, Traits>::iterator basic_path<String, Traits>::end() const
{
iterator itr;
itr.m_path_ptr = this;
itr.m_pos = m_path.size();
return itr;
}
namespace detail
{
// do_increment ------------------------------------------------------//
template<class Path>
void iterator_helper<Path>::do_increment( iterator & itr )
{
typedef typename Path::string_type string_type;
typedef typename Path::traits_type traits_type;
assert( itr.m_pos < itr.m_path_ptr->m_path.size() && "basic_path::iterator increment past end()" );
bool was_net( itr.m_name.size() > 2
&& itr.m_name[0] == slash<Path>::value
&& itr.m_name[1] == slash<Path>::value
&& itr.m_name[2] != slash<Path>::value );
// increment to position past current element
itr.m_pos += itr.m_name.size();
// if end reached, create end iterator
if ( itr.m_pos == itr.m_path_ptr->m_path.size() )
{
itr.m_name.erase( itr.m_name.begin(), itr.m_name.end() ); // VC++ 6.0 lib didn't supply clear()
return;
}
// process separator (Windows drive spec is only case not a separator)
if ( itr.m_path_ptr->m_path[itr.m_pos] == slash<Path>::value )
{
// detect root directory
if ( was_net
# ifdef BOOST_WINDOWS_PATH
// case "c:/"
|| itr.m_name[itr.m_name.size()-1] == colon<Path>::value
# endif
)
{
itr.m_name = slash<Path>::value;
return;
}
// bypass separators
while ( itr.m_pos != itr.m_path_ptr->m_path.size()
&& itr.m_path_ptr->m_path[itr.m_pos] == slash<Path>::value )
{ ++itr.m_pos; }
// detect trailing separator, and treat it as ".", per POSIX spec
if ( itr.m_pos == itr.m_path_ptr->m_path.size()
&& detail::is_non_root_slash< string_type, traits_type >(
itr.m_path_ptr->m_path, itr.m_pos-1 ) )
{
--itr.m_pos;
itr.m_name = dot<Path>::value;
return;
}
}
// get next element
typename string_type::size_type end_pos(
itr.m_path_ptr->m_path.find( slash<Path>::value, itr.m_pos ) );
itr.m_name = itr.m_path_ptr->m_path.substr( itr.m_pos, end_pos - itr.m_pos );
}
// do_decrement ------------------------------------------------------//
template<class Path>
void iterator_helper<Path>::do_decrement( iterator & itr )
{
assert( itr.m_pos && "basic_path::iterator decrement past begin()" );
typedef typename Path::string_type string_type;
typedef typename Path::traits_type traits_type;
typename string_type::size_type end_pos( itr.m_pos );
typename string_type::size_type root_dir_pos(
detail::root_directory_start<string_type, traits_type>(
itr.m_path_ptr->m_path, end_pos ) );
// if at end and there was a trailing non-root '/', return "."
if ( itr.m_pos == itr.m_path_ptr->m_path.size()
&& itr.m_path_ptr->m_path.size() > 1
&& itr.m_path_ptr->m_path[itr.m_pos-1] == slash<Path>::value
&& detail::is_non_root_slash< string_type, traits_type >(
itr.m_path_ptr->m_path, itr.m_pos-1 )
)
{
--itr.m_pos;
itr.m_name = dot<Path>::value;
return;
}
// skip separators unless root directory
for (
;
end_pos > 0
&& (end_pos-1) != root_dir_pos
&& itr.m_path_ptr->m_path[end_pos-1] == slash<Path>::value
;
--end_pos ) {}
itr.m_pos = detail::filename_pos<string_type, traits_type>
( itr.m_path_ptr->m_path, end_pos );
itr.m_name = itr.m_path_ptr->m_path.substr( itr.m_pos, end_pos - itr.m_pos );
}
} // namespace detail
// basic_filesystem_error implementation --------------------------------//
template<class Path>
basic_filesystem_error<Path>::basic_filesystem_error(
const std::string & what_arg, system::error_code ec )
: system::system_error(ec, what_arg)
{
try
{
m_imp_ptr.reset( new m_imp );
}
catch (...) { m_imp_ptr.reset(); }
}
template<class Path>
basic_filesystem_error<Path>::basic_filesystem_error(
const std::string & what_arg, const path_type & path1_arg,
system::error_code ec )
: system::system_error(ec, what_arg)
{
try
{
m_imp_ptr.reset( new m_imp );
m_imp_ptr->m_path1 = path1_arg;
}
catch (...) { m_imp_ptr.reset(); }
}
template<class Path>
basic_filesystem_error<Path>::basic_filesystem_error(
const std::string & what_arg, const path_type & path1_arg,
const path_type & path2_arg, system::error_code ec )
: system::system_error(ec, what_arg)
{
try
{
m_imp_ptr.reset( new m_imp );
m_imp_ptr->m_path1 = path1_arg;
m_imp_ptr->m_path2 = path2_arg;
}
catch (...) { m_imp_ptr.reset(); }
}
} // namespace BOOST_FILESYSTEM_NAMESPACE
} // namespace boost
#include <boost/config/abi_suffix.hpp> // pops abi_prefix.hpp pragmas
#endif // BOOST_FILESYSTEM_PATH_HPP