Compare commits

..

No commits in common. "master" and "v1.5.8" have entirely different histories.

17 changed files with 2362 additions and 6907 deletions

View File

@ -1,6 +1,6 @@
freebsd_task: freebsd_task:
freebsd_instance: freebsd_instance:
image_family: freebsd-14-0 image_family: freebsd-12-1
install_script: | install_script: |
pkg install -y cmake pkg install -y cmake
pw groupadd testgrp pw groupadd testgrp
@ -11,24 +11,25 @@ freebsd_task:
test_script: | test_script: |
sudo -u testuser .ci/unix-test.sh sudo -u testuser .ci/unix-test.sh
rockylinux8_task: centos7_task:
container: container:
image: docker.io/rockylinux:8 image: centos:7
install_script: | install_script: |
dnf group install -y "Development Tools" yum install -y centos-release-scl
dnf install cmake -y yum install -y devtoolset-9
curl -L https://github.com/Kitware/CMake/releases/download/v3.16.4/cmake-3.16.4-Linux-x86_64.tar.gz | tar xzvf - -C /usr/local --strip-components 1
build_script: | build_script: |
.ci/unix-build.sh source /opt/rh/devtoolset-9/enable && PATH=$PATH:/usr/local/bin .ci/unix-build.sh
test_script: | test_script: |
.ci/unix-test.sh PATH=$PATH:/usr/local/bin .ci/unix-test.sh
rockylinux9_task: centos8_task:
container: container:
image: docker.io/rockylinux:9 image: centos:8
install_script: | install_script: |
dnf group install -y "Development Tools" yum group install -y "Development Tools"
dnf install cmake -y curl -L https://github.com/Kitware/CMake/releases/download/v3.16.4/cmake-3.16.4-Linux-x86_64.tar.gz | tar xzvf - -C /usr/local --strip-components 1
build_script: | build_script: |
.ci/unix-build.sh PATH=$PATH:/usr/local/bin .ci/unix-build.sh
test_script: | test_script: |
.ci/unix-test.sh PATH=$PATH:/usr/local/bin .ci/unix-test.sh

View File

@ -1,170 +0,0 @@
name: CMake Build Matrix
on: [ push, pull_request ]
jobs:
build:
name: ${{ matrix.config.name }}
runs-on: ${{ matrix.config.os }}
strategy:
fail-fast: false
matrix:
config:
- name: "Ubuntu 22.04 GCC 11"
os: ubuntu-22.04
build_type: Release
packages: ninja-build
generator: Ninja
compatibility: "cxx_std_11;cxx_std_17;cxx_std_20"
cc: gcc
cxx: g++
- name: "Ubuntu 22.04 Clang 13.0"
os: ubuntu-22.04
build_type: Release
packages: ninja-build libc++-13-dev libc++abi-13-dev
generator: Ninja
compatibility: "cxx_std_11;cxx_std_17;cxx_std_20"
cc: clang-13
cxx: clang++-13
- name: "Ubuntu 22.04 Clang 15.0"
os: ubuntu-22.04
build_type: Release
packages: ninja-build libc++-15-dev libc++abi-15-dev
generator: Ninja
compatibility: "cxx_std_11;cxx_std_17;cxx_std_20"
cc: clang-15
cxx: clang++-15
- name: "Ubuntu 22.04 GCC 11 coverage"
os: ubuntu-22.04
build_type: Debug
packages: ninja-build lcov
generator: Ninja
compatibility: "cxx_std_11;cxx_std_17;cxx_std_20"
cc: gcc
cxx: g++
- name: "Ubuntu 20.04 GCC 9.3"
os: ubuntu-20.04
build_type: Release
packages: ninja-build
generator: Ninja
compatibility: "cxx_std_11;cxx_std_17;cxx_std_20"
cc: gcc
cxx: g++
- name: "Ubuntu 20.04 Clang 10.0"
os: ubuntu-20.04
build_type: Release
packages: ninja-build
generator: Ninja
compatibility: "cxx_std_11;cxx_std_17;cxx_std_20"
cc: clang-10
cxx: clang++-10
- name: "Ubuntu 20.04 Clang 11.0"
os: ubuntu-20.04
build_type: Release
packages: ninja-build clang-11 libc++-11-dev libc++abi-11-dev
generator: Ninja
compatibility: "cxx_std_11;cxx_std_17;cxx_std_20"
cc: clang-11
cxx: clang++-11
- name: "Ubuntu 20.04 GCC 9.3 coverage"
os: ubuntu-20.04
build_type: Debug
packages: ninja-build lcov
generator: Ninja
compatibility: "cxx_std_11;cxx_std_17;cxx_std_20"
cc: gcc
cxx: g++
- name: "Windows MSVC 2019"
os: windows-2019
build_type: Release
packages: ninja
generator: "Visual Studio 16 2019"
compatibility: "cxx_std_11;cxx_std_17;cxx_std_20"
cc: cl
cxx: cl
- name: "macOS 13 AppleClang"
os: macos-13
build_type: Release
packages: ninja
generator: Ninja
compatibility: "cxx_std_11;cxx_std_17;cxx_std_20"
cc: clang
cxx: clang++
steps:
- uses: actions/checkout@v2
- name: print environment
run: |
echo github.event.action: ${{ github.event.action }}
echo github.event_name: ${{ github.event_name }}
- name: Install dependencies on Ubuntu
if: startsWith(matrix.config.os, 'ubuntu')
shell: bash
run: |
sudo add-apt-repository ppa:ubuntu-toolchain-r/test -y
sudo apt update
sudo apt install ${{ matrix.config.packages }}
- name: Install dependencies on windows
if: startsWith(matrix.config.os, 'windows')
run: |
choco install ${{ matrix.config.packages }}
- name: Install dependencies on macOS
if: startsWith(matrix.config.os, 'macos')
run: |
brew install ${{ matrix.config.packages }}
- name: Configure project
shell: bash
run: |
export CC=${{ matrix.config.cc }}
export CXX=${{ matrix.config.cxx }}
ninja --version
cmake --version
mkdir build
mkdir install
if [[ "${{ matrix.config.build_type }}" == "Debug" ]]; then
cmake -G "${{ matrix.config.generator }}" -S . -B build -DCMAKE_BUILD_TYPE=Debug -DGHC_COVERAGE=ON -DGHC_FILESYSTEM_TEST_COMPILE_FEATURES="${{ matrix.config.compatibility }}" -DCMAKE_INSTALL_PREFIX:PATH=install
else
cmake -G "${{ matrix.config.generator }}" -S . -B build -DCMAKE_BUILD_TYPE=${{ matrix.config.build_type }} -DGHC_FILESYSTEM_TEST_COMPILE_FEATURES="${{ matrix.config.compatibility }}" -DCMAKE_INSTALL_PREFIX:PATH=install
fi
- name: Build project
shell: bash
run: |
cmake --build build --config ${{ matrix.config.build_type }}
- name: Run tests
run: |
cd build && ctest -C ${{ matrix.config.build_type }}
- name: Collect coverage info
if: startsWith(matrix.config.build_type, 'Debug')
run: |
cd build
lcov --compat-libtool --directory . --capture --output-file coverage_output.info
lcov --remove coverage_output.info '/usr/*' '*/c++/*' '*.h' '*/catch.hpp' -o coverage.info
# sed -i 's|SF:/.*/filesystem/|SF:../|g' coverage.info
- name: Upload coverage info
if: startsWith(matrix.config.build_type, 'Debug')
env:
COVERALLS_DEBUG: true
NODE_COVERALLS_DEBUG: 1
uses: coverallsapp/github-action@master
with:
path-to-lcov: ${{ github.workspace }}/build/coverage.info
github-token: ${{ secrets.GITHUB_TOKEN }}

100
.travis.yml Normal file
View File

@ -0,0 +1,100 @@
language: cpp
dist: xenial # default distribution
os: linux # default os
sudo: false
matrix:
fast_finish: true
include:
- env: MATRIX_EVAL="CC=gcc-5 && CXX=g++-5 && GENERATOR='Unix Makefiles' && CONFIG=Release"
addons: { apt: { packages: ["g++-5"], sources: ["ubuntu-toolchain-r-test"] } }
- env: MATRIX_EVAL="CC=gcc-6 && CXX=g++-6 && GENERATOR='Unix Makefiles' && CONFIG=Release"
addons: { apt: { packages: ["g++-6"], sources: ["ubuntu-toolchain-r-test"] } }
- env: MATRIX_EVAL="CC=gcc-7 && CXX=g++-7 && GENERATOR='Unix Makefiles' && CONFIG=Release"
addons: { apt: { packages: ["g++-7"], sources: ["ubuntu-toolchain-r-test"] } }
- env: MATRIX_EVAL="CC=gcc-7 && CXX=g++-7 && GENERATOR='Unix Makefiles' && CONFIG=Debug && GHC_COVERAGE=1"
addons: { apt: { packages: ["g++-7", "lcov"], sources: ["ubuntu-toolchain-r-test"] } }
- env: MATRIX_EVAL="CC=gcc-8 && CXX=g++-8 && GENERATOR='Unix Makefiles' && CONFIG=Release"
addons: { apt: { packages: ["g++-8"], sources: ["ubuntu-toolchain-r-test"] } }
- env: MATRIX_EVAL="CC=gcc-9 && CXX=g++-9 && GENERATOR='Unix Makefiles' && CONFIG=Release"
addons: { apt: { packages: ["g++-9"], sources: ["ubuntu-toolchain-r-test"] } }
- env: MATRIX_EVAL="CC=clang-5.0 && CXX=clang++-5.0 && GENERATOR='Unix Makefiles' && CONFIG=Release"
addons: { apt: { packages: ["clang-5.0"] } }
- env: MATRIX_EVAL="CC=clang-6.0 && CXX=clang++-6.0 && GENERATOR='Unix Makefiles' && CONFIG=Release"
addons: { apt: { packages: ["clang-6.0"] } }
- env: MATRIX_EVAL="CC=clang-7 && CXX=clang++-7 && GENERATOR='Unix Makefiles' && CONFIG=Release"
addons: { apt: { packages: ["clang-7", "libc++-7-dev", "libc++abi-7-dev"], sources: ["llvm-toolchain-xenial-7"] } }
- env: MATRIX_EVAL="CC=clang-8 && CXX=clang++-8 && GENERATOR='Unix Makefiles' && CONFIG=Release"
addons: { apt: { packages: ["clang-8", "libc++-8-dev", "libc++abi-8-dev"], sources: ["llvm-toolchain-xenial-8"] } }
- env: MATRIX_EVAL="CC=clang-9 && CXX=clang++-9 && GENERATOR='Unix Makefiles' && CONFIG=Release"
addons: { apt: { packages: ["clang-9", "libc++-9-dev", "libc++abi-9-dev"], sources: [{sourceline: 'deb https://apt.llvm.org/xenial/ llvm-toolchain-xenial-9 main', key_url: 'https://apt.llvm.org/llvm-snapshot.gpg.key'}] } }
- os: osx
env: MATRIX_EVAL="CC=clang && CXX=clang++ && GENERATOR=Xcode && CONFIG=Release"
osx_image: xcode9.2
- os: osx
env: MATRIX_EVAL="CC=clang && CXX=clang++ && GENERATOR=Xcode && CONFIG=Release"
osx_image: xcode10.1
- os: osx
env: MATRIX_EVAL="CC=clang && CXX=clang++ && GENERATOR=Xcode && CONFIG=Release"
osx_image: xcode11.3
install:
- eval "${MATRIX_EVAL}"
- |
if [ "${GHC_COVERAGE}" = "1" ]; then
gem install coveralls-lcov
fi
- $CC --version
- $CXX --version
- cmake --version
before_script:
- eval "${MATRIX_EVAL}"
- mkdir build
- cd build
- export VERBOSE=1
- |
if [ "${CONFIG}" = "Debug" ]; then
cmake -G"${GENERATOR}" -DCMAKE_CXX_COMPILER=${CXX} -DCMAKE_C_COMPILER=${CC} -DCMAKE_BUILD_TYPE=${CONFIG} -DGHC_COVERAGE=ON ..
else
cmake -G"${GENERATOR}" -DCMAKE_CXX_COMPILER=${CXX} -DCMAKE_C_COMPILER=${CC} -DCMAKE_BUILD_TYPE=${CONFIG} ..
fi
script:
- export VERBOSE=1
- cmake --build . --config ${CONFIG} --target filesystem_test
- |
if [ "${GHC_COVERAGE}" = "1" ]; then
cmake --build . --config ${CONFIG} --target filesystem_test
test/filesystem_test
else
cmake --build . --config ${CONFIG}
ctest -C ${CONFIG} -E Windows
if [ -f "test/std_filesystem_test" ]; then
test/std_filesystem_test || true
fi
fi
after_success:
- |
if [ "${GHC_COVERAGE}" = "1" ]; then
lcov --compat-libtool --directory . --capture --output-file coverage_output.info
lcov --remove coverage_output.info '/usr/*' '*/c++/*' '*.h' '*/catch.hpp' -o coverage.info
sed -i 's|SF:/.*/filesystem/|SF:../|g' coverage.info
coveralls-lcov coverage.info
fi

View File

