1 // path implementation -----------------------------------------------------//
3 // (C) Copyright Beman Dawes 2002. Permission to copy,
4 // use, modify, sell and distribute this software is granted provided this
5 // copyright notice appears in all copies. This software is provided "as is"
6 // without express or implied warranty, and with no claim as to its
7 // suitability for any purpose.
9 // See http://www.boost.org/libs/filesystem for documentation.
12 //****************************************************************************//
14 // WARNING: This code was hacked time and time again as different library
15 // designs were tried. Thus portions may be residue from the earlier
16 // experiments, and be totally stupid or wrong in light of the final library
17 // specifications. The code needs to be reviewed line-by-line to elmininate
20 //****************************************************************************//
22 // BOOST_POSIX or BOOST_WINDOWS specify which API to use.
23 # if !defined( BOOST_WINDOWS ) && !defined( BOOST_POSIX )
24 # if defined(_WIN32) || defined(__WIN32__) || defined(WIN32) || defined(__CYGWIN__)
25 # define BOOST_WINDOWS
31 #include <boost/filesystem/path.hpp>
32 #include <boost/filesystem/exception.hpp>
34 namespace fs = boost::filesystem;
36 #include <boost/config.hpp>
37 #ifdef BOOST_NO_STDC_NAMESPACE
38 namespace std { using ::strlen; }
41 #include <boost/throw_exception.hpp>
42 #include <cstring> // SGI MIPSpro compilers need this
46 // helpers -----------------------------------------------------------------//
50 // POSIX & Windows cases: "", "/", "/foo", "foo", "foo/bar"
51 // Windows only cases: "c:", "c:/", "c:foo", "c:/foo",
52 // "prn:", "//share", "//share/", "//share/foo"
54 std::string::size_type leaf_pos( const std::string & str,
55 std::string::size_type end_pos ) // end_pos is past-the-end position
56 // return 0 if str itself is leaf (or empty)
58 if ( end_pos && str[end_pos-1] == '/' ) return end_pos-1;
60 std::string::size_type pos( str.find_last_of( '/', end_pos-1 ) );
62 if ( pos == std::string::npos ) pos = str.find_last_of( ':', end_pos-2 );
65 return ( pos == std::string::npos // path itself must be a leaf (or empty)
67 || (pos == 1 && str[0] == '/') // or share
69 ) ? 0 // so leaf is entire string
70 : pos + 1; // or starts after delimiter
73 void first_name( const std::string & src, std::string & target )
75 target = ""; // VC++ 6.0 doesn't have string::clear()
76 std::string::const_iterator itr( src.begin() );
80 if ( src.size() >= 2 && src[0] == '/' && src[1] == '/' )
81 { target = "//"; itr += 2; }
84 while ( itr != src.end()
88 && *itr != '/' ) { target += *itr++; }
90 if ( itr == src.end() ) return;
101 if ( itr == src.begin() ) { target += '/'; }
105 const char invalid_chars[] =
106 "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F"
107 "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F"
109 // note that the terminating '\0' is part of the string - thus the size below
110 // is sizeof(invalid_chars) rather than sizeof(invalid_chars)-1. I
111 const std::string invalid_generic( invalid_chars, sizeof(invalid_chars) );
113 const std::string valid_posix(
114 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789._-" );
116 const std::string valid_boost_file(
117 "abcdefghijklmnopqrstuvwxyz0123456789._-" );
118 const std::string valid_boost_directory(
119 "abcdefghijklmnopqrstuvwxyz0123456789_-" );
121 } // unnamed namespace
123 //----------------------------------------------------------------------------//
130 // error checking functions --------------------------------------------//
132 bool generic_name( const std::string & name )
134 return name.size() != 0
135 && name.find_first_of( invalid_generic ) == std::string::npos
138 && *name.begin() != ' '
139 && *(name.end()-1) != ' ';
142 bool posix_name( const std::string & name )
144 return name.find_first_not_of( valid_posix ) == std::string::npos
148 const path & check_posix_leaf( const path & ph )
150 if ( !posix_name( ph.leaf() ) )
151 boost::throw_exception( filesystem_error(
152 "boost::filesystem::check_posix_leaf",
153 ph, "invalid posix name: \""
154 + ph.leaf() + "\"" ) );
158 bool boost_file_name( const std::string & name )
160 return name.size() <= 31
161 && name.find_first_not_of( valid_boost_file ) == std::string::npos
165 bool boost_directory_name( const std::string & name )
167 return name.size() <= 31
168 && name.find_first_not_of( valid_boost_directory ) == std::string::npos;
171 // path implementation -----------------------------------------------------//
173 path::path( const std::string & src )
175 m_path_append( src );
178 path::path( const char * src )
180 m_path_append( src );
183 path::path( const std::string & src, path_format )
185 m_path_append( src, platform );
188 path::path( const char * src, path_format )
190 m_path_append( src, platform );
193 path & path::operator /=( const path & rhs )
195 m_path_append( rhs.m_path, nocheck );
199 void path::m_path_append( const std::string & src, source_context context )
201 // convert backslash to forward slash if context is "platform"
202 // check names if context is "generic"
203 // allow system-specific-root if context is not "generic"
205 assert( src.size() == std::strlen( src.c_str() ) ); // no embedded 0
207 if ( src.size() == 0 ) return;
209 std::string::const_iterator itr( src.begin() );
212 # ifdef BOOST_WINDOWS
213 if ( context != generic && src.size() >= 2 )
216 if ( src[1] == ':' || src[src.size()-1] == ':' )
218 for ( ; *itr != ':'; ++itr ) m_path += *itr;
224 else if ( (*itr == '/' || (*itr == '\\' && context == platform))
225 && (*(itr+1) == '/' || (*(itr+1) == '\\' && context == platform)) )
229 itr != src.end() && *itr != '/' && *itr != '\\';
230 ++itr ) m_path += *itr;
235 // root directory [ "/" ]
236 if ( itr != src.end() && (*itr == '/'
237 # ifdef BOOST_WINDOWS
238 || (*itr == '\\' && context == platform)
243 if ( m_path.size() == 0
244 # ifdef BOOST_WINDOWS
245 || m_path[m_path.size()-1] == ':' // drive or device
250 && m_path.find( '/', 2 ) == std::string::npos
256 // element { "/" element } [ "/" ]
257 while ( itr != src.end() )
260 // append '/' if needed
262 && *(m_path.end()-1) != ':' && *(m_path.end()-1) != '/' )
265 if ( *itr == '.'&& (itr+1) != src.end() && *(itr+1) == '.' ) // ".."
267 if ( m_path.size() >= 2 // there is a named parent directory present
268 && *(m_path.end()-1) == '/'
269 # ifdef BOOST_WINDOWS
270 && *(m_path.end()-2) != ':'
272 && *(m_path.end()-2) != '.' )
274 // reference to parent so erase child
275 std::string::iterator child( m_path.end()-2 );
277 while ( child != m_path.begin() && *child != '/'
278 # ifdef BOOST_WINDOWS
283 // only erase '/' if it is a separator rather than part of the root
285 && (child == m_path.begin()
286 # ifdef BOOST_WINDOWS
287 || *(child-1) == ':'))
294 m_path.erase( child, m_path.end() );
296 else { m_path += ".."; }
300 else // element is name
305 while ( ++itr != src.end() && *itr != '/'
306 # ifdef BOOST_WINDOWS
307 && (*itr != '\\' || context != platform)
311 if ( context == generic && !generic_name( name ) )
313 boost::throw_exception( filesystem_error(
314 "boost::filesystem::path",
315 "invalid name \"" + name + "\" in path: \"" + src + "\"" ) );
321 if ( itr != src.end() )
324 # ifdef BOOST_WINDOWS
325 || (*itr == '\\' && context == platform)
329 boost::throw_exception( filesystem_error(
330 "boost::filesystem::path",
331 "invalid path syntax: \"" + src + "\"" ) );
334 } // while more elements
337 // path conversion functions ------------------------------------------------//
339 std::string path::native_file_string() const
341 # ifdef BOOST_WINDOWS
342 std::string s( m_path );
343 for ( std::string::iterator itr( s.begin() );
344 itr != s.end(); ++itr )
345 if ( *itr == '/' ) *itr = '\\';
352 std::string path::native_directory_string() const
353 { return native_file_string(); }
355 // path decomposition functions ---------------------------------------------//
357 path::iterator path::begin() const
360 itr.base().path_ptr = this;
361 first_name( m_path, itr.base().name );
366 void path::m_replace_leaf( const char * new_leaf )
368 m_path.erase( leaf_pos( m_path, m_path.size() ) );
372 std::string path::leaf() const
374 return m_path.substr( leaf_pos( m_path, m_path.size() ) );
379 inline bool is_absolute_root( const std::string & s,
380 std::string::size_type len )
383 len && s[len-1] == '/'
387 # ifdef BOOST_WINDOWS
389 && ( s[len-2] == ':' // drive or device
390 || ( s[0] == '/' // share
392 && s.find( '/', 2 ) == len-1
401 path path::branch_path() const
403 std::string::size_type end_pos( leaf_pos( m_path, m_path.size() ) );
405 // skip a '/' unless it is a root directory
406 if ( end_pos && m_path[end_pos-1] == '/'
407 && !detail::is_absolute_root( m_path, end_pos ) ) --end_pos;
408 return path( m_path.substr( 0, end_pos ), native );
411 path path::relative_path() const
413 std::string::size_type pos( 0 );
414 if ( m_path.size() && m_path[0] == '/' )
416 # ifdef BOOST_WINDOWS
417 if ( m_path.size()>1 && m_path[1] == '/' ) // share
419 if ( (pos = m_path.find( '/', 2 )) != std::string::npos ) ++pos;
423 else if ( (pos = m_path.find( ':' )) == std::string::npos ) pos = 0;
426 if ( ++pos < m_path.size() && m_path[pos] == '/' ) ++pos;
429 return path( m_path.substr( pos ) );
432 std::string path::root_name() const
434 # ifdef BOOST_WINDOWS
435 std::string::size_type pos( m_path.find( ':' ) );
436 if ( pos != std::string::npos ) return m_path.substr( 0, pos+1 );
437 if ( m_path.size() > 2 && m_path[0] == '/' && m_path[1] == '/' )
439 pos = m_path.find( '/', 2 );
440 return m_path.substr( 0, pos );
443 return std::string();
446 std::string path::root_directory() const
449 ( m_path.size() && m_path[0] == '/' ) // covers both "/" and "//share"
450 # ifdef BOOST_WINDOWS
451 || ( m_path.size() > 2
453 && m_path[2] == '/' ) // "c:/"
458 path path::root_path() const
461 # ifdef BOOST_WINDOWS
462 root_name(), native ) /= root_directory();
468 // path query functions -----------------------------------------------------//
470 bool path::is_complete() const
472 # ifdef BOOST_WINDOWS
473 return m_path.size() > 2
474 && ( (m_path[1] == ':' && m_path[2] == '/') // "c:/"
475 || (m_path[0] == '/' && m_path[1] == '/') // "//share"
476 || m_path[m_path.size()-1] == ':' );
478 return m_path.size() && m_path[0] == '/';
482 bool path::has_root_path() const
484 return ( m_path.size()
485 && m_path[0] == '/' ) // covers both "/" and "//share"
486 # ifdef BOOST_WINDOWS
487 || ( m_path.size() > 1 && m_path[1] == ':' ) // "c:" and "c:/"
488 || ( m_path.size() > 3
489 && m_path[m_path.size()-1] == ':' ) // "device:"
494 bool path::has_root_name() const
496 # ifdef BOOST_WINDOWS
497 return m_path.size() > 1
498 && ( m_path[1] == ':' // "c:"
499 || m_path[m_path.size()-1] == ':' // "prn:"
500 || (m_path[0] == '/' && m_path[1] == '/') // "//share"
507 bool path::has_root_directory() const
509 return ( m_path.size()
510 && m_path[0] == '/' ) // covers both "/" and "//share"
511 # ifdef BOOST_WINDOWS
512 || ( m_path.size() > 2
513 && m_path[1] == ':' && m_path[2] == '/' ) // "c:/"
518 bool path::has_relative_path() const { return !relative_path().empty(); }
519 bool path::has_branch_path() const { return !branch_path().empty(); }
522 // path_itr_imp implementation ----------------------------------------------//
526 void path_itr_imp::operator++()
528 assert( pos < path_ptr->m_path.size() ); // detect increment past end
530 if ( pos == path_ptr->m_path.size() )
532 name = ""; // not strictly required, but might aid debugging
535 if ( path_ptr->m_path[pos] == '/' )
537 # ifdef BOOST_WINDOWS
538 if ( name[name.size()-1] == ':' // drive or device
539 || (name[0] == '/' && name[1] == '/') ) // share
547 std::string::size_type end_pos( path_ptr->m_path.find( '/', pos ) );
548 if ( end_pos == std::string::npos ) end_pos = path_ptr->m_path.size();
549 name = path_ptr->m_path.substr( pos, end_pos - pos );
552 void path_itr_imp::operator--()
554 assert( pos ); // detect decrement of begin
555 std::string::size_type end_pos( pos );
557 // skip a '/' unless it is a root directory
558 if ( path_ptr->m_path[end_pos-1] == '/'
559 && !detail::is_absolute_root( path_ptr->m_path, end_pos ) ) --end_pos;
560 pos = leaf_pos( path_ptr->m_path, end_pos );
561 name = path_ptr->m_path.substr( pos, end_pos - pos );
564 } // namespace detail
565 } // namespace filesystem