Boost logo

Boost :

From: Walter Landry (wlandry_at_[hidden])
Date: 2006-09-09 04:05:06


Beman Dawes <bdawes_at_[hidden]> wrote:
> Walter Landry wrote:
> > Except for nlink, I have already implemented all of these functions
> > because I needed them. Is there any interest in getting these
> > functions into the filesystem library?
>
> I'm interested, particularly if they can be portably implemented.

Attached are patches to

         boost/filesystem/operations.hpp
         libs/filesystem/src/operations.cpp

in CVS. They implement readlink, copy_directory, and nlink. There is
also a standalone file with all of the other functions, since they can
be implemented solely in terms of these two functions and other
existing functions. The windows versions should be complete, but are
completely untested. The posix versions were well tested with boost
1.32 (except for nlink), but have not been well tested with the
current CVS.

The copy() function has a few more features than I suggested earlier.
It allows recursive and hard linked copies.

Cheers,
Walter Landry
wlandry_at_[hidden]


--- ,,pristine/boost/filesystem/operations.hpp 2006-09-01 13:48:55.000000000 -0700
+++ /home/boo/arx/arx/src/boost/boost/filesystem/operations.hpp 2006-09-05 13:00:56.885670680 -0700
@@ -153,6 +153,8 @@
         equivalent_api( const std::string & ph1, const std::string & ph2 );
       BOOST_FILESYSTEM_DECL uintmax_pair
         file_size_api( const std::string & ph );
+ BOOST_FILESYSTEM_DECL uintmax_pair
+ nlink_api( const std::string & ph );
       BOOST_FILESYSTEM_DECL space_pair
         space_api( const std::string & ph );
       BOOST_FILESYSTEM_DECL time_pair
@@ -161,6 +163,8 @@
         last_write_time_api( const std::string & ph, std::time_t new_value );
       BOOST_FILESYSTEM_DECL boost::filesystem::system_error_type
         get_current_path_api( std::string & ph );
+ BOOST_FILESYSTEM_DECL boost::filesystem::system_error_type
+ get_readlink_api( const std::string & p, std::string & ph );
       BOOST_FILESYSTEM_DECL query_pair
         create_directory_api( const std::string & ph );
       BOOST_FILESYSTEM_DECL boost::filesystem::system_error_type
@@ -175,6 +179,8 @@
         rename_api( const std::string & from, const std::string & to );
       BOOST_FILESYSTEM_DECL boost::filesystem::system_error_type
         copy_file_api( const std::string & from, const std::string & to );
+ BOOST_FILESYSTEM_DECL boost::filesystem::system_error_type
+ copy_directory_api( const std::string & from, const std::string & to );
 
 # if defined(BOOST_WINDOWS_API)
       
@@ -191,6 +197,8 @@
         equivalent_api( const std::wstring & ph1, const std::wstring & ph2 );
       BOOST_FILESYSTEM_DECL uintmax_pair
         file_size_api( const std::wstring & ph );
+ BOOST_FILESYSTEM_DECL uintmax_pair
+ nlink_api( const std::wstring & ph );
       BOOST_FILESYSTEM_DECL space_pair
         space_api( const std::wstring & ph );
       BOOST_FILESYSTEM_DECL boost::filesystem::system_error_type
@@ -201,6 +209,8 @@
         last_write_time_api( const std::wstring & ph, std::time_t new_value );
       BOOST_FILESYSTEM_DECL boost::filesystem::system_error_type
         get_current_path_api( std::wstring & ph );
+ BOOST_FILESYSTEM_DECL boost::filesystem::system_error_type
+ get_readlink_api( const std::wstring & p, std::wstring & ph );
       BOOST_FILESYSTEM_DECL query_pair
         create_directory_api( const std::wstring & ph );
 # ifdef BOOST_FS_HARD_LINK
@@ -217,6 +227,8 @@
         rename_api( const std::wstring & from, const std::wstring & to );
       BOOST_FILESYSTEM_DECL boost::filesystem::system_error_type
         copy_file_api( const std::wstring & from, const std::wstring & to );
+ BOOST_FILESYSTEM_DECL boost::filesystem::system_error_type
+ copy_directory_api( const std::wstring & from, const std::wstring & to );
 
 # endif
 # endif
