diff --git a/README.md b/README.md index 2340104c..8f2524b4 100644 --- a/README.md +++ b/README.md @@ -324,3 +324,5 @@ TinyUSDZ is licensed under MIT license. * nanobind : BSD-3 license. https://github.com/wjakob/nanobind * pybind11 : BSD-3 license. https://github.com/pybind/pybind11 * pystring : BSD-3 license. https://github.com/imageworks/pystring +* gulrak/filesytem : MIT license. https://github.com/gulrak/filesystem +* p-ranav/glob : MIT license. https://github.com/p-ranav/glob diff --git a/android/app/src/main/cpp/CMakeLists.txt b/android/app/src/main/cpp/CMakeLists.txt index 48aa66eb..e076ed0c 100644 --- a/android/app/src/main/cpp/CMakeLists.txt +++ b/android/app/src/main/cpp/CMakeLists.txt @@ -14,9 +14,10 @@ set(TINYUSDZ_SOURCES ${PROJECT_SOURCE_DIR}/../../../../../src/crate-pprint.cc ${PROJECT_SOURCE_DIR}/../../../../../src/io-util.cc ${PROJECT_SOURCE_DIR}/../../../../../src/pprinter.cc - ${PROJECT_SOURCE_DIR}/../../../../../src/value-type.cc + ${PROJECT_SOURCE_DIR}/../../../../../src/value-types.cc ${PROJECT_SOURCE_DIR}/../../../../../src/value-pprint.cc ${PROJECT_SOURCE_DIR}/../../../../../src/primvar.cc + ${PROJECT_SOURCE_DIR}/../../../../../src/image-loader.cc ${PROJECT_SOURCE_DIR}/../../../../../src/usda-writer.cc) if(TINYUSDZ_USE_USDOBJ) @@ -29,7 +30,7 @@ endif() set(TINYUSDZ_DEP_SOURCES ${PROJECT_SOURCE_DIR}/../../../../../src/integerCoding.cpp ${PROJECT_SOURCE_DIR}/../../../../../src/lz4-compression.cc - ${PROJECT_SOURCE_DIR}/../../../../../src/pxrLZ4/lz4.cpp + ${PROJECT_SOURCE_DIR}/../../../../../src/lz4/lz4.c ${PROJECT_SOURCE_DIR}/../../../../../src/external/string_id/database.cpp ${PROJECT_SOURCE_DIR}/../../../../../src/external/string_id/string_id.cpp ${PROJECT_SOURCE_DIR}/../../../../../src/external/string_id/error.cpp diff --git a/android/app/src/main/cpp/jni-tinyusdz.cc b/android/app/src/main/cpp/jni-tinyusdz.cc index 447688c8..17c4ae50 100644 --- a/android/app/src/main/cpp/jni-tinyusdz.cc +++ b/android/app/src/main/cpp/jni-tinyusdz.cc @@ -123,10 +123,10 @@ Java_com_example_hellotinyusdz_MainActivity_touchMove(JNIEnv *env, jobject obj, tinyusdz::USDLoadOptions options; // load from Android asset folder - example::g_gui_ctx.scene = tinyusdz::HighLevelScene(); // reset + example::g_gui_ctx.stage = tinyusdz::Stage(); // reset std::string warn, err; - bool ret = LoadUSDCFromFile(filename, &example::g_gui_ctx.scene, &warn, &err, options); + bool ret = LoadUSDCFromFile(filename, &example::g_gui_ctx.stage, &warn, &err, options); if (warn.size()) { __android_log_print(ANDROID_LOG_WARN, "tinyusdz", "USD load warning: %s", warn.c_str()); @@ -140,7 +140,7 @@ Java_com_example_hellotinyusdz_MainActivity_touchMove(JNIEnv *env, jobject obj, if (ret) { // TODO - //__android_log_print(ANDROID_LOG_INFO, "tinyusdz", "USD loaded. #of geom_meshes: %d", int(example::g_gui_ctx.scene.geom_meshes.size())); + //__android_log_print(ANDROID_LOG_INFO, "tinyusdz", "USD loaded. #of geom_meshes: %d", int(example::g_gui_ctx.stage.geom_meshes.size())); } ret = example::SetupScene(example::g_gui_ctx); @@ -151,7 +151,7 @@ Java_com_example_hellotinyusdz_MainActivity_touchMove(JNIEnv *env, jobject obj, // TODO - //return int(example::g_gui_ctx.scene.geom_meshes.size()); // OK + //return int(example::g_gui_ctx.stage.geom_meshes.size()); // OK return 0; } } diff --git a/android/app/src/main/cpp/render-ctx.cc b/android/app/src/main/cpp/render-ctx.cc index ea61c99b..f73665ee 100644 --- a/android/app/src/main/cpp/render-ctx.cc +++ b/android/app/src/main/cpp/render-ctx.cc @@ -57,7 +57,8 @@ inline uint8_t ftouc(float f) { bool SetupScene(GUIContext &ctx) { __android_log_print(ANDROID_LOG_INFO, "tinyusdz", "SetupScene"); - if (ctx.scene.root_nodes.empty()) { +#if 0 // TODO + if (ctx.stage.root_nodes.empty()) { __android_log_print(ANDROID_LOG_ERROR, "tinyusdz", "No GeomMesh"); // No GeomMesh in the scene return false; @@ -78,6 +79,7 @@ bool SetupScene(GUIContext &ctx) { __android_log_print(ANDROID_LOG_ERROR, "tinyusdz", "Scene::Setup failed"); return false; } +#endif // init camera matrix { diff --git a/android/app/src/main/cpp/render-ctx.hh b/android/app/src/main/cpp/render-ctx.hh index 5e00003e..c8b40227 100644 --- a/android/app/src/main/cpp/render-ctx.hh +++ b/android/app/src/main/cpp/render-ctx.hh @@ -55,7 +55,7 @@ struct GUIContext { int render_height = 512; // scene reload - tinyusdz::HighLevelScene scene; + tinyusdz::Stage stage; std::atomic request_reload{false}; std::string filename; diff --git a/lgtm.yaml b/lgtm.yaml index baa1638e..81eae19e 100644 --- a/lgtm.yaml +++ b/lgtm.yaml @@ -1,3 +1,4 @@ path_classifiers: legacy: - - exclude: src/stb_image.h "src/dr_*" + - exclude: src/miniz.c + - exclude: src/miniz.h diff --git a/src/external/filesystem/LICENSE b/src/external/filesystem/LICENSE new file mode 100644 index 00000000..8b24faa7 --- /dev/null +++ b/src/external/filesystem/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2018, Steffen Schümann + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/external/filesystem/README.md b/src/external/filesystem/README.md new file mode 100644 index 00000000..db1b2f9b --- /dev/null +++ b/src/external/filesystem/README.md @@ -0,0 +1,1074 @@ +![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) +[![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://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://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) +[![Latest Release Tag](https://img.shields.io/github/tag/gulrak/filesystem.svg)](https://github.com/gulrak/filesystem/tree/v1.5.12) + +- [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](#fspath-refhttpsencppreferencecomwcppfilesystempath) + - [Open Issues](#open-issues) + - [Windows](#windows) + - [Symbolic Links on Windows](#symbolic-links-on-windows) + - [Permissions](#permissions) + - [Release Notes](#release-notes) + +# Filesystem + +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 +(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, +Alpine ARM/ARM64 Linux and Solaris 10 but should work on other systems too, as long as you have +at least a C++11 compatible compiler. It should work with Android NDK, Emscripten and I even +had reports of it being used on iOS (within sandboxing constraints) and with v1.5.6 there +is experimental support for QNX. The support of Android NDK, Emscripten and QNX is not +backed up by automated testing but PRs and bug reports are welcome for those too. +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). + +*Test coverage is well above 90%, and starting with v1.3.6 and in v1.5.0 +more time was invested in benchmarking and optimizing parts of the library. I'll try +to continue to optimize some parts and refactor others, striving +to improve it as long as it doesn't introduce additional C++17/C++20 compatibility +issues. Feedback is always welcome. Simply open an issue if you see something missing +or wrong or not behaving as expected and I'll comment.* + + +## Motivation + +I'm often in need of filesystem functionality, mostly `fs::path`, but directory +access too, and when beginning to use C++11, I used that language update +to try to reduce my third-party dependencies. I could drop most of what +I used, but still missed some stuff that I started implementing for the +fun of it. Originally I based these helpers on my own coding- and naming +conventions. When C++17 was finalized, I wanted to use that interface, +but it took a while, to push myself to convert my classes. + +The implementation is closely based on chapter 30.10 from the C++17 standard +and a draft close to that version is +[Working Draft N4687](https://github.com/cplusplus/draft/raw/master/papers/n4687.pdf). +It is from after the standardization of C++17 but it contains the latest filesystem +interface changes compared to the +[Working Draft N4659](https://github.com/cplusplus/draft/raw/master/papers/n4659.pdf). +Staring with v1.4.0, when compiled using C++20, it adapts to the changes according to path sorting order +and `std::u8string` handling from [Working Draft N4860](https://isocpp.org/files/papers/N4860.pdf). + +I want to thank the people working on improving C++, I really liked how the language +evolved with C++11 and the following standards. Keep on the good work! + +## Why the namespace GHC? +If you ask yourself, what `ghc` is standing for, it is simply +`gulraks helper classes`, yeah, I know, not very imaginative, but I wanted a +short namespace and I use it in some of my private classes (so **it has nothing +to do with Haskell**, sorry for the name clash). + +## Platforms + +`ghc::filesystem` is developed on macOS but CI tested on macOS, Windows, +various Linux Distributions, FreeBSD and starting with v1.5.12 on Solaris. +It should work on any of these with a C++11-capable compiler. Also there are some +checks to hopefully better work on Android, but as I currently don't test with the +Android NDK, I wouldn't call it a supported platform yet, same is valid for using +it with Emscripten. It is now part of the detected platforms, I fixed the obvious +issues and ran some tests with it, so it should be fine. All in all, I don't see it +replacing `std::filesystem` where full C++17 or C++20 is available, it doesn't try +to be a "better" `std::filesystem`, just an almost drop-in if you can't use it +(with the exception of the UTF-8 preference). + +: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 +wise and as being in UTF-8. The `std::u16string` will be seen as UTF-16. See *Differences in API* +for more information._ + +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 +* 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 (Alpine ARM/ARM64): GCC 9.2.0 +* FreeBSD: Clang 8.0 +* Solaris: GCC 5.5 + + +## Tests + +The header comes with a set of unit-tests and uses [CMake](https://cmake.org/) +as a build tool and [Catch2](https://github.com/catchorg/Catch2) as test framework. +All tests are registered with in CMake, so the [ctest](https://cmake.org/cmake/help/latest/manual/ctest.1.html) +commando can be used to run the tests. + +All tests against this implementation should succeed, depending on your environment +it might be that there are some warnings, e.g. if you have no rights to create +Symlinks on Windows or at least the test thinks so, but these are just informative. + +To build the tests from inside the project directory under macOS or Linux just: + +```cpp +mkdir build +cd build +cmake -DCMAKE_BUILD_TYPE=Debug .. +make +ctest +``` + +This generates the test binaries that run the tests and the last command executes +them. + +If the default compiler is a GCC 8 or newer, or Clang 7 or newer, it +additionally tries to build a version of the test binary compiled against GCCs/Clangs +`std::filesystem` implementation, named `std_filesystem_test` +as an additional test of conformance. Ideally all tests should compile and +succeed with all filesystem implementations, but in reality, there are +some differences in behavior, sometimes due to room for interpretation in +in the standard, and there might be issues in these implementations too. + + +## Usage + +### Downloads + +The latest release version is [v1.5.12](https://github.com/gulrak/filesystem/tree/v1.5.12) and +source archives can be found [here](https://github.com/gulrak/filesystem/releases/tag/v1.5.12). + +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). + +The latest pre-C++20-support release version is [v1.3.10](https://github.com/gulrak/filesystem/tree/v1.3.10) and +source archives can be found [here](https://github.com/gulrak/filesystem/releases/tag/v1.3.10). + +Currently only the latest minor release version receives bugfixes, so if possible, +you should use the latest release. + +### Using it as Single-File-Header + +As `ghc::filesystem` is at first a header-only library, it should be enough to copy the header +or the `include/ghc` directory into your project folder or point your include path to this place and +simply include the `filesystem.hpp` header (or `ghc/filesystem.hpp` if you use the subdirectory). + +Everything is in the namespace `ghc::filesystem`, so one way to use it only as +a fallback could be: + +```cpp +#ifdef __APPLE__ +#include // for deployment target to support pre-catalina targets without std::fs +#endif +#if ((defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) || (defined(__cplusplus) && __cplusplus >= 201703L)) && defined(__has_include) +#if __has_include() && (!defined(__MAC_OS_X_VERSION_MIN_REQUIRED) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 101500) +#define GHC_USE_STD_FS +#include +namespace fs = std::filesystem; +#endif +#endif +#ifndef GHC_USE_STD_FS +#include +namespace fs = ghc::filesystem; +#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, +you might use: + +```cpp +#ifdef __APPLE__ +#include // for deployment target to support pre-catalina targets without std::fs +#endif +#if ((defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) || (defined(__cplusplus) && __cplusplus >= 201703L)) && defined(__has_include) +#if __has_include() && (!defined(__MAC_OS_X_VERSION_MIN_REQUIRED) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 101500) +#define GHC_USE_STD_FS +#include +namespace fs { +using namespace std::filesystem; +using ifstream = std::ifstream; +using ofstream = std::ofstream; +using fstream = std::fstream; +} +#endif +#endif +#ifndef GHC_USE_STD_FS +#include +namespace fs { +using namespace ghc::filesystem; +using ifstream = ghc::filesystem::ifstream; +using ofstream = ghc::filesystem::ofstream; +using fstream = ghc::filesystem::fstream; +} +#endif +``` + +Now you have e.g. `fs::ofstream out(somePath);` and it is either the wrapper or +the C++17 `std::ofstream`. + +:information_source: **Be aware, as a header-only library, it is not hiding the fact, that it +uses system includes, so they "pollute" your global namespace. Use the +forwarding-/implementation-header based approach (see below) to avoid this. +For Windows it needs `Windows.h` and it might be a good idea to define +`WIN32_LEAN_AND_MEAN` or `NOMINMAX` prior to including `filesystem.hpp` or +`fs_std.hpp` headers to reduce pollution of your global namespace and compile +time. They are not defined by `ghc::filesystem` to allow combination with contexts +where the full `Windows.h`is needed, e.g. for UI elements.** + +:information_source: **Hint:** There is an additional header named `ghc/fs_std.hpp` that implements this +dynamic selection of a filesystem implementation, that you can include +instead of `ghc/filesystem.hpp` when you want `std::filesystem` where +available and `ghc::filesystem` where not. + + +### Using it as Forwarding-/Implementation-Header + +Alternatively, starting from v1.1.0 `ghc::filesystem` can also be used by +including one of two additional wrapper headers. These allow to include +a forwarded version in most places (`ghc/fs_fwd.hpp`) while hiding the +implementation details in a single cpp file that includes `ghc/fs_impl.hpp` to +implement the needed code. Using `ghc::filesystem` this way makes sure +system includes are only visible from inside the cpp file, all other places are clean. + +Be aware, that it is currently not supported to hide the implementation +into a Windows-DLL, as a DLL interface with C++ standard templates in interfaces +is a different beast. If someone is willing to give it a try, I might integrate +a PR but currently working on that myself is not a priority. + +If you use the forwarding/implementation approach, you can still use the dynamic +switching like this: + +```cpp +#ifdef __APPLE__ +#include // for deployment target to support pre-catalina targets without std::fs +#endif +#if ((defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) || (defined(__cplusplus) && __cplusplus >= 201703L)) && defined(__has_include) +#if __has_include() && (!defined(__MAC_OS_X_VERSION_MIN_REQUIRED) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 101500) +#define GHC_USE_STD_FS +#include +namespace fs { +using namespace std::filesystem; +using ifstream = std::ifstream; +using ofstream = std::ofstream; +using fstream = std::fstream; +} +#endif +#endif +#ifndef GHC_USE_STD_FS +#include +namespace fs { +using namespace ghc::filesystem; +using ifstream = ghc::filesystem::ifstream; +using ofstream = ghc::filesystem::ofstream; +using fstream = ghc::filesystem::fstream; +} +#endif +``` + +and in the implementation hiding cpp, you might use (before any include that includes `ghc/fs_fwd.hpp` +to take precedence: + +```cpp +#ifdef __APPLE__ // for deployment target to support pre-catalina targets without std::fs +#include +#endif +#if ((defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) || (defined(__cplusplus) && __cplusplus >= 201703L)) && defined(__has_include) +#if __has_include() && (!defined(__MAC_OS_X_VERSION_MIN_REQUIRED) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 101500) +#define GHC_USE_STD_FS +#endif +#endif +#ifndef GHC_USE_STD_FS +#define GHC_FILESYSTEM_IMPLEMENTATION +#include +#endif +``` + +: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 +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. + + + +### Git Submodule and CMake + +Starting from v1.1.0, it is possible to add `ghc::filesystem` +as a git submodule, add the directory to your `CMakeLists.txt` with +`add_subdirectory()` and then simply use `target_link_libraries(your-target ghc_filesystem)` +to ensure correct include path that allow `#include ` +to work. + +The `CMakeLists.txt` offers a few options to customize its behavior: + +* `GHC_FILESYSTEM_BUILD_TESTING` - Compile tests, default is `OFF` when used as + a submodule, else `ON`. +* `GHC_FILESYSTEM_BUILD_EXAMPLES` - Compile the examples, default is `OFF` when used as + a submodule, else `ON`. +* `GHC_FILESYSTEM_WITH_INSTALL` - Add install target to build, default is `OFF` when used as + a submodule, else `ON`. +* `GHC_FILESYSTEM_BUILD_STD_TESTING` - Compile `std_filesystem_test`, the variant of + the test suite running against `std::filesystem`, defaulting to `GHC_FILESYSTEM_BUILD_TESTING`. + This is only done if the compiler is detected as being able to do it. +* `GHC_FILESYSTEM_TEST_COMPILE_FEATURES` can be set to a list of features to override + `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). + +### Versioning + +There is a version macro `GHC_FILESYSTEM_VERSION` defined in case future changes +might make it needed to react on the version, but I don't plan to break anything. +It's the version as decimal number `(major * 10000 + minor * 100 + patch)`. + +:information_source: **Note:** Only even patch versions will be used for releases +and odd patch version will only be used for in between commits while working on +the next version. + + +## Documentation + +There is almost no documentation in this release, as any `std::filesystem` +documentation would work, besides the few differences explained in the next +section. So you might head over to https://en.cppreference.com/w/cpp/filesystem +for a description of the components of this library. + +When compiling with C++11, C++14 or C++17, the API is following the C++17 +standard, where possible, with the exception that `std::string_view` parameters +are only supported on C++17. When Compiling with C++20, `ghc::filesysytem` +defaults to the C++20 API, with the `char8_t` and `std::u8string` interfaces +and the deprecated `fs::u8path` factory method. + +:information_source: **Note:** If the C++17 API should be enforced even in C++20 mode, +use the define `GHC_FILESYSTEM_ENFORCE_CPP17_API`. +Even then it is possible to create `fws::path` from `std::u8string` but +`fs::path::u8string()` and `fs::path::generic_u8string()` return normal +UTF-8 encoded `std::string` instances, so code written for C++17 could +still work with `ghc::filesystem` when compiled with C++20. + +The only additions to the standard are documented here: + + +### `ghc::filesystem::ifstream`, `ghc::filesystem::ofstream`, `ghc::filesystem::fstream` + +These are simple wrappers around `std::ifstream`, `std::ofstream` and `std::fstream`. +They simply add an `open()` method and a constructor with an `ghc::filesystem::path` +argument as the `fstream` variants in C++17 have them. + +### `ghc::filesystem::u8arguments` + +This is a helper class that currently checks for UTF-8 encoding on non-Windows platforms but on Windows it +fetches the command line arguments as Unicode strings from the OS with + +```cpp +::CommandLineToArgvW(::GetCommandLineW(), &argc) +``` + +and then converts them to UTF-8, and replaces `argc` and `argv`. It is a guard-like +class that reverts its changes when going out of scope. + +So basic usage is: + +```cpp +namespace fs = ghc::filesystem; + +int main(int argc, char* argv[]) +{ + fs::u8arguments u8guard(argc, argv); + if(!u8guard.valid()) { + std::cerr << "Bad encoding, needs UTF-8." << std::endl; + exit(EXIT_FAILURE); + } + + // now use argc/argv as usual, they have utf-8 enconding on windows + // ... + + return 0; +} +``` + +That way `argv` is UTF-8 encoded as long as the scope from `main` is valid. + +**Note:** On macOS, while debugging under Xcode the code currently will return +`false` as Xcode starts the application with `US-ASCII` as encoding, no matter what +encoding is actually used and even setting `LC_ALL` in the product scheme doesn't +change anything. I still need to investigate this. + + +## Differences + +As this implementation is based on existing code from my private helper +classes, it derived some constraints of it. Starting from v1.5.0 most of the +differences between this and the standard C++17/C++20 API where removed. + + +### LWG Defects + +This implementation has switchable behavior for the LWG defects +[#2682](https://wg21.cmeerw.net/lwg/issue2682), +[#2935](http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html#2935), +[#2936](http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html#2936) and +[#2937](http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html#2937). +The currently selected behavior (starting from v1.4.0) is following +[#2682](https://wg21.cmeerw.net/lwg/issue2682), [#2936](http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html#2936), +[#2937](http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html#2937) but +not following [#2935](http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html#2935), +as I feel it is a bug to report no error on a `create_directory()` or `create_directories()` +where a regular file of the same name prohibits the creation of a directory and forces +the user of those functions to double-check via `fs::is_directory` if it really worked. +The more intuitive approach to directory creation of treating a file with that name as an +error is also advocated by the newer paper +[WG21 P1164R0](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p1164r0.pdf), the revision +P1161R1 was agreed upon on Kona 2019 meeting [see merge](https://github.com/cplusplus/draft/issues/2703) +and GCC by now switched to following its proposal +([GCC #86910](https://gcc.gnu.org/bugzilla/show_bug.cgi?id=86910)). + +### Not Implemented on C++ before C++17 + +```cpp +// methods in ghc::filesystem::path: +path& operator+=(basic_string_view x); +int compare(basic_string_view s) const; +``` + +These are not implemented under C++11 and C++14, as there is no +`std::basic_string_view` available and I did want to keep this +implementation self-contained and not write a full C++17-upgrade for +C++11/14. Starting with v1.1.0 these are supported when compiling +`ghc::filesystem` under C++17 of C++20. + +Starting with v1.5.2 `ghc::filesystem` will try to allow the use of +`std::experimental::basic_string_view` where it detects is availability. +Additionally if you have a `basic_string_view` compatible c++11 +implementation it can be used instead of `std::basic_string_view` +by defining `GHC_HAS_CUSTOM_STRING_VIEW` and importing the +implementation into the `ghc::filesystem` namespace with: + +```cpp +namespace ghc { + namespace filesystem { + using my::basic_string_view; + } +} +``` + +before including the filesystem header. + +### Differences in API + +To not depend on any external third party libraries and still stay portable and +compact, 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 +wise and as being in UTF-8. The `std::u16string` will be seen as UTF-16 and `std::u32string` will be +seen as Unicode codepoints. Depending on the size of `std::wstring` characters, it will handle +`std::wstring` as being UTF-16 (e.g. Windows) or `char32_t` Unicode codepoints +(currently all other platforms). + +#### Differences of Specific Interfaces + +Starting with v1.5.0 `ghc::filesystem` is following the C++17 standard in +using `wchar_t` and `std::wstring` on Windows as the types internally used +for path representation. It is still possible to get the old behavior by defining +`GHC_WIN_DISABLE_WSTRING_STORAGE_TYPE` and get `filesystem::path::string_type` as +`std::string` and `filesystem::path::value_type` as `wchar_t`. + +If you need to call some Windows API, with v1.5.0 and above, simply +use the W-variant of the Windows-API call (e.g. `GetFileAttributesW(p.c_str())`). + +:information_source: **Note:** _When using the old behavior by defining +`GHC_WIN_DISABLE_WSTRING_STORAGE_TYPE`, use the `path::wstring()` member +(e.g. `GetFileAttributesW(p.wstring().c_str())`). This gives you the +Unicode variant independent of the `UNICODE` macro and makes sharing code +between Windows, Linux and macOS easier and works with `std::filesystem` and +`ghc::filesystem`._ + +```cpp +std::string path::u8string() const; +std::string path::generic_u8string() const; +vs. +std::u8string path::u8string() const; +std::u8string path::generic_u8string() const; +``` + +The return type of these two methods is depending on the used C++ standard +and if `GHC_FILESYSTEM_ENFORCE_CPP17_API` is defined. On C++11, C++14 and +C++17 or when `GHC_FILESYSTEM_ENFORCE_CPP17_API` is defined, the return +type is `std::string`, and on C++20 without the define it is `std::u8string`. + +### Differences in Behavior + +I created a wiki entry about quite a lot of [behavioral differences](https://github.com/gulrak/filesystem/wiki/Differences-to-Standard-Filesystem-Implementations) +between different `std::filesystem` implementations that could result in a +mention here, but this readme only tries to address the design choice +differences between `ghc::filesystem` and those. I try to update the wiki page +from time to time. + +Any additional observations are welcome! + +#### fs.path ([ref](https://en.cppreference.com/w/cpp/filesystem/path)) + +Since v1.5.0 the complete inner mechanics of this implementations `fs::path` +where changed to the _native_ format as the internal representation. +Creating any mixed slash `fs::path` object under Windows (e.g. with `"C:\foo/bar"`) +will lead clean path with `"C:\foo\bar"` via `native()` and `"C:/foo/bar"` via +`generic_string()` API. On all platforms redundant additional separators are +removed, even if this is not enforced by the standard and other implementations +mostly not do this. + +Additionally this implementation follows the standards suggestion to handle +posix paths of the form `"//host/path"` and USC path on windows also as having +a root-name (e.g. `"//host"`). The GCC implementation didn't choose to do that +while testing on Ubuntu 18.04 and macOS with GCC 8.1.0 or Clang 7.0.0. This difference +will show as warnings under `std::filesystem`. This leads to a change in the +algorithm described in the standard for `operator/=(path& p)` where any path +`p` with `p.is_absolute()` will degrade to an assignment, while this implementation +has the exception where `*this == *this.root_name()` and `p == preferred_separator` +a normal append will be done, to allow: + +```cpp +fs::path p1 = "//host/foo/bar/file.txt"; +fs::path p2; +for (auto p : p1) p2 /= p; +ASSERT(p1 == p2); +``` + +For all non-host-leading paths the behavior will match the one described by +the standard. + + +## Open Issues + +### Windows + +#### Symbolic Links on Windows + +As symbolic links on Windows, while being supported more or less since +Windows Vista (with some strict security constraints) and fully since some earlier +build of Windows 10, when "Developer Mode" is activated, are at time of writing +(2018) rarely used, still they are supported wiit th this implementation. + +#### Permissions + +The Windows ACL permission feature translates badly to the POSIX permission +bit mask used in the interface of C++17 filesystem. The permissions returned +in the `file_status` are therefore currently synthesized for the `user`-level +and copied to the `group`- and `other`-level. There is still some potential +for more interaction with the Windows permission system, but currently setting +or reading permissions with this implementation will most certainly not lead +to the expected behavior. + + +## Release Notes + +### [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) + +* Fix for [#125](https://github.com/gulrak/filesystem/issues/124), where + `fs::create_directories` on Windows no longer breaks on long filenames. + +### [v1.5.6](https://github.com/gulrak/filesystem/releases/tag/v1.5.6) + +* Fix for [#124](https://github.com/gulrak/filesystem/issues/124), + `ghc::filesystem` treated mounted folder/volumes erroneously as symlinks, + leading `fs::canonical` to fail on paths containing those. +* Fix for [#122](https://github.com/gulrak/filesystem/issues/122), incrementing + the `recursive_directory_iterator` will not try to enter dead symlinks. +* Fix for [#121](https://github.com/gulrak/filesystem/issues/121), on Windows + backend the `fs::remove` failed when the path pointed to a read-only entry, + see also ([microsoft/STL#1511](https://github.com/microsoft/STL/issues/1511)) + for the corresponding issue in `std::fs` on windows. +* Fix for [#119](https://github.com/gulrak/filesystem/issues/119), added missing + support for char16_t and char32_t and on C++20 char8_t literals. +* Pull request [#118](https://github.com/gulrak/filesystem/pull/118), when + running tests as root, disable tests that would not work. +* Pull request [#117](https://github.com/gulrak/filesystem/pull/117), added + checks to tests to detect the clang/libstdc++ combination. +* Fix for [#116](https://github.com/gulrak/filesystem/issues/116), internal + macro `GHC_NO_DIRENT_D_TYPE` allows os detection to support systems without + the `dirent.d_type` member, experimental first QNX compile support as + initial use case, fixed issue with filesystems returning DT_UNKNOWN + (e.g. reiserfs). +* Pull request [#115](https://github.com/gulrak/filesystem/pull/115), added + `string_view` support when clang with libstdc++ is detected. +* Fix for [#114](https://github.com/gulrak/filesystem/issues/114), for macOS + the pre-Catalina deployment target detection worked only if `` + was included before `` or ``/``. +* Fix for [#113](https://github.com/gulrak/filesystem/issues/113), the use of + standard chapter numbers was misleading since C++17 and C++20 `std::filesystem` + features are supported, and was replaced by the tag-like chapter names that + stay (mostly) consistent over the versions. + +### [v1.5.4](https://github.com/gulrak/filesystem/releases/tag/v1.5.4) + +* Pull request [#112](https://github.com/gulrak/filesystem/pull/112), lots + of cleanup work on the readme, thanks! +* Enhancement for [#111](https://github.com/gulrak/filesystem/issues/111), + further optimization of directory iteration, performance for + `recursive_directory_iterator` over large trees now somewhere between + libc++ and libstdc++. +* Enhancement for [#110](https://github.com/gulrak/filesystem/issues/110), + `ghc::filesystem` now has preliminary support for Cygwin. Changes where + made to allow the tests to compile and run successfully (tested with GCC + 10.2.0), feedback and additional PRs welcome as it is currently not + part of the CI configuration. +* Pull request [#109](https://github.com/gulrak/filesystem/pull/109), various + spelling errors in error messages and comments fixed. +* Pull request [#108](https://github.com/gulrak/filesystem/pull/108), old + style casts removed. +* Fix for [#107](https://github.com/gulrak/filesystem/issues/107), the error + handling for status calls was suppressing errors on symlink targets. +* Pull request [#106](https://github.com/gulrak/filesystem/pull/106), fixed + detection of AppleClang for compile options. +* Pull request [#105](https://github.com/gulrak/filesystem/pull/105), added + option `GHC_FILESYSTEM_BUILD_STD_TESTING` to override additional build of + `std::filesystem` versions of the tests for comparison and the possibility + to use `GHC_FILESYSTEM_TEST_COMPILE_FEATURES` to prefill the used compile + features defaulting to `CMAKE_CXX_COMPILE_FEATURES` when not given. + +### [v1.5.2](https://github.com/gulrak/filesystem/releases/tag/v1.5.2) + +* Enhancement [#104](https://github.com/gulrak/filesystem/issues/104), + on POSIX backend: optimized reuse of status information and reduced + `directory_entry` creation leads to about 20%-25% in tests with + `recursive_directory_iterator` over a larger directory tree. +* Pull request [#103](https://github.com/gulrak/filesystem/pull/103), `wchar_t` + was not in the list of supported char types on non-Windows backends. +* Pull request [#102](https://github.com/gulrak/filesystem/pull/102), improved + `string_view` support makes use of `` or `` + when available, and allows use of custom `basic_string_view` implementation + when defining `GHC_HAS_CUSTOM_STRING_VIEW` and importing the string view + into the `ghc::filesystem` namespace before including filesystem header. +* Pull request [#101](https://github.com/gulrak/filesystem/pull/101), fix for + [#100](https://github.com/gulrak/filesystem/issues/100), append and concat + type of operations on path called redundant conversions. +* Pull request [#98](https://github.com/gulrak/filesystem/pull/98), on older + linux variants (GCC 7/8), the comparison `std::filesystem` tests now link + with `-lrt` to avoid issues. +* Fix for [#97](https://github.com/gulrak/filesystem/issues/97), on BTRFS the + test case for `fs::hard_link_count` failed due to the filesystems behavior, + the test case was adapted to take that into account. +* Pull request [#96](https://github.com/gulrak/filesystem/pull/96), the export + attribute defines `GHC_FS_API` and `GHC_FS_API_CLASS` are now honored when when + set from outside to allow override of behavior. +* Fix for [#95](https://github.com/gulrak/filesystem/issues/95), the syntax for + disabling the deprecated warning in tests in MSVC was wrong. +* Pull request [#93](https://github.com/gulrak/filesystem/pull/93), now the + CMake configuration file is configured and part of the `make install` files. + +### [v1.5.0](https://github.com/gulrak/filesystem/releases/tag/v1.5.0) + +* Fix for [#91](https://github.com/gulrak/filesystem/issues/91), the way + the CMake build options `GHC_FILESYSTEM_BUILD_TESTING`, `GHC_FILESYSTEM_BUILD_EXAMPLES` + and `GHC_FILESYSTEM_WITH_INSTALL` where implemented, prohibited setting them + from a parent project when using this via `add_subdirectory`, this fix + allows to set them again. +* Major refactoring for [#90](https://github.com/gulrak/filesystem/issues/90), + the way, the Windows version of `fs::path` was originally created from the + POSIX based implementation was, by adaption of the incoming and outgoing + strings. This resulted in a mutable cache inside `fs::path`on Windows, that + was inherently not thread-safe, even for `const` methods. + To not add additional patches to a suboptimal solution, this time I reworked + the `path` code to now store _native_ path-representation. This changed a + lot of code, but when combined with `wchar_t` as `value_type` helped to avoid + lots of conversion for calls to Win-API.
+ As interfaces where changed, it had to be released in a new minor version. + The set of refactorings resulted in the following changes: + * `fs::path::native()` and `fs::path::c_str()` can now be `noexcept` as the + standard mandates + * On Windows `wchar_t` is now the default for `fs::path::value_type` and + `std::wstring` is the default for `fs::path::string_type`. + * This allows the implementation to call Win-API without allocating + conversions + * Thread-safety on `const` methods of `fs::path` is no longer an issue + * Some code could be simplified during this refactoring + * Automatic prefixing of long path on Windows can now be disabled with + defining `GHC_WIN_DISABLE_AUTO_PREFIXES`, for all other types of prefixes + or namespaces the behavior follows that of MSVC `std::filesystem::path` + * In case the old `char`/`std::string` based approach for Windows is still + needed, it can be activated with `GHC_WIN_DISABLE_WSTRING_STORAGE_TYPE` +* Enhancement for [#89](https://github.com/gulrak/filesystem/issues/89), `fs::file_status` + now supports `operator==` introduced in `std::filesystem` with C++20. +* Refactoring for [#88](https://github.com/gulrak/filesystem/issues/88), `fs::path::parent_path()` + had a performance issue, as it was still using a loop based approach to recreate + the parent from elements. This created lots of temporaries and was too slow + especially on long paths. + +### [v1.4.0](https://github.com/gulrak/filesystem/releases/tag/v1.4.0) + +* Enhancements for [#71](https://github.com/gulrak/filesystem/issues/71), when compiled with C++20: + * `char8_t` and `std::u8string` are supported where `Source` is the parameter type + * `fs::path::u8string()` and `fs::path::generic_u8string()` now return a `std::u8string` + * The _spaceship operator_ `<=>` is now supported for `fs::path` + * With the define `GHC_FILESYSTEM_ENFORCE_CPP17_API` `ghc::filesystem` will fall back + to the old `fs::path::u8string()` and `fs::path::generic_u8string()` API if preferred +* Bugfix for `fs::proximate(p, ec)` where the internal call to `fs::current_path()` was not + using the `error_code` variant, throwing possible exceptions instead of setting `ec`. +* Enhancement `LWG_2936_BEHAVIOUR` is now on by default. +* Some cleanup work to reduce preprocessor directives for better readability and remove unneeded + template specializations. + +### [v1.3.10](https://github.com/gulrak/filesystem/releases/tag/v1.3.10) + +* Fix for [#81](https://github.com/gulrak/filesystem/issues/81), fixed issues with + handling `Source` parameters that are string views. +* Fix for [#79](https://github.com/gulrak/filesystem/issues/79), the bit operations + for filesystem bitmasks that should be are now `constexpr`. + +### [v1.3.8](https://github.com/gulrak/filesystem/releases/tag/v1.3.8) + +* Refactoring for [#78](https://github.com/gulrak/filesystem/issues/78), the dynamic + switching helper includes are now using `__MAC_OS_X_VERSION_MIN_REQUIRED` to + ensure that `std::filesystem` is only selected on macOS if the deployment target is + at least Catalina. +* Bugfix for [#77](https://github.com/gulrak/filesystem/issues/77), the `directory_iterator` + and the `recursive_directory_iterator` had an issue with the `skip_permission_denied` + option, that leads to the inability to skip SIP protected folders on macOS. +* Enhancement for [#76](https://github.com/gulrak/filesystem/issues/76), `_MSVC_LANG` is + now used when available, additionally to `__cplusplus`, in the helping headers to + allow them to work even when `/Zc:__cplusplus` is not used. +* Bugfix for [#75](https://github.com/gulrak/filesystem/issues/75), NTFS reparse points + to mapped volumes where handled incorrect, leading to `false` on `fs::exists` or + not-found-errors on `fs::status`. Namespaced paths are not filtered anymore. + +### [v1.3.6](https://github.com/gulrak/filesystem/releases/tag/v1.3.6) + +* Pull request [#74](https://github.com/gulrak/filesystem/pull/74), on Windows symlink + evaluation used the wrong reparse struct information and was not handling the case + of relative paths well, thanks for the contribution. +* Refactoring for [#73](https://github.com/gulrak/filesystem/issues/73), enhanced performance + in path handling. the changes lead to much fewer path/string creations or copies, speeding + up large directory iteration or operations on many path instances. +* Bugfix for [#72](https://github.com/gulrak/filesystem/issues/72), the `TestAllocator` in + `filesystem_test.cpp` was completed to fulfill the requirements to build on CentOS 7 with + `devtoolset-9`. CentOS 7 and CentOS 8 are now part of the CI builds. +* Bugfix for [#70](https://github.com/gulrak/filesystem/issues/70), root names are now case + insensitive on Windows. This fix also adds the new behavior switch `LWG_2936_BEHAVIOUR` + that allows to enable post C++17 `fs::path::compare` behavior, where the comparison is as + if it was an element wise path comparison as described in + [LWG 2936](https://cplusplus.github.io/LWG/issue2936) and C++20 `[fs.path.compare]`. + It is default off in v1.3.6 and will be default starting from v1.4.0 as it changes ordering. + +### [v1.3.4](https://github.com/gulrak/filesystem/releases/tag/v1.3.4) + +* Pull request [#69](https://github.com/gulrak/filesystem/pull/69), use `wchar_t` versions of + `std::fstream` from `ghc::filesystem::fstream` wrappers on Windows if using GCC with libc++. +* Bugfix for [#68](https://github.com/gulrak/filesystem/issues/68), better handling of + permission issues for directory iterators when using `fs::directory_options::skip_permission_denied` + and initial support for compilation with emscripten. +* Refactoring for [#66](https://github.com/gulrak/filesystem/issues/63), unneeded shared_ptr guards + where removed and the file handles closed where needed to avoid unnecessary allocations. +* Bugfix for [#63](https://github.com/gulrak/filesystem/issues/63), fixed issues on Windows + with clang++ and C++17. +* Pull request [#62](https://github.com/gulrak/filesystem/pull/62), various fixes for + better Android support, thanks for the PR +* Pull request [#61](https://github.com/gulrak/filesystem/pull/61), `ghc::filesystem` now + supports use in projects with disabled exceptions. API signatures using exceptions for + error handling are not available in this mode, thanks for the PR (this resolves + [#60](https://github.com/gulrak/filesystem/issues/60) and + [#43](https://github.com/gulrak/filesystem/issues/43)) + +### [v1.3.2](https://github.com/gulrak/filesystem/releases/tag/v1.3.2) + +* Bugfix for [#58](https://github.com/gulrak/filesystem/issues/58), on MinGW the + compilation could fail with an error about an undefined `ERROR_FILE_TOO_LARGE` + constant. +* Bugfix for [#56](https://github.com/gulrak/filesystem/issues/58), `fs::lexically_relative` + didn't ignore trailing slash on the base parameter, thanks for PR + [#57](https://github.com/gulrak/filesystem/pull/57). +* Bugfix for [#55](https://github.com/gulrak/filesystem/issues/55), `fs::create_directories` + returned `true` when nothing needed to be created, because the directory already existed. +* Bugfix for [#54](https://github.com/gulrak/filesystem/issues/54), `error_code` + was not reset, if cached result was returned. +* Pull request [#53](https://github.com/gulrak/filesystem/pull/53), fix for wrong + handling of leading whitespace when reading `fs::path` from a stream. +* Pull request [#52](https://github.com/gulrak/filesystem/pull/52), an ARM Linux + target is now part of the CI infrastructure with the service of Drone CI. +* Pull request [#51](https://github.com/gulrak/filesystem/pull/51), FreeBSD is now + part of the CI infrastructure with the service of Cirrus CI. +* Pull request [#50](https://github.com/gulrak/filesystem/pull/50), adaptive cast to + `timespec` fields to avoid warnings. + +### [v1.3.0](https://github.com/gulrak/filesystem/releases/tag/v1.3.0) + +* **Important: `ghc::filesystem` is re-licensed from BSD-3-Clause to MIT license.** (see + [#47](https://github.com/gulrak/filesystem/issues/47)) +* Pull request [#46](https://github.com/gulrak/filesystem/pull/46), suppresses + unused parameter warning on Android. +* Bugfix for [#44](https://github.com/gulrak/filesystem/issues/44), fixes + for warnings from newer Xcode versions. + +### [v1.2.10](https://github.com/gulrak/filesystem/releases/tag/v1.2.10) + +* The Visual Studio 2019 compiler, GCC 9.2 and Clang 9.0 where added to the + CI configuration. +* Bugfix for [#41](https://github.com/gulrak/filesystem/issues/41), `fs::rename` + on Windows didn't replace an existing regular file as required by the standard, + but gave an error. New tests and a fix as provided in the issue was implemented. +* Bugfix for [#39](https://github.com/gulrak/filesystem/issues/39), for the + forwarding use via `fs_fwd.hpp` or `fs_std_fwd.hpp` there was a use of + `DWORD` in the forwarding part leading to an error if `Windows.h` was not + included before the header. The tests were changed to give an error in that + case too and the useage of `DWORD` was removed. +* Bugfix for [#38](https://github.com/gulrak/filesystem/issues/38), casting the + return value of `GetProcAddress` gave a warning with `-Wcast-function-type` + on MSYS2 and MinGW GCC 9 builds. + +### [v1.2.8](https://github.com/gulrak/filesystem/releases/tag/v1.2.8) + +* Pull request [#30](https://github.com/gulrak/filesystem/pull/30), the + `CMakeLists.txt` will automatically exclude building examples and tests when + used as submodule, the configuration options now use a prefixed name to + reduce risk of conflicts. +* Pull request [#24](https://github.com/gulrak/filesystem/pull/24), install + target now creates a `ghcFilesystemConfig.cmake` in + `${CMAKE_INSTALL_LIBDIR}/cmake/ghcFilesystem` for `find_package` that + exports a target as `ghcFilesystem::ghc_filesystem`. +* Pull request [#31](https://github.com/gulrak/filesystem/pull/31), fixes + `error: redundant redeclaration of 'constexpr' static data member` deprecation + warning in C++17 mode. +* Pull request [#32](https://github.com/gulrak/filesystem/pull/32), fixes + old-style-cast warnings. +* Pull request [#34](https://github.com/gulrak/filesystem/pull/34), fixes + [TOCTOU](https://en.wikipedia.org/wiki/Time-of-check_to_time-of-use) situation + on `fs::create_directories`, thanks for the PR! +* Feature [#35](https://github.com/gulrak/filesystem/issues/35), new CMake + option to add an install target `GHC_FILESYSTEM_WITH_INSTALL` that is + defaulted to OFF if `ghc::filesystem` is used via `add_subdirectory`. +* Bugfix for [#33](https://github.com/gulrak/filesystem/issues/33), fixes + an issue with `fs::path::lexically_normal()` that leaves a trailing separator + in case of a resulting path ending with `..` as last element. +* Bugfix for [#36](https://github.com/gulrak/filesystem/issues/36), warnings + on Xcode 11.2 due to unhelpful references in path element iteration. + +### [v1.2.6](https://github.com/gulrak/filesystem/releases/tag/v1.2.6) + +* Pull request [#23](https://github.com/gulrak/filesystem/pull/23), tests and + examples can now be disabled in CMake via setting `BUILD_TESTING` and + `BUILD_EXAMPLES` to `NO`, `OFF` or `FALSE`. +* Pull request [#25](https://github.com/gulrak/filesystem/pull/25), + missing specialization for construction from `std::string_view` when + available was added. +* Additional test case when `std::string_view` is available. +* Bugfix for [#27](https://github.com/gulrak/filesystem/issues/27), the + `fs::path::preferred_separator` declaration was not compiling on pre + C++17 compilers and no test accessed it, to show the problem. Fixed + it to an construction C++11 compiler should accept and added a test that + is successful on all combinations tested. +* Bugfix for [#29](https://github.com/gulrak/filesystem/issues/29), stricter + warning settings where chosen and resulting warnings where fixed. + +### [v1.2.4](https://github.com/gulrak/filesystem/releases/tag/v1.2.4) + +* Enabled stronger warning switches and resulting fixed issues on GCC and MinGW +* Bugfix for #22, the `fs::copy_options` where not forwarded from `fs::copy` to + `fs::copy_file` in one of the cases. + +### [v1.2.2](https://github.com/gulrak/filesystem/releases/tag/v1.2.2) + +* Fix for ([#21](https://github.com/gulrak/filesystem/pull/21)), when compiling + on Alpine Linux with musl instead of glibc, the wrong `strerror_r` signature + was expected. The complex preprocessor define mix was dropped in favor of + the usual dispatch by overloading a unifying wrapper. + +### [v1.2.0](https://github.com/gulrak/filesystem/releases/tag/v1.2.0) + +* Added MinGW 32/64 and Visual Studio 2015 builds to the CI configuration. +* Fixed additional compilation issues on MinGW. +* Pull request ([#13](https://github.com/gulrak/filesystem/pull/13)), set + minimum required CMake version to 3.7.2 (as in Debian 8). +* Pull request ([#14](https://github.com/gulrak/filesystem/pull/14)), added + support for a make install target. +* Bugfix for ([#15](https://github.com/gulrak/filesystem/issues/15)), the + forward/impl way of using `ghc::filesystem` missed a `` include + in the windows case. +* Bugfix for ([#16](https://github.com/gulrak/filesystem/issues/16)), + VS2019 didn't like the old size dispatching in the utf8 decoder, so it + was changed to a sfinae based approach. +* New feature ([#17](https://github.com/gulrak/filesystem/issues/17)), optional + support for standard conforming `wchar_t/std::wstring` interface when + compiling on Windows with defined `GHC_WIN_WSTRING_STRING_TYPE`, this is + default when using the `ghc/fs_std*.hpp` header, to enhance compatibility. +* New feature ([#18](https://github.com/gulrak/filesystem/issues/18)), optional + filesystem exceptions/errors on Unicode errors with defined + `GHC_RAISE_UNICODE_ERRORS` (instead of replacing invalid code points or + UTF-8 encoding errors with the replacement character `U+FFFD`). +* Pull request ([#20](https://github.com/gulrak/filesystem/pull/20)), fix for + file handle leak in `fs::copy_file`. +* Coverage now checked in CI (~95% line coverage). + +### [v1.1.4](https://github.com/gulrak/filesystem/releases/tag/v1.1.4) + +* Additional Bugfix for ([#12](https://github.com/gulrak/filesystem/issues/12)), + error in old unified `readdir/readdir_r` code of `fs::directory_iterator`; + as `readdir_r` is now deprecated, I decided to drop it and the resulting + code is much easier, shorter and due to more refactoring faster +* Fix for crashing unit tests against MSVC C++17 `std::filesystem` +* Travis-CI now additionally test with Xcode 10.2 on macOS +* Some minor refactorings + +### [v1.1.2](https://github.com/gulrak/filesystem/releases/tag/v1.1.2) + +* Bugfix for ([#11](https://github.com/gulrak/filesystem/issues/11)), + `fs::path::lexically_normal()` had some issues with `".."`-sequences. +* Bugfix for ([#12](https://github.com/gulrak/filesystem/issues/12)), + `fs::recursive_directory_iterator` could run into endless loops, + the methods depth() and pop() had issues and the copy behavior and + `input_iterator_tag` conformance was broken, added tests +* Restructured some CMake code into a macro to ease the support for + C++17 `std::filesystem` builds of tests and examples for interoperability + checks. +* Some fixes on Windows tests to ease interoperability test runs. +* Reduced noise on `fs::weakly_canonical()` tests against `std::fs` +* Added simple `du` example showing the `recursive_directory_iterator` + used to add the sizes of files in a directory tree. +* Added error checking in `fs::file_time_type` test helpers +* `fs::copy()` now conforms LWG #2682, disallowing the use of + `copy_option::create_symlinks' to be used on directories + +### [v1.1.0](https://github.com/gulrak/filesystem/releases/tag/v1.1.0) + +* Restructuring of the project directory. The header files are now using + `hpp` as extension to be marked as c++ and they where moved to + `include/ghc/` to be able to include by `` as the + former include name might have been to generic and conflict with other + files. +* Better CMake support: `ghc::filesystem` now can be used as a submodul + and added with `add_subdirectory` and will export itself as `ghc_filesystem` + target. To use it, only `target_link_libraries(your-target ghc_filesystem)` + is needed and the include directories will be set so `#include ` + will be a valid directive. + Still you can simply only add the header file to you project and include it + from there. +* Enhancement ([#10](https://github.com/gulrak/filesystem/issues/10)), + support for separation of implementation and forwarded api: Two + additional simple includes are added, that can be used to forward + `ghc::filesystem` declarations (`fs_fwd.hpp`) and to wrap the + implementation into a single cpp (`fs_impl.hpp`) +* The `std::basic_string_view` variants of the `fs::path` api are + now supported when compiling with C++17. +* Added CI integration for Travis-CI and Appveyor. +* Fixed MinGW compilation issues. +* Added long filename support for Windows. + +### [v1.0.10](https://github.com/gulrak/filesystem/releases/tag/v1.0.10) + +* Bugfix for ([#9](https://github.com/gulrak/filesystem/issues/9)), added + missing return statement to `ghc::filesystem::path::generic_string()` +* Added checks to hopefully better compile against Android NDK. There where + no tests run yet, so feedback is needed to actually call this supported. +* `filesystem.h` was renamed `filesystem.hpp` to better reflect that it is + a c++ language header. + +### [v1.0.8](https://github.com/gulrak/filesystem/releases/tag/v1.0.8) + +* Bugfix for ([#6](https://github.com/gulrak/filesystem/issues/6)), where + `ghc::filesystem::remove()` and `ghc::filesystem::remove_all()` both are + now able to remove a single file and both will not raise an error if the + path doesn't exist. +* Merged pull request ([#7](https://github.com/gulrak/filesystem/pull/7)), + a typo leading to setting error code instead of comparing it in + `ghc::filesystem::remove()` under Windows. +* Bugfix for (([#8](https://github.com/gulrak/filesystem/issues/8)), the + Windows version of `ghc::filesystem::directory_iterator` now releases + resources when reaching `end()` like the POSIX one does. + + +### [v1.0.6](https://github.com/gulrak/filesystem/releases/tag/v1.0.6) + +* Bugfix for ([#4](https://github.com/gulrak/filesystem/issues/4)), missing error_code + propagation in `ghc::filesystem::copy()` and `ghc::filesystem::remove_all` fixed. +* Bugfix for ([#5](https://github.com/gulrak/filesystem/issues/5)), added missing std + namespace in `ghc::filesystem::recursive_directory_iterator::difference_type`. + +### [v1.0.4](https://github.com/gulrak/filesystem/releases/tag/v1.0.4) + +* Bugfix for ([#3](https://github.com/gulrak/filesystem/issues/3)), fixed missing inlines + and added test to ensure including into multiple implementation files works as expected. +* Building tests with `-Wall -Wextra -Werror` and fixed resulting issues. + +### [v1.0.2](https://github.com/gulrak/filesystem/releases/tag/v1.0.2) + +* Updated catch2 to v2.4.0. +* Refactored `fs.op.permissions` test to work with all tested `std::filesystem` + implementations (gcc, clang, msvc++). +* Added helper class `ghc::filesystem::u8arguments` as `argv` converter, to + help follow the UTF-8 path on windows. Simply instantiate it with `argc` and + `argv` and it will fetch the Unicode version of the command line and convert + it to UTF-8. The destructor reverts the change. +* Added `examples` folder with hopefully some usefull example usage. Examples are + tested (and build) with `ghc::filesystem` and C++17 `std::filesystem` when + available. +* Starting with this version, only even patch level versions will be tagged and + odd patch levels mark in-between non-stable wip states. +* Tests can now also be run against MS version of `std::filesystem` for comparison. +* Added missing `fstream` include. +* Removed non-conforming C99 `timespec`/`timeval` usage. +* Fixed some integer type mismatches that could lead to warnings. +* Fixed `chrono` conversion issues in test and example on clang 7.0.0. + +### [v1.0.1](https://github.com/gulrak/filesystem/releases/tag/v1.0.1) + +* Bugfix: `ghc::filesystem::canonical` now sees empty path as non-existant and reports + an error. Due to this `ghc::filesystem::weakly_canonical` now returns relative + paths for non-existant argument paths. ([#1](https://github.com/gulrak/filesystem/issues/1)) +* Bugfix: `ghc::filesystem::remove_all` now also counts directories removed ([#2](https://github.com/gulrak/filesystem/issues/2)) +* Bugfix: `recursive_directory_iterator` tests didn't respect equality domain issues + and dereferencapable constraints, leading to fails on `std::filesystem` tests. +* Bugfix: Some `noexcept` tagged methods and functions could indirectly throw exceptions + due to UFT-8 decoding issues. +* `std_filesystem_test` is now also generated if LLVM/clang 7.0.0 is found. + + +### [v1.0.0](https://github.com/gulrak/filesystem/releases/tag/v1.0.0) + +This was the first public release version. It implements the full range of +C++17 `std::filesystem`, as far as possible without other C++17 dependencies. + diff --git a/src/external/filesystem/include/ghc/filesystem.hpp b/src/external/filesystem/include/ghc/filesystem.hpp new file mode 100644 index 00000000..45f2c03b --- /dev/null +++ b/src/external/filesystem/include/ghc/filesystem.hpp @@ -0,0 +1,6065 @@ +//--------------------------------------------------------------------------------------- +// +// ghc::filesystem - A C++17-like filesystem implementation for C++11/C++14/C++17/C++20 +// +//--------------------------------------------------------------------------------------- +// +// Copyright (c) 2018, Steffen Schümann +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// 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() && (!defined(__MAC_OS_X_VERSION_MIN_REQUIRED) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 101500) +// #define GHC_USE_STD_FS +// #include +// namespace fs = std::filesystem; +// #endif +// #endif +// #ifndef GHC_USE_STD_FS +// #include +// namespace fs = ghc::filesystem; +// #endif +// +//--------------------------------------------------------------------------------------- +#ifndef GHC_FILESYSTEM_H +#define GHC_FILESYSTEM_H + +// #define BSD manifest constant only in +// sys/param.h +#ifndef _WIN32 +#include +#endif + +#ifndef GHC_OS_DETECTED +#if defined(__APPLE__) && defined(__MACH__) +#define GHC_OS_MACOS +#elif defined(__linux__) +#define GHC_OS_LINUX +#if defined(__ANDROID__) +#define GHC_OS_ANDROID +#endif +#elif defined(_WIN64) +#define GHC_OS_WINDOWS +#define GHC_OS_WIN64 +#elif defined(_WIN32) +#define GHC_OS_WINDOWS +#define GHC_OS_WIN32 +#elif defined(__CYGWIN__) +#define GHC_OS_CYGWIN +#elif defined(__sun) && defined(__SVR4) +#define GHC_OS_SOLARIS +#elif defined(__svr4__) +#define GHC_OS_SYS5R4 +#elif defined(BSD) +#define GHC_OS_BSD +#elif defined(__EMSCRIPTEN__) +#define GHC_OS_WEB +#include +#elif defined(__QNX__) +#define GHC_OS_QNX +#else +#error "Operating system currently not supported!" +#endif +#define GHC_OS_DETECTED +#if (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) +#if _MSVC_LANG == 201703L +#define GHC_FILESYSTEM_RUNNING_CPP17 +#else +#define GHC_FILESYSTEM_RUNNING_CPP20 +#endif +#elif (defined(__cplusplus) && __cplusplus >= 201703L) +#if __cplusplus == 201703L +#define GHC_FILESYSTEM_RUNNING_CPP17 +#else +#define GHC_FILESYSTEM_RUNNING_CPP20 +#endif +#endif +#endif + +#if defined(GHC_FILESYSTEM_IMPLEMENTATION) +#define GHC_EXPAND_IMPL +#define GHC_INLINE +#ifdef GHC_OS_WINDOWS +#ifndef GHC_FS_API +#define GHC_FS_API +#endif +#ifndef GHC_FS_API_CLASS +#define GHC_FS_API_CLASS +#endif +#else +#ifndef GHC_FS_API +#define GHC_FS_API __attribute__((visibility("default"))) +#endif +#ifndef GHC_FS_API_CLASS +#define GHC_FS_API_CLASS __attribute__((visibility("default"))) +#endif +#endif +#elif defined(GHC_FILESYSTEM_FWD) +#define GHC_INLINE +#ifdef GHC_OS_WINDOWS +#ifndef GHC_FS_API +#define GHC_FS_API extern +#endif +#ifndef GHC_FS_API_CLASS +#define GHC_FS_API_CLASS +#endif +#else +#ifndef GHC_FS_API +#define GHC_FS_API extern +#endif +#ifndef GHC_FS_API_CLASS +#define GHC_FS_API_CLASS +#endif +#endif +#else +#define GHC_EXPAND_IMPL +#define GHC_INLINE inline +#ifndef GHC_FS_API +#define GHC_FS_API +#endif +#ifndef GHC_FS_API_CLASS +#define GHC_FS_API_CLASS +#endif +#endif + +#ifdef GHC_EXPAND_IMPL + +#ifdef GHC_OS_WINDOWS +#include +// additional includes +#include +#include +#include +#include +#include +#else +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef GHC_OS_ANDROID +#include +#if __ANDROID_API__ < 12 +#include +#endif +#include +#define statvfs statfs +#else +#include +#endif +#ifdef GHC_OS_CYGWIN +#include +#endif +#if !defined(__ANDROID__) || __ANDROID_API__ >= 26 +#include +#endif +#endif +#ifdef GHC_OS_MACOS +#include +#endif + +#if defined(__cpp_impl_three_way_comparison) && defined(__has_include) +#if __has_include() +#define GHC_HAS_THREEWAY_COMP +#include +#endif +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#else // GHC_EXPAND_IMPL + +#if defined(__cpp_impl_three_way_comparison) && defined(__has_include) +#if __has_include() +#define GHC_HAS_THREEWAY_COMP +#include +#endif +#endif +#include +#include +#include +#include +#include +#include +#include +#ifdef GHC_OS_WINDOWS +#include +#endif +#endif // GHC_EXPAND_IMPL + +// After standard library includes. +// Standard library support for std::string_view. +#if defined(__cpp_lib_string_view) +#define GHC_HAS_STD_STRING_VIEW +#elif defined(_LIBCPP_VERSION) && (_LIBCPP_VERSION >= 4000) && (__cplusplus >= 201402) +#define GHC_HAS_STD_STRING_VIEW +#elif defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE >= 7) && (__cplusplus >= 201703) +#define GHC_HAS_STD_STRING_VIEW +#elif defined(_MSC_VER) && (_MSC_VER >= 1910 && _MSVC_LANG >= 201703) +#define GHC_HAS_STD_STRING_VIEW +#endif + +// Standard library support for std::experimental::string_view. +#if defined(_LIBCPP_VERSION) && (_LIBCPP_VERSION >= 3700 && _LIBCPP_VERSION < 7000) && (__cplusplus >= 201402) +#define GHC_HAS_STD_EXPERIMENTAL_STRING_VIEW +#elif defined(__GNUC__) && (((__GNUC__ == 4) && (__GNUC_MINOR__ >= 9)) || (__GNUC__ > 4)) && (__cplusplus >= 201402) +#define GHC_HAS_STD_EXPERIMENTAL_STRING_VIEW +#elif defined(__GLIBCXX__) && defined(_GLIBCXX_USE_DUAL_ABI) && (__cplusplus >= 201402) +// macro _GLIBCXX_USE_DUAL_ABI is always defined in libstdc++ from gcc-5 and newer +#define GHC_HAS_STD_EXPERIMENTAL_STRING_VIEW +#endif + +#if defined(GHC_HAS_STD_STRING_VIEW) +#include +#elif defined(GHC_HAS_STD_EXPERIMENTAL_STRING_VIEW) +#include +#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): +//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// Enforce C++17 API where possible when compiling for C++20, handles the following cases: +// * fs::path::u8string() returns std::string instead of std::u8string +// #define GHC_FILESYSTEM_ENFORCE_CPP17_API +//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// LWG #2682 disables the since then invalid use of the copy option create_symlinks on directories +// configure LWG conformance () +#define LWG_2682_BEHAVIOUR +//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// LWG #2395 makes crate_directory/create_directories not emit an error if there is a regular +// file with that name, it is superseded by P1164R1, so only activate if really needed +// #define LWG_2935_BEHAVIOUR +//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// LWG #2936 enables new element wise (more expensive) path comparison +// * if this->root_name().native().compare(p.root_name().native()) != 0 return result +// * 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 +// if all comparisons are 0 (on Windows this implementation does case-insensitive root_name() +// comparison) +#define LWG_2936_BEHAVIOUR +//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// LWG #2937 enforces that fs::equivalent emits an error, if !fs::exists(p1)||!exists(p2) +#define LWG_2937_BEHAVIOUR +//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// 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 +// as UTF-8 encoded. With this define you can enforce the old behavior on Windows, using +// 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 +// homogeneous in a multi-platform project. +// #define GHC_WIN_DISABLE_WSTRING_STORAGE_TYPE +//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// Raise errors/exceptions when invalid unicode codepoints or UTF-8 sequences are found, +// instead of replacing them with the unicode replacement character (U+FFFD). +// #define GHC_RAISE_UNICODE_ERRORS +//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// Automatic prefix windows path with "\\?\" if they would break the MAX_PATH length. +// instead of replacing them with the unicode replacement character (U+FFFD). +#ifndef GHC_WIN_DISABLE_AUTO_PREFIXES +#define GHC_WIN_AUTO_PREFIX_LONG_PATH +#endif // GHC_WIN_DISABLE_AUTO_PREFIXES +//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +// ghc::filesystem version in decimal (major * 10000 + minor * 100 + patch) +#define GHC_FILESYSTEM_VERSION 10512L + +// TinyUSDZ mod +#ifndef GHC_NO_EXCEPTION +#define GHC_NO_EXCEPTION +#endif + +#if defined(GHC_NO_EXCEPTION) + +// Should go here. +#if defined(GHC_RAISE_UNICODE_ERRORS) +#error "Can't raise unicode errors with exception support disabled" +#endif + +#else + +#if !defined(GHC_WITH_EXCEPTIONS) && (defined(__EXCEPTIONS) || defined(__cpp_exceptions) || defined(_CPPUNWIND)) +#define GHC_WITH_EXCEPTIONS +#endif +#if !defined(GHC_WITH_EXCEPTIONS) && defined(GHC_RAISE_UNICODE_ERRORS) +#error "Can't raise unicode errors with exception support disabled" +#endif + +#endif + +namespace ghc { +namespace filesystem { + +#if defined(GHC_HAS_CUSTOM_STRING_VIEW) +#define GHC_WITH_STRING_VIEW +#elif defined(GHC_HAS_STD_STRING_VIEW) +#define GHC_WITH_STRING_VIEW +using std::basic_string_view; +#elif defined(GHC_HAS_STD_EXPERIMENTAL_STRING_VIEW) +#define GHC_WITH_STRING_VIEW +using std::experimental::basic_string_view; +#endif + +// temporary existing exception type for yet unimplemented parts +class GHC_FS_API_CLASS not_implemented_exception : public std::logic_error +{ +public: + not_implemented_exception() + : std::logic_error("function not implemented yet.") + { + } +}; + +template +class path_helper_base +{ +public: + using value_type = char_type; +#ifdef GHC_OS_WINDOWS + static constexpr value_type preferred_separator = '\\'; +#else + static constexpr value_type preferred_separator = '/'; +#endif +}; + +#if __cplusplus < 201703L +template +constexpr char_type path_helper_base::preferred_separator; +#endif + +#ifdef GHC_OS_WINDOWS +class path; +namespace detail { +bool has_executable_extension(const path& p); +} +#endif + +// [fs.class.path] class path +class GHC_FS_API_CLASS path +#if defined(GHC_OS_WINDOWS) && !defined(GHC_WIN_DISABLE_WSTRING_STORAGE_TYPE) +#define GHC_USE_WCHAR_T +#define GHC_NATIVEWP(p) p.c_str() +#define GHC_PLATFORM_LITERAL(str) L##str + : private path_helper_base +{ +public: + using path_helper_base::value_type; +#else +#define GHC_NATIVEWP(p) p.wstring().c_str() +#define GHC_PLATFORM_LITERAL(str) str + : private path_helper_base +{ +public: + using path_helper_base::value_type; +#endif + using string_type = std::basic_string; + using path_helper_base::preferred_separator; + + // [fs.enum.path.format] enumeration format + /// The path format in which the constructor argument is given. + enum format { + generic_format, ///< The generic format, internally used by + ///< ghc::filesystem::path with slashes + native_format, ///< The format native to the current platform this code + ///< is build for + auto_format, ///< Try to auto-detect the format, fallback to native + }; + + template + struct _is_basic_string : std::false_type + { + }; + template + struct _is_basic_string> : std::true_type + { + }; + template + struct _is_basic_string, std::allocator>> : std::true_type + { + }; +#ifdef GHC_WITH_STRING_VIEW + template + struct _is_basic_string> : std::true_type + { + }; + template + struct _is_basic_string>> : std::true_type + { + }; +#endif + + template + using path_type = typename std::enable_if::value, path>::type; + template +#if defined(__cpp_lib_char8_t) && !defined(GHC_FILESYSTEM_ENFORCE_CPP17_API) + using path_from_string = + typename std::enable_if<_is_basic_string::value || std::is_same::type>::value || std::is_same::type>::value || std::is_same::type>::value || + std::is_same::type>::value || std::is_same::type>::value || std::is_same::type>::value || + std::is_same::type>::value || std::is_same::type>::value || std::is_same::type>::value || + std::is_same::type>::value, + path>::type; + template + using path_type_EcharT = typename std::enable_if::value || std::is_same::value || std::is_same::value || std::is_same::value || std::is_same::value, path>::type; +#else + using path_from_string = + typename std::enable_if<_is_basic_string::value || std::is_same::type>::value || std::is_same::type>::value || + std::is_same::type>::value || std::is_same::type>::value || std::is_same::type>::value || + std::is_same::type>::value || std::is_same::type>::value || std::is_same::type>::value, + path>::type; + template + using path_type_EcharT = typename std::enable_if::value || std::is_same::value || std::is_same::value || std::is_same::value, path>::type; +#endif + // [fs.path.construct] constructors and destructor + path() noexcept; + path(const path& p); + path(path&& p) noexcept; + path(string_type&& source, format fmt = auto_format); + template > + path(const Source& source, format fmt = auto_format); + template + path(InputIterator first, InputIterator last, format fmt = auto_format); +#ifdef GHC_WITH_EXCEPTIONS + template > + path(const Source& source, const std::locale& loc, format fmt = auto_format); + template + path(InputIterator first, InputIterator last, const std::locale& loc, format fmt = auto_format); +#endif + ~path(); + + // [fs.path.assign] assignments + path& operator=(const path& p); + path& operator=(path&& p) noexcept; + path& operator=(string_type&& source); + path& assign(string_type&& source); + template + path& operator=(const Source& source); + template + path& assign(const Source& source); + template + path& assign(InputIterator first, InputIterator last); + + // [fs.path.append] appends + path& operator/=(const path& p); + template + path& operator/=(const Source& source); + template + path& append(const Source& source); + template + path& append(InputIterator first, InputIterator last); + + // [fs.path.concat] concatenation + path& operator+=(const path& x); + path& operator+=(const string_type& x); +#ifdef GHC_WITH_STRING_VIEW + path& operator+=(basic_string_view x); +#endif + path& operator+=(const value_type* x); + path& operator+=(value_type x); + template + path_from_string& operator+=(const Source& x); + template + path_type_EcharT& operator+=(EcharT x); + template + path& concat(const Source& x); + template + path& concat(InputIterator first, InputIterator last); + + // [fs.path.modifiers] modifiers + void clear() noexcept; + path& make_preferred(); + path& remove_filename(); + path& replace_filename(const path& replacement); + path& replace_extension(const path& replacement = path()); + void swap(path& rhs) noexcept; + + // [fs.path.native.obs] native format observers + const string_type& native() const noexcept; + const value_type* c_str() const noexcept; + operator string_type() const; + template , class Allocator = std::allocator> + std::basic_string string(const Allocator& a = Allocator()) const; + std::string string() const; + std::wstring wstring() const; +#if defined(__cpp_lib_char8_t) && !defined(GHC_FILESYSTEM_ENFORCE_CPP17_API) + std::u8string u8string() const; +#else + std::string u8string() const; +#endif + std::u16string u16string() const; + std::u32string u32string() const; + + // [fs.path.generic.obs] generic format observers + template , class Allocator = std::allocator> + std::basic_string generic_string(const Allocator& a = Allocator()) const; + std::string generic_string() const; + std::wstring generic_wstring() const; +#if defined(__cpp_lib_char8_t) && !defined(GHC_FILESYSTEM_ENFORCE_CPP17_API) + std::u8string generic_u8string() const; +#else + std::string generic_u8string() const; +#endif + std::u16string generic_u16string() const; + std::u32string generic_u32string() const; + + // [fs.path.compare] compare + int compare(const path& p) const noexcept; + int compare(const string_type& s) const; +#ifdef GHC_WITH_STRING_VIEW + int compare(basic_string_view s) const; +#endif + int compare(const value_type* s) const; + + // [fs.path.decompose] decomposition + path root_name() const; + path root_directory() const; + path root_path() const; + path relative_path() const; + path parent_path() const; + path filename() const; + path stem() const; + path extension() const; + + // [fs.path.query] query + bool empty() const noexcept; + bool has_root_name() const; + bool has_root_directory() const; + bool has_root_path() const; + bool has_relative_path() const; + bool has_parent_path() const; + bool has_filename() const; + bool has_stem() const; + bool has_extension() const; + bool is_absolute() const; + bool is_relative() const; + + // [fs.path.gen] generation + path lexically_normal() const; + path lexically_relative(const path& base) const; + path lexically_proximate(const path& base) const; + + // [fs.path.itr] iterators + class iterator; + using const_iterator = iterator; + iterator begin() const; + iterator end() const; + +private: + using impl_value_type = value_type; + using impl_string_type = std::basic_string; + friend class directory_iterator; + void append_name(const value_type* name); + static constexpr impl_value_type generic_separator = '/'; + template + class input_iterator_range + { + public: + typedef InputIterator iterator; + typedef InputIterator const_iterator; + typedef typename InputIterator::difference_type difference_type; + + input_iterator_range(const InputIterator& first, const InputIterator& last) + : _first(first) + , _last(last) + { + } + + InputIterator begin() const { return _first; } + InputIterator end() const { return _last; } + + private: + InputIterator _first; + InputIterator _last; + }; + friend void swap(path& lhs, path& rhs) noexcept; + friend size_t hash_value(const path& p) noexcept; + friend path canonical(const path& p, std::error_code& ec); + friend bool create_directories(const path& p, std::error_code& ec) noexcept; + string_type::size_type root_name_length() const noexcept; + void postprocess_path_with_format(format fmt); + void check_long_path(); + impl_string_type _path; +#ifdef GHC_OS_WINDOWS + void handle_prefixes(); + friend bool detail::has_executable_extension(const path& p); +#ifdef GHC_WIN_AUTO_PREFIX_LONG_PATH + string_type::size_type _prefixLength{0}; +#else // GHC_WIN_AUTO_PREFIX_LONG_PATH + static const string_type::size_type _prefixLength{0}; +#endif // GHC_WIN_AUTO_PREFIX_LONG_PATH +#else + static const string_type::size_type _prefixLength{0}; +#endif +}; + +// [fs.path.nonmember] path non-member functions +GHC_FS_API void swap(path& lhs, path& rhs) noexcept; +GHC_FS_API size_t hash_value(const path& p) noexcept; +#ifdef GHC_HAS_THREEWAY_COMP +GHC_FS_API std::strong_ordering operator<=>(const path& lhs, const path& rhs) noexcept; +#endif +GHC_FS_API bool operator==(const path& lhs, const path& rhs) noexcept; +GHC_FS_API bool operator!=(const path& lhs, const path& rhs) noexcept; +GHC_FS_API bool operator<(const path& lhs, const path& rhs) noexcept; +GHC_FS_API bool operator<=(const path& lhs, const path& rhs) noexcept; +GHC_FS_API bool operator>(const path& lhs, const path& rhs) noexcept; +GHC_FS_API bool operator>=(const path& lhs, const path& rhs) noexcept; +GHC_FS_API path operator/(const path& lhs, const path& rhs); + +// [fs.path.io] path inserter and extractor +template +std::basic_ostream& operator<<(std::basic_ostream& os, const path& p); +template +std::basic_istream& operator>>(std::basic_istream& is, path& p); + +// [pfs.path.factory] path factory functions +template > +#if defined(__cpp_lib_char8_t) && !defined(GHC_FILESYSTEM_ENFORCE_CPP17_API) +[[deprecated("use ghc::filesystem::path::path() with std::u8string instead")]] +#endif +path u8path(const Source& source); +template +#if defined(__cpp_lib_char8_t) && !defined(GHC_FILESYSTEM_ENFORCE_CPP17_API) +[[deprecated("use ghc::filesystem::path::path() with std::u8string instead")]] +#endif +path u8path(InputIterator first, InputIterator last); + +// [fs.class.filesystem_error] class filesystem_error +class GHC_FS_API_CLASS filesystem_error : public std::system_error +{ +public: + filesystem_error(const std::string& what_arg, std::error_code ec); + filesystem_error(const std::string& what_arg, const path& p1, std::error_code ec); + filesystem_error(const std::string& what_arg, const path& p1, const path& p2, std::error_code ec); + const path& path1() const noexcept; + const path& path2() const noexcept; + const char* what() const noexcept override; + +private: + std::string _what_arg; + std::error_code _ec; + path _p1, _p2; +}; + +class GHC_FS_API_CLASS path::iterator +{ +public: + using value_type = const path; + using difference_type = std::ptrdiff_t; + using pointer = const path*; + using reference = const path&; + using iterator_category = std::bidirectional_iterator_tag; + + iterator(); + iterator(const path& p, const impl_string_type::const_iterator& pos); + iterator& operator++(); + iterator operator++(int); + iterator& operator--(); + iterator operator--(int); + bool operator==(const iterator& other) const; + bool operator!=(const iterator& other) const; + reference operator*() const; + pointer operator->() const; + +private: + friend class path; + impl_string_type::const_iterator increment(const impl_string_type::const_iterator& pos) const; + impl_string_type::const_iterator decrement(const impl_string_type::const_iterator& pos) const; + void updateCurrent(); + impl_string_type::const_iterator _first; + impl_string_type::const_iterator _last; + impl_string_type::const_iterator _prefix; + impl_string_type::const_iterator _root; + impl_string_type::const_iterator _iter; + path _current; +}; + +struct space_info +{ + uintmax_t capacity; + uintmax_t free; + uintmax_t available; +}; + +// [fs.enum] enumerations +// [fs.enum.file_type] +enum class file_type { + none, + not_found, + regular, + directory, + symlink, + block, + character, + fifo, + socket, + unknown, +}; + +// [fs.enum.perms] +enum class perms : uint16_t { + none = 0, + + owner_read = 0400, + owner_write = 0200, + owner_exec = 0100, + owner_all = 0700, + + group_read = 040, + group_write = 020, + group_exec = 010, + group_all = 070, + + others_read = 04, + others_write = 02, + others_exec = 01, + others_all = 07, + + all = 0777, + set_uid = 04000, + set_gid = 02000, + sticky_bit = 01000, + + mask = 07777, + unknown = 0xffff +}; + +// [fs.enum.perm.opts] +enum class perm_options : uint16_t { + replace = 3, + add = 1, + remove = 2, + nofollow = 4, +}; + +// [fs.enum.copy.opts] +enum class copy_options : uint16_t { + none = 0, + + skip_existing = 1, + overwrite_existing = 2, + update_existing = 4, + + recursive = 8, + + copy_symlinks = 0x10, + skip_symlinks = 0x20, + + directories_only = 0x40, + create_symlinks = 0x80, +#ifndef GHC_OS_WEB + create_hard_links = 0x100 +#endif +}; + +// [fs.enum.dir.opts] +enum class directory_options : uint16_t { + none = 0, + follow_directory_symlink = 1, + skip_permission_denied = 2, +}; + +// [fs.class.file_status] class file_status +class GHC_FS_API_CLASS file_status +{ +public: + // [fs.file_status.cons] constructors and destructor + file_status() noexcept; + explicit file_status(file_type ft, perms prms = perms::unknown) noexcept; + file_status(const file_status&) noexcept; + file_status(file_status&&) noexcept; + ~file_status(); + // assignments: + file_status& operator=(const file_status&) noexcept; + file_status& operator=(file_status&&) noexcept; + // [fs.file_status.mods] modifiers + void type(file_type ft) noexcept; + void permissions(perms prms) noexcept; + // [fs.file_status.obs] observers + file_type type() 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(); } + +private: + file_type _type; + perms _perms; +}; + +using file_time_type = std::chrono::time_point; + +// [fs.class.directory_entry] Class directory_entry +class GHC_FS_API_CLASS directory_entry +{ +public: + // [fs.dir.entry.cons] constructors and destructor + directory_entry() noexcept = default; + directory_entry(const directory_entry&) = default; + directory_entry(directory_entry&&) noexcept = default; +#ifdef GHC_WITH_EXCEPTIONS + explicit directory_entry(const path& p); +#endif + directory_entry(const path& p, std::error_code& ec); + ~directory_entry(); + + // assignments: + directory_entry& operator=(const directory_entry&) = default; + directory_entry& operator=(directory_entry&&) noexcept = default; + + // [fs.dir.entry.mods] modifiers +#ifdef GHC_WITH_EXCEPTIONS + void assign(const path& p); + void replace_filename(const path& p); + void refresh(); +#endif + void assign(const path& p, std::error_code& ec); + void replace_filename(const path& p, std::error_code& ec); + void refresh(std::error_code& ec) noexcept; + + // [fs.dir.entry.obs] observers + const filesystem::path& path() const noexcept; + operator const filesystem::path&() const noexcept; +#ifdef GHC_WITH_EXCEPTIONS + bool exists() const; + bool is_block_file() const; + bool is_character_file() const; + bool is_directory() const; + bool is_fifo() const; + bool is_other() const; + bool is_regular_file() const; + bool is_socket() const; + bool is_symlink() const; + uintmax_t file_size() const; + file_time_type last_write_time() const; + file_status status() const; + file_status symlink_status() const; +#endif + bool exists(std::error_code& ec) const noexcept; + bool is_block_file(std::error_code& ec) const noexcept; + bool is_character_file(std::error_code& ec) const noexcept; + bool is_directory(std::error_code& ec) const noexcept; + bool is_fifo(std::error_code& ec) const noexcept; + bool is_other(std::error_code& ec) const noexcept; + bool is_regular_file(std::error_code& ec) const noexcept; + bool is_socket(std::error_code& ec) const noexcept; + bool is_symlink(std::error_code& ec) const noexcept; + uintmax_t file_size(std::error_code& ec) const noexcept; + file_time_type last_write_time(std::error_code& ec) const noexcept; + file_status status(std::error_code& ec) const noexcept; + file_status symlink_status(std::error_code& ec) const noexcept; + +#ifndef GHC_OS_WEB +#ifdef GHC_WITH_EXCEPTIONS + uintmax_t hard_link_count() const; +#endif + uintmax_t hard_link_count(std::error_code& ec) const noexcept; +#endif + +#ifdef GHC_HAS_THREEWAY_COMP + std::strong_ordering operator<=>(const directory_entry& rhs) const noexcept; +#endif + bool operator<(const directory_entry& rhs) const noexcept; + bool operator==(const directory_entry& rhs) const noexcept; + bool operator!=(const directory_entry& rhs) const noexcept; + bool operator<=(const directory_entry& rhs) const noexcept; + bool operator>(const directory_entry& rhs) const noexcept; + bool operator>=(const directory_entry& rhs) const noexcept; + +private: + friend class directory_iterator; +#ifdef GHC_WITH_EXCEPTIONS + file_type status_file_type() const; +#endif + file_type status_file_type(std::error_code& ec) const noexcept; + filesystem::path _path; + file_status _status; + file_status _symlink_status; + uintmax_t _file_size = static_cast(-1); +#ifndef GHC_OS_WINDOWS + uintmax_t _hard_link_count = static_cast(-1); +#endif + time_t _last_write_time = 0; +}; + +// [fs.class.directory.iterator] Class directory_iterator +class GHC_FS_API_CLASS directory_iterator +{ +public: + class GHC_FS_API_CLASS proxy + { + public: + const directory_entry& operator*() const& noexcept { return _dir_entry; } + directory_entry operator*() && noexcept { return std::move(_dir_entry); } + + private: + explicit proxy(const directory_entry& dir_entry) + : _dir_entry(dir_entry) + { + } + friend class directory_iterator; + friend class recursive_directory_iterator; + directory_entry _dir_entry; + }; + using iterator_category = std::input_iterator_tag; + using value_type = directory_entry; + using difference_type = std::ptrdiff_t; + using pointer = const directory_entry*; + using reference = const directory_entry&; + + // [fs.dir.itr.members] member functions + directory_iterator() noexcept; +#ifdef GHC_WITH_EXCEPTIONS + explicit directory_iterator(const path& p); + directory_iterator(const path& p, directory_options options); +#endif + directory_iterator(const path& p, std::error_code& ec) noexcept; + directory_iterator(const path& p, directory_options options, std::error_code& ec) noexcept; + directory_iterator(const directory_iterator& rhs); + directory_iterator(directory_iterator&& rhs) noexcept; + ~directory_iterator(); + directory_iterator& operator=(const directory_iterator& rhs); + directory_iterator& operator=(directory_iterator&& rhs) noexcept; + const directory_entry& operator*() const; + const directory_entry* operator->() const; +#ifdef GHC_WITH_EXCEPTIONS + directory_iterator& operator++(); +#endif + directory_iterator& increment(std::error_code& ec) noexcept; + + // other members as required by [input.iterators] +#ifdef GHC_WITH_EXCEPTIONS + proxy operator++(int) + { + proxy p{**this}; + ++*this; + return p; + } +#endif + bool operator==(const directory_iterator& rhs) const; + bool operator!=(const directory_iterator& rhs) const; + +private: + friend class recursive_directory_iterator; + class impl; + std::shared_ptr _impl; +}; + +// [fs.dir.itr.nonmembers] directory_iterator non-member functions +GHC_FS_API directory_iterator begin(directory_iterator iter) noexcept; +GHC_FS_API directory_iterator end(const directory_iterator&) noexcept; + +// [fs.class.re.dir.itr] class recursive_directory_iterator +class GHC_FS_API_CLASS recursive_directory_iterator +{ +public: + using iterator_category = std::input_iterator_tag; + using value_type = directory_entry; + using difference_type = std::ptrdiff_t; + using pointer = const directory_entry*; + using reference = const directory_entry&; + + // [fs.rec.dir.itr.members] constructors and destructor + recursive_directory_iterator() noexcept; +#ifdef GHC_WITH_EXCEPTIONS + explicit recursive_directory_iterator(const path& p); + recursive_directory_iterator(const path& p, directory_options options); +#endif + recursive_directory_iterator(const path& p, directory_options options, std::error_code& ec) noexcept; + recursive_directory_iterator(const path& p, std::error_code& ec) noexcept; + recursive_directory_iterator(const recursive_directory_iterator& rhs); + recursive_directory_iterator(recursive_directory_iterator&& rhs) noexcept; + ~recursive_directory_iterator(); + + // [fs.rec.dir.itr.members] observers + directory_options options() const; + int depth() const; + bool recursion_pending() const; + + const directory_entry& operator*() const; + const directory_entry* operator->() const; + + // [fs.rec.dir.itr.members] modifiers recursive_directory_iterator& + recursive_directory_iterator& operator=(const recursive_directory_iterator& rhs); + recursive_directory_iterator& operator=(recursive_directory_iterator&& rhs) noexcept; +#ifdef GHC_WITH_EXCEPTIONS + recursive_directory_iterator& operator++(); +#endif + recursive_directory_iterator& increment(std::error_code& ec) noexcept; + +#ifdef GHC_WITH_EXCEPTIONS + void pop(); +#endif + void pop(std::error_code& ec); + void disable_recursion_pending(); + + // other members as required by [input.iterators] +#ifdef GHC_WITH_EXCEPTIONS + directory_iterator::proxy operator++(int) + { + directory_iterator::proxy proxy{**this}; + ++*this; + return proxy; + } +#endif + bool operator==(const recursive_directory_iterator& rhs) const; + bool operator!=(const recursive_directory_iterator& rhs) const; + +private: + struct recursive_directory_iterator_impl + { + directory_options _options; + bool _recursion_pending; + std::stack _dir_iter_stack; + recursive_directory_iterator_impl(directory_options options, bool recursion_pending) + : _options(options) + , _recursion_pending(recursion_pending) + { + } + }; + std::shared_ptr _impl; +}; + +// [fs.rec.dir.itr.nonmembers] directory_iterator non-member functions +GHC_FS_API recursive_directory_iterator begin(recursive_directory_iterator iter) noexcept; +GHC_FS_API recursive_directory_iterator end(const recursive_directory_iterator&) noexcept; + +// [fs.op.funcs] filesystem operations +#ifdef GHC_WITH_EXCEPTIONS +GHC_FS_API path absolute(const path& p); +GHC_FS_API path canonical(const path& p); +GHC_FS_API void copy(const path& from, const path& to); +GHC_FS_API void copy(const path& from, const path& to, copy_options options); +GHC_FS_API bool copy_file(const path& from, const path& to); +GHC_FS_API bool copy_file(const path& from, const path& to, copy_options option); +GHC_FS_API void copy_symlink(const path& existing_symlink, const path& new_symlink); +GHC_FS_API bool create_directories(const path& p); +GHC_FS_API bool create_directory(const path& p); +GHC_FS_API bool create_directory(const path& p, const path& attributes); +GHC_FS_API void create_directory_symlink(const path& to, const path& new_symlink); +GHC_FS_API void create_symlink(const path& to, const path& new_symlink); +GHC_FS_API path current_path(); +GHC_FS_API void current_path(const path& p); +GHC_FS_API bool exists(const path& p); +GHC_FS_API bool equivalent(const path& p1, const path& p2); +GHC_FS_API uintmax_t file_size(const path& p); +GHC_FS_API bool is_block_file(const path& p); +GHC_FS_API bool is_character_file(const path& p); +GHC_FS_API bool is_directory(const path& p); +GHC_FS_API bool is_empty(const path& p); +GHC_FS_API bool is_fifo(const path& p); +GHC_FS_API bool is_other(const path& p); +GHC_FS_API bool is_regular_file(const path& p); +GHC_FS_API bool is_socket(const path& p); +GHC_FS_API bool is_symlink(const path& p); +GHC_FS_API file_time_type last_write_time(const path& p); +GHC_FS_API void last_write_time(const path& p, file_time_type new_time); +GHC_FS_API void permissions(const path& p, perms prms, perm_options opts = perm_options::replace); +GHC_FS_API path proximate(const path& p, const path& base = current_path()); +GHC_FS_API path read_symlink(const path& p); +GHC_FS_API path relative(const path& p, const path& base = current_path()); +GHC_FS_API bool remove(const path& p); +GHC_FS_API uintmax_t remove_all(const path& p); +GHC_FS_API void rename(const path& from, const path& to); +GHC_FS_API void resize_file(const path& p, uintmax_t size); +GHC_FS_API space_info space(const path& p); +GHC_FS_API file_status status(const path& p); +GHC_FS_API file_status symlink_status(const path& p); +GHC_FS_API path temp_directory_path(); +GHC_FS_API path weakly_canonical(const path& p); +#endif +GHC_FS_API path absolute(const path& p, std::error_code& ec); +GHC_FS_API path canonical(const path& p, std::error_code& ec); +GHC_FS_API void copy(const path& from, const path& to, std::error_code& ec) noexcept; +GHC_FS_API void copy(const path& from, const path& to, copy_options options, std::error_code& ec) noexcept; +GHC_FS_API bool copy_file(const path& from, const path& to, std::error_code& ec) noexcept; +GHC_FS_API bool copy_file(const path& from, const path& to, copy_options option, std::error_code& ec) noexcept; +GHC_FS_API void copy_symlink(const path& existing_symlink, const path& new_symlink, std::error_code& ec) noexcept; +GHC_FS_API bool create_directories(const path& p, std::error_code& ec) noexcept; +GHC_FS_API bool create_directory(const path& p, std::error_code& ec) noexcept; +GHC_FS_API bool create_directory(const path& p, const path& attributes, std::error_code& ec) noexcept; +GHC_FS_API void create_directory_symlink(const path& to, const path& new_symlink, std::error_code& ec) noexcept; +GHC_FS_API void create_symlink(const path& to, const path& new_symlink, std::error_code& ec) noexcept; +GHC_FS_API path current_path(std::error_code& ec); +GHC_FS_API void current_path(const path& p, std::error_code& ec) noexcept; +GHC_FS_API bool exists(file_status s) noexcept; +GHC_FS_API bool exists(const path& p, std::error_code& ec) noexcept; +GHC_FS_API bool equivalent(const path& p1, const path& p2, std::error_code& ec) noexcept; +GHC_FS_API uintmax_t file_size(const path& p, std::error_code& ec) noexcept; +GHC_FS_API bool is_block_file(file_status s) noexcept; +GHC_FS_API bool is_block_file(const path& p, std::error_code& ec) noexcept; +GHC_FS_API bool is_character_file(file_status s) noexcept; +GHC_FS_API bool is_character_file(const path& p, std::error_code& ec) noexcept; +GHC_FS_API bool is_directory(file_status s) noexcept; +GHC_FS_API bool is_directory(const path& p, std::error_code& ec) noexcept; +GHC_FS_API bool is_empty(const path& p, std::error_code& ec) noexcept; +GHC_FS_API bool is_fifo(file_status s) noexcept; +GHC_FS_API bool is_fifo(const path& p, std::error_code& ec) noexcept; +GHC_FS_API bool is_other(file_status s) noexcept; +GHC_FS_API bool is_other(const path& p, std::error_code& ec) noexcept; +GHC_FS_API bool is_regular_file(file_status s) noexcept; +GHC_FS_API bool is_regular_file(const path& p, std::error_code& ec) noexcept; +GHC_FS_API bool is_socket(file_status s) noexcept; +GHC_FS_API bool is_socket(const path& p, std::error_code& ec) noexcept; +GHC_FS_API bool is_symlink(file_status s) noexcept; +GHC_FS_API bool is_symlink(const path& p, std::error_code& ec) noexcept; +GHC_FS_API file_time_type last_write_time(const path& p, std::error_code& ec) noexcept; +GHC_FS_API void last_write_time(const path& p, file_time_type new_time, std::error_code& ec) noexcept; +GHC_FS_API void permissions(const path& p, perms prms, std::error_code& ec) noexcept; +GHC_FS_API void permissions(const path& p, perms prms, perm_options opts, std::error_code& ec) noexcept; +GHC_FS_API path proximate(const path& p, std::error_code& ec); +GHC_FS_API path proximate(const path& p, const path& base, std::error_code& ec); +GHC_FS_API path read_symlink(const path& p, std::error_code& ec); +GHC_FS_API path relative(const path& p, std::error_code& ec); +GHC_FS_API path relative(const path& p, const path& base, std::error_code& ec); +GHC_FS_API bool remove(const path& p, std::error_code& ec) noexcept; +GHC_FS_API uintmax_t remove_all(const path& p, std::error_code& ec) noexcept; +GHC_FS_API void rename(const path& from, const path& to, std::error_code& ec) noexcept; +GHC_FS_API void resize_file(const path& p, uintmax_t size, std::error_code& ec) noexcept; +GHC_FS_API space_info space(const path& p, std::error_code& ec) noexcept; +GHC_FS_API file_status status(const path& p, std::error_code& ec) noexcept; +GHC_FS_API bool status_known(file_status s) noexcept; +GHC_FS_API file_status symlink_status(const path& p, std::error_code& ec) noexcept; +GHC_FS_API path temp_directory_path(std::error_code& ec) noexcept; +GHC_FS_API path weakly_canonical(const path& p, std::error_code& ec) noexcept; + +#ifndef GHC_OS_WEB +#ifdef GHC_WITH_EXCEPTIONS +GHC_FS_API void create_hard_link(const path& to, const path& new_hard_link); +GHC_FS_API uintmax_t hard_link_count(const path& p); +#endif +GHC_FS_API void create_hard_link(const path& to, const path& new_hard_link, std::error_code& ec) noexcept; +GHC_FS_API uintmax_t hard_link_count(const path& p, std::error_code& ec) noexcept; +#endif + +// Non-C++17 add-on std::fstream wrappers with path +template > +class basic_filebuf : public std::basic_filebuf +{ +public: + basic_filebuf() {} + ~basic_filebuf() override {} + basic_filebuf(const basic_filebuf&) = delete; + const basic_filebuf& operator=(const basic_filebuf&) = delete; + basic_filebuf* open(const path& p, std::ios_base::openmode mode) + { +#if defined(GHC_OS_WINDOWS) && !defined(__GLIBCXX__) + return std::basic_filebuf::open(p.wstring().c_str(), mode) ? this : 0; +#else + return std::basic_filebuf::open(p.string().c_str(), mode) ? this : 0; +#endif + } +}; + +template > +class basic_ifstream : public std::basic_ifstream +{ +public: + basic_ifstream() {} +#if defined(GHC_OS_WINDOWS) && !defined(__GLIBCXX__) + explicit basic_ifstream(const path& p, std::ios_base::openmode mode = std::ios_base::in) + : std::basic_ifstream(p.wstring().c_str(), mode) + { + } + void open(const path& p, std::ios_base::openmode mode = std::ios_base::in) { std::basic_ifstream::open(p.wstring().c_str(), mode); } +#else + explicit basic_ifstream(const path& p, std::ios_base::openmode mode = std::ios_base::in) + : std::basic_ifstream(p.string().c_str(), mode) + { + } + void open(const path& p, std::ios_base::openmode mode = std::ios_base::in) { std::basic_ifstream::open(p.string().c_str(), mode); } +#endif + basic_ifstream(const basic_ifstream&) = delete; + const basic_ifstream& operator=(const basic_ifstream&) = delete; + ~basic_ifstream() override {} +}; + +template > +class basic_ofstream : public std::basic_ofstream +{ +public: + basic_ofstream() {} +#if defined(GHC_OS_WINDOWS) && !defined(__GLIBCXX__) + explicit basic_ofstream(const path& p, std::ios_base::openmode mode = std::ios_base::out) + : std::basic_ofstream(p.wstring().c_str(), mode) + { + } + void open(const path& p, std::ios_base::openmode mode = std::ios_base::out) { std::basic_ofstream::open(p.wstring().c_str(), mode); } +#else + explicit basic_ofstream(const path& p, std::ios_base::openmode mode = std::ios_base::out) + : std::basic_ofstream(p.string().c_str(), mode) + { + } + void open(const path& p, std::ios_base::openmode mode = std::ios_base::out) { std::basic_ofstream::open(p.string().c_str(), mode); } +#endif + basic_ofstream(const basic_ofstream&) = delete; + const basic_ofstream& operator=(const basic_ofstream&) = delete; + ~basic_ofstream() override {} +}; + +template > +class basic_fstream : public std::basic_fstream +{ +public: + basic_fstream() {} +#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) + : std::basic_fstream(p.wstring().c_str(), mode) + { + } + void open(const path& p, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out) { std::basic_fstream::open(p.wstring().c_str(), mode); } +#else + explicit basic_fstream(const path& p, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out) + : std::basic_fstream(p.string().c_str(), mode) + { + } + void open(const path& p, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out) { std::basic_fstream::open(p.string().c_str(), mode); } +#endif + basic_fstream(const basic_fstream&) = delete; + const basic_fstream& operator=(const basic_fstream&) = delete; + ~basic_fstream() override {} +}; + +typedef basic_filebuf filebuf; +typedef basic_filebuf wfilebuf; +typedef basic_ifstream ifstream; +typedef basic_ifstream wifstream; +typedef basic_ofstream ofstream; +typedef basic_ofstream wofstream; +typedef basic_fstream fstream; +typedef basic_fstream wfstream; + +class GHC_FS_API_CLASS u8arguments +{ +public: + u8arguments(int& argc, char**& argv); + ~u8arguments() + { + _refargc = _argc; + _refargv = _argv; + } + + bool valid() const { return _isvalid; } + +private: + int _argc; + char** _argv; + int& _refargc; + char**& _refargv; + bool _isvalid; +#ifdef GHC_OS_WINDOWS + std::vector _args; + std::vector _argp; +#endif +}; + +//------------------------------------------------------------------------------------------------- +// Implementation +//------------------------------------------------------------------------------------------------- + +namespace detail { +enum utf8_states_t { S_STRT = 0, S_RJCT = 8 }; +GHC_FS_API void appendUTF8(std::string& str, uint32_t unicode); +GHC_FS_API bool is_surrogate(uint32_t c); +GHC_FS_API bool is_high_surrogate(uint32_t c); +GHC_FS_API bool is_low_surrogate(uint32_t c); +GHC_FS_API unsigned consumeUtf8Fragment(const unsigned state, const uint8_t fragment, uint32_t& codepoint); +enum class portable_error { + none = 0, + exists, + not_found, + not_supported, + not_implemented, + invalid_argument, + is_a_directory, +}; +GHC_FS_API std::error_code make_error_code(portable_error err); +#ifdef GHC_OS_WINDOWS +GHC_FS_API std::error_code make_system_error(uint32_t err = 0); +#else +GHC_FS_API std::error_code make_system_error(int err = 0); + +template +struct has_d_type : std::false_type{}; + +template +struct has_d_type : std::true_type {}; + +template +GHC_INLINE file_type file_type_from_dirent_impl(const T&, std::false_type) +{ + return file_type::none; +} + +template +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 +GHC_INLINE file_type file_type_from_dirent(const T& t) +{ + return file_type_from_dirent_impl(t, has_d_type{}); +} +#endif +} // namespace detail + +namespace detail { + +#ifdef GHC_EXPAND_IMPL + +GHC_INLINE std::error_code make_error_code(portable_error err) +{ +#ifdef GHC_OS_WINDOWS + switch (err) { + case portable_error::none: + return std::error_code(); + case portable_error::exists: + return std::error_code(ERROR_ALREADY_EXISTS, std::system_category()); + case portable_error::not_found: + return std::error_code(ERROR_PATH_NOT_FOUND, std::system_category()); + case portable_error::not_supported: + return std::error_code(ERROR_NOT_SUPPORTED, std::system_category()); + case portable_error::not_implemented: + return std::error_code(ERROR_CALL_NOT_IMPLEMENTED, std::system_category()); + case portable_error::invalid_argument: + return std::error_code(ERROR_INVALID_PARAMETER, std::system_category()); + case portable_error::is_a_directory: +#ifdef ERROR_DIRECTORY_NOT_SUPPORTED + return std::error_code(ERROR_DIRECTORY_NOT_SUPPORTED, std::system_category()); +#else + return std::error_code(ERROR_NOT_SUPPORTED, std::system_category()); +#endif + } +#else + switch (err) { + case portable_error::none: + return std::error_code(); + case portable_error::exists: + return std::error_code(EEXIST, std::system_category()); + case portable_error::not_found: + return std::error_code(ENOENT, std::system_category()); + case portable_error::not_supported: + return std::error_code(ENOTSUP, std::system_category()); + case portable_error::not_implemented: + return std::error_code(ENOSYS, std::system_category()); + case portable_error::invalid_argument: + return std::error_code(EINVAL, std::system_category()); + case portable_error::is_a_directory: + return std::error_code(EISDIR, std::system_category()); + } +#endif + return std::error_code(); +} + +#ifdef GHC_OS_WINDOWS +GHC_INLINE std::error_code make_system_error(uint32_t err) +{ + return std::error_code(err ? static_cast(err) : static_cast(::GetLastError()), std::system_category()); +} +#else +GHC_INLINE std::error_code make_system_error(int err) +{ + return std::error_code(err ? err : errno, std::system_category()); +} +#endif + +#endif // GHC_EXPAND_IMPL + +template +using EnableBitmask = typename std::enable_if::value || std::is_same::value || std::is_same::value || std::is_same::value, Enum>::type; +} // namespace detail + +template +constexpr detail::EnableBitmask operator&(Enum X, Enum Y) +{ + using underlying = typename std::underlying_type::type; + return static_cast(static_cast(X) & static_cast(Y)); +} + +template +constexpr detail::EnableBitmask operator|(Enum X, Enum Y) +{ + using underlying = typename std::underlying_type::type; + return static_cast(static_cast(X) | static_cast(Y)); +} + +template +constexpr detail::EnableBitmask operator^(Enum X, Enum Y) +{ + using underlying = typename std::underlying_type::type; + return static_cast(static_cast(X) ^ static_cast(Y)); +} + +template +constexpr detail::EnableBitmask operator~(Enum X) +{ + using underlying = typename std::underlying_type::type; + return static_cast(~static_cast(X)); +} + +template +detail::EnableBitmask& operator&=(Enum& X, Enum Y) +{ + X = X & Y; + return X; +} + +template +detail::EnableBitmask& operator|=(Enum& X, Enum Y) +{ + X = X | Y; + return X; +} + +template +detail::EnableBitmask& operator^=(Enum& X, Enum Y) +{ + X = X ^ Y; + return X; +} + +#ifdef GHC_EXPAND_IMPL + +namespace detail { + +GHC_INLINE bool in_range(uint32_t c, uint32_t lo, uint32_t hi) +{ + return (static_cast(c - lo) < (hi - lo + 1)); +} + +GHC_INLINE bool is_surrogate(uint32_t c) +{ + return in_range(c, 0xd800, 0xdfff); +} + +GHC_INLINE bool is_high_surrogate(uint32_t c) +{ + return (c & 0xfffffc00) == 0xd800; +} + +GHC_INLINE bool is_low_surrogate(uint32_t c) +{ + return (c & 0xfffffc00) == 0xdc00; +} + +GHC_INLINE void appendUTF8(std::string& str, uint32_t unicode) +{ + if (unicode <= 0x7f) { + str.push_back(static_cast(unicode)); + } + else if (unicode >= 0x80 && unicode <= 0x7ff) { + str.push_back(static_cast((unicode >> 6) + 192)); + str.push_back(static_cast((unicode & 0x3f) + 128)); + } + else if ((unicode >= 0x800 && unicode <= 0xd7ff) || (unicode >= 0xe000 && unicode <= 0xffff)) { + str.push_back(static_cast((unicode >> 12) + 224)); + str.push_back(static_cast(((unicode & 0xfff) >> 6) + 128)); + str.push_back(static_cast((unicode & 0x3f) + 128)); + } + else if (unicode >= 0x10000 && unicode <= 0x10ffff) { + str.push_back(static_cast((unicode >> 18) + 240)); + str.push_back(static_cast(((unicode & 0x3ffff) >> 12) + 128)); + str.push_back(static_cast(((unicode & 0xfff) >> 6) + 128)); + str.push_back(static_cast((unicode & 0x3f) + 128)); + } + else { +#ifdef GHC_RAISE_UNICODE_ERRORS + throw filesystem_error("Illegal code point for unicode character.", str, std::make_error_code(std::errc::illegal_byte_sequence)); +#else + appendUTF8(str, 0xfffd); +#endif + } +} + +// Thanks to Bjoern Hoehrmann (https://bjoern.hoehrmann.de/utf-8/decoder/dfa/) +// and Taylor R Campbell for the ideas to this DFA approach of UTF-8 decoding; +// Generating debugging and shrinking my own DFA from scratch was a day of fun! +GHC_INLINE unsigned consumeUtf8Fragment(const unsigned state, const uint8_t fragment, uint32_t& codepoint) +{ + static const uint32_t utf8_state_info[] = { + // encoded states + 0x11111111u, 0x11111111u, 0x77777777u, 0x77777777u, 0x88888888u, 0x88888888u, 0x88888888u, 0x88888888u, 0x22222299u, 0x22222222u, 0x22222222u, 0x22222222u, 0x3333333au, 0x33433333u, 0x9995666bu, 0x99999999u, + 0x88888880u, 0x22818108u, 0x88888881u, 0x88888882u, 0x88888884u, 0x88888887u, 0x88888886u, 0x82218108u, 0x82281108u, 0x88888888u, 0x88888883u, 0x88888885u, 0u, 0u, 0u, 0u, + }; + uint8_t category = fragment < 128 ? 0 : (utf8_state_info[(fragment >> 3) & 0xf] >> ((fragment & 7) << 2)) & 0xf; + codepoint = (state ? (codepoint << 6) | (fragment & 0x3fu) : (0xffu >> category) & fragment); + return state == S_RJCT ? static_cast(S_RJCT) : static_cast((utf8_state_info[category + 16] >> (state << 2)) & 0xf); +} + +GHC_INLINE bool validUtf8(const std::string& utf8String) +{ + std::string::const_iterator iter = utf8String.begin(); + unsigned utf8_state = S_STRT; + std::uint32_t codepoint = 0; + while (iter < utf8String.end()) { + if ((utf8_state = consumeUtf8Fragment(utf8_state, static_cast(*iter++), codepoint)) == S_RJCT) { + return false; + } + } + if (utf8_state) { + return false; + } + return true; +} + +} // namespace detail + +#endif + +namespace detail { + +template ::value && (sizeof(typename Utf8String::value_type) == 1) && (sizeof(typename StringType::value_type) == 1)>::type* = nullptr> +inline StringType fromUtf8(const Utf8String& utf8String, const typename StringType::allocator_type& alloc = typename StringType::allocator_type()) +{ + return StringType(utf8String.begin(), utf8String.end(), alloc); +} + +template ::value && (sizeof(typename Utf8String::value_type) == 1) && (sizeof(typename StringType::value_type) == 2)>::type* = nullptr> +inline StringType fromUtf8(const Utf8String& utf8String, const typename StringType::allocator_type& alloc = typename StringType::allocator_type()) +{ + StringType result(alloc); + result.reserve(utf8String.length()); + auto iter = utf8String.cbegin(); + unsigned utf8_state = S_STRT; + std::uint32_t codepoint = 0; + while (iter < utf8String.cend()) { + if ((utf8_state = consumeUtf8Fragment(utf8_state, static_cast(*iter++), codepoint)) == S_STRT) { + if (codepoint <= 0xffff) { + result += static_cast(codepoint); + } + else { + codepoint -= 0x10000; + result += static_cast((codepoint >> 10) + 0xd800); + result += static_cast((codepoint & 0x3ff) + 0xdc00); + } + codepoint = 0; + } + else if (utf8_state == S_RJCT) { +#ifdef GHC_RAISE_UNICODE_ERRORS + throw filesystem_error("Illegal byte sequence for unicode character.", utf8String, std::make_error_code(std::errc::illegal_byte_sequence)); +#else + result += static_cast(0xfffd); + utf8_state = S_STRT; + codepoint = 0; +#endif + } + } + if (utf8_state) { +#ifdef GHC_RAISE_UNICODE_ERRORS + throw filesystem_error("Illegal byte sequence for unicode character.", utf8String, std::make_error_code(std::errc::illegal_byte_sequence)); +#else + result += static_cast(0xfffd); +#endif + } + return result; +} + +template ::value && (sizeof(typename Utf8String::value_type) == 1) && (sizeof(typename StringType::value_type) == 4)>::type* = nullptr> +inline StringType fromUtf8(const Utf8String& utf8String, const typename StringType::allocator_type& alloc = typename StringType::allocator_type()) +{ + StringType result(alloc); + result.reserve(utf8String.length()); + auto iter = utf8String.cbegin(); + unsigned utf8_state = S_STRT; + std::uint32_t codepoint = 0; + while (iter < utf8String.cend()) { + if ((utf8_state = consumeUtf8Fragment(utf8_state, static_cast(*iter++), codepoint)) == S_STRT) { + result += static_cast(codepoint); + codepoint = 0; + } + else if (utf8_state == S_RJCT) { +#ifdef GHC_RAISE_UNICODE_ERRORS + throw filesystem_error("Illegal byte sequence for unicode character.", utf8String, std::make_error_code(std::errc::illegal_byte_sequence)); +#else + result += static_cast(0xfffd); + utf8_state = S_STRT; + codepoint = 0; +#endif + } + } + if (utf8_state) { +#ifdef GHC_RAISE_UNICODE_ERRORS + throw filesystem_error("Illegal byte sequence for unicode character.", utf8String, std::make_error_code(std::errc::illegal_byte_sequence)); +#else + result += static_cast(0xfffd); +#endif + } + return result; +} + +template +inline StringType fromUtf8(const charT (&utf8String)[N]) +{ +#ifdef GHC_WITH_STRING_VIEW + return fromUtf8(basic_string_view(utf8String, N - 1)); +#else + return fromUtf8(std::basic_string(utf8String, N - 1)); +#endif +} + +template ::value && (sizeof(typename strT::value_type) == 1), int>::type size = 1> +inline std::string toUtf8(const strT& unicodeString) +{ + return std::string(unicodeString.begin(), unicodeString.end()); +} + +template ::value && (sizeof(typename strT::value_type) == 2), int>::type size = 2> +inline std::string toUtf8(const strT& unicodeString) +{ + std::string result; + for (auto iter = unicodeString.begin(); iter != unicodeString.end(); ++iter) { + char32_t c = *iter; + if (is_surrogate(c)) { + ++iter; + if (iter != unicodeString.end() && is_high_surrogate(c) && is_low_surrogate(*iter)) { + appendUTF8(result, (char32_t(c) << 10) + *iter - 0x35fdc00); + } + else { +#ifdef GHC_RAISE_UNICODE_ERRORS + throw filesystem_error("Illegal code point for unicode character.", result, std::make_error_code(std::errc::illegal_byte_sequence)); +#else + appendUTF8(result, 0xfffd); + if (iter == unicodeString.end()) { + break; + } +#endif + } + } + else { + appendUTF8(result, c); + } + } + return result; +} + +template ::value && (sizeof(typename strT::value_type) == 4), int>::type size = 4> +inline std::string toUtf8(const strT& unicodeString) +{ + std::string result; + for (auto c : unicodeString) { + appendUTF8(result, static_cast(c)); + } + return result; +} + +template +inline std::string toUtf8(const charT* unicodeString) +{ +#ifdef GHC_WITH_STRING_VIEW + return toUtf8(basic_string_view>(unicodeString)); +#else + return toUtf8(std::basic_string>(unicodeString)); +#endif +} + +#ifdef GHC_USE_WCHAR_T +template ::value && (sizeof(typename WString::value_type) == 2) && (sizeof(typename StringType::value_type) == 1), bool>::type = false> +inline StringType fromWChar(const WString& wString, const typename StringType::allocator_type& alloc = typename StringType::allocator_type()) +{ + auto temp = toUtf8(wString); + return StringType(temp.begin(), temp.end(), alloc); +} + +template ::value && (sizeof(typename WString::value_type) == 2) && (sizeof(typename StringType::value_type) == 2), bool>::type = false> +inline StringType fromWChar(const WString& wString, const typename StringType::allocator_type& alloc = typename StringType::allocator_type()) +{ + return StringType(wString.begin(), wString.end(), alloc); +} + +template ::value && (sizeof(typename WString::value_type) == 2) && (sizeof(typename StringType::value_type) == 4), bool>::type = false> +inline StringType fromWChar(const WString& wString, const typename StringType::allocator_type& alloc = typename StringType::allocator_type()) +{ + auto temp = toUtf8(wString); + return fromUtf8(temp, alloc); +} + +template ::value && (sizeof(typename strT::value_type) == 1), bool>::type = false> +inline std::wstring toWChar(const strT& unicodeString) +{ + return fromUtf8(unicodeString); +} + +template ::value && (sizeof(typename strT::value_type) == 2), bool>::type = false> +inline std::wstring toWChar(const strT& unicodeString) +{ + return std::wstring(unicodeString.begin(), unicodeString.end()); +} + +template ::value && (sizeof(typename strT::value_type) == 4), bool>::type = false> +inline std::wstring toWChar(const strT& unicodeString) +{ + auto temp = toUtf8(unicodeString); + return fromUtf8(temp); +} + +template +inline std::wstring toWChar(const charT* unicodeString) +{ +#ifdef GHC_WITH_STRING_VIEW + return toWChar(basic_string_view>(unicodeString)); +#else + return toWChar(std::basic_string>(unicodeString)); +#endif +} +#endif // GHC_USE_WCHAR_T + +} // namespace detail + +#ifdef GHC_EXPAND_IMPL + +namespace detail { + +template ::value, bool>::type = true> +GHC_INLINE bool startsWith(const strT& what, const strT& with) +{ + return with.length() <= what.length() && equal(with.begin(), with.end(), what.begin()); +} + +template ::value, bool>::type = true> +GHC_INLINE bool endsWith(const strT& what, const strT& with) +{ + return with.length() <= what.length() && what.compare(what.length() - with.length(), with.size(), with) == 0; +} + +} // namespace detail + +GHC_INLINE void path::check_long_path() +{ +#if defined(GHC_OS_WINDOWS) && defined(GHC_WIN_AUTO_PREFIX_LONG_PATH) + if (is_absolute() && _path.length() >= MAX_PATH - 12 && !detail::startsWith(_path, impl_string_type(GHC_PLATFORM_LITERAL("\\\\?\\")))) { + postprocess_path_with_format(native_format); + } +#endif +} + +GHC_INLINE void path::postprocess_path_with_format(path::format fmt) +{ +#ifdef GHC_RAISE_UNICODE_ERRORS + if (!detail::validUtf8(_path)) { + path t; + t._path = _path; + throw filesystem_error("Illegal byte sequence for unicode character.", t, std::make_error_code(std::errc::illegal_byte_sequence)); + } +#endif + switch (fmt) { +#ifdef GHC_OS_WINDOWS + case path::native_format: + case path::auto_format: + case path::generic_format: + for (auto& c : _path) { + if (c == generic_separator) { + c = preferred_separator; + } + } +#ifdef GHC_WIN_AUTO_PREFIX_LONG_PATH + if (is_absolute() && _path.length() >= MAX_PATH - 12 && !detail::startsWith(_path, impl_string_type(GHC_PLATFORM_LITERAL("\\\\?\\")))) { + _path = GHC_PLATFORM_LITERAL("\\\\?\\") + _path; + } +#endif + handle_prefixes(); + break; +#else + case path::auto_format: + case path::native_format: + case path::generic_format: + // nothing to do + break; +#endif + } + if (_path.length() > _prefixLength + 2 && _path[_prefixLength] == preferred_separator && _path[_prefixLength + 1] == preferred_separator && _path[_prefixLength + 2] != preferred_separator) { + impl_string_type::iterator new_end = std::unique(_path.begin() + static_cast(_prefixLength) + 2, _path.end(), [](path::value_type lhs, path::value_type rhs) { return lhs == rhs && lhs == preferred_separator; }); + _path.erase(new_end, _path.end()); + } + else { + impl_string_type::iterator new_end = std::unique(_path.begin() + static_cast(_prefixLength), _path.end(), [](path::value_type lhs, path::value_type rhs) { return lhs == rhs && lhs == preferred_separator; }); + _path.erase(new_end, _path.end()); + } +} + +#endif // GHC_EXPAND_IMPL + +template +inline path::path(const Source& source, format fmt) +#ifdef GHC_USE_WCHAR_T + : _path(detail::toWChar(source)) +#else + : _path(detail::toUtf8(source)) +#endif +{ + postprocess_path_with_format(fmt); +} + +template +inline path u8path(const Source& source) +{ + return path(source); +} +template +inline path u8path(InputIterator first, InputIterator last) +{ + return path(first, last); +} + +template +inline path::path(InputIterator first, InputIterator last, format fmt) + : path(std::basic_string::value_type>(first, last), fmt) +{ + // delegated +} + +#ifdef GHC_EXPAND_IMPL + +namespace detail { + +GHC_INLINE bool equals_simple_insensitive(const path::value_type* str1, const path::value_type* str2) +{ +#ifdef GHC_OS_WINDOWS +#ifdef __GNUC__ + while (::tolower((unsigned char)*str1) == ::tolower((unsigned char)*str2++)) { + if (*str1++ == 0) + return true; + } + return false; +#else // __GNUC__ +#ifdef GHC_USE_WCHAR_T + return 0 == ::_wcsicmp(str1, str2); +#else // GHC_USE_WCHAR_T + return 0 == ::_stricmp(str1, str2); +#endif // GHC_USE_WCHAR_T +#endif // __GNUC__ +#else // GHC_OS_WINDOWS + return 0 == ::strcasecmp(str1, str2); +#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) +{ + while (len1 > 0 && len2 > 0 && ::tolower(static_cast(*str1)) == ::tolower(static_cast(*str2))) { + --len1; + --len2; + ++str1; + ++str2; + } + if (len1 && len2) { + return *str1 < *str2 ? -1 : 1; + } + if (len1 == 0 && len2 == 0) { + return 0; + } + return len1 == 0 ? -1 : 1; +} + +GHC_INLINE const char* strerror_adapter(char* gnu, char*) +{ + return gnu; +} + +GHC_INLINE const char* strerror_adapter(int posix, char* buffer) +{ + if (posix) { + return "Error in strerror_r!"; + } + return buffer; +} + +template +GHC_INLINE std::string systemErrorText(ErrorNumber code = 0) +{ +#if defined(GHC_OS_WINDOWS) + LPVOID msgBuf; + DWORD dw = code ? static_cast(code) : ::GetLastError(); + FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR)&msgBuf, 0, NULL); + std::string msg = toUtf8(std::wstring((LPWSTR)msgBuf)); + LocalFree(msgBuf); + return msg; +#else + char buffer[512]; + return strerror_adapter(strerror_r(code ? code : errno, buffer, sizeof(buffer)), buffer); +#endif +} + +#ifdef GHC_OS_WINDOWS +using CreateSymbolicLinkW_fp = BOOLEAN(WINAPI*)(LPCWSTR, LPCWSTR, DWORD); +using CreateHardLinkW_fp = BOOLEAN(WINAPI*)(LPCWSTR, LPCWSTR, LPSECURITY_ATTRIBUTES); + +GHC_INLINE void create_symlink(const path& target_name, const path& new_symlink, bool to_directory, std::error_code& ec) +{ + std::error_code tec; + auto fs = status(target_name, tec); + if ((fs.type() == file_type::directory && !to_directory) || (fs.type() == file_type::regular && to_directory)) { + ec = detail::make_error_code(detail::portable_error::not_supported); + return; + } +#if defined(__GNUC__) && __GNUC__ >= 8 +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-function-type" +#endif + static CreateSymbolicLinkW_fp api_call = reinterpret_cast(GetProcAddress(GetModuleHandleW(L"kernel32.dll"), "CreateSymbolicLinkW")); +#if defined(__GNUC__) && __GNUC__ >= 8 +#pragma GCC diagnostic pop +#endif + if (api_call) { + if (api_call(GHC_NATIVEWP(new_symlink), GHC_NATIVEWP(target_name), to_directory ? 1 : 0) == 0) { + auto result = ::GetLastError(); + if (result == ERROR_PRIVILEGE_NOT_HELD && api_call(GHC_NATIVEWP(new_symlink), GHC_NATIVEWP(target_name), to_directory ? 3 : 2) != 0) { + return; + } + ec = detail::make_system_error(result); + } + } + else { + ec = detail::make_system_error(ERROR_NOT_SUPPORTED); + } +} + +GHC_INLINE void create_hardlink(const path& target_name, const path& new_hardlink, std::error_code& ec) +{ +#if defined(__GNUC__) && __GNUC__ >= 8 +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-function-type" +#endif + static CreateHardLinkW_fp api_call = reinterpret_cast(GetProcAddress(GetModuleHandleW(L"kernel32.dll"), "CreateHardLinkW")); +#if defined(__GNUC__) && __GNUC__ >= 8 +#pragma GCC diagnostic pop +#endif + if (api_call) { + if (api_call(GHC_NATIVEWP(new_hardlink), GHC_NATIVEWP(target_name), NULL) == 0) { + ec = detail::make_system_error(); + } + } + else { + ec = detail::make_system_error(ERROR_NOT_SUPPORTED); + } +} + +GHC_INLINE path getFullPathName(const wchar_t* p, std::error_code& ec) +{ + ULONG size = ::GetFullPathNameW(p, 0, 0, 0); + if (size) { + std::vector buf(size, 0); + ULONG s2 = GetFullPathNameW(p, size, buf.data(), nullptr); + if (s2 && s2 < size) { + return path(std::wstring(buf.data(), s2)); + } + } + ec = detail::make_system_error(); + return path(); +} + +#else +GHC_INLINE void create_symlink(const path& target_name, const path& new_symlink, bool, std::error_code& ec) +{ + if (::symlink(target_name.c_str(), new_symlink.c_str()) != 0) { + ec = detail::make_system_error(); + } +} + +#ifndef GHC_OS_WEB +GHC_INLINE void create_hardlink(const path& target_name, const path& new_hardlink, std::error_code& ec) +{ + if (::link(target_name.c_str(), new_hardlink.c_str()) != 0) { + ec = detail::make_system_error(); + } +} +#endif +#endif + +template +GHC_INLINE file_status file_status_from_st_mode(T mode) +{ +#ifdef GHC_OS_WINDOWS + file_type ft = file_type::unknown; + if ((mode & _S_IFDIR) == _S_IFDIR) { + ft = file_type::directory; + } + else if ((mode & _S_IFREG) == _S_IFREG) { + ft = file_type::regular; + } + else if ((mode & _S_IFCHR) == _S_IFCHR) { + ft = file_type::character; + } + perms prms = static_cast(mode & 0xfff); + return file_status(ft, prms); +#else + file_type ft = file_type::unknown; + if (S_ISDIR(mode)) { + ft = file_type::directory; + } + else if (S_ISREG(mode)) { + ft = file_type::regular; + } + else if (S_ISCHR(mode)) { + ft = file_type::character; + } + else if (S_ISBLK(mode)) { + ft = file_type::block; + } + else if (S_ISFIFO(mode)) { + ft = file_type::fifo; + } + else if (S_ISLNK(mode)) { + ft = file_type::symlink; + } + else if (S_ISSOCK(mode)) { + ft = file_type::socket; + } + perms prms = static_cast(mode & 0xfff); + return file_status(ft, prms); +#endif +} + +#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 +typedef struct _REPARSE_DATA_BUFFER +{ + ULONG ReparseTag; + USHORT ReparseDataLength; + USHORT Reserved; + union + { + struct + { + USHORT SubstituteNameOffset; + USHORT SubstituteNameLength; + USHORT PrintNameOffset; + USHORT PrintNameLength; + ULONG Flags; + WCHAR PathBuffer[1]; + } SymbolicLinkReparseBuffer; + struct + { + USHORT SubstituteNameOffset; + USHORT SubstituteNameLength; + USHORT PrintNameOffset; + USHORT PrintNameLength; + WCHAR PathBuffer[1]; + } MountPointReparseBuffer; + struct + { + UCHAR DataBuffer[1]; + } GenericReparseBuffer; + } DUMMYUNIONNAME; +} REPARSE_DATA_BUFFER; +#ifndef MAXIMUM_REPARSE_DATA_BUFFER_SIZE +#define MAXIMUM_REPARSE_DATA_BUFFER_SIZE (16 * 1024) +#endif +#endif + +template +struct free_deleter +{ + void operator()(T* p) const { std::free(p); } +}; + +GHC_INLINE std::unique_ptr> 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(); + return nullptr; + } + + std::unique_ptr> reparseData(reinterpret_cast(std::calloc(1, MAXIMUM_REPARSE_DATA_BUFFER_SIZE))); + ULONG bufferUsed; + if (DeviceIoControl(file.get(), FSCTL_GET_REPARSE_POINT, 0, 0, reparseData.get(), MAXIMUM_REPARSE_DATA_BUFFER_SIZE, &bufferUsed, 0)) { + return reparseData; + } + else { + ec = detail::make_system_error(); + } + return nullptr; +} +#endif + +GHC_INLINE path resolveSymlink(const path& p, std::error_code& ec) +{ +#ifdef GHC_OS_WINDOWS + path result; + auto reparseData = detail::getReparseData(p, ec); + if (!ec) { + if (reparseData && IsReparseTagMicrosoft(reparseData->ReparseTag)) { + switch (reparseData->ReparseTag) { + case IO_REPARSE_TAG_SYMLINK: { + auto printName = std::wstring(&reparseData->SymbolicLinkReparseBuffer.PathBuffer[reparseData->SymbolicLinkReparseBuffer.PrintNameOffset / sizeof(WCHAR)], reparseData->SymbolicLinkReparseBuffer.PrintNameLength / sizeof(WCHAR)); + auto substituteName = + std::wstring(&reparseData->SymbolicLinkReparseBuffer.PathBuffer[reparseData->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)], reparseData->SymbolicLinkReparseBuffer.SubstituteNameLength / sizeof(WCHAR)); + if (detail::endsWith(substituteName, printName) && detail::startsWith(substituteName, std::wstring(L"\\??\\"))) { + result = printName; + } + else { + result = substituteName; + } + if (reparseData->SymbolicLinkReparseBuffer.Flags & 0x1 /*SYMLINK_FLAG_RELATIVE*/) { + result = p.parent_path() / result; + } + break; + } + case IO_REPARSE_TAG_MOUNT_POINT: + result = detail::getFullPathName(GHC_NATIVEWP(p), ec); + // result = std::wstring(&reparseData->MountPointReparseBuffer.PathBuffer[reparseData->MountPointReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)], reparseData->MountPointReparseBuffer.SubstituteNameLength / sizeof(WCHAR)); + break; + default: + break; + } + } + } + return result; +#else + size_t bufferSize = 256; + while (true) { + std::vector buffer(bufferSize, static_cast(0)); + auto rc = ::readlink(p.c_str(), buffer.data(), buffer.size()); + if (rc < 0) { + ec = detail::make_system_error(); + return path(); + } + else if (rc < static_cast(bufferSize)) { + return path(std::string(buffer.data(), static_cast(rc))); + } + bufferSize *= 2; + } + return path(); +#endif +} + +#ifdef GHC_OS_WINDOWS +GHC_INLINE time_t timeFromFILETIME(const FILETIME& ft) +{ + ULARGE_INTEGER ull; + ull.LowPart = ft.dwLowDateTime; + ull.HighPart = ft.dwHighDateTime; + return static_cast(ull.QuadPart / 10000000ULL - 11644473600ULL); +} + +GHC_INLINE void timeToFILETIME(time_t t, FILETIME& ft) +{ + LONGLONG ll; + ll = Int32x32To64(t, 10000000) + 116444736000000000; + ft.dwLowDateTime = static_cast(ll); + ft.dwHighDateTime = static_cast(ll >> 32); +} + +template +GHC_INLINE uintmax_t hard_links_from_INFO(const INFO* info) +{ + return static_cast(-1); +} + +template <> +GHC_INLINE uintmax_t hard_links_from_INFO(const BY_HANDLE_FILE_INFORMATION* info) +{ + return info->nNumberOfLinks; +} + +template +GHC_INLINE DWORD reparse_tag_from_INFO(const INFO*) +{ + return 0; +} + +template <> +GHC_INLINE DWORD reparse_tag_from_INFO(const WIN32_FIND_DATAW* info) +{ + return info->dwReserved0; +} + +template +GHC_INLINE file_status status_from_INFO(const path& p, const INFO* info, std::error_code& ec, uintmax_t* sz = nullptr, time_t* lwt = nullptr) +{ + file_type ft = file_type::unknown; + if (sizeof(INFO) == sizeof(WIN32_FIND_DATAW)) { + if (detail::reparse_tag_from_INFO(info) == IO_REPARSE_TAG_SYMLINK) { + ft = file_type::symlink; + } + } + else { + if ((info->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) { + auto reparseData = detail::getReparseData(p, ec); + if (!ec && reparseData && IsReparseTagMicrosoft(reparseData->ReparseTag) && reparseData->ReparseTag == IO_REPARSE_TAG_SYMLINK) { + ft = file_type::symlink; + } + } + } + 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; + if (!(info->dwFileAttributes & FILE_ATTRIBUTE_READONLY)) { + prms = prms | perms::owner_write | perms::group_write | perms::others_write; + } + if (has_executable_extension(p)) { + prms = prms | perms::owner_exec | perms::group_exec | perms::others_exec; + } + if (sz) { + *sz = static_cast(info->nFileSizeHigh) << (sizeof(info->nFileSizeHigh) * 8) | info->nFileSizeLow; + } + if (lwt) { + *lwt = detail::timeFromFILETIME(info->ftLastWriteTime); + } + return file_status(ft, prms); +} + +#endif + +GHC_INLINE bool is_not_found_error(std::error_code& ec) +{ +#ifdef GHC_OS_WINDOWS + return ec.value() == ERROR_FILE_NOT_FOUND || ec.value() == ERROR_PATH_NOT_FOUND || ec.value() == ERROR_INVALID_NAME; +#else + return ec.value() == ENOENT || ec.value() == ENOTDIR; +#endif +} + +GHC_INLINE file_status symlink_status_ex(const path& p, std::error_code& ec, uintmax_t* sz = nullptr, uintmax_t* nhl = nullptr, time_t* lwt = nullptr) noexcept +{ +#ifdef GHC_OS_WINDOWS + file_status fs; + WIN32_FILE_ATTRIBUTE_DATA attr; + if (!GetFileAttributesExW(GHC_NATIVEWP(p), GetFileExInfoStandard, &attr)) { + ec = detail::make_system_error(); + } + else { + ec.clear(); + fs = detail::status_from_INFO(p, &attr, ec, sz, lwt); + if (nhl) { + *nhl = 0; + } + } + if (detail::is_not_found_error(ec)) { + return file_status(file_type::not_found); + } + return ec ? file_status(file_type::none) : fs; +#else + (void)sz; + (void)nhl; + (void)lwt; + struct ::stat fs; + auto result = ::lstat(p.c_str(), &fs); + if (result == 0) { + ec.clear(); + file_status f_s = detail::file_status_from_st_mode(fs.st_mode); + return f_s; + } + ec = detail::make_system_error(); + if (detail::is_not_found_error(ec)) { + return file_status(file_type::not_found, perms::unknown); + } + return file_status(file_type::none); +#endif +} + +GHC_INLINE file_status status_ex(const path& p, std::error_code& ec, file_status* sls = nullptr, uintmax_t* sz = nullptr, uintmax_t* nhl = nullptr, time_t* lwt = nullptr, int recurse_count = 0) noexcept +{ + ec.clear(); +#ifdef GHC_OS_WINDOWS + if (recurse_count > 16) { + ec = detail::make_system_error(0x2A9 /*ERROR_STOPPED_ON_SYMLINK*/); + return file_status(file_type::unknown); + } + WIN32_FILE_ATTRIBUTE_DATA attr; + if (!::GetFileAttributesExW(GHC_NATIVEWP(p), GetFileExInfoStandard, &attr)) { + ec = detail::make_system_error(); + } + else if (attr.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) { + auto reparseData = detail::getReparseData(p, ec); + if (!ec && reparseData && IsReparseTagMicrosoft(reparseData->ReparseTag) && reparseData->ReparseTag == IO_REPARSE_TAG_SYMLINK) { + path target = resolveSymlink(p, ec); + file_status result; + if (!ec && !target.empty()) { + if (sls) { + *sls = status_from_INFO(p, &attr, ec); + } + return detail::status_ex(target, ec, nullptr, sz, nhl, lwt, recurse_count + 1); + } + return file_status(file_type::unknown); + } + } + if (ec) { + if (detail::is_not_found_error(ec)) { + return file_status(file_type::not_found); + } + return file_status(file_type::none); + } + if (nhl) { + *nhl = 0; + } + return detail::status_from_INFO(p, &attr, ec, sz, lwt); +#else + (void)recurse_count; + struct ::stat st; + auto result = ::lstat(p.c_str(), &st); + if (result == 0) { + ec.clear(); + file_status fs = detail::file_status_from_st_mode(st.st_mode); + if (sls) { + *sls = fs; + } + if (fs.type() == file_type::symlink) { + result = ::stat(p.c_str(), &st); + if (result == 0) { + fs = detail::file_status_from_st_mode(st.st_mode); + } + else { + ec = detail::make_system_error(); + if (detail::is_not_found_error(ec)) { + return file_status(file_type::not_found, perms::unknown); + } + return file_status(file_type::none); + } + } + if (sz) { + *sz = static_cast(st.st_size); + } + if (nhl) { + *nhl = st.st_nlink; + } + if (lwt) { + *lwt = st.st_mtime; + } + return fs; + } + else { + ec = detail::make_system_error(); + if (detail::is_not_found_error(ec)) { + return file_status(file_type::not_found, perms::unknown); + } + return file_status(file_type::none); + } +#endif +} + +} // namespace detail + +GHC_INLINE u8arguments::u8arguments(int& argc, char**& argv) + : _argc(argc) + , _argv(argv) + , _refargc(argc) + , _refargv(argv) + , _isvalid(false) +{ +#ifdef GHC_OS_WINDOWS + LPWSTR* p; + p = ::CommandLineToArgvW(::GetCommandLineW(), &argc); + _args.reserve(static_cast(argc)); + _argp.reserve(static_cast(argc)); + for (size_t i = 0; i < static_cast(argc); ++i) { + _args.push_back(detail::toUtf8(std::wstring(p[i]))); + _argp.push_back((char*)_args[i].data()); + } + argv = _argp.data(); + ::LocalFree(p); + _isvalid = true; +#else + std::setlocale(LC_ALL, ""); +#if defined(__ANDROID__) && __ANDROID_API__ < 26 + _isvalid = true; +#else + if (detail::equals_simple_insensitive(::nl_langinfo(CODESET), "UTF-8")) { + _isvalid = true; + } +#endif +#endif +} + +//----------------------------------------------------------------------------- +// [fs.path.construct] constructors and destructor + +GHC_INLINE path::path() noexcept {} + +GHC_INLINE path::path(const path& p) + : _path(p._path) +#if defined(GHC_OS_WINDOWS) && defined(GHC_WIN_AUTO_PREFIX_LONG_PATH) + , _prefixLength(p._prefixLength) +#endif +{ +} + +GHC_INLINE path::path(path&& p) noexcept + : _path(std::move(p._path)) +#if defined(GHC_OS_WINDOWS) && defined(GHC_WIN_AUTO_PREFIX_LONG_PATH) + , _prefixLength(p._prefixLength) +#endif +{ +} + +GHC_INLINE path::path(string_type&& source, format fmt) + : _path(std::move(source)) +{ + postprocess_path_with_format(fmt); +} + +#endif // GHC_EXPAND_IMPL + +#ifdef GHC_WITH_EXCEPTIONS +template +inline path::path(const Source& source, const std::locale& loc, format fmt) + : path(source, fmt) +{ + std::string locName = loc.name(); + if (!(locName.length() >= 5 && (locName.substr(locName.length() - 5) == "UTF-8" || locName.substr(locName.length() - 5) == "utf-8"))) { + throw filesystem_error("This implementation only supports UTF-8 locales!", path(_path), detail::make_error_code(detail::portable_error::not_supported)); + } +} + +template +inline path::path(InputIterator first, InputIterator last, const std::locale& loc, format fmt) + : path(std::basic_string::value_type>(first, last), fmt) +{ + std::string locName = loc.name(); + if (!(locName.length() >= 5 && (locName.substr(locName.length() - 5) == "UTF-8" || locName.substr(locName.length() - 5) == "utf-8"))) { + throw filesystem_error("This implementation only supports UTF-8 locales!", path(_path), detail::make_error_code(detail::portable_error::not_supported)); + } +} +#endif + +#ifdef GHC_EXPAND_IMPL + +GHC_INLINE path::~path() {} + +//----------------------------------------------------------------------------- +// [fs.path.assign] assignments + +GHC_INLINE path& path::operator=(const path& p) +{ + _path = p._path; +#if defined(GHC_OS_WINDOWS) && defined(GHC_WIN_AUTO_PREFIX_LONG_PATH) + _prefixLength = p._prefixLength; +#endif + return *this; +} + +GHC_INLINE path& path::operator=(path&& p) noexcept +{ + _path = std::move(p._path); +#if defined(GHC_OS_WINDOWS) && defined(GHC_WIN_AUTO_PREFIX_LONG_PATH) + _prefixLength = p._prefixLength; +#endif + return *this; +} + +GHC_INLINE path& path::operator=(path::string_type&& source) +{ + return assign(source); +} + +GHC_INLINE path& path::assign(path::string_type&& source) +{ + _path = std::move(source); + postprocess_path_with_format(native_format); + return *this; +} + +#endif // GHC_EXPAND_IMPL + +template +inline path& path::operator=(const Source& source) +{ + return assign(source); +} + +template +inline path& path::assign(const Source& source) +{ +#ifdef GHC_USE_WCHAR_T + _path.assign(detail::toWChar(source)); +#else + _path.assign(detail::toUtf8(source)); +#endif + postprocess_path_with_format(native_format); + return *this; +} + +template <> +inline path& path::assign(const path& source) +{ + _path = source._path; +#if defined(GHC_OS_WINDOWS) && defined(GHC_WIN_AUTO_PREFIX_LONG_PATH) + _prefixLength = source._prefixLength; +#endif + return *this; +} + +template +inline path& path::assign(InputIterator first, InputIterator last) +{ + _path.assign(first, last); + postprocess_path_with_format(native_format); + return *this; +} + +#ifdef GHC_EXPAND_IMPL + +//----------------------------------------------------------------------------- +// [fs.path.append] appends + +GHC_INLINE path& path::operator/=(const path& p) +{ + if (p.empty()) { + // was: if ((!has_root_directory() && is_absolute()) || has_filename()) + if (!_path.empty() && _path[_path.length() - 1] != preferred_separator && _path[_path.length() - 1] != ':') { + _path += preferred_separator; + } + return *this; + } + if ((p.is_absolute() && (_path != root_name()._path || p._path != "/")) || (p.has_root_name() && p.root_name() != root_name())) { + assign(p); + return *this; + } + if (p.has_root_directory()) { + assign(root_name()); + } + else if ((!has_root_directory() && is_absolute()) || has_filename()) { + _path += preferred_separator; + } + auto iter = p.begin(); + bool first = true; + if (p.has_root_name()) { + ++iter; + } + while (iter != p.end()) { + if (!first && !(!_path.empty() && _path[_path.length() - 1] == preferred_separator)) { + _path += preferred_separator; + } + first = false; + _path += (*iter++).native(); + } + check_long_path(); + return *this; +} + +GHC_INLINE void path::append_name(const value_type* name) +{ + if (_path.empty()) { + this->operator/=(path(name)); + } + else { + if (_path.back() != path::preferred_separator) { + _path.push_back(path::preferred_separator); + } + _path += name; + check_long_path(); + } +} + +#endif // GHC_EXPAND_IMPL + +template +inline path& path::operator/=(const Source& source) +{ + return append(source); +} + +template +inline path& path::append(const Source& source) +{ + return this->operator/=(path(source)); +} + +template <> +inline path& path::append(const path& p) +{ + return this->operator/=(p); +} + +template +inline path& path::append(InputIterator first, InputIterator last) +{ + std::basic_string::value_type> part(first, last); + return append(part); +} + +#ifdef GHC_EXPAND_IMPL + +//----------------------------------------------------------------------------- +// [fs.path.concat] concatenation + +GHC_INLINE path& path::operator+=(const path& x) +{ + return concat(x._path); +} + +GHC_INLINE path& path::operator+=(const string_type& x) +{ + return concat(x); +} + +#ifdef GHC_WITH_STRING_VIEW +GHC_INLINE path& path::operator+=(basic_string_view x) +{ + return concat(x); +} +#endif + +GHC_INLINE path& path::operator+=(const value_type* x) +{ +#ifdef GHC_WITH_STRING_VIEW + basic_string_view part(x); +#else + string_type part(x); +#endif + return concat(part); +} + +GHC_INLINE path& path::operator+=(value_type x) +{ +#ifdef GHC_OS_WINDOWS + if (x == generic_separator) { + x = preferred_separator; + } +#endif + if (_path.empty() || _path.back() != preferred_separator) { + _path += x; + } + check_long_path(); + return *this; +} + +#endif // GHC_EXPAND_IMPL + +template +inline path::path_from_string& path::operator+=(const Source& x) +{ + return concat(x); +} + +template +inline path::path_type_EcharT& path::operator+=(EcharT x) +{ +#ifdef GHC_WITH_STRING_VIEW + basic_string_view part(&x, 1); +#else + std::basic_string part(1, x); +#endif + concat(part); + return *this; +} + +template +inline path& path::concat(const Source& x) +{ + path p(x); + _path += p._path; + postprocess_path_with_format(native_format); + return *this; +} +template +inline path& path::concat(InputIterator first, InputIterator last) +{ + _path.append(first, last); + postprocess_path_with_format(native_format); + return *this; +} + +#ifdef GHC_EXPAND_IMPL + +//----------------------------------------------------------------------------- +// [fs.path.modifiers] modifiers +GHC_INLINE void path::clear() noexcept +{ + _path.clear(); +#if defined(GHC_OS_WINDOWS) && defined(GHC_WIN_AUTO_PREFIX_LONG_PATH) + _prefixLength = 0; +#endif +} + +GHC_INLINE path& path::make_preferred() +{ + // as this filesystem implementation only uses generic_format + // internally, this must be a no-op + return *this; +} + +GHC_INLINE path& path::remove_filename() +{ + if (has_filename()) { + _path.erase(_path.size() - filename()._path.size()); + } + return *this; +} + +GHC_INLINE path& path::replace_filename(const path& replacement) +{ + remove_filename(); + return append(replacement); +} + +GHC_INLINE path& path::replace_extension(const path& replacement) +{ + if (has_extension()) { + _path.erase(_path.size() - extension()._path.size()); + } + if (!replacement.empty() && replacement._path[0] != '.') { + _path += '.'; + } + return concat(replacement); +} + +GHC_INLINE void path::swap(path& rhs) noexcept +{ + _path.swap(rhs._path); +#if defined(GHC_OS_WINDOWS) && defined(GHC_WIN_AUTO_PREFIX_LONG_PATH) + std::swap(_prefixLength, rhs._prefixLength); +#endif +} + +//----------------------------------------------------------------------------- +// [fs.path.native.obs] native format observers +GHC_INLINE const path::string_type& path::native() const noexcept +{ + return _path; +} + +GHC_INLINE const path::value_type* path::c_str() const noexcept +{ + return native().c_str(); +} + +GHC_INLINE path::operator path::string_type() const +{ + return native(); +} + +#endif // GHC_EXPAND_IMPL + +template +inline std::basic_string path::string(const Allocator& a) const +{ +#ifdef GHC_USE_WCHAR_T + return detail::fromWChar>(_path, a); +#else + return detail::fromUtf8>(_path, a); +#endif +} + +#ifdef GHC_EXPAND_IMPL + +GHC_INLINE std::string path::string() const +{ +#ifdef GHC_USE_WCHAR_T + return detail::toUtf8(native()); +#else + return native(); +#endif +} + +GHC_INLINE std::wstring path::wstring() const +{ +#ifdef GHC_USE_WCHAR_T + return native(); +#else + return detail::fromUtf8(native()); +#endif +} + +#if defined(__cpp_lib_char8_t) && !defined(GHC_FILESYSTEM_ENFORCE_CPP17_API) +GHC_INLINE std::u8string path::u8string() const +{ +#ifdef GHC_USE_WCHAR_T + return std::u8string(reinterpret_cast(detail::toUtf8(native()).c_str())); +#else + return std::u8string(reinterpret_cast(c_str())); +#endif +} +#else +GHC_INLINE std::string path::u8string() const +{ +#ifdef GHC_USE_WCHAR_T + return detail::toUtf8(native()); +#else + return native(); +#endif +} +#endif + +GHC_INLINE std::u16string path::u16string() const +{ + // TODO: optimize + return detail::fromUtf8(string()); +} + +GHC_INLINE std::u32string path::u32string() const +{ + // TODO: optimize + return detail::fromUtf8(string()); +} + +#endif // GHC_EXPAND_IMPL + +//----------------------------------------------------------------------------- +// [fs.path.generic.obs] generic format observers +template +inline std::basic_string path::generic_string(const Allocator& a) const +{ +#ifdef GHC_OS_WINDOWS +#ifdef GHC_USE_WCHAR_T + auto result = detail::fromWChar, path::string_type>(_path, a); +#else + auto result = detail::fromUtf8>(_path, a); +#endif + for (auto& c : result) { + if (c == preferred_separator) { + c = generic_separator; + } + } + return result; +#else + return detail::fromUtf8>(_path, a); +#endif +} + +#ifdef GHC_EXPAND_IMPL + +GHC_INLINE std::string path::generic_string() const +{ +#ifdef GHC_OS_WINDOWS + return generic_string(); +#else + return _path; +#endif +} + +GHC_INLINE std::wstring path::generic_wstring() const +{ +#ifdef GHC_OS_WINDOWS + return generic_string(); +#else + return detail::fromUtf8(_path); +#endif +} // namespace filesystem + +#if defined(__cpp_lib_char8_t) && !defined(GHC_FILESYSTEM_ENFORCE_CPP17_API) +GHC_INLINE std::u8string path::generic_u8string() const +{ +#ifdef GHC_OS_WINDOWS + return generic_string(); +#else + return std::u8string(reinterpret_cast(_path.c_str())); +#endif +} +#else +GHC_INLINE std::string path::generic_u8string() const +{ +#ifdef GHC_OS_WINDOWS + return generic_string(); +#else + return _path; +#endif +} +#endif + +GHC_INLINE std::u16string path::generic_u16string() const +{ +#ifdef GHC_OS_WINDOWS + return generic_string(); +#else + return detail::fromUtf8(_path); +#endif +} + +GHC_INLINE std::u32string path::generic_u32string() const +{ +#ifdef GHC_OS_WINDOWS + return generic_string(); +#else + return detail::fromUtf8(_path); +#endif +} + +//----------------------------------------------------------------------------- +// [fs.path.compare] compare +GHC_INLINE int path::compare(const path& p) const noexcept +{ +#ifdef LWG_2936_BEHAVIOUR + auto rnl1 = root_name_length(); + auto rnl2 = p.root_name_length(); +#ifdef GHC_OS_WINDOWS + auto rnc = detail::compare_simple_insensitive(_path.c_str(), rnl1, p._path.c_str(), rnl2); +#else + auto rnc = _path.compare(0, rnl1, p._path, 0, (std::min(rnl1, rnl2))); +#endif + if (rnc) { + return rnc; + } + bool hrd1 = has_root_directory(), hrd2 = p.has_root_directory(); + if (hrd1 != hrd2) { + return hrd1 ? 1 : -1; + } + if (hrd1) { + ++rnl1; + ++rnl2; + } + auto iter1 = _path.begin() + static_cast(rnl1); + auto iter2 = p._path.begin() + static_cast(rnl2); + while (iter1 != _path.end() && iter2 != p._path.end() && *iter1 == *iter2) { + ++iter1; + ++iter2; + } + if (iter1 == _path.end()) { + return iter2 == p._path.end() ? 0 : -1; + } + if (iter2 == p._path.end()) { + return 1; + } + if (*iter1 == preferred_separator) { + return -1; + } + if (*iter2 == preferred_separator) { + return 1; + } + return *iter1 < *iter2 ? -1 : 1; +#else // LWG_2936_BEHAVIOUR +#ifdef GHC_OS_WINDOWS + auto rnl1 = root_name_length(); + auto rnl2 = p.root_name_length(); + auto rnc = detail::compare_simple_insensitive(_path.c_str(), rnl1, p._path.c_str(), rnl2); + if (rnc) { + return rnc; + } + return _path.compare(rnl1, std::string::npos, p._path, rnl2, std::string::npos); +#else + return _path.compare(p._path); +#endif +#endif +} + +GHC_INLINE int path::compare(const string_type& s) const +{ + return compare(path(s)); +} + +#ifdef GHC_WITH_STRING_VIEW +GHC_INLINE int path::compare(basic_string_view s) const +{ + return compare(path(s)); +} +#endif + +GHC_INLINE int path::compare(const value_type* s) const +{ + return compare(path(s)); +} + +//----------------------------------------------------------------------------- +// [fs.path.decompose] decomposition +#ifdef GHC_OS_WINDOWS +GHC_INLINE void path::handle_prefixes() +{ +#if defined(GHC_WIN_AUTO_PREFIX_LONG_PATH) + _prefixLength = 0; + if (_path.length() >= 6 && _path[2] == '?' && std::toupper(static_cast(_path[4])) >= 'A' && std::toupper(static_cast(_path[4])) <= 'Z' && _path[5] == ':') { + if (detail::startsWith(_path, impl_string_type(GHC_PLATFORM_LITERAL("\\\\?\\"))) || detail::startsWith(_path, impl_string_type(GHC_PLATFORM_LITERAL("\\??\\")))) { + _prefixLength = 4; + } + } +#endif // GHC_WIN_AUTO_PREFIX_LONG_PATH +} +#endif + +GHC_INLINE path::string_type::size_type path::root_name_length() const noexcept +{ +#ifdef GHC_OS_WINDOWS + if (_path.length() >= _prefixLength + 2 && std::toupper(static_cast(_path[_prefixLength])) >= 'A' && std::toupper(static_cast(_path[_prefixLength])) <= 'Z' && _path[_prefixLength + 1] == ':') { + return 2; + } +#endif + if (_path.length() > _prefixLength + 2 && _path[_prefixLength] == preferred_separator && _path[_prefixLength + 1] == preferred_separator && _path[_prefixLength + 2] != preferred_separator && std::isprint(_path[_prefixLength + 2])) { + impl_string_type::size_type pos = _path.find(preferred_separator, _prefixLength + 3); + if (pos == impl_string_type::npos) { + return _path.length(); + } + else { + return pos; + } + } + return 0; +} + +GHC_INLINE path path::root_name() const +{ + return path(_path.substr(_prefixLength, root_name_length()), native_format); +} + +GHC_INLINE path path::root_directory() const +{ + if (has_root_directory()) { + static const path _root_dir(std::string(1, preferred_separator), native_format); + return _root_dir; + } + return path(); +} + +GHC_INLINE path path::root_path() const +{ + return path(root_name().string() + root_directory().string(), native_format); +} + +GHC_INLINE path path::relative_path() const +{ + auto rootPathLen = _prefixLength + root_name_length() + (has_root_directory() ? 1 : 0); + return path(_path.substr((std::min)(rootPathLen, _path.length())), generic_format); +} + +GHC_INLINE path path::parent_path() const +{ + auto rootPathLen = _prefixLength + root_name_length() + (has_root_directory() ? 1 : 0); + if (rootPathLen < _path.length()) { + if (empty()) { + return path(); + } + else { + auto piter = end(); + auto iter = piter.decrement(_path.end()); + if (iter > _path.begin() + static_cast(rootPathLen) && *iter != preferred_separator) { + --iter; + } + return path(_path.begin(), iter, native_format); + } + } + else { + return *this; + } +} + +GHC_INLINE path path::filename() const +{ + return !has_relative_path() ? path() : path(*--end()); +} + +GHC_INLINE path path::stem() const +{ + impl_string_type fn = filename().native(); + if (fn != "." && fn != "..") { + impl_string_type::size_type pos = fn.rfind('.'); + if (pos != impl_string_type::npos && pos > 0) { + return path{fn.substr(0, pos), native_format}; + } + } + return path{fn, native_format}; +} + +GHC_INLINE path path::extension() const +{ + if (has_relative_path()) { + auto iter = end(); + const auto& fn = *--iter; + impl_string_type::size_type pos = fn._path.rfind('.'); + if (pos != std::string::npos && pos > 0) { + return path(fn._path.substr(pos), native_format); + } + } + return path(); +} + +#ifdef GHC_OS_WINDOWS +namespace detail { +GHC_INLINE bool has_executable_extension(const path& p) +{ + if (p.has_relative_path()) { + auto iter = p.end(); + const auto& fn = *--iter; + auto pos = fn._path.find_last_of('.'); + if (pos == std::string::npos || pos == 0 || fn._path.length() - pos != 3) { + return false; + } + 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")) || + detail::equals_simple_insensitive(ext, GHC_PLATFORM_LITERAL("com"))) { + return true; + } + } + return false; +} +} // namespace detail +#endif + +//----------------------------------------------------------------------------- +// [fs.path.query] query +GHC_INLINE bool path::empty() const noexcept +{ + return _path.empty(); +} + +GHC_INLINE bool path::has_root_name() const +{ + return root_name_length() > 0; +} + +GHC_INLINE bool path::has_root_directory() const +{ + auto rootLen = _prefixLength + root_name_length(); + return (_path.length() > rootLen && _path[rootLen] == preferred_separator); +} + +GHC_INLINE bool path::has_root_path() const +{ + return has_root_name() || has_root_directory(); +} + +GHC_INLINE bool path::has_relative_path() const +{ + auto rootPathLen = _prefixLength + root_name_length() + (has_root_directory() ? 1 : 0); + return rootPathLen < _path.length(); +} + +GHC_INLINE bool path::has_parent_path() const +{ + return !parent_path().empty(); +} + +GHC_INLINE bool path::has_filename() const +{ + return has_relative_path() && !filename().empty(); +} + +GHC_INLINE bool path::has_stem() const +{ + return !stem().empty(); +} + +GHC_INLINE bool path::has_extension() const +{ + return !extension().empty(); +} + +GHC_INLINE bool path::is_absolute() const +{ +#ifdef GHC_OS_WINDOWS + return has_root_name() && has_root_directory(); +#else + return has_root_directory(); +#endif +} + +GHC_INLINE bool path::is_relative() const +{ + return !is_absolute(); +} + +//----------------------------------------------------------------------------- +// [fs.path.gen] generation +GHC_INLINE path path::lexically_normal() const +{ + path dest; + bool lastDotDot = false; + for (string_type s : *this) { + if (s == ".") { + dest /= ""; + continue; + } + else if (s == ".." && !dest.empty()) { + auto root = root_path(); + if (dest == root) { + continue; + } + else if (*(--dest.end()) != "..") { + if (dest._path.back() == preferred_separator) { + dest._path.pop_back(); + } + dest.remove_filename(); + continue; + } + } + if (!(s.empty() && lastDotDot)) { + dest /= s; + } + lastDotDot = s == ".."; + } + if (dest.empty()) { + dest = "."; + } + return dest; +} + +GHC_INLINE path path::lexically_relative(const path& base) const +{ + if (root_name() != base.root_name() || is_absolute() != base.is_absolute() || (!has_root_directory() && base.has_root_directory())) { + return path(); + } + const_iterator a = begin(), b = base.begin(); + while (a != end() && b != base.end() && *a == *b) { + ++a; + ++b; + } + if (a == end() && b == base.end()) { + return path("."); + } + int count = 0; + for (const auto& element : input_iterator_range(b, base.end())) { + if (element != "." && element != "" && element != "..") { + ++count; + } + else if (element == "..") { + --count; + } + } + if (count < 0) { + return path(); + } + path result; + for (int i = 0; i < count; ++i) { + result /= ".."; + } + for (const auto& element : input_iterator_range(a, end())) { + result /= element; + } + return result; +} + +GHC_INLINE path path::lexically_proximate(const path& base) const +{ + path result = lexically_relative(base); + return result.empty() ? *this : result; +} + +//----------------------------------------------------------------------------- +// [fs.path.itr] iterators +GHC_INLINE path::iterator::iterator() {} + +GHC_INLINE path::iterator::iterator(const path& p, const impl_string_type::const_iterator& pos) + : _first(p._path.begin()) + , _last(p._path.end()) + , _prefix(_first + static_cast(p._prefixLength)) + , _root(p.has_root_directory() ? _first + static_cast(p._prefixLength + p.root_name_length()) : _last) + , _iter(pos) +{ + if (pos != _last) { + updateCurrent(); + } +} + +GHC_INLINE path::impl_string_type::const_iterator path::iterator::increment(const path::impl_string_type::const_iterator& pos) const +{ + path::impl_string_type::const_iterator i = pos; + bool fromStart = i == _first || i == _prefix; + if (i != _last) { + if (fromStart && i == _first && _prefix > _first) { + i = _prefix; + } + else if (*i++ == preferred_separator) { + // we can only sit on a slash if it is a network name or a root + if (i != _last && *i == preferred_separator) { + if (fromStart && !(i + 1 != _last && *(i + 1) == preferred_separator)) { + // leadind double slashes detected, treat this and the + // following until a slash as one unit + i = std::find(++i, _last, preferred_separator); + } + else { + // skip redundant slashes + while (i != _last && *i == preferred_separator) { + ++i; + } + } + } + } + else { + if (fromStart && i != _last && *i == ':') { + ++i; + } + else { + i = std::find(i, _last, preferred_separator); + } + } + } + return i; +} + +GHC_INLINE path::impl_string_type::const_iterator path::iterator::decrement(const path::impl_string_type::const_iterator& pos) const +{ + path::impl_string_type::const_iterator i = pos; + if (i != _first) { + --i; + // if this is now the root slash or the trailing slash, we are done, + // else check for network name + if (i != _root && (pos != _last || *i != preferred_separator)) { +#ifdef GHC_OS_WINDOWS + static const impl_string_type seps = GHC_PLATFORM_LITERAL("\\:"); + i = std::find_first_of(std::reverse_iterator(i), std::reverse_iterator(_first), seps.begin(), seps.end()).base(); + if (i > _first && *i == ':') { + i++; + } +#else + i = std::find(std::reverse_iterator(i), std::reverse_iterator(_first), preferred_separator).base(); +#endif + // Now we have to check if this is a network name + if (i - _first == 2 && *_first == preferred_separator && *(_first + 1) == preferred_separator) { + i -= 2; + } + } + } + return i; +} + +GHC_INLINE void path::iterator::updateCurrent() +{ + if ((_iter == _last) || (_iter != _first && _iter != _last && (*_iter == preferred_separator && _iter != _root) && (_iter + 1 == _last))) { + _current.clear(); + } + else { + _current.assign(_iter, increment(_iter)); + } +} + +GHC_INLINE path::iterator& path::iterator::operator++() +{ + _iter = increment(_iter); + while (_iter != _last && // we didn't reach the end + _iter != _root && // this is not a root position + *_iter == preferred_separator && // we are on a separator + (_iter + 1) != _last // the slash is not the last char + ) { + ++_iter; + } + updateCurrent(); + return *this; +} + +GHC_INLINE path::iterator path::iterator::operator++(int) +{ + path::iterator i{*this}; + ++(*this); + return i; +} + +GHC_INLINE path::iterator& path::iterator::operator--() +{ + _iter = decrement(_iter); + updateCurrent(); + return *this; +} + +GHC_INLINE path::iterator path::iterator::operator--(int) +{ + auto i = *this; + --(*this); + return i; +} + +GHC_INLINE bool path::iterator::operator==(const path::iterator& other) const +{ + return _iter == other._iter; +} + +GHC_INLINE bool path::iterator::operator!=(const path::iterator& other) const +{ + return _iter != other._iter; +} + +GHC_INLINE path::iterator::reference path::iterator::operator*() const +{ + return _current; +} + +GHC_INLINE path::iterator::pointer path::iterator::operator->() const +{ + return &_current; +} + +GHC_INLINE path::iterator path::begin() const +{ + return iterator(*this, _path.begin()); +} + +GHC_INLINE path::iterator path::end() const +{ + return iterator(*this, _path.end()); +} + +//----------------------------------------------------------------------------- +// [fs.path.nonmember] path non-member functions +GHC_INLINE void swap(path& lhs, path& rhs) noexcept +{ + swap(lhs._path, rhs._path); +} + +GHC_INLINE size_t hash_value(const path& p) noexcept +{ + return std::hash()(p.generic_string()); +} + +#ifdef GHC_HAS_THREEWAY_COMP +GHC_INLINE std::strong_ordering operator<=>(const path& lhs, const path& rhs) noexcept +{ + return lhs.compare(rhs) <=> 0; +} +#endif + +GHC_INLINE bool operator==(const path& lhs, const path& rhs) noexcept +{ + return lhs.compare(rhs) == 0; +} + +GHC_INLINE bool operator!=(const path& lhs, const path& rhs) noexcept +{ + return !(lhs == rhs); +} + +GHC_INLINE bool operator<(const path& lhs, const path& rhs) noexcept +{ + return lhs.compare(rhs) < 0; +} + +GHC_INLINE bool operator<=(const path& lhs, const path& rhs) noexcept +{ + return lhs.compare(rhs) <= 0; +} + +GHC_INLINE bool operator>(const path& lhs, const path& rhs) noexcept +{ + return lhs.compare(rhs) > 0; +} + +GHC_INLINE bool operator>=(const path& lhs, const path& rhs) noexcept +{ + return lhs.compare(rhs) >= 0; +} + +GHC_INLINE path operator/(const path& lhs, const path& rhs) +{ + path result(lhs); + result /= rhs; + return result; +} + +#endif // GHC_EXPAND_IMPL + +//----------------------------------------------------------------------------- +// [fs.path.io] path inserter and extractor +template +inline std::basic_ostream& operator<<(std::basic_ostream& os, const path& p) +{ + os << "\""; + auto ps = p.string(); + for (auto c : ps) { + if (c == '"' || c == '\\') { + os << '\\'; + } + os << c; + } + os << "\""; + return os; +} + +template +inline std::basic_istream& operator>>(std::basic_istream& is, path& p) +{ + std::basic_string tmp; + charT c; + is >> c; + if (c == '"') { + auto sf = is.flags(); + is >> std::noskipws; + while (is) { + auto c2 = is.get(); + if (is) { + if (c2 == '\\') { + c2 = is.get(); + if (is) { + tmp += static_cast(c2); + } + } + else if (c2 == '"') { + break; + } + else { + tmp += static_cast(c2); + } + } + } + if ((sf & std::ios_base::skipws) == std::ios_base::skipws) { + is >> std::skipws; + } + p = path(tmp); + } + else { + is >> tmp; + p = path(static_cast(c) + tmp); + } + return is; +} + +#ifdef GHC_EXPAND_IMPL + +//----------------------------------------------------------------------------- +// [fs.class.filesystem_error] Class filesystem_error +GHC_INLINE filesystem_error::filesystem_error(const std::string& what_arg, std::error_code ec) + : std::system_error(ec, what_arg) + , _what_arg(what_arg) + , _ec(ec) +{ +} + +GHC_INLINE filesystem_error::filesystem_error(const std::string& what_arg, const path& p1, std::error_code ec) + : std::system_error(ec, what_arg) + , _what_arg(what_arg) + , _ec(ec) + , _p1(p1) +{ + if (!_p1.empty()) { + _what_arg += ": '" + _p1.string() + "'"; + } +} + +GHC_INLINE filesystem_error::filesystem_error(const std::string& what_arg, const path& p1, const path& p2, std::error_code ec) + : std::system_error(ec, what_arg) + , _what_arg(what_arg) + , _ec(ec) + , _p1(p1) + , _p2(p2) +{ + if (!_p1.empty()) { + _what_arg += ": '" + _p1.string() + "'"; + } + if (!_p2.empty()) { + _what_arg += ", '" + _p2.string() + "'"; + } +} + +GHC_INLINE const path& filesystem_error::path1() const noexcept +{ + return _p1; +} + +GHC_INLINE const path& filesystem_error::path2() const noexcept +{ + return _p2; +} + +GHC_INLINE const char* filesystem_error::what() const noexcept +{ + return _what_arg.c_str(); +} + +//----------------------------------------------------------------------------- +// [fs.op.funcs] filesystem operations +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE path absolute(const path& p) +{ + std::error_code ec; + path result = absolute(p, ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), p, ec); + } + return result; +} +#endif + +GHC_INLINE path absolute(const path& p, std::error_code& ec) +{ + ec.clear(); +#ifdef GHC_OS_WINDOWS + if (p.empty()) { + return absolute(current_path(ec), ec) / ""; + } + ULONG size = ::GetFullPathNameW(GHC_NATIVEWP(p), 0, 0, 0); + if (size) { + std::vector buf(size, 0); + ULONG s2 = GetFullPathNameW(GHC_NATIVEWP(p), size, buf.data(), nullptr); + if (s2 && s2 < size) { + path result = path(std::wstring(buf.data(), s2)); + if (p.filename() == ".") { + result /= "."; + } + return result; + } + } + ec = detail::make_system_error(); + return path(); +#else + path base = current_path(ec); + if (!ec) { + if (p.empty()) { + return base / p; + } + if (p.has_root_name()) { + if (p.has_root_directory()) { + return p; + } + else { + return p.root_name() / base.root_directory() / base.relative_path() / p.relative_path(); + } + } + else { + if (p.has_root_directory()) { + return base.root_name() / p; + } + else { + return base / p; + } + } + } + ec = detail::make_system_error(); + return path(); +#endif +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE path canonical(const path& p) +{ + std::error_code ec; + auto result = canonical(p, ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), p, ec); + } + return result; +} +#endif + +GHC_INLINE path canonical(const path& p, std::error_code& ec) +{ + if (p.empty()) { + ec = detail::make_error_code(detail::portable_error::not_found); + return path(); + } + path work = p.is_absolute() ? p : absolute(p, ec); + path result; + + auto fs = status(work, ec); + if (ec) { + return path(); + } + if (fs.type() == file_type::not_found) { + ec = detail::make_error_code(detail::portable_error::not_found); + return path(); + } + bool redo; + do { + auto rootPathLen = work._prefixLength + work.root_name_length() + (work.has_root_directory() ? 1 : 0); + redo = false; + result.clear(); + for (auto pe : work) { + if (pe.empty() || pe == ".") { + continue; + } + else if (pe == "..") { + result = result.parent_path(); + continue; + } + else if ((result / pe).string().length() <= rootPathLen) { + result /= pe; + continue; + } + auto sls = symlink_status(result / pe, ec); + if (ec) { + return path(); + } + if (is_symlink(sls)) { + redo = true; + auto target = read_symlink(result / pe, ec); + if (ec) { + return path(); + } + if (target.is_absolute()) { + result = target; + continue; + } + else { + result /= target; + continue; + } + } + else { + result /= pe; + } + } + work = result; + } while (redo); + ec.clear(); + return result; +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE void copy(const path& from, const path& to) +{ + copy(from, to, copy_options::none); +} + +GHC_INLINE void copy(const path& from, const path& to, copy_options options) +{ + std::error_code ec; + copy(from, to, options, ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), from, to, ec); + } +} +#endif + +GHC_INLINE void copy(const path& from, const path& to, std::error_code& ec) noexcept +{ + copy(from, to, copy_options::none, ec); +} + +GHC_INLINE void copy(const path& from, const path& to, copy_options options, std::error_code& ec) noexcept +{ + std::error_code tec; + file_status fs_from, fs_to; + ec.clear(); + if ((options & (copy_options::skip_symlinks | copy_options::copy_symlinks | copy_options::create_symlinks)) != copy_options::none) { + fs_from = symlink_status(from, ec); + } + else { + fs_from = status(from, ec); + } + if (!exists(fs_from)) { + if (!ec) { + ec = detail::make_error_code(detail::portable_error::not_found); + } + return; + } + if ((options & (copy_options::skip_symlinks | copy_options::create_symlinks)) != copy_options::none) { + fs_to = symlink_status(to, tec); + } + else { + fs_to = status(to, tec); + } + if (is_other(fs_from) || is_other(fs_to) || (is_directory(fs_from) && is_regular_file(fs_to)) || (exists(fs_to) && equivalent(from, to, ec))) { + ec = detail::make_error_code(detail::portable_error::invalid_argument); + } + else if (is_symlink(fs_from)) { + if ((options & copy_options::skip_symlinks) == copy_options::none) { + if (!exists(fs_to) && (options & copy_options::copy_symlinks) != copy_options::none) { + copy_symlink(from, to, ec); + } + else { + ec = detail::make_error_code(detail::portable_error::invalid_argument); + } + } + } + else if (is_regular_file(fs_from)) { + if ((options & copy_options::directories_only) == copy_options::none) { + if ((options & copy_options::create_symlinks) != copy_options::none) { + create_symlink(from.is_absolute() ? from : canonical(from, ec), to, ec); + } +#ifndef GHC_OS_WEB + else if ((options & copy_options::create_hard_links) != copy_options::none) { + create_hard_link(from, to, ec); + } +#endif + else if (is_directory(fs_to)) { + copy_file(from, to / from.filename(), options, ec); + } + else { + copy_file(from, to, options, ec); + } + } + } +#ifdef LWG_2682_BEHAVIOUR + else if (is_directory(fs_from) && (options & copy_options::create_symlinks) != copy_options::none) { + ec = detail::make_error_code(detail::portable_error::is_a_directory); + } +#endif + else if (is_directory(fs_from) && (options == copy_options::none || (options & copy_options::recursive) != copy_options::none)) { + if (!exists(fs_to)) { + create_directory(to, from, ec); + if (ec) { + return; + } + } + for (auto iter = directory_iterator(from, ec); iter != directory_iterator(); iter.increment(ec)) { + if (!ec) { + copy(iter->path(), to / iter->path().filename(), options | static_cast(0x8000), ec); + } + if (ec) { + return; + } + } + } + return; +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE bool copy_file(const path& from, const path& to) +{ + return copy_file(from, to, copy_options::none); +} + +GHC_INLINE bool copy_file(const path& from, const path& to, copy_options option) +{ + std::error_code ec; + auto result = copy_file(from, to, option, ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), from, to, ec); + } + return result; +} +#endif + +GHC_INLINE bool copy_file(const path& from, const path& to, std::error_code& ec) noexcept +{ + return copy_file(from, to, copy_options::none, ec); +} + +GHC_INLINE bool copy_file(const path& from, const path& to, copy_options options, std::error_code& ec) noexcept +{ + std::error_code tecf, tect; + auto sf = status(from, tecf); + auto st = status(to, tect); + bool overwrite = false; + ec.clear(); + if (!is_regular_file(sf)) { + ec = tecf; + return false; + } + if (exists(st) && (!is_regular_file(st) || equivalent(from, to, ec) || (options & (copy_options::skip_existing | copy_options::overwrite_existing | copy_options::update_existing)) == copy_options::none)) { + ec = tect ? tect : detail::make_error_code(detail::portable_error::exists); + return false; + } + if (exists(st)) { + if ((options & copy_options::update_existing) == copy_options::update_existing) { + auto from_time = last_write_time(from, ec); + if (ec) { + ec = detail::make_system_error(); + return false; + } + auto to_time = last_write_time(to, ec); + if (ec) { + ec = detail::make_system_error(); + return false; + } + if (from_time <= to_time) { + return false; + } + } + overwrite = true; + } +#ifdef GHC_OS_WINDOWS + if (!::CopyFileW(GHC_NATIVEWP(from), GHC_NATIVEWP(to), !overwrite)) { + ec = detail::make_system_error(); + return false; + } + return true; +#else + std::vector buffer(16384, '\0'); + int in = -1, out = -1; + if ((in = ::open(from.c_str(), O_RDONLY)) < 0) { + ec = detail::make_system_error(); + return false; + } + int mode = O_CREAT | O_WRONLY | O_TRUNC; + if (!overwrite) { + mode |= O_EXCL; + } + if ((out = ::open(to.c_str(), mode, static_cast(sf.permissions() & perms::all))) < 0) { + ec = detail::make_system_error(); + ::close(in); + return false; + } + ssize_t br, bw; + while ((br = ::read(in, buffer.data(), buffer.size())) > 0) { + ssize_t offset = 0; + do { + if ((bw = ::write(out, buffer.data() + offset, static_cast(br))) > 0) { + br -= bw; + offset += bw; + } + else if (bw < 0) { + ec = detail::make_system_error(); + ::close(in); + ::close(out); + return false; + } + } while (br); + } + ::close(in); + ::close(out); + return true; +#endif +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE void copy_symlink(const path& existing_symlink, const path& new_symlink) +{ + std::error_code ec; + copy_symlink(existing_symlink, new_symlink, ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), existing_symlink, new_symlink, ec); + } +} +#endif + +GHC_INLINE void copy_symlink(const path& existing_symlink, const path& new_symlink, std::error_code& ec) noexcept +{ + ec.clear(); + auto to = read_symlink(existing_symlink, ec); + if (!ec) { + if (exists(to, ec) && is_directory(to, ec)) { + create_directory_symlink(to, new_symlink, ec); + } + else { + create_symlink(to, new_symlink, ec); + } + } +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE bool create_directories(const path& p) +{ + std::error_code ec; + auto result = create_directories(p, ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), p, ec); + } + return result; +} +#endif + +GHC_INLINE bool create_directories(const path& p, std::error_code& ec) noexcept +{ + path current; + ec.clear(); + bool didCreate = false; + auto rootPathLen = p._prefixLength + p.root_name_length() + (p.has_root_directory() ? 1 : 0); + current = p.native().substr(0, rootPathLen); + path folders(p._path.substr(rootPathLen)); + for (path::string_type part : folders) { + current /= part; + std::error_code tec; + auto fs = status(current, tec); + if (tec && fs.type() != file_type::not_found) { + ec = tec; + return false; + } + if (!exists(fs)) { + create_directory(current, ec); + if (ec) { + std::error_code tmp_ec; + if (is_directory(current, tmp_ec)) { + ec.clear(); + } + else { + return false; + } + } + didCreate = true; + } +#ifndef LWG_2935_BEHAVIOUR + else if (!is_directory(fs)) { + ec = detail::make_error_code(detail::portable_error::exists); + return false; + } +#endif + } + return didCreate; +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE bool create_directory(const path& p) +{ + std::error_code ec; + auto result = create_directory(p, path(), ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), p, ec); + } + return result; +} +#endif + +GHC_INLINE bool create_directory(const path& p, std::error_code& ec) noexcept +{ + return create_directory(p, path(), ec); +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE bool create_directory(const path& p, const path& attributes) +{ + std::error_code ec; + auto result = create_directory(p, attributes, ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), p, ec); + } + return result; +} +#endif + +GHC_INLINE bool create_directory(const path& p, const path& attributes, std::error_code& ec) noexcept +{ + std::error_code tec; + ec.clear(); + auto fs = status(p, tec); +#ifdef LWG_2935_BEHAVIOUR + if (status_known(fs) && exists(fs)) { + return false; + } +#else + if (status_known(fs) && exists(fs) && is_directory(fs)) { + return false; + } +#endif +#ifdef GHC_OS_WINDOWS + if (!attributes.empty()) { + if (!::CreateDirectoryExW(GHC_NATIVEWP(attributes), GHC_NATIVEWP(p), NULL)) { + ec = detail::make_system_error(); + return false; + } + } + else if (!::CreateDirectoryW(GHC_NATIVEWP(p), NULL)) { + ec = detail::make_system_error(); + return false; + } +#else + ::mode_t attribs = static_cast(perms::all); + if (!attributes.empty()) { + struct ::stat fileStat; + if (::stat(attributes.c_str(), &fileStat) != 0) { + ec = detail::make_system_error(); + return false; + } + attribs = fileStat.st_mode; + } + if (::mkdir(p.c_str(), attribs) != 0) { + ec = detail::make_system_error(); + return false; + } +#endif + return true; +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE void create_directory_symlink(const path& to, const path& new_symlink) +{ + std::error_code ec; + create_directory_symlink(to, new_symlink, ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), to, new_symlink, ec); + } +} +#endif + +GHC_INLINE void create_directory_symlink(const path& to, const path& new_symlink, std::error_code& ec) noexcept +{ + detail::create_symlink(to, new_symlink, true, ec); +} + +#ifndef GHC_OS_WEB +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE void create_hard_link(const path& to, const path& new_hard_link) +{ + std::error_code ec; + create_hard_link(to, new_hard_link, ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), to, new_hard_link, ec); + } +} +#endif + +GHC_INLINE void create_hard_link(const path& to, const path& new_hard_link, std::error_code& ec) noexcept +{ + detail::create_hardlink(to, new_hard_link, ec); +} +#endif + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE void create_symlink(const path& to, const path& new_symlink) +{ + std::error_code ec; + create_symlink(to, new_symlink, ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), to, new_symlink, ec); + } +} +#endif + +GHC_INLINE void create_symlink(const path& to, const path& new_symlink, std::error_code& ec) noexcept +{ + detail::create_symlink(to, new_symlink, false, ec); +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE path current_path() +{ + std::error_code ec; + auto result = current_path(ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), ec); + } + return result; +} +#endif + +GHC_INLINE path current_path(std::error_code& ec) +{ + ec.clear(); +#ifdef GHC_OS_WINDOWS + DWORD pathlen = ::GetCurrentDirectoryW(0, 0); + std::unique_ptr buffer(new wchar_t[size_t(pathlen) + 1]); + if (::GetCurrentDirectoryW(pathlen, buffer.get()) == 0) { + ec = detail::make_system_error(); + return path(); + } + return path(std::wstring(buffer.get()), path::native_format); +#else + size_t pathlen = static_cast(std::max(int(::pathconf(".", _PC_PATH_MAX)), int(PATH_MAX))); + std::unique_ptr buffer(new char[pathlen + 1]); + if (::getcwd(buffer.get(), pathlen) == nullptr) { + ec = detail::make_system_error(); + return path(); + } + return path(buffer.get()); +#endif +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE void current_path(const path& p) +{ + std::error_code ec; + current_path(p, ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), p, ec); + } +} +#endif + +GHC_INLINE void current_path(const path& p, std::error_code& ec) noexcept +{ + ec.clear(); +#ifdef GHC_OS_WINDOWS + if (!::SetCurrentDirectoryW(GHC_NATIVEWP(p))) { + ec = detail::make_system_error(); + } +#else + if (::chdir(p.string().c_str()) == -1) { + ec = detail::make_system_error(); + } +#endif +} + +GHC_INLINE bool exists(file_status s) noexcept +{ + return status_known(s) && s.type() != file_type::not_found; +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE bool exists(const path& p) +{ + return exists(status(p)); +} +#endif + +GHC_INLINE bool exists(const path& p, std::error_code& ec) noexcept +{ + file_status s = status(p, ec); + if (status_known(s)) { + ec.clear(); + } + return exists(s); +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE bool equivalent(const path& p1, const path& p2) +{ + std::error_code ec; + bool result = equivalent(p1, p2, ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), p1, p2, ec); + } + return result; +} +#endif + +GHC_INLINE bool equivalent(const path& p1, const path& p2, std::error_code& ec) noexcept +{ + ec.clear(); +#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)); + 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)); + if (!file1 || !file2) { +#ifdef LWG_2937_BEHAVIOUR + ec = detail::make_system_error(e1 ? e1 : ::GetLastError()); +#else + if (file1 == file2) { + ec = detail::make_system_error(e1 ? e1 : ::GetLastError()); + } +#endif + return false; + } + BY_HANDLE_FILE_INFORMATION inf1, inf2; + if (!::GetFileInformationByHandle(file1.get(), &inf1)) { + ec = detail::make_system_error(); + return false; + } + if (!::GetFileInformationByHandle(file2.get(), &inf2)) { + ec = detail::make_system_error(); + return false; + } + return inf1.ftLastWriteTime.dwLowDateTime == inf2.ftLastWriteTime.dwLowDateTime && inf1.ftLastWriteTime.dwHighDateTime == inf2.ftLastWriteTime.dwHighDateTime && inf1.nFileIndexHigh == inf2.nFileIndexHigh && inf1.nFileIndexLow == inf2.nFileIndexLow && + inf1.nFileSizeHigh == inf2.nFileSizeHigh && inf1.nFileSizeLow == inf2.nFileSizeLow && inf1.dwVolumeSerialNumber == inf2.dwVolumeSerialNumber; +#else + struct ::stat s1, s2; + auto rc1 = ::stat(p1.c_str(), &s1); + auto e1 = errno; + auto rc2 = ::stat(p2.c_str(), &s2); + if (rc1 || rc2) { +#ifdef LWG_2937_BEHAVIOUR + ec = detail::make_system_error(e1 ? e1 : errno); +#else + if (rc1 && rc2) { + ec = detail::make_system_error(e1 ? e1 : errno); + } +#endif + return false; + } + return s1.st_dev == s2.st_dev && s1.st_ino == s2.st_ino && s1.st_size == s2.st_size && s1.st_mtime == s2.st_mtime; +#endif +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE uintmax_t file_size(const path& p) +{ + std::error_code ec; + auto result = file_size(p, ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), p, ec); + } + return result; +} +#endif + +GHC_INLINE uintmax_t file_size(const path& p, std::error_code& ec) noexcept +{ + ec.clear(); +#ifdef GHC_OS_WINDOWS + WIN32_FILE_ATTRIBUTE_DATA attr; + if (!GetFileAttributesExW(GHC_NATIVEWP(p), GetFileExInfoStandard, &attr)) { + ec = detail::make_system_error(); + return static_cast(-1); + } + return static_cast(attr.nFileSizeHigh) << (sizeof(attr.nFileSizeHigh) * 8) | attr.nFileSizeLow; +#else + struct ::stat fileStat; + if (::stat(p.c_str(), &fileStat) == -1) { + ec = detail::make_system_error(); + return static_cast(-1); + } + return static_cast(fileStat.st_size); +#endif +} + +#ifndef GHC_OS_WEB +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE uintmax_t hard_link_count(const path& p) +{ + std::error_code ec; + auto result = hard_link_count(p, ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), p, ec); + } + return result; +} +#endif + +GHC_INLINE uintmax_t hard_link_count(const path& p, std::error_code& ec) noexcept +{ + ec.clear(); +#ifdef GHC_OS_WINDOWS + uintmax_t result = static_cast(-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)); + BY_HANDLE_FILE_INFORMATION inf; + if (!file) { + ec = detail::make_system_error(); + } + else { + if (!::GetFileInformationByHandle(file.get(), &inf)) { + ec = detail::make_system_error(); + } + else { + result = inf.nNumberOfLinks; + } + } + return result; +#else + uintmax_t result = 0; + file_status fs = detail::status_ex(p, ec, nullptr, nullptr, &result, nullptr); + if (fs.type() == file_type::not_found) { + ec = detail::make_error_code(detail::portable_error::not_found); + } + return ec ? static_cast(-1) : result; +#endif +} +#endif + +GHC_INLINE bool is_block_file(file_status s) noexcept +{ + return s.type() == file_type::block; +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE bool is_block_file(const path& p) +{ + return is_block_file(status(p)); +} +#endif + +GHC_INLINE bool is_block_file(const path& p, std::error_code& ec) noexcept +{ + return is_block_file(status(p, ec)); +} + +GHC_INLINE bool is_character_file(file_status s) noexcept +{ + return s.type() == file_type::character; +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE bool is_character_file(const path& p) +{ + return is_character_file(status(p)); +} +#endif + +GHC_INLINE bool is_character_file(const path& p, std::error_code& ec) noexcept +{ + return is_character_file(status(p, ec)); +} + +GHC_INLINE bool is_directory(file_status s) noexcept +{ + return s.type() == file_type::directory; +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE bool is_directory(const path& p) +{ + return is_directory(status(p)); +} +#endif + +GHC_INLINE bool is_directory(const path& p, std::error_code& ec) noexcept +{ + return is_directory(status(p, ec)); +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE bool is_empty(const path& p) +{ + if (is_directory(p)) { + return directory_iterator(p) == directory_iterator(); + } + else { + return file_size(p) == 0; + } +} +#endif + +GHC_INLINE bool is_empty(const path& p, std::error_code& ec) noexcept +{ + auto fs = status(p, ec); + if (ec) { + return false; + } + if (is_directory(fs)) { + directory_iterator iter(p, ec); + if (ec) { + return false; + } + return iter == directory_iterator(); + } + else { + auto sz = file_size(p, ec); + if (ec) { + return false; + } + return sz == 0; + } +} + +GHC_INLINE bool is_fifo(file_status s) noexcept +{ + return s.type() == file_type::fifo; +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE bool is_fifo(const path& p) +{ + return is_fifo(status(p)); +} +#endif + +GHC_INLINE bool is_fifo(const path& p, std::error_code& ec) noexcept +{ + return is_fifo(status(p, ec)); +} + +GHC_INLINE bool is_other(file_status s) noexcept +{ + return exists(s) && !is_regular_file(s) && !is_directory(s) && !is_symlink(s); +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE bool is_other(const path& p) +{ + return is_other(status(p)); +} +#endif + +GHC_INLINE bool is_other(const path& p, std::error_code& ec) noexcept +{ + return is_other(status(p, ec)); +} + +GHC_INLINE bool is_regular_file(file_status s) noexcept +{ + return s.type() == file_type::regular; +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE bool is_regular_file(const path& p) +{ + return is_regular_file(status(p)); +} +#endif + +GHC_INLINE bool is_regular_file(const path& p, std::error_code& ec) noexcept +{ + return is_regular_file(status(p, ec)); +} + +GHC_INLINE bool is_socket(file_status s) noexcept +{ + return s.type() == file_type::socket; +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE bool is_socket(const path& p) +{ + return is_socket(status(p)); +} +#endif + +GHC_INLINE bool is_socket(const path& p, std::error_code& ec) noexcept +{ + return is_socket(status(p, ec)); +} + +GHC_INLINE bool is_symlink(file_status s) noexcept +{ + return s.type() == file_type::symlink; +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE bool is_symlink(const path& p) +{ + return is_symlink(symlink_status(p)); +} +#endif + +GHC_INLINE bool is_symlink(const path& p, std::error_code& ec) noexcept +{ + return is_symlink(symlink_status(p, ec)); +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE file_time_type last_write_time(const path& p) +{ + std::error_code ec; + auto result = last_write_time(p, ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), p, ec); + } + return result; +} +#endif + +GHC_INLINE file_time_type last_write_time(const path& p, std::error_code& ec) noexcept +{ + time_t result = 0; + ec.clear(); + file_status fs = detail::status_ex(p, ec, nullptr, nullptr, nullptr, &result); + return ec ? (file_time_type::min)() : std::chrono::system_clock::from_time_t(result); +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE void last_write_time(const path& p, file_time_type new_time) +{ + std::error_code ec; + last_write_time(p, new_time, ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), p, ec); + } +} +#endif + +GHC_INLINE void last_write_time(const path& p, file_time_type new_time, std::error_code& ec) noexcept +{ + ec.clear(); + auto d = new_time.time_since_epoch(); +#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)); + FILETIME ft; + auto tt = std::chrono::duration_cast(d).count() * 10 + 116444736000000000; + ft.dwLowDateTime = static_cast(tt); + ft.dwHighDateTime = static_cast(tt >> 32); + if (!::SetFileTime(file.get(), 0, 0, &ft)) { + ec = detail::make_system_error(); + } +#elif defined(GHC_OS_MACOS) +#ifdef __MAC_OS_X_VERSION_MIN_REQUIRED +#if __MAC_OS_X_VERSION_MIN_REQUIRED < 101300 + struct ::stat fs; + if (::stat(p.c_str(), &fs) == 0) { + struct ::timeval tv[2]; + tv[0].tv_sec = fs.st_atimespec.tv_sec; + tv[0].tv_usec = static_cast(fs.st_atimespec.tv_nsec / 1000); + tv[1].tv_sec = std::chrono::duration_cast(d).count(); + tv[1].tv_usec = static_cast(std::chrono::duration_cast(d).count() % 1000000); + if (::utimes(p.c_str(), tv) == 0) { + return; + } + } + ec = detail::make_system_error(); + return; +#else + struct ::timespec times[2]; + times[0].tv_sec = 0; + times[0].tv_nsec = UTIME_OMIT; + times[1].tv_sec = std::chrono::duration_cast(d).count(); + times[1].tv_nsec = 0; // std::chrono::duration_cast(d).count() % 1000000000; + if (::utimensat(AT_FDCWD, p.c_str(), times, AT_SYMLINK_NOFOLLOW) != 0) { + ec = detail::make_system_error(); + } + return; +#endif +#endif +#else +#ifndef UTIME_OMIT +#define UTIME_OMIT ((1l << 30) - 2l) +#endif + struct ::timespec times[2]; + times[0].tv_sec = 0; + times[0].tv_nsec = UTIME_OMIT; + times[1].tv_sec = static_cast(std::chrono::duration_cast(d).count()); + times[1].tv_nsec = static_cast(std::chrono::duration_cast(d).count() % 1000000000); +#if defined(__ANDROID_API__) && __ANDROID_API__ < 12 + if (syscall(__NR_utimensat, AT_FDCWD, p.c_str(), times, AT_SYMLINK_NOFOLLOW) != 0) { +#else + if (::utimensat((int)AT_FDCWD, p.c_str(), times, AT_SYMLINK_NOFOLLOW) != 0) { +#endif + ec = detail::make_system_error(); + } + return; +#endif +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE void permissions(const path& p, perms prms, perm_options opts) +{ + std::error_code ec; + permissions(p, prms, opts, ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), p, ec); + } +} +#endif + +GHC_INLINE void permissions(const path& p, perms prms, std::error_code& ec) noexcept +{ + permissions(p, prms, perm_options::replace, ec); +} + +GHC_INLINE void permissions(const path& p, perms prms, perm_options opts, std::error_code& ec) noexcept +{ + if (static_cast(opts & (perm_options::replace | perm_options::add | perm_options::remove)) == 0) { + ec = detail::make_error_code(detail::portable_error::invalid_argument); + return; + } + auto fs = symlink_status(p, ec); + if ((opts & perm_options::replace) != perm_options::replace) { + if ((opts & perm_options::add) == perm_options::add) { + prms = fs.permissions() | prms; + } + else { + prms = fs.permissions() & ~prms; + } + } +#ifdef GHC_OS_WINDOWS +#ifdef __GNUC__ + auto oldAttr = GetFileAttributesW(GHC_NATIVEWP(p)); + if (oldAttr != INVALID_FILE_ATTRIBUTES) { + DWORD newAttr = ((prms & perms::owner_write) == perms::owner_write) ? oldAttr & ~(static_cast(FILE_ATTRIBUTE_READONLY)) : oldAttr | FILE_ATTRIBUTE_READONLY; + if (oldAttr == newAttr || SetFileAttributesW(GHC_NATIVEWP(p), newAttr)) { + return; + } + } + ec = detail::make_system_error(); +#else + int mode = 0; + if ((prms & perms::owner_read) == perms::owner_read) { + mode |= _S_IREAD; + } + if ((prms & perms::owner_write) == perms::owner_write) { + mode |= _S_IWRITE; + } + if (::_wchmod(p.wstring().c_str(), mode) != 0) { + ec = detail::make_system_error(); + } +#endif +#else + if ((opts & perm_options::nofollow) != perm_options::nofollow) { + if (::chmod(p.c_str(), static_cast(prms)) != 0) { + ec = detail::make_system_error(); + } + } +#endif +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE path proximate(const path& p, std::error_code& ec) +{ + auto cp = current_path(ec); + if (!ec) { + return proximate(p, cp, ec); + } + return path(); +} +#endif + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE path proximate(const path& p, const path& base) +{ + return weakly_canonical(p).lexically_proximate(weakly_canonical(base)); +} +#endif + +GHC_INLINE path proximate(const path& p, const path& base, std::error_code& ec) +{ + return weakly_canonical(p, ec).lexically_proximate(weakly_canonical(base, ec)); +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE path read_symlink(const path& p) +{ + std::error_code ec; + auto result = read_symlink(p, ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), p, ec); + } + return result; +} +#endif + +GHC_INLINE path read_symlink(const path& p, std::error_code& ec) +{ + file_status fs = symlink_status(p, ec); + if (fs.type() != file_type::symlink) { + ec = detail::make_error_code(detail::portable_error::invalid_argument); + return path(); + } + auto result = detail::resolveSymlink(p, ec); + return ec ? path() : result; +} + +GHC_INLINE path relative(const path& p, std::error_code& ec) +{ + return relative(p, current_path(ec), ec); +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE path relative(const path& p, const path& base) +{ + return weakly_canonical(p).lexically_relative(weakly_canonical(base)); +} +#endif + +GHC_INLINE path relative(const path& p, const path& base, std::error_code& ec) +{ + return weakly_canonical(p, ec).lexically_relative(weakly_canonical(base, ec)); +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE bool remove(const path& p) +{ + std::error_code ec; + auto result = remove(p, ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), p, ec); + } + return result; +} +#endif + +GHC_INLINE bool remove(const path& p, std::error_code& ec) noexcept +{ + ec.clear(); +#ifdef GHC_OS_WINDOWS +#ifdef GHC_USE_WCHAR_T + auto cstr = p.c_str(); +#else + std::wstring np = detail::fromUtf8(p.u8string()); + auto cstr = np.c_str(); +#endif + DWORD attr = GetFileAttributesW(cstr); + if (attr == INVALID_FILE_ATTRIBUTES) { + auto error = ::GetLastError(); + if (error == ERROR_FILE_NOT_FOUND || error == ERROR_PATH_NOT_FOUND) { + return false; + } + ec = detail::make_system_error(error); + } + else if (attr & FILE_ATTRIBUTE_READONLY) { + auto new_attr = attr & ~static_cast(FILE_ATTRIBUTE_READONLY); + if (!SetFileAttributesW(cstr, new_attr)) { + auto error = ::GetLastError(); + ec = detail::make_system_error(error); + } + } + if (!ec) { + if (attr & FILE_ATTRIBUTE_DIRECTORY) { + if (!RemoveDirectoryW(cstr)) { + ec = detail::make_system_error(); + } + } + else { + if (!DeleteFileW(cstr)) { + ec = detail::make_system_error(); + } + } + } +#else + if (::remove(p.c_str()) == -1) { + auto error = errno; + if (error == ENOENT) { + return false; + } + ec = detail::make_system_error(); + } +#endif + return ec ? false : true; +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE uintmax_t remove_all(const path& p) +{ + std::error_code ec; + auto result = remove_all(p, ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), p, ec); + } + return result; +} +#endif + +GHC_INLINE uintmax_t remove_all(const path& p, std::error_code& ec) noexcept +{ + ec.clear(); + uintmax_t count = 0; + if (p == "/") { + ec = detail::make_error_code(detail::portable_error::not_supported); + return static_cast(-1); + } + std::error_code tec; + auto fs = symlink_status(p, tec); + if (exists(fs) && is_directory(fs)) { + for (auto iter = directory_iterator(p, ec); iter != directory_iterator(); iter.increment(ec)) { + if (ec && !detail::is_not_found_error(ec)) { + break; + } + bool is_symlink_result = iter->is_symlink(ec); + if (ec) + return static_cast(-1); + if (!is_symlink_result && iter->is_directory(ec)) { + count += remove_all(iter->path(), ec); + if (ec) { + return static_cast(-1); + } + } + else { + if (!ec) { + remove(iter->path(), ec); + } + if (ec) { + return static_cast(-1); + } + ++count; + } + } + } + if (!ec) { + if (remove(p, ec)) { + ++count; + } + } + if (ec) { + return static_cast(-1); + } + return count; +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE void rename(const path& from, const path& to) +{ + std::error_code ec; + rename(from, to, ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), from, to, ec); + } +} +#endif + +GHC_INLINE void rename(const path& from, const path& to, std::error_code& ec) noexcept +{ + ec.clear(); +#ifdef GHC_OS_WINDOWS + if (from != to) { + if (!MoveFileExW(GHC_NATIVEWP(from), GHC_NATIVEWP(to), (DWORD)MOVEFILE_REPLACE_EXISTING)) { + ec = detail::make_system_error(); + } + } +#else + if (from != to) { + if (::rename(from.c_str(), to.c_str()) != 0) { + ec = detail::make_system_error(); + } + } +#endif +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE void resize_file(const path& p, uintmax_t size) +{ + std::error_code ec; + resize_file(p, size, ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), p, ec); + } +} +#endif + +GHC_INLINE void resize_file(const path& p, uintmax_t size, std::error_code& ec) noexcept +{ + ec.clear(); +#ifdef GHC_OS_WINDOWS + LARGE_INTEGER lisize; + lisize.QuadPart = static_cast(size); + if (lisize.QuadPart < 0) { +#ifdef ERROR_FILE_TOO_LARGE + ec = detail::make_system_error(ERROR_FILE_TOO_LARGE); +#else + ec = detail::make_system_error(223); +#endif + return; + } + detail::unique_handle file(CreateFileW(GHC_NATIVEWP(p), GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL)); + if (!file) { + ec = detail::make_system_error(); + } + else if (SetFilePointerEx(file.get(), lisize, NULL, FILE_BEGIN) == 0 || SetEndOfFile(file.get()) == 0) { + ec = detail::make_system_error(); + } +#else + if (::truncate(p.c_str(), static_cast(size)) != 0) { + ec = detail::make_system_error(); + } +#endif +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE space_info space(const path& p) +{ + std::error_code ec; + auto result = space(p, ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), p, ec); + } + return result; +} +#endif + +GHC_INLINE space_info space(const path& p, std::error_code& ec) noexcept +{ + ec.clear(); +#ifdef GHC_OS_WINDOWS + ULARGE_INTEGER freeBytesAvailableToCaller = {{ 0, 0 }}; + ULARGE_INTEGER totalNumberOfBytes = {{ 0, 0 }}; + ULARGE_INTEGER totalNumberOfFreeBytes = {{ 0, 0 }}; + if (!GetDiskFreeSpaceExW(GHC_NATIVEWP(p), &freeBytesAvailableToCaller, &totalNumberOfBytes, &totalNumberOfFreeBytes)) { + ec = detail::make_system_error(); + return {static_cast(-1), static_cast(-1), static_cast(-1)}; + } + return {static_cast(totalNumberOfBytes.QuadPart), static_cast(totalNumberOfFreeBytes.QuadPart), static_cast(freeBytesAvailableToCaller.QuadPart)}; +#else + struct ::statvfs sfs; + if (::statvfs(p.c_str(), &sfs) != 0) { + ec = detail::make_system_error(); + return {static_cast(-1), static_cast(-1), static_cast(-1)}; + } + return {static_cast(sfs.f_blocks) * static_cast(sfs.f_frsize), static_cast(sfs.f_bfree) * static_cast(sfs.f_frsize), static_cast(sfs.f_bavail) * static_cast(sfs.f_frsize)}; +#endif +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE file_status status(const path& p) +{ + std::error_code ec; + auto result = status(p, ec); + if (result.type() == file_type::none) { + throw filesystem_error(detail::systemErrorText(ec.value()), p, ec); + } + return result; +} +#endif + +GHC_INLINE file_status status(const path& p, std::error_code& ec) noexcept +{ + return detail::status_ex(p, ec); +} + +GHC_INLINE bool status_known(file_status s) noexcept +{ + return s.type() != file_type::none; +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE file_status symlink_status(const path& p) +{ + std::error_code ec; + auto result = symlink_status(p, ec); + if (result.type() == file_type::none) { + throw filesystem_error(detail::systemErrorText(ec.value()), ec); + } + return result; +} +#endif + +GHC_INLINE file_status symlink_status(const path& p, std::error_code& ec) noexcept +{ + return detail::symlink_status_ex(p, ec); +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE path temp_directory_path() +{ + std::error_code ec; + path result = temp_directory_path(ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), ec); + } + return result; +} +#endif + +GHC_INLINE path temp_directory_path(std::error_code& ec) noexcept +{ + ec.clear(); +#ifdef GHC_OS_WINDOWS + wchar_t buffer[512]; + auto rc = GetTempPathW(511, buffer); + if (!rc || rc > 511) { + ec = detail::make_system_error(); + return path(); + } + return path(std::wstring(buffer)); +#else + static const char* temp_vars[] = {"TMPDIR", "TMP", "TEMP", "TEMPDIR", nullptr}; + const char* temp_path = nullptr; + for (auto temp_name = temp_vars; *temp_name != nullptr; ++temp_name) { + temp_path = std::getenv(*temp_name); + if (temp_path) { + return path(temp_path); + } + } + return path("/tmp"); +#endif +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE path weakly_canonical(const path& p) +{ + std::error_code ec; + auto result = weakly_canonical(p, ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), p, ec); + } + return result; +} +#endif + +GHC_INLINE path weakly_canonical(const path& p, std::error_code& ec) noexcept +{ + path result; + ec.clear(); + bool scan = true; + for (auto pe : p) { + if (scan) { + std::error_code tec; + if (exists(result / pe, tec)) { + result /= pe; + } + else { + if (ec) { + return path(); + } + scan = false; + if (!result.empty()) { + result = canonical(result, ec) / pe; + if (ec) { + break; + } + } + else { + result /= pe; + } + } + } + else { + result /= pe; + } + } + if (scan) { + if (!result.empty()) { + result = canonical(result, ec); + } + } + return ec ? path() : result.lexically_normal(); +} + +//----------------------------------------------------------------------------- +// [fs.class.file_status] class file_status +// [fs.file_status.cons] constructors and destructor +GHC_INLINE file_status::file_status() noexcept + : file_status(file_type::none) +{ +} + +GHC_INLINE file_status::file_status(file_type ft, perms prms) noexcept + : _type(ft) + , _perms(prms) +{ +} + +GHC_INLINE file_status::file_status(const file_status& other) noexcept + : _type(other._type) + , _perms(other._perms) +{ +} + +GHC_INLINE file_status::file_status(file_status&& other) noexcept + : _type(other._type) + , _perms(other._perms) +{ +} + +GHC_INLINE file_status::~file_status() {} + +// assignments: +GHC_INLINE file_status& file_status::operator=(const file_status& rhs) noexcept +{ + _type = rhs._type; + _perms = rhs._perms; + return *this; +} + +GHC_INLINE file_status& file_status::operator=(file_status&& rhs) noexcept +{ + _type = rhs._type; + _perms = rhs._perms; + return *this; +} + +// [fs.file_status.mods] modifiers +GHC_INLINE void file_status::type(file_type ft) noexcept +{ + _type = ft; +} + +GHC_INLINE void file_status::permissions(perms prms) noexcept +{ + _perms = prms; +} + +// [fs.file_status.obs] observers +GHC_INLINE file_type file_status::type() const noexcept +{ + return _type; +} + +GHC_INLINE perms file_status::permissions() const noexcept +{ + return _perms; +} + +//----------------------------------------------------------------------------- +// [fs.class.directory_entry] class directory_entry +// [fs.dir.entry.cons] constructors and destructor +// directory_entry::directory_entry() noexcept = default; +// directory_entry::directory_entry(const directory_entry&) = default; +// directory_entry::directory_entry(directory_entry&&) noexcept = default; +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE directory_entry::directory_entry(const filesystem::path& p) + : _path(p) + , _file_size(static_cast(-1)) +#ifndef GHC_OS_WINDOWS + , _hard_link_count(static_cast(-1)) +#endif + , _last_write_time(0) +{ + refresh(); +} +#endif + +GHC_INLINE directory_entry::directory_entry(const filesystem::path& p, std::error_code& ec) + : _path(p) + , _file_size(static_cast(-1)) +#ifndef GHC_OS_WINDOWS + , _hard_link_count(static_cast(-1)) +#endif + , _last_write_time(0) +{ + refresh(ec); +} + +GHC_INLINE directory_entry::~directory_entry() {} + +// assignments: +// directory_entry& directory_entry::operator=(const directory_entry&) = default; +// directory_entry& directory_entry::operator=(directory_entry&&) noexcept = default; + +// [fs.dir.entry.mods] directory_entry modifiers +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE void directory_entry::assign(const filesystem::path& p) +{ + _path = p; + refresh(); +} +#endif + +GHC_INLINE void directory_entry::assign(const filesystem::path& p, std::error_code& ec) +{ + _path = p; + refresh(ec); +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE void directory_entry::replace_filename(const filesystem::path& p) +{ + _path.replace_filename(p); + refresh(); +} +#endif + +GHC_INLINE void directory_entry::replace_filename(const filesystem::path& p, std::error_code& ec) +{ + _path.replace_filename(p); + refresh(ec); +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE void directory_entry::refresh() +{ + std::error_code ec; + refresh(ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), _path, ec); + } +} +#endif + +GHC_INLINE void directory_entry::refresh(std::error_code& ec) noexcept +{ +#ifdef GHC_OS_WINDOWS + _status = detail::status_ex(_path, ec, &_symlink_status, &_file_size, nullptr, &_last_write_time); +#else + _status = detail::status_ex(_path, ec, &_symlink_status, &_file_size, &_hard_link_count, &_last_write_time); +#endif +} + +// [fs.dir.entry.obs] directory_entry observers +GHC_INLINE const filesystem::path& directory_entry::path() const noexcept +{ + return _path; +} + +GHC_INLINE directory_entry::operator const filesystem::path&() const noexcept +{ + return _path; +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE file_type directory_entry::status_file_type() const +{ + return _status.type() != file_type::none ? _status.type() : filesystem::status(path()).type(); +} +#endif + +GHC_INLINE file_type directory_entry::status_file_type(std::error_code& ec) const noexcept +{ + if (_status.type() != file_type::none) { + ec.clear(); + return _status.type(); + } + return filesystem::status(path(), ec).type(); +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE bool directory_entry::exists() const +{ + return status_file_type() != file_type::not_found; +} +#endif + +GHC_INLINE bool directory_entry::exists(std::error_code& ec) const noexcept +{ + return status_file_type(ec) != file_type::not_found; +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE bool directory_entry::is_block_file() const +{ + return status_file_type() == file_type::block; +} +#endif +GHC_INLINE bool directory_entry::is_block_file(std::error_code& ec) const noexcept +{ + return status_file_type(ec) == file_type::block; +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE bool directory_entry::is_character_file() const +{ + return status_file_type() == file_type::character; +} +#endif + +GHC_INLINE bool directory_entry::is_character_file(std::error_code& ec) const noexcept +{ + return status_file_type(ec) == file_type::character; +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE bool directory_entry::is_directory() const +{ + return status_file_type() == file_type::directory; +} +#endif + +GHC_INLINE bool directory_entry::is_directory(std::error_code& ec) const noexcept +{ + return status_file_type(ec) == file_type::directory; +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE bool directory_entry::is_fifo() const +{ + return status_file_type() == file_type::fifo; +} +#endif + +GHC_INLINE bool directory_entry::is_fifo(std::error_code& ec) const noexcept +{ + return status_file_type(ec) == file_type::fifo; +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE bool directory_entry::is_other() const +{ + auto ft = status_file_type(); + return ft != file_type::none && ft != file_type::not_found && ft != file_type::regular && ft != file_type::directory && !is_symlink(); +} +#endif + +GHC_INLINE bool directory_entry::is_other(std::error_code& ec) const noexcept +{ + auto ft = status_file_type(ec); + bool other = ft != file_type::none && ft != file_type::not_found && ft != file_type::regular && ft != file_type::directory && !is_symlink(ec); + return !ec && other; +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE bool directory_entry::is_regular_file() const +{ + return status_file_type() == file_type::regular; +} +#endif + +GHC_INLINE bool directory_entry::is_regular_file(std::error_code& ec) const noexcept +{ + return status_file_type(ec) == file_type::regular; +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE bool directory_entry::is_socket() const +{ + return status_file_type() == file_type::socket; +} +#endif + +GHC_INLINE bool directory_entry::is_socket(std::error_code& ec) const noexcept +{ + return status_file_type(ec) == file_type::socket; +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE bool directory_entry::is_symlink() const +{ + return _symlink_status.type() != file_type::none ? _symlink_status.type() == file_type::symlink : filesystem::is_symlink(symlink_status()); +} +#endif + +GHC_INLINE bool directory_entry::is_symlink(std::error_code& ec) const noexcept +{ + if (_symlink_status.type() != file_type::none) { + ec.clear(); + return _symlink_status.type() == file_type::symlink; + } + return filesystem::is_symlink(symlink_status(ec)); +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE uintmax_t directory_entry::file_size() const +{ + if (_file_size != static_cast(-1)) { + return _file_size; + } + return filesystem::file_size(path()); +} +#endif + +GHC_INLINE uintmax_t directory_entry::file_size(std::error_code& ec) const noexcept +{ + if (_file_size != static_cast(-1)) { + ec.clear(); + return _file_size; + } + return filesystem::file_size(path(), ec); +} + +#ifndef GHC_OS_WEB +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE uintmax_t directory_entry::hard_link_count() const +{ +#ifndef GHC_OS_WINDOWS + if (_hard_link_count != static_cast(-1)) { + return _hard_link_count; + } +#endif + return filesystem::hard_link_count(path()); +} +#endif + +GHC_INLINE uintmax_t directory_entry::hard_link_count(std::error_code& ec) const noexcept +{ +#ifndef GHC_OS_WINDOWS + if (_hard_link_count != static_cast(-1)) { + ec.clear(); + return _hard_link_count; + } +#endif + return filesystem::hard_link_count(path(), ec); +} +#endif + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE file_time_type directory_entry::last_write_time() const +{ + if (_last_write_time != 0) { + return std::chrono::system_clock::from_time_t(_last_write_time); + } + return filesystem::last_write_time(path()); +} +#endif + +GHC_INLINE file_time_type directory_entry::last_write_time(std::error_code& ec) const noexcept +{ + if (_last_write_time != 0) { + ec.clear(); + return std::chrono::system_clock::from_time_t(_last_write_time); + } + return filesystem::last_write_time(path(), ec); +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE file_status directory_entry::status() const +{ + if (_status.type() != file_type::none && _status.permissions() != perms::unknown) { + return _status; + } + return filesystem::status(path()); +} +#endif + +GHC_INLINE file_status directory_entry::status(std::error_code& ec) const noexcept +{ + if (_status.type() != file_type::none && _status.permissions() != perms::unknown) { + ec.clear(); + return _status; + } + return filesystem::status(path(), ec); +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE file_status directory_entry::symlink_status() const +{ + if (_symlink_status.type() != file_type::none && _symlink_status.permissions() != perms::unknown) { + return _symlink_status; + } + return filesystem::symlink_status(path()); +} +#endif + +GHC_INLINE file_status directory_entry::symlink_status(std::error_code& ec) const noexcept +{ + if (_symlink_status.type() != file_type::none && _symlink_status.permissions() != perms::unknown) { + ec.clear(); + return _symlink_status; + } + return filesystem::symlink_status(path(), ec); +} + +#ifdef GHC_HAS_THREEWAY_COMP +GHC_INLINE std::strong_ordering directory_entry::operator<=>(const directory_entry& rhs) const noexcept +{ + return _path <=> rhs._path; +} +#endif + +GHC_INLINE bool directory_entry::operator<(const directory_entry& rhs) const noexcept +{ + return _path < rhs._path; +} + +GHC_INLINE bool directory_entry::operator==(const directory_entry& rhs) const noexcept +{ + return _path == rhs._path; +} + +GHC_INLINE bool directory_entry::operator!=(const directory_entry& rhs) const noexcept +{ + return _path != rhs._path; +} + +GHC_INLINE bool directory_entry::operator<=(const directory_entry& rhs) const noexcept +{ + return _path <= rhs._path; +} + +GHC_INLINE bool directory_entry::operator>(const directory_entry& rhs) const noexcept +{ + return _path > rhs._path; +} + +GHC_INLINE bool directory_entry::operator>=(const directory_entry& rhs) const noexcept +{ + return _path >= rhs._path; +} + +//----------------------------------------------------------------------------- +// [fs.class.directory_iterator] class directory_iterator + +#ifdef GHC_OS_WINDOWS +class directory_iterator::impl +{ +public: + impl(const path& p, directory_options options) + : _base(p) + , _options(options) + , _dirHandle(INVALID_HANDLE_VALUE) + { + if (!_base.empty()) { + ZeroMemory(&_findData, sizeof(WIN32_FIND_DATAW)); + if ((_dirHandle = FindFirstFileW(GHC_NATIVEWP((_base / "*")), &_findData)) != INVALID_HANDLE_VALUE) { + if (std::wstring(_findData.cFileName) == L"." || std::wstring(_findData.cFileName) == L"..") { + increment(_ec); + } + else { + _dir_entry._path = _base / std::wstring(_findData.cFileName); + copyToDirEntry(_ec); + } + } + else { + auto error = ::GetLastError(); + _base = filesystem::path(); + if (error != ERROR_ACCESS_DENIED || (options & directory_options::skip_permission_denied) == directory_options::none) { + _ec = detail::make_system_error(); + } + } + } + } + impl(const impl& other) = delete; + ~impl() + { + if (_dirHandle != INVALID_HANDLE_VALUE) { + FindClose(_dirHandle); + _dirHandle = INVALID_HANDLE_VALUE; + } + } + void increment(std::error_code& ec) + { + if (_dirHandle != INVALID_HANDLE_VALUE) { + do { + if (FindNextFileW(_dirHandle, &_findData)) { + _dir_entry._path = _base; +#ifdef GHC_USE_WCHAR_T + _dir_entry._path.append_name(_findData.cFileName); +#else +#ifdef GHC_RAISE_UNICODE_ERRORS + try { + _dir_entry._path.append_name(detail::toUtf8(_findData.cFileName).c_str()); + } + catch (filesystem_error& fe) { + ec = fe.code(); + return; + } +#else + _dir_entry._path.append_name(detail::toUtf8(_findData.cFileName).c_str()); +#endif +#endif + copyToDirEntry(ec); + } + else { + auto err = ::GetLastError(); + if (err != ERROR_NO_MORE_FILES) { + _ec = ec = detail::make_system_error(err); + } + FindClose(_dirHandle); + _dirHandle = INVALID_HANDLE_VALUE; + _dir_entry._path.clear(); + break; + } + } while (std::wstring(_findData.cFileName) == L"." || std::wstring(_findData.cFileName) == L".."); + } + else { + ec = _ec; + } + } + void copyToDirEntry(std::error_code& ec) + { + if (_findData.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) { + _dir_entry._status = detail::status_ex(_dir_entry._path, ec, &_dir_entry._symlink_status, &_dir_entry._file_size, nullptr, &_dir_entry._last_write_time); + } + else { + _dir_entry._status = detail::status_from_INFO(_dir_entry._path, &_findData, ec, &_dir_entry._file_size, &_dir_entry._last_write_time); + _dir_entry._symlink_status = _dir_entry._status; + } + if (ec) { + if (_dir_entry._status.type() != file_type::none && _dir_entry._symlink_status.type() != file_type::none) { + ec.clear(); + } + else { + _dir_entry._file_size = static_cast(-1); + _dir_entry._last_write_time = 0; + } + } + } + path _base; + directory_options _options; + WIN32_FIND_DATAW _findData; + HANDLE _dirHandle; + directory_entry _dir_entry; + std::error_code _ec; +}; +#else +// POSIX implementation +class directory_iterator::impl +{ +public: + impl(const path& path, directory_options options) + : _base(path) + , _options(options) + , _dir(nullptr) + , _entry(nullptr) + { + if (!path.empty()) { + _dir = ::opendir(path.native().c_str()); + if (!_dir) { + auto error = errno; + _base = filesystem::path(); + if ((error != EACCES && error != EPERM) || (options & directory_options::skip_permission_denied) == directory_options::none) { + _ec = detail::make_system_error(); + } + } + else { + increment(_ec); + } + } + } + impl(const impl& other) = delete; + ~impl() + { + if (_dir) { + ::closedir(_dir); + } + } + void increment(std::error_code& ec) + { + if (_dir) { + bool skip; + do { + skip = false; + errno = 0; + _entry = ::readdir(_dir); + if (_entry) { + _dir_entry._path = _base; + _dir_entry._path.append_name(_entry->d_name); + copyToDirEntry(); + if (ec && (ec.value() == EACCES || ec.value() == EPERM) && (_options & directory_options::skip_permission_denied) == directory_options::skip_permission_denied) { + ec.clear(); + skip = true; + } + } + else { + ::closedir(_dir); + _dir = nullptr; + _dir_entry._path.clear(); + if (errno) { + ec = detail::make_system_error(); + } + break; + } + } while (skip || std::strcmp(_entry->d_name, ".") == 0 || std::strcmp(_entry->d_name, "..") == 0); + } + } + + void copyToDirEntry() + { + _dir_entry._symlink_status.permissions(perms::unknown); + auto ft = detail::file_type_from_dirent(*_entry); + _dir_entry._symlink_status.type(ft); + if (ft != file_type::symlink) { + _dir_entry._status = _dir_entry._symlink_status; + } + else { + _dir_entry._status.type(file_type::none); + _dir_entry._status.permissions(perms::unknown); + } + _dir_entry._file_size = static_cast(-1); + _dir_entry._hard_link_count = static_cast(-1); + _dir_entry._last_write_time = 0; + } + path _base; + directory_options _options; + DIR* _dir; + struct ::dirent* _entry; + directory_entry _dir_entry; + std::error_code _ec; +}; +#endif + +// [fs.dir.itr.members] member functions +GHC_INLINE directory_iterator::directory_iterator() noexcept + : _impl(new impl(path(), directory_options::none)) +{ +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE directory_iterator::directory_iterator(const path& p) + : _impl(new impl(p, directory_options::none)) +{ + if (_impl->_ec) { + throw filesystem_error(detail::systemErrorText(_impl->_ec.value()), p, _impl->_ec); + } + _impl->_ec.clear(); +} + +GHC_INLINE directory_iterator::directory_iterator(const path& p, directory_options options) + : _impl(new impl(p, options)) +{ + if (_impl->_ec) { + throw filesystem_error(detail::systemErrorText(_impl->_ec.value()), p, _impl->_ec); + } +} +#endif + +GHC_INLINE directory_iterator::directory_iterator(const path& p, std::error_code& ec) noexcept + : _impl(new impl(p, directory_options::none)) +{ + if (_impl->_ec) { + ec = _impl->_ec; + } +} + +GHC_INLINE directory_iterator::directory_iterator(const path& p, directory_options options, std::error_code& ec) noexcept + : _impl(new impl(p, options)) +{ + if (_impl->_ec) { + ec = _impl->_ec; + } +} + +GHC_INLINE directory_iterator::directory_iterator(const directory_iterator& rhs) + : _impl(rhs._impl) +{ +} + +GHC_INLINE directory_iterator::directory_iterator(directory_iterator&& rhs) noexcept + : _impl(std::move(rhs._impl)) +{ +} + +GHC_INLINE directory_iterator::~directory_iterator() {} + +GHC_INLINE directory_iterator& directory_iterator::operator=(const directory_iterator& rhs) +{ + _impl = rhs._impl; + return *this; +} + +GHC_INLINE directory_iterator& directory_iterator::operator=(directory_iterator&& rhs) noexcept +{ + _impl = std::move(rhs._impl); + return *this; +} + +GHC_INLINE const directory_entry& directory_iterator::operator*() const +{ + return _impl->_dir_entry; +} + +GHC_INLINE const directory_entry* directory_iterator::operator->() const +{ + return &_impl->_dir_entry; +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE directory_iterator& directory_iterator::operator++() +{ + std::error_code ec; + _impl->increment(ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), _impl->_dir_entry._path, ec); + } + return *this; +} +#endif + +GHC_INLINE directory_iterator& directory_iterator::increment(std::error_code& ec) noexcept +{ + _impl->increment(ec); + return *this; +} + +GHC_INLINE bool directory_iterator::operator==(const directory_iterator& rhs) const +{ + return _impl->_dir_entry._path == rhs._impl->_dir_entry._path; +} + +GHC_INLINE bool directory_iterator::operator!=(const directory_iterator& rhs) const +{ + return _impl->_dir_entry._path != rhs._impl->_dir_entry._path; +} + +// [fs.dir.itr.nonmembers] directory_iterator non-member functions + +GHC_INLINE directory_iterator begin(directory_iterator iter) noexcept +{ + return iter; +} + +GHC_INLINE directory_iterator end(const directory_iterator&) noexcept +{ + return directory_iterator(); +} + +//----------------------------------------------------------------------------- +// [fs.class.rec.dir.itr] class recursive_directory_iterator + +GHC_INLINE recursive_directory_iterator::recursive_directory_iterator() noexcept + : _impl(new recursive_directory_iterator_impl(directory_options::none, true)) +{ + _impl->_dir_iter_stack.push(directory_iterator()); +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE recursive_directory_iterator::recursive_directory_iterator(const path& p) + : _impl(new recursive_directory_iterator_impl(directory_options::none, true)) +{ + _impl->_dir_iter_stack.push(directory_iterator(p)); +} + +GHC_INLINE recursive_directory_iterator::recursive_directory_iterator(const path& p, directory_options options) + : _impl(new recursive_directory_iterator_impl(options, true)) +{ + _impl->_dir_iter_stack.push(directory_iterator(p, options)); +} +#endif + +GHC_INLINE recursive_directory_iterator::recursive_directory_iterator(const path& p, directory_options options, std::error_code& ec) noexcept + : _impl(new recursive_directory_iterator_impl(options, true)) +{ + _impl->_dir_iter_stack.push(directory_iterator(p, options, ec)); +} + +GHC_INLINE recursive_directory_iterator::recursive_directory_iterator(const path& p, std::error_code& ec) noexcept + : _impl(new recursive_directory_iterator_impl(directory_options::none, true)) +{ + _impl->_dir_iter_stack.push(directory_iterator(p, ec)); +} + +GHC_INLINE recursive_directory_iterator::recursive_directory_iterator(const recursive_directory_iterator& rhs) + : _impl(rhs._impl) +{ +} + +GHC_INLINE recursive_directory_iterator::recursive_directory_iterator(recursive_directory_iterator&& rhs) noexcept + : _impl(std::move(rhs._impl)) +{ +} + +GHC_INLINE recursive_directory_iterator::~recursive_directory_iterator() {} + +// [fs.rec.dir.itr.members] observers +GHC_INLINE directory_options recursive_directory_iterator::options() const +{ + return _impl->_options; +} + +GHC_INLINE int recursive_directory_iterator::depth() const +{ + return static_cast(_impl->_dir_iter_stack.size() - 1); +} + +GHC_INLINE bool recursive_directory_iterator::recursion_pending() const +{ + return _impl->_recursion_pending; +} + +GHC_INLINE const directory_entry& recursive_directory_iterator::operator*() const +{ + return *(_impl->_dir_iter_stack.top()); +} + +GHC_INLINE const directory_entry* recursive_directory_iterator::operator->() const +{ + return &(*(_impl->_dir_iter_stack.top())); +} + +// [fs.rec.dir.itr.members] modifiers recursive_directory_iterator& +GHC_INLINE recursive_directory_iterator& recursive_directory_iterator::operator=(const recursive_directory_iterator& rhs) +{ + _impl = rhs._impl; + return *this; +} + +GHC_INLINE recursive_directory_iterator& recursive_directory_iterator::operator=(recursive_directory_iterator&& rhs) noexcept +{ + _impl = std::move(rhs._impl); + return *this; +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE recursive_directory_iterator& recursive_directory_iterator::operator++() +{ + std::error_code ec; + increment(ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), _impl->_dir_iter_stack.empty() ? path() : _impl->_dir_iter_stack.top()->path(), ec); + } + return *this; +} +#endif + +GHC_INLINE recursive_directory_iterator& recursive_directory_iterator::increment(std::error_code& ec) noexcept +{ + bool isSymLink = (*this)->is_symlink(ec); + bool isDir = !ec && (*this)->is_directory(ec); + if (isSymLink && detail::is_not_found_error(ec)) { + ec.clear(); + } + if (!ec) { + 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)); + } + else { + _impl->_dir_iter_stack.top().increment(ec); + } + if (!ec) { + while (depth() && _impl->_dir_iter_stack.top() == directory_iterator()) { + _impl->_dir_iter_stack.pop(); + _impl->_dir_iter_stack.top().increment(ec); + } + } + else if (!_impl->_dir_iter_stack.empty()) { + _impl->_dir_iter_stack.pop(); + } + _impl->_recursion_pending = true; + } + return *this; +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE void recursive_directory_iterator::pop() +{ + std::error_code ec; + pop(ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), _impl->_dir_iter_stack.empty() ? path() : _impl->_dir_iter_stack.top()->path(), ec); + } +} +#endif + +GHC_INLINE void recursive_directory_iterator::pop(std::error_code& ec) +{ + if (depth() == 0) { + *this = recursive_directory_iterator(); + } + else { + do { + _impl->_dir_iter_stack.pop(); + _impl->_dir_iter_stack.top().increment(ec); + } while (depth() && _impl->_dir_iter_stack.top() == directory_iterator()); + } +} + +GHC_INLINE void recursive_directory_iterator::disable_recursion_pending() +{ + _impl->_recursion_pending = false; +} + +// other members as required by [input.iterators] +GHC_INLINE bool recursive_directory_iterator::operator==(const recursive_directory_iterator& rhs) const +{ + return _impl->_dir_iter_stack.top() == rhs._impl->_dir_iter_stack.top(); +} + +GHC_INLINE bool recursive_directory_iterator::operator!=(const recursive_directory_iterator& rhs) const +{ + return _impl->_dir_iter_stack.top() != rhs._impl->_dir_iter_stack.top(); +} + +// [fs.rec.dir.itr.nonmembers] directory_iterator non-member functions +GHC_INLINE recursive_directory_iterator begin(recursive_directory_iterator iter) noexcept +{ + return iter; +} + +GHC_INLINE recursive_directory_iterator end(const recursive_directory_iterator&) noexcept +{ + return recursive_directory_iterator(); +} + +#endif // GHC_EXPAND_IMPL + +} // namespace filesystem +} // namespace ghc + +// cleanup some macros +#undef GHC_INLINE +#undef GHC_EXPAND_IMPL + +#endif // GHC_FILESYSTEM_H diff --git a/src/external/filesystem/include/ghc/fs_fwd.hpp b/src/external/filesystem/include/ghc/fs_fwd.hpp new file mode 100644 index 00000000..31188d16 --- /dev/null +++ b/src/external/filesystem/include/ghc/fs_fwd.hpp @@ -0,0 +1,38 @@ +//--------------------------------------------------------------------------------------- +// +// ghc::filesystem - A C++17-like filesystem implementation for C++11/C++14 +// +//--------------------------------------------------------------------------------------- +// +// Copyright (c) 2018, Steffen Schümann +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +//--------------------------------------------------------------------------------------- +// fs_fwd.hpp - The forwarding header for the header/implementation seperated usage of +// ghc::filesystem. +// 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, +// as long as one cpp includes fs_impl.hpp to deliver the matching implementations. +//--------------------------------------------------------------------------------------- +#ifndef GHC_FILESYSTEM_FWD_H +#define GHC_FILESYSTEM_FWD_H +#define GHC_FILESYSTEM_FWD +#include +#endif // GHC_FILESYSTEM_FWD_H diff --git a/src/external/filesystem/include/ghc/fs_impl.hpp b/src/external/filesystem/include/ghc/fs_impl.hpp new file mode 100644 index 00000000..92e3eaef --- /dev/null +++ b/src/external/filesystem/include/ghc/fs_impl.hpp @@ -0,0 +1,35 @@ +//--------------------------------------------------------------------------------------- +// +// ghc::filesystem - A C++17-like filesystem implementation for C++11/C++14 +// +//--------------------------------------------------------------------------------------- +// +// Copyright (c) 2018, Steffen Schümann +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +//--------------------------------------------------------------------------------------- +// fs_impl.hpp - The implementation header for the header/implementation seperated usage of +// ghc::filesystem. +// 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 +// header to work. +//--------------------------------------------------------------------------------------- +#define GHC_FILESYSTEM_IMPLEMENTATION +#include diff --git a/src/external/filesystem/include/ghc/fs_std.hpp b/src/external/filesystem/include/ghc/fs_std.hpp new file mode 100644 index 00000000..c9492fdc --- /dev/null +++ b/src/external/filesystem/include/ghc/fs_std.hpp @@ -0,0 +1,60 @@ +//--------------------------------------------------------------------------------------- +// +// ghc::filesystem - A C++17-like filesystem implementation for C++11/C++14 +// +//--------------------------------------------------------------------------------------- +// +// Copyright (c) 2018, Steffen Schümann +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +//--------------------------------------------------------------------------------------- +// fs_std.hpp - The dynamic switching header that includes std::filesystem if detected +// or ghc::filesystem if not, and makes the resulting API available in the +// namespace fs. +//--------------------------------------------------------------------------------------- +#ifndef GHC_FILESYSTEM_STD_H +#define GHC_FILESYSTEM_STD_H +#if defined(__APPLE__) +#include +#endif +#if ((defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) || (defined(__cplusplus) && __cplusplus >= 201703L)) && defined(__has_include) +#if __has_include() && (!defined(__MAC_OS_X_VERSION_MIN_REQUIRED) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 101500) +#define GHC_USE_STD_FS +#include +namespace fs { +using namespace std::filesystem; +using ifstream = std::ifstream; +using ofstream = std::ofstream; +using fstream = std::fstream; +} +#endif +#endif +#ifndef GHC_USE_STD_FS +//#define GHC_WIN_DISABLE_WSTRING_STORAGE_TYPE +#include +namespace fs { +using namespace ghc::filesystem; +using ifstream = ghc::filesystem::ifstream; +using ofstream = ghc::filesystem::ofstream; +using fstream = ghc::filesystem::fstream; +} +#endif +#endif // GHC_FILESYSTEM_STD_H + diff --git a/src/external/filesystem/include/ghc/fs_std_fwd.hpp b/src/external/filesystem/include/ghc/fs_std_fwd.hpp new file mode 100644 index 00000000..163c956a --- /dev/null +++ b/src/external/filesystem/include/ghc/fs_std_fwd.hpp @@ -0,0 +1,63 @@ +//--------------------------------------------------------------------------------------- +// +// ghc::filesystem - A C++17-like filesystem implementation for C++11/C++14 +// +//--------------------------------------------------------------------------------------- +// +// Copyright (c) 2018, Steffen Schümann +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +//--------------------------------------------------------------------------------------- +// fs_std_fwd.hpp - The forwarding header for the header/implementation seperated usage of +// 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 +// not bleeding implementation details (e.g. system includes) into the global namespace, +// as long as one cpp includes fs_std_impl.hpp to deliver the matching implementations. +//--------------------------------------------------------------------------------------- +#ifndef GHC_FILESYSTEM_STD_FWD_H +#define GHC_FILESYSTEM_STD_FWD_H +#if defined(__APPLE__) +#include +#endif +#if ((defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) || (defined(__cplusplus) && __cplusplus >= 201703L)) && defined(__has_include) +#if __has_include() && (!defined(__MAC_OS_X_VERSION_MIN_REQUIRED) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 101500) +#define GHC_USE_STD_FS +#include +namespace fs { +using namespace std::filesystem; +using ifstream = std::ifstream; +using ofstream = std::ofstream; +using fstream = std::fstream; +} +#endif +#endif +#ifndef GHC_USE_STD_FS +//#define GHC_WIN_DISABLE_WSTRING_STORAGE_TYPE +#define GHC_FILESYSTEM_FWD +#include +namespace fs { +using namespace ghc::filesystem; +using ifstream = ghc::filesystem::ifstream; +using ofstream = ghc::filesystem::ofstream; +using fstream = ghc::filesystem::fstream; +} +#endif +#endif // GHC_FILESYSTEM_STD_FWD_H + diff --git a/src/external/filesystem/include/ghc/fs_std_impl.hpp b/src/external/filesystem/include/ghc/fs_std_impl.hpp new file mode 100644 index 00000000..7042edca --- /dev/null +++ b/src/external/filesystem/include/ghc/fs_std_impl.hpp @@ -0,0 +1,46 @@ +//--------------------------------------------------------------------------------------- +// +// ghc::filesystem - A C++17-like filesystem implementation for C++11/C++14 +// +//--------------------------------------------------------------------------------------- +// +// Copyright (c) 2018, Steffen Schümann +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +//--------------------------------------------------------------------------------------- +// fs_std_impl.hpp - The implementation header for the header/implementation seperated usage of +// 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. +// The cpp has to include this before including fs_std_fwd.hpp directly or via a different +// header to work. +//--------------------------------------------------------------------------------------- +#if defined(__APPLE__) +#include +#endif +#if ((defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) || (defined(__cplusplus) && __cplusplus >= 201703L)) && defined(__has_include) +#if __has_include() && (!defined(__MAC_OS_X_VERSION_MIN_REQUIRED) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 101500) +#define GHC_USE_STD_FS +#endif +#endif +#ifndef GHC_USE_STD_FS +//#define GHC_WIN_DISABLE_WSTRING_STORAGE_TYPE +#define GHC_FILESYSTEM_IMPLEMENTATION +#include +#endif diff --git a/src/external/glob/LICENSE b/src/external/glob/LICENSE new file mode 100644 index 00000000..bcae1c6a --- /dev/null +++ b/src/external/glob/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2019 Pranav + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/external/glob/README.md b/src/external/glob/README.md new file mode 100644 index 00000000..30a48d5a --- /dev/null +++ b/src/external/glob/README.md @@ -0,0 +1,283 @@ +

+ +

+ +

+ Unix-style pathname pattern expansion +

+ +## Table of Contents + +- [Quick Start](#quick-start) + * [Build Library and Standalone Sample](#build-library-and-standalone-sample) +- [Usage](#usage) +- [API](#api) +- [Wildcards](#wildcards) +- [Examples](#examples) + * [Match file extensions](#match-file-extensions) + * [Match files in absolute pathnames](#match-files-in-absolute-pathnames) + * [Wildcards: Match a range of characters listed in brackets ('[]')](#wildcards-match-a-range-of-characters-listed-in-brackets-) + * [Exclude files from the matching](#exclude-files-from-the-matching) + * [Wildcards: Match any one character with question mark ('?')](#wildcards-match-any-one-character-with-question-mark-) + * [Case sensitivity](#case-sensitivity) + * [Tilde expansion](#tilde-expansion) +- [Contributing](#contributing) +- [License](#license) + +## Quick Start + +* This library is available in two flavors: + 1. Two file version: `glob.h` and `glob.cpp` + 2. Single header file version in `single_include/` +* No external dependencies - just the standard library +* Requires C++17 `std::filesystem` + - If you can't use `C++17`, you can integrate [gulrak/filesystem](https://github.com/gulrak/filesystem) with minimal effort. +* MIT License + +### Build Library and Standalone Sample + +```bash +cmake -Hall -Bbuild +cmake --build build + +# run standalone `glob` sample +./build/standalone/glob --help +``` + +### Usage + +```cpp +// Match on a single pattern +for (auto& p : glob::glob("~/.b*")) { // e.g., .bash_history, .bashrc + // do something with `p` +} + +// Match on multiple patterns +for (auto& p : glob::glob({"*.png", "*.jpg"})) { // e.g., foo.png, bar.jpg + // do something with `p` +} + +// Match recursively with `rglob` +for (auto& p : glob::rglob("**/*.hpp")) { // e.g., include/foo.hpp, include/foo/bar.hpp + // do something with `p` +} +``` + +## API + +```cpp +/// e.g., glob("*.hpp") +/// e.g., glob("**/*.cpp") +/// e.g., glob("test_files_02/[0-9].txt") +/// e.g., glob("/usr/local/include/nc*.h") +/// e.g., glob("test_files_02/?.txt") +vector glob(string pathname); + +/// Globs recursively +/// e.g., rglob("Documents/Projects/Foo/**/*.hpp") +/// e.g., rglob("test_files_02/*[0-9].txt") +vector rglob(string pathname); +``` + +There are also two convenience functions to `glob` on a list of patterns: + +```cpp +/// e.g., glob({"*.png", "*.jpg"}) +vector glob(vector pathnames); + +/// Globs recursively +/// e.g., rglob({"**/*.h", "**/*.hpp", "**/*.cpp"}) +vector rglob(vector pathnames); +``` + +## Wildcards + +| Wildcard | Matches | Example +|--- |--- |--- | +| `*` | any characters | `*.txt` matches all files with the txt extension | +| `?` | any one character | `???` matches files with 3 characters long | +| `[]` | any character listed in the brackets | `[ABC]*` matches files starting with A,B or C | +| `[-]` | any character in the range listed in brackets | `[A-Z]*` matches files starting with capital letters | +| `[!]` | any character not listed in the brackets | `[!ABC]*` matches files that do not start with A,B or C | + +## Examples + +The following examples use the [standalone](standalone/source/main.cpp) sample that is part of this repository to illustrate the library functionality. + +```console +foo@bar:~$ ./build/standalone/glob -h +Run glob to find all the pathnames matching a specified pattern +Usage: + ./build/standalone/glob [OPTION...] + + -h, --help Show help + -v, --version Print the current version number + -r, --recursive Run glob recursively + -i, --input arg Patterns to match +``` + +### Match file extensions + +```console +foo@bar:~$ tree +. +├── include +│   └── foo +│   ├── bar.hpp +│   ├── baz.hpp +│   └── foo.hpp +└── test + ├── bar.cpp + ├── doctest.hpp + ├── foo.cpp + └── main.cpp + +3 directories, 7 files + +foo@bar:~$ ./glob -i "**/*.hpp" +"test/doctest.hpp" + +foo@bar:~$ ./glob -i "**/**/*.hpp" +"include/foo/baz.hpp" +"include/foo/foo.hpp" +"include/foo/bar.hpp" +``` + +***NOTE*** If you run glob recursively, i.e., using `rglob`: + +```console +foo@bar:~$ ./glob -r -i "**/*.hpp" +"test/doctest.hpp" +"include/foo/baz.hpp" +"include/foo/foo.hpp" +"include/foo/bar.hpp" +``` + +### Match files in absolute pathnames + +```console +foo@bar:~$ ./glob -i '/usr/local/include/nc*.h' +"/usr/local/include/ncCheck.h" +"/usr/local/include/ncGroupAtt.h" +"/usr/local/include/ncUshort.h" +"/usr/local/include/ncByte.h" +"/usr/local/include/ncString.h" +"/usr/local/include/ncUint64.h" +"/usr/local/include/ncGroup.h" +"/usr/local/include/ncUbyte.h" +"/usr/local/include/ncvalues.h" +"/usr/local/include/ncInt.h" +"/usr/local/include/ncAtt.h" +"/usr/local/include/ncVar.h" +"/usr/local/include/ncUint.h" +``` + +### Wildcards: Match a range of characters listed in brackets ('[]') + +```console +foo@bar:~$ ls test_files_02 +1.txt 2.txt 3.txt 4.txt + +foo@bar:~$ ./glob -i 'test_files_02/[0-9].txt' +"test_files_02/4.txt" +"test_files_02/3.txt" +"test_files_02/2.txt" +"test_files_02/1.txt" + +foo@bar:~$ ./glob -i 'test_files_02/[1-2]*' +"test_files_02/2.txt" +"test_files_02/1.txt" +``` + +```console +foo@bar:~$ ls test_files_03 +file1.txt file2.txt file3.txt file4.txt + +foo@bar:~$ ./glob -i 'test_files_03/file[0-9].*' +"test_files_03/file2.txt" +"test_files_03/file3.txt" +"test_files_03/file1.txt" +"test_files_03/file4.txt" +``` + +### Exclude files from the matching + +```console +foo@bar:~$ ls test_files_01 +__init__.py bar.py foo.py + +foo@bar:~$ ./glob -i 'test_files_01/*[!__init__].py' +"test_files_01/bar.py" +"test_files_01/foo.py" + +foo@bar:~$ ./glob -i 'test_files_01/*[!__init__][!bar].py' +"test_files_01/foo.py" + +foo@bar:~$ ./glob -i 'test_files_01/[!_]*.py' +"test_files_01/bar.py" +"test_files_01/foo.py" +``` + +### Wildcards: Match any one character with question mark ('?') + +```console +foo@bar:~$ ls test_files_02 +1.txt 2.txt 3.txt 4.txt + +foo@bar:~$ ./glob -i 'test_files_02/?.txt' +"test_files_02/4.txt" +"test_files_02/3.txt" +"test_files_02/2.txt" +"test_files_02/1.txt" +``` + +```console +foo@bar:~$ ls test_files_03 +file1.txt file2.txt file3.txt file4.txt + +foo@bar:~$ ./glob -i 'test_files_03/????[3-4].txt' +"test_files_03/file3.txt" +"test_files_03/file4.txt" +``` + +### Case sensitivity + +`glob` matching is case-sensitive: + +```console +foo@bar:~$ ls test_files_05 +file1.png file2.png file3.PNG file4.PNG + +foo@bar:~$ ./glob -i 'test_files_05/*.png' +"test_files_05/file2.png" +"test_files_05/file1.png" + +foo@bar:~$ ./glob -i 'test_files_05/*.PNG' +"test_files_05/file3.PNG" +"test_files_05/file4.PNG" + +foo@bar:~$ ./glob -i "test_files_05/*.png","test_files_05/*.PNG" +"test_files_05/file2.png" +"test_files_05/file1.png" +"test_files_05/file3.PNG" +"test_files_05/file4.PNG" +``` + +### Tilde expansion + +```console +foo@bar:~$ ./glob -i "~/.b*" +"/Users/pranav/.bashrc" +"/Users/pranav/.bash_sessions" +"/Users/pranav/.bash_profile" +"/Users/pranav/.bash_history" + +foo@bar:~$ ./glob -i "~/Documents/Projects/glob/**/glob/*.h" +"/Users/pranav/Documents/Projects/glob/include/glob/glob.h" +``` + +## Contributing +Contributions are welcome, have a look at the [CONTRIBUTING.md](CONTRIBUTING.md) document for more information. + +## License +The project is available under the [MIT](https://opensource.org/licenses/MIT) license. diff --git a/src/external/glob/include/glob/glob.h b/src/external/glob/include/glob/glob.h new file mode 100644 index 00000000..2a7d1bff --- /dev/null +++ b/src/external/glob/include/glob/glob.h @@ -0,0 +1,40 @@ + +#pragma once +#include +#include +#include + +namespace glob { + +/// \param pathname string containing a path specification +/// \return vector of paths that match the pathname +/// +/// Pathnames can be absolute (/usr/src/Foo/Makefile) or relative (../../Tools/*/*.gif) +/// Pathnames can contain shell-style wildcards +/// Broken symlinks are included in the results (as in the shell) +std::vector glob(const std::string &pathname); + +/// \param pathnames string containing a path specification +/// \return vector of paths that match the pathname +/// +/// Globs recursively. +/// The pattern “**” will match any files and zero or more directories, subdirectories and +/// symbolic links to directories. +std::vector rglob(const std::string &pathname); + +/// Runs `glob` against each pathname in `pathnames` and accumulates the results +std::vector glob(const std::vector &pathnames); + +/// Runs `rglob` against each pathname in `pathnames` and accumulates the results +std::vector rglob(const std::vector &pathnames); + +/// Initializer list overload for convenience +std::vector glob(const std::initializer_list &pathnames); + +/// Initializer list overload for convenience +std::vector rglob(const std::initializer_list &pathnames); + +/// Returns true if the input path matche the glob pattern + bool fnmatch(const std::filesystem::path &name, const std::string &pattern); + +} // namespace glob diff --git a/src/external/glob/single_include/glob/glob.hpp b/src/external/glob/single_include/glob/glob.hpp new file mode 100644 index 00000000..1cb1d18c --- /dev/null +++ b/src/external/glob/single_include/glob/glob.hpp @@ -0,0 +1,447 @@ +// +// TinyUSDZ modification: +// - Disable exception +// - Use GHC filesystem +// +#pragma once +#include +// Assume ghc filesystem header is included(with NO_EXCEPTION version) +//#include +#include +#include +#include +#include +#include +#include +#include + +#if 0 +namespace fs = std::filesystem; +#else +namespace fs = ghc::filesystem; +#endif + +namespace glob { + +namespace { + +static inline +bool string_replace(std::string &str, const std::string &from, const std::string &to) { + std::size_t start_pos = str.find(from); + if (start_pos == std::string::npos) + return false; + str.replace(start_pos, from.length(), to); + return true; +} + +static inline +std::string translate(const std::string &pattern) { + std::size_t i = 0, n = pattern.size(); + std::string result_string; + + while (i < n) { + auto c = pattern[i]; + i += 1; + if (c == '*') { + result_string += ".*"; + } else if (c == '?') { + result_string += "."; + } else if (c == '[') { + auto j = i; + if (j < n && pattern[j] == '!') { + j += 1; + } + if (j < n && pattern[j] == ']') { + j += 1; + } + while (j < n && pattern[j] != ']') { + j += 1; + } + if (j >= n) { + result_string += "\\["; + } else { + auto stuff = std::string(pattern.begin() + i, pattern.begin() + j); + if (stuff.find("--") == std::string::npos) { + string_replace(stuff, std::string{"\\"}, std::string{R"(\\)"}); + } else { + std::vector chunks; + std::size_t k = 0; + if (pattern[i] == '!') { + k = i + 2; + } else { + k = i + 1; + } + + while (true) { + k = pattern.find("-", k, j); + if (k == std::string::npos) { + break; + } + chunks.push_back(std::string(pattern.begin() + i, pattern.begin() + k)); + i = k + 1; + k = k + 3; + } + + chunks.push_back(std::string(pattern.begin() + i, pattern.begin() + j)); + // Escape backslashes and hyphens for set difference (--). + // Hyphens that create ranges shouldn't be escaped. + bool first = false; + for (auto &s : chunks) { + string_replace(s, std::string{"\\"}, std::string{R"(\\)"}); + string_replace(s, std::string{"-"}, std::string{R"(\-)"}); + if (first) { + stuff += s; + first = false; + } else { + stuff += "-" + s; + } + } + } + + // Escape set operations (&&, ~~ and ||). + std::string result; + std::regex_replace(std::back_inserter(result), // result + stuff.begin(), stuff.end(), // string + std::regex(std::string{R"([&~|])"}), // pattern + std::string{R"(\\\1)"}); // repl + stuff = result; + i = j + 1; + if (stuff[0] == '!') { + stuff = "^" + std::string(stuff.begin() + 1, stuff.end()); + } else if (stuff[0] == '^' || stuff[0] == '[') { + stuff = "\\\\" + stuff; + } + result_string = result_string + "[" + stuff + "]"; + } + } else { + // SPECIAL_CHARS + // closing ')', '}' and ']' + // '-' (a range in character set) + // '&', '~', (extended character set operations) + // '#' (comment) and WHITESPACE (ignored) in verbose mode + static std::string special_characters = "()[]{}?*+-|^$\\.&~# \t\n\r\v\f"; + static std::map special_characters_map; + if (special_characters_map.empty()) { + for (auto &sc : special_characters) { + special_characters_map.insert( + std::make_pair(static_cast(sc), std::string{"\\"} + std::string(1, sc))); + } + } + + if (special_characters.find(c) != std::string::npos) { + result_string += special_characters_map[static_cast(c)]; + } else { + result_string += c; + } + } + } + return std::string{"(("} + result_string + std::string{R"()|[\r\n])$)"}; +} + +static inline +std::regex compile_pattern(const std::string &pattern) { + return std::regex(translate(pattern), std::regex::ECMAScript); +} + +static inline +bool fnmatch(const fs::path &name, const std::string &pattern) { + return std::regex_match(name.string(), compile_pattern(pattern)); +} + +static inline +std::vector filter(const std::vector &names, + const std::string &pattern) { + // std::cout << "Pattern: " << pattern << "\n"; + std::vector result; + for (auto &name : names) { + // std::cout << "Checking for " << name.string() << "\n"; + if (fnmatch(name, pattern)) { + result.push_back(name); + } + } + return result; +} + +static inline +fs::path expand_tilde(fs::path path) { + if (path.empty()) return path; +#ifdef _WIN32 + char* home; + size_t sz; + _dupenv_s(&home, &sz, "USERPROFILE"); +#else + const char * home = std::getenv("HOME"); +#endif + if (home == nullptr) { + //throw std::invalid_argument("error: Unable to expand `~` - HOME environment variable not set."); + return path; + } + + std::string s = path.string(); + if (s[0] == '~') { + s = std::string(home) + s.substr(1, s.size() - 1); + return fs::path(s); + } else { + return path; + } +} + +static inline +bool has_magic(const std::string &pathname) { + static const auto magic_check = std::regex("([*?[])"); + return std::regex_search(pathname, magic_check); +} + +static inline +bool is_hidden(const std::string &pathname) { + return std::regex_match(pathname, std::regex("^(.*\\/)*\\.[^\\.\\/]+\\/*$")); +} + +static inline +bool is_recursive(const std::string &pattern) { return pattern == "**"; } + +static inline +std::vector iter_directory(const fs::path &dirname, bool dironly) { + std::vector result; + + std::error_code ec; + + auto current_directory = dirname; + if (current_directory.empty()) { + current_directory = fs::current_path(ec); + } + + if (fs::exists(current_directory, ec)) { +#if 0 + try { + for (auto &entry : fs::directory_iterator( + current_directory, fs::directory_options::follow_directory_symlink | + fs::directory_options::skip_permission_denied, ec)) { + if (!dironly || entry.is_directory()) { + if (dirname.is_absolute()) { + result.push_back(entry.path()); + } else { + result.push_back(fs::relative(entry.path())); + } + } + } + } catch (std::exception&) { + // not a directory + // do nothing + } +#else + auto it = fs::directory_iterator(current_directory, ec); + auto itE = fs::end(it); + for (; it != itE; it.increment(ec)) { + + if (ec) { + // TODO: Report error + continue; + } + + if (!dironly || it->is_directory(ec)) { + if (dirname.is_absolute()) { + result.push_back(it->path()); + } else { + result.push_back(fs::relative(it->path(), ec)); + } + } + } + +#endif + } + + return result; +} + +// Recursively yields relative pathnames inside a literal directory. +static inline +std::vector rlistdir(const fs::path &dirname, bool dironly) { + std::vector result; + auto names = iter_directory(dirname, dironly); + for (auto &x : names) { + if (!is_hidden(x.string())) { + result.push_back(x); + for (auto &y : rlistdir(x, dironly)) { + result.push_back(y); + } + } + } + return result; +} + +// This helper function recursively yields relative pathnames inside a literal +// directory. +static inline +std::vector glob2(const fs::path &dirname, [[maybe_unused]] const std::string &pattern, + bool dironly) { + // std::cout << "In glob2\n"; + std::vector result; + assert(is_recursive(pattern)); + for (auto &dir : rlistdir(dirname, dironly)) { + result.push_back(dir); + } + return result; +} + +// These 2 helper functions non-recursively glob inside a literal directory. +// They return a list of basenames. _glob1 accepts a pattern while _glob0 +// takes a literal basename (so it only has to check for its existence). +static inline +std::vector glob1(const fs::path &dirname, const std::string &pattern, + bool dironly) { + // std::cout << "In glob1\n"; + auto names = iter_directory(dirname, dironly); + std::vector filtered_names; + for (auto &n : names) { + if (!is_hidden(n.string())) { + filtered_names.push_back(n.filename()); + // if (n.is_relative()) { + // // std::cout << "Filtered (Relative): " << n << "\n"; + // filtered_names.push_back(fs::relative(n)); + // } else { + // // std::cout << "Filtered (Absolute): " << n << "\n"; + // filtered_names.push_back(n.filename()); + // } + } + } + return filter(filtered_names, pattern); +} + +static inline +std::vector glob0(const fs::path &dirname, const fs::path &basename, + bool /*dironly*/) { + // std::cout << "In glob0\n"; + std::vector result; + std::error_code ec; + if (basename.empty()) { + // 'q*x/' should match only directories. + if (fs::is_directory(dirname, ec)) { + result = {basename}; + } + } else { + if (fs::exists(dirname / basename, ec)) { + result = {basename}; + } + } + return result; +} + +static inline +std::vector glob(const std::string &pathname, bool recursive = false, + bool dironly = false) { + std::vector result; + + auto path = fs::path(pathname); + std::error_code ec; + + if (pathname[0] == '~') { + // expand tilde + path = expand_tilde(path); + } + + auto dirname = path.parent_path(); + const auto basename = path.filename(); + + if (!has_magic(pathname)) { + assert(!dironly); + if (!basename.empty()) { + if (fs::exists(path, ec)) { + result.push_back(path); + } + } else { + // Patterns ending with a slash should match only directories + if (fs::is_directory(dirname, ec)) { + result.push_back(path); + } + } + return result; + } + + if (dirname.empty()) { + if (recursive && is_recursive(basename.string())) { + return glob2(dirname, basename.string(), dironly); + } else { + return glob1(dirname, basename.string(), dironly); + } + } + + std::vector dirs; + if (dirname != fs::path(pathname) && has_magic(dirname.string())) { + dirs = glob(dirname.string(), recursive, true); + } else { + dirs = {dirname}; + } + + std::function(const fs::path &, const std::string &, bool)> + glob_in_dir; + if (has_magic(basename.string())) { + if (recursive && is_recursive(basename.string())) { + glob_in_dir = glob2; + } else { + glob_in_dir = glob1; + } + } else { + glob_in_dir = glob0; + } + + for (auto &d : dirs) { + for (auto &name : glob_in_dir(d, basename.string(), dironly)) { + fs::path subresult = name; + if (name.parent_path().empty()) { + subresult = d / name; + } + result.push_back(subresult); + } + } + + return result; +} + +} // namespace end + +static inline +std::vector glob(const std::string &pathname) { + return glob(pathname, false); +} + +static inline +std::vector rglob(const std::string &pathname) { + return glob(pathname, true); +} + +static inline +std::vector glob(const std::vector &pathnames) { + std::vector result; + for (auto &pathname : pathnames) { + for (auto &match : glob(pathname, false)) { + result.push_back(std::move(match)); + } + } + return result; +} + +static inline +std::vector rglob(const std::vector &pathnames) { + std::vector result; + for (auto &pathname : pathnames) { + for (auto &match : glob(pathname, true)) { + result.push_back(std::move(match)); + } + } + return result; +} + +static inline +std::vector +glob(const std::initializer_list &pathnames) { + return glob(std::vector(pathnames)); +} + +static inline +std::vector +rglob(const std::initializer_list &pathnames) { + return rglob(std::vector(pathnames)); +} + +} // namespace glob diff --git a/src/external/glob/source/glob.cpp b/src/external/glob/source/glob.cpp new file mode 100644 index 00000000..e48fe091 --- /dev/null +++ b/src/external/glob/source/glob.cpp @@ -0,0 +1,381 @@ + +#include +#include +#include +#include +#include +#include +namespace fs = std::filesystem; + +namespace glob { + +namespace { + +bool string_replace(std::string &str, const std::string &from, const std::string &to) { + std::size_t start_pos = str.find(from); + if (start_pos == std::string::npos) + return false; + str.replace(start_pos, from.length(), to); + return true; +} + +std::string translate(const std::string &pattern) { + std::size_t i = 0, n = pattern.size(); + std::string result_string; + + while (i < n) { + auto c = pattern[i]; + i += 1; + if (c == '*') { + result_string += ".*"; + } else if (c == '?') { + result_string += "."; + } else if (c == '[') { + auto j = i; + if (j < n && pattern[j] == '!') { + j += 1; + } + if (j < n && pattern[j] == ']') { + j += 1; + } + while (j < n && pattern[j] != ']') { + j += 1; + } + if (j >= n) { + result_string += "\\["; + } else { + auto stuff = std::string(pattern.begin() + i, pattern.begin() + j); + if (stuff.find("--") == std::string::npos) { + string_replace(stuff, std::string{"\\"}, std::string{R"(\\)"}); + } else { + std::vector chunks; + std::size_t k = 0; + if (pattern[i] == '!') { + k = i + 2; + } else { + k = i + 1; + } + + while (true) { + k = pattern.find("-", k, j); + if (k == std::string::npos) { + break; + } + chunks.push_back(std::string(pattern.begin() + i, pattern.begin() + k)); + i = k + 1; + k = k + 3; + } + + chunks.push_back(std::string(pattern.begin() + i, pattern.begin() + j)); + // Escape backslashes and hyphens for set difference (--). + // Hyphens that create ranges shouldn't be escaped. + bool first = true; + for (auto &s : chunks) { + string_replace(s, std::string{"\\"}, std::string{R"(\\)"}); + string_replace(s, std::string{"-"}, std::string{R"(\-)"}); + if (first) { + stuff += s; + first = false; + } else { + stuff += "-" + s; + } + } + } + + // Escape set operations (&&, ~~ and ||). + std::string result; + std::regex_replace(std::back_inserter(result), // ressult + stuff.begin(), stuff.end(), // string + std::regex(std::string{R"([&~|])"}), // pattern + std::string{R"(\\\1)"}); // repl + stuff = result; + i = j + 1; + if (stuff[0] == '!') { + stuff = "^" + std::string(stuff.begin() + 1, stuff.end()); + } else if (stuff[0] == '^' || stuff[0] == '[') { + stuff = "\\\\" + stuff; + } + result_string = result_string + "[" + stuff + "]"; + } + } else { + // SPECIAL_CHARS + // closing ')', '}' and ']' + // '-' (a range in character set) + // '&', '~', (extended character set operations) + // '#' (comment) and WHITESPACE (ignored) in verbose mode + static std::string special_characters = "()[]{}?*+-|^$\\.&~# \t\n\r\v\f"; + static std::map special_characters_map; + if (special_characters_map.empty()) { + for (auto &sc : special_characters) { + special_characters_map.insert( + std::make_pair(static_cast(sc), std::string{"\\"} + std::string(1, sc))); + } + } + + if (special_characters.find(c) != std::string::npos) { + result_string += special_characters_map[static_cast(c)]; + } else { + result_string += c; + } + } + } + return std::string{"(("} + result_string + std::string{R"()|[\r\n])$)"}; +} + +std::regex compile_pattern(const std::string &pattern) { + return std::regex(translate(pattern), std::regex::ECMAScript); +} + +bool fnmatch(const fs::path &name, const std::string &pattern) { + return std::regex_match(name.string(), compile_pattern(pattern)); +} + +std::vector filter(const std::vector &names, + const std::string &pattern) { + // std::cout << "Pattern: " << pattern << "\n"; + std::vector result; + for (auto &name : names) { + // std::cout << "Checking for " << name.string() << "\n"; + if (fnmatch(name, pattern)) { + result.push_back(name); + } + } + return result; +} + +fs::path expand_tilde(fs::path path) { + if (path.empty()) return path; + + const char * home = std::getenv("HOME"); + if (home == nullptr) { + throw std::invalid_argument("error: Unable to expand `~` - HOME environment variable not set."); + } + + std::string s = path.string(); + if (s[0] == '~') { + s = std::string(home) + s.substr(1, s.size() - 1); + return fs::path(s); + } else { + return path; + } +} + +bool has_magic(const std::string &pathname) { + static const auto magic_check = std::regex("([*?[])"); + return std::regex_search(pathname, magic_check); +} + +bool is_hidden(const std::string &pathname) { return pathname[0] == '.'; } + +bool is_recursive(const std::string &pattern) { return pattern == "**"; } + +std::vector iter_directory(const fs::path &dirname, bool dironly) { + std::vector result; + + auto current_directory = dirname; + if (current_directory.empty()) { + current_directory = fs::current_path(); + } + + if (fs::exists(current_directory)) { + try { + for (auto &entry : fs::directory_iterator( + current_directory, fs::directory_options::follow_directory_symlink | + fs::directory_options::skip_permission_denied)) { + if (!dironly || entry.is_directory()) { + if (dirname.is_absolute()) { + result.push_back(entry.path()); + } else { + result.push_back(fs::relative(entry.path())); + } + } + } + } catch (std::exception&) { + // not a directory + // do nothing + } + } + + return result; +} + +// Recursively yields relative pathnames inside a literal directory. +std::vector rlistdir(const fs::path &dirname, bool dironly) { + std::vector result; + auto names = iter_directory(dirname, dironly); + for (auto &x : names) { + if (!is_hidden(x.string())) { + result.push_back(x); + for (auto &y : rlistdir(x, dironly)) { + result.push_back(y); + } + } + } + return result; +} + +// This helper function recursively yields relative pathnames inside a literal +// directory. +std::vector glob2(const fs::path &dirname, [[maybe_unused]] const fs::path &pattern, + bool dironly) { + // std::cout << "In glob2\n"; + std::vector result; + assert(is_recursive(pattern.string())); + for (auto &dir : rlistdir(dirname, dironly)) { + result.push_back(dir); + } + return result; +} + +// These 2 helper functions non-recursively glob inside a literal directory. +// They return a list of basenames. _glob1 accepts a pattern while _glob0 +// takes a literal basename (so it only has to check for its existence). + +std::vector glob1(const fs::path &dirname, const fs::path &pattern, + bool dironly) { + // std::cout << "In glob1\n"; + auto names = iter_directory(dirname, dironly); + std::vector filtered_names; + for (auto &n : names) { + if (!is_hidden(n.string())) { + filtered_names.push_back(n.filename()); + // if (n.is_relative()) { + // // std::cout << "Filtered (Relative): " << n << "\n"; + // filtered_names.push_back(fs::relative(n)); + // } else { + // // std::cout << "Filtered (Absolute): " << n << "\n"; + // filtered_names.push_back(n.filename()); + // } + } + } + return filter(filtered_names, pattern.string()); +} + +std::vector glob0(const fs::path &dirname, const fs::path &basename, + bool /*dironly*/) { + // std::cout << "In glob0\n"; + std::vector result; + if (basename.empty()) { + // 'q*x/' should match only directories. + if (fs::is_directory(dirname)) { + result = {basename}; + } + } else { + if (fs::exists(dirname / basename)) { + result = {basename}; + } + } + return result; +} + +std::vector glob(const fs::path &inpath, bool recursive = false, + bool dironly = false) { + std::vector result; + + const auto pathname = inpath.string(); + auto path = fs::path(pathname); + + if (pathname[0] == '~') { + // expand tilde + path = expand_tilde(path); + } + + auto dirname = path.parent_path(); + const auto basename = path.filename(); + + if (!has_magic(pathname)) { + assert(!dironly); + if (!basename.empty()) { + if (fs::exists(path)) { + result.push_back(path); + } + } else { + // Patterns ending with a slash should match only directories + if (fs::is_directory(dirname)) { + result.push_back(path); + } + } + return result; + } + + if (dirname.empty()) { + if (recursive && is_recursive(basename.string())) { + return glob2(dirname, basename, dironly); + } else { + return glob1(dirname, basename, dironly); + } + } + + std::vector dirs; + if (dirname != fs::path(pathname) && has_magic(dirname.string())) { + dirs = glob(dirname, recursive, true); + } else { + dirs = {dirname}; + } + + std::function(const fs::path &, const fs::path &, bool)> + glob_in_dir; + if (has_magic(basename.string())) { + if (recursive && is_recursive(basename.string())) { + glob_in_dir = glob2; + } else { + glob_in_dir = glob1; + } + } else { + glob_in_dir = glob0; + } + + for (auto &d : dirs) { + for (auto &name : glob_in_dir(d, basename, dironly)) { + fs::path subresult = name; + if (name.parent_path().empty()) { + subresult = d / name; + } + result.push_back(subresult); + } + } + + return result; +} + +} // namespace end + +std::vector glob(const std::string &pathname) { + return glob(pathname, false); +} + +std::vector rglob(const std::string &pathname) { + return glob(pathname, true); +} + +std::vector glob(const std::vector &pathnames) { + std::vector result; + for (auto &pathname : pathnames) { + for (auto &match : glob(pathname, false)) { + result.push_back(std::move(match)); + } + } + return result; +} + +std::vector rglob(const std::vector &pathnames) { + std::vector result; + for (auto &pathname : pathnames) { + for (auto &match : glob(pathname, true)) { + result.push_back(std::move(match)); + } + } + return result; +} + +std::vector +glob(const std::initializer_list &pathnames) { + return glob(std::vector(pathnames)); +} + +std::vector +rglob(const std::initializer_list &pathnames) { + return rglob(std::vector(pathnames)); +} + +} // namespace glob diff --git a/src/external/tiny_gltf.h b/src/external/tiny_gltf.h index 586f8da1..5722ee18 100644 --- a/src/external/tiny_gltf.h +++ b/src/external/tiny_gltf.h @@ -26,6 +26,8 @@ // THE SOFTWARE. // Version: +// - v2.6.0 Disable expanding file path for security(no use of awkward `wordexp` anymore). +// Support serializing sparse accessor(Thanks to @fynv). // - v2.5.0 Add SetPreserveImageChannels() option to load image data as is. // - v2.4.3 Fix null object output when when material has all default // parameters. @@ -108,7 +110,11 @@ namespace tinygltf { #define TINYGLTF_COMPONENT_TYPE_INT (5124) #define TINYGLTF_COMPONENT_TYPE_UNSIGNED_INT (5125) #define TINYGLTF_COMPONENT_TYPE_FLOAT (5126) -#define TINYGLTF_COMPONENT_TYPE_DOUBLE (5130) // OpenGL double type. Note that some of glTF 2.0 validator does not support double type even the schema seems allow any value of integer: https://github.com/KhronosGroup/glTF/blob/b9884a2fd45130b4d673dd6c8a706ee21ee5c5f7/specification/2.0/schema/accessor.schema.json#L22 +#define TINYGLTF_COMPONENT_TYPE_DOUBLE \ + (5130) // OpenGL double type. Note that some of glTF 2.0 validator does not + // support double type even the schema seems allow any value of + // integer: + // https://github.com/KhronosGroup/glTF/blob/b9884a2fd45130b4d673dd6c8a706ee21ee5c5f7/specification/2.0/schema/accessor.schema.json#L22 #define TINYGLTF_TEXTURE_FILTER_NEAREST (9728) #define TINYGLTF_TEXTURE_FILTER_LINEAR (9729) @@ -613,7 +619,8 @@ struct Sampler { int wrapT = TINYGLTF_TEXTURE_WRAP_REPEAT; // ["CLAMP_TO_EDGE", "MIRRORED_REPEAT", // "REPEAT"], default "REPEAT" - //int wrapR = TINYGLTF_TEXTURE_WRAP_REPEAT; // TinyGLTF extension. currently not used. + // int wrapR = TINYGLTF_TEXTURE_WRAP_REPEAT; // TinyGLTF extension. currently + // not used. Value extras; ExtensionMap extensions; @@ -1302,8 +1309,10 @@ class TinyGLTF { /// /// Loads glTF ASCII asset from string(memory). /// `length` = strlen(str); - /// Set warning message to `warn` for example it fails to load asserts. - /// Returns false and set error string to `err` if there's an error. + /// `base_dir` is a search path of glTF asset(e.g. images). Path Must be an + /// expanded path (e.g. no tilde(`~`), no environment variables). Set warning + /// message to `warn` for example it fails to load asserts. Returns false and + /// set error string to `err` if there's an error. /// bool LoadASCIIFromString(Model *model, std::string *err, std::string *warn, const char *str, const unsigned int length, @@ -1322,6 +1331,8 @@ class TinyGLTF { /// /// Loads glTF binary asset from memory. /// `length` = strlen(str); + /// `base_dir` is a search path of glTF asset(e.g. images). Path Must be an + /// expanded path (e.g. no tilde(`~`), no environment variables). /// Set warning message to `warn` for example it fails to load asserts. /// Returns false and set error string to `err` if there's an error. /// @@ -1424,6 +1435,10 @@ class TinyGLTF { bool preserve_image_channels_ = false; /// Default false(expand channels to /// RGBA) for backward compatibility. + // Warning & error messages + std::string warn_; + std::string err_; + FsCallbacks fs = { #ifndef TINYGLTF_NO_FS &tinygltf::FileExists, &tinygltf::ExpandFilePath, @@ -1605,10 +1620,10 @@ class TinyGLTF { #endif #elif !defined(__ANDROID__) && !defined(__OpenBSD__) -#include +//#include #endif -#if defined(__sparcv9) +#if defined(__sparcv9) || defined(__powerpc__) // Big endian #else #if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) || MINIZ_X86_OR_X64_CPU @@ -1933,10 +1948,9 @@ bool Sampler::operator==(const Sampler &other) const { return this->extensions == other.extensions && this->extras == other.extras && this->magFilter == other.magFilter && this->minFilter == other.minFilter && this->name == other.name && - this->wrapS == other.wrapS && - this->wrapT == other.wrapT; + this->wrapS == other.wrapS && this->wrapT == other.wrapT; - //this->wrapR == other.wrapR + // this->wrapR == other.wrapR } bool Scene::operator==(const Scene &other) const { return this->extensions == other.extensions && this->extras == other.extras && @@ -2042,8 +2056,7 @@ static std::string GetBaseDir(const std::string &filepath) { static std::string GetBaseFilename(const std::string &filepath) { auto idx = filepath.find_last_of("/\\"); - if (idx != std::string::npos) - return filepath.substr(idx + 1); + if (idx != std::string::npos) return filepath.substr(idx + 1); return filepath; } @@ -2605,6 +2618,18 @@ bool FileExists(const std::string &abs_filename, void *) { } std::string ExpandFilePath(const std::string &filepath, void *) { + // https://github.com/syoyo/tinygltf/issues/368 + // + // No file path expansion in built-in FS function anymore, since glTF URI + // should not contain tilde('~') and environment variables, and for security + // reason(`wordexp`). + // + // Users need to supply `base_dir`(in `LoadASCIIFromString`, + // `LoadBinaryFromMemory`) in expanded absolute path. + + return filepath; + +#if 0 #ifdef _WIN32 // Assume input `filepath` is encoded in UTF-8 std::wstring wfilepath = UTF8ToWchar(filepath); @@ -2652,6 +2677,7 @@ std::string ExpandFilePath(const std::string &filepath, void *) { return s; #endif +#endif } bool ReadWholeFile(std::vector *out, std::string *err, @@ -3092,11 +3118,17 @@ std::string JsonToString(const json &o, int spacing = -1) { StringBuffer buffer; if (spacing == -1) { Writer writer(buffer); - o.Accept(writer); + // TODO: Better error handling. + // https://github.com/syoyo/tinygltf/issues/332 + if (!o.Accept(writer)) { + return "tiny_gltf::JsonToString() failed rapidjson conversion"; + } } else { PrettyWriter writer(buffer); writer.SetIndent(' ', uint32_t(spacing)); - o.Accept(writer); + if (!o.Accept(writer)) { + return "tiny_gltf::JsonToString() failed rapidjson conversion"; + } } return buffer.GetString(); #else @@ -4236,20 +4268,20 @@ static bool ParseSparseAccessor(Accessor *accessor, std::string *err, const json &values_obj = GetValue(values_iterator); int indices_buffer_view = 0, indices_byte_offset = 0, component_type = 0; - if (!ParseIntegerProperty(&indices_buffer_view, err, indices_obj, "bufferView", - true, "SparseAccessor")) { + if (!ParseIntegerProperty(&indices_buffer_view, err, indices_obj, + "bufferView", true, "SparseAccessor")) { return false; } ParseIntegerProperty(&indices_byte_offset, err, indices_obj, "byteOffset", false); if (!ParseIntegerProperty(&component_type, err, indices_obj, "componentType", - true, "SparseAccessor")) { + true, "SparseAccessor")) { return false; } int values_buffer_view = 0, values_byte_offset = 0; if (!ParseIntegerProperty(&values_buffer_view, err, values_obj, "bufferView", - true, "SparseAccessor")) { + true, "SparseAccessor")) { return false; } ParseIntegerProperty(&values_byte_offset, err, values_obj, "byteOffset", @@ -5088,12 +5120,13 @@ static bool ParseSampler(Sampler *sampler, std::string *err, const json &o, int magFilter = -1; int wrapS = TINYGLTF_TEXTURE_WRAP_REPEAT; int wrapT = TINYGLTF_TEXTURE_WRAP_REPEAT; - //int wrapR = TINYGLTF_TEXTURE_WRAP_REPEAT; + // int wrapR = TINYGLTF_TEXTURE_WRAP_REPEAT; ParseIntegerProperty(&minFilter, err, o, "minFilter", false); ParseIntegerProperty(&magFilter, err, o, "magFilter", false); ParseIntegerProperty(&wrapS, err, o, "wrapS", false); ParseIntegerProperty(&wrapT, err, o, "wrapT", false); - //ParseIntegerProperty(&wrapR, err, o, "wrapR", false); // tinygltf extension + // ParseIntegerProperty(&wrapR, err, o, "wrapR", false); // tinygltf + // extension // TODO(syoyo): Check the value is alloed one. // (e.g. we allow 9728(NEAREST), but don't allow 9727) @@ -5102,7 +5135,7 @@ static bool ParseSampler(Sampler *sampler, std::string *err, const json &o, sampler->magFilter = magFilter; sampler->wrapS = wrapS; sampler->wrapT = wrapT; - //sampler->wrapR = wrapR; + // sampler->wrapR = wrapR; ParseExtensionsProperty(&(sampler->extensions), err, o); ParseExtrasProperty(&(sampler->extras), o); @@ -6683,6 +6716,27 @@ static void SerializeGltfAccessor(Accessor &accessor, json &o) { if (accessor.extras.Type() != NULL_TYPE) { SerializeValue("extras", accessor.extras, o); } + + // sparse + if (accessor.sparse.isSparse) + { + json sparse; + SerializeNumberProperty("count", accessor.sparse.count, sparse); + { + json indices; + SerializeNumberProperty("bufferView", accessor.sparse.indices.bufferView, indices); + SerializeNumberProperty("byteOffset", accessor.sparse.indices.byteOffset, indices); + SerializeNumberProperty("componentType", accessor.sparse.indices.componentType, indices); + JsonAddMember(sparse, "indices", std::move(indices)); + } + { + json values; + SerializeNumberProperty("bufferView", accessor.sparse.values.bufferView, values); + SerializeNumberProperty("byteOffset", accessor.sparse.values.byteOffset, values); + JsonAddMember(sparse, "values", std::move(values)); + } + JsonAddMember(o, "sparse", std::move(sparse)); + } } static void SerializeGltfAnimationChannel(AnimationChannel &channel, json &o) { @@ -7165,7 +7219,7 @@ static void SerializeGltfSampler(Sampler &sampler, json &o) { if (sampler.minFilter != -1) { SerializeNumberProperty("minFilter", sampler.minFilter, o); } - //SerializeNumberProperty("wrapR", sampler.wrapR, o); + // SerializeNumberProperty("wrapR", sampler.wrapR, o); SerializeNumberProperty("wrapS", sampler.wrapS, o); SerializeNumberProperty("wrapT", sampler.wrapT, o); @@ -7519,7 +7573,7 @@ static bool WriteGltfFile(const std::string &output, return WriteGltfStream(gltfFile, content); } -static void WriteBinaryGltfStream(std::ostream &stream, +static bool WriteBinaryGltfStream(std::ostream &stream, const std::string &content, const std::vector &binBuffer) { const std::string header = "glTF"; @@ -7528,8 +7582,10 @@ static void WriteBinaryGltfStream(std::ostream &stream, const uint32_t content_size = uint32_t(content.size()); const uint32_t binBuffer_size = uint32_t(binBuffer.size()); // determine number of padding bytes required to ensure 4 byte alignment - const uint32_t content_padding_size = content_size % 4 == 0 ? 0 : 4 - content_size % 4; - const uint32_t bin_padding_size = binBuffer_size % 4 == 0 ? 0 : 4 - binBuffer_size % 4; + const uint32_t content_padding_size = + content_size % 4 == 0 ? 0 : 4 - content_size % 4; + const uint32_t bin_padding_size = + binBuffer_size % 4 == 0 ? 0 : 4 - binBuffer_size % 4; // 12 bytes for header, JSON content length, 8 bytes for JSON chunk info. // Chunk data must be located at 4-byte boundary, which may require padding @@ -7573,9 +7629,12 @@ static void WriteBinaryGltfStream(std::ostream &stream, std::streamsize(padding.size())); } } + + // TODO: Check error on stream.write + return true; } -static void WriteBinaryGltfFile(const std::string &output, +static bool WriteBinaryGltfFile(const std::string &output, const std::string &content, const std::vector &binBuffer) { #ifdef _WIN32 @@ -7593,7 +7652,7 @@ static void WriteBinaryGltfFile(const std::string &output, #else std::ofstream gltfFile(output.c_str(), std::ios::binary); #endif - WriteBinaryGltfStream(gltfFile, content, binBuffer); + return WriteBinaryGltfStream(gltfFile, content, binBuffer); } bool TinyGLTF::WriteGltfSceneToStream(Model *model, std::ostream &stream, @@ -7632,7 +7691,7 @@ bool TinyGLTF::WriteGltfSceneToStream(Model *model, std::ostream &stream, // UpdateImageObject need baseDir but only uses it if embeddedImages is // enabled, since we won't write separate images when writing to a stream // we - UpdateImageObject(model->images[i], dummystring, int(i), false, + UpdateImageObject(model->images[i], dummystring, int(i), true, &this->WriteImageData, this->write_image_user_data_); SerializeGltfImage(model->images[i], image); JsonPushBack(images, std::move(image)); @@ -7641,12 +7700,11 @@ bool TinyGLTF::WriteGltfSceneToStream(Model *model, std::ostream &stream, } if (writeBinary) { - WriteBinaryGltfStream(stream, JsonToString(output), binBuffer); + return WriteBinaryGltfStream(stream, JsonToString(output), binBuffer); } else { - WriteGltfStream(stream, JsonToString(output, prettyPrint ? 2 : -1)); + return WriteGltfStream(stream, JsonToString(output, prettyPrint ? 2 : -1)); } - return true; } bool TinyGLTF::WriteGltfSceneToFile(Model *model, const std::string &filename, @@ -7731,12 +7789,11 @@ bool TinyGLTF::WriteGltfSceneToFile(Model *model, const std::string &filename, } if (writeBinary) { - WriteBinaryGltfFile(filename, JsonToString(output), binBuffer); + return WriteBinaryGltfFile(filename, JsonToString(output), binBuffer); } else { - WriteGltfFile(filename, JsonToString(output, (prettyPrint ? 2 : -1))); + return WriteGltfFile(filename, JsonToString(output, (prettyPrint ? 2 : -1))); } - return true; } } // namespace tinygltf diff --git a/src/io-util.cc b/src/io-util.cc index ece73f31..fdd7951a 100644 --- a/src/io-util.cc +++ b/src/io-util.cc @@ -44,8 +44,21 @@ #endif + #endif // _WIN32 +#ifdef __clang__ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Weverything" +#endif + +#include "external/filesystem/include/ghc/filesystem.hpp" +#include "external/glob/single_include/glob/glob.hpp" + +#ifdef __clang__ +#pragma clang diagnostic pop +#endif + #include "io-util.hh" namespace tinyusdz { @@ -85,7 +98,8 @@ std::string ExpandFilePath(const std::string &filepath, void *) { // Quote the string to keep any spaces in filepath intact. std::string quoted_path = "\"" + filepath + "\""; // char** w; - int ret = wordexp(quoted_path.c_str(), &p, 0); + // TODO: Implement our own file path expansion routine. + int ret = wordexp(quoted_path.c_str(), &p, WRDE_NOCMD); if (ret) { // err s = filepath;