From 543669496a5b57883ba16db264eece5e58665aba Mon Sep 17 00:00:00 2001 From: Beman Dawes Date: Wed, 9 Oct 2002 19:49:35 +0000 Subject: [PATCH] initial commit - review comments not yet applied [SVN r15831] --- .gitattributes | 96 ++++ build/Jamfile | 10 + doc/design.htm | 291 ++++++++++++ doc/do-list.htm | 45 ++ doc/exception.htm | 31 ++ doc/faq.htm | 178 ++++++++ doc/fstream.htm | 107 +++++ doc/index.htm | 288 ++++++++++++ doc/operations.htm | 298 ++++++++++++ doc/path.htm | 373 +++++++++++++++ doc/portability_guide.htm | 134 ++++++ include/boost/filesystem/exception.hpp | 63 +++ include/boost/filesystem/fstream.hpp | 116 +++++ include/boost/filesystem/operations.hpp | 129 ++++++ include/boost/filesystem/path.hpp | 198 ++++++++ .../recursive_directory_iterator.hpp | 116 +++++ index.html | 9 + src/exception.cpp | 104 +++++ src/operations_posix_windows.cpp | 424 ++++++++++++++++++ src/path_posix_windows.cpp | 396 ++++++++++++++++ test/Jamfile | 34 ++ test/fstream_test.cpp | 134 ++++++ test/operations_test.cpp | 220 +++++++++ test/path_test.cpp | 315 +++++++++++++ test/recursive_dir_itr_test.cpp | 49 ++ 25 files changed, 4158 insertions(+) create mode 100644 .gitattributes create mode 100644 build/Jamfile create mode 100644 doc/design.htm create mode 100644 doc/do-list.htm create mode 100644 doc/exception.htm create mode 100644 doc/faq.htm create mode 100644 doc/fstream.htm create mode 100644 doc/index.htm create mode 100644 doc/operations.htm create mode 100644 doc/path.htm create mode 100644 doc/portability_guide.htm create mode 100644 include/boost/filesystem/exception.hpp create mode 100644 include/boost/filesystem/fstream.hpp create mode 100644 include/boost/filesystem/operations.hpp create mode 100644 include/boost/filesystem/path.hpp create mode 100644 include/boost/filesystem/recursive_directory_iterator.hpp create mode 100644 index.html create mode 100644 src/exception.cpp create mode 100644 src/operations_posix_windows.cpp create mode 100644 src/path_posix_windows.cpp create mode 100644 test/Jamfile create mode 100644 test/fstream_test.cpp create mode 100644 test/operations_test.cpp create mode 100644 test/path_test.cpp create mode 100644 test/recursive_dir_itr_test.cpp diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 000000000..3e84d7c70 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,96 @@ +* text=auto !eol svneol=native#text/plain +*.gitattributes text svneol=native#text/plain + +# Scriptish formats +*.bat text svneol=native#text/plain +*.bsh text svneol=native#text/x-beanshell +*.cgi text svneol=native#text/plain +*.cmd text svneol=native#text/plain +*.js text svneol=native#text/javascript +*.php text svneol=native#text/x-php +*.pl text svneol=native#text/x-perl +*.pm text svneol=native#text/x-perl +*.py text svneol=native#text/x-python +*.sh eol=lf svneol=LF#text/x-sh +configure eol=lf svneol=LF#text/x-sh + +# Image formats +*.bmp binary svneol=unset#image/bmp +*.gif binary svneol=unset#image/gif +*.ico binary svneol=unset#image/ico +*.jpeg binary svneol=unset#image/jpeg +*.jpg binary svneol=unset#image/jpeg +*.png binary svneol=unset#image/png +*.tif binary svneol=unset#image/tiff +*.tiff binary svneol=unset#image/tiff +*.svg text svneol=native#image/svg%2Bxml + +# Data formats +*.pdf binary svneol=unset#application/pdf +*.avi binary svneol=unset#video/avi +*.doc binary svneol=unset#application/msword +*.dsp text svneol=crlf#text/plain +*.dsw text svneol=crlf#text/plain +*.eps binary svneol=unset#application/postscript +*.gz binary svneol=unset#application/gzip +*.mov binary svneol=unset#video/quicktime +*.mp3 binary svneol=unset#audio/mpeg +*.ppt binary svneol=unset#application/vnd.ms-powerpoint +*.ps binary svneol=unset#application/postscript +*.psd binary svneol=unset#application/photoshop +*.rdf binary svneol=unset#text/rdf +*.rss text svneol=unset#text/xml +*.rtf binary svneol=unset#text/rtf +*.sln text svneol=native#text/plain +*.swf binary svneol=unset#application/x-shockwave-flash +*.tgz binary svneol=unset#application/gzip +*.vcproj text svneol=native#text/xml +*.vcxproj text svneol=native#text/xml +*.vsprops text svneol=native#text/xml +*.wav binary svneol=unset#audio/wav +*.xls binary svneol=unset#application/vnd.ms-excel +*.zip binary svneol=unset#application/zip + +# Text formats +.htaccess text svneol=native#text/plain +*.bbk text svneol=native#text/xml +*.cmake text svneol=native#text/plain +*.css text svneol=native#text/css +*.dtd text svneol=native#text/xml +*.htm text svneol=native#text/html +*.html text svneol=native#text/html +*.ini text svneol=native#text/plain +*.log text svneol=native#text/plain +*.mak text svneol=native#text/plain +*.qbk text svneol=native#text/plain +*.rst text svneol=native#text/plain +*.sql text svneol=native#text/x-sql +*.txt text svneol=native#text/plain +*.xhtml text svneol=native#text/xhtml%2Bxml +*.xml text svneol=native#text/xml +*.xsd text svneol=native#text/xml +*.xsl text svneol=native#text/xml +*.xslt text svneol=native#text/xml +*.xul text svneol=native#text/xul +*.yml text svneol=native#text/plain +boost-no-inspect text svneol=native#text/plain +CHANGES text svneol=native#text/plain +COPYING text svneol=native#text/plain +INSTALL text svneol=native#text/plain +Jamfile text svneol=native#text/plain +Jamroot text svneol=native#text/plain +Jamfile.v2 text svneol=native#text/plain +Jamrules text svneol=native#text/plain +Makefile* text svneol=native#text/plain +README text svneol=native#text/plain +TODO text svneol=native#text/plain + +# Code formats +*.c text svneol=native#text/plain +*.cpp text svneol=native#text/plain +*.h text svneol=native#text/plain +*.hpp text svneol=native#text/plain +*.ipp text svneol=native#text/plain +*.tpp text svneol=native#text/plain +*.jam text svneol=native#text/plain +*.java text svneol=native#text/plain diff --git a/build/Jamfile b/build/Jamfile new file mode 100644 index 000000000..0abe55a4a --- /dev/null +++ b/build/Jamfile @@ -0,0 +1,10 @@ +# Boost Filesystem Library Build Jamfile + +subproject libs/filesystem/build ; + +SOURCES = exception operations_posix_windows path_posix_windows ; + +lib fs : ../src/$(SOURCES).cpp + : $(BOOST_ROOT) $(BOOST_ROOT) + : debug release + ; \ No newline at end of file diff --git a/doc/design.htm b/doc/design.htm new file mode 100644 index 000000000..79abceb2d --- /dev/null +++ b/doc/design.htm @@ -0,0 +1,291 @@ + + + + + + + +Boost Filesystem Library Design + + + + +

+Filesystem +Library Design

+ +

Introduction
+Requirements
+Realities
+Rationale
+Abandoned Designs
+References

+ +

Introduction

+ +

The primary motivation for beginning work on the Filesystem Library was +frustration with Boost administrative tools.  Scripts were written in +Python, Perl, Bash, and Windows command languages.  There was no single +scripting language familiar and acceptable to all Boost administrators. Yet they +were all skilled C++ programmers - why couldn't C++ be used as the scripting +language?

+ +

The key feature C++ lacked for script-like applications was the ability to +perform portable filesystem operations on directories and their contents. The +Filesystem Library was developed to fill that void.

+ +

The intent is not to compete with traditional scripting languages, but to +provide a solution for situations where C++ is already the language +of choice..

+ +

Requirements

+
    +
  • Be able to write portable script-style filesystem operations in modern + C++.
    +
    + Rationale: This is a common programming need. It is both an + embarrassment and a hardship that this is not possible with either the current + C++ or Boost libraries.  The need is particularly acute + when C++ is the only toolset allowed in the tool chain.  File system + operations are provided by many languages used on multiple platforms, + such as Perl and Python, as well as by many platform specific scripting + languages. All operating systems provide some form of API for filesystem + operations, and the POSIX bindings are increasingly available even on + operating systems not normally associated with POSIX, such as the Mac, z/OS, + or OS/390.
  • +
  • Work within the realities described below.
    +
    + Rationale: This isn't a research project. The need is for something that works on + today's platforms, including some of the embedded operating systems + with limited file systems. Because of the emphasis on portability, such a + library would be much more useful if standardized. That means being able to + work with a much wider range of platforms that just Unix or Windows and their + clones.
  • +
  • Avoid dangerous programming practices. Particularly, all-too-easy-to-ignore error notifications + and use of global variables. If a dangerous feature is provided, identify it as such.
    +
    + Rationale: Normally this would be covered by "the usual Boost requirements...", + but it is mentioned explicitly because the equivalent native platform and + scripting language interfaces often depend on all-too-easy-to-ignore error + notifications and global variables like "current + working directory".
  • +
  • Structure the library so that it is still useful even if some functionality + does not map well onto a given platform or directory tree. Particularly, much + useful functionality should be portable even to flat +(non-hierarchical) filesystems.
    +
    + Rationale: Much functionality which does not + require a hierarchical directory structure is still useful on flat-structure + filesystems.  There are many systems, particularly embedded systems, + where even very limited functionality is still useful.
  • +
+
    +
  • Interface smoothly with current C++ Standard Library input/output + facilities.  For example, file 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.  + Thus the need to interface smoothly with standard library I/O.
  • +
  • Suitable for eventual standardization. The implication of this requirement + is that the interface be close to minimal, and that great care be take + regarding portability.
    +
    + Rationale: The lack of file system operations is a serious hole + in the current standard, with no other known candidates to fill that hole. + Libraries with elaborate interfaces and difficult to port specifications are much less likely to be accepted for + standardization.
  • +
  • The usual Boost requirements and + guidelines apply.
  • +
  • Encourage, but do not require, portability in path names.
    +
    + Rationale: For paths which originate from user input it is unreasonable to + require portable path syntax.
  • +
  • Avoid giving the illusion of portability where portability in fact does not + exist.
    +
    + Rationale: Defining important behavior unspecified or "implementation defined" does a + great disservice to programmers using a library because it makes it appear + that code relying on the behavior is portable, when in fact there is nothing + at all portable about it. The only case where such under-specification is acceptable is when both users and implementors know from + other sources exactly what behavior is required, yet for some reason it isn't + possible to specify it exactly.
  • +
+

Realities