@@ -367,6 +379,16 @@
       return result.second;
     }
 
+ BOOST_FS_FUNC(boost::uintmax_t) nlink( const Path & ph )
+ {
+ detail::uintmax_pair result
+ = detail::nlink_api( ph.external_file_string() );
+ if ( result.first != 0 )
+ boost::throw_exception( basic_filesystem_error<Path>(
+ "boost::filesystem::nlink", ph, result.first ) );
+ return result.second;
+ }
+
     BOOST_FS_FUNC(space_info) space( const Path & ph )
     {
       detail::space_pair result
@@ -495,6 +517,18 @@
           from_path, to_path, result ) );
     }
 
+ BOOST_FS_FUNC(void) copy_directory( const Path & from_path,
+ const Path & to_path )
+ {
+ system_error_type result = detail::copy_directory_api(
+ from_path.external_directory_string(),
+ to_path.external_directory_string() );
+ if ( result != 0 )
+ boost::throw_exception( basic_filesystem_error<Path>(
+ "boost::filesystem::copy_directory",
+ from_path, to_path, result ) );
+ }
+
     template< class Path >
     Path current_path()
     {
@@ -507,6 +541,17 @@
     }
 
     template< class Path >
+ Path readlink(const Path &source_ph)
+ {
+ typename Path::external_string_type ph;
+ boost::filesystem::system_error_type result;
+ if ( (result = detail::get_readlink_api( source_ph.external_file_string(), ph )) != 0 )
+ boost::throw_exception( basic_filesystem_error<Path>(
+ "boost::filesystem::current_path", result ) );
+ return Path( Path::traits_type::to_internal( ph ) );
+ }
+
+ template< class Path >
     const Path & initial_path()
     {
       static Path init_path;
@@ -635,6 +680,11 @@
     inline boost::uintmax_t file_size( const wpath & ph )
       { return file_size<wpath>( ph ); }
 
+ inline boost::uintmax_t nlink( const path & ph )
+ { return nlink<path>( ph ); }
+ inline boost::uintmax_t nlink( const wpath & ph )
+ { return nlink<wpath>( ph ); }
+
     inline space_info space( const path & ph )
       { return space<path>( ph ); }
     inline space_info space( const wpath & ph )
@@ -700,6 +750,11 @@
     inline void copy_file( const wpath & from_path, const wpath & to_path )
       { return copy_file<wpath>( from_path, to_path ); }
 
+ inline void copy_directory( const path & from_path, const path & to_path )
+ { return copy_directory<path>( from_path, to_path ); }
+ inline void copy_directory( const wpath & from_path, const wpath & to_path )
+ { return copy_directory<wpath>( from_path, to_path ); }
+
     inline path system_complete( const path & ph )
       { return system_complete<path>( ph ); }
     inline wpath system_complete( const wpath & ph )


--- ,,pristine/libs/filesystem/src/operations.cpp 2006-09-01 13:49:00.000000000 -0700
+++ /home/boo/arx/arx/src/boost/libs/filesystem/src/operations.cpp 2006-09-05 13:15:33.726370632 -0700
@@ -166,6 +166,10 @@
   inline bool create_directory( const std::wstring & dir )
     { return ::CreateDirectoryW( dir.c_str(), 0 ) != 0; }
 
+ inline bool copy_directory( const std::wstring & from,
+ const std::wstring & to )
+ { return ::CreateDirectoryExW( from.c_str(), to.c_str(), 0 ) != 0; }
+
 #if _WIN32_WINNT >= 0x500
   inline bool create_hard_link( const std::wstring & to_ph,
     const std::wstring & from_ph )
@@ -317,6 +321,25 @@
       + fad.nFileSizeLow );
   }
 
