From ecdecd6353ac96442d5f61e73e529189be5fac86 Mon Sep 17 00:00:00 2001 From: Beman Dawes Date: Mon, 25 Nov 2002 01:14:05 +0000 Subject: [PATCH] many path related review comments applied [SVN r16394] --- doc/faq.htm | 24 +- doc/path.htm | 273 ++++++++++++++- include/boost/filesystem/exception.hpp | 12 +- include/boost/filesystem/fstream.hpp | 2 +- include/boost/filesystem/operations.hpp | 4 +- include/boost/filesystem/path.hpp | 40 +-- .../recursive_directory_iterator.hpp | 2 +- src/exception.cpp | 2 +- src/operations_posix_windows.cpp | 81 +++-- src/path_posix_windows.cpp | 264 +++++++++++---- test/operations_test.cpp | 16 +- test/path_test.cpp | 318 ++++++++++++------ 12 files changed, 735 insertions(+), 303 deletions(-) diff --git a/doc/faq.htm b/doc/faq.htm index ff49f73..525b4ac 100644 --- a/doc/faq.htm +++ b/doc/faq.htm @@ -153,26 +153,10 @@ conditions for error are very different between the source and the target.

detection were evaluated, including at least four complete implementations.  While the details for rejection differed, they all tended to distort the otherwise simple design of the rest of the library.

-

Why doesn't the generic path grammar include syntax for portably -specifying the root directory?

-

The concept of "root directory" appears to be inherently non-portable.  -For example, "/"  means one thing on POSIX (an absolute path the single -filesystem root), and another on Windows (a relative path to the root of the -current drive).  It goes rapidly downhill from there; on the classic Mac -OS, root names can be ambiguous!

-

Why isn't there a path::is_absolute() or similar function?

-

Because useful semantics are not obvious. On some operating systems a path is clearly either absolute or -relative, but on others the distinction isn't clear. For example, on Windows consider these -paths:

- +

Why do some path member function names have a "system_specific_" prefix?

+

To alert users that the results are inherently non-portable. The names are +deliberately ugly to discourage use except where really necessary.


© Copyright Beman Dawes, 2002

Revised -12 September, 2002

\ No newline at end of file +20 November, 2002

\ No newline at end of file diff --git a/doc/path.htm b/doc/path.htm index d954ec5..4da9960 100644 --- a/doc/path.htm +++ b/doc/path.htm @@ -20,7 +20,8 @@ Header synopsis
Class path
Member functions
-Non-member functions

+Non-member functions
+Path decomposition

Introduction

Many Filesystem Library functions traffic in objects of class path, @@ -68,19 +69,15 @@ constructor is provided which takes a system-specific format as an argument.

the operating system's format, and a file path string using the operating system's format.  Additional access functions retrieve specific portions of the contained path.

-

-

-

-

-

-

Grammar for portable generic path string

The grammar is specified in extended BNF, with terminal symbols in quotes:

-
path ::= [system-specific-root] [relative-path] 
-
relative-path ::= element { "/" element } 
-
element ::= name | parent-directory 
+
path ::= [root] [relative-path]  // an empty path is valid
+
root ::= [system-specific-root] [root-directory]
+
root-directory ::= "/"
+
relative-path ::= path-element { "/" path-element } ["/"]
+
path-element ::= name | parent-directory 
parent-directory ::= ".." 
name ::= char { char }
@@ -92,12 +89,17 @@ with the system_specific decorator.

Although implementation-defined, it is desirable that system-specific-root have a grammar which is distinguishable from other grammar elements, and follow the conventions of the operating system.

+

The optional trailing "/" in a relative-path is allowed as a +notational convenience. It has no semantic meaning and is discarded in +conversions to canonical form.

Whether or not a generic path string is actually portable to a particular operating system will depend on the names used.  See the Portability Guide.

Canonical form

Adjacent name, parent-directory elements in m_name -have been recursively removed.

+have been recursively removed.

+

relative-path does not have a trailing +"/".

Header boost/filesystem/path.hpp synopsis

namespace boost
@@ -221,6 +223,17 @@ in the example produces a useless result. On this operating system, the
 programmer should only use this path as a file path. (There is a
 portability recommendation 
 to not use periods in directory names.)

+

Warning for POSIX and UNIX programmers

+

POSIX and other UNIX-like file systems are single-rooted, while most other +file systems are multi-rooted. Multi-rooted file systems require a system-root +such as a drive, device, disk, volume, or share name for a path to be absolute.  +Because of this, the root() and root_directory() functions return +identical results on UNIX and other single-rooted file systems, but different +results on multi-rooted file systems. Thus use of the wrong function will not be +apparent on UNIX-like systems, but will result in non-portable code which will +fail when used on multi-rooted systems. Thus UNIX programmers should use +particular care to choose between root() and root_directory().

+

The same warning applies to has_root() and has_root_directory().

constructors

path();
@@ -231,7 +244,7 @@ path( const char * src );
path string grammar relative-path syntax, and contains no embedded '\0' characters.

Effects: For each src elementm_name.push_back( element ).

-

Postcondition: m_name has been reduced to +

Postcondition: m_name is in canonical form.

Rationale: These constructors are not explicit because an intended use is automatic conversion of strings to paths.

@@ -239,17 +252,25 @@ path( const char * src ); path( const char * src, path_format );

Precondition: src conforms to the operating system's grammar for path strings, and contains no embedded '\0' characters.

-

Effects: For each src element (where an element represents a - directory name, file name, or parent-directory indicator),  m_name.push_back( element ).

-

Postcondition: m_name has been reduced to +

Effects: For each src element,  m_name.push_back( element ).

+

Postcondition: m_name is in canonical form.

operator <<=

path & operator<<=( const path & rhs );
-

Effects: Append rhs.m_name to m_name.

+

Effects: If any of the following conditions are met, then + m_name.push_back("/").

+ +

 Then append rhs.m_name to m_name.

+

(Footnote: Thus on Windows, (path("//share") /= "foo").string() is + "//share/foo")

Returns: *this

-

Postcondition: m_name has been reduced to +

Postcondition: m_name is in canonical form.

Rationale: It is not considered an error for rhs to include a system-specific-root because it might relative, and @@ -363,10 +384,226 @@ also supplies several non-member functions which can be used to verify that a path meets certain requirements. These subsidiary functions are undocumented pending more research and discussion, and should not be relied upon as they are likely to change.

+

Path decomposition

+

It is often useful to extract specific elements from a path object.  +While any decomposition can be achieved by iterating over the elements of a +path, convenience functions are provided which are easier to use, more +efficient, and less error prone.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
generic_
+ string()
Elementsroot_
+ path()
system_
+ specific_
+ root()
root_
+ directory()
relative_
+ path()
root_
+ directory()
+ / relative_
+ path()
system_
+ specific_
+ root() /
+ relative_
+ path()
branch_
+ path()
leaf()
All systems         
///""/""/""""/
foofoo""""""foofoofoo""foo
/foo/,foo/""/foo/foofoo/foo
foo/barfoo,bar""""""foo/barfoo/barfoo/barfoobar
/foo/bar/,foo,bar/""/foo/bar/foo/barfoo/bar/foobar
Windows         
c:c:c:c:""""""c:""c:
c:fooc:,fooc:c:""foofooc:fooc:foo
c:/c:,/c:/c:/""/c:c:/
c:/fooc:,/,fooc:/c:/foo/fooc:fooc:/foo
//shr//shr//shr//shr""""""//shr""//shr
//shr///shr,///shr///shr/""///shr//shr/
//shr/foo//shr,
+ /,foo
//shr///shr/foo/foo//shr/foo//shr/foo
prn:prn:prn:prn:""""""prn:""prn:
+

 


© Copyright Beman Dawes, 2002

Revised -20 September, 2002

+24 November, 2002