+
    +
  • Some file systems are single rooted, others are multi-rooted.
  • +
  • Some file systems provide both a long and short form of filenames.
  • +
  • Some file systems have different syntax for file paths and directory + paths.
  • +
  • Some file systems have different rules for valid file names and valid + directory names.
  • +
  • Some file systems (ISO-9660, level 1, for example) use very restricted + (so-called 8.3) file names.
  • +
  • Some file systems allow other file systems with different + characteristics to be "mounted" within a directory tree.  Thus a + ISO-9660 or Windows + file system may end up as a sub-tree of a POSIX directory tree.
  • +
  • Wide-character versions of directory and file operations are available on some operating + systems, and not available on others.
  • +
  • There is no law that says directory hierarchies have to be specified in + terms of left-to-right decent from the root.
  • +
  • Some file systems have a concept of file "version number" or "generation + number".  Some don't.
  • +
  • Not all file systems use single character separators in path names.  Some use + paired notations. A typical fully-specified OpenVMS filename + might look something like this:
    +
    +    DISK$SCRATCH:[GEORGE.PROJECT1.DAT]BIG_DATA_FILE.NTP;5
    +

    + The general OpenVMS format is:
    +
    +     + Device:[directories.dot.separated]filename.extension;version_number
  • +
  • For common file systems, determining if two descriptors are for same + entity is extremely difficult or impossible.  For example, the concept of + equality can be different for each portion of a path - some portions may be + case or locale sensitive, others not. Case sensitivity is a property of the + pathname itself, and not the platform. Determining collating sequence is even + worse.
  • +
  • Race-conditions may occur. Directory trees, directories, files, and file attributes are in effect shared between all threads, processes, and computers which have access to the + filesystem.  That may well include computers on the other side of the + world or in orbit around the world. This implies that file system operations + may fail in unexpected ways. For example:
    +
    +      assert( exists("foo") == exists("foo") ); + // may fail!
    +     assert( is_directory("foo") == is_directory("foo"); + // may fail!
    +

    + In the first example, the file may have been deleted between calls to + exists().  In the second example, the file may have been deleted and then + replaced by a directory of the same name between the calls to is_directory().
  • +
  • Even though an application may be portable, it still will have to traffic + in system specific paths occasionally; user provided input is a common + example.
  • +
+ +

Rationale

+ +

The Requirements and +Realities above drove much of the C++ interface design.  In particular, +the desire to make script-like code straightforward caused a great deal of +effort to go into ensuring that apparently simple expressions like exists( "foo" +) work as expected.

+ +

See the FAQ for the rationale behind many detailed +design decisions.

+ +

Several key insights went into the path class design:

+
    +
  • Decoupling the input formats, internal conceptual (vector<string> + or other sequence) + model, and output formats.
  • +
  • Providing two input formats (generic and O/S specific) broke a major + design deadlock.
  • +
  • Providing several output formats solved another set of previously + intractable problems.
  • +
+ +

Error checking was a particularly difficult area. One key insight was that +with file and directory names, portability isn't a universal truth.  +Rather, the programmer must think out the question "What operating systems do I +want this path to be portable to?"  By providing support for several +answers to that question, the Filesystem Library alerts programmers of the need +to ask it in the first place.

+

Abandoned Designs

+

operations.hpp

+

Dietmar Kühl's original dir_it design and implementation supported +wide-character file and directory names. It was abandoned after extensive +discussions among Library Working Group members failed to identify portable +semantics for wide-character names on systems not providing native support. See +FAQ.

+

Previous iterations of the interface design used explicitly named functions providing a +large number of convenience operations, with no compile-time or run-time +options. There were so many function names that they were very confusing to use, +and the interface was much larger. Any benefits seemed theoretical rather than +real.

+

Designs based on compile time (rather than runtime) flag and option selection +(via policy, enum, or int template parameters) became so complicated that they +were abandoned, often after investing quite a bit of time and effort. The need +to qualify attribute or option names with namespaces, even aliases, made use in +template parameters ugly; that wasn't fully appreciated until actually writing +real code.

+

Yet another set of convenience functions ( for example, remove with +permissive, prune, recurse, and other options, plus predicate, and possibly +other, filtering features) were abandoned because the details became both +complex and contentious. What is left is a toolkit of low-level operations from +which the user can create more complex convenience operations, plus a very small +number of convenience functions which were found to be useful enough to justify +inclusion.

+ +

path.hpp

+ +

There were so many abandoned path designs, I've lost track. Policy-based +class templates in several flavors, constructor supplied runtime policies, +operation specific runtime policies, they were all considered, often +implemented, and ultimately abandoned as far too complicated for any small +benefits observed.

+ +

error checking

+ +

A number of designs for the error checking machinery were abandoned, some +after experiments with implementations. Totally automatic error checking was +attempted in particular. But automatic error checking tended to make the overall +library design much more complicated.

+ +

Some designs associated error checking mechanisms with paths.  Some with +operations functions.  A policy-based error checking template design was +partially implemented, then abandoned as too complicated for everyday +script-like programs.

+ +

The final design, which depends partially on explicit error checking function +calls,  is much simpler and straightforward, although it does depend to +some extent on programmer discipline.  But it should allow programmers who +are concerned about portability to be reasonably sure that their programs will +work correctly on their choice of target systems.

+ +

References

+ +

[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.

+ +

[POSIX-01] Open Group, IEEE Std 1003.1-2001 [AKA +POSIX], 2001, + +http://www.opengroup.org/onlinepubs/007904975/toc.htm

+ +

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

+ +
+

© Copyright Beman Dawes, 2002

+

Revised +13 September, 2002

+ + + + \ No newline at end of file diff --git a/doc/do-list.htm b/doc/do-list.htm new file mode 100644 index 000000000..9d02b66b6 --- /dev/null +++ b/doc/do-list.htm @@ -0,0 +1,45 @@ +Boost Filesystem Do-list +

+Filesystem +Do-list

+
    +
  • 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?
  • +
  • Cyclic paths:
  • +
+
+
    +
  • General requirements.
  • +
  • Add cycle-breaking code if needed.
  • +
  • Add test case to make sure functions like remove_all don't loop.
  • +
+
+
    +
  • As a lexical concept, parent-directory is portable unless it escapes to + the operating system. But do all operating recognize such a concept in a path?  + I doubt it.  Maybe there should be a checking function that verifies that + generic_path() contains no "..".
  • +
  • Once the review is complete, ask for help porting to the Mac, etc.
  • +
  • From John Maddock:
  • +
+
+
>All the functions generic_path, file_path, directory_path, leaf, and 
+>branch,
+>are under-documented IMO. I had to read the specs quite closely before I
+>could figure out which did what. Adding examples to each would probably be
+>a help, or maybe a "description" section that provides a less terse
+>description than the standardese.
+
+
    +
  • Change remove() to return a bool, now that the file doesn't have to + exist..  (Keith Burton)
  • +
+
+

© Copyright Beman Dawes, 2002

+

Revised +22 September, 2002

\ No newline at end of file diff --git a/doc/exception.htm b/doc/exception.htm new file mode 100644 index 000000000..5fb9ae0b2 --- /dev/null +++ b/doc/exception.htm @@ -0,0 +1,31 @@ + + + + + + + +Boost Filesystem exception.hpp Header + + + + +

+boost/filesystem/exception.hpp

+ +

Introduction

+ +

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

+ +

There isn't any HTML documentation available yet.  The header itself, boost/filesystem/exception.hpp, +contains comments as to the specifications for important member functions.

+
+

© Copyright Beman Dawes, 2002

+

Revised +11 July, 2002

+ + + + \ No newline at end of file diff --git a/doc/faq.htm b/doc/faq.htm new file mode 100644 index 000000000..ff49f738c --- /dev/null +++ b/doc/faq.htm @@ -0,0 +1,178 @@ +Boost Filesystem FAQ + + +

+Filesystem +FAQ

+

Why not use a URI (Universal Resource Identifier) based path?

+

URI's would promise more than the Filesystem Library can actually deliver, +since URI's extend far beyond what most operating systems consider a file or a +directory.  Thus for the primary "portable script-style file system +operations" requirement of the Filesystem Library, full URI's appear to be over-specification.

+

Why base the generic-path string format on POSIX?
+
+
POSIX 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 +operating system supplied +POSIX library (z/OS, OS/390, and many more.)

+

Why isn't path a base class with derived directory_path and +file_path classes?

+

Why bother?  The behavior of all three classes is essentially identical. +Several early versions did require users to identify each path as a file or +directory path, and this seemed to increase errors and decrease code +readability. There was no apparent upside benefit.

+

Why not support a concept of specific kinds 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.

+

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 the POSIX operating system, for example?

+

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 +file system operations" requirement with such a system. File systems exist where operations are usually performed on + some non-string handle type. The classic Mac OS has been mentioned explicitly as a case where +trafficking in paths isn't always natural.   

+

The case for the "handle" (opaque data type to identify a file) +style may be strongest for directory iterator value type.  (See Jesse Jones' Jan 28, +2002, Boost postings). However, as class path has evolved, it seems sufficient +even as the directory iterator value type.

+

Why aren't directories considered to be files?

+

Because +directories cannot portably and usefully be opened as files using the C++ Standard Library stdio or fstream +I/O facilities. An important additional rationale is that separating the concept +of directories and files makes exposition and specification clearer. A +particular problem is the naming and description of function arguments.

+
+
+ + + + + + + + + + + + + + + + + + + + + + + + +
+

Meaningful Names for Arguments

Argument IntentMeaningful name if
+ directories are files
Meaningful name if
+ directories aren't files
A path to either a directory or a non-directorypathpath
A path to a directory, but not to a non-directorydirectory_pathdirectory_path
A path to a non-directory, but not a directorynon_directory_pathfile_path
+
+
+

The problem is that when directories are considered files, +non_directory_path as an argument name, and the corresponding "non-directory +path" in documentation, is ugly and lengthy, and so is shortened to just path, +causing the code and documentation to be confusing if not downright wrong. The +names which result from the "directories aren't files" approach are more +acceptable and less likely to be used incorrectly.

+

Why are the operations.hpp non-member functions so low-level?

+

To provide a toolkit from which higher-level functionality can be created.

+

An +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 +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.

+

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 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 .

+

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

+

Safety. Return codes or error notification variables are often ignored +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), +simple syntax and guaranteed presence outweigh other considerations. Because +access to virtually all 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 +system depend as to be disqualified as a "guaranteed presence" operation.)

+

Why isn't there a set_current_directory function?

+

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 the gun.

+

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.

+

Why aren't file and directory name portability errors detected automatically, +rather than by separate function calls?

+

Applications mix use of portable and non-portable names, and the situations +where non-portable names constitute errors vary widely.  For example, a +non-portable name found by a directory_iterator may or may not constitute +an application error. In another example, for an application copying selected +native directories and files for later use restricted to ISO-6990 filesystem, +conditions for error are very different between the source and the target.

+

A number (at least six) of designs for automatic name validity error +detection were evaluated, including at least four complete implementations.  +While the details for rejection differed, they all tended to distort the +otherwise simple design of the rest of the library.

+

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

+

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

+

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

+

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

+
    +
  • "/foo" is not strictly an absolute path since it is relative to the + current drive, yet appending it to another path as if it were relative clearly + isn't correct.
  • +
  • "c:foo" is relative to the current working directory on drive "c:", + yet it can't be treated like other relative paths in that it can't be appended + to an absolute path.
  • +
+
+

© Copyright Beman Dawes, 2002

+

Revised +12 September, 2002

\ No newline at end of file diff --git a/doc/fstream.htm b/doc/fstream.htm new file mode 100644 index 000000000..a8a878048 --- /dev/null +++ b/doc/fstream.htm @@ -0,0 +1,107 @@ + + + + + + + +Boost Filesystem fstream Header + + + + +

+boost/filesystem/fstream.hpp

+ +

Introduction

+ +

The C++ Standard Library's <fstream> header uses const char* to +pass arguments representing file names, with that usage occurring seven +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.

+ +

The Filesystem Library's fstream.hpp header simply uses the <fstream> standard library +components as base classes, and then overrides the members requiring argument +types of path.

+ +

For documentation beyond the synopsis, see the +tutorial and +examples.

+ +

Synopsis

+
namespace boost
+{
+  namespace filesystem
+  {
+    template < class charT, class traits = std::char_traits<charT> >
+    class basic_filebuf : public std::basic_filebuf<charT,traits>
+    {
+    public:
+      virtual ~basic_filebuf() {}
+
+      std::basic_filebuf<charT,traits> * open( const path & file_ph,
+        std::ios_base::openmode mode );
+    };
+
+    typedef basic_filebuf<char> filebuf;
+    typedef basic_filebuf<wchar_t> wfilebuf;
+
+    template < class charT, class traits = std::char_traits<charT> >
+    class basic_ifstream : public std::basic_ifstream<charT,traits>
+    {
+    public:
+      basic_ifstream() {}
+      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,
+        std::ios_base::openmode mode = std::ios_base::in );
+    };
+
+    typedef basic_ifstream<char> ifstream;
+    typedef basic_ifstream<wchar_t> wifstream;
+
+    template < class charT, class traits = std::char_traits<charT> >
+    class basic_ofstream : public std::basic_ofstream<charT,traits>
+    {
+    public:
+      basic_ofstream() {}
+      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,
+        std::ios_base::openmode mode = std::ios_base::out );
+    };
+
+    typedef basic_ofstream<char> ofstream;
+    typedef basic_ofstream<wchar_t> wofstream;
+
+    template < class charT, class traits = std::char_traits<charT> >
+    class basic_fstream : public std::basic_fstream<charT,traits>
+    {
+    public:
+      basic_fstream() {}
+      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,
+        std::ios_base::openmode mode = std::ios_base::in|std::ios_base::out );
+    };
+ 
+    typedef basic_fstream<char> fstream;
+    typedef basic_fstream<wchar_t> wfstream;
+  } // namespace filesystem
+} // namespace boost
+
+
+

© Copyright Beman Dawes, 2002

+

Revised +14 September, 2002

+ + + + \ No newline at end of file diff --git a/doc/index.htm b/doc/index.htm new file mode 100644 index 000000000..572cd5fbe --- /dev/null +++ b/doc/index.htm @@ -0,0 +1,288 @@ + + + + + + + +Boost Filesystem Library + + + + +

+Boost +Filesystem Library

+ + + + + +
This Document
+    Introduction
+    Two-minute tutorial
+    Examples
+    +Definitions
+    +Requirements
+    Race-condition danger
+    Acknowledgements
Other Documents
+    Library Design
+    FAQ
+    Portability Guide
+    path.hpp documentation
+    operations.hpp documentation
+    fstream.hpp documentation
+    exception.hpp documentation
+    Do-list
+

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 +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.

+ +

Filesystem Library components are supplied by several  headers, all in +directory boost/filesystem:

+ +
    +
  • Header path.hpp provides class path, a portable mechanism for representing + paths in C++ programs. Validity checking + functions are also provided. See path.hpp documentation.
  • +
  • Header operations.hpp provides functions operating on files and directories, + and includes class directory_iterator. See + operations.hpp documentation.
  • +
  • Header fstream.hpp provides the same components as the C++ Standard + Library's fstream header, except + that files are identified by path objects rather that char *'s. + See fstream.hpp documentation.
  • +
  • Header exception.hpp provides class filesystem_error. See + exception.hpp documentation.
  • +
  • Experimental header + + recursive_directory_iterator.hpp provides an undocumented directory + iterator which recurses into any sub-directories encountered. It will be + incorporated into the Filesystem Library if user feedback is favorable.
  • +
+

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 cout
+namespace fs = boost::filesystem;
+
+

A class path object can be created:

+
+
fs::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, +such as "some_dir:file.txt", "[some_dir]file.txt", +"some_dir/file.txt", or whatever is appropriate 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:

+
+
fs::remove_all( "foobar" );
+fs::create_directory( "foobar" );
+fs::ofstream file( "foobar/cheeze" );
+file << "tastes good!\n";
+file.close();
+if ( !fs::exists( "foobar/cheeze" ) )
+  std::cout << "Something is rotten in foobar\n";
+
+

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

+
+
int main( int argc, char * argv[] ) {
+fs::path arg_path( argv[1], fs::system_specific );
+
+

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

+
+
fs::ifstream file1( arg_path << "foo/bar" );
+fs::ifstream file2( arg_path << "foo" << "bar" );
+
+

Note that expressions arg_path << "foo/bar" and arg_path << "foo" +<< "bar" yield equivalent results.

+

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.

+

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 fs::path & dir_path,     // in this directory,
+                const std::string & file_name, // search for this name,
+                fs::path & path_found )        // placing path here if found
+{
+  if ( !fs::exists( dir_path ) ) return false;
+  fs::directory_iterator end_itr; // default construction yields past-the-end
+  for ( fs::directory_iterator itr( dir_path );
+        itr != end_itr;
+        ++itr )
+  {
+    if ( fs::is_directory( *itr ) )
+    {
+      if ( find_file( *itr, file_name, path_found ) ) return true;
+    }
+    else if ( itr->leaf() == file_name ) // see below
+    {
+      path_found = *itr;
+      return true;
+    }
+  }
+  return false;
+}
+
+

The expression itr->leaf() == file_name, in the line commented // +see below, calls the leaf() function on the path object +returned by the iterator. leaf() returns a string which is a copy of the +last (closest to the leaf, farthest from the root) file or directory name in the +path object.

+

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 +exceptions if they do not complete successfully, so there is enough implicit +error checking that this application doesn't need to include additional error +checking code.

+

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

+

Examples

+

Until a custom-made example is available, see +compiler_status.cpp, an actual +program which uses the library.

+

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

+ +

Definitions

+

directory - A container provided by the operating system, +containing the names of files, other directories, or both. Directories are identified +by directory path.

+

directory tree - A directory and file +hierarchy viewed as an acyclic graph.

+

path - A possibly empty sequence of names. Each +element in the sequence, except the last, names a directory +which contains the +next element. The last element may name either a directory or file. The first +element is closest to the root of the directory tree, the last element is +farthest from the root.

+

It is traditional to represent a path as a string, where each element in the +path is represented by a name, and some operating system +defined syntax distinguishes between the name elements. Other representations of +a path are possible, such as each name being an element in a std::vector<std::string>.

+

file path - A path whose +last element is a file.

+

directory path - A path +whose last element is a directory.

+

name - A file or directory name, without any +directory path information to indicate the file or +directory's actual location within a directory tree. For some +operating systems, files and directories may have more than one valid name, such +as a short-form name and a long-form name.

+

Requirements

+

Unless otherwise specified, all Filesystem Library functions are required to +throw a filesystem_error exception if the +implementation cannot successfully complete operations required to meet the +function's specifications. Such exceptions are in addition to any exceptions +specified in the function's "Throws" paragraph.

