diff --git a/build/Jamfile b/build/Jamfile index 29cf315..86b10b8 100644 --- a/build/Jamfile +++ b/build/Jamfile @@ -9,14 +9,13 @@ subproject libs/filesystem/build ; -SOURCES = exception operations_posix_windows path_posix_windows convenience ; +SOURCES = exception operations path portability utf8_codecvt_facet ; lib boost_filesystem : ../src/$(SOURCES).cpp : # build requirements BOOST_FILESYSTEM_STATIC_LINK $(BOOST_ROOT) $(BOOST_ROOT) - exception.cpp operations_posix_windows.cpp # common-variant-tag ensures that the library will # be named according to the rules used by the install # and auto-link features: @@ -30,7 +29,6 @@ dll boost_filesystem BOOST_FILESYSTEM_DYN_LINK=1 # tell source we're building dll's dynamic # build only for dynamic runtimes $(BOOST_ROOT) $(BOOST_ROOT) - exception.cpp operations_posix_windows.cpp # common-variant-tag ensures that the library will # be named according to the rules used by the install # and auto-link features: diff --git a/doc/convenience.htm b/doc/convenience.htm index 203af77..3c2319a 100644 --- a/doc/convenience.htm +++ b/doc/convenience.htm @@ -6,8 +6,6 @@ - - Boost Filesystem convenience.hpp Header @@ -16,24 +14,29 @@

boost/filesystem/convenience.hpp

+

Contents

+ +
+
Introduction
create_directories +
extension +
basename +
change_extension
+basic_recursive_directory_iterator
+ +

Introduction

+

Header convenience.hpp provides convenience functions that combine lower-level functions in useful ways.

-

Contents

+

The entire contents of the header is in namespace boost::filesystem.

-
-
create_directories -
extension -
basename -
change_extension -
- -

create_directories

+

create_directories

-

bool create_directories( const path & ph );

+

template<class Path>
+bool create_directories( const Path & ph );

Precondition: ph.empty() ||
forall p: p == ph || is_parent(p, ph): is_directory(p) || !exists( p )
@@ -41,7 +44,7 @@ forall p: p == ph || is_parent(p, ph): is_directory(p) || !exists( p )

Returns: The value of !exists(ph) prior to the establishment of the postcondition.

-

Postcondition: exists(ph) && is_directory(ph)

+

Postcondition: is_directory(ph)

Throws: exists(ph) && !is_directory(ph)

@@ -49,20 +52,21 @@ establishment of the postcondition.

-

extension

+

extension

-

std::string extension( const path & ph );

+

template<class Path>
+typename Path::string_type extension( const Path & ph );

Returns: if ph.leaf() contains a dot ('.'), returns the substring of ph.leaf() starting from the last dot and -ending at the string's end. Otherwise, returns empty string. +ending at the string's end. Otherwise, returns an empty string.

Rationale:

  • The dot is included in the return value so that it's possible to tell if extension is empty or absent.
  • It was noted that this definition of extension is probably not sufficient -when using Alternate Data Streams — +when using Alternate Data Streams — a filesystem feature specific to NTFS. However, semantics in this case were not clear, and the current behavior is still quite useful.
@@ -73,11 +77,12 @@ discussed the ADS issue.
-

basename

+

basename

-

std::string basename( const path & ph );

+

template<class Path>
+typename Path::string_type basename( const Path & ph );

Returns: if ph.leaf() contains a dot ('.'), returns the substring of ph.leaf() starting from beginning and @@ -90,11 +95,12 @@ ending at the last dot (the dot is not included). Otherwise, returns

-

change_extension

+

change_extension

-

path change_extension( const path & ph, const std::string & new_extension );

+

template<class Path>
+Path change_extension( const Path & ph, const typename Path::string_type & new_extension );

Postcondition: basename(return_value) == basename(ph) && extension(return_value) == new_extension @@ -114,10 +120,85 @@ is to drop the precondition.

+

basic_recursive_directory_iterator

+ +
template< class Path >
+class basic_recursive_directory_iterator
+  : public boost::iterator_facade<
+      basic_recursive_directory_iterator<Path>, Path,
+      boost::single_pass_traversal_tag >
+{
+public:
+  typedef Path path_type;
+
+  basic_recursive_directory_iterator(){} // creates the "end" iterator
+
+  explicit basic_recursive_directory_iterator( const Path & dir_path );
+
+  int level() const;
+
+  void pop();
+  void no_push();
+
+  status_flags status( system_error_type * ec=0 ) const;
+  status_flags status( const symlink_t &, system_error_type * ec=0 ) const;
+
+  bool exists() const;
+  bool is_directory() const;
+  bool is_file() const;
+  bool is_other() const;
+  bool is_symlink() const;
+
+private:
+  int m_level; // for exposition only
+};
+
+typedef basic_recursive_directory_iterator<path> recursive_directory_iterator;
+typedef basic_recursive_directory_iterator<wpath> wrecursive_directory_iterator;
+ +

The behavior of basic_recursive_directory_iterator member functions is +the same as basic_directory_iterator +functions of the same name, except:

+
    +
  • When an iterator is constructed, m_level is set to 0;
  • +
  • When an iterator it for which it.is_directory() + is true is incremented, ++m_level, the directory is visited, and + its contents recursively iterated over, unless no_push() was + called prior to incrementing.
  • +
  • When an iterator reaches the end of the directory currently being iterated + over, or when pop() is called, --m_level, and + iteration continues with the parent directory, until the dir_path + directory is reached.
  • +
  • level() returns m_level.
  • +
  • level(), pop(), and no_push() all + have the precondition that the iterator not be the end iterator.
  • +
+

The issue of duplicates (caused by symlinks and hard links) should be +consider when using basic_recursive_directory_iterator. + Both duplicate detecting and non-detecting are needed, depending on the +application. Non-detecting is far + more efficient, but some apps will require duplicate detection.

+

When duplicate prevention is required, consider simply not following symlinks. +Use the no_push() function to avoid recursion into directories +reached by symlinks. If a more comprehensive solution is required, consider +these factors:

+
    +
  • Names are useless for duplicate detection because of links and other file + system alias mechanisms such as drive mapping, mounts.
  • +
  • inodes are unstable if handles are not held open, and it isn't + feasible to hold unlimited number of handles open.
  • +
  • Size, dates, etc., are subject to race conditions.
  • +
  • No one method is likely to serve all needs, but a starting point might + be to keep a hash-table keyed on a size/date based signature, then do + equivalent() on all equal keys.
  • +
+ +

Contributed by Beman Dawes


Revised -11 March, 2004

-

© Copyright Vladimir Prus, 2003

+12 July, 2005

+

© Copyright Beman Dawes, 2005
+© Copyright Vladimir Prus, 2003

Use, modification, and distribution are subject to the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at diff --git a/doc/design.htm b/doc/design.htm index 4fb47ca..ccf0774 100644 --- a/doc/design.htm +++ b/doc/design.htm @@ -84,7 +84,8 @@ of choice..

  • Interface smoothly with current C++ Standard Library input/output - facilities.  For example, paths should work in std::basic_fstream constructors.
    + facilities.  For example, paths should be + easy to use in std::basic_fstream constructors.

    Rationale: One of the most common uses of file system functionality is to manipulate paths for eventual use in input/output operations.  @@ -259,6 +260,9 @@ operation specific runtime policies, they were all considered, often implemented, and ultimately abandoned as far too complicated for any small benefits observed.

    +

    Additional design considerations apply to +Internationalization.

    +

    error checking

    A number of designs for the error checking machinery were abandoned, some @@ -279,30 +283,60 @@ work correctly on their choice of target systems.

    References

    -

    [IBM-01] IBM Corporation, z/OS V1R3.0 C/C++ Run-Time + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    [IBM-01]IBM Corporation, z/OS V1R3.0 C/C++ Run-Time Library Reference, SA22-7821-02, 2001, -http://www-1.ibm.com/servers/eserver/zseries/zos/bkserv/

    - -

    [ISO-9660] International Standards Organization, 1988.

    - -

    [MSDN] Microsoft Platform SDK for Windows, Storage Start + www-1.ibm.com/servers/eserver/zseries/zos/bkserv/

    [ISO-9660]International Standards Organization, 1988
    [Kuhn]UTF-8 and Unicode FAQ for Unix/Linux, + + www.cl.cam.ac.uk/~mgk25/unicode.html
    [MSDN] Microsoft Platform SDK for Windows, Storage Start Page, -http://msdn.microsoft.com/library/en-us/fileio/base/storage_start_page.asp

    - -

    [POSIX-01] IEEE Std 1003.1-2001/ISO/IEC 9945:2002 , - -http://www.unix-systems.org/version3/. The ISO JTC1/SC22/WG15 - POSIX -homepage is -http://std.dkuug.dk/JTC1/SC22/WG15/.

    - -

    [URI] RFC-2396, Uniform Resource Identifiers (URI): Generic + msdn.microsoft.com/library/en-us/fileio/base/storage_start_page.asp

    [POSIX-01]IEEE Std 1003.1-2001, ISO/IEC 9945:2002, and The Open Group Base Specifications, Issue 6. Also known as The + Single Unix® Specification, Version 3. + Available from each of the organizations involved in its creation. For + example, read online or download from + + www.unix.org/single_unix_specification/. The ISO JTC1/SC22/WG15 - POSIX +homepage is + www.open-std.org/jtc1/sc22/WG15/
    [URI]RFC-2396, Uniform Resource Identifiers (URI): Generic Syntax, -http://www.ietf.org/rfc/rfc2396.txt

    - -

    [Wulf-Shaw-73] William Wulf, Mary Shaw, Global -Variable Considered Harmful, ACM SIGPLAN Notices, 8, 2, 1973, pp. 23-34

    + www.ietf.org/rfc/rfc2396.txt
    [UTF-16]Wikipedia, UTF-16, + + en.wikipedia.org/wiki/UTF-16
    [Wulf-Shaw-73]William Wulf, Mary Shaw, Global +Variable Considered Harmful, ACM SIGPLAN Notices, 8, 2, 1973, pp. 23-34


    Revised diff --git a/doc/do-list.htm b/doc/do-list.htm index dd1a695..f20620e 100644 --- a/doc/do-list.htm +++ b/doc/do-list.htm @@ -2,44 +2,64 @@

    Filesystem Do-list

    +

    Current

      -
    • Windows: Some files seem to be poisoned to the point where you can't even - do is_directory() on them without an access error. For example,  pagefile.sys. Should directory_iterator ignore these files?
    • -
    • Windows: //share style paths need more analysis and test cases for system_complete() - and complete(). Also, path_test cases at line 410 need review, correction.
    • -
    • Add a path::swap member function guaranteed not to throw. (Geurt Vos).
    • -
    • Windows: What happens when a directory_itorator encounters a - wide-character filename? Write a test case.
    • +
    • Mini-review
    +

    Defer until after mini-review

      -
    • Add "." current directory to generic path? Add test cases one - way or the other. Change simple_ls accordingly.
    • -
    • Ask for help porting to other operating systems, such as the Mac, etc.
    • +
    • Add test cases and docs for Windows \\?\ and + \\?\UNC\ naming conventions.
    • +
    • Should operations.cpp assert or BOOST_ASSERT on more preconditions?
    • +
    • Apply PJP's Wide/Narrow conversion proposal to traits, once he stabilizes it.
    • +
    • Glob syntax -> regex syntax converter. See + Rich Johnson's + lists.boost.org/boost-users/2004/01/5770.php, and John Maddock's + lists.boost.org/boost-users/2004/01/5770.php
    • +
    • Add a path::swap member function guaranteed not to throw? (Geurt Vos).
    • +
    • Add an operations function based on statvfs/GetDiskFreeSpaceEx? (Steve Hartmann/Thomas Matelich)
    • +
    • Add basic_path constructor from InputIterator (Adrian Martin). Note + requirement that InputIterator value type be convertible to + basic_path::value_type.
    • +
    • Issue (Adrian Martin): >Comparison between paths doesn't use locale.
      + >
      + >The paths are not treated as plain characters since a lexicographical
      + >compare is made. But the individual elements are treated
      + >as plain characters and the internal string type's operator< is used. This +
      + >is a bit contradicting.
      + >
      + >I think the comparison operators should compare the elements in a locale
      + >dependent way.
      +
      + I've opened this as an issue. It needs to be answered in the context of what + comparison is used for. Remember that equivalence should be determined by the + equivalent() function. An important use of operator< is when path is used as a + map or set key. Not sure how locale impacts that.
    • +
    • Move canonize() and normalize() to convenience.hpp
    • +
    • Need wording to the effect that basic_path::iterators are invalidated in + the basic_path object's lifetime ends.
    • +
    • Move name checks to convenience.hpp.
    • +
    • Move rename_all to convenience.hpp.
    • +
    • Templatize name checks.
    • +
    • Provide path_checks (which in turn use name checks)?
    • +
    • It should be possible with two argument operational functions for each + argument type to be a different basic_path type. Is that the real need? The + real need might be just to be able to rename.
        +
      • Work out use cases. Windows. POSIX. On POSIX does renaming from one + multi-byte encoding to another work?
      • +
      • Implement
      • +
      • Test
      • +
      • Document
      • +
      • Change proposal accordingly
      -
        -
      • Finish the probe program, and ask Boost people to run it on various O/S's.
      • -
      • Finish portability guide and checking functions. Get opinions on default, Boost, and other error checks.  POSIX?  - Windows? Mac?  ISO 6990? Document the checking functions.
      • -
      • Operations_test line 171 - why only check iterator tag? Why not - Assignable, etc?
      • -
      • The wrapped fstream functions which return the type of the stream (*this) - would have to be overridden to get the wrapped type (boost::filesystem::ifstream - rather than std::ifstream, for example). But does it matter? Does anyone care? - Will any programs fail?
      • +

      Revised -02 October, 2003

      +28 July, 2005

      © Copyright Beman Dawes, 2002

      Use, modification, and distribution are subject to the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at -www.boost.org/LICENSE_1_0.txt)

      +www.boost.org/LICENSE_1_0.txt)

      \ No newline at end of file diff --git a/doc/exception.htm b/doc/exception.htm index 99c43cd..cee4968 100644 --- a/doc/exception.htm +++ b/doc/exception.htm @@ -11,17 +11,22 @@

      -boost/filesystem/exception.hpp

      +Error +Reporting

      Introduction
      Synopsis
      Member functions
      +Non-member functions
      +    lookup_error_code
      +    system_message
      Acknowledgements

      Introduction

      -

      The header provides class filesystem_error, publicly derived from -std::runtime_error, which is used by functions in the Filesystem Library to +

      The boost/filesystem/path.hpp header provides +various error reporting facilities, including class template basic_filesystem_error, publicly derived from +std::exception. These facilities are used by functions in the Filesystem Library to report operational errors.

      The design evolved based on user requests to ease portability and @@ -33,138 +38,67 @@ Error and Exception Handling guidelines.

      { namespace filesystem { - enum error_code - { - no_error = 0, - system_error, // system generated error; if possible, is translated - // to one of the more specific errors below. - other_error, // library generated error - security_error, // includes access rights, permissions failures - read_only_error, - io_error, - path_error, - not_found_error, - not_directory_error, - busy_error, // implies trying again might succeed - already_exists_error, - not_empty_error, - is_directory_error, - out_of_space_error, - out_of_memory_error, - out_of_resource_error - }; + typedef int errno_type; // determined by C standard; POSIX defers to C standard + typedef implementation-defined system_error_type; - class filesystem_error : public std::exception + errno_type lookup_error_code( system_error_type sys_err_code ); + + void system_message( system_error_type sys_err_code, std::string & target ); + + template<class Path> + class basic_filesystem_error : public std::exception { public: + // compiler generates copy constructor and copy assignment - filesystem_error( - const std::string & who, - const std::string & message ); + typedef Path path_type; - filesystem_error( - const std::string & who, - const path & path1, - const std::string & message, - error_code ec = other_error ); + basic_filesystem_error( const std::string & what, system_error_type se ); + basic_filesystem_error( const std::string & what, const path & path1, system_error_type se ); + basic_filesystem_error( const std::string & what, const path & path1, + const path & path2, system_error_type se ); - filesystem_error( - const std::string & who, - const path & path1, - sys_err sys_err_code ); + ~basic_filesystem_error() throw(); - filesystem_error( - const std::string & who, - const path & path1, - const path & path2, - sys_err sys_err_code ); + virtual const char * what() const throw(); - ~filesystem_error() throw(); - - virtual const char * what() const throw(); - - sys_err native_error() const; - error_code error() const; - const std::string & who() const; + system_error_type system_error() const; const path & path1() const; const path & path2() const; }; + + typedef basic_filesystem_error<path> filesystem_error; + typedef basic_filesystem_error<wpath> wfilesystem_error; + } // namespace filesystem } // namespace boost -

      For POSIX and Windows, sys_err is int. For -other operating systems, it is implementation defined.

      +

      For POSIX and Windows, system_error_type is int.

      Member functions

      Constructors

      -
            filesystem_error(
      -        const std::string & who,
      -        const std::string & message );
      -
      -      filesystem_error(
      -        const std::string & who,
      -        const path & path1,
      -        const std::string & message,
      -        error_code ec = other_error );
      -
      -      filesystem_error(
      -        const std::string & who,
      -        const path & path1,
      -        sys_err sys_err_code );
      -
      -      filesystem_error(
      -        const std::string & who,
      -        const path & path1,
      -        const path & path2,
      -        sys_err sys_err_code );
      -
      +
           basic_filesystem_error( const std::string & what, system_error_type se );
      +     basic_filesystem_error( const std::string & what, const path & path1, system_error_type se );
      +     basic_filesystem_error( const std::string & what, const path & path1,
      +        const path & path2, system_error_type se );
      -

      Precondition: The who argument is in the form, as - appropriate:

      -
        -
      • boost::filesystem::class-name::function-name for errors from public - member functions.
      • -
      • boost::filesystem::class-name for errors not identified with a - particular member function.
      • -
      • boost::filesystem::function-name for errors from non-member functions.
      • -
      -

      These forms are explicitly specified to ensure portability of user programs - between library implementations.

      -

      Effects: Constructs a filesystem_error object, initialized +

      Effects: Constructs a basic_filesystem_error object, initialized from the appropriate arguments.

      what

      virtual const char * what() const throw();

      -

      Returns: A string identifying the error, including who(), path1(), +

      Returns: A string identifying the error, including path1(), path2(), and related messages. If an error occurs in the preparation of the string, particularly in low-memory situations, an implementation is permitted to return a simpler static string.

      -

      native_error

      +

      system_error

      -

      sys_err native_error() const;

      -

      Returns: The sys_err_code argument to the constructor, - if any. Otherwise, 0.

      -
      -

      error

      -
      -
      error_code error() const;
      -

      Returns: native_error() translated to - error_code. The translation is - implementation-defined. For the POSIX and Windows implementations, see - libs/filesystem/src/exception.cpp.

      -
      - -

      who

      -
      -
      const std::string & who() const;
      - -

      Returns: The who argument to the constructor. An -implementation is permitted to return an empty string if an exception, for -example,  std::bad_alloc,  occurs during processing.

      +

      system_error_type system_error() const;

      +

      Returns: The se argument to the constructor.

      path1

      @@ -173,7 +107,7 @@ example,  std::bad_alloc,  occurs during processing.

      Returns: The path1 argument to the constructor, if any, otherwise path(). An implementation is permitted to return an empty -path if an exception, for example, std::bad_alloc,  occurs during +path if an exception, for example, std::bad_alloc, occurs during processing.

@@ -183,17 +117,40 @@ processing.

Returns: The path2 argument to the constructor, if any, otherwise path(). An implementation is permitted to return an empty -path if an exception, for example, std::bad_alloc,  occurs during +path if an exception, for example, std::bad_alloc, occurs during processing.

+

Non-member functions

+ +

lookup_error_code

+ +
+
errno_type lookup_error_code( system_error_type sys_err_code );
+

Returns: A value + corresponding to sys_err_code. Macros for errno_type values are + defined in <boost/filesystem/cerrno.hpp>, + and are described in [POSIX-01] under + + <errno.h>..

+
+ +

system_message

+ +
+
void system_message( system_error_type sys_err_code, std::string & target );
+

Effects: Appends an operating system message corresponding to + sys_err_code to target.

+
+

Acknowledgements

Peter Dimov patiently identified requirements for portability and -internationalization of error messages.

+internationalization of error messages. He also suggested basing the portable +error codes on POSIX.


Revised -28 February, 2005

+21 June, 2005

© Copyright Beman Dawes, 2002

Use, modification, and distribution are subject to the Boost Software diff --git a/doc/faq.htm b/doc/faq.htm index d8f77a6..378d3de 100644 --- a/doc/faq.htm +++ b/doc/faq.htm @@ -5,7 +5,7 @@ Filesystem FAQ

Why base the generic-path string format on POSIX?

-

POSIX is the basis for the most familiar path-string formats, including the +

[POSIX-01] is the basis for the most familiar path-string formats, including the URL portion of URI's and the native Windows format. It is ubiquitous and familiar.  On many systems, it is very easy to implement because it is either the native operating system format (Unix and Windows) or via a @@ -28,19 +28,16 @@ readability. There was no apparent upside benefit.

absolute on some systems?) by programmers used to single-rooted filesystems. Using an unfamiliar name for the concept and related functions causes programmers to read the specs rather than just assuming the meaning is known.

-

Why do some function names have a "native_" prefix?

-

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

-

Why not support a concept of specific kinds of file systems, such as -posix_file_system or windows_file_system?

+

Why not support a concept of specific kinds of file systems, such as posix_file_system or windows_file_system.

Portability is one of the one or two most important requirements for the library. Gaining some advantage by using features specific to particular operating systems is not a requirement. There doesn't appear to be much need for the ability to manipulate, say, a classic Mac OS path while running on an OpenVMS machine.

-

Furthermore, concepts like "posix_file_system" -are very slippery. What happens when a NTFS or ISO 9660 file system is mounted -in directory on a machine running a POSIX-like operating system, for example?

+

Furthermore, concepts like "file system" +are very slippery. What happens when a NTFS or FAT file system is mounted +in directory on a machine running a POSIX-like operating system, for example? +Some of the POSIX API's may return very un-POSIX like results.

Why not supply a 'handle' type, and let the file and directory operations traffic in it?

It isn't clear there is any feasible way to meet the "portable script-style @@ -101,19 +98,29 @@ acceptable and less likely to be used incorrectly.

extended attempt to add convenience functions on top of, or as a replacement for, the low-level functionality failed because there is no widely acceptable set of simple semantics for most convenience functions considered.  -Attempts to provide alternate semantics, via either run-time options or -compile-time polices, became overly complicated in relation to the value +Attempts to provide alternate semantics via either run-time options or +compile-time polices became overly complicated in relation to the value delivered, or became contentious.  OTOH, the specific functionality needed for several trial applications was very easy for the user to construct from the lower-level -toolkit functions.  See Failed Attempts.

+toolkit functions.  See Failed +Attempts.

Isn't it inconsistent then to provide a few convenience functions?

Yes, but experience with both this library, POSIX, and Windows indicates the utility of certain convenience functions, and that it is possible to provide simple, yet widely acceptable, semantics for them. For example, remove_all.

+

Why are there basic_directory_iterator<> overloads for operations.hpp +predicate functions? Isn't two ways to do the same thing poor design?

+

Yes, two ways to do the same thing is often a poor design practice. But the +iterator versions are often much more efficient. Calling status() during +iteration over a directory containing 15,000 files took 6 seconds for the path +overload, and 1 second for the iterator overload, for tests on a freshly booted +machine. Times were .90 seconds and .30 seconds, for tests after prior use of +the directory. This performance gain is large enough to justify deviating from +preferred design practices. Neither overload alone meets all needs.

Why are library functions so picky about errors?

Safety. The default is to be safe rather than sorry. This is particularly important given the reality that on many computer systems files and directories -are globally shared resources, and thus subject to +are globally shared resources, and thus subject to unexpected errors.

Why are errors reported by exception rather than return code or error notification variable?

@@ -122,9 +129,9 @@ by programmers.  Exceptions are much harder to ignore, provided desired default behavior (program termination) if not caught, yet allow error recovery if desired.

Why are attributes accessed via named functions rather than property maps?

-

For a few commonly used attributes (existence, directory or file, emptiness), +

For commonly used attributes (existence, directory or file, emptiness), simple syntax and guaranteed presence outweigh other considerations. Because -access to virtually all other attributes is inherently system dependent, +access to many other attributes is inherently system dependent, property maps are viewed as the best hope for access and modification, but it is better design to provide such functionality in a separate library. (Historical note: even the apparently simple attribute "read-only" turned out to be so @@ -133,39 +140,10 @@ system depend as to be disqualified as a "guaranteed presence" operati

Global variables are considered harmful [wulf-shaw-73]. While we can't prevent people from shooting themselves in the foot, we aren't about to hand them a loaded gun pointed right at their big toe.

-

Why aren't there query functions for compound conditions like existing_directory?

-

After several attempts, named queries for multi-attribute proved a -slippery-slope; where do you stop?

Why aren't wide-character names supported? Why not std::wstring or even a templated type?

-

Wide-character names would provide an illusion of portability where -portability does not in fact exist. Behavior would be completely different on -operating systems (Windows, for example) that support wide-character names, than -on systems which don't (POSIX). Providing functionality that appears to provide -portability but in fact delivers only implementation-defined behavior is highly -undesirable. Programs would not even be portable between library implementations -on the same operating system, let alone portable to different operating systems.

-

The C++ standards committee Library Working Group discussed this in some -detail both on the committee's library reflector and at the Spring, 2002,  meeting, and feels that (1) names based on types other than char are -extremely non-portable, (2) there are no agreed upon semantics for conversion -between wide-character and narrow-character names for file systems which do not support -wide-character name, and -(3) even the committee members most interested in wide-character names are -unsure that they are a good idea in the context of a portable library.

-

[October, 2002 - PJ Plauger has suggested a  locale based conversion -scheme. Others have indicated support for such an experiment.]

-

Why are file and directory name portability errors detected automatically -when these aren't actually errors in some applications?

-

For many uses, automatic portability error detection based on the generic -path grammar is a sensible default. For cases where some other error check -(including no check at all) is preferred  for the entire application, -functionality is provided to change the default. For cases where some other -error check (including no check at all) is preferred  for a particular -path, the error check can be specified in the path constructor.

-

The error checking functions call also be explicitly called. That provides -yet another way to check for errors.

-

The design makes error checking easy and automatic for common cases, yet -provides explicit control when that is required.

+

They are supported, starting with version 1.33. See +Internationalization.

Why isn't more powerful name portability error detection provided, such as deferring checking until a path is actually used?

A number (at least six) of designs for name validity error @@ -177,7 +155,7 @@ isn't important enough to justify added a lot of additional complexity.

Why are paths sometimes manipulated by member functions and sometimes by non-member functions?

The design rule is that purely lexical operations are supplied as class -path member +basic_path member functions, while operations performed by the operating system are provided as free functions.

Why is path normalized form different diff --git a/doc/fstream.htm b/doc/fstream.htm index 0983a42..9c620b5 100644 --- a/doc/fstream.htm +++ b/doc/fstream.htm @@ -21,8 +21,13 @@ times.

The Filesystem Library's fstream.hpp header provides equivalent components, in namespace -boost::filesystem, except that the seven  -const char* arguments have been replaced by const path& arguments.

+boost::filesystem, except that the seven functions taking  +const char* arguments have been replaced by functions with +basic_path arguments. A +"do-the-right-thing" rule +applies, so that path, wpath, and user-defined basic_path's +work appropriately. For ease of exposition, only the templated form is shown in +the following synopsis and documentation.

The Filesystem Library's fstream.hpp header simply uses the <fstream> standard library components as base classes, and then redeclares constructors @@ -44,7 +49,8 @@ examples.

public: virtual ~basic_filebuf() {} - std::basic_filebuf<charT,traits> * open( const path & file_ph, + template<class Path> + std::basic_filebuf<charT,traits> * open( const Path & file_ph, std::ios_base::openmode mode ); }; @@ -56,10 +62,15 @@ examples.

{ public: basic_ifstream() {} - explicit basic_ifstream( const path & file_ph, + + template<class Path> + explicit basic_ifstream( const Path & file_ph, std::ios_base::openmode mode = std::ios_base::in ); + virtual ~basic_ifstream() {} - void open( const path & file_ph, + + template<class Path> + void open( const Path & file_ph, std::ios_base::openmode mode = std::ios_base::in ); }; @@ -71,10 +82,15 @@ examples.

{ public: basic_ofstream() {} - explicit basic_ofstream( const path & file_ph, + + template<class Path> + explicit basic_ofstream( const Path & file_ph, std::ios_base::openmode mode = std::ios_base::out ); + virtual ~basic_ofstream() {} - void open( const path & file_ph, + + template<class Path> + void open( const Path & file_ph, std::ios_base::openmode mode = std::ios_base::out ); }; @@ -86,10 +102,15 @@ examples.

{ public: basic_fstream() {} - explicit basic_fstream( const path & file_ph, + + template<class Path> + explicit basic_fstream( const Path & file_ph, std::ios_base::openmode mode = std::ios_base::in|std::ios_base::out ); + virtual ~basic_fstream() {} - void open( const path & file_ph, + + template<class Path> + void open( const Path & file_ph, std::ios_base::openmode mode = std::ios_base::in|std::ios_base::out ); }; @@ -100,7 +121,7 @@ examples.


Revised -12 March, 2004

+27 June, 2005

© Copyright Beman Dawes, 2002

Use, modification, and distribution are subject to the Boost Software diff --git a/doc/i18n.html b/doc/i18n.html new file mode 100644 index 0000000..ae2d380 --- /dev/null +++ b/doc/i18n.html @@ -0,0 +1,428 @@ + + + + + + + +1.34 (Internationalization) Changes + + + + +

1.34 (Internationalization) Changes

+

Introduction

+

This release is a major upgrade for the Filesystem Library, in preparation +for submission to the C++ Standards Committee. Features of this release +include:

+
    +
  • Internationalization, provided by + class templates basic_path, basic_filesystem_error, + basic_directory_iterator, and basic_directory_entry.
  • +
  • Simplification of the path interface, + including elimination of distinction between native and generic formats, + and separation of name checking functionality from general path functionality. + Also simplification of basic_filesystem_error.
  • +
  • Rationalization of predicate function + design, including the addition of several new functions.
  • +
  • Clearer specification by reference to [POSIX-01], + the ISO/IEEE Single Unix Standard, with provisions for Windows and other + operating systems.
  • +
  • Preservation of existing user code whenever + possible.
  • +
  • More efficient operations when iterating over directories.
  • +
  • A recursive + directory iterator is now provided.
  • +
+

Rationale for some of the changes is also provided.

+

Internationalization

+

Cass templates basic_path, basic_filesystem_error, and +basic_directory_iterator provide the basic mechanisms for +internationalization, in ways very similar to the C++ Standard Library's +basic_string and similar class templates. The following typedefs are also +provided:

+
+
typedef basic_path<std::string, ...> path;
+typedef basic_path<std::wstring, ...> wpath;
+
+typedef basic_filesystem_error<path> filesystem_error;
+typedef basic_filesystem_error<wpath> wfilesystem_error;
+
+typedef basic_directory_iterator<path> directory_iterator;
+typedef basic_directory_iterator<wpath> wdirectory_iterator;
+
+

The string type used by Boost.Filesystem basic_path (std::string, +std::wstring, or whatever) is called the internal string type. The string +type used by the operating system for paths (often char*, sometimes wchar_t*) is +called the external string type. Conversion between internal and external +types is performed by path traits classes. The specific conversions for path +and wpath is implementation defined, with normative encouragement to use +the operating system's preferred file system encoding. For many modern POSIX-based +file systems the wpath external encoding is +UTF-8, while for modern Windows file systems such as NTFS it is +UTF-16.

+

The operational functions in +operations.hpp are provided with overloads for +path, wpath, and user-defined basic_path's. A +"do-the-right-thing" rule +applies to implementations, ensuring that the correct overload will be chosen.

+

Simplification of path interface

+

Prior versions of the library required users of class path to identify +the format (native or generic) and name error-checking policy, either via a +second constructor argument or via a default mechanism. That approach caused +complaints, particularly from users not needing the name checking features. The +interface has now been simplified:

+
    +
  • The distinction between native and generic formats has been eliminated. + See rationale. Two argument forms of path + constructors are now deprecated, with the second argument having no effect. + These constructors are only provided to ease the transition of existing code.
  • +
  • Path name checking functionality has been moved out of class path and into + separate free-functions. This still provides name checking for those who need + it, but with much less impact on those who don't need it.
  • +
+

Additionally, basic_filesystem_error has been put +on a diet and generally simplified.

+

Error codes have been simplified and aligned with [POSIX-01]. A supporting +header +<boost/filesystem/cerrno.hpp> is also provided.

+

"//:" has been introduced as a path escape prefix to identify +native paths. Rationale: simplifies basic_path constructor interfaces, easier +use for platforms needing explicit native format identification.

+

Rationalization of predicate functions

+

In discussions and bug reports on the Boost developers mailing list, it +became obvious that Boost.Filesystem's exists(), symbolic_link_exists(), and +is_directory() predicate functions were poorly specified. There were suggestions +to add an is_accessible() function, but Peter Dimov argued that this amounted to +papering over the lack of a clear specification and would likely lead to future +problems.

+

Peter suggested that an interesting way to analyze the problem was to ask +what the expectations were for true and false values of the various predicates. +See the table below.

+

status()

+

As part of the predicate discussions, particularly with Rob Stewart, it +became obvious that sometimes applications need access to raw status information +without any possibility of an exception being thrown. The +status() function was added to meet this +need. It also proved clearer to specify the semantics of predicate functions in +terms of status().

+

is_file()

+

About the same time, Jeff Garland suggested that an +is_file() predicate would +compliment +is_directory(). In working on the analysis below, it became obvious +that the expectations for is_file() were different from the expectations for !is_directory(), +so is_file() was added.

+

is_other()

+

On some operating systems, it is possible to have a directory entry which is +not for either a directory or a file. The is_other() +function identifies such cases.

+

Should predicates throw on errors?

+

Some conditions reported by operating systems as errors (see +footnote) clearly simply indicate that the predicate is +false, rather than indicating serious failure. But other errors represent +serious hardware or network problems, or permissions problems.

+

Some people, particularly Rob Stewart, argue that in a function like +is_directory(), any error should simply cause the function to return false. If +there is actually an underlying problem, it will be detected it due course when +a directory_iterator or fstream operation is attempted.

+

That view is was rejected because of the following considerations:

+
    +
  • As a general principle, the earlier errors can be reported, the better. + The rationale being that it is often much cheaper to fix errors sooner rather + than later. I've also had a lot of negative experiences where failure to + detect errors early caused a lot of pain and unhappy customers. Some of these + were directly caused by ignoring error returns from file system operations.
    +  
  • +
  • Analysis of existing programs indicated that as much as 30% of the use of + a predicate was not followed by directory_iterator or fstream operations on + the path in question. Instead, the applications performed reporting or + fall-back operations that would not fail, and thus were either misleading or + completely wrong if the false return value was in fact caused by + hardware or network failure, or permissions problems.
  • +
+

However, the discussion did identify that there are valid cases where +non-throwing behavior is a requirement, and a programmer may prefer to deal with +file or directory attributes and errors at a very low, bit-mask, level. Function status() +was proposed to meet those needs.

+

Expectations table

+

In the table below, p is a non-empty path.

+

Unless otherwise specified, all functions throw on hardware or general +failure errors, permission or access errors, symbolic link loop errors, and +invalid path errors. If an O/S fails to distinguish between error types, +predicate operations return false on such ambiguous errors.

+

Expectations identify operations that are expected to succeed +or fail, assuming no hardware, permission, or access right errors, and no race +conditions.

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
ExpressionExpectationsSemantics
is_directory(p)Returns true if p is found and is a directory, else false.
+ If true, then directory_iterator(p) would succeed.
+ If false, then directory_iterator(p) would fail.
Throws: if status() & error_flag
+ Returns: status() & directory_flag
is_file(p)Returns true if p is found and is not a directory, else + false.
+ If true, then ifstream(p) would succeed.
+ False, however, does not imply ifstream(p) would fail (because some + operating systems allow directories to be opened as files, but stat() does + set the "regular file" flag.)
Throws: if status() & error_flag
+ Returns: status() & file_flag
exists(p) Returns is_directory(p) || is_file(p) || is_other(p)Throws: if status() & error_flag
+ Returns: status() &   (directory_flag|file_flag|other_flag)
is_symlink(p)Returns true if p is found by shallow (non-transitive) + search, and is a symbolic link, else false.
+ If true, and p points to q, then for any filesystem function f except those + specified as working shallowly on symlinks themselves, f(p) calls f(q), and + returns any value returned by f(q).
Throws: if symlink_status() & + error_flag
+ Returns: symlink_status() & symlink_flag
!exists(p) && ((p.has_branch_path() && exists( p.branch_path()) + || (!p.has_branch_path() && !p.has_root_path()))
+ In other words, if the path does not exist, and (the branch does exist, + or (there is no branch and no root)).
If true, create_directory(p) would succeed.
+ If true, ofstream(p) would succeed.
+  
 
directory_iterator it(p)If it != directory_iterator(), assert(exists(*it)||is_symlink(*it)). + Note: exists(*it) may throw, and likewise status(*it) may return error_flag + - there is no guarantee of accessibility. 
+

Conclusion

+

Predicate operations is_directory(), is_file(), is_symlink(), and exists() +with the indicated semantics form a self-consistent set that meets expectations.

+

Preservation of existing user code

+

Although the change to a template based approach required a complete overhaul +of the implementation code, the interface as used by existing applications is mostly unchanged. +Conversion problems which would +otherwise affect user code have been reduced by providing deprecated +functions to ease transition. The deprecated functions are:

+
+
// class basic_path - 2nd constructor argument ignored:
+basic_path( const string_type & str, name_check );
+basic_path( const typename string_type::value_type * s, name_check );
+
+// class basic_path - old names provided for renamed functions:
+string_type native_file_string() const;
+string_type native_directory_string() const;
+
+// class basic_path - now defined such that these no longer have any real effect:
+static bool default_name_check_writable() { return false; } 
+static void default_name_check( name_check ) {}
+static name_check default_name_check() { return 0; }
+
+// non-deducible operations functions assume class path
+inline path current_path()
+inline const path & initial_path()
+
+// the new basic_directory_entry provides leaf()
+// to cover the common existing use case itr->leaf()
+typename Path::string_type leaf() const;
+
+

If you do not want the deprecated functions to be included, define the macro BOOST_FILESYSTEM_NO_DEPRECATED.

+

The greatest impact on existing code is the change of directory iterator +value type from path to directory_entry. To ease the +most common directory iterator use case, basic_directory_entry +provides an automatic conversion to basic_path, and this also +serves to prevent breakage of a lot of existing code. See the +next section for discussion of rationale.

+
+
// the new basic_directory_entry provides:
+operator const path_type &() const;
+
+

More efficient operations when iterating over +directories

+

Several common real-world operating systems (BSD derivatives, Linux, Windows) +provide status information during directory iteration. Caching of this status +information results in three to six times faster operation for typical predicate +operations. (For a directory containing 15,047 files, iteration in 1 second vs 6 +seconds on a freshly booted system, and 0.3 seconds vs 0.9 seconds after prior use of +the directory.

+

The efficiency gains from caching such status information were considered too +significant to ignore. Because the possibility of race-conditions differs +depending on whether the cached information is used or an actual system call is +performed, it was considered necessary to provide explicit functions utilizing +the cached information, rather than implicitly using the cache behind the +scenes.

+

Three options were explored for exposing the cached status information, with +full implementations of each. After initial implementation of option 1 exposed +the problems noted below, option 2 was tested as a possible engineering +tradeoff. Option 3 +was finally chosen as the cleanest design.

+ + + + + + + + + + + + + + + + + + + + + +
OptionHow cache accessedPros and Cons
1Predicate function overloads
+ (basic_directory_iterator value_type is path)
+
    +
  • Very Questionable design (friendship abuse, overload abuse, etc)
  • +
  • User cannot reuse cache
  • +
  • Readability problem; easy to miss difference between f(*it) and f(it)
  • +
  • Write-ability problem (error prone?)
  • +
  • Most common iterator use is brief: *it
  • +
  • Preserves existing code
  • +
+
2Predicate member functions of basic_directory_iterator
+ (basic_directory_iterator value_type is path)
+
    +
  • Somewhat cleaner design (although added iterator functions is unusual)
  • +
  • User cannot reuse cache
  • +
  • Readability and write-ability is OK: f(*it) and it.f() sufficiently + different
  • +
  • Most common iterator use is brief: *it
  • +
  • Preserves existing code
  • +
+
3Predicate member functions of basic_directory_entry
+ (basic_directory_iterator value_type is basic_directory_entry)
+
    +
  • Cleanest design.
  • +
  • User can reuse cache.
  • +
  • Readability and write-ability is OK: f(*it) and it->f() sufficiently + different.
  • +
  • Most common iterator use is longer: it->path(), but by providing + "operator const basic_path &" it is still possible to write a bare *it.
  • +
  • Breaks some existing code. The "operator const basic_path &" + conversion eliminates breakage of the most common use case, while + providing a (deprecated) leaf() prevents breakage of the second most + common use case.
  • +
+
+

Rationale

+

Elimination of the native versus generic distinction

+

Elimination of user confusion and general design simplification was the +original motivation for elimination of the distinction between native and +generic paths.

+

During design work, a further technical argument was discovered. Consider the +path "c:foo/bar". On many POSIX systems, "c:foo" is a +valid directory name, so we have a two element path and there is no issue of +native versus generic format. On Windows system, however, "c:" is a +drive specification, so we have a three element path. All calls to the operating +system will result in "c:" being considered a drive specification; +there is no way that fact-of-life can be changed by claiming the format is +generic. The native versus generic distinction is thus useless and misleading +for POSIX, Windows, and probably most other operating systems.

+

If paths for a particular operating system did require a distinction be made, +it could be done by requiring that native paths be prefixed with some unique +implementation-defined identification. For example, "native-path:". +This would only be required for operating systems where (1) the distinction +mattered, and (2) there was no lexical way to distinguish the two forms. For +example, a native operating system that used the same syntax as the Filesystem +Library's generic POSIX-like format, but processed the elements right-to-left +instead of left-to-right.

+

Preservation of existing code

+

Allowing existing user code to continue to work with the updated version of +the library has obvious benefits in terms of preserving the effort users have +applied to both learning the library and writing code which uses the library.

+

There is an additional motivation; other than the name checking portion of +class path,  the existing interface has proven to be useful and robust, so +there is no reason to fiddle with it.

+

Single path design

+

During preliminary internationalization discussion on the Boost developer's +list, a design was considered for a single path class which could hold either +narrow or wide character based paths. That design was rejected because:

+
    +
  • The design was, for many applications, an over-generalization with runtime + memory and speed costs which would have to be paid for even when not needed.
  • +
  • There was concern that the design would be confusing to users, given that + the standard library already uses single-value-type strings, rather than + strings which morph value types as needed.
  • +
  • There were technical issues with conversions when a narrow path was + appended to a wide path, and visa versa. The concern was that double + conversions could cause incorrect results, that conversions best left to the + operating system would be performed, and that the technical complexity was too + great in relation to perceived benefits. User-defined types would only make + the problem worse.
  • +
+

No versions of status() which throw exceptions on +errors

+

The rationale for not including versions of status() +which throw exceptions on errors is that (1) the primary purpose of this +function is to perform queries at a very low-level, where exceptions are usually +unwanted, and (2) exceptions on errors are already provided by the predicate +functions. There would be little or no efficiency gain from providing a throwing +version of status().

+

Symlink identifying version of status() function

+

A symlink identifying version of the status() function is distinguished by a +second argument. Often separately named functions are more appropriate than +overloading when behavior +differs, which is the case here, while overloads are more appropriate when +behavior is the same but argument types differ (Iain Hanson). Overloading was +chosen in this particular case because a subjective judgment that a single +function name with an optional "symlink" second argument produced more +understandable code. The original implementation of the function used the name "symlink_status", +but that just didn't read right in real code.

+

POSIX wpath_traits defaults to locale(""), but allows imbuing of locale

+

Vladimir Prus pointed out that for Linux (and presumably other POSIX +operating systems) that need to convert wide character paths to narrow +characters, the default conversion should not depend on the operating system +alone, but on the std::locale("") default. For example, the usual encoding +for Russian on Linux (and Russian web sites) is KOI8-R (RFC1489). The ability to safely specify a different locale +is also provided, to meet unforeseen needs.

+
+

Revised +16 December, 2005

+

© Copyright Beman Dawes, 2005

+

Use, modification, and distribution are subject to the Boost Software +License, Version 1.0. (See accompanying file + +LICENSE_1_0.txt or copy at +www.boost.org/LICENSE_1_0.txt)

+ + + + \ No newline at end of file diff --git a/doc/index.htm b/doc/index.htm index 81f467f..b4b3a51 100644 --- a/doc/index.htm +++ b/doc/index.htm @@ -24,84 +24,127 @@ Filesystem Library     Common Specifications
    Race-condition danger
    Implementation
+    Restricting library to narrow + character paths
    Building the object-library
-      Notes for Cygwin users
+        Notes for Cygwin users
    Acknowledgements
    Change history - Other Documents
+ Other Documents
    Library Design
    FAQ
    Portability Guide
    path.hpp documentation
+    path.hpp Error + Reporting documentation
    operations.hpp documentation
    fstream.hpp documentation
-    exception.hpp documentation
    convenience.hpp documentation
-    Do-list +   
+    Do-list
+
+    C++ Standards + Committee proposal for TR2

Introduction

The Boost Filesystem Library provides portable facilities to query and manipulate paths, files, and directories.

-

The motivation for the library is the need to be able to perform portable script-like operations from within C++ programs. The intent is not to +

The motivation for the library is the need to perform portable script-like operations from within C++ programs. The intent is not to compete with Python, Perl, or shell languages, but rather to provide portable filesystem operations when C++ is already the language of choice. The -design encourages, but does not require, safe and portable filesystem usage.

+design encourages, but does not require, safe and portable usage.

-

The Filesystem Library supplies several  headers, all in -directory boost/filesystem:

+

Programs using the library are portable, both in the sense that +the syntax of program code is portable, and the sense that the semantics or +behavior of code is portable. The generic path +grammar is another important aid to portability.

+ +

Usage is safe in the sense that errors cannot be ignored since most functions throw C++ +exceptions when errors are detected. This is also convenient for users because +it alleviates the  need to explicitly check error +return codes.

+ +

A proposal has been +submitted to the C++ Standards Committee for inclusion of the library in the +Standard Library Technical Report 2 (TR2). The Boost.Filesystem library will +stay in alignment with the TR2 Filesystem proposal as it works its way through +the committee process. Note, however, that namespaces and header granularity +differs between Boost.Filesystem and the TR2 proposal.

+ +

The Filesystem Library supplies several  headers:

The organizing principle is that purely lexical operations on - paths are supplied as class path member + paths are supplied as class basic_path member functions in path.hpp, while operations performed by the operating system on the actual external filesystem directories and files are provided in operations.hpp, primarily as free functions.

+

Heads up! There are effectively two sets of documentation - the +traditional Boost documentation and the newer TR2 proposal. Although lots of +effort has gone into keeping them in sync, be warned that there are inadvertent +errors, omissions, and divergences.

Two-minute tutorial

First some preliminaries:

-
#include "boost/filesystem/operations.hpp" // includes boost/filesystem/path.hpp
-#include "boost/filesystem/fstream.hpp"    // ditto
-#include <iostream>                        // for std::cout
-using boost::filesystem;                   // for ease of tutorial presentation;
-                                           // a namespace alias is preferred in real code
+
#include "boost/filesystem.hpp"   // includes all needed Boost.Filesystem declarations
+#include <iostream>               // for std::cout
+using boost::filesystem;          // for ease of tutorial presentation;
+                                  //  a namespace alias is preferred practice in real code

A class path object can be created:

path my_path( "some_dir/file.txt" );
-

The string passed to the path constructor is in a -portable generic path format. Access functions -make my_path contents available in an operating system dependent format, +

The string passed to the path constructor may be in a +portable generic path format or an +implementation-defined native operating system format. Access functions +make my_path contents available to the underlying operating system API in an operating system dependent format, such as "some_dir:file.txt", "[some_dir]file.txt", "some_dir/file.txt", or whatever is appropriate for the -operating system.

+operating system. If class wpath is used instead of class path, +translation between wide and narrow character paths is performed automatically +if necessary for the operating system.

Class path has conversion constructors from const char* and -const std:: string&, so that even though the Filesystem Library functions in -the following code snippet take const path& arguments, the user can just -code C-style strings:

+const std:: string&, so that even though the Filesystem Library +functions used in the following code snippet have const path& formal +parameters, the user can just +code C-style strings as actual arguments:

remove_all( "foobar" );
 create_directory( "foobar" );
@@ -111,12 +154,6 @@ file.close();
 if ( !exists( "foobar/cheeze" ) )
   std::cout << "Something is rotten in foobar\n";
-

Additional class path constructors provide for an operating system dependent -format, useful for user provided input:

-
-
int main( int argc, char * argv[] ) {
-path arg_path( argv[1], native ); // native means use O/S path format
-

To make class path objects easy to use in expressions, operator/ appends paths:

@@ -125,22 +162,22 @@ ifstream file2( arg_path / "foo" / "bar" );

The expressions arg_path / "foo/bar" and arg_path / "foo" / "bar" yield identical results.

-

Paths can include references to the parent directory, using the ".." -notation. Paths are always automatically converted to -canonical form, so "foo/../bar" gets -converted to "bar", and "../foo/../bar" gets converted to -"../bar".

-

Class directory_iterator -is an important component of the library. It provides input iterators over the -contents of a directory, with the value type being class path.

+

Paths can include references to the current directory, using "." +notation, and the parent directory, using ".." +notation.

+

Class basic_directory_iterator +is an important component of the library. It provides an input iterator over the +contents of a directory, with the value type being class basic_path. +Typedefs directory_iterator and wdirectory_iterator are provided +to cover the most common use cases.

The following function, given a directory path and a file name, recursively searches the directory and its sub-directories for the file name, returning a bool, and if successful, the path to the file that was found.  The code below is extracted from a real program, slightly modified for clarity:

-
bool find_file( const path & dir_path,     // in this directory,
+  
bool find_file( const path & dir_path,         // in this directory,
                 const std::string & file_name, // search for this name,
-                path & path_found )        // placing path here if found
+                path & path_found )            // placing path here if found
 {
   if ( !exists( dir_path ) ) return false;
   directory_iterator end_itr; // default construction yields past-the-end
@@ -148,13 +185,13 @@ below is extracted from a real program, slightly modified for clarity:

itr != end_itr; ++itr ) { - if ( is_directory( *itr ) ) + if ( itr->is_directory() ) { - if ( find_file( *itr, file_name, path_found ) ) return true; + if ( find_file( itr->path(), file_name, path_found ) ) return true; } else if ( itr->leaf() == file_name ) // see below { - path_found = *itr; + path_found = itr->path(); return true; } } @@ -169,13 +206,19 @@ last (closest to the leaf, farthest from the root) file or directory name in the

In addition to leaf(), several other function names use the tree/root/branch/leaf metaphor.

Notice that find_file() does not do explicit error checking, such as -verifying that the dir_path argument really represents a directory. All -Filesystem Library functions throw filesystem_error +verifying that the dir_path argument really represents a directory. +Boost.Filesystem functions throw exceptions if they do not complete successfully, so there is enough implicit error checking that this application doesn't need to supply additional error -checking code.

+checking code unless desired. Several operations functions have nothrow +versions, to ease use cases where exceptions would not be appropriate.

+
+

Note: Sometime after the above tutorial code was added, recursive +directory iterations was provided as a convenience function. So nowadays you +don't have to actually code the recursion yourself.

+

The tutorial is now over; hopefully you now are ready to write simple, -script-like, programs using the Filesystem Library!

+script-like programs using the Filesystem Library!

Examples

simple_ls.cpp

The example program simple_ls.cpp is @@ -203,6 +246,7 @@ Filesystem Library extensively.  See:

Test programs are sometimes useful in understanding a library, as they illustrate what the developer expected to work and not work. See:

    +
  • wide_test.cpp
  • path_test.cpp
  • operations_test.cpp
  • fstream_test.cpp
  • @@ -250,7 +294,7 @@ represents the directory tree for a filesystem.

    multi-root operating system - An operating system which has multiple roots. Some operating systems have different directory trees for each different -disk, drive, device, volume, share, or other entity managed the system, with each having its +disk, drive, device, volume, network share, or other entity managed the system, with each having its own root-name.

    link - A name in a directory can be viewed as a pointer to some underlying directory or file @@ -271,50 +315,88 @@ link itself. A few uses, such as remove() and rename(), modify the symbolic link rather than it's target. See an important symbolic links warning.

    Common Specifications

    +

    Portions of the Boost.Filesystem specifications are defined by reference to +the [POSIX-01] standard. How such +functionality is actually implemented is unspecified. In other words, an "as if" +rule applies to the actual implementation.

    +

    Rationale: Reference to [POSIX-01] +allows precise semantics based on a joint ISO and IEEE standard that is widely +implemented and has been proven and refined during many years of existing +practice. Operating-system wars neutrality is preserved by allowing implementers +freedom to use native operating system API's for actual implementations.

    +

    Requirements on implementations

    Unless otherwise specified, all Filesystem Library member and non-member -functions have the following common specifications:

    -

    Postconditions not guaranteed in the presence of race-conditions - -Filesystem function specifications follow the form of C++ Standard Library +functions meet the following common requirements.

    +

    Portable Behavior

    +

    Implementations are encouraged, but not required, to provide behavior as +specified for each Boost.Filesystem function. If the specified behavior is not +provided, an implementation shall document the actual behavior provided.

    +

    Rationale: Portable behavior is strongly encouraged, since this is a +key objective of the library.  It is not required because in some environments +portable behavior is impossible or unreasonably difficult to implement. The +documentation requirement allows users to be aware of potential portability +problems.

    +

    Graceful degradation

    +

    Filesystem Library functions which cannot +be fully supported on a particular operating system will be partially supported +if possible. Implementations must document such partial support. Functions which +are requested to provide some operation which they cannot support should report +an error at compile time (preferred) or throw an exception at runtime.

    +

    For environments not supporting file systems with hierarchical directory +structures Boost.Filesystem headers shall fail to compile.

    +

    If no file systems with hierarchical directory structures are available at +runtime, all Boost.Filesystem functions shall throw a basic_filesystem_error<path> +exception.

    +

    If a Boost.Filesystem component depends on file system functionality not +available in the current environment:

    +
      +
    • Safe fallback behavior must be provided if possible.
    • +
    • Otherwise a basic_filesystem_error exception is thrown.
    • +
    +

    Rationale: Although the Boost.Filesystem implement runs on POSIX or +Windows, the above guidance is provided for implementations on operating systems +which can be supported only partially or not at all. Implementations on +less-powerful operating systems should provide useful functionality if possible, +but are not be required to simulate features not present in the underlying +operating system.

    +

    Postconditions not guaranteed in the presence of race-conditions

    +

    Filesystem function specifications follow the form of C++ Standard Library specifications, and so sometimes specify behavior in terms of postconditions. If a race-condition exists, a function's postconditions may no longer be true by the time the function returns to the caller.

    -

    May throw exceptions - Filesystem Library functions may throw filesystem_error +

    May throw exceptions

    +

    Filesystem Library functions throw +basic_filesystem_error exceptions if they cannot successfully complete their operational -specifications. Function implementations may use C++ Standard Library functions, +specifications. Also, implementations may use C++ Standard Library functions, which may throw std::bad_alloc. These exceptions may be thrown even though the error condition leading to the exception is not explicitly specified in the function's "Throws" paragraph.

    -

    Exceptions thrown via -boost::throw_exception() - All exceptions thrown by the Filesystem +

    All exceptions thrown by the Filesystem Library are implemented by calling boost::throw_exception(). Thus exact behavior may differ depending on BOOST_NO_EXCEPTIONS at the time the filesystem source files are compiled.

    -

    Links follow operating system rules- Links are +

    Symbolic and hard links

    +

    Links follow operating system rules- Links are transparent in that Filesystem Library functions simply follow operating system -rules. That implies that some functions may throw filesystem_error +rules. That implies that some functions may throw +basic_filesystem_error exceptions if a link is cyclic or has other problems. Also, see an important symbolic links warning.

    Typical operating systems rules call for deep operations on all links except that destructive operations on non-reference counted links are either shallow, or fail altogether in the case of trying to remove a non-reference counted link to a directory.

    -

    Rationale: Follows existing practice (POSIX, Windows, etc.).

    -

    No atomic-operation or rollback guarantee - Filesystem Library +

    Rationale: Follows existing practice (POSIX, Windows, etc.).

    +

    No atomic-operation or rollback guarantee

    +

    Filesystem Library functions which throw exceptions may leave the external file system in an altered state. It is suggested that implementations provide stronger guarantees -when possible.

    -

    Rationale: Implementers shouldn't be required to provide guarantees which are -impossible to meet on some operating systems. Implementers should be given -normative encouragement to provide those guarantees when possible.

    -

    Graceful degradation -  Filesystem Library functions which cannot -be fully supported on a particular operating system will be partially supported -if possible. Implementations must document such partial support. Functions which -are requested to provide some operation which they cannot support should report -an error at compile time (preferred) or throw an exception at runtime.

    -

    Rationale: Implementations on less-powerful operating systems should provide -useful functionality if possible, but are not be required to simulate -features not present in the underlying operating system.

    +when reasonable.

    +

    Rationale: Implementers shouldn't be required to provide guarantees which are +impossible to meet on some operating systems. Implementers are given +normative encouragement to provide those guarantees when reasonable.

    Race-condition danger

    The state of files and directories is often globally shared, and thus may be changed unexpectedly by other threads, @@ -346,29 +428,34 @@ or directory resources should be prepared for filesystem_error exceptions to be thrown from any filesystem function except those explicitly specified as not throwing exceptions.

    Implementation

    -

    The current implementation supports operating systems that have -either the POSIX or Windows API's available.

    +

    The current implementation supports operating systems which provide +either the POSIX or Windows API.

    The following tests are provided:

    -

    The library is in regular use on Apple Mac OS, HP-UX, IBM AIX, Linux, +

    The library is in regular use on Apple OS X, HP-UX, IBM AIX, Linux, Microsoft Windows, SGI IRIX, and Sun Solaris operating systems using a variety of compilers.

    +

    Restricting library to narrow character paths

    +

    Compilers or standard libraries which do not support wide characters (wchar_t) +or wide character strings (std::wstring) are detected automatically, and cause +the library to compile code that is restricted to narrow character paths +(boost::filesystem::path). Users can force this restriction by defining the +macro BOOST_FILESYSTEM_NARROW_ONLY. That may be useful for dealing with legacy +compilers or operating systems.

    Building the object-library

    The object-library will normally be built automatically. See Getting Started. It can also be built manually using a Jamfile supplied in directory libs/filesystem/build, or the user can construct an IDE project or make file which includes the object-library source files.

    -

    The object-library source files (convenience.cpp, -exception.cpp, -operations_posix_windows.cpp, -and path_posix_windows.cpp) are -supplied in directory libs/filesystem/src. These source files implement the +

    The object-library source files are +supplied in directory libs/filesystem/src. These source files implement the library for POSIX or Windows compatible operating systems; no implementation is supplied for other operating systems. Note that many operating systems not normally thought of as POSIX systems, such as mainframe legacy @@ -382,8 +469,8 @@ Compilation page for a description of the techniques used.

    The library's implementation code automatically detects the current platform, and compiles the POSIX or Windows implementation code accordingly. Automatic platform detection during object library compilation can be overridden by -defining BOOST_POSIX or BOOST_WINDOWS macros. With the exception of the Cygwin -environment, there is usually no reason to define one of the macros, as the +defining either BOOST_POSIX_API or BOOST_WINDOWS_API macros. With the exception of the Cygwin +environment, there is usually no reason to define these macros, as the software development kits supplied with most compilers only support a single platform.

    The Cygwin package of tools supports @@ -393,13 +480,15 @@ Linux look-and-feel. GCC is usually the compiler of choice in this environment, as it can be installed via the Cygwin install process. Other compilers can also use the Cygwin emulation of POSIX, at least in theory.

    Those wishing to use the Cygwin POSIX emulation layer should define the -BOOST_POSIX macro when compiling the Boost Filesystem Library's object-library. -The macro does not need to be defined (and will have no effect if defined) for -Boost Filesystem Library user programs.

    +BOOST_POSIX_API macro when compiling bother user programs and the +Boost.Filesystem's object-library.

    Acknowledgements

    -

    The Filesystem Library was designed and implemented by Beman Dawes. The directory_iterator and filesystem_error classes were +

    The Filesystem Library was designed and implemented by Beman Dawes. The +original directory_iterator and filesystem_error classes were based on prior work from Dietmar Kühl, as modified by Jan Langer. Thomas Witt -was a particular help in later stages of development.

    +was a particular help in later stages of initial development. Peter Dimov and +Rob Stewart made many useful suggestions and comments over a long period of +time. Howard Hinnant helped with internationalization issues.

    Key design requirements and design realities were developed during @@ -500,8 +589,55 @@ of portability" problem, particularly in postings by PJ Plauger and Pete Be

    Walter Landry provided much help illuminating symbolic link use cases for version 1.31.0.

    +

    Version 1.34 (i18n) acknowledgements

    + +

    So many people have contributed comments and bug reports that it isn't any +longer possible to acknowledge them individually. That said, Peter Dimov and Rob +Stewart need to be specially thanked for their many constructive criticisms and +suggestions. Terence +Wilson and Chris Frey contributed timing programs which helped illuminate +performance issues.

    +

    Change history

    +

    Version 1.34.0

    + +
      +
    • Internationalization, provided by + class templates basic_path, basic_filesystem_error, and + basic_directory_iterator.
    • +
    • Simplification of the path interface, + including elimination of distinction between native and generic formats, + and separation of name checking functionality from general path functionality.
    • +
    • Rationalization of predicate + function design, including the addition of several new functions.
    • +
    • Preservation of existing user code whenever + possible. Deprecated features (symbolic_link_exists(), etc.) remain + available for legacy code.
    • +
    • Clearer specification, by reference to [POSIX-01], + the ISO/IEEE Single Unix Standard, with provisions for Windows and other + operating systems.
    • +
    • New functions + status, + symlink_status, + is_file, + is_symlink, create_hard_link, + create_symlink, path member and non-member swap, path inserter, path + extractor, additional path relational and "/" operator overloads, additional + path member template functions taking iterator arguments.
    • +
    • More efficient operations when iterating over directories.
    • +
    • Separation within the implementation of the distinction between the native + operating system path syntax and API. This is important for CYGWIN users + because it allows them to build for either the Windows or POSIX API's.
    • +
    • Numerous small implementation fixes.
    • +
    + +

    Version 1.33.0

    + +
      +
    • Some small implementation fixes.
    • +
    +

    Version 1.32.0

      @@ -541,9 +677,9 @@ version 1.31.0.


      Revised -02 August, 2005

      +16 December, 2005

      -

      © Copyright Beman Dawes, 2002

      +

      © Copyright Beman Dawes, 2002-2005

      Use, modification, and distribution are subject to the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at diff --git a/doc/operations.htm b/doc/operations.htm index c999f02..cf963f3 100644 --- a/doc/operations.htm +++ b/doc/operations.htm @@ -15,23 +15,28 @@

      Introduction
      Header synopsis
      -Class directory_iterator
      +Class basic_directory_entry
      +Class basic_directory_iterator
          Constructors
      -    Other_functions
      -Operations functions
      +Operations non-member functions
      +    "Do-the-right-thing" rule
      +    status
          exists
      -    symbolic_link_exists
      -    is_directory
      +    is_directory
      +    is_file
      +    is_symlink
          is_empty
          equivalent
          file_size
          last_write_time
          create_directory
      +    create_hard_link
          remove
          remove_all
          rename
          copy_file
          initial_path
      +    current_path
          complete
          system_complete

      @@ -51,100 +56,179 @@ documentation.

      Specifications apply to all member and non-member functions supplied by this header.

      -

      Header boost/filesystem/operations.hpp -synopsis

      +

      Header boost/filesystem/operations.hpp

      + +

      Synopsis

      namespace boost
       {
         namespace filesystem
         {
      +    typedef bitmask-type status_flags; // see C++ std 17.3.2.1.2 [lib.bitmask.types]
       
      -    class directory_iterator
      +    static const status_flags  error_flag;
      +    static const status_flags  not_found_flag;
      +    static const status_flags  directory_flag;
      +    static const status_flags  file_flag;
      +    static const status_flags  other_flag;
      +    static const status_flags  symlink_flag;
      +
      +    struct symlink_t{};
      +    extern symlink_t symlink;
      +
      +    //  basic_directory_entry
      +
      +    template<class Path> class basic_directory_entry
           {
           public:
      -      typedef path                     value_type;
      -      typedef std::ptrdiff_t           difference_type;
      -      typedef const path *             pointer;
      -      typedef const path &             reference;
      -      typedef std::input_iterator_tag  iterator_category;
      +      typedef Path path_type;
      +      typedef typename Path::string_type string_type;
       
      -      directory_iterator();
      -      explicit directory_iterator( const path & directory_ph );
      +      // compiler generated copy-ctor, copy assignment, and destructor apply
       
      -      // Other_functions
      -      // ...
      +      basic_directory_entry() : m_status(0), m_symlink_status(0) {}
      +      explicit basic_directory_entry( const path_type & p, status_flags sf=0, status_flags symlink_sf=0 );
      +
      +      void assign( const path_type & p, status_flags sf=0, status_flags symlink_sf=0 );
      +
      +      void replace_leaf( const string_type & s, status_flags sf=0, status_flags symlink_sf=0 );
      +
      +      // conversion simplifies most common use of basic_directory_entry
      +      operator const path_type &() const { return m_path; }
      +
      +#   ifndef BOOST_FILESYSTEM_NO_DEPRECATED
      +      // deprecated function preserves common use case in legacy code
      +      typename Path::string_type leaf() const;
      +#   endif
      +
      +      const Path & path() const;
      +
      +      status_flags status( system_error_type * ec=0 ) const;
      +      status_flags status( const symlink_t &, system_error_type * ec=0 ) const;
      +
      +      bool exists() const;
      +      bool is_directory() const;
      +      bool is_file() const;
      +      bool is_other() const;
      +      bool is_symlink() const;
      +
      +    // for exposition only:
      +    private:
      +      path_type m_path;
      +      mutable status_flags m_status; // stat()-like
      +      mutable status_flags m_symlink_status; // lstat()-like
           };
       
      -    bool exists( const path & ph );
      -    bool symbolic_link_exists( const path & ph );
      -    bool is_directory( const path & ph );
      -    bool is_empty( const path & ph );
      -    bool equivalent( const path & ph1, const path & ph2 );
      +    typedef basic_directory_entry<path> directory_entry;
      +    typedef basic_directory_entry<wpath> wdirectory_entry;
       
      -    boost::intmax_t file_size( const path & ph );
      +    //  basic_directory_iterator
       
      -    std::time_t last_write_time( const path & ph );
      -    void last_write_time( const path & ph, std::time_t new_time );
      +    template<class Path> class basic_directory_iterator 
      +      : public boost::iterator_facade<
      +          basic_directory_iterator<Path>,
      +          basic_directory_entry<Path>,
      +          boost::single_pass_traversal_tag >
      +    {
      +    public:
      +      typedef Path path_type;
       
      -    bool create_directory( const path & directory_ph );
      -    bool remove( const path & ph );
      -    unsigned long remove_all( const path & ph );
      -    void rename( const path & from_path,
      -                 const path & to_path );
      -    void copy_file( const path & source_file,
      -                    const path & target_file );
      +      basic_directory_iterator(); // creates the "end" iterator
      +      explicit basic_directory_iterator( const Path & directory_path );
      +    };
       
      -    const path & initial_path();
      -    path current_path();
      -    path complete( const path & ph,
      -                   const path & base = initial_path() );
      -    path system_complete( const path & ph );
      +    typedef basic_directory_iterator<path> directory_iterator;
      +    typedef basic_directory_iterator<wpath> wdirectory_iterator;
      +
      +    //  operations functions
      +
      +    template<class Path> status_flags status( const Path & ph, system_error_type * ec = 0 );
      +    template<class Path> status_flags status( const Path & ph, const symlink_t &, system_error_type * ec = 0 );
      +
      +    template<class Path> bool exists( const Path & ph );
      +    template<class Path> bool is_directory( const Path & ph );
      +    template<class Path> bool is_file( const Path & ph );
      +    template<class Path> bool is_other( const Path & ph );
      +    template<class Path> bool is_symlink( const Path & ph );
      +    template<class Path> bool is_empty( const Path & ph );
      +    template<class Path> bool equivalent( const Path & ph1, const Path & ph2 );
      +    template<class Path> boost::intmax_t file_size( const Path & ph );
      +    template<class Path> std::time_t last_write_time( const Path & ph );
      +    template<class Path> void last_write_time( const Path & ph, std::time_t new_time );
      +    template<class Path> bool create_directory( const Path & directory_ph );
      +    template<class Path> void create_hard_link( const Path & existing_file_ph, const Path & new_file_ph );
      +    template<class Path> bool remove( const Path & ph );
      +    template<class Path> unsigned long remove_all( const Path & ph );
      +    template<class Path> void rename( const Path & from_path, const Path & to_path );
      +    template<class Path> void copy_file( const Path & source_file, const Path & target_file );
      +    template<class Path> Path complete( const Path & ph, const Path & base = initial_path<Path>() );
      +    template<class Path> Path system_complete( const Path & ph );
      +    template<class Path> const Path & initial_path();
      +    template<class Path> Path current_path();
       
         } // namespace filesystem
       } // namespace boost
       
      -

      Class directory_iterator

      +

      Class basic_directory_entry

      -

      Class directory_iterator provides a C++ standard conforming input +

      Performance note: During directory +iteration, implementations are +permitted but not required to cache a copy of status information +within a directory entry. This attribute information can later be used by member query functions, resulting in +three +to six times faster operation than for the non-member query functions. Note, however, +that attributes cached in directory entries are not updated when the contents of the external file system +changes, so the directory entry form of query functions should not be used if stale +attributes might pose a problem.

      + +

      path() function

      + +
      +
      const Path & path() const;
      +

      Returns: m_path.

      +
      + +

      Other member functions

      + +

      These member functions have the same behavior as the non-member operations +functions of the same name, except that they may rely on cached status +information. See performance note.

      + +

      Class basic_directory_iterator

      + +

      Class basic_directory_iterator provides a C++ standard conforming input iterator which accesses the contents of a -directory.

      +directory. Except as specified below, iteration is performed as if by +calling [POSIX-01] readdir_r() or readdir().

      -

      The value type is boost::filesystem::path, so -dereferencing a directory_iterator yields a +

      The value type is boost::filesystem::basic_path, so +dereferencing a basic_directory_iterator yields a path to a file or sub-directory contained within the directory represented by the directory-path argument supplied at construction. The path returned by -dereferencing a directory_iterator is composed by appending the name of +dereferencing a basic_directory_iterator is composed by appending the name of the directory entry to the directory path supplied at construction.

      The order of the path entries returned by dereferencing successive increments -of a directory_iterator is unspecified. Thus depending on the ordering +of a basic_directory_iterator is unspecified. Thus depending on the ordering provided by a particular implementation will result in non-portable code.

      -

      A path returned by dereferencing a directory_iterator is, if -representing a directory, suitable for use as an argument to Filesystem Library -functions specified as accepting paths or directory paths. If not representing a -directory, the dereferenced path is suitable for use as an argument to -Filesystem Library functions specified as accepting paths or file paths, or C++ +

      A directory path returned by dereferencing a basic_directory_iterator is suitable for use as an argument to Boost.Filesystem +functions specified as accepting paths or directory paths. A file path returned by dereferencing a basic_directory_iterator is suitable for use as an argument to +Boost.Filesystem functions specified as accepting paths or file paths, or C++ Standard Library functions specified as taking file names. The leaf of a path -returned by dereferencing a directory_iterator will never be ".." +returned by dereferencing a basic_directory_iterator will never be ".." or ".".

      -

      Note: The implication of the above requirement is that if an operating -system's directories can contain entries which are not usable by Filesystem -Library or Standard Library functions, these entries will be skipped during -directory iteration. Such entries are by definition non-portable, but can always -be accessed via the native operating system API if required.

      -

      Warning: Programs performing directory iteration may wish to test, via exists(), if the path returned by dereferencing an iterator actually exists. It could be a symbolic link to a non-existent file or directory. Programs recursively walking directory trees for purposes of removing and renaming entries may wish to avoid following symbolic links, which can be -detected with symbolic_link_exists().

      +detected with the is_symlink() function.

      Warning: If a file or sub-directory is removed from or added to a -directory after the construction of a directory_iterator for the +directory after the construction of a basic_directory_iterator for the directory, it is unspecified whether or not subsequent incrementing of the iterator will ever result in an iterator whose value is the removed or added directory entry.

      @@ -152,34 +236,28 @@ directory entry.

      Constructors

      -

      directory_iterator();

      +

      basic_directory_iterator();

      -

      Effects: Constructs a directory_iterator having the +

      Effects: Constructs a basic_directory_iterator having the past-the-end value as described in the C++ standard, section 24.1.

      -

      explicit directory_iterator( const path & directory_ph );

      +

      explicit basic_directory_iterator( const Path & directory_ph );

      -

      Effects: Constructs a directory_iterator with a value +

      Effects: Constructs a basic_directory_iterator with a value representing the first path in directory_ph, or if empty(directory_ph), the past-the-end value.

      Throws: if !exists( directory_ph )

      Note: To iterate over the current directory, write -directory_iterator(current_path()) rather than directory_iterator("").

      +directory_iterator(".") rather than directory_iterator("").

      -

      Other functions

      - -

      Class directory_iterator also supplies all the other functions -required by the C++ standard clause 24 for input iterators, such as operator==, -operator++, and operator*.

      - -

      Non-member functions

      +

      Operations non-member functions

      -The non-member functions provide common operations on files and directories. -They follow traditional practice of the C and C++ standard libraries, except +The operations non-member functions provide common operations on files and directories. +They follow traditional practice of the C, and C++ standard libraries, except that they:

      @@ -216,94 +294,174 @@ above. The error condition is also specified in the Throws paragraph to ensure that the error results in well-defined rather than implementation-defined behavior, and because existing practice for the equivalent operating system function is usually to treat an empty path as an error. The intended use of the -Filesystem Library in script-like programs makes undefined behavior particularly +Filesystem Library in portable script-like programs makes undefined behavior particularly unattractive.

      Naming Rationale: See class path Naming Rationale.

      +

      "Do-the-right-thing" rule

      + +

      To allow automatic +conversions to function properly for classes path and wpath, as +well as to support class template basic_path, several +overloads of each non-member function are supplied. The overloads must "do-the-right-thing" using +boost::enable_if or equivalent techniques, so that the correct +overload is selected for calls like exists( "foo" ) which +depend on automatic conversion to the appropriate basic_path type. To simplify +exposition, only the templated version of each non-member function is shown.

      + +

      status

      +
      +
      template<class Path>   status_flags status( const Path & ph, system_error_type * ec = 0 );
      +template<class Path>   status_flags status( const Path & ph, const symlink_t &, system_error_type * ec = 0 );
      +

      Effects:

      +
        +
      • If ph.empty() or it->empty():
          +
        • If ec != 0, set *ec to a system error code indicating "not found".
        • +
        • Return not_found_flag.
        • +
        +
      • +
      • Determine the attributes of + path ph or path *it.
        +
        + For ph, the attributes are always found by querying the + operating system. For *it, the implementation is permitted, + but not required, to associate a copy of previously obtained attribute + information with the iterator.
        +
        + If the symlink_t argument is not present, the attributes are + determined as if by [POSIX-01] + stat(). Thus if ph or *it refers to a symbolic link, the link is + resolved (that is, deep semantics).
        +
        + If the symlink_t argument is present, the attributes are + determined as if by [POSIX-01] lstat(). Thus if ph + or *it refers to a symbolic link, the link is not + resolved (that is, shallow semantics).
      • +
      • If the attribute determination does not report an error:
          +
        • If the attributes indicate a symbolic link, as if by + [POSIX-01] S_ISLNK() (also see + expectations), return + symlink_flag.
        • +
        • Set result to 0.
        • +
        • If the attributes indicate a directory, as if by + [POSIX-01] S_ISDIR() (also see + expectations), result |= + directory_flag.
        • +
        • If the attributes indicate a file, as if by + [POSIX-01] S_ISREG() (also see + expectations), result |= + file_flag.
        • +
        • If result is 0, set result to other_flag.
        • +
        • Return result.
        • +
        +
      • +
      +
        +
      • If the attribute determination reports an error:
          +
        • If ec != 0, set *ec to the error code reported by the operating + system.
        • +
        • If the query reports an error indicating that ph or + *it + could not be found, return + not_found_flag.
        • +
        • Otherwise, return error_flag.
        • +
        +
      • +
      +

      Returns:

      +
        +
      • One of error_flag, not_found_flag, + other_flag, or symlink_flag. The symlink_flag + is a possibility only when the symlink_t argument is present.
      • +
      • Otherwise, one or more of the following, or'ed together: + directory_flag,  + file_flag. [More status bits may be added in the future.]
      • +
      +

      See performance note for subtle + differences between behavior of Path and DirItr template signatures.

      +

      Note: Certain conditions reported + by the operating system as errors are treated as + not_found_flag rather than + error_flag. On POSIX, these are ENOENT and ENOTDIR. On Windows, + these are ERROR_FILE_NOT_FOUND, ERROR_PATH_NOT_FOUND, and ERROR_BAD_NETPATH.

      +

      Note: The Microsoft Windows operating system does not currently +support symbolic links, so + symlink_flag is never returned on that platform. (Short-cut files (.lnk) are a Windows application +feature, not an O/S feature.) Windows programmers should still test for symbolic links +if applicable, so that programs will continue to work if later Windows versions add the feature, and also so that programs +will be portable to systems like POSIX, where symbolic links may be present.

      +
      +

      exists

      -

      bool exists( const path & ph );

      -

      Returns: True if the operating system reports the path -represented by ph exists, else false.

      -

      Note: Even if exists( ph ) == true, there is no guarantee that it -will be possible to perform other operations on the file or directory. Access -rights or other security concerns, for example, may cause other operations to -fail.

      -

      Note: exists("") is valid and returns false;

      -
      -

      symbolic_link_exists

      -
      -

      bool symbolic_link_exists( const path & ph );

      -

      Returns: True if the operating system reports the path represented by  -ph is present and is a symbolic link, else false.

      -

      Note: See the directory iterator -warning for one use of symbolic_link_exists().

      -

      Note: The Microsoft Windows operating system does not currently -support symbolic links, so symbolic_link_exists() always returns -false on that platform. (Short-cut files (.lnk) are a Windows application -feature, not an O/S feature.) Programmers should still test for symbolic links -where applicable in case Windows adds the feature, and also so that programs -will be portable to systems like POSIX, where symbolic links may be present.

      -

      Rationale: The function does not throw if ph is not present, -and is accordingly named symbolic_link_exists rather than -is_symbolic_link. Non-throwing behavior permits testing for all four -possible conditions:

      -
        -
      • ph not present: !exists(ph) && !symbolic_link_exists(ph)
      • -
      • ph present and is not a symbolic link: exists(ph) && !symbolic_link_exists(ph)
      • -
      • ph present and is a symbolic link to a non-existent file or - directory: !exists(ph) && symbolic_link_exists(ph)
      • -
      • ph present and is a symbolic link to an existing file or - directory: exists(ph) && symbolic_link_exists(ph)
      • -
      +

      template<class Path> bool exists( const Path & ph );

      +

      Throws: If status(ph) == error_flag.

      +

      Returns: status(ph) != not_found_flag

      is_directory

      -

      bool is_directory( const path & ph );

      -

      Returns: True if the operating system reports the path represented by  -ph is a directory, else false.

      -

      Throws: if !exists(ph)

      -

      Rationale: Treating !exists(ph) as an exception rather -than just returning false came about because in real code !exists(ph) -has -often been the first indicate of a programming error.  A compound function returning -exists(ph) && is_directory(ph) can always be added later.

      +

      template<class Path> bool is_directory( const Path & ph );

      +

      Throws: If status(ph) == error_flag.

      +

      Returns: (status(ph) & directory_flag) != 0

      +
      +

      is_file

      +
      +

      template<class Path> bool is_file( const Path & ph );

      +

      Throws: If status(ph) == error_flag.

      +

      Returns: (status(ph) & file_flag) != 0

      +
      +

      is_other

      +
      +

      template<class Path> bool is_other( const Path & ph );

      +

      Throws: If status(ph) == error_flag.

      +

      Returns: (status(ph) & other_flag) != 0

      +
      +

      is_symlink

      +
      +

      template<class Path> bool is_symlink( const Path & ph );
      +template<class DirItr> bool is_symlink( const DirItr & ph );

      +

      See performance note for subtle + differences between behavior of Path and DirItr template signatures.

      +

      Throws: If symlink_status(ph) == error_flag.

      +

      Returns: symlink_status(ph) == symlink_flag)

      is_empty

      -

      bool is_empty( const path & ph );

      +

      template<class Path> bool is_empty( const Path & ph );

      Returns: True if the operating system reports the path represented by  ph is an empty file or empty directory, else false.

      Throws: if !exists(ph)

      This function determines if the file or directory identified by the contents of ph is empty. To determine if a path string itself is empty, - use the path::empty() function.

      + use the basic_path::empty() function.

      equivalent

      -
      -
      bool equivalent( const path & ph1, const path & ph2 );
      +
      template<class Path> bool equivalent( const Path & ph1, const Path & ph2 );
      +

      Returns: false if !exists(ph1) || !exists(ph2). Otherwise, returns true if ph1 and ph2 resolve to the same file - or directory, else false. The criteria used to determine sameness - are implementation defined:

      -
      -
        -
      • POSIX: stat() reports identical st_dev, st_ino, st_size, and st_mtime - values. The POSIX implementation does not protect against - race conditions.
      • -
      • Windows: GetFileInformationByHandle() reports identical dwVolumeSerialNumber, - nFileIndexHigh, nFileIndexLow, nFileSizeHigh, nFileSizeLow values, - ftLastWriteTime.dwLowDateTime, and ftLastWriteTime.dwHighDateTime. - Note that for identical media (particularly bit-for-bit duplicate CD's, - Floppies, or memory cards) equivalent() will return true even though the - media are physically different. The Windows implementation does protect - against race conditions.
      • -
      -
      + or directory, else false.

      +

      The criteria used to determine sameness is as if + [POSIX-01] stat() reports identical st_dev, st_ino, st_size, and st_mtime + values.

      +

      Note: The POSIX implementation does not protect against + race conditions. POSIX requires + that "st_dev must be unique within a Local Area Network".

      +

      Note: The Windows implementation protects against race conditions. + As a surrogate for [POSIX-01] stat(), the + Windows implementation uses GetFileInformationByHandle(), and considers + "same" to be equal values forl dwVolumeSerialNumber, + nFileIndexHigh, nFileIndexLow, nFileSizeHigh, nFileSizeLow, ftLastWriteTime.dwLowDateTime, and ftLastWriteTime.dwHighDateTime. + Thus for identical media (such as bit-for-bit duplicate CD's, floppies, or memory cards) equivalent() will return true + even though the media are physically different.

      Throws: if !exists(ph1) && !exists(ph2)

      Warning: This function may be impossible to implement on some operating systems; users may wish to avoid use for code which may be ported to @@ -311,11 +469,10 @@ exists(ph) && is_directory(ph) can always be added later.

      file_size

      -

      boost::intmax_t file_size( const path & ph );

      +

      template<class Path> boost::intmax_t file_size( const Path & ph );

      Returns: The size of the file in bytes as reported by the operating system.

      -

      Throws: if !exists(ph) || is_directory(ph) || the file - size cannot be determined (such as for an input stream).

      +

      Throws: if !is_file(ph)

      Warming: If a compiler does not support maxint_t large enough to represent the operating system's maximum file size, or if the implementation does not know how to query the operating system for large file @@ -331,22 +488,22 @@ function which iterates over a directory returning a count.

      last_write_time

      +

      template<class Path> std::time_t last_write_time( const Path & ph );

      Warning: The times associated with files are subject to many -vicissitudes. Each type of filesystem differs slightly in the details and +vicissitudes. Each type of file system differs slightly in the details and resolution of how times are recorded. The resolution is as low as one hour on -some filesystems. It is not uncommon for a program to +some file systems. It is not uncommon for a program to simultaneously have access to files maintained by FAT, ISO9660, NTFS, and POSIX -filesystems, and so experience different last_write_time behavior for different +file systems, and so experience different last_write_time behavior for different files. During program execution, the system clock may be set to a new value by some other, possibly automatic, process. Another thread or process may -write to a file, causing the last write time to change unexpectedly.

      -

      std::time_t last_write_time( const path & ph );

      +write to a file, and this may also cause the last write time to change unexpectedly.

      Returns: The time the file was last modified, as reported by the operating system. If the time cannot be determined, returns (std::time_t)(-1).

      To convert the returned value to UTC or local time, use std::gmtime() or std::localtime() respectively.

      Throws: if !exists(ph)

      -

      void last_write_time( const path & ph, std::time_t new_time );

      +

      template<class Path> void last_write_time( const Path & ph, std::time_t new_time );

      Effects: Asks the operating system to set the last write time to new_time, or to the current time if new_time==std::time_t().

      Throws: if !exists(ph)

      @@ -356,19 +513,51 @@ systems.

      create_directory

      -

      bool create_directory( const path & directory_ph );

      +

      template<class Path> bool create_directory( const Path & directory_ph );

      Precondition: !directory_ph.empty()

      +

      Effects: If the postcondition is not already met, it is established as +if by [POSIX-01] mkdir() with a second +argument of S_IRWXU|S_IRWXG|S_IRWXO.

      Returns: The value of !exists( directory_ph ) prior to the establishment of the postcondition.

      -

      Postcondition: exists(directory_ph) && -is_directory(directory_ph)

      +

      Postcondition: is_directory(directory_ph)

      Throws: if directory_ph.empty() || (exists(directory_ph) && !is_directory(directory_ph)) || !exists(directory_ph/".."). See empty path rationale.

      +

      create_hard_link

      +
      +
      template<class Path> void create_hard_link(
      +   const Path & existing_file_ph, const Path & new_file_ph );
      +

      Precondition: exists(existing_file_ph) && !is_directory(existing_file_ph)
      +   && !exists(new_file_ph) && !is_symlink(new_file_ph)

      +

      Effects: The postcondition is established, as if by +[POSIX-01] link().

      +

      Postconditions:

      +
        +
      • exists(existing_file_ph) && exists(new_file_ph)
        +   && equivalent( existing_file_ph, new_file_ph )
      • +
      • The contents of the file existing_file_ph are unchanged.
      • +
      +

      Remarks: As with all Boost.Filesystem functions, + basic_filesystem_error will be thrown if the + operation fails. See Common + Specifications. The exact causes of possible failures vary with the + operating system. For example,  some file systems do not support hard + links at all, even on operating systems with hard link support. Many + operating systems do not permit hard links between file systems, and many + limit the number of links per file to a fairly small value (1023, on Windows NTFS, + for example). See [POSIX-01] link() + specifications for a complete + list of POSIX errors which may be encountered.

      +

      Rationale: Hard links to directories are not allowed because they + are too non-portable.

      +

      remove

      -

      bool remove( const path & ph );

      +

      template<class Path> bool remove( const Path & ph );

      Precondition: !ph.empty()

      +

      Effects: If the postcondition is not already met, it is established as +if by [POSIX-01] remove().

      Returns: The value of exists( ph ) prior to the establishment of the postcondition.

      Postcondition: !exists( ph )

      @@ -393,9 +582,9 @@ was changed to reflect user complaints.

      remove_all

      -

      unsigned long remove_all( const path & ph );

      +

      template<class Path> unsigned long remove_all( const Path & ph );

      Precondition: !ph.empty()

      -

      Postcondition: !exists( ph ) && !symbolic_link_exists( ph )

      +

      Postcondition: !exists( ph ) && !is_symlink( ph )

      Returns: The number of files and directories removed.

      Throws: if ph.empty(). See empty path rationale.

      @@ -404,11 +593,12 @@ themselves deleted, rather than what they point to being deleted.

      rename

      -

      void rename( const path & source, const path & target +

      template<class Path> void rename( const Path & old_ph, const Path & new_ph );

      -

      Precondition: !source.empty() && !target.empty()

      -

      Effects: Changes the name of  file or directory source -to target. Specifically:

      +

      Precondition: !old_ph.empty() && !new_ph.empty()

      +

      Effects: As if by [POSIX-01] +rename(), changes the name of  file or directory old_ph +to new_ph. Specifically:

      @@ -419,11 +609,11 @@ to target. Specifically:

      + covers the old_ph.empty() case. [case 1] - + @@ -435,33 +625,33 @@ to target. Specifically:

      - + The old_ph.leaf() name is changed to + new_ph.leaf(). [case 4B]
      + If old_ph / ".." resolves to a different directory than + new_ph / "..", the renamed old_ph file is moved there. [case 4C] - + The old_ph.leaf() name is changed to + new_ph.leaf(). [case 5B]
      + If system_complete(old_ph.banch_path()) resolves to a + different directory than system_complete(new_ph.branch_path()),  the + renamed old_ph directory is moved there. [case 5C]
      Source !exists()   Throw filesystem_error. Note that !exists() - covers the source.empty() case. [case 1]
       target.empty()new_ph.empty() Throw filesystem_error. See create_directory() rationale. [case 2]
      !is_directory()  If !exists( target / ".." ) throw + If !exists( new_ph / ".." ) throw filesystem_error. [case 4A]
      - The source.leaf() name is changed to - target.leaf(). [case 4B]
      - If source / ".." resolves to a different directory than - target / "..", the renamed source file is moved there. [case 4C]
      is_directory()  If !exists( target / ".." ) throw + If !exists( new_ph / ".." ) throw filesystem_error. [case 5A]
      - The source.leaf() name is changed to - target.leaf(). [case 5B]
      - If system_complete(source.banch_path()) resolves to a - different directory than system_complete(target.branch_path()),  the - renamed source directory is moved there. [case 5C]
      -

      Postconditions: !exists(source) && exists(target), -and the source file or directory contents and attributes are otherwise unchanged.

      +

      Postconditions: !exists(old_ph) && exists(new_ph), +and the old_ph file or directory contents and attributes are otherwise unchanged.

      Throws: See Effects table above. See empty path rationale.

      -

      Rationale: Because rename is logically the same operation as move, -there is no need for a separate move function. The choice of the name is -based on existing practice in the C, C++, and POSIX libraries, and because the -name move implies physical file movement, which does not in fact occur.

      +

      Rationale: A separate move function is not provided because rename is logically the same operation as move,. The choice of the +rename name is based on existing practice in the C, C++, and POSIX +libraries. Existence of new_ph is considered an error because that is +safer that removing an existing new_ph.

      Note: Some operating systems with multiple roots do not allow rename operations between roots, and such an attempted rename with throw a @@ -473,20 +663,20 @@ themselves renamed, rather than what they point to being renamed.

      copy_file

      -

      void copy_file( const path & source_file, const path & -target_file );

      -

      Precondition: !source.empty() && !target.empty()

      -

      Effects: Copies the file represented by source_file to -target_file.

      -

      Throws: if !exists(source_file) || is_directory(source_file) -|| exists(target_file) || target_file.empty() || !exists(to_file_path/"..")). +

      template<class Path> void copy_file( const Path & old_ph_file, const +Path & new_ph_file );

      +

      Precondition: !old_ph.empty() && !new_ph.empty()

      +

      Effects: Copies the file represented by old_ph_file to +new_ph_file.

      +

      Throws: if !exists(old_ph_file) || is_directory(old_ph_file) +|| exists(new_ph_file) || new_ph_file.empty() || !exists(to_file_path/"..")). See empty path rationale.

      Note: File attributes are also copied. Specifically, POSIX stat::st_mode, and Windows BY_HANDLE_FILE_INFORMATION::dwFileAttributes.

      initial_path

      -

      const path & initial_path();

      +

      template<class Path> const Path & initial_path();

      Effects: The first time called, stores the path returned by current_path().

      The preferred implementation would be to call initial_path() during program @@ -506,11 +696,10 @@ initial_path().

      current_path

      -
      path current_path();
      -

      Returns: The current path as maintained by the operating system.

      +
      template<class Path> Path current_path();
      +

      Returns: The current path, as if by [POSIX-01] + getcwd().

      Postcondition: current_path().is_complete()

      -

      Note: The equivalent POSIX function is getcwd(). The - equivalent Windows function is GetCurrentDirectoryA().

      Warning: The current path maintained by the operating system is in-effect a dangerous global variable. It may be changed unexpectedly by a third-party or system library function, or by another thread. For a safer @@ -522,9 +711,9 @@ initial_path().

      complete

      -

      path complete( const path & ph, const path & base = initial_path() );

      +

      template<class Path> Path complete( const Path & ph, const Path & base = initial_path<Path>() );

      -

      Precondition: !ph.empty() && base.is_complete() && (ph.is_complete() || !ph.has_root_name())

      +

      Precondition: base.is_complete() && (ph.is_complete() || !ph.has_root_name())

      Effects: Composes a complete path from ph and base, using the following rules:

      @@ -588,7 +777,7 @@ root_name() equivalence requirement.

      system_complete

      -

      path system_complete( const path & ph );

      +

      template<class Path> Path system_complete( const Path & ph );

      Precondition: !ph.empty()

      @@ -630,7 +819,7 @@ by a prior program run by the command processor.


      Revised -02 August, 2005

      +22 July, 2005

      © Copyright Beman Dawes, 2002

      Use, modification, and distribution are subject to the Boost Software License, Version 1.0. (See accompanying file diff --git a/doc/path.htm b/doc/path.htm index 5063197..063600a 100644 --- a/doc/path.htm +++ b/doc/path.htm @@ -11,31 +11,32 @@

      -boost/filesystem/path.hpp

      +boost/filesystem/basic_path.hpp

      Introduction
      Grammar for generic path strings
      -Canonical form
      +Normalized Form
      Header synopsis
      -Class path
      -    Native path -representation
      +Class basic_path
      +    +System-specific path representation
          Representation example
          Caution for POSIX and UNIX programmers
          Good programming practice: relative paths
          Path equality vs path equivalence
      +    User-defined path example
      Member functions
      Non-member functions
      -Default name_check mechansim
      Rationale
      Path decomposition examples

      Introduction

      -

      Filesystem Library functions traffic in objects of class path, -provided by this header. The header also supplies non-member functions for error checking.

      +

      Boost.Filesystem traffics in objects of class template basic_path, +provided by this header. The header also declares functions for error checking +and class template basic_filesystem_error.

      For actual operations on files and directories, see boost/filesystem/operations.hpp documentation.

      @@ -47,22 +48,26 @@ documentation.

      Specifications apply to all member and non-member functions supplied by this header.

      -

      The Portability Guide discusses path +

      The Portability Guide discusses +basic_path naming issues which are important when portability is a concern.

      -

      Class path

      -

      Class path provides for portable mechanism for representing +

      Class basic_path

      +

      Class template basic_path provides for portable mechanism for representing paths in C++ programs, using a portable generic -path string grammar. Class path +path string grammar. When portability is not a +requirement, native file system specific formats can be used. Class +basic_path is concerned with the lexical and syntactic aspects of a path. The path does not have to exist in the operating system's -filesystem, and may contain names which are not even valid for the current +file system, and may contain names which are not even valid for the current operating system.

      -

      Rationale: If Filesystem functions trafficked in std::strings or C-style strings, the +

      Rationale: If the library's functions trafficked in std::string or C-style strings, the functions -would provide only an illusion of portability since the function calls would be -portable but the strings they operate on would not be portable.

      +would provide only an illusion of portability since while the syntax of function calls would be +portable, the semantics of the strings they operate on would not be portable. A +number of supporting functions are also neatly packaged in class basic_path.

      Conceptual model of a path

      -

      An object of class path can be conceptualized as containing a sequence +

      An object of class basic_path can be conceptualized as containing a sequence of strings. Each string is said to be an element of the path. Each element represents the name of a directory, or, in the case of the string representing the element farthest from the root in the directory @@ -71,116 +76,155 @@ hierarchy, the name of a directory or file. The names ".."directory-placeholder.

      This conceptual path representation is -independent of any particular representation of the path as a single +independent of any particular operating system's representation of the path as a single string.

      -

      There is no requirement that an implementation of class path actually +

      There is no requirement that an implementation of class basic_path actually contain a sequence of strings, but conceptualizing the contents as a sequence of strings provides a completely portable way to reason about paths.

      -

      So that programs can portably express paths as a single string, class path +

      So that programs can portably express paths as a single string, class +basic_path defines a grammar for a portable generic path string format, and supplies constructor and append operations taking such strings as -arguments. Because user input or third-party library functions may supply path -strings formatted according to operating system specific rules, an additional -constructor is provided which takes a system-specific format as an argument.

      +arguments.

      Access functions are provided to retrieve the contents of a object of class -path formatted as a portable path string, a directory path string using +basic_path formatted as a portable path string, a directory path string using 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 strings

      -

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

      Grammar for portable generic basic_path strings

      +

      The grammar is as specified in + [POSIX-01]  3.266 Pathname, 3.169 Filename, and 4.11 +Pathname Resolution. This is the familiar left-to-right, "/" separated,  +list of names used by POSIX, Windows, and Internet URL's.

      +

      The grammar can be described by the following +Extended BNF, with terminal symbols in quotes:

      path ::= [root] [relative-path]  // an empty path is valid
      -
      root ::= [root-name] [root-directory]
      -
      root-directory ::= separator
      -
      relative-path ::= path-element { separator path-element } [separator]
      +
      root ::= [root-name] [root-directory] | separator separator {separator} name
      +
      root-directory ::= separator { separator }
      +
      relative-path ::= path-element { separator {separator} path-element } {separator}
      path-element ::= name | parent-directory | directory-placeholder
      name ::= char { char }
      directory-placeholder ::= "."
      parent-directory ::= ".."
       
      -separator ::= "/"  // an implementation may define additional separators 
      +separator ::= "/" // an implementation may define additional alternate separators
-

root-name grammar is implementation-defined. -root-name must not be present in generic input. It may be part of the strings returned by path -member functions, and may be present in the src argument to path constructors -when the native name check is in effect.

-

char may not be slash ('/') or '\0'. In additional, many operating and -file systems may place additional restrictions on the characters which may +

Although it isn't used very often,  + [POSIX-01] specifies that multiple +separators be treated as a single separator, and that is reflected in the EBNF +grammar.

+

root-name grammar is implementation-defined for the particular +operating system. For example, it is used to represent drive specifiers for the +Window operating system. It is not used for POSIX implementations.

+

char may not be slash ('/') or '\0'. In additional, operating and +file systems place additional restrictions on the characters which may appear in names. See File and Directory Name Recommendations.

Although implementation-defined, it is desirable that root-name 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 simply discarded.

+

The meaning, if any, of a root in the form separator separator name +is operating system dependent.

+

An optional trailing "/" is allowed in a relative-basic_path. It +has no particular meaning to class basic_path. It may or may not have meaning +to operations functions, depending on the particular operating system. It will +be ignored by operations functions on operating systems for which a trailing "/" +is ignored or invalid.

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

-

Canonical form

-

All operations modifying path objects leave the path object in -canonical form.

-

An empty path is in canonical form.

-

A non-empty path is converted to canonical form as if by first converting it -to the conceptual model, and then:

-
    -
  • Repeatedly replacing any leading root-directory, parent-directory - elements with a single root-directory element. Rationale: Both POSIX - and Windows specify this reduction; specifying it for canonical form ensures - portable semantics for other operating systems.
  • -
  • Removing each directory-placeholder element.
  • -
  • If the path is now empty, add a single directory-placeholder - element.
  • -

Normalized form

-

Normalized form is the same as canonical form, except that adjacent name, parent-directory -elements are recursively removed.

+

In Normalized form, adjacent name, parent-directory +elements are recursively removed. Except in a single element path, +directory-placeholders are removed.

Thus a non-empty path in normal form either has no directory-placeholders, or consists solely of one directory-placeholder. If it has parent-directory elements, they precede all name elements.

Header -boost/filesystem/path.hpp synopsis

+boost/filesystem/basic_path.hpp synopsis +

See Error Handling for portion of synopsis +dealing with exceptions and error reporting.

namespace boost
 {
   namespace filesystem
   {
-    class path
+    template<class String, class Traits> class basic_path;
+
+    struct path_traits;
+    struct wpath_traits;
+
+    typedef basic_path< string_type, path_traits > path;
+    typedef basic_path< std::wstring, wpath_traits > wpath;
+
+    struct path_traits
+    {
+      typedef std::string internal_string_type;
+      typedef implementation-defined external_string_type;
+      static external_string_type to_external( const path &, const internal_string_type & src );
+      static internal_string_type to_internal( const external_string_type & src );
+    };
+
+    struct wpath_traits
+    {
+      typedef std::wstring internal_string_type;
+      typedef implementation-defined external_string_type;
+      static external_string_type to_external( const path &, const internal_string_type & src );
+      static internal_string_type to_internal( const external_string_type & src );
+      static void imbue( const std::locale & loc );
+      static bool imbue( const std::locale & loc, const std::nothrow_t & );
+    private:
+      static bool m_locked = false; // for exposition only
+      static std::locale m_locale = std::locale(""); // for exposition only
+    };
+
+    template<class String, class Traits>
+    class basic_path
     {
     public:
-      typedef bool (*name_check)( const std::string & name );
+      typedef basic_path<String, Traits> path_type;
+      typedef String string_type;
+      typedef Traits traits_type;
+      typedef typename Traits::external_string_type external_string_type; 
 
-      // compiler generates copy constructor,
-      // copy assignment, and destructor
+      // compiler generates copy constructor and copy assignment
 
       // constructors:
-      path();
-      path( const std::string & src );
-      path( const char * src );
-      path( const std::string & src, name_check checker );
-      path( const char * src, name_check checker );
+      basic_path();
+      basic_path( const string_type & src );
+      basic_path( const char * src );
+
+      ~basic_path();
 
       // append operations:
-      path & operator /= ( const path & rhs );
-      path   operator /  ( const path & rhs ) const;
+      basic_path & operator /= ( const basic_path & rhs );
+      basic_path & operator /= ( const string_type & rhs );
+      basic_path & operator /= ( const typename string_type::value_type * rhs );
+      basic_path   operator /  ( const basic_path & rhs ) const;
+      basic_path   operator /  ( const string_type & rhs ) const;
+      basic_path   operator /  ( const typename string_type::value_type * rhs ) const;
+
+      // modification functions:
+      basic_path & normalize();
+      basic_path & remove_leaf();
 
       // conversion functions:
-      const std::string & string() const;
-      std::string native_file_string() const;
-      std::string native_directory_string() const;
+      const string_type &        string() const;
+      const string_type          file_string() const;
+      const string_type          directory_string() const;
+      const external_string_type external_file_string() const;
+      const external_string_type external_directory_string() const;
       
-      // modification functions:
-      path &      normalize();
-
       // decomposition functions:
-      path        root_path() const;
-      std::string root_name() const;
-      std::string root_directory() const;
-      path        relative_path() const;
-      std::string leaf() const;
-      path        branch_path() const;
+      basic_path  root_path() const;
+      string_type root_name() const;
+      string_type root_directory() const;
+      basic_path  relative_path() const;
+      string_type leaf() const;
+      basic_path  branch_path() const;
       
       // query functions: 
       bool empty() const;
@@ -194,27 +238,30 @@ boost/filesystem/path.hpp synopsis
       
       // iteration:
       typedef implementation-defined iterator;
+      typedef iterator const_iterator;
       iterator begin() const;
       iterator end() const;
 
-      // default name_check mechanism:
-      static bool default_name_check_writable(); 
-      static void default_name_check( name_check new_check );
-      static name_check default_name_check();
-
      // relational operators:
-      bool operator==( const path & that ) const;
-      bool operator!=( const path & that ) const;
-      bool operator<( const path & that ) const;
-      bool operator<=( const path & that ) const;
-      bool operator>( const path & that ) const;
-      bool operator>=( const path & that ) const;
+      // relational operators:
+      bool operator==( const basic_path & that ) const;
+      bool operator!=( const basic_path & that ) const;
+      bool operator<( const basic_path & that ) const;
+      bool operator<=( const basic_path & that ) const;
+      bool operator>( const basic_path & that ) const;
+      bool operator>=( const basic_path & that ) const;
 
     private:
-      std::vector<std::string> m_name;  // for exposition only
+      std::vector<string_type> m_name;  // for exposition only
+      bool m_trailing_separator;        // for exposition only
     };
 
-    path operator / ( const char * lhs, const path & rhs );
-    path operator / ( const std::string & lhs, const path & rhs );
+    template<class String, class Traits>
+    basic_path<String, Traits>
+    operator/( const String & lhs, const basic_path<String, Traits> & rhs );
+
+    template<class String, class Traits>
+    basic_path<String, Traits>
+    operator/( const typename String::value_type * lhs, const basic_path<String, Traits> & rhs );
 
     // name_check functions
     bool portable_posix_name( const std::string & name );
@@ -222,228 +269,379 @@ boost/filesystem/path.hpp synopsis
     bool portable_name( const std::string & name );
     bool portable_directory_name( const std::string & name );
     bool portable_file_name( const std::string & name );
-    bool no_check( const std::string & name );
     bool native( const std::string & name );
+
+    // is_basic_path trait class
+    template<class Path> struct is_basic_path { static constant bool value = false; };
+
+    // specializations for library supplied basic_path instances
+    // ( must also be specialized by user for user supplied basic_path types )
+    template<> struct is_basic_path<path> { static constant bool value = true; };
+    template<> struct is_basic_path<wpath> { static constant bool value = true; };
   }
 }
-

For the sake of exposition, class path member functions are described -as if the class contains a private member std::vector<std::string> m_name. +

basic_path and related classes

+

String template arguments shall support the same set of well-formed +expressions as std::basic_string.

+

Traits template arguments shall support as well-formed the expressions in the following +table, where p is an object of type +basic_path<String, Traits>, s is an object of type +String, and loc is an object of type std::locale.

+ + + + + + + + + + + + + + + + + + + + + + + + +
Traits + Requirements
ExpressionType
Traits::internal_string_typeImplementation defined
Traits::external_string_typeImplementation defined
Traits::to_internal(p, s)internal_string_type
Traits::to_external(p, s)external_string_type
+

Class path is supplied with the following traits members:

+ + + + + + + + + + + + + + + + + + + + + + + + +
+ Struct path_traits
MemberBehavior
internal_string_typestd::string
external_string_typeFor POSIX and Windows: std::string
to_internal(p, s)Returns: s
to_external(p, s)Returns: s
+

For POSIX implementations, class wpath is supplied with the following traits members:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Struct wpath_traits for POSIX
MemberBehavior
internal_string_typestd::string
external_string_typeFor POSIX: std::string. For Windows: std::wstring
to_internal(p, s)Effects: m_locked = true;
+ Returns:
s, converted by m_locale's codecvt facet + to the internal string type and encoding.
to_external(p, s)Effects: m_locked = true;
+ Returns:
s, converted by m_locale's codecvt facet + to the internal string type and encoding.
imbue(loc)Throws: if m_locked == true.
+ Effects: m_locked = true; m_locale = loc;
+
Returns: void
imbue(loc, std::nothrow) + Effects: if (!m_locked) m_locale = loc; bool temp(m_locked); m_locked = true;
+
Returns: temp
+

For Windows implementations, class wpath is supplied with the following traits members:

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
+ Struct wpath_traits for + Windows
MemberBehavior
internal_string_typestd::string
external_string_typeFor POSIX: std::string. For Windows: std::wstring
to_internal(p, s)Effects: m_locked = true;
+ Returns:
s
to_external(p, s)Effects: m_locked = true;
+ Returns:
s
imbue(loc)Throws: if m_locked == true.
+ Effects: m_locked = true;
+
Returns: void
imbue(loc, std::nothrow) + Effects: bool temp(m_locked); m_locked = true;
+
Returns: temp
+

For an example of a user supplied Traits class, see +lpath.hpp, which is used to test user defined +wide character support.

+

For the sake of exposition, certain member functions are described +as if the class contains the indicated private members. Actual implementations may differ.

-

Class path member, or non-member operator/, functions may throw a -filesystem_error exception if the path is not in the -syntax specified for the grammar.

-

Note: There is no guarantee that a path object represents a +

For functions string, file_string, directory_string, +external_file_string, and external_directory_string, implementations +are permitted to return either const or const & +values.

+

Note: There is no guarantee that a basic_path object represents a path which is considered valid by the current operating system. A path might be invalid to the operating system because it contains invalid names (too long, invalid characters, and so on), or because it is a partial path still as yet unfinished by the program. An invalid path will normally be detected at time of -use, such as by one of the Filesystem Library's +use, such as by one of the library's operations or fstream functions.

-

Portability Warning: There is no guarantee that a path object -represents a path which would be portable to another operating system. A path -might be non-portable because it contains names which the operating systems -considers too long or contains invalid characters. A -default name_check mechanism is provided to -aid in the detection of non-portable names, or a name_check function can be -specified in path constructors. The library supplies several +

Portability Warning: There is no guarantee that a basic_path object +represents a path which would be portable to another operating system or file +system. A path +might be non-portable because it contains names which the operating system +considers too long or containing invalid characters. To detect portability +problems, the library supplies several name_check -functions, or users can supply their own.

-

Native path representation

-

Several path member functions return representations of m_name -in formats specific to the operating system. These formats are implementation -defined. If an m_name +functions.

+

System-specific path +representation

+

Several basic_path member functions return representations of m_name +in formats specific to the operating system. For POSIX, the system specific +format is the same as the generic grammar. For Window, +the system specific format is the same as the generic grammar +except that the alternate separator (backslash) is always used. For other +operating systems, the system specific format is implementation +defined.

+

For operating systems other than POSIX and Windows, if an m_name element contains characters which are invalid under the operating system's -rules, and there is an unambiguous translation between the invalid character and -a valid character, the implementation is required to perform that translation. -For example, if an operating system does not permit lowercase letters in file or -directory names, these letters will be translated to uppercase if unambiguous. -Such translation does not apply to generic path string format representations.

+rules, and there is an unambiguous and reasonable translation between the +invalid character and a valid character, the implementation is required to +perform that translation. For example, if an operating system does not permit +lowercase letters in file or directory names, these letters must be translated +to uppercase if unambiguous. Such translation does not apply to the representation +returned by string().

Representation example

The rule-of-thumb is to use string() when a generic string representation of -the path is required, and use either -native_directory_string() or -native_file_string() when a string representation formatted for +the basic_path is required, and use either +directory_string() or +file_string() when a string representation formatted for the particular operating system is required.

The difference between the representations returned by string(), -native_directory_string(), and -native_file_string() are illustrated by the following +directory_string(), and +file_string() are illustrated by the following code:

path my_path( "foo/bar/data.txt" );
 std::cout
   << "string------------------: " << my_path.string() << '\n'
-  << "native_directory_string-: " << my_path.native_directory_string() << '\n'
-  << "native_file_string------: " << my_path.native_file_string() << '\n';
+ << "directory_string--------: " << my_path.directory_string() << '\n' + << "file_string-------------: " << my_path.file_string() << '\n';

On POSIX systems, the output would be:

string------------------: foo/bar/data.txt
-native_directory_string-: foo/bar/data.txt
-native_file_string------: foo/bar/data.txt
+directory_string--------: foo/bar/data.txt +file_string-------------: foo/bar/data.txt

On Windows, the output would be:

string------------------: foo/bar/data.txt
-native_directory_string-: foo\bar\data.txt
-native_file_string------: foo\bar\data.txt
+directory_string--------: foo\bar\data.txt +file_string-------------: foo\bar\data.txt

On classic Mac OS, the output would be:

string------------------: foo/bar/data.txt
-native_directory_string-: foo:bar:data.txt
-native_file_string------: foo:bar:data.txt
+directory_string--------: foo:bar:data.txt +file_string-------------: foo:bar:data.txt

On a hypothetical operating system using OpenVMS format representations, it would be:

string------------------: foo/bar/data.txt
-native_directory_string-: [foo.bar.data.txt]
-native_file_string------: [foo.bar]data.txt
+directory_string--------: [foo.bar.data.txt] +file_string-------------: [foo.bar]data.txt

Note that that because OpenVMS uses period as both a directory separator character and as a separator between filename and extension, -native_directory_string() +directory_string() 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 +programmer should only use this path as a file path. (There is also a portability recommendation to not use periods in directory names.)

Caution for POSIX and UNIX programmers

-

POSIX and other UNIX-like operating systems have a single root, while most other -operating systems have multiple roots. Multi-root operating systems require a +

POSIX and other UNIX-like operating systems have a single root, while many other +operating systems support multiple roots. Multi-root operating systems require a root-name -such as a drive, device, disk, volume, or share name for a path to be resolved +such as a drive, device, disk, volume, or network name for a basic_path to be resolved to an actual specific file or directory.  -Because of this, the root() and root_directory() functions return +Because of this, the root_path() and +root_directory() functions return identical results on UNIX and other single-root operating systems, but different results on multi-root operating 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-root systems. UNIX programmers are cautioned to use -particular care in choosing between root() and root_directory(). If -undecided, use root().

-

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

+particular care in choosing between root_path() and +root_directory(). If +undecided, use root_path().

+

The same warning applies to has_root_path() and +has_root_directory().

Good programming practice: relative paths

It is usually bad programming practice to hard-code complete paths into programs. Such programs tend to be fragile because they break when directory trees get reorganized or the programs are moved to other machines or operating systems.

-

The most robust way to deal with path completion is to hard-code only -relative paths. When a complete path is required, it can be obtained in several +

The most robust way to deal with basic_path completion is to hard-code only +relative paths. When a complete basic_path is required, it can be obtained in several ways:

  • Implicitly. Allow the operating system to complete the path according to the operating system's path completion algorithm. For example:
        create_directory( "foo" ); // operating system will complete path
  • -
  • User input. The path is often best - constructed with the native name check, so that the user input - follows the operating system's native path format, which will usually be what - the program user expects. For example:
        path foo( argv[1], native );
    -    foo /= "foo";
    -
  • initial_path(). Particularly for command line programs, specifying paths relative to the - current path at the time the program is started is a common practice. For - example:
        path foo( initial_path() / "foo" );
    + current directory's path at the time the program is started is a common practice. For + example:
        path foo( initial_path<path>() / "foo" );
  • Algorithmically. See complete() and system_complete() functions.

Path equality vs path equivalence

-

Are paths "abc" and "ABC" equal? No, never, if you determine equality via -class path's operator==, which considers only the two paths -lexical representations.

+

Are paths "abc" and "ABC" equal? No, never, when equality +is determined via +class basic_path's operator==, which considers the two path's +lexical representations only.

Do paths "abc" and "ABC" resolve to the same file or directory? The answer is -"yes", "no", or "maybe" depending on the external file system. The (pending) -operations function equivalent() is the only way to determine if two paths +"yes", "no", or "maybe" depending on the operating +system and external file system. The operations function +equivalent() determines if two paths resolve to the same external file system entity.

Programmers wishing to determine if two paths are "the same" must decide if -that means "the same representation" or "resolve to the same actual file or +"the same" means "the same representation" or "resolve to the same actual file or directory", and choose the appropriate function accordingly.

+

User-defined path example

+

The example class mbpath provides a wide-character +basic_path which uses a multi-byte character string as the external +representation. This is the same behavior as wpath on POSIX, but wpath +on Windows assumes wide character representation supplied by NTFS and other file +systems which support wide-character filenames. Class mbpath is thus +useful because it always works as intended, even on FAT or other +narrow-character name file systems. It is also useful when the same program must +support multiple locales.

+

See mbpath.hpp and +mbpath.cpp. For a use example, see +mbcopy.cpp.

Member functions

constructors

-
path();
-

Effects: Default constructs an object of class path.

-

Postcondition: path().empty()

-
path( const std::string & src, name_check checker );
-path( const char * src, name_check checker );
-path( const std::string & src );
-path( const char * src );
-

For the single-argument forms, default_name_check() is used as - checker.

+
basic_path();
+

Effects: Default constructs an object of class basic_path.

+

Postcondition: basic_path().empty()

+
basic_path( const string_type & src );
+basic_path( const char * src );

Precondition: src != 0.

-

Effects: Select the grammar as follows:

- -

Parse src into a sequence of names, according to the grammar, then, for each name in srcm_name.push_back( - name ).

-

Throws: For each name in src, throw if checker( - name ) returns - false.

-

Postcondition: m_name is in - canonical form. For the single-argument forms only, !default_name_check_writable().

+

Effects: For each element in src, as determined by the + grammar, m_name.push_back(element). Set + m_trailing_separator to true if src has a trailing + separator, else false.

Rationale: The single-argument constructors are not explicit because an intended use is automatic conversion of strings to paths.

operator /=

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

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

-
    -
  • has_relative_path().
  • -
  • !is_absolute() && has_root_name(), and the operating system - requires the system-specific root be absolute
  • -
-

 Then append rhs.m_name to m_name.

-

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

+
basic_path & operator/=( const basic_path & rhs );
+basic_path & operator /=( const string_type & rhs );
+basic_path & operator /=( const typename string_type::value_type * rhs );
+

Effects: For each element in rhs, as determined by the + grammar, m_name.push_back(element). Set + m_trailing_separator to true if rhs has a trailing + separator, else false.

Returns: *this

-

Postcondition: m_name is in - canonical form.

-

Rationale: It is not considered an error for rhs to - include a root-directory because m_name might be relative - or empty, and - thus it is valid for rhs to supply root-directory.  For example, on Windows, the following must succeed:

-
-
path p( "c:", native );
-p /= "/foo";
-assert( p.string() == "c:/foo" );
-

operator /

-
const path operator/ ( const path & rhs ) const;
-

Returns: path( *this ) /= rhs

+
basic_path operator/( const basic_path & rhs ) const;
+basic_path operator/( const string_type & rhs ) const;
+basic_path operator/( const typename string_type::value_type * rhs ) const;
+

Returns: basic_path( *this ) /= rhs

Rationale: Operator / is supplied because together with operator /=, it provides a convenient way for users to supply paths with a variable number of elements.  - For example, initial_path() / "src" / test_name. - Operator+ and operator+= were considered as alternatives, but deemed too - easy to confuse with those operators for std::string. Operator<< and - operator=<< were used originally until during public review Dave - Abrahams pointed out that / and /= - match the generic path syntax.

+ For example, initial_path<path>() / "src" / src_name. + Operator+ and operator+= were considered as alternative names, but deemed too + easy to confuse with those operators for std::basic_string. Operator<< and + operator=<< were used until Dave Abrahams pointed out that / and /= + match the generic path syntax.

Note: Also see non-member operator/ functions.

normalize

-

path & normalize();

+

basic_path & normalize();

Postcondition: m_name is in normalized form.

Returns: *this

+

remove_leaf

+
+
basic_path & remove_leaf();
+

Effects: If has_branch_path() then + m_name.pop_back().

+

Returns: *this

+

Rationale: This function is needed to implement directory iterators. + It is made public so other uses can benefit from the functionality.

+

string

-
const std::string & string() const;
-

Returns: The contents of m_name, formatted according to -the rules of the generic path string grammar.

+
const string_type & string() const;
+

Returns: The contents of m_name, formatted as a string according to +the rules of the grammar, with a separator appended if +m_trailing_separator is true.

Note: The returned string must be unambiguous according to the grammar. That means that for an operating system with root-names indistinguishable from relative-path names, names containing "/", or allowing "." or ".." as @@ -453,38 +651,51 @@ current implementations are on operating systems with any of those problems.

See: Representation example above.

-

native_file_string

+

file_string

-
std::string native_file_string() const;
+
const string_type file_string() const;

Returns: The contents of m_name, formatted in the -native representation of -a file path.

+system-specific representation of +a file path.

See: Representation example above.

-

Naming rationale: The name is deliberately ugly to warn users that -this function yields non-portable results.

-

native_directory_string

+

directory_string

-
const std::string native_directory_string() const;
+
const string_type directory_string() const;

Returns: The contents of m_name, formatted in the -native representation of -a directory path.

+system-specific representation of +a directory path.

See: Representation example above.

-

Naming rationale: The name is deliberately ugly to warn users that -this function yields non-portable results.

+
+

external_file_string

+
+
const external_string_type external_file_string() const;
+

Returns: file_string(), converted to the type and + encoding of external_string_type.

+

Using wpath as an example, on Linux the external string type will be + string_type, and the external encoding will normally be + UTF-8. On Windows the wpath internal and + external string types are both std::wstring, and the encoding of both + is UTF-16.

+
+

external_directory_string

+
+
const external_string_type external_directory_string() const;
+

Returns: directory_string(), converted to the type and + encoding of external_string_type.

root_path

-
path root_path() const;
+
basic_path root_path() const;

Returns: root_name() / root_directory()

-

Portably provides a copy of a path's full root path, if any. See +

Portably provides a copy of a path's full root path, if any. See Path decomposition examples.

root_name

-
std::string root_name() const;
+
string_type root_name() const;

Returns: If !m_name.empty() && m_name[0] is a root-name, returns m_name[0], else returns a null string.

@@ -493,44 +704,44 @@ if any. See Path decomposition examples.

root_directory

-
std::string root_directory() const;
-

Returns: If the path contains root-directory, - then string("/"), else string().

+
string_type root_directory() const;
+

Returns: If the path contains root-directory, + then a string containing "/", else string().

Portably provides a copy of a path's root-directory, - if any. The only possible results are "/" or "". See + if any. See Path decomposition examples.

relative_path

-
path relative_path() const;
-

Returns: A new path containing only the - relative-path portion of the source path.

+
basic_path relative_path() const;
+

Returns: A new path containing only the + relative-path portion of the source path.

Portably provides a copy of a path's relative portion, if any. See Path decomposition examples.

leaf

-
std::string leaf() const;
-

Returns: empty() ? std::string() : m_name.back()

-

A typical use is to obtain a file or directory name without path information -from a path returned by a +

string_type leaf() const;
+

Returns: empty() ? string_type() : m_name.back()

+

A typical use is to obtain a file or directory name without path information +from a path returned by a directory_iterator. See Path decomposition examples.

branch_path

-
path branch_path() const;
-

Returns: m_name.size() <= 1 ? path("") : x, where x -is a path constructed from all the elements of m_name except the +

basic_path branch_path() const;
+

Returns: m_name.size() <= 1 ? path_type("") : x, where x +is a path constructed from all the elements of m_name except the last.

-

A typical use is to obtain the parent path for a path supplied by the user. +

A typical use is to obtain the parent path for a path supplied by the user. See Path decomposition examples.

empty

bool empty() const;

Returns: string().empty().

-

The path::empty() function determines if a path string itself is - empty. To determine if the file or directory identified by the path is empty, +

Determines if a path string itself is + empty. To determine if the file or directory identified by the path is empty, use the operations.hpp is_empty() function.

Naming rationale: C++ Standard Library containers use the empty @@ -547,8 +758,8 @@ See Path decomposition examples.

believe root_name() should participate in is_absolute(), and some don't. See the FAQ.

Note: On most operating systems, a - complete path always unambiguously identifies a specific file or directory. On a few - systems (classic Mac OS, for example), even a complete path may be ambiguous + complete path always unambiguously identifies a specific file or directory. On a few + systems (classic Mac OS, for example), even a complete path may be ambiguous in unusual cases because the OS does not require unambiguousness.

has_root_path

@@ -583,11 +794,14 @@ See Path decomposition examples.

iterator

-

typedef implementation-defined iterator;

-

A const iterator meeting the C++ Standard Library requirements for bidirectional +

typedef implementation-defined iterator;
+typedef iterator const_iterator;

+

A constant iterator meeting the C++ Standard Library requirements for bidirectional iterators (24.1). The iterator is a class type (so that operator++ and -- will -work on temporaries). The value, reference, and pointer types are std::string, -const std::string &, and const std::string *, respectively.

+work on temporaries). The value, reference, and pointer types are std::basic_string<>, +const std::basic_string<> &, and const std::basic_string<> *, respectively.

+

Note that non-root paths ending with a slash ("foo/") are treated as if there +was a trailing "." ("foo/."), per the POSIX and Windows specifications.

begin

@@ -599,40 +813,24 @@ work on temporaries). The value, reference, and pointer types are std::string

iterator end() const;

Returns: m_path.end()

-

default_name_check_writable

-
-

static bool default_name_check_writable();

-

Returns: True, unless a default_name_check function has been -previously called.

-
-

default_name_check

-
-

static void default_name_check( name_check new_check );

-

Precondition: new_check != 0

-

Postcondition: default_name_check(new_check) && !default_name_check_writable()

-

Throws: if !default_name_check_writable()

-

static name_check default_name_check();

-

Returns: the default name_check.

-

Postcondition: !default_name_check_writable()

-

operator ==

-
bool operator==( const path & that ) const;
+
bool operator==( const basic_path & that ) const;

Returns: !(*this < that) && !(that < *this)

-

See Path equality vs path equivalence.

+

See Path equality vs basic_path equivalence.

operator !=

-
bool operator!=( const path & that ) const;
+
bool operator!=( const basic_path & that ) const;

Returns: !(*this == that)

-

See Path equality vs path equivalence.

+

See Path equality vs basic_path equivalence.

operator <

-
bool operator<( const path & that ) const;
+
bool operator<( const basic_path & that ) const;

Returns: std::lexicographical_compare( begin(), end(), that.begin(), that.end() )

-

See Path equality vs path equivalence.

+

See Path equality vs basic_path equivalence.

Rationale: Relational operators are provided to ease uses such as specifying paths as keys in associative containers. Lexicographical comparison is used because:

@@ -640,103 +838,88 @@ previously called.

  • Even though not a full-fledged standard container, paths are enough like containers to merit meeting the C++ Standard Library's container comparison requirements (23.1 table 65).
  • -
  • The alternative is to return this->string(), that.string(). - But path::string() as currently specified can yield non-unique results for - differing paths. The case (from Peter Dimov) is path("first/")/"second" - and path("first")/"second" both returning a string() of - "first//second".
  • +
  • The alternative is to return this->string() < that.string(). + But basic_path::string() as currently specified can yield non-unique results for + differing paths. The case (from Peter Dimov) is basic_path("first/")/"second" + and basic_path("first")/"second" both returning a string() of + "first/second". [TODO: reanalyze this. It doesn't + make sense anymore Did something change?]
  • operator <=

    -
    bool operator<=( const path & that ) const;
    +
    bool operator<=( const basic_path & that ) const;

    Returns: !(that < *this)

    -

    See Path equality vs path equivalence.

    +

    See Path equality vs basic_path equivalence.

    operator >

    -
    bool operator>( const path & that ) const;
    +
    bool operator>( const basic_path & that ) const;

    Returns: that < *this

    -

    See Path equality vs path equivalence.

    +

    See Path equality vs basic_path equivalence.

    operator >=

    -
    bool operator>=( const path & that ) const;
    +
    bool operator>=( const basic_path & that ) const;

    Returns: !(*this < that)

    -

    See Path equality vs path equivalence.

    +

    See Path equality vs basic_path equivalence.

    Non-member functions

    Non-member operator /

    -

    path operator / ( const char * lhs, const path & rhs );
    -path operator / ( const std::string & lhs, const path & rhs );

    -

    Returns: path( lhs ) /= rhs

    +

    template<class String, class Traits>
    +basic_path<String, Traits> operator/(
    +  const typename String::value_type * lhs, const basic_path<String, Traits> +& rhs );
    +
    +template<class String, class Traits>
    +basic_path<String, Traits> operator/(
    +  const String & lhs, const basic_path<String, Traits> & rhs );

    +

    Returns: basic_path( lhs ) /= rhs

    -

    Default name_check mechanism

    -

    It is difficult or impossible to write portable programs without some way to -verify that directory and file names are portable. Without automatic name -checking, verification is tedious, error prone, and ugly. Yet no single name -check function can serve all applications, and within an application -different paths or portions of paths may require different name check -functions. Sometimes there should be no checking at all.

    -

    Those needs are met by providing a default name check function to meet an -application's most common needs, and then providing path constructors -which override the default name check function to handle less common needs. -The default name check function can be set by the application, allowing the -most common case for the particular application to be handled by the default -check.

    -

    Dangers

    -

    The default name check function is set and retrieved by path static -member functions, and as such is similar to a global variable. Since global variables are -considered harmful [Wulf-Shaw-73], class -path allows the default name check function to be set only once, and -only before the first use. This turns a dangerous global variable into a -safer global constant. Even with this protection, the ability to set the default name check function is -still a powerful feature, and is still dangerous in that it can change -the behavior of code buried out-of-sight in libraries or elsewhere. Thus -changing the default error check function should only be done when explicitly -specifying the function via the two argument path constructors is not -reasonable.

    Rationale

    Also see the FAQ for additional rationale.

    -

    Function naming: Class path +

    Function naming: Class basic_path member function names and operations.hpp non-member function names were chosen to be somewhat distinct from one another. The objective was to avoid cases like foo.empty() and empty( foo ) both being -valid, but with completely different semantics. At one point path::empty() -was renamed path::is_null(), but that caused many coding typos because +valid, but with completely different semantics. At one point basic_path::empty() +was renamed basic_path::is_null(), but that caused many coding typos because std::string::empty() is often used nearby.

    -

    Decomposition functions: Decomposition functions are provided because without them it is impossible to write portable path +

    Decomposition functions: Decomposition functions are provided because without them it is impossible to write portable +basic_path manipulations. Convenience is also a factor.

    Const vs non-const returns: In some earlier versions of the library, member functions returned values as const rather than non-const. See Scott Myers, Effective C++, Item 21. The const qualifiers were eliminated (1) to conform with C++ Standard Library practice, (2) because non-const returns allow occasionally useful expressions, and (3) because the - number of coding errors eliminated were deemed rare. A requirement that path::iterator be a class type was added to eliminate non-const - iterator errors.

    + number of coding errors eliminated were deemed rare. A requirement that + basic_path::iterator be a class type was added to eliminate non-const + iterator errors. The returns from conversion functions are still const + qualified, to ensure that user programs do not break if implementations change + from return by value to return by reference, as they are permitted to do.

    Path decomposition examples

    -

    It is often useful to extract specific elements from a path object.  +

    It is often useful to extract specific elements from a basic_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 +basic_path, convenience functions are provided which are easier to use, more efficient, and less error prone.

    -

    The first column of the table gives the example path, formatted by the -string() function. The second column shows the values which would be returned by -dereferencing each element iterator. The remaining columns show the results of +

    The first column of the table gives the example path, formatted by the +string() function. The second column shows the values which would be returned by dereferencing each element iterator. The remaining columns show the results of various expressions.

    + _path() - - - - + + + + - + - + - - - - + + + + - - + + - - + - - + + - - + + @@ -981,7 +1164,7 @@ various expressions.

    p.string() Elements p.root_
    - path()
    + _path()
     
    p.root_
    name()
    p.root_
    directory()
    p.relative_
    - path()
    p.root_
    directory()
    / p.relative_
    @@ -930,40 +1113,40 @@ various expressions.

    foo
    //shr//shr//shr//shr//net//net//net//net "" "" ""//shr//net ""//shr//net
    //shr///shr,///shr///shr//net///net,///net///net / "" ///shr//shr//net//net /
    //shr/foo//shr,
    +
    //net/foo//net,
    /,foo
    //shr///shr//net///net / foo /foo//shr/foo//shr///net/foo//net/ foo

    Revised -02 August, 2005

    +21 June, 2005

    © Copyright Beman Dawes, 2002

    Use, modification, and distribution are subject to the Boost Software diff --git a/doc/portability_guide.htm b/doc/portability_guide.htm index 9536132..2b233e9 100644 --- a/doc/portability_guide.htm +++ b/doc/portability_guide.htm @@ -21,7 +21,7 @@ Guide File and directory name recommendations

    Introduction

    Like any other C++ program which performs I/O operations, there is no -guarantee that a program using the Filesystem Library will be portable between +guarantee that a program using Boost.Filesystem will be portable between operating systems. Critical aspects of I/O, such as how the operating system interprets paths, are unspecified by the C and C++ Standards.

    It is not possible to know if a file or directory name will be @@ -44,8 +44,8 @@ for some of those file systems differ a great deal. Each file system may have differing rules for overall path validity, such as a maximum length or number of sub-directories.

    -

    As a result, the Boost Filesystem Library's name_check mechanism -cannot guarantee directory and file name portability. Rather, it is intended to +

    As a result, Boost.Filesystem's name_check functions +cannot guarantee directory and file name portability. Rather, they are intended to give the programmer a "fighting chance" to achieve portability by early detection of common naming problems.

    @@ -57,13 +57,9 @@ system. A number of these functions are supplied, and user-supplied name_check functions are also allowed.

    The portable_name function is of particular -interest because it is the initial -default name_check function and is carefully chosen to provide wide -portability yet without severe restrictions on expressiveness.

    +interest because it has been carefully designed to provide wide +portability yet not overly restrict expressiveness.

    -

    The native function is of particular interest because -it is often used when the source of the path is operator input or other sources -which are formatted according to operating system rules.

    @@ -95,9 +91,7 @@ which are formatted according to operating system rules.

    @@ -115,28 +109,10 @@ which are formatted according to operating system rules.

    including OpenVMS and other systems which have a concept of "file extension" but limit its length. - - - - - @@ -155,7 +131,7 @@ which are formatted according to operating system rules.

    underscore.

    Use any of the "portable_" name check functions to enforce this recommendation.

    + file names, and are also valid for Windows, Mac, and many other modern file systems. - + - - @@ -193,7 +169,7 @@ which are formatted according to operating system rules.

    - + @@ -215,7 +191,7 @@ which are formatted according to operating system rules.

       31 characters: Classic Mac OS
       8 characters + period + 3 characters: ISO 9660 level 1
       32 characters: ISO 9660 level 2 and 3
    -   128 characters (64 if unicode): ISO 9660 with Juliet extensions +   128 characters (64 if Unicode): ISO 9660 with Juliet extensions @@ -223,7 +199,7 @@ which are formatted according to operating system rules.


    Revised -01 December, 2003

    +22 March, 2005

    © Copyright Beman Dawes, 2002, 2003

    Use, modification, and distribution are subject to the Boost Software diff --git a/example/Jamfile b/example/Jamfile index 239c315..f76d163 100644 --- a/example/Jamfile +++ b/example/Jamfile @@ -11,9 +11,10 @@ { exe file_size : file_size.cpp - ../src/operations_posix_windows.cpp + ../src/operations.cpp ../src/exception.cpp - ../src/path_posix_windows.cpp + ../src/path.cpp + ../src/portability.cpp : BOOST_ALL_NO_LIB $(BOOST_ROOT) $(BOOST_ROOT) ; diff --git a/example/mbcopy.cpp b/example/mbcopy.cpp new file mode 100644 index 0000000..b4e5949 --- /dev/null +++ b/example/mbcopy.cpp @@ -0,0 +1,87 @@ +// Boost.Filesystem mbcopy.cpp ---------------------------------------------// + +// © Copyright Beman Dawes 2005 + +// Use, modification, and distribution is subject to 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) + +// Copy the files in a directory, using mbpath to represent the new file names +// See http://../doc/path.htm#mbpath for more information + +#include +# ifdef BOOST_FILESYSTEM_NARROW_ONLY +# error This compiler or standard library does not support wide-character strings or paths +# endif + +#include "mbpath.hpp" +#include +#include +#include +#include "../src/utf8_codecvt_facet.hpp" + +namespace fs = boost::filesystem; + +namespace +{ + // we can't use boost::filesystem::copy_file() because the argument types + // differ, so provide a not-very-smart replacement. + + void copy_file( const fs::wpath & from, const user::mbpath & to ) + { + fs::ifstream from_file( from, std::ios_base::in | std::ios_base::binary ); + if ( !from_file ) { std::cout << "input open failed\n"; return; } + + fs::ofstream to_file( to, std::ios_base::out | std::ios_base::binary ); + if ( !to_file ) { std::cout << "output open failed\n"; return; } + + char c; + while ( from_file.get(c) ) + { + to_file.put(c); + if ( to_file.fail() ) { std::cout << "write error\n"; return; } + } + + if ( !from_file.eof() ) { std::cout << "read error\n"; } + } +} + +int main( int argc, char * argv[] ) +{ + if ( argc != 2 ) + { + std::cout << "Copy files in the current directory to a target directory\n" + << "Usage: mbcopy \n"; + return 1; + } + + // For encoding, use Boost UTF-8 codecvt + std::locale global_loc = std::locale(); + std::locale loc( global_loc, new fs::detail::utf8_codecvt_facet ); + user::mbpath_traits::imbue( loc ); + + std::string target_string( argv[1] ); + user::mbpath target_dir( user::mbpath_traits::to_internal( target_string ) ); + + if ( !fs::is_directory( target_dir ) ) + { + std::cout << "Error: " << argv[1] << " is not a directory\n"; + return 1; + } + + for ( fs::wdirectory_iterator it( L"." ); + it != fs::wdirectory_iterator(); ++it ) + { + if ( it->is_file() ) + { + copy_file( *it, target_dir / it->path().leaf() ); + } + } + + return 0; +} + + + + + diff --git a/example/mbpath.cpp b/example/mbpath.cpp new file mode 100644 index 0000000..89f7d3e --- /dev/null +++ b/example/mbpath.cpp @@ -0,0 +1,79 @@ +// Boost.Filesystem mbpath.hpp ---------------------------------------------// + +// © Copyright Beman Dawes 2005 + +// Use, modification, and distribution is subject to 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 Boost.Filesystem home page at http://www.boost.org/libs/filesystem + +#include +# ifdef BOOST_FILESYSTEM_NARROW_ONLY +# error This compiler or standard library does not support wide-character strings or paths +# endif + +#include "mbpath.hpp" +#include +#include + +namespace fs = boost::filesystem; + +namespace +{ + // ISO C calls this "the locale-specific native environment": + std::locale loc(""); + + const std::codecvt * + cvt( &std::use_facet > + ( loc ) ); +} + +namespace user +{ + mbpath_traits::external_string_type + mbpath_traits::to_external( const mbpath & ph, + const internal_string_type & src ) + { + std::size_t work_size( cvt->max_length() * (src.size()+1) ); + boost::scoped_array work( new char[ work_size ] ); + std::mbstate_t state; + const internal_string_type::value_type * from_next; + external_string_type::value_type * to_next; + if ( cvt->out( + state, src.c_str(), src.c_str()+src.size(), from_next, work.get(), + work.get()+work_size, to_next ) != std::codecvt_base::ok ) + boost::throw_exception >( + fs::basic_filesystem_error( + "user::mbpath::to_external conversion error", + ph, EINVAL ) ); + *to_next = '\0'; + return external_string_type( work.get() ); + } + + mbpath_traits::internal_string_type + mbpath_traits::to_internal( const external_string_type & src ) + { + std::size_t work_size( src.size()+1 ); + boost::scoped_array work( new wchar_t[ work_size ] ); + std::mbstate_t state; + const external_string_type::value_type * from_next; + internal_string_type::value_type * to_next; + if ( cvt->in( + state, src.c_str(), src.c_str()+src.size(), from_next, work.get(), + work.get()+work_size, to_next ) != std::codecvt_base::ok ) + boost::throw_exception >( + fs::basic_filesystem_error( + "user::mbpath::to_internal conversion error", EINVAL ) ); + *to_next = L'\0'; + return internal_string_type( work.get() ); + } + + void mbpath_traits::imbue( const std::locale & new_loc ) + { + loc = new_loc; + cvt = &std::use_facet + >( loc ); + } + +} // namespace user diff --git a/example/mbpath.hpp b/example/mbpath.hpp new file mode 100644 index 0000000..8cd530f --- /dev/null +++ b/example/mbpath.hpp @@ -0,0 +1,43 @@ +// Boost.Filesystem mbpath.hpp ---------------------------------------------// + +// © Copyright Beman Dawes 2005 + +// Use, modification, and distribution is subject to 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) + +// Encodes wide character paths as MBCS +// See http://../doc/path.htm#mbpath for more information + +#include +#include +#include + +namespace user +{ + struct mbpath_traits; + + typedef boost::filesystem::basic_path mbpath; + + struct mbpath_traits + { + typedef std::wstring internal_string_type; + typedef std::string external_string_type; + + static external_string_type to_external( const mbpath & ph, + const internal_string_type & src ); + + static internal_string_type to_internal( const external_string_type & src ); + + static void imbue( const std::locale & loc ); + }; +} // namespace user + +namespace boost +{ + namespace filesystem + { + template<> struct is_basic_path + { static const bool value = true; }; + } +} diff --git a/example/path_table.cpp b/example/path_table.cpp new file mode 100644 index 0000000..b175873 --- /dev/null +++ b/example/path_table.cpp @@ -0,0 +1,264 @@ +// Generate an HTML table showing path decomposition -----------------------// + +// Copyright Beman Dawes 2005. 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 http://www.boost.org/libs/filesystem for documentation. + +// For purposes of generating the table, support both POSIX and Windows paths +#define BOOST_FILESYSTEM_NAMESPACE posix +#define BOOST_POSIX_PATH +#include "boost/filesystem/path.hpp" + +#undef BOOST_FILESYSTEM_PATH_HPP +#undef BOOST_FILESYSTEM_NAMESPACE +#define BOOST_FILESYSTEM_NAMESPACE windows +#define BOOST_WINDOWS_PATH +#include "boost/filesystem/path.hpp" + +namespace pos = boost::posix; +namespace win = boost::windows; + +#include +#include + +using std::string; +using std::cout; + +namespace +{ + std::ifstream infile; + std::ofstream outfile; + + const string empty_string; + + struct column_abc + { + virtual string heading() const = 0; + virtual string cell_value( const pos::path & p ) const = 0; + virtual string cell_value( const win::path & p ) const = 0; + }; + + struct c0 : public column_abc + { + string heading() const { return string("string()"); } + string cell_value( const pos::path & p ) const + { + return p.string(); + } + string cell_value( const win::path & p ) const + { + return p.string(); + } + } o0; + + struct c1 : public column_abc + { + string heading() const { return string("file_
    string()"); } + string cell_value( const pos::path & p ) const { return p.file_string(); } + string cell_value( const win::path & p ) const { return p.file_string(); } + } o1; + + struct c2 : public column_abc + { + string heading() const { return string("Elements found
    by iteration"); } + string cell_value( const pos::path & p ) const + { + string s; + for( pos::path::iterator i(p.begin()); i != p.end(); ++i ) + { + if ( i != p.begin() ) s += ','; + s += '\"'; + s += *i; + s += '\"'; + } + if ( s.empty() ) s += "\"\""; + return s; + } + string cell_value( const win::path & p ) const + { + string s; + for( win::path::iterator i(p.begin()); i != p.end(); ++i ) + { + if ( i != p.begin() ) s += ','; + s += '\"'; + s += *i; + s += '\"'; + } + if ( s.empty() ) s += "\"\""; + return s; + } + } o2; + + struct c3 : public column_abc + { + string heading() const { return string("root_
    path()
    .string()
    "); } + string cell_value( const pos::path & p ) const { return p.root_path().string(); } + string cell_value( const win::path & p ) const { return p.root_path().string(); } + } o3; + + struct c4 : public column_abc + { + string heading() const { return string("root_
    name()
    "); } + string cell_value( const pos::path & p ) const { return p.root_name(); } + string cell_value( const win::path & p ) const { return p.root_name(); } + } o4; + + struct c5 : public column_abc + { + string heading() const { return string("root_
    directory()
    "); } + string cell_value( const pos::path & p ) const { return p.root_directory(); } + string cell_value( const win::path & p ) const { return p.root_directory(); } + } o5; + + struct c6 : public column_abc + { + string heading() const { return string("relative_
    path()
    .string()
    "); } + string cell_value( const pos::path & p ) const { return p.relative_path().string(); } + string cell_value( const win::path & p ) const { return p.relative_path().string(); } + } o6; + + struct c7 : public column_abc + { + string heading() const { return string("branch_
    path()
    .string()
    "); } + string cell_value( const pos::path & p ) const { return p.branch_path().string(); } + string cell_value( const win::path & p ) const { return p.branch_path().string(); } + } o7; + + struct c8 : public column_abc + { + string heading() const { return string("leaf()"); } + string cell_value( const pos::path & p ) const { return p.leaf(); } + string cell_value( const win::path & p ) const { return p.leaf(); } + } o8; + + const column_abc * column[] = { &o2, &o0, &o1, &o3, &o4, &o5, &o6, &o7, &o8 }; + + // do_cell ---------------------------------------------------------------// + + void do_cell( const string & test_case, int i ) + { + + string posix_result( column[i]->cell_value( pos::path(test_case) ) ); + string windows_result( column[i]->cell_value( win::path(test_case) ) ); + + outfile << + (posix_result != windows_result + ? "

    \n"; + } + +// do_row ------------------------------------------------------------------// + + void do_row( const string & test_case ) + { + outfile << "\n\n"; + + for ( int i = 0; i < sizeof(column)/sizeof(column_abc&); ++i ) + { + do_cell( test_case, i ); + } + + outfile << "\n"; + } + +// do_table ----------------------------------------------------------------// + + void do_table() + { + outfile << + "

    Path Decomposition Table for POSIX and Windows

    \n" + "

    Shaded entries indicate cases where POSIX and Windows\n" + "implementations yield different results. The top value is the\n" + "POSIX result and the bottom value is the Windows result.\n" + "

    Library Supplied name_check Functions
    portable_name windows_name(name) && portable_posix_name(name), - and first character not period or hyphen.

    - Note: This is the initial default name_check.

    -

    Use: applications which must be portable to a wide variety of + and the first character not a period or hyphen.

    Use: applications which must be portable to a wide variety of modern operating systems, large and small, and to some legacy O/S's.

    no_checkReturns true.

    Use: When the generic grammar - is desired, but name checking is not desired. For example, a program which - traffics in names created elsewhere may have no choice but to accept those - names. Another example is a application which prefers to use the Filesystem - Library and its generic grammar, but is uninterested in portability. An - alternative to no_check might be native, but native - has the side effect of altering the grammar accepted.

    nativeImplementation defined name_check. Guaranteed to return - true for all names considered valid by the operating system.

    Side - effect: Syntax for path constructor src string is implementation - defined according to the path syntax rules for the operating system.

    -

    Use: In path constructors, when the source is operator input or - other sources which are formatted according to operating system rules. Note - that  default_name_check( native ) causes all path src - strings to be treated according to the path syntax rules for the operating - system, which may or may not be desirable.

    -

    Note: May return true for some names not considered valid +

    Implementation defined name_check. Returns + true for all names considered valid by the operating system.

    Note: May return true for some names not considered valid by the operating system under all conditions (particularly on operating systems which support multiple file systems.)

    These are the characters specified by the POSIX standard for portable directory and - file names, and are also valid for Windows, Mac, and many other modern filesystems.
    Do not use a period or hyphen as the first @@ -172,20 +148,20 @@ which are formatted according to operating system rules.

    Do not use periods in directory names.

    Use portable_directory_name to enforce this recommendation.

    Requirement for ISO-9660 without Juliet extensions, OpenVMS native filesystem, and other legacy systems.Requirement for ISO-9660 without Juliet extensions, OpenVMS filesystem, and other legacy systems.
    Do not use more that one period in a file name, and limit the portion after the period to three characters.

    Use portable_file_name to enforce this recommendation.

    Requirement for ISO-9660 level 1, OpenVMS native filesystem, and + Requirement for ISO-9660 level 1, OpenVMS filesystem, and other legacy systems.
    Do not assume names are case sensitive. For example, do not expected a directory to be able to hold separate elements named "Foo" and "foo". Some filesystems are case insensitive.  For example, Windows + Some file systems are case insensitive.  For example, Windows NTFS is case preserving in the way it stores names, but case insensitive in searching for names (unless running under the POSIX sub-system, it which case it does case sensitive searches).
    Do not assume names are case insensitive.  For example, do not expect a file created with the name of "Foo" to be opened successfully with the name of "foo".Some filesystems are case sensitive.  For example, POSIX.Some file systems are case sensitive.  For example, POSIX.
    Don't use hyphens in names. Limiting name length can markedly reduce the expressiveness of file names, yet placing only very high limits on lengths inhibits widest portability.
    " + : ""); + + if ( posix_result.empty() || posix_result[0] != '\"' ) + outfile << '\"' << posix_result << '\"'; + else + outfile << posix_result; + + if ( posix_result != windows_result ) + { + outfile << "
    "; + if ( windows_result.empty() || windows_result[0] != '\"' ) + outfile << '\"' << windows_result << '\"'; + else + outfile << windows_result; + } + + outfile << "
    \"" << test_case << "\"
    \n" + "

    \n" + ; + + // generate the column headings + + outfile << "

    \n"; + + for ( int i = 0; i < sizeof(column)/sizeof(column_abc&); ++i ) + { + outfile << "\n"; + } + + outfile << "\n"; + + // generate a row for each input line + + string test_case; + while ( std::getline( infile, test_case ) ) + { + do_row( test_case ); + } + + outfile << "
    Constructor
    argument
    " << column[i]->heading() << "
    \n"; + } + +} // unnamed namespace + +// main --------------------------------------------------------------------// + +#define BOOST_NO_CPP_MAIN_SUCCESS_MESSAGE +#include + +int cpp_main( int argc, char * argv[] ) // note name! +{ + if ( argc != 3 ) + { + std::cerr << + "Usage: path_table input-file output-file\n" + " input-file contains the paths to appear in the table.\n" + " output-file will contain the generated HTML.\n" + ; + return 1; + } + + infile.open( argv[1] ); + if ( !infile ) + { + std::cerr << "Could not open input file: " << argv[1] << std::endl; + return 1; + } + + outfile.open( argv[2] ); + if ( !outfile ) + { + std::cerr << "Could not open output file: " << argv[2] << std::endl; + return 1; + } + + outfile << "\n" + "\n" + "Path Decomposition Table\n" + "\n" + "\n" + ; + + do_table(); + + outfile << "\n" + "\n" + ; + + return 0; +} diff --git a/example/simple_ls.cpp b/example/simple_ls.cpp index f8dd394..076d49a 100644 --- a/example/simple_ls.cpp +++ b/example/simple_ls.cpp @@ -10,14 +10,16 @@ #include "boost/filesystem/operations.hpp" #include "boost/filesystem/path.hpp" +#include "boost/progress.hpp" #include namespace fs = boost::filesystem; int main( int argc, char* argv[] ) { + boost::progress_timer t( std::clog ); - fs::path full_path( fs::initial_path() ); + fs::path full_path( fs::initial_path() ); if ( argc > 1 ) full_path = fs::system_complete( fs::path( argv[1], fs::native ) ); @@ -26,6 +28,7 @@ int main( int argc, char* argv[] ) unsigned long file_count = 0; unsigned long dir_count = 0; + unsigned long other_count = 0; unsigned long err_count = 0; if ( !fs::exists( full_path ) ) @@ -45,16 +48,24 @@ int main( int argc, char* argv[] ) { try { - if ( fs::is_directory( *dir_itr ) ) + fs::status_flags flags( dir_itr->status() ); + + if ( (flags & fs::directory_flag) == fs::directory_flag ) { ++dir_count; - std::cout << dir_itr->leaf()<< " [directory]\n"; + std::cout << dir_itr->leaf() << " [directory]\n"; } - else + else if ( (flags & fs::file_flag) == fs::file_flag ) { ++file_count; std::cout << dir_itr->leaf() << "\n"; } + else + { + ++other_count; + std::cout << dir_itr->leaf() << " [other]\n"; + } + } catch ( const std::exception & ex ) { @@ -64,6 +75,7 @@ int main( int argc, char* argv[] ) } std::cout << "\n" << file_count << " files\n" << dir_count << " directories\n" + << other_count << " others\n" << err_count << " errors\n"; } else // must be a file diff --git a/include/boost/filesystem/cerrno.hpp b/include/boost/filesystem/cerrno.hpp new file mode 100644 index 0000000..5a3f082 --- /dev/null +++ b/include/boost/filesystem/cerrno.hpp @@ -0,0 +1,23 @@ +// Boost Filesystem cerrno.hpp header --------------------------------------// + +// © Copyright Beman Dawes 2005. +// Use, modification, and distribution is subject to 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 + +#ifndef BOOST_FILESYSTEM_CERRNO_HPP +#define BOOST_FILESYSTEM_CERRNO_HPP + +#include + +#if defined __BORLANDC__ +#define ENOSYS 9997 +#endif + +#define EBADHANDLE 9998 // bad handle +#define EOTHER 9999 // Other error not translatable + // to a POSIX errno value + +#endif // include guard diff --git a/include/boost/filesystem/config.hpp b/include/boost/filesystem/config.hpp index aed9585..a02d2ef 100644 --- a/include/boost/filesystem/config.hpp +++ b/include/boost/filesystem/config.hpp @@ -12,11 +12,66 @@ #ifndef BOOST_FILESYSTEM_CONFIG_HPP #define BOOST_FILESYSTEM_CONFIG_HPP +#define BOOST_FILESYSTEM_I18N // aid users wishing to compile several versions + +// ability to change namespace aids path_table.cpp ------------------------// +#ifndef BOOST_FILESYSTEM_NAMESPACE +# define BOOST_FILESYSTEM_NAMESPACE filesystem +#endif + // This header implements separate compilation features as described in // http://www.boost.org/more/separate_compilation.html #include +// determine platform ------------------------------------------------------// + +// BOOST_CYGWIN_PATH implies BOOST_WINDOWS_PATH and BOOST_POSIX_API + +# if defined(BOOST_CYGWIN_PATH) +# if defined(BOOST_POSIX_PATH) +# error BOOST_POSIX_PATH is invalid when BOOST_CYGWIN_PATH is defined +# endif +# if defined(BOOST_WINDOWS_API) +# error BOOST_WINDOWS_API is invalid when BOOST_CYGWIN_PATH is defined +# endif +# define BOOST_WINDOWS_PATH +# define BOOST_POSIX_API +# endif + +// BOOST_POSIX_API or BOOST_WINDOWS_API specify which API to use + +# if defined( BOOST_WINDOWS_API ) && defined( BOOST_POSIX_API ) +# error both BOOST_WINDOWS_API and BOOST_POSIX_API are defined +# elif !defined( BOOST_WINDOWS_API ) && !defined( BOOST_POSIX_API ) +# if defined(_WIN32) || defined(__WIN32__) || defined(WIN32) || defined(__CYGWIN__) +# define BOOST_WINDOWS_API +# else +# define BOOST_POSIX_API +# endif +# endif + +// BOOST_WINDOWS_PATH enables Windows path syntax recognition + +# if !defined(BOOST_POSIX_PATH) && (defined(_WIN32) || defined(__WIN32__) || defined(WIN32) || defined(__CYGWIN__)) +# define BOOST_WINDOWS_PATH +# endif + +// BOOST_FILESYSTEM_STATUS_CACHE enables status_flags cache in +// basic_directory_iterator -----------------------------------------------// + +// TODO: "|| defined(__APPLE__)" compiles, but at runtime d_type is alwasy 0. Why? +// TODO: find out what macros enable dirent::d_type on various operating systems. +# if !defined(__CYGWIN__) && (defined(BOOST_WINDOWS_API) || defined(__USE_BSD)) +# define BOOST_FILESYSTEM_STATUS_CACHE +# endif + +// narrow support only for badly broken compilers or libraries -------------// + +# if defined(BOOST_NO_STD_WSTRING) || defined(BOOST_NO_SFINAE) || defined(BOOST_NO_STD_LOCALE) +# define BOOST_FILESYSTEM_NARROW_ONLY +# endif + // enable dynamic linking on Windows ---------------------------------------// # if (defined(BOOST_ALL_DYN_LINK) || defined(BOOST_FILESYSTEM_DYN_LINK)) && defined(__BORLANDC__) && defined(__WIN32__) diff --git a/include/boost/filesystem/convenience.hpp b/include/boost/filesystem/convenience.hpp index cae90c0..9f11314 100644 --- a/include/boost/filesystem/convenience.hpp +++ b/include/boost/filesystem/convenience.hpp @@ -1,6 +1,6 @@ // boost/filesystem/convenience.hpp ----------------------------------------// -// © Copyright Beman Dawes, 2002 +// © Copyright Beman Dawes, 2002-2005 // © Copyright Vladimir Prus, 2002 // Use, modification, and distribution is subject to the Boost Software // License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at @@ -13,27 +13,285 @@ #ifndef BOOST_FILESYSTEM_CONVENIENCE_HPP #define BOOST_FILESYSTEM_CONVENIENCE_HPP -#include // includes #include +#include +#include -#include // must be the last header +#include // must be the last #include + +# ifndef BOOST_FILESYSTEM_NARROW_ONLY +# define BOOST_FS_FUNC(BOOST_FS_TYPE) \ + template typename boost::enable_if, \ + BOOST_FS_TYPE>::type +# define BOOST_FS_FUNC_STRING BOOST_FS_FUNC(typename Path::string_type) +# define BOOST_FS_TYPENAME typename +# else +# define BOOST_FS_FUNC(BOOST_FS_TYPE) inline BOOST_FS_TYPE + typedef boost::filesystem::path Path; +# define BOOST_FS_FUNC_STRING std::string +# define BOOST_FS_TYPENAME +# endif namespace boost { namespace filesystem { - BOOST_FILESYSTEM_DECL bool create_directories(const path& ph); + BOOST_FS_FUNC(bool) create_directories(const Path& ph) + { + if (ph.empty() || exists(ph)) + { + if ( !ph.empty() && !is_directory(ph) ) + boost::throw_exception( basic_filesystem_error( + "boost::filesystem::create_directories", ph, -1 ) ); + return false; + } - BOOST_FILESYSTEM_DECL std::string extension(const path& ph); + // First create branch, by calling ourself recursively + create_directories(ph.branch_path()); + // Now that parent's path exists, create the directory + create_directory(ph); + return true; + } - BOOST_FILESYSTEM_DECL std::string basename(const path& ph); + BOOST_FS_FUNC_STRING extension(const Path& ph) + { + typedef BOOST_FS_TYPENAME Path::string_type string_type; + string_type leaf = ph.leaf(); - BOOST_FILESYSTEM_DECL path change_extension(const path& ph, - const std::string& new_extension); + BOOST_FS_TYPENAME string_type::size_type n = leaf.rfind('.'); + if (n != string_type::npos) + return leaf.substr(n); + else + return string_type(); + } + + BOOST_FS_FUNC_STRING basename(const Path& ph) + { + typedef BOOST_FS_TYPENAME Path::string_type string_type; + string_type leaf = ph.leaf(); + BOOST_FS_TYPENAME string_type::size_type n = leaf.rfind('.'); + return leaf.substr(0, n); + } + + BOOST_FS_FUNC(Path) change_extension( const Path & ph, + const BOOST_FS_TYPENAME Path::string_type & new_extension ) + { return ph.branch_path() / (basename(ph) + new_extension); } + +# ifndef BOOST_FILESYSTEM_NARROW_ONLY + + // "do-the-right-thing" overloads ---------------------------------------// + + inline bool create_directories(const path& ph) + { return create_directories(ph); } + inline bool create_directories(const wpath& ph) + { return create_directories(ph); } + + inline std::string extension(const path& ph) + { return extension(ph); } + inline std::wstring extension(const wpath& ph) + { return extension(ph); } + + inline std::string basename(const path& ph) + { return basename( ph ); } + inline std::wstring basename(const wpath& ph) + { return basename( ph ); } + + inline path change_extension( const path & ph, const std::string& new_ex ) + { return change_extension( ph, new_ex ); } + inline wpath change_extension( const wpath & ph, const std::wstring& new_ex ) + { return change_extension( ph, new_ex ); } + +# endif + + + // basic_recursive_directory_iterator helpers --------------------------// + + namespace detail + { + template< class Path > + struct recur_dir_itr_imp + { + typedef basic_directory_iterator< Path > element_type; + std::stack< element_type, std::vector< element_type > > m_stack; + int m_level; + bool m_no_push; + + recur_dir_itr_imp() : m_level(0), m_no_push(false) {} + }; + + } // namespace detail + + // basic_recursive_directory_iterator ----------------------------------// + + template< class Path > + class basic_recursive_directory_iterator + : public boost::iterator_facade< + basic_recursive_directory_iterator, + basic_directory_entry, + boost::single_pass_traversal_tag > + { + public: + typedef Path path_type; + + basic_recursive_directory_iterator(){} // creates the "end" iterator + + explicit basic_recursive_directory_iterator( const Path & dir_path ); + + int level() const { return m_imp->m_level; } + + void pop(); + void no_push() + { + BOOST_ASSERT( m_imp.get() && "attempt to no_push() on end iterator" ); + m_imp->m_no_push = true; + } + + private: + + // shared_ptr provides shallow-copy semantics required for InputIterators. + // m_imp.get()==0 indicates the end iterator. + boost::shared_ptr< detail::recur_dir_itr_imp< Path > > m_imp; + + friend class boost::iterator_core_access; + + typename boost::iterator_facade< + basic_recursive_directory_iterator, + basic_directory_entry, + boost::single_pass_traversal_tag >::reference + dereference() const + { + BOOST_ASSERT( m_imp.get() && "attempt to dereference end iterator" ); + return *m_imp->m_stack.top(); + } + + void increment(); + + bool equal( const basic_recursive_directory_iterator & rhs ) const + { return m_imp == rhs.m_imp; } + }; + + typedef basic_recursive_directory_iterator recursive_directory_iterator; +# ifndef BOOST_FILESYSTEM_NARROW_ONLY + typedef basic_recursive_directory_iterator wrecursive_directory_iterator; +# endif + + // basic_recursive_directory_iterator implementation -------------------// + + // constructor + template + basic_recursive_directory_iterator:: + basic_recursive_directory_iterator( const Path & dir_path ) + : m_imp( new detail::recur_dir_itr_imp ) + { + m_imp->m_stack.push( basic_directory_iterator( dir_path ) ); + } + + // increment + template + void basic_recursive_directory_iterator::increment() + { + BOOST_ASSERT( m_imp.get() && "increment on end iterator" ); + + static const basic_directory_iterator end_itr; + + if ( m_imp->m_no_push ) m_imp->m_no_push = false; + else if ( m_imp->m_stack.top()->is_directory() ) + { + m_imp->m_stack.push( + basic_directory_iterator( *m_imp->m_stack.top() ) ); + if ( m_imp->m_stack.top() != end_itr ) + { + ++m_imp->m_level; + return; + } + m_imp->m_stack.pop(); + } + + while ( !m_imp->m_stack.empty() + && ++m_imp->m_stack.top() == end_itr ) + { + m_imp->m_stack.pop(); + --m_imp->m_level; + } + + if ( m_imp->m_stack.empty() ) m_imp.reset(); // done, so make end iterator + } + + // pop + template + void basic_recursive_directory_iterator::pop() + { + BOOST_ASSERT( m_imp.get() && "pop on end iterator" ); + BOOST_ASSERT( m_imp->m_level > 0 && "pop with level < 1" ); + + m_imp->m_stack.pop(); + --m_imp->m_level; + } + + // what() basic_filesystem_error_decoder -------------------------------// + + namespace detail + { + void decode_system_message( system_error_type ec, std::string & target ) + { + system_message( ec, target ); + } + +# if defined(BOOST_WINDOWS_API) && !defined(BOOST_FILESYSTEM_NARROW_ONLY) + void decode_system_message( system_error_type ec, std::wstring & target ) + { + system_message( ec, target ); + } +# endif + + template + void decode_system_message( system_error_type ec, String & target ) + { + std::string temp; + system_message( ec, temp ); + for ( const char * p = temp.c_str(); *p != 0; ++p ) + { target += static_cast( *p ); } + } + } + + template + typename Path::string_type what( const basic_filesystem_error & ex ) + { + typename Path::string_type s; + for ( const char * p = ex.what(); *p != 0; ++p ) + { s += static_cast( *p ); } + + if ( !ex.path1().empty() ) + { + s += static_cast( ':' ); + s += static_cast( ' ' ); + s += static_cast( '\"' ); + s += ex.path1().file_string(); + s += static_cast( '\"' ); + } + if ( !ex.path2().empty() ) + { + s += static_cast( ',' ); + s += static_cast( ' ' ); + s += static_cast( '\"' ); + s += ex.path2().file_string(); + s += static_cast( '\"' ); + } + if ( ex.system_error() ) + { + s += static_cast( ' ' ); + + detail::decode_system_message( ex.system_error(), s ); + } + return s; + } } // namespace filesystem } // namespace boost -#include // pops abi_suffix.hpp pragmas +#undef BOOST_FS_FUNC_STRING +#undef BOOST_FS_FUNC + +#include // pops abi_prefix.hpp pragmas #endif // BOOST_FILESYSTEM_CONVENIENCE_HPP diff --git a/include/boost/filesystem/exception.hpp b/include/boost/filesystem/exception.hpp index 3306584..23b3224 100644 --- a/include/boost/filesystem/exception.hpp +++ b/include/boost/filesystem/exception.hpp @@ -1,106 +1,9 @@ -// boost/filesystem/exception.hpp ------------------------------------------// +// boost/filesystem/exception.hpp -------------------------------------------// -// Copyright © 2002 Beman Dawes -// Copyright © 2001 Dietmar Kuehl -// -// Use, modification, and distribution is subject to 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) +// © Copyright Beman Dawes 2003 +// Use, modification, and distribution is subject to 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 - -//----------------------------------------------------------------------------// - -#ifndef BOOST_FILESYSTEM_EXCEPTION_HPP -#define BOOST_FILESYSTEM_EXCEPTION_HPP - -#include -#include - -#include -#include -#include - -#include // must be the last header - -//----------------------------------------------------------------------------// - -namespace boost -{ - namespace filesystem - { - namespace detail - { - BOOST_FILESYSTEM_DECL int system_error_code(); // artifact of POSIX and WINDOWS error reporting - } - - enum error_code - { - no_error = 0, - system_error, // system generated error; if possible, is translated - // to one of the more specific errors below. - other_error, // library generated error - security_error, // includes access rights, permissions failures - read_only_error, - io_error, - path_error, - not_found_error, - not_directory_error, - busy_error, // implies trying again might succeed - already_exists_error, - not_empty_error, - is_directory_error, - out_of_space_error, - out_of_memory_error, - out_of_resource_error - }; - - - class BOOST_FILESYSTEM_DECL filesystem_error : public std::exception - { - public: - - filesystem_error( - const std::string & who, - const std::string & message ); // assumed to be error_code::other_error - - filesystem_error( - const std::string & who, - const path & path1, - const std::string & message, - error_code ec = other_error ); - - filesystem_error( - const std::string & who, - const path & path1, - int sys_err_code ); - - filesystem_error( - const std::string & who, - const path & path1, - const path & path2, - int sys_err_code ); - - ~filesystem_error() throw(); - - virtual const char * what() const throw(); - - int native_error() const { return m_sys_err; } - // Note: a value of 0 implies a library (rather than system) error - error_code error() const { return m_err; } - const std::string & who() const; // name of who throwing exception - const path & path1() const; // argument 1 to who; may be empty() - const path & path2() const; // argument 2 to who; may be empty() - - private: - class m_imp; - shared_ptr m_imp_ptr; - int m_sys_err; - error_code m_err; - }; - - } // namespace filesystem -} // namespace boost - -#include // pops abi_suffix.hpp pragmas -#endif // BOOST_FILESYSTEM_EXCEPTION_HPP +// This header is no long used. The contents have been moved to path.hpp. +// It is provided so that user code #includes do not have to be changed. diff --git a/include/boost/filesystem/fstream.hpp b/include/boost/filesystem/fstream.hpp index a800182..f867717 100644 --- a/include/boost/filesystem/fstream.hpp +++ b/include/boost/filesystem/fstream.hpp @@ -12,180 +12,562 @@ #ifndef BOOST_FILESYSTEM_FSTREAM_HPP #define BOOST_FILESYSTEM_FSTREAM_HPP -#include // includes +#include // for 8.3 hack (see below) +#include #include #include #include -#include // must be the last header +#include // must be the last #include + +// NOTE: fstream.hpp for Boost 1.32.0 and earlier supplied workarounds for +// various compiler problems. They have been removed to ease development of the +// basic i18n functionality. Once the new interface is stable, the workarounds +// will be reinstated for any compilers that otherwise can support the rest of +// the library after internationalization. namespace boost { namespace filesystem { + namespace detail + { +# if defined(BOOST_WINDOWS_API) && !defined(BOOST_FILESYSTEM_NARROW_ONLY) + // The 8.3 hack: + // C++98 does not supply a wchar_t open, so try to get an equivalent + // narrow char name based on the short, so-called 8.3, name. + BOOST_FILESYSTEM_DECL bool create_file_api( const std::wstring & ph, + std::ios_base::openmode mode ); // true if succeeds + BOOST_FILESYSTEM_DECL std::string narrow_path_api( + const std::wstring & ph ); // return is empty if fails + + std::string narrow_path( const std::wstring & file_ph, + std::ios_base::openmode mode ) + // return will be empty() if cannot supply narrow short path + { + std::string narrow_ph; + bool created_file( false ); + if ( status_api( file_ph, 0 ) == boost::filesystem::not_found_flag ) + { + if ( (mode & std::ios_base::out) == 0 + || !create_file_api( file_ph, mode ) ) return narrow_ph; + created_file = true; + } + narrow_ph = narrow_path_api( file_ph ); + if ( narrow_ph.empty()&& created_file ) remove_api( file_ph ); + return narrow_ph; + } +# endif + + inline const std::string & narrow_path( const std::string & file_ph, + std::ios_base::openmode ) + { return file_ph; } + + } // namespace detail + template < class charT, class traits = std::char_traits > class basic_filebuf : public std::basic_filebuf { + private: // disallow copying + basic_filebuf( const basic_filebuf & ); + const basic_filebuf & operator=( const basic_filebuf & ); public: + basic_filebuf() {} virtual ~basic_filebuf() {} -#if !BOOST_WORKAROUND( BOOST_MSVC, < 1300 ) - std::basic_filebuf * open( const path & file_ph, - std::ios_base::openmode mode ) - { - return std::basic_filebuf::open( - file_ph.native_file_string().c_str(), mode ); - } -#endif - }; - typedef basic_filebuf filebuf; -# ifndef BOOST_NO_STD_WSTRING - typedef basic_filebuf wfilebuf; +# ifndef BOOST_FILESYSTEM_NARROW_ONLY + template + typename boost::enable_if, + basic_filebuf *>::type + open( const Path & file_ph, std::ios_base::openmode mode ); + + basic_filebuf * + open( const wpath & file_ph, std::ios_base::openmode mode ); # endif +# if !BOOST_WORKAROUND( BOOST_MSVC, <= 1200 ) // VC++ 6.0 can't handle this + basic_filebuf * + open( const path & file_ph, std::ios_base::openmode mode ); +# endif + }; + template < class charT, class traits = std::char_traits > class basic_ifstream : public std::basic_ifstream { + private: // disallow copying + basic_ifstream( const basic_ifstream & ); + const basic_ifstream & operator=( const basic_ifstream & ); public: basic_ifstream() {} -#if !BOOST_WORKAROUND( BOOST_MSVC, == 1310 ) - explicit basic_ifstream( const path & file_ph, - std::ios_base::openmode mode = std::ios_base::in ) - : std::basic_ifstream( - file_ph.native_file_string().c_str(), mode ) {} -# if !BOOST_WORKAROUND( BOOST_MSVC, < 1300 ) // VC++ 6.0 can't handle this - void open( const path & file_ph, - std::ios_base::openmode mode = std::ios_base::in ) - { - std::basic_ifstream::open( - file_ph.native_file_string().c_str(), mode ); - } -# endif -#else // workaround for VC++ 7.1 bug id VSWhidbey 38416 - explicit basic_ifstream( const path & file_ph ) - : std::basic_ifstream( - file_ph.native_file_string().c_str(), std::ios_base::in ) {} - basic_ifstream( const path & file_ph, - std::ios_base::openmode mode ) - : std::basic_ifstream( - file_ph.native_file_string().c_str(), mode ) {} - void open( const path & file_ph ) - { - std::basic_ifstream::open( - file_ph.native_file_string().c_str(), std::ios_base::in ); - } - void open( const path & file_ph, - std::ios_base::openmode mode ) - { - std::basic_ifstream::open( - file_ph.native_file_string().c_str(), mode ); - } -#endif + + // use two signatures, rather than one signature with default second + // argument, to workaround VC++ 7.1 bug (ID VSWhidbey 38416) + +# ifndef BOOST_FILESYSTEM_NARROW_ONLY + template + explicit basic_ifstream( const Path & file_ph, + typename boost::enable_if >::type* dummy = 0 ); + + template + basic_ifstream( const Path & file_ph, std::ios_base::openmode mode, + typename boost::enable_if >::type* dummy = 0 ); + + template + typename boost::enable_if, void>::type + open( const Path & file_ph ); + + template + typename boost::enable_if, void>::type + open( const Path & file_ph, std::ios_base::openmode mode ); + + explicit basic_ifstream( const wpath & file_ph ); + basic_ifstream( const wpath & file_ph, std::ios_base::openmode mode ); + void open( const wpath & file_ph ); + void open( const wpath & file_ph, std::ios_base::openmode mode ); +# endif + + explicit basic_ifstream( const path & file_ph ); + basic_ifstream( const path & file_ph, std::ios_base::openmode mode ); +# if !BOOST_WORKAROUND( BOOST_MSVC, <= 1200 ) // VC++ 6.0 can't handle this + void open( const path & file_ph ); + void open( const path & file_ph, std::ios_base::openmode mode ); +# endif virtual ~basic_ifstream() {} }; - typedef basic_ifstream ifstream; -# ifndef BOOST_NO_STD_WSTRING - typedef basic_ifstream wifstream; -# endif - template < class charT, class traits = std::char_traits > class basic_ofstream : public std::basic_ofstream { + private: // disallow copying + basic_ofstream( const basic_ofstream & ); + const basic_ofstream & operator=( const basic_ofstream & ); public: basic_ofstream() {} -#if !BOOST_WORKAROUND( BOOST_MSVC, == 1310 ) - explicit basic_ofstream( const path & file_ph, - std::ios_base::openmode mode = std::ios_base::out ) - : std::basic_ofstream( - file_ph.native_file_string().c_str(), mode ) {} -# if !BOOST_WORKAROUND( BOOST_MSVC, < 1300 ) - void open( const path & file_ph, - std::ios_base::openmode mode = std::ios_base::out ) - { - std::basic_ofstream::open( - file_ph.native_file_string().c_str(), mode ); - } -# endif -#else // workaround for VC++ 7.1 bug id VSWhidbey 38416 - explicit basic_ofstream( const path & file_ph ) - : std::basic_ofstream( - file_ph.native_file_string().c_str(), std::ios_base::out ) {} - basic_ofstream( const path & file_ph, - std::ios_base::openmode mode ) - : std::basic_ofstream( - file_ph.native_file_string().c_str(), mode ) {} - void open( const path & file_ph ) - { - std::basic_ofstream::open( - file_ph.native_file_string().c_str(), std::ios_base::out ); - } - void open( const path & file_ph, - std::ios_base::openmode mode ) - { - std::basic_ofstream::open( - file_ph.native_file_string().c_str(), mode ); - } -#endif + + // use two signatures, rather than one signature with default second + // argument, to workaround VC++ 7.1 bug (ID VSWhidbey 38416) + +# ifndef BOOST_FILESYSTEM_NARROW_ONLY + + template + explicit basic_ofstream( const Path & file_ph, + typename boost::enable_if >::type* dummy = 0 ); + explicit basic_ofstream( const wpath & file_ph ); + + template + basic_ofstream( const Path & file_ph, std::ios_base::openmode mode, + typename boost::enable_if >::type* dummy = 0 ); + basic_ofstream( const wpath & file_ph, std::ios_base::openmode mode ); + + template + typename boost::enable_if, void>::type + open( const Path & file_ph ); + void open( const wpath & file_ph ); + + template + typename boost::enable_if, void>::type + open( const Path & file_ph, std::ios_base::openmode mode ); + void open( const wpath & file_ph, std::ios_base::openmode mode ); + +# endif + + explicit basic_ofstream( const path & file_ph ); + basic_ofstream( const path & file_ph, std::ios_base::openmode mode ); +# if !BOOST_WORKAROUND( BOOST_MSVC, <= 1200 ) // VC++ 6.0 can't handle this + void open( const path & file_ph ); + void open( const path & file_ph, std::ios_base::openmode mode ); +# endif virtual ~basic_ofstream() {} }; - typedef basic_ofstream ofstream; -# ifndef BOOST_NO_STD_WSTRING - typedef basic_ofstream wofstream; -# endif - template < class charT, class traits = std::char_traits > class basic_fstream : public std::basic_fstream { + private: // disallow copying + basic_fstream( const basic_fstream & ); + const basic_fstream & operator=( const basic_fstream & ); public: basic_fstream() {} -#if !BOOST_WORKAROUND( BOOST_MSVC, == 1310 ) - explicit basic_fstream( const path & file_ph, - std::ios_base::openmode mode = std::ios_base::in|std::ios_base::out ) - : std::basic_fstream( - file_ph.native_file_string().c_str(), mode ) {} -# if !BOOST_WORKAROUND( BOOST_MSVC, < 1300 ) - void open( const path & file_ph, - std::ios_base::openmode mode = std::ios_base::in|std::ios_base::out ) - { - std::basic_fstream::open( - file_ph.native_file_string().c_str(), mode ); - } -# endif -#else // workaround for VC++ 7.1 bug id VSWhidbey 38416 - explicit basic_fstream( const path & file_ph ) - : std::basic_fstream( - file_ph.native_file_string().c_str(), - std::ios_base::in|std::ios_base::out ) {} - basic_fstream( const path & file_ph, - std::ios_base::openmode mode ) - : std::basic_fstream( - file_ph.native_file_string().c_str(), mode ) {} - void open( const path & file_ph ) - { - std::basic_fstream::open( - file_ph.native_file_string().c_str(), - std::ios_base::in|std::ios_base::out ); - } - void open( const path & file_ph, - std::ios_base::openmode mode ) - { - std::basic_fstream::open( - file_ph.native_file_string().c_str(), mode ); - } -#endif + + // use two signatures, rather than one signature with default second + // argument, to workaround VC++ 7.1 bug (ID VSWhidbey 38416) + +# ifndef BOOST_FILESYSTEM_NARROW_ONLY + + template + explicit basic_fstream( const Path & file_ph, + typename boost::enable_if >::type* dummy = 0 ); + explicit basic_fstream( const wpath & file_ph ); + + template + basic_fstream( const Path & file_ph, std::ios_base::openmode mode, + typename boost::enable_if >::type* dummy = 0 ); + basic_fstream( const wpath & file_ph, std::ios_base::openmode mode ); + + template + typename boost::enable_if, void>::type + open( const Path & file_ph ); + void open( const wpath & file_ph ); + + template + typename boost::enable_if, void>::type + open( const Path & file_ph, std::ios_base::openmode mode ); + void open( const wpath & file_ph, std::ios_base::openmode mode ); + +# endif + + explicit basic_fstream( const path & file_ph ); + basic_fstream( const path & file_ph, std::ios_base::openmode mode ); +# if !BOOST_WORKAROUND( BOOST_MSVC, <= 1200 ) // VC++ 6.0 can't handle this + void open( const path & file_ph ); + void open( const path & file_ph, std::ios_base::openmode mode ); +# endif virtual ~basic_fstream() {} + }; + typedef basic_filebuf filebuf; + typedef basic_ifstream ifstream; + typedef basic_ofstream ofstream; typedef basic_fstream fstream; -# ifndef BOOST_NO_STD_WSTRING + +# ifndef BOOST_FILESYSTEM_NARROW_ONLY + typedef basic_filebuf wfilebuf; + typedef basic_ifstream wifstream; typedef basic_fstream wfstream; + typedef basic_ofstream wofstream; +# endif + +# ifndef BOOST_FILESYSTEM_NARROW_ONLY + +// basic_filebuf definitions -----------------------------------------------// + + template + template + typename boost::enable_if, + basic_filebuf *>::type + basic_filebuf::open( const Path & file_ph, + std::ios_base::openmode mode ) + { + + std::string narrow_ph( detail::narrow_path( + file_ph.external_file_string(), mode ) ); + return narrow_ph.empty() + || (std::basic_filebuf::open( narrow_ph.c_str(), mode ) + == 0) ? 0 : this; + } + + template + basic_filebuf * + basic_filebuf::open( const wpath & file_ph, + std::ios_base::openmode mode ) + { + return open( file_ph, mode ); + } + +// basic_ifstream definitions ----------------------------------------------// + + template template + basic_ifstream::basic_ifstream(const Path & file_ph, + typename boost::enable_if >::type* ) + : std::basic_ifstream( + detail::narrow_path( file_ph.external_file_string(), + std::ios_base::in ).c_str(), std::ios_base::in ) {} + + template + basic_ifstream::basic_ifstream( const wpath & file_ph ) + : std::basic_ifstream( + detail::narrow_path( file_ph.external_file_string(), + std::ios_base::in ).c_str(), std::ios_base::in ) {} + + template template + basic_ifstream::basic_ifstream( const Path & file_ph, + std::ios_base::openmode mode, + typename boost::enable_if >::type* ) + : std::basic_ifstream( + detail::narrow_path( file_ph.external_file_string(), + mode ).c_str(), mode ) {} + + template + basic_ifstream::basic_ifstream( const wpath & file_ph, + std::ios_base::openmode mode ) + : std::basic_ifstream( + detail::narrow_path( file_ph.external_file_string(), + mode ).c_str(), mode ) {} + + template template + typename boost::enable_if, void>::type + basic_ifstream::open( const Path & file_ph ) + { + std::basic_ifstream::open( + detail::narrow_path( file_ph.external_file_string(), + std::ios_base::in ).c_str(), std::ios_base::in ); + } + + template + void basic_ifstream::open( const wpath & file_ph ) + { + std::basic_ifstream::open( + detail::narrow_path( file_ph.external_file_string(), + std::ios_base::in ).c_str(), std::ios_base::in ); + } + + template template + typename boost::enable_if, void>::type + basic_ifstream::open( const Path & file_ph, + std::ios_base::openmode mode ) + { + std::basic_ifstream::open( + detail::narrow_path( file_ph.external_file_string(), + mode ).c_str(), mode ); + } + + template + void basic_ifstream::open( const wpath & file_ph, + std::ios_base::openmode mode ) + { + std::basic_ifstream::open( + detail::narrow_path( file_ph.external_file_string(), + mode ).c_str(), mode ); + } + +// basic_ofstream definitions ----------------------------------------------// + + template template + basic_ofstream::basic_ofstream(const Path & file_ph, + typename boost::enable_if >::type* ) + : std::basic_ofstream( + detail::narrow_path( file_ph.external_file_string(), + std::ios_base::out ).c_str(), std::ios_base::out ) {} + + template + basic_ofstream::basic_ofstream( const wpath & file_ph ) + : std::basic_ofstream( + detail::narrow_path( file_ph.external_file_string(), + std::ios_base::out ).c_str(), std::ios_base::out ) {} + + template template + basic_ofstream::basic_ofstream( const Path & file_ph, + std::ios_base::openmode mode, + typename boost::enable_if >::type* ) + : std::basic_ofstream( + detail::narrow_path( file_ph.external_file_string(), + mode ).c_str(), mode ) {} + + template + basic_ofstream::basic_ofstream( const wpath & file_ph, + std::ios_base::openmode mode ) + : std::basic_ofstream( + detail::narrow_path( file_ph.external_file_string(), + mode ).c_str(), mode ) {} + + template template + typename boost::enable_if, void>::type + basic_ofstream::open( const Path & file_ph ) + { + std::basic_ofstream::open( + detail::narrow_path( file_ph.external_file_string(), + std::ios_base::out ).c_str(), std::ios_base::out ); + } + + template + void basic_ofstream::open( const wpath & file_ph ) + { + std::basic_ofstream::open( + detail::narrow_path( file_ph.external_file_string(), + std::ios_base::out ).c_str(), std::ios_base::out ); + } + + template template + typename boost::enable_if, void>::type + basic_ofstream::open( const Path & file_ph, + std::ios_base::openmode mode ) + { + std::basic_ofstream::open( + detail::narrow_path( file_ph.external_file_string(), + mode ).c_str(), mode ); + } + + template + void basic_ofstream::open( const wpath & file_ph, + std::ios_base::openmode mode ) + { + std::basic_ofstream::open( + detail::narrow_path( file_ph.external_file_string(), + mode ).c_str(), mode ); + } + +// basic_fstream definitions -----------------------------------------------// + + template template + basic_fstream::basic_fstream(const Path & file_ph, + typename boost::enable_if >::type* ) + : std::basic_fstream( + detail::narrow_path( file_ph.external_file_string(), + std::ios_base::in|std::ios_base::out ).c_str(), + std::ios_base::in|std::ios_base::out ) {} + + template + basic_fstream::basic_fstream( const wpath & file_ph ) + : std::basic_fstream( + detail::narrow_path( file_ph.external_file_string(), + std::ios_base::in|std::ios_base::out ).c_str(), + std::ios_base::in|std::ios_base::out ) {} + + template template + basic_fstream::basic_fstream( const Path & file_ph, + std::ios_base::openmode mode, + typename boost::enable_if >::type* ) + : std::basic_fstream( + detail::narrow_path( file_ph.external_file_string(), + mode ).c_str(), mode ) {} + + template + basic_fstream::basic_fstream( const wpath & file_ph, + std::ios_base::openmode mode ) + : std::basic_fstream( + detail::narrow_path( file_ph.external_file_string(), + mode ).c_str(), mode ) {} + + template template + typename boost::enable_if, void>::type + basic_fstream::open( const Path & file_ph ) + { + std::basic_fstream::open( + detail::narrow_path( file_ph.external_file_string(), + std::ios_base::in|std::ios_base::out ).c_str(), + std::ios_base::in|std::ios_base::out ); + } + + template + void basic_fstream::open( const wpath & file_ph ) + { + std::basic_fstream::open( + detail::narrow_path( file_ph.external_file_string(), + std::ios_base::in|std::ios_base::out ).c_str(), + std::ios_base::in|std::ios_base::out ); + } + + template template + typename boost::enable_if, void>::type + basic_fstream::open( const Path & file_ph, + std::ios_base::openmode mode ) + { + std::basic_fstream::open( + detail::narrow_path( file_ph.external_file_string(), + mode ).c_str(), mode ); + } + + template + void basic_fstream::open( const wpath & file_ph, + std::ios_base::openmode mode ) + { + std::basic_fstream::open( + detail::narrow_path( file_ph.external_file_string(), + mode ).c_str(), mode ); + } + +# endif + +# if !BOOST_WORKAROUND( BOOST_MSVC, <= 1200 ) // VC++ 6.0 can't handle this + template + basic_filebuf * + basic_filebuf::open( const path & file_ph, + std::ios_base::openmode mode ) + { + return std::basic_filebuf::open( + file_ph.file_string().c_str(), mode ) == 0 ? 0 : this; + } +# endif + + template + basic_ifstream::basic_ifstream( const path & file_ph ) + : std::basic_ifstream( + file_ph.file_string().c_str(), std::ios_base::in ) {} + + template + basic_ifstream::basic_ifstream( const path & file_ph, + std::ios_base::openmode mode ) + : std::basic_ifstream( + file_ph.file_string().c_str(), mode ) {} + +# if !BOOST_WORKAROUND( BOOST_MSVC, <= 1200 ) // VC++ 6.0 can't handle this + template + void basic_ifstream::open( const path & file_ph ) + { + std::basic_ifstream::open( + file_ph.file_string().c_str(), std::ios_base::in ); + } + + template + void basic_ifstream::open( const path & file_ph, + std::ios_base::openmode mode ) + { + std::basic_ifstream::open( + file_ph.file_string().c_str(), mode ); + } +# endif + + template + basic_ofstream::basic_ofstream( const path & file_ph ) + : std::basic_ofstream( + file_ph.file_string().c_str(), std::ios_base::out ) {} + + template + basic_ofstream::basic_ofstream( const path & file_ph, + std::ios_base::openmode mode ) + : std::basic_ofstream( + file_ph.file_string().c_str(), mode ) {} + +# if !BOOST_WORKAROUND( BOOST_MSVC, <= 1200 ) // VC++ 6.0 can't handle this + template + void basic_ofstream::open( const path & file_ph ) + { + std::basic_ofstream::open( + file_ph.file_string().c_str(), std::ios_base::out ); + } + + template + void basic_ofstream::open( const path & file_ph, + std::ios_base::openmode mode ) + { + std::basic_ofstream::open( + file_ph.file_string().c_str(), mode ); + } +# endif + + template + basic_fstream::basic_fstream( const path & file_ph ) + : std::basic_fstream( + file_ph.file_string().c_str(), + std::ios_base::in|std::ios_base::out ) {} + + + template + basic_fstream::basic_fstream( const path & file_ph, + std::ios_base::openmode mode ) + : std::basic_fstream( + file_ph.file_string().c_str(), mode ) {} + +# if !BOOST_WORKAROUND( BOOST_MSVC, <= 1200 ) // VC++ 6.0 can't handle this + template + void basic_fstream::open( const path & file_ph ) + { + std::basic_fstream::open( + file_ph.file_string().c_str(), std::ios_base::in|std::ios_base::out ); + } + + template + void basic_fstream::open( const path & file_ph, + std::ios_base::openmode mode ) + { + std::basic_fstream::open( + file_ph.file_string().c_str(), mode ); + } # endif } // namespace filesystem - } // namespace boost -#include // pops abi_suffix.hpp pragmas +#include // pops abi_prefix.hpp pragmas #endif // BOOST_FILESYSTEM_FSTREAM_HPP diff --git a/include/boost/filesystem/operations.hpp b/include/boost/filesystem/operations.hpp index 891012d..ab3b2aa 100644 --- a/include/boost/filesystem/operations.hpp +++ b/include/boost/filesystem/operations.hpp @@ -1,6 +1,6 @@ -// boost/filesystem/directory.hpp ------------------------------------------// +// boost/filesystem/operations.hpp -----------------------------------------// -// Copyright © 2002, 2003 Beman Dawes +// Copyright © 2002-2005 Beman Dawes // Copyright © 2002 Jan Langer // Copyright © 2001 Dietmar Kuehl // @@ -12,138 +12,981 @@ //----------------------------------------------------------------------------// -#ifndef BOOST_FILESYSTEM_DIRECTORY_HPP -#define BOOST_FILESYSTEM_DIRECTORY_HPP +#ifndef BOOST_FILESYSTEM_OPERATIONS_HPP +#define BOOST_FILESYSTEM_OPERATIONS_HPP #include // includes + #include +#include +#include #include #include +#include #include +#include // for pair #include -#include // must be the last header +#ifdef BOOST_WINDOWS_API +#include +#endif + +#include // must be the last #include # ifdef BOOST_NO_STDC_NAMESPACE namespace std { using ::time_t; } # endif +# ifndef BOOST_FILESYSTEM_NARROW_ONLY +# define BOOST_FS_FUNC(BOOST_FS_TYPE) \ + template typename boost::enable_if, \ + BOOST_FS_TYPE>::type +# define BOOST_FS_TYPENAME typename +# else +# define BOOST_FS_FUNC(BOOST_FS_TYPE) inline BOOST_FS_TYPE + typedef boost::filesystem::path Path; +# define BasicDirItr boost::filesystem::directory_iterator +# define BOOST_FS_TYPENAME +# endif + //----------------------------------------------------------------------------// namespace boost { namespace filesystem { + template class basic_directory_iterator; + + // BOOST_FILESYSTEM_NARROW_ONLY needs this: + typedef basic_directory_iterator directory_iterator; + + typedef char status_flags; + static const status_flags error_flag = 1; + static const status_flags not_found_flag = 2; + static const status_flags directory_flag = 4; + static const status_flags file_flag = 8; + static const status_flags other_flag = 16; + static const status_flags symlink_flag = 32; + + struct symlink_t{}; + BOOST_FILESYSTEM_DECL extern symlink_t symlink; + + template class basic_directory_entry; + + struct space_info + { + // all values are byte counts + boost::uintmax_t capacity; + boost::uintmax_t free; // <= capacity + boost::uintmax_t available; // <= free + }; + + namespace detail + { + typedef std::pair< boost::filesystem::system_error_type, bool > + query_pair; + + typedef std::pair< boost::filesystem::system_error_type, boost::uintmax_t > + uintmax_pair; + + typedef std::pair< boost::filesystem::system_error_type, std::time_t > + time_pair; + + typedef std::pair< boost::filesystem::system_error_type, space_info > + space_pair; + + template< class Path > + struct directory_pair + { + typedef std::pair< boost::filesystem::system_error_type, + typename Path::external_string_type > type; + }; + + + BOOST_FILESYSTEM_DECL boost::filesystem::status_flags + status_api( const std::string & ph, + boost::filesystem::system_error_type * ec = 0 ); + BOOST_FILESYSTEM_DECL bool + symbolic_link_exists_api( const std::string & ); // deprecated +# ifndef BOOST_WINDOWS_API + BOOST_FILESYSTEM_DECL boost::filesystem::status_flags + symlink_status_api( const std::string & ph, + boost::filesystem::system_error_type * ec = 0 ); +# endif + BOOST_FILESYSTEM_DECL query_pair + is_empty_api( const std::string & ph ); + BOOST_FILESYSTEM_DECL query_pair + equivalent_api( const std::string & ph1, const std::string & ph2 ); + BOOST_FILESYSTEM_DECL uintmax_pair + file_size_api( const std::string & ph ); + BOOST_FILESYSTEM_DECL space_pair + space_api( const std::string & ph ); + BOOST_FILESYSTEM_DECL time_pair + last_write_time_api( const std::string & ph ); + BOOST_FILESYSTEM_DECL boost::filesystem::system_error_type + last_write_time_api( const std::string & ph, std::time_t new_value ); + BOOST_FILESYSTEM_DECL boost::filesystem::system_error_type + get_current_path_api( std::string & ph ); + BOOST_FILESYSTEM_DECL query_pair + create_directory_api( const std::string & ph ); + BOOST_FILESYSTEM_DECL boost::filesystem::system_error_type + create_hard_link_api( const std::string & to_ph, + const std::string & from_ph ); + BOOST_FILESYSTEM_DECL boost::filesystem::system_error_type + create_symlink_api( const std::string & to_ph, + const std::string & from_ph ); + BOOST_FILESYSTEM_DECL boost::filesystem::system_error_type + remove_api( const std::string & ph ); + BOOST_FILESYSTEM_DECL boost::filesystem::system_error_type + rename_api( const std::string & from, const std::string & to ); + BOOST_FILESYSTEM_DECL boost::filesystem::system_error_type + copy_file_api( const std::string & from, const std::string & to ); + +# if defined(BOOST_WINDOWS_API) + + BOOST_FILESYSTEM_DECL boost::filesystem::system_error_type + get_full_path_name_api( const std::string & ph, std::string & target ); + +# if !defined(BOOST_FILESYSTEM_NARROW_ONLY) + + BOOST_FILESYSTEM_DECL boost::filesystem::status_flags + status_api( const std::wstring & ph, + boost::filesystem::system_error_type * ec = 0 ); + BOOST_FILESYSTEM_DECL query_pair + is_empty_api( const std::wstring & ph ); + BOOST_FILESYSTEM_DECL query_pair + equivalent_api( const std::wstring & ph1, const std::wstring & ph2 ); + BOOST_FILESYSTEM_DECL uintmax_pair + file_size_api( const std::wstring & ph ); + BOOST_FILESYSTEM_DECL space_pair + space_api( const std::wstring & ph ); + BOOST_FILESYSTEM_DECL boost::filesystem::system_error_type + get_full_path_name_api( const std::wstring & ph, std::wstring & target ); + BOOST_FILESYSTEM_DECL time_pair + last_write_time_api( const std::wstring & ph ); + BOOST_FILESYSTEM_DECL boost::filesystem::system_error_type + last_write_time_api( const std::wstring & ph, std::time_t new_value ); + BOOST_FILESYSTEM_DECL boost::filesystem::system_error_type + get_current_path_api( std::wstring & ph ); + BOOST_FILESYSTEM_DECL query_pair + create_directory_api( const std::wstring & ph ); + BOOST_FILESYSTEM_DECL boost::filesystem::system_error_type + create_hard_link_api( const std::wstring & existing_ph, + const std::wstring & new_ph ); + BOOST_FILESYSTEM_DECL boost::filesystem::system_error_type + create_symlink_api( const std::wstring & to_ph, + const std::wstring & from_ph ); + BOOST_FILESYSTEM_DECL boost::filesystem::system_error_type + remove_api( const std::wstring & ph ); + BOOST_FILESYSTEM_DECL boost::filesystem::system_error_type + rename_api( const std::wstring & from, const std::wstring & to ); + BOOST_FILESYSTEM_DECL boost::filesystem::system_error_type + copy_file_api( const std::wstring & from, const std::wstring & to ); + +# endif +# endif + + template + unsigned long remove_all_aux( const Path & ph ); + + } // namespace detail + +// operations functions ----------------------------------------------------// + + // The non-template overloads enable automatic conversion from std and + // C-style strings. See basic_path constructors. The enable_if for the + // templates implements the famous "do-the-right-thing" rule. // query functions ---------------------------------------------------------// - BOOST_FILESYSTEM_DECL bool exists( const path & ph ); - BOOST_FILESYSTEM_DECL bool symbolic_link_exists( const path & ph ); - BOOST_FILESYSTEM_DECL bool is_directory( const path & ph ); + BOOST_FS_FUNC(status_flags) + status( const Path & ph, system_error_type * ec = 0 ) + { return detail::status_api( ph.external_file_string(), ec ); } + + BOOST_FS_FUNC(status_flags) + status( const Path & ph, const symlink_t &, system_error_type * ec = 0 ) +# ifdef BOOST_WINDOWS_API + { return detail::status_api( ph.external_file_string(), ec ); } +# else + { return detail::symlink_status_api( ph.external_file_string(), ec ); } +# endif + + inline bool symbolic_link_exists( const path & ph ) // deprecated + { return detail::symbolic_link_exists_api( ph.string() ); } + + BOOST_FS_FUNC(bool) exists( const Path & ph ) + { + system_error_type ec; + status_flags sf( detail::status_api( ph.external_file_string(), &ec ) ); + if ( sf == error_flag ) + boost::throw_exception( basic_filesystem_error( + "boost::filesystem::exists", ph, ec ) ); + return sf != not_found_flag; + } + + BOOST_FS_FUNC(bool) is_directory( const Path & ph ) + { + system_error_type ec; + status_flags sf( detail::status_api( ph.external_file_string(), &ec ) ); + if ( sf == error_flag ) + boost::throw_exception( basic_filesystem_error( + "boost::filesystem::is_directory", ph, ec ) ); + return (sf & directory_flag) != 0; + } + + BOOST_FS_FUNC(bool) is_file( const Path & ph ) + { + system_error_type ec; + status_flags sf( detail::status_api( ph.external_file_string(), &ec ) ); + if ( sf == error_flag ) + boost::throw_exception( basic_filesystem_error( + "boost::filesystem::is_file", ph, ec ) ); + return (sf & file_flag) != 0; + } + + BOOST_FS_FUNC(bool) is_other( const Path & ph ) + { + system_error_type ec; + status_flags sf( detail::status_api( ph.external_file_string(), &ec ) ); + if ( sf == error_flag ) + boost::throw_exception( basic_filesystem_error( + "boost::filesystem::is_other", ph, ec ) ); + return (sf & other_flag) != 0; + } + + BOOST_FS_FUNC(bool) is_symlink( const Path & ph ) + { +# ifdef BOOST_WINDOWS_API + return false; +# else + system_error_type ec; + status_flags sf( detail::symlink_status_api( ph.external_file_string(), &ec ) ); + if ( sf == error_flag ) + boost::throw_exception( basic_filesystem_error( + "boost::filesystem::is_symlink", ph, ec ) ); + return sf == symlink_flag; +# endif + } // VC++ 7.0 and earlier has a serious namespace bug that causes a clash // between boost::filesystem::is_empty and the unrelated type trait - // boost::is_empty. The workaround for those who must use broken versions - // of VC++ is to use the function _is_empty. All others should use the - // correct is_empty name. - BOOST_FILESYSTEM_DECL bool _is_empty( const path & ph ); // deprecated + // boost::is_empty. -# if !defined( BOOST_MSVC ) || BOOST_MSVC > 1300 - inline bool is_empty( const path & ph ) { return _is_empty( ph ); } -# endif +# if !defined( BOOST_MSVC ) || BOOST_MSVC > 1300 + BOOST_FS_FUNC(bool) is_empty( const Path & ph ) +# else + BOOST_FS_FUNC(bool) _is_empty( const Path & ph ) +# endif + { + detail::query_pair result = detail::is_empty_api( ph.external_file_string() ); + if ( result.first != 0 ) + boost::throw_exception( basic_filesystem_error( + "boost::filesystem::is_empty", ph, result.first ) ); + return result.second; + } + + BOOST_FS_FUNC(bool) equivalent( const Path & ph1, const Path & ph2 ) + { + detail::query_pair result = detail::equivalent_api( + ph1.external_file_string(), ph2.external_file_string() ); + if ( result.first != 0 ) + boost::throw_exception( basic_filesystem_error( + "boost::filesystem::equivalent", ph1, ph2, result.first ) ); + return result.second; + } + + BOOST_FS_FUNC(boost::uintmax_t) file_size( const Path & ph ) + { + detail::uintmax_pair result + = detail::file_size_api( ph.external_file_string() ); + if ( result.first != 0 ) + boost::throw_exception( basic_filesystem_error( + "boost::filesystem::file_size", ph, result.first ) ); + return result.second; + } + + BOOST_FS_FUNC(space_info) space( const Path & ph ) + { + detail::space_pair result + = detail::space_api( ph.external_file_string() ); + if ( result.first != 0 ) + boost::throw_exception( basic_filesystem_error( + "boost::filesystem::space", ph, result.first ) ); + return result.second; + } + + BOOST_FS_FUNC(std::time_t) last_write_time( const Path & ph ) + { + detail::time_pair result + = detail::last_write_time_api( ph.external_file_string() ); + if ( result.first != 0 ) + boost::throw_exception( basic_filesystem_error( + "boost::filesystem::last_write_time", ph, result.first ) ); + return result.second; + } - BOOST_FILESYSTEM_DECL bool equivalent( const path & ph1, const path & ph2 ); - BOOST_FILESYSTEM_DECL boost::intmax_t file_size( const path & ph ); - BOOST_FILESYSTEM_DECL std::time_t last_write_time( const path & ph ); - BOOST_FILESYSTEM_DECL void last_write_time( const path & ph, const std::time_t new_time ); // operations --------------------------------------------------------------// - BOOST_FILESYSTEM_DECL bool create_directory( const path & directory_ph ); + BOOST_FS_FUNC(bool) create_directory( const Path & dir_ph ) + { + detail::query_pair result( + detail::create_directory_api( dir_ph.external_directory_string() ) ); + if ( result.first != 0 ) + boost::throw_exception( basic_filesystem_error( + "boost::filesystem::create_directory", + dir_ph, result.first ) ); + return result.second; + } - BOOST_FILESYSTEM_DECL bool remove( const path & ph ); - BOOST_FILESYSTEM_DECL unsigned long remove_all( const path & ph ); + BOOST_FS_FUNC(void) + create_hard_link( const Path & to_ph, const Path & from_ph ) + { + system_error_type result( + detail::create_hard_link_api( + to_ph.external_file_string(), + from_ph.external_file_string() ) ); + if ( result != 0 ) + boost::throw_exception( basic_filesystem_error( + "boost::filesystem::create_hard_link", + to_ph, from_ph, result ) ); + } - BOOST_FILESYSTEM_DECL void rename( const path & from_path, - const path & to_path ); + BOOST_FS_FUNC(system_error_type) + create_hard_link( const Path & to_ph, const Path & from_ph, + const std::nothrow_t & ) + { + return detail::create_hard_link_api( + to_ph.external_file_string(), + from_ph.external_file_string() ); + } - BOOST_FILESYSTEM_DECL void copy_file( const path & from_file_ph, - const path & to_file_ph ); + BOOST_FS_FUNC(void) + create_symlink( const Path & to_ph, const Path & from_ph ) + { + system_error_type result( + detail::create_symlink_api( + to_ph.external_file_string(), + from_ph.external_file_string() ) ); + if ( result != 0 ) + boost::throw_exception( basic_filesystem_error( + "boost::filesystem::create_symlink", + to_ph, from_ph, result ) ); + } - BOOST_FILESYSTEM_DECL path current_path(); - BOOST_FILESYSTEM_DECL const path & initial_path(); + BOOST_FS_FUNC(system_error_type) + create_symlink( const Path & to_ph, const Path & from_ph, + const std::nothrow_t & ) + { + return detail::create_symlink_api( + to_ph.external_file_string(), + from_ph.external_file_string() ); + } - BOOST_FILESYSTEM_DECL path system_complete( const path & ph ); - BOOST_FILESYSTEM_DECL path complete( const path & ph, const path & base = initial_path() ); + BOOST_FS_FUNC(bool) remove( const Path & ph ) + { + if ( exists( ph ) + || is_symlink( ph ) ) // handle dangling symbolic links + // note that the POSIX behavior for symbolic links is what we want; + // the link rather than what it points to is deleted. Windows behavior + // doesn't matter; is_symlink() is always false on Windows. + { + system_error_type result = detail::remove_api( ph.external_file_string() ); + if ( result != 0 ) + boost::throw_exception( basic_filesystem_error( + "boost::filesystem::remove", + ph, result ) ); + return true; + } + return false; + } + + BOOST_FS_FUNC(unsigned long) remove_all( const Path & ph ) + { + return exists( ph )|| is_symlink( ph ) + ? detail::remove_all_aux( ph ) : 0; + } + + BOOST_FS_FUNC(void) rename( const Path & from_path, const Path & to_path ) + { + system_error_type result = detail::rename_api( + from_path.external_directory_string(), + to_path.external_directory_string() ); + if ( result != 0 ) + boost::throw_exception( basic_filesystem_error( + "boost::filesystem::rename", + from_path, to_path, result ) ); + } + + BOOST_FS_FUNC(void) copy_file( const Path & from_path, const Path & to_path ) + { + system_error_type result = detail::copy_file_api( + from_path.external_directory_string(), + to_path.external_directory_string() ); + if ( result != 0 ) + boost::throw_exception( basic_filesystem_error( + "boost::filesystem::copy_file", + from_path, to_path, result ) ); + } + + template< class Path > + Path current_path() + { + typename Path::external_string_type ph; + boost::filesystem::system_error_type result; + if ( (result = detail::get_current_path_api( ph )) != 0 ) + boost::throw_exception( basic_filesystem_error( + "boost::filesystem::current_path", result ) ); + return Path( Path::traits_type::to_internal( ph ) ); + } + + template< class Path > + const Path & initial_path() + { + static Path init_path; + if ( init_path.empty() ) init_path = current_path(); + return init_path; + } + +# ifndef BOOST_FILESYSTEM_NO_DEPRECATED + // legacy support + inline path current_path() // overload supports pre-i18n apps + { return current_path(); } + inline const path & initial_path() // overload supports pre-i18n apps + { return initial_path(); } +# endif + + BOOST_FS_FUNC(Path) system_complete( const Path & ph ) + { +# ifdef BOOST_WINDOWS_API + if ( ph.empty() ) return ph; + BOOST_FS_TYPENAME Path::external_string_type sys_ph; + boost::filesystem::system_error_type result; + if ( (result = detail::get_full_path_name_api( ph.external_file_string(), + sys_ph )) != 0 ) + boost::throw_exception( basic_filesystem_error( + "boost::filesystem::system_complete", ph, result ) ); + return Path( Path::traits_type::to_internal( sys_ph ) ); +# else + return (ph.empty() || ph.is_complete()) + ? ph : current_path() / ph; +# endif + } + + BOOST_FS_FUNC(Path) + complete( const Path & ph, + const Path & base/* = initial_path() */) + { + BOOST_ASSERT( base.is_complete() + && (ph.is_complete() || !ph.has_root_name()) + && "boost::filesystem::complete() precondition not met" ); +# ifdef BOOST_WINDOWS_PATH + if (ph.empty() || ph.is_complete()) return ph; + if ( !ph.has_root_name() ) + return ph.has_root_directory() + ? Path( base.root_name() ) / ph + : base / ph; + return base / ph; +# else + return (ph.empty() || ph.is_complete()) ? ph : base / ph; +# endif + } + + // VC++ 7.1 had trouble with default arguments, so separate one argument + // signatures are provided as workarounds; the effect is the same. + BOOST_FS_FUNC(Path) complete( const Path & ph ) + { return complete( ph, initial_path() ); } + + BOOST_FS_FUNC(void) + last_write_time( const Path & ph, const std::time_t new_time ) + { + boost::filesystem::system_error_type result; + if ( (result = detail::last_write_time_api( ph.external_file_string(), + new_time )) != 0 ) + boost::throw_exception( basic_filesystem_error( + "boost::filesystem::last_write_time", ph, result ) ); + } + +# ifndef BOOST_FILESYSTEM_NARROW_ONLY + + // "do-the-right-thing" overloads ---------------------------------------// + + inline status_flags status( const path & ph, system_error_type * ec = 0 ) + { return status( ph, ec ); } + inline status_flags status( const wpath & ph, system_error_type * ec = 0 ) + { return status( ph, ec ); } + + inline status_flags status( const path & ph, const symlink_t &, + system_error_type * ec = 0 ) + { return status( ph, symlink, ec ); } + inline status_flags status( const wpath & ph, const symlink_t &, + system_error_type * ec = 0 ) + { return status( ph, symlink, ec ); } + + inline bool exists( const path & ph ) { return exists( ph ); } + inline bool exists( const wpath & ph ) { return exists( ph ); } + + inline bool is_directory( const path & ph ) + { return is_directory( ph ); } + inline bool is_directory( const wpath & ph ) + { return is_directory( ph ); } + + inline bool is_file( const path & ph ) + { return is_file( ph ); } + inline bool is_file( const wpath & ph ) + { return is_file( ph ); } + + inline bool is_other( const path & ph ) + { return is_other( ph ); } + inline bool is_other( const wpath & ph ) + { return is_other( ph ); } + + inline bool is_symlink( const path & ph ) + { return is_symlink( ph ); } + inline bool is_symlink( const wpath & ph ) + { return is_symlink( ph ); } + + inline bool is_empty( const path & ph ) + { return is_empty( ph ); } + inline bool is_empty( const wpath & ph ) + { return is_empty( ph ); } + + inline bool equivalent( const path & ph1, const path & ph2 ) + { return equivalent( ph1, ph2 ); } + inline bool equivalent( const wpath & ph1, const wpath & ph2 ) + { return equivalent( ph1, ph2 ); } + + inline boost::uintmax_t file_size( const path & ph ) + { return file_size( ph ); } + inline boost::uintmax_t file_size( const wpath & ph ) + { return file_size( ph ); } + + inline space_info space( const path & ph ) + { return space( ph ); } + inline space_info space( const wpath & ph ) + { return space( ph ); } + + inline std::time_t last_write_time( const path & ph ) + { return last_write_time( ph ); } + inline std::time_t last_write_time( const wpath & ph ) + { return last_write_time( ph ); } + + inline bool create_directory( const path & dir_ph ) + { return create_directory( dir_ph ); } + inline bool create_directory( const wpath & dir_ph ) + { return create_directory( dir_ph ); } + + inline void create_hard_link( const path & to_ph, + const path & from_ph ) + { return create_hard_link( to_ph, from_ph ); } + inline void create_hard_link( const wpath & to_ph, + const wpath & from_ph ) + { return create_hard_link( to_ph, from_ph ); } + + inline system_error_type create_hard_link( const path & to_ph, + const path & from_ph, std::nothrow_t & ) + { return create_hard_link( to_ph, from_ph, std::nothrow ); } + inline system_error_type create_hard_link( const wpath & to_ph, + const wpath & from_ph, std::nothrow_t & ) + { return create_hard_link( to_ph, from_ph, std::nothrow ); } + + inline void create_symlink( const path & to_ph, + const path & from_ph ) + { return create_symlink( to_ph, from_ph ); } + inline void create_symlink( const wpath & to_ph, + const wpath & from_ph ) + { return create_symlink( to_ph, from_ph ); } + + inline system_error_type create_symlink( const path & to_ph, + const path & from_ph, const std::nothrow_t & ) + { return create_symlink( to_ph, from_ph, std::nothrow ); } + inline system_error_type create_symlink( const wpath & to_ph, + const wpath & from_ph, const std::nothrow_t & ) + { return create_symlink( to_ph, from_ph, std::nothrow ); } + + inline bool remove( const path & ph ) + { return remove( ph ); } + inline bool remove( const wpath & ph ) + { return remove( ph ); } + + inline unsigned long remove_all( const path & ph ) + { return remove_all( ph ); } + inline unsigned long remove_all( const wpath & ph ) + { return remove_all( ph ); } + + inline void rename( const path & from_path, const path & to_path ) + { return rename( from_path, to_path ); } + inline void rename( const wpath & from_path, const wpath & to_path ) + { return rename( from_path, to_path ); } + + inline void copy_file( const path & from_path, const path & to_path ) + { return copy_file( from_path, to_path ); } + inline void copy_file( const wpath & from_path, const wpath & to_path ) + { return copy_file( from_path, to_path ); } + + inline path system_complete( const path & ph ) + { return system_complete( ph ); } + inline wpath system_complete( const wpath & ph ) + { return system_complete( ph ); } + + inline path complete( const path & ph, + const path & base/* = initial_path()*/ ) + { return complete( ph, base ); } + inline wpath complete( const wpath & ph, + const wpath & base/* = initial_path()*/ ) + { return complete( ph, base ); } + + inline path complete( const path & ph ) + { return complete( ph, initial_path() ); } + inline wpath complete( const wpath & ph ) + { return complete( ph, initial_path() ); } + + inline void last_write_time( const path & ph, const std::time_t new_time ) + { last_write_time( ph, new_time ); } + inline void last_write_time( const wpath & ph, const std::time_t new_time ) + { last_write_time( ph, new_time ); } + +# endif // BOOST_FILESYSTEM_NARROW_ONLY + + namespace detail + { + template + unsigned long remove_all_aux( const Path & ph ) + { + static const boost::filesystem::basic_directory_iterator end_itr; + unsigned long count = 1; + if ( !boost::filesystem::is_symlink( ph ) // don't recurse symbolic links + && boost::filesystem::is_directory( ph ) ) + { + for ( boost::filesystem::basic_directory_iterator itr( ph ); + itr != end_itr; ++itr ) + { + count += remove_all_aux( itr->path() ); + } + } + boost::filesystem::remove( ph ); + return count; + } // test helper -------------------------------------------------------------// // not part of the documented interface because false positives are possible; // there is no law that says that an OS that has large stat.st_size // actually supports large file sizes. - BOOST_FILESYSTEM_DECL bool possible_large_file_size_support(); - + BOOST_FILESYSTEM_DECL bool possible_large_file_size_support(); // directory_iterator helpers ----------------------------------------------// + // forwarding functions avoid need for BOOST_FILESYSTEM_DECL for class -// directory_iterator, and so avoid iterator_facade DLL template problems - namespace detail - { - class dir_itr_imp; - // shared_ptr provides shallow-copy semantics required for InputIterators - typedef boost::shared_ptr< dir_itr_imp > dir_itr_imp_ptr; - BOOST_FILESYSTEM_DECL void dir_itr_init( dir_itr_imp_ptr & m_imp, - const path & dir_path ); - BOOST_FILESYSTEM_DECL path & dir_itr_dereference( - const dir_itr_imp_ptr & m_imp ); - BOOST_FILESYSTEM_DECL void dir_itr_increment( dir_itr_imp_ptr & m_imp ); - } // namespace detail +// basic_directory_iterator, and so avoid iterator_facade DLL template +// problems. They also overload to the proper external path character type. -// directory_iterator ------------------------------------------------------// + BOOST_FILESYSTEM_DECL boost::filesystem::system_error_type + dir_itr_first( void *& handle, const std::string & dir_path, + std::string & target, status_flags & sf, status_flags & symlink_sf ); + // eof: return==0 && handle==0 - class directory_iterator - : public boost::iterator_facade< - directory_iterator, - path, - boost::single_pass_traversal_tag > - { - public: - directory_iterator(){} // creates the "end" iterator - explicit directory_iterator( const path & p ) - { detail::dir_itr_init( m_imp, p ); } + BOOST_FILESYSTEM_DECL boost::filesystem::system_error_type + dir_itr_increment( void *& handle, std::string & target, + status_flags & sf, status_flags & symlink_sf ); + // eof: return==0 && handle==0 -/* -The *r++ requirement doesn't appear to apply to the new single_pass_traversal category -Thus I'm leaving the proxy out pending confirmation from the N1477 authors -struct path_proxy // allows *r++ to work, as required by 24.1.1 + BOOST_FILESYSTEM_DECL boost::filesystem::system_error_type + dir_itr_close( void *& handle ); + // Effects: none if handle==0, otherwise close handle, set handle=0 + +# if defined(BOOST_WINDOWS_API) && !defined(BOOST_FILESYSTEM_NARROW_ONLY) + BOOST_FILESYSTEM_DECL boost::filesystem::system_error_type + dir_itr_first( void *& handle, const std::wstring & ph, + std::wstring & target, status_flags & sf, status_flags & symlink_sf ); + BOOST_FILESYSTEM_DECL boost::filesystem::system_error_type + dir_itr_increment( void *& handle, std::wstring & target, + status_flags & sf, status_flags & symlink_sf ); +# endif + + template< class Path > + class dir_itr_imp { - path pv; - explicit path_proxy( const path & p ) : pv(p) {} - path operator*() const { return pv; } + public: + basic_directory_entry m_directory_entry; + void * m_handle; + + dir_itr_imp() : m_handle(0) {} + + ~dir_itr_imp() { dir_itr_close( m_handle ); } }; - path_proxy operator++(int) - { - path_proxy pp( m_deref() ); - ++*this; - return pp; - } -*/ + } // namespace detail + +// basic_directory_iterator ------------------------------------------------// + + template< class Path > + class basic_directory_iterator + : public boost::iterator_facade< + basic_directory_iterator, + basic_directory_entry, + boost::single_pass_traversal_tag > + { + public: + typedef Path path_type; + + basic_directory_iterator(){} // creates the "end" iterator + + explicit basic_directory_iterator( const Path & dir_path ); private: - detail::dir_itr_imp_ptr m_imp; + + // shared_ptr provides shallow-copy semantics required for InputIterators. + // m_imp.get()==0 indicates the end iterator. + boost::shared_ptr< detail::dir_itr_imp< Path > > m_imp; + friend class boost::iterator_core_access; - reference dereference() const - { return detail::dir_itr_dereference( m_imp ); } - void increment() - { detail::dir_itr_increment( m_imp ); } - bool equal( const directory_iterator & rhs ) const + + typename boost::iterator_facade< + basic_directory_iterator, + basic_directory_entry, + boost::single_pass_traversal_tag >::reference dereference() const + { + BOOST_ASSERT( m_imp.get() && "attempt to dereference end iterator" ); + return m_imp->m_directory_entry; + } + + void increment(); + + bool equal( const basic_directory_iterator & rhs ) const { return m_imp == rhs.m_imp; } }; + + typedef basic_directory_iterator< path > directory_iterator; +# ifndef BOOST_FILESYSTEM_NARROW_ONLY + typedef basic_directory_iterator< wpath > wdirectory_iterator; +# endif + + // basic_directory_iterator implementation ---------------------------// + + template + basic_directory_iterator::basic_directory_iterator( + const Path & dir_path ) : m_imp( new detail::dir_itr_imp ) + { + system_error_type sys_err(0); + typename Path::external_string_type name; + status_flags sf, symlink_sf; + + if ( dir_path.empty() + || (sys_err = detail::dir_itr_first( m_imp->m_handle, + dir_path.external_directory_string(), + name, sf, symlink_sf )) != 0 ) + { + boost::throw_exception( basic_filesystem_error( + "boost::filesystem::basic_directory_iterator constructor", + dir_path, sys_err ) ); + } + + if ( m_imp->m_handle == 0 ) m_imp.reset(); // eof, so make end iterator + else // not eof + { + m_imp->m_directory_entry.assign( dir_path + / Path::traits_type::to_internal( name ), sf, symlink_sf ); + if ( name[0] == dot::value // dot or dot-dot + && (name.size() == 1 + || (name[1] == dot::value + && name.size() == 2)) ) + { increment(); } + } + } + + template + void basic_directory_iterator::increment() + { + BOOST_ASSERT( m_imp.get() && "attempt to increment end iterator" ); + BOOST_ASSERT( m_imp->m_handle != 0 && "internal program error" ); + + system_error_type sys_err(0); + typename Path::external_string_type name; + status_flags sf, symlink_sf; + + for (;;) + { + if ( (sys_err = detail::dir_itr_increment( m_imp->m_handle, name, + sf, symlink_sf )) != 0 ) + { + boost::throw_exception( basic_filesystem_error( + "boost::filesystem::basic_directory_iterator increment", + m_imp->m_directory_entry.path().branch_path(), sys_err ) ); + } + if ( m_imp->m_handle == 0 ) { m_imp.reset(); return; } // eof, make end + if ( !(name[0] == dot::value // !(dot or dot-dot) + && (name.size() == 1 + || (name[1] == dot::value + && name.size() == 2))) ) + { + m_imp->m_directory_entry.replace_leaf( + Path::traits_type::to_internal( name ), sf, symlink_sf ); + return; + } + } + } + + // basic_directory_entry -----------------------------------------------// + + template + class basic_directory_entry + { + public: + typedef Path path_type; + typedef typename Path::string_type string_type; + + // compiler generated copy-ctor, copy assignment, and destructor apply + + basic_directory_entry() : m_status(0), m_symlink_status(0) {} + explicit basic_directory_entry( const path_type & p, + status_flags sf = 0, status_flags symlink_sf = 0 ) + : m_path(p), m_status(sf), m_symlink_status(symlink_sf) + {} + + void assign( const path_type & p, + status_flags sf = 0, status_flags symlink_sf = 0 ) + { m_path = p; m_status = sf; m_symlink_status = symlink_sf; } + + void replace_leaf( const string_type & s, + status_flags sf = 0, status_flags symlink_sf = 0 ) + { + m_path.remove_leaf(); + m_path /= s; + m_status = sf; + m_symlink_status = symlink_sf; + } + + // conversion simplifies most common use of basic_directory_entry + operator const path_type &() const { return m_path; } + +# ifndef BOOST_FILESYSTEM_NO_DEPRECATED + // deprecated functions preserve common use cases in legacy code + typename Path::string_type leaf() const + { + return path().leaf(); + } + typename Path::string_type string() const + { + return path().string(); + } +# endif + + const Path & path() const { return m_path; } + status_flags status( system_error_type * ec=0 ) const; + status_flags status( const symlink_t &, system_error_type * ec=0 ) const; + + bool exists() const; + bool is_directory() const; + bool is_file() const; + bool is_other() const; + bool is_symlink() const; + + private: + path_type m_path; + mutable status_flags m_status; // stat()-like + mutable status_flags m_symlink_status; // lstat()-like + + // cache on query is guaranteed, even if directory iteration doesn't cache + void m_get_status_if_needed( system_error_type * ec = 0 ) const + { + if ( m_status == 0 ) + { +# ifndef BOOST_WINDOWS_API + if ( m_symlink_status != 0 + && (m_symlink_status & symlink_flag) != symlink_flag ) + { m_status = m_symlink_status; } + else { m_status = boost::filesystem::status( m_path, ec ); } +# else + m_status = boost::filesystem::status( m_path, ec ); +# endif + } + } + +# ifndef BOOST_WINDOWS_API + void m_get_symlink_status_if_needed( system_error_type * ec = 0 ) const + { + if ( m_symlink_status == 0 ) + m_symlink_status = boost::filesystem::status( m_path, symlink, ec ); + } +# endif + + }; // basic_directory_status + + typedef basic_directory_entry directory_entry; +# ifndef BOOST_FILESYSTEM_NARROW_ONLY + typedef basic_directory_entry wdirectory_entry; +# endif + + // basic_directory_entry implementation --------------------------------// + + template + status_flags + basic_directory_entry::status( system_error_type * ec ) const + { + m_get_status_if_needed( ec ); + return m_status; + } + + template + status_flags + basic_directory_entry::status( const symlink_t &, + system_error_type * ec ) const + { + # ifndef BOOST_WINDOWS_API + m_get_symlink_status_if_needed( ec ); + return m_symlink_status; +# else + m_get_status_if_needed( ec ); + return m_status; +# endif + } + + template + bool basic_directory_entry::exists() const + { + m_get_status_if_needed(); + return m_status != not_found_flag; + } + + template + bool basic_directory_entry::is_directory() const + { + m_get_status_if_needed(); + return (m_status & directory_flag) != 0; + } + + template + bool basic_directory_entry::is_file() const + { + m_get_status_if_needed(); + return (m_status & file_flag) != 0; + } + + template + bool basic_directory_entry::is_other() const + { + m_get_status_if_needed(); + return (m_status & other_flag) != 0; + } + + template + bool basic_directory_entry::is_symlink() const + { +# ifndef BOOST_WINDOWS_API + m_get_symlink_status_if_needed(); + return (m_symlink_status & symlink_flag) != 0; +# else + return false; +# endif + } + } // namespace filesystem } // namespace boost +#undef BOOST_FS_FUNC -#include // pops abi_suffix.hpp pragmas -#endif // BOOST_FILESYSTEM_DIRECTORY_HPP + +#include // pops abi_prefix.hpp pragmas +#endif // BOOST_FILESYSTEM_OPERATIONS_HPP diff --git a/include/boost/filesystem/path.hpp b/include/boost/filesystem/path.hpp index fc4d98d..f4502c6 100644 --- a/include/boost/filesystem/path.hpp +++ b/include/boost/filesystem/path.hpp @@ -1,6 +1,6 @@ // boost/filesystem/path.hpp -----------------------------------------------// -// © Copyright Beman Dawes 2002-2003 +// © Copyright Beman Dawes 2002-2005 // Use, modification, and distribution is subject to 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) @@ -14,96 +14,1182 @@ #include #include +#include +#include +#include +#include + #include +#include // for lexicographical_compare +#include // needed by basic_path inserter and extractor +#include #include -#include // must be the last header +# ifndef BOOST_FILESYSTEM_NARROW_ONLY +# include +# endif + +#include // must be the last #include //----------------------------------------------------------------------------// namespace boost { - namespace filesystem + namespace BOOST_FILESYSTEM_NAMESPACE { - class directory_iterator; + template class basic_path; + struct path_traits; + typedef basic_path< std::string, path_traits > path; - // 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; } + }; - class BOOST_FILESYSTEM_DECL path +# ifndef BOOST_FILESYSTEM_NARROW_ONLY + + struct wpath_traits; + + typedef basic_path< std::wstring, wpath_traits > wpath; + + struct 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 + +// error reporting support -------------------------------------------------// + + typedef int errno_type; // determined by C standard + typedef int system_error_type; // both POSIX and Windows use int + +# ifdef BOOST_WINDOWS_API + BOOST_FILESYSTEM_DECL + errno_type lookup_errno( system_error_type sys_err_code ); +# else + inline errno_type lookup_errno( system_error_type sys_err_code ) + { return sys_err_code; } +# endif + + // deprecated support for legacy function name + inline errno_type lookup_error_code( system_error_type sys_err_code ) + { return lookup_errno( sys_err_code ); } + + BOOST_FILESYSTEM_DECL + void system_message( system_error_type sys_err_code, std::string & target ); + // Effects: appends error message to target + +# if defined(BOOST_WINDOWS_API) && !defined(BOOST_FILESYSTEM_NARROW_ONLY) + BOOST_FILESYSTEM_DECL void + system_message( system_error_type sys_err_code, std::wstring & target ); +# endif + + // filesystem_error ----------------------------------------------------// + + class filesystem_error : public std::runtime_error + // see http://www.boost.org/more/error_handling.html for design rationale { public: - typedef bool (*name_check)( const std::string & name ); + filesystem_error() + : std::runtime_error("filesystem error"), m_sys_err(0) {} + explicit filesystem_error( + const std::string & what_arg, system_error_type sys_ec = 0 ) + : std::runtime_error(what_arg), m_sys_err(sys_ec) {} - // compiler generates copy constructor, copy assignment, and destructor + system_error_type system_error() const { return m_sys_err; } + // Note: system_error() == 0 implies a library (rather than system) error - path(){} + private: + system_error_type m_sys_err; + }; - path( const std::string & src ); - path( const char * src ); + // basic_filesystem_error ----------------------------------------------// - path( const std::string & src, name_check checker ); - path( const char * src, name_check checker ); + template + class basic_filesystem_error : public filesystem_error + { + // see http://www.boost.org/more/error_handling.html for design rationale + public: + // compiler generates copy constructor and copy assignment - // append operations: - path & operator /=( const path & rhs ); - path operator /( const path & rhs ) const - { return path( *this ) /= rhs; } + typedef Path path_type; - // modification functions: - path & normalize(); + basic_filesystem_error( const std::string & what, + system_error_type sys_err_code ); - // conversion functions: - const std::string & string() const { return m_path; } - std::string native_file_string() const; - std::string native_directory_string() const; + basic_filesystem_error( const std::string & what, + const path_type & path1, system_error_type sys_err_code ); - // decomposition functions: - path root_path() const; - std::string root_name() const; - std::string root_directory() const; - path relative_path() const; - std::string leaf() const; - path branch_path() const; + basic_filesystem_error( const std::string & what, const path_type & path1, + const path_type & path2, system_error_type sys_err_code ); - // query functions: - bool empty() const { return m_path.empty(); } // name consistent with std containers + ~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 ; + } + + private: + struct m_imp + { + path_type m_path1; // may be empty() + path_type m_path2; // may be empty() + }; + boost::shared_ptr m_imp_ptr; + }; + + typedef basic_filesystem_error filesystem_path_error; + +# ifndef BOOST_FILESYSTEM_NARROW_ONLY + typedef basic_filesystem_error filesystem_wpath_error; +# endif + + // path traits ---------------------------------------------------------// + + template struct is_basic_path + { BOOST_STATIC_CONSTANT( bool, value = false ); }; + template<> struct is_basic_path + { BOOST_STATIC_CONSTANT( bool, value = true ); }; +# ifndef BOOST_FILESYSTEM_NARROW_ONLY + template<> struct is_basic_path + { BOOST_STATIC_CONSTANT( bool, value = true ); }; +# endif + + // these only have to be specialized if Path::string_type::value_type + // is not convertible from char + template struct slash + { BOOST_STATIC_CONSTANT( char, value = '/' ); }; + + template struct dot + { BOOST_STATIC_CONSTANT( char, value = '.' ); }; + + template struct colon + { BOOST_STATIC_CONSTANT( char, value = ':' ); }; + +# ifdef BOOST_WINDOWS_PATH + template struct path_alt_separator + { BOOST_STATIC_CONSTANT( char, value = '\\' ); }; +# endif + + // workaround for VC++ 7.0 and earlier issues with nested classes + namespace detail + { + template + 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 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::value ); +// BOOST_STATIC_ASSERT( ::boost::is_same::value || ::boost::is_same::value ); + + public: + // compiler generates copy constructor and copy assignment + + typedef basic_path 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 + basic_path( InputIterator first, InputIterator last ) + { append( first, last ); } +# endif + ~basic_path() {} + + // assignments + basic_path & operator=( const string_type & s ) { m_path=""; operator/=( s ); return *this; } + basic_path & operator=( const value_type * s ) { m_path=""; operator/=( s ); return *this; } +# ifndef BOOST_NO_MEMBER_TEMPLATES + template + 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 + basic_path & append( InputIterator first, InputIterator last ); +# 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_leaf(); + + // 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; + string_type leaf() const; + basic_path branch_path() const; + + 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; - bool has_leaf() const { return !m_path.empty(); } - bool has_branch_path() const; + bool has_relative_path() const { return !relative_path().empty(); } + bool has_leaf() const { return !m_path.empty(); } + bool has_branch_path() const { return !branch_path().empty(); } - // iteration over the names in the path: + // iterators class iterator : public boost::iterator_facade< iterator, - std::string const, + string_type const, boost::bidirectional_traversal_tag > { private: friend class boost::iterator_core_access; - friend class boost::filesystem::path; + friend class boost::BOOST_FILESYSTEM_NAMESPACE::basic_path; - reference dereference() const { return m_name; } + 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; } - BOOST_FILESYSTEM_DECL void increment(); - BOOST_FILESYSTEM_DECL void decrement(); - std::string m_name; // cache current element. - const path * m_path_ptr; // path being iterated over. - std::string::size_type m_pos; // position of name in + friend class boost::BOOST_FILESYSTEM_NAMESPACE::detail::iterator_helper; + + void increment() + { + boost::BOOST_FILESYSTEM_NAMESPACE::detail::iterator_helper::do_increment( + *this ); + } + void decrement() + { + boost::BOOST_FILESYSTEM_NAMESPACE::detail::iterator_helper::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->string().size() - }; + // pos == path_ptr->m_path.size() + }; // iterator + + typedef iterator const_iterator; iterator begin() const; - iterator end() 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; + + // 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 & lhs, + basic_path & rhs ) { lhs.swap( rhs ); } + + template< class String, class Traits > + bool operator<( const basic_path & lhs, const basic_path & 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_type::value_type * lhs, + const basic_path & rhs ) + { + basic_path 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_type & lhs, + const basic_path & rhs ) + { + basic_path tmp( lhs ); + return std::lexicographical_compare( + tmp.begin(), tmp.end(), rhs.begin(), rhs.end() ); + } + + template< class String, class Traits > + bool operator<( const basic_path & lhs, + const typename basic_path::string_type::value_type * rhs ) + { + basic_path tmp( rhs ); + return std::lexicographical_compare( + lhs.begin(), lhs.end(), tmp.begin(), tmp.end() ); + } + + template< class String, class Traits > + bool operator<( const basic_path & lhs, + const typename basic_path::string_type & rhs ) + { + basic_path tmp( rhs ); + return std::lexicographical_compare( + lhs.begin(), lhs.end(), tmp.begin(), tmp.end() ); + } + + template< class String, class Traits > + inline bool operator==( const basic_path & lhs, const basic_path & rhs ) + { + return !(lhs < rhs) && !(rhs < lhs); + } + + template< class String, class Traits > + inline bool operator==( const typename basic_path::string_type::value_type * lhs, + const basic_path & rhs ) + { + basic_path tmp( lhs ); + return !(tmp < rhs) && !(rhs < tmp); + } + + template< class String, class Traits > + inline bool operator==( const typename basic_path::string_type & lhs, + const basic_path & rhs ) + { + basic_path tmp( lhs ); + return !(tmp < rhs) && !(rhs < tmp); + } + + template< class String, class Traits > + inline bool operator==( const basic_path & lhs, + const typename basic_path::string_type::value_type * rhs ) + { + basic_path tmp( rhs ); + return !(lhs < tmp) && !(tmp < lhs); + } + + template< class String, class Traits > + inline bool operator==( const basic_path & lhs, + const typename basic_path::string_type & rhs ) + { + basic_path tmp( rhs ); + return !(lhs < tmp) && !(tmp < lhs); + } + + template< class String, class Traits > + inline bool operator!=( const basic_path & lhs, const basic_path & rhs ) { return !(lhs == rhs); } + + template< class String, class Traits > + inline bool operator!=( const typename basic_path::string_type::value_type * lhs, + const basic_path & rhs ) { return !(basic_path(lhs) == rhs); } + + template< class String, class Traits > + inline bool operator!=( const typename basic_path::string_type & lhs, + const basic_path & rhs ) { return !(basic_path(lhs) == rhs); } + + template< class String, class Traits > + inline bool operator!=( const basic_path & lhs, + const typename basic_path::string_type::value_type * rhs ) + { return !(lhs == basic_path(rhs)); } + + template< class String, class Traits > + inline bool operator!=( const basic_path & lhs, + const typename basic_path::string_type & rhs ) + { return !(lhs == basic_path(rhs)); } + + template< class String, class Traits > + inline bool operator>( const basic_path & lhs, const basic_path & rhs ) { return rhs < lhs; } + + template< class String, class Traits > + inline bool operator>( const typename basic_path::string_type::value_type * lhs, + const basic_path & rhs ) { return rhs < basic_path(lhs); } + + template< class String, class Traits > + inline bool operator>( const typename basic_path::string_type & lhs, + const basic_path & rhs ) { return rhs < basic_path(lhs); } + + template< class String, class Traits > + inline bool operator>( const basic_path & lhs, + const typename basic_path::string_type::value_type * rhs ) + { return basic_path(rhs) < lhs; } + + template< class String, class Traits > + inline bool operator>( const basic_path & lhs, + const typename basic_path::string_type & rhs ) + { return basic_path(rhs) < lhs; } + + template< class String, class Traits > + inline bool operator<=( const basic_path & lhs, const basic_path & rhs ) { return !(rhs < lhs); } + + template< class String, class Traits > + inline bool operator<=( const typename basic_path::string_type::value_type * lhs, + const basic_path & rhs ) { return !(rhs < basic_path(lhs)); } + + template< class String, class Traits > + inline bool operator<=( const typename basic_path::string_type & lhs, + const basic_path & rhs ) { return !(rhs < basic_path(lhs)); } + + template< class String, class Traits > + inline bool operator<=( const basic_path & lhs, + const typename basic_path::string_type::value_type * rhs ) + { return !(basic_path(rhs) < lhs); } + + template< class String, class Traits > + inline bool operator<=( const basic_path & lhs, + const typename basic_path::string_type & rhs ) + { return !(basic_path(rhs) < lhs); } + + template< class String, class Traits > + inline bool operator>=( const basic_path & lhs, const basic_path & rhs ) { return !(lhs < rhs); } + + template< class String, class Traits > + inline bool operator>=( const typename basic_path::string_type::value_type * lhs, + const basic_path & rhs ) { return !(lhs < basic_path(rhs)); } + + template< class String, class Traits > + inline bool operator>=( const typename basic_path::string_type & lhs, + const basic_path & rhs ) { return !(lhs < basic_path(rhs)); } + + template< class String, class Traits > + inline bool operator>=( const basic_path & lhs, + const typename basic_path::string_type::value_type * rhs ) + { return !(basic_path(lhs) < rhs); } + + template< class String, class Traits > + inline bool operator>=( const basic_path & lhs, + const typename basic_path::string_type & rhs ) + { return !(basic_path(lhs) < rhs); } + + // operator / + + template< class String, class Traits > + inline basic_path operator/( + const basic_path & lhs, + const basic_path & rhs ) + { return basic_path( lhs ) /= rhs; } + + template< class String, class Traits > + inline basic_path operator/( + const basic_path & lhs, + const typename String::value_type * rhs ) + { return basic_path( lhs ) /= + basic_path( rhs ); } + + template< class String, class Traits > + inline basic_path operator/( + const basic_path & lhs, const String & rhs ) + { return basic_path( lhs ) /= + basic_path( rhs ); } + + template< class String, class Traits > + inline basic_path operator/( + const typename String::value_type * lhs, + const basic_path & rhs ) + { return basic_path( lhs ) /= rhs; } + + template< class String, class Traits > + inline basic_path operator/( + const String & lhs, const basic_path & rhs ) + { return basic_path( lhs ) /= rhs; } + + // inserters and extractors --------------------------------------------// + +# if !defined( BOOST_MSVC ) || BOOST_MSVC > 1300 // bypass VC++ 7.0 and earlier + + 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; + } +# 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 + inline bool is_separator( typename Path::string_type::value_type c ) + { + return c == slash::value +# ifdef BOOST_WINDOWS_PATH + || c == path_alt_separator::value +# endif + ; + } + + // leaf_pos helper ----------------------------------------------------// + + template + typename String::size_type leaf_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 leaf (or empty) + { + typedef typename + boost::BOOST_FILESYSTEM_NAMESPACE::basic_path path_type; + + // case: "//" + if ( end_pos == 2 + && str[0] == slash::value + && str[1] == slash::value ) return 0; + + // case: ends in "/" + if ( end_pos && str[end_pos-1] == slash::value ) + return end_pos-1; + + // set pos to start of last element + typename String::size_type pos( + str.find_last_of( slash::value, end_pos-1 ) ); +# ifdef BOOST_WINDOWS_PATH + if ( pos == String::npos ) + pos = str.find_last_of( path_alt_separator::value, end_pos-1 ); + if ( pos == String::npos ) + pos = str.find_last_of( colon::value, end_pos-2 ); +# endif + + return ( pos == String::npos // path itself must be a leaf (or empty) + || (pos == 1 && str[0] == slash::value) ) // or net + ? 0 // so leaf 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 + void first_element( + const String & src, // precondition: portable generic path grammar + typename String::size_type & element_pos, + typename String::size_type & element_size, + typename String::size_type size = -1 + ) + { + 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 path_type; + + typename String::size_type cur(0); + + // deal with // [network] + if ( size >= 2 && src[0] == slash::value + && src[1] == slash::value + && (size == 2 + || src[2] != slash::value) ) + { + cur += 2; + element_size += 2; + } + + // leading (not non-network) separator + else if ( src[0] == slash::value ) + { + ++element_size; + // bypass extra leading separators + while ( cur+1 < size + && src[cur+1] == slash::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::value +# endif + && src[cur] != slash::value ) + { + ++cur; + ++element_size; + } + +# ifdef BOOST_WINDOWS_PATH + if ( cur == size ) return; + // include device delimiter + if ( src[cur] == colon::value ) + { ++element_size; } +# endif + + return; + } + + // root_directory_start helper ----------------------------------------// + + template + 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 path_type; + +# ifdef BOOST_WINDOWS_PATH + // case "c:/" + if ( size > 2 + && s[1] == colon::value + && s[2] == slash::value ) return 2; +# endif + + // case "//" + if ( size == 2 + && s[0] == slash::value + && s[1] == slash::value ) return String::npos; + + // case "//net {/}" + if ( size > 3 + && s[0] == slash::value + && s[1] == slash::value + && s[2] != slash::value ) + { + typename String::size_type pos( + s.find( slash::value, 2 ) ); + return pos < size ? pos : String::npos; + } + + // case "/" + if ( size > 0 && s[0] == slash::value ) return 0; + + return String::npos; + } + + // is_non_root_slash helper -------------------------------------------// + + template + 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 + path_type; + + assert( !str.empty() && str[pos] == slash::value + && "precondition violation" ); + + // subsequent logic expects pos to be for leftmost slash of a set + while ( pos > 0 && str[pos-1] == slash::value ) + --pos; + + return pos != 0 + && (pos <= 2 || str[1] != slash::value + || str.find( slash::value, 2 ) != pos) +# ifdef BOOST_WINDOWS_PATH + && (pos !=2 || str[1] != colon::value) +# endif + ; + } + } // namespace detail + + // decomposition functions ----------------------------------------------// + + template + String basic_path::leaf() const + { + typename String::size_type end_pos( + detail::leaf_pos( m_path, m_path.size() ) ); + return (m_path.size() + && end_pos + && m_path[end_pos] == slash::value + && detail::is_non_root_slash< String, Traits >(m_path, end_pos)) + ? String( 1, dot::value ) + : m_path.substr( end_pos ); + } + + template + basic_path basic_path::branch_path() const + { + typename String::size_type end_pos( + detail::leaf_pos( m_path, m_path.size() ) ); + + bool leaf_was_separator( m_path.size() + && m_path[end_pos] == slash::value ); + + // skip separators unless root directory + typename string_type::size_type root_dir_pos( detail::root_directory_start + ( m_path, end_pos ) ); + for ( ; + end_pos > 0 + && (end_pos-1) != root_dir_pos + && m_path[end_pos-1] == slash::value + ; + --end_pos ) {} + + return (end_pos == 1 && root_dir_pos == 0 && leaf_was_separator) + ? path_type() + : path_type( m_path.substr( 0, end_pos ) ); + } + + template + basic_path basic_path::relative_path() const + { + iterator itr( begin() ); + for ( ; itr.m_pos != m_path.size() + && (itr.m_name[0] == slash::value +# ifdef BOOST_WINDOWS_PATH + || itr.m_name[itr.m_name.size()-1] + == colon::value +# endif + ); ++itr ) {} + + return basic_path( m_path.substr( itr.m_pos ) ); + } + + template + String basic_path::root_name() const + { + iterator itr( begin() ); + + return ( itr.m_pos != m_path.size() + && ( + ( itr.m_name.size() > 1 + && itr.m_name[0] == slash::value + && itr.m_name[1] == slash::value + ) +# ifdef BOOST_WINDOWS_PATH + || itr.m_name[itr.m_name.size()-1] + == colon::value +# endif + ) ) + ? *itr + : String(); + } + + template + String basic_path::root_directory() const + { + typename string_type::size_type start( + detail::root_directory_start( m_path, m_path.size() ) ); + + return start == string_type::npos + ? string_type() + : m_path.substr( start, 1 ); + } + + template + basic_path basic_path::root_path() const + { + // even on POSIX, root_name() is non-empty() on network paths + return basic_path( root_name() ) /= root_directory(); + } + + // path query functions -------------------------------------------------// + + template + inline bool basic_path::is_complete() const + { +# ifdef BOOST_WINDOWS_PATH + return has_root_name() && has_root_directory(); +# else + return has_root_directory(); +# endif + } + + template + inline bool basic_path::has_root_path() const + { + return !root_path().empty(); + } + + template + inline bool basic_path::has_root_name() const + { + return !root_name().empty(); + } + + template + inline bool basic_path::has_root_directory() const + { + return !root_directory().empty(); + } + + // append ---------------------------------------------------------------// + + template + void basic_path::m_append_separator_if_needed() + // requires: !empty() + { + if ( +# ifdef BOOST_WINDOWS_PATH + *(m_path.end()-1) != colon::value && +# endif + *(m_path.end()-1) != slash::value ) + { + m_path += slash::value; + } + } + + template + void basic_path::m_append( value_type value ) + { +# ifdef BOOST_CYGWIN_PATH + if ( m_path.empty() ) m_cygwin_root = (value == slash::value); +# endif + +# ifdef BOOST_WINDOWS_PATH + // for BOOST_WINDOWS_PATH, convert alt_separator ('\') to separator ('/') + m_path += ( value == path_alt_separator::value + ? slash::value + : value ); +# else + m_path += value; +# endif + } + + // except that it wouldn't work for BOOST_NO_MEMBER_TEMPLATES compilers, + // the append() member template called with a second argument of 0 could + // replace this code. + template + basic_path & basic_path::operator /= + ( const value_type * next_p ) + { + // bypass native path escape sequence as meaningless for POSIX or Windows + if ( detail::is_separator( *next_p ) + && detail::is_separator( *(next_p+1) ) + && *(next_p+2) == colon::value ) next_p += 3; + + // append slash::value if needed + if ( !empty() && *next_p != 0 + && !detail::is_separator( *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 template + basic_path & basic_path::append( + InputIterator first, InputIterator last ) + { + // bypass native path escape sequence as meaningless for POSIX or Windows + if ( detail::is_separator( *first ) + && detail::is_separator( *(first+1) ) + && *(first+2) == colon::value ) first += 3; + + // append slash::value if needed + if ( !empty() && first != last + && !detail::is_separator( *first ) ) + { m_append_separator_if_needed(); } + + for ( ; first != last && *first; ++first ) m_append( *first ); + return *this; + } +# endif + +# ifndef BOOST_FILESYSTEM_NO_DEPRECATED + + // canonize ------------------------------------------------------------// + + template + basic_path & basic_path::canonize() + { + static const typename string_type::value_type dot_str[] + = { dot::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 + basic_path & basic_path::normalize() + { + static const typename string_type::value_type dot_str[] + = { dot::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::value + && itr != start + && itr != last ) continue; + + // ignore a name and following ".." + if ( !temp.empty() + && itr->size() == 2 + && (*itr)[0] == dot::value + && (*itr)[1] == dot::value ) // dot dot + { + string_type lf( temp.leaf() ); + if ( lf.size() > 0 + && (lf.size() != 1 + || (lf[0] != dot::value + && lf[0] != slash::value)) + && (lf.size() != 2 + || (lf[0] != dot::value + && lf[1] != dot::value +# ifdef BOOST_WINDOWS_PATH + && lf[1] != colon::value +# endif + ) + ) + ) + { + temp.remove_leaf(); + // if not root directory, must also remove "/" if any + if ( temp.m_path.size() > 0 + && temp.m_path[temp.m_path.size()-1] + == slash::value ) + { + typename string_type::size_type rds( + detail::root_directory_start( 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 + + // remove_leaf ----------------------------------------------------------// + + template + basic_path & basic_path::remove_leaf() + { + m_path.erase( + detail::leaf_pos( m_path, m_path.size() ) ); + return *this; + } + + // path conversion functions --------------------------------------------// + + template + const String + basic_path::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( 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::value + && m_path[1] == slash::value + && ( m_path.size() == 2 + || !detail::is_separator( m_path[2] ) + ) ) + { + ++pos; + s += path_alt_separator::value; + s += path_alt_separator::value; + continue; + } + + // bypass extra root separators + if ( in_root ) + { + if ( s.size() > 0 + && s[s.size()-1] == path_alt_separator::value + && m_path[pos] == slash::value + ) continue; + } + + if ( m_path[pos] == slash::value ) + s += path_alt_separator::value; + else + s += m_path[pos]; + + if ( pos > root_dir_start + && m_path[pos] == slash::value ) + { in_root = false; } + } +# ifdef BOOST_CYGWIN_PATH + if ( m_cygwin_root ) s[0] = slash::value; +# endif + return s; +# else + return m_path; +# endif + } + + // iterator functions ---------------------------------------------------// + + template + typename basic_path::iterator basic_path::begin() const + { + iterator itr; + itr.m_path_ptr = this; + typename string_type::size_type element_size; + detail::first_element( m_path, itr.m_pos, element_size ); + itr.m_name = m_path.substr( itr.m_pos, element_size ); + return itr; + } + + template + typename basic_path::iterator basic_path::end() const { iterator itr; itr.m_path_ptr = this; @@ -111,61 +1197,160 @@ namespace boost return itr; } - // default name_check mechanism: - static bool default_name_check_writable(); - static void default_name_check( name_check new_check ); - static name_check default_name_check(); + namespace detail + { + // do_increment ------------------------------------------------------// - // relational operators - bool operator<( const path & that ) const; - bool operator==( const path & that ) const { return !(*this < that) && !(that < *this); } - bool operator!=( const path & that ) const { return !(*this == that); } - bool operator>( const path & that ) const { return that < *this; } - bool operator<=( const path & that ) const { return !(that < *this); } - bool operator>=( const path & that ) const { return !(*this < that); } + template + void iterator_helper::do_increment( iterator & itr ) + { + typedef typename Path::string_type string_type; + typedef typename Path::traits_type traits_type; - private: - // Note: This is an implementation for POSIX and Windows, where there - // are only minor differences between generic and system-specific - // 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 native_file_string() and native_directory_string() formats. + assert( itr.m_pos < itr.m_path_ptr->m_path.size() && "basic_path::iterator increment past end()" ); - std::string m_path; + bool was_net( itr.m_name.size() > 2 + && itr.m_name[0] == slash::value + && itr.m_name[1] == slash::value + && itr.m_name[2] != slash::value ); - friend class directory_iterator; - // Was qualified; como433beta8 reports: - // warning #427-D: qualified name is not allowed in member declaration - friend class iterator; + // increment to position past current element + itr.m_pos += itr.m_name.size(); - public: // should be private, but friend functions don't work for me - void m_path_append( const std::string & src, name_check checker ); - void m_replace_leaf( const char * new_leaf ); - }; + // 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; + } - // path non-member functions ---------------------------------------------// + // process separator (Windows drive spec is only case not a separator) + if ( itr.m_path_ptr->m_path[itr.m_pos] == slash::value ) + { + // detect root directory + if ( was_net + # ifdef BOOST_WINDOWS_PATH + // case "c:/" + || itr.m_name[itr.m_name.size()-1] == colon::value + # endif + ) + { + itr.m_name = slash::value; + return; + } - inline path operator / ( const char * lhs, const path & rhs ) - { return path( lhs ) /= rhs; } + // bypass separators + while ( itr.m_pos != itr.m_path_ptr->m_path.size() + && itr.m_path_ptr->m_path[itr.m_pos] == slash::value ) + { ++itr.m_pos; } - inline path operator / ( const std::string & lhs, const path & rhs ) - { return path( lhs ) /= rhs; } - - // path::name_checks ---------------------------------------------------// + // 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::value; + return; + } + } - 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 no_check( const std::string & name ); // always returns true - BOOST_FILESYSTEM_DECL bool native( const std::string & name ); - // native(name) must return true for any name which MIGHT be valid - // on the native platform. + // get next element + typename string_type::size_type end_pos( + itr.m_path_ptr->m_path.find( slash::value, itr.m_pos ) ); + itr.m_name = itr.m_path_ptr->m_path.substr( itr.m_pos, end_pos - itr.m_pos ); + } - } // namespace filesystem + // do_decrement ------------------------------------------------------// + + template + void iterator_helper::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( + 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::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::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::value + ; + --end_pos ) {} + + itr.m_pos = detail::leaf_pos + ( 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 + basic_filesystem_error::basic_filesystem_error( + const std::string & what, system_error_type sys_err_code ) + : filesystem_error(what, sys_err_code) + { + try + { + m_imp_ptr.reset( new m_imp ); + } + catch (...) { m_imp_ptr.reset(); } + } + + template + basic_filesystem_error::basic_filesystem_error( + const std::string & what, const path_type & path1, + system_error_type sys_err_code ) + : filesystem_error(what, sys_err_code) + { + try + { + m_imp_ptr.reset( new m_imp ); + m_imp_ptr->m_path1 = path1; + } + catch (...) { m_imp_ptr.reset(); } + } + + template + basic_filesystem_error::basic_filesystem_error( + const std::string & what, const path_type & path1, + const path_type & path2, system_error_type sys_err_code ) + : filesystem_error(what, sys_err_code) + { + try + { + m_imp_ptr.reset( new m_imp ); + m_imp_ptr->m_path1 = path1; + m_imp_ptr->m_path2 = path2; + } + catch (...) { m_imp_ptr.reset(); } + } + + } // namespace BOOST_FILESYSTEM_NAMESPACE } // namespace boost -#include // pops abi_suffix.hpp pragmas +#include // pops abi_prefix.hpp pragmas + #endif // BOOST_FILESYSTEM_PATH_HPP diff --git a/src/convenience.cpp b/src/convenience.cpp deleted file mode 100644 index a40333c..0000000 --- a/src/convenience.cpp +++ /dev/null @@ -1,75 +0,0 @@ -// libs/filesystem/src/convenience.cpp -------------------------------------// - -// © Copyright Beman Dawes, 2002 -// © Copyright Vladimir Prus, 2002 -// Use, modification, and distribution is subject to 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 - -//----------------------------------------------------------------------------// - -// define BOOST_FILESYSTEM_SOURCE so that knows -// the library is being built (possibly exporting rather than importing code) -#define BOOST_FILESYSTEM_SOURCE - -#include -#include -#include - -#include // must be the last header - -namespace boost -{ - namespace filesystem - { - -// create_directories (contributed by Vladimir Prus) -----------------------// - - BOOST_FILESYSTEM_DECL bool create_directories(const path& ph) - { - if (ph.empty() || exists(ph)) - { - if ( !ph.empty() && !is_directory(ph) ) - boost::throw_exception( filesystem_error( - "boost::filesystem::create_directories", - ph, "path exists and is not a directory", - not_directory_error ) ); - return false; - } - - // First create branch, by calling ourself recursively - create_directories(ph.branch_path()); - // Now that parent's path exists, create the directory - create_directory(ph); - return true; - } - - BOOST_FILESYSTEM_DECL std::string extension(const path& ph) - { - std::string leaf = ph.leaf(); - - std::string::size_type n = leaf.rfind('.'); - if (n != std::string::npos) - return leaf.substr(n); - else - return std::string(); - } - - BOOST_FILESYSTEM_DECL std::string basename(const path& ph) - { - std::string leaf = ph.leaf(); - - std::string::size_type n = leaf.rfind('.'); - return leaf.substr(0, n); - } - - BOOST_FILESYSTEM_DECL path change_extension(const path& ph, const std::string& new_extension) - { - return ph.branch_path() / (basename(ph) + new_extension); - } - - - } // namespace filesystem -} // namespace boost diff --git a/src/exception.cpp b/src/exception.cpp index 30569c8..eed7b1e 100644 --- a/src/exception.cpp +++ b/src/exception.cpp @@ -15,298 +15,152 @@ #define BOOST_FILESYSTEM_SOURCE #include -#include +#include +#include namespace fs = boost::filesystem; #include // SGI MIPSpro compilers need this -#include # ifdef BOOST_NO_STDC_NAMESPACE namespace std { using ::strerror; } # endif -// BOOST_POSIX or BOOST_WINDOWS specify which API to use. -# if !defined( BOOST_WINDOWS ) && !defined( BOOST_POSIX ) -# if defined(_WIN32) || defined(__WIN32__) || defined(WIN32) || defined(__CYGWIN__) -# define BOOST_WINDOWS -# else -# define BOOST_POSIX -# endif -# endif -# if defined( BOOST_WINDOWS ) +# if defined( BOOST_WINDOWS_API ) # include "windows.h" -# else -# include // for POSIX error codes # endif -#include // must be the last header - //----------------------------------------------------------------------------// namespace { -# ifdef BOOST_WINDOWS - std::string system_message( int sys_err_code ) - { - std::string str; - LPVOID lpMsgBuf; - ::FormatMessageA( - FORMAT_MESSAGE_ALLOCATE_BUFFER | - FORMAT_MESSAGE_FROM_SYSTEM | - FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, - sys_err_code, - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language - (LPSTR) &lpMsgBuf, - 0, - NULL - ); - str += static_cast(lpMsgBuf); - ::LocalFree( lpMsgBuf ); // free the buffer - while ( str.size() - && (str[str.size()-1] == '\n' || str[str.size()-1] == '\r') ) - str.erase( str.size()-1 ); - return str; - } -# else - std::string system_message( int ) - { - std::string str; - str += std::strerror( errno ); - return str; - } -# endif - - struct ec_xlate { int sys_ec; fs::error_code ec; }; +#ifdef BOOST_WINDOWS_API + struct ec_xlate { fs::system_error_type sys_ec; fs::errno_type ec; }; const ec_xlate ec_table[] = { -# ifdef BOOST_WINDOWS - { ERROR_ACCESS_DENIED, fs::security_error }, - { ERROR_INVALID_ACCESS, fs::security_error }, - { ERROR_SHARING_VIOLATION, fs::security_error }, - { ERROR_LOCK_VIOLATION, fs::security_error }, - { ERROR_LOCKED, fs::security_error }, - { ERROR_NOACCESS, fs::security_error }, - { ERROR_WRITE_PROTECT, fs::read_only_error }, - { ERROR_NOT_READY, fs::io_error }, - { ERROR_SEEK, fs::io_error }, - { ERROR_READ_FAULT, fs::io_error }, - { ERROR_WRITE_FAULT, fs::io_error }, - { ERROR_CANTOPEN, fs::io_error }, - { ERROR_CANTREAD, fs::io_error }, - { ERROR_CANTWRITE, fs::io_error }, - { ERROR_DIRECTORY, fs::path_error }, - { ERROR_INVALID_NAME, fs::path_error }, - { ERROR_FILE_NOT_FOUND, fs::not_found_error }, - { ERROR_PATH_NOT_FOUND, fs::not_found_error }, - { ERROR_DEV_NOT_EXIST, fs::not_found_error }, - { ERROR_DEVICE_IN_USE, fs::busy_error }, - { ERROR_OPEN_FILES, fs::busy_error }, - { ERROR_BUSY_DRIVE, fs::busy_error }, - { ERROR_BUSY, fs::busy_error }, - { ERROR_FILE_EXISTS, fs::already_exists_error }, - { ERROR_ALREADY_EXISTS, fs::already_exists_error }, - { ERROR_DIR_NOT_EMPTY, fs::not_empty_error }, - { ERROR_HANDLE_DISK_FULL, fs::out_of_space_error }, - { ERROR_DISK_FULL, fs::out_of_space_error }, - { ERROR_OUTOFMEMORY, fs::out_of_memory_error }, - { ERROR_NOT_ENOUGH_MEMORY, fs::out_of_memory_error }, - { ERROR_TOO_MANY_OPEN_FILES, fs::out_of_resource_error } -# else - { EACCES, fs::security_error }, - { EROFS, fs::read_only_error }, - { EIO, fs::io_error }, - { ENAMETOOLONG, fs::path_error }, - { ENOENT, fs::not_found_error }, - { ENOTDIR, fs::not_directory_error }, - { EAGAIN, fs::busy_error }, - { EBUSY, fs::busy_error }, - { ETXTBSY, fs::busy_error }, - { EEXIST, fs::already_exists_error }, - { ENOTEMPTY, fs::not_empty_error }, - { EISDIR, fs::is_directory_error }, - { ENOSPC, fs::out_of_space_error }, - { ENOMEM, fs::out_of_memory_error }, - { EMFILE, fs::out_of_resource_error } -# endif + // see WinError.h comments for descriptions of errors + + // most common errors first to speed sequential search + { ERROR_FILE_NOT_FOUND, ENOENT }, + { ERROR_PATH_NOT_FOUND, ENOENT }, + + // alphabetical for easy maintenance + { 0, 0 }, // no error + { ERROR_ACCESS_DENIED, EACCES }, + { ERROR_ALREADY_EXISTS, EEXIST }, + { ERROR_BAD_UNIT, ENODEV }, + { ERROR_BUFFER_OVERFLOW, ENAMETOOLONG }, + { ERROR_BUSY, EBUSY }, + { ERROR_BUSY_DRIVE, EBUSY }, + { ERROR_CANNOT_MAKE, EACCES }, + { ERROR_CANTOPEN, EIO }, + { ERROR_CANTREAD, EIO }, + { ERROR_CANTWRITE, EIO }, + { ERROR_CURRENT_DIRECTORY, EACCES }, + { ERROR_DEV_NOT_EXIST, ENODEV }, + { ERROR_DEVICE_IN_USE, EBUSY }, + { ERROR_DIR_NOT_EMPTY, ENOTEMPTY }, + { ERROR_DIRECTORY, EINVAL }, // WinError.h: "The directory name is invalid" + { ERROR_DISK_FULL, ENOSPC }, + { ERROR_FILE_EXISTS, EEXIST }, + { ERROR_HANDLE_DISK_FULL, ENOSPC }, + { ERROR_INVALID_ACCESS, EACCES }, + { ERROR_INVALID_DRIVE, ENODEV }, + { ERROR_INVALID_FUNCTION, ENOSYS }, + { ERROR_INVALID_HANDLE, EBADHANDLE }, + { ERROR_INVALID_NAME, EINVAL }, + { ERROR_LOCK_VIOLATION, EACCES }, + { ERROR_LOCKED, EACCES }, + { ERROR_NOACCESS, EACCES }, + { ERROR_NOT_ENOUGH_MEMORY, ENOMEM }, + { ERROR_NOT_READY, EAGAIN }, + { ERROR_NOT_SAME_DEVICE, EXDEV }, + { ERROR_OPEN_FAILED, EIO }, + { ERROR_OPEN_FILES, EBUSY }, + { ERROR_OUTOFMEMORY, ENOMEM }, + { ERROR_READ_FAULT, EIO }, + { ERROR_SEEK, EIO }, + { ERROR_SHARING_VIOLATION, EACCES }, + { ERROR_TOO_MANY_OPEN_FILES, ENFILE }, + { ERROR_WRITE_FAULT, EIO }, + { ERROR_WRITE_PROTECT, EROFS }, + { 0,EOTHER } }; +#endif - fs::error_code lookup_error( int sys_err_code ) - { - for ( const ec_xlate * cur = &ec_table[0]; - cur != ec_table - + sizeof(ec_table)/sizeof(ec_xlate); ++cur ) - { - if ( sys_err_code == cur->sys_ec ) return cur->ec; - } - return fs::system_error; // general system error code - } - - // These helper functions work for POSIX and Windows. For systems where - // path->native_file_string() != path->native_directory_string(), more - // care would be required to get the right form for the function involved. - - std::string other_error_prep( - const std::string & who, - const std::string & message ) - { - return who + ": " + message; - } - - std::string other_error_prep( - const std::string & who, - const fs::path & path1, - const std::string & message ) - { - return who + ": \"" + path1.native_file_string() + "\": " + message; - } - - std::string system_error_prep( - const std::string & who, - const fs::path & path1, - int sys_err_code ) - { - return who + ": \"" + path1.native_file_string() + "\": " - + system_message( sys_err_code ); - } - - std::string system_error_prep( - const std::string & who, - const fs::path & path1, - const fs::path & path2, - int sys_err_code ) - { - return who + ": \"" + path1.native_file_string() - + "\", \"" + path2.native_file_string() + "\": " - + system_message( sys_err_code ); - } - - const fs::path empty_path; - const std::string empty_string; } // unnamed namespace namespace boost { namespace filesystem { -// filesystem_error m_imp class --------------------------------------------// -// see www.boost.org/more/error_handling.html for implementation rationale +# ifdef BOOST_WINDOWS_API - class filesystem_error::m_imp + BOOST_FILESYSTEM_DECL + errno_type lookup_errno( system_error_type sys_err_code ) { - public: - std::string m_who; - path m_path1; - path m_path2; - std::string m_what; - }; - - -// filesystem_error implementation -----------------------------------------// - - filesystem_error::filesystem_error( - const std::string & who, - const std::string & message ) - : m_sys_err(0), m_err(other_error) - { - try + for ( const ec_xlate * cur = &ec_table[0]; + cur != ec_table + + sizeof(ec_table)/sizeof(ec_xlate); ++cur ) { - m_imp_ptr.reset( new m_imp ); - m_imp_ptr->m_who = who; - m_imp_ptr->m_what = other_error_prep( who, message ); + if ( sys_err_code == cur->sys_ec ) return cur->ec; } - catch (...) { m_imp_ptr.reset(); } - } - - filesystem_error::filesystem_error( - const std::string & who, - const path & path1, - const std::string & message, - error_code ec ) - : m_sys_err(0), m_err(ec) - { - try - { - m_imp_ptr.reset( new m_imp ); - m_imp_ptr->m_who = who; - m_imp_ptr->m_what = other_error_prep( who, path1, message ); - m_imp_ptr->m_path1 = path1; - } - catch (...) { m_imp_ptr.reset(); } - } - - filesystem_error::filesystem_error( - const std::string & who, - const path & path1, - int sys_err_code ) - : m_sys_err(sys_err_code), m_err(lookup_error(sys_err_code)) - { - try - { - m_imp_ptr.reset( new m_imp ); - m_imp_ptr->m_who = who; - m_imp_ptr->m_what = system_error_prep( who, path1, sys_err_code ); - m_imp_ptr->m_path1 = path1; - } - catch (...) { m_imp_ptr.reset(); } + return EOTHER; } - filesystem_error::filesystem_error( - const std::string & who, - const path & path1, - const path & path2, - int sys_err_code ) - : m_sys_err(sys_err_code), m_err(lookup_error(sys_err_code)) + BOOST_FILESYSTEM_DECL void + system_message( system_error_type sys_err_code, std::string & target ) { - try - { - m_imp_ptr.reset( new m_imp ); - m_imp_ptr->m_who = who; - m_imp_ptr->m_what = system_error_prep( who, path1, path2, sys_err_code ); - m_imp_ptr->m_path1 = path1; - m_imp_ptr->m_path2 = path2; - } - catch (...) { m_imp_ptr.reset(); } + LPVOID lpMsgBuf; + ::FormatMessageA( + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + sys_err_code, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language + (LPSTR) &lpMsgBuf, + 0, + NULL + ); + target += static_cast(lpMsgBuf); + ::LocalFree( lpMsgBuf ); // free the buffer + while ( target.size() + && (target[target.size()-1] == '\n' || target[target.size()-1] == '\r') ) + target.erase( target.size()-1 ); } - filesystem_error::~filesystem_error() throw() +# ifndef BOOST_FILESYSTEM_NARROW_ONLY + BOOST_FILESYSTEM_DECL void + system_message( system_error_type sys_err_code, std::wstring & target ) { + LPVOID lpMsgBuf; + ::FormatMessageW( + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + sys_err_code, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language + (LPWSTR) &lpMsgBuf, + 0, + NULL + ); + target += static_cast(lpMsgBuf); + ::LocalFree( lpMsgBuf ); // free the buffer + while ( target.size() + && (target[target.size()-1] == L'\n' || target[target.size()-1] == L'\r') ) + target.erase( target.size()-1 ); } - - const std::string & filesystem_error::who() const +# endif +# else + void + system_message( system_error_type sys_err_code, std::string & target ) { - return m_imp_ptr.get() == 0 ? empty_string : m_imp_ptr->m_who; + target += std::strerror( sys_err_code ); } +# endif - const path & filesystem_error::path1() const - { - return m_imp_ptr.get() == 0 ? empty_path : m_imp_ptr->m_path1; - } - - const path & filesystem_error::path2() const - { - return m_imp_ptr.get() == 0 ? empty_path : m_imp_ptr->m_path2; - } - - const char * filesystem_error::what() const throw() - { - return m_imp_ptr.get() == 0 ? empty_string.c_str() - : m_imp_ptr->m_what.c_str(); - } - - namespace detail - { - BOOST_FILESYSTEM_DECL int system_error_code() // artifact of POSIX and WINDOWS error reporting - { - # ifdef BOOST_WINDOWS - return ::GetLastError(); - # else - return errno; // GCC 3.1 won't accept ::errno - # endif - } - } // namespace detail } // namespace filesystem } // namespace boost - diff --git a/src/operations.cpp b/src/operations.cpp new file mode 100644 index 0000000..3a3c686 --- /dev/null +++ b/src/operations.cpp @@ -0,0 +1,1180 @@ +// operations.cpp ----------------------------------------------------------// + +// Copyright © 2002-2005 Beman Dawes +// Copyright © 2001 Dietmar Kuehl +// Use, modification, and distribution is subject to 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 + +//----------------------------------------------------------------------------// + +// define BOOST_FILESYSTEM_SOURCE so that knows +// the library is being built (possibly exporting rather than importing code) +#define BOOST_FILESYSTEM_SOURCE + +#define __USE_BSD // needed to get struct dirent d_type defines on Linux + +#define _FILE_OFFSET_BITS 64 // at worst, these defines may have no effect, +#define __USE_FILE_OFFSET64 // but that is harmless on Windows and on POSIX + // 64-bit systems or on 32-bit systems which don't have files larger + // than can be represented by a traditional POSIX/UNIX off_t type. + // OTOH, defining them should kick in 64-bit off_t's (and thus + // st_size) on 32-bit systems that provide the Large File + // Support (LFS) interface, such as Linux, Solaris, and IRIX. + // The defines are given before any headers are included to + // ensure that they are available to all included headers. + // That is required at least on Solaris, and possibly on other + // systems as well. + +// for some compilers (CodeWarrior, for example), windows.h +// is getting included by some other boost header, so do this early: +#define _WIN32_WINNT 0x0500 // Windows 2K or later + +#include +#include +#include +#include + +namespace fs = boost::filesystem; + +# if defined(BOOST_WINDOWS_API) +# include "windows.h" +# if defined(__BORLANDC__) || defined(__MWERKS__) +# if defined(__BORLANDC__) + using std::time_t; +# endif +# include "utime.h" +# else +# include "sys/utime.h" +# endif + +# else // BOOST_POSIX_API +# include +# include +# include "dirent.h" +# include "unistd.h" +# include "fcntl.h" +# include "utime.h" +# endif + +#include // even on Windows some functions use stat() +#include +#include +#include // for remove, rename +#include +#include +//#include // for debugging only; comment out when not in use + +#ifdef BOOST_NO_STDC_NAMESPACE +namespace std { using ::strcmp; using ::remove; using ::rename; } +#endif + +// helpers -----------------------------------------------------------------// + +namespace +{ + static const fs::directory_iterator end_itr; + bool is_empty_directory( const std::string & dir_path ) + { + return fs::directory_iterator(fs::path(dir_path)) == end_itr; + } + +#ifdef BOOST_WINDOWS_API + +// 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.) + + inline DWORD get_file_attributes( const char * ph ) + { return ::GetFileAttributesA( ph ); } + +# ifndef BOOST_FILESYSTEM_NARROW_ONLY + + inline DWORD get_file_attributes( const wchar_t * ph ) + { return ::GetFileAttributesW( ph ); } + + static const fs::wdirectory_iterator wend_itr; + bool is_empty_directory( const std::wstring & dir_path ) + { + return fs::wdirectory_iterator(fs::wpath(dir_path)) == wend_itr; + } + + inline BOOL get_file_attributes_ex( const wchar_t * ph, + WIN32_FILE_ATTRIBUTE_DATA & fad ) + { return ::GetFileAttributesExW( ph, ::GetFileExInfoStandard, &fad ); } + + HANDLE create_file( const wchar_t * ph, DWORD dwDesiredAccess, + DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes, + DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, + HANDLE hTemplateFile ) + { + return ::CreateFileW( ph, dwDesiredAccess, dwShareMode, + lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, + hTemplateFile ); + } + + inline DWORD get_current_directory( DWORD sz, wchar_t * buf ) + { return ::GetCurrentDirectoryW( sz, buf ); } + + inline bool get_free_disk_space( const std::wstring & ph, + PULARGE_INTEGER avail, PULARGE_INTEGER total, PULARGE_INTEGER free ) + { return ::GetDiskFreeSpaceExW( ph.c_str(), avail, total, free ) != 0; } + + inline std::size_t get_full_path_name( + const std::wstring & ph, std::size_t len, wchar_t * buf, wchar_t ** p ) + { + return static_cast( + ::GetFullPathNameW( ph.c_str(), + static_cast(len), buf, p )); + } + + inline bool remove_directory( const std::wstring & ph ) + { return ::RemoveDirectoryW( ph.c_str() ) != 0; } + + inline bool delete_file( const std::wstring & ph ) + { return ::DeleteFileW( ph.c_str() ) != 0; } + + inline bool create_directory( const std::wstring & dir ) + { return ::CreateDirectoryW( dir.c_str(), 0 ) != 0; } + + inline bool create_hard_link( const std::wstring & to_ph, + const std::wstring & from_ph ) + { return ::CreateHardLinkW( from_ph.c_str(), to_ph.c_str(), 0 ) != 0; } + +# endif // ifndef BOOST_FILESYSTEM_NARROW_ONLY + + template< class String > + fs::status_flags status_template( const String & ph, + boost::filesystem::system_error_type * ec ) + { + DWORD attr( get_file_attributes( ph.c_str() ) ); + if ( attr == 0xFFFFFFFF ) + { + UINT err = ::GetLastError(); + if ( ec != 0 ) *ec = err; + return ((err == ERROR_FILE_NOT_FOUND) + || (err == ERROR_PATH_NOT_FOUND) + || (err == ERROR_BAD_NETPATH )) + ? fs::not_found_flag : fs::error_flag; + } + return (attr & FILE_ATTRIBUTE_DIRECTORY) != 0 + ? fs::directory_flag : fs::file_flag; + } + + BOOL get_file_attributes_ex( const char * ph, + WIN32_FILE_ATTRIBUTE_DATA & fad ) + { return ::GetFileAttributesExA( ph, ::GetFileExInfoStandard, &fad ); } + + template< class String > + boost::filesystem::detail::query_pair + is_empty_template( const String & ph ) + { + WIN32_FILE_ATTRIBUTE_DATA fad; + if ( get_file_attributes_ex( ph.c_str(), fad ) == 0 ) + return std::make_pair( ::GetLastError(), false ); + return std::make_pair( 0, + ( fad.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) + ? is_empty_directory( ph ) + :( !fad.nFileSizeHigh && !fad.nFileSizeLow ) ); + } + + HANDLE create_file( const char * ph, DWORD dwDesiredAccess, + DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes, + DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, + HANDLE hTemplateFile ) + { + return ::CreateFileA( ph, dwDesiredAccess, dwShareMode, + lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, + hTemplateFile ); + } + + // Thanks to Jeremy Maitin-Shepard for much help and for permission to + // base the equivalent() implementation on portions of his + // file-equivalence-win32.cpp experimental code. + struct handle_wrapper + { + HANDLE handle; + handle_wrapper( HANDLE h ) + : handle(h) {} + ~handle_wrapper() + { + if ( handle != INVALID_HANDLE_VALUE ) + ::CloseHandle(handle); + } + }; + + template< class String > + boost::filesystem::detail::query_pair + equivalent_template( const String & ph1, const String & ph2 ) + { + // Note well: Physical location on external media is part of the + // equivalence criteria. If there are no open handles, physical location + // can change due to defragmentation or other relocations. Thus handles + // must be held open until location information for both paths has + // been retrieved. + handle_wrapper p1( + create_file( + ph1.c_str(), + 0, + FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, + 0, + OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS, + 0 ) ); + int error1(0); // save error code in case we have to throw + if ( p1.handle == INVALID_HANDLE_VALUE ) + error1 = ::GetLastError(); + handle_wrapper p2( + create_file( + ph2.c_str(), + 0, + FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, + 0, + OPEN_EXISTING, + FILE_FLAG_BACKUP_SEMANTICS, + 0 ) ); + if ( p1.handle == INVALID_HANDLE_VALUE + || p2.handle == INVALID_HANDLE_VALUE ) + { + if ( p1.handle != INVALID_HANDLE_VALUE + || p2.handle != INVALID_HANDLE_VALUE ) + { return std::make_pair( 0, false ); } + assert( p1.handle == INVALID_HANDLE_VALUE + && p2.handle == INVALID_HANDLE_VALUE ); + { return std::make_pair( error1, false ); } + } + // at this point, both handles are known to be valid + BY_HANDLE_FILE_INFORMATION info1, info2; + if ( !::GetFileInformationByHandle( p1.handle, &info1 ) ) + { return std::make_pair( ::GetLastError(), false ); } + if ( !::GetFileInformationByHandle( p2.handle, &info2 ) ) + { return std::make_pair( ::GetLastError(), false ); } + // In theory, volume serial numbers are sufficient to distinguish between + // devices, but in practice VSN's are sometimes duplicated, so last write + // time and file size are also checked. + return std::make_pair( 0, + info1.dwVolumeSerialNumber == info2.dwVolumeSerialNumber + && info1.nFileIndexHigh == info2.nFileIndexHigh + && info1.nFileIndexLow == info2.nFileIndexLow + && info1.nFileSizeHigh == info2.nFileSizeHigh + && info1.nFileSizeLow == info2.nFileSizeLow + && info1.ftLastWriteTime.dwLowDateTime + == info2.ftLastWriteTime.dwLowDateTime + && info1.ftLastWriteTime.dwHighDateTime + == info2.ftLastWriteTime.dwHighDateTime ); + } + + template< class String > + boost::filesystem::detail::uintmax_pair + file_size_template( const String & ph ) + { + WIN32_FILE_ATTRIBUTE_DATA fad; + // by now, intmax_t is 64-bits on all Windows compilers + if ( get_file_attributes_ex( ph.c_str(), fad ) == 0 ) + return std::make_pair( ::GetLastError(), 0 ); + if ( (fad.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) !=0 ) + return std::make_pair( ERROR_FILE_NOT_FOUND, 0 ); + return std::make_pair( 0, + (static_cast(fad.nFileSizeHigh) + << (sizeof(fad.nFileSizeLow)*8)) + + fad.nFileSizeLow ); + } + + inline bool get_free_disk_space( const std::string & ph, + PULARGE_INTEGER avail, PULARGE_INTEGER total, PULARGE_INTEGER free ) + { return ::GetDiskFreeSpaceExA( ph.c_str(), avail, total, free ) != 0; } + + template< class String > + boost::filesystem::detail::space_pair + space_template( String & ph ) + { + ULARGE_INTEGER avail, total, free; + boost::filesystem::detail::space_pair result; + if ( get_free_disk_space( ph, &avail, &total, &free ) ) + { + result.first = 0; + result.second.capacity + = (static_cast(total.HighPart) << 32) + + total.LowPart; + result.second.free + = (static_cast(free.HighPart) << 32) + + free.LowPart; + result.second.available + = (static_cast(avail.HighPart) << 32) + + avail.LowPart; + } + else + { + result.first = ::GetLastError(); + result.second.capacity = result.second.free + = result.second.available = 0; + } + return result; + } + + inline DWORD get_current_directory( DWORD sz, char * buf ) + { return ::GetCurrentDirectoryA( sz, buf ); } + + template< class String > + boost::filesystem::system_error_type + get_current_path_template( String & ph ) + { + DWORD sz; + if ( (sz = get_current_directory( 0, + static_cast(0) )) == 0 ) + { sz = 1; } + typedef typename String::value_type value_type; + boost::scoped_array buf( new value_type[sz] ); + if ( get_current_directory( sz, buf.get() ) == 0 ) + return ::GetLastError(); + ph = buf.get(); + return 0; + } + + inline std::size_t get_full_path_name( + const std::string & ph, std::size_t len, char * buf, char ** p ) + { + return static_cast( + ::GetFullPathNameA( ph.c_str(), + static_cast(len), buf, p )); + } + + const std::size_t buf_size( 128 ); + + template + boost::filesystem::system_error_type + get_full_path_name_template( const String & ph, String & target ) + { + typename String::value_type buf[buf_size]; + typename String::value_type * pfn; + std::size_t len = get_full_path_name( ph, + sizeof(buf) , buf, &pfn ); + if ( len == 0 ) return ::GetLastError(); + if ( len > buf_size ) + { + typedef typename String::value_type value_type; + boost::scoped_array big_buf( new value_type[len] ); + if ( (len=get_full_path_name( ph, len , big_buf.get(), &pfn )) + == 0 ) return ::GetLastError(); + big_buf[len] = '\0'; + target = big_buf.get(); + return 0; + } + buf[len] = '\0'; + target = buf; + return 0; + } + + template + boost::filesystem::system_error_type + get_file_write_time( const String & ph, FILETIME & last_write_time ) + { + handle_wrapper hw( + create_file( ph.c_str(), 0, + FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, 0, + OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0 ) ); + if ( hw.handle == INVALID_HANDLE_VALUE ) + return ::GetLastError(); + return ::GetFileTime( hw.handle, 0, 0, &last_write_time ) != 0 + ? 0 : ::GetLastError(); + } + + template + boost::filesystem::system_error_type + set_file_write_time( const String & ph, const FILETIME & last_write_time ) + { + handle_wrapper hw( + create_file( ph.c_str(), FILE_WRITE_ATTRIBUTES, + FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, 0, + OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0 ) ); + if ( hw.handle == INVALID_HANDLE_VALUE ) + return ::GetLastError(); + return ::SetFileTime( hw.handle, 0, 0, &last_write_time ) != 0 + ? 0 : ::GetLastError(); + } + + // these constants come from inspecting some Microsoft sample code + std::time_t to_time_t( const FILETIME & ft ) + { + __int64 t = (static_cast<__int64>( ft.dwHighDateTime ) << 32) + + ft.dwLowDateTime; +# if !defined( BOOST_MSVC ) || BOOST_MSVC > 1300 // > VC++ 7.0 + t -= 116444736000000000LL; +# else + t -= 116444736000000000; +# endif + t /= 10000000; + return static_cast( t ); + } + + void to_FILETIME( std::time_t t, FILETIME & ft ) + { + __int64 temp = t; + temp *= 10000000; +# if !defined( BOOST_MSVC ) || BOOST_MSVC > 1300 // > VC++ 7.0 + temp += 116444736000000000LL; +# else + temp += 116444736000000000; +# endif + ft.dwLowDateTime = static_cast( temp ); + ft.dwHighDateTime = static_cast( temp >> 32 ); + } + + template + boost::filesystem::detail::time_pair + last_write_time_template( const String & ph ) + { + FILETIME lwt; + boost::filesystem::system_error_type result + = get_file_write_time( ph, lwt ); + return std::make_pair( result, to_time_t( lwt ) ); + } + + template + boost::filesystem::system_error_type + last_write_time_template( const String & ph, const std::time_t new_time ) + { + FILETIME lwt; + to_FILETIME( new_time, lwt ); + return set_file_write_time( ph, lwt ); + } + + bool remove_directory( const std::string & ph ) + { return ::RemoveDirectoryA( ph.c_str() ) != 0; } + + bool delete_file( const std::string & ph ) + { return ::DeleteFileA( ph.c_str() ) != 0; } + + template + boost::filesystem::system_error_type + remove_template( const String & ph ) + { + fs::system_error_type ec; + fs::status_flags sf( fs::detail::status_api( ph, &ec ) ); + if ( sf == fs::error_flag ) return ec; + if ( (sf & fs::directory_flag) != 0 ) + { + if ( !remove_directory( ph ) ) + return ::GetLastError(); + } + else + { + if ( !delete_file( ph ) ) return ::GetLastError(); + } + return 0; + } + + inline bool create_directory( const std::string & dir ) + { return ::CreateDirectoryA( dir.c_str(), 0 ) != 0; } + + template + boost::filesystem::detail::query_pair + create_directory_template( const String & dir_ph ) + { + boost::filesystem::system_error_type error(0); + if ( create_directory( dir_ph ) ) return std::make_pair( error, true ); + error = ::GetLastError(); + // an error here may simply mean the postcondition is already met + if ( error == ERROR_ALREADY_EXISTS + && (fs::detail::status_api( dir_ph ) & fs::directory_flag) != 0 ) + return std::make_pair( 0, false ); + return std::make_pair( error, false ); + } + + inline bool create_hard_link( const std::string & to_ph, + const std::string & from_ph ) + { return ::CreateHardLinkA( from_ph.c_str(), to_ph.c_str(), 0 ) != 0; } + + template + boost::filesystem::system_error_type + create_hard_link_template( const String & to_ph, + const String & from_ph ) + { + return create_hard_link( to_ph.c_str(), from_ph.c_str() ) + ? 0 : ::GetLastError(); + } + +#endif +} // unnamed namespace + +namespace boost +{ + namespace filesystem + { + BOOST_FILESYSTEM_DECL symlink_t symlink; + + namespace detail + { + +// free functions ----------------------------------------------------------// + + BOOST_FILESYSTEM_DECL bool possible_large_file_size_support() + { +# ifdef BOOST_POSIX_API + struct stat lcl_stat; + return sizeof( lcl_stat.st_size ) > 4; +# else + return true; +# endif + } + +# ifdef BOOST_WINDOWS_API + + BOOST_FILESYSTEM_DECL fs::status_flags + status_api( const std::string & ph, + boost::filesystem::system_error_type * ec ) + { return status_template( ph, ec ); } + +# ifndef BOOST_FILESYSTEM_NARROW_ONLY + + BOOST_FILESYSTEM_DECL fs::status_flags + status_api( const std::wstring & ph, + boost::filesystem::system_error_type * ec ) + { return status_template( ph, ec ); } + + BOOST_FILESYSTEM_DECL bool symbolic_link_exists_api( const std::wstring & ) + { return false; } + + BOOST_FILESYSTEM_DECL + fs::detail::query_pair is_empty_api( const std::wstring & ph ) + { return is_empty_template( ph ); } + + BOOST_FILESYSTEM_DECL + fs::detail::query_pair + equivalent_api( const std::wstring & ph1, const std::wstring & ph2 ) + { return equivalent_template( ph1, ph2 ); } + + BOOST_FILESYSTEM_DECL + fs::detail::uintmax_pair file_size_api( const std::wstring & ph ) + { return file_size_template( ph ); } + + BOOST_FILESYSTEM_DECL + fs::detail::space_pair space_api( const std::wstring & ph ) + { return space_template( ph ); } + + BOOST_FILESYSTEM_DECL + boost::filesystem::system_error_type + get_current_path_api( std::wstring & ph ) + { return get_current_path_template( ph ); } + + BOOST_FILESYSTEM_DECL boost::filesystem::system_error_type + get_full_path_name_api( const std::wstring & ph, std::wstring & target ) + { return get_full_path_name_template( ph, target ); } + + BOOST_FILESYSTEM_DECL time_pair + last_write_time_api( const std::wstring & ph ) + { return last_write_time_template( ph ); } + + BOOST_FILESYSTEM_DECL boost::filesystem::system_error_type + last_write_time_api( const std::wstring & ph, std::time_t new_value ) + { return last_write_time_template( ph, new_value ); } + + BOOST_FILESYSTEM_DECL fs::detail::query_pair + create_directory_api( const std::wstring & ph ) + { return create_directory_template( ph ); } + + BOOST_FILESYSTEM_DECL fs::system_error_type + create_hard_link_api( const std::wstring & to_ph, + const std::wstring & from_ph ) + { return create_hard_link_template( to_ph, from_ph ); } + + BOOST_FILESYSTEM_DECL fs::system_error_type + create_symlink_api( const std::wstring & to_ph, + const std::wstring & from_ph ) + { return ERROR_NOT_SUPPORTED; } + + BOOST_FILESYSTEM_DECL boost::filesystem::system_error_type + remove_api( const std::wstring & ph ) { return remove_template( ph ); } + + BOOST_FILESYSTEM_DECL boost::filesystem::system_error_type + rename_api( const std::wstring & from, const std::wstring & to ) + { + return ::MoveFileW( from.c_str(), to.c_str() ) + ? 0 : ::GetLastError(); + } + + BOOST_FILESYSTEM_DECL boost::filesystem::system_error_type + copy_file_api( const std::wstring & from, const std::wstring & to ) + { + return ::CopyFileW( from.c_str(), to.c_str(), /*fail_if_exists=*/true ) + ? 0 : ::GetLastError(); + } + + BOOST_FILESYSTEM_DECL bool create_file_api( const std::wstring & ph, + std::ios_base::openmode mode ) // true if succeeds + { + DWORD access( + ((mode & std::ios_base::in) == 0 ? 0 : GENERIC_READ) + | ((mode & std::ios_base::out) == 0 ? 0 : GENERIC_WRITE) ); + + DWORD disposition(0); // see 27.8.1.3 Table 92 + if ( (mode&~std::ios_base::binary) + == (std::ios_base::out|std::ios_base::app) ) + disposition = OPEN_ALWAYS; + else if ( (mode&~(std::ios_base::binary|std::ios_base::out)) + == std::ios_base::in ) disposition = OPEN_EXISTING; + else if ( ((mode&~(std::ios_base::binary|std::ios_base::trunc)) + == std::ios_base::out ) + || ((mode&~std::ios_base::binary) + == (std::ios_base::in|std::ios_base::out|std::ios_base::trunc)) ) + disposition = CREATE_ALWAYS; + else assert( 0 && "invalid mode argument" ); + + HANDLE handle ( ::CreateFileW( ph.c_str(), access, + FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, 0, + disposition, (mode &std::ios_base::out) != 0 + ? FILE_ATTRIBUTE_ARCHIVE : FILE_ATTRIBUTE_NORMAL, 0 ) ); + if ( handle == INVALID_HANDLE_VALUE ) return false; + ::CloseHandle( handle ); + return true; + } + + BOOST_FILESYSTEM_DECL std::string narrow_path_api( + const std::wstring & ph ) // return is empty if fails + { + std::string narrow_short_form; + std::wstring short_form; + for ( DWORD buf_sz( static_cast( ph.size()+1 ));; ) + { + boost::scoped_array buf( new wchar_t[buf_sz] ); + DWORD sz( ::GetShortPathNameW( ph.c_str(), buf.get(), buf_sz ) ); + if ( sz == 0 ) return narrow_short_form; + if ( sz <= buf_sz ) + { + short_form += buf.get(); + break; + } + buf_sz = sz + 1; + } + for ( std::wstring::iterator it( short_form.begin() ); + it != short_form.end(); ++it ) + { + // my expectation is that Unicode characters in long names will be + // converted to 8-bit characters in the short 8.3 form names; this + // expectation is based on observation rather than Windows API + // documentation. + assert( (*it & 0xFF) == *it || "program logic error; see program comments" ); + if ( (*it & 0xFF) != *it ) + boost::throw_exception( "program logic error" ); + narrow_short_form += static_cast( *it ); + } + return narrow_short_form; + } + + BOOST_FILESYSTEM_DECL boost::filesystem::system_error_type + dir_itr_first( void *& handle, const std::wstring & dir, + std::wstring & target, status_flags & sf, status_flags & symlink_sf ) + { + // use a form of search Sebastian Martel reports will work with Win98 + std::wstring dirpath( dir ); + dirpath += (dirpath.empty() + || dirpath[dirpath.size()-1] != L'\\') ? L"\\*" : L"*"; + + WIN32_FIND_DATAW data; + if ( (handle = ::FindFirstFileW( dirpath.c_str(), &data )) + == INVALID_HANDLE_VALUE ) + { + handle = 0; + return ::GetLastError() == ERROR_FILE_NOT_FOUND + ? 0 : ::GetLastError(); + } + target = data.cFileName; + symlink_sf = sf = ((data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + == FILE_ATTRIBUTE_DIRECTORY) + ? fs::directory_flag + : fs::file_flag; + return 0; + } + + BOOST_FILESYSTEM_DECL boost::filesystem::system_error_type + dir_itr_increment( void *& handle, std::wstring & target, + status_flags & sf, status_flags & symlink_sf ) + { + WIN32_FIND_DATAW data; + if ( ::FindNextFileW( handle, &data ) == 0 ) // fails + { + int error = ::GetLastError(); + dir_itr_close( handle ); + return error == ERROR_NO_MORE_FILES ? 0 : error; + } + target = data.cFileName; + symlink_sf = sf = ((data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + == FILE_ATTRIBUTE_DIRECTORY) + ? fs::directory_flag + : fs::file_flag; + return 0; + } + +# endif // ifndef BOOST_FILESYSTEM_NARROW_ONLY + + // suggested by Walter Landry + BOOST_FILESYSTEM_DECL bool symbolic_link_exists_api( const std::string & ) + { return false; } + + BOOST_FILESYSTEM_DECL + fs::detail::query_pair is_empty_api( const std::string & ph ) + { return is_empty_template( ph ); } + + BOOST_FILESYSTEM_DECL + fs::detail::query_pair + equivalent_api( const std::string & ph1, const std::string & ph2 ) + { return equivalent_template( ph1, ph2 ); } + + BOOST_FILESYSTEM_DECL + fs::detail::uintmax_pair file_size_api( const std::string & ph ) + { return file_size_template( ph ); } + + BOOST_FILESYSTEM_DECL + fs::detail::space_pair space_api( const std::string & ph ) + { return space_template( ph ); } + + BOOST_FILESYSTEM_DECL + boost::filesystem::system_error_type + get_current_path_api( std::string & ph ) + { return get_current_path_template( ph ); } + + BOOST_FILESYSTEM_DECL boost::filesystem::system_error_type + get_full_path_name_api( const std::string & ph, std::string & target ) + { return get_full_path_name_template( ph, target ); } + + BOOST_FILESYSTEM_DECL time_pair + last_write_time_api( const std::string & ph ) + { return last_write_time_template( ph ); } + + BOOST_FILESYSTEM_DECL boost::filesystem::system_error_type + last_write_time_api( const std::string & ph, std::time_t new_value ) + { return last_write_time_template( ph, new_value ); } + + BOOST_FILESYSTEM_DECL fs::detail::query_pair + create_directory_api( const std::string & ph ) + { return create_directory_template( ph ); } + + BOOST_FILESYSTEM_DECL fs::system_error_type + create_hard_link_api( const std::string & to_ph, + const std::string & from_ph ) + { return create_hard_link_template( to_ph, from_ph ); } + + BOOST_FILESYSTEM_DECL fs::system_error_type + create_symlink_api( const std::string & to_ph, + const std::string & from_ph ) + { return ERROR_NOT_SUPPORTED; } + + BOOST_FILESYSTEM_DECL boost::filesystem::system_error_type + remove_api( const std::string & ph ) { return remove_template( ph ); } + + BOOST_FILESYSTEM_DECL boost::filesystem::system_error_type + rename_api( const std::string & from, const std::string & to ) + { + return ::MoveFileA( from.c_str(), to.c_str() ) + ? 0 : ::GetLastError(); + } + + BOOST_FILESYSTEM_DECL boost::filesystem::system_error_type + copy_file_api( const std::string & from, const std::string & to ) + { + return ::CopyFileA( from.c_str(), to.c_str(), /*fail_if_exists=*/true ) + ? 0 : ::GetLastError(); + } + + BOOST_FILESYSTEM_DECL boost::filesystem::system_error_type + dir_itr_first( void *& handle, const std::string & dir, + std::string & target, status_flags & sf, status_flags & symlink_sf ) + // Note: an empty root directory has no "." or ".." entries, so this + // causes a ERROR_FILE_NOT_FOUND error which we do not considered an + // error. It is treated as eof instead. + { + // use a form of search Sebastian Martel reports will work with Win98 + std::string dirpath( dir ); + dirpath += (dirpath.empty() + || (dirpath[dirpath.size()-1] != '\\' + && dirpath[dirpath.size()-1] != ':')) ? "\\*" : "*"; + + WIN32_FIND_DATAA data; + if ( (handle = ::FindFirstFileA( dirpath.c_str(), &data )) + == INVALID_HANDLE_VALUE ) + { + handle = 0; + return ::GetLastError() == ERROR_FILE_NOT_FOUND + ? 0 : ::GetLastError(); + } + target = data.cFileName; + symlink_sf = sf = ((data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + == FILE_ATTRIBUTE_DIRECTORY) + ? fs::directory_flag + : fs::file_flag; + return 0; + } + + BOOST_FILESYSTEM_DECL boost::filesystem::system_error_type + dir_itr_close( void *& handle ) + { + if ( handle != 0 ) + { + bool ok = ::FindClose( handle ) != 0; + handle = 0; + return ok ? 0 : ::GetLastError(); + } + return 0; + } + + BOOST_FILESYSTEM_DECL boost::filesystem::system_error_type + dir_itr_increment( void *& handle, std::string & target, + status_flags & sf, status_flags & symlink_sf ) + { + WIN32_FIND_DATAA data; + if ( ::FindNextFileA( handle, &data ) == 0 ) // fails + { + int error = ::GetLastError(); + dir_itr_close( handle ); + return error == ERROR_NO_MORE_FILES ? 0 : error; + } + target = data.cFileName; + symlink_sf = sf = ((data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + == FILE_ATTRIBUTE_DIRECTORY) + ? fs::directory_flag + : fs::file_flag; + return 0; + } + +# else // BOOST_POSIX_API + + BOOST_FILESYSTEM_DECL boost::filesystem::status_flags + status_api( const std::string & ph, + boost::filesystem::system_error_type * ec ) + { + struct stat path_stat; + if ( ::stat( ph.c_str(), &path_stat ) != 0 ) + { + if ( ec != 0 ) *ec = errno; + return ( (errno == ENOENT) || (errno == ENOTDIR) ) + ? fs::not_found_flag : fs::error_flag; + } + fs::status_flags result(0); + if ( S_ISDIR( path_stat.st_mode ) ) result |= fs::directory_flag; + if ( S_ISREG( path_stat.st_mode ) ) result |= fs::file_flag; + if ( result == 0 ) result = fs::other_flag; + return result; + } + + BOOST_FILESYSTEM_DECL boost::filesystem::status_flags + symlink_status_api( const std::string & ph, + boost::filesystem::system_error_type * ec ) + { + struct stat path_stat; + if ( ::lstat( ph.c_str(), &path_stat ) != 0 ) + { + if ( ec != 0 ) *ec = errno; + return ( (errno == ENOENT) || (errno == ENOTDIR) ) + ? fs::not_found_flag : fs::error_flag; + } + fs::status_flags result(0); + if ( S_ISLNK( path_stat.st_mode ) ) result |= fs::symlink_flag; + if ( S_ISDIR( path_stat.st_mode ) ) result |= fs::directory_flag; + if ( S_ISREG( path_stat.st_mode ) ) result |= fs::file_flag; + if ( result == 0 ) result = fs::other_flag; + return result; + } + + // suggested by Walter Landry + BOOST_FILESYSTEM_DECL bool + symbolic_link_exists_api( const std::string & ph ) + { + struct stat path_stat; + return ::lstat( ph.c_str(), &path_stat ) == 0 + && S_ISLNK( path_stat.st_mode ); + } + + BOOST_FILESYSTEM_DECL query_pair + is_empty_api( const std::string & ph ) + { + struct stat path_stat; + if ( (::stat( ph.c_str(), &path_stat )) != 0 ) + return std::make_pair( errno, false ); + return std::make_pair( 0, S_ISDIR( path_stat.st_mode ) + ? is_empty_directory( ph ) + : path_stat.st_size == 0 ); + } + + BOOST_FILESYSTEM_DECL query_pair + equivalent_api( const std::string & ph1, const std::string & ph2 ) + { + struct stat s2; + int e2( ::stat( ph2.c_str(), &s2 ) ); + struct stat s1; + int e1( ::stat( ph1.c_str(), &s1 ) ); + if ( e1 != 0 || e2 != 0 ) + return std::make_pair( e1 != 0 && e2 != 0 ? errno : 0, false ); + // at this point, both stats are known to be valid + return std::make_pair( 0, + s1.st_dev == s2.st_dev + && s1.st_ino == s2.st_ino + // According to the POSIX stat specs, "The st_ino and st_dev fields + // taken together uniquely identify the file within the system." + // Just to be sure, size and mod time are also checked. + && s1.st_size == s2.st_size + && s1.st_mtime == s2.st_mtime ); + } + + BOOST_FILESYSTEM_DECL uintmax_pair + file_size_api( const std::string & ph ) + { + struct stat path_stat; + if ( ::stat( ph.c_str(), &path_stat ) != 0 ) + return std::make_pair( errno, 0 ); + if ( !S_ISREG( path_stat.st_mode ) ) + return std::make_pair( EPERM, 0 ); + return std::make_pair( 0, + static_cast(path_stat.st_size) ); + } + + BOOST_FILESYSTEM_DECL space_pair + space_api( const std::string & ph ) + { + struct statvfs vfs; + space_pair result; + if ( ::statvfs( ph.c_str(), &vfs ) != 0 ) + { + result.first = errno; + result.second.capacity = result.second.free + = result.second.available = 0; + } + else + { + result.first = 0; + result.second.capacity + = static_cast(vfs.f_blocks) * vfs.f_frsize; + result.second.free + = static_cast(vfs.f_bfree) * vfs.f_frsize; + result.second.available + = static_cast(vfs.f_bavail) * vfs.f_frsize; + } + return result; + } + + BOOST_FILESYSTEM_DECL time_pair + last_write_time_api( const std::string & ph ) + { + struct stat path_stat; + if ( ::stat( ph.c_str(), &path_stat ) != 0 ) + return std::make_pair( errno, 0 ); + return std::make_pair( 0, path_stat.st_mtime ); + } + + BOOST_FILESYSTEM_DECL fs::system_error_type + last_write_time_api( const std::string & ph, std::time_t new_value ) + { + struct stat path_stat; + if ( ::stat( ph.c_str(), &path_stat ) != 0 ) return errno; + ::utimbuf buf; + buf.actime = path_stat.st_atime; // utime() updates access time too:-( + buf.modtime = new_value; + return ::utime( ph.c_str(), &buf ) != 0 ? errno : 0; + } + + BOOST_FILESYSTEM_DECL fs::system_error_type + get_current_path_api( std::string & ph ) + { + for ( long path_max = 32;; path_max *=2 ) // loop 'til buffer large enough + { + boost::scoped_array + buf( new char[static_cast(path_max)] ); + if ( ::getcwd( buf.get(), static_cast(path_max) ) == 0 ) + { + if ( errno != ERANGE + // bug in some versions of the Metrowerks C lib on the Mac: wrong errno set +# if defined(__MSL__) && (defined(macintosh) || defined(__APPLE__) || defined(__APPLE_CC__)) + && errno != 0 +# endif + ) return errno; + } + else + { + ph = buf.get(); + break; + } + } + return 0; + } + + BOOST_FILESYSTEM_DECL fs::detail::query_pair + create_directory_api( const std::string & ph ) + { + if ( ::mkdir( ph.c_str(), S_IRWXU|S_IRWXG|S_IRWXO ) == 0 ) + { return std::make_pair( 0, true ); } + if ( errno != EEXIST + || (status_api( ph ) & fs::directory_flag) == 0 ) + { return std::make_pair( errno, false ); } + return std::make_pair( 0, false ); + } + + BOOST_FILESYSTEM_DECL boost::filesystem::system_error_type + create_hard_link_api( const std::string & to_ph, + const std::string & from_ph ) + { + return ::link( to_ph.c_str(), from_ph.c_str() ) == 0 + ? 0 : errno; + } + + BOOST_FILESYSTEM_DECL boost::filesystem::system_error_type + create_symlink_api( const std::string & to_ph, + const std::string & from_ph ) + { + return ::symlink( to_ph.c_str(), from_ph.c_str() ) == 0 + ? 0 : errno; + } + + BOOST_FILESYSTEM_DECL boost::filesystem::system_error_type + remove_api( const std::string & ph ) + { +# if defined(__QNXNTO__) || (defined(__MSL__) && (defined(macintosh) || defined(__APPLE__) || defined(__APPLE_CC__))) + // Some Metrowerks C library versions fail on directories because of a + // known Metrowerks coding error in ::remove. Workaround is to call + // rmdir() or unlink() as indicated. + // Same bug also reported for QNX, with the same fix. + if ( (is_directory( ph ) + ? ::rmdir( ph.string().c_str() ) + : ::unlink( ph.string().c_str() )) != 0 ) +# else + // note that the POSIX behavior for symbolic links is what we want; + // the link rather than what it points to is deleted + if ( std::remove( ph.c_str() ) != 0 ) +# endif + { + int error = errno; + // POSIX says "If the directory is not an empty directory, rmdir() + // shall fail and set errno to EEXIST or ENOTEMPTY." + // Linux uses ENOTEMPTY, Solaris uses EEXIST. + if ( error == EEXIST ) error = ENOTEMPTY; + return error; + } + return 0; + } + + BOOST_FILESYSTEM_DECL boost::filesystem::system_error_type + rename_api( const std::string & from, const std::string & to ) + { + // POSIX is too permissive so must check + if ( status_api( to ) != fs::not_found_flag ) + return EEXIST; + return std::rename( from.c_str(), to.c_str() ) != 0 + ? errno : 0; + } + + BOOST_FILESYSTEM_DECL boost::filesystem::system_error_type + copy_file_api( const std::string & from_file_ph, + const std::string & to_file_ph ) + { + const std::size_t buf_sz = 32768; + boost::scoped_array buf( new char [buf_sz] ); + int infile=0, outfile=0; // init quiets compiler warning + struct stat from_stat; + + if ( ::stat( from_file_ph.c_str(), &from_stat ) != 0 + || (infile = ::open( from_file_ph.c_str(), + O_RDONLY )) < 0 + || (outfile = ::open( to_file_ph.c_str(), + O_WRONLY | O_CREAT | O_EXCL, + from_stat.st_mode )) < 0 ) + { + if ( infile >= 0 ) ::close( infile ); + return errno; + } + + ssize_t sz, sz_read=1, sz_write; + while ( sz_read > 0 + && (sz_read = ::read( infile, buf.get(), buf_sz )) > 0 ) + { + // Allow for partial writes - see Advanced Unix Programming (2nd Ed.), + // Marc Rochkind, Addison-Wesley, 2004, page 94 + sz_write = 0; + do + { + if ( (sz = ::write( outfile, buf.get(), sz_read - sz_write )) < 0 ) + { + sz_read = sz; // cause read loop termination + break; // and error to be thrown after closes + } + sz_write += sz; + } while ( sz_write < sz_read ); + } + + if ( ::close( infile) < 0 ) sz_read = -1; + if ( ::close( outfile) < 0 ) sz_read = -1; + + return sz_read < 0 ? errno : 0; + } + + BOOST_FILESYSTEM_DECL boost::filesystem::system_error_type + dir_itr_first( void *& handle, const std::string & dir, + std::string & target, status_flags &, status_flags & ) + { + static const std::string dummy_first_name( "." ); + if ( (handle = ::opendir( dir.c_str() )) == 0 ) return errno; + target = dummy_first_name; + return 0; + } + + BOOST_FILESYSTEM_DECL boost::filesystem::system_error_type + dir_itr_close( void *& handle ) + { + if ( handle == 0 ) return 0; + DIR * h( static_cast(handle) ); + handle = 0; + return ::closedir( h ) == 0 ? 0 : errno; + } + + // warning: the only dirent member updated is d_name + inline int readdir_r_simulator( DIR * dirp, struct dirent * entry, + struct dirent ** result ) // *result set to 0 on end of directory + { + # if !defined(__CYGWIN__) \ + && defined(_POSIX_THREAD_SAFE_FUNCTIONS) \ + && defined(_SC_THREAD_SAFE_FUNCTIONS) \ + && (_POSIX_THREAD_SAFE_FUNCTIONS+0 >= 0) \ + && (!defined(__HP_aCC) || (defined(__HP_aCC) && defined(_REENTRANT))) + if ( ::sysconf( _SC_THREAD_SAFE_FUNCTIONS ) >= 0 ) + { return ::readdir_r( dirp, entry, result ); } + # endif + + struct dirent * p; + errno = 0; + *result = 0; + if ( (p = ::readdir( dirp )) == 0 ) + return errno; + // POSIX specs require entry->d_name be large enough: + std::strcpy( entry->d_name, p->d_name ); + *result = entry; + return 0; + } + + BOOST_FILESYSTEM_DECL boost::filesystem::system_error_type + dir_itr_increment( void *& handle, std::string & target, + status_flags & sf, status_flags & symlink_sf ) + { + struct dirent entry, * result; + int return_code; + if ( (return_code = readdir_r_simulator( static_cast(handle), + &entry, &result )) != 0 ) return errno; + if ( result == 0 ) return dir_itr_close( handle ); + target = entry.d_name; +# ifdef BOOST_FILESYSTEM_STATUS_CACHE +// std::cout << "entry.d_type is " << (int)entry.d_type << std::endl; +// std::cout << "DT_DIR is " << (int)DT_DIR << std::endl; + if ( entry.d_type == DT_DIR ) sf = symlink_sf = fs::directory_flag; + else if ( entry.d_type == DT_REG ) sf = symlink_sf = fs::file_flag; + else if ( entry.d_type == DT_LNK ) + { sf = 0; symlink_sf = fs::symlink_flag; } + else sf = symlink_sf = fs::other_flag; +# else + sf = symlink_sf = 0; +# endif + return 0; + } + +# endif + } // namespace detail + } // namespace filesystem +} // namespace boost diff --git a/src/operations_posix_windows.cpp b/src/operations_posix_windows.cpp deleted file mode 100644 index ff53af9..0000000 --- a/src/operations_posix_windows.cpp +++ /dev/null @@ -1,857 +0,0 @@ -// directory_posix_windows.cpp ---------------------------------------------// - -// Copyright © 2002 Beman Dawes -// Copyright © 2001 Dietmar Kuehl -// Use, modification, and distribution is subject to 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 - -//----------------------------------------------------------------------------// - - -// The point of this implementation is to prove the interface. There is no -// claim the implementation is efficient, follows good coding practice, etc. - - -//----------------------------------------------------------------------------// - -// define BOOST_FILESYSTEM_SOURCE so that knows -// the library is being built (possibly exporting rather than importing code) -#define BOOST_FILESYSTEM_SOURCE - -#define _FILE_OFFSET_BITS 64 // at worst, these defines may have no effect, -#define __USE_FILE_OFFSET64 // but that is harmless on Windows and on POSIX - // 64-bit systems or on 32-bit systems which don't have files larger - // than can be represented by a traditional POSIX/UNIX off_t type. - // OTOH, defining them should kick in 64-bit off_t's (and thus - // st_size) on 32-bit systems that provide the Large File - // Support (LFS) interface, such as Linux, Solaris, and IRIX. - // The defines are given before any headers are included to - // ensure that they are available to all included headers. - // That is required at least on Solaris, and possibly on other - // systems as well. - -#include -#include -#include -#include -#include -#include - -namespace fs = boost::filesystem; - -// BOOST_POSIX or BOOST_WINDOWS specify which API to use. -# if !defined( BOOST_WINDOWS ) && !defined( BOOST_POSIX ) -# if defined(_WIN32) || defined(__WIN32__) || defined(WIN32) || defined(__CYGWIN__) -# define BOOST_WINDOWS -# else -# define BOOST_POSIX -# endif -# endif - -# if defined(BOOST_WINDOWS) -# include "windows.h" -# if defined(__BORLANDC__) || defined(__MWERKS__) -# if defined(__BORLANDC__) - using std::time_t; -# endif -# include "utime.h" -# else -# include "sys/utime.h" -# endif - -// 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 // BOOST_POSIX -# include -# include "dirent.h" -# include "unistd.h" -# include "fcntl.h" -# include "utime.h" -# endif - -#include // even on Windows some functions use stat() -#include -#include -#include // for remove, rename -#include -#include -// #include // for debugging only; comment out when not in use - -#ifdef BOOST_NO_STDC_NAMESPACE -namespace std { using ::strcmp; using ::remove; using ::rename; } -#endif - -#include // must be the last header - -// helpers -----------------------------------------------------------------// - -namespace -{ -#ifdef BOOST_POSIX - -# define BOOST_HANDLE DIR * -# define BOOST_INVALID_HANDLE_VALUE 0 -# define BOOST_SYSTEM_DIRECTORY_TYPE struct dirent - - inline const char * find_first_file( const char * dir, - BOOST_HANDLE & handle, BOOST_SYSTEM_DIRECTORY_TYPE & ) - // Returns: 0 if error, otherwise name - { - const char * dummy_first_name = "."; - return ( (handle = ::opendir( dir )) - == BOOST_INVALID_HANDLE_VALUE ) ? 0 : dummy_first_name; - } - - inline void find_close( BOOST_HANDLE handle ) - { - assert( handle != BOOST_INVALID_HANDLE_VALUE ); - ::closedir( handle ); - } - - // warning: the only dirent member updated is d_name - inline int readdir_r_simulator( DIR * dirp, struct dirent * entry, - struct dirent ** result ) // *result set to 0 on end of directory - { -# if defined(_POSIX_THREAD_SAFE_FUNCTIONS) \ - && defined(_SC_THREAD_SAFE_FUNCTIONS) \ - && (_POSIX_THREAD_SAFE_FUNCTIONS+0 >= 0) \ - && ( !defined(__HP_aCC) || ( defined(__HP_aCC) && defined(_REENTRANT) ) ) - if ( ::sysconf( _SC_THREAD_SAFE_FUNCTIONS ) >= 0 ) - { return ::readdir_r( dirp, entry, result ); } -# endif - - struct dirent * p; - errno = 0; - *result = 0; - if ( (p = ::readdir( dirp )) == 0 ) - return errno; - // POSIX specs require entry->d_name be large enough: - std::strcpy( entry->d_name, p->d_name ); - *result = entry; - return 0; - } - - inline const char * find_next_file( BOOST_HANDLE handle, - const fs::path & ph, BOOST_SYSTEM_DIRECTORY_TYPE & entry ) - // Returns: if EOF 0, otherwise name - // Throws: if system reports error - { - struct dirent * result; - int return_code; - if ( (return_code = ::readdir_r_simulator( handle, &entry, &result )) != 0 ) - { - boost::throw_exception( - fs::filesystem_error( - "boost::filesystem::directory_iterator::operator++", - ph, return_code ) ); - } - return result ? entry.d_name : 0; - } -#else // BOOST_WINDOWS - -# define BOOST_HANDLE HANDLE -# define BOOST_INVALID_HANDLE_VALUE INVALID_HANDLE_VALUE -# define BOOST_SYSTEM_DIRECTORY_TYPE WIN32_FIND_DATAA - - inline const char * find_first_file( const char * dir, - BOOST_HANDLE & handle, BOOST_SYSTEM_DIRECTORY_TYPE & data ) - // Returns: 0 if error, otherwise name - // Note: an empty root directory has no "." or ".." entries, so this causes - // a ERROR_FILE_NOT_FOUND error which we do not considered an error. Instead, - // the handle is set to BOOST_INVALID_HANDLE_VALUE and a non-zero is returned. - { - // use a form of search Sebastian Martel reports will work with Win98 - std::string dirpath( dir ); - dirpath += (dirpath.empty() - || (dirpath[dirpath.size()-1] != '\\' - && dirpath[dirpath.size()-1] != '/' - && dirpath[dirpath.size()-1] != ':')) ? "\\*" : "*"; - - return ( (handle = ::FindFirstFileA( dirpath.c_str(), &data )) - == BOOST_INVALID_HANDLE_VALUE - && ::GetLastError() != ERROR_FILE_NOT_FOUND) ? 0 : data.cFileName; - } - - inline void find_close( BOOST_HANDLE handle ) - { - assert( handle != BOOST_INVALID_HANDLE_VALUE ); - ::FindClose( handle ); - } - - inline const char * find_next_file( - BOOST_HANDLE handle, const fs::path & ph, - BOOST_SYSTEM_DIRECTORY_TYPE & data ) - // Returns: 0 if EOF, otherwise name - // Throws: if system reports error - { - if ( ::FindNextFileA( handle, &data ) == 0 ) - { - if ( ::GetLastError() != ERROR_NO_MORE_FILES ) - { - boost::throw_exception( fs::filesystem_error( - "boost::filesystem::directory_iterator::operator++", - ph.branch_path(), fs::detail::system_error_code() ) ); - } - else { return 0; } // end reached - } - return data.cFileName; - } - -#endif - - - fs::directory_iterator end_itr; - - bool is_empty_directory( const fs::path & dir_path ) - { - return fs::directory_iterator(dir_path) == end_itr; - } - - unsigned long remove_all_aux( const fs::path & ph ) - { - unsigned long count = 1; - if ( !fs::symbolic_link_exists( ph ) // don't recurse symbolic links - && fs::is_directory( ph ) ) - { - for ( fs::directory_iterator itr( ph ); - itr != end_itr; ++itr ) - { - count += remove_all_aux( *itr ); - } - } - fs::remove( ph ); - return count; - } - -} // unnamed namespace - -namespace boost -{ - namespace filesystem - { - namespace detail - { - -// dir_itr_imp -------------------------------------------------------------// - - class dir_itr_imp - { - public: - path entry_path; - BOOST_HANDLE handle; - - ~dir_itr_imp() - { - if ( handle != BOOST_INVALID_HANDLE_VALUE ) find_close( handle ); - } - }; - -// dot_or_dot_dot ----------------------------------------------------------// - - inline bool dot_or_dot_dot( const char * name ) - { -# if !BOOST_WORKAROUND( __BORLANDC__, BOOST_TESTED_AT(0x0564) ) - return std::strcmp( name, "." ) == 0 - || std::strcmp( name, ".." ) == 0; -# else - // Borland workaround for failure of intrinsics to be placed in - // namespace std with certain combinations of compiler options. - // To ensure test coverage, the workaround is applied to all - // configurations, regardless of option settings. - return name[0]=='.' - && (name[1]=='\0' || (name[1]=='.' && name[2]=='\0')); -# endif - } - -// directory_iterator implementation ---------------------------------------// - - BOOST_FILESYSTEM_DECL void dir_itr_init( dir_itr_imp_ptr & m_imp, - const path & dir_path ) - { - m_imp.reset( new dir_itr_imp ); - BOOST_SYSTEM_DIRECTORY_TYPE scratch; - const char * name = 0; // initialization quiets compiler warnings - if ( dir_path.empty() ) - m_imp->handle = BOOST_INVALID_HANDLE_VALUE; - else - { - name = find_first_file( dir_path.native_directory_string().c_str(), - m_imp->handle, scratch ); // sets handle - if ( m_imp->handle == BOOST_INVALID_HANDLE_VALUE - && name ) // eof - { - m_imp.reset(); // make end iterator - return; - } - } - if ( m_imp->handle != BOOST_INVALID_HANDLE_VALUE ) - { - m_imp->entry_path = dir_path; - // append name, except ignore "." or ".." - if ( !dot_or_dot_dot( name ) ) - { - m_imp->entry_path.m_path_append( name, no_check ); - } - else - { - m_imp->entry_path.m_path_append( "dummy", no_check ); - dir_itr_increment( m_imp ); - } - } - else - { - boost::throw_exception( filesystem_error( - "boost::filesystem::directory_iterator constructor", - dir_path, fs::detail::system_error_code() ) ); - } - } - - BOOST_FILESYSTEM_DECL path & dir_itr_dereference( - const dir_itr_imp_ptr & m_imp ) - { - assert( m_imp.get() ); // fails if dereference end iterator - return m_imp->entry_path; - } - - BOOST_FILESYSTEM_DECL void dir_itr_increment( dir_itr_imp_ptr & m_imp ) - { - assert( m_imp.get() ); // fails on increment end iterator - assert( m_imp->handle != BOOST_INVALID_HANDLE_VALUE ); // reality check - - BOOST_SYSTEM_DIRECTORY_TYPE scratch; - const char * name; - - while ( (name = find_next_file( m_imp->handle, - m_imp->entry_path, scratch )) != 0 ) - { - // append name, except ignore "." or ".." - if ( !dot_or_dot_dot( name ) ) - { - m_imp->entry_path.m_replace_leaf( name ); - return; - } - } - m_imp.reset(); // make base() the end iterator - } - } // namespace detail - -// free functions ----------------------------------------------------------// - - BOOST_FILESYSTEM_DECL bool exists( const path & ph ) - { -# ifdef BOOST_POSIX - struct stat path_stat; - if(::stat( ph.string().c_str(), &path_stat ) != 0) - { - if((errno == ENOENT) || (errno == ENOTDIR)) - return false; // stat failed because the path does not exist - // for any other error we assume the file does exist and fall through, - // this may not be the best policy though... (JM 20040330) - } - return true; -# else - if(::GetFileAttributesA( ph.string().c_str() ) == 0xFFFFFFFF) - { - UINT err = ::GetLastError(); - if((err == ERROR_FILE_NOT_FOUND) - || (err == ERROR_INVALID_PARAMETER) - || (err == ERROR_NOT_READY) - || (err == ERROR_PATH_NOT_FOUND) - || (err == ERROR_INVALID_NAME) - || (err == ERROR_BAD_NETPATH )) - return false; // GetFileAttributes failed because the path does not exist - // for any other error we assume the file does exist and fall through, - // this may not be the best policy though... (JM 20040330) - return true; - } - return true; -# endif - } - - BOOST_FILESYSTEM_DECL bool possible_large_file_size_support() - { -# ifdef BOOST_POSIX - struct stat lcl_stat; - return sizeof( lcl_stat.st_size ) > 4; -# else - return true; -# endif - } - - // suggested by Walter Landry - BOOST_FILESYSTEM_DECL bool symbolic_link_exists( const path & ph ) - { -# ifdef BOOST_POSIX - struct stat path_stat; - return ::lstat( ph.native_file_string().c_str(), &path_stat ) == 0 - && S_ISLNK( path_stat.st_mode ); -# else - return false; // Windows has no O/S concept of symbolic links - // (.lnk files are an application feature, not - // a Windows operating system feature) -# endif - } - - BOOST_FILESYSTEM_DECL bool is_directory( const path & ph ) - { -# ifdef BOOST_POSIX - struct stat path_stat; - if ( ::stat( ph.native_directory_string().c_str(), &path_stat ) != 0 ) - boost::throw_exception( filesystem_error( - "boost::filesystem::is_directory", - ph, fs::detail::system_error_code() ) ); - return S_ISDIR( path_stat.st_mode ); -# else - DWORD attributes = ::GetFileAttributesA( ph.native_directory_string().c_str() ); - if ( attributes == 0xFFFFFFFF ) - boost::throw_exception( filesystem_error( - "boost::filesystem::is_directory", ph, -// fs::detail::system_error_code() - ERROR_FILE_NOT_FOUND - ) ); - return (attributes & FILE_ATTRIBUTE_DIRECTORY) != 0; -# endif - } - - BOOST_FILESYSTEM_DECL bool _is_empty( const path & ph ) - { -# ifdef BOOST_POSIX - struct stat path_stat; - if ( ::stat( ph.string().c_str(), &path_stat ) != 0 ) - boost::throw_exception( filesystem_error( - "boost::filesystem::is_empty", - ph, fs::detail::system_error_code() ) ); - - return S_ISDIR( path_stat.st_mode ) - ? is_empty_directory( ph ) - : path_stat.st_size == 0; -# else - WIN32_FILE_ATTRIBUTE_DATA fad; - if ( !::GetFileAttributesExA( ph.string().c_str(), - ::GetFileExInfoStandard, &fad ) ) - boost::throw_exception( filesystem_error( - "boost::filesystem::is_empty", - ph, fs::detail::system_error_code() ) ); - - return ( fad.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) - ? is_empty_directory( ph ) - :( !fad.nFileSizeHigh && !fad.nFileSizeLow ); -# endif - } - -# ifdef BOOST_WINDOWS - // Thanks to Jeremy Maitin-Shepard for much help and for permission to - // base the implementation on portions of his file-equivalence-win32.cpp - // experimental code. - struct handle_wrapper - { - BOOST_HANDLE handle; - handle_wrapper( BOOST_HANDLE h ) - : handle(h) {} - ~handle_wrapper() - { - if ( handle != BOOST_INVALID_HANDLE_VALUE ) - ::CloseHandle(handle); - } - }; -# endif - - BOOST_FILESYSTEM_DECL bool equivalent( const path & ph1, const path & ph2 ) - { -# ifdef BOOST_POSIX - struct stat s1; - int s1_result = ::stat( ph1.string().c_str(), &s1 ); - // save error code in case we have to throw - int error1 = (s1_result != 0 ? fs::detail::system_error_code() : 0); - struct stat s2; - int s2_result = ::stat( ph2.string().c_str(), &s2 ); - if ( s1_result != 0 - || s2_result != 0 ) - { - if ( s1_result == 0 || s2_result == 0 ) return false; - assert( s1_result != 0 && s2_result != 0 ); - boost::throw_exception( filesystem_error( - "boost::filesystem::equivalent", - ph1, error1 ) ); - } - // at this point, both stats are known to be valid - return s1.st_dev == s2.st_dev - && s1.st_ino == s2.st_ino - // According to the POSIX stat specs, "The st_ino and st_dev fields - // taken together uniquely identify the file within the system." - // Just to be sure, size and mod time are also checked. - && s1.st_size == s2.st_size - && s1.st_mtime == s2.st_mtime; -# else - // Note well: Physical location on external media is part of the - // equivalence criteria. If there are no open handles, physical location - // can change due to defragmentation or other relocations. Thus handles - // must be held open until location information for both paths has - // been retrieved. - handle_wrapper p1( - ::CreateFileA( - ph1.string().c_str(), - 0, - FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, - 0, - OPEN_EXISTING, - FILE_FLAG_BACKUP_SEMANTICS, - 0 ) ); - int error1; // save error code in case we have to throw - if ( p1.handle == BOOST_INVALID_HANDLE_VALUE ) - error1 = fs::detail::system_error_code(); - handle_wrapper p2( - ::CreateFileA( - ph2.string().c_str(), - 0, - FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, - 0, - OPEN_EXISTING, - FILE_FLAG_BACKUP_SEMANTICS, - 0 ) ); - if ( p1.handle == BOOST_INVALID_HANDLE_VALUE - || p2.handle == BOOST_INVALID_HANDLE_VALUE ) - { - if ( p1.handle != BOOST_INVALID_HANDLE_VALUE - || p2.handle != BOOST_INVALID_HANDLE_VALUE ) return false; - assert( p1.handle == BOOST_INVALID_HANDLE_VALUE - && p2.handle == BOOST_INVALID_HANDLE_VALUE ); - boost::throw_exception( filesystem_error( - "boost::filesystem::equivalent", - ph1, error1 ) ); - } - // at this point, both handles are known to be valid - BY_HANDLE_FILE_INFORMATION info1, info2; - if ( !::GetFileInformationByHandle( p1.handle, &info1 ) ) - boost::throw_exception( filesystem_error( - "boost::filesystem::equivalent", - ph1, fs::detail::system_error_code() ) ); - if ( !::GetFileInformationByHandle( p2.handle, &info2 ) ) - boost::throw_exception( filesystem_error( - "boost::filesystem::equivalent", - ph2, fs::detail::system_error_code() ) ); - // In theory, volume serial numbers are sufficient to distinguish between - // devices, but in practice VSN's are sometimes duplicated, so last write - // time and file size are also checked. - return info1.dwVolumeSerialNumber == info2.dwVolumeSerialNumber - && info1.nFileIndexHigh == info2.nFileIndexHigh - && info1.nFileIndexLow == info2.nFileIndexLow - && info1.nFileSizeHigh == info2.nFileSizeHigh - && info1.nFileSizeLow == info2.nFileSizeLow - && info1.ftLastWriteTime.dwLowDateTime - == info2.ftLastWriteTime.dwLowDateTime - && info1.ftLastWriteTime.dwHighDateTime - == info2.ftLastWriteTime.dwHighDateTime; -# endif - } - - - BOOST_FILESYSTEM_DECL boost::intmax_t file_size( const path & ph ) - { -# ifdef BOOST_POSIX - struct stat path_stat; - if ( ::stat( ph.string().c_str(), &path_stat ) != 0 ) - boost::throw_exception( filesystem_error( - "boost::filesystem::file_size", - ph, fs::detail::system_error_code() ) ); - if ( S_ISDIR( path_stat.st_mode ) ) - boost::throw_exception( filesystem_error( - "boost::filesystem::file_size", - ph, "invalid: is a directory", - is_directory_error ) ); - return static_cast(path_stat.st_size); -# else - // by now, intmax_t is 64-bits on all Windows compilers - WIN32_FILE_ATTRIBUTE_DATA fad; - if ( !::GetFileAttributesExA( ph.string().c_str(), - ::GetFileExInfoStandard, &fad ) ) - boost::throw_exception( filesystem_error( - "boost::filesystem::file_size", - ph, fs::detail::system_error_code() ) ); - if ( (fad.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) !=0 ) - boost::throw_exception( filesystem_error( - "boost::filesystem::file_size", - ph, "invalid: is a directory", - is_directory_error ) ); - return (static_cast(fad.nFileSizeHigh) - << (sizeof(fad.nFileSizeLow)*8)) - + fad.nFileSizeLow; -# endif - } - - BOOST_FILESYSTEM_DECL std::time_t last_write_time( const path & ph ) - { - // Works for both Windows and POSIX - struct stat path_stat; - if ( ::stat( ph.string().c_str(), &path_stat ) != 0 ) - boost::throw_exception( filesystem_error( - "boost::filesystem::last_write_time", - ph, fs::detail::system_error_code() ) ); - return path_stat.st_mtime; - } - - BOOST_FILESYSTEM_DECL void last_write_time( const path & ph, const std::time_t new_time ) - { - // Works for both Windows and POSIX - struct stat path_stat; - if ( ::stat( ph.string().c_str(), &path_stat ) != 0 ) - boost::throw_exception( filesystem_error( - "boost::filesystem::last_write_time", - ph, fs::detail::system_error_code() ) ); - ::utimbuf buf; - buf.actime = path_stat.st_atime; // utime() updates access time too:-( - buf.modtime = new_time; - if ( ::utime( ph.string().c_str(), &buf ) != 0 ) - boost::throw_exception( filesystem_error( - "boost::filesystem::last_write_time", - ph, fs::detail::system_error_code() ) ); - } - - BOOST_FILESYSTEM_DECL bool create_directory( const path & dir_path ) - { -# ifdef BOOST_POSIX - if ( ::mkdir( dir_path.native_directory_string().c_str(), - S_IRWXU|S_IRWXG|S_IRWXO ) == 0 ) return true; - if ( errno != EEXIST ) -# else - if ( ::CreateDirectoryA( dir_path.native_directory_string().c_str(), 0 ) ) - return true; - if ( ::GetLastError() != ERROR_ALREADY_EXISTS ) -# endif - { - boost::throw_exception( filesystem_error( - "boost::filesystem::create_directory", - dir_path, fs::detail::system_error_code() ) ); - } - if ( !is_directory( dir_path ) ) - boost::throw_exception( filesystem_error( - "boost::filesystem::create_directory", - dir_path, "path exists and is not a directory", not_directory_error ) ); - return false; - } - - BOOST_FILESYSTEM_DECL bool remove( const path & ph ) - { - if ( exists( ph ) -# ifdef BOOST_POSIX - || symbolic_link_exists( ph ) ) // handle dangling symbolic links - { -# if defined(__QNXNTO__) || (defined(__MSL__) && (defined(macintosh) || defined(__APPLE__) || defined(__APPLE_CC__))) - // Some Metrowerks C library versions fail on directories because of a - // known Metrowerks coding error in ::remove. Workaround is to call - // rmdir() or unlink() as indicated. - // Same bug reported for QNX; same fix. - if ( (is_directory( ph ) - ? ::rmdir( ph.string().c_str() ) - : ::unlink( ph.string().c_str() )) != 0 ) -# else - // note that the POSIX behavior for symbolic links is what we want; - // the link rather than what it points to is deleted - if ( std::remove( ph.string().c_str() ) != 0 ) -# endif - - { - int error = fs::detail::system_error_code(); - // POSIX says "If the directory is not an empty directory, rmdir() - // shall fail and set errno to EEXIST or ENOTEMPTY." - // Linux uses ENOTEMPTY, Solaris uses EEXIST. - if ( error == EEXIST ) error = ENOTEMPTY; - boost::throw_exception( filesystem_error( - "boost::filesystem::remove", ph, error ) ); - } -# else - ) - { - if ( is_directory( ph ) ) - { - if ( !::RemoveDirectoryA( ph.string().c_str() ) ) - boost::throw_exception( filesystem_error( - "boost::filesystem::remove", - ph, fs::detail::system_error_code() ) ); - } - else - { - if ( !::DeleteFileA( ph.string().c_str() ) ) - boost::throw_exception( filesystem_error( - "boost::filesystem::remove", - ph, fs::detail::system_error_code() ) ); - } -# endif - return true; - } - return false; - } - - BOOST_FILESYSTEM_DECL unsigned long remove_all( const path & ph ) - { - return exists( ph )|| symbolic_link_exists( ph ) - ? remove_all_aux( ph ) : 0; - } - - BOOST_FILESYSTEM_DECL void rename( const path & old_path, - const path & new_path ) - { -# ifdef BOOST_POSIX - if ( exists( new_path ) // POSIX is too permissive so must check - || std::rename( old_path.string().c_str(), new_path.string().c_str() ) != 0 ) -# else - if ( !::MoveFileA( old_path.string().c_str(), new_path.string().c_str() ) ) -# endif - boost::throw_exception( filesystem_error( - "boost::filesystem::rename", - old_path, new_path, fs::detail::system_error_code() ) ); - } - -#ifdef BOOST_POSIX - namespace detail - { - void throw_copy_file_error( const path & from_file_ph, - const path & to_file_ph ) - { - boost::throw_exception( fs::filesystem_error( - "boost::filesystem::copy_file", - from_file_ph, to_file_ph, system_error_code() ) ); - } - } -#endif - - BOOST_FILESYSTEM_DECL void copy_file( const path & from_file_ph, - const path & to_file_ph ) - { -# ifdef BOOST_POSIX - const std::size_t buf_sz = 32768; - boost::scoped_array buf( new char [buf_sz] ); - int infile=0, outfile=0; // init quiets compiler warning - struct stat from_stat; - - if ( ::stat( from_file_ph.string().c_str(), &from_stat ) != 0 - || (infile = ::open( from_file_ph.string().c_str(), - O_RDONLY )) < 0 - || (outfile = ::open( to_file_ph.string().c_str(), - O_WRONLY | O_CREAT | O_EXCL, - from_stat.st_mode )) < 0 ) - { - if ( infile >= 0 ) ::close( infile ); - detail::throw_copy_file_error( from_file_ph, to_file_ph ); - } - - ssize_t sz, sz_read=1, sz_write; - while ( sz_read > 0 - && (sz_read = ::read( infile, buf.get(), buf_sz )) > 0 ) - { - // Allow for partial writes - see Advanced Unix Programming (2nd Ed.), - // Marc Rochkind, Addison-Wesley, 2004, page 94 - sz_write = 0; - do - { - if ( (sz = ::write( outfile, buf.get(), sz_read - sz_write )) < 0 ) - { - sz_read = sz; // cause read loop termination - break; // and error to be thrown after closes - } - sz_write += sz; - } while ( sz_write < sz_read ); - } - - if ( ::close( infile) < 0 ) sz_read = -1; - if ( ::close( outfile) < 0 ) sz_read = -1; - - if ( sz_read < 0 ) - detail::throw_copy_file_error( from_file_ph, to_file_ph ); -# else - if ( !::CopyFileA( from_file_ph.string().c_str(), - to_file_ph.string().c_str(), /*fail_if_exists=*/true ) ) - boost::throw_exception( fs::filesystem_error( - "boost::filesystem::copy_file", - from_file_ph, to_file_ph, detail::system_error_code() ) ); -# endif - } - - BOOST_FILESYSTEM_DECL path current_path() - { -# ifdef BOOST_POSIX - for ( long path_max = 32;; path_max *=2 ) // loop 'til buffer large enough - { - boost::scoped_array - buf( new char[static_cast(path_max)] ); - if ( ::getcwd( buf.get(), static_cast(path_max) ) == 0 ) - { - if ( errno != ERANGE -// there is a bug in some versions of the Metrowerks C lib on the Mac: wrong errno set -#if defined(__MSL__) && (defined(macintosh) || defined(__APPLE__) || defined(__APPLE_CC__)) - && errno != 0 -#endif - ) - boost::throw_exception( - filesystem_error( "boost::filesystem::current_path", path(), - fs::detail::system_error_code() ) ); - } - else return path( buf.get(), native ); - } - BOOST_UNREACHABLE_RETURN(0) -# else - DWORD sz; - if ( (sz = ::GetCurrentDirectoryA( 0, static_cast(0) )) == 0 ) - boost::throw_exception( - filesystem_error( "boost::filesystem::current_path", - "size is 0" ) ); - boost::scoped_array buf( new char[sz] ); - if ( ::GetCurrentDirectoryA( sz, buf.get() ) == 0 ) - boost::throw_exception( - filesystem_error( "boost::filesystem::current_path", path(), - fs::detail::system_error_code() ) ); - return path( buf.get(), native ); -# endif - } - - BOOST_FILESYSTEM_DECL const path & initial_path() - { - static path init_path; - if ( init_path.empty() ) init_path = current_path(); - return init_path; - } - - BOOST_FILESYSTEM_DECL path system_complete( const path & ph ) - { -# ifdef BOOST_WINDOWS - if ( ph.empty() ) return ph; - char buf[MAX_PATH]; - char * pfn; - std::size_t len = ::GetFullPathNameA( ph.string().c_str(), - sizeof(buf) , buf, &pfn ); - if ( !len ) - { boost::throw_exception( - filesystem_error( "boost::filesystem::system_complete", - ph, "size is 0" ) ); } - buf[len] = '\0'; - return path( buf, native ); -# else - return (ph.empty() || ph.is_complete()) - ? ph : current_path() / ph; -# endif - } - - BOOST_FILESYSTEM_DECL path complete( const path & ph, const path & base ) - { - assert( base.is_complete() - && (ph.is_complete() || !ph.has_root_name()) ); // precondition -# ifdef BOOST_WINDOWS - if (ph.empty() || ph.is_complete()) return ph; - if ( !ph.has_root_name() ) - return ph.has_root_directory() - ? path( base.root_name(), native ) / ph - : base / ph; - return base / ph; -# else - return (ph.empty() || ph.is_complete()) ? ph : base / ph; -# endif - } - } // namespace filesystem -} // namespace boost - diff --git a/src/path.cpp b/src/path.cpp new file mode 100644 index 0000000..60eb71a --- /dev/null +++ b/src/path.cpp @@ -0,0 +1,106 @@ +// path.cpp ----------------------------------------------------------------// + +// Copyright © 2005 Beman Dawes +// Use, modification, and distribution is subject to 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 + +//----------------------------------------------------------------------------// + +// define BOOST_FILESYSTEM_SOURCE so that knows +// the library is being built (possibly exporting rather than importing code) +#define BOOST_FILESYSTEM_SOURCE + +#include + +#ifndef BOOST_FILESYSTEM_NARROW_ONLY + +#include +#include + +#include +#include + +namespace +{ + // ISO C calls this "the locale-specific native environment": + std::locale loc(""); + + const std::codecvt * + converter( + &std::use_facet > + ( loc ) ); + bool locked(false); +} // unnamed namespace + +namespace boost +{ + namespace filesystem + { + bool wpath_traits::imbue( const std::locale & new_loc, const std::nothrow_t & ) + { + if ( locked ) return false; + locked = true; + loc = new_loc; + converter = &std::use_facet + >( loc ); + return true; + } + + void wpath_traits::imbue( const std::locale & new_loc ) + { + if ( locked ) boost::throw_exception( filesystem_wpath_error( + "boost::filesystem::wpath_traits::imbue() after lockdown", 0 ) ); + imbue( new_loc, std::nothrow ); + } + +# ifdef BOOST_POSIX_API + +// Because this is POSIX only code, we don't have to worry about ABI issues +// described in http://www.boost.org/more/separate_compilation.html + + wpath_traits::external_string_type + wpath_traits::to_external( const wpath & ph, + const internal_string_type & src ) + { + locked = true; + std::size_t work_size( converter->max_length() * (src.size()+1) ); + boost::scoped_array work( new char[ work_size ] ); + std::mbstate_t state; + const internal_string_type::value_type * from_next; + external_string_type::value_type * to_next; + if ( converter->out( + state, src.c_str(), src.c_str()+src.size(), from_next, work.get(), + work.get()+work_size, to_next ) != std::codecvt_base::ok ) + boost::throw_exception( boost::filesystem::filesystem_wpath_error( + "boost::filesystem::wpath::to_external conversion error", + ph, EINVAL ) ); + *to_next = '\0'; + return external_string_type( work.get() ); + } + + wpath_traits::internal_string_type + wpath_traits::to_internal( const external_string_type & src ) + { + locked = true; + std::size_t work_size( src.size()+1 ); + boost::scoped_array work( new wchar_t[ work_size ] ); + std::mbstate_t state; + const external_string_type::value_type * from_next; + internal_string_type::value_type * to_next; + if ( converter->in( + state, src.c_str(), src.c_str()+src.size(), from_next, work.get(), + work.get()+work_size, to_next ) != std::codecvt_base::ok ) + boost::throw_exception( boost::filesystem::filesystem_wpath_error( + "boost::filesystem::wpath::to_internal conversion error", EINVAL ) ); + *to_next = L'\0'; + return internal_string_type( work.get() ); + } +# endif // BOOST_POSIX_API + + } // namespace filesystem +} // namespace boost + +#endif // ifndef BOOST_FILESYSTEM_NARROW_ONLY diff --git a/src/path_posix_windows.cpp b/src/path_posix_windows.cpp deleted file mode 100644 index caeb9e4..0000000 --- a/src/path_posix_windows.cpp +++ /dev/null @@ -1,692 +0,0 @@ -// path implementation -----------------------------------------------------// - -// © Copyright Beman Dawes 2002-2003 -// Use, modification, and distribution is subject to 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 - - -//****************************************************************************// - -// WARNING: This code was hacked time and time again as different library -// designs were tried. Thus portions may be residue from the earlier -// experiments, and be totally stupid or wrong in light of the final library -// specifications. The code needs to be reviewed line-by-line to elmininate -// such problems. - -//****************************************************************************// - -// define BOOST_FILESYSTEM_SOURCE so that knows -// the library is being built (possibly exporting rather than importing code) -#define BOOST_FILESYSTEM_SOURCE - -#include -#include - -// BOOST_POSIX or BOOST_WINDOWS specify which API to use. -# if !defined( BOOST_WINDOWS ) && !defined( BOOST_POSIX ) -# if defined(_WIN32) || defined(__WIN32__) || defined(WIN32) || defined(__CYGWIN__) -# define BOOST_WINDOWS -# else -# define BOOST_POSIX -# endif -# endif - - -namespace fs = boost::filesystem; - -#include -#ifdef BOOST_NO_STDC_NAMESPACE - namespace std { using ::strlen; } -#endif - -#include -#include // SGI MIPSpro compilers need this -#include -#include // for lexicographical_compare -#include - -#include // must be the last header - -// helpers -----------------------------------------------------------------// - -namespace -{ - // POSIX & Windows cases: "", "/", "/foo", "foo", "foo/bar" - // Windows only cases: "c:", "c:/", "c:foo", "c:/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 && 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-2 ); -# endif - - return ( pos == std::string::npos // path itself must be a leaf (or empty) -# ifdef BOOST_WINDOWS - || (pos == 1 && str[0] == '/') // or share -# endif - ) ? 0 // so leaf is entire string - : pos + 1; // or starts after delimiter - } - - void first_name( const std::string & src, std::string & target ) - { - target = ""; // VC++ 6.0 doesn't have string::clear() - std::string::const_iterator itr( src.begin() ); - -# ifdef BOOST_WINDOWS - // deal with //share - if ( src.size() >= 2 && src[0] == '/' && src[1] == '/' ) - { target = "//"; itr += 2; } -# endif - - while ( itr != src.end() -# ifdef BOOST_WINDOWS - && *itr != ':' -# endif - && *itr != '/' ) { target += *itr++; } - - if ( itr == src.end() ) return; - -# ifdef BOOST_WINDOWS - if ( *itr == ':' ) - { - target += *itr++; - return; - } -# endif - - // *itr is '/' - if ( itr == src.begin() ) { target += '/'; } - return; - } - - const char invalid_chars[] = - "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F" - "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F" - "<>:\"/\\|"; - // note that the terminating '\0' is part of the string - thus the size below - // is sizeof(invalid_chars) rather than sizeof(invalid_chars)-1. I - const std::string windows_invalid_chars( invalid_chars, sizeof(invalid_chars) ); - - const std::string valid_posix( - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789._-" ); - - fs::path::name_check default_check = fs::portable_name; - bool safe_to_write_check = true; // write-once-before-read allowed - -} // unnamed namespace - -//----------------------------------------------------------------------------// - -namespace boost -{ - namespace filesystem - { - // name_check functions ----------------------------------------------// - -# ifdef BOOST_WINDOWS - BOOST_FILESYSTEM_DECL bool native( const std::string & name ) - { - return windows_name( name ); - } -# else - BOOST_FILESYSTEM_DECL bool native( const std::string & name ) - { - return name.find('/') == std::string::npos; - } -# endif - - BOOST_FILESYSTEM_DECL bool no_check( const std::string & ) { return true; } - - BOOST_FILESYSTEM_DECL bool portable_posix_name( const std::string & name ) - { - return name.size() != 0 - && name.find_first_not_of( valid_posix ) == std::string::npos; - } - - BOOST_FILESYSTEM_DECL bool windows_name( const std::string & name ) - { - return name.size() != 0 - && name.find_first_of( windows_invalid_chars ) == std::string::npos - && *(name.end()-1) != ' ' - && (*(name.end()-1) != '.' - || name.length() == 1 || name == ".."); - } - - BOOST_FILESYSTEM_DECL bool portable_name( const std::string & name ) - { - return - name.size() == 0 - || name == "." - || name == ".." - || (windows_name( name ) - && portable_posix_name( name ) - && name[0] != '.' && name[0] != '-'); - } - - BOOST_FILESYSTEM_DECL bool portable_directory_name( const std::string & name ) - { - return - name == "." - || name == ".." - || (portable_name( name ) - && name.find('.') == std::string::npos); - } - - BOOST_FILESYSTEM_DECL bool portable_file_name( const std::string & name ) - { - std::string::size_type pos; - return - name == "." - || name == ".." - || (portable_name( name ) - && ( (pos = name.find( '.' )) == std::string::npos - || (name.find( '.', pos+1 )== std::string::npos - && (pos + 5) > name.length() ))) - ; - } - - -// path implementation -----------------------------------------------------// - - path::path( const std::string & src ) - { - m_path_append( src, default_name_check() ); - } - - path::path( const char * src ) - { - assert( src != 0 ); - m_path_append( src, default_name_check() ); - } - - path::path( const std::string & src, name_check checker ) - { - m_path_append( src, checker ); - } - - path::path( const char * src, name_check checker ) - { - assert( src != 0 ); - m_path_append( src, checker ); - } - - path & path::operator /=( const path & rhs ) - { - m_path_append( rhs.m_path, no_check ); - return *this; - } - - void path::m_path_append( const std::string & src, name_check checker ) - { - // convert backslash to forward slash if checker==native - // allow system-specific-root if checker==no_check || checker==native - - assert( checker ); - assert( src.size() == std::strlen( src.c_str() ) ); // no embedded 0 - - if ( src.size() == 0 ) return; - - std::string::const_iterator itr( src.begin() ); - - // [root-filesystem] -# ifdef BOOST_WINDOWS - if ( (checker == no_check || checker == native) && src.size() >= 2 ) - { - // drive or device - if ( src[1] == ':' || src[src.size()-1] == ':' ) - { - for ( ; *itr != ':'; ++itr ) m_path += *itr; - m_path += ':'; - ++itr; - } - - // share - else if ( (*itr == '/' || (*itr == '\\' && checker == native)) - && (*(itr+1) == '/' || (*(itr+1) == '\\' && checker == native)) ) - { - 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 == '\\' && checker == native) -# endif - ) ) - { - ++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 += '/'; - } - - // element { "/" element } [ "/" ] - while ( itr != src.end() ) - { - if ( m_path == "." ) m_path = ""; - - // directory-placeholder - if ( *itr == '.' && ((itr+1) == src.end() || *(itr+1) == '/' -# ifdef BOOST_WINDOWS - || *(itr+1) == '\\' -# endif - ) ) - { - if ( empty() ) m_path += '.'; - ++itr; - } - - // parent-directory or name - else - { - // append '/' if needed - if ( !empty() -# ifdef BOOST_WINDOWS - && *(m_path.end()-1) != ':' -# endif - && *(m_path.end()-1) != '/' ) - m_path += '/'; - - // parent-directory - if ( *itr == '.' - && (itr+1) != src.end() && *(itr+1) == '.' - && ((itr+2) == src.end() || *(itr+2) == '/' -# ifdef BOOST_WINDOWS - || *(itr+2) == '\\' -# endif - ) ) - { - m_path += ".."; - ++itr; - ++itr; - } // parent-directory - - // name - else - { - std::string name; - do - { name += *itr; } - while ( ++itr != src.end() && *itr != '/' -# ifdef BOOST_WINDOWS - && (*itr != '\\' || checker != native) -# endif - ); - - if ( !checker( name ) ) - { - boost::throw_exception( filesystem_error( - "boost::filesystem::path", - "invalid name \"" + name + "\" in path: \"" + src + "\"" ) ); - } - - m_path += name; - } - } // parent-directory or name - - // end or "/" - if ( itr != src.end() ) - { - if ( *itr == '/' -# ifdef BOOST_WINDOWS - || (*itr == '\\' && checker == native) -# endif - ) ++itr; - else - boost::throw_exception( filesystem_error( - "boost::filesystem::path", - "invalid path syntax: \"" + src + "\"" ) ); - } - - } // while more elements - - // special case: remove one or more leading "/.." - - std::string::size_type pos = 0, sz = m_path.size(); - -# ifdef BOOST_WINDOWS - if ( sz > 2 && m_path[pos] != '/' && m_path[pos+1] == ':' ) // drive - { pos += 2; sz -= 2; } -# endif - - while ( sz >= 3 && m_path[pos] == '/' - && m_path[pos+1] == '.' && m_path[pos+2] == '.' - && (sz == 3 || m_path[pos+3] == '/') ) - { - m_path.erase( pos+1, 3 ); // "..[/]" - sz -= 3; // on last, 3 should be 2; that doesn't matter - } - } - -// path conversion functions ------------------------------------------------// - - std::string path::native_file_string() const - { -# ifdef BOOST_WINDOWS - std::string s( m_path ); - for ( std::string::iterator itr( s.begin() ); - itr != s.end(); ++itr ) - if ( *itr == '/' ) *itr = '\\'; - return s; -# else - return m_path; -# endif - } - - std::string path::native_directory_string() const - { return native_file_string(); } - -// path modification functions -----------------------------------------------// - - path & path::normalize() - { - if ( m_path.empty() ) return *this; - std::string::size_type end, beg(0), start(0); - -# ifdef BOOST_WINDOWS - if ( m_path.size() > 2 - && m_path[0] != '/' && m_path[1] == ':' ) start = 2; // drive -# endif - - while ( (beg=m_path.find( "/..", beg )) != std::string::npos ) - { - end = beg + 3; - if ( (beg == 1 && m_path[0] == '.') - || (beg == 2 && m_path[0] == '.' && m_path[1] == '.') - || (beg > 2 && m_path[beg-3] == '/' - && m_path[beg-2] == '.' && m_path[beg-1] == '.') ) - { - beg = end; - continue; - } - if ( end < m_path.size() ) - { - if ( m_path[end] == '/' ) ++end; - else { beg = end; continue; } // name starts with .. - } - - // end is one past end of substr to be erased; now set beg - while ( beg > start && m_path[--beg] != '/' ) {} - if ( m_path[beg] == '/') ++beg; - m_path.erase( beg, end-beg ); - if ( beg ) --beg; - } - - if ( m_path.empty() ) m_path = "."; - else - { // remove trailing '/' if not root directory - std::string::size_type sz = m_path.size(); - -# ifdef BOOST_WINDOWS - if ( start ) sz -= 2; // drive -# endif - - if ( sz > 1 && m_path[m_path.size()-1] == '/' ) - { m_path.erase( m_path.size()-1 ); } - } - return *this; - } - - // path decomposition functions ---------------------------------------------// - - path::iterator path::begin() const - { - iterator itr; - itr.m_path_ptr = this; - first_name( m_path, itr.m_name ); - itr.m_pos = 0; - return itr; - } - - void path::m_replace_leaf( const char * new_leaf ) - { - m_path.erase( leaf_pos( m_path, m_path.size() ) ); - m_path += new_leaf; - } - - std::string path::leaf() const - { - return m_path.substr( leaf_pos( m_path, m_path.size() ) ); - } - - namespace detail - { - 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 - ); - } - } - - path path::branch_path() const - { - std::string::size_type end_pos( leaf_pos( m_path, m_path.size() ) ); - - // 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 ), no_check ); - } - - 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 ), no_check ); - } - - std::string path::root_name() 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 - root_name(), no_check ) /= root_directory(); -# else - root_directory() ); -# endif - } - -// path query functions -----------------------------------------------------// - - bool path::is_complete() const - { -# ifdef BOOST_WINDOWS - return m_path.size() > 2 - && ( (m_path[1] == ':' && m_path[2] == '/') // "c:/" - || (m_path[0] == '/' && m_path[1] == '/') // "//share" - || m_path[m_path.size()-1] == ':' ); -# else - return m_path.size() && m_path[0] == '/'; -# endif - } - - bool path::has_root_path() const - { - return ( m_path.size() - && m_path[0] == '/' ) // covers both "/" and "//share" -# ifdef BOOST_WINDOWS - || ( m_path.size() > 1 && m_path[1] == ':' ) // "c:" and "c:/" - || ( m_path.size() > 3 - && m_path[m_path.size()-1] == ':' ) // "device:" -# endif - ; - } - - bool path::has_root_name() const - { -# ifdef BOOST_WINDOWS - return m_path.size() > 1 - && ( m_path[1] == ':' // "c:" - || m_path[m_path.size()-1] == ':' // "prn:" - || (m_path[0] == '/' && m_path[1] == '/') // "//share" - ); -# else - return false; -# endif - } - - bool path::has_root_directory() 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:/" -# endif - ; - } - - bool path::has_relative_path() const { return !relative_path().empty(); } - bool path::has_branch_path() const { return !branch_path().empty(); } - - // default name_check mechanism ----------------------------------------// - - bool path::default_name_check_writable() - { - return safe_to_write_check; - } - - void path::default_name_check( name_check new_check ) - { - assert( new_check ); - if ( !safe_to_write_check ) - boost::throw_exception( - filesystem_error( "boost::filesystem::default_name_check", - "default name check already set" )); - default_check = new_check; - safe_to_write_check = false; - } - - path::name_check path::default_name_check() - { - safe_to_write_check = false; - return default_check; - } - - // path operator< ------------------------------------------------------// - bool path::operator<( const path & that ) const - { - return std::lexicographical_compare( - begin(), end(), that.begin(), that.end() ); - } - - -// path::iterator implementation --------------------------------------------// - - BOOST_FILESYSTEM_DECL void path::iterator::increment() - { - assert( m_pos < m_path_ptr->m_path.size() ); // detect increment past end - m_pos += m_name.size(); - if ( m_pos == m_path_ptr->m_path.size() ) - { - m_name = ""; // not strictly required, but might aid debugging - return; - } - if ( m_path_ptr->m_path[m_pos] == '/' ) - { -# ifdef BOOST_WINDOWS - if ( m_name[m_name.size()-1] == ':' // drive or device - || (m_name[0] == '/' && m_name[1] == '/') ) // share - { - m_name = "/"; - return; - } -# endif - ++m_pos; - } - std::string::size_type end_pos( m_path_ptr->m_path.find( '/', m_pos ) ); - if ( end_pos == std::string::npos ) - end_pos = m_path_ptr->m_path.size(); - m_name = m_path_ptr->m_path.substr( m_pos, end_pos - m_pos ); - } - - BOOST_FILESYSTEM_DECL void path::iterator::decrement() - { - assert( m_pos ); // detect decrement of begin - std::string::size_type end_pos( m_pos ); - - // skip a '/' unless it is a root directory - if ( m_path_ptr->m_path[end_pos-1] == '/' - && !detail::is_absolute_root( m_path_ptr->m_path, end_pos ) ) --end_pos; - m_pos = leaf_pos( m_path_ptr->m_path, end_pos ); - m_name = m_path_ptr->m_path.substr( m_pos, end_pos - m_pos ); - } - } // namespace filesystem -} // namespace boost diff --git a/src/portability.cpp b/src/portability.cpp new file mode 100644 index 0000000..78dd810 --- /dev/null +++ b/src/portability.cpp @@ -0,0 +1,112 @@ +// portability.cpp ---------------------------------------------------------// + +// Copyright © 2002-2005 Beman Dawes +// Use, modification, and distribution is subject to 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 + +//----------------------------------------------------------------------------// + +// define BOOST_FILESYSTEM_SOURCE so that knows +// the library is being built (possibly exporting rather than importing code) +#define BOOST_FILESYSTEM_SOURCE + +#include +#include + +namespace fs = boost::filesystem; + +#include // SGI MIPSpro compilers need this + +# ifdef BOOST_NO_STDC_NAMESPACE + namespace std { using ::strerror; } +# endif + +//----------------------------------------------------------------------------// + +namespace +{ + const char invalid_chars[] = + "\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0A\x0B\x0C\x0D\x0E\x0F" + "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1A\x1B\x1C\x1D\x1E\x1F" + "<>:\"/\\|"; + // note that the terminating '\0' is part of the string - thus the size below + // is sizeof(invalid_chars) rather than sizeof(invalid_chars)-1. I + const std::string windows_invalid_chars( invalid_chars, sizeof(invalid_chars) ); + + const std::string valid_posix( + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789._-" ); + +} // unnamed namespace + +namespace boost +{ + namespace filesystem + { + + // name_check functions ----------------------------------------------// + +# ifdef BOOST_WINDOWS + BOOST_FILESYSTEM_DECL bool native( const std::string & name ) + { + return windows_name( name ); + } +# else + BOOST_FILESYSTEM_DECL bool native( const std::string & name ) + { + return name.find('/') == std::string::npos; + } +# endif + + BOOST_FILESYSTEM_DECL bool portable_posix_name( const std::string & name ) + { + return name.size() != 0 + && name.find_first_not_of( valid_posix ) == std::string::npos; + } + + BOOST_FILESYSTEM_DECL bool windows_name( const std::string & name ) + { + return name.size() != 0 + && name.find_first_of( windows_invalid_chars ) == std::string::npos + && *(name.end()-1) != ' ' + && (*(name.end()-1) != '.' + || name.length() == 1 || name == ".."); + } + + BOOST_FILESYSTEM_DECL bool portable_name( const std::string & name ) + { + return + name.size() == 0 + || name == "." + || name == ".." + || (windows_name( name ) + && portable_posix_name( name ) + && name[0] != '.' && name[0] != '-'); + } + + BOOST_FILESYSTEM_DECL bool portable_directory_name( const std::string & name ) + { + return + name == "." + || name == ".." + || (portable_name( name ) + && name.find('.') == std::string::npos); + } + + BOOST_FILESYSTEM_DECL bool portable_file_name( const std::string & name ) + { + std::string::size_type pos; + return + name == "." + || name == ".." + || (portable_name( name ) + && ( (pos = name.find( '.' )) == std::string::npos + || (name.find( '.', pos+1 )== std::string::npos + && (pos + 5) > name.length() ))) + ; + } + + } // namespace filesystem +} // namespace boost diff --git a/src/utf8_codecvt_facet.cpp b/src/utf8_codecvt_facet.cpp new file mode 100644 index 0000000..3fe3e95 --- /dev/null +++ b/src/utf8_codecvt_facet.cpp @@ -0,0 +1,20 @@ +// Copyright Vladimir Prus 2004. +// 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) + +#define BOOST_FILESYSTEM_SOURCE +#include + +#define BOOST_UTF8_BEGIN_NAMESPACE \ + namespace boost { namespace filesystem { namespace detail { + +#define BOOST_UTF8_END_NAMESPACE }}} +#define BOOST_UTF8_DECL BOOST_FILESYSTEM_DECL + +#include "libs/detail/utf8_codecvt_facet.cpp" + + +#undef BOOST_UTF8_BEGIN_NAMESPACE +#undef BOOST_UTF8_END_NAMESPACE +#undef BOOST_UTF8_DECL diff --git a/src/utf8_codecvt_facet.hpp b/src/utf8_codecvt_facet.hpp new file mode 100644 index 0000000..1df876a --- /dev/null +++ b/src/utf8_codecvt_facet.hpp @@ -0,0 +1,25 @@ +// Copyright © 2001 Ronald Garcia, Indiana University (garcia@osl.iu.edu) +// Andrew Lumsdaine, Indiana University (lums@osl.iu.edu). Permission to copy, +// use, modify, sell and distribute this software is granted provided this +// copyright notice appears 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. + +#ifndef BOOST_FILESYSTEM_UTF8_CODECVT_FACET_HPP +#define BOOST_FILESYSTEM_UTF8_CODECVT_FACET_HPP + +#include + +#define BOOST_UTF8_BEGIN_NAMESPACE \ + namespace boost { namespace filesystem { namespace detail { + +#define BOOST_UTF8_END_NAMESPACE }}} +#define BOOST_UTF8_DECL BOOST_FILESYSTEM_DECL + +#include + +#undef BOOST_UTF8_BEGIN_NAMESPACE +#undef BOOST_UTF8_END_NAMESPACE +#undef BOOST_UTF8_DECL + +#endif diff --git a/test/Jamfile b/test/Jamfile index 9a4d887..0e320bd 100644 --- a/test/Jamfile +++ b/test/Jamfile @@ -30,10 +30,6 @@ DEPENDS all : test ; : : : BOOST_ALL_NO_LIB BOOST_FILESYSTEM_DYN_LINK dynamic : path_test_dll ] - [ run libs/filesystem/test/default_name_check_test.cpp - ../build/boost_filesystem - : : : BOOST_ALL_NO_LIB BOOST_FILESYSTEM_STATIC_LINK - ] [ run libs/filesystem/test/operations_test.cpp ../build/boost_filesystem : : : BOOST_ALL_NO_LIB BOOST_FILESYSTEM_STATIC_LINK @@ -46,7 +42,6 @@ DEPENDS all : test ; [ run libs/filesystem/test/fstream_test.cpp ../build/boost_filesystem : : : BOOST_ALL_NO_LIB BOOST_FILESYSTEM_STATIC_LINK - ] [ run libs/filesystem/test/convenience_test.cpp ../build/boost_filesystem @@ -56,5 +51,12 @@ DEPENDS all : test ; ../build/boost_filesystem : : : BOOST_ALL_NO_LIB BOOST_FILESYSTEM_STATIC_LINK ] + [ run libs/filesystem/test/wide_test.cpp + ../build/boost_filesystem + : : : BOOST_ALL_NO_LIB BOOST_FILESYSTEM_STATIC_LINK + ] + [ compile libs/filesystem/example/mbcopy.cpp ] + [ compile libs/filesystem/example/mbpath.cpp ] + [ compile libs/filesystem/example/simple_ls.cpp ] ; } diff --git a/test/convenience_test.cpp b/test/convenience_test.cpp index 498bf01..4193cd9 100644 --- a/test/convenience_test.cpp +++ b/test/convenience_test.cpp @@ -8,8 +8,14 @@ // See library home page at http://www.boost.org/libs/filesystem +// VC++ 8.0 warns on various less-than-safe practices. +// See http://msdn.microsoft.com/msdnmag/issues/05/05/SafeCandC/default.aspx +// But at least in VC++ 8.0 betas, their own libraries use the problem +// practices. So turn off the warnings. +#define _CRT_SECURE_NO_DEPRECATE +#define _SCL_SECURE_NO_DEPRECATE + #include -#include namespace fs = boost::filesystem; using fs::path; @@ -18,21 +24,31 @@ using fs::path; #include #include +#ifndef BOOST_FILESYSTEM_NARROW_ONLY +# define BOOST_FS_IS_EMPTY fs::is_empty +# define BOOST_BND(BOOST_FUNC_TO_DO) BOOST_FUNC_TO_DO +#else +# define BOOST_FS_IS_EMPTY fs::_is_empty +# define BOOST_BND(BOOST_FUNC_TO_DO) BOOST_FUNC_TO_DO +#endif + namespace { template< typename F > - bool throws_fs_error( F func, fs::error_code ec = - ::boost::filesystem::no_error ) // VC++ 7.1 build 2292 won't accept fs:: + bool throws_fs_error( F func, fs::errno_type ec = 0 ) { try { func(); } catch ( const fs::filesystem_error & ex ) { - if ( ec == fs::no_error || ec == ex.error() ) return true; - std::cout << "filesystem_error::error() reports " << ex.error() + if ( ec == 0 + || ec == fs::lookup_error_code(ex.system_error()) ) return true; + std::cout + << "exception reports " << fs::lookup_error_code(ex.system_error()) << ", should be " << ec - << "\n native_error() is " << ex.native_error() + << "\n system_error() is " << ex.system_error() << std::endl; + return false; } return false; } @@ -42,7 +58,7 @@ int test_main( int, char*[] ) path::default_name_check( fs::no_check ); // names below not valid on all O/S's // but they must be tested anyhow - // create_directories() tests ----------------------------------------------// +// create_directories() tests ----------------------------------------------// BOOST_CHECK( !fs::create_directories( "" ) ); // should be harmless BOOST_CHECK( !fs::create_directories( "/" ) ); // ditto @@ -67,12 +83,12 @@ int test_main( int, char*[] ) std::ofstream f( is_a_file.native_file_string().c_str() ); BOOST_CHECK( !!f ); } - BOOST_CHECK( - throws_fs_error( boost::bind( fs::create_directories, is_a_file ) ) ); - BOOST_CHECK( - throws_fs_error( boost::bind( fs::create_directories, is_a_file / "aa" ) ) ); + BOOST_CHECK( throws_fs_error( + boost::bind( BOOST_BND(fs::create_directories), is_a_file ) ) ); + BOOST_CHECK( throws_fs_error( + boost::bind( BOOST_BND(fs::create_directories), is_a_file / "aa" ) ) ); -// extension() tests ----------------------------------------------------------// +// extension() tests ---------------------------------------------------------// BOOST_CHECK( fs::extension("a/b") == "" ); BOOST_CHECK( fs::extension("a/b.txt") == ".txt" ); @@ -80,7 +96,7 @@ int test_main( int, char*[] ) BOOST_CHECK( fs::extension("a.b.c") == ".c" ); BOOST_CHECK( fs::extension("a.b.c.") == "." ); BOOST_CHECK( fs::extension("") == "" ); - BOOST_CHECK( fs::extension("a/") == "" ); + BOOST_CHECK( fs::extension("a/") == "." ); // basename() tests ----------------------------------------------------------// @@ -100,5 +116,16 @@ int test_main( int, char*[] ) // see the rationale in html docs for explanation why this works BOOST_CHECK( fs::change_extension("", ".png").string() == ".png" ); +// what() tests --------------------------------------------------------------// + + try { throw fs::filesystem_path_error( "abc", "p1", "p2", 0 ); } + catch ( const fs::filesystem_path_error & ex ) + { +# if !defined( BOOST_MSVC ) || BOOST_MSVC >= 1300 // > VC++ 7.0 + BOOST_CHECK( fs::what(ex) == std::string( "abc: \"p1\", \"p2\"" ) ); +# endif + } + + return 0; } diff --git a/test/default_name_check_test.cpp b/test/default_name_check_test.cpp deleted file mode 100644 index 3ea47b8..0000000 --- a/test/default_name_check_test.cpp +++ /dev/null @@ -1,26 +0,0 @@ -// default_name_check_test program ------------------------------------------// - -// Copyright Beman Dawes 2003. -// Use, modification, and distribution is subject to 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 - -#include -#include - -namespace fs = boost::filesystem; -using boost::filesystem::path; - -#include - -int test_main( int, char*[] ) -{ - BOOST_CHECK( path::default_name_check_writable() ); - path::default_name_check( fs::no_check ); - BOOST_CHECK( !path::default_name_check_writable() ); - BOOST_CHECK( path::default_name_check() == fs::no_check ); - return 0; -} - diff --git a/test/fstream_test.cpp b/test/fstream_test.cpp index 0f3a518..a568574 100644 --- a/test/fstream_test.cpp +++ b/test/fstream_test.cpp @@ -7,10 +7,25 @@ // See library home page at http://www.boost.org/libs/filesystem +// VC++ 8.0 warns on various less-than-safe practices. +// See http://msdn.microsoft.com/msdnmag/issues/05/05/SafeCandC/default.aspx +// But at least in VC++ 8.0 betas, their own libraries use the problem +// practices. So turn off the warnings. +#define _CRT_SECURE_NO_DEPRECATE +#define _SCL_SECURE_NO_DEPRECATE + #include +#include #include +#include #include // for std::remove +#include "../src/utf8_codecvt_facet.hpp" + +#ifndef BOOST_FILESYSTEM_NARROW_ONLY +# include "lpath.hpp" +#endif + namespace fs = boost::filesystem; #include @@ -20,113 +35,137 @@ namespace fs = boost::filesystem; #include +namespace +{ + template< class Path > + void test( const Path & p ) + { +# if !BOOST_WORKAROUND( BOOST_MSVC, <= 1200 ) // VC++ 6.0 can't handle open + { + std::cout << " in test 1\n"; + fs::filebuf fb; + fb.open( p, std::ios_base::in ); + BOOST_CHECK( fb.is_open() == fs::exists( p ) ); + } + { + std::cout << " in test 2\n"; + fs::filebuf fb1; + fb1.open( p, std::ios_base::out ); + BOOST_CHECK( fb1.is_open() ); + } + { + std::cout << " in test 3\n"; + fs::filebuf fb2; + fb2.open( p, std::ios_base::in ); + BOOST_CHECK( fb2.is_open() ); + } +# else + std::cout << "\n"; + std::cout << + "VC++6.0 does not support boost::filesystem open()\n"; +# endif + { + std::cout << " in test 4\n"; + fs::ifstream tfs( p ); + BOOST_CHECK( tfs.is_open() ); + } + { + std::cout << " in test 5\n"; + fs::ifstream tfs( p, std::ios_base::in ); + BOOST_CHECK( tfs.is_open() ); + } +# if !BOOST_WORKAROUND( BOOST_MSVC, <= 1200 ) // VC++ 6.0 can't handle open + { + std::cout << " in test 6\n"; + fs::ifstream tfs; + tfs.open( p ); + BOOST_CHECK( tfs.is_open() ); + } + { + std::cout << " in test 7\n"; + fs::ifstream tfs; + tfs.open( p, std::ios_base::in ); + BOOST_CHECK( tfs.is_open() ); + } +# endif + { + std::cout << " in test 8\n"; + fs::ofstream tfs( p ); + BOOST_CHECK( tfs.is_open() ); + } + { + std::cout << " in test 9\n"; + fs::ofstream tfs( p, std::ios_base::out ); + BOOST_CHECK( tfs.is_open() ); + } +# if !BOOST_WORKAROUND( BOOST_MSVC, <= 1200 ) // VC++ 6.0 can't handle open + { + std::cout << " in test 10\n"; + fs::ofstream tfs; + tfs.open( p ); + BOOST_CHECK( tfs.is_open() ); + } + { + std::cout << " in test 11\n"; + fs::ofstream tfs; + tfs.open( p, std::ios_base::out ); + BOOST_CHECK( tfs.is_open() ); + } +# endif + { + std::cout << " in test 12\n"; + fs::fstream tfs( p ); + BOOST_CHECK( tfs.is_open() ); + } + { + std::cout << " in test 13\n"; + fs::fstream tfs( p, std::ios_base::in|std::ios_base::out ); + BOOST_CHECK( tfs.is_open() ); + } +# if !BOOST_WORKAROUND( BOOST_MSVC, <= 1200 ) // VC++ 6.0 can't handle open + { + std::cout << " in test 14\n"; + fs::fstream tfs; + tfs.open( p ); + BOOST_CHECK( tfs.is_open() ); + } + { + std::cout << " in test 15\n"; + fs::fstream tfs; + tfs.open( p, std::ios_base::in|std::ios_base::out ); + BOOST_CHECK( tfs.is_open() ); + } +# endif + } // test +} // unnamed namespace + int test_main( int, char*[] ) { - { // basic_filebuf runtime results are ignored; as long as they don't crash - // or throw we are satisfied - fs::basic_filebuf bfb; - fs::filebuf cfb; + + // test fs::path + std::cout << "path tests:\n"; + test( fs::path( "fstream_test_foo" ) ); - bfb.open( "fstream_test_bffoo", std::ios_base::in ); - cfb.open( "fstream_test_bffoo", std::ios_base::in ); +#ifndef BOOST_FILESYSTEM_NARROW_ONLY -# ifndef BOOST_NO_STD_WSTRING - fs::wfilebuf wfb; - wfb.open( "fstream_test_bffoo", std::ios_base::in ); -# endif - } + // So that tests are run with known encoding, use Boost UTF-8 codecvt + std::locale global_loc = std::locale(); + std::locale loc( global_loc, new fs::detail::utf8_codecvt_facet ); + fs::wpath_traits::imbue( loc ); - std::remove( "fstream_test_bfoo" ); - std::remove( "fstream_test_cfoo" ); -# ifndef BOOST_NO_STD_WSTRING - std::remove( "fstream_test_wfoo" ); -# endif - { - fs::basic_ofstream bofs( "fstream_test_bfoo" ); - fs::ofstream cofs( "fstream_test_cfoo" ); + // test fs::wpath + // x2780 is circled 1 against white background == e2 9e 80 in UTF-8 + // x2781 is circled 2 against white background == e2 9e 81 in UTF-8 + std::cout << "\nwpath tests:\n"; + test( fs::wpath( L"fstream_test_\x2780" ) ); - BOOST_CHECK( bofs.is_open() ); - BOOST_CHECK( cofs.is_open() ); + // test user supplied basic_path + const long lname[] = { 'f', 's', 'r', 'e', 'a', 'm', '_', 't', 'e', 's', + 't', '_', 'l', 'p', 'a', 't', 'h', 0 }; + std::cout << "\nlpath tests:\n"; + test( user::lpath( lname ) ); - bofs << "fstream_test_bfoo"; - cofs << "fstream_test_cfoo"; +#endif - // these will fail, but they still test the interface - bofs.open( "fstream_test_bfoo" ); - cofs.open( "fstream_test_cfoo" ); - -# ifndef BOOST_NO_STD_WSTRING - fs::wofstream wofs( "fstream_test_wfoo" ); - BOOST_CHECK( wofs.is_open() ); - wofs << L"fstream_test_wfoo"; - wofs.open( "fstream_test_wfoo" ); // expected to fail -# endif - } - - { - fs::basic_ifstream bifs( "fstream_test_bfoo" ); - fs::ifstream cifs( "fstream_test_cfoo" ); - - BOOST_CHECK( bifs.is_open() ); - BOOST_CHECK( cifs.is_open() ); - - std::string b; - std::string c; - - bifs >> b; - cifs >> c; - - BOOST_CHECK( b == "fstream_test_bfoo" ); - BOOST_CHECK( c == "fstream_test_cfoo" ); - - // these will fail, but they still test the interface - bifs.open( "fstream_test_bfoo" ); - cifs.open( "fstream_test_cfoo" ); - -# ifndef BOOST_NO_STD_WSTRING - fs::wifstream wifs( "fstream_test_wfoo" ); - BOOST_CHECK( wifs.is_open() ); - std::wstring w; - wifs >> w; - BOOST_CHECK( w == L"fstream_test_wfoo" ); - wifs.open( "fstream_test_wfoo" ); // expected to fail -# endif - } - - { - fs::basic_fstream bfs( "fstream_test_bfoo" ); - fs::fstream cfs( "fstream_test_cfoo" ); - - BOOST_CHECK( bfs.is_open() ); - BOOST_CHECK( cfs.is_open() ); - - std::string b; - std::string c; - - bfs >> b; - cfs >> c; - - BOOST_CHECK( b == "fstream_test_bfoo" ); - BOOST_CHECK( c == "fstream_test_cfoo" ); - - // these will fail, but they still test the interface - bfs.open( "fstream_test_bfoo" ); - cfs.open( "fstream_test_cfoo" ); - -# ifndef BOOST_NO_STD_WSTRING - fs::wfstream wfs( "fstream_test_wfoo" ); - BOOST_CHECK( wfs.is_open() ); - std::wstring w; - wfs >> w; - BOOST_CHECK( w == L"fstream_test_wfoo" ); - wfs.open( "fstream_test_wfoo" ); // expected to fail -# endif - } - -// std::remove( "fstream_test_bfoo" ); -// std::remove( "fstream_test_cfoo" ); -// # ifndef BOOST_NO_STD_WSTRING -// std::remove( "fstream_test_wfoo" ); -// # endif return 0; } diff --git a/test/large_file_support_test.cpp b/test/large_file_support_test.cpp index 69cb213..bac5d09 100644 --- a/test/large_file_support_test.cpp +++ b/test/large_file_support_test.cpp @@ -15,7 +15,7 @@ namespace fs = boost::filesystem; int main() { - if ( fs::possible_large_file_size_support() ) + if ( fs::detail::possible_large_file_size_support() ) { std::cout << "It appears that file sizes greater that 2 gigabytes are possible\n" "for this configuration on this platform since the operating system\n" diff --git a/test/lpath.hpp b/test/lpath.hpp new file mode 100644 index 0000000..6fd625b --- /dev/null +++ b/test/lpath.hpp @@ -0,0 +1,99 @@ +// Boost lpath.hpp ---------------------------------------------------------// + +// Copyright Beman Dawes 2005 + +// Use, modification, and distribution is subject to 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 + +#include +#include + +namespace std +{ + // Note well: this specialization is meant only to support wide_test.cpp. + // It is not fully functional, fully correct, or efficient. + template<> struct char_traits + { + typedef long char_type; + typedef long int_type; + typedef streamoff off_type; + typedef streampos pos_type; + typedef mbstate_t state_type; + static void assign(char_type& c1, const char_type& c2){c1=c2;} + static bool eq(const char_type& c1, const char_type& c2){return c1==c2;} + static bool lt(const char_type& c1, const char_type& c2){return c1 lstring; + struct lpath_traits; + typedef boost::filesystem::basic_path lpath; + + struct lpath_traits + { + typedef lstring internal_string_type; + typedef std::string external_string_type; + + static external_string_type to_external( const lpath &, + const internal_string_type & src ) + { + external_string_type tmp; + for ( internal_string_type::const_iterator it( src.begin() ); + it != src.end(); ++it ) + { + tmp += static_cast(*it); + } + return tmp; + } + + static internal_string_type to_internal( const external_string_type & src ) + { + internal_string_type tmp; + for ( external_string_type::const_iterator it( src.begin() ); + it != src.end(); ++it ) tmp += *it; + return tmp; + } + }; + +} // namespace user + +namespace boost +{ + namespace filesystem + { + template<> struct is_basic_path + { static const bool value = true; }; + } +} diff --git a/test/operations_test.cpp b/test/operations_test.cpp index 9805b38..912a4c8 100644 --- a/test/operations_test.cpp +++ b/test/operations_test.cpp @@ -7,8 +7,15 @@ // See library home page at http://www.boost.org/libs/filesystem +// VC++ 8.0 warns on various less-than-safe practices. +// See http://msdn.microsoft.com/msdnmag/issues/05/05/SafeCandC/default.aspx +// But at least in VC++ 8.0 betas, their own libraries use the problem +// practices. So turn off the warnings. +#define _CRT_SECURE_NO_DEPRECATE +#define _SCL_SECURE_NO_DEPRECATE + #include -#include +#include namespace fs = boost::filesystem; #include @@ -20,14 +27,31 @@ using boost::bind; #include #include #include -#include #include +#include // for system() + +#ifndef BOOST_FILESYSTEM_NARROW_ONLY +# define BOOST_BND(BOOST_FUNC_TO_DO) BOOST_FUNC_TO_DO +#else +# define BOOST_BND(BOOST_FUNC_TO_DO) BOOST_FUNC_TO_DO +#endif + +// VC++ 7.0 and earlier has a serious namespace bug that causes a clash +// between boost::filesystem::is_empty and the unrelated type trait +// boost::is_empty. +#if !defined( BOOST_MSVC ) || BOOST_MSVC > 1300 +# define BOOST_FS_IS_EMPTY fs::is_empty +#else +# define BOOST_FS_IS_EMPTY fs::_is_empty +#endif # ifdef BOOST_NO_STDC_NAMESPACE namespace std { using ::asctime; using ::gmtime; using ::localtime; - using ::difftime; using ::time; using ::tm; using ::mktime; } + using ::difftime; using ::time; using ::tm; using ::mktime; using ::system; } # endif +#define CHECK_EXCEPTION(b,e) throws_fs_error(b,e,__LINE__) + namespace { bool report_throws; @@ -37,45 +61,64 @@ namespace void create_file( const fs::path & ph, const std::string & contents ) { - std::ofstream f( ph.native_file_string().c_str() ); + std::ofstream f( ph.file_string().c_str() ); if ( !f ) - throw fs::filesystem_error( "operations_test create_file", + throw fs::filesystem_path_error( "operations_test create_file", ph, errno ); if ( !contents.empty() ) f << contents; } void verify_file( const fs::path & ph, const std::string & expected ) { - std::ifstream f( ph.native_file_string().c_str() ); + std::ifstream f( ph.file_string().c_str() ); if ( !f ) - throw fs::filesystem_error( "operations_test verify_file", + throw fs::filesystem_path_error( "operations_test verify_file", ph, errno ); std::string contents; f >> contents; if ( contents != expected ) - throw fs::filesystem_error( "operations_test verify_file", - ph, " contents \"" + contents - + "\" != \"" + expected + "\"" ); + throw fs::filesystem_path_error( "operations_test verify_file contents \"" + + contents + "\" != \"" + expected + "\"", ph, 0 ); } template< typename F > - bool throws_fs_error( F func, fs::error_code ec = - ::boost::filesystem::no_error ) // VC++ 7.1 build 2292 won't accept fs:: + bool throws_fs_error( F func, fs::errno_type ec, int line ) { try { func(); } catch ( const fs::filesystem_error & ex ) { if ( report_throws ) std::cout << ex.what() << "\n"; - if ( ec == fs::no_error || ec == ex.error() ) return true; - std::cout << "filesystem_error::error() reports " << ex.error() + if ( ec == 0 + || ec == fs::lookup_error_code(ex.system_error()) ) return true; + std::cout + << "\nline " << line + << " exception reports " << fs::lookup_error_code(ex.system_error()) << ", should be " << ec - << "\n native_error() is " << ex.native_error() + << "\n system_error() is " << ex.system_error() << std::endl; } return false; } + // compile-only two argument "do-the-right-thing" tests + // verifies that all overload combinations compile without error + void do_not_call() + { + fs::path p; + std::string s; + const char * a = 0; + fs::copy_file( p, p ); + fs::copy_file( s, p ); + fs::copy_file( a, p ); + fs::copy_file( p, s ); + fs::copy_file( p, a ); + fs::copy_file( s, s ); + fs::copy_file( a, s ); + fs::copy_file( s, a ); + fs::copy_file( a, a ); + } + } // unnamed namespace // test_main ---------------------------------------------------------------// @@ -89,116 +132,134 @@ int test_main( int argc, char * argv[] ) // The choice of platform is make at runtime rather than compile-time // so that compile errors for all platforms will be detected even though // only the current platform is runtime tested. -# if defined( BOOST_POSIX ) +# if defined( BOOST_POSIX_API ) platform = "POSIX"; -# elif defined( BOOST_WINDOWS ) +# elif defined( BOOST_WINDOWS_API ) platform = "Windows"; # else platform = ( platform == "Win32" || platform == "Win64" || platform == "Cygwin" ) ? "Windows" : "POSIX"; # endif - std::cout << "Platform is " << platform << '\n'; + std::cout << "API is " << platform << '\n'; - std::cout << "initial_path().string() is\n \"" - << fs::initial_path().string() + std::cout << "\ninitial_path().string() is\n \"" + << fs::initial_path().string() << "\"\n"; - std::cout << "initial_path().native_file_string() is\n \"" - << fs::initial_path().native_file_string() - << "\"\n"; - - BOOST_CHECK( fs::initial_path().is_complete() ); - BOOST_CHECK( fs::current_path().is_complete() ); - BOOST_CHECK( fs::initial_path().string() == fs::current_path().string() ); + std::cout << "\ninitial_path().file_string() is\n \"" + << fs::initial_path().file_string() + << "\"\n\n"; + BOOST_CHECK( fs::initial_path().is_complete() ); + BOOST_CHECK( fs::current_path().is_complete() ); + BOOST_CHECK( fs::initial_path().string() + == fs::current_path().string() ); BOOST_CHECK( fs::complete( "" ).empty() ); - BOOST_CHECK( fs::complete( "/" ).string() - == fs::initial_path().root_path().string() ); - BOOST_CHECK( fs::complete( "foo" ).string() - == fs::initial_path().string()+"/foo" ); - BOOST_CHECK( fs::complete( "/foo" ).string() - == fs::initial_path().root_path().string()+"foo" ); + BOOST_CHECK( fs::complete( "/" ).string() == fs::initial_path().root_path().string() ); + BOOST_CHECK( fs::complete( "foo" ).string() == fs::initial_path().string()+"/foo" ); + BOOST_CHECK( fs::complete( "/foo" ).string() == fs::initial_path().root_path().string()+"foo" ); + BOOST_CHECK( fs::complete( "foo", fs::path( "//net/bar" ) ).string() + == "//net/bar/foo" ); - fs::path dir( fs::initial_path() / temp_dir_name ); + // predicate and status tests + fs::path ng( " no-way, Jose" ); + BOOST_CHECK( !fs::exists( ng ) ); + BOOST_CHECK( !fs::is_directory( ng ) ); + BOOST_CHECK( !fs::is_file( ng ) ); + BOOST_CHECK( !fs::is_symlink( ng ) ); + BOOST_CHECK( (fs::status( ng ) & fs::not_found_flag) != 0 ); + BOOST_CHECK( (fs::status( "" ) & fs::not_found_flag) != 0 ); + + fs::path dir( fs::initial_path() / temp_dir_name ); // Windows only tests if ( platform == "Windows" ) { - BOOST_CHECK( !fs::exists( fs::path( "//share-not/foo", fs::native ) ) ); + BOOST_CHECK( !fs::exists( fs::path( "//share-not/foo" ) ) ); BOOST_CHECK( dir.string().size() > 1 && dir.string()[1] == ':' ); // verify path includes drive BOOST_CHECK( fs::system_complete( "" ).empty() ); BOOST_CHECK( fs::system_complete( "/" ).string() - == fs::initial_path().root_path().string() ); + == fs::initial_path().root_path().string() ); BOOST_CHECK( fs::system_complete( "foo" ).string() - == fs::initial_path().string()+"/foo" ); + == fs::initial_path().string()+"/foo" ); BOOST_CHECK( fs::system_complete( "/foo" ).string() - == fs::initial_path().root_path().string()+"foo" ); - -// BOOST_CHECK( fs::complete( fs::path( "c:", fs::native ) ).string() -// == fs::initial_path().string() ); -// BOOST_CHECK( fs::complete( fs::path( "c:foo", fs::native ) ).string() -// == fs::initial_path().string()+"/foo" ); - BOOST_CHECK( fs::complete( fs::path( "c:/", fs::native ) ).string() + == fs::initial_path().root_path().string()+"foo" ); + BOOST_CHECK( fs::complete( fs::path( "c:/" ) ).string() == "c:/" ); - BOOST_CHECK( fs::complete( fs::path( "c:/foo", fs::native ) ).string() + BOOST_CHECK( fs::complete( fs::path( "c:/foo" ) ).string() == "c:/foo" ); - BOOST_CHECK( fs::complete( fs::path( "//share", fs::native ) ).string() - == "//share" ); - BOOST_CHECK( fs::system_complete( fs::path( fs::initial_path().root_name(), - fs::native ) ).string() == fs::initial_path().string() ); - BOOST_CHECK( fs::system_complete( fs::path( fs::initial_path().root_name() - + "foo", fs::native ) ).string() == fs::initial_path().string()+"/foo" ); - BOOST_CHECK( fs::system_complete( fs::path( "c:/", fs::native ) ).string() + BOOST_CHECK( fs::system_complete( fs::path( fs::initial_path().root_name() ) ).string() == fs::initial_path().string() ); + BOOST_CHECK( fs::system_complete( fs::path( fs::initial_path().root_name() + + "foo" ) ).string() == fs::initial_path().string()+"/foo" ); + BOOST_CHECK( fs::system_complete( fs::path( "c:/" ) ).string() == "c:/" ); - BOOST_CHECK( fs::system_complete( fs::path( "c:/foo", fs::native ) ).string() + BOOST_CHECK( fs::system_complete( fs::path( "c:/foo" ) ).string() == "c:/foo" ); - BOOST_CHECK( fs::system_complete( fs::path( "//share", fs::native ) ).string() + BOOST_CHECK( fs::system_complete( fs::path( "//share" ) ).string() == "//share" ); - } + } // Windows else if ( platform == "POSIX" ) { BOOST_CHECK( fs::system_complete( "" ).empty() ); - BOOST_CHECK( fs::initial_path().root_path().string() == "/" ); + BOOST_CHECK( fs::initial_path().root_path().string() == "/" ); BOOST_CHECK( fs::system_complete( "/" ).string() == "/" ); BOOST_CHECK( fs::system_complete( "foo" ).string() - == fs::initial_path().string()+"/foo" ); + == fs::initial_path().string()+"/foo" ); BOOST_CHECK( fs::system_complete( "/foo" ).string() - == fs::initial_path().root_path().string()+"foo" ); - } - - fs::path ng( " no-way, Jose", fs::native ); + == fs::initial_path().root_path().string()+"foo" ); + } // POSIX fs::remove_all( dir ); // in case residue from prior failed tests BOOST_CHECK( !fs::exists( dir ) ); - // the bound functions should throw, so throws_fs_error() should return true - BOOST_CHECK( throws_fs_error( bind( fs::is_directory, ng ), fs::not_found_error ) ); - BOOST_CHECK( throws_fs_error( bind( fs::file_size, ng ), fs::not_found_error ) ); - BOOST_CHECK( throws_fs_error( bind( fs::is_directory, dir ) ) ); - BOOST_CHECK( throws_fs_error( bind( fs::_is_empty, dir ) ) ); + // the bound functions should throw, so CHECK_EXCEPTION() should return true + BOOST_CHECK( CHECK_EXCEPTION( bind( BOOST_BND(fs::file_size), ng ), ENOENT ) ); // test path::exception members - try { fs::is_directory( ng ); } // will throw + try { fs::file_size( ng ); } // will throw - catch ( const fs::filesystem_error & ex ) + catch ( const fs::filesystem_path_error & ex ) { - BOOST_CHECK( ex.who() == "boost::filesystem::is_directory" ); BOOST_CHECK( ex.path1().string() == " no-way, Jose" ); } BOOST_CHECK( fs::create_directory( dir ) ); + // several functions give unreasonable results if uintmax_t isn't 64-bits + std::cout << "sizeof(boost::uintmax_t) = " << sizeof(boost::uintmax_t) << '\n'; + BOOST_CHECK( sizeof( boost::uintmax_t ) >= 8 ); + + // make some reasonable assuptions for testing purposes + fs::space_info spi( fs::space( dir ) ); + BOOST_CHECK( spi.capacity > 1000000 ); + BOOST_CHECK( spi.free > 1000 ); + BOOST_CHECK( spi.capacity > spi.free ); + BOOST_CHECK( spi.free >= spi.available ); + + // it is convenient to display space, but older VC++ versions choke +# if !defined(BOOST_MSVC) || _MSC_VER >= 1300 // 1300 == VC++ 7.0 + std::cout << " capacity = " << spi.capacity << '\n'; + std::cout << " free = " << spi.free << '\n'; + std::cout << "available = " << spi.available << '\n'; +# endif + BOOST_CHECK( fs::exists( dir ) ); - BOOST_CHECK( fs::_is_empty( dir ) ); + BOOST_CHECK( BOOST_FS_IS_EMPTY( dir ) ); BOOST_CHECK( fs::is_directory( dir ) ); - BOOST_CHECK( throws_fs_error( bind( fs::file_size, dir ), - fs::is_directory_error ) ); + if ( platform == "Windows" ) + BOOST_CHECK( CHECK_EXCEPTION( bind( BOOST_BND(fs::file_size), dir ), + ENOENT ) ); + else + BOOST_CHECK( CHECK_EXCEPTION( bind( BOOST_BND(fs::file_size), dir ), 0 ) ); BOOST_CHECK( !fs::create_directory( dir ) ); + BOOST_CHECK( !fs::is_symlink( dir ) ); + BOOST_CHECK( !fs::is_symlink( "nosuchfileordirectory" ) ); + BOOST_CHECK( !fs::symbolic_link_exists( dir ) ); BOOST_CHECK( !fs::symbolic_link_exists( "nosuchfileordirectory" ) ); @@ -206,13 +267,17 @@ int test_main( int argc, char * argv[] ) BOOST_CHECK( fs::create_directory( d1 ) ); BOOST_CHECK( fs::exists( d1 ) ); BOOST_CHECK( fs::is_directory( d1 ) ); - BOOST_CHECK( fs::_is_empty( d1 ) ); + BOOST_CHECK( BOOST_FS_IS_EMPTY( d1 ) ); boost::function_requires< boost::InputIteratorConcept< fs::directory_iterator > >(); { fs::directory_iterator dir_itr( dir ); + BOOST_CHECK( dir_itr->status() == fs::directory_flag ); + BOOST_CHECK( dir_itr->status( fs::symlink ) == fs::directory_flag ); BOOST_CHECK( dir_itr->leaf() == "d1" ); + BOOST_CHECK( fs::status(*dir_itr) == fs::directory_flag ); + BOOST_CHECK( fs::status( *dir_itr, fs::symlink ) == fs::directory_flag ); } // create a second directory named d2 @@ -225,8 +290,15 @@ int test_main( int argc, char * argv[] ) // stepping one iterator doesn't affect a different iterator. { fs::directory_iterator dir_itr( dir ); + BOOST_CHECK( dir_itr->exists() ); + BOOST_CHECK( dir_itr->is_directory() ); + BOOST_CHECK( !dir_itr->is_file() ); + BOOST_CHECK( !dir_itr->is_other() ); + BOOST_CHECK( !dir_itr->is_symlink() ); + fs::directory_iterator dir_itr2( dir ); - BOOST_CHECK( dir_itr->leaf() == "d1" || dir_itr->leaf() == "d2" ); + BOOST_CHECK( dir_itr->leaf() == "d1" + || dir_itr->leaf() == "d2" ); BOOST_CHECK( dir_itr2->leaf() == "d1" || dir_itr2->leaf() == "d2" ); if ( dir_itr->leaf() == "d1" ) { @@ -236,9 +308,9 @@ int test_main( int argc, char * argv[] ) } else { - BOOST_CHECK( (dir_itr)->leaf() == "d2" ); + BOOST_CHECK( dir_itr->leaf() == "d2" ); BOOST_CHECK( (++dir_itr)->leaf() == "d1" ); - BOOST_CHECK( dir_itr2->leaf() == "d2" ); + BOOST_CHECK( (++dir_itr2)->leaf() == "d2" ); BOOST_CHECK( (++dir_itr2)->leaf() == "d1" ); } BOOST_CHECK( ++dir_itr == fs::directory_iterator() ); @@ -248,7 +320,8 @@ int test_main( int argc, char * argv[] ) { // *i++ must work to meet the standard's InputIterator requirements fs::directory_iterator dir_itr( dir ); - BOOST_CHECK( dir_itr->leaf() == "d1" || dir_itr->leaf() == "d2" ); + BOOST_CHECK( dir_itr->leaf() == "d1" + || dir_itr->leaf() == "d2" ); if ( dir_itr->leaf() == "d1" ) { BOOST_CHECK( (*dir_itr++).leaf() == "d1" ); @@ -278,11 +351,11 @@ int test_main( int argc, char * argv[] ) // Reported as S/F bug [ 1259176 ] if ( platform == "Windows" ) { - fs::path root_name_path( fs::current_path().root_name(), fs::native ); + fs::path root_name_path( fs::current_path().root_name() ); fs::directory_iterator it( root_name_path ); BOOST_CHECK( it != fs::directory_iterator() ); BOOST_CHECK( fs::exists( *it ) ); - BOOST_CHECK( it->branch_path() == root_name_path ); + BOOST_CHECK( it->path().branch_path() == root_name_path ); bool found(false); do { @@ -296,18 +369,25 @@ int test_main( int argc, char * argv[] ) create_file( file_ph, "" ); BOOST_CHECK( fs::exists( file_ph ) ); BOOST_CHECK( !fs::is_directory( file_ph ) ); - BOOST_CHECK( fs::_is_empty( file_ph ) ); + BOOST_CHECK( fs::is_file( file_ph ) ); + BOOST_CHECK( BOOST_FS_IS_EMPTY( file_ph ) ); BOOST_CHECK( fs::file_size( file_ph ) == 0 ); - BOOST_CHECK( throws_fs_error( bind( fs::create_directory, file_ph ), - fs::not_directory_error ) ); - + BOOST_CHECK( CHECK_EXCEPTION( bind( BOOST_BND(fs::create_directory), + file_ph ), EEXIST ) ); // create a file named "f1" file_ph = dir / "f1"; create_file( file_ph, "foobar1" ); + BOOST_CHECK( fs::exists( file_ph ) ); + BOOST_CHECK( !fs::is_directory( file_ph ) ); + BOOST_CHECK( fs::is_file( file_ph ) ); + BOOST_CHECK( fs::file_size( file_ph ) == 7 ); + verify_file( file_ph, "foobar1" ); + // equivalence tests fs::path ng2("does_not_exist2"); - BOOST_CHECK( throws_fs_error( bind( fs::equivalent, ng, ng2 ) ) ); + BOOST_CHECK( CHECK_EXCEPTION( + bind( BOOST_BND(fs::equivalent), ng, ng2 ), ENOENT ) ); BOOST_CHECK( fs::equivalent( file_ph, dir / "f1" ) ); BOOST_CHECK( fs::equivalent( dir, d1 / ".." ) ); BOOST_CHECK( !fs::equivalent( file_ph, dir ) ); @@ -317,41 +397,59 @@ int test_main( int argc, char * argv[] ) BOOST_CHECK( !fs::equivalent( ng, dir ) ); BOOST_CHECK( !fs::equivalent( file_ph, ng ) ); BOOST_CHECK( !fs::equivalent( ng, file_ph ) ); - - std::time_t ft = fs::last_write_time( file_ph ); - std::cout << "UTC should currently be about " << std::asctime(std::gmtime(&ft)) << "\n"; - std::cout << "Local time should currently be about " << std::asctime(std::localtime(&ft)) << std::endl; - - // hard to test time exactly, but except under the most unusual circumstances, - // time since file creation should be no more than one minute, I'm hoping. - double time_diff = std::difftime( std::time(0), fs::last_write_time( file_ph ) ); - BOOST_CHECK( time_diff > -60.0 && time_diff < 60.0 ); - + + // hard link tests + fs::path from_ph( dir / "f3" ); + BOOST_CHECK( !fs::exists( from_ph ) ); BOOST_CHECK( fs::exists( file_ph ) ); - BOOST_CHECK( !fs::is_directory( file_ph ) ); - BOOST_CHECK( fs::file_size( file_ph ) == 7 ); - verify_file( file_ph, "foobar1" ); + bool create_hard_link_ok(true); + try { fs::create_hard_link( file_ph, from_ph ); } + catch ( const fs::filesystem_error & ex ) + { + create_hard_link_ok = false; + std::cout + << "create_hard_link() attempt failed\n" + << "filesystem_error.what() reports: " << ex.what() << '\n' + << "create_hard_link() may not be supported on this file system\n"; + } - std::tm * tmp = std::localtime( &ft ); - std::cout << "Year is " << tmp->tm_year << std::endl; - --tmp->tm_year; - std::cout << "Change year to " << tmp->tm_year << std::endl; - fs::last_write_time( file_ph, std::mktime( tmp ) ); - std::cout << "Get new value" << std::endl; - ft = fs::last_write_time( file_ph ); - std::cout << "Local time one year ago should currently be about " << std::asctime(std::localtime(&ft)) << "\n"; - std::cout << "Now get time difference" << std::endl; - time_diff = std::difftime( std::time(0), fs::last_write_time( file_ph ) ); - time_diff -= 365*24*3600.0; - std::cout << "Time difference is : " << time_diff << std::endl; - BOOST_CHECK( time_diff >= -60.0 && time_diff <= 60.0 ); - std::cout << "Reset to current time" << std::endl; - fs::last_write_time( file_ph, std::time(0) ); - std::cout << "And check that" << std::endl; - time_diff = std::difftime( std::time(0), fs::last_write_time( file_ph ) ); - BOOST_CHECK( time_diff >= -60.0 && time_diff <= 60.0 ); - ft = fs::last_write_time( file_ph ); - std::cout << "Local time should currently be about " << std::asctime(std::localtime(&ft)) << "\n"; + if ( create_hard_link_ok ) + { + std::cout << "create_hard_link() succeeded\n"; + BOOST_CHECK( fs::exists( from_ph ) ); + BOOST_CHECK( fs::exists( file_ph ) ); + BOOST_CHECK( fs::equivalent( from_ph, file_ph ) ); + } + + BOOST_CHECK( fs::create_hard_link( fs::path("doesnotexist"), fs::path("shouldnotwork"), + std::nothrow ) != 0 ); + + // symbolic link tests + from_ph = dir / "f4"; + BOOST_CHECK( !fs::exists( from_ph ) ); + BOOST_CHECK( fs::exists( file_ph ) ); + bool create_symlink_ok(true); + try { fs::create_symlink( file_ph, from_ph ); } + catch ( const fs::filesystem_error & ex ) + { + create_symlink_ok = false; + std::cout + << "create_symlink() attempt failed\n" + << "filesystem_error.what() reports: " << ex.what() << '\n' + << "create_symlink() may not be supported on this file system\n"; + } + + if ( create_symlink_ok ) + { + std::cout << "create_symlink() succeeded\n"; + BOOST_CHECK( fs::exists( from_ph ) ); + BOOST_CHECK( fs::is_symlink( from_ph ) ); + BOOST_CHECK( fs::exists( file_ph ) ); + BOOST_CHECK( fs::equivalent( from_ph, file_ph ) ); + } + + BOOST_CHECK( fs::create_symlink( "doesnotexist", "", + std::nothrow ) != 0 ); // there was an inital bug in directory_iterator that caused premature // close of an OS handle. This block will detect regression. @@ -376,28 +474,29 @@ int test_main( int argc, char * argv[] ) // [case 1] make sure can't rename() a non-existent file BOOST_CHECK( !fs::exists( d1 / "f99" ) ); BOOST_CHECK( !fs::exists( d1 / "f98" ) ); - BOOST_CHECK( throws_fs_error( bind( fs::rename, d1 / "f99", d1 / "f98" ), - fs::not_found_error ) ); - BOOST_CHECK( throws_fs_error( bind( fs::rename, fs::path(""), d1 / "f98" ), - fs::not_found_error ) ); + BOOST_CHECK( CHECK_EXCEPTION( bind( BOOST_BND(fs::rename), d1 / "f99", d1 / "f98" ), + ENOENT ) ); + BOOST_CHECK( CHECK_EXCEPTION( bind( BOOST_BND(fs::rename), fs::path(""), d1 / "f98" ), + ENOENT ) ); // [case 2] rename() target.empty() - BOOST_CHECK( throws_fs_error( bind( fs::rename, file_ph, "" ), - fs::not_found_error ) ); + BOOST_CHECK( CHECK_EXCEPTION( bind( BOOST_BND(fs::rename), file_ph, "" ), + ENOENT ) ); // [case 3] make sure can't rename() to an existent file or directory BOOST_CHECK( fs::exists( dir / "f1" ) ); BOOST_CHECK( fs::exists( d1 / "f2" ) ); - BOOST_CHECK( throws_fs_error( bind( fs::rename, dir / "f1", d1 / "f2" ) ) ); + BOOST_CHECK( CHECK_EXCEPTION( bind( BOOST_BND(fs::rename), + dir / "f1", d1 / "f2" ), EEXIST ) ); // several POSIX implementations (cygwin, openBSD) report ENOENT instead of EEXIST, // so we don't verify error type on the above test. - BOOST_CHECK( throws_fs_error( bind( fs::rename, dir, d1 ) ) ); + BOOST_CHECK( CHECK_EXCEPTION( bind( BOOST_BND(fs::rename), dir, d1 ), 0 ) ); // [case 4A] can't rename() file to a nonexistent parent directory BOOST_CHECK( !fs::is_directory( dir / "f1" ) ); BOOST_CHECK( !fs::exists( dir / "d3/f3" ) ); - BOOST_CHECK( throws_fs_error( bind( fs::rename, dir / "f1", dir / "d3/f3" ), - fs::not_found_error ) ); + BOOST_CHECK( CHECK_EXCEPTION( bind( BOOST_BND(fs::rename), dir / "f1", dir / "d3/f3" ), + ENOENT ) ); // [case 4B] rename() file in same directory BOOST_CHECK( fs::exists( d1 / "f2" ) ); @@ -423,8 +522,8 @@ int test_main( int argc, char * argv[] ) BOOST_CHECK( fs::exists( d1 ) ); BOOST_CHECK( !fs::exists( dir / "d3/d5" ) ); BOOST_CHECK( !fs::exists( dir / "d3" ) ); - BOOST_CHECK( throws_fs_error( bind( fs::rename, d1, dir / "d3/d5" ), - fs::not_found_error ) ); + BOOST_CHECK( CHECK_EXCEPTION( bind( BOOST_BND(fs::rename), d1, dir / "d3/d5" ), + ENOENT ) ); // [case 5B] rename() on directory fs::path d3( dir / "d3" ); @@ -472,23 +571,24 @@ int test_main( int argc, char * argv[] ) fs::create_directory( d1 ); BOOST_CHECK( fs::exists( d1 ) ); BOOST_CHECK( fs::is_directory( d1 ) ); - BOOST_CHECK( fs::_is_empty( d1 ) ); - BOOST_CHECK( throws_fs_error( bind( fs::remove, dir ), fs::not_empty_error ) ); + BOOST_CHECK( BOOST_FS_IS_EMPTY( d1 ) ); + BOOST_CHECK( CHECK_EXCEPTION( bind( BOOST_BND(fs::remove), dir ), ENOTEMPTY ) ); BOOST_CHECK( fs::remove( d1 ) ); BOOST_CHECK( !fs::exists( d1 ) ); -// STLPort is allergic to std::system, so don't use runtime platform test +// STLport is allergic to std::system, so don't use runtime platform test # ifdef BOOST_POSIX + // remove() test on dangling symbolic link fs::path link( "dangling_link" ); fs::remove( link ); - BOOST_CHECK( !fs::symbolic_link_exists( link ) ); + BOOST_CHECK( !fs::is_symlink( link ) ); BOOST_CHECK( !fs::exists( link ) ); std::system("ln -s nowhere dangling_link"); BOOST_CHECK( !fs::exists( link ) ); - BOOST_CHECK( fs::symbolic_link_exists( link ) ); + BOOST_CHECK( fs::is_symlink( link ) ); BOOST_CHECK( fs::remove( link ) ); - BOOST_CHECK( !fs::symbolic_link_exists( link ) ); + BOOST_CHECK( !fs::is_symlink( link ) ); // remove() test on symbolic link to a file file_ph = "link_target"; @@ -497,27 +597,64 @@ int test_main( int argc, char * argv[] ) create_file( file_ph, "" ); BOOST_CHECK( fs::exists( file_ph ) ); BOOST_CHECK( !fs::is_directory( file_ph ) ); + BOOST_CHECK( fs::is_file( file_ph ) ); std::system("ln -s link_target non_dangling_link"); link = "non_dangling_link"; BOOST_CHECK( fs::exists( link ) ); BOOST_CHECK( !fs::is_directory( link ) ); - BOOST_CHECK( fs::symbolic_link_exists( link ) ); + BOOST_CHECK( fs::is_file( link ) ); + BOOST_CHECK( fs::is_symlink( link ) ); BOOST_CHECK( fs::remove( link ) ); BOOST_CHECK( fs::exists( file_ph ) ); BOOST_CHECK( !fs::exists( link ) ); - BOOST_CHECK( !fs::symbolic_link_exists( link ) ); + BOOST_CHECK( !fs::is_symlink( link ) ); BOOST_CHECK( fs::remove( file_ph ) ); BOOST_CHECK( !fs::exists( file_ph ) ); - } # endif + // write time tests + + file_ph = dir / "foobar2"; + create_file( file_ph, "foobar2" ); + BOOST_CHECK( fs::exists( file_ph ) ); + BOOST_CHECK( !fs::is_directory( file_ph ) ); + BOOST_CHECK( fs::is_file( file_ph ) ); + BOOST_CHECK( fs::file_size( file_ph ) == 7 ); + verify_file( file_ph, "foobar2" ); + + // Some file system report last write time as local (FAT), while + // others (NTFS) report it as UTC. The C standard does not specify + // if time_t is local or UTC. + + std::time_t ft = fs::last_write_time( file_ph ); + std::cout << "\nUTC last_write_time() for a file just created is " + << std::asctime(std::gmtime(&ft)) << std::endl; + + std::tm * tmp = std::localtime( &ft ); + std::cout << "\nYear is " << tmp->tm_year << std::endl; + --tmp->tm_year; + std::cout << "Change year to " << tmp->tm_year << std::endl; + fs::last_write_time( file_ph, std::mktime( tmp ) ); + std::time_t ft2 = fs::last_write_time( file_ph ); + std::cout << "last_write_time() for the file is now " + << std::asctime(std::gmtime(&ft2)) << std::endl; + BOOST_CHECK( ft != fs::last_write_time( file_ph ) ); + + + std::cout << "\nReset to current time" << std::endl; + fs::last_write_time( file_ph, ft ); + double time_diff = std::difftime( ft, fs::last_write_time( file_ph ) ); + std::cout + << "original last_write_time() - current last_write_time() is " + << time_diff << " seconds" << std::endl; + BOOST_CHECK( time_diff >= -60.0 && time_diff <= 60.0 ); + // post-test cleanup BOOST_CHECK( fs::remove_all( dir ) != 0 ); // above was added just to simplify testing, but it ended up detecting // a bug (failure to close an internal search handle). BOOST_CHECK( !fs::exists( dir ) ); BOOST_CHECK( fs::remove_all( dir ) == 0 ); - return 0; } // main diff --git a/test/path_test.cpp b/test/path_test.cpp index 9c02888..be51794 100644 --- a/test/path_test.cpp +++ b/test/path_test.cpp @@ -8,9 +8,9 @@ // See library home page at http://www.boost.org/libs/filesystem #include -#include #include #include +#include #include #include #include @@ -23,10 +23,16 @@ using boost::prior; #include #define PATH_CHECK( a, b ) check( a, b, __LINE__ ) +#define DIR_CHECK( a, b ) check_dir( a, b, __LINE__ ) +#define CHECK_EQUAL( a,b ) check_equal( a, b, __LINE__ ) -namespace { + +namespace +{ int errors; + std::string platform( BOOST_PLATFORM ); + void check( const fs::path & source, const std::string & expected, int line ) { @@ -39,18 +45,154 @@ namespace { << "\"" << std::endl; } - void check_throw( const std::string & arg ) + void check_dir( const fs::path & source, + const std::string & expected, int line ) { - try + if ( source.directory_string()== expected ) return; + + ++errors; + + std::cout << '(' << line << ") source.directory_string(): \"" + << source.directory_string() + << "\" != expected: \"" << expected + << "\"" << std::endl; + } + + void check_equal( const std::string & value, + const std::string & expected, int line ) + { + if ( value == expected ) return; + + ++errors; + + std::cout << '(' << line << ") value: \"" << value + << "\" != expected: \"" << expected + << "\"" << std::endl; + } + + void check_normalize() + { + PATH_CHECK( path("").normalize(), "" ); + PATH_CHECK( path("/").normalize(), "/" ); + PATH_CHECK( path("//").normalize(), "//" ); + PATH_CHECK( path("///").normalize(), "/" ); + PATH_CHECK( path("f").normalize(), "f" ); + PATH_CHECK( path("foo").normalize(), "foo" ); + PATH_CHECK( path("foo/").normalize(), "foo/." ); + PATH_CHECK( path("f/").normalize(), "f/." ); + PATH_CHECK( path( "/foo" ).normalize(), "/foo" ); + PATH_CHECK( path( "foo/bar" ).normalize(), "foo/bar" ); + PATH_CHECK( path("..").normalize(), ".." ); + PATH_CHECK( path("../..").normalize(), "../.." ); + PATH_CHECK( path("/..").normalize(), "/.." ); + PATH_CHECK( path("/../..").normalize(), "/../.." ); + PATH_CHECK( path("../foo").normalize(), "../foo" ); + PATH_CHECK( path("foo/..").normalize(), "." ); + PATH_CHECK( path("foo/../").normalize(), "./." ); + PATH_CHECK( (path("foo") / "..").normalize() , "." ); + PATH_CHECK( path("foo/...").normalize(), "foo/..." ); + PATH_CHECK( path("foo/.../").normalize(), "foo/.../." ); + PATH_CHECK( path("foo/..bar").normalize(), "foo/..bar" ); + PATH_CHECK( path("../f").normalize(), "../f" ); + PATH_CHECK( path("/../f").normalize(), "/../f" ); + PATH_CHECK( path("f/..").normalize(), "." ); + PATH_CHECK( (path("f") / "..").normalize() , "." ); + PATH_CHECK( path("foo/../..").normalize(), ".." ); + PATH_CHECK( path("foo/../../").normalize(), "../." ); + PATH_CHECK( path("foo/../../..").normalize(), "../.." ); + PATH_CHECK( path("foo/../../../").normalize(), "../../." ); + PATH_CHECK( path("foo/../bar").normalize(), "bar" ); + PATH_CHECK( path("foo/../bar/").normalize(), "bar/." ); + PATH_CHECK( path("foo/bar/..").normalize(), "foo" ); + PATH_CHECK( path("foo/bar/../").normalize(), "foo/." ); + PATH_CHECK( path("foo/bar/../..").normalize(), "." ); + PATH_CHECK( path("foo/bar/../../").normalize(), "./." ); + PATH_CHECK( path("foo/bar/../blah").normalize(), "foo/blah" ); + PATH_CHECK( path("f/../b").normalize(), "b" ); + PATH_CHECK( path("f/b/..").normalize(), "f" ); + PATH_CHECK( path("f/b/../").normalize(), "f/." ); + PATH_CHECK( path("f/b/../a").normalize(), "f/a" ); + PATH_CHECK( path("foo/bar/blah/../..").normalize(), "foo" ); + PATH_CHECK( path("foo/bar/blah/../../bletch").normalize(), "foo/bletch" ); + PATH_CHECK( path( "//net" ).normalize(), "//net" ); + PATH_CHECK( path( "//net/" ).normalize(), "//net/" ); + PATH_CHECK( path( "//..net" ).normalize(), "//..net" ); + PATH_CHECK( path( "//net/.." ).normalize(), "//net/.." ); + PATH_CHECK( path( "//net/foo" ).normalize(), "//net/foo" ); + PATH_CHECK( path( "//net/foo/" ).normalize(), "//net/foo/." ); + PATH_CHECK( path( "//net/foo/.." ).normalize(), "//net/" ); + PATH_CHECK( path( "//net/foo/../" ).normalize(), "//net/." ); + + PATH_CHECK( path( "/net/foo/bar" ).normalize(), "/net/foo/bar" ); + PATH_CHECK( path( "/net/foo/bar/" ).normalize(), "/net/foo/bar/." ); + PATH_CHECK( path( "/net/foo/.." ).normalize(), "/net" ); + PATH_CHECK( path( "/net/foo/../" ).normalize(), "/net/." ); + + PATH_CHECK( path( "//net//foo//bar" ).normalize(), "//net/foo/bar" ); + PATH_CHECK( path( "//net//foo//bar//" ).normalize(), "//net/foo/bar/." ); + PATH_CHECK( path( "//net//foo//.." ).normalize(), "//net/" ); + PATH_CHECK( path( "//net//foo//..//" ).normalize(), "//net/." ); + + PATH_CHECK( path( "///net///foo///bar" ).normalize(), "/net/foo/bar" ); + PATH_CHECK( path( "///net///foo///bar///" ).normalize(), "/net/foo/bar/." ); + PATH_CHECK( path( "///net///foo///.." ).normalize(), "/net" ); + PATH_CHECK( path( "///net///foo///..///" ).normalize(), "/net/." ); + + if ( platform == "Windows" ) { - fs::path arg_path( arg ); - ++errors; - std::cout << "failed to throw with argument \"" << arg - << "\"" << std::endl; + PATH_CHECK( path( "c:.." ).normalize(), "c:.." ); + PATH_CHECK( path( "c:foo/.." ).normalize(), "c:" ); + + PATH_CHECK( path( "c:foo/../" ).normalize(), "c:." ); + + PATH_CHECK( path( "c:/foo/.." ).normalize(), "c:/" ); + PATH_CHECK( path( "c:/foo/../" ).normalize(), "c:/." ); + PATH_CHECK( path( "c:/.." ).normalize(), "c:/.." ); + PATH_CHECK( path( "c:/../" ).normalize(), "c:/../." ); + PATH_CHECK( path( "c:/../.." ).normalize(), "c:/../.." ); + PATH_CHECK( path( "c:/../../" ).normalize(), "c:/../../." ); + PATH_CHECK( path( "c:/../foo" ).normalize(), "c:/../foo" ); + PATH_CHECK( path( "c:/../foo/" ).normalize(), "c:/../foo/." ); + PATH_CHECK( path( "c:/../../foo" ).normalize(), "c:/../../foo" ); + PATH_CHECK( path( "c:/../../foo/" ).normalize(), "c:/../../foo/." ); + PATH_CHECK( path( "c:/..foo" ).normalize(), "c:/..foo" ); } - catch ( const fs::filesystem_error & /*ex*/ ) + else // POSIX { -// std::cout << ex.what() << "\n"; + PATH_CHECK( path( "c:.." ).normalize(), "c:.." ); + PATH_CHECK( path( "c:foo/.." ).normalize(), "." ); + PATH_CHECK( path( "c:foo/../" ).normalize(), "./." ); + PATH_CHECK( path( "c:/foo/.." ).normalize(), "c:" ); + PATH_CHECK( path( "c:/foo/../" ).normalize(), "c:/." ); + PATH_CHECK( path( "c:/.." ).normalize(), "." ); + PATH_CHECK( path( "c:/../" ).normalize(), "./." ); + PATH_CHECK( path( "c:/../.." ).normalize(), ".." ); + PATH_CHECK( path( "c:/../../" ).normalize(), "../." ); + PATH_CHECK( path( "c:/../foo" ).normalize(), "foo" ); + PATH_CHECK( path( "c:/../foo/" ).normalize(), "foo/." ); + PATH_CHECK( path( "c:/../../foo" ).normalize(), "../foo" ); + PATH_CHECK( path( "c:/../../foo/" ).normalize(), "../foo/." ); + PATH_CHECK( path( "c:/..foo" ).normalize(), "c:/..foo" ); + } + } + + void exception_tests() + { + const std::string str_1("string-1"); + try { throw fs::filesystem_error( str_1, 12345 ); } + catch ( const fs::filesystem_error & ex ) + { + BOOST_CHECK( ex.what() == str_1 ); + BOOST_CHECK( ex.system_error() == 12345 ); + } + + try { throw fs::filesystem_path_error( str_1, "p1", "p2", 12345 ); } + catch ( const fs::filesystem_path_error & ex ) + { + BOOST_CHECK( ex.what() == str_1 ); + BOOST_CHECK( ex.system_error() == 12345 ); + BOOST_CHECK( ex.path1().string() == "p1" ); + BOOST_CHECK( ex.path2().string() == "p2" ); } } @@ -58,48 +200,60 @@ namespace { int test_main( int, char*[] ) { - std::string platform( BOOST_PLATFORM ); - // The choice of platform is make at runtime rather than compile-time // so that compile errors for all platforms will be detected even though // only the current platform is runtime tested. -# if defined( BOOST_POSIX ) - platform = "POSIX"; -# elif defined( BOOST_WINDOWS ) - platform = "Windows"; -# else - platform = ( platform == "Win32" || platform == "Win64" || platform == "Cygwin" ) + platform = ( platform == "Win32" || platform == "Win64" || platform == "Cygwin" ) ? "Windows" : "POSIX"; -# endif std::cout << "Platform is " << platform << '\n'; - BOOST_CHECK( path::default_name_check_writable() ); - BOOST_CHECK( path::default_name_check() == fs::portable_name ); - BOOST_CHECK( !path::default_name_check_writable() ); - bool default_name_check_threw = false; - try { path::default_name_check( fs::no_check ); } - catch ( const fs::filesystem_error & ) { default_name_check_threw = true; } - BOOST_CHECK( default_name_check_threw ); - BOOST_CHECK( path::default_name_check() == fs::portable_name ); - - path p1( "fe/fi/fo/fum" ); path p2( p1 ); path p3; BOOST_CHECK( p1.string() != p3.string() ); p3 = p2; + BOOST_CHECK( p1.string() == p3.string() ); -// p1.branch_path() = p2; // should fail -// *p1.begin() = ""; // should fail + path p4( "foobar" ); + BOOST_CHECK( p4.string() == "foobar" ); + p4 = p4; // self-assignment + BOOST_CHECK( p4.string() == "foobar" ); + + exception_tests(); // These verify various overloads don't cause compiler errors + + fs::exists( p1 ); fs::exists( "foo" ); fs::exists( std::string( "foo" ) ); - fs::exists( p1 ); + + fs::exists( p1 / path( "foo" ) ); + fs::exists( p1 / "foo" ); + fs::exists( p1 / std::string( "foo" ) ); + fs::exists( "foo" / p1 ); fs::exists( std::string( "foo" ) / p1 ); + p4 /= path( "foo" ); + p4 /= "foo"; + p4 /= std::string( "foo" ); + +# ifndef BOOST_NO_MEMBER_TEMPLATES + // check the path member templates + BOOST_CHECK( p4.string() == path( p4.string().begin(), p4.string().end() ).string() ); + + path p5; + p5 /= "foo/bar"; + PATH_CHECK( p5, "foo/bar" ); + char bf[]= "bar/foo"; + p5.assign( bf, bf + sizeof(bf) ); + PATH_CHECK( p5, bf ); + p5.append( bf, bf + sizeof(bf) ); + PATH_CHECK( p5, "bar/foo/bar/foo" ); +# endif + + BOOST_CHECK( p1 != p4 ); BOOST_CHECK( p1.string() == p2.string() ); BOOST_CHECK( p1.string() == p3.string() ); BOOST_CHECK( path( "foo" ).leaf() == "foo" ); @@ -113,24 +267,31 @@ int test_main( int, char*[] ) PATH_CHECK( "foo", "foo" ); PATH_CHECK( "f", "f" ); - PATH_CHECK( "foo/", "foo" ); - PATH_CHECK( path("foo/").normalize(), "foo" ); - PATH_CHECK( "f/", "f" ); - PATH_CHECK( path("f/").normalize(), "f" ); + + PATH_CHECK( "foo/", "foo/" ); + PATH_CHECK( "f/", "f/" ); + PATH_CHECK( "foo/..", "foo/.." ); + PATH_CHECK( "foo/../", "foo/../" ); + PATH_CHECK( "foo/bar/../..", "foo/bar/../.." ); + PATH_CHECK( "foo/bar/../../", "foo/bar/../../" ); PATH_CHECK( path("") / "foo", "foo" ); + PATH_CHECK( path("") / "foo/", "foo/" ); PATH_CHECK( path("foo") / "", "foo" ); PATH_CHECK( path( "/" ), "/" ); PATH_CHECK( path( "/" ) / "", "/" ); PATH_CHECK( path( "/f" ), "/f" ); - PATH_CHECK( path( "/foo" ).normalize(), "/foo" ); PATH_CHECK( "/foo", "/foo" ); PATH_CHECK( path("") / "/foo", "/foo" ); PATH_CHECK( path("/foo") / "", "/foo" ); - PATH_CHECK( "foo/", "foo" ); - PATH_CHECK( path("") / "foo/", "foo" ); - PATH_CHECK( path("foo") / "/", "foo" ); + if ( platform == "Windows" ) + { + PATH_CHECK( path("c:") / "foo", "c:foo" ); + PATH_CHECK( path("c:") / "/foo", "c:/foo" ); + } + + check_normalize(); if ( platform == "Windows" ) { @@ -139,7 +300,6 @@ int test_main( int, char*[] ) } PATH_CHECK( "foo/bar", "foo/bar" ); - PATH_CHECK( path( "foo/bar" ).normalize(), "foo/bar" ); PATH_CHECK( path("foo") / path("bar"), "foo/bar" ); // path arg PATH_CHECK( path("foo") / "bar", "foo/bar" ); // const char * arg PATH_CHECK( path("foo") / path("woo/bar").leaf(), "foo/bar" ); // const std::string & arg @@ -149,235 +309,134 @@ int test_main( int, char*[] ) PATH_CHECK( path("a") / "b", "a/b" ); PATH_CHECK( "..", ".." ); - PATH_CHECK( path("..").normalize(), ".." ); PATH_CHECK( path("..") / "", ".." ); PATH_CHECK( path("") / "..", ".." ); PATH_CHECK( "../..", "../.." ); - PATH_CHECK( path("../..").normalize(), "../.." ); PATH_CHECK( path("..") / ".." , "../.." ); - PATH_CHECK( "/..", "/" ); - PATH_CHECK( path("/..").normalize(), "/" ); - PATH_CHECK( path("/") / ".." , "/" ); + PATH_CHECK( "/..", "/.." ); + PATH_CHECK( path("/") / ".." , "/.." ); - PATH_CHECK( "/../..", "/" ); - PATH_CHECK( path("/../..").normalize(), "/" ); - PATH_CHECK( path("/..") / ".." , "/" ); + PATH_CHECK( "/../..", "/../.." ); + PATH_CHECK( path("/..") / ".." , "/../.." ); PATH_CHECK( "../foo", "../foo" ); - PATH_CHECK( path("../foo").normalize(), "../foo" ); PATH_CHECK( path("..") / "foo" , "../foo" ); PATH_CHECK( "foo/..", "foo/.." ); PATH_CHECK( path("foo") / ".." , "foo/.." ); - PATH_CHECK( path("foo/..").normalize(), "." ); - PATH_CHECK( (path("foo") / "..").normalize() , "." ); - PATH_CHECK( path( "foo/..bar", fs::no_check ), "foo/..bar" ); - PATH_CHECK( path("foo/..bar", fs::no_check ).normalize(), "foo/..bar" ); + PATH_CHECK( path( "foo/..bar"), "foo/..bar" ); PATH_CHECK( "../f", "../f" ); - PATH_CHECK( path("../f").normalize(), "../f" ); PATH_CHECK( path("..") / "f" , "../f" ); - PATH_CHECK( "/../f", "/f" ); - PATH_CHECK( path("/../f").normalize(), "/f" ); - PATH_CHECK( path("/..") / "f" , "/f" ); + PATH_CHECK( "/../f", "/../f" ); + PATH_CHECK( path("/..") / "f" , "/../f" ); PATH_CHECK( "f/..", "f/.." ); PATH_CHECK( path("f") / ".." , "f/.." ); - PATH_CHECK( path("f/..").normalize(), "." ); - PATH_CHECK( (path("f") / "..").normalize() , "." ); PATH_CHECK( "foo/../..", "foo/../.." ); - PATH_CHECK( path("foo/../..").normalize(), ".." ); PATH_CHECK( path("foo") / ".." / ".." , "foo/../.." ); PATH_CHECK( "foo/../../..", "foo/../../.." ); - PATH_CHECK( path("foo/../../..").normalize(), "../.." ); PATH_CHECK( path("foo") / ".." / ".." / ".." , "foo/../../.." ); PATH_CHECK( "foo/../bar", "foo/../bar" ); - PATH_CHECK( path("foo/../bar").normalize(), "bar" ); PATH_CHECK( path("foo") / ".." / "bar" , "foo/../bar" ); PATH_CHECK( "foo/bar/..", "foo/bar/.." ); - PATH_CHECK( path("foo/bar/..").normalize(), "foo" ); PATH_CHECK( path("foo") / "bar" / ".." , "foo/bar/.." ); PATH_CHECK( "foo/bar/../..", "foo/bar/../.." ); - PATH_CHECK( path("foo/bar/../..").normalize(), "." ); PATH_CHECK( path("foo") / "bar" / ".." / "..", "foo/bar/../.." ); PATH_CHECK( "foo/bar/../blah", "foo/bar/../blah" ); - PATH_CHECK( path("foo/bar/../blah").normalize(), "foo/blah" ); PATH_CHECK( path("foo") / "bar" / ".." / "blah", "foo/bar/../blah" ); PATH_CHECK( "f/../b", "f/../b" ); - PATH_CHECK( path("f/../b").normalize(), "b" ); PATH_CHECK( path("f") / ".." / "b" , "f/../b" ); PATH_CHECK( "f/b/..", "f/b/.." ); - PATH_CHECK( path("f/b/..").normalize(), "f" ); PATH_CHECK( path("f") / "b" / ".." , "f/b/.." ); PATH_CHECK( "f/b/../a", "f/b/../a" ); - PATH_CHECK( path("f/b/../a").normalize(), "f/a" ); PATH_CHECK( path("f") / "b" / ".." / "a", "f/b/../a" ); PATH_CHECK( "foo/bar/blah/../..", "foo/bar/blah/../.." ); - PATH_CHECK( path("foo/bar/blah/../..").normalize(), "foo" ); PATH_CHECK( path("foo") / "bar" / "blah" / ".." / "..", "foo/bar/blah/../.." ); PATH_CHECK( "foo/bar/blah/../../bletch", "foo/bar/blah/../../bletch" ); - PATH_CHECK( path("foo/bar/blah/../../bletch").normalize(), "foo/bletch" ); PATH_CHECK( path("foo") / "bar" / "blah" / ".." / ".." / "bletch", "foo/bar/blah/../../bletch" ); - PATH_CHECK( path("...", fs::portable_posix_name ), "..." ); - PATH_CHECK( path("....", fs::portable_posix_name ), "...." ); - PATH_CHECK( path("foo/...", fs::portable_posix_name ), "foo/..." ); - PATH_CHECK( path("foo/...", fs::portable_posix_name ).normalize(), "foo/..." ); - PATH_CHECK( path("abc.", fs::portable_posix_name ), "abc." ); - PATH_CHECK( path("abc..", fs::portable_posix_name ), "abc.." ); - PATH_CHECK( path("foo/abc.", fs::portable_posix_name ), "foo/abc." ); - PATH_CHECK( path("foo/abc..", fs::portable_posix_name ), "foo/abc.." ); + PATH_CHECK( "...", "..." ); + PATH_CHECK( "....", "...." ); + PATH_CHECK( "foo/...", "foo/..." ); + PATH_CHECK( "abc.", "abc." ); + PATH_CHECK( "abc..", "abc.." ); + PATH_CHECK( "foo/abc.", "foo/abc." ); + PATH_CHECK( "foo/abc..", "foo/abc.." ); - PATH_CHECK( path(".abc", fs::no_check), ".abc" ); + PATH_CHECK( path(".abc"), ".abc" ); PATH_CHECK( "a.c", "a.c" ); - PATH_CHECK( path("..abc", fs::no_check), "..abc" ); + PATH_CHECK( path("..abc"), "..abc" ); PATH_CHECK( "a..c", "a..c" ); - PATH_CHECK( path("foo/.abc", fs::no_check), "foo/.abc" ); + PATH_CHECK( path("foo/.abc"), "foo/.abc" ); PATH_CHECK( "foo/a.c", "foo/a.c" ); - PATH_CHECK( path("foo/..abc", fs::no_check), "foo/..abc" ); - PATH_CHECK( path("foo/..abc", fs::no_check).normalize(), "foo/..abc" ); + PATH_CHECK( path("foo/..abc"), "foo/..abc" ); PATH_CHECK( "foo/a..c", "foo/a..c" ); PATH_CHECK( ".", "." ); PATH_CHECK( path("") / ".", "." ); - PATH_CHECK( "./foo", "foo" ); - PATH_CHECK( path(".") / "foo", "foo" ); - PATH_CHECK( "./..", ".." ); - PATH_CHECK( path(".") / "..", ".." ); - PATH_CHECK( "./../foo", "../foo" ); - PATH_CHECK( "foo/.", "foo" ); - PATH_CHECK( path("foo") / ".", "foo" ); - PATH_CHECK( "../.", ".." ); - PATH_CHECK( path("..") / ".", ".." ); - PATH_CHECK( "./.", "." ); - PATH_CHECK( path(".") / ".", "." ); - PATH_CHECK( "././.", "." ); - PATH_CHECK( path(".") / "." / ".", "." ); - PATH_CHECK( "./foo/.", "foo" ); - PATH_CHECK( path(".") / "foo" / ".", "foo" ); - PATH_CHECK( "foo/./bar", "foo/bar" ); - PATH_CHECK( path("foo") / "." / "bar", "foo/bar" ); - PATH_CHECK( "foo/./.", "foo" ); - PATH_CHECK( path("foo") / "." / ".", "foo" ); - PATH_CHECK( "foo/./..", "foo/.." ); - PATH_CHECK( path("foo") / "." / "..", "foo/.." ); - PATH_CHECK( "foo/./../bar", "foo/../bar" ); - PATH_CHECK( "foo/../.", "foo/.." ); - PATH_CHECK( path(".") / "." / "..", ".." ); - PATH_CHECK( "././..", ".." ); - PATH_CHECK( path(".") / "." / "..", ".." ); - PATH_CHECK( "./../.", ".." ); - PATH_CHECK( path(".") / ".." / ".", ".." ); - PATH_CHECK( ".././.", ".." ); - PATH_CHECK( path("..") / "." / ".", ".." ); + PATH_CHECK( "./foo", "./foo" ); + PATH_CHECK( path(".") / "foo", "./foo" ); + PATH_CHECK( "./..", "./.." ); + PATH_CHECK( path(".") / "..", "./.." ); + PATH_CHECK( "./../foo", "./../foo" ); + PATH_CHECK( "foo/.", "foo/." ); + PATH_CHECK( path("foo") / ".", "foo/." ); + PATH_CHECK( "../.", "../." ); + PATH_CHECK( path("..") / ".", "../." ); + PATH_CHECK( "./.", "./." ); + PATH_CHECK( path(".") / ".", "./." ); + PATH_CHECK( "././.", "././." ); + PATH_CHECK( path(".") / "." / ".", "././." ); + PATH_CHECK( "./foo/.", "./foo/." ); + PATH_CHECK( path(".") / "foo" / ".", "./foo/." ); + PATH_CHECK( "foo/./bar", "foo/./bar" ); + PATH_CHECK( path("foo") / "." / "bar", "foo/./bar" ); + PATH_CHECK( "foo/./.", "foo/./." ); + PATH_CHECK( path("foo") / "." / ".", "foo/./." ); + PATH_CHECK( "foo/./..", "foo/./.." ); + PATH_CHECK( path("foo") / "." / "..", "foo/./.." ); + PATH_CHECK( "foo/./../bar", "foo/./../bar" ); + PATH_CHECK( "foo/../.", "foo/../." ); + PATH_CHECK( path(".") / "." / "..", "././.." ); + PATH_CHECK( "././..", "././.." ); + PATH_CHECK( path(".") / "." / "..", "././.." ); + PATH_CHECK( "./../.", "./../." ); + PATH_CHECK( path(".") / ".." / ".", "./../." ); + PATH_CHECK( ".././.", ".././." ); + PATH_CHECK( path("..") / "." / ".", ".././." ); - BOOST_CHECK( path("foo\\bar", fs::no_check).leaf() == "foo\\bar" ); - - BOOST_CHECK( fs::portable_posix_name(".") ); - BOOST_CHECK( fs::portable_posix_name("..") ); - BOOST_CHECK( fs::portable_posix_name("...") ); - BOOST_CHECK( fs::portable_posix_name("....") ); - BOOST_CHECK( fs::portable_posix_name("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789.-_") ); - BOOST_CHECK( !fs::portable_posix_name("F$O") ); + // iterator tests - BOOST_CHECK( fs::portable_name(".") ); - BOOST_CHECK( fs::portable_name("..") ); - BOOST_CHECK( fs::portable_name("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789.-_") ); - BOOST_CHECK( !fs::portable_name("A.") ); - BOOST_CHECK( fs::portable_name("A-") ); - BOOST_CHECK( !fs::portable_name(".A") ); - BOOST_CHECK( !fs::portable_name("-A") ); - BOOST_CHECK( !fs::portable_name("F$O") ); + path itr_ck = ""; + path::const_iterator itr = itr_ck.begin(); + BOOST_CHECK( itr == itr_ck.end() ); - - BOOST_CHECK( fs::portable_file_name(".") ); - BOOST_CHECK( fs::portable_file_name("..") ); - BOOST_CHECK( fs::portable_file_name("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789.-_") ); - BOOST_CHECK( fs::portable_file_name("0123456789.-_") ); - BOOST_CHECK( fs::portable_file_name("1234567890123456789012345678901") ); - BOOST_CHECK( !fs::portable_file_name("a.") ); - BOOST_CHECK( !fs::portable_file_name("a..b") ); - BOOST_CHECK( !fs::portable_file_name("a.bcde") ); - BOOST_CHECK( !fs::portable_file_name("a..cde") ); - BOOST_CHECK( !fs::portable_file_name("a.c.de") ); - BOOST_CHECK( !fs::portable_file_name("a.cd.e") ); - BOOST_CHECK( fs::portable_file_name("a.b") ); - BOOST_CHECK( fs::portable_file_name("a.bc") ); - BOOST_CHECK( fs::portable_file_name("a.bcd") ); - BOOST_CHECK( !fs::portable_file_name("A.") ); - BOOST_CHECK( fs::portable_file_name("A-") ); - BOOST_CHECK( !fs::portable_file_name(".A") ); - BOOST_CHECK( !fs::portable_file_name("-A") ); - BOOST_CHECK( !fs::portable_file_name("F$O") ); - - BOOST_CHECK( fs::portable_directory_name(".") ); - BOOST_CHECK( fs::portable_directory_name("..") ); - BOOST_CHECK( fs::portable_directory_name("ABCDEFGHIJKLMNOPQRSTUVWXYZ") ); - BOOST_CHECK( fs::portable_directory_name("abcdefghijklmnopqrstuvwxyz") ); - BOOST_CHECK( fs::portable_directory_name("0123456789-_") ); - BOOST_CHECK( fs::portable_directory_name("1234567890123456789012345678901") ); - BOOST_CHECK( !fs::portable_directory_name("a.") ); - BOOST_CHECK( !fs::portable_directory_name("a.bcde") ); - BOOST_CHECK( !fs::portable_directory_name("a..cde") ); - BOOST_CHECK( !fs::portable_directory_name("a.c.de") ); - BOOST_CHECK( !fs::portable_directory_name("a.cd.e") ); - BOOST_CHECK( !fs::portable_directory_name("a.b") ); - BOOST_CHECK( !fs::portable_directory_name("a.bc") ); - BOOST_CHECK( !fs::portable_directory_name("a.bcd") ); - BOOST_CHECK( !fs::portable_directory_name("A.") ); - BOOST_CHECK( fs::portable_directory_name("A-") ); - BOOST_CHECK( !fs::portable_directory_name(".A") ); - BOOST_CHECK( !fs::portable_directory_name("-A") ); - BOOST_CHECK( !fs::portable_directory_name("F$O") ); - - check_throw( "foo//bar" ); - check_throw( "foo\\bar" ); - check_throw( " " ); - check_throw( " foo" ); - check_throw( "foo " ); - check_throw( ">" ); - check_throw( "<" ); - check_throw( ":" ); - check_throw( "\"" ); - check_throw( "|" ); - - check_throw( "c:" ); - check_throw( "c:/" ); - check_throw( "//share" ); - check_throw( "prn:" ); - - path itr_ck( "/foo/bar" ); - path::iterator itr( itr_ck.begin() ); + itr_ck = "/"; + itr = itr_ck.begin(); BOOST_CHECK( *itr == std::string( "/" ) ); - BOOST_CHECK( *++itr == std::string( "foo" ) ); - BOOST_CHECK( *++itr == std::string( "bar" ) ); BOOST_CHECK( ++itr == itr_ck.end() ); - BOOST_CHECK( *--itr == std::string( "bar" ) ); - BOOST_CHECK( *--itr == std::string( "foo" ) ); BOOST_CHECK( *--itr == std::string( "/" ) ); - itr_ck = ""; - BOOST_CHECK( itr_ck.begin() == itr_ck.end() ); - - itr_ck = path( "/" ); - BOOST_CHECK( *itr_ck.begin() == std::string( "/" ) ); - BOOST_CHECK( next(itr_ck.begin()) == itr_ck.end() ); - BOOST_CHECK( *prior(itr_ck.end()) == std::string( "/" ) ); - BOOST_CHECK( prior(itr_ck.end()) == itr_ck.begin() ); + itr_ck = "foo"; + BOOST_CHECK( *itr_ck.begin() == std::string( "foo" ) ); + BOOST_CHECK( next( itr_ck.begin() ) == itr_ck.end() ); + BOOST_CHECK( *prior( itr_ck.end() ) == std::string( "foo" ) ); + BOOST_CHECK( prior( itr_ck.end() ) == itr_ck.begin() ); itr_ck = path( "/foo" ); BOOST_CHECK( *itr_ck.begin() == std::string( "/" ) ); @@ -388,11 +447,136 @@ int test_main( int, char*[] ) BOOST_CHECK( *prior(prior( itr_ck.end() )) == std::string( "/" ) ); BOOST_CHECK( prior(prior( itr_ck.end() )) == itr_ck.begin() ); - itr_ck = "foo"; - BOOST_CHECK( *itr_ck.begin() == std::string( "foo" ) ); - BOOST_CHECK( next( itr_ck.begin() ) == itr_ck.end() ); - BOOST_CHECK( *prior( itr_ck.end() ) == std::string( "foo" ) ); - BOOST_CHECK( prior( itr_ck.end() ) == itr_ck.begin() ); + itr_ck = "/foo/bar"; + itr = itr_ck.begin(); + BOOST_CHECK( *itr == std::string( "/" ) ); + BOOST_CHECK( *++itr == std::string( "foo" ) ); + BOOST_CHECK( *++itr == std::string( "bar" ) ); + BOOST_CHECK( ++itr == itr_ck.end() ); + CHECK_EQUAL( *--itr, "bar" ); + CHECK_EQUAL( *--itr, "foo" ); + CHECK_EQUAL( *--itr, "/" ); + + itr_ck = "../f"; // previously failed due to short name bug + itr = itr_ck.begin(); + CHECK_EQUAL( *itr, ".." ); + CHECK_EQUAL( *++itr, "f" ); + BOOST_CHECK( ++itr == itr_ck.end() ); + CHECK_EQUAL( *--itr, "f" ); + CHECK_EQUAL( *--itr, ".." ); + + // POSIX says treat "/foo/bar/" as "/foo/bar/." + itr_ck = "/foo/bar/"; + itr = itr_ck.begin(); + CHECK_EQUAL( *itr, "/" ); + CHECK_EQUAL( *++itr, "foo" ); + CHECK_EQUAL( *++itr, "bar" ); + CHECK_EQUAL( *++itr, "." ); + BOOST_CHECK( ++itr == itr_ck.end() ); + CHECK_EQUAL( *--itr, "." ); + CHECK_EQUAL( *--itr, "bar" ); + CHECK_EQUAL( *--itr, "foo" ); + CHECK_EQUAL( *--itr, "/" ); + + // POSIX says treat "/f/b/" as "/f/b/." + itr_ck = "/f/b/"; + itr = itr_ck.begin(); + CHECK_EQUAL( *itr, "/" ); + CHECK_EQUAL( *++itr, "f" ); + CHECK_EQUAL( *++itr, "b" ); + CHECK_EQUAL( *++itr, "." ); + BOOST_CHECK( ++itr == itr_ck.end() ); + CHECK_EQUAL( *--itr, "." ); + CHECK_EQUAL( *--itr, "b" ); + CHECK_EQUAL( *--itr, "f" ); + CHECK_EQUAL( *--itr, "/" ); + + itr_ck = "//net"; + itr = itr_ck.begin(); + // two leading slashes are permitted by POSIX (as implementation defined), + // while for Windows it is always well defined (as a network name) + CHECK_EQUAL( *itr, "//net" ); + BOOST_CHECK( ++itr == itr_ck.end() ); + CHECK_EQUAL( *--itr, "//net" ); + + itr_ck = "//net/"; + itr = itr_ck.begin(); + CHECK_EQUAL( *itr, "//net" ); + CHECK_EQUAL( *++itr, "/" ); + BOOST_CHECK( ++itr == itr_ck.end() ); + CHECK_EQUAL( *--itr, "/" ); + CHECK_EQUAL( *--itr, "//net" ); + + itr_ck = "//foo///bar///"; + itr = itr_ck.begin(); + CHECK_EQUAL( *itr, "//foo" ); + CHECK_EQUAL( *++itr, "/" ); + CHECK_EQUAL( *++itr, "bar" ); + CHECK_EQUAL( *++itr, "." ); + BOOST_CHECK( ++itr == itr_ck.end() ); + CHECK_EQUAL( *--itr, "." ); + CHECK_EQUAL( *--itr, "bar" ); + CHECK_EQUAL( *--itr, "/" ); + CHECK_EQUAL( *--itr, "//foo" ); + + itr_ck = "///foo///bar///"; + itr = itr_ck.begin(); + // three or more leading slashes are to be treated as a single slash + CHECK_EQUAL( *itr, "/" ); + CHECK_EQUAL( *++itr, "foo" ); + CHECK_EQUAL( *++itr, "bar" ); + CHECK_EQUAL( *++itr, "." ); + BOOST_CHECK( ++itr == itr_ck.end() ); + CHECK_EQUAL( *--itr, "." ); + CHECK_EQUAL( *--itr, "bar" ); + CHECK_EQUAL( *--itr, "foo" ); + CHECK_EQUAL( *--itr, "/" ); + + if ( platform == "Windows" ) + { + itr_ck = "c:/"; + itr = itr_ck.begin(); + CHECK_EQUAL( *itr, "c:" ); + CHECK_EQUAL( *++itr, "/" ); + BOOST_CHECK( ++itr == itr_ck.end() ); + CHECK_EQUAL( *--itr, "/" ); + CHECK_EQUAL( *--itr, "c:" ); + + itr_ck = "c:/foo"; + itr = itr_ck.begin(); + BOOST_CHECK( *itr == std::string( "c:" ) ); + BOOST_CHECK( *++itr == std::string( "/" ) ); + BOOST_CHECK( *++itr == std::string( "foo" ) ); + BOOST_CHECK( ++itr == itr_ck.end() ); + BOOST_CHECK( *--itr == std::string( "foo" ) ); + BOOST_CHECK( *--itr == std::string( "/" ) ); + BOOST_CHECK( *--itr == std::string( "c:" ) ); + + itr_ck = "c:foo"; + itr = itr_ck.begin(); + BOOST_CHECK( *itr == std::string( "c:" ) ); + BOOST_CHECK( *++itr == std::string( "foo" ) ); + BOOST_CHECK( ++itr == itr_ck.end() ); + BOOST_CHECK( *--itr == std::string( "foo" ) ); + BOOST_CHECK( *--itr == std::string( "c:" ) ); + + itr_ck = "c:foo/"; + itr = itr_ck.begin(); + BOOST_CHECK( *itr == std::string( "c:" ) ); + BOOST_CHECK( *++itr == std::string( "foo" ) ); + BOOST_CHECK( *++itr == std::string( "." ) ); + BOOST_CHECK( ++itr == itr_ck.end() ); + BOOST_CHECK( *--itr == std::string( "." ) ); + BOOST_CHECK( *--itr == std::string( "foo" ) ); + BOOST_CHECK( *--itr == std::string( "c:" ) ); + } + else + { + itr_ck = "///"; + itr = itr_ck.begin(); + CHECK_EQUAL( *itr, "/" ); + BOOST_CHECK( ++itr == itr_ck.end() ); + } path p; @@ -429,6 +613,40 @@ int test_main( int, char*[] ) else BOOST_CHECK( !p.is_complete() ); + p = "//"; + CHECK_EQUAL( p.relative_path().string(), "" ); + CHECK_EQUAL( p.branch_path().string(), "" ); + CHECK_EQUAL( p.leaf(), "//" ); + CHECK_EQUAL( p.root_name(), "//" ); + CHECK_EQUAL( p.root_directory(), "" ); + CHECK_EQUAL( p.root_path().string(), "//" ); + BOOST_CHECK( p.has_root_path() ); + BOOST_CHECK( p.has_root_name() ); + BOOST_CHECK( !p.has_root_directory() ); + BOOST_CHECK( !p.has_relative_path() ); + BOOST_CHECK( p.has_leaf() ); + BOOST_CHECK( !p.has_branch_path() ); + BOOST_CHECK( !p.is_complete() ); + + + p = "///"; + CHECK_EQUAL( p.relative_path().string(), "" ); + CHECK_EQUAL( p.branch_path().string(), "" ); + CHECK_EQUAL( p.leaf(), "/" ); + CHECK_EQUAL( p.root_name(), "" ); + CHECK_EQUAL( p.root_directory(), "/" ); + CHECK_EQUAL( p.root_path().string(), "/" ); + BOOST_CHECK( p.has_root_path() ); + BOOST_CHECK( !p.has_root_name() ); + BOOST_CHECK( p.has_root_directory() ); + BOOST_CHECK( !p.has_relative_path() ); + BOOST_CHECK( p.has_leaf() ); + BOOST_CHECK( !p.has_branch_path() ); + if ( platform == "POSIX" ) + BOOST_CHECK( p.is_complete() ); + else + BOOST_CHECK( !p.is_complete() ); + p = "."; BOOST_CHECK( p.relative_path().string() == "." ); BOOST_CHECK( p.branch_path().string() == "" ); @@ -475,12 +693,48 @@ int test_main( int, char*[] ) BOOST_CHECK( !p.is_complete() ); p = "/foo"; - BOOST_CHECK( p.relative_path().string() == "foo" ); - BOOST_CHECK( p.branch_path().string() == "/" ); - BOOST_CHECK( p.leaf() == "foo" ); - BOOST_CHECK( p.root_name() == "" ); - BOOST_CHECK( p.root_directory() == "/" ); - BOOST_CHECK( p.root_path().string() == "/" ); + CHECK_EQUAL( p.relative_path().string(), "foo" ); + CHECK_EQUAL( p.branch_path().string(), "/" ); + CHECK_EQUAL( p.leaf(), "foo" ); + CHECK_EQUAL( p.root_name(), "" ); + CHECK_EQUAL( p.root_directory(), "/" ); + CHECK_EQUAL( p.root_path().string(), "/" ); + BOOST_CHECK( p.has_root_path() ); + BOOST_CHECK( !p.has_root_name() ); + BOOST_CHECK( p.has_root_directory() ); + BOOST_CHECK( p.has_relative_path() ); + BOOST_CHECK( p.has_leaf() ); + BOOST_CHECK( p.has_branch_path() ); + if ( platform == "POSIX" ) + BOOST_CHECK( p.is_complete() ); + else + BOOST_CHECK( !p.is_complete() ); + + p = "/foo/"; + CHECK_EQUAL( p.relative_path().string(), "foo/" ); + CHECK_EQUAL( p.branch_path().string(), "/foo" ); + CHECK_EQUAL( p.leaf(), "." ); + CHECK_EQUAL( p.root_name(), "" ); + CHECK_EQUAL( p.root_directory(), "/" ); + CHECK_EQUAL( p.root_path().string(), "/" ); + BOOST_CHECK( p.has_root_path() ); + BOOST_CHECK( !p.has_root_name() ); + BOOST_CHECK( p.has_root_directory() ); + BOOST_CHECK( p.has_relative_path() ); + BOOST_CHECK( p.has_leaf() ); + BOOST_CHECK( p.has_branch_path() ); + if ( platform == "POSIX" ) + BOOST_CHECK( p.is_complete() ); + else + BOOST_CHECK( !p.is_complete() ); + + p = "///foo"; + CHECK_EQUAL( p.relative_path().string(), "foo" ); + CHECK_EQUAL( p.branch_path().string(), "/" ); + CHECK_EQUAL( p.leaf(), "foo" ); + CHECK_EQUAL( p.root_name(), "" ); + CHECK_EQUAL( p.root_directory(), "/" ); + CHECK_EQUAL( p.root_path().string(), "/" ); BOOST_CHECK( p.has_root_path() ); BOOST_CHECK( !p.has_root_name() ); BOOST_CHECK( p.has_root_directory() ); @@ -522,6 +776,21 @@ int test_main( int, char*[] ) BOOST_CHECK( p.has_branch_path() ); BOOST_CHECK( !p.is_complete() ); + p = "..///foo"; + CHECK_EQUAL( p.relative_path().string(), "..///foo" ); + CHECK_EQUAL( p.branch_path().string(), ".." ); + CHECK_EQUAL( p.leaf(), "foo" ); + CHECK_EQUAL( p.root_name(), "" ); + CHECK_EQUAL( p.root_directory(), "" ); + CHECK_EQUAL( p.root_path().string(), "" ); + BOOST_CHECK( !p.has_root_path() ); + BOOST_CHECK( !p.has_root_name() ); + BOOST_CHECK( !p.has_root_directory() ); + BOOST_CHECK( p.has_relative_path() ); + BOOST_CHECK( p.has_leaf() ); + BOOST_CHECK( p.has_branch_path() ); + BOOST_CHECK( !p.is_complete() ); + p = "/foo/bar"; BOOST_CHECK( p.relative_path().string() == "foo/bar" ); BOOST_CHECK( p.branch_path().string() == "/foo" ); @@ -540,75 +809,121 @@ int test_main( int, char*[] ) else BOOST_CHECK( !p.is_complete() ); - // decomposition and query functions must work even for paths which - // do not pass the default name_check - p = path( "/", fs::no_check ); - BOOST_CHECK( p.relative_path().string() == "" ); - BOOST_CHECK( p.branch_path().string() == "/<" ); - BOOST_CHECK( p.leaf() == ">" ); - BOOST_CHECK( p.root_name() == "" ); - BOOST_CHECK( p.root_directory() == "/" ); - BOOST_CHECK( p.root_path().string() == "/" ); + // Both POSIX and Windows allow two leading slashs + // (POSIX meaning is implementation defined) + PATH_CHECK( path( "//resource" ), "//resource" ); + PATH_CHECK( path( "//resource/" ), "//resource/" ); + PATH_CHECK( path( "//resource/foo" ), "//resource/foo" ); + + p = path( "//net" ); + CHECK_EQUAL( p.string(), "//net" ); + CHECK_EQUAL( p.relative_path().string(), "" ); + CHECK_EQUAL( p.branch_path().string(), "" ); + CHECK_EQUAL( p.leaf(), "//net" ); + CHECK_EQUAL( p.root_name(), "//net" ); + CHECK_EQUAL( p.root_directory(), "" ); + CHECK_EQUAL( p.root_path().string(), "//net" ); BOOST_CHECK( p.has_root_path() ); - BOOST_CHECK( !p.has_root_name() ); + BOOST_CHECK( p.has_root_name() ); + BOOST_CHECK( !p.has_root_directory() ); + BOOST_CHECK( !p.has_relative_path() ); + BOOST_CHECK( p.has_leaf() ); + BOOST_CHECK( !p.has_branch_path() ); + BOOST_CHECK( !p.is_complete() ); + + p = path( "//net/" ); + BOOST_CHECK( p.relative_path().string() == "" ); + BOOST_CHECK( p.branch_path().string() == "//net" ); + BOOST_CHECK( p.leaf() == "/" ); + BOOST_CHECK( p.root_name() == "//net" ); + BOOST_CHECK( p.root_directory() == "/" ); + BOOST_CHECK( p.root_path().string() == "//net/" ); + BOOST_CHECK( p.has_root_path() ); + BOOST_CHECK( p.has_root_name() ); + BOOST_CHECK( p.has_root_directory() ); + BOOST_CHECK( !p.has_relative_path() ); + BOOST_CHECK( p.has_leaf() ); + BOOST_CHECK( p.has_branch_path() ); + BOOST_CHECK( p.is_complete() ); + + p = path( "//net/foo" ); + BOOST_CHECK( p.relative_path().string() == "foo" ); + BOOST_CHECK( p.branch_path().string() == "//net/" ); + BOOST_CHECK( p.leaf() == "foo" ); + BOOST_CHECK( p.root_name() == "//net" ); + BOOST_CHECK( p.root_directory() == "/" ); + BOOST_CHECK( p.root_path().string() == "//net/" ); + BOOST_CHECK( p.has_root_path() ); + BOOST_CHECK( p.has_root_name() ); BOOST_CHECK( p.has_root_directory() ); BOOST_CHECK( p.has_relative_path() ); BOOST_CHECK( p.has_leaf() ); BOOST_CHECK( p.has_branch_path() ); - if ( platform == "POSIX" ) - BOOST_CHECK( p.is_complete() ); - else - BOOST_CHECK( !p.is_complete() ); + BOOST_CHECK( p.is_complete() ); + + p = path( "//net///foo" ); + CHECK_EQUAL( p.relative_path().string(), "foo" ); + CHECK_EQUAL( p.branch_path().string(), "//net/" ); + CHECK_EQUAL( p.leaf(), "foo" ); + CHECK_EQUAL( p.root_name(), "//net" ); + CHECK_EQUAL( p.root_directory(), "/" ); + CHECK_EQUAL( p.root_path().string(), "//net/" ); + BOOST_CHECK( p.has_root_path() ); + BOOST_CHECK( p.has_root_name() ); + BOOST_CHECK( p.has_root_directory() ); + BOOST_CHECK( p.has_relative_path() ); + BOOST_CHECK( p.has_leaf() ); + BOOST_CHECK( p.has_branch_path() ); + BOOST_CHECK( p.is_complete() ); if ( platform == "Windows" ) { - PATH_CHECK( path( "\\", fs::native ), "/" ); - PATH_CHECK( path( "\\f", fs::native ), "/f" ); - PATH_CHECK( path( "\\foo", fs::native ), "/foo" ); - PATH_CHECK( path( "foo\\bar", fs::native ), "foo/bar" ); - PATH_CHECK( path( "foo bar", fs::native ), "foo bar" ); - PATH_CHECK( path( "c:", fs::native ), "c:" ); - PATH_CHECK( path( "c:/", fs::native ), "c:/" ); - PATH_CHECK( path( "c:.", fs::native ), "c:" ); - PATH_CHECK( path( "c:./foo", fs::native ), "c:foo" ); - PATH_CHECK( path( "c:.\\foo", fs::native ), "c:foo" ); - PATH_CHECK( path( "c:..", fs::native ), "c:.." ); - PATH_CHECK( path( "c:..", fs::native ).normalize(), "c:.." ); - PATH_CHECK( path( "c:/.", fs::native ), "c:/" ); - PATH_CHECK( path( "c:/..", fs::native ), "c:/" ); - PATH_CHECK( path( "c:/..", fs::native ).normalize(), "c:/" ); - PATH_CHECK( path( "c:/../", fs::native ), "c:/" ); - PATH_CHECK( path( "c:\\..\\", fs::native ), "c:/" ); - PATH_CHECK( path( "c:/../", fs::native ).normalize(), "c:/" ); - PATH_CHECK( path( "c:/../..", fs::native ), "c:/" ); - PATH_CHECK( path( "c:/../..", fs::native ).normalize(), "c:/" ); - PATH_CHECK( path( "c:/../foo", fs::native ), "c:/foo" ); - PATH_CHECK( path( "c:\\..\\foo", fs::native ), "c:/foo" ); - PATH_CHECK( path( "c:../foo", fs::native ), "c:../foo" ); - PATH_CHECK( path( "c:..\\foo", fs::native ), "c:../foo" ); - PATH_CHECK( path( "c:/../foo", fs::native ).normalize(), "c:/foo" ); - PATH_CHECK( path( "c:/../../foo", fs::native ), "c:/foo" ); - PATH_CHECK( path( "c:\\..\\..\\foo", fs::native ), "c:/foo" ); - PATH_CHECK( path( "c:/../../foo", fs::native ).normalize(), "c:/foo" ); - PATH_CHECK( path( "c:foo/..", fs::native ), "c:foo/.." ); - PATH_CHECK( path( "c:foo/..", fs::native ).normalize(), "c:" ); - PATH_CHECK( path( "c:/foo/..", fs::native ), "c:/foo/.." ); - PATH_CHECK( path( "c:/foo/..", fs::native ).normalize(), "c:/" ); - PATH_CHECK( path( "c:/..foo", fs::native ), "c:/..foo" ); - PATH_CHECK( path( "c:/..foo", fs::native ).normalize(), "c:/..foo" ); - PATH_CHECK( path( "c:foo", fs::native ), "c:foo" ); - PATH_CHECK( path( "c:/foo", fs::native ), "c:/foo" ); - PATH_CHECK( path( "c++", fs::native ), "c++" ); - PATH_CHECK( path( "//share", fs::native ), "//share" ); - PATH_CHECK( path( "//share/", fs::native ), "//share/" ); - PATH_CHECK( path( "//share/foo", fs::native ), "//share/foo" ); - PATH_CHECK( path( "\\\\share", fs::native ), "//share" ); - PATH_CHECK( path( "\\\\share\\", fs::native ), "//share/" ); - PATH_CHECK( path( "\\\\share\\foo", fs::native ), "//share/foo" ); - PATH_CHECK( path( "c:/foo", fs::native ), "c:/foo" ); - PATH_CHECK( path( "prn:", fs::native ), "prn:" ); + DIR_CHECK( path( "/foo/bar/" ), "\\foo\\bar\\" ); + DIR_CHECK( path( "//foo//bar//" ), "\\\\foo\\bar\\\\" ); + DIR_CHECK( path( "///foo///bar///" ), "\\foo\\\\\\bar\\\\\\" ); - p = path( "c:", fs::native ); + DIR_CHECK( path( "\\/foo\\/bar\\/" ), "\\\\foo\\bar\\\\" ); + DIR_CHECK( path( "\\//foo\\//bar\\//" ), "\\foo\\\\\\bar\\\\\\" ); + + + DIR_CHECK( path( "\\foo\\bar\\" ), "\\foo\\bar\\" ); + DIR_CHECK( path( "\\\\foo\\\\bar\\\\" ), "\\\\foo\\bar\\\\" ); + DIR_CHECK( path( "\\\\\\foo\\\\\\bar\\\\\\" ), "\\foo\\\\\\bar\\\\\\" ); + + PATH_CHECK( path( "\\" ), "/" ); + PATH_CHECK( path( "\\f" ), "/f" ); + PATH_CHECK( path( "\\foo" ), "/foo" ); + PATH_CHECK( path( "foo\\bar" ), "foo/bar" ); + PATH_CHECK( path( "foo bar" ), "foo bar" ); + PATH_CHECK( path( "c:" ), "c:" ); + PATH_CHECK( path( "c:/" ), "c:/" ); + PATH_CHECK( path( "c:." ), "c:." ); + PATH_CHECK( path( "c:./foo" ), "c:./foo" ); + PATH_CHECK( path( "c:.\\foo" ), "c:./foo" ); + PATH_CHECK( path( "c:.." ), "c:.." ); + PATH_CHECK( path( "c:/." ), "c:/." ); + PATH_CHECK( path( "c:/.." ), "c:/.." ); + PATH_CHECK( path( "c:/../" ), "c:/../" ); + PATH_CHECK( path( "c:\\..\\" ), "c:/../" ); + PATH_CHECK( path( "c:/../.." ), "c:/../.." ); + PATH_CHECK( path( "c:/../foo" ), "c:/../foo" ); + PATH_CHECK( path( "c:\\..\\foo" ), "c:/../foo" ); + PATH_CHECK( path( "c:../foo" ), "c:../foo" ); + PATH_CHECK( path( "c:..\\foo" ), "c:../foo" ); + PATH_CHECK( path( "c:/../../foo" ), "c:/../../foo" ); + PATH_CHECK( path( "c:\\..\\..\\foo" ), "c:/../../foo" ); + PATH_CHECK( path( "c:foo/.." ), "c:foo/.." ); + PATH_CHECK( path( "c:/foo/.." ), "c:/foo/.." ); + PATH_CHECK( path( "c:/..foo" ), "c:/..foo" ); + PATH_CHECK( path( "c:foo" ), "c:foo" ); + PATH_CHECK( path( "c:/foo" ), "c:/foo" ); + PATH_CHECK( path( "\\\\netname" ), "//netname" ); + PATH_CHECK( path( "\\\\netname\\" ), "//netname/" ); + PATH_CHECK( path( "\\\\netname\\foo" ), "//netname/foo" ); + PATH_CHECK( path( "c:/foo" ), "c:/foo" ); + PATH_CHECK( path( "prn:" ), "prn:" ); + + p = path( "c:" ); BOOST_CHECK( p.relative_path().string() == "" ); BOOST_CHECK( p.branch_path().string() == "" ); BOOST_CHECK( p.leaf() == "c:" ); @@ -623,7 +938,7 @@ int test_main( int, char*[] ) BOOST_CHECK( !p.has_branch_path() ); BOOST_CHECK( !p.is_complete() ); - p = path( "c:foo", fs::native ); + p = path( "c:foo" ); BOOST_CHECK( p.relative_path().string() == "foo" ); BOOST_CHECK( p.branch_path().string() == "c:" ); BOOST_CHECK( p.leaf() == "foo" ); @@ -638,7 +953,7 @@ int test_main( int, char*[] ) BOOST_CHECK( p.has_branch_path() ); BOOST_CHECK( !p.is_complete() ); - p = path( "c:/", fs::native ); + p = path( "c:/" ); BOOST_CHECK( p.relative_path().string() == "" ); BOOST_CHECK( p.branch_path().string() == "c:" ); BOOST_CHECK( p.leaf() == "/" ); @@ -653,7 +968,7 @@ int test_main( int, char*[] ) BOOST_CHECK( p.has_branch_path() ); BOOST_CHECK( p.is_complete() ); - p = path( "c:..", fs::native ); + p = path( "c:.." ); BOOST_CHECK( p.relative_path().string() == ".." ); BOOST_CHECK( p.branch_path().string() == "c:" ); BOOST_CHECK( p.leaf() == ".." ); @@ -668,13 +983,13 @@ int test_main( int, char*[] ) BOOST_CHECK( p.has_branch_path() ); BOOST_CHECK( !p.is_complete() ); - p = path( "c:/foo", fs::native ); - BOOST_CHECK( p.relative_path().string() == "foo" ); - BOOST_CHECK( p.branch_path().string() == "c:/" ); - BOOST_CHECK( p.leaf() == "foo" ); - BOOST_CHECK( p.root_name() == "c:" ); - BOOST_CHECK( p.root_directory() == "/" ); - BOOST_CHECK( p.root_path().string() == "c:/" ); + p = path( "c:/foo" ); + CHECK_EQUAL( p.relative_path().string(), "foo" ); + CHECK_EQUAL( p.branch_path().string(), "c:/" ); + CHECK_EQUAL( p.leaf(), "foo" ); + CHECK_EQUAL( p.root_name(), "c:" ); + CHECK_EQUAL( p.root_directory(), "/" ); + CHECK_EQUAL( p.root_path().string(), "c:/" ); BOOST_CHECK( p.has_root_path() ); BOOST_CHECK( p.has_root_name() ); BOOST_CHECK( p.has_root_directory() ); @@ -683,55 +998,7 @@ int test_main( int, char*[] ) BOOST_CHECK( p.has_branch_path() ); BOOST_CHECK( p.is_complete() ); -/* Commented out until the semantics of //share are clearer. - - p = path( "//share", fs::native ); - BOOST_CHECK( p.string() == "//share" ); - BOOST_CHECK( p.relative_path().string() == "" ); - BOOST_CHECK( p.branch_path().string() == "" ); - BOOST_CHECK( p.leaf() == "//share" ); - BOOST_CHECK( p.root_name() == "//share" ); - BOOST_CHECK( p.root_directory() == "/" ); - BOOST_CHECK( p.root_path().string() == "//share/" ); - BOOST_CHECK( p.has_root_path() ); - BOOST_CHECK( p.has_root_name() ); - BOOST_CHECK( !p.has_root_directory() ); - BOOST_CHECK( !p.has_relative_path() ); - BOOST_CHECK( p.has_leaf() ); - BOOST_CHECK( !p.has_branch_path() ); - BOOST_CHECK( !p.is_complete() ); -*/ - p = path( "//share/", fs::native ); - BOOST_CHECK( p.relative_path().string() == "" ); - BOOST_CHECK( p.branch_path().string() == "//share" ); - BOOST_CHECK( p.leaf() == "/" ); - BOOST_CHECK( p.root_name() == "//share" ); - BOOST_CHECK( p.root_directory() == "/" ); - BOOST_CHECK( p.root_path().string() == "//share/" ); - BOOST_CHECK( p.has_root_path() ); - BOOST_CHECK( p.has_root_name() ); - BOOST_CHECK( p.has_root_directory() ); - BOOST_CHECK( !p.has_relative_path() ); - BOOST_CHECK( p.has_leaf() ); - BOOST_CHECK( p.has_branch_path() ); - BOOST_CHECK( p.is_complete() ); - - p = path( "//share/foo", fs::native ); - BOOST_CHECK( p.relative_path().string() == "foo" ); - BOOST_CHECK( p.branch_path().string() == "//share/" ); - BOOST_CHECK( p.leaf() == "foo" ); - BOOST_CHECK( p.root_name() == "//share" ); - BOOST_CHECK( p.root_directory() == "/" ); - BOOST_CHECK( p.root_path().string() == "//share/" ); - BOOST_CHECK( p.has_root_path() ); - BOOST_CHECK( p.has_root_name() ); - BOOST_CHECK( p.has_root_directory() ); - BOOST_CHECK( p.has_relative_path() ); - BOOST_CHECK( p.has_leaf() ); - BOOST_CHECK( p.has_branch_path() ); - BOOST_CHECK( p.is_complete() ); - - p = path( "prn:", fs::native ); + p = path( "prn:" ); BOOST_CHECK( p.relative_path().string() == "" ); BOOST_CHECK( p.branch_path().string() == "" ); BOOST_CHECK( p.leaf() == "prn:" ); @@ -744,15 +1011,30 @@ int test_main( int, char*[] ) BOOST_CHECK( !p.has_relative_path() ); BOOST_CHECK( p.has_leaf() ); BOOST_CHECK( !p.has_branch_path() ); + BOOST_CHECK( !p.is_complete() ); + + p = path( "\\\\net\\\\\\foo" ); + CHECK_EQUAL( p.relative_path().string(), "foo" ); + CHECK_EQUAL( p.branch_path().string(), "//net/" ); + CHECK_EQUAL( p.leaf(), "foo" ); + CHECK_EQUAL( p.root_name(), "//net" ); + CHECK_EQUAL( p.root_directory(), "/" ); + CHECK_EQUAL( p.root_path().string(), "//net/" ); + BOOST_CHECK( p.has_root_path() ); + BOOST_CHECK( p.has_root_name() ); + BOOST_CHECK( p.has_root_directory() ); + BOOST_CHECK( p.has_relative_path() ); + BOOST_CHECK( p.has_leaf() ); + BOOST_CHECK( p.has_branch_path() ); BOOST_CHECK( p.is_complete() ); - itr_ck = path( "c:", fs::native ); + itr_ck = path( "c:" ); BOOST_CHECK( *itr_ck.begin() == std::string( "c:" ) ); BOOST_CHECK( next( itr_ck.begin() ) == itr_ck.end() ); BOOST_CHECK( prior( itr_ck.end() ) == itr_ck.begin() ); BOOST_CHECK( *prior( itr_ck.end() ) == std::string( "c:" ) ); - itr_ck = path( "c:/", fs::native ); + itr_ck = path( "c:/" ); BOOST_CHECK( *itr_ck.begin() == std::string( "c:" ) ); BOOST_CHECK( *next( itr_ck.begin() ) == std::string( "/" ) ); BOOST_CHECK( next( next( itr_ck.begin() )) == itr_ck.end() ); @@ -760,7 +1042,7 @@ int test_main( int, char*[] ) BOOST_CHECK( *prior( itr_ck.end() ) == std::string( "/" ) ); BOOST_CHECK( *prior( prior( itr_ck.end() )) == std::string( "c:" ) ); - itr_ck = path( "c:foo", fs::native ); + itr_ck = path( "c:foo" ); BOOST_CHECK( *itr_ck.begin() == std::string( "c:" ) ); BOOST_CHECK( *next( itr_ck.begin() ) == std::string( "foo" ) ); BOOST_CHECK( next(next( itr_ck.begin() )) == itr_ck.end() ); @@ -768,7 +1050,7 @@ int test_main( int, char*[] ) BOOST_CHECK( *prior( itr_ck.end() ) == std::string( "foo" ) ); BOOST_CHECK( *prior(prior( itr_ck.end() )) == std::string( "c:" ) ); - itr_ck = path( "c:/foo", fs::native ); + itr_ck = path( "c:/foo" ); BOOST_CHECK( *itr_ck.begin() == std::string( "c:" ) ); BOOST_CHECK( *next( itr_ck.begin() ) == std::string( "/" ) ); BOOST_CHECK( *next( next( itr_ck.begin() )) == std::string( "foo" ) ); @@ -778,31 +1060,31 @@ int test_main( int, char*[] ) BOOST_CHECK( *prior( prior( itr_ck.end() )) == std::string( "/" ) ); BOOST_CHECK( *prior( prior( prior( itr_ck.end() ))) == std::string( "c:" ) ); - itr_ck = path( "//share", fs::native ); - BOOST_CHECK( *itr_ck.begin() == std::string( "//share" ) ); + itr_ck = path( "//net" ); + BOOST_CHECK( *itr_ck.begin() == std::string( "//net" ) ); BOOST_CHECK( next( itr_ck.begin() ) == itr_ck.end() ); BOOST_CHECK( prior( itr_ck.end() ) == itr_ck.begin() ); - BOOST_CHECK( *prior( itr_ck.end() ) == std::string( "//share" ) ); + BOOST_CHECK( *prior( itr_ck.end() ) == std::string( "//net" ) ); - itr_ck = path( "//share/", fs::native ); - BOOST_CHECK( *itr_ck.begin() == std::string( "//share" ) ); - BOOST_CHECK( *next( itr_ck.begin() ) == std::string( "/" ) ); + itr_ck = path( "//net/" ); + CHECK_EQUAL( *itr_ck.begin(), "//net" ); + CHECK_EQUAL( *next( itr_ck.begin() ), "/" ); BOOST_CHECK( next(next( itr_ck.begin() )) == itr_ck.end() ); BOOST_CHECK( prior(prior( itr_ck.end() )) == itr_ck.begin() ); - BOOST_CHECK( *prior( itr_ck.end() ) == std::string( "/" ) ); - BOOST_CHECK( *prior(prior( itr_ck.end() )) == std::string( "//share" ) ); + CHECK_EQUAL( *prior( itr_ck.end() ), "/" ); + CHECK_EQUAL( *prior(prior( itr_ck.end() )), "//net" ); - itr_ck = path( "//share/foo", fs::native ); - BOOST_CHECK( *itr_ck.begin() == std::string( "//share" ) ); + itr_ck = path( "//net/foo" ); + BOOST_CHECK( *itr_ck.begin() == std::string( "//net" ) ); BOOST_CHECK( *next( itr_ck.begin() ) == std::string( "/" ) ); BOOST_CHECK( *next(next( itr_ck.begin() )) == std::string( "foo" ) ); BOOST_CHECK( next(next(next( itr_ck.begin() ))) == itr_ck.end() ); BOOST_CHECK( prior(prior(prior( itr_ck.end() ))) == itr_ck.begin() ); BOOST_CHECK( *prior( itr_ck.end() ) == std::string( "foo" ) ); BOOST_CHECK( *prior(prior( itr_ck.end() )) == std::string( "/" ) ); - BOOST_CHECK( *prior(prior(prior( itr_ck.end() ))) == std::string( "//share" ) ); + BOOST_CHECK( *prior(prior(prior( itr_ck.end() ))) == std::string( "//net" ) ); - itr_ck = path( "prn:", fs::native ); + itr_ck = path( "prn:" ); BOOST_CHECK( *itr_ck.begin() == std::string( "prn:" ) ); BOOST_CHECK( next( itr_ck.begin() ) == itr_ck.end() ); BOOST_CHECK( prior( itr_ck.end() ) == itr_ck.begin() ); @@ -811,22 +1093,78 @@ int test_main( int, char*[] ) else { // POSIX - p = path( "/usr/local/bin:/usr/bin:/bin", fs::no_check ); - BOOST_CHECK( p.native_file_string() == "/usr/local/bin:/usr/bin:/bin" ); + DIR_CHECK( path( "/foo/bar/" ), "/foo/bar/" ); + DIR_CHECK( path( "//foo//bar//" ), "//foo//bar//" ); + DIR_CHECK( path( "///foo///bar///" ), "///foo///bar///" ); + + p = path( "/usr/local/bin:/usr/bin:/bin" ); + BOOST_CHECK( p.file_string() == "/usr/local/bin:/usr/bin:/bin" ); } // POSIX - // test relational operators + // test non-member functions, particularly operator overloads path e, e2; - path a( "a" ); - path a2( "a" ); - path b( "b" ); + std::string es, es2; + char ecs[] = ""; + char ecs2[] = ""; + + char acs[] = "a"; + std::string as(acs); + path a( as ); + + char acs2[] = "a"; + std::string as2(acs2); + path a2( as2 ); + + char bcs[] = "b"; + std::string bs(bcs); + path b( bs ); + + // swap + a.swap( b ); + BOOST_CHECK( a.string() == "b" ); + BOOST_CHECK( b.string() == "a" ); + fs::swap( a, b ); + BOOST_CHECK( a.string() == "a" ); + BOOST_CHECK( b.string() == "b" ); + + // probe operator / + BOOST_CHECK( (b / a).string() == "b/a" ); + BOOST_CHECK( (bs / a).string() == "b/a" ); + BOOST_CHECK( (bcs / a).string() == "b/a" ); + BOOST_CHECK( (b / as).string() == "b/a" ); + BOOST_CHECK( (b / acs).string() == "b/a" ); // probe operator < BOOST_CHECK( !(e < e2) ); + BOOST_CHECK( !(es < e2) ); + BOOST_CHECK( !(ecs < e2) ); + BOOST_CHECK( !(e < es2) ); + BOOST_CHECK( !(e < ecs2) ); + BOOST_CHECK( e < a ); + BOOST_CHECK( es < a ); + BOOST_CHECK( ecs < a ); + BOOST_CHECK( e < as ); + BOOST_CHECK( e < acs ); + BOOST_CHECK( a < b ); + BOOST_CHECK( as < b ); + BOOST_CHECK( acs < b ); + BOOST_CHECK( a < bs ); + BOOST_CHECK( a < bcs ); + BOOST_CHECK( !(a < a2) ); + BOOST_CHECK( !(as < a2) ); + BOOST_CHECK( !(acs < a2) ); + BOOST_CHECK( !(a < as2) ); + BOOST_CHECK( !(a < acs2) ); + + // make sure basic_path overloads don't conflict with std::string overloads + + BOOST_CHECK( !(as < as) ); + BOOST_CHECK( !(as < acs) ); + BOOST_CHECK( !(acs < as) ); // reality check character set is as expected BOOST_CHECK( std::string("a.b") < std::string("a/b") ); @@ -835,13 +1173,66 @@ int test_main( int, char*[] ) // make sure the derivative operators also work BOOST_CHECK( a == a2 ); - BOOST_CHECK( a != b ); - BOOST_CHECK( a <= b ); - BOOST_CHECK( a <= a2 ); - BOOST_CHECK( b >= a ); - BOOST_CHECK( a2 >= a ); + BOOST_CHECK( as == a2 ); + BOOST_CHECK( acs == a2 ); + BOOST_CHECK( a == as2 ); + BOOST_CHECK( a == acs2 ); -// std::cout << errors << " errors detected\n"; + BOOST_CHECK( a != b ); + BOOST_CHECK( as != b ); + BOOST_CHECK( acs != b ); + BOOST_CHECK( a != bs ); + BOOST_CHECK( a != bcs ); + + BOOST_CHECK( b > a ); + BOOST_CHECK( b > as ); + BOOST_CHECK( b > acs ); + BOOST_CHECK( bs > a); + BOOST_CHECK( bcs > a); + + BOOST_CHECK( !(a2 > a) ); + BOOST_CHECK( !(a2 > as) ); + BOOST_CHECK( !(a2 > acs) ); + BOOST_CHECK( !(as2 > a) ); + BOOST_CHECK( !(acs2 > a) ); + + BOOST_CHECK( a <= b ); + BOOST_CHECK( as <= b ); + BOOST_CHECK( acs <= b ); + BOOST_CHECK( a <= bs ); + BOOST_CHECK( a <= bcs ); + + BOOST_CHECK( a <= a2 ); + BOOST_CHECK( as <= a2 ); + BOOST_CHECK( acs <= a2 ); + BOOST_CHECK( a <= as2 ); + BOOST_CHECK( a <= acs2 ); + + BOOST_CHECK( b >= a ); + BOOST_CHECK( bs >= a ); + BOOST_CHECK( bcs >= a ); + BOOST_CHECK( b >= as ); + BOOST_CHECK( b >= acs ); + + BOOST_CHECK( a2 >= a ); + BOOST_CHECK( as2 >= a ); + BOOST_CHECK( acs2 >= a ); + BOOST_CHECK( a2 >= as ); + BOOST_CHECK( a2 >= acs ); + + // inserter and extractor tests +# if !defined( BOOST_MSVC ) || BOOST_MSVC > 1300 // bypass VC++ 7.0 and earlier + std::cout << "\nInserter and extractor test..."; + std::stringstream ss; + ss << fs::path( "foo/bar" ) << std::endl; + fs::path round_trip; + ss >> round_trip; + BOOST_CHECK( round_trip.string() == "foo/bar" ); + std::cout << round_trip.string() << "..." << round_trip << " complete\n"; +# endif + + + std::cout << errors << " errors detected\n"; return errors; } diff --git a/test/wide_test.cpp b/test/wide_test.cpp new file mode 100644 index 0000000..99a3f26 --- /dev/null +++ b/test/wide_test.cpp @@ -0,0 +1,145 @@ +// Boost wide_test.cpp -----------------------------------------------------// + +// Copyright Beman Dawes 2005 + +// Use, modification, and distribution is subject to 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 + +// VC++ 8.0 warns on various less-than-safe practices. +// See http://msdn.microsoft.com/msdnmag/issues/05/05/SafeCandC/default.aspx +// But at least in VC++ 8.0 betas, their own libraries use the problem +// practices. So turn off the warnings. +#define _CRT_SECURE_NO_DEPRECATE +#define _SCL_SECURE_NO_DEPRECATE + +#include +# ifdef BOOST_FILESYSTEM_NARROW_ONLY +# error This compiler or standard library does not support wide-character strings or paths +# endif + +#include +#include +#include +#include + +#include "../src/utf8_codecvt_facet.hpp" + +namespace fs = boost::filesystem; + +#include +#include +#include +#include + +#include "lpath.hpp" + +namespace +{ + template< class Path > + void create_file( const Path & ph, const std::string & contents ) + { + // TODO: why missing symbol error on Darwin +# ifndef __APPLE__ + fs::ofstream f( ph ); +# else + std::ofstream f( ph.external_file_string().c_str() ); +# endif + if ( !f ) + throw fs::basic_filesystem_error( "wide_test create_file", + ph, errno ); + if ( !contents.empty() ) f << contents; + } + + template< class Path > + void test( const Path & dir, const Path & file, const Path & dot ) + { + fs::initial_path(); + fs::current_path(); + fs::remove( dir / file ); + fs::remove( dir ); + BOOST_CHECK( !fs::exists( dir / file ) ); + BOOST_CHECK( !fs::exists( dir ) ); + BOOST_CHECK( fs::create_directory( dir ) ); + BOOST_CHECK( fs::exists( dir ) ); + BOOST_CHECK( fs::is_directory( dir ) ); + BOOST_CHECK( fs::is_empty( dir ) ); + create_file( dir / file, "wide_test file contests" ); + BOOST_CHECK( fs::exists( dir / file ) ); + BOOST_CHECK( !fs::is_directory( dir / file ) ); + BOOST_CHECK( !fs::is_empty( dir / file ) ); + BOOST_CHECK( fs::file_size( dir / file ) == 23 || fs::file_size( dir / file ) == 24 ); + BOOST_CHECK( fs::equivalent( dir / file, dot / dir / file ) ); + BOOST_CHECK( fs::last_write_time( dir / file ) ); + typedef fs::basic_directory_iterator it_t; + int count(0); + for ( it_t it( dir ); it != it_t(); ++it ) + { + BOOST_CHECK( it->path() == dir / file ); + BOOST_CHECK( !fs::is_empty( it->path() ) ); + ++count; + } + BOOST_CHECK( count == 1 ); + } + + // test boost::detail::utf8_codecvt_facet - even though it is not used by + // Boost.Filesystem on Windows, early detection of problems is worthwhile. + std::string to_external( const std::wstring & src ) + { + fs::detail::utf8_codecvt_facet convertor; + std::size_t work_size( convertor.max_length() * (src.size()+1) ); + boost::scoped_array work( new char[ work_size ] ); + std::mbstate_t state; + const wchar_t * from_next; + char * to_next; + if ( convertor.out( + state, src.c_str(), src.c_str()+src.size(), from_next, work.get(), + work.get()+work_size, to_next ) != std::codecvt_base::ok ) + boost::throw_exception( "to_external conversion error" ); + *to_next = '\0'; + return std::string( work.get() ); + } + +} // unnamed namespace + +// test_main ---------------------------------------------------------------// + +int test_main( int argc, char * argv[] ) +{ + + // So that tests are run with known encoding, use Boost UTF-8 codecvt + std::locale global_loc = std::locale(); + std::locale loc( global_loc, new fs::detail::utf8_codecvt_facet ); + fs::wpath_traits::imbue( loc ); + + std::string s( to_external( L"\x2780" ) ); + for (std::size_t i = 0; i < s.size(); ++i ) + std::cout << std::hex << int( static_cast(s[i]) ) << " "; + std::cout << std::dec << std::endl; + BOOST_CHECK( to_external( L"\x2780" ).size() == 3 ); + BOOST_CHECK( to_external( L"\x2780" ) == "\xE2\x9E\x80" ); + + // test fs::path + std::cout << "begin path test..." << std::endl; + test( fs::path( "foo" ), fs::path( "bar" ), fs::path( "." ) ); + std::cout << "complete\n\n"; + + // test fs::wpath + // x2780 is circled 1 against white background == e2 9e 80 in UTF-8 + // x2781 is circled 2 against white background == e2 9e 81 in UTF-8 + std::cout << "begin wpath test..." << std::endl; + test( fs::wpath( L"\x2780" ), fs::wpath( L"\x2781" ), fs::wpath( L"." ) ); + std::cout << "complete\n\n"; + + // test user supplied basic_path + const long dir[] = { 'b', 'o', 'o', 0 }; + const long file[] = { 'f', 'a', 'r', 0 }; + const long dot[] = { '.', 0 }; + std::cout << "begin lpath test..." << std::endl; + test( ::user::lpath( dir ), ::user::lpath( file ), ::user::lpath( dot ) ); + std::cout << "complete\n\n"; + + return 0; +}