+ template< class String >
+ boost::filesystem::detail::uintmax_pair
+ nlink_template( const String & ph )
+ {
+ // Link count info is only available through GetFileInformationByHandle
+ handle_wrapper p1(
+ create_file( ph1.c_str(), 0,
+ FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, 0,
+ OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0 ) );
+ int error1(0); // save error code in case we have to throw
+ if ( p1.handle == INVALID_HANDLE_VALUE )
+ error1 = ::GetLastError();
+ // at this point, the handle is known to be valid
+ BY_HANDLE_FILE_INFORMATION info1;
+ if ( !::GetFileInformationByHandle( p1.handle, &info1 ) )
+ { return std::make_pair( ::GetLastError(), false ); }
+ return std::make_pair( 0,info1.nNumberOfLinks);
+ }
+
   inline bool get_free_disk_space( const std::string & ph,
     PULARGE_INTEGER avail, PULARGE_INTEGER total, PULARGE_INTEGER free )
     { return ::GetDiskFreeSpaceExA( ph.c_str(), avail, total, free ) != 0; }
@@ -503,6 +526,10 @@
 
   inline bool create_directory( const std::string & dir )
     { return ::CreateDirectoryA( dir.c_str(), 0 ) != 0; }
+
+ inline bool copy_directory( const std::string & from,
+ const std::string & to )
+ { return ::CreateDirectoryExA( from.c_str(), to.c_str(), 0 ) != 0; }
          
   template<class String>
   boost::filesystem::detail::query_pair
@@ -518,6 +545,16 @@
     return std::make_pair( error, false );
   }
 
+ template<class String>
+ boost::filesystem::detail::query_pair
+ copy_directory_template( const String & from, const String & to )
+ {
+ boost::filesystem::system_error_type error(0), dummy;
+ if ( copy_directory( from, to ) ) return std::make_pair( error, true );
+ error = ::GetLastError();
+ return std::make_pair( error, false );
+ }
+
 #if _WIN32_WINNT >= 0x500
   inline bool create_hard_link( const std::string & to_ph,
     const std::string & from_ph )
@@ -592,6 +629,10 @@
         { return file_size_template( ph ); }
 
       BOOST_FILESYSTEM_DECL
+ fs::detail::uintmax_pair nlink_api( const std::wstring & ph )
+ { return nlink_template( ph ); }
+
+ BOOST_FILESYSTEM_DECL
       fs::detail::space_pair space_api( const std::wstring & ph )
         { return space_template( ph ); }
 
@@ -600,6 +641,11 @@
       get_current_path_api( std::wstring & ph )
         { return get_current_path_template( ph ); }
 
+ BOOST_FILESYSTEM_DECL
+ boost::filesystem::system_error_type
+ get_readlink_api( const std::wstring &source_ph, std::wstring & ph )
+ { return ERROR_NOT_SUPPORTED; }
+
       BOOST_FILESYSTEM_DECL boost::filesystem::system_error_type
         get_full_path_name_api( const std::wstring & ph, std::wstring & target )
          { return get_full_path_name_template( ph, target ); }
@@ -645,6 +691,12 @@
           ? 0 : ::GetLastError();
       }
 
+ BOOST_FILESYSTEM_DECL boost::filesystem::system_error_type
+ copy_directory_api( const std::wstring & from, const std::wstring & to )
+ {
+ return copy_directory_template(from,to);
+ }
+
       BOOST_FILESYSTEM_DECL bool create_file_api( const std::wstring & ph,
         std::ios_base::openmode mode ) // true if succeeds
       {
@@ -765,6 +817,10 @@
         { return file_size_template( ph ); }
 
       BOOST_FILESYSTEM_DECL
+ fs::detail::uintmax_pair nlink_api( const std::string & ph )
+ { return nlink_template( ph ); }
+
+ BOOST_FILESYSTEM_DECL
       fs::detail::space_pair space_api( const std::string & ph )
         { return space_template( ph ); }
 
@@ -773,6 +829,11 @@
       get_current_path_api( std::string & ph )
         { return get_current_path_template( ph ); }
 
+ BOOST_FILESYSTEM_DECL
+ boost::filesystem::system_error_type
+ get_readlink_api( std::string & source_ph, std::string & ph )
+ { return ERROR_NOT_SUPPORTED; }
+
       BOOST_FILESYSTEM_DECL boost::filesystem::system_error_type
         get_full_path_name_api( const std::string & ph, std::string & target )
          { return get_full_path_name_template( ph, target ); }
@@ -819,6 +880,12 @@
       }
 
       BOOST_FILESYSTEM_DECL boost::filesystem::system_error_type