+

Filesystem Library functions are permitted to call C++ Standard Library +functions, so std::bad_alloc exceptions may also be thrown, unless +otherwise specified.

+

There is no rollback guarantee; a Filesystem Library function which throws an +exception may leave the external file system in an altered state.

+

Race-condition danger

+

The state of files and directories is often +globally shared, and thus may be changed unexpectedly by other threads, +processes, or even other computers which have access to the filesystem. As an +example of the difficulties this can cause, consider that the following asserts +may fail:

+
+

assert( exists( "foo" ) == exists( "foo" ) );  // +(1)
+
+remove_all( "foo" );
+assert( !exists( "foo" ) );  // (2)
+
+assert( is_directory( "foo" ) == is_directory( "foo" ) ); // +(3)

+
+

(1) will fail if a non-existent "foo" comes into existence, or an +existent "foo" is removed, between the first and second call to exists(). +This could happen if, during the execution of the example code, another thread, +process, or computer is also performing operations in the same directory.

+

(2) will fail if between the call to remove_all() and the call to +exists() a new file or directory named "foo" is created by another +thread, process, or computer.

+

(3) will fail if another thread, process, or computer removes an +existing file "foo" and then creates a directory named "foo", between the +example code's two calls to is_directory().

+

A program which needs to be robust when operating on potentially-shared file +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 (September, 2002) supports operating systems that have +either the POSIX or Windows API's available.

+

The following tests are provided:

+ +

As of September, 2002, these tests succeed for the following compilers on Windows:

+
    +
  • Borland 5.5.1
  • +
  • GCC 3.1 (using POSIX implementation, but excluding wide-character fstream tests)
  • +
  • Intel 6.0
  • +
  • Metrowerks 8.2
  • +
  • Microsoft 7.0
  • +
  • Microsoft 6.0 except fstream_test failed.
  • +
+

As of September, 2002, some limited use has been successful on Linux using +GCC and IBM/AIX using Visual Age C++.

+

Acknowledgements

+

The Filesystem Library was designed and implemented by Beman Dawes, except +for the directory_iterator and filesystem_error classes which were +based on prior work from Dietmar Kühl, as modified by Jan Langer.

+ +

Key design requirements and +design realities were developed during extensive discussions on the Boost mailing list, +followed by comments on the actual implementation.  Participants included +(in more-or-less chronological order) Beman Dawes, Jan Langer, Darin Adler, Michiel +Salters, Jani Kajala, Jason Stewart, Carl Daniel, David Abrahams, Bill Kempf, +Jonathan Caves, George Heintzelman, Ken Hagen, Eric Jensen, Joel de Guzman, Jim +Hyslop, John Maddock, Matt Austern, Peter Dimov, Davlet Panech, Dylan Nicholson, Tom Harris, +Giovanni Bajo, Baptiste Lepilleur, Thomas Witt, Keith Burton, Mattias Flodin, +Daniel Frey, Vladimir Prus, Toon Knapen.

+ +

Specific improvements for a preliminary design document came from Dan Nuffer and Jeff +Garland.

+ +

A lengthy discussion on the C++ committee's library reflector illuminated the "illusion +of portability" problem, particularly in postings by JP Plauger and Pete Becker.

+ +
+

© Copyright Beman Dawes, 2002

+

Revised +13 September, 2002

+ + + + \ No newline at end of file diff --git a/doc/operations.htm b/doc/operations.htm new file mode 100644 index 000000000..97412e630 --- /dev/null +++ b/doc/operations.htm @@ -0,0 +1,298 @@ + + + + + + + +Boost Filesystem operations.hpp Header + + + + +

+boost/filesystem/operations.hpp

+ +

Introduction
+Header synopsis
+Class directory_iterator
+    Constructors
+    Destructor
+    Other functions
+Non-member functions
+    exists
+    is_directory
+    is_empty
+    create_directory
+    remove
+    remove_all
+    rename
+    copy_file
+    initial_directory

+ +

Introduction

+ +

The +boost/filesystem/operations.hpp header provides operations on files and +directories.

+ +

These operations traffic in paths; see +boost/filesystem/path.hpp documentation.

+ +

For file I/O streams, see boost/filesystem/fstream.hpp +documentation.

+ +

As with all Filesystem Library components, errors may result in +filesystem_error or std::bad_alloc +exceptions being thrown. See Requirements.

+ +

Header boost/filesystem/operations.hpp +synopsis

+
namespace boost
+{
+  namespace filesystem
+  {
+
+    class directory_iterator
+    {
+    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;
+
+      directory_iterator();
+      explicit directory_iterator( const path & directory_ph );
+
+      // other functions
+      // ...
+    };
+
+    bool exists( const path & ph );
+    bool is_directory( const path & ph );
+    bool is_empty( const path & ph );
+
+    void create_directory( const path & directory_ph );
+    void 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 & from_file_ph,
+                    const path & to_file_ph );
+
+    const path & initial_directory();
+
+  } // namespace filesystem
+} // namespace boost
+
+ +

Class directory_iterator

+ +

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

+ +

The value type is boost::filesystem::path, so +dereferencing a directory_iterator yields a +path to a file or 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 +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 +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++ +Standard Library functions specified as taking file names. The leaf of a path +returned by dereferencing a 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.

+ +

Constructors

+
+ +

directory_iterator();

+ +

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

+ +

explicit directory_iterator( const path & directory_ph );

+ +

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

+
+ +

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

+ +

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

+ +
    +
  • Traffic in paths rather than char*'s, for much + enhanced portability.
  • +
  • Report errors by throwing exceptions, for safer and better error handling.
  • +
  • Tighten specifications slightly, for improved portability.
  • +
+ +

+Rationale: Functions which already exist in the C++ Standard Library, +such as remove() and rename(), +retain the same names and general behavior in the Filesystem Library, to +minimize surprises.

+ +

+Rationale: Errors which might appear to be preconditions are not +specified as such, but instead are specified to throw exceptions. This is +because the possibility of race-conditions +makes it unreliable to test for preconditions before calling the function. As a +design practice, preconditions should always be testable by a program, so that +violations can be avoided. It is not always possible or desirable to abstract +away the fact that the library is implemented by calls to the operating system, +and this is one of those cases.

+ +

+Naming Rationale: See class path +Naming Rationale.

+ +

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.

+
+

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.

+
+

is_empty

+
+

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(path)

+
+

create_directory

+
+

void create_directory( const path & directory_ph );

+

Postcondition: exists(directory_ph) && +is_directory(directory_ph) && is_empty(directory_ph)

+

Throws: if exists(directory_ph)) || !exists(branch(directory_ph))

+
+

remove

+
+

void remove( const path & ph );

+

Postcondition: !exists( ph )

+

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

+

Rationale: Does not throw when !exists( ph ) because not +throwing is:

+
    +
  • Slightly easier-to-use for many common use cases.
  • +
  • Slightly higher-level because it implies use of postcondition semantics + rather than effects semantics, which would be specified in the somewhat + lower-level terms of interactions with the operating system.
  • +
+

There is, however, a slight decrease in safety because some errors will slip +by which otherwise would have been detected. For example, a misspelled path name +could go undetected for a long time.

+

The initial version of the library did throw when the path did not exist; it +was changed only reluctantly.

+
+

remove_all

+
+

unsigned long remove_all( const path & ph );

+

Postcondition: !exists( ph )

+

Returns: The number of files and directories removed.

+
+

rename

+
+

void rename( const path & from_ph, const path & to_ph +);

+

Effects: Changes the name of  file or directory from_ph +to to_ph.

+

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

+

Throws: if !exists(from_ph) || exists(to_ph) || !exists(branch(to_ph))

+

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 +inherited from the C Library.

+

Note: If branch(from_path) resolves to the same directory +as branch(to_ph), then leaf(from_ph) must not be +the same name as leaf(to_ph), but if the branch directories are +different, it doesn't matter if the leaf names are the same.

+

Note: Some operating systems with multi-rooted file systems +representing different drives, devices, or volumes, do not allow rename +operations between roots . Implementations should not take heroic efforts, such +as switching to a copy mode, to make an otherwise failing rename succeed +across drives, devices, or volumes.

+
+

copy_file

+
+

void copy_file( const path & from_file_ph, const path & +to_file_ph );

+

Effects: Copies the file represented by from_file_ph to +to_file_ph.

+

Throws: if !exists(from_file_ph) || directory(from_file_ph) +|| exists(to_file_ph) || !exist(branch(to_path))

+
+

initial_directory

+
+

const path & initial_directory();

+

Effects: The first time the function is called, stores an absolute +directory path.

+

The preferred value for the stored path is the initial working directory path when +main() was called. Implementations are permitted, however, to store the +current working directory at the time of the first call to initial_directory(). If neither of these actions is possible, a diagnostic +is required.

+

Returns: The stored path.

+

Rationale:  The semantics, in effect, turn a global variable into +a safer global constant. The preferred implementation requires runtime library +support, so alternate semantics are supplied for those implementations which +cannot change an existing the runtime library.

+

Note: It would be good practice in a program dependent on +initial_directory() to call it immediately upon entering main(). That +protects against another function altering the current working +directory (using a native platform function) before the first call to +initial_directory().

+ +
+
+

© Copyright Beman Dawes, 2002

+

Revised +22 September, 2002

+ + + + \ No newline at end of file diff --git a/doc/path.htm b/doc/path.htm new file mode 100644 index 000000000..d954ec5a0 --- /dev/null +++ b/doc/path.htm @@ -0,0 +1,373 @@ + + + + + + + +Boost Filesystem path.hpp Header + + + + +

+boost/filesystem/path.hpp

+ +

+Introduction
+Grammar for generic path strings
+Canonical form
+Header synopsis
+Class path
+Member functions
+Non-member functions

+

Introduction

+ +

Many Filesystem Library functions traffic in objects of class path, +provided by this header.  Non-member functions for error checking are also +supplied.

+ +

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

+ +

For file I/O stream operations, see boost/filesystem/fstream.hpp +documentation.

+ +

As with all Filesystem Library components, errors may result in +filesystem_error or std::bad_alloc +exceptions being thrown. See Requirements.

+

Class path

+

Class path provides for portable mechanism for representing +paths in C++ programs.  Class path +is concerned with the lexical and syntactic aspects of a path, regardless of +whether or not such a path currently exists in the operating system's +filesystem.

+

Rationale: If filesystem functions trafficked in std::strings 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.

+

Conceptual model of a path

+

An object of class path can be conceptualized as containing a sequence +of strings, where each string contains the name of a directory, or, in the case +of the string representing the element farthest from the root in the directory +hierarchy, the name of a directory or file. Such a path representation is + +independent of any particular representation of the path as a single +string.

+

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

+

So that programs can portably express paths as a single string, class 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.

+

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 +the operating system's format, and a file path string using the operating +system's format.  Additional access functions retrieve specific portions of +the contained path.

+

+

+

+

+

+

+

Grammar for portable generic path string

+

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

+
+
path ::= [system-specific-root] [relative-path] 
+
relative-path ::= element { "/" element } 
+
element ::= name | parent-directory 
+
parent-directory ::= ".." 
+
name ::= char { char }
+
+

system-specific-root grammar is implementation-defined. +system-specific-root must not be present in generic input (the undecorated +path constructors); it may be part of the strings returned by path +member functions, and may be present in the argument to path constructors +with the system_specific decorator.

+

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

+

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

+

Canonical form

+

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

+

Header +boost/filesystem/path.hpp synopsis

+
namespace boost
+{
+  namespace filesystem
+  {
+    enum path_format { system_specific };
+
+    class path
+    {
+    public:
+      // compiler generates copy constructor,
+      // copy assignment, and destructor
+
+      // constructors:
+      path();
+      path( const std::string & src );
+      path( const char * src );
+      path( const std::string & src, path_format );
+      path( const char * src, path_format );
+
+      // append operations:
+      path & operator<<=( const path & rhs );
+      const path operator<<( const path & rhs ) const;
+
+      // query functions: 
+      bool is_null() const;
+      const std::string & generic_path() const;
+      const std::string & file_path() const;
+      const std::string & directory_path() const;
+      const std::string leaf() const;
+      const path branch() const;
+
+      // iteration:
+      typedef implementation-defined iterator;
+      const iterator begin() const;
+      const iterator end() const;
+
+    private:
+      std::vector<std::string> m_name;  // for exposition only
+    };
+
+    const path operator<< ( const char * lhs, const path & rhs );
+    const path operator<< ( const std::string & lhs, const path & rhs );
+
+    // Also see Undocumented non-member functions below
+
+  }
+}
+

Rationale: The return type of several functions (operator<<, + leaf, branch) is const path instead of path to disallow + expressions like (p1<<p2) = p3.  See Scott Myers, Effective C++, + Item 21. Likewise, begin() and end() return const iterator + rather than iterator. This detects non-portable code such as ++pth.begin(), + which will not work if iterator is a non-class type. See next() + and prior() in boost/utility.hpp.

+

Member functions

+

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. +Actual implementations may differ.

+

Rationale: Return types of query functions have been chosen to match +the types needed by important uses, and to be efficient in common +implementations.

+

Note: There is no guarantee that a 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 +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. +Validity checking functions are supplied to +ensure names in paths are as portable as desired, but they must be explicitly +called by the user.

+

Naming Rationale: Class path +member function names and operations.hpp non-member +function names are chosen to be distinct from one another. Otherwise, given a +path foo, for example, both foo.empty() and empty( foo ) +would be valid, but with completely different semantics. Avoiding this was +considered more important than consistency with some C++ Standard Library naming +conventions, which aren't followed uniformly anyhow, even in the standard.

+

System-specific Representation

+

Several path non-member functions return representations of m_name +in formats specific to the operating system. These formats are implementation +defined. 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.

+

Representation example

+

The difference between the representations returned by generic path(), +directory_path(), and file_path() are illustrated by the following +code:

+
+
path my_path( "foo/bar/data.txt" );
+std::cout << "generic_path---: " << my_path.generic_path() << '\n'
+          << "directory_path-: " << my_path.directory_path() << '\n'
+          << "file_path------: " << my_path.file_path() << '\n';
+
+

On POSIX or Windows, the output representations would be identical:

+
+
generic_path---: foo/bar/data.txt
+directory_path-: foo/bar/data.txt
+file_path------: foo/bar/data.txt
+
+

But on a hypothetical operating system using OpenVMS format representations, +they would each be different:

+
+
generic_path---: foo/bar/data.txt
+directory_path-: [foo.bar.data.txt]
+file_path------: [foo.bar]data.txt
+
+

Note that that because this system uses period as both a directory separator +character and as a separator between filename and extension, directory_path() +in the example produces a useless result. On this operating system, the +programmer should only use this path as a file path. (There is a +portability recommendation +to not use periods in directory names.)

+

