// operations.cpp ----------------------------------------------------------// // Copyright 2002-2005 Beman Dawes // Copyright 2001 Dietmar Kuehl // Use, modification, and distribution is subject to the Boost Software // License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy // at http://www.boost.org/LICENSE_1_0.txt) // See library home page at http://www.boost.org/libs/filesystem //----------------------------------------------------------------------------// // define BOOST_FILESYSTEM_SOURCE so that knows // the library is being built (possibly exporting rather than importing code) #define BOOST_FILESYSTEM_SOURCE #define _POSIX_PTHREAD_SEMANTICS // Sun readdir_r() needs this // enable the XPG-compliant version of readdir_r() on AIX #if defined(_AIX) # define _LINUX_SOURCE_COMPAT #endif #if !(defined(__HP_aCC) && defined(_ILP32) && \ !defined(_STATVFS_ACPP_PROBLEMS_FIXED)) #define _FILE_OFFSET_BITS 64 // at worst, these defines may have no effect, #endif #define __USE_FILE_OFFSET64 // but that is harmless on Windows and on POSIX // 64-bit systems or on 32-bit systems which don't have files larger // than can be represented by a traditional POSIX/UNIX off_t type. // OTOH, defining them should kick in 64-bit off_t's (and thus // st_size) on 32-bit systems that provide the Large File // Support (LFS) interface, such as Linux, Solaris, and IRIX. // The defines are given before any headers are included to // ensure that they are available to all included headers. // That is required at least on Solaris, and possibly on other // systems as well. // for some compilers (CodeWarrior, for example), windows.h // is getting included by some other boost header, so do this early: #if !defined(_WIN32_WINNT) #define _WIN32_WINNT 0x0500 // Default to Windows 2K or later #endif #include #include #include #include namespace fs = boost::filesystem; using boost::system::error_code; using boost::system::system_category; # if defined(BOOST_WINDOWS_API) # include # if defined(__BORLANDC__) || defined(__MWERKS__) # if defined(__BORLANDC__) using std::time_t; # endif # include # else # include # endif # else // BOOST_POSIX_API # include # if !defined(__APPLE__) && !defined(__OpenBSD__) # include # define BOOST_STATVFS statvfs # define BOOST_STATVFS_F_FRSIZE vfs.f_frsize # else #ifdef __OpenBSD__ # include #endif # include # define BOOST_STATVFS statfs # define BOOST_STATVFS_F_FRSIZE static_cast( vfs.f_bsize ) # endif # include # include # include # include # include "limits.h" # endif // BOOST_FILESYSTEM_STATUS_CACHE enables file_status cache in // dir_itr_increment. The config tests are placed here because some of the // macros being tested come from dirent.h. // // TODO: find out what macros indicate dirent::d_type present in more libraries # if defined(BOOST_WINDOWS_API) \ || (defined(_DIRENT_HAVE_D_TYPE) /* defined by GNU C library if d_type present */ \ && !(defined(__SUNPRO_CC) && !defined(__sun))) // _DIRENT_HAVE_D_TYPE wrong for Sun compiler on Linux # define BOOST_FILESYSTEM_STATUS_CACHE # endif #include // even on Windows some functions use stat() #include #include #include // for remove, rename #include #include // #include // for debugging only; comment out when not in use #ifdef BOOST_NO_STDC_NAMESPACE namespace std { using ::strcmp; using ::remove; using ::rename; } #endif // helpers -----------------------------------------------------------------// namespace { const error_code ok; bool is_empty_directory( const std::string & dir_path ) { static const fs::directory_iterator end_itr; return fs::directory_iterator(fs::path(dir_path)) == end_itr; } #ifdef BOOST_WINDOWS_API // For Windows, the xxxA form of various function names is used to avoid // inadvertently getting wide forms of the functions. (The undecorated // forms are actually macros, so can misfire if the user has various // other macros defined. There was a bug report of this happening.) inline DWORD get_file_attributes( const char * ph ) { return ::GetFileAttributesA( ph ); } # ifndef BOOST_FILESYSTEM_NARROW_ONLY inline DWORD get_file_attributes( const wchar_t * ph ) { return ::GetFileAttributesW( ph ); } bool is_empty_directory( const std::wstring & dir_path ) { static const fs::wdirectory_iterator wend_itr; return fs::wdirectory_iterator(fs::wpath(dir_path)) == wend_itr; } inline BOOL get_file_attributes_ex( const wchar_t * ph, WIN32_FILE_ATTRIBUTE_DATA & fad ) { return ::GetFileAttributesExW( ph, ::GetFileExInfoStandard, &fad ); } HANDLE create_file( const wchar_t * ph, DWORD dwDesiredAccess, DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile ) { return ::CreateFileW( ph, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile ); } inline DWORD get_current_directory( DWORD sz, wchar_t * buf ) { return ::GetCurrentDirectoryW( sz, buf ); } inline bool set_current_directory( const wchar_t * buf ) { return ::SetCurrentDirectoryW( buf ) != 0 ; } inline bool get_free_disk_space( const std::wstring & ph, PULARGE_INTEGER avail, PULARGE_INTEGER total, PULARGE_INTEGER free ) { return ::GetDiskFreeSpaceExW( ph.c_str(), avail, total, free ) != 0; } inline std::size_t get_full_path_name( const std::wstring & ph, std::size_t len, wchar_t * buf, wchar_t ** p ) { return static_cast( ::GetFullPathNameW( ph.c_str(), static_cast(len), buf, p )); } inline bool remove_directory( const std::wstring & ph ) { return ::RemoveDirectoryW( ph.c_str() ) != 0; } inline bool delete_file( const std::wstring & ph ) { return ::DeleteFileW( ph.c_str() ) != 0; } inline bool create_directory( const std::wstring & dir ) { return ::CreateDirectoryW( dir.c_str(), 0 ) != 0; } #if _WIN32_WINNT >= 0x500 inline bool create_hard_link( const std::wstring & to_ph, const std::wstring & from_ph ) { return ::CreateHardLinkW( from_ph.c_str(), to_ph.c_str(), 0 ) != 0; } #endif # endif // ifndef BOOST_FILESYSTEM_NARROW_ONLY template< class String > fs::file_status status_template( const String & ph, error_code & ec ) { DWORD attr( get_file_attributes( ph.c_str() ) ); if ( attr == 0xFFFFFFFF ) { ec = error_code( ::GetLastError(), system_category ); if ((ec.value() == ERROR_FILE_NOT_FOUND) || (ec.value() == ERROR_PATH_NOT_FOUND) || (ec.value() == ERROR_INVALID_NAME) // "tools/jam/src/:sys:stat.h", "//foo" || (ec.value() == ERROR_INVALID_DRIVE) // USB card reader with no card inserted || (ec.value() == ERROR_INVALID_PARAMETER) // ":sys:stat.h" || (ec.value() == ERROR_BAD_PATHNAME) // "//nosuch" on Win64 || (ec.value() == ERROR_BAD_NETPATH)) // "//nosuch" on Win32 { ec = ok; // these are not considered errors; // the status is considered not found return fs::file_status( fs::file_not_found ); } else if ((ec.value() == ERROR_SHARING_VIOLATION)) { ec = ok; // these are not considered errors; // the file exists but the type is not known return fs::file_status( fs::type_unknown ); } return fs::file_status( fs::status_unknown ); } ec = ok;; return (attr & FILE_ATTRIBUTE_DIRECTORY) ? fs::file_status( fs::directory_file ) : fs::file_status( fs::regular_file ); } BOOL get_file_attributes_ex( const char * ph, WIN32_FILE_ATTRIBUTE_DATA & fad ) { return ::GetFileAttributesExA( ph, ::GetFileExInfoStandard, &fad ); } template< class String > boost::filesystem::detail::query_pair is_empty_template( const String & ph ) { WIN32_FILE_ATTRIBUTE_DATA fad; if ( get_file_attributes_ex( ph.c_str(), fad ) == 0 ) return std::make_pair( error_code( ::GetLastError(), system_category ), false ); return std::make_pair( ok, ( fad.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) ? is_empty_directory( ph ) :( !fad.nFileSizeHigh && !fad.nFileSizeLow ) ); } HANDLE create_file( const char * ph, DWORD dwDesiredAccess, DWORD dwShareMode, LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition, DWORD dwFlagsAndAttributes, HANDLE hTemplateFile ) { return ::CreateFileA( ph, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile ); } // Thanks to Jeremy Maitin-Shepard for much help and for permission to // base the equivalent() implementation on portions of his // file-equivalence-win32.cpp experimental code. struct handle_wrapper { HANDLE handle; handle_wrapper( HANDLE h ) : handle(h) {} ~handle_wrapper() { if ( handle != INVALID_HANDLE_VALUE ) ::CloseHandle(handle); } }; template< class String > boost::filesystem::detail::query_pair equivalent_template( const String & ph1, const String & ph2 ) { // Note well: Physical location on external media is part of the // equivalence criteria. If there are no open handles, physical location // can change due to defragmentation or other relocations. Thus handles // must be held open until location information for both paths has // been retrieved. handle_wrapper p1( create_file( ph1.c_str(), 0, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0 ) ); int error1(0); // save error code in case we have to throw if ( p1.handle == INVALID_HANDLE_VALUE ) error1 = ::GetLastError(); handle_wrapper p2( create_file( ph2.c_str(), 0, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0 ) ); if ( p1.handle == INVALID_HANDLE_VALUE || p2.handle == INVALID_HANDLE_VALUE ) { if ( p1.handle != INVALID_HANDLE_VALUE || p2.handle != INVALID_HANDLE_VALUE ) { return std::make_pair( ok, false ); } assert( p1.handle == INVALID_HANDLE_VALUE && p2.handle == INVALID_HANDLE_VALUE ); { return std::make_pair( error_code( error1, system_category), false ); } } // at this point, both handles are known to be valid BY_HANDLE_FILE_INFORMATION info1, info2; if ( !::GetFileInformationByHandle( p1.handle, &info1 ) ) { return std::make_pair( error_code( ::GetLastError(), system_category ), false ); } if ( !::GetFileInformationByHandle( p2.handle, &info2 ) ) { return std::make_pair( error_code( ::GetLastError(), system_category ), false ); } // In theory, volume serial numbers are sufficient to distinguish between // devices, but in practice VSN's are sometimes duplicated, so last write // time and file size are also checked. return std::make_pair( ok, info1.dwVolumeSerialNumber == info2.dwVolumeSerialNumber && info1.nFileIndexHigh == info2.nFileIndexHigh && info1.nFileIndexLow == info2.nFileIndexLow && info1.nFileSizeHigh == info2.nFileSizeHigh && info1.nFileSizeLow == info2.nFileSizeLow && info1.ftLastWriteTime.dwLowDateTime == info2.ftLastWriteTime.dwLowDateTime && info1.ftLastWriteTime.dwHighDateTime == info2.ftLastWriteTime.dwHighDateTime ); } template< class String > boost::filesystem::detail::uintmax_pair file_size_template( const String & ph ) { WIN32_FILE_ATTRIBUTE_DATA fad; // by now, intmax_t is 64-bits on all Windows compilers if ( get_file_attributes_ex( ph.c_str(), fad ) == 0 ) return std::make_pair( error_code( ::GetLastError(), system_category ), 0 ); if ( (fad.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) !=0 ) return std::make_pair( error_code( ERROR_FILE_NOT_FOUND, system_category), 0 ); return std::make_pair( ok, (static_cast(fad.nFileSizeHigh) << (sizeof(fad.nFileSizeLow)*8)) + fad.nFileSizeLow ); } inline bool get_free_disk_space( const std::string & ph, PULARGE_INTEGER avail, PULARGE_INTEGER total, PULARGE_INTEGER free ) { return ::GetDiskFreeSpaceExA( ph.c_str(), avail, total, free ) != 0; } template< class String > boost::filesystem::detail::space_pair space_template( String & ph ) { ULARGE_INTEGER avail, total, free; boost::filesystem::detail::space_pair result; if ( get_free_disk_space( ph, &avail, &total, &free ) ) { result.first = ok; result.second.capacity = (static_cast(total.HighPart) << 32) + total.LowPart; result.second.free = (static_cast(free.HighPart) << 32) + free.LowPart; result.second.available = (static_cast(avail.HighPart) << 32) + avail.LowPart; } else { result.first = error_code( ::GetLastError(), system_category ); result.second.capacity = result.second.free = result.second.available = 0; } return result; } inline DWORD get_current_directory( DWORD sz, char * buf ) { return ::GetCurrentDirectoryA( sz, buf ); } template< class String > error_code get_current_path_template( String & ph ) { DWORD sz; if ( (sz = get_current_directory( 0, static_cast(0) )) == 0 ) { sz = 1; } typedef typename String::value_type value_type; boost::scoped_array buf( new value_type[sz] ); if ( get_current_directory( sz, buf.get() ) == 0 ) return error_code( ::GetLastError(), system_category ); ph = buf.get(); return ok; } inline bool set_current_directory( const char * buf ) { return ::SetCurrentDirectoryA( buf ) != 0; } template< class String > error_code set_current_path_template( const String & ph ) { return error_code( set_current_directory( ph.c_str() ) ? 0 : ::GetLastError(), system_category ); } inline std::size_t get_full_path_name( const std::string & ph, std::size_t len, char * buf, char ** p ) { return static_cast( ::GetFullPathNameA( ph.c_str(), static_cast(len), buf, p )); } const std::size_t buf_size( 128 ); template error_code get_full_path_name_template( const String & ph, String & target ) { typename String::value_type buf[buf_size]; typename String::value_type * pfn; std::size_t len = get_full_path_name( ph, buf_size , buf, &pfn ); if ( len == 0 ) return error_code( ::GetLastError(), system_category ); if ( len > buf_size ) { typedef typename String::value_type value_type; boost::scoped_array big_buf( new value_type[len] ); if ( (len=get_full_path_name( ph, len , big_buf.get(), &pfn )) == 0 ) return error_code( ::GetLastError(), system_category ); big_buf[len] = '\0'; target = big_buf.get(); return ok; } buf[len] = '\0'; target = buf; return ok; } template error_code get_file_write_time( const String & ph, FILETIME & last_write_time ) { handle_wrapper hw( create_file( ph.c_str(), 0, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0 ) ); if ( hw.handle == INVALID_HANDLE_VALUE ) return error_code( ::GetLastError(), system_category ); return error_code( ::GetFileTime( hw.handle, 0, 0, &last_write_time ) != 0 ? 0 : ::GetLastError(), system_category ); } template error_code set_file_write_time( const String & ph, const FILETIME & last_write_time ) { handle_wrapper hw( create_file( ph.c_str(), FILE_WRITE_ATTRIBUTES, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0 ) ); if ( hw.handle == INVALID_HANDLE_VALUE ) return error_code( ::GetLastError(), system_category ); return error_code( ::SetFileTime( hw.handle, 0, 0, &last_write_time ) != 0 ? 0 : ::GetLastError(), system_category ); } // these constants come from inspecting some Microsoft sample code std::time_t to_time_t( const FILETIME & ft ) { __int64 t = (static_cast<__int64>( ft.dwHighDateTime ) << 32) + ft.dwLowDateTime; # if !defined( BOOST_MSVC ) || BOOST_MSVC > 1300 // > VC++ 7.0 t -= 116444736000000000LL; # else t -= 116444736000000000; # endif t /= 10000000; return static_cast( t ); } void to_FILETIME( std::time_t t, FILETIME & ft ) { __int64 temp = t; temp *= 10000000; # if !defined( BOOST_MSVC ) || BOOST_MSVC > 1300 // > VC++ 7.0 temp += 116444736000000000LL; # else temp += 116444736000000000; # endif ft.dwLowDateTime = static_cast( temp ); ft.dwHighDateTime = static_cast( temp >> 32 ); } template boost::filesystem::detail::time_pair last_write_time_template( const String & ph ) { FILETIME lwt; error_code ec( get_file_write_time( ph, lwt ) ); return std::make_pair( ec, to_time_t( lwt ) ); } template error_code last_write_time_template( const String & ph, const std::time_t new_time ) { FILETIME lwt; to_FILETIME( new_time, lwt ); return set_file_write_time( ph, lwt ); } bool remove_directory( const std::string & ph ) { return ::RemoveDirectoryA( ph.c_str() ) != 0; } bool delete_file( const std::string & ph ) { return ::DeleteFileA( ph.c_str() ) != 0; } template error_code remove_template( const String & ph ) { // TODO: test this code in the presence of Vista symlinks, // including dangling, self-referal, and cyclic symlinks error_code ec; fs::file_status sf( fs::detail::status_api( ph, ec ) ); if ( ec ) return ec; if ( sf.type() == fs::file_not_found ) return ok; if ( fs::is_directory( sf ) ) { if ( !remove_directory( ph ) ) return error_code(::GetLastError(), system_category); } else { if ( !delete_file( ph ) ) return error_code(::GetLastError(), system_category); } return ok; } inline bool create_directory( const std::string & dir ) { return ::CreateDirectoryA( dir.c_str(), 0 ) != 0; } template boost::filesystem::detail::query_pair create_directory_template( const String & dir_ph ) { error_code error, dummy; if ( create_directory( dir_ph ) ) return std::make_pair( error, true ); error = error_code( ::GetLastError(), system_category ); // an error here may simply mean the postcondition is already met if ( error.value() == ERROR_ALREADY_EXISTS && fs::is_directory( fs::detail::status_api( dir_ph, dummy ) ) ) return std::make_pair( ok, false ); 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 ) { return ::CreateHardLinkA( from_ph.c_str(), to_ph.c_str(), 0 ) != 0; } #endif #if _WIN32_WINNT >= 0x500 template error_code create_hard_link_template( const String & to_ph, const String & from_ph ) { return error_code( create_hard_link( to_ph.c_str(), from_ph.c_str() ) ? 0 : ::GetLastError(), system_category ); } #endif #else // BOOST_POSIX_API int posix_remove( const char * p ) { # if defined(__QNXNTO__) || (defined(__MSL__) && (defined(macintosh) || defined(__APPLE__) || defined(__APPLE_CC__))) // Some Metrowerks C library versions fail on directories because of a // known Metrowerks coding error in ::remove. Workaround is to call // rmdir() or unlink() as indicated. // Same bug also reported for QNX, with the same fix. int err = ::unlink( p ); if ( err == 0 || errno != EPERM ) return err; return ::rmdir( p ); # else return std::remove( p ); # endif } #endif } // unnamed namespace namespace boost { namespace filesystem { namespace detail { BOOST_FILESYSTEM_DECL system::error_code throws; // free functions ----------------------------------------------------------// BOOST_FILESYSTEM_DECL error_code not_found_error() { # ifdef BOOST_WINDOWS_API return error_code(ERROR_PATH_NOT_FOUND, system_category); # else return error_code(ENOENT, system_category); # endif } BOOST_FILESYSTEM_DECL bool possible_large_file_size_support() { # ifdef BOOST_POSIX_API struct stat lcl_stat; return sizeof( lcl_stat.st_size ) > 4; # else return true; # endif } # ifdef BOOST_WINDOWS_API BOOST_FILESYSTEM_DECL fs::file_status status_api( const std::string & ph, error_code & ec ) { return status_template( ph, ec ); } # ifndef BOOST_FILESYSTEM_NARROW_ONLY BOOST_FILESYSTEM_DECL fs::file_status status_api( const std::wstring & ph, error_code & ec ) { return status_template( ph, ec ); } BOOST_FILESYSTEM_DECL bool symbolic_link_exists_api( const std::wstring & ) { return false; } BOOST_FILESYSTEM_DECL fs::detail::query_pair is_empty_api( const std::wstring & ph ) { return is_empty_template( ph ); } BOOST_FILESYSTEM_DECL fs::detail::query_pair equivalent_api( const std::wstring & ph1, const std::wstring & ph2 ) { return equivalent_template( ph1, ph2 ); } BOOST_FILESYSTEM_DECL fs::detail::uintmax_pair file_size_api( const std::wstring & ph ) { return file_size_template( ph ); } BOOST_FILESYSTEM_DECL fs::detail::space_pair space_api( const std::wstring & ph ) { return space_template( ph ); } BOOST_FILESYSTEM_DECL error_code get_current_path_api( std::wstring & ph ) { return get_current_path_template( ph ); } BOOST_FILESYSTEM_DECL error_code set_current_path_api( const std::wstring & ph ) { return set_current_path_template( ph ); } BOOST_FILESYSTEM_DECL error_code get_full_path_name_api( const std::wstring & ph, std::wstring & target ) { return get_full_path_name_template( ph, target ); } BOOST_FILESYSTEM_DECL time_pair last_write_time_api( const std::wstring & ph ) { return last_write_time_template( ph ); } BOOST_FILESYSTEM_DECL error_code last_write_time_api( const std::wstring & ph, std::time_t new_value ) { return last_write_time_template( ph, new_value ); } BOOST_FILESYSTEM_DECL fs::detail::query_pair create_directory_api( const std::wstring & ph ) { return create_directory_template( ph ); } #if _WIN32_WINNT >= 0x500 BOOST_FILESYSTEM_DECL error_code create_hard_link_api( const std::wstring & to_ph, const std::wstring & from_ph ) { return create_hard_link_template( to_ph, from_ph ); } #endif BOOST_FILESYSTEM_DECL error_code create_symlink_api( const std::wstring & /*to_ph*/, const std::wstring & /*from_ph*/ ) { return error_code( ERROR_NOT_SUPPORTED, system_category ); } BOOST_FILESYSTEM_DECL error_code remove_api( const std::wstring & ph ) { return remove_template( ph ); } BOOST_FILESYSTEM_DECL error_code rename_api( const std::wstring & from, const std::wstring & to ) { return error_code( ::MoveFileW( from.c_str(), to.c_str() ) ? 0 : ::GetLastError(), system_category ); } BOOST_FILESYSTEM_DECL error_code copy_file_api( const std::wstring & from, const std::wstring & to ) { return error_code( ::CopyFileW( from.c_str(), to.c_str(), /*fail_if_exists=*/true ) ? 0 : ::GetLastError(), system_category ); } BOOST_FILESYSTEM_DECL bool create_file_api( const std::wstring & ph, std::ios_base::openmode mode ) // true if succeeds { DWORD access( ((mode & std::ios_base::in) == 0 ? 0 : GENERIC_READ) | ((mode & std::ios_base::out) == 0 ? 0 : GENERIC_WRITE) ); DWORD disposition(0); // see 27.8.1.3 Table 92 if ( (mode&~std::ios_base::binary) == (std::ios_base::out|std::ios_base::app) ) disposition = OPEN_ALWAYS; else if ( (mode&~(std::ios_base::binary|std::ios_base::out)) == std::ios_base::in ) disposition = OPEN_EXISTING; else if ( ((mode&~(std::ios_base::binary|std::ios_base::trunc)) == std::ios_base::out ) || ((mode&~std::ios_base::binary) == (std::ios_base::in|std::ios_base::out|std::ios_base::trunc)) ) disposition = CREATE_ALWAYS; else assert( 0 && "invalid mode argument" ); HANDLE handle ( ::CreateFileW( ph.c_str(), access, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, 0, disposition, (mode &std::ios_base::out) != 0 ? FILE_ATTRIBUTE_ARCHIVE : FILE_ATTRIBUTE_NORMAL, 0 ) ); if ( handle == INVALID_HANDLE_VALUE ) return false; ::CloseHandle( handle ); return true; } BOOST_FILESYSTEM_DECL std::string narrow_path_api( const std::wstring & ph ) // return is empty if fails { std::string narrow_short_form; std::wstring short_form; for ( DWORD buf_sz( static_cast( ph.size()+1 ));; ) { boost::scoped_array buf( new wchar_t[buf_sz] ); DWORD sz( ::GetShortPathNameW( ph.c_str(), buf.get(), buf_sz ) ); if ( sz == 0 ) return narrow_short_form; if ( sz <= buf_sz ) { short_form += buf.get(); break; } buf_sz = sz + 1; } // contributed by Takeshi Mouri: int narrow_sz( ::WideCharToMultiByte( CP_ACP, 0, short_form.c_str(), static_cast(short_form.size()), 0, 0, 0, 0 ) ); boost::scoped_array narrow_buf( new char[narrow_sz] ); ::WideCharToMultiByte( CP_ACP, 0, short_form.c_str(), static_cast(short_form.size()), narrow_buf.get(), narrow_sz, 0, 0 ); narrow_short_form.assign(narrow_buf.get(), narrow_sz); return narrow_short_form; } BOOST_FILESYSTEM_DECL error_code dir_itr_first( void *& handle, const std::wstring & dir, std::wstring & target, file_status & sf, file_status & symlink_sf ) { // use a form of search Sebastian Martel reports will work with Win98 std::wstring dirpath( dir ); dirpath += (dirpath.empty() || dirpath[dirpath.size()-1] != L'\\') ? L"\\*" : L"*"; WIN32_FIND_DATAW data; if ( (handle = ::FindFirstFileW( dirpath.c_str(), &data )) == INVALID_HANDLE_VALUE ) { handle = 0; return error_code( ::GetLastError() == ERROR_FILE_NOT_FOUND ? 0 : ::GetLastError(), system_category ); } target = data.cFileName; if ( data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) { sf.type( directory_file ); symlink_sf.type( directory_file ); } else { sf.type( regular_file ); symlink_sf.type( regular_file ); } return ok; } BOOST_FILESYSTEM_DECL error_code dir_itr_increment( void *& handle, std::wstring & target, file_status & sf, file_status & symlink_sf ) { WIN32_FIND_DATAW data; if ( ::FindNextFileW( handle, &data ) == 0 ) // fails { int error = ::GetLastError(); dir_itr_close( handle ); return error_code( error == ERROR_NO_MORE_FILES ? 0 : error, system_category ); } target = data.cFileName; if ( data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) { sf.type( directory_file ); symlink_sf.type( directory_file ); } else { sf.type( regular_file ); symlink_sf.type( regular_file ); } return ok; } # endif // ifndef BOOST_FILESYSTEM_NARROW_ONLY // suggested by Walter Landry BOOST_FILESYSTEM_DECL bool symbolic_link_exists_api( const std::string & ) { return false; } BOOST_FILESYSTEM_DECL fs::detail::query_pair is_empty_api( const std::string & ph ) { return is_empty_template( ph ); } BOOST_FILESYSTEM_DECL fs::detail::query_pair equivalent_api( const std::string & ph1, const std::string & ph2 ) { return equivalent_template( ph1, ph2 ); } BOOST_FILESYSTEM_DECL fs::detail::uintmax_pair file_size_api( const std::string & ph ) { return file_size_template( ph ); } BOOST_FILESYSTEM_DECL fs::detail::space_pair space_api( const std::string & ph ) { return space_template( ph ); } BOOST_FILESYSTEM_DECL error_code get_current_path_api( std::string & ph ) { return get_current_path_template( ph ); } BOOST_FILESYSTEM_DECL error_code set_current_path_api( const std::string & ph ) { return set_current_path_template( ph ); } BOOST_FILESYSTEM_DECL error_code get_full_path_name_api( const std::string & ph, std::string & target ) { return get_full_path_name_template( ph, target ); } BOOST_FILESYSTEM_DECL time_pair last_write_time_api( const std::string & ph ) { return last_write_time_template( ph ); } BOOST_FILESYSTEM_DECL error_code last_write_time_api( const std::string & ph, std::time_t new_value ) { return last_write_time_template( ph, new_value ); } BOOST_FILESYSTEM_DECL fs::detail::query_pair create_directory_api( const std::string & ph ) { return create_directory_template( ph ); } #if _WIN32_WINNT >= 0x500 BOOST_FILESYSTEM_DECL error_code create_hard_link_api( const std::string & to_ph, const std::string & from_ph ) { return create_hard_link_template( to_ph, from_ph ); } #endif BOOST_FILESYSTEM_DECL error_code create_symlink_api( const std::string & /*to_ph*/, const std::string & /*from_ph*/ ) { return error_code( ERROR_NOT_SUPPORTED, system_category ); } BOOST_FILESYSTEM_DECL error_code remove_api( const std::string & ph ) { return remove_template( ph ); } BOOST_FILESYSTEM_DECL error_code rename_api( const std::string & from, const std::string & to ) { return error_code( ::MoveFileA( from.c_str(), to.c_str() ) ? 0 : ::GetLastError(), system_category ); } BOOST_FILESYSTEM_DECL error_code copy_file_api( const std::string & from, const std::string & to ) { return error_code( ::CopyFileA( from.c_str(), to.c_str(), /*fail_if_exists=*/true ) ? 0 : ::GetLastError(), system_category ); } BOOST_FILESYSTEM_DECL error_code 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 // causes a ERROR_FILE_NOT_FOUND error which we do not considered an // error. It is treated as eof instead. { // use a form of search Sebastian Martel reports will work with Win98 std::string dirpath( dir ); dirpath += (dirpath.empty() || (dirpath[dirpath.size()-1] != '\\' && dirpath[dirpath.size()-1] != ':')) ? "\\*" : "*"; WIN32_FIND_DATAA data; if ( (handle = ::FindFirstFileA( dirpath.c_str(), &data )) == INVALID_HANDLE_VALUE ) { handle = 0; return error_code( ::GetLastError() == ERROR_FILE_NOT_FOUND ? 0 : ::GetLastError(), system_category ); } target = data.cFileName; if ( data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) { sf.type( directory_file ); symlink_sf.type( directory_file ); } else { sf.type( regular_file ); symlink_sf.type( regular_file ); } return ok; } BOOST_FILESYSTEM_DECL error_code dir_itr_close( void *& handle ) { if ( handle != 0 ) { bool ok = ::FindClose( handle ) != 0; handle = 0; return error_code( ok ? 0 : ::GetLastError(), system_category ); } return ok; } BOOST_FILESYSTEM_DECL error_code dir_itr_increment( void *& handle, std::string & target, file_status & sf, file_status & symlink_sf ) { WIN32_FIND_DATAA data; if ( ::FindNextFileA( handle, &data ) == 0 ) // fails { int error = ::GetLastError(); dir_itr_close( handle ); return error_code( error == ERROR_NO_MORE_FILES ? 0 : error, system_category ); } target = data.cFileName; if ( data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) { sf.type( directory_file ); symlink_sf.type( directory_file ); } else { sf.type( regular_file ); symlink_sf.type( regular_file ); } return ok; } # else // BOOST_POSIX_API BOOST_FILESYSTEM_DECL fs::file_status status_api( const std::string & ph, error_code & ec ) { struct stat path_stat; if ( ::stat( ph.c_str(), &path_stat ) != 0 ) { if ( errno == ENOENT || errno == ENOTDIR ) { ec = ok; return fs::file_status( fs::file_not_found ); } ec = error_code( errno, system_category ); return fs::file_status( fs::status_unknown ); } ec = ok; if ( S_ISDIR( path_stat.st_mode ) ) return fs::file_status( fs::directory_file ); if ( S_ISREG( path_stat.st_mode ) ) return fs::file_status( fs::regular_file ); if ( S_ISBLK( path_stat.st_mode ) ) return fs::file_status( fs::block_file ); if ( S_ISCHR( path_stat.st_mode ) ) return fs::file_status( fs::character_file ); if ( S_ISFIFO( path_stat.st_mode ) ) return fs::file_status( fs::fifo_file ); if ( S_ISSOCK( path_stat.st_mode ) ) return fs::file_status( fs::socket_file ); return fs::file_status( fs::type_unknown ); } BOOST_FILESYSTEM_DECL fs::file_status symlink_status_api( const std::string & ph, error_code & ec ) { struct stat path_stat; if ( ::lstat( ph.c_str(), &path_stat ) != 0 ) { if ( errno == ENOENT || errno == ENOTDIR ) { ec = ok; return fs::file_status( fs::file_not_found ); } ec = error_code( errno, system_category ); return fs::file_status( fs::status_unknown ); } ec = ok; if ( S_ISREG( path_stat.st_mode ) ) return fs::file_status( fs::regular_file ); if ( S_ISDIR( path_stat.st_mode ) ) return fs::file_status( fs::directory_file ); if ( S_ISLNK( path_stat.st_mode ) ) return fs::file_status( fs::symlink_file ); if ( S_ISBLK( path_stat.st_mode ) ) return fs::file_status( fs::block_file ); if ( S_ISCHR( path_stat.st_mode ) ) return fs::file_status( fs::character_file ); if ( S_ISFIFO( path_stat.st_mode ) ) return fs::file_status( fs::fifo_file ); if ( S_ISSOCK( path_stat.st_mode ) ) return fs::file_status( fs::socket_file ); return fs::file_status( fs::type_unknown ); } // suggested by Walter Landry BOOST_FILESYSTEM_DECL bool symbolic_link_exists_api( const std::string & ph ) { struct stat path_stat; return ::lstat( ph.c_str(), &path_stat ) == 0 && S_ISLNK( path_stat.st_mode ); } BOOST_FILESYSTEM_DECL query_pair is_empty_api( const std::string & ph ) { struct stat path_stat; if ( (::stat( ph.c_str(), &path_stat )) != 0 ) return std::make_pair( error_code( errno, system_category ), false ); return std::make_pair( ok, S_ISDIR( path_stat.st_mode ) ? is_empty_directory( ph ) : path_stat.st_size == 0 ); } BOOST_FILESYSTEM_DECL query_pair equivalent_api( const std::string & ph1, const std::string & ph2 ) { struct stat s2; int e2( ::stat( ph2.c_str(), &s2 ) ); struct stat s1; int e1( ::stat( ph1.c_str(), &s1 ) ); if ( e1 != 0 || e2 != 0 ) return std::make_pair( error_code( e1 != 0 && e2 != 0 ? errno : 0, system_category ), false ); // at this point, both stats are known to be valid return std::make_pair( ok, s1.st_dev == s2.st_dev && s1.st_ino == s2.st_ino // According to the POSIX stat specs, "The st_ino and st_dev fields // taken together uniquely identify the file within the system." // Just to be sure, size and mod time are also checked. && s1.st_size == s2.st_size && s1.st_mtime == s2.st_mtime ); } BOOST_FILESYSTEM_DECL uintmax_pair file_size_api( const std::string & ph ) { struct stat path_stat; if ( ::stat( ph.c_str(), &path_stat ) != 0 ) return std::make_pair( error_code( errno, system_category ), 0 ); if ( !S_ISREG( path_stat.st_mode ) ) return std::make_pair( error_code( EPERM, system_category ), 0 ); return std::make_pair( ok, static_cast(path_stat.st_size) ); } BOOST_FILESYSTEM_DECL space_pair space_api( const std::string & ph ) { struct BOOST_STATVFS vfs; space_pair result; if ( ::BOOST_STATVFS( ph.c_str(), &vfs ) != 0 ) { result.first = error_code( errno, system_category ); result.second.capacity = result.second.free = result.second.available = 0; } else { result.first = ok; result.second.capacity = static_cast(vfs.f_blocks) * BOOST_STATVFS_F_FRSIZE; result.second.free = static_cast(vfs.f_bfree) * BOOST_STATVFS_F_FRSIZE; result.second.available = static_cast(vfs.f_bavail) * BOOST_STATVFS_F_FRSIZE; } return result; } BOOST_FILESYSTEM_DECL time_pair last_write_time_api( const std::string & ph ) { struct stat path_stat; if ( ::stat( ph.c_str(), &path_stat ) != 0 ) return std::make_pair( error_code( errno, system_category ), 0 ); return std::make_pair( ok, path_stat.st_mtime ); } BOOST_FILESYSTEM_DECL error_code last_write_time_api( const std::string & ph, std::time_t new_value ) { struct stat path_stat; if ( ::stat( ph.c_str(), &path_stat ) != 0 ) return error_code( errno, system_category ); ::utimbuf buf; buf.actime = path_stat.st_atime; // utime() updates access time too:-( buf.modtime = new_value; return error_code( ::utime( ph.c_str(), &buf ) != 0 ? errno : 0, system_category ); } BOOST_FILESYSTEM_DECL error_code get_current_path_api( std::string & ph ) { for ( long path_max = 32;; path_max *=2 ) // loop 'til buffer large enough { boost::scoped_array buf( new char[static_cast(path_max)] ); if ( ::getcwd( buf.get(), static_cast(path_max) ) == 0 ) { if ( errno != ERANGE // bug in some versions of the Metrowerks C lib on the Mac: wrong errno set # if defined(__MSL__) && (defined(macintosh) || defined(__APPLE__) || defined(__APPLE_CC__)) && errno != 0 # endif ) return error_code( errno, system_category ); } else { ph = buf.get(); break; } } return ok; } BOOST_FILESYSTEM_DECL error_code set_current_path_api( const std::string & ph ) { return error_code( ::chdir( ph.c_str() ) ? errno : 0, system_category ); } BOOST_FILESYSTEM_DECL fs::detail::query_pair create_directory_api( const std::string & ph ) { if ( ::mkdir( ph.c_str(), S_IRWXU|S_IRWXG|S_IRWXO ) == 0 ) { return std::make_pair( ok, true ); } int ec=errno; error_code dummy; if ( ec != EEXIST || !fs::is_directory( status_api( ph, dummy ) ) ) { return std::make_pair( error_code( ec, system_category ), false ); } return std::make_pair( ok, false ); } BOOST_FILESYSTEM_DECL error_code create_hard_link_api( const std::string & to_ph, const std::string & from_ph ) { return error_code( ::link( to_ph.c_str(), from_ph.c_str() ) == 0 ? 0 : errno, system_category ); } BOOST_FILESYSTEM_DECL error_code create_symlink_api( const std::string & to_ph, const std::string & from_ph ) { return error_code( ::symlink( to_ph.c_str(), from_ph.c_str() ) == 0 ? 0 : errno, system_category ); } BOOST_FILESYSTEM_DECL error_code remove_api( const std::string & ph ) { if ( posix_remove( ph.c_str() ) == 0 ) return ok; int error = errno; // POSIX says "If the directory is not an empty directory, rmdir() // shall fail and set errno to EEXIST or ENOTEMPTY." // Linux uses ENOTEMPTY, Solaris uses EEXIST. if ( error == EEXIST ) error = ENOTEMPTY; error_code ec; // ignore errors if post-condition satisfied return status_api(ph, ec).type() == file_not_found ? ok : error_code( error, system_category ) ; } BOOST_FILESYSTEM_DECL error_code rename_api( const std::string & from, const std::string & to ) { // POSIX is too permissive so must check error_code dummy; if ( fs::exists( status_api( to, dummy ) ) ) return error_code( EEXIST, system_category ); return error_code( std::rename( from.c_str(), to.c_str() ) != 0 ? errno : 0, system_category ); } BOOST_FILESYSTEM_DECL error_code copy_file_api( const std::string & from_file_ph, const std::string & to_file_ph ) { const std::size_t buf_sz = 32768; boost::scoped_array buf( new char [buf_sz] ); int infile=-1, outfile=-1; // -1 means not open struct stat from_stat; if ( ::stat( from_file_ph.c_str(), &from_stat ) != 0 || (infile = ::open( from_file_ph.c_str(), O_RDONLY )) < 0 || (outfile = ::open( to_file_ph.c_str(), O_WRONLY | O_CREAT | O_EXCL, from_stat.st_mode )) < 0 ) { if ( infile >= 0 ) ::close( infile ); return error_code( errno, system_category ); } ssize_t sz, sz_read=1, sz_write; while ( sz_read > 0 && (sz_read = ::read( infile, buf.get(), buf_sz )) > 0 ) { // Allow for partial writes - see Advanced Unix Programming (2nd Ed.), // Marc Rochkind, Addison-Wesley, 2004, page 94 sz_write = 0; do { if ( (sz = ::write( outfile, buf.get() + sz_write, sz_read - sz_write )) < 0 ) { sz_read = sz; // cause read loop termination break; // and error to be thrown after closes } sz_write += sz; } while ( sz_write < sz_read ); } if ( ::close( infile) < 0 ) sz_read = -1; if ( ::close( outfile) < 0 ) sz_read = -1; return error_code( sz_read < 0 ? errno : 0, system_category ); } // this code is based on Stevens and Rago, Advanced Programming in the // UNIX envirnment, 2nd Ed., ISBN 0-201-43307-9, page 49 error_code path_max( std::size_t & result ) { # ifdef PATH_MAX static std::size_t max = PATH_MAX; # else static std::size_t max = 0; # endif if ( max == 0 ) { errno = 0; long tmp = ::pathconf( "/", _PC_NAME_MAX ); if ( tmp < 0 ) { if ( errno == 0 ) // indeterminate max = 4096; // guess else return error_code( errno, system_category ); } else max = static_cast( tmp + 1 ); // relative root } result = max; return ok; } BOOST_FILESYSTEM_DECL error_code dir_itr_first( void *& handle, void *& buffer, const std::string & dir, std::string & target, file_status &, file_status & ) { if ( (handle = ::opendir( dir.c_str() )) == 0 ) return error_code( errno, system_category ); target = std::string( "." ); // string was static but caused trouble // when iteration called from dtor, after // static had already been destroyed std::size_t path_size; error_code ec = path_max( path_size ); if ( ec ) return ec; dirent de; buffer = std::malloc( (sizeof(dirent) - sizeof(de.d_name)) + path_size + 1 ); // + 1 for "/0" return ok; } BOOST_FILESYSTEM_DECL error_code dir_itr_close( void *& handle, void*& buffer ) { std::free( buffer ); buffer = 0; if ( handle == 0 ) return ok; DIR * h( static_cast(handle) ); handle = 0; return error_code( ::closedir( h ) == 0 ? 0 : errno, system_category ); } // warning: the only dirent member updated is d_name inline int readdir_r_simulator( DIR * dirp, struct dirent * entry, struct dirent ** result ) // *result set to 0 on end of directory { errno = 0; # if !defined(__CYGWIN__) \ && defined(_POSIX_THREAD_SAFE_FUNCTIONS) \ && defined(_SC_THREAD_SAFE_FUNCTIONS) \ && (_POSIX_THREAD_SAFE_FUNCTIONS+0 >= 0) \ && (!defined(__hpux) || (defined(__hpux) && defined(_REENTRANT))) if ( ::sysconf( _SC_THREAD_SAFE_FUNCTIONS ) >= 0 ) { return ::readdir_r( dirp, entry, result ); } # endif struct dirent * p; *result = 0; if ( (p = ::readdir( dirp )) == 0 ) return errno; std::strcpy( entry->d_name, p->d_name ); *result = entry; return 0; } BOOST_FILESYSTEM_DECL error_code dir_itr_increment( void *& handle, void *& buffer, std::string & target, file_status & sf, file_status & symlink_sf ) { BOOST_ASSERT( buffer != 0 ); dirent * entry( static_cast(buffer) ); dirent * result; int return_code; if ( (return_code = readdir_r_simulator( static_cast(handle), entry, &result )) != 0 ) return error_code( errno, system_category ); if ( result == 0 ) return dir_itr_close( handle, buffer ); target = entry->d_name; # ifdef BOOST_FILESYSTEM_STATUS_CACHE if ( entry->d_type == DT_UNKNOWN ) // filesystem does not supply d_type value { sf = symlink_sf = fs::file_status(fs::status_unknown); } else // filesystem supplies d_type value { if ( entry->d_type == DT_DIR ) sf = symlink_sf = fs::file_status( fs::directory_file ); else if ( entry->d_type == DT_REG ) sf = symlink_sf = fs::file_status( fs::regular_file ); else if ( entry->d_type == DT_LNK ) { sf = fs::file_status( fs::status_unknown ); symlink_sf = fs::file_status( fs::symlink_file ); } else sf = symlink_sf = fs::file_status( fs::status_unknown ); } # else sf = symlink_sf = fs::file_status( fs::status_unknown ); # endif return ok; } # endif } // namespace detail } // namespace filesystem } // namespace boost