@ -1,8 +1,5 @@
cmake_minimum_required(VERSION 3.7.2) cmake_minimum_required(VERSION 3.7.2)
project( project(ghcfilesystem)
ghcfilesystem,
VERSION 1.5.15
)
if (POLICY CMP0077) if (POLICY CMP0077)
cmake_policy(SET CMP0077 NEW) cmake_policy(SET CMP0077 NEW)
@ -18,7 +15,7 @@ if(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR)
else() else()
option(GHC_FILESYSTEM_BUILD_EXAMPLES "Build examples" OFF) option(GHC_FILESYSTEM_BUILD_EXAMPLES "Build examples" OFF)
option(GHC_FILESYSTEM_BUILD_TESTING "Enable tests" OFF) option(GHC_FILESYSTEM_BUILD_TESTING "Enable tests" OFF)
option(GHC_FILESYSTEM_WITH_INSTALL "With install target" OFF) option(GHC_FILESYSTEM_WITH_INSTALL "With install target" ON)
endif() endif()
option(GHC_FILESYSTEM_BUILD_STD_TESTING "Enable STD tests" ${GHC_FILESYSTEM_BUILD_TESTING}) option(GHC_FILESYSTEM_BUILD_STD_TESTING "Enable STD tests" ${GHC_FILESYSTEM_BUILD_TESTING})
if(NOT DEFINED GHC_FILESYSTEM_TEST_COMPILE_FEATURES) if(NOT DEFINED GHC_FILESYSTEM_TEST_COMPILE_FEATURES)
@ -35,14 +32,8 @@ endif()
if(CMAKE_CXX_STANDARD LESS 11) if(CMAKE_CXX_STANDARD LESS 11)
message(FATAL_ERROR "CMAKE_CXX_STANDARD is less than 11, ghc::filesystem only works with C++11 and above.") message(FATAL_ERROR "CMAKE_CXX_STANDARD is less than 11, ghc::filesystem only works with C++11 and above.")
endif() endif()
if(CMAKE_SOURCE_DIR STREQUAL PROJECT_SOURCE_DIR)
message(STATUS "System name: ${CMAKE_SYSTEM_NAME}")
message(STATUS "Compiler ID: ${CMAKE_CXX_COMPILER_ID}")
message(STATUS "CMAKE_CXX_COMPILE_FEATURES: ${CMAKE_CXX_COMPILE_FEATURES}")
endif()
add_library(ghc_filesystem INTERFACE) add_library(ghc_filesystem INTERFACE)
add_library(ghcFilesystem::ghc_filesystem ALIAS ghc_filesystem)
target_include_directories(ghc_filesystem INTERFACE target_include_directories(ghc_filesystem INTERFACE
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include> $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/include>
$<INSTALL_INTERFACE:include>) $<INSTALL_INTERFACE:include>)
@ -76,15 +67,7 @@ if(GHC_FILESYSTEM_WITH_INSTALL)
"${PROJECT_BINARY_DIR}/cmake/ghc_filesystem-config.cmake" "${PROJECT_BINARY_DIR}/cmake/ghc_filesystem-config.cmake"
INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/ghc_filesystem" INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/ghc_filesystem"
PATH_VARS CMAKE_INSTALL_INCLUDEDIR) PATH_VARS CMAKE_INSTALL_INCLUDEDIR)
write_basic_package_version_file( 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")
VERSION ${PROJECT_VERSION} add_library(ghcFilesystem::ghc_filesystem ALIAS ghc_filesystem)
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() endif()

358
README.md
View File

@ -1,54 +1,23 @@
![Supported Platforms](https://img.shields.io/badge/platform-macOS%20%7C%20Linux%20%7C%20Windows%20%7C%20FreeBSD-blue.svg) ![Supported Platforms](https://img.shields.io/badge/platform-macOS%20%7C%20Linux%20%7C%20Windows%20%7C%20FreeBSD-blue.svg)
![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg) ![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)
[![CMake Build Matrix](https://github.com/gulrak/filesystem/actions/workflows/build_cmake.yml/badge.svg?branch=master)](https://github.com/gulrak/filesystem/actions/workflows/build_cmake.yml) [![Build Status](https://travis-ci.org/gulrak/filesystem.svg?branch=master)](https://travis-ci.org/gulrak/filesystem)
[![Build Status](https://ci.appveyor.com/api/projects/status/t07wp3k2cddo0hpo/branch/master?svg=true)](https://ci.appveyor.com/project/gulrak/filesystem) [![Build Status](https://ci.appveyor.com/api/projects/status/t07wp3k2cddo0hpo/branch/master?svg=true)](https://ci.appveyor.com/project/gulrak/filesystem)
[![Build Status](https://api.cirrus-ci.com/github/gulrak/filesystem.svg?branch=master)](https://cirrus-ci.com/github/gulrak/filesystem) [![Build Status](https://api.cirrus-ci.com/github/gulrak/filesystem.svg?branch=master)](https://cirrus-ci.com/github/gulrak/filesystem)
[![Build Status](https://cloud.drone.io/api/badges/gulrak/filesystem/status.svg?ref=refs/heads/master)](https://cloud.drone.io/gulrak/filesystem)
[![Coverage Status](https://coveralls.io/repos/github/gulrak/filesystem/badge.svg?branch=master)](https://coveralls.io/github/gulrak/filesystem?branch=master) [![Coverage Status](https://coveralls.io/repos/github/gulrak/filesystem/badge.svg?branch=master)](https://coveralls.io/github/gulrak/filesystem?branch=master)
[![Latest Release Tag](https://img.shields.io/github/tag/gulrak/filesystem.svg)](https://github.com/gulrak/filesystem/tree/v1.5.14) [![Latest Release Tag](https://img.shields.io/github/tag/gulrak/filesystem.svg)](https://github.com/gulrak/filesystem/tree/v1.5.8)
<!-- [![Build Status](https://cloud.drone.io/api/badges/gulrak/filesystem/status.svg?ref=refs/heads/master)](https://cloud.drone.io/gulrak/filesystem) -->
<!-- TOC -->
* [Filesystem](#filesystem)
* [Motivation](#motivation)
* [Why the namespace GHC?](#why-the-namespace-ghc)
* [Platforms](#platforms)
* [Tests](#tests)
* [Usage](#usage)
* [Downloads](#downloads)
* [Using it as Single-File-Header](#using-it-as-single-file-header)
* [Using it as Forwarding-/Implementation-Header](#using-it-as-forwarding-implementation-header)
* [Git Submodule and CMake](#git-submodule-and-cmake)
* [Versioning](#versioning)
* [Documentation](#documentation)
* [`ghc::filesystem::ifstream`, `ghc::filesystem::ofstream`, `ghc::filesystem::fstream`](#ghcfilesystemifstream--ghcfilesystemofstream--ghcfilesystemfstream)
* [`ghc::filesystem::u8arguments`](#ghcfilesystemu8arguments)
* [Differences](#differences)
* [LWG Defects](#lwg-defects)
* [Not Implemented on C++ before C++17](#not-implemented-on-c-before-c17)
* [Differences in API](#differences-in-api)
* [Differences of Specific Interfaces](#differences-of-specific-interfaces)
* [Differences in Behavior](#differences-in-behavior)
* [fs.path (ref)](#fspath--ref-)
* [Open Issues](#open-issues)
* [Windows](#windows)
* [Symbolic Links on Windows](#symbolic-links-on-windows)
* [Permissions](#permissions)
* [Release Notes](#release-notes)
<!-- TOC -->
# Filesystem # Filesystem
This is a header-only single-file `std::filesystem` compatible helper library, This is a header-only single-file `std::filesystem` compatible helper library,
based on the C++17 and C++20 specs, but implemented for C++11, C++14, C++17 or C++20 based on the C++17 and C++20 specs, but implemented for C++11, C++14, C++17 or C++20
(tightly following the C++17 standard with very few documented exceptions). It is currently tested on (tightly following the C++17 standard with very few documented exceptions). It is currently tested on
macOS 10.12/10.14/10.15/11.6, Windows 10, Ubuntu 18.04, Ubuntu 20.04, CentOS 7, CentOS 8, FreeBSD 12, macOS 10.12/10.14/10.15, Windows 10, Ubuntu 18.04, CentOS 7, CentOS 8, FreeBSD 12
Alpine ARM/ARM64 Linux and Solaris 10 but should work on other systems too, as long as you have and Alpine ARM/ARM64 Linux 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 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 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, QNX, and since 1.5.14 is experimental support for QNX. The support of Android NDK, Emscripten and QNX is not
GNU/Hurd and Haiku is not backed up by automated testing but PRs and bug reports are welcome backed up by automated testing but PRs and bug reports are welcome for those too.
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` 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). should you use it in a mixed C++17 environment (which is possible).
@ -91,15 +60,15 @@ to do with Haskell**, sorry for the name clash).
## Platforms ## Platforms
`ghc::filesystem` is developed on macOS but CI tested on macOS, Windows, `ghc::filesystem` is developed on macOS but CI tested on macOS, Windows,
various Linux Distributions, FreeBSD and starting with v1.5.12 on Solaris. various Linux Distributions and FreeBSD. It should work on any of these with a C++11-capable
It should work on any of these with a C++11-capable compiler. Also there are some compiler. Also there are some checks to hopefully better work on Android, but
checks to hopefully better work on Android, but as I currently don't test with the as I currently don't test with the Android NDK, I wouldn't call it a
Android NDK, I wouldn't call it a supported platform yet, same is valid for using supported platform yet, same is valid for using it with Emscripten. It is now
it with Emscripten. It is now part of the detected platforms, I fixed the obvious part of the detected platforms, I fixed the obvious issues and ran some tests with
issues and ran some tests with it, so it should be fine. All in all, I don't see it it, so it should be fine. All in all, I don't see it replacing `std::filesystem`
replacing `std::filesystem` where full C++17 or C++20 is available, it doesn't try where full C++17 or C++20 is available, it doesn't try to be a "better"
to be a "better" `std::filesystem`, just an almost drop-in if you can't use it `std::filesystem`, just an almost drop-in if you can't use it (with the exception
(with the exception of the UTF-8 preference). of the UTF-8 preference).
:information_source: **Important:** _This implementation is following the ["UTF-8 Everywhere" philosophy](https://utf8everywhere.org/) in that all :information_source: **Important:** _This implementation is following the ["UTF-8 Everywhere" philosophy](https://utf8everywhere.org/) in that all
`std::string` instances will be interpreted the same as `std::u8string` encoding `std::string` instances will be interpreted the same as `std::u8string` encoding
@ -111,10 +80,8 @@ Unit tests are currently run with:
* macOS 10.12: Xcode 9.2 (clang-900.0.39.2), GCC 9.2, Clang 9.0, macOS 10.13: Xcode 10.1, macOS 10.14: Xcode 11.2, macOS 10.15: Xcode 11.6, Xcode 12.4 * macOS 10.12: Xcode 9.2 (clang-900.0.39.2), GCC 9.2, Clang 9.0, macOS 10.13: Xcode 10.1, macOS 10.14: Xcode 11.2, macOS 10.15: Xcode 11.6, Xcode 12.4
* Windows: Visual Studio 2017, Visual Studio 2015, Visual Studio 2019, MinGW GCC 6.3 (Win32), GCC 7.2 (Win64), Cygwin GCC 10.2 (no CI yet) * Windows: Visual Studio 2017, Visual Studio 2015, Visual Studio 2019, MinGW GCC 6.3 (Win32), GCC 7.2 (Win64), Cygwin GCC 10.2 (no CI yet)
* Linux (Ubuntu): GCC (5.5, 6.5, 7.4, 8.3, 9.2), Clang (5.0, 6.0, 7.1, 8.0, 9.0) * Linux (Ubuntu): GCC (5.5, 6.5, 7.4, 8.3, 9.2), Clang (5.0, 6.0, 7.1, 8.0, 9.0)
* ~~Linux (Alpine ARM/ARM64): GCC 9.2.0~~ (The Drone build scripts stopped working, * Linux (Alpine ARM/ARM64): GCC 9.2.0
as they where a contribution, I couldn't figure out what went wrong, any help appreciated.)
* FreeBSD: Clang 8.0 * FreeBSD: Clang 8.0
* Solaris: GCC 5.5
## Tests ## Tests
@ -154,8 +121,8 @@ in the standard, and there might be issues in these implementations too.
### Downloads ### Downloads
The latest release version is [v1.5.14](https://github.com/gulrak/filesystem/tree/v1.5.14) and The latest release version is [v1.5.8](https://github.com/gulrak/filesystem/tree/v1.5.8) and
source archives can be found [here](https://github.com/gulrak/filesystem/releases/tag/v1.5.14). source archives can be found [here](https://github.com/gulrak/filesystem/releases/tag/v1.5.8).
The latest pre-native-backend version is [v1.4.0](https://github.com/gulrak/filesystem/tree/v1.4.0) and The latest pre-native-backend version is [v1.4.0](https://github.com/gulrak/filesystem/tree/v1.4.0) and
source archives can be found [here](https://github.com/gulrak/filesystem/releases/tag/v1.4.0). source archives can be found [here](https://github.com/gulrak/filesystem/releases/tag/v1.4.0).
@ -176,83 +143,60 @@ Everything is in the namespace `ghc::filesystem`, so one way to use it only as
a fallback could be: a fallback could be:
```cpp ```cpp
#if _MSVC_LANG >= 201703L || __cplusplus >= 201703L && defined(__has_include) #ifdef __APPLE__
// ^ Supports MSVC prior to 15.7 without setting /Zc:__cplusplus to fix __cplusplus #include <Availability.h> // for deployment target to support pre-catalina targets without std::fs
// _MSVC_LANG works regardless. But without the switch, the compiler always reported 199711L: https://blogs.msdn.microsoft.com/vcblog/2018/04/09/msvc-now-correctly-reports-__cplusplus/
#if __has_include(<filesystem>) // Two stage __has_include needed for MSVC 2015 and per https://gcc.gnu.org/onlinedocs/cpp/_005f_005fhas_005finclude.html
#define GHC_USE_STD_FS
// Old Apple OSs don't support std::filesystem, though the header is available at compile
// time. In particular, std::filesystem is unavailable before macOS 10.15, iOS/tvOS 13.0,
// and watchOS 6.0.
#ifdef __APPLE__
#include <Availability.h>
// Note: This intentionally uses std::filesystem on any new Apple OS, like visionOS
// released after std::filesystem, where std::filesystem is always available.
// (All other __<platform>_VERSION_MIN_REQUIREDs will be undefined and thus 0.)
#if __MAC_OS_X_VERSION_MIN_REQUIRED && __MAC_OS_X_VERSION_MIN_REQUIRED < 101500 \
|| __IPHONE_OS_VERSION_MIN_REQUIRED && __IPHONE_OS_VERSION_MIN_REQUIRED < 130000 \
|| __TV_OS_VERSION_MIN_REQUIRED && __TV_OS_VERSION_MIN_REQUIRED < 130000 \
|| __WATCH_OS_VERSION_MAX_ALLOWED && __WATCH_OS_VERSION_MAX_ALLOWED < 60000
#undef GHC_USE_STD_FS
#endif
#endif
#endif
#endif #endif
#if ((defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) || (defined(__cplusplus) && __cplusplus >= 201703L)) && defined(__has_include)
#ifdef GHC_USE_STD_FS #if __has_include(<filesystem>) && (!defined(__MAC_OS_X_VERSION_MIN_REQUIRED) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 101500)
#include <filesystem> #define GHC_USE_STD_FS
namespace fs = std::filesystem; #include <filesystem>
#else namespace fs = std::filesystem;
#include "filesystem.hpp" #endif
namespace fs = ghc::filesystem; #endif
#ifndef GHC_USE_STD_FS
#include <ghc/filesystem.hpp>
namespace fs = ghc::filesystem;
#endif #endif
``` ```
**Note that this code uses a two-stage preprocessor condition because Visual Studio 2015
doesn't like the `(<...>)` syntax, even if it could cut evaluation early before. This code also
used the minimum deployment target to detect if `std::filesystem` really is available on macOS
compilation.**
**Note also, this detection now works on MSVC versions prior to 15.7 on, or without setting
the `/Zc:__cplusplus` compile switch that would fix `__cplusplus` on MSVC. (Without the switch
the compiler always reports `199711L`
([see](https://blogs.msdn.microsoft.com/vcblog/2018/04/09/msvc-now-correctly-reports-__cplusplus/)),
but `_MSVC_LANG` works without it.**
If you want to also use the `fstream` wrapper with `path` support as fallback, If you want to also use the `fstream` wrapper with `path` support as fallback,
you might use: you might use:
```cpp ```cpp
#if _MSVC_LANG >= 201703L || __cplusplus >= 201703L && defined(__has_include) #ifdef __APPLE__
// ^ Supports MSVC prior to 15.7 without setting /Zc:__cplusplus to fix __cplusplus #include <Availability.h> // for deployment target to support pre-catalina targets without std::fs
// _MSVC_LANG works regardless. But without the switch, the compiler always reported 199711L: https://blogs.msdn.microsoft.com/vcblog/2018/04/09/msvc-now-correctly-reports-__cplusplus/
#if __has_include(<filesystem>) // Two stage __has_include needed for MSVC 2015 and per https://gcc.gnu.org/onlinedocs/cpp/_005f_005fhas_005finclude.html
#define GHC_USE_STD_FS
// Old Apple OSs don't support std::filesystem, though the header is available at compile
// time. In particular, std::filesystem is unavailable before macOS 10.15, iOS/tvOS 13.0,
// and watchOS 6.0.
#ifdef __APPLE__
#include <Availability.h>
// Note: This intentionally uses std::filesystem on any new Apple OS, like visionOS
// released after std::filesystem, where std::filesystem is always available.
// (All other __<platform>_VERSION_MIN_REQUIREDs will be undefined and thus 0.)
#if __MAC_OS_X_VERSION_MIN_REQUIRED && __MAC_OS_X_VERSION_MIN_REQUIRED < 101500 \
|| __IPHONE_OS_VERSION_MIN_REQUIRED && __IPHONE_OS_VERSION_MIN_REQUIRED < 130000 \
|| __TV_OS_VERSION_MIN_REQUIRED && __TV_OS_VERSION_MIN_REQUIRED < 130000 \
|| __WATCH_OS_VERSION_MAX_ALLOWED && __WATCH_OS_VERSION_MAX_ALLOWED < 60000
#undef GHC_USE_STD_FS
#endif
#endif
#endif
#endif #endif
#if ((defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) || (defined(__cplusplus) && __cplusplus >= 201703L)) && defined(__has_include)
#ifdef GHC_USE_STD_FS #if __has_include(<filesystem>) && (!defined(__MAC_OS_X_VERSION_MIN_REQUIRED) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 101500)
#include <filesystem> #define GHC_USE_STD_FS
namespace fs { #include <filesystem>
using namespace std::filesystem; namespace fs {
using ifstream = std::ifstream; using namespace std::filesystem;
using ofstream = std::ofstream; using ifstream = std::ifstream;
using fstream = std::fstream; using ofstream = std::ofstream;
} using fstream = std::fstream;
#else }
#include "filesystem.hpp" #endif
namespace fs { #endif
using namespace ghc::filesystem; #ifndef GHC_USE_STD_FS
using ifstream = ghc::filesystem::ifstream; #include <ghc/filesystem.hpp>
using ofstream = ghc::filesystem::ofstream; namespace fs {
using fstream = ghc::filesystem::fstream; using namespace ghc::filesystem;
} using ifstream = ghc::filesystem::ifstream;
using ofstream = ghc::filesystem::ofstream;
using fstream = ghc::filesystem::fstream;
}
#endif #endif
``` ```
@ -292,46 +236,29 @@ If you use the forwarding/implementation approach, you can still use the dynamic
switching like this: switching like this:
```cpp ```cpp
#if _MSVC_LANG >= 201703L || __cplusplus >= 201703L && defined(__has_include) #ifdef __APPLE__
// ^ Supports MSVC prior to 15.7 without setting /Zc:__cplusplus to fix __cplusplus #include <Availability.h> // for deployment target to support pre-catalina targets without std::fs
// _MSVC_LANG works regardless. But without the switch, the compiler always reported 199711L: https://blogs.msdn.microsoft.com/vcblog/2018/04/09/msvc-now-correctly-reports-__cplusplus/
#if __has_include(<filesystem>) // Two stage __has_include needed for MSVC 2015 and per https://gcc.gnu.org/onlinedocs/cpp/_005f_005fhas_005finclude.html
#define GHC_USE_STD_FS
// Old Apple OSs don't support std::filesystem, though the header is available at compile
// time. In particular, std::filesystem is unavailable before macOS 10.15, iOS/tvOS 13.0,
// and watchOS 6.0.
#ifdef __APPLE__
#include <Availability.h>
// Note: This intentionally uses std::filesystem on any new Apple OS, like visionOS
// released after std::filesystem, where std::filesystem is always available.
// (All other __<platform>_VERSION_MIN_REQUIREDs will be undefined and thus 0.)
#if __MAC_OS_X_VERSION_MIN_REQUIRED && __MAC_OS_X_VERSION_MIN_REQUIRED < 101500 \
|| __IPHONE_OS_VERSION_MIN_REQUIRED && __IPHONE_OS_VERSION_MIN_REQUIRED < 130000 \
|| __TV_OS_VERSION_MIN_REQUIRED && __TV_OS_VERSION_MIN_REQUIRED < 130000 \
|| __WATCH_OS_VERSION_MAX_ALLOWED && __WATCH_OS_VERSION_MAX_ALLOWED < 60000
#undef GHC_USE_STD_FS
#endif
#endif
#endif
#endif #endif
#if ((defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) || (defined(__cplusplus) && __cplusplus >= 201703L)) && defined(__has_include)
#ifdef GHC_USE_STD_FS #if __has_include(<filesystem>) && (!defined(__MAC_OS_X_VERSION_MIN_REQUIRED) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 101500)
#include <filesystem> #define GHC_USE_STD_FS
namespace fs { #include <filesystem>
using namespace std::filesystem; namespace fs {
using ifstream = std::ifstream; using namespace std::filesystem;
using ofstream = std::ofstream; using ifstream = std::ifstream;
using fstream = std::fstream; using ofstream = std::ofstream;
} using fstream = std::fstream;
#else }
#include "fs_fwd.hpp" #endif
namespace fs { #endif
using namespace ghc::filesystem; #ifndef GHC_USE_STD_FS
using ifstream = ghc::filesystem::ifstream; #include <ghc/fs-fwd.hpp>
using ofstream = ghc::filesystem::ofstream; namespace fs {
using fstream = ghc::filesystem::fstream; using namespace ghc::filesystem;
} using ifstream = ghc::filesystem::ifstream;
using ofstream = ghc::filesystem::ofstream;
using fstream = ghc::filesystem::fstream;
}
#endif #endif
``` ```
@ -339,38 +266,25 @@ and in the implementation hiding cpp, you might use (before any include that inc
to take precedence: to take precedence:
```cpp ```cpp
#if _MSVC_LANG >= 201703L || __cplusplus >= 201703L && defined(__has_include) #ifdef __APPLE__ // for deployment target to support pre-catalina targets without std::fs
// ^ Supports MSVC prior to 15.7 without setting /Zc:__cplusplus to fix __cplusplus #include <Availability.h>
// _MSVC_LANG works regardless. But without the switch, the compiler always reported 199711L: https://blogs.msdn.microsoft.com/vcblog/2018/04/09/msvc-now-correctly-reports-__cplusplus/ #endif
#if __has_include(<filesystem>) // Two stage __has_include needed for MSVC 2015 and per https://gcc.gnu.org/onlinedocs/cpp/_005f_005fhas_005finclude.html #if ((defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) || (defined(__cplusplus) && __cplusplus >= 201703L)) && defined(__has_include)
#define GHC_USE_STD_FS #if __has_include(<filesystem>) && (!defined(__MAC_OS_X_VERSION_MIN_REQUIRED) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 101500)
#define GHC_USE_STD_FS
// Old Apple OSs don't support std::filesystem, though the header is available at compile #endif
// time. In particular, std::filesystem is unavailable before macOS 10.15, iOS/tvOS 13.0,
// and watchOS 6.0.
#ifdef __APPLE__
#include <Availability.h>
// Note: This intentionally uses std::filesystem on any new Apple OS, like visionOS
// released after std::filesystem, where std::filesystem is always available.
// (All other __<platform>_VERSION_MIN_REQUIREDs will be undefined and thus 0.)
#if __MAC_OS_X_VERSION_MIN_REQUIRED && __MAC_OS_X_VERSION_MIN_REQUIRED < 101500 \
|| __IPHONE_OS_VERSION_MIN_REQUIRED && __IPHONE_OS_VERSION_MIN_REQUIRED < 130000 \
|| __TV_OS_VERSION_MIN_REQUIRED && __TV_OS_VERSION_MIN_REQUIRED < 130000 \
|| __WATCH_OS_VERSION_MAX_ALLOWED && __WATCH_OS_VERSION_MAX_ALLOWED < 60000
#undef GHC_USE_STD_FS
#endif
#endif
#endif
#endif #endif
#ifndef GHC_USE_STD_FS #ifndef GHC_USE_STD_FS
#include "fs_impl.hpp" #define GHC_FILESYSTEM_IMPLEMENTATION
#include <ghc/filesystem.hpp>
#endif #endif
``` ```
:information_source: **Hint:** There are additional helper headers, named `ghc/fs_std_fwd.hpp` and :information_source: **Hint:** There are additional helper headers, named `ghc/fs_std_fwd.hpp` and
`ghc/fs_std_impl.hpp` that use this technique, so you can simply include them `ghc/fs_std_impl.hpp` that use this technique, so you can simply include them
if you want to dynamically select the filesystem implementation. if you want to dynamically select the filesystem implementation. they also
enable the `wchar_t` support on `ghc::filesystem` on Windows, so the resulting
implementation in the `fs` namespace will be compatible.
@ -397,10 +311,6 @@ The `CMakeLists.txt` offers a few options to customize its behavior:
`CMAKE_CXX_COMPILE_FEATURES` when the detection of C++17 or C++20 for additional tests `CMAKE_CXX_COMPILE_FEATURES` when the detection of C++17 or C++20 for additional tests
is not working (e.g. `cxx_std_20` to enforce building a `filesystem_test_cpp20` with C++20). is not working (e.g. `cxx_std_20` to enforce building a `filesystem_test_cpp20` with C++20).
### Bazel
Please use [hedronvision/bazel-cc-filesystem-backport](https://github.com/hedronvision/bazel-cc-filesystem-backport), which will automatically set everything up for you.
### Versioning ### Versioning
There is a version macro `GHC_FILESYSTEM_VERSION` defined in case future changes There is a version macro `GHC_FILESYSTEM_VERSION` defined in case future changes
@ -466,7 +376,7 @@ int main(int argc, char* argv[])
exit(EXIT_FAILURE); exit(EXIT_FAILURE);
} }
// now use argc/argv as usual, they have utf-8 encoding on windows // now use argc/argv as usual, they have utf-8 enconding on windows
// ... // ...
return 0; return 0;
@ -646,77 +556,9 @@ to the expected behavior.
## Release Notes ## Release Notes
### v1.5.15 (wip)
* Fix for [#166](https://github.com/gulrak/filesystem/issues/166),
`extension()` did return non empty result for the directory name
`".."`
### [v1.5.14](https://github.com/gulrak/filesystem/releases/tag/v1.5.14)
* 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
* Pull request [#161](https://github.com/gulrak/filesystem/pull/161), the
CMake alias `ghcFilesystem::ghc_filesystem` is now set unconditionally
* 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
for `GHC_NO_DIRENT_D_TYPE` on systems that don't support `dirent::d_type` and
fixed build configuration and tests to support Solaris as new platform.
* Pull request [#138](https://github.com/gulrak/filesystem/pull/138), if the
platform uses the POSIX backend and has no `PATH_MAX`, one is defined.
* Pull request [#137](https://github.com/gulrak/filesystem/pull/137), update
of Catch2 to version v2.13.7
* Added macOS 11 to the automatically tested platforms.
### [v1.5.10](https://github.com/gulrak/filesystem/releases/tag/v1.5.10)
* Pull request [#136](https://github.com/gulrak/filesystem/pull/136), the Windows
implementation used some unnecessary expensive shared pointer for resource
management and these where replaced by a dedicated code.
* Fix for [#132](https://github.com/gulrak/filesystem/issues/132), pull request
[#135](https://github.com/gulrak/filesystem/pull/135), `fs::remove_all` now
just deletes symbolic links instead of following them.
* Pull request [#133](https://github.com/gulrak/filesystem/pull/133), fix for
`fs::space` where a numerical overflow could happen in a multiplication.
* Replaced _travis-ci.org_ with GitHub Workflow for the configurations:
Ubuntu 20.04: GCC 9.3, Ubuntu 18.04: GCC 7.5, GCC 8.4, macOS 10.15: Xcode 12.4,
Windows 10: Visual Studio 2019
### [v1.5.8](https://github.com/gulrak/filesystem/releases/tag/v1.5.8) ### [v1.5.8](https://github.com/gulrak/filesystem/releases/tag/v1.5.8)
* Fix for [#125](https://github.com/gulrak/filesystem/issues/124), where * Fix for [#125]((https://github.com/gulrak/filesystem/issues/124), where
`fs::create_directories` on Windows no longer breaks on long filenames. `fs::create_directories` on Windows no longer breaks on long filenames.
### [v1.5.6](https://github.com/gulrak/filesystem/releases/tag/v1.5.6) ### [v1.5.6](https://github.com/gulrak/filesystem/releases/tag/v1.5.6)

View File

@ -1,5 +1,6 @@
macro(AddExecutableWithStdFS targetName) macro(AddExecutableWithStdFS targetName)
if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang" AND (CMAKE_CXX_COMPILER_VERSION VERSION_EQUAL 7.0 OR CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 7.0))
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" AND (CMAKE_CXX_COMPILER_VERSION VERSION_EQUAL 7.0 OR CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 7.0))
if(APPLE) if(APPLE)
include_directories(/usr/local/opt/llvm/include) include_directories(/usr/local/opt/llvm/include)
link_directories(/usr/local/opt/llvm/lib) link_directories(/usr/local/opt/llvm/lib)
@ -19,12 +20,6 @@ if ("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang" AND (CMAKE_CXX_COMPILER_VERSION V
target_link_libraries(${targetName} -stdlib=libc++) target_link_libraries(${targetName} -stdlib=libc++)
endif() endif()
endif() endif()
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) target_compile_definitions(${targetName} PRIVATE USE_STD_FS)
endif() endif()
@ -34,12 +29,6 @@ if (CMAKE_COMPILER_IS_GNUCXX AND (CMAKE_CXX_COMPILER_VERSION VERSION_EQUAL 8.0 O
if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 9.0) if (CMAKE_CXX_COMPILER_VERSION VERSION_LESS 9.0)
target_link_libraries(${targetName} -lstdc++fs) target_link_libraries(${targetName} -lstdc++fs)
endif() endif()
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_options(${targetName} PRIVATE $<$<BOOL:${CYGWIN}>:-Wa,-mbig-obj>)
target_compile_definitions(${targetName} PRIVATE USE_STD_FS) target_compile_definitions(${targetName} PRIVATE USE_STD_FS)
endif() endif()
@ -58,19 +47,12 @@ macro(AddTestExecutableWithStdCpp cppStd)
add_executable(filesystem_test_cpp${cppStd} ${ARGN}) add_executable(filesystem_test_cpp${cppStd} ${ARGN})
set_property(TARGET filesystem_test_cpp${cppStd} PROPERTY CXX_STANDARD ${cppStd}) set_property(TARGET filesystem_test_cpp${cppStd} PROPERTY CXX_STANDARD ${cppStd})
target_link_libraries(filesystem_test_cpp${cppStd} ghc_filesystem) target_link_libraries(filesystem_test_cpp${cppStd} ghc_filesystem)
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 target_compile_options(filesystem_test_cpp${cppStd} PRIVATE
$<$<BOOL:${EMSCRIPTEN}>:-s DISABLE_EXCEPTION_CATCHING=0> $<$<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> $<$<OR:$<CXX_COMPILER_ID:Clang>,$<CXX_COMPILER_ID:AppleClang>>:-Wall -Wextra -Wshadow -Wconversion -Wsign-conversion -Wpedantic -Werror -Wno-error=deprecated-declarations>
$<$<CXX_COMPILER_ID:GNU>:-Wall -Wextra -Wshadow -Wconversion -Wsign-conversion -Wpedantic -Wno-psabi -Werror -Wno-error=deprecated-declarations> $<$<CXX_COMPILER_ID:GNU>:-Wall -Wextra -Wshadow -Wconversion -Wsign-conversion -Wpedantic -Wno-psabi -Werror -Wno-error=deprecated-declarations>
$<$<CXX_COMPILER_ID:MSVC>:/WX /wd4996> $<$<CXX_COMPILER_ID:MSVC>:/WX /wd4996>
$<$<BOOL:${CYGWIN}>:-Wa,-mbig-obj> $<$<BOOL:${CYGWIN}>:-Wa,-mbig-obj>)
$<$<BOOL:${GHC_COVERAGE}>:--coverage>)
if(CMAKE_CXX_COMPILER_ID MATCHES MSVC) if(CMAKE_CXX_COMPILER_ID MATCHES MSVC)
target_compile_definitions(filesystem_test_cpp${cppStd} PRIVATE _CRT_SECURE_NO_WARNINGS) target_compile_definitions(filesystem_test_cpp${cppStd} PRIVATE _CRT_SECURE_NO_WARNINGS)
endif() endif()

View File

@ -25,6 +25,23 @@
// SOFTWARE. // SOFTWARE.
// //
//--------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------
//
// To dynamically select std::filesystem where available on most platforms,
// you could use:
//
// #if ((defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) || (defined(__cplusplus) && __cplusplus >= 201703L)) && defined(__has_include)
// #if __has_include(<filesystem>) && (!defined(__MAC_OS_X_VERSION_MIN_REQUIRED) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 101500)
// #define GHC_USE_STD_FS
// #include <filesystem>
// namespace fs = std::filesystem;
// #endif
// #endif
// #ifndef GHC_USE_STD_FS
// #include <ghc/filesystem.hpp>
// namespace fs = ghc::filesystem;
// #endif
//
//---------------------------------------------------------------------------------------
#ifndef GHC_FILESYSTEM_H #ifndef GHC_FILESYSTEM_H
#define GHC_FILESYSTEM_H #define GHC_FILESYSTEM_H
@ -36,7 +53,7 @@
#ifndef GHC_OS_DETECTED #ifndef GHC_OS_DETECTED
#if defined(__APPLE__) && defined(__MACH__) #if defined(__APPLE__) && defined(__MACH__)
#define GHC_OS_APPLE #define GHC_OS_MACOS
#elif defined(__linux__) #elif defined(__linux__)
#define GHC_OS_LINUX #define GHC_OS_LINUX
#if defined(__ANDROID__) #if defined(__ANDROID__)
@ -50,8 +67,6 @@
#define GHC_OS_WIN32 #define GHC_OS_WIN32
#elif defined(__CYGWIN__) #elif defined(__CYGWIN__)
#define GHC_OS_CYGWIN #define GHC_OS_CYGWIN
#elif defined(__sun) && defined(__SVR4)
#define GHC_OS_SOLARIS
#elif defined(__svr4__) #elif defined(__svr4__)
#define GHC_OS_SYS5R4 #define GHC_OS_SYS5R4
#elif defined(BSD) #elif defined(BSD)
@ -61,8 +76,7 @@
#include <wasi/api.h> #include <wasi/api.h>
#elif defined(__QNX__) #elif defined(__QNX__)
#define GHC_OS_QNX #define GHC_OS_QNX
#elif defined(__HAIKU__) #define GHC_NO_DIRENT_D_TYPE
#define GHC_OS_HAIKU
#else #else
#error "Operating system currently not supported!" #error "Operating system currently not supported!"
#endif #endif
@ -164,7 +178,7 @@
#include <langinfo.h> #include <langinfo.h>
#endif #endif
#endif #endif
#ifdef GHC_OS_APPLE #ifdef GHC_OS_MACOS
#include <Availability.h> #include <Availability.h>
#endif #endif
@ -240,10 +254,6 @@
#include <experimental/string_view> #include <experimental/string_view>
#endif #endif
#if !defined(GHC_OS_WINDOWS) && !defined(PATH_MAX)
#define PATH_MAX 4096
#endif
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Behaviour Switches (see README.md, should match the config in test/filesystem_test.cpp): // Behaviour Switches (see README.md, should match the config in test/filesystem_test.cpp):
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -264,19 +274,19 @@
// * if this->has_root_directory() and !p.has_root_directory() return -1 // * if this->has_root_directory() and !p.has_root_directory() return -1
// * if !this->has_root_directory() and p.has_root_directory() return -1 // * if !this->has_root_directory() and p.has_root_directory() return -1
// * else result of element wise comparison of path iteration where first comparison is != 0 or 0 // * else result of element wise comparison of path iteration where first comparison is != 0 or 0
// if all comparisons are 0 (on Windows this implementation does case-insensitive root_name() // if all comparisons are 0 (on Windows this implementation does case insensitive root_name()
// comparison) // comparison)
#define LWG_2936_BEHAVIOUR #define LWG_2936_BEHAVIOUR
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// LWG #2937 enforces that fs::equivalent emits an error, if !fs::exists(p1)||!exists(p2) // LWG #2937 enforces that fs::equivalent emits an error, if !fs::exists(p1)||!exists(p2)
#define LWG_2937_BEHAVIOUR #define LWG_2937_BEHAVIOUR
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// UTF8-Everywhere is the original behaviour of ghc::filesystem. But since v1.5 the Windows // UTF8-Everywhere is the original behaviour of ghc::filesystem. But since v1.5 the windows
// version defaults to std::wstring storage backend. Still all std::string will be interpreted // version defaults to std::wstring storage backend. Still all std::string will be interpreted
// as UTF-8 encoded. With this define you can enforce the old behavior on Windows, using // as UTF-8 encoded. With this define you can enfoce the old behavior on Windows, using
// std::string as backend and for fs::path::native() and char for fs::path::c_str(). This // std::string as backend and for fs::path::native() and char for fs::path::c_str(). This
// needs more conversions, so it is (and was before v1.5) slower, bot might help keeping source // needs more conversions so it is (an was before v1.5) slower, bot might help keeping source
// homogeneous in a multi-platform project. // homogeneous in a multi platform project.
// #define GHC_WIN_DISABLE_WSTRING_STORAGE_TYPE // #define GHC_WIN_DISABLE_WSTRING_STORAGE_TYPE
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// Raise errors/exceptions when invalid unicode codepoints or UTF-8 sequences are found, // Raise errors/exceptions when invalid unicode codepoints or UTF-8 sequences are found,
@ -291,7 +301,7 @@
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
// ghc::filesystem version in decimal (major * 10000 + minor * 100 + patch) // ghc::filesystem version in decimal (major * 10000 + minor * 100 + patch)
#define GHC_FILESYSTEM_VERSION 10515L #define GHC_FILESYSTEM_VERSION 10508L
#if !defined(GHC_WITH_EXCEPTIONS) && (defined(__EXCEPTIONS) || defined(__cpp_exceptions) || defined(_CPPUNWIND)) #if !defined(GHC_WITH_EXCEPTIONS) && (defined(__EXCEPTIONS) || defined(__cpp_exceptions) || defined(_CPPUNWIND))
#define GHC_WITH_EXCEPTIONS #define GHC_WITH_EXCEPTIONS
@ -414,11 +424,10 @@ public:
template <typename T> template <typename T>
using path_type_EcharT = typename std::enable_if<std::is_same<T, char>::value || std::is_same<T, char8_t>::value || std::is_same<T, char16_t>::value || std::is_same<T, char32_t>::value || std::is_same<T, wchar_t>::value, path>::type; using path_type_EcharT = typename std::enable_if<std::is_same<T, char>::value || std::is_same<T, char8_t>::value || std::is_same<T, char16_t>::value || std::is_same<T, char32_t>::value || std::is_same<T, wchar_t>::value, path>::type;
#else #else
using path_from_string = using path_from_string = typename std::enable_if<_is_basic_string<T>::value || std::is_same<char const*, typename std::decay<T>::type>::value || std::is_same<char*, typename std::decay<T>::type>::value ||
typename std::enable_if<_is_basic_string<T>::value || std::is_same<char const*, typename std::decay<T>::type>::value || std::is_same<char*, typename std::decay<T>::type>::value ||
std::is_same<char16_t const*, typename std::decay<T>::type>::value || std::is_same<char16_t*, typename std::decay<T>::type>::value || std::is_same<char32_t const*, typename std::decay<T>::type>::value || std::is_same<char16_t const*, typename std::decay<T>::type>::value || std::is_same<char16_t*, typename std::decay<T>::type>::value || std::is_same<char32_t const*, typename std::decay<T>::type>::value ||
std::is_same<char32_t*, typename std::decay<T>::type>::value || std::is_same<wchar_t const*, typename std::decay<T>::type>::value || std::is_same<wchar_t*, typename std::decay<T>::type>::value, std::is_same<char32_t*, typename std::decay<T>::type>::value || std::is_same<wchar_t const*, typename std::decay<T>::type>::value || std::is_same<wchar_t*, typename std::decay<T>::type>::value,
path>::type; path>::type;
template <typename T> template <typename T>
using path_type_EcharT = typename std::enable_if<std::is_same<T, char>::value || std::is_same<T, char16_t>::value || std::is_same<T, char32_t>::value || std::is_same<T, wchar_t>::value, path>::type; using path_type_EcharT = typename std::enable_if<std::is_same<T, char>::value || std::is_same<T, char16_t>::value || std::is_same<T, char32_t>::value || std::is_same<T, wchar_t>::value, path>::type;
#endif #endif
@ -596,7 +605,7 @@ private:
friend bool detail::has_executable_extension(const path& p); friend bool detail::has_executable_extension(const path& p);
#ifdef GHC_WIN_AUTO_PREFIX_LONG_PATH #ifdef GHC_WIN_AUTO_PREFIX_LONG_PATH
string_type::size_type _prefixLength{0}; string_type::size_type _prefixLength{0};
#else // GHC_WIN_AUTO_PREFIX_LONG_PATH #else // GHC_WIN_AUTO_PREFIX_LONG_PATH
static const string_type::size_type _prefixLength{0}; static const string_type::size_type _prefixLength{0};
#endif // GHC_WIN_AUTO_PREFIX_LONG_PATH #endif // GHC_WIN_AUTO_PREFIX_LONG_PATH
#else #else
@ -791,7 +800,6 @@ public:
file_type type() const noexcept; file_type type() const noexcept;
perms permissions() const noexcept; perms permissions() const noexcept;
friend bool operator==(const file_status& lhs, const file_status& rhs) noexcept { return lhs.type() == rhs.type() && lhs.permissions() == rhs.permissions(); } friend bool operator==(const file_status& lhs, const file_status& rhs) noexcept { return lhs.type() == rhs.type() && lhs.permissions() == rhs.permissions(); }
private: private:
file_type _type; file_type _type;
perms _perms; perms _perms;
@ -1142,10 +1150,6 @@ GHC_FS_API void create_hard_link(const path& to, const path& new_hard_link, std:
GHC_FS_API uintmax_t hard_link_count(const path& p, std::error_code& ec) noexcept; GHC_FS_API uintmax_t hard_link_count(const path& p, std::error_code& ec) noexcept;
#endif #endif
#if defined(GHC_OS_WINDOWS) && (!defined(__GLIBCXX__) || (defined(_GLIBCXX_HAVE__WFOPEN) && defined(_GLIBCXX_USE_WCHAR_T)))
#define GHC_HAS_FSTREAM_OPEN_WITH_WCHAR
#endif
// Non-C++17 add-on std::fstream wrappers with path // Non-C++17 add-on std::fstream wrappers with path
template <class charT, class traits = std::char_traits<charT>> template <class charT, class traits = std::char_traits<charT>>
class basic_filebuf : public std::basic_filebuf<charT, traits> class basic_filebuf : public std::basic_filebuf<charT, traits>
@ -1157,7 +1161,7 @@ public:
const basic_filebuf& operator=(const basic_filebuf&) = delete; const basic_filebuf& operator=(const basic_filebuf&) = delete;
basic_filebuf<charT, traits>* open(const path& p, std::ios_base::openmode mode) basic_filebuf<charT, traits>* open(const path& p, std::ios_base::openmode mode)
{ {
#ifdef GHC_HAS_FSTREAM_OPEN_WITH_WCHAR #if defined(GHC_OS_WINDOWS) && !defined(__GLIBCXX__)
return std::basic_filebuf<charT, traits>::open(p.wstring().c_str(), mode) ? this : 0; return std::basic_filebuf<charT, traits>::open(p.wstring().c_str(), mode) ? this : 0;
#else #else
return std::basic_filebuf<charT, traits>::open(p.string().c_str(), mode) ? this : 0; return std::basic_filebuf<charT, traits>::open(p.string().c_str(), mode) ? this : 0;
@ -1170,7 +1174,7 @@ class basic_ifstream : public std::basic_ifstream<charT, traits>
{ {
public: public:
basic_ifstream() {} basic_ifstream() {}
#ifdef GHC_HAS_FSTREAM_OPEN_WITH_WCHAR #if defined(GHC_OS_WINDOWS) && !defined(__GLIBCXX__)
explicit basic_ifstream(const path& p, std::ios_base::openmode mode = std::ios_base::in) explicit basic_ifstream(const path& p, std::ios_base::openmode mode = std::ios_base::in)
: std::basic_ifstream<charT, traits>(p.wstring().c_str(), mode) : std::basic_ifstream<charT, traits>(p.wstring().c_str(), mode)
{ {
@ -1193,7 +1197,7 @@ class basic_ofstream : public std::basic_ofstream<charT, traits>
{ {
public: public:
basic_ofstream() {} basic_ofstream() {}
#ifdef GHC_HAS_FSTREAM_OPEN_WITH_WCHAR #if defined(GHC_OS_WINDOWS) && !defined(__GLIBCXX__)
explicit basic_ofstream(const path& p, std::ios_base::openmode mode = std::ios_base::out) explicit basic_ofstream(const path& p, std::ios_base::openmode mode = std::ios_base::out)
: std::basic_ofstream<charT, traits>(p.wstring().c_str(), mode) : std::basic_ofstream<charT, traits>(p.wstring().c_str(), mode)
{ {
@ -1216,7 +1220,7 @@ class basic_fstream : public std::basic_fstream<charT, traits>
{ {
public: public:
basic_fstream() {} basic_fstream() {}
#ifdef GHC_HAS_FSTREAM_OPEN_WITH_WCHAR #if defined(GHC_OS_WINDOWS) && !defined(__GLIBCXX__)
explicit basic_fstream(const path& p, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out) explicit basic_fstream(const path& p, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out)
: std::basic_fstream<charT, traits>(p.wstring().c_str(), mode) : std::basic_fstream<charT, traits>(p.wstring().c_str(), mode)
{ {
@ -1292,65 +1296,6 @@ GHC_FS_API std::error_code make_error_code(portable_error err);
GHC_FS_API std::error_code make_system_error(uint32_t err = 0); GHC_FS_API std::error_code make_system_error(uint32_t err = 0);
#else #else
GHC_FS_API std::error_code make_system_error(int err = 0); GHC_FS_API std::error_code make_system_error(int err = 0);
template <typename T, typename = int>
struct has_d_type : std::false_type{};
template <typename T>
struct has_d_type<T, decltype((void)T::d_type, 0)> : std::true_type {};
template <typename T>
GHC_INLINE file_type file_type_from_dirent_impl(const T&, std::false_type)
{
return file_type::none;
}
template <typename T>
GHC_INLINE file_type file_type_from_dirent_impl(const T& t, std::true_type)
{
switch (t.d_type) {
#ifdef DT_BLK
case DT_BLK:
return file_type::block;
#endif
#ifdef DT_CHR
case DT_CHR:
return file_type::character;
#endif
#ifdef DT_DIR
case DT_DIR:
return file_type::directory;
#endif
#ifdef DT_FIFO
case DT_FIFO:
return file_type::fifo;
#endif
#ifdef DT_LNK
case DT_LNK:
return file_type::symlink;
#endif
#ifdef DT_REG
case DT_REG:
return file_type::regular;
#endif
#ifdef DT_SOCK
case DT_SOCK:
return file_type::socket;
#endif
#ifdef DT_UNKNOWN
case DT_UNKNOWN:
return file_type::none;
#endif
default:
return file_type::unknown;
}
}
template <class T>
GHC_INLINE file_type file_type_from_dirent(const T& t)
{
return file_type_from_dirent_impl(t, has_d_type<T>{});
}
#endif #endif
} // namespace detail } // namespace detail
@ -1752,7 +1697,7 @@ inline std::wstring toWChar(const charT* unicodeString)
return toWChar(std::basic_string<charT, std::char_traits<charT>>(unicodeString)); return toWChar(std::basic_string<charT, std::char_traits<charT>>(unicodeString));
#endif #endif
} }
#endif // GHC_USE_WCHAR_T #endif // GHC_USE_WCHAR_T
} // namespace detail } // namespace detail
@ -1871,16 +1816,16 @@ GHC_INLINE bool equals_simple_insensitive(const path::value_type* str1, const pa
return true; return true;
} }
return false; return false;
#else // __GNUC__ #else // __GNUC__
#ifdef GHC_USE_WCHAR_T #ifdef GHC_USE_WCHAR_T
return 0 == ::_wcsicmp(str1, str2); return 0 == ::_wcsicmp(str1, str2);
#else // GHC_USE_WCHAR_T #else // GHC_USE_WCHAR_T
return 0 == ::_stricmp(str1, str2); return 0 == ::_stricmp(str1, str2);
#endif // GHC_USE_WCHAR_T #endif // GHC_USE_WCHAR_T
#endif // __GNUC__ #endif // __GNUC__
#else // GHC_OS_WINDOWS #else // GHC_OS_WINDOWS
return 0 == ::strcasecmp(str1, str2); return 0 == ::strcasecmp(str1, str2);
#endif // GHC_OS_WINDOWS #endif // GHC_OS_WINDOWS
} }
GHC_INLINE int compare_simple_insensitive(const path::value_type* str1, size_t len1, const path::value_type* str2, size_t len2) GHC_INLINE int compare_simple_insensitive(const path::value_type* str1, size_t len1, const path::value_type* str2, size_t len2)
@ -1944,15 +1889,10 @@ GHC_INLINE void create_symlink(const path& target_name, const path& new_symlink,
#if defined(__GNUC__) && __GNUC__ >= 8 #if defined(__GNUC__) && __GNUC__ >= 8
#pragma GCC diagnostic push #pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wcast-function-type" #pragma GCC diagnostic ignored "-Wcast-function-type"
#elif defined(_MSC_VER) && !defined(__INTEL_COMPILER) && !defined(__clang__)
#pragma warning(push)
#pragma warning(disable : 4191)
#endif #endif
static CreateSymbolicLinkW_fp api_call = reinterpret_cast<CreateSymbolicLinkW_fp>(GetProcAddress(GetModuleHandleW(L"kernel32.dll"), "CreateSymbolicLinkW")); static CreateSymbolicLinkW_fp api_call = reinterpret_cast<CreateSymbolicLinkW_fp>(GetProcAddress(GetModuleHandleW(L"kernel32.dll"), "CreateSymbolicLinkW"));
#if defined(__GNUC__) && __GNUC__ >= 8 #if defined(__GNUC__) && __GNUC__ >= 8
#pragma GCC diagnostic pop #pragma GCC diagnostic pop
#elif defined(_MSC_VER) && !defined(__INTEL_COMPILER) && !defined(__clang__)
#pragma warning(pop)
#endif #endif
if (api_call) { if (api_call) {
if (api_call(GHC_NATIVEWP(new_symlink), GHC_NATIVEWP(target_name), to_directory ? 1 : 0) == 0) { if (api_call(GHC_NATIVEWP(new_symlink), GHC_NATIVEWP(target_name), to_directory ? 1 : 0) == 0) {
@ -1973,15 +1913,10 @@ GHC_INLINE void create_hardlink(const path& target_name, const path& new_hardlin
#if defined(__GNUC__) && __GNUC__ >= 8 #if defined(__GNUC__) && __GNUC__ >= 8
#pragma GCC diagnostic push #pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wcast-function-type" #pragma GCC diagnostic ignored "-Wcast-function-type"
#elif defined(_MSC_VER) && !defined(__INTEL_COMPILER) && !defined(__clang__)
#pragma warning(push)
#pragma warning(disable : 4191)
#endif #endif
static CreateHardLinkW_fp api_call = reinterpret_cast<CreateHardLinkW_fp>(GetProcAddress(GetModuleHandleW(L"kernel32.dll"), "CreateHardLinkW")); static CreateHardLinkW_fp api_call = reinterpret_cast<CreateHardLinkW_fp>(GetProcAddress(GetModuleHandleW(L"kernel32.dll"), "CreateHardLinkW"));
#if defined(__GNUC__) && __GNUC__ >= 8 #if defined(__GNUC__) && __GNUC__ >= 8
#pragma GCC diagnostic pop #pragma GCC diagnostic pop
#elif defined(_MSC_VER) && !defined(__INTEL_COMPILER) && !defined(__clang__)
#pragma warning(pop)
#endif #endif
if (api_call) { if (api_call) {
if (api_call(GHC_NATIVEWP(new_hardlink), GHC_NATIVEWP(target_name), NULL) == 0) { if (api_call(GHC_NATIVEWP(new_hardlink), GHC_NATIVEWP(target_name), NULL) == 0) {
@ -2070,52 +2005,6 @@ GHC_INLINE file_status file_status_from_st_mode(T mode)
} }
#ifdef GHC_OS_WINDOWS #ifdef GHC_OS_WINDOWS
class unique_handle
{
public:
typedef HANDLE element_type;
unique_handle() noexcept
: _handle(INVALID_HANDLE_VALUE)
{
}
explicit unique_handle(element_type h) noexcept
: _handle(h)
{
}
unique_handle(unique_handle&& u) noexcept
: _handle(u.release())
{
}
~unique_handle() { reset(); }
unique_handle& operator=(unique_handle&& u) noexcept
{
reset(u.release());
return *this;
}
element_type get() const noexcept { return _handle; }
explicit operator bool() const noexcept { return _handle != INVALID_HANDLE_VALUE; }
element_type release() noexcept
{
element_type tmp = _handle;
_handle = INVALID_HANDLE_VALUE;
return tmp;
}
void reset(element_type h = INVALID_HANDLE_VALUE) noexcept
{
element_type tmp = _handle;
_handle = h;
if (tmp != INVALID_HANDLE_VALUE) {
CloseHandle(tmp);
}
}
void swap(unique_handle& u) noexcept { std::swap(_handle, u._handle); }
private:
element_type _handle;
};
#ifndef REPARSE_DATA_BUFFER_HEADER_SIZE #ifndef REPARSE_DATA_BUFFER_HEADER_SIZE
typedef struct _REPARSE_DATA_BUFFER typedef struct _REPARSE_DATA_BUFFER
{ {
@ -2152,21 +2041,15 @@ typedef struct _REPARSE_DATA_BUFFER
#endif #endif
#endif #endif
template <class T> GHC_INLINE std::shared_ptr<REPARSE_DATA_BUFFER> getReparseData(const path& p, std::error_code& ec)
struct free_deleter
{ {
void operator()(T* p) const { std::free(p); } std::shared_ptr<void> file(CreateFileW(GHC_NATIVEWP(p), 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, 0), CloseHandle);
}; if (file.get() == INVALID_HANDLE_VALUE) {
GHC_INLINE std::unique_ptr<REPARSE_DATA_BUFFER, free_deleter<REPARSE_DATA_BUFFER>> getReparseData(const path& p, std::error_code& ec)
{
unique_handle file(CreateFileW(GHC_NATIVEWP(p), 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, 0));
if (!file) {
ec = detail::make_system_error(); ec = detail::make_system_error();
return nullptr; return nullptr;
} }
std::unique_ptr<REPARSE_DATA_BUFFER, free_deleter<REPARSE_DATA_BUFFER>> reparseData(reinterpret_cast<REPARSE_DATA_BUFFER*>(std::calloc(1, MAXIMUM_REPARSE_DATA_BUFFER_SIZE))); std::shared_ptr<REPARSE_DATA_BUFFER> reparseData((REPARSE_DATA_BUFFER*)std::calloc(1, MAXIMUM_REPARSE_DATA_BUFFER_SIZE), std::free);
ULONG bufferUsed; ULONG bufferUsed;
if (DeviceIoControl(file.get(), FSCTL_GET_REPARSE_POINT, 0, 0, reparseData.get(), MAXIMUM_REPARSE_DATA_BUFFER_SIZE, &bufferUsed, 0)) { if (DeviceIoControl(file.get(), FSCTL_GET_REPARSE_POINT, 0, 0, reparseData.get(), MAXIMUM_REPARSE_DATA_BUFFER_SIZE, &bufferUsed, 0)) {
return reparseData; return reparseData;
@ -2203,7 +2086,7 @@ GHC_INLINE path resolveSymlink(const path& p, std::error_code& ec)
} }
case IO_REPARSE_TAG_MOUNT_POINT: case IO_REPARSE_TAG_MOUNT_POINT:
result = detail::getFullPathName(GHC_NATIVEWP(p), ec); result = detail::getFullPathName(GHC_NATIVEWP(p), ec);
// result = std::wstring(&reparseData->MountPointReparseBuffer.PathBuffer[reparseData->MountPointReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)], reparseData->MountPointReparseBuffer.SubstituteNameLength / sizeof(WCHAR)); //result = std::wstring(&reparseData->MountPointReparseBuffer.PathBuffer[reparseData->MountPointReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)], reparseData->MountPointReparseBuffer.SubstituteNameLength / sizeof(WCHAR));
break; break;
default: default:
break; break;
@ -2240,10 +2123,10 @@ GHC_INLINE time_t timeFromFILETIME(const FILETIME& ft)
GHC_INLINE void timeToFILETIME(time_t t, FILETIME& ft) GHC_INLINE void timeToFILETIME(time_t t, FILETIME& ft)
{ {
ULARGE_INTEGER ull; LONGLONG ll;
ull.QuadPart = static_cast<ULONGLONG>((t * 10000000LL) + 116444736000000000LL); ll = Int32x32To64(t, 10000000) + 116444736000000000;
ft.dwLowDateTime = ull.LowPart; ft.dwLowDateTime = static_cast<DWORD>(ll);
ft.dwHighDateTime = ull.HighPart; ft.dwHighDateTime = static_cast<DWORD>(ll >> 32);
} }
template <typename INFO> template <typename INFO>
@ -2259,43 +2142,41 @@ GHC_INLINE uintmax_t hard_links_from_INFO<BY_HANDLE_FILE_INFORMATION>(const BY_H
} }
template <typename INFO> template <typename INFO>
GHC_INLINE bool is_symlink_from_INFO(const path &p, const INFO* info, std::error_code& ec) GHC_INLINE DWORD reparse_tag_from_INFO(const INFO*)
{ {
if ((info->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) { return 0;
auto reparseData = detail::getReparseData(p, ec);
if (!ec && reparseData && IsReparseTagMicrosoft(reparseData->ReparseTag) && reparseData->ReparseTag == IO_REPARSE_TAG_SYMLINK) {
return true;
}
}
return false;
} }
template <> template <>
GHC_INLINE bool is_symlink_from_INFO(const path &, const WIN32_FIND_DATAW* info, std::error_code&) GHC_INLINE DWORD reparse_tag_from_INFO(const WIN32_FIND_DATAW* info)
{ {
// dwReserved0 is undefined unless dwFileAttributes includes the return info->dwReserved0;
// 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> 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) 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; file_type ft = file_type::unknown;
if (is_symlink_from_INFO(p, info, ec)) { if (sizeof(INFO) == sizeof(WIN32_FIND_DATAW)) {
ft = file_type::symlink; if (detail::reparse_tag_from_INFO(info) == IO_REPARSE_TAG_SYMLINK) {
} ft = file_type::symlink;
else if ((info->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { }
ft = file_type::directory;
} }
else { else {
ft = file_type::regular; 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;
}
}
}
if (ft == file_type::unknown) {
if ((info->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
ft = file_type::directory;
}
else {
ft = file_type::regular;
}
} }
perms prms = perms::owner_read | perms::group_read | perms::others_read; perms prms = perms::owner_read | perms::group_read | perms::others_read;
if (!(info->dwFileAttributes & FILE_ATTRIBUTE_READONLY)) { if (!(info->dwFileAttributes & FILE_ATTRIBUTE_READONLY)) {
@ -3157,7 +3038,7 @@ GHC_INLINE path path::extension() const
auto iter = end(); auto iter = end();
const auto& fn = *--iter; const auto& fn = *--iter;
impl_string_type::size_type pos = fn._path.rfind('.'); impl_string_type::size_type pos = fn._path.rfind('.');
if (pos != std::string::npos && pos > 0 && fn._path != "..") { if (pos != std::string::npos && pos > 0) {
return path(fn._path.substr(pos), native_format); return path(fn._path.substr(pos), native_format);
} }
} }
@ -3176,8 +3057,7 @@ GHC_INLINE bool has_executable_extension(const path& p)
return false; return false;
} }
const path::value_type* ext = fn._path.c_str() + pos + 1; const path::value_type* ext = fn._path.c_str() + pos + 1;
if (detail::equals_simple_insensitive(ext, GHC_PLATFORM_LITERAL("exe")) || detail::equals_simple_insensitive(ext, GHC_PLATFORM_LITERAL("cmd")) || detail::equals_simple_insensitive(ext, GHC_PLATFORM_LITERAL("bat")) || if (detail::equals_simple_insensitive(ext, GHC_PLATFORM_LITERAL("exe")) || detail::equals_simple_insensitive(ext, GHC_PLATFORM_LITERAL("cmd")) || detail::equals_simple_insensitive(ext, GHC_PLATFORM_LITERAL("bat")) || detail::equals_simple_insensitive(ext, GHC_PLATFORM_LITERAL("com"))) {
detail::equals_simple_insensitive(ext, GHC_PLATFORM_LITERAL("com"))) {
return true; return true;
} }
} }
@ -3306,9 +3186,6 @@ GHC_INLINE path path::lexically_relative(const path& base) const
--count; --count;
} }
} }
if (count == 0 && (a == end() || a->empty())) {
return path(".");
}
if (count < 0) { if (count < 0) {
return path(); return path();
} }
@ -3339,7 +3216,7 @@ GHC_INLINE path::iterator::iterator(const path& p, const impl_string_type::const
, _root(p.has_root_directory() ? _first + static_cast<string_type::difference_type>(p._prefixLength + p.root_name_length()) : _last) , _root(p.has_root_directory() ? _first + static_cast<string_type::difference_type>(p._prefixLength + p.root_name_length()) : _last)
, _iter(pos) , _iter(pos)
{ {
if (pos != _last) { if(pos != _last) {
updateCurrent(); updateCurrent();
} }
} }
@ -3369,14 +3246,10 @@ GHC_INLINE path::impl_string_type::const_iterator path::iterator::increment(cons
} }
} }
else { else {
#ifdef GHC_OS_WINDOWS
if (fromStart && i != _last && *i == ':') { if (fromStart && i != _last && *i == ':') {
++i; ++i;
} }
else { else {
#else
{
#endif
i = std::find(i, _last, preferred_separator); i = std::find(i, _last, preferred_separator);
} }
} }
@ -3423,10 +3296,10 @@ GHC_INLINE void path::iterator::updateCurrent()
GHC_INLINE path::iterator& path::iterator::operator++() GHC_INLINE path::iterator& path::iterator::operator++()
{ {
_iter = increment(_iter); _iter = increment(_iter);
while (_iter != _last && // we didn't reach the end while (_iter != _last && // we didn't reach the end
_iter != _root && // this is not a root position _iter != _root && // this is not a root position
*_iter == preferred_separator && // we are on a separator *_iter == preferred_separator && // we are on a separator
(_iter + 1) != _last // the slash is not the last char (_iter + 1) != _last // the slash is not the last char
) { ) {
++_iter; ++_iter;
} }
@ -3920,14 +3793,11 @@ GHC_INLINE bool copy_file(const path& from, const path& to, copy_options options
ec = tecf; ec = tecf;
return false; 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 (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) { if ((options & copy_options::update_existing) == copy_options::update_existing) {
auto from_time = last_write_time(from, ec); auto from_time = last_write_time(from, ec);
if (ec) { if (ec) {
@ -3967,33 +3837,15 @@ GHC_INLINE bool copy_file(const path& from, const path& to, copy_options options
::close(in); ::close(in);
return false; 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; ssize_t br, bw;
while (true) { while ((br = ::read(in, buffer.data(), buffer.size())) > 0) {
do { br = ::read(in, buffer.data(), buffer.size()); } while(errno == EINTR && !br);
if(!br) {
break;
}
if(br < 0) {
ec = detail::make_system_error();
::close(in);
::close(out);
return false;
}
ssize_t offset = 0; ssize_t offset = 0;
do { do {
if ((bw = ::write(out, buffer.data() + offset, static_cast<size_t>(br))) > 0) { if ((bw = ::write(out, buffer.data() + offset, static_cast<size_t>(br))) > 0) {
br -= bw; br -= bw;
offset += bw; offset += bw;
} }
else if (bw < 0 && errno != EINTR) { else if (bw < 0) {
ec = detail::make_system_error(); ec = detail::make_system_error();
::close(in); ::close(in);
::close(out); ::close(out);
@ -4228,13 +4080,6 @@ GHC_INLINE path current_path(std::error_code& ec)
return path(); return path();
} }
return path(std::wstring(buffer.get()), path::native_format); return path(std::wstring(buffer.get()), path::native_format);
#elif defined(__GLIBC__)
std::unique_ptr<char, decltype(&std::free)> buffer { ::get_current_dir_name(), std::free };
if (buffer == nullptr) {
ec = detail::make_system_error();
return path();
}
return path(buffer.get());
#else #else
size_t pathlen = static_cast<size_t>(std::max(int(::pathconf(".", _PC_PATH_MAX)), int(PATH_MAX))); 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]); std::unique_ptr<char[]> buffer(new char[pathlen + 1]);
@ -4308,10 +4153,10 @@ GHC_INLINE bool equivalent(const path& p1, const path& p2, std::error_code& ec)
{ {
ec.clear(); ec.clear();
#ifdef GHC_OS_WINDOWS #ifdef GHC_OS_WINDOWS
detail::unique_handle file1(::CreateFileW(GHC_NATIVEWP(p1), 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0)); std::shared_ptr<void> file1(::CreateFileW(GHC_NATIVEWP(p1), 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0), CloseHandle);
auto e1 = ::GetLastError(); auto e1 = ::GetLastError();
detail::unique_handle file2(::CreateFileW(GHC_NATIVEWP(p2), 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0)); std::shared_ptr<void> file2(::CreateFileW(GHC_NATIVEWP(p2), 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0), CloseHandle);
if (!file1 || !file2) { if (file1.get() == INVALID_HANDLE_VALUE || file2.get() == INVALID_HANDLE_VALUE) {
#ifdef LWG_2937_BEHAVIOUR #ifdef LWG_2937_BEHAVIOUR
ec = detail::make_system_error(e1 ? e1 : ::GetLastError()); ec = detail::make_system_error(e1 ? e1 : ::GetLastError());
#else #else
@ -4401,9 +4246,9 @@ GHC_INLINE uintmax_t hard_link_count(const path& p, std::error_code& ec) noexcep
ec.clear(); ec.clear();
#ifdef GHC_OS_WINDOWS #ifdef GHC_OS_WINDOWS
uintmax_t result = static_cast<uintmax_t>(-1); uintmax_t result = static_cast<uintmax_t>(-1);
detail::unique_handle file(::CreateFileW(GHC_NATIVEWP(p), 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0)); std::shared_ptr<void> file(::CreateFileW(GHC_NATIVEWP(p), 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0), CloseHandle);
BY_HANDLE_FILE_INFORMATION inf; BY_HANDLE_FILE_INFORMATION inf;
if (!file) { if (file.get() == INVALID_HANDLE_VALUE) {
ec = detail::make_system_error(); ec = detail::make_system_error();
} }
else { else {
@ -4632,7 +4477,7 @@ GHC_INLINE void last_write_time(const path& p, file_time_type new_time, std::err
ec.clear(); ec.clear();
auto d = new_time.time_since_epoch(); auto d = new_time.time_since_epoch();
#ifdef GHC_OS_WINDOWS #ifdef GHC_OS_WINDOWS
detail::unique_handle file(::CreateFileW(GHC_NATIVEWP(p), FILE_WRITE_ATTRIBUTES, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL)); std::shared_ptr<void> file(::CreateFileW(GHC_NATIVEWP(p), FILE_WRITE_ATTRIBUTES, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL), ::CloseHandle);
FILETIME ft; FILETIME ft;
auto tt = std::chrono::duration_cast<std::chrono::microseconds>(d).count() * 10 + 116444736000000000; auto tt = std::chrono::duration_cast<std::chrono::microseconds>(d).count() * 10 + 116444736000000000;
ft.dwLowDateTime = static_cast<DWORD>(tt); ft.dwLowDateTime = static_cast<DWORD>(tt);
@ -4640,11 +4485,9 @@ GHC_INLINE void last_write_time(const path& p, file_time_type new_time, std::err
if (!::SetFileTime(file.get(), 0, 0, &ft)) { if (!::SetFileTime(file.get(), 0, 0, &ft)) {
ec = detail::make_system_error(); ec = detail::make_system_error();
} }
#elif defined(GHC_OS_APPLE) && \ #elif defined(GHC_OS_MACOS)
(__MAC_OS_X_VERSION_MIN_REQUIRED && __MAC_OS_X_VERSION_MIN_REQUIRED < 101300 \ #ifdef __MAC_OS_X_VERSION_MIN_REQUIRED
|| __IPHONE_OS_VERSION_MIN_REQUIRED && __IPHONE_OS_VERSION_MIN_REQUIRED < 110000 \ #if __MAC_OS_X_VERSION_MIN_REQUIRED < 101300
|| __TV_OS_VERSION_MIN_REQUIRED && __TVOS_VERSION_MIN_REQUIRED < 110000 \
|| __WATCH_OS_VERSION_MIN_REQUIRED && __WATCHOS_VERSION_MIN_REQUIRED < 40000)
struct ::stat fs; struct ::stat fs;
if (::stat(p.c_str(), &fs) == 0) { if (::stat(p.c_str(), &fs) == 0) {
struct ::timeval tv[2]; struct ::timeval tv[2];
@ -4658,6 +4501,18 @@ GHC_INLINE void last_write_time(const path& p, file_time_type new_time, std::err
} }
ec = detail::make_system_error(); ec = detail::make_system_error();
return; 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 #else
#ifndef UTIME_OMIT #ifndef UTIME_OMIT
#define UTIME_OMIT ((1l << 30) - 2l) #define UTIME_OMIT ((1l << 30) - 2l)
@ -4670,7 +4525,7 @@ GHC_INLINE void last_write_time(const path& p, file_time_type new_time, std::err
#if defined(__ANDROID_API__) && __ANDROID_API__ < 12 #if defined(__ANDROID_API__) && __ANDROID_API__ < 12
if (syscall(__NR_utimensat, AT_FDCWD, p.c_str(), times, AT_SYMLINK_NOFOLLOW) != 0) { if (syscall(__NR_utimensat, AT_FDCWD, p.c_str(), times, AT_SYMLINK_NOFOLLOW) != 0) {
#else #else
if (::utimensat((int)AT_FDCWD, p.c_str(), times, AT_SYMLINK_NOFOLLOW) != 0) { if (::utimensat(AT_FDCWD, p.c_str(), times, AT_SYMLINK_NOFOLLOW) != 0) {
#endif #endif
ec = detail::make_system_error(); ec = detail::make_system_error();
} }
@ -4833,9 +4688,9 @@ GHC_INLINE bool remove(const path& p, std::error_code& ec) noexcept
} }
ec = detail::make_system_error(error); ec = detail::make_system_error(error);
} }
else if (attr & FILE_ATTRIBUTE_READONLY) { else if(attr & FILE_ATTRIBUTE_READONLY) {
auto new_attr = attr & ~static_cast<DWORD>(FILE_ATTRIBUTE_READONLY); auto new_attr = attr & ~static_cast<DWORD>(FILE_ATTRIBUTE_READONLY);
if (!SetFileAttributesW(cstr, new_attr)) { if(!SetFileAttributesW(cstr, new_attr)) {
auto error = ::GetLastError(); auto error = ::GetLastError();
ec = detail::make_system_error(error); ec = detail::make_system_error(error);
} }
@ -4885,7 +4740,7 @@ GHC_INLINE uintmax_t remove_all(const path& p, std::error_code& ec) noexcept
return static_cast<uintmax_t>(-1); return static_cast<uintmax_t>(-1);
} }
std::error_code tec; std::error_code tec;
auto fs = symlink_status(p, tec); auto fs = status(p, tec);
if (exists(fs) && is_directory(fs)) { if (exists(fs) && is_directory(fs)) {
for (auto iter = directory_iterator(p, ec); iter != directory_iterator(); iter.increment(ec)) { for (auto iter = directory_iterator(p, ec); iter != directory_iterator(); iter.increment(ec)) {
if (ec && !detail::is_not_found_error(ec)) { if (ec && !detail::is_not_found_error(ec)) {
@ -4976,8 +4831,8 @@ GHC_INLINE void resize_file(const path& p, uintmax_t size, std::error_code& ec)
#endif #endif
return; return;
} }
detail::unique_handle file(CreateFileW(GHC_NATIVEWP(p), GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL)); std::shared_ptr<void> file(CreateFileW(GHC_NATIVEWP(p), GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL), CloseHandle);
if (!file) { if (file.get() == INVALID_HANDLE_VALUE) {
ec = detail::make_system_error(); ec = detail::make_system_error();
} }
else if (SetFilePointerEx(file.get(), lisize, NULL, FILE_BEGIN) == 0 || SetEndOfFile(file.get()) == 0) { else if (SetFilePointerEx(file.get(), lisize, NULL, FILE_BEGIN) == 0 || SetEndOfFile(file.get()) == 0) {
@ -5020,7 +4875,7 @@ GHC_INLINE space_info space(const path& p, std::error_code& ec) noexcept
ec = detail::make_system_error(); ec = detail::make_system_error();
return {static_cast<uintmax_t>(-1), static_cast<uintmax_t>(-1), static_cast<uintmax_t>(-1)}; return {static_cast<uintmax_t>(-1), static_cast<uintmax_t>(-1), static_cast<uintmax_t>(-1)};
} }
return {static_cast<uintmax_t>(sfs.f_blocks) * static_cast<uintmax_t>(sfs.f_frsize), static_cast<uintmax_t>(sfs.f_bfree) * static_cast<uintmax_t>(sfs.f_frsize), static_cast<uintmax_t>(sfs.f_bavail) * static_cast<uintmax_t>(sfs.f_frsize)}; return {static_cast<uintmax_t>(sfs.f_blocks * sfs.f_frsize), static_cast<uintmax_t>(sfs.f_bfree * sfs.f_frsize), static_cast<uintmax_t>(sfs.f_bavail * sfs.f_frsize)};
#endif #endif
} }
@ -5285,7 +5140,7 @@ GHC_INLINE void directory_entry::refresh()
{ {
std::error_code ec; std::error_code ec;
refresh(ec); refresh(ec);
if (ec && (_status.type() == file_type::none || _symlink_status.type() != file_type::symlink)) { if (ec) {
throw filesystem_error(detail::systemErrorText(ec.value()), _path, ec); throw filesystem_error(detail::systemErrorText(ec.value()), _path, ec);
} }
} }
@ -5320,7 +5175,7 @@ GHC_INLINE file_type directory_entry::status_file_type() const
GHC_INLINE file_type directory_entry::status_file_type(std::error_code& ec) const noexcept GHC_INLINE file_type directory_entry::status_file_type(std::error_code& ec) const noexcept
{ {
if (_status.type() != file_type::none) { if(_status.type() != file_type::none) {
ec.clear(); ec.clear();
return _status.type(); return _status.type();
} }
@ -5434,7 +5289,7 @@ GHC_INLINE bool directory_entry::is_symlink() const
GHC_INLINE bool directory_entry::is_symlink(std::error_code& ec) const noexcept GHC_INLINE bool directory_entry::is_symlink(std::error_code& ec) const noexcept
{ {
if (_symlink_status.type() != file_type::none) { if(_symlink_status.type() != file_type::none) {
ec.clear(); ec.clear();
return _symlink_status.type() == file_type::symlink; return _symlink_status.type() == file_type::symlink;
} }
@ -5696,7 +5551,7 @@ public:
, _entry(nullptr) , _entry(nullptr)
{ {
if (!path.empty()) { if (!path.empty()) {
do { _dir = ::opendir(path.native().c_str()); } while(errno == EINTR && !_dir); _dir = ::opendir(path.native().c_str());
if (!_dir) { if (!_dir) {
auto error = errno; auto error = errno;
_base = filesystem::path(); _base = filesystem::path();
@ -5723,7 +5578,7 @@ public:
do { do {
skip = false; skip = false;
errno = 0; errno = 0;
do { _entry = ::readdir(_dir); } while(errno == EINTR && !_entry); _entry = ::readdir(_dir);
if (_entry) { if (_entry) {
_dir_entry._path = _base; _dir_entry._path = _base;
_dir_entry._path.append_name(_entry->d_name); _dir_entry._path.append_name(_entry->d_name);
@ -5737,7 +5592,7 @@ public:
::closedir(_dir); ::closedir(_dir);
_dir = nullptr; _dir = nullptr;
_dir_entry._path.clear(); _dir_entry._path.clear();
if (errno && errno != EINTR) { if (errno) {
ec = detail::make_system_error(); ec = detail::make_system_error();
} }
break; break;
@ -5748,16 +5603,30 @@ public:
void copyToDirEntry() void copyToDirEntry()
{ {
#ifdef GHC_NO_DIRENT_D_TYPE
_dir_entry._symlink_status = file_status();
_dir_entry._status = file_status();
#else
_dir_entry._symlink_status.permissions(perms::unknown); _dir_entry._symlink_status.permissions(perms::unknown);
auto ft = detail::file_type_from_dirent(*_entry); switch(_entry->d_type) {
_dir_entry._symlink_status.type(ft); case DT_BLK: _dir_entry._symlink_status.type(file_type::block); break;
if (ft != file_type::symlink) { case DT_CHR: _dir_entry._symlink_status.type(file_type::character); break;
case DT_DIR: _dir_entry._symlink_status.type(file_type::directory); break;
case DT_FIFO: _dir_entry._symlink_status.type(file_type::fifo); break;
case DT_LNK: _dir_entry._symlink_status.type(file_type::symlink); break;
case DT_REG: _dir_entry._symlink_status.type(file_type::regular); break;
case DT_SOCK: _dir_entry._symlink_status.type(file_type::socket); break;
case DT_UNKNOWN: _dir_entry._symlink_status.type(file_type::none); break;
default: _dir_entry._symlink_status.type(file_type::unknown); break;
}
if (_entry->d_type != DT_LNK) {
_dir_entry._status = _dir_entry._symlink_status; _dir_entry._status = _dir_entry._symlink_status;
} }
else { else {
_dir_entry._status.type(file_type::none); _dir_entry._status.type(file_type::none);
_dir_entry._status.permissions(perms::unknown); _dir_entry._status.permissions(perms::unknown);
} }
#endif
_dir_entry._file_size = static_cast<uintmax_t>(-1); _dir_entry._file_size = static_cast<uintmax_t>(-1);
_dir_entry._hard_link_count = static_cast<uintmax_t>(-1); _dir_entry._hard_link_count = static_cast<uintmax_t>(-1);
_dir_entry._last_write_time = 0; _dir_entry._last_write_time = 0;
@ -5988,10 +5857,10 @@ GHC_INLINE recursive_directory_iterator& recursive_directory_iterator::increment
{ {
bool isSymLink = (*this)->is_symlink(ec); bool isSymLink = (*this)->is_symlink(ec);
bool isDir = !ec && (*this)->is_directory(ec); bool isDir = !ec && (*this)->is_directory(ec);
if (isSymLink && detail::is_not_found_error(ec)) { if(isSymLink && detail::is_not_found_error(ec)) {
ec.clear(); ec.clear();
} }
if (!ec) { if(!ec) {
if (recursion_pending() && isDir && (!isSymLink || (options() & directory_options::follow_directory_symlink) != directory_options::none)) { if (recursion_pending() && isDir && (!isSymLink || (options() & directory_options::follow_directory_symlink) != directory_options::none)) {
_impl->_dir_iter_stack.push(directory_iterator((*this)->path(), _impl->_options, ec)); _impl->_dir_iter_stack.push(directory_iterator((*this)->path(), _impl->_options, ec));
} }

View File

@ -25,7 +25,7 @@
// SOFTWARE. // SOFTWARE.
// //
//--------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------
// fs_fwd.hpp - The forwarding header for the header/implementation separated usage of // fs_fwd.hpp - The forwarding header for the header/implementation seperated usage of
// ghc::filesystem. // ghc::filesystem.
// This file can be include at any place, where ghc::filesystem api is needed while // This file can be include at any place, where ghc::filesystem api is needed while
// not bleeding implementation details (e.g. system includes) into the global namespace, // not bleeding implementation details (e.g. system includes) into the global namespace,
@ -34,5 +34,5 @@
#ifndef GHC_FILESYSTEM_FWD_H #ifndef GHC_FILESYSTEM_FWD_H
#define GHC_FILESYSTEM_FWD_H #define GHC_FILESYSTEM_FWD_H
#define GHC_FILESYSTEM_FWD #define GHC_FILESYSTEM_FWD
#include "filesystem.hpp" #include <ghc/filesystem.hpp>
#endif // GHC_FILESYSTEM_FWD_H #endif // GHC_FILESYSTEM_FWD_H

View File

@ -25,11 +25,11 @@
// SOFTWARE. // SOFTWARE.
// //
//--------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------
// fs_impl.hpp - The implementation header for the header/implementation separated usage of // fs_impl.hpp - The implementation header for the header/implementation seperated usage of
// ghc::filesystem. // ghc::filesystem.
// This file can be used to hide the implementation of ghc::filesystem into a single cpp. // This file can be used to hide the implementation of ghc::filesystem into a single cpp.
// The cpp has to include this before including fs_fwd.hpp directly or via a different // The cpp has to include this before including fs_fwd.hpp directly or via a different
// header to work. // header to work.
//--------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------
#define GHC_FILESYSTEM_IMPLEMENTATION #define GHC_FILESYSTEM_IMPLEMENTATION
#include "filesystem.hpp" #include <ghc/filesystem.hpp>

View File

@ -31,47 +31,30 @@
//--------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------
#ifndef GHC_FILESYSTEM_STD_H #ifndef GHC_FILESYSTEM_STD_H
#define GHC_FILESYSTEM_STD_H #define GHC_FILESYSTEM_STD_H
#if defined(__APPLE__)
#if defined(_MSVC_LANG) && _MSVC_LANG >= 201703L || __cplusplus >= 201703L && defined(__has_include) #include <Availability.h>
// ^ Supports MSVC prior to 15.7 without setting /Zc:__cplusplus to fix __cplusplus
// _MSVC_LANG works regardless. But without the switch, the compiler always reported 199711L: https://blogs.msdn.microsoft.com/vcblog/2018/04/09/msvc-now-correctly-reports-__cplusplus/
#if __has_include(<filesystem>) // Two stage __has_include needed for MSVC 2015 and per https://gcc.gnu.org/onlinedocs/cpp/_005f_005fhas_005finclude.html
#define GHC_USE_STD_FS
// Old Apple OSs don't support std::filesystem, though the header is available at compile
// time. In particular, std::filesystem is unavailable before macOS 10.15, iOS/tvOS 13.0,
// and watchOS 6.0.
#ifdef __APPLE__
#include <Availability.h>
// Note: This intentionally uses std::filesystem on any new Apple OS, like visionOS
// released after std::filesystem, where std::filesystem is always available.
// (All other __<platform>_VERSION_MIN_REQUIREDs will be undefined and thus 0.)
#if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED < 101500 \
|| defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && __IPHONE_OS_VERSION_MIN_REQUIRED < 130000 \
|| defined(__TV_OS_VERSION_MIN_REQUIRED) && __TV_OS_VERSION_MIN_REQUIRED < 130000 \
|| defined(__WATCH_OS_VERSION_MAX_ALLOWED) && __WATCH_OS_VERSION_MAX_ALLOWED < 60000
#undef GHC_USE_STD_FS
#endif
#endif
#endif
#endif #endif
#if ((defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) || (defined(__cplusplus) && __cplusplus >= 201703L)) && defined(__has_include)
#ifdef GHC_USE_STD_FS #if __has_include(<filesystem>) && (!defined(__MAC_OS_X_VERSION_MIN_REQUIRED) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 101500)
#include <filesystem> #define GHC_USE_STD_FS
namespace fs { #include <filesystem>
using namespace std::filesystem; namespace fs {
using ifstream = std::ifstream; using namespace std::filesystem;
using ofstream = std::ofstream; using ifstream = std::ifstream;
using fstream = std::fstream; using ofstream = std::ofstream;
} using fstream = std::fstream;
#else }
#include "filesystem.hpp" #endif
namespace fs { #endif
using namespace ghc::filesystem; #ifndef GHC_USE_STD_FS
using ifstream = ghc::filesystem::ifstream; //#define GHC_WIN_DISABLE_WSTRING_STORAGE_TYPE
using ofstream = ghc::filesystem::ofstream; #include <ghc/filesystem.hpp>
using fstream = ghc::filesystem::fstream; namespace fs {
} using namespace ghc::filesystem;
using ifstream = ghc::filesystem::ifstream;
using ofstream = ghc::filesystem::ofstream;
using fstream = ghc::filesystem::fstream;
}
#endif #endif
#endif // GHC_FILESYSTEM_STD_H #endif // GHC_FILESYSTEM_STD_H

View File

@ -25,7 +25,7 @@
// SOFTWARE. // SOFTWARE.
// //
//--------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------
// fs_std_fwd.hpp - The forwarding header for the header/implementation separated usage of // fs_std_fwd.hpp - The forwarding header for the header/implementation seperated usage of
// ghc::filesystem that uses std::filesystem if it detects it. // ghc::filesystem that uses std::filesystem if it detects it.
// This file can be include at any place, where fs::filesystem api is needed while // This file can be include at any place, where fs::filesystem api is needed while
// not bleeding implementation details (e.g. system includes) into the global namespace, // not bleeding implementation details (e.g. system includes) into the global namespace,
@ -33,47 +33,31 @@
//--------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------
#ifndef GHC_FILESYSTEM_STD_FWD_H #ifndef GHC_FILESYSTEM_STD_FWD_H
#define GHC_FILESYSTEM_STD_FWD_H #define GHC_FILESYSTEM_STD_FWD_H
#if defined(__APPLE__)
#if defined(_MSVC_LANG) && _MSVC_LANG >= 201703L || __cplusplus >= 201703L && defined(__has_include) #include <Availability.h>
// ^ Supports MSVC prior to 15.7 without setting /Zc:__cplusplus to fix __cplusplus
// _MSVC_LANG works regardless. But without the switch, the compiler always reported 199711L: https://blogs.msdn.microsoft.com/vcblog/2018/04/09/msvc-now-correctly-reports-__cplusplus/
#if __has_include(<filesystem>) // Two stage __has_include needed for MSVC 2015 and per https://gcc.gnu.org/onlinedocs/cpp/_005f_005fhas_005finclude.html
#define GHC_USE_STD_FS
// Old Apple OSs don't support std::filesystem, though the header is available at compile
// time. In particular, std::filesystem is unavailable before macOS 10.15, iOS/tvOS 13.0,
// and watchOS 6.0.
#ifdef __APPLE__
#include <Availability.h>
// Note: This intentionally uses std::filesystem on any new Apple OS, like visionOS
// released after std::filesystem, where std::filesystem is always available.
// (All other __<platform>_VERSION_MIN_REQUIREDs will be undefined and thus 0.)
#if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED < 101500 \
|| defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && __IPHONE_OS_VERSION_MIN_REQUIRED < 130000 \
|| defined(__TV_OS_VERSION_MIN_REQUIRED) && __TV_OS_VERSION_MIN_REQUIRED < 130000 \
|| defined(__WATCH_OS_VERSION_MAX_ALLOWED) && __WATCH_OS_VERSION_MAX_ALLOWED < 60000
#undef GHC_USE_STD_FS
#endif
#endif
#endif
#endif #endif
#if ((defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) || (defined(__cplusplus) && __cplusplus >= 201703L)) && defined(__has_include)
#ifdef GHC_USE_STD_FS #if __has_include(<filesystem>) && (!defined(__MAC_OS_X_VERSION_MIN_REQUIRED) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 101500)
#include <filesystem> #define GHC_USE_STD_FS
namespace fs { #include <filesystem>
using namespace std::filesystem; namespace fs {
using ifstream = std::ifstream; using namespace std::filesystem;
using ofstream = std::ofstream; using ifstream = std::ifstream;
using fstream = std::fstream; using ofstream = std::ofstream;
} using fstream = std::fstream;
#else }
#include "fs_fwd.hpp" #endif
namespace fs { #endif
using namespace ghc::filesystem; #ifndef GHC_USE_STD_FS
using ifstream = ghc::filesystem::ifstream; //#define GHC_WIN_DISABLE_WSTRING_STORAGE_TYPE
using ofstream = ghc::filesystem::ofstream; #define GHC_FILESYSTEM_FWD
using fstream = ghc::filesystem::fstream; #include <ghc/filesystem.hpp>
} namespace fs {
using namespace ghc::filesystem;
using ifstream = ghc::filesystem::ifstream;
using ofstream = ghc::filesystem::ofstream;
using fstream = ghc::filesystem::fstream;
}
#endif #endif
#endif // GHC_FILESYSTEM_STD_FWD_H #endif // GHC_FILESYSTEM_STD_FWD_H

View File

@ -25,36 +25,22 @@
// SOFTWARE. // SOFTWARE.
// //
//--------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------
// fs_std_impl.hpp - The implementation header for the header/implementation separated usage of // fs_std_impl.hpp - The implementation header for the header/implementation seperated usage of
// ghc::filesystem that does nothing if std::filesystem is detected. // ghc::filesystem that does nothing if std::filesystem is detected.
// This file can be used to hide the implementation of ghc::filesystem into a single cpp. // This file can be used to hide the implementation of ghc::filesystem into a single cpp.
// The cpp has to include this before including fs_std_fwd.hpp directly or via a different // The cpp has to include this before including fs_std_fwd.hpp directly or via a different
// header to work. // header to work.
//--------------------------------------------------------------------------------------- //---------------------------------------------------------------------------------------
#if defined(_MSVC_LANG) && _MSVC_LANG >= 201703L || __cplusplus >= 201703L && defined(__has_include) #if defined(__APPLE__)
// ^ Supports MSVC prior to 15.7 without setting /Zc:__cplusplus to fix __cplusplus #include <Availability.h>
// _MSVC_LANG works regardless. But without the switch, the compiler always reported 199711L: https://blogs.msdn.microsoft.com/vcblog/2018/04/09/msvc-now-correctly-reports-__cplusplus/ #endif
#if __has_include(<filesystem>) // Two stage __has_include needed for MSVC 2015 and per https://gcc.gnu.org/onlinedocs/cpp/_005f_005fhas_005finclude.html #if ((defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) || (defined(__cplusplus) && __cplusplus >= 201703L)) && defined(__has_include)
#define GHC_USE_STD_FS #if __has_include(<filesystem>) && (!defined(__MAC_OS_X_VERSION_MIN_REQUIRED) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 101500)
#define GHC_USE_STD_FS
// Old Apple OSs don't support std::filesystem, though the header is available at compile #endif
// time. In particular, std::filesystem is unavailable before macOS 10.15, iOS/tvOS 13.0,
// and watchOS 6.0.
#ifdef __APPLE__
#include <Availability.h>
// Note: This intentionally uses std::filesystem on any new Apple OS, like visionOS
// released after std::filesystem, where std::filesystem is always available.
// (All other __<platform>_VERSION_MIN_REQUIREDs will be undefined and thus 0.)
#if defined(__MAC_OS_X_VERSION_MIN_REQUIRED) && __MAC_OS_X_VERSION_MIN_REQUIRED < 101500 \
|| defined(__IPHONE_OS_VERSION_MIN_REQUIRED) && __IPHONE_OS_VERSION_MIN_REQUIRED < 130000 \
|| defined(__TV_OS_VERSION_MIN_REQUIRED) && __TV_OS_VERSION_MIN_REQUIRED < 130000 \
|| defined(__WATCH_OS_VERSION_MAX_ALLOWED) && __WATCH_OS_VERSION_MAX_ALLOWED < 60000
#undef GHC_USE_STD_FS
#endif
#endif
#endif
#endif #endif
#ifndef GHC_USE_STD_FS #ifndef GHC_USE_STD_FS
#include "fs_impl.hpp" //#define GHC_WIN_DISABLE_WSTRING_STORAGE_TYPE
#define GHC_FILESYSTEM_IMPLEMENTATION
#include <ghc/filesystem.hpp>
#endif #endif

View File

@ -3,20 +3,6 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake/")
set(PARSE_CATCH_TESTS_ADD_TO_CONFIGURE_DEPENDS ON) set(PARSE_CATCH_TESTS_ADD_TO_CONFIGURE_DEPENDS ON)
include(ParseAndAddCatchTests) include(ParseAndAddCatchTests)
if("${CMAKE_CXX_COMPILER_ID}" MATCHES "Clang" AND UNIX AND NOT APPLE AND NOT BSD)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -stdlib=libc++")
set(CMAKE_EXE_LINKER_FLAGS "-lc++abi")
endif()
function(SetTestCompileOptions target_name)
target_compile_options(${target_name} 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-deprecated-declarations>
$<$<CXX_COMPILER_ID:GNU>:-Wall -Wextra -Wshadow -Wconversion -Wsign-conversion -Wpedantic -Wno-psabi -Werror -Wno-deprecated-declarations>
$<$<CXX_COMPILER_ID:MSVC>:/WX /wd4996>
$<$<BOOL:${CYGWIN}>:-Wa,-mbig-obj>)
endfunction()
if(GHC_COVERAGE) if(GHC_COVERAGE)
message("Generating test runner for coverage run...") message("Generating test runner for coverage run...")
set(CMAKE_EXE_LINKER_FLAGS "${CMCMAKE_EXE_LINKER_FLAGS} --coverage") set(CMAKE_EXE_LINKER_FLAGS "${CMCMAKE_EXE_LINKER_FLAGS} --coverage")
@ -27,29 +13,16 @@ if(GHC_COVERAGE)
target_compile_options(filesystem_test PUBLIC --coverage) target_compile_options(filesystem_test PUBLIC --coverage)
endif() endif()
target_link_libraries(filesystem_test PUBLIC ghc_filesystem --coverage) target_link_libraries(filesystem_test PUBLIC ghc_filesystem --coverage)
if(${CMAKE_SYSTEM_NAME} MATCHES "(SunOS|Solaris)")
target_link_libraries(filesystem_test PUBLIC xnet)
endif()
if(${CMAKE_SYSTEM_NAME} MATCHES "Haiku")
target_link_libraries(filesystem_test PUBLIC network)
endif()
if("cxx_std_17" IN_LIST GHC_FILESYSTEM_TEST_COMPILE_FEATURES)
AddTestExecutableWithStdCpp(17 filesystem_test.cpp catch.hpp)
endif()
if("cxx_std_20" IN_LIST GHC_FILESYSTEM_TEST_COMPILE_FEATURES)
AddTestExecutableWithStdCpp(20 filesystem_test.cpp catch.hpp)
endif()
else() else()
message("Generating test runner for normal test...") message("Generating test runner for normal test...")
add_executable(filesystem_test filesystem_test.cpp catch.hpp) add_executable(filesystem_test filesystem_test.cpp catch.hpp)
target_link_libraries(filesystem_test ghc_filesystem) target_link_libraries(filesystem_test ghc_filesystem)
if(${CMAKE_SYSTEM_NAME} MATCHES "(SunOS|Solaris)") target_compile_options(filesystem_test PRIVATE
target_link_libraries(filesystem_test xnet) $<$<BOOL:${EMSCRIPTEN}>:-s DISABLE_EXCEPTION_CATCHING=0>
endif() $<$<OR:$<CXX_COMPILER_ID:Clang>,$<CXX_COMPILER_ID:AppleClang>>:-Wall -Wextra -Wshadow -Wconversion -Wsign-conversion -Wpedantic -Werror>
if(${CMAKE_SYSTEM_NAME} MATCHES "Haiku") $<$<CXX_COMPILER_ID:GNU>:-Wall -Wextra -Wshadow -Wconversion -Wsign-conversion -Wpedantic -Wno-psabi -Werror>
target_link_libraries(filesystem_test network) $<$<CXX_COMPILER_ID:MSVC>:/WX>
endif() $<$<BOOL:${CYGWIN}>:-Wa,-mbig-obj>)
SetTestCompileOptions(filesystem_test)
if(CMAKE_CXX_COMPILER_ID MATCHES MSVC) if(CMAKE_CXX_COMPILER_ID MATCHES MSVC)
target_compile_definitions(filesystem_test PRIVATE _CRT_SECURE_NO_WARNINGS WIN32_LEAN_AND_MEAN NOMINMAX) target_compile_definitions(filesystem_test PRIVATE _CRT_SECURE_NO_WARNINGS WIN32_LEAN_AND_MEAN NOMINMAX)
endif() endif()
@ -63,7 +36,10 @@ else()
if(WIN32) if(WIN32)
add_executable(filesystem_test_char filesystem_test.cpp catch.hpp) add_executable(filesystem_test_char filesystem_test.cpp catch.hpp)
target_link_libraries(filesystem_test_char ghc_filesystem) target_link_libraries(filesystem_test_char ghc_filesystem)
SetTestCompileOptions(filesystem_test_char) target_compile_options(filesystem_test_char PRIVATE
$<$<CXX_COMPILER_ID:Clang>:-Wall -Wextra -Werror>
$<$<CXX_COMPILER_ID:GNU>:-Wall -Werror>
$<$<CXX_COMPILER_ID:MSVC>:/WX>)
if(CMAKE_CXX_COMPILER_ID MATCHES MSVC) if(CMAKE_CXX_COMPILER_ID MATCHES MSVC)
target_compile_definitions(filesystem_test_char PRIVATE _CRT_SECURE_NO_WARNINGS GHC_WIN_DISABLE_WSTRING_STORAGE_TYPE) target_compile_definitions(filesystem_test_char PRIVATE _CRT_SECURE_NO_WARNINGS GHC_WIN_DISABLE_WSTRING_STORAGE_TYPE)
else() else()
@ -85,13 +61,12 @@ add_test(multifile_test multifile_test)
add_executable(fwd_impl_test fwd_test.cpp impl_test.cpp) add_executable(fwd_impl_test fwd_test.cpp impl_test.cpp)
target_link_libraries(fwd_impl_test ghc_filesystem) target_link_libraries(fwd_impl_test ghc_filesystem)
if(${CMAKE_SYSTEM_NAME} MATCHES "(SunOS|Solaris)") target_compile_options(fwd_impl_test PRIVATE
target_link_libraries(fwd_impl_test xnet) $<$<BOOL:${EMSCRIPTEN}>:-s DISABLE_EXCEPTION_CATCHING=0>
endif() $<$<OR:$<CXX_COMPILER_ID:Clang>,$<CXX_COMPILER_ID:AppleClang>>:-Wall -Wextra -Wshadow -Wconversion -Wsign-conversion -Wpedantic -Werror>
if(${CMAKE_SYSTEM_NAME} MATCHES "Haiku") $<$<CXX_COMPILER_ID:GNU>:-Wall -Wextra -Wshadow -Wconversion -Wsign-conversion -Wpedantic -Wno-psabi -Werror>
target_link_libraries(fwd_impl_test network) $<$<CXX_COMPILER_ID:MSVC>:/WX>
endif() $<$<BOOL:${CYGWIN}>:-Wa,-mbig-obj>)
SetTestCompileOptions(fwd_impl_test)
if(CMAKE_CXX_COMPILER_ID MATCHES MSVC) if(CMAKE_CXX_COMPILER_ID MATCHES MSVC)
target_compile_definitions(fwd_impl_test PRIVATE _CRT_SECURE_NO_WARNINGS WIN32_LEAN_AND_MEAN NOMINMAX) target_compile_definitions(fwd_impl_test PRIVATE _CRT_SECURE_NO_WARNINGS WIN32_LEAN_AND_MEAN NOMINMAX)
endif() endif()
@ -102,4 +77,9 @@ if(NOT MSVC)
target_compile_options(exception PRIVATE -fno-exceptions) target_compile_options(exception PRIVATE -fno-exceptions)
endif() endif()
target_include_directories(exception PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../include) target_include_directories(exception PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../include)
SetTestCompileOptions(exception) target_compile_options(exception 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>
$<$<CXX_COMPILER_ID:GNU>:-Wall -Wextra -Wshadow -Wconversion -Wsign-conversion -Wpedantic -Wno-psabi -Werror>
$<$<CXX_COMPILER_ID:MSVC>:/WX>
$<$<BOOL:${CYGWIN}>:-Wa,-mbig-obj>)

File diff suppressed because it is too large Load Diff

View File

@ -99,7 +99,7 @@ using fstream = ghc::filesystem::fstream;
//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - //- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
template <typename TP> template <typename TP>
static std::time_t to_time_t(TP tp) std::time_t to_time_t(TP tp)
{ {
using namespace std::chrono; using namespace std::chrono;
auto sctp = time_point_cast<system_clock::duration>(tp - TP::clock::now() + system_clock::now()); auto sctp = time_point_cast<system_clock::duration>(tp - TP::clock::now() + system_clock::now());
@ -107,7 +107,7 @@ static std::time_t to_time_t(TP tp)
} }
template <typename TP> template <typename TP>
static TP from_time_t(std::time_t t) TP from_time_t(std::time_t t)
{ {
using namespace std::chrono; using namespace std::chrono;
auto sctp = system_clock::from_time_t(t); auto sctp = system_clock::from_time_t(t);
@ -214,8 +214,7 @@ static void generateFile(const fs::path& pathname, int withSize = -1)
} }
#ifdef GHC_OS_WINDOWS #ifdef GHC_OS_WINDOWS
#if !defined(_WIN64) && defined(KEY_WOW64_64KEY) inline bool isWow64Proc()
static bool isWow64Proc()
{ {
typedef BOOL(WINAPI * IsWow64Process_t)(HANDLE, PBOOL); typedef BOOL(WINAPI * IsWow64Process_t)(HANDLE, PBOOL);
BOOL bIsWow64 = FALSE; BOOL bIsWow64 = FALSE;
@ -227,7 +226,6 @@ static bool isWow64Proc()
} }
return bIsWow64 == TRUE; return bIsWow64 == TRUE;
} }
#endif
static bool is_symlink_creation_supported() static bool is_symlink_creation_supported()
{ {
@ -305,13 +303,13 @@ public:
}; };
template <class T, class U> template <class T, class U>
static bool operator==(TestAllocator<T> const&, TestAllocator<U> const&) noexcept bool operator==(TestAllocator<T> const&, TestAllocator<U> const&) noexcept
{ {
return true; return true;
} }
template <class T, class U> template <class T, class U>
static bool operator!=(TestAllocator<T> const& x, TestAllocator<U> const& y) noexcept bool operator!=(TestAllocator<T> const& x, TestAllocator<U> const& y) noexcept
{ {
return !(x == y); return !(x == y);
} }
@ -792,9 +790,6 @@ TEST_CASE("fs.path.decompose - path decomposition", "[filesystem][path][fs.path.
CHECK(fs::path("C:/foo").filename() == "foo"); CHECK(fs::path("C:/foo").filename() == "foo");
CHECK(fs::path("C:\\foo").filename() == "foo"); CHECK(fs::path("C:\\foo").filename() == "foo");
CHECK(fs::path("C:foo").filename() == "foo"); CHECK(fs::path("C:foo").filename() == "foo");
CHECK(fs::path("t:est.txt").filename() == "est.txt");
#else
CHECK(fs::path("t:est.txt").filename() == "t:est.txt");
#endif #endif
// stem() // stem()
@ -812,13 +807,6 @@ TEST_CASE("fs.path.decompose - path decomposition", "[filesystem][path][fs.path.
CHECK(fs::path("/foo/.profile").stem() == ".profile"); CHECK(fs::path("/foo/.profile").stem() == ".profile");
CHECK(fs::path(".bar").stem() == ".bar"); CHECK(fs::path(".bar").stem() == ".bar");
CHECK(fs::path("..bar").stem() == "."); CHECK(fs::path("..bar").stem() == ".");
#ifdef GHC_OS_WINDOWS
CHECK(fs::path("t:est.txt").stem() == "est");
#else
CHECK(fs::path("t:est.txt").stem() == "t:est");
#endif
CHECK(fs::path("/foo/.").stem() == ".");
CHECK(fs::path("/foo/..").stem() == "..");
// extension() // extension()
CHECK(fs::path("/foo/bar.txt").extension() == ".txt"); CHECK(fs::path("/foo/bar.txt").extension() == ".txt");
@ -826,9 +814,6 @@ TEST_CASE("fs.path.decompose - path decomposition", "[filesystem][path][fs.path.
CHECK(fs::path("/foo/.profile").extension() == ""); CHECK(fs::path("/foo/.profile").extension() == "");
CHECK(fs::path(".bar").extension() == ""); CHECK(fs::path(".bar").extension() == "");
CHECK(fs::path("..bar").extension() == ".bar"); CHECK(fs::path("..bar").extension() == ".bar");
CHECK(fs::path("t:est.txt").extension() == ".txt");
CHECK(fs::path("/foo/.").extension() == "");
CHECK(fs::path("/foo/..").extension() == "");
if (has_host_root_name_support()) { if (has_host_root_name_support()) {
// //host-based root-names // //host-based root-names
@ -965,10 +950,6 @@ TEST_CASE("fs.path.gen - path generation", "[filesystem][path][fs.path.gen]")
// lexically_relative() // lexically_relative()
CHECK(fs::path("/a/d").lexically_relative("/a/b/c") == "../../d"); CHECK(fs::path("/a/d").lexically_relative("/a/b/c") == "../../d");
CHECK(fs::path("/a/b/c").lexically_relative("/a/d") == "../b/c"); CHECK(fs::path("/a/b/c").lexically_relative("/a/d") == "../b/c");
CHECK(fs::path("/a/b/c").lexically_relative("/a/b/c/d/..") == ".");
CHECK(fs::path("/a/b/c/").lexically_relative("/a/b/c/d/..") == ".");
CHECK(fs::path("").lexically_relative("/a/..") == "");
CHECK(fs::path("").lexically_relative("a/..") == ".");
CHECK(fs::path("a/b/c").lexically_relative("a") == "b/c"); CHECK(fs::path("a/b/c").lexically_relative("a") == "b/c");
CHECK(fs::path("a/b/c").lexically_relative("a/b/c/x/y") == "../.."); CHECK(fs::path("a/b/c").lexically_relative("a/b/c/x/y") == "../..");
CHECK(fs::path("a/b/c").lexically_relative("a/b/c") == "."); CHECK(fs::path("a/b/c").lexically_relative("a/b/c") == ".");
@ -1218,7 +1199,7 @@ TEST_CASE("fs.class.filesystem_error - class filesystem_error", "[filesystem][fi
CHECK(fse.path2() == "some/other"); CHECK(fse.path2() == "some/other");
} }
static constexpr fs::perms constExprOwnerAll() constexpr fs::perms constExprOwnerAll()
{ {
return fs::perms::owner_read | fs::perms::owner_write | fs::perms::owner_exec; return fs::perms::owner_read | fs::perms::owner_write | fs::perms::owner_exec;
} }
@ -1396,16 +1377,6 @@ TEST_CASE("fs.dir.entry - class directory_entry", "[filesystem][directory_entry]
CHECK(!(d2 != d2)); CHECK(!(d2 != d2));
CHECK(d1 == d1); CHECK(d1 == d1);
CHECK(!(d1 == d2)); CHECK(!(d1 == d2));
if(is_symlink_creation_supported()) {
fs::create_symlink(t.path() / "nonexistent", t.path() / "broken");
for (auto d3 : fs::directory_iterator(t.path())) {
CHECK_NOTHROW(d3.symlink_status());
CHECK_NOTHROW(d3.status());
CHECK_NOTHROW(d3.refresh());
}
fs::directory_entry entry(t.path() / "broken");
CHECK_NOTHROW(entry.refresh());
}
} }
TEST_CASE("fs.class.directory_iterator - class directory_iterator", "[filesystem][directory_iterator][fs.class.directory_iterator]") TEST_CASE("fs.class.directory_iterator - class directory_iterator", "[filesystem][directory_iterator][fs.class.directory_iterator]")
@ -1807,19 +1778,6 @@ TEST_CASE("fs.op.copy_file - copy_file", "[filesystem][operations][fs.op.copy_fi
CHECK_NOTHROW(fs::copy_file("foobar", "foobar2", ec)); CHECK_NOTHROW(fs::copy_file("foobar", "foobar2", ec));
CHECK(ec); CHECK(ec);
CHECK(!fs::exists("foobar")); CHECK(!fs::exists("foobar"));
fs::path file1("temp1.txt");
fs::path file2("temp2.txt");
generateFile(file1, 200);
generateFile(file2, 200);
auto allWrite = fs::perms::owner_write | fs::perms::group_write | fs::perms::others_write;
CHECK_NOTHROW(fs::permissions(file1, allWrite, fs::perm_options::remove));
CHECK((fs::status(file1).permissions() & fs::perms::owner_write) != fs::perms::owner_write);
CHECK_NOTHROW(fs::permissions(file2, allWrite, fs::perm_options::add));
CHECK((fs::status(file2).permissions() & fs::perms::owner_write) == fs::perms::owner_write);
fs::copy_file(file1, file2, fs::copy_options::overwrite_existing);
CHECK((fs::status(file2).permissions() & fs::perms::owner_write) != fs::perms::owner_write);
CHECK_NOTHROW(fs::permissions(file1, allWrite, fs::perm_options::add));
CHECK_NOTHROW(fs::permissions(file2, allWrite, fs::perm_options::add));
} }
TEST_CASE("fs.op.copy_symlink - copy_symlink", "[filesystem][operations][fs.op.copy_symlink]") TEST_CASE("fs.op.copy_symlink - copy_symlink", "[filesystem][operations][fs.op.copy_symlink]")
@ -2205,7 +2163,6 @@ public:
fs::path character_path() const fs::path character_path() const
{ {
#ifndef GHC_OS_SOLARIS
std::error_code ec; std::error_code ec;
if (fs::exists("/dev/null", ec)) { if (fs::exists("/dev/null", ec)) {
return "/dev/null"; return "/dev/null";
@ -2213,7 +2170,6 @@ public:
else if (fs::exists("NUL", ec)) { else if (fs::exists("NUL", ec)) {
return "NUL"; return "NUL";
} }
#endif
return fs::path(); return fs::path();
} }
fs::path temp_path() const { return _t.path(); } fs::path temp_path() const { return _t.path(); }
@ -2651,10 +2607,6 @@ TEST_CASE("fs.op.remove_all - remove_all", "[filesystem][operations][fs.op.remov
CHECK_NOTHROW(fs::remove_all("dir1/non-existing", ec)); CHECK_NOTHROW(fs::remove_all("dir1/non-existing", ec));
CHECK(!ec); CHECK(!ec);
CHECK(fs::remove_all("dir1/non-existing", ec) == 0); CHECK(fs::remove_all("dir1/non-existing", ec) == 0);
if (is_symlink_creation_supported()) {
fs::create_directory_symlink("dir1", "dir1link");
CHECK(fs::remove_all("dir1link") == 1);
}
CHECK(fs::remove_all("dir1") == 5); CHECK(fs::remove_all("dir1") == 5);
CHECK(fs::directory_iterator(t.path()) == fs::directory_iterator()); CHECK(fs::directory_iterator(t.path()) == fs::directory_iterator());
} }

View File

@ -27,7 +27,7 @@
#include <ghc/filesystem.hpp> #include <ghc/filesystem.hpp>
namespace fs = ghc::filesystem; namespace fs = ghc::filesystem;
// This test and the one in multi2.cpp doesn't actually test relevant functionality, // This test and the one in multi2.cpp doesn't actualy test relevant functionality,
// it is just used to check that it is possible to include filesystem.h in multiple // it is just used to check that it is possible to include filesystem.h in multiple
// source files. // source files.
TEST_CASE("Multifile-test 1", "[multi]") TEST_CASE("Multifile-test 1", "[multi]")