+ copy_directory_api( const std::string & from, const std::string & to )
+ {
+ return copy_directory_template(from,to);
+ }
+
+ BOOST_FILESYSTEM_DECL boost::filesystem::system_error_type
       dir_itr_first( void *& handle, const std::string & dir,
         std::string & target, file_status & sf, file_status & symlink_sf )
       // Note: an empty root directory has no "." or ".." entries, so this
@@ -991,6 +1058,16 @@
         return std::make_pair( 0,
           static_cast<boost::uintmax_t>(path_stat.st_size) );
       }
+
+ BOOST_FILESYSTEM_DECL uintmax_pair
+ nlink_api( const std::string & ph )
+ {
+ struct stat path_stat;
+ if ( ::stat( ph.c_str(), &path_stat ) != 0 )
+ return std::make_pair( errno, 0 );
+ return std::make_pair( 0,
+ static_cast<boost::uintmax_t>(path_stat.st_nlink) );
+ }
 
       BOOST_FILESYSTEM_DECL space_pair
       space_api( const std::string & ph )
@@ -1061,6 +1138,31 @@
         return 0;
       }
 
+ BOOST_FILESYSTEM_DECL fs::system_error_type
+ get_readlink_api( const std::string & source_ph, std::string & ph )
+ {
+ for ( long path_max = 32;; path_max *=2 ) // loop 'til buffer large enough
+ {
+ boost::scoped_array<char>
+ buf( new char[static_cast<std::size_t>(path_max)] );
+ ssize_t result;
+ if ( (result=::readlink( source_ph.c_str(), buf.get(),
+ static_cast<std::size_t>(path_max) )) == -1 )
+ {
+ return errno;
+ }
+ else
+ {
+ if(result!=path_max)
+ {
+ ph = buf.get();
+ break;
+ }
+ }
+ }
+ return 0;
+ }
+
       BOOST_FILESYSTEM_DECL fs::detail::query_pair
       create_directory_api( const std::string & ph )
       {
@@ -1171,6 +1273,17 @@
 
         return sz_read < 0 ? errno : 0;
       }
+
+ BOOST_FILESYSTEM_DECL boost::filesystem::system_error_type
+ copy_directory_api( const std::string & from_dir_ph,
+ const std::string & to_dir_ph )
+ {
+ struct stat from_stat;
+ if ( (::stat( from_dir_ph.c_str(), &from_stat ) != 0)
+ || ::mkdir(to_dir_ph.c_str(),from_stat.st_mode)!=0)
+ return errno;
+ return 0;
+ }
   
       // this code is based on Stevens and Rago, Advanced Programming in the
       // UNIX envirnment, 2nd Ed., ISBN 0-201-43307-9, page 49


#include "boost_filesystem_extras.hpp"
#include <boost/filesystem/config.hpp>
#include <boost/filesystem/operations.hpp>
#include <boost/filesystem/exception.hpp>
#include <boost/throw_exception.hpp>
#include <sys/stat.h>

namespace boost {
  namespace filesystem {

    // this function copies files, symlinks and directories
    void copy( const path & from_ph,
               const path & to_ph,
               bool recursive,
               bool hard_link)
    {
      // Make sure that the destination, if it exists, is a directory
      if(is_symlink(from_ph))
        {
          if(exists(to_ph))
            {
              copy_link(from_ph,to_ph/from_ph.leaf());
            }
          else
            {
              copy_link(from_ph,to_ph);
            }
        }
      else if(!is_directory(from_ph))
        {
          if(exists(to_ph))
            {
              copy_file(from_ph,to_ph/from_ph.leaf(),hard_link);
            }
          else
            {
              copy_file(from_ph,to_ph,hard_link);
            }
        }
      else
        {
          copy_directory(from_ph,to_ph);
          
          if(recursive)
            for(directory_iterator i(from_ph); i!=directory_iterator(); ++i)
              copy(*i,to_ph/i->leaf(),recursive,hard_link);
        }
    }
    
    void copy_file(const path &from_path, const path &to_path, bool hard_link)
    {
      if(hard_link)
        {
          create_hard_link(from_path,to_path);
        }
      else
        {
          copy_file(from_path,to_path);
        }
    }

    void copy_link( const path &from_ph,
                    const path &to_ph)
    {
      create_symlink(readlink(from_ph),to_ph);
    }
  }
}


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk