Skip to content

Commit

Permalink
Merge branch 'master' into cmake-alias-target
Browse files Browse the repository at this point in the history
  • Loading branch information
gulrak authored Mar 5, 2023
2 parents 6c6f2c2 + d1f0d79 commit 9f61ee0
Show file tree
Hide file tree
Showing 7 changed files with 194 additions and 60 deletions.
File renamed without changes.
20 changes: 16 additions & 4 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
cmake_minimum_required(VERSION 3.7.2)
project(ghcfilesystem)
project(
ghcfilesystem,
VERSION 1.5.13
)

if (POLICY CMP0077)
cmake_policy(SET CMP0077 NEW)
Expand All @@ -15,7 +18,7 @@ if(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR)
else()
option(GHC_FILESYSTEM_BUILD_EXAMPLES "Build examples" OFF)
option(GHC_FILESYSTEM_BUILD_TESTING "Enable tests" OFF)
option(GHC_FILESYSTEM_WITH_INSTALL "With install target" ON)
option(GHC_FILESYSTEM_WITH_INSTALL "With install target" OFF)
endif()
option(GHC_FILESYSTEM_BUILD_STD_TESTING "Enable STD tests" ${GHC_FILESYSTEM_BUILD_TESTING})
if(NOT DEFINED GHC_FILESYSTEM_TEST_COMPILE_FEATURES)
Expand Down Expand Up @@ -71,6 +74,15 @@ if(GHC_FILESYSTEM_WITH_INSTALL)
"${PROJECT_BINARY_DIR}/cmake/ghc_filesystem-config.cmake"
INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/ghc_filesystem"
PATH_VARS CMAKE_INSTALL_INCLUDEDIR)
install(FILES "${PROJECT_BINARY_DIR}/cmake/ghc_filesystem-config.cmake"
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/ghc_filesystem")
write_basic_package_version_file(
"${PROJECT_BINARY_DIR}/cmake/ghc_filesystem-config-version.cmake"
VERSION ${PROJECT_VERSION}
COMPATIBILITY SameMinorVersion
)
install(
FILES
"${PROJECT_BINARY_DIR}/cmake/ghc_filesystem-config.cmake"
"${PROJECT_BINARY_DIR}/cmake/ghc_filesystem-config-version.cmake"
DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/ghc_filesystem"
)
endif()
40 changes: 38 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,9 @@ macOS 10.12/10.14/10.15/11.6, Windows 10, Ubuntu 18.04, Ubuntu 20.04, CentOS 7,
Alpine ARM/ARM64 Linux and Solaris 10 but should work on other systems too, as long as you have
at least a C++11 compatible compiler. It should work with Android NDK, Emscripten and I even
had reports of it being used on iOS (within sandboxing constraints) and with v1.5.6 there
is experimental support for QNX. The support of Android NDK, Emscripten and QNX is not
backed up by automated testing but PRs and bug reports are welcome for those too.
is experimental support for QNX. The support of Android NDK, Emscripten, QNX, GNU/Hurd and Haiku is not
backed up by automated testing but PRs and bug reports are welcome for those too and they are reported
to work.
It is of course in its own namespace `ghc::filesystem` to not interfere with a regular `std::filesystem`
should you use it in a mixed C++17 environment (which is possible).

Expand Down Expand Up @@ -584,6 +585,41 @@ to the expected behavior.
## Release Notes
### v1.5.13 (wip)
* Pull request [#163](https://github.com/gulrak/filesystem/pull/163), build
support for Haiku (also fixes [#159](https://github.com/gulrak/filesystem/issues/159))
* Pull request [#162](https://github.com/gulrak/filesystem/pull/162), fix for
directory iterator treating all files subsequent to a symlink as symlink
on Windows
* Fix for [#160](https://github.com/gulrak/filesystem/issues/160), the cmake
config now only sets install targets by default if the project is no
subproject, as documented
* Fix for [#157](https://github.com/gulrak/filesystem/issues/157), suppress
C4191 warning on MSVC for GetProcAddress casts
* Fix for [#156](https://github.com/gulrak/filesystem/issues/156), on POSIX
`stem()`, `filename()` and `extension()` of `fs::path` would return wrong
result if a colon was in the filename
* Pull request [#154](https://github.com/gulrak/filesystem/pull/145), build
support for GNU/Hurd
* Pull request [#153](https://github.com/gulrak/filesystem/pull/153), fixed
`fs::last_write_time(path, time, ec)` setter on iOS, tvOS and watchOS
* Fix for [#151](https://github.com/gulrak/filesystem/issues/156),
`fs::directory_entry::refresh()` now, consistently with `status()` will not
throw on symlinks to non-existing targets, but make the entry have
`file_type::not_found` as the type
* Pull request [#149](https://github.com/gulrak/filesystem/pull/149), add
version to CMake project and export it
* Fix for [#146](https://github.com/gulrak/filesystem/issues/146), handle `EINTR`
on POSIX directory iteration and file copy to avoid errors on network
filesystems
* Pull request [#145](https://github.com/gulrak/filesystem/pull/145), fix for
Y2038 bug in timeToFILETIME on Windows
* Pull request [#144](https://github.com/gulrak/filesystem/pull/144), `fs::copy_file()`
now also copies the permissions
* Pull request [#143](https://github.com/gulrak/filesystem/pull/143), fix
for `fs::copy_file()` ignoring the `skip_existing` option.
### [v1.5.12](https://github.com/gulrak/filesystem/releases/tag/v1.5.12)
* Fix for [#142](https://github.com/gulrak/filesystem/issues/142), removed need
Expand Down
9 changes: 9 additions & 0 deletions cmake/GhcHelper.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang" AND (CMAKE_CXX_COMPILER_VERSION V
if(${CMAKE_SYSTEM_NAME} MATCHES "(SunOS|Solaris)")
target_link_libraries(filesystem_test xnet)
endif()
if(${CMAKE_SYSTEM_NAME} MATCHES "Haiku")
target_link_libraries(filesystem_test network)
endif()
target_compile_definitions(${targetName} PRIVATE USE_STD_FS)
endif()

Expand All @@ -34,6 +37,9 @@ if (CMAKE_COMPILER_IS_GNUCXX AND (CMAKE_CXX_COMPILER_VERSION VERSION_EQUAL 8.0 O
if(${CMAKE_SYSTEM_NAME} MATCHES "(SunOS|Solaris)")
target_link_libraries(${targetName} xnet)
endif()
if(${CMAKE_SYSTEM_NAME} MATCHES "Haiku")
target_link_libraries(${targetName} network)
endif()
target_compile_options(${targetName} PRIVATE $<$<BOOL:${CYGWIN}>:-Wa,-mbig-obj>)
target_compile_definitions(${targetName} PRIVATE USE_STD_FS)
endif()
Expand All @@ -55,6 +61,9 @@ macro(AddTestExecutableWithStdCpp cppStd)
if(${CMAKE_SYSTEM_NAME} MATCHES "(SunOS|Solaris)")
target_link_libraries(filesystem_test_cpp${cppStd} xnet)
endif()
if(${CMAKE_SYSTEM_NAME} MATCHES "Haiku")
target_link_libraries(filesystem_test_cpp${cppStd} network)
endif()
target_compile_options(filesystem_test_cpp${cppStd} PRIVATE
$<$<BOOL:${EMSCRIPTEN}>:-s DISABLE_EXCEPTION_CATCHING=0>
$<$<OR:$<CXX_COMPILER_ID:Clang>,$<CXX_COMPILER_ID:AppleClang>>:-Wall -Wextra -Wshadow -Wconversion -Wsign-conversion -Wpedantic -Werror -Wno-error=deprecated-declarations>
Expand Down
130 changes: 82 additions & 48 deletions include/ghc/filesystem.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@
#include <wasi/api.h>
#elif defined(__QNX__)
#define GHC_OS_QNX
#elif defined(__HAIKU__)
#define GHC_OS_HAIKU
#else
#error "Operating system currently not supported!"
#endif
Expand Down Expand Up @@ -306,7 +308,7 @@
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

// ghc::filesystem version in decimal (major * 10000 + minor * 100 + patch)
#define GHC_FILESYSTEM_VERSION 10512L
#define GHC_FILESYSTEM_VERSION 10513L

#if !defined(GHC_WITH_EXCEPTIONS) && (defined(__EXCEPTIONS) || defined(__cpp_exceptions) || defined(_CPPUNWIND))
#define GHC_WITH_EXCEPTIONS
Expand Down Expand Up @@ -1955,10 +1957,15 @@ GHC_INLINE void create_symlink(const path& target_name, const path& new_symlink,
#if defined(__GNUC__) && __GNUC__ >= 8
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wcast-function-type"
#elif defined(_MSC_VER) && !defined(__INTEL_COMPILER) && !defined(__clang__)
#pragma warning(push)
#pragma warning(disable : 4191)
#endif
static CreateSymbolicLinkW_fp api_call = reinterpret_cast<CreateSymbolicLinkW_fp>(GetProcAddress(GetModuleHandleW(L"kernel32.dll"), "CreateSymbolicLinkW"));
#if defined(__GNUC__) && __GNUC__ >= 8
#pragma GCC diagnostic pop
#elif defined(_MSC_VER) && !defined(__INTEL_COMPILER) && !defined(__clang__)
#pragma warning(pop)
#endif
if (api_call) {
if (api_call(GHC_NATIVEWP(new_symlink), GHC_NATIVEWP(target_name), to_directory ? 1 : 0) == 0) {
Expand All @@ -1979,10 +1986,15 @@ GHC_INLINE void create_hardlink(const path& target_name, const path& new_hardlin
#if defined(__GNUC__) && __GNUC__ >= 8
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wcast-function-type"
#elif defined(_MSC_VER) && !defined(__INTEL_COMPILER) && !defined(__clang__)
#pragma warning(push)
#pragma warning(disable : 4191)
#endif
static CreateHardLinkW_fp api_call = reinterpret_cast<CreateHardLinkW_fp>(GetProcAddress(GetModuleHandleW(L"kernel32.dll"), "CreateHardLinkW"));
#if defined(__GNUC__) && __GNUC__ >= 8
#pragma GCC diagnostic pop
#elif defined(_MSC_VER) && !defined(__INTEL_COMPILER) && !defined(__clang__)
#pragma warning(pop)
#endif
if (api_call) {
if (api_call(GHC_NATIVEWP(new_hardlink), GHC_NATIVEWP(target_name), NULL) == 0) {
Expand Down Expand Up @@ -2260,41 +2272,43 @@ GHC_INLINE uintmax_t hard_links_from_INFO<BY_HANDLE_FILE_INFORMATION>(const BY_H
}

template <typename INFO>
GHC_INLINE DWORD reparse_tag_from_INFO(const INFO*)
GHC_INLINE bool is_symlink_from_INFO(const path &p, const INFO* info, std::error_code& ec)
{
return 0;
if ((info->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) {
auto reparseData = detail::getReparseData(p, ec);
if (!ec && reparseData && IsReparseTagMicrosoft(reparseData->ReparseTag) && reparseData->ReparseTag == IO_REPARSE_TAG_SYMLINK) {
return true;
}
}
return false;
}

template <>
GHC_INLINE DWORD reparse_tag_from_INFO(const WIN32_FIND_DATAW* info)
GHC_INLINE bool is_symlink_from_INFO(const path &, const WIN32_FIND_DATAW* info, std::error_code&)
{
return info->dwReserved0;
// dwReserved0 is undefined unless dwFileAttributes includes the
// FILE_ATTRIBUTE_REPARSE_POINT attribute according to microsoft
// documentation. In practice, dwReserved0 is not reset which
// causes it to report the incorrect symlink status.
// Note that microsoft documentation does not say whether there is
// a null value for dwReserved0, so we test for symlink directly
// instead of returning the tag which requires returning a null
// value for non-reparse-point files.
return (info->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) && info->dwReserved0 == IO_REPARSE_TAG_SYMLINK;
}

template <typename INFO>
GHC_INLINE file_status status_from_INFO(const path& p, const INFO* info, std::error_code& ec, uintmax_t* sz = nullptr, time_t* lwt = nullptr)
{
file_type ft = file_type::unknown;
if (sizeof(INFO) == sizeof(WIN32_FIND_DATAW)) {
if (detail::reparse_tag_from_INFO(info) == IO_REPARSE_TAG_SYMLINK) {
ft = file_type::symlink;
}
if (is_symlink_from_INFO(p, info, ec)) {
ft = file_type::symlink;
}
else {
if ((info->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) {
auto reparseData = detail::getReparseData(p, ec);
if (!ec && reparseData && IsReparseTagMicrosoft(reparseData->ReparseTag) && reparseData->ReparseTag == IO_REPARSE_TAG_SYMLINK) {
ft = file_type::symlink;
}
}
else if ((info->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
ft = file_type::directory;
}
if (ft == file_type::unknown) {
if ((info->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
ft = file_type::directory;
}
else {
ft = file_type::regular;
}
else {
ft = file_type::regular;
}
perms prms = perms::owner_read | perms::group_read | perms::others_read;
if (!(info->dwFileAttributes & FILE_ATTRIBUTE_READONLY)) {
Expand Down Expand Up @@ -3365,10 +3379,14 @@ GHC_INLINE path::impl_string_type::const_iterator path::iterator::increment(cons
}
}
else {
#ifdef GHC_OS_WINDOWS
if (fromStart && i != _last && *i == ':') {
++i;
}
else {
#else
{
#endif
i = std::find(i, _last, preferred_separator);
}
}
Expand Down Expand Up @@ -3912,11 +3930,14 @@ GHC_INLINE bool copy_file(const path& from, const path& to, copy_options options
ec = tecf;
return false;
}
if (exists(st) && (!is_regular_file(st) || equivalent(from, to, ec) || (options & (copy_options::skip_existing | copy_options::overwrite_existing | copy_options::update_existing)) == copy_options::none)) {
ec = tect ? tect : detail::make_error_code(detail::portable_error::exists);
return false;
}
if (exists(st)) {
if ((options & copy_options::skip_existing) == copy_options::skip_existing) {
return false;
}
if (!is_regular_file(st) || equivalent(from, to, ec) || (options & (copy_options::overwrite_existing | copy_options::update_existing)) == copy_options::none) {
ec = tect ? tect : detail::make_error_code(detail::portable_error::exists);
return false;
}
if ((options & copy_options::update_existing) == copy_options::update_existing) {
auto from_time = last_write_time(from, ec);
if (ec) {
Expand Down Expand Up @@ -3956,15 +3977,33 @@ GHC_INLINE bool copy_file(const path& from, const path& to, copy_options options
::close(in);
return false;
}
if (st.permissions() != sf.permissions()) {
if (::fchmod(out, static_cast<mode_t>(sf.permissions() & perms::all)) != 0) {
ec = detail::make_system_error();
::close(in);
::close(out);
return false;
}
}
ssize_t br, bw;
while ((br = ::read(in, buffer.data(), buffer.size())) > 0) {
while (true) {
do { br = ::read(in, buffer.data(), buffer.size()); } while(errno == EINTR);
if(!br) {
break;
}
if(br < 0) {
ec = detail::make_system_error();
::close(in);
::close(out);
return false;
}
ssize_t offset = 0;
do {
if ((bw = ::write(out, buffer.data() + offset, static_cast<size_t>(br))) > 0) {
br -= bw;
offset += bw;
}
else if (bw < 0) {
else if (bw < 0 && errno != EINTR) {
ec = detail::make_system_error();
::close(in);
::close(out);
Expand Down Expand Up @@ -4199,6 +4238,13 @@ GHC_INLINE path current_path(std::error_code& ec)
return path();
}
return path(std::wstring(buffer.get()), path::native_format);
#elif defined(__GLIBC__)
std::unique_ptr<char, decltype(&std::free)> buffer { ::getcwd(NULL, 0), std::free };
if (buffer == nullptr) {
ec = detail::make_system_error();
return path();
}
return path(buffer.get());
#else
size_t pathlen = static_cast<size_t>(std::max(int(::pathconf(".", _PC_PATH_MAX)), int(PATH_MAX)));
std::unique_ptr<char[]> buffer(new char[pathlen + 1]);
Expand Down Expand Up @@ -4604,9 +4650,9 @@ GHC_INLINE void last_write_time(const path& p, file_time_type new_time, std::err
if (!::SetFileTime(file.get(), 0, 0, &ft)) {
ec = detail::make_system_error();
}
#elif defined(GHC_OS_MACOS)
#ifdef __MAC_OS_X_VERSION_MIN_REQUIRED
#if __MAC_OS_X_VERSION_MIN_REQUIRED < 101300
#elif defined(GHC_OS_MACOS) && \
(__MAC_OS_X_VERSION_MIN_REQUIRED < __MAC_10_13) || (__IPHONE_OS_VERSION_MIN_REQUIRED < __IPHONE_11_0) || \
(__TV_OS_VERSION_MIN_REQUIRED < __TVOS_11_0) || (__WATCH_OS_VERSION_MIN_REQUIRED < __WATCHOS_4_0)
struct ::stat fs;
if (::stat(p.c_str(), &fs) == 0) {
struct ::timeval tv[2];
Expand All @@ -4620,18 +4666,6 @@ GHC_INLINE void last_write_time(const path& p, file_time_type new_time, std::err
}
ec = detail::make_system_error();
return;
#else
struct ::timespec times[2];
times[0].tv_sec = 0;
times[0].tv_nsec = UTIME_OMIT;
times[1].tv_sec = std::chrono::duration_cast<std::chrono::seconds>(d).count();
times[1].tv_nsec = 0; // std::chrono::duration_cast<std::chrono::nanoseconds>(d).count() % 1000000000;
if (::utimensat(AT_FDCWD, p.c_str(), times, AT_SYMLINK_NOFOLLOW) != 0) {
ec = detail::make_system_error();
}
return;
#endif
#endif
#else
#ifndef UTIME_OMIT
#define UTIME_OMIT ((1l << 30) - 2l)
Expand Down Expand Up @@ -5259,7 +5293,7 @@ GHC_INLINE void directory_entry::refresh()
{
std::error_code ec;
refresh(ec);
if (ec) {
if (ec && (_status.type() == file_type::none || _symlink_status.type() != file_type::symlink)) {
throw filesystem_error(detail::systemErrorText(ec.value()), _path, ec);
}
}
Expand Down Expand Up @@ -5670,7 +5704,7 @@ class directory_iterator::impl
, _entry(nullptr)
{
if (!path.empty()) {
_dir = ::opendir(path.native().c_str());
do { _dir = ::opendir(path.native().c_str()); } while(errno == EINTR);
if (!_dir) {
auto error = errno;
_base = filesystem::path();
Expand All @@ -5697,7 +5731,7 @@ class directory_iterator::impl
do {
skip = false;
errno = 0;
_entry = ::readdir(_dir);
do { _entry = ::readdir(_dir); } while(errno == EINTR);
if (_entry) {
_dir_entry._path = _base;
_dir_entry._path.append_name(_entry->d_name);
Expand All @@ -5711,7 +5745,7 @@ class directory_iterator::impl
::closedir(_dir);
_dir = nullptr;
_dir_entry._path.clear();
if (errno) {
if (errno && errno != EINTR) {
ec = detail::make_system_error();
}
break;
Expand Down
Loading

0 comments on commit 9f61ee0

Please sign in to comment.