constructors

+
+
path();
+

Effects: Default constructs an object of class path.

+
path( const std::string & src );
+path( const char * src );
+

Precondition: src conforms to the generic + path string grammar relative-path syntax, and contains no embedded + '\0' characters.

+

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

+

Postcondition: m_name has been reduced to + canonical form.

+

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

+
path( const std::string & src, path_format );
+path( const char * src, path_format );
+

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

+

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

+

Postcondition: m_name has been reduced to + canonical form.

+
+

operator <<=

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

Effects: Append rhs.m_name to m_name.

+

Returns: *this

+

Postcondition: m_name has been reduced to + canonical form.

+

Rationale: It is not considered an error for rhs to + include a system-specific-root because it might relative, and + thus valid.  For example, on Windows, the follow must succeed:

+
+
path p( "c:", system_specific );
+p <<= path( "/foo", system_specific );
+assert( p.generic_path() == "c:/foo" );
+
+
+

operator <<

+
+
const path operator<< ( const path & rhs ) const;
+

Returns: path( *this ) <<= rhs

+

Rationale: Operator << is supplied, because it, together with operator <<=, provides a + convenient way for users to supply paths with a variable number of elements.  + For example, initial_directory() << "src" << test_name. + Operator+, with operator+=, were considered as alternatives, but deemed too + easy to confuse with those operators for std::string.

+

Note: Also see non-member operator<< functions.

+
+

is_null

+
+
bool is_null() const;
+

Returns: m_name.size() == 0

+
+

generic_path

+
+
const std::string & generic_path() const;
+

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

+

Note: If any m_name elements originated from the system specific +constructors, there is no guarantee that the returned string is unambiguous +according to the grammar. A system-specific-root indistinguishable from a +relative-path name, a name containing "/", a name "..", and a +system-specific-root beyond the first element all could cause ambiguities. Such +an ambiguous representation might still be useful for some purposes, such as +display. If no m_name elements originated from the system specific constructors, +the returned string is always unambiguous.

+

See: Representation example +above.

+
+

file_path

+
+
const std::string & file_path() const;
+

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

+

See: Representation example +above.

+

Warning: This function is intended only for use in calls to operating +system or third-party libraries. Use in other contexts is probably a programming +error. The preferred way to obtain a std::string from a path is generic_path().

+
+

directory_path

+
+
const std::string & directory_path() const;
+

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

+

See: Representation example +above.

+

Warning: This function is intended only for use in calls to operating +system or third-party libraries. Use in other contexts is probably a programming +error. The preferred way to obtain a std::string from a path is generic_path().

+
+

leaf

+
+
const std::string leaf() const;
+

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

+

Rationale: Return type is const string rather than const +string & to give implementations freedom to avoid  maintaining the +leaf as a separate string object.

+
+

branch

+
+
const path branch() const;
+

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

+
+

iterator

+
+

typedef implementation-defined iterator;

+

An iterator meeting the C++ Standard Library requirements for bidirectional +iterators (24.1). The value, reference, and pointer types are std::string, +const std::string &, and const std::string *, respectively.

+
+

begin

+
+

const iterator begin() const;

+

Returns: m_path.begin()

+
+

end

+
+

const iterator end() const;

+

Returns: m_path.end()

+
+

Non-member functions

+

Non-member operator<<

+
+

const path operator << ( const char * lhs, const path & rhs );
+const +path operator << ( const std::string & lhs, const path & rhs );

+

Returns: path( lhs ) <<= rhs

+
+

Undocumented non-member +functions

+

The header boost/filesystem/path.hpp +also supplies several non-member functions which can be used to verify that a +path meets certain requirements. These subsidiary functions are undocumented +pending more research and discussion, and should not be relied upon as they are +likely to change.

+
+

© Copyright Beman Dawes, 2002

+

Revised +20 September, 2002

+ + + + \ No newline at end of file diff --git a/doc/portability_guide.htm b/doc/portability_guide.htm new file mode 100644 index 000000000..99f8b7b69 --- /dev/null +++ b/doc/portability_guide.htm @@ -0,0 +1,134 @@ + + + + + + + +Portability Guide + + + + +

+Portability +Guide

+

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 +operating systems. The file and directory names in paths present a particularly +troublesome portability problem.

+

It is not possible to know in advance if a file or directory name will be +valid (and thus portable) for an operating system whose requirements are +unknown. There is always the possibility that an operating system could use +names which are unusual (number between 17 and 4095, for example) or very +limited (maximum of two character names, for example).

+

It is possible, however, to know if a name is valid for a particular +operating system.  It is also possible to construct names which are very +likely to be portable to a large number of modern and legacy operating systems.

+

By providing name validity checking facilities, the Filesystem Library allows +programmers to ensure that a given name meets the requirements for specific +operating systems, or meets the recommendations for general portability.

+

To do: An implementation should be required to provide check functions +for the host O/S, and for the portability recommendations.  But it isn't +possible to require implementors to supply an open ended number of checking +functions. It would be helpful if there was a registry to ensure that checking +functions for a particular O/S had the same specification regardless of +implementor.

+ +

Limits imposed by specific operating systems

+

(To be supplied)

+ + + + + + +
Operating
+ system
Check
+ function
Requirements
+

File and directory name recommendations

+ +

(Very preliminary draft)

+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
RecommendationCheck
+ function
Rationale
Limit file and directory names to the characters A-Z, a-z, 0-9, period, hyphen, and + underscore.
+
+ For even greater portability, don't use hyphens at all.
 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. + ISO-9660, and possibly some legacy systems, do not permit hyphens at all.
Do not use periods in directory names.  Requirement for Requirement for ISO-9660, OpenVMS native + filesystem, and other legacy systems.
Do not use more that one period in a file name. Requirement for ISO-9660, OpenVMS native filesystem, and + other legacy systems.
Do not use period, hyphen, or underscore as the first + character of a file or directory name. Some operating systems treat have special rules for the + first character of names. POSIX, for example.
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 + 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 open successfully with the name of "foo". Some filesystems are case sensitive.  For example, POSIX.
Limit the length of names and the depth of the directory + tree so that the total length of the string returned by generic_path() to + 255 characters.  + Note that ISO 9660 level 1 has an explicit directory tree depth limit of 8. Some operating systems place limits on the total path length.  For example, + Windows 2000 limits paths to 260 characters total length.
Limit the length of any one name in a path.  Pick the specific limit according to + the operating systems you wish portability to:
+   Greater than 31 characters:  POSIX, Windows, MAC OS X.
+   31 characters: Classic Mac OS
+  8 characters + period + 3 characters: ISO 9660 (CD-ROM)
+ ...
 Limiting name length can markedly reduce the expressiveness of file names, yet placing + only very high limits on lengths inhibits wide portability.
+ +
+

© Copyright Beman Dawes, 2002

+

Revised +13 September, 2002