diff --git a/include/boost/filesystem/exception.hpp b/include/boost/filesystem/exception.hpp index 6c40146..28b7bc8 100644 --- a/include/boost/filesystem/exception.hpp +++ b/include/boost/filesystem/exception.hpp @@ -14,7 +14,7 @@ // Original author: Dietmar Kühl. Revised by Beman Dawes. -// See http://www.boost.org for most recent version including documentation. +// See http://www.boost.org/libs/filesystem for documentation. //----------------------------------------------------------------------------// @@ -32,20 +32,12 @@ namespace boost { enum error_type { system_error }; - class filesystem_error: - public std::runtime_error + class filesystem_error : public std::runtime_error { public: explicit filesystem_error( std::string const& msg ); - // Effects: : std::runtime_error(implementation-defined), - // m_msg(msg), m_err(0) - explicit filesystem_error( std::string const& msg, error_type ); - // Effects: : std::runtime_error(implementation-defined), - // m_msg(msg), m_err(value), where value is appropriate - // for the operating system (for example, GetLastError() on Windows, - // errno on POSIX) ~filesystem_error() throw(); char const* what() const throw(); diff --git a/include/boost/filesystem/fstream.hpp b/include/boost/filesystem/fstream.hpp index 1977f75..d9fd066 100644 --- a/include/boost/filesystem/fstream.hpp +++ b/include/boost/filesystem/fstream.hpp @@ -5,7 +5,7 @@ // in all copies. This software is provided "as is" without express or implied // warranty, and with no claim as to its suitability for any purpose. -// See http://www.boost.org for most recent version including documentation. +// See http://www.boost.org/libs/filesystem for documentation. //----------------------------------------------------------------------------// diff --git a/include/boost/filesystem/operations.hpp b/include/boost/filesystem/operations.hpp index e90009f..8f35060 100644 --- a/include/boost/filesystem/operations.hpp +++ b/include/boost/filesystem/operations.hpp @@ -14,7 +14,7 @@ // < "as is" without express or implied warranty. > // < ----------------------------------------------------------------------- > -// See http://www.boost.org for most recent version including documentation. +// See http://www.boost.org/libs/filesystem for documentation. //----------------------------------------------------------------------------// @@ -116,7 +116,7 @@ namespace boost { public: directory_iterator(); // creates the "end" iterator - explicit directory_iterator( const path & directory_path ); + explicit directory_iterator( const path & system_specific_directory_string ); // workaround iterator_adaptor / compiler interactions const boost::filesystem::path * operator->() const diff --git a/include/boost/filesystem/path.hpp b/include/boost/filesystem/path.hpp index 6933949..61fb1fc 100644 --- a/include/boost/filesystem/path.hpp +++ b/include/boost/filesystem/path.hpp @@ -32,9 +32,9 @@ namespace boost std::string name; // cache current element. const path * path_ptr; // path being iterated over. std::string::size_type pos; // position of name in - // path_ptr->generic_path(). The + // path_ptr->string(). The // end() iterator is indicated by - // pos == path_ptr->generic_path().size() + // pos == path_ptr->string().size() const std::string & operator*() const { return name; } void operator++(); @@ -72,27 +72,27 @@ namespace boost path & make_absolute( const path & root_source ); // decomposition functions: - path root() const; - std::string system_root() const; - std::string root_directory() const; - - path relative_path() const; - - std::string leaf() const; - path branch() const; + path root_path() const; + std::string system_specific_root() const; + std::string root_directory() const; + path relative_path() const; + std::string leaf() const; + path branch_path() const; // query functions: bool is_null() const { return m_path.size() == 0; } bool is_absolute() const; - bool has_root() const; - bool has_system_root() const; +/* + bool has_root_path() const; + bool has_system_specific_root() const; bool has_root_directory() const; bool has_relative_path() const; +*/ + const std::string & string() const { return m_path; } - const std::string & generic_string() const { return m_path; } - const std::string & file_string() const { return m_path; } // native format - const std::string & directory_string() const { return m_path; } // ditto + std::string system_specific_file_string() const; + std::string system_specific_directory_string() const; // iteration over the names in the path: typedef boost::iterator_adaptor< @@ -120,7 +120,7 @@ namespace boost // constructor input formats. Private members might be quite different // in other implementations, particularly where there were wide // differences between generic and system-specific argument formats, - // or between file_path() and directory_path() formats. + // or between system_specific_file_string() and system_specific_directory_string() formats. std::string m_path; @@ -138,11 +138,11 @@ namespace boost // path non-member functions ---------------------------------------------// - inline const path operator << ( const char * lhs, const path & rhs ) - { return path( lhs ) <<= rhs; } + inline const path operator / ( const char * lhs, const path & rhs ) + { return path( lhs ) /= rhs; } - inline const path operator << ( const std::string & lhs, const path & rhs ) - { return path( lhs ) <<= rhs; } + inline const path operator / ( const std::string & lhs, const path & rhs ) + { return path( lhs ) /= rhs; } // error checking --------------------------------------------------------// diff --git a/include/boost/filesystem/recursive_directory_iterator.hpp b/include/boost/filesystem/recursive_directory_iterator.hpp index bf68ca1..bd136ac 100644 --- a/include/boost/filesystem/recursive_directory_iterator.hpp +++ b/include/boost/filesystem/recursive_directory_iterator.hpp @@ -5,7 +5,7 @@ // in all copies. This software is provided "as is" without express or implied // warranty, and with no claim as to its suitability for any purpose. -// See http://www.boost.org for most recent version including documentation. +// See http://www.boost.org/libs/filesystem for documentation. #ifndef BOOST_FILESYSTEM_RECURSIVE_DIRECTORY_ITERATOR_HPP #define BOOST_FILESYSTEM_RECURSIVE_DIRECTORY_ITERATOR_HPP diff --git a/src/exception.cpp b/src/exception.cpp index 51fece7..3f3d1f3 100644 --- a/src/exception.cpp +++ b/src/exception.cpp @@ -14,7 +14,7 @@ // Original author: Dietmar Kühl. Revised by Beman Dawes. -// See http://www.boost.org for most recent version including documentation. +// See http://www.boost.org/libs/filesystem for documentation. //----------------------------------------------------------------------------// diff --git a/src/operations_posix_windows.cpp b/src/operations_posix_windows.cpp index b033ae2..c985a4c 100644 --- a/src/operations_posix_windows.cpp +++ b/src/operations_posix_windows.cpp @@ -13,7 +13,7 @@ // < "as is" without expressed or implied warranty. > // < ----------------------------------------------------------------------- > -// See http://www.boost.org for most recent version including documentation. +// See http://www.boost.org/libs/filesystem for documentation. //----------------------------------------------------------------------------// @@ -36,6 +36,12 @@ namespace fs = boost::filesystem; # if defined(BOOST_WINDOWS) || (!defined(BOOST_POSIX) && defined(_WIN32)) # include "windows.h" + +// For Windows, the xxxA form of various function names is used to avoid +// inadvertently getting wide forms of the functions. (The undecorated +// forms are actually macros, so can misfire if the user has various +// other macros defined. There was a bug report of this happening.) + # else # define BOOST_POSIX # include "sys/stat.h" @@ -106,7 +112,7 @@ namespace // Returns: 0 if error, otherwise name { std::string dirpath( std::string(dir) + "/*" ); - return ( (handle = ::FindFirstFile( dirpath.c_str(), &data )) + return ( (handle = ::FindFirstFileA( dirpath.c_str(), &data )) == BOOST_INVALID_HANDLE_VALUE ) ? 0 : data.cFileName; } @@ -121,7 +127,7 @@ namespace // Returns: 0 if EOF, otherwise name // Throws: if system reports error { - if ( ::FindNextFile( handle, &data ) == 0 ) + if ( ::FindNextFileA( handle, &data ) == 0 ) { if ( ::GetLastError() != ERROR_NO_MORE_FILES ) { @@ -200,7 +206,7 @@ namespace boost if ( dir_path.is_null() ) base().imp->handle = BOOST_INVALID_HANDLE_VALUE; else - name = find_first_file( dir_path.directory_path().c_str(), + name = find_first_file( dir_path.system_specific_directory_string().c_str(), base().imp->handle, scratch ); // sets handle if ( base().imp->handle != BOOST_INVALID_HANDLE_VALUE ) @@ -216,7 +222,7 @@ namespace boost { throw filesystem_error( std::string( "directory_iterator constructor failure: " ) - + dir_path.directory_path().c_str(), system_error ); + + dir_path.system_specific_directory_string().c_str(), system_error ); } } @@ -251,9 +257,9 @@ namespace boost { # ifdef BOOST_POSIX struct stat path_stat; - return ::stat( ph.file_path().c_str(), &path_stat ) == 0; + return ::stat( ph.string().c_str(), &path_stat ) == 0; # else - return ::GetFileAttributes( ph.file_path().c_str() ) != 0xFFFFFFFF; + return ::GetFileAttributesA( ph.string().c_str() ) != 0xFFFFFFFF; # endif } @@ -261,15 +267,15 @@ namespace boost { # ifdef BOOST_POSIX struct stat path_stat; - if ( ::stat( ph.directory_path().c_str(), &path_stat ) != 0 ) + if ( ::stat( ph.system_specific_directory_string().c_str(), &path_stat ) != 0 ) throw filesystem_error( std::string("is_directory(): ") - + ph.directory_path().c_str(), system_error ); + + ph.system_specific_directory_string().c_str(), system_error ); return S_ISDIR( path_stat.st_mode ); # else - DWORD attributes = ::GetFileAttributes( ph.directory_path().c_str() ); + DWORD attributes = ::GetFileAttributesA( ph.system_specific_directory_string().c_str() ); if ( attributes == 0xFFFFFFFF ) throw filesystem_error( std::string("is_directory(): ") - + ph.directory_path().c_str(), system_error ); + + ph.system_specific_directory_string().c_str(), system_error ); return (attributes & FILE_ATTRIBUTE_DIRECTORY) != 0; # endif } @@ -278,19 +284,19 @@ namespace boost { # ifdef BOOST_POSIX struct stat path_stat; - if ( ::stat( ph.file_path().c_str(), &path_stat ) != 0 ) + if ( ::stat( ph.string().c_str(), &path_stat ) != 0 ) throw filesystem_error( std::string("is_empty(): ") - + ph.file_path().c_str(), system_error ); + + ph.system_specific_file_string().c_str(), system_error ); return S_ISDIR( path_stat.st_mode ) ? is_empty_directory( ph ) : path_stat.st_size == 0; # else WIN32_FILE_ATTRIBUTE_DATA fad; - if ( !::GetFileAttributesEx( ph.file_path().c_str(), + if ( !::GetFileAttributesExA( ph.string().c_str(), ::GetFileExInfoStandard, &fad ) ) throw filesystem_error( std::string("is_empty(): ") - + ph.file_path().c_str(), system_error ); + + ph.system_specific_file_string().c_str(), system_error ); return ( fad.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) ? is_empty_directory( ph ) @@ -301,13 +307,13 @@ namespace boost void create_directory( const path & dir_path ) { # ifdef BOOST_POSIX - if ( ::mkdir( dir_path.directory_path().c_str(), + if ( ::mkdir( dir_path.system_specific_directory_string().c_str(), S_IRWXU|S_IRWXG|S_IRWXO ) != 0 ) # else - if ( !::CreateDirectory( dir_path.directory_path().c_str(), 0 ) ) + if ( !::CreateDirectoryA( dir_path.system_specific_directory_string().c_str(), 0 ) ) # endif throw filesystem_error( std::string("create_directory(): ") - + dir_path.directory_path().c_str(), system_error ); + + dir_path.system_specific_directory_string().c_str(), system_error ); } void remove( const path & ph ) @@ -317,22 +323,22 @@ namespace boost if ( is_directory( ph ) ) { # ifdef BOOST_POSIX - if ( ::rmdir( ph.file_path().c_str() ) != 0 ) + if ( ::rmdir( ph.string().c_str() ) != 0 ) # else - if ( !::RemoveDirectory( ph.file_path().c_str() ) ) + if ( !::RemoveDirectoryA( ph.string().c_str() ) ) # endif throw fs::filesystem_error( std::string("remove() on: ") - + ph.file_path().c_str(), system_error ); + + ph.system_specific_file_string().c_str(), system_error ); } else { # ifdef BOOST_POSIX - if ( ::remove( ph.file_path().c_str() ) != 0 ) + if ( ::remove( ph.string().c_str() ) != 0 ) # else - if ( !::DeleteFile( ph.file_path().c_str() ) ) + if ( !::DeleteFileA( ph.string().c_str() ) ) # endif throw fs::filesystem_error( std::string("remove() on: ") - + ph.file_path().c_str(), system_error ); + + ph.system_specific_file_string().c_str(), system_error ); } } } @@ -347,12 +353,13 @@ namespace boost { # ifdef BOOST_POSIX if ( exists( new_path ) // POSIX is too permissive so must check - || ::rename( old_path.file_path().c_str(), new_path.file_path().c_str() ) != 0 ) + || ::rename( old_path.string().c_str(), new_path.string().c_str() ) != 0 ) # else - if ( !::MoveFile( old_path.file_path().c_str(), new_path.file_path().c_str() ) ) + if ( !::MoveFileA( old_path.string().c_str(), new_path.string().c_str() ) ) # endif throw filesystem_error( std::string("move_file(): ") - + old_path.file_path().c_str() + ", " + new_path.file_path().c_str(), system_error ); + + old_path.system_specific_file_string().c_str() + + ", " + new_path.system_specific_file_string().c_str(), system_error ); } void copy_file( const path & from_file_ph, @@ -365,16 +372,16 @@ namespace boost boost::scoped_array buf( new char [buf_sz] ); int infile, outfile; - if ( (infile = ::open( from_file_ph.file_path().c_str(), + if ( (infile = ::open( from_file_ph.string().c_str(), O_RDONLY )) < 0 - || (outfile = ::open( to_file_ph.file_path().c_str(), + || (outfile = ::open( to_file_ph.string().c_str(), O_WRONLY | O_CREAT | O_EXCL, S_IRWXU|S_IRWXG|S_IRWXO )) < 0 ) { if ( infile != 0 ) ::close( infile ); throw fs::filesystem_error( std::string("copy() files: ") - + from_file_ph.file_path().c_str() - + ", " + to_file_ph.file_path().c_str(), system_error ); + + from_file_ph.string().c_str() + + ", " + to_file_ph.string().c_str(), system_error ); } ssize_t sz; @@ -386,12 +393,12 @@ namespace boost if ( sz != 0 ) # else - if ( !::CopyFile( from_file_ph.file_path().c_str(), - to_file_ph.file_path().c_str(), /*fail_if_exists=*/true ) ) + if ( !::CopyFileA( from_file_ph.string().c_str(), + to_file_ph.string().c_str(), /*fail_if_exists=*/true ) ) # endif throw fs::filesystem_error( std::string("copy() files: ") - + from_file_ph.file_path().c_str() - + ", " + to_file_ph.file_path().c_str(), system_error ); + + from_file_ph.system_specific_file_string().c_str() + + ", " + to_file_ph.system_specific_file_string().c_str(), system_error ); } const path & initial_directory() @@ -408,10 +415,10 @@ namespace boost if ( ::getcwd( buf.get(), static_cast(path_max) ) == 0 ) # else DWORD sz; - if ( (sz = ::GetCurrentDirectory( 0, static_cast(0) )) == 0 ) + if ( (sz = ::GetCurrentDirectoryA( 0, static_cast(0) )) == 0 ) throw filesystem_error( "initial_directory()" ); boost::scoped_array buf( new char[sz] ); - if ( ::GetCurrentDirectory( sz, buf.get() ) == 0 ) + if ( ::GetCurrentDirectoryA( sz, buf.get() ) == 0 ) # endif { throw filesystem_error( "initial_directory()", system_error ); } init_dir = path( buf.get(), system_specific ); diff --git a/src/path_posix_windows.cpp b/src/path_posix_windows.cpp index a2e7ddf..39aa9b8 100644 --- a/src/path_posix_windows.cpp +++ b/src/path_posix_windows.cpp @@ -6,7 +6,7 @@ // without express or implied warranty, and with no claim as to its // suitability for any purpose. -// See http://www.boost.org for most recent version including documentation. +// See http://www.boost.org/libs/filesystem for documentation. //****************************************************************************// @@ -50,22 +50,17 @@ namespace { // POSIX & Windows cases: "", "/", "/foo", "foo", "foo/bar" // Windows only cases: "c:", "c:/", "c:foo", "c:/foo", - // "prn:", "//share", "//share/foo" + // "prn:", "//share", "//share/", "//share/foo" std::string::size_type leaf_pos( const std::string & str, std::string::size_type end_pos ) // end_pos is past-the-end position // return 0 if str itself is leaf (or empty) { - if ( (end_pos == 1 && str[0] == '/') -# ifdef BOOST_WINDOWS - || (end_pos > 1 // drive or device - && (str[end_pos-1] == ':' || str[end_pos-2] == ':')) -# endif - ) return 0; - + if ( end_pos && str[end_pos-1] == '/' ) return end_pos-1; + std::string::size_type pos( str.find_last_of( '/', end_pos-1 ) ); # ifdef BOOST_WINDOWS - if ( pos == std::string::npos ) pos = str.find_last_of( ':', end_pos-1 ); + if ( pos == std::string::npos ) pos = str.find_last_of( ':', end_pos-2 ); # endif return ( pos == std::string::npos // path itself must be a leaf (or empty) @@ -99,8 +94,6 @@ namespace if ( *itr == ':' ) { target += *itr++; - if ( itr == src.end() ) return; - if ( *itr == '/' ) { target += *itr++; } return; } # endif @@ -137,7 +130,6 @@ namespace boost // error checking functions --------------------------------------------// - bool generic_name( const std::string & name ) { return name.size() != 0 @@ -197,12 +189,24 @@ namespace boost m_path_append( src, platform ); } - path & path::operator <<=( const path & rhs ) + path & path::operator /=( const path & rhs ) { m_path_append( rhs.m_path, nocheck ); return *this; } + std::string path::system_specific_file_string() const + { + std::string s( m_path ); + for ( std::string::iterator itr( s.begin() ); + itr != s.end(); ++itr ) + if ( *itr == '/' ) *itr = '\\'; + return s; + } + + std::string path::system_specific_directory_string() const + { return system_specific_file_string(); } + void path::m_path_append( const std::string & src, source_context context ) { // convert backslash to forward slash if context is "platform" @@ -215,31 +219,57 @@ namespace boost std::string::const_iterator itr( src.begin() ); - // system-specific-root like drive: or first slash of //share - if ( context != generic ) + // [system-specific-root] +# ifdef BOOST_WINDOWS + if ( context != generic && src.size() >= 2 ) { - if ( *itr == '/' + // drive or device + if ( src[1] == ':' || src[src.size()-1] == ':' ) + { + for ( ; *itr != ':'; ++itr ) m_path += *itr; + m_path += ':'; + ++itr; + } + + // share + else if ( (*itr == '/' || (*itr == '\\' && context == platform)) + && (*(itr+1) == '/' || (*(itr+1) == '\\' && context == platform)) ) + { + m_path += "//"; + for ( itr += 2; + itr != src.end() && *itr != '/' && *itr != '\\'; + ++itr ) m_path += *itr; + } + } +# endif + + // root directory [ "/" ] + if ( itr != src.end() && (*itr == '/' # ifdef BOOST_WINDOWS || (*itr == '\\' && context == platform) # endif - ) { ++itr; m_path += '/'; } - else if ( itr+1 != src.end() && *(itr+1) == ':' ) - { m_path += *itr++; m_path += *itr++; } - -# ifdef BOOST_WINDOWS - // "/" or second slash of //share - if ( *itr == '/' || (*itr == '\\' && context == platform) ) - { ++itr; m_path += '/';} -# endif - - if ( itr == src.end() ) return; + ) ) + { + ++itr; + if ( m_path.size() == 0 +# ifdef BOOST_WINDOWS + || m_path[m_path.size()-1] == ':' // drive or device + || ( // share + m_path.size() > 2 + && m_path[0] == '/' + && m_path[1] == '/' + && m_path.find( '/', 2 ) == std::string::npos + ) +# endif + ) m_path += '/'; } - // relative-path ::= element { "/" element } + // element { "/" element } [ "/" ] while ( itr != src.end() ) { + // append '/' if needed - if ( !is_null() // append '/' + if ( !is_null() && *(m_path.end()-1) != ':' && *(m_path.end()-1) != '/' ) m_path += '/'; @@ -278,7 +308,7 @@ namespace boost ++itr; ++itr; } // ".." - else // name + else // element is name { std::string name; do @@ -292,30 +322,28 @@ namespace boost if ( context == generic && !generic_name( name ) ) { throw filesystem_error( "invalid path name: \"" - + src + "\"" ); + + src + "\"" ); } m_path += name; } if ( itr != src.end() ) - { - if ( !( (*itr == '/' -# ifdef BOOST_WINDOWS - || (context == platform && *itr == '\\') -# endif - ) && (itr+1) != src.end() ) ) - { + { + if ( *itr == '/' +# ifdef BOOST_WINDOWS + || (*itr == '\\' && context == platform) +# endif + ) ++itr; + else throw filesystem_error( "invalid path syntax: \"" - + src + "\"" ); - } - ++itr; + + src + "\"" ); } } // while more elements } - const path::iterator path::begin() const + path::iterator path::begin() const { iterator itr; itr.base().path_ptr = this; @@ -330,37 +358,117 @@ namespace boost m_path += new_leaf; } - const std::string path::leaf() const + std::string path::leaf() const { return m_path.substr( leaf_pos( m_path, m_path.size() ) ); } - const path path::branch() const + namespace detail { - std::string::size_type len( leaf_pos( m_path, m_path.size() ) ); - - if ( len > 1 // unless delimiter is part of root, don't include it -# ifdef BOOST_WINDOWS - && m_path[len-1] != ':' - && m_path[len-2] != ':' -# endif - ) --len; - - return path( m_path.substr( 0, len ), system_specific ); + inline bool is_absolute_root( const std::string & s, + std::string::size_type len ) + { + return + len && s[len-1] == '/' + && + ( + len == 1 // "/" +# ifdef BOOST_WINDOWS + || ( len > 1 + && ( s[len-2] == ':' // drive or device + || ( s[0] == '/' // share + && s[1] == '/' + && s.find( '/', 2 ) == len-1 + ) + ) + ) +# endif + ); + } } - //bool path::is_absolute() const - //{ - // return ( m_path.size() - // && m_path[0] == '/' ) // covers both "/" and "//share" - // || ( m_path.size() > 2 - // && m_path[1] == ':' - // && m_path[2] == '/' ) // "c:/" - // || ( m_path.size() > 3 - // && m_path[m_path.size()-1] == ':' ); // "device:" - //} + path path::branch_path() const + { + std::string::size_type end_pos( leaf_pos( m_path, m_path.size() ) ); - namespace detail + // skip a '/' unless it is a root directory + if ( end_pos && m_path[end_pos-1] == '/' + && !detail::is_absolute_root( m_path, end_pos ) ) --end_pos; + return path( m_path.substr( 0, end_pos ), system_specific ); + } + + bool path::is_absolute() const + { + return ( m_path.size() + && m_path[0] == '/' ) // covers both "/" and "//share" +# ifdef BOOST_WINDOWS + || ( m_path.size() > 2 + && m_path[1] == ':' + && m_path[2] == '/' ) // "c:/" + || ( m_path.size() > 3 + && m_path[m_path.size()-1] == ':' ) // "device:" +# endif + ; + } + + path path::relative_path() const + { + std::string::size_type pos( 0 ); + if ( m_path.size() && m_path[0] == '/' ) + { pos = 1; +# ifdef BOOST_WINDOWS + if ( m_path.size()>1 && m_path[1] == '/' ) // share + { + if ( (pos = m_path.find( '/', 2 )) != std::string::npos ) ++pos; + else return path(); + } + } + else if ( (pos = m_path.find( ':' )) == std::string::npos ) pos = 0; + else // has ':' + { + if ( ++pos < m_path.size() && m_path[pos] == '/' ) ++pos; +# endif + } + return path( m_path.substr( pos ) ); + } + + std::string path::system_specific_root() const + { +# ifdef BOOST_WINDOWS + std::string::size_type pos( m_path.find( ':' ) ); + if ( pos != std::string::npos ) return m_path.substr( 0, pos+1 ); + if ( m_path.size() > 2 && m_path[0] == '/' && m_path[1] == '/' ) + { + pos = m_path.find( '/', 2 ); + return m_path.substr( 0, pos ); + } +# endif + return std::string(); + } + + std::string path::root_directory() const + { + return std::string( + ( m_path.size() && m_path[0] == '/' ) // covers both "/" and "//share" +# ifdef BOOST_WINDOWS + || ( m_path.size() > 2 + && m_path[1] == ':' + && m_path[2] == '/' ) // "c:/" +# endif + ? "/" : "" ); + } + + path path::root_path() const + { + return path( +# ifdef BOOST_WINDOWS + system_specific_root(), system_specific ) /= root_directory(); +# else + root_directory() ); +# endif + } + + namespace detail { void path_itr_imp::operator++() { @@ -371,7 +479,18 @@ namespace boost name = ""; // not strictly required, but might aid debugging return; } - if ( path_ptr->m_path[pos] == '/' ) ++pos; + if ( path_ptr->m_path[pos] == '/' ) + { +# ifdef BOOST_WINDOWS + if ( name[name.size()-1] == ':' // drive or device + || (name[0] == '/' && name[1] == '/') ) // share + { + name = "/"; + return; + } +# endif + ++pos; + } std::string::size_type end_pos( path_ptr->m_path.find( '/', pos ) ); if ( end_pos == std::string::npos ) end_pos = path_ptr->m_path.size(); name = path_ptr->m_path.substr( pos, end_pos - pos ); @@ -381,13 +500,10 @@ namespace boost { assert( pos ); // detect decrement of begin std::string::size_type end_pos( pos ); - if ( end_pos != path_ptr->m_path.size() - && end_pos > 1 -# ifdef BOOST_WINDOWS - && path_ptr->m_path[end_pos-1] != ':' - && path_ptr->m_path[end_pos-2] != ':' -# endif - ) --end_pos; + + // skip a '/' unless it is a root directory + if ( path_ptr->m_path[end_pos-1] == '/' + && !detail::is_absolute_root( path_ptr->m_path, end_pos ) ) --end_pos; pos = leaf_pos( path_ptr->m_path, end_pos ); name = path_ptr->m_path.substr( pos, end_pos - pos ); } diff --git a/test/operations_test.cpp b/test/operations_test.cpp index 327bed4..c24f02a 100644 --- a/test/operations_test.cpp +++ b/test/operations_test.cpp @@ -28,21 +28,21 @@ namespace void create_file( const fs::path & ph, const std::string & contents ) { - std::ofstream f( ph.file_path().c_str() ); + std::ofstream f( ph.system_specific_file_string().c_str() ); if ( !f ) - throw fs::filesystem_error( "ofstream(): " + ph.generic_path() ); + throw fs::filesystem_error( "ofstream(): " + ph.string() ); if ( !contents.empty() ) f << contents; } void verify_file( const fs::path & ph, const std::string & expected ) { - std::ifstream f( ph.file_path().c_str() ); + std::ifstream f( ph.system_specific_file_string().c_str() ); if ( !f ) - throw fs::filesystem_error( "ifstream(): " + ph.generic_path() ); + throw fs::filesystem_error( "ifstream(): " + ph.string() ); std::string contents; f >> contents; if ( contents != expected ) - throw fs::filesystem_error("verify_file(): " + ph.generic_path() + throw fs::filesystem_error("verify_file(): " + ph.string() + " contents \"" + contents + "\" != \"" + expected + "\"" ); } @@ -70,15 +70,15 @@ int test_main( int, char * [] ) std::cout << "implemenation name: " << fs::detail::implementation_name() << "\n"; std::cout << "initial_directory() is \"" - << fs::initial_directory().generic_path() + << fs::initial_directory().string() << "\"\n"; fs::path dir( fs::initial_directory() << "temp_fs_test_directory" ); if ( std::strcmp( fs::detail::implementation_name(), "Windows" ) == 0 ) { - BOOST_TEST( dir.generic_path().size() > 1 - && dir.generic_path()[1] == ':' ); // verify path includes drive + BOOST_TEST( dir.string().size() > 1 + && dir.string()[1] == ':' ); // verify path includes drive } fs::path ng( " no-way, Jose ", fs::system_specific ); diff --git a/test/path_test.cpp b/test/path_test.cpp index dd7fcc0..6d7e09a 100644 --- a/test/path_test.cpp +++ b/test/path_test.cpp @@ -21,20 +21,21 @@ using boost::filesystem::path; using boost::next; using boost::prior; -#define BOOST_INCLUDE_MAIN -#include +#include + +#define PATH_CHECK( a, b ) check( a, b, __LINE__ ) namespace { int errors; void check( const fs::path & source, - const std::string & expected ) + const std::string & expected, int line ) { - if ( source.generic_path()== expected ) return; + if ( source.string()== expected ) return; ++errors; - std::cout << "source.generic_path(): \"" << source.generic_path() + std::cout << '(' << line << ") source.string(): \"" << source.string() << "\" != expected: \"" << expected << "\"" << std::endl; } @@ -64,91 +65,102 @@ int test_main( int, char*[] ) path p3; p3 = p2; -// p1.branch() = p2; // should fail +// p1.branch_path() = p2; // should fail // *p1.begin() = ""; // should fail // These verify various overloads don't cause compiler errors fs::exists( "foo" ); fs::exists( std::string( "foo" ) ); fs::exists( p1 ); - fs::exists( "foo" << p1 ); - fs::exists( std::string( "foo" ) << p1 ); + fs::exists( "foo" / p1 ); + fs::exists( std::string( "foo" ) / p1 ); fs::exists( fs::check_posix_leaf( "foo" ) ); - BOOST_TEST( p1.generic_path() == p2.generic_path() ); - BOOST_TEST( p1.generic_path() == p3.generic_path() ); + BOOST_TEST( p1.string() == p2.string() ); + BOOST_TEST( p1.string() == p3.string() ); BOOST_TEST( path( "foo" ).leaf() == "foo" ); - BOOST_TEST( path( "foo" ).branch().generic_path() == "" ); + BOOST_TEST( path( "foo" ).branch_path().string() == "" ); BOOST_TEST( p1.leaf() == "fum" ); - BOOST_TEST( p1.branch().generic_path() == "fe/fi/fo" ); + BOOST_TEST( p1.branch_path().string() == "fe/fi/fo" ); BOOST_TEST( path( "" ).is_null() == true ); BOOST_TEST( path( "foo" ).is_null() == false ); - check( "", "" ); + PATH_CHECK( "", "" ); - check( "foo", "foo" ); - check( path("") << "foo", "foo" ); - check( path("foo") << "", "foo" ); + PATH_CHECK( "foo", "foo" ); + PATH_CHECK( path("") / "foo", "foo" ); + PATH_CHECK( path("foo") / "", "foo" ); + PATH_CHECK( path( "/" ), "/" ); + PATH_CHECK( path( "/" ) / "", "/" ); + PATH_CHECK( path( "/f" ), "/f" ); - check( "foo/bar", "foo/bar" ); - check( path("foo") << "bar", "foo/bar" ); - check( path("foo") << path("bar"), "foo/bar" ); - check( "foo" << path("bar"), "foo/bar" ); + PATH_CHECK( "/foo", "/foo" ); + PATH_CHECK( path("") / "/foo", "/foo" ); + PATH_CHECK( path("/foo") / "", "/foo" ); - check( "a/b", "a/b" ); // probe for length effects - check( path("a") << "b", "a/b" ); + PATH_CHECK( "foo/", "foo" ); + PATH_CHECK( path("") / "foo/", "foo" ); + PATH_CHECK( path("foo") / "/", "foo" ); - check( "..", ".." ); - check( path("..") << "", ".." ); - check( path("") << "..", ".." ); + PATH_CHECK( "foo/bar", "foo/bar" ); + PATH_CHECK( path("foo") / "bar", "foo/bar" ); + PATH_CHECK( path("foo") / path("bar"), "foo/bar" ); + PATH_CHECK( "foo" / path("bar"), "foo/bar" ); - check( "../..", "../.." ); - check( path("..") << ".." , "../.." ); + PATH_CHECK( "a/b", "a/b" ); // probe for length effects + PATH_CHECK( path("a") / "b", "a/b" ); - check( "../foo", "../foo" ); - check( path("..") << "foo" , "../foo" ); + PATH_CHECK( "..", ".." ); + PATH_CHECK( path("..") / "", ".." ); + PATH_CHECK( path("") / "..", ".." ); - check( "foo/..", "" ); - check( path("foo") << ".." , "" ); + PATH_CHECK( "../..", "../.." ); + PATH_CHECK( path("..") / ".." , "../.." ); - check( "../f", "../f" ); - check( path("..") << "f" , "../f" ); + PATH_CHECK( "../foo", "../foo" ); + PATH_CHECK( path("..") / "foo" , "../foo" ); - check( "f/..", "" ); - check( path("f") << ".." , "" ); + PATH_CHECK( "foo/..", "" ); + PATH_CHECK( path("foo") / ".." , "" ); - check( "foo/../..", ".." ); - check( path("foo") << ".." << ".." , ".." ); + PATH_CHECK( "../f", "../f" ); + PATH_CHECK( path("..") / "f" , "../f" ); - check( "foo/../../..", "../.." ); - check( path("foo") << ".." << ".." << ".." , "../.." ); + PATH_CHECK( "f/..", "" ); + PATH_CHECK( path("f") / ".." , "" ); - check( "foo/../bar", "bar" ); - check( path("foo") << ".." << "bar" , "bar" ); + PATH_CHECK( "foo/../..", ".." ); + PATH_CHECK( path("foo") / ".." / ".." , ".." ); - check( "foo/bar/..", "foo" ); - check( path("foo") << "bar" << ".." , "foo" ); + PATH_CHECK( "foo/../../..", "../.." ); + PATH_CHECK( path("foo") / ".." / ".." / ".." , "../.." ); - check( "foo/bar/../..", "" ); - check( path("foo") << "bar" << ".." << "..", "" ); + PATH_CHECK( "foo/../bar", "bar" ); + PATH_CHECK( path("foo") / ".." / "bar" , "bar" ); - check( "foo/bar/../blah", "foo/blah" ); - check( path("foo") << "bar" << ".." << "blah", "foo/blah" ); + PATH_CHECK( "foo/bar/..", "foo" ); + PATH_CHECK( path("foo") / "bar" / ".." , "foo" ); - check( "f/../b", "b" ); - check( path("f") << ".." << "b" , "b" ); + PATH_CHECK( "foo/bar/../..", "" ); + PATH_CHECK( path("foo") / "bar" / ".." / "..", "" ); - check( "f/b/..", "f" ); - check( path("f") << "b" << ".." , "f" ); + PATH_CHECK( "foo/bar/../blah", "foo/blah" ); + PATH_CHECK( path("foo") / "bar" / ".." / "blah", "foo/blah" ); - check( "f/b/../a", "f/a" ); - check( path("f") << "b" << ".." << "a", "f/a" ); + PATH_CHECK( "f/../b", "b" ); + PATH_CHECK( path("f") / ".." / "b" , "b" ); - check( "foo/bar/blah/../..", "foo" ); - check( path("foo") << "bar" << "blah" << ".." << "..", "foo" ); + PATH_CHECK( "f/b/..", "f" ); + PATH_CHECK( path("f") / "b" / ".." , "f" ); - check( "foo/bar/blah/../../bletch", "foo/bletch" ); - check( path("foo") << "bar" << "blah" << ".." << ".." << "bletch", "foo/bletch" ); + PATH_CHECK( "f/b/../a", "f/a" ); + PATH_CHECK( path("f") / "b" / ".." / "a", "f/a" ); + + PATH_CHECK( "foo/bar/blah/../..", "foo" ); + PATH_CHECK( path("foo") / "bar" / "blah" / ".." / "..", "foo" ); + + PATH_CHECK( "foo/bar/blah/../../bletch", "foo/bletch" ); + PATH_CHECK( path("foo") / "bar" / "blah" / ".." / ".." / "bletch", "foo/bletch" ); BOOST_TEST( fs::posix_name("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789.-_") ); BOOST_TEST( !fs::posix_name("F$O") ); @@ -167,10 +179,7 @@ int test_main( int, char*[] ) BOOST_TEST( !fs::boost_directory_name("12345678901234567890123456789012") ); BOOST_TEST( !fs::boost_directory_name("F$O") ); - check_throw( "/" ); check_throw( "...." ); - check_throw( "/foo" ); - check_throw( "foo/" ); check_throw( "foo/...." ); check_throw( "foo//bar" ); check_throw( "foo\\bar" ); @@ -189,9 +198,8 @@ int test_main( int, char*[] ) check_throw( "//share" ); check_throw( "prn:" ); - path itr_ck( "/foo/bar", fs::system_specific ); + path itr_ck( "/foo/bar" ); path::iterator itr( itr_ck.begin() ); -//std::cout << "\"" << *itr << "\"" << std::endl; BOOST_TEST( *itr == std::string( "/" ) ); BOOST_TEST( *++itr == std::string( "foo" ) ); BOOST_TEST( *++itr == std::string( "bar" ) ); @@ -203,13 +211,13 @@ int test_main( int, char*[] ) itr_ck = ""; BOOST_TEST( itr_ck.begin() == itr_ck.end() ); - itr_ck = path( "/", fs::system_specific ); + itr_ck = path( "/" ); BOOST_TEST( *itr_ck.begin() == std::string( "/" ) ); BOOST_TEST( next(itr_ck.begin()) == itr_ck.end() ); BOOST_TEST( *prior(itr_ck.end()) == std::string( "/" ) ); BOOST_TEST( prior(itr_ck.end()) == itr_ck.begin() ); - itr_ck = path( "/foo", fs::system_specific ); + itr_ck = path( "/foo" ); BOOST_TEST( *itr_ck.begin() == std::string( "/" ) ); BOOST_TEST( *next( itr_ck.begin() ) == std::string( "foo" ) ); BOOST_TEST( next(next( itr_ck.begin() )) == itr_ck.end() ); @@ -224,8 +232,117 @@ int test_main( int, char*[] ) BOOST_TEST( *prior( itr_ck.end() ) == std::string( "foo" ) ); BOOST_TEST( prior( itr_ck.end() ) == itr_ck.begin() ); + BOOST_TEST( path( "/" ).relative_path().string() == "" ); + BOOST_TEST( path( "/" ).branch_path().string() == "" ); + BOOST_TEST( path( "/" ).leaf() == "/" ); + BOOST_TEST( path( "/" ).system_specific_root() == "" ); + BOOST_TEST( path( "/" ).root_directory() == "/" ); + BOOST_TEST( path( "/" ).root_path().string() == "/" ); + + BOOST_TEST( path( "foo" ).relative_path().string() == "foo" ); + BOOST_TEST( path( "foo" ).branch_path().string() == "" ); + BOOST_TEST( path( "foo" ).leaf() == "foo" ); + BOOST_TEST( path( "foo" ).system_specific_root() == "" ); + BOOST_TEST( path( "foo" ).root_directory() == "" ); + BOOST_TEST( path( "foo" ).root_path().string() == "" ); + + BOOST_TEST( path( "/foo" ).relative_path().string() == "foo" ); + BOOST_TEST( path( "/foo" ).branch_path().string() == "/" ); + BOOST_TEST( path( "/foo" ).leaf() == "foo" ); + BOOST_TEST( path( "/foo" ).system_specific_root() == "" ); + BOOST_TEST( path( "/foo" ).root_directory() == "/" ); + BOOST_TEST( path( "/foo" ).root_path().string() == "/" ); + + BOOST_TEST( path( "foo/bar" ).relative_path().string() == "foo/bar" ); + BOOST_TEST( path( "foo/bar" ).branch_path().string() == "foo" ); + BOOST_TEST( path( "foo/bar" ).leaf() == "bar" ); + BOOST_TEST( path( "foo/bar" ).system_specific_root() == "" ); + BOOST_TEST( path( "foo/bar" ).root_directory() == "" ); + BOOST_TEST( path( "foo/bar" ).root_path().string() == "" ); + + BOOST_TEST( path( "/foo/bar" ).relative_path().string() == "foo/bar" ); + BOOST_TEST( path( "/foo/bar" ).branch_path().string() == "/foo" ); + BOOST_TEST( path( "/foo/bar" ).leaf() == "bar" ); + BOOST_TEST( path( "/foo/bar" ).system_specific_root() == "" ); + BOOST_TEST( path( "/foo/bar" ).root_directory() == "/" ); + BOOST_TEST( path( "/foo/bar" ).root_path().string() == "/" ); + if ( std::strcmp( fs::detail::implementation_name(), "Windows" ) == 0 ) { + PATH_CHECK( path( "\\", fs::system_specific ), "/" ); + PATH_CHECK( path( "\\f", fs::system_specific ), "/f" ); + PATH_CHECK( path( "\\foo", fs::system_specific ), "/foo" ); + PATH_CHECK( path( "foo\\bar", fs::system_specific ), "foo/bar" ); + PATH_CHECK( path( "foo bar", fs::system_specific ), "foo bar" ); + PATH_CHECK( path( "c:", fs::system_specific ), "c:" ); + PATH_CHECK( path( "c:/", fs::system_specific ), "c:/" ); + PATH_CHECK( path( "c:foo", fs::system_specific ), "c:foo" ); + PATH_CHECK( path( "c:/foo", fs::system_specific ), "c:/foo" ); + PATH_CHECK( path( "//share", fs::system_specific ), "//share" ); + PATH_CHECK( path( "//share/", fs::system_specific ), "//share/" ); + PATH_CHECK( path( "//share/foo", fs::system_specific ), "//share/foo" ); + PATH_CHECK( path( "\\\\share", fs::system_specific ), "//share" ); + PATH_CHECK( path( "\\\\share\\", fs::system_specific ), "//share/" ); + PATH_CHECK( path( "\\\\share\\foo", fs::system_specific ), "//share/foo" ); + PATH_CHECK( path( "c:/foo", fs::system_specific ), "c:/foo" ); + PATH_CHECK( path( "prn:", fs::system_specific ), "prn:" ); + + BOOST_TEST( path( "c:", fs::system_specific ).relative_path().string() == "" ); + BOOST_TEST( path( "c:", fs::system_specific ).branch_path().string() == "" ); + BOOST_TEST( path( "c:", fs::system_specific ).leaf() == "c:" ); + BOOST_TEST( path( "c:", fs::system_specific ).system_specific_root() == "c:" ); + BOOST_TEST( path( "c:", fs::system_specific ).root_directory() == "" ); + BOOST_TEST( path( "c:", fs::system_specific ).root_path().string() == "c:" ); + + BOOST_TEST( path( "c:foo", fs::system_specific ).relative_path().string() == "foo" ); + BOOST_TEST( path( "c:foo", fs::system_specific ).branch_path().string() == "c:" ); + BOOST_TEST( path( "c:foo", fs::system_specific ).leaf() == "foo" ); + BOOST_TEST( path( "c:foo", fs::system_specific ).system_specific_root() == "c:" ); + BOOST_TEST( path( "c:foo", fs::system_specific ).root_directory() == "" ); + BOOST_TEST( path( "c:foo", fs::system_specific ).root_path().string() == "c:" ); + + BOOST_TEST( path( "c:/", fs::system_specific ).relative_path().string() == "" ); + BOOST_TEST( path( "c:/", fs::system_specific ).branch_path().string() == "c:" ); + BOOST_TEST( path( "c:/", fs::system_specific ).leaf() == "/" ); + BOOST_TEST( path( "c:/", fs::system_specific ).system_specific_root() == "c:" ); + BOOST_TEST( path( "c:/", fs::system_specific ).root_directory() == "/" ); + BOOST_TEST( path( "c:/", fs::system_specific ).root_path().string() == "c:/" ); + + BOOST_TEST( path( "c:/foo", fs::system_specific ).relative_path().string() == "foo" ); + BOOST_TEST( path( "c:/foo", fs::system_specific ).branch_path().string() == "c:/" ); + BOOST_TEST( path( "c:/foo", fs::system_specific ).leaf() == "foo" ); + BOOST_TEST( path( "c:/foo", fs::system_specific ).system_specific_root() == "c:" ); + BOOST_TEST( path( "c:/foo", fs::system_specific ).root_directory() == "/" ); + BOOST_TEST( path( "c:/foo", fs::system_specific ).root_path().string() == "c:/" ); + + BOOST_TEST( path( "//share", fs::system_specific ).relative_path().string() == "" ); + BOOST_TEST( path( "//share", fs::system_specific ).branch_path().string() == "" ); + BOOST_TEST( path( "//share", fs::system_specific ).leaf() == "//share" ); + BOOST_TEST( path( "//share", fs::system_specific ).system_specific_root() == "//share" ); + BOOST_TEST( path( "//share", fs::system_specific ).root_directory() == "/" ); + BOOST_TEST( path( "//share", fs::system_specific ).root_path().string() == "//share/" ); + + BOOST_TEST( path( "//share/", fs::system_specific ).relative_path().string() == "" ); + BOOST_TEST( path( "//share/", fs::system_specific ).branch_path().string() == "//share" ); + BOOST_TEST( path( "//share/", fs::system_specific ).leaf() == "/" ); + BOOST_TEST( path( "//share/", fs::system_specific ).system_specific_root() == "//share" ); + BOOST_TEST( path( "//share/", fs::system_specific ).root_directory() == "/" ); + BOOST_TEST( path( "//share/", fs::system_specific ).root_path().string() == "//share/" ); + + BOOST_TEST( path( "//share/foo", fs::system_specific ).relative_path().string() == "foo" ); + BOOST_TEST( path( "//share/foo", fs::system_specific ).branch_path().string() == "//share/" ); + BOOST_TEST( path( "//share/foo", fs::system_specific ).leaf() == "foo" ); + BOOST_TEST( path( "//share/foo", fs::system_specific ).system_specific_root() == "//share" ); + BOOST_TEST( path( "//share/foo", fs::system_specific ).root_directory() == "/" ); + BOOST_TEST( path( "//share/foo", fs::system_specific ).root_path().string() == "//share/" ); + + BOOST_TEST( path( "prn:", fs::system_specific ).relative_path().string() == "" ); + BOOST_TEST( path( "prn:", fs::system_specific ).branch_path().string() == "" ); + BOOST_TEST( path( "prn:", fs::system_specific ).leaf() == "prn:" ); + BOOST_TEST( path( "prn:", fs::system_specific ).system_specific_root() == "prn:" ); + BOOST_TEST( path( "prn:", fs::system_specific ).root_directory() == "" ); + BOOST_TEST( path( "prn:", fs::system_specific ).root_path().string() == "prn:" ); + itr_ck = path( "c:", fs::system_specific ); BOOST_TEST( *itr_ck.begin() == std::string( "c:" ) ); BOOST_TEST( next( itr_ck.begin() ) == itr_ck.end() ); @@ -233,10 +350,12 @@ int test_main( int, char*[] ) BOOST_TEST( *prior( itr_ck.end() ) == std::string( "c:" ) ); itr_ck = path( "c:/", fs::system_specific ); - BOOST_TEST( *itr_ck.begin() == std::string( "c:/" ) ); - BOOST_TEST( next( itr_ck.begin() ) == itr_ck.end() ); - BOOST_TEST( prior( itr_ck.end() ) == itr_ck.begin() ); - BOOST_TEST( *prior( itr_ck.end() ) == std::string( "c:/" ) ); + BOOST_TEST( *itr_ck.begin() == std::string( "c:" ) ); + BOOST_TEST( *next( itr_ck.begin() ) == std::string( "/" ) ); + BOOST_TEST( next( next( itr_ck.begin() )) == itr_ck.end() ); + BOOST_TEST( prior( prior( itr_ck.end() )) == itr_ck.begin() ); + BOOST_TEST( *prior( itr_ck.end() ) == std::string( "/" ) ); + BOOST_TEST( *prior( prior( itr_ck.end() )) == std::string( "c:" ) ); itr_ck = path( "c:foo", fs::system_specific ); BOOST_TEST( *itr_ck.begin() == std::string( "c:" ) ); @@ -247,12 +366,14 @@ int test_main( int, char*[] ) BOOST_TEST( *prior(prior( itr_ck.end() )) == std::string( "c:" ) ); itr_ck = path( "c:/foo", fs::system_specific ); - BOOST_TEST( *itr_ck.begin() == std::string( "c:/" ) ); - BOOST_TEST( *next( itr_ck.begin() ) == std::string( "foo" ) ); - BOOST_TEST( next(next( itr_ck.begin() )) == itr_ck.end() ); - BOOST_TEST( prior(prior( itr_ck.end() )) == itr_ck.begin() ); + BOOST_TEST( *itr_ck.begin() == std::string( "c:" ) ); + BOOST_TEST( *next( itr_ck.begin() ) == std::string( "/" ) ); + BOOST_TEST( *next( next( itr_ck.begin() )) == std::string( "foo" ) ); + BOOST_TEST( next( next( next( itr_ck.begin() ))) == itr_ck.end() ); + BOOST_TEST( prior( prior( prior( itr_ck.end() ))) == itr_ck.begin() ); BOOST_TEST( *prior( itr_ck.end() ) == std::string( "foo" ) ); - BOOST_TEST( *prior(prior( itr_ck.end() )) == std::string( "c:/" ) ); + BOOST_TEST( *prior( prior( itr_ck.end() )) == std::string( "/" ) ); + BOOST_TEST( *prior( prior( prior( itr_ck.end() ))) == std::string( "c:" ) ); itr_ck = path( "//share", fs::system_specific ); BOOST_TEST( *itr_ck.begin() == std::string( "//share" ) ); @@ -260,54 +381,29 @@ int test_main( int, char*[] ) BOOST_TEST( prior( itr_ck.end() ) == itr_ck.begin() ); BOOST_TEST( *prior( itr_ck.end() ) == std::string( "//share" ) ); - itr_ck = path( "//share/foo", fs::system_specific ); + itr_ck = path( "//share/", fs::system_specific ); BOOST_TEST( *itr_ck.begin() == std::string( "//share" ) ); - BOOST_TEST( *next( itr_ck.begin() ) == std::string( "foo" ) ); + BOOST_TEST( *next( itr_ck.begin() ) == std::string( "/" ) ); BOOST_TEST( next(next( itr_ck.begin() )) == itr_ck.end() ); BOOST_TEST( prior(prior( itr_ck.end() )) == itr_ck.begin() ); - BOOST_TEST( *prior( itr_ck.end() ) == std::string( "foo" ) ); + BOOST_TEST( *prior( itr_ck.end() ) == std::string( "/" ) ); BOOST_TEST( *prior(prior( itr_ck.end() )) == std::string( "//share" ) ); + itr_ck = path( "//share/foo", fs::system_specific ); + BOOST_TEST( *itr_ck.begin() == std::string( "//share" ) ); + BOOST_TEST( *next( itr_ck.begin() ) == std::string( "/" ) ); + BOOST_TEST( *next(next( itr_ck.begin() )) == std::string( "foo" ) ); + BOOST_TEST( next(next(next( itr_ck.begin() ))) == itr_ck.end() ); + BOOST_TEST( prior(prior(prior( itr_ck.end() ))) == itr_ck.begin() ); + BOOST_TEST( *prior( itr_ck.end() ) == std::string( "foo" ) ); + BOOST_TEST( *prior(prior( itr_ck.end() )) == std::string( "/" ) ); + BOOST_TEST( *prior(prior(prior( itr_ck.end() ))) == std::string( "//share" ) ); + itr_ck = path( "prn:", fs::system_specific ); BOOST_TEST( *itr_ck.begin() == std::string( "prn:" ) ); BOOST_TEST( next( itr_ck.begin() ) == itr_ck.end() ); BOOST_TEST( prior( itr_ck.end() ) == itr_ck.begin() ); BOOST_TEST( *prior( itr_ck.end() ) == std::string( "prn:" ) ); - - check( path( "/", fs::system_specific ), "/" ); - check( path( "/f", fs::system_specific ), "/f" ); - check( path( "/foo", fs::system_specific ), "/foo" ); - check( path( "\\", fs::system_specific ), "/" ); - check( path( "\\f", fs::system_specific ), "/f" ); - check( path( "\\foo", fs::system_specific ), "/foo" ); - check( path( "foo\\bar", fs::system_specific ), "foo/bar" ); - check( path( "foo bar", fs::system_specific ), "foo bar" ); - check( path( "c:", fs::system_specific ), "c:" ); - check( path( "c:/", fs::system_specific ), "c:/" ); - check( path( "c:foo", fs::system_specific ), "c:foo" ); - check( path( "c:/foo", fs::system_specific ), "c:/foo" ); - check( path( "//share", fs::system_specific ), "//share" ); - check( path( "//share/foo", fs::system_specific ), "//share/foo" ); - check( path( "\\\\share", fs::system_specific ), "//share" ); - check( path( "\\\\share\\foo", fs::system_specific ), "//share/foo" ); - check( path( "c:/foo", fs::system_specific ), "c:/foo" ); - check( path( "prn:", fs::system_specific ), "prn:" ); - - BOOST_TEST( path( "/", fs::system_specific ).leaf() == "/" ); - BOOST_TEST( path( "c:", fs::system_specific ).leaf() == "c:" ); - BOOST_TEST( path( "c:/", fs::system_specific ).leaf() == "c:/" ); - BOOST_TEST( path( "c:foo", fs::system_specific ).leaf() == "foo" ); - BOOST_TEST( path( "c:/foo", fs::system_specific ).leaf() == "foo" ); - BOOST_TEST( path( "//share", fs::system_specific ).leaf() == "//share" ); - BOOST_TEST( path( "//share/foo", fs::system_specific ).leaf() == "foo" ); - - BOOST_TEST( path( "/", fs::system_specific ).branch().generic_path() == "" ); - BOOST_TEST( path( "c:", fs::system_specific ).branch().generic_path() == "" ); - BOOST_TEST( path( "c:/", fs::system_specific ).branch().generic_path() == "" ); - BOOST_TEST( path( "c:foo", fs::system_specific ).branch().generic_path() == "c:" ); - BOOST_TEST( path( "c:/foo", fs::system_specific ).branch().generic_path() == "c:/" ); - BOOST_TEST( path( "//share", fs::system_specific ).branch().generic_path() == "" ); - BOOST_TEST( path( "//share/foo", fs::system_specific ).branch().generic_path() == "//share" ); } // std::cout << errors << " errors detected\n";