+ + + + \ No newline at end of file diff --git a/include/boost/filesystem/exception.hpp b/include/boost/filesystem/exception.hpp new file mode 100644 index 000000000..6c4014685 --- /dev/null +++ b/include/boost/filesystem/exception.hpp @@ -0,0 +1,63 @@ +// boost/filesystem/exception.hpp ------------------------------------------// + +// < ----------------------------------------------------------------------- > +// < Copyright © 2001 Dietmar Kühl, All Rights Reserved > +// < > +// < Permission to use, copy, modify, distribute and sell this > +// < software for any purpose is hereby granted without fee, provided > +// < that the above copyright notice appears in all copies and that > +// < both that copyright notice and this permission notice appear in > +// < supporting documentation. Dietmar Kühl makes no representations about > +// < the suitability of this software for any purpose. It is provided > +// < "as is" without express or implied warranty. > +// < ----------------------------------------------------------------------- > + +// Original author: Dietmar Kühl. Revised by Beman Dawes. + +// See http://www.boost.org for most recent version including documentation. + +//----------------------------------------------------------------------------// + +#ifndef BOOST_FILESYSTEM_EXCEPTION_HPP +#define BOOST_FILESYSTEM_EXCEPTION_HPP + +#include +#include + +//----------------------------------------------------------------------------// + +namespace boost +{ + namespace filesystem + { + enum error_type { system_error }; + + class filesystem_error: + public std::runtime_error + { + public: + + explicit filesystem_error( std::string const& msg ); + // Effects: : std::runtime_error(implementation-defined), + // m_msg(msg), m_err(0) + + explicit filesystem_error( std::string const& msg, error_type ); + // Effects: : std::runtime_error(implementation-defined), + // m_msg(msg), m_err(value), where value is appropriate + // for the operating system (for example, GetLastError() on Windows, + // errno on POSIX) + + ~filesystem_error() throw(); + char const* what() const throw(); + int error() const { return m_err; } + // Note: a value of 0 implies an internal (rather than system) error + + private: + mutable std::string m_msg; + mutable int m_err; + }; + + } // namespace filesystem +} // namespace boost + +#endif // BOOST_FILESYSTEM_EXCEPTION_HPP diff --git a/include/boost/filesystem/fstream.hpp b/include/boost/filesystem/fstream.hpp new file mode 100644 index 000000000..1977f7565 --- /dev/null +++ b/include/boost/filesystem/fstream.hpp @@ -0,0 +1,116 @@ +// boost/filesystem/fstream.hpp --------------------------------------------// + +// (C) Copyright Beman Dawes 2002. 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. + +// See http://www.boost.org for most recent version including documentation. + +//----------------------------------------------------------------------------// + +#ifndef BOOST_FILESYSTEM_FSTREAM_HPP +#define BOOST_FILESYSTEM_FSTREAM_HPP + +#include + +#include +#include + +namespace boost +{ + namespace filesystem + { + template < class charT, class traits = std::char_traits > + class basic_filebuf : public std::basic_filebuf + { + public: + virtual ~basic_filebuf() {} + + std::basic_filebuf * open( const path & file_ph, + std::ios_base::openmode mode ) + { + return std::basic_filebuf::open( + file_ph.file_path().c_str(), mode ); + } + }; + + typedef basic_filebuf filebuf; +# ifndef BOOST_NO_STD_WSTRING + typedef basic_filebuf wfilebuf; +# endif + + template < class charT, class traits = std::char_traits > + class basic_ifstream : public std::basic_ifstream + { + public: + basic_ifstream() {} + explicit basic_ifstream( const path & file_ph, + std::ios_base::openmode mode = std::ios_base::in ) + : std::basic_ifstream( + file_ph.file_path().c_str(), mode ) {} + virtual ~basic_ifstream() {} + void open( const path & file_ph, + std::ios_base::openmode mode = std::ios_base::in ) + { + std::basic_ifstream::open( + file_ph.file_path().c_str(), mode ); + } + }; + + 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 + { + public: + basic_ofstream() {} + explicit basic_ofstream( const path & file_ph, + std::ios_base::openmode mode = std::ios_base::out ) + : std::basic_ofstream( + file_ph.file_path().c_str(), mode ) {} + virtual ~basic_ofstream() {} + void open( const path & file_ph, + std::ios_base::openmode mode = std::ios_base::out ) + { + std::basic_ofstream::open( + file_ph.file_path().c_str(), mode ); + } + }; + + 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 + { + public: + basic_fstream() {} + 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.file_path().c_str(), mode ) {} + virtual ~basic_fstream() {} + 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.file_path().c_str(), mode ); + } + }; + + typedef basic_fstream fstream; +# ifndef BOOST_NO_STD_WSTRING + typedef basic_fstream wfstream; +# endif + } // namespace filesystem + +} // namespace boost + +#endif // BOOST_FILESYSTEM_FSTREAM_HPP + diff --git a/include/boost/filesystem/operations.hpp b/include/boost/filesystem/operations.hpp new file mode 100644 index 000000000..e90009fd8 --- /dev/null +++ b/include/boost/filesystem/operations.hpp @@ -0,0 +1,129 @@ +// boost/filesystem/directory.hpp ------------------------------------------// + +// < ----------------------------------------------------------------------- > +// < Copyright © 2002 Beman Dawes. > +// < Copyright © 2002 Jan Langer. > +// < Copyright © 2001 Dietmar Kühl, All Rights Reserved > +// < > +// < Permission to use, copy, modify, distribute and sell this > +// < software for any purpose is hereby granted without fee, provided > +// < that the above copyright notice appears in all copies and that > +// < both that copyright notice and this permission notice appear in > +// < supporting documentation. The authors make no representations about > +// < the suitability of this software for any purpose. It is provided > +// < "as is" without express or implied warranty. > +// < ----------------------------------------------------------------------- > + +// See http://www.boost.org for most recent version including documentation. + +//----------------------------------------------------------------------------// + +#ifndef BOOST_FILESYSTEM_DIRECTORY_HPP +#define BOOST_FILESYSTEM_DIRECTORY_HPP + +#include +#include +#include +#include + +#include + +//----------------------------------------------------------------------------// + +namespace boost +{ + namespace filesystem + { + +// query functions ---------------------------------------------------------// + + bool exists( const path & ph ); + + bool is_directory( const path & ph ); + + // 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. + bool _is_empty( const path & ph ); // deprecated + +# if !defined( BOOST_MSVC ) || BOOST_MSVC > 1300 + inline bool is_empty( const path & ph ) { return _is_empty( ph ); } +# endif + +// operations --------------------------------------------------------------// + + void create_directory( const path & directory_ph ); + + void remove( const path & ph ); + + void rename( const path & from_path, + const path & to_path ); + + unsigned long remove_all( const path & ph ); + + void copy_file( const path & from_file_ph, + const path & to_file_ph ); + + const path & initial_directory(); + +// directory_iterator details ----------------------------------------------// + + namespace detail + { + const char * implementation_name(); + + class directory_iterator_imp; + + struct directory_iterator_internals + { + typedef boost::shared_ptr< detail::directory_iterator_imp > dii_ptr; + dii_ptr imp; + + const path & deref() const; + void inc(); + }; + + struct dii_policies : public default_iterator_policies + { + + template + typename IteratorAdaptor::reference + dereference(const IteratorAdaptor& x) const + { return x.base().deref(); } + + template + void increment(IteratorAdaptor& x) + { x.base().inc(); } + + template + bool equal(const IteratorAdaptor1& x, const IteratorAdaptor2& y) const + { return x.base().imp == y.base().imp; } + }; + } + +// directory_iterator ------------------------------------------------------// + + class directory_iterator + : public boost::iterator_adaptor< + // because directory_iterator is an InputIterator, shallow copy- + // semantics are required, and shared_ptr provides that. + detail::directory_iterator_internals, + detail::dii_policies, + path, const path &, const path *, + std::input_iterator_tag, std::ptrdiff_t > + { + public: + directory_iterator(); // creates the "end" iterator + explicit directory_iterator( const path & directory_path ); + + // workaround iterator_adaptor / compiler interactions + const boost::filesystem::path * operator->() const + { return &base().deref(); } + }; + + } // namespace filesystem +} // namespace boost + +#endif // BOOST_FILESYSTEM_DIRECTORY_HPP diff --git a/include/boost/filesystem/path.hpp b/include/boost/filesystem/path.hpp new file mode 100644 index 000000000..0a8135a77 --- /dev/null +++ b/include/boost/filesystem/path.hpp @@ -0,0 +1,198 @@ +// boost/filesystem/path.hpp -----------------------------------------------// + +// (C) Copyright Beman Dawes 2002. 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. + + +// See http://www.boost.org for most recent version including documentation. + +//----------------------------------------------------------------------------// + +#ifndef BOOST_FILESYSTEM_PATH_HPP +#define BOOST_FILESYSTEM_PATH_HPP + +#include +#include +#include + +//----------------------------------------------------------------------------// + +namespace boost +{ + namespace filesystem + { + class path; + + namespace detail + { + struct path_itr_imp + { + std::string name; // cache current element. + const path * path_ptr; // path being iterated over. + std::string::size_type pos; // position of name in + // path_ptr->generic_path(). The + // end() iterator is indicated by + // pos == path_ptr->generic_path().size() + + const std::string & operator*() const { return name; } + void operator++(); + void operator--(); + bool operator==( const path_itr_imp & rhs ) const + { return path_ptr == rhs.path_ptr && pos == rhs.pos; } + }; + } + + enum path_format { system_specific }; // ugly enough to discourage use + // except when actually needed + + // path -------------------------------------------------------------------// + + class path + { + public: + // compiler generates copy constructor, copy assignment, and destructor + + path(){} + + path( const std::string & src ); + path( const char * src ); + + path( const std::string & src, path_format ); + path( const char * src, path_format ); + + // append operations: + path & operator <<=( const path & rhs ); + const path operator << ( const path & rhs ) const + { return path( *this ) <<= rhs; } + + // query functions: + bool is_null() const { return m_path.size() == 0; } + + const std::string & generic_path() const { return m_path; } + const std::string & file_path() const { return m_path; } // native format + const std::string & directory_path() const { return m_path; } // ditto + + const std::string leaf() const; + const path branch() const; + + // iteration over the names in the path: + typedef boost::iterator_adaptor< + detail::path_itr_imp, + boost::default_iterator_policies, + std::string, + const std::string &, + const std::string *, + std::bidirectional_iterator_tag, + std::ptrdiff_t + > iterator; + + const iterator begin() const; + const iterator end() const + { + iterator itr; + itr.base().path_ptr = this; + itr.base().pos = m_path.size(); + return itr; + } + + 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 file_path() and directory_path() formats. + + std::string m_path; + + friend class directory_iterator; + friend struct boost::filesystem::detail::path_itr_imp; + + enum source_context { generic, platform, nocheck }; + + void m_path_append( const std::string & src, + source_context context = generic ); + + public: // should be private, but friend functions don't work for me + void m_replace_leaf( const char * new_leaf ); + }; + + // path non-member functions ---------------------------------------------// + + inline const path operator << ( const char * lhs, const path & rhs ) + { return path( lhs ) <<= rhs; } + + inline const path operator << ( const std::string & lhs, const path & rhs ) + { return path( lhs ) <<= rhs; } + + // error checking --------------------------------------------------------// + +// TODO: write a program that probes valid file and directory names. Ask +// Boost people to report results from many operating systems. Use results +// to adjust generic_name(). + + // generic_name() is extremely permissive; its intent is not to ensure + // general portablity, but rather to detect names so trouble-prone that + // they likely represent coding errors or gross misconceptions. + // + // Any characters are allowed except: + // + // Those characters < ' ', including '\0'. These are the so-called + // control characters, in both ASCII (and its decendents) and EBCDIC. + // Hard to imagine how these could be useful in a generic path name. + // + // < > : " / \ | * ? These have special meaning to enough operating + // systems that use in a generic name would be a serious problem. + // + // The names "." and ".." are not allowed. + // An empty name (null string) is not allowed. + // Names beginning or ending with spaces are not allowed. + // + bool generic_name( const std::string & name ); + + // posix_name() is based on POSIX (IEEE Std 1003.1-2001) + // "Portable Filename Character Set" rules. + // http://www.opengroup.org/onlinepubs/007904975/basedefs/xbd_chap03.html + // + // That character set only allows 0-9, a-z, A-Z, '.', '_', and '-'. + // Note that such names are also portable to other popular operating + // systems, such as Windows. + // + bool posix_name( const std::string & name ); + + const path & check_posix_leaf( const path & ph ); + // Throws: if !posix_name( ph.leaf() ) + // Returns: ph + // Note: Although useful in its own right, check_posix_leaf() also serves + // as an example. A user might provide similar functions; behavior might + // be to assert or warn rather than throw. A user provided function + // could also check the entire path instead of just the leaf; a leaf + // check is often, but not always, the required behavior. + // Rationale: For the "const path &" rather than "void" return is to + // allow (and encourage portability checking) uses like: + // create_directory( check_posix_leaf( "foo" ) ); + // While there is some chance of misuse (by passing through a reference + // to a temporary), the benefits outweigh the costs. + + // For Boost, we often tighten name restrictions for maximum portability: + // + // * The portable POSIX character restrictions, plus + // * Maximum name length 31 characters (for Classic Mac OS). + // * Lowercase only (so code written on case-insensitive platforms like + // Windows works properly when used on case-sensitive systems like + // POSIX. + // * Directory names do not contain '.', as this is not a valid character + // for directory names on some systems. + // + // TODO: provide some check_boost_xxx functions once error handling + // approach ratified. + + bool boost_file_name( const std::string & name ); + bool boost_directory_name( const std::string & name ); + + } // namespace filesystem +} // namespace boost + +#endif // BOOST_FILESYSTEM_PATH_HPP diff --git a/include/boost/filesystem/recursive_directory_iterator.hpp b/include/boost/filesystem/recursive_directory_iterator.hpp new file mode 100644 index 000000000..bf68ca1af --- /dev/null +++ b/include/boost/filesystem/recursive_directory_iterator.hpp @@ -0,0 +1,116 @@ +// boost/filesystem/recursive_directory_iterator.hpp -----------------------// + +// (C) Copyright Beman Dawes 2002. 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. + +// See http://www.boost.org for most recent version including documentation. + +#ifndef BOOST_FILESYSTEM_RECURSIVE_DIRECTORY_ITERATOR_HPP +#define BOOST_FILESYSTEM_RECURSIVE_DIRECTORY_ITERATOR_HPP + +#include +#include + +#include +#include +//#include + +//----------------------------------------------------------------------------// + +namespace boost +{ + namespace filesystem + { + namespace detail + { + const directory_iterator end_itr; + + template< typename Predicate > + struct rdi_imp + { + std::stack< directory_iterator, std::vector > + stac; + Predicate pred; + + rdi_imp( const path & pth, Predicate pred ) + : pred(pred) + { + stac.push( directory_iterator( pth ) ); + while ( stac.top() != end_itr + && !pred( *stac.top() ) ) ++stac.top(); + } + + rdi_imp() + : pred( Predicate() ) + { + stac.push( directory_iterator() ); + } + + void increment() + { + if ( is_directory( *stac.top() ) ) + stac.push( directory_iterator( *stac.top() ) ); + else ++stac.top(); + for (;;) // 'til end or pred true + { + while ( stac.top() != end_itr && !pred( *stac.top() ) ) + { ++stac.top(); } + if ( stac.top() != end_itr || stac.size() == 1 ) return; + stac.pop(); + ++stac.top(); + } + } + }; + + struct rdi_policies : public default_iterator_policies + { + + template + typename IteratorAdaptor::reference dereference(const IteratorAdaptor& x) const + { + return *x.base()->stac.top(); + } + + template + void increment(IteratorAdaptor& x) { x.base()->increment(); } + + template + bool equal(const IteratorAdaptor1& x, const IteratorAdaptor2& y) const + { + return x.base()->stac.top() == y.base()->stac.top(); + } + }; + + } // namespace detail + + template< typename Predicate > + class recursive_directory_iterator + : public boost::iterator_adaptor< + boost::shared_ptr< detail::rdi_imp< Predicate > >, + detail::rdi_policies, + boost::filesystem::path, + const boost::filesystem::path &, + const boost::filesystem::path *, + std::input_iterator_tag, + std::ptrdiff_t > + { + public: + recursive_directory_iterator( const path & pth, Predicate pred ) + { + this->base().reset( new detail::rdi_imp( pth, pred ) ); + } + recursive_directory_iterator() // the past-the-end iterator + { + this->base().reset( new detail::rdi_imp ); + } + + // workaround iterator_adaptor / compiler interactions + const boost::filesystem::path * operator->() const + { return &*this->base()->stac.top(); } + }; + } +} + +#endif // BOOST_FILESYSTEM_RECURSIVE_DIRECTORY_ITERATOR_HPP diff --git a/index.html b/index.html new file mode 100644 index 000000000..6576c8814 --- /dev/null +++ b/index.html @@ -0,0 +1,9 @@ + + + + + +Automatic redirection failed, please go to +doc/index.htm. + + \ No newline at end of file diff --git a/src/exception.cpp b/src/exception.cpp new file mode 100644 index 000000000..9cd177ac7 --- /dev/null +++ b/src/exception.cpp @@ -0,0 +1,104 @@ +// Exception implementation file -------------------------------------------// + +// < ----------------------------------------------------------------------- > +// < Copyright © 2001 Dietmar Kühl, All Rights Reserved > +// < > +// < Permission to use, copy, modify, distribute and sell this > +// < software for any purpose is hereby granted without fee, provided > +// < that the above copyright notice appears in all copies and that > +// < both that copyright notice and this permission notice appear in > +// < supporting documentation. Dietmar Kühl makes no representations about > +// < the suitability of this software for any purpose. It is provided > +// < "as is" without express or implied warranty. > +// < ----------------------------------------------------------------------- > + +// Original author: Dietmar Kühl. Revised by Beman Dawes. + +// See http://www.boost.org for most recent version including documentation. + +//----------------------------------------------------------------------------// + +#include +#include + +#include +#include + +# ifdef BOOST_NO_STDC_NAMESPACE + namespace std { using ::strerror; } +# endif + +// BOOST_POSIX or BOOST_WINDOWS specify which API to use, not the current +// operating system. GCC defaults to BOOST_POSIX; it doesn't predefine _WIN32. + +# if !defined( BOOST_WINDOWS ) && !defined( BOOST_POSIX ) +# if defined(_WIN32) +# define BOOST_WINDOWS +# else +# define BOOST_POSIX +# endif +# endif + +# if defined( BOOST_WINDOWS ) +# include "windows.h" +# endif + +//----------------------------------------------------------------------------// + +namespace boost +{ + namespace filesystem + { + + filesystem_error::filesystem_error( std::string const& msg ): + std::runtime_error("filesystem error"), + m_msg(msg), + m_err(0) + { + } + + filesystem_error::filesystem_error( std::string const& msg, error_type ): + std::runtime_error("filesystem error"), + m_msg(msg), +# ifdef BOOST_WINDOWS + m_err( ::GetLastError() ) +# else + m_err(errno) // GCC 3.1 won't accept ::errno +# endif + { + } + + filesystem_error::~filesystem_error() throw() + { + } + + char const* filesystem_error::what() const throw() + { + if (m_err) + { + m_msg += ": "; +# ifdef BOOST_WINDOWS + LPVOID lpMsgBuf; + ::FormatMessageA( + FORMAT_MESSAGE_ALLOCATE_BUFFER | + FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, + m_err, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language + (LPSTR) &lpMsgBuf, + 0, + NULL + ); + m_msg += static_cast(lpMsgBuf); + LocalFree( lpMsgBuf ); // free the buffer +# else + m_msg += std::strerror(m_err); +# endif + m_err = 0; + } + return m_msg.c_str(); + } + } // namespace filesystem +} // namespace boost + diff --git a/src/operations_posix_windows.cpp b/src/operations_posix_windows.cpp new file mode 100644 index 000000000..b033ae2b5 --- /dev/null +++ b/src/operations_posix_windows.cpp @@ -0,0 +1,424 @@ +// directory_posix_windows.cpp ---------------------------------------------// + +// < ----------------------------------------------------------------------- > +// < Copyright © 2002 Beman Dawes. > +// < Copyright © 2001 Dietmar Kühl, All Rights Reserved > +// < > +// < Permission to use, copy, modify, distribute and sell this > +// < software for any purpose is hereby granted without fee, provided > +// < that the above copyright notice appears in all copies and that > +// < both that copyright notice and this permission notice appear in > +// < supporting documentation. The authors make no representations about > +// < the suitability of this software for any purpose. It is provided > +// < "as is" without expressed or implied warranty. > +// < ----------------------------------------------------------------------- > + +// See http://www.boost.org for most recent version including documentation. + +//----------------------------------------------------------------------------// + + +// The point of this implementation is to prove the interface. There is no +// claim the implementation is efficient, follows good coding practice, etc. + + +//----------------------------------------------------------------------------// + +#include +#include +#include +//#include + +namespace fs = boost::filesystem; + +// BOOST_POSIX or BOOST_WINDOWS specify which API to use, not the current +// operating system. GCC defaults to BOOST_POSIX; it doesn't predefine _WIN32. + +# if defined(BOOST_WINDOWS) || (!defined(BOOST_POSIX) && defined(_WIN32)) +# include "windows.h" +# else +# define BOOST_POSIX +# include "sys/stat.h" +# include "dirent.h" +# include "unistd.h" +# include "fcntl.h" +# endif + +#include +#include +#include +#include + +// 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 ); + } + + inline const char * find_next_file( + BOOST_HANDLE handle, BOOST_SYSTEM_DIRECTORY_TYPE & ) + // Returns: if EOF 0, otherwise name + // Throws: if system reports error + { + +// TODO: use readdir_r() if available, so code is multi-thread safe. +// Fly-in-ointment: not all platforms support readdir_r(). + + struct dirent * dp; + errno = 0; + if ( (dp = ::readdir( handle )) == 0 ) + { + if ( errno != 0 ) + { + throw fs::filesystem_error( "directory_iterator ++() failure" ); + } + else { return 0; } // end reached + } + return dp->d_name; + } +#else // BOOST_WINDOWS + +# define BOOST_HANDLE HANDLE +# define BOOST_INVALID_HANDLE_VALUE INVALID_HANDLE_VALUE +# define BOOST_SYSTEM_DIRECTORY_TYPE WIN32_FIND_DATA + + inline const char * find_first_file( const char * dir, + BOOST_HANDLE & handle, BOOST_SYSTEM_DIRECTORY_TYPE & data ) + // Returns: 0 if error, otherwise name + { + std::string dirpath( std::string(dir) + "/*" ); + return ( (handle = ::FindFirstFile( dirpath.c_str(), &data )) + == BOOST_INVALID_HANDLE_VALUE ) ? 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, BOOST_SYSTEM_DIRECTORY_TYPE & data ) + // Returns: 0 if EOF, otherwise name + // Throws: if system reports error + { + if ( ::FindNextFile( handle, &data ) == 0 ) + { + if ( ::GetLastError() != ERROR_NO_MORE_FILES ) + { + throw fs::filesystem_error( + "directory_iterator ++() failure", fs::system_error ); + } + 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::is_directory( ph ) ) + { + for ( boost::filesystem::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 + { +#ifdef BOOST_POSIX + const char * implementation_name() { return "POSIX"; } +#else + const char * implementation_name() { return "Windows"; } +#endif + +// directory_iterator_imp --------------------------------------------------// + + class directory_iterator_imp + { + public: + path entry_path; + BOOST_HANDLE handle; + + ~directory_iterator_imp() + { + if ( handle != BOOST_INVALID_HANDLE_VALUE ) find_close( handle ); + } + }; + + } // namespace detail + +// directory_iterator implementation ---------------------------------------// + + // default ctor creates the "end" iterator (by letting base default to 0) + directory_iterator::directory_iterator() {} + + directory_iterator::directory_iterator( const path & dir_path ) + { + base().imp.reset( new detail::directory_iterator_imp ); + BOOST_SYSTEM_DIRECTORY_TYPE scratch; + const char * name; + if ( dir_path.is_null() ) + base().imp->handle = BOOST_INVALID_HANDLE_VALUE; + else + name = find_first_file( dir_path.directory_path().c_str(), + base().imp->handle, scratch ); // sets handle + + if ( base().imp->handle != BOOST_INVALID_HANDLE_VALUE ) + { + base().imp->entry_path = dir_path; + base().imp->entry_path.m_path_append( name, path::nocheck ); + while ( base().imp.get() + && ( base().imp->entry_path.leaf() == "." + || base().imp->entry_path.leaf() == ".." ) ) + { operator++(); } + } + else + { + throw filesystem_error( std::string( + "directory_iterator constructor failure: " ) + + dir_path.directory_path().c_str(), system_error ); + } + } + + namespace detail + { + path const & directory_iterator_internals::deref() const + { + assert( imp.get() ); // fails if dereference end iterator + return imp->entry_path; + } + + void directory_iterator_internals::inc() + { + assert( imp.get() ); // fails on increment end iterator + assert( imp->handle != BOOST_INVALID_HANDLE_VALUE ); // imp reality check + + BOOST_SYSTEM_DIRECTORY_TYPE scratch; + const char * name; + if ( (name = find_next_file( imp->handle, scratch )) != 0 ) + { + imp->entry_path.m_replace_leaf( name ); + } + else { + imp.reset(); // make base() the end iterator + } + } + } + +// free functions ----------------------------------------------------------// + + bool exists( const path & ph ) + { +# ifdef BOOST_POSIX + struct stat path_stat; + return ::stat( ph.file_path().c_str(), &path_stat ) == 0; +# else + return ::GetFileAttributes( ph.file_path().c_str() ) != 0xFFFFFFFF; +# endif + } + + bool is_directory( const path & ph ) + { +# ifdef BOOST_POSIX + struct stat path_stat; + if ( ::stat( ph.directory_path().c_str(), &path_stat ) != 0 ) + throw filesystem_error( std::string("is_directory(): ") + + ph.directory_path().c_str(), system_error ); + return S_ISDIR( path_stat.st_mode ); +# else + DWORD attributes = ::GetFileAttributes( ph.directory_path().c_str() ); + if ( attributes == 0xFFFFFFFF ) + throw filesystem_error( std::string("is_directory(): ") + + ph.directory_path().c_str(), system_error ); + return (attributes & FILE_ATTRIBUTE_DIRECTORY) != 0; +# endif + } + + bool _is_empty( const path & ph ) + { +# ifdef BOOST_POSIX + struct stat path_stat; + if ( ::stat( ph.file_path().c_str(), &path_stat ) != 0 ) + throw filesystem_error( std::string("is_empty(): ") + + ph.file_path().c_str(), system_error ); + + return S_ISDIR( path_stat.st_mode ) + ? is_empty_directory( ph ) + : path_stat.st_size == 0; +# else + WIN32_FILE_ATTRIBUTE_DATA fad; + if ( !::GetFileAttributesEx( ph.file_path().c_str(), + ::GetFileExInfoStandard, &fad ) ) + throw filesystem_error( std::string("is_empty(): ") + + ph.file_path().c_str(), system_error ); + + return ( fad.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) + ? is_empty_directory( ph ) + :( !fad.nFileSizeHigh && !fad.nFileSizeLow ); +# endif + } + + void create_directory( const path & dir_path ) + { +# ifdef BOOST_POSIX + if ( ::mkdir( dir_path.directory_path().c_str(), + S_IRWXU|S_IRWXG|S_IRWXO ) != 0 ) +# else + if ( !::CreateDirectory( dir_path.directory_path().c_str(), 0 ) ) +# endif + throw filesystem_error( std::string("create_directory(): ") + + dir_path.directory_path().c_str(), system_error ); + } + + void remove( const path & ph ) + { + if ( exists( ph ) ) + { + if ( is_directory( ph ) ) + { +# ifdef BOOST_POSIX + if ( ::rmdir( ph.file_path().c_str() ) != 0 ) +# else + if ( !::RemoveDirectory( ph.file_path().c_str() ) ) +# endif + throw fs::filesystem_error( std::string("remove() on: ") + + ph.file_path().c_str(), system_error ); + } + else + { +# ifdef BOOST_POSIX + if ( ::remove( ph.file_path().c_str() ) != 0 ) +# else + if ( !::DeleteFile( ph.file_path().c_str() ) ) +# endif + throw fs::filesystem_error( std::string("remove() on: ") + + ph.file_path().c_str(), system_error ); + } + } + } + + unsigned long remove_all( const path & ph ) + { + return exists( ph ) ? remove_all_aux( ph ) : 0; + } + + void rename( const path & old_path, + const path & new_path ) + { +# ifdef BOOST_POSIX + if ( exists( new_path ) // POSIX is too permissive so must check + || ::rename( old_path.file_path().c_str(), new_path.file_path().c_str() ) != 0 ) +# else + if ( !::MoveFile( old_path.file_path().c_str(), new_path.file_path().c_str() ) ) +# endif + throw filesystem_error( std::string("move_file(): ") + + old_path.file_path().c_str() + ", " + new_path.file_path().c_str(), system_error ); + } + + void copy_file( const path & from_file_ph, + const path & to_file_ph ) + { +# ifdef BOOST_POSIX + // TODO: Ask POSIX experts if this is the best way to copy a file + + const std::size_t buf_sz = 32768; + boost::scoped_array buf( new char [buf_sz] ); + int infile, outfile; + + if ( (infile = ::open( from_file_ph.file_path().c_str(), + O_RDONLY )) < 0 + || (outfile = ::open( to_file_ph.file_path().c_str(), + O_WRONLY | O_CREAT | O_EXCL, + S_IRWXU|S_IRWXG|S_IRWXO )) < 0 ) + { + if ( infile != 0 ) ::close( infile ); + throw fs::filesystem_error( std::string("copy() files: ") + + from_file_ph.file_path().c_str() + + ", " + to_file_ph.file_path().c_str(), system_error ); + } + + ssize_t sz; + while ( (sz = ::read( infile, buf.get(), buf_sz )) > 0 + && (sz = ::write( outfile, buf.get(), sz )) > 0 ) {} + + ::close( infile ); + ::close( outfile ); + + if ( sz != 0 ) +# else + if ( !::CopyFile( from_file_ph.file_path().c_str(), + to_file_ph.file_path().c_str(), /*fail_if_exists=*/true ) ) +# endif + throw fs::filesystem_error( std::string("copy() files: ") + + from_file_ph.file_path().c_str() + + ", " + to_file_ph.file_path().c_str(), system_error ); + } + + const path & initial_directory() + { + static path init_dir; + if ( init_dir.is_null() ) + { +# ifdef BOOST_POSIX + long path_max = ::pathconf( ".", _PC_PATH_MAX ); + if ( path_max == -1 ) + throw filesystem_error( "initial_directory()" ); + boost::scoped_array + buf( new char[static_cast(path_max)] ); + if ( ::getcwd( buf.get(), static_cast(path_max) ) == 0 ) +# else + DWORD sz; + if ( (sz = ::GetCurrentDirectory( 0, static_cast(0) )) == 0 ) + throw filesystem_error( "initial_directory()" ); + boost::scoped_array buf( new char[sz] ); + if ( ::GetCurrentDirectory( sz, buf.get() ) == 0 ) +# endif + { throw filesystem_error( "initial_directory()", system_error ); } + init_dir = path( buf.get(), system_specific ); + } + return init_dir; + } + + } // namespace filesystem +} // namespace boost + diff --git a/src/path_posix_windows.cpp b/src/path_posix_windows.cpp new file mode 100644 index 000000000..c07acee5b --- /dev/null +++ b/src/path_posix_windows.cpp @@ -0,0 +1,396 @@ +// path implementation -----------------------------------------------------// + +// (C) Copyright Beman Dawes 2002. 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. + +// See http://www.boost.org for most recent version including documentation. + + +//****************************************************************************// + +// 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. + +//****************************************************************************// + +// BOOST_POSIX or BOOST_WINDOWS specify which API to use, not the current +// operating system. GCC defaults to BOOST_POSIX; it doesn't predefine _WIN32. + +# if !defined( BOOST_WINDOWS ) && !defined( BOOST_POSIX ) +# if defined(_WIN32) +# define BOOST_WINDOWS +# else +# define BOOST_POSIX +# endif +# endif + +#include +#include + +namespace fs = boost::filesystem; + +#include +#ifdef BOOST_NO_STDC_NAMESPACE + namespace std { using ::strlen; } +#endif + +#include +#include + +// helpers -----------------------------------------------------------------// + +namespace +{ + // POSIX & Windows cases: "", "/", "/foo", "foo", "foo/bar" + // Windows only cases: "c:", "c:/", "c:foo", "c:/foo", + // "prn:", "//share", "//share/foo" + + std::string::size_type leaf_pos( const std::string & str, + std::string::size_type end_pos ) // end_pos is past-the-end position + // return 0 if str itself is leaf (or empty) + { + if ( (end_pos == 1 && str[0] == '/') +# ifdef BOOST_WINDOWS + || (end_pos > 1 // drive or device + && (str[end_pos-1] == ':' || str[end_pos-2] == ':')) +# endif + ) return 0; + + std::string::size_type pos( str.find_last_of( '/', end_pos-1 ) ); +# ifdef BOOST_WINDOWS + if ( pos == std::string::npos ) pos = str.find_last_of( ':', end_pos-1 ); +# 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++; + if ( itr == src.end() ) return; + 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 invalid_generic( invalid_chars, sizeof(invalid_chars) ); + + const std::string valid_posix( + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789._-" ); + + const std::string valid_boost_file( + "abcdefghijklmnopqrstuvwxyz0123456789._-" ); + const std::string valid_boost_directory( + "abcdefghijklmnopqrstuvwxyz0123456789_-" ); + +} // unnamed namespace + +//----------------------------------------------------------------------------// + +namespace boost +{ + namespace filesystem + { + + // error checking functions --------------------------------------------// + + + bool generic_name( const std::string & name ) + { + return name.size() != 0 + && name.find_first_of( invalid_generic ) == std::string::npos + && name != "." + && name != ".." + && *name.begin() != ' ' + && *(name.end()-1) != ' '; + } + + bool posix_name( const std::string & name ) + { + return name.find_first_not_of( valid_posix ) == std::string::npos + && name != "."; + } + + const path & check_posix_leaf( const path & ph ) + { + if ( !posix_name( ph.leaf() ) ) + throw filesystem_error( "invalid posix name: \"" + + ph.leaf() + "\"" ); + return ph; + } + + bool boost_file_name( const std::string & name ) + { + return name.size() <= 31 + && name.find_first_not_of( valid_boost_file ) == std::string::npos + && name != "."; + } + + bool boost_directory_name( const std::string & name ) + { + return name.size() <= 31 + && name.find_first_not_of( valid_boost_directory ) == std::string::npos; + } + + // path implementation -------------------------------------------------// + + path::path( const std::string & src ) + { + m_path_append( src ); + } + + path::path( const char * src ) + { + m_path_append( src ); + } + + path::path( const std::string & src, path_format ) + { + m_path_append( src, platform ); + } + + path::path( const char * src, path_format ) + { + m_path_append( src, platform ); + } + + path & path::operator <<=( const path & rhs ) + { + m_path_append( rhs.m_path, nocheck ); + return *this; + } + + void path::m_path_append( const std::string & src, source_context context ) + { + // convert backslash to forward slash if context is "platform" + // check names if context is "generic" + // allow system-specific-root if context is not "generic" + + assert( src.size() == std::strlen( src.c_str() ) ); // no embedded 0 + + if ( src.size() == 0 ) return; + + std::string::const_iterator itr( src.begin() ); + + // system-specific-root like drive: or first slash of //share + if ( context != generic ) + { + if ( *itr == '/' +# ifdef BOOST_WINDOWS + || (*itr == '\\' && context == platform) +# endif + ) { ++itr; m_path += '/'; } + else if ( itr+1 != src.end() && *(itr+1) == ':' ) + { m_path += *itr++; m_path += *itr++; } + +# ifdef BOOST_WINDOWS + // "/" or second slash of //share + if ( *itr == '/' || (*itr == '\\' && context == platform) ) + { ++itr; m_path += '/';} +# endif + + if ( itr == src.end() ) return; + } + + // relative-path ::= element { "/" element } + while ( itr != src.end() ) + { + // append '/' if needed + if ( !is_null() // append '/' + && *(m_path.end()-1) != ':' && *(m_path.end()-1) != '/' ) + m_path += '/'; + + if ( *itr == '.'&& (itr+1) != src.end() && *(itr+1) == '.' ) // ".." + { + if ( m_path.size() >= 2 // there is a named parent directory present + && *(m_path.end()-1) == '/' +# ifdef BOOST_WINDOWS + && *(m_path.end()-2) != ':' +# endif + && *(m_path.end()-2) != '.' ) + { + // reference to parent so erase child + std::string::iterator child( m_path.end()-2 ); + + while ( child != m_path.begin() && *child != '/' +# ifdef BOOST_WINDOWS + && *child != ':' +# endif + ) --child; + + // only erase '/' if it is a separator rather than part of the root + if ( (*child == '/' + && (child == m_path.begin() +# ifdef BOOST_WINDOWS + || *(child-1) == ':')) + || *child == ':' +# else + )) +# endif + ) ++child; + + m_path.erase( child, m_path.end() ); + } + else { m_path += ".."; } + ++itr; + ++itr; + } // ".." + else // name + { + std::string name; + do + { name += *itr; } + while ( ++itr != src.end() && *itr != '/' +# ifdef BOOST_WINDOWS + && (*itr != '\\' || context != platform) +# endif + ); + + if ( context == generic && !generic_name( name ) ) + { + throw filesystem_error( "invalid path name: \"" + + src + "\"" ); + } + + m_path += name; + } + + if ( itr != src.end() ) + { + if ( !( (*itr == '/' +# ifdef BOOST_WINDOWS + || (context == platform && *itr == '\\') +# endif + ) && (itr+1) != src.end() ) ) + { + throw filesystem_error( "invalid path syntax: \"" + + src + "\"" ); + } + ++itr; + } + + } // while more elements + } + + const path::iterator path::begin() const + { + iterator itr; + itr.base().path_ptr = this; + first_name( m_path, itr.base().name ); + itr.base().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; + } + + const std::string path::leaf() const + { + return m_path.substr( leaf_pos( m_path, m_path.size() ) ); + } + + const path path::branch() const + { + std::string::size_type len( leaf_pos( m_path, m_path.size() ) ); + + if ( len > 1 // unless delimiter is part of root, don't include it +# ifdef BOOST_WINDOWS + && m_path[len-1] != ':' + && m_path[len-2] != ':' +# endif + ) --len; + + return path( m_path.substr( 0, len ), system_specific ); + } + + //bool path::is_absolute() const + //{ + // return ( m_path.size() + // && m_path[0] == '/' ) // covers both "/" and "//share" + // || ( m_path.size() > 2 + // && m_path[1] == ':' + // && m_path[2] == '/' ) // "c:/" + // || ( m_path.size() > 3 + // && m_path[m_path.size()-1] == ':' ); // "device:" + //} + + namespace detail + { + void path_itr_imp::operator++() + { + assert( pos < path_ptr->m_path.size() ); // detect increment past end + pos += name.size(); + if ( pos == path_ptr->m_path.size() ) + { + name = ""; // not strictly required, but might aid debugging + return; + } + if ( path_ptr->m_path[pos] == '/' ) ++pos; + std::string::size_type end_pos( path_ptr->m_path.find( '/', pos ) ); + if ( end_pos == std::string::npos ) end_pos = path_ptr->m_path.size(); + name = path_ptr->m_path.substr( pos, end_pos - pos ); + } + + void path_itr_imp::operator--() + { + assert( pos ); // detect decrement of begin + std::string::size_type end_pos( pos ); + if ( end_pos != path_ptr->m_path.size() + && end_pos > 1 +# ifdef BOOST_WINDOWS + && path_ptr->m_path[end_pos-1] != ':' + && path_ptr->m_path[end_pos-2] != ':' +# endif + ) --end_pos; + pos = leaf_pos( path_ptr->m_path, end_pos ); + name = path_ptr->m_path.substr( pos, end_pos - pos ); + } + + } // namespace detail + } // namespace filesystem +} // namespace boost diff --git a/test/Jamfile b/test/Jamfile new file mode 100644 index 000000000..fe5d13caa --- /dev/null +++ b/test/Jamfile @@ -0,0 +1,34 @@ +# +# !!!!!!!!!!!!!!!! You must set BOOST_ROOT=../../.. before running bjam test !!!!!!!!!!!!!!!!!!!!!! +# + + +subproject libs/filesystem/test ; + +# bring in rules for testing +SEARCH on testing.jam = $(BOOST_BUILD_PATH) ; +include testing.jam ; + +{ + # look in BOOST_ROOT for sources first, just in this Jamfile + local SEARCH_SOURCE = $(BOOST_ROOT) $(SEARCH_SOURCE) ; + + test-suite "filesystem" + : [ run libs/filesystem/test/path_test.cpp + ../../../libs/filesystem/build/fs + ../../../libs/test/build/test_exec_monitor + ] + [ run libs/filesystem/test/operations_test.cpp + ../../../libs/filesystem/build/fs + ../../../libs/test/build/test_exec_monitor + ] + [ run libs/filesystem/test/fstream_test.cpp + ../../../libs/filesystem/build/fs + ../../../libs/test/build/test_exec_monitor + ] + [ run libs/filesystem/test/recursive_dir_itr_test.cpp + ../../../libs/filesystem/build/fs + ../../../libs/test/build/test_exec_monitor + ] + ; +} diff --git a/test/fstream_test.cpp b/test/fstream_test.cpp new file mode 100644 index 000000000..db360ae3b --- /dev/null +++ b/test/fstream_test.cpp @@ -0,0 +1,134 @@ +// fstream_test.cpp --------------------------------------------------------// + +// (C) Copyright Beman Dawes 2002. 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. + +// See http://www.boost.org for most recent version including documentation. + +#include +#include +#include // for std::remove + +namespace fs = boost::filesystem; + +#include +#ifdef BOOST_NO_STDC_NAMESPACE + namespace std { using ::remove; } +#endif + + +#define BOOST_INCLUDE_MAIN +#include + +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; + + bfb.open( "fstream_test_bffoo", std::ios_base::in ); + cfb.open( "fstream_test_bffoo", std::ios_base::in ); + +# ifndef BOOST_NO_STD_WSTRING + fs::wfilebuf wfb; + wfb.open( "fstream_test_bffoo", std::ios_base::in ); +# endif + } + + 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" ); + + BOOST_TEST( bofs.is_open() ); + BOOST_TEST( cofs.is_open() ); + + bofs << "fstream_test_bfoo"; + cofs << "fstream_test_cfoo"; + + // 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_TEST( 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_TEST( bifs.is_open() ); + BOOST_TEST( cifs.is_open() ); + + std::string b; + std::string c; + + bifs >> b; + cifs >> c; + + BOOST_TEST( b == "fstream_test_bfoo" ); + BOOST_TEST( 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_TEST( wifs.is_open() ); + std::wstring w; + wifs >> w; + BOOST_TEST( 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_TEST( bfs.is_open() ); + BOOST_TEST( cfs.is_open() ); + + std::string b; + std::string c; + + bfs >> b; + cfs >> c; + + BOOST_TEST( b == "fstream_test_bfoo" ); + BOOST_TEST( 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_TEST( wfs.is_open() ); + std::wstring w; + wfs >> w; + BOOST_TEST( 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/operations_test.cpp b/test/operations_test.cpp new file mode 100644 index 000000000..327bed4aa --- /dev/null +++ b/test/operations_test.cpp @@ -0,0 +1,220 @@ +// Boost operations_test.cpp -----------------------------------------------// + +// (C) Copyright Beman Dawes 2002. 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. + +// See http://www.boost.org for most recent version including documentation. + +#include +#include +namespace fs = boost::filesystem; + +//#include +#include + +#include +using boost::bind; + +#include +#include +#include + +namespace +{ + fs::directory_iterator end_itr; + + void create_file( const fs::path & ph, const std::string & contents ) + { + std::ofstream f( ph.file_path().c_str() ); + if ( !f ) + throw fs::filesystem_error( "ofstream(): " + ph.generic_path() ); + if ( !contents.empty() ) f << contents; + } + + void verify_file( const fs::path & ph, const std::string & expected ) + { + std::ifstream f( ph.file_path().c_str() ); + if ( !f ) + throw fs::filesystem_error( "ifstream(): " + ph.generic_path() ); + std::string contents; + f >> contents; + if ( contents != expected ) + throw fs::filesystem_error("verify_file(): " + ph.generic_path() + + " contents \"" + contents + + "\" != \"" + expected + "\"" ); + } + + template< typename F > + bool throws_fs_error( F func ) + { + try { func(); } + + catch ( const fs::filesystem_error & /*ex*/ ) + { +// std::cout << ex.what() << "\n"; + return true; + } + + return false; + } + +} // unnamed namespace + +// test_main ---------------------------------------------------------------// + +int test_main( int, char * [] ) +{ + std::cout << "implemenation name: " + << fs::detail::implementation_name() << "\n"; + std::cout << "initial_directory() is \"" + << fs::initial_directory().generic_path() + << "\"\n"; + + fs::path dir( fs::initial_directory() << "temp_fs_test_directory" ); + + if ( std::strcmp( fs::detail::implementation_name(), "Windows" ) == 0 ) + { + BOOST_TEST( dir.generic_path().size() > 1 + && dir.generic_path()[1] == ':' ); // verify path includes drive + } + + fs::path ng( " no-way, Jose ", fs::system_specific ); + + fs::remove_all( dir ); // in case residue from prior failed tests + BOOST_TEST( !fs::exists( dir ) ); + + // the bound functions should throw, so throws_fs_error() should return true + BOOST_TEST( throws_fs_error( bind( fs::is_directory, ng ) ) ); + BOOST_TEST( throws_fs_error( bind( fs::is_directory, dir ) ) ); + BOOST_TEST( throws_fs_error( bind( fs::_is_empty, dir ) ) ); + + fs::create_directory( dir ); + + BOOST_TEST( fs::exists( dir ) ); + BOOST_TEST( fs::_is_empty( dir ) ); + + BOOST_TEST( fs::is_directory( dir ) ); + + fs::path d1( dir << "d1" ); + fs::create_directory( d1 ); + BOOST_TEST( fs::exists( d1 ) ); + BOOST_TEST( fs::is_directory( d1 ) ); + BOOST_TEST( fs::_is_empty( d1 ) ); + + { + fs::directory_iterator dir_itr( dir ); + BOOST_TEST( dir_itr->leaf() == "d1" ); + } + + // create a second directory named d2 + fs::path d2( dir << "d2" ); + fs::create_directory(d2 ); + BOOST_TEST( fs::exists( d2 ) ); + BOOST_TEST( fs::is_directory( d2 ) ); + + { + fs::directory_iterator dir_itr( dir ); + BOOST_TEST( dir_itr->leaf() == "d1" || dir_itr->leaf() == "d2" ); + if ( dir_itr->leaf() == "d1" ) + { + // Thomas Witt suggested the ++ to verify a compiler problem workaround +// BOOST_TEST( (++fs::directory_iterator(dir))->leaf() == "d2" ); + ++dir_itr; + BOOST_TEST( dir_itr->leaf() == "d2" ); + } + else + { + BOOST_TEST( (++fs::directory_iterator(dir))->leaf() == "d1" ); + } + } + + // create an empty file named "f0" + fs::path file_ph( dir << "f0"); + create_file( file_ph, "" ); + BOOST_TEST( fs::exists( file_ph ) ); + BOOST_TEST( !fs::is_directory( file_ph ) ); + BOOST_TEST( fs::_is_empty( file_ph ) ); + + // create a file named "f1" + file_ph = dir << "f1"; + create_file( file_ph, "foobar1" ); + BOOST_TEST( fs::exists( file_ph ) ); + BOOST_TEST( !fs::is_directory( file_ph ) ); + verify_file( file_ph, "foobar1" ); + + // there was an inital bug in directory_iterator that caused premature + // close of an OS handle. This block will detect regression. + { + fs::directory_iterator di; + { di = fs::directory_iterator( dir ); } + BOOST_TEST( ++di != fs::directory_iterator() ); + } + + // copy_file() tests + fs::copy_file( file_ph, d1 << "f2" ); + BOOST_TEST( fs::exists( file_ph ) ); + BOOST_TEST( fs::exists( d1 << "f2" ) ); + BOOST_TEST( !fs::is_directory( d1 << "f2" ) ); + verify_file( d1 << "f2", "foobar1" ); + + // rename() on file d1/f2 to d2/f3 + fs::rename( d1 << "f2", d2 << "f3" ); + BOOST_TEST( !fs::exists( d1 << "f2" ) ); + BOOST_TEST( !fs::exists( d2 << "f2" ) ); + BOOST_TEST( fs::exists( d2 << "f3" ) ); + BOOST_TEST( !fs::is_directory( d2 << "f3" ) ); + verify_file( d2 << "f3", "foobar1" ); + + // make sure can't rename() a non-existent file + BOOST_TEST( throws_fs_error( bind( fs::rename, d1 << "f2", d2 << "f4" ) ) ); + + // make sure can't rename() to an existent file + BOOST_TEST( throws_fs_error( bind( fs::rename, dir << "f1", d2 << "f3" ) ) ); + + // make sure can't rename() to a nonexistent parent directory + BOOST_TEST( throws_fs_error( bind( fs::rename, dir << "f1", dir << "d3/f3" ) ) ); + + // rename() on directory + fs::path d3( dir << "d3" ); + fs::rename( d2, d3 ); + BOOST_TEST( !fs::exists( d2 ) ); + BOOST_TEST( fs::exists( d3 ) ); + BOOST_TEST( fs::is_directory( d3 ) ); + BOOST_TEST( !fs::exists( d2 << "f3" ) ); + BOOST_TEST( fs::exists( d3 << "f3" ) ); + + // remove() tests on file + file_ph = dir << "shortlife"; + BOOST_TEST( !fs::exists( file_ph ) ); + create_file( file_ph, "" ); + BOOST_TEST( fs::exists( file_ph ) ); + BOOST_TEST( !fs::is_directory( file_ph ) ); + fs::remove( file_ph ); + BOOST_TEST( !fs::exists( file_ph ) ); + fs::remove( "no-such-file" ); // should be harmless + fs::remove( "no-such-directory/no-such-file" ); // ditto + + // remove test on directory + d1 = dir << "shortlife_dir"; + BOOST_TEST( !fs::exists( d1 ) ); + fs::create_directory( d1 ); + BOOST_TEST( fs::exists( d1 ) ); + BOOST_TEST( fs::is_directory( d1 ) ); + BOOST_TEST( fs::_is_empty( d1 ) ); + BOOST_TEST( throws_fs_error( bind( fs::remove, dir ) ) ); + fs::remove( d1 ); + BOOST_TEST( !fs::exists( d1 ) ); + + // post-test cleanup + BOOST_TEST( 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_TEST( !fs::exists( dir ) ); + BOOST_TEST( fs::remove_all( dir ) == 0 ); + + return 0; +} // main + diff --git a/test/path_test.cpp b/test/path_test.cpp new file mode 100644 index 000000000..dd7fcc01d --- /dev/null +++ b/test/path_test.cpp @@ -0,0 +1,315 @@ +// path_test program -------------------------------------------------------// + +// (C) Copyright Beman Dawes 2002. 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. + +// See http://www.boost.org for most recent version including documentation. + +#include +#include +#include +#include +#include +#include +#include + +namespace fs = boost::filesystem; +using boost::filesystem::path; +using boost::next; +using boost::prior; + +#define BOOST_INCLUDE_MAIN +#include + +namespace { + int errors; + + void check( const fs::path & source, + const std::string & expected ) + { + if ( source.generic_path()== expected ) return; + + ++errors; + + std::cout << "source.generic_path(): \"" << source.generic_path() + << "\" != expected: \"" << expected + << "\"" << std::endl; + } + + void check_throw( const std::string & arg ) + { + try + { + fs::path arg_path( arg ); + ++errors; + std::cout << "failed to throw with argument \"" << arg + << "\"" << std::endl; + } + catch ( const fs::filesystem_error & /*ex*/ ) + { +// std::cout << ex.what() << "\n"; + } + } + +} // unnamed namespace + +int test_main( int, char*[] ) +{ + + path p1( "fe/fi/fo/fum" ); + path p2( p1 ); + path p3; + p3 = p2; + +// p1.branch() = p2; // should fail +// *p1.begin() = ""; // should fail + + // These verify various overloads don't cause compiler errors + fs::exists( "foo" ); + fs::exists( std::string( "foo" ) ); + fs::exists( p1 ); + fs::exists( "foo" << p1 ); + fs::exists( std::string( "foo" ) << p1 ); + fs::exists( fs::check_posix_leaf( "foo" ) ); + + BOOST_TEST( p1.generic_path() == p2.generic_path() ); + BOOST_TEST( p1.generic_path() == p3.generic_path() ); + BOOST_TEST( path( "foo" ).leaf() == "foo" ); + BOOST_TEST( path( "foo" ).branch().generic_path() == "" ); + BOOST_TEST( p1.leaf() == "fum" ); + BOOST_TEST( p1.branch().generic_path() == "fe/fi/fo" ); + BOOST_TEST( path( "" ).is_null() == true ); + BOOST_TEST( path( "foo" ).is_null() == false ); + + check( "", "" ); + + check( "foo", "foo" ); + check( path("") << "foo", "foo" ); + check( path("foo") << "", "foo" ); + + check( "foo/bar", "foo/bar" ); + check( path("foo") << "bar", "foo/bar" ); + check( path("foo") << path("bar"), "foo/bar" ); + check( "foo" << path("bar"), "foo/bar" ); + + check( "a/b", "a/b" ); // probe for length effects + check( path("a") << "b", "a/b" ); + + check( "..", ".." ); + check( path("..") << "", ".." ); + check( path("") << "..", ".." ); + + check( "../..", "../.." ); + check( path("..") << ".." , "../.." ); + + check( "../foo", "../foo" ); + check( path("..") << "foo" , "../foo" ); + + check( "foo/..", "" ); + check( path("foo") << ".." , "" ); + + check( "../f", "../f" ); + check( path("..") << "f" , "../f" ); + + check( "f/..", "" ); + check( path("f") << ".." , "" ); + + check( "foo/../..", ".." ); + check( path("foo") << ".." << ".." , ".." ); + + check( "foo/../../..", "../.." ); + check( path("foo") << ".." << ".." << ".." , "../.." ); + + check( "foo/../bar", "bar" ); + check( path("foo") << ".." << "bar" , "bar" ); + + check( "foo/bar/..", "foo" ); + check( path("foo") << "bar" << ".." , "foo" ); + + check( "foo/bar/../..", "" ); + check( path("foo") << "bar" << ".." << "..", "" ); + + check( "foo/bar/../blah", "foo/blah" ); + check( path("foo") << "bar" << ".." << "blah", "foo/blah" ); + + check( "f/../b", "b" ); + check( path("f") << ".." << "b" , "b" ); + + check( "f/b/..", "f" ); + check( path("f") << "b" << ".." , "f" ); + + check( "f/b/../a", "f/a" ); + check( path("f") << "b" << ".." << "a", "f/a" ); + + check( "foo/bar/blah/../..", "foo" ); + check( path("foo") << "bar" << "blah" << ".." << "..", "foo" ); + + check( "foo/bar/blah/../../bletch", "foo/bletch" ); + check( path("foo") << "bar" << "blah" << ".." << ".." << "bletch", "foo/bletch" ); + + BOOST_TEST( fs::posix_name("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789.-_") ); + BOOST_TEST( !fs::posix_name("F$O") ); + BOOST_TEST( !fs::posix_name(".") ); + BOOST_TEST( !fs::boost_file_name("ABCDEFGHIJKLMNOPQRSTUVWXYZ") ); + BOOST_TEST( fs::boost_file_name("abcdefghijklmnopqrstuvwxyz") ); + BOOST_TEST( fs::boost_file_name("0123456789.-_") ); + BOOST_TEST( fs::boost_file_name("1234567890123456789012345678901") ); + BOOST_TEST( !fs::boost_file_name("12345678901234567890123456789012") ); + BOOST_TEST( !fs::boost_file_name("F$O") ); + BOOST_TEST( !fs::boost_file_name(".") ); + BOOST_TEST( !fs::boost_directory_name("ABCDEFGHIJKLMNOPQRSTUVWXYZ") ); + BOOST_TEST( fs::boost_directory_name("abcdefghijklmnopqrstuvwxyz") ); + BOOST_TEST( fs::boost_directory_name("0123456789-_") ); + BOOST_TEST( fs::boost_directory_name("1234567890123456789012345678901") ); + BOOST_TEST( !fs::boost_directory_name("12345678901234567890123456789012") ); + BOOST_TEST( !fs::boost_directory_name("F$O") ); + + check_throw( "/" ); + check_throw( "...." ); + check_throw( "/foo" ); + check_throw( "foo/" ); + check_throw( "foo/...." ); + check_throw( "foo//bar" ); + check_throw( "foo\\bar" ); + check_throw( " " ); + check_throw( " foo" ); + check_throw( "foo " ); + check_throw( ">" ); + 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", fs::system_specific ); + path::iterator itr( itr_ck.begin() ); +//std::cout << "\"" << *itr << "\"" << std::endl; + BOOST_TEST( *itr == std::string( "/" ) ); + BOOST_TEST( *++itr == std::string( "foo" ) ); + BOOST_TEST( *++itr == std::string( "bar" ) ); + BOOST_TEST( ++itr == itr_ck.end() ); + BOOST_TEST( *--itr == std::string( "bar" ) ); + BOOST_TEST( *--itr == std::string( "foo" ) ); + BOOST_TEST( *--itr == std::string( "/" ) ); + + itr_ck = ""; + BOOST_TEST( itr_ck.begin() == itr_ck.end() ); + + itr_ck = path( "/", fs::system_specific ); + BOOST_TEST( *itr_ck.begin() == std::string( "/" ) ); + BOOST_TEST( next(itr_ck.begin()) == itr_ck.end() ); + BOOST_TEST( *prior(itr_ck.end()) == std::string( "/" ) ); + BOOST_TEST( prior(itr_ck.end()) == itr_ck.begin() ); + + itr_ck = path( "/foo", fs::system_specific ); + BOOST_TEST( *itr_ck.begin() == std::string( "/" ) ); + BOOST_TEST( *next( itr_ck.begin() ) == std::string( "foo" ) ); + BOOST_TEST( next(next( itr_ck.begin() )) == itr_ck.end() ); + BOOST_TEST( next( itr_ck.begin() ) == prior( itr_ck.end() ) ); + BOOST_TEST( *prior( itr_ck.end() ) == std::string( "foo" ) ); + BOOST_TEST( *prior(prior( itr_ck.end() )) == std::string( "/" ) ); + BOOST_TEST( prior(prior( itr_ck.end() )) == itr_ck.begin() ); + + itr_ck = "foo"; + BOOST_TEST( *itr_ck.begin() == std::string( "foo" ) ); + BOOST_TEST( next( itr_ck.begin() ) == itr_ck.end() ); + BOOST_TEST( *prior( itr_ck.end() ) == std::string( "foo" ) ); + BOOST_TEST( prior( itr_ck.end() ) == itr_ck.begin() ); + + if ( std::strcmp( fs::detail::implementation_name(), "Windows" ) == 0 ) + { + itr_ck = path( "c:", fs::system_specific ); + BOOST_TEST( *itr_ck.begin() == std::string( "c:" ) ); + BOOST_TEST( next( itr_ck.begin() ) == itr_ck.end() ); + BOOST_TEST( prior( itr_ck.end() ) == itr_ck.begin() ); + BOOST_TEST( *prior( itr_ck.end() ) == std::string( "c:" ) ); + + itr_ck = path( "c:/", fs::system_specific ); + BOOST_TEST( *itr_ck.begin() == std::string( "c:/" ) ); + BOOST_TEST( next( itr_ck.begin() ) == itr_ck.end() ); + BOOST_TEST( prior( itr_ck.end() ) == itr_ck.begin() ); + BOOST_TEST( *prior( itr_ck.end() ) == std::string( "c:/" ) ); + + itr_ck = path( "c:foo", fs::system_specific ); + BOOST_TEST( *itr_ck.begin() == std::string( "c:" ) ); + BOOST_TEST( *next( itr_ck.begin() ) == std::string( "foo" ) ); + BOOST_TEST( next(next( itr_ck.begin() )) == itr_ck.end() ); + BOOST_TEST( prior(prior( itr_ck.end() )) == itr_ck.begin() ); + BOOST_TEST( *prior( itr_ck.end() ) == std::string( "foo" ) ); + BOOST_TEST( *prior(prior( itr_ck.end() )) == std::string( "c:" ) ); + + itr_ck = path( "c:/foo", fs::system_specific ); + BOOST_TEST( *itr_ck.begin() == std::string( "c:/" ) ); + BOOST_TEST( *next( itr_ck.begin() ) == std::string( "foo" ) ); + BOOST_TEST( next(next( itr_ck.begin() )) == itr_ck.end() ); + BOOST_TEST( prior(prior( itr_ck.end() )) == itr_ck.begin() ); + BOOST_TEST( *prior( itr_ck.end() ) == std::string( "foo" ) ); + BOOST_TEST( *prior(prior( itr_ck.end() )) == std::string( "c:/" ) ); + + itr_ck = path( "//share", fs::system_specific ); + BOOST_TEST( *itr_ck.begin() == std::string( "//share" ) ); + BOOST_TEST( next( itr_ck.begin() ) == itr_ck.end() ); + BOOST_TEST( prior( itr_ck.end() ) == itr_ck.begin() ); + BOOST_TEST( *prior( itr_ck.end() ) == std::string( "//share" ) ); + + itr_ck = path( "//share/foo", fs::system_specific ); + BOOST_TEST( *itr_ck.begin() == std::string( "//share" ) ); + BOOST_TEST( *next( itr_ck.begin() ) == std::string( "foo" ) ); + BOOST_TEST( next(next( itr_ck.begin() )) == itr_ck.end() ); + BOOST_TEST( prior(prior( itr_ck.end() )) == itr_ck.begin() ); + BOOST_TEST( *prior( itr_ck.end() ) == std::string( "foo" ) ); + BOOST_TEST( *prior(prior( itr_ck.end() )) == std::string( "//share" ) ); + + itr_ck = path( "prn:", fs::system_specific ); + BOOST_TEST( *itr_ck.begin() == std::string( "prn:" ) ); + BOOST_TEST( next( itr_ck.begin() ) == itr_ck.end() ); + BOOST_TEST( prior( itr_ck.end() ) == itr_ck.begin() ); + BOOST_TEST( *prior( itr_ck.end() ) == std::string( "prn:" ) ); + + check( path( "/", fs::system_specific ), "/" ); + check( path( "/f", fs::system_specific ), "/f" ); + check( path( "/foo", fs::system_specific ), "/foo" ); + check( path( "\\", fs::system_specific ), "/" ); + check( path( "\\f", fs::system_specific ), "/f" ); + check( path( "\\foo", fs::system_specific ), "/foo" ); + check( path( "foo\\bar", fs::system_specific ), "foo/bar" ); + check( path( "foo bar", fs::system_specific ), "foo bar" ); + check( path( "c:", fs::system_specific ), "c:" ); + check( path( "c:/", fs::system_specific ), "c:/" ); + check( path( "c:foo", fs::system_specific ), "c:foo" ); + check( path( "c:/foo", fs::system_specific ), "c:/foo" ); + check( path( "//share", fs::system_specific ), "//share" ); + check( path( "//share/foo", fs::system_specific ), "//share/foo" ); + check( path( "\\\\share", fs::system_specific ), "//share" ); + check( path( "\\\\share\\foo", fs::system_specific ), "//share/foo" ); + check( path( "c:/foo", fs::system_specific ), "c:/foo" ); + check( path( "prn:", fs::system_specific ), "prn:" ); + + BOOST_TEST( path( "/", fs::system_specific ).leaf() == "/" ); + BOOST_TEST( path( "c:", fs::system_specific ).leaf() == "c:" ); + BOOST_TEST( path( "c:/", fs::system_specific ).leaf() == "c:/" ); + BOOST_TEST( path( "c:foo", fs::system_specific ).leaf() == "foo" ); + BOOST_TEST( path( "c:/foo", fs::system_specific ).leaf() == "foo" ); + BOOST_TEST( path( "//share", fs::system_specific ).leaf() == "//share" ); + BOOST_TEST( path( "//share/foo", fs::system_specific ).leaf() == "foo" ); + + BOOST_TEST( path( "/", fs::system_specific ).branch().generic_path() == "" ); + BOOST_TEST( path( "c:", fs::system_specific ).branch().generic_path() == "" ); + BOOST_TEST( path( "c:/", fs::system_specific ).branch().generic_path() == "" ); + BOOST_TEST( path( "c:foo", fs::system_specific ).branch().generic_path() == "c:" ); + BOOST_TEST( path( "c:/foo", fs::system_specific ).branch().generic_path() == "c:/" ); + BOOST_TEST( path( "//share", fs::system_specific ).branch().generic_path() == "" ); + BOOST_TEST( path( "//share/foo", fs::system_specific ).branch().generic_path() == "//share" ); + } +// std::cout << errors << " errors detected\n"; + + return errors; +} diff --git a/test/recursive_dir_itr_test.cpp b/test/recursive_dir_itr_test.cpp new file mode 100644 index 000000000..3350175c8 --- /dev/null +++ b/test/recursive_dir_itr_test.cpp @@ -0,0 +1,49 @@ +// recursive_dir_itr_test program ------------------------------------------// + +// (C) Copyright Beman Dawes 2002. 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. + +// See http://www.boost.org for most recent version including documentation. + +#include +#include +#include +#include + +namespace fs = boost::filesystem; + +namespace +{ + + bool dummy( const fs::path & /*pth*/ ) + { +// std::cout << "predicate called with " << pth.generic_path() << '\n'; + return true; + } + +} // unnamed namespace + +int main( int argc, char * argv[] ) +{ + fs::path dir( argc > 1 + ? fs::path( argv[1], fs::system_specific ) + : fs::initial_directory() ); + + std::cout << "Directory being inspected: " << dir.generic_path() << std::endl; + + typedef bool(*pred_type)(const fs::path&); + + fs::recursive_directory_iterator rdi( dir , dummy ); + fs::recursive_directory_iterator rdi_end; + + for ( ; rdi != rdi_end; ++rdi ) + { + std::cout << (*rdi).generic_path() << std::endl; + std::cout << rdi->generic_path() << std::endl; + } + + return 0; +}