IVGCVSW-4487 Adds ghc::filesystem to third-party

 * as replacement for boost::filesystem
 * added to cmake
 * added to TPIP in readme.md

Signed-off-by: Jan Eilers <jan.eilers@arm.com>
Change-Id: I0098e490e8597a9bae4781cacba8e19f8a50d1f0
diff --git a/README.md b/README.md
index 6b9dea5..5e21aa2 100644
--- a/README.md
+++ b/README.md
@@ -59,6 +59,7 @@
 | half    | MIT               |
 | stb     | MIT               |
 | cxxopts | MIT               |
+| ghc     | MIT               |
 
 ### Contributions
 
diff --git a/cmake/GlobalConfig.cmake b/cmake/GlobalConfig.cmake
index 9cf825e..3e0f0fe 100644
--- a/cmake/GlobalConfig.cmake
+++ b/cmake/GlobalConfig.cmake
@@ -135,6 +135,10 @@
 find_path(CXXOPTS_INCLUDE cxxopts/cxxopts.hpp PATHS third-party)
 include_directories(SYSTEM "${CXXOPTS_INCLUDE}")
 
+# ghc (Alternative to boost::filesystem)
+find_path(GHC_INCLUDE ghc/filesystem.hpp PATHS third-party)
+include_directories(SYSTEM "${GHC_INCLUDE}")
+
 # pthread
 find_package (Threads)
 
diff --git a/third-party/ghc/LICENSE b/third-party/ghc/LICENSE
new file mode 100644
index 0000000..8b24faa
--- /dev/null
+++ b/third-party/ghc/LICENSE
@@ -0,0 +1,19 @@
+Copyright (c) 2018, Steffen Schümann <s.schuemann@pobox.com>
+
+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/third-party/ghc/README.md b/third-party/ghc/README.md
new file mode 100644
index 0000000..00ba1ce
--- /dev/null
+++ b/third-party/ghc/README.md
@@ -0,0 +1,746 @@
+![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)
+[![Build Status](https://travis-ci.org/gulrak/filesystem.svg?branch=master)](https://travis-ci.org/gulrak/filesystem)
+[![Build Status](https://ci.appveyor.com/api/projects/status/t07wp3k2cddo0hpo/branch/master?svg=true)](https://ci.appveyor.com/project/gulrak/filesystem)
+[![Build Status](https://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.3.2)
+
+# Filesystem
+
+This is a header-only single-file std::filesystem compatible helper library,
+based on the C++17 specs, but implemented for C++11, C++14 or C++17 (tightly following
+the C++17 with very few documented exceptions). It is currently tested on
+macOS 10.12/10.14, Windows 10, Ubuntu 18.04, FreeBSD 12 and Alpine ARM/ARM64 Linux
+but should work on other systems too, as long as you have at least a 
+C++11 compatible compiler. It 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.
+
+*It could still use some polishing, test coverage is above 90%, I didn't benchmark
+much yet, but I'll try to optimize some parts and refactor others, so I'm striving
+to improve it as long as it doesn't introduce additional C++17 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).
+
+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!
+
+Oh, and 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).
+
+## Platforms
+
+`ghc::filesystem` is developed on macOS but tested on Windows and Linux.
+It should work on any of these with a C++11-capable compiler. I currently
+don't have a BSD derivate besides macOS, so the preprocessor checks will
+cry out if you try to use it there, but if there is demand, I can try to
+help. 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. All in all, I don't see it replacing `std::filesystem`
+where full C++17 is available, it doesn't try to be a "better"
+`std::filesystem`, just a drop-in if you can't use it (with the exception
+of the UTF-8 preference on Windows).
+
+
+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
+* Windows: Visual Studio 2017, Visual Studio 2015, Visual Studio 2019, MinGW GCC 6.3 (Win32), GCC 7.2 (Win64)
+* 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
+
+
+## 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 agains 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
+```
+
+This generates `filesystem_test`, the binary that runs all tests.
+
+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.3.2](https://github.com/gulrak/filesystem/tree/v1.3.2) and
+source archives can be found [here](https://github.com/gulrak/filesystem/releases/tag/v1.3.2).
+
+### 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 oder 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
+#if defined(__cplusplus) && __cplusplus >= 201703L && defined(__has_include)
+#if __has_include(<filesystem>)
+#define GHC_USE_STD_FS
+#include <filesystem>
+namespace fs = std::filesystem;
+#endif
+#endif
+#ifndef GHC_USE_STD_FS
+#include <ghc/filesystem.hpp>
+namespace fs = ghc::filesystem;
+#endif
+```
+
+**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.**
+
+**Note also, that on MSVC this detection only works starting from version 15.7 on and when setting
+the `/Zc:__cplusplus` compile switch, as the compiler allways reports `199711L`
+without that switch ([see](https://blogs.msdn.microsoft.com/vcblog/2018/04/09/msvc-now-correctly-reports-__cplusplus/)).**
+
+If you want to also use the `fstream` wrapper with `path` support as fallback,
+you might use:
+
+```cpp
+#if defined(__cplusplus) && __cplusplus >= 201703L && defined(__has_include)
+#if __has_include(<filesystem>)
+#define GHC_USE_STD_FS
+#include <filesystem>
+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 <ghc/filesystem.hpp>
+namespace fs {
+using namespace ghc::filesystem;
+using ifstream = ghc::filesystem::ifstream;
+using ofstream = ghc::filesystem::ofstream;
+using fstream = ghc::filesystem::fstream;
+} 
+#endif
+```
+
+Now you have e.g. `fs::ofstream out(somePath);` and it is either the wrapper or
+the C++17 `std::ofstream`.
+
+**Be aware, as a header-only library, it is not hiding the fact, that it
+uses system includes, so they "pollute" your global namespace.**
+
+: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. It also enables the `wchar_t`
+support on `ghc::filesystem` on Windows, so the resulting implementation
+in the `fs` namespace will be compatible.
+
+
+### 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 that includes `ghc/fs_impl.hpp` to
+implement the needed code. That way system includes are only visible from
+inside the cpp, 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
+#if defined(__cplusplus) && __cplusplus >= 201703L && defined(__has_include)
+#if __has_include(<filesystem>)
+#define GHC_USE_STD_FS
+#include <filesystem>
+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 <ghc/fs-fwd.hpp>
+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
+#if !(defined(__cplusplus) && __cplusplus >= 201703L && defined(__has_include)
+#if __has_include(<filesystem>))
+#include <ghc/fs_impl.hpp>
+#endif
+#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 <ghc/filesystem.hpp>`
+to work.
+
+The `CMakeLists.txt` offers a few options to customize its behaviour:
+
+* `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`.
+
+### 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)`.
+
+**Note:** Starting from v1.0.2 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.
+
+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 constuctor 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 als 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, leading to some differences
+between this and the standard C++17 API.
+
+
+### 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) and
+[#2937](http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html#2937).
+The currently selected behavior is following
+[#2682](https://wg21.cmeerw.net/lwg/issue2682),
+[#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 revison
+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<value_type> x);
+int compare(basic_string_view<value_type> 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.
+
+
+### Differences in API
+
+```cpp
+filesystem::path::string_type
+filesystem::path::value_type
+```
+
+In Windows, an implementation should use `std::wstring` and `wchar_t` as types used
+for the native representation, but as I'm a big fan of the
+["UTF-8 Everywhere" philosophy](https://utf8everywhere.org/), I decided
+agains it for now. If you need to call some Windows API, use the W-variant
+with the `path::wstring()` member
+(e.g. `GetFileAttributesW(p.wstring().c_str())`). This gives you the
+Unicode variant independant of the `UNICODE` macro and makes sharing code
+between Windows, Linux and macOS easier.
+
+Starting with v1.2.0 `ghc::filesystem` has the option to select the more
+standard conforming APi with `wchar_t` and `std::wstring` on Windows by
+defining `GHC_WIN_WSTRING_STRING_TYPE`. This define has no effect on other
+platforms and will be set by the helping headers `ghc/fs_std.hpp` and
+the pair `ghc/fs_std_fwd.hpp`/`ghc/fs_std_impl.hpp` to enhance compatibility.
+
+
+```cpp
+const path::string_type& path::native() const /*noexcept*/;
+const path::value_type *path::c_str() const /*noexcept*/;
+```
+
+These two can not be `noexcept` with the current implementation. This due
+to the fact, that internally path is working on the generic path version
+only, and the getters need to do a conversion to native path format.
+
+```cpp
+const path::string_type& path::generic_string() const;
+```
+
+This returns a const reference, instead of a value, because it can. This
+implementation uses the generic representation for internal workings, so
+it's "free" to return that.
+
+
+### Differences in Behavior
+
+#### fs.path ([ref](https://en.cppreference.com/w/cpp/filesystem/path))
+
+As the complete inner mechanics of this implementation `fs::path` are working
+on the generic format, it is the internal representation. So creating any mixed
+slash `fs::path` object under Windows (e.g. with `"C:\foo/bar"`) will lead to a
+unified path with `"C:\foo\bar"` via `native()` and `"C:/foo/bar"` via
+`generic_string()` API.
+
+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_seperator`
+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 behaviour will match the one described by
+the standard.
+
+#### fs.op.copy ([ref](https://en.cppreference.com/w/cpp/filesystem/copy))
+
+Then there is `fs::copy`. The tests in the suite fail partially with C++17 `std::filesystem`
+on GCC/Clang. They complain about a copy call with `fs::copy_options::recursive` combined
+with `fs::copy_options::create_symlinks` or `fs::copy_options::create_hard_links` if the
+source is a directory. There is nothing in the standard that forbids this combination
+and it is the only way to deep-copy a tree while only create links for the files.
+There is [LWG #2682](https://wg21.cmeerw.net/lwg/issue2682) that supports this
+interpretation, but the issue ignores the usefulness of the combination with recursive
+and part of the justification for the proposed solution is "we did it so for almost two years".
+But this makes `fs::copy` with `fs::copy_options::create_symlinks` or `fs::copy_options::create_hard_links`
+just a more complicated syntax for the `fs::create_symlink` or `fs::create_hardlink` operation
+and I don't want to believe, that this was the intention of the original writing.
+As there is another issue related to copy, with a different take on the description.
+
+**Note:** With v1.1.2 I decided to integrate a behavior switch for this and make the LWG #2682
+the default.
+
+## Open Issues
+
+### General Known Issues
+
+There are still some methods that break the `noexcept` clause, some
+are related to LWG defects, some are due to my implementation. I
+work on fixing the later ones, and might in cases where there is no
+way of implementing the feature without risk of an exception, break
+conformance and remove the `noexcept`.
+
+### 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 with 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.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 infrastucture 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 axisting 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` der 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), warings
+  on Xcode 11.2 due to unhelpfull 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 seting `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_seperator` 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 `<vector>` 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 depricated, 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 behaviour 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 `<ghc/filesystem.hpp>` 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 <ghc/filesystem.hpp>`
+  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 dereferencable 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/third-party/ghc/filesystem.hpp b/third-party/ghc/filesystem.hpp
new file mode 100644
index 0000000..1f0fe39
--- /dev/null
+++ b/third-party/ghc/filesystem.hpp
@@ -0,0 +1,5214 @@
+//---------------------------------------------------------------------------------------
+//
+// ghc::filesystem - A C++17-like filesystem implementation for C++11/C++14/C++17
+//
+//---------------------------------------------------------------------------------------
+//
+// Copyright (c) 2018, Steffen Schümann <s.schuemann@pobox.com>
+//
+// 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, you could use:
+//
+// #if defined(__cplusplus) && __cplusplus >= 201703L && defined(__has_include) && __has_include(<filesystem>)
+// #include <filesystem>
+// namespace fs = std::filesystem;
+// #else
+// #include <ghc/filesystem.hpp>
+// 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 <sys/param.h>
+#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(__svr4__)
+#define GHC_OS_SYS5R4
+#elif defined(BSD)
+#define GHC_OS_BSD
+#else
+#error "Operating system currently not supported!"
+#endif
+#define GHC_OS_DETECTED
+#endif
+
+#if defined(GHC_FILESYSTEM_IMPLEMENTATION)
+#define GHC_EXPAND_IMPL
+#define GHC_INLINE
+#ifdef GHC_OS_WINDOWS
+#define GHC_FS_API
+#define GHC_FS_API_CLASS
+#else
+#define GHC_FS_API __attribute__((visibility("default")))
+#define GHC_FS_API_CLASS __attribute__((visibility("default")))
+#endif
+#elif defined(GHC_FILESYSTEM_FWD)
+#define GHC_INLINE
+#ifdef GHC_OS_WINDOWS
+#define GHC_FS_API extern
+#define GHC_FS_API_CLASS
+#else
+#define GHC_FS_API extern
+#define GHC_FS_API_CLASS
+#endif
+#else
+#define GHC_EXPAND_IMPL
+#define GHC_INLINE inline
+#define GHC_FS_API
+#define GHC_FS_API_CLASS
+#endif
+
+#ifdef GHC_EXPAND_IMPL
+
+#ifdef GHC_OS_WINDOWS
+#include <windows.h>
+// additional includes
+#include <shellapi.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <wchar.h>
+#include <winioctl.h>
+#else
+#include <dirent.h>
+#include <fcntl.h>
+#include <langinfo.h>
+#include <sys/param.h>
+#include <sys/stat.h>
+#include <sys/statvfs.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <limits.h>
+#ifdef GHC_OS_ANDROID
+#include <android/api-level.h>
+#endif
+#endif
+#ifdef GHC_OS_MACOS
+#include <Availability.h>
+#endif
+
+#include <algorithm>
+#include <cctype>
+#include <chrono>
+#include <clocale>
+#include <cstdlib>
+#include <cstring>
+#include <fstream>
+#include <functional>
+#include <memory>
+#include <stack>
+#include <stdexcept>
+#include <string>
+#include <system_error>
+#include <type_traits>
+#include <utility>
+#include <vector>
+
+#else  // GHC_EXPAND_IMPL
+#include <chrono>
+#include <fstream>
+#include <memory>
+#include <stack>
+#include <stdexcept>
+#include <string>
+#include <system_error>
+#ifdef GHC_OS_WINDOWS
+#include <vector>
+#endif
+#endif  // GHC_EXPAND_IMPL
+
+//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+// Behaviour Switches (see README.md, should match the config in test/filesystem_test.cpp):
+//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+// 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 superceded by P1164R1, so only activate if really needed
+// #define LWG_2935_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. With this define you can
+// enable the more standard conforming implementation option that uses wstring on Windows
+// as ghc::filesystem::string_type.
+// #define GHC_WIN_WSTRING_STRING_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
+//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
+
+// ghc::filesystem version in decimal (major * 10000 + minor * 100 + patch)
+#define GHC_FILESYSTEM_VERSION 10302L
+
+namespace ghc {
+namespace filesystem {
+
+// 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<typename char_type>
+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 <typename char_type>
+constexpr char_type path_helper_base<char_type>::preferred_separator;
+#endif
+    
+// 30.10.8 class path
+class GHC_FS_API_CLASS path
+#if defined(GHC_OS_WINDOWS) && defined(GHC_WIN_WSTRING_STRING_TYPE)
+#define GHC_USE_WCHAR_T
+    : private path_helper_base<std::wstring::value_type>
+{
+public:
+    using path_helper_base<std::wstring::value_type>::value_type;
+#else
+    : private path_helper_base<std::string::value_type>
+{
+public:
+    using path_helper_base<std::string::value_type>::value_type;
+#endif
+    using string_type = std::basic_string<value_type>;
+    using path_helper_base<value_type>::preferred_separator;
+    
+    // 30.10.10.1 enumeration format
+    /// The path format in wich 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 <class T>
+    struct _is_basic_string : std::false_type
+    {
+    };
+    template <class CharT, class Traits, class Alloc>
+    struct _is_basic_string<std::basic_string<CharT, Traits, Alloc>> : std::true_type
+    {
+    };
+#ifdef __cpp_lib_string_view
+    template <class CharT>
+    struct _is_basic_string<std::basic_string_view<CharT>> : std::true_type
+    {
+    };
+#endif
+
+    template <typename T1, typename T2 = void>
+    using path_type = typename std::enable_if<!std::is_same<path, T1>::value, path>::type;
+#ifdef GHC_USE_WCHAR_T
+    template <typename T>
+    using path_from_string = typename std::enable_if<_is_basic_string<T>::value || std::is_same<char const*, typename std::decay<T>::type>::value || std::is_same<char*, typename std::decay<T>::type>::value ||
+                                                         std::is_same<wchar_t const*, typename std::decay<T>::type>::value || std::is_same<wchar_t*, typename std::decay<T>::type>::value,
+                                                     path>::type;
+    template <typename T>
+    using path_type_EcharT = typename std::enable_if<std::is_same<T, char>::value || std::is_same<T, char16_t>::value || std::is_same<T, char32_t>::value, path>::type;
+#else
+    template <typename T>
+    using path_from_string = typename std::enable_if<_is_basic_string<T>::value || std::is_same<char const*, typename std::decay<T>::type>::value || std::is_same<char*, typename std::decay<T>::type>::value, path>::type;
+    template <typename T>
+    using path_type_EcharT = typename std::enable_if<std::is_same<T, char>::value || std::is_same<T, char16_t>::value || std::is_same<T, char32_t>::value || std::is_same<T, wchar_t>::value, path>::type;
+#endif
+    // 30.10.8.4.1 constructors and destructor
+    path() noexcept;
+    path(const path& p);
+    path(path&& p) noexcept;
+    path(string_type&& source, format fmt = auto_format);
+    template <class Source, typename = path_from_string<Source>>
+    path(const Source& source, format fmt = auto_format);
+    template <class InputIterator>
+    path(InputIterator first, InputIterator last, format fmt = auto_format);
+    template <class Source, typename = path_from_string<Source>>
+    path(const Source& source, const std::locale& loc, format fmt = auto_format);
+    template <class InputIterator>
+    path(InputIterator first, InputIterator last, const std::locale& loc, format fmt = auto_format);
+    ~path();
+
+    // 30.10.8.4.2 assignments
+    path& operator=(const path& p);
+    path& operator=(path&& p) noexcept;
+    path& operator=(string_type&& source);
+    path& assign(string_type&& source);
+    template <class Source>
+    path& operator=(const Source& source);
+    template <class Source>
+    path& assign(const Source& source);
+    template <class InputIterator>
+    path& assign(InputIterator first, InputIterator last);
+
+    // 30.10.8.4.3 appends
+    path& operator/=(const path& p);
+    template <class Source>
+    path& operator/=(const Source& source);
+    template <class Source>
+    path& append(const Source& source);
+    template <class InputIterator>
+    path& append(InputIterator first, InputIterator last);
+
+    // 30.10.8.4.4 concatenation
+    path& operator+=(const path& x);
+    path& operator+=(const string_type& x);
+#ifdef __cpp_lib_string_view
+    path& operator+=(std::basic_string_view<value_type> x);
+#endif
+    path& operator+=(const value_type* x);
+    path& operator+=(value_type x);
+    template <class Source>
+    path_from_string<Source>& operator+=(const Source& x);
+    template <class EcharT>
+    path_type_EcharT<EcharT>& operator+=(EcharT x);
+    template <class Source>
+    path& concat(const Source& x);
+    template <class InputIterator>
+    path& concat(InputIterator first, InputIterator last);
+
+    // 30.10.8.4.5 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;
+
+    // 30.10.8.4.6 native format observers
+    const string_type& native() const;  // this implementation doesn't support noexcept for native()
+    const value_type* c_str() const;    // this implementation doesn't support noexcept for c_str()
+    operator string_type() const;
+    template <class EcharT, class traits = std::char_traits<EcharT>, class Allocator = std::allocator<EcharT>>
+    std::basic_string<EcharT, traits, Allocator> string(const Allocator& a = Allocator()) const;
+    std::string string() const;
+    std::wstring wstring() const;
+    std::string u8string() const;
+    std::u16string u16string() const;
+    std::u32string u32string() const;
+
+    // 30.10.8.4.7 generic format observers
+    template <class EcharT, class traits = std::char_traits<EcharT>, class Allocator = std::allocator<EcharT>>
+    std::basic_string<EcharT, traits, Allocator> generic_string(const Allocator& a = Allocator()) const;
+    const std::string& generic_string() const;  // this is different from the standard, that returns by value
+    std::wstring generic_wstring() const;
+    std::string generic_u8string() const;
+    std::u16string generic_u16string() const;
+    std::u32string generic_u32string() const;
+
+    // 30.10.8.4.8 compare
+    int compare(const path& p) const noexcept;
+    int compare(const string_type& s) const;
+#ifdef __cpp_lib_string_view
+    int compare(std::basic_string_view<value_type> s) const;
+#endif
+    int compare(const value_type* s) const;
+
+    // 30.10.8.4.9 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;
+
+    // 30.10.8.4.10 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;
+
+    // 30.10.8.4.11 generation
+    path lexically_normal() const;
+    path lexically_relative(const path& base) const;
+    path lexically_proximate(const path& base) const;
+
+    // 30.10.8.5 iterators
+    class iterator;
+    using const_iterator = iterator;
+    iterator begin() const;
+    iterator end() const;
+
+private:
+    using impl_value_type = std::string::value_type;
+    using impl_string_type = std::basic_string<impl_value_type>;
+    friend class directory_iterator;
+    void append_name(const char* name);
+    static constexpr impl_value_type generic_separator = '/';
+    template <typename InputIterator>
+    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;
+    static void postprocess_path_with_format(impl_string_type& p, format fmt);
+    impl_string_type _path;
+#ifdef GHC_OS_WINDOWS
+    impl_string_type native_impl() const;
+    mutable string_type _native_cache;
+#else
+    const impl_string_type& native_impl() const;
+#endif
+};
+
+// 30.10.8.6 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;
+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);
+
+// 30.10.8.6.1 path inserter and extractor
+template <class charT, class traits>
+std::basic_ostream<charT, traits>& operator<<(std::basic_ostream<charT, traits>& os, const path& p);
+template <class charT, class traits>
+std::basic_istream<charT, traits>& operator>>(std::basic_istream<charT, traits>& is, path& p);
+
+// 30.10.8.6.2 path factory functions
+template <class Source, typename = path::path_from_string<Source>>
+path u8path(const Source& source);
+template <class InputIterator>
+path u8path(InputIterator first, InputIterator last);
+
+// 30.10.9 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 impl_string_type::const_iterator& first, const impl_string_type::const_iterator& last, 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:
+    impl_string_type::const_iterator increment(const std::string::const_iterator& pos) const;
+    impl_string_type::const_iterator decrement(const std::string::const_iterator& pos) const;
+    void updateCurrent();
+    impl_string_type::const_iterator _first;
+    impl_string_type::const_iterator _last;
+    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;
+};
+
+// 30.10.10, enumerations
+enum class file_type {
+    none,
+    not_found,
+    regular,
+    directory,
+    symlink,
+    block,
+    character,
+    fifo,
+    socket,
+    unknown,
+};
+
+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
+};
+
+enum class perm_options : uint16_t {
+    replace = 3,
+    add = 1,
+    remove = 2,
+    nofollow = 4,
+};
+
+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,
+    create_hard_links = 0x100
+};
+
+enum class directory_options : uint16_t {
+    none = 0,
+    follow_directory_symlink = 1,
+    skip_permission_denied = 2,
+};
+
+// 30.10.11 class file_status
+class GHC_FS_API_CLASS file_status
+{
+public:
+    // 30.10.11.1 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;
+    // 30.10.11.3 modifiers
+    void type(file_type ft) noexcept;
+    void permissions(perms prms) noexcept;
+    // 30.10.11.2 observers
+    file_type type() const noexcept;
+    perms permissions() const noexcept;
+
+private:
+    file_type _type;
+    perms _perms;
+};
+
+using file_time_type = std::chrono::time_point<std::chrono::system_clock>;
+
+// 30.10.12 Class directory_entry
+class GHC_FS_API_CLASS directory_entry
+{
+public:
+    // 30.10.12.1 constructors and destructor
+    directory_entry() noexcept = default;
+    directory_entry(const directory_entry&) = default;
+    directory_entry(directory_entry&&) noexcept = default;
+    explicit directory_entry(const path& p);
+    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;
+
+    // 30.10.12.2 modifiers
+    void assign(const path& p);
+    void assign(const path& p, std::error_code& ec);
+    void replace_filename(const path& p);
+    void replace_filename(const path& p, std::error_code& ec);
+    void refresh();
+    void refresh(std::error_code& ec) noexcept;
+
+    // 30.10.12.3 observers
+    const filesystem::path& path() const noexcept;
+    operator const filesystem::path&() const noexcept;
+    bool exists() const;
+    bool exists(std::error_code& ec) const noexcept;
+    bool is_block_file() const;
+    bool is_block_file(std::error_code& ec) const noexcept;
+    bool is_character_file() const;
+    bool is_character_file(std::error_code& ec) const noexcept;
+    bool is_directory() const;
+    bool is_directory(std::error_code& ec) const noexcept;
+    bool is_fifo() const;
+    bool is_fifo(std::error_code& ec) const noexcept;
+    bool is_other() const;
+    bool is_other(std::error_code& ec) const noexcept;
+    bool is_regular_file() const;
+    bool is_regular_file(std::error_code& ec) const noexcept;
+    bool is_socket() const;
+    bool is_socket(std::error_code& ec) const noexcept;
+    bool is_symlink() const;
+    bool is_symlink(std::error_code& ec) const noexcept;
+    uintmax_t file_size() const;
+    uintmax_t file_size(std::error_code& ec) const noexcept;
+    uintmax_t hard_link_count() const;
+    uintmax_t hard_link_count(std::error_code& ec) const noexcept;
+    file_time_type last_write_time() const;
+    file_time_type last_write_time(std::error_code& ec) const noexcept;
+
+    file_status status() const;
+    file_status status(std::error_code& ec) const noexcept;
+
+    file_status symlink_status() const;
+    file_status symlink_status(std::error_code& ec) 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;
+    bool operator>=(const directory_entry& rhs) const noexcept;
+
+private:
+    friend class directory_iterator;
+    filesystem::path _path;
+    file_status _status;
+    file_status _symlink_status;
+    uintmax_t _file_size = 0;
+#ifndef GHC_OS_WINDOWS
+    uintmax_t _hard_link_count = 0;
+#endif
+    time_t _last_write_time = 0;
+};
+
+// 30.10.13 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&;
+
+    // 30.10.13.1 member functions
+    directory_iterator() noexcept;
+    explicit directory_iterator(const path& p);
+    directory_iterator(const path& p, directory_options options);
+    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;
+    directory_iterator& operator++();
+    directory_iterator& increment(std::error_code& ec) noexcept;
+
+    // other members as required by 27.2.3, input iterators
+    proxy operator++(int)
+    {
+        proxy p{**this};
+        ++*this;
+        return p;
+    }
+    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> _impl;
+};
+
+// 30.10.13.2 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;
+
+// 30.10.14 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&;
+
+    // 30.10.14.1 constructors and destructor
+    recursive_directory_iterator() noexcept;
+    explicit recursive_directory_iterator(const path& p);
+    recursive_directory_iterator(const path& p, directory_options options);
+    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();
+
+    // 30.10.14.1 observers
+    directory_options options() const;
+    int depth() const;
+    bool recursion_pending() const;
+
+    const directory_entry& operator*() const;
+    const directory_entry* operator->() const;
+
+    // 30.10.14.1 modifiers recursive_directory_iterator&
+    recursive_directory_iterator& operator=(const recursive_directory_iterator& rhs);
+    recursive_directory_iterator& operator=(recursive_directory_iterator&& rhs) noexcept;
+    recursive_directory_iterator& operator++();
+    recursive_directory_iterator& increment(std::error_code& ec) noexcept;
+
+    void pop();
+    void pop(std::error_code& ec);
+    void disable_recursion_pending();
+
+    // other members as required by 27.2.3, input iterators
+    directory_iterator::proxy operator++(int)
+    {
+        directory_iterator::proxy proxy{**this};
+        ++*this;
+        return proxy;
+    }
+    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<directory_iterator> _dir_iter_stack;
+        recursive_directory_iterator_impl(directory_options options, bool recursion_pending)
+            : _options(options)
+            , _recursion_pending(recursion_pending)
+        {
+        }
+    };
+    std::shared_ptr<recursive_directory_iterator_impl> _impl;
+};
+
+// 30.10.14.2 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;
+
+// 30.10.15 filesystem operations
+GHC_FS_API path absolute(const path& p);
+GHC_FS_API path absolute(const path& p, std::error_code& ec);
+
+GHC_FS_API path canonical(const path& p);
+GHC_FS_API path canonical(const path& p, std::error_code& ec);
+
+GHC_FS_API void copy(const path& from, const path& to);
+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);
+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);
+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);
+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);
+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);
+GHC_FS_API bool create_directories(const path& p, std::error_code& ec) noexcept;
+
+GHC_FS_API bool create_directory(const path& p);
+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);
+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);
+GHC_FS_API void create_directory_symlink(const path& to, const path& new_symlink, std::error_code& ec) noexcept;
+
+GHC_FS_API void create_hard_link(const path& to, const path& new_hard_link);
+GHC_FS_API void create_hard_link(const path& to, const path& new_hard_link, std::error_code& ec) noexcept;
+
+GHC_FS_API void create_symlink(const path& to, const path& new_symlink);
+GHC_FS_API void create_symlink(const path& to, const path& new_symlink, std::error_code& ec) noexcept;
+
+GHC_FS_API path current_path();
+GHC_FS_API path current_path(std::error_code& ec);
+GHC_FS_API void current_path(const path& p);
+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);
+GHC_FS_API bool exists(const path& p, std::error_code& ec) noexcept;
+
+GHC_FS_API bool equivalent(const path& p1, const path& p2);
+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);
+GHC_FS_API uintmax_t file_size(const path& p, std::error_code& ec) noexcept;
+
+GHC_FS_API uintmax_t hard_link_count(const path& p);
+GHC_FS_API uintmax_t hard_link_count(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);
+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);
+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);
+GHC_FS_API bool is_directory(const path& p, std::error_code& ec) noexcept;
+GHC_FS_API bool is_empty(const path& p);
+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);
+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);
+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);
+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);
+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);
+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);
+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);
+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, perm_options opts = perm_options::replace);
+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);
+
+GHC_FS_API path proximate(const path& p, std::error_code& ec);
+GHC_FS_API path proximate(const path& p, const path& base = current_path());
+GHC_FS_API path proximate(const path& p, const path& base, std::error_code& ec);
+
+GHC_FS_API path read_symlink(const path& p);
+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 = current_path());
+GHC_FS_API path relative(const path& p, const path& base, std::error_code& ec);
+
+GHC_FS_API bool remove(const path& p);
+GHC_FS_API bool remove(const path& p, std::error_code& ec) noexcept;
+
+GHC_FS_API uintmax_t remove_all(const path& p);
+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);
+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);
+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);
+GHC_FS_API space_info space(const path& p, std::error_code& ec) noexcept;
+
+GHC_FS_API file_status status(const path& p);
+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);
+GHC_FS_API file_status symlink_status(const path& p, std::error_code& ec) noexcept;
+
+GHC_FS_API path temp_directory_path();
+GHC_FS_API path temp_directory_path(std::error_code& ec) noexcept;
+
+GHC_FS_API path weakly_canonical(const path& p);
+GHC_FS_API path weakly_canonical(const path& p, std::error_code& ec) noexcept;
+
+// Non-C++17 add-on std::fstream wrappers with path
+template <class charT, class traits = std::char_traits<charT>>
+class basic_filebuf : public std::basic_filebuf<charT, traits>
+{
+public:
+    basic_filebuf() {}
+    ~basic_filebuf() override {}
+    basic_filebuf(const basic_filebuf&) = delete;
+    const basic_filebuf& operator=(const basic_filebuf&) = delete;
+    basic_filebuf<charT, traits>* open(const path& p, std::ios_base::openmode mode)
+    {
+#if defined(GHC_OS_WINDOWS) && !defined(__GNUC__)
+        return std::basic_filebuf<charT, traits>::open(p.wstring().c_str(), mode) ? this : 0;
+#else
+        return std::basic_filebuf<charT, traits>::open(p.string().c_str(), mode) ? this : 0;
+#endif
+    }
+};
+
+template <class charT, class traits = std::char_traits<charT>>
+class basic_ifstream : public std::basic_ifstream<charT, traits>
+{
+public:
+    basic_ifstream() {}
+#if defined(GHC_OS_WINDOWS) && !defined(__GNUC__)
+    explicit basic_ifstream(const path& p, std::ios_base::openmode mode = std::ios_base::in)
+        : std::basic_ifstream<charT, traits>(p.wstring().c_str(), mode)
+    {
+    }
+    void open(const path& p, std::ios_base::openmode mode = std::ios_base::in) { std::basic_ifstream<charT, traits>::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<charT, traits>(p.string().c_str(), mode)
+    {
+    }
+    void open(const path& p, std::ios_base::openmode mode = std::ios_base::in) { std::basic_ifstream<charT, traits>::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 charT, class traits = std::char_traits<charT>>
+class basic_ofstream : public std::basic_ofstream<charT, traits>
+{
+public:
+    basic_ofstream() {}
+#if defined(GHC_OS_WINDOWS) && !defined(__GNUC__)
+    explicit basic_ofstream(const path& p, std::ios_base::openmode mode = std::ios_base::out)
+        : std::basic_ofstream<charT, traits>(p.wstring().c_str(), mode)
+    {
+    }
+    void open(const path& p, std::ios_base::openmode mode = std::ios_base::out) { std::basic_ofstream<charT, traits>::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<charT, traits>(p.string().c_str(), mode)
+    {
+    }
+    void open(const path& p, std::ios_base::openmode mode = std::ios_base::out) { std::basic_ofstream<charT, traits>::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 charT, class traits = std::char_traits<charT>>
+class basic_fstream : public std::basic_fstream<charT, traits>
+{
+public:
+    basic_fstream() {}
+#if defined(GHC_OS_WINDOWS) && !defined(__GNUC__)
+    explicit basic_fstream(const path& p, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out)
+        : std::basic_fstream<charT, traits>(p.wstring().c_str(), mode)
+    {
+    }
+    void open(const path& p, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out) { std::basic_fstream<charT, traits>::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<charT, traits>(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<charT, traits>::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<char> filebuf;
+typedef basic_filebuf<wchar_t> wfilebuf;
+typedef basic_ifstream<char> ifstream;
+typedef basic_ifstream<wchar_t> wifstream;
+typedef basic_ofstream<char> ofstream;
+typedef basic_ofstream<wchar_t> wofstream;
+typedef basic_fstream<char> fstream;
+typedef basic_fstream<wchar_t> 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<std::string> _args;
+    std::vector<char*> _argp;
+#endif
+};
+
+//-------------------------------------------------------------------------------------------------
+//  Implementation
+//-------------------------------------------------------------------------------------------------
+
+namespace detail {
+// GHC_FS_API void postprocess_path_with_format(path::impl_string_type& p, path::format fmt);
+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);
+#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<int>(err) : static_cast<int>(::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 <typename Enum>
+using EnableBitmask = typename std::enable_if<std::is_same<Enum, perms>::value || std::is_same<Enum, perm_options>::value || std::is_same<Enum, copy_options>::value || std::is_same<Enum, directory_options>::value, Enum>::type;
+}  // namespace detail
+
+template <typename Enum>
+detail::EnableBitmask<Enum> operator&(Enum X, Enum Y)
+{
+    using underlying = typename std::underlying_type<Enum>::type;
+    return static_cast<Enum>(static_cast<underlying>(X) & static_cast<underlying>(Y));
+}
+
+template <typename Enum>
+detail::EnableBitmask<Enum> operator|(Enum X, Enum Y)
+{
+    using underlying = typename std::underlying_type<Enum>::type;
+    return static_cast<Enum>(static_cast<underlying>(X) | static_cast<underlying>(Y));
+}
+
+template <typename Enum>
+detail::EnableBitmask<Enum> operator^(Enum X, Enum Y)
+{
+    using underlying = typename std::underlying_type<Enum>::type;
+    return static_cast<Enum>(static_cast<underlying>(X) ^ static_cast<underlying>(Y));
+}
+
+template <typename Enum>
+detail::EnableBitmask<Enum> operator~(Enum X)
+{
+    using underlying = typename std::underlying_type<Enum>::type;
+    return static_cast<Enum>(~static_cast<underlying>(X));
+}
+
+template <typename Enum>
+detail::EnableBitmask<Enum>& operator&=(Enum& X, Enum Y)
+{
+    X = X & Y;
+    return X;
+}
+
+template <typename Enum>
+detail::EnableBitmask<Enum>& operator|=(Enum& X, Enum Y)
+{
+    X = X | Y;
+    return X;
+}
+
+template <typename Enum>
+detail::EnableBitmask<Enum>& 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<uint32_t>(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<char>(unicode));
+    }
+    else if (unicode >= 0x80 && unicode <= 0x7ff) {
+        str.push_back(static_cast<char>((unicode >> 6) + 192));
+        str.push_back(static_cast<char>((unicode & 0x3f) + 128));
+    }
+    else if ((unicode >= 0x800 && unicode <= 0xd7ff) || (unicode >= 0xe000 && unicode <= 0xffff)) {
+        str.push_back(static_cast<char>((unicode >> 12) + 224));
+        str.push_back(static_cast<char>(((unicode & 0xfff) >> 6) + 128));
+        str.push_back(static_cast<char>((unicode & 0x3f) + 128));
+    }
+    else if (unicode >= 0x10000 && unicode <= 0x10ffff) {
+        str.push_back(static_cast<char>((unicode >> 18) + 240));
+        str.push_back(static_cast<char>(((unicode & 0x3ffff) >> 12) + 128));
+        str.push_back(static_cast<char>(((unicode & 0xfff) >> 6) + 128));
+        str.push_back(static_cast<char>((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<unsigned>(S_RJCT) : static_cast<unsigned>((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<uint8_t>(*iter++), codepoint)) == S_RJCT) {
+            return false;
+        }
+    }
+    if (utf8_state) {
+        return false;
+    }
+    return true;
+}
+
+}  // namespace detail
+    
+#endif
+    
+namespace detail {
+
+template <class StringType, typename std::enable_if<(sizeof(typename StringType::value_type) == 1)>::type* = nullptr>
+inline StringType fromUtf8(const std::string& utf8String, const typename StringType::allocator_type& alloc = typename StringType::allocator_type())
+{
+    return StringType(utf8String.begin(), utf8String.end(), alloc);
+}
+
+template <class StringType, typename std::enable_if<(sizeof(typename StringType::value_type) == 2)>::type* = nullptr>
+inline StringType fromUtf8(const std::string& utf8String, const typename StringType::allocator_type& alloc = typename StringType::allocator_type())
+{
+    StringType result(alloc);
+    result.reserve(utf8String.length());
+    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<uint8_t>(*iter++), codepoint)) == S_STRT) {
+            if (codepoint <= 0xffff) {
+                result += static_cast<typename StringType::value_type>(codepoint);
+            }
+            else {
+                codepoint -= 0x10000;
+                result += static_cast<typename StringType::value_type>((codepoint >> 10) + 0xd800);
+                result += static_cast<typename StringType::value_type>((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<typename StringType::value_type>(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<typename StringType::value_type>(0xfffd);
+#endif
+    }
+    return result;
+}
+
+template <class StringType, typename std::enable_if<(sizeof(typename StringType::value_type) == 4)>::type* = nullptr>
+inline StringType fromUtf8(const std::string& utf8String, const typename StringType::allocator_type& alloc = typename StringType::allocator_type())
+{
+    StringType result(alloc);
+    result.reserve(utf8String.length());
+    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<uint8_t>(*iter++), codepoint)) == S_STRT) {
+            result += static_cast<typename StringType::value_type>(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<typename StringType::value_type>(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<typename StringType::value_type>(0xfffd);
+#endif
+    }
+    return result;
+}
+
+template <typename charT, typename traits, typename Alloc, typename std::enable_if<(sizeof(charT) == 1), int>::type size = 1>
+inline std::string toUtf8(const std::basic_string<charT, traits, Alloc>& unicodeString)
+{
+    return std::string(unicodeString.begin(), unicodeString.end());
+}
+
+template <typename charT, typename traits, typename Alloc, typename std::enable_if<(sizeof(charT) == 2), int>::type size = 2>
+inline std::string toUtf8(const std::basic_string<charT, traits, Alloc>& 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 <typename charT, typename traits, typename Alloc, typename std::enable_if<(sizeof(charT) == 4), int>::type size = 4>
+inline std::string toUtf8(const std::basic_string<charT, traits, Alloc>& unicodeString)
+{
+    std::string result;
+    for (auto c : unicodeString) {
+        appendUTF8(result, static_cast<uint32_t>(c));
+    }
+    return result;
+}
+
+template <typename charT>
+inline std::string toUtf8(const charT* unicodeString)
+{
+    return toUtf8(std::basic_string<charT, std::char_traits<charT>>(unicodeString));
+}
+
+}  // namespace detail
+
+#ifdef GHC_EXPAND_IMPL
+
+namespace detail {
+
+GHC_INLINE bool startsWith(const std::string& what, const std::string& with)
+{
+    return with.length() <= what.length() && equal(with.begin(), with.end(), what.begin());
+}
+
+}  // namespace detail
+
+GHC_INLINE void path::postprocess_path_with_format(path::impl_string_type& p, path::format fmt)
+{
+#ifdef GHC_RAISE_UNICODE_ERRORS
+    if(!detail::validUtf8(p)) {
+        path t;
+        t._path = p;
+        throw filesystem_error("Illegal byte sequence for unicode character.", t, std::make_error_code(std::errc::illegal_byte_sequence));
+    }
+#endif
+    switch (fmt) {
+#ifndef GHC_OS_WINDOWS
+        case path::auto_format:
+        case path::native_format:
+#endif
+        case path::generic_format:
+            // nothing to do
+            break;
+#ifdef GHC_OS_WINDOWS
+        case path::auto_format:
+        case path::native_format:
+            if (detail::startsWith(p, std::string("\\\\?\\"))) {
+                // remove Windows long filename marker
+                p.erase(0, 4);
+                if (detail::startsWith(p, std::string("UNC\\"))) {
+                    p.erase(0, 2);
+                    p[0] = '\\';
+                }
+            }
+            for (auto& c : p) {
+                if (c == '\\') {
+                    c = '/';
+                }
+            }
+            break;
+#endif
+    }
+    if (p.length() > 2 && p[0] == '/' && p[1] == '/' && p[2] != '/') {
+        std::string::iterator new_end = std::unique(p.begin() + 2, p.end(), [](path::value_type lhs, path::value_type rhs) { return lhs == rhs && lhs == '/'; });
+        p.erase(new_end, p.end());
+    }
+    else {
+        std::string::iterator new_end = std::unique(p.begin(), p.end(), [](path::value_type lhs, path::value_type rhs) { return lhs == rhs && lhs == '/'; });
+        p.erase(new_end, p.end());
+    }
+}
+
+#endif  // GHC_EXPAND_IMPL
+
+template <class Source, typename>
+inline path::path(const Source& source, format fmt)
+    : _path(detail::toUtf8(source))
+{
+    postprocess_path_with_format(_path, fmt);
+}
+template <>
+inline path::path(const std::wstring& source, format fmt)
+{
+    _path = detail::toUtf8(source);
+    postprocess_path_with_format(_path, fmt);
+}
+template <>
+inline path::path(const std::u16string& source, format fmt)
+{
+    _path = detail::toUtf8(source);
+    postprocess_path_with_format(_path, fmt);
+}
+template <>
+inline path::path(const std::u32string& source, format fmt)
+{
+    _path = detail::toUtf8(source);
+    postprocess_path_with_format(_path, fmt);
+}
+
+#ifdef __cpp_lib_string_view
+template <>
+inline path::path(const std::string_view& source, format fmt)
+{
+    _path = detail::toUtf8(std::string(source));
+    postprocess_path_with_format(_path, fmt);
+}
+#endif
+
+template <class Source, typename>
+inline path u8path(const Source& source)
+{
+    return path(source);
+}
+template <class InputIterator>
+inline path u8path(InputIterator first, InputIterator last)
+{
+    return path(first, last);
+}
+
+template <class InputIterator>
+inline path::path(InputIterator first, InputIterator last, format fmt)
+    : path(std::basic_string<typename std::iterator_traits<InputIterator>::value_type>(first, last), fmt)
+{
+    // delegated
+}
+
+#ifdef GHC_EXPAND_IMPL
+
+namespace detail {
+
+GHC_INLINE bool equals_simple_insensitive(const char* str1, const char* str2)
+{
+#ifdef GHC_OS_WINDOWS
+#ifdef __GNUC__
+    while (::tolower((unsigned char)*str1) == ::tolower((unsigned char)*str2++)) {
+        if (*str1++ == 0)
+            return true;
+    }
+    return false;
+#else
+    return 0 == ::_stricmp(str1, str2);
+#endif
+#else
+    return 0 == ::strcasecmp(str1, str2);
+#endif
+}
+
+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 <typename ErrorNumber>
+GHC_INLINE std::string systemErrorText(ErrorNumber code = 0)
+{
+#if defined(GHC_OS_WINDOWS)
+    LPVOID msgBuf;
+    DWORD dw = code ? static_cast<DWORD>(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<CreateSymbolicLinkW_fp>(GetProcAddress(GetModuleHandleW(L"kernel32.dll"), "CreateSymbolicLinkW"));
+#if defined(__GNUC__) && __GNUC__ >= 8
+#pragma GCC diagnostic pop
+#endif
+    if (api_call) {
+        if (api_call(detail::fromUtf8<std::wstring>(new_symlink.u8string()).c_str(), detail::fromUtf8<std::wstring>(target_name.u8string()).c_str(), to_directory ? 1 : 0) == 0) {
+            auto result = ::GetLastError();
+            if (result == ERROR_PRIVILEGE_NOT_HELD && api_call(detail::fromUtf8<std::wstring>(new_symlink.u8string()).c_str(), detail::fromUtf8<std::wstring>(target_name.u8string()).c_str(), 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<CreateHardLinkW_fp>(GetProcAddress(GetModuleHandleW(L"kernel32.dll"), "CreateHardLinkW"));
+#if defined(__GNUC__) && __GNUC__ >= 8
+#pragma GCC diagnostic pop
+#endif
+    if (api_call) {
+        if (api_call(detail::fromUtf8<std::wstring>(new_hardlink.u8string()).c_str(), detail::fromUtf8<std::wstring>(target_name.u8string()).c_str(), NULL) == 0) {
+            ec = detail::make_system_error();
+        }
+    }
+    else {
+        ec = detail::make_system_error(ERROR_NOT_SUPPORTED);
+    }
+}
+#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();
+    }
+}
+
+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
+
+template <typename T>
+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<perms>(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<perms>(mode & 0xfff);
+    return file_status(ft, prms);
+#endif
+}
+
+GHC_INLINE path resolveSymlink(const path& p, std::error_code& ec)
+{
+#ifdef GHC_OS_WINDOWS
+#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
+
+    std::shared_ptr<void> file(CreateFileW(p.wstring().c_str(), 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, 0), CloseHandle);
+    if (file.get() == INVALID_HANDLE_VALUE) {
+        ec = detail::make_system_error();
+        return path();
+    }
+
+    std::shared_ptr<REPARSE_DATA_BUFFER> reparseData((REPARSE_DATA_BUFFER*)std::calloc(1, MAXIMUM_REPARSE_DATA_BUFFER_SIZE), std::free);
+    ULONG bufferUsed;
+    path result;
+    if (DeviceIoControl(file.get(), FSCTL_GET_REPARSE_POINT, 0, 0, reparseData.get(), MAXIMUM_REPARSE_DATA_BUFFER_SIZE, &bufferUsed, 0)) {
+        if (IsReparseTagMicrosoft(reparseData->ReparseTag)) {
+            switch (reparseData->ReparseTag) {
+                case IO_REPARSE_TAG_SYMLINK:
+                    result = std::wstring(&reparseData->SymbolicLinkReparseBuffer.PathBuffer[reparseData->SymbolicLinkReparseBuffer.PrintNameOffset / sizeof(WCHAR)], reparseData->SymbolicLinkReparseBuffer.PrintNameLength / sizeof(WCHAR));
+                    break;
+                case IO_REPARSE_TAG_MOUNT_POINT:
+                    result = std::wstring(&reparseData->MountPointReparseBuffer.PathBuffer[reparseData->MountPointReparseBuffer.PrintNameOffset / sizeof(WCHAR)], reparseData->MountPointReparseBuffer.PrintNameLength / sizeof(WCHAR));
+                    break;
+                default:
+                    break;
+            }
+        }
+    }
+    else {
+        ec = detail::make_system_error();
+    }
+    return result;
+#else
+    size_t bufferSize = 256;
+    while (true) {
+        std::vector<char> buffer(bufferSize, static_cast<char>(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<int>(bufferSize)) {
+            return path(std::string(buffer.data(), static_cast<std::string::size_type>(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<time_t>(ull.QuadPart / 10000000ULL - 11644473600ULL);
+}
+
+GHC_INLINE void timeToFILETIME(time_t t, FILETIME& ft)
+{
+    LONGLONG ll;
+    ll = Int32x32To64(t, 10000000) + 116444736000000000;
+    ft.dwLowDateTime = static_cast<DWORD>(ll);
+    ft.dwHighDateTime = static_cast<DWORD>(ll >> 32);
+}
+
+template <typename INFO>
+GHC_INLINE uintmax_t hard_links_from_INFO(const INFO* info)
+{
+    return static_cast<uintmax_t>(-1);
+}
+
+template <>
+GHC_INLINE uintmax_t hard_links_from_INFO<BY_HANDLE_FILE_INFORMATION>(const BY_HANDLE_FILE_INFORMATION* info)
+{
+    return info->nNumberOfLinks;
+}
+
+template <typename INFO>
+GHC_INLINE file_status status_from_INFO(const path& p, const INFO* info, std::error_code&, uintmax_t* sz = nullptr, time_t* lwt = nullptr) noexcept
+{
+    file_type ft = file_type::unknown;
+    if ((info->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) {
+        ft = file_type::symlink;
+    }
+    else {
+        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;
+    }
+    std::string ext = p.extension().generic_string();
+    if (equals_simple_insensitive(ext.c_str(), ".exe") || equals_simple_insensitive(ext.c_str(), ".cmd") || equals_simple_insensitive(ext.c_str(), ".bat") || equals_simple_insensitive(ext.c_str(), ".com")) {
+        prms = prms | perms::owner_exec | perms::group_exec | perms::others_exec;
+    }
+    if (sz) {
+        *sz = static_cast<uintmax_t>(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(detail::fromUtf8<std::wstring>(p.u8string()).c_str(), 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 (attr.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
+            fs.type(file_type::symlink);
+        }
+    }
+    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(p.wstring().c_str(), GetFileExInfoStandard, &attr)) {
+        ec = detail::make_system_error();
+    }
+    else if (attr.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
+        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 (fs.type() == file_type::symlink) {
+            result = ::stat(p.c_str(), &st);
+            if (result == 0) {
+                if (sls) {
+                    *sls = fs;
+                }
+                fs = detail::file_status_from_st_mode(st.st_mode);
+            }
+        }
+        if (sz) {
+            *sz = static_cast<uintmax_t>(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<size_t>(argc));
+    _argp.reserve(static_cast<size_t>(argc));
+    for (size_t i = 0; i < static_cast<size_t>(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
+}
+
+//-----------------------------------------------------------------------------
+// 30.10.8.4.1 constructors and destructor
+
+GHC_INLINE path::path() noexcept {}
+
+GHC_INLINE path::path(const path& p)
+    : _path(p._path)
+{
+}
+
+GHC_INLINE path::path(path&& p) noexcept
+    : _path(std::move(p._path))
+{
+}
+
+GHC_INLINE path::path(string_type&& source, format fmt)
+#ifdef GHC_USE_WCHAR_T
+    : _path(detail::toUtf8(source))
+#else
+    : _path(std::move(source))
+#endif
+{
+    postprocess_path_with_format(_path, fmt);
+}
+
+#endif  // GHC_EXPAND_IMPL
+
+template <class Source, typename>
+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 <class InputIterator>
+inline path::path(InputIterator first, InputIterator last, const std::locale& loc, format fmt)
+    : path(std::basic_string<typename std::iterator_traits<InputIterator>::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));
+    }
+}
+
+#ifdef GHC_EXPAND_IMPL
+
+GHC_INLINE path::~path() {}
+
+//-----------------------------------------------------------------------------
+// 30.10.8.4.2 assignments
+
+GHC_INLINE path& path::operator=(const path& p)
+{
+    _path = p._path;
+    return *this;
+}
+
+GHC_INLINE path& path::operator=(path&& p) noexcept
+{
+    _path = std::move(p._path);
+    return *this;
+}
+
+GHC_INLINE path& path::operator=(path::string_type&& source)
+{
+    return assign(source);
+}
+
+GHC_INLINE path& path::assign(path::string_type&& source)
+{
+#ifdef GHC_USE_WCHAR_T
+    _path = detail::toUtf8(source);
+#else
+    _path = std::move(source);
+#endif
+    postprocess_path_with_format(_path, native_format);
+    return *this;
+}
+
+#endif  // GHC_EXPAND_IMPL
+
+template <class Source>
+inline path& path::operator=(const Source& source)
+{
+    return assign(source);
+}
+
+template <class Source>
+inline path& path::assign(const Source& source)
+{
+    _path.assign(detail::toUtf8(source));
+    postprocess_path_with_format(_path, native_format);
+    return *this;
+}
+
+template <>
+inline path& path::assign<path>(const path& source)
+{
+    _path = source._path;
+    return *this;
+}
+
+template <class InputIterator>
+inline path& path::assign(InputIterator first, InputIterator last)
+{
+    _path.assign(first, last);
+    postprocess_path_with_format(_path, native_format);
+    return *this;
+}
+
+#ifdef GHC_EXPAND_IMPL
+
+//-----------------------------------------------------------------------------
+// 30.10.8.4.3 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] != '/' && _path[_path.length() - 1] != ':') {
+            _path += '/';
+        }
+        return *this;
+    }
+    if ((p.is_absolute() && (_path != root_name() || 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 += '/';
+    }
+    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] == '/')) {
+            _path += '/';
+        }
+        first = false;
+        _path += (*iter++).generic_string();
+    }
+    return *this;
+}
+
+GHC_INLINE void path::append_name(const char* name)
+{
+    if (_path.empty()) {
+        this->operator/=(path(name));
+    }
+    else {
+        if (_path.back() != path::generic_separator) {
+            _path.push_back(path::generic_separator);
+        }
+        _path += name;
+    }
+}
+
+#endif  // GHC_EXPAND_IMPL
+
+template <class Source>
+inline path& path::operator/=(const Source& source)
+{
+    return append(source);
+}
+
+template <class Source>
+inline path& path::append(const Source& source)
+{
+    return this->operator/=(path(detail::toUtf8(source)));
+}
+
+template <>
+inline path& path::append<path>(const path& p)
+{
+    return this->operator/=(p);
+}
+
+template <class InputIterator>
+inline path& path::append(InputIterator first, InputIterator last)
+{
+    std::basic_string<typename std::iterator_traits<InputIterator>::value_type> part(first, last);
+    return append(part);
+}
+
+#ifdef GHC_EXPAND_IMPL
+
+//-----------------------------------------------------------------------------
+// 30.10.8.4.4 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 __cpp_lib_string_view
+GHC_INLINE path& path::operator+=(std::basic_string_view<value_type> x)
+{
+    return concat(x);
+}
+#endif
+
+GHC_INLINE path& path::operator+=(const value_type* x)
+{
+    return concat(string_type(x));
+}
+
+GHC_INLINE path& path::operator+=(value_type x)
+{
+#ifdef GHC_OS_WINDOWS
+    if (x == '\\') {
+        x = generic_separator;
+    }
+#endif
+    if (_path.empty() || _path.back() != generic_separator) {
+#ifdef GHC_USE_WCHAR_T
+        _path += detail::toUtf8(string_type(1, x));
+#else
+        _path += x;
+#endif
+    }
+    return *this;
+}
+
+#endif  // GHC_EXPAND_IMPL
+
+template <class Source>
+inline path::path_from_string<Source>& path::operator+=(const Source& x)
+{
+    return concat(x);
+}
+
+template <class EcharT>
+inline path::path_type_EcharT<EcharT>& path::operator+=(EcharT x)
+{
+    std::basic_string<EcharT> part(1, x);
+    concat(detail::toUtf8(part));
+    return *this;
+}
+
+template <class Source>
+inline path& path::concat(const Source& x)
+{
+    path p(x);
+    postprocess_path_with_format(p._path, native_format);
+    _path += p._path;
+    return *this;
+}
+template <class InputIterator>
+inline path& path::concat(InputIterator first, InputIterator last)
+{
+    _path.append(first, last);
+    postprocess_path_with_format(_path, native_format);
+    return *this;
+}
+
+#ifdef GHC_EXPAND_IMPL
+
+//-----------------------------------------------------------------------------
+// 30.10.8.4.5 modifiers
+GHC_INLINE void path::clear() noexcept
+{
+    _path.clear();
+}
+
+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);
+}
+
+//-----------------------------------------------------------------------------
+// 30.10.8.4.6, native format observers
+#ifdef GHC_OS_WINDOWS
+GHC_INLINE path::impl_string_type path::native_impl() const
+{
+    impl_string_type result;
+    if (is_absolute() && _path.length() > MAX_PATH - 10) {
+        // expand long Windows filenames with marker
+        if (has_root_name() && _path[0] == '/') {
+            result = "\\\\?\\UNC" + _path.substr(1);
+        }
+        else {
+            result = "\\\\?\\" + _path;
+        }
+    }
+    else {
+        result = _path;
+    }
+    /*if (has_root_name() && root_name()._path[0] == '/') {
+        return _path;
+    }*/
+    for (auto& c : result) {
+        if (c == '/') {
+            c = '\\';
+        }
+    }
+    return result;
+}
+#else
+GHC_INLINE const path::impl_string_type& path::native_impl() const
+{
+    return _path;
+}
+#endif
+
+GHC_INLINE const path::string_type& path::native() const
+{
+#ifdef GHC_OS_WINDOWS
+#ifdef GHC_USE_WCHAR_T
+    _native_cache = detail::fromUtf8<string_type>(native_impl());
+#else
+    _native_cache = native_impl();
+#endif
+    return _native_cache;
+#else
+    return _path;
+#endif
+}
+
+GHC_INLINE const path::value_type* path::c_str() const
+{
+    return native().c_str();
+}
+
+GHC_INLINE path::operator path::string_type() const
+{
+    return native();
+}
+
+#endif  // GHC_EXPAND_IMPL
+
+template <class EcharT, class traits, class Allocator>
+inline std::basic_string<EcharT, traits, Allocator> path::string(const Allocator& a) const
+{
+    return detail::fromUtf8<std::basic_string<EcharT, traits, Allocator>>(native_impl(), a);
+}
+
+#ifdef GHC_EXPAND_IMPL
+
+GHC_INLINE std::string path::string() const
+{
+    return native_impl();
+}
+
+GHC_INLINE std::wstring path::wstring() const
+{
+#ifdef GHC_USE_WCHAR_T
+    return native();
+#else
+    return detail::fromUtf8<std::wstring>(native());
+#endif
+}
+
+GHC_INLINE std::string path::u8string() const
+{
+    return native_impl();
+}
+
+GHC_INLINE std::u16string path::u16string() const
+{
+    return detail::fromUtf8<std::u16string>(native_impl());
+}
+
+GHC_INLINE std::u32string path::u32string() const
+{
+    return detail::fromUtf8<std::u32string>(native_impl());
+}
+
+#endif  // GHC_EXPAND_IMPL
+
+//-----------------------------------------------------------------------------
+// 30.10.8.4.7, generic format observers
+template <class EcharT, class traits, class Allocator>
+inline std::basic_string<EcharT, traits, Allocator> path::generic_string(const Allocator& a) const
+{
+    return detail::fromUtf8<std::basic_string<EcharT, traits, Allocator>>(_path, a);
+}
+
+#ifdef GHC_EXPAND_IMPL
+
+GHC_INLINE const std::string& path::generic_string() const
+{
+    return _path;
+}
+
+GHC_INLINE std::wstring path::generic_wstring() const
+{
+    return detail::fromUtf8<std::wstring>(_path);
+}
+
+GHC_INLINE std::string path::generic_u8string() const
+{
+    return _path;
+}
+
+GHC_INLINE std::u16string path::generic_u16string() const
+{
+    return detail::fromUtf8<std::u16string>(_path);
+}
+
+GHC_INLINE std::u32string path::generic_u32string() const
+{
+    return detail::fromUtf8<std::u32string>(_path);
+}
+
+//-----------------------------------------------------------------------------
+// 30.10.8.4.8, compare
+GHC_INLINE int path::compare(const path& p) const noexcept
+{
+    return native().compare(p.native());
+}
+
+GHC_INLINE int path::compare(const string_type& s) const
+{
+    return native().compare(path(s).native());
+}
+
+#ifdef __cpp_lib_string_view
+GHC_INLINE int path::compare(std::basic_string_view<value_type> s) const
+{
+    return native().compare(path(s).native());
+}
+#endif
+
+GHC_INLINE int path::compare(const value_type* s) const
+{
+    return native().compare(path(s).native());
+}
+
+//-----------------------------------------------------------------------------
+// 30.10.8.4.9, decomposition
+GHC_INLINE path path::root_name() const
+{
+#ifdef GHC_OS_WINDOWS
+    if (_path.length() >= 2 && std::toupper(static_cast<unsigned char>(_path[0])) >= 'A' && std::toupper(static_cast<unsigned char>(_path[0])) <= 'Z' && _path[1] == ':') {
+        return path(_path.substr(0, 2));
+    }
+#endif
+    if (_path.length() > 2 && _path[0] == '/' && _path[1] == '/' && _path[2] != '/' && std::isprint(_path[2])) {
+        impl_string_type::size_type pos = _path.find_first_of("/\\", 3);
+        if (pos == impl_string_type::npos) {
+            return path(_path);
+        }
+        else {
+            return path(_path.substr(0, pos));
+        }
+    }
+    return path();
+}
+
+GHC_INLINE path path::root_directory() const
+{
+    path root = root_name();
+    if (_path.length() > root._path.length() && _path[root._path.length()] == '/') {
+        return path("/");
+    }
+    return path();
+}
+
+GHC_INLINE path path::root_path() const
+{
+    return root_name().generic_string() + root_directory().generic_string();
+}
+
+GHC_INLINE path path::relative_path() const
+{
+    std::string root = root_path()._path;
+    return path(_path.substr((std::min)(root.length(), _path.length())), generic_format);
+}
+
+GHC_INLINE path path::parent_path() const
+{
+    if (has_relative_path()) {
+        if (empty() || begin() == --end()) {
+            return path();
+        }
+        else {
+            path pp;
+            for (string_type s : input_iterator_range<iterator>(begin(), --end())) {
+                if (s == "/") {
+                    // don't use append to join a path-
+                    pp += s;
+                }
+                else {
+                    pp /= s;
+                }
+            }
+            return pp;
+        }
+    }
+    else {
+        return *this;
+    }
+}
+
+GHC_INLINE path path::filename() const
+{
+    return relative_path().empty() ? path() : path(*--end());
+}
+
+GHC_INLINE path path::stem() const
+{
+    impl_string_type fn = filename().string();
+    if (fn != "." && fn != "..") {
+        impl_string_type::size_type n = fn.rfind('.');
+        if (n != impl_string_type::npos && n != 0) {
+            return path{fn.substr(0, n)};
+        }
+    }
+    return path{fn};
+}
+
+GHC_INLINE path path::extension() const
+{
+    impl_string_type fn = filename().string();
+    impl_string_type::size_type pos = fn.find_last_of('.');
+    if (pos == std::string::npos || pos == 0) {
+        return "";
+    }
+    return fn.substr(pos);
+}
+
+//-----------------------------------------------------------------------------
+// 30.10.8.4.10, query
+GHC_INLINE bool path::empty() const noexcept
+{
+    return _path.empty();
+}
+
+GHC_INLINE bool path::has_root_name() const
+{
+    return !root_name().empty();
+}
+
+GHC_INLINE bool path::has_root_directory() const
+{
+    return !root_directory().empty();
+}
+
+GHC_INLINE bool path::has_root_path() const
+{
+    return !root_path().empty();
+}
+
+GHC_INLINE bool path::has_relative_path() const
+{
+    return !relative_path().empty();
+}
+
+GHC_INLINE bool path::has_parent_path() const
+{
+    return !parent_path().empty();
+}
+
+GHC_INLINE bool path::has_filename() const
+{
+    return !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();
+}
+
+//-----------------------------------------------------------------------------
+// 30.10.8.4.11, 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() == generic_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<const_iterator>(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<const_iterator>(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;
+}
+
+//-----------------------------------------------------------------------------
+// 30.10.8.5, iterators
+GHC_INLINE path::iterator::iterator() {}
+
+GHC_INLINE path::iterator::iterator(const path::impl_string_type::const_iterator& first, const path::impl_string_type::const_iterator& last, const path::impl_string_type::const_iterator& pos)
+    : _first(first)
+    , _last(last)
+    , _iter(pos)
+{
+    updateCurrent();
+    // find the position of a potential root directory slash
+#ifdef GHC_OS_WINDOWS
+    if (_last - _first >= 3 && std::toupper(static_cast<unsigned char>(*first)) >= 'A' && std::toupper(static_cast<unsigned char>(*first)) <= 'Z' && *(first + 1) == ':' && *(first + 2) == '/') {
+        _root = _first + 2;
+    }
+    else
+#endif
+    {
+        if (_first != _last && *_first == '/') {
+            if (_last - _first >= 2 && *(_first + 1) == '/' && !(_last - _first >= 3 && *(_first + 2) == '/')) {
+                _root = increment(_first);
+            }
+            else {
+                _root = _first;
+            }
+        }
+        else {
+            _root = _last;
+        }
+    }
+}
+
+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;
+    if (i != _last) {
+        // we can only sit on a slash if it is a network name or a root
+        if (*i++ == '/') {
+            if (i != _last && *i == '/') {
+                if (fromStart && !(i + 1 != _last && *(i + 1) == '/')) {
+                    // leadind double slashes detected, treat this and the
+                    // following until a slash as one unit
+                    i = std::find(++i, _last, '/');
+                }
+                else {
+                    // skip redundant slashes
+                    while (i != _last && *i == '/') {
+                        ++i;
+                    }
+                }
+            }
+        }
+        else {
+            if (fromStart && i != _last && *i == ':') {
+                ++i;
+            }
+            else {
+                i = std::find(i, _last, '/');
+            }
+        }
+    }
+    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 != '/')) {
+#ifdef GHC_OS_WINDOWS
+            static const std::string seps = "/:";
+            i = std::find_first_of(std::reverse_iterator<path::impl_string_type::const_iterator>(i), std::reverse_iterator<path::impl_string_type::const_iterator>(_first), seps.begin(), seps.end()).base();
+            if (i > _first && *i == ':') {
+                i++;
+            }
+#else
+            i = std::find(std::reverse_iterator<path::impl_string_type::const_iterator>(i), std::reverse_iterator<path::impl_string_type::const_iterator>(_first), '/').base();
+#endif
+            // Now we have to check if this is a network name
+            if (i - _first == 2 && *_first == '/' && *(_first + 1) == '/') {
+                i -= 2;
+            }
+        }
+    }
+    return i;
+}
+
+GHC_INLINE void path::iterator::updateCurrent()
+{
+    if (_iter != _first && _iter != _last && (*_iter == '/' && _iter != _root) && (_iter + 1 == _last)) {
+        _current = "";
+    }
+    else {
+        _current.assign(_iter, increment(_iter));
+        if (_current.generic_string().size() > 1 && _current.generic_string()[0] == '/' && _current.generic_string()[_current.generic_string().size() - 1] == '/') {
+            // shrink successive slashes to one
+            _current = "/";
+        }
+    }
+}
+
+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 == '/' &&      // we are on a slash
+           (_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(_path.begin(), _path.end(), _path.begin());
+}
+
+GHC_INLINE path::iterator path::end() const
+{
+    return iterator(_path.begin(), _path.end(), _path.end());
+}
+
+//-----------------------------------------------------------------------------
+// 30.10.8.6, 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<std::string>()(p.generic_string());
+}
+
+GHC_INLINE bool operator==(const path& lhs, const path& rhs) noexcept
+{
+    return lhs.generic_string() == rhs.generic_string();
+}
+
+GHC_INLINE bool operator!=(const path& lhs, const path& rhs) noexcept
+{
+    return lhs.generic_string() != rhs.generic_string();
+}
+
+GHC_INLINE bool operator<(const path& lhs, const path& rhs) noexcept
+{
+    return lhs.generic_string() < rhs.generic_string();
+}
+
+GHC_INLINE bool operator<=(const path& lhs, const path& rhs) noexcept
+{
+    return lhs.generic_string() <= rhs.generic_string();
+}
+
+GHC_INLINE bool operator>(const path& lhs, const path& rhs) noexcept
+{
+    return lhs.generic_string() > rhs.generic_string();
+}
+
+GHC_INLINE bool operator>=(const path& lhs, const path& rhs) noexcept
+{
+    return lhs.generic_string() >= rhs.generic_string();
+}
+
+GHC_INLINE path operator/(const path& lhs, const path& rhs)
+{
+    path result(lhs);
+    result /= rhs;
+    return result;
+}
+
+#endif  // GHC_EXPAND_IMPL
+
+//-----------------------------------------------------------------------------
+// 30.10.8.6.1 path inserter and extractor
+template <class charT, class traits>
+inline std::basic_ostream<charT, traits>& operator<<(std::basic_ostream<charT, traits>& os, const path& p)
+{
+    os << "\"";
+    auto ps = p.string<charT, traits>();
+    for (auto c : ps) {
+        if (c == '"' || c == '\\') {
+            os << '\\';
+        }
+        os << c;
+    }
+    os << "\"";
+    return os;
+}
+
+template <class charT, class traits>
+inline std::basic_istream<charT, traits>& operator>>(std::basic_istream<charT, traits>& is, path& p)
+{
+    std::basic_string<charT, traits> 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<charT>(c2);
+                    }
+                }
+                else if (c2 == '"') {
+                    break;
+                }
+                else {
+                    tmp += static_cast<charT>(c2);
+                }
+            }
+        }
+        if ((sf & std::ios_base::skipws) == std::ios_base::skipws) {
+            is >> std::skipws;
+        }
+        p = path(tmp);
+    }
+    else {
+        is >> tmp;
+        p = path(static_cast<charT>(c) + tmp);
+    }
+    return is;
+}
+
+#ifdef GHC_EXPAND_IMPL
+
+//-----------------------------------------------------------------------------
+// 30.10.9 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.u8string() + "'";
+    }
+}
+
+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.u8string() + "'";
+    }
+    if (!_p2.empty()) {
+        _what_arg += ", '" + _p2.u8string() + "'";
+    }
+}
+
+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();
+}
+
+//-----------------------------------------------------------------------------
+// 30.10.15, filesystem operations
+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;
+}
+
+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(p.wstring().c_str(), 0, 0, 0);
+    if (size) {
+        std::vector<wchar_t> buf(size, 0);
+        ULONG s2 = GetFullPathNameW(p.wstring().c_str(), 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
+}
+
+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;
+}
+
+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 root = work.root_path();
+    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 {
+        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() <= root.string().length()) {
+                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;
+}
+
+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, 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;
+    copy(from, to, options, ec);
+    if (ec) {
+        throw filesystem_error(detail::systemErrorText(ec.value()), from, to, 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);
+            }
+            else if ((options & copy_options::create_hard_links) != copy_options::none) {
+                create_hard_link(from, to, ec);
+            }
+            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<copy_options>(0x8000), ec);
+            }
+            if (ec) {
+                return;
+            }
+        }
+    }
+    return;
+}
+
+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, 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 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;
+}
+
+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(detail::fromUtf8<std::wstring>(from.u8string()).c_str(), detail::fromUtf8<std::wstring>(to.u8string()).c_str(), !overwrite)) {
+        ec = detail::make_system_error();
+        return false;
+    }
+    return true;
+#else
+    std::vector<char> buffer(16384, '\0');
+    int in = -1, out = -1;
+    if ((in = ::open(from.c_str(), O_RDONLY)) < 0) {
+        ec = detail::make_system_error();
+        return false;
+    }
+    std::shared_ptr<void> guard_in(nullptr, [in](void*) { ::close(in); });
+    int mode = O_CREAT | O_WRONLY | O_TRUNC;
+    if (!overwrite) {
+        mode |= O_EXCL;
+    }
+    if ((out = ::open(to.c_str(), mode, static_cast<int>(sf.permissions() & perms::all))) < 0) {
+        ec = detail::make_system_error();
+        return false;
+    }
+    std::shared_ptr<void> guard_out(nullptr, [out](void*) { ::close(out); });
+    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<size_t>(br))) > 0) {
+                br -= bw;
+                offset += bw;
+            }
+            else if (bw < 0) {
+                ec = detail::make_system_error();
+                return false;
+            }
+        } while (br);
+    }
+    return true;
+#endif
+}
+
+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);
+    }
+}
+
+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);
+        }
+    }
+}
+
+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;
+}
+
+GHC_INLINE bool create_directories(const path& p, std::error_code& ec) noexcept
+{
+    path current;
+    ec.clear();
+    bool didCreate = false;
+    for (path::string_type part : p) {
+        current /= part;
+        if (current != p.root_name() && current != p.root_path()) {
+            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;
+}
+
+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;
+}
+
+GHC_INLINE bool create_directory(const path& p, std::error_code& ec) noexcept
+{
+    return create_directory(p, path(), ec);
+}
+
+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;
+}
+
+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(detail::fromUtf8<std::wstring>(attributes.u8string()).c_str(), detail::fromUtf8<std::wstring>(p.u8string()).c_str(), NULL)) {
+            ec = detail::make_system_error();
+            return false;
+        }
+    }
+    else if (!::CreateDirectoryW(detail::fromUtf8<std::wstring>(p.u8string()).c_str(), NULL)) {
+        ec = detail::make_system_error();
+        return false;
+    }
+#else
+    ::mode_t attribs = static_cast<mode_t>(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;
+}
+
+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);
+    }
+}
+
+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);
+}
+
+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);
+    }
+}
+
+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);
+}
+
+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);
+    }
+}
+
+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);
+}
+
+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;
+}
+
+GHC_INLINE path current_path(std::error_code& ec)
+{
+    ec.clear();
+#ifdef GHC_OS_WINDOWS
+    DWORD pathlen = ::GetCurrentDirectoryW(0, 0);
+    std::unique_ptr<wchar_t[]> 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<size_t>(std::max(int(::pathconf(".", _PC_PATH_MAX)), int(PATH_MAX)));
+    std::unique_ptr<char[]> buffer(new char[pathlen + 1]);
+    if (::getcwd(buffer.get(), pathlen) == nullptr) {
+        ec = detail::make_system_error();
+        return path();
+    }
+    return path(buffer.get());
+#endif
+}
+
+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);
+    }
+}
+
+GHC_INLINE void current_path(const path& p, std::error_code& ec) noexcept
+{
+    ec.clear();
+#ifdef GHC_OS_WINDOWS
+    if (!::SetCurrentDirectoryW(detail::fromUtf8<std::wstring>(p.u8string()).c_str())) {
+        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;
+}
+
+GHC_INLINE bool exists(const path& p)
+{
+    return exists(status(p));
+}
+
+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);
+}
+
+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;
+}
+
+GHC_INLINE bool equivalent(const path& p1, const path& p2, std::error_code& ec) noexcept
+{
+    ec.clear();
+#ifdef GHC_OS_WINDOWS
+    std::shared_ptr<void> file1(::CreateFileW(p1.wstring().c_str(), 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0), CloseHandle);
+    auto e1 = ::GetLastError();
+    std::shared_ptr<void> file2(::CreateFileW(p2.wstring().c_str(), 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0), CloseHandle);
+    if (file1.get() == INVALID_HANDLE_VALUE || file2.get() == INVALID_HANDLE_VALUE) {
+#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
+}
+
+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;
+}
+
+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(detail::fromUtf8<std::wstring>(p.u8string()).c_str(), GetFileExInfoStandard, &attr)) {
+        ec = detail::make_system_error();
+        return static_cast<uintmax_t>(-1);
+    }
+    return static_cast<uintmax_t>(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<uintmax_t>(-1);
+    }
+    return static_cast<uintmax_t>(fileStat.st_size);
+#endif
+}
+
+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;
+}
+
+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<uintmax_t>(-1);
+    std::shared_ptr<void> file(::CreateFileW(p.wstring().c_str(), 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0), CloseHandle);
+    BY_HANDLE_FILE_INFORMATION inf;
+    if (file.get() == INVALID_HANDLE_VALUE) {
+        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<uintmax_t>(-1) : result;
+#endif
+}
+
+GHC_INLINE bool is_block_file(file_status s) noexcept
+{
+    return s.type() == file_type::block;
+}
+
+GHC_INLINE bool is_block_file(const path& p)
+{
+    return is_block_file(status(p));
+}
+
+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;
+}
+
+GHC_INLINE bool is_character_file(const path& p)
+{
+    return is_character_file(status(p));
+}
+
+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;
+}
+
+GHC_INLINE bool is_directory(const path& p)
+{
+    return is_directory(status(p));
+}
+
+GHC_INLINE bool is_directory(const path& p, std::error_code& ec) noexcept
+{
+    return is_directory(status(p, ec));
+}
+
+GHC_INLINE bool is_empty(const path& p)
+{
+    if (is_directory(p)) {
+        return directory_iterator(p) == directory_iterator();
+    }
+    else {
+        return file_size(p) == 0;
+    }
+}
+
+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;
+}
+
+GHC_INLINE bool is_fifo(const path& p)
+{
+    return is_fifo(status(p));
+}
+
+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);
+}
+
+GHC_INLINE bool is_other(const path& p)
+{
+    return is_other(status(p));
+}
+
+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;
+}
+
+GHC_INLINE bool is_regular_file(const path& p)
+{
+    return is_regular_file(status(p));
+}
+
+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;
+}
+
+GHC_INLINE bool is_socket(const path& p)
+{
+    return is_socket(status(p));
+}
+
+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;
+}
+
+GHC_INLINE bool is_symlink(const path& p)
+{
+    return is_symlink(symlink_status(p));
+}
+
+GHC_INLINE bool is_symlink(const path& p, std::error_code& ec) noexcept
+{
+    return is_symlink(symlink_status(p, ec));
+}
+
+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;
+}
+
+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);
+}
+
+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);
+    }
+}
+
+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
+    std::shared_ptr<void> file(::CreateFileW(p.wstring().c_str(), FILE_WRITE_ATTRIBUTES, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL), ::CloseHandle);
+    FILETIME ft;
+    auto tt = std::chrono::duration_cast<std::chrono::microseconds>(d).count() * 10 + 116444736000000000;
+    ft.dwLowDateTime = static_cast<DWORD>(tt);
+    ft.dwHighDateTime = static_cast<DWORD>(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<int>(fs.st_atimespec.tv_nsec / 1000);
+        tv[1].tv_sec = std::chrono::duration_cast<std::chrono::seconds>(d).count();
+        tv[1].tv_usec = static_cast<int>(std::chrono::duration_cast<std::chrono::microseconds>(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<std::chrono::seconds>(d).count();
+    times[1].tv_nsec = std::chrono::duration_cast<std::chrono::nanoseconds>(d).count() % 1000000000;
+    if (::utimensat(AT_FDCWD, p.c_str(), times, AT_SYMLINK_NOFOLLOW) != 0) {
+        ec = detail::make_system_error();
+    }
+    return;
+#endif
+#endif
+#else
+    struct ::timespec times[2];
+    times[0].tv_sec = 0;
+    times[0].tv_nsec = UTIME_OMIT;
+    times[1].tv_sec = static_cast<decltype(times[1].tv_sec)>(std::chrono::duration_cast<std::chrono::seconds>(d).count());
+    times[1].tv_nsec = static_cast<decltype(times[1].tv_nsec)>(std::chrono::duration_cast<std::chrono::nanoseconds>(d).count() % 1000000000);
+    if (::utimensat(AT_FDCWD, p.c_str(), times, AT_SYMLINK_NOFOLLOW) != 0) {
+        ec = detail::make_system_error();
+    }
+    return;
+#endif
+}
+
+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);
+    }
+}
+
+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)
+{
+    if (static_cast<int>(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(p.wstring().c_str());
+    if (oldAttr != INVALID_FILE_ATTRIBUTES) {
+        DWORD newAttr = ((prms & perms::owner_write) == perms::owner_write) ? oldAttr & ~(static_cast<DWORD>(FILE_ATTRIBUTE_READONLY)) : oldAttr | FILE_ATTRIBUTE_READONLY;
+        if (oldAttr == newAttr || SetFileAttributesW(p.wstring().c_str(), 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<mode_t>(prms)) != 0) {
+            ec = detail::make_system_error();
+        }
+    }
+#endif
+}
+
+GHC_INLINE path proximate(const path& p, std::error_code& ec)
+{
+    return proximate(p, current_path(), ec);
+}
+
+GHC_INLINE path proximate(const path& p, const path& base)
+{
+    return weakly_canonical(p).lexically_proximate(weakly_canonical(base));
+}
+
+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));
+}
+
+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;
+}
+
+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);
+}
+
+GHC_INLINE path relative(const path& p, const path& base)
+{
+    return weakly_canonical(p).lexically_relative(weakly_canonical(base));
+}
+
+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));
+}
+
+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;
+}
+
+GHC_INLINE bool remove(const path& p, std::error_code& ec) noexcept
+{
+    ec.clear();
+#ifdef GHC_OS_WINDOWS
+    std::wstring np = detail::fromUtf8<std::wstring>(p.u8string());
+    DWORD attr = GetFileAttributesW(np.c_str());
+    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);
+    }
+    if (!ec) {
+        if (attr & FILE_ATTRIBUTE_DIRECTORY) {
+            if (!RemoveDirectoryW(np.c_str())) {
+                ec = detail::make_system_error();
+            }
+        }
+        else {
+            if (!DeleteFileW(np.c_str())) {
+                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;
+}
+
+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;
+}
+
+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<uintmax_t>(-1);
+    }
+    std::error_code tec;
+    auto fs = status(p, tec);
+    if (exists(fs) && is_directory(fs)) {
+        for (auto iter = directory_iterator(p, ec); iter != directory_iterator(); iter.increment(ec)) {
+            if (ec) {
+                break;
+            }
+            if (!iter->is_symlink() && iter->is_directory()) {
+                count += remove_all(iter->path(), ec);
+                if (ec) {
+                    return static_cast<uintmax_t>(-1);
+                }
+            }
+            else {
+                remove(iter->path(), ec);
+                if (ec) {
+                    return static_cast<uintmax_t>(-1);
+                }
+                ++count;
+            }
+        }
+    }
+    if (!ec) {
+        if (remove(p, ec)) {
+            ++count;
+        }
+    }
+    if (ec) {
+        return static_cast<uintmax_t>(-1);
+    }
+    return count;
+}
+
+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);
+    }
+}
+
+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(detail::fromUtf8<std::wstring>(from.u8string()).c_str(), detail::fromUtf8<std::wstring>(to.u8string()).c_str(), (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
+}
+
+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);
+    }
+}
+
+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<LONGLONG>(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;
+    }
+    std::shared_ptr<void> file(CreateFileW(detail::fromUtf8<std::wstring>(p.u8string()).c_str(), GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL), CloseHandle);
+    if (file.get() == INVALID_HANDLE_VALUE) {
+        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<off_t>(size)) != 0) {
+        ec = detail::make_system_error();
+    }
+#endif
+}
+
+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;
+}
+
+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(detail::fromUtf8<std::wstring>(p.u8string()).c_str(), &freeBytesAvailableToCaller, &totalNumberOfBytes, &totalNumberOfFreeBytes)) {
+        ec = detail::make_system_error();
+        return {static_cast<uintmax_t>(-1), static_cast<uintmax_t>(-1), static_cast<uintmax_t>(-1)};
+    }
+    return {static_cast<uintmax_t>(totalNumberOfBytes.QuadPart), static_cast<uintmax_t>(totalNumberOfFreeBytes.QuadPart), static_cast<uintmax_t>(freeBytesAvailableToCaller.QuadPart)};
+#elif !defined(__ANDROID__) || __ANDROID_API__ >= 19
+    struct ::statvfs sfs;
+    if (::statvfs(p.c_str(), &sfs) != 0) {
+        ec = detail::make_system_error();
+        return {static_cast<uintmax_t>(-1), static_cast<uintmax_t>(-1), static_cast<uintmax_t>(-1)};
+    }
+    return {static_cast<uintmax_t>(sfs.f_blocks * sfs.f_frsize), static_cast<uintmax_t>(sfs.f_bfree * sfs.f_frsize), static_cast<uintmax_t>(sfs.f_bavail * sfs.f_frsize)};
+#else
+    (void)p;
+    ec = detail::make_error_code(detail::portable_error::not_supported);
+    return {static_cast<uintmax_t>(-1), static_cast<uintmax_t>(-1), static_cast<uintmax_t>(-1)};
+#endif
+}
+
+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;
+}
+
+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;
+}
+
+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;
+}
+
+GHC_INLINE file_status symlink_status(const path& p, std::error_code& ec) noexcept
+{
+    return detail::symlink_status_ex(p, ec);
+}
+
+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;
+}
+
+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
+}
+
+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;
+}
+
+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();
+}
+
+//-----------------------------------------------------------------------------
+// 30.10.11 class file_status
+// 30.10.11.1 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;
+}
+
+// 30.10.11.3 modifiers
+GHC_INLINE void file_status::type(file_type ft) noexcept
+{
+    _type = ft;
+}
+
+GHC_INLINE void file_status::permissions(perms prms) noexcept
+{
+    _perms = prms;
+}
+
+// 30.10.11.2 observers
+GHC_INLINE file_type file_status::type() const noexcept
+{
+    return _type;
+}
+
+GHC_INLINE perms file_status::permissions() const noexcept
+{
+    return _perms;
+}
+
+//-----------------------------------------------------------------------------
+// 30.10.12 class directory_entry
+// 30.10.12.1 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;
+GHC_INLINE directory_entry::directory_entry(const filesystem::path& p)
+    : _path(p)
+    , _file_size(0)
+#ifndef GHC_OS_WINDOWS
+    , _hard_link_count(0)
+#endif
+    , _last_write_time(0)
+{
+    refresh();
+}
+
+GHC_INLINE directory_entry::directory_entry(const filesystem::path& p, std::error_code& ec)
+    : _path(p)
+    , _file_size(0)
+#ifndef GHC_OS_WINDOWS
+    , _hard_link_count(0)
+#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;
+
+// 30.10.12.2 directory_entry modifiers
+GHC_INLINE void directory_entry::assign(const filesystem::path& p)
+{
+    _path = p;
+    refresh();
+}
+
+GHC_INLINE void directory_entry::assign(const filesystem::path& p, std::error_code& ec)
+{
+    _path = p;
+    refresh(ec);
+}
+
+GHC_INLINE void directory_entry::replace_filename(const filesystem::path& p)
+{
+    _path.replace_filename(p);
+    refresh();
+}
+
+GHC_INLINE void directory_entry::replace_filename(const filesystem::path& p, std::error_code& ec)
+{
+    _path.replace_filename(p);
+    refresh(ec);
+}
+
+GHC_INLINE void directory_entry::refresh()
+{
+    std::error_code ec;
+    refresh(ec);
+    if (ec) {
+        throw filesystem_error(detail::systemErrorText(ec.value()), _path, ec);
+    }
+}
+
+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
+}
+
+// 30.10.12.3 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;
+}
+
+GHC_INLINE bool directory_entry::exists() const
+{
+    return filesystem::exists(status());
+}
+
+GHC_INLINE bool directory_entry::exists(std::error_code& ec) const noexcept
+{
+    return filesystem::exists(status(ec));
+}
+
+GHC_INLINE bool directory_entry::is_block_file() const
+{
+    return filesystem::is_block_file(status());
+}
+GHC_INLINE bool directory_entry::is_block_file(std::error_code& ec) const noexcept
+{
+    return filesystem::is_block_file(status(ec));
+}
+
+GHC_INLINE bool directory_entry::is_character_file() const
+{
+    return filesystem::is_character_file(status());
+}
+
+GHC_INLINE bool directory_entry::is_character_file(std::error_code& ec) const noexcept
+{
+    return filesystem::is_character_file(status(ec));
+}
+
+GHC_INLINE bool directory_entry::is_directory() const
+{
+    return filesystem::is_directory(status());
+}
+
+GHC_INLINE bool directory_entry::is_directory(std::error_code& ec) const noexcept
+{
+    return filesystem::is_directory(status(ec));
+}
+
+GHC_INLINE bool directory_entry::is_fifo() const
+{
+    return filesystem::is_fifo(status());
+}
+
+GHC_INLINE bool directory_entry::is_fifo(std::error_code& ec) const noexcept
+{
+    return filesystem::is_fifo(status(ec));
+}
+
+GHC_INLINE bool directory_entry::is_other() const
+{
+    return filesystem::is_other(status());
+}
+
+GHC_INLINE bool directory_entry::is_other(std::error_code& ec) const noexcept
+{
+    return filesystem::is_other(status(ec));
+}
+
+GHC_INLINE bool directory_entry::is_regular_file() const
+{
+    return filesystem::is_regular_file(status());
+}
+
+GHC_INLINE bool directory_entry::is_regular_file(std::error_code& ec) const noexcept
+{
+    return filesystem::is_regular_file(status(ec));
+}
+
+GHC_INLINE bool directory_entry::is_socket() const
+{
+    return filesystem::is_socket(status());
+}
+
+GHC_INLINE bool directory_entry::is_socket(std::error_code& ec) const noexcept
+{
+    return filesystem::is_socket(status(ec));
+}
+
+GHC_INLINE bool directory_entry::is_symlink() const
+{
+    return filesystem::is_symlink(symlink_status());
+}
+
+GHC_INLINE bool directory_entry::is_symlink(std::error_code& ec) const noexcept
+{
+    return filesystem::is_symlink(symlink_status(ec));
+}
+
+GHC_INLINE uintmax_t directory_entry::file_size() const
+{
+    if (_status.type() != file_type::none) {
+        return _file_size;
+    }
+    return filesystem::file_size(path());
+}
+
+GHC_INLINE uintmax_t directory_entry::file_size(std::error_code& ec) const noexcept
+{
+    if (_status.type() != file_type::none) {
+        ec.clear();
+        return _file_size;
+    }
+    return filesystem::file_size(path(), ec);
+}
+
+GHC_INLINE uintmax_t directory_entry::hard_link_count() const
+{
+#ifndef GHC_OS_WINDOWS
+    if (_status.type() != file_type::none) {
+        return _hard_link_count;
+    }
+#endif
+    return filesystem::hard_link_count(path());
+}
+
+GHC_INLINE uintmax_t directory_entry::hard_link_count(std::error_code& ec) const noexcept
+{
+#ifndef GHC_OS_WINDOWS
+    if (_status.type() != file_type::none) {
+        ec.clear();
+        return _hard_link_count;
+    }
+#endif
+    return filesystem::hard_link_count(path(), ec);
+}
+
+GHC_INLINE file_time_type directory_entry::last_write_time() const
+{
+    if (_status.type() != file_type::none) {
+        return std::chrono::system_clock::from_time_t(_last_write_time);
+    }
+    return filesystem::last_write_time(path());
+}
+
+GHC_INLINE file_time_type directory_entry::last_write_time(std::error_code& ec) const noexcept
+{
+    if (_status.type() != file_type::none) {
+        ec.clear();
+        return std::chrono::system_clock::from_time_t(_last_write_time);
+    }
+    return filesystem::last_write_time(path(), ec);
+}
+
+GHC_INLINE file_status directory_entry::status() const
+{
+    if (_status.type() != file_type::none) {
+        return _status;
+    }
+    return filesystem::status(path());
+}
+
+GHC_INLINE file_status directory_entry::status(std::error_code& ec) const noexcept
+{
+    if (_status.type() != file_type::none) {
+        ec.clear();
+        return _status;
+    }
+    return filesystem::status(path(), ec);
+}
+
+GHC_INLINE file_status directory_entry::symlink_status() const
+{
+    if (_symlink_status.type() != file_type::none) {
+        return _symlink_status;
+    }
+    return filesystem::symlink_status(path());
+}
+
+GHC_INLINE file_status directory_entry::symlink_status(std::error_code& ec) const noexcept
+{
+    if (_symlink_status.type() != file_type::none) {
+        ec.clear();
+        return _symlink_status;
+    }
+    return filesystem::symlink_status(path(), ec);
+}
+
+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;
+}
+
+//-----------------------------------------------------------------------------
+// 30.10.13 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(detail::fromUtf8<std::wstring>((_base / "*").u8string()).c_str(), &_findData)) != INVALID_HANDLE_VALUE) {
+                if (std::wstring(_findData.cFileName) == L"." || std::wstring(_findData.cFileName) == L"..") {
+                    increment(_ec);
+                }
+                else {
+                    _current = _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)) {
+                    _current = _base;
+                    try {
+                        _current.append_name(detail::toUtf8(_findData.cFileName).c_str());
+                    }
+                    catch(filesystem_error& fe) {
+                        ec = fe.code();
+                        return;
+                    }
+                    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;
+                    _current = filesystem::path();
+                    break;
+                }
+            } while (std::wstring(_findData.cFileName) == L"." || std::wstring(_findData.cFileName) == L"..");
+        }
+        else {
+            ec = _ec;
+        }
+    }
+    void copyToDirEntry(std::error_code& ec)
+    {
+        _dir_entry._path = _current;
+        if (_findData.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) {
+            _dir_entry._status = detail::status_ex(_current, ec, &_dir_entry._symlink_status, &_dir_entry._file_size, nullptr, &_dir_entry._last_write_time);
+        }
+        else {
+            _dir_entry._status = detail::status_from_INFO(_current, &_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<uintmax_t>(-1);
+                _dir_entry._last_write_time = 0;
+            }
+        }
+    }
+    path _base;
+    directory_options _options;
+    WIN32_FIND_DATAW _findData;
+    HANDLE _dirHandle;
+    path _current;
+    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 (!path.empty()) {
+            if (!_dir) {
+                auto error = errno;
+                _base = filesystem::path();
+                if (error != EACCES || (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) {
+            do {
+                errno = 0;
+                _entry = readdir(_dir);
+                if (_entry) {
+                    _current = _base;
+                    _current.append_name(_entry->d_name);
+                    _dir_entry = directory_entry(_current, ec);
+                }
+                else {
+                    ::closedir(_dir);
+                    _dir = nullptr;
+                    _current = path();
+                    if (errno) {
+                        ec = detail::make_system_error();
+                    }
+                    break;
+                }
+            } while (std::strcmp(_entry->d_name, ".") == 0 || std::strcmp(_entry->d_name, "..") == 0);
+        }
+    }
+    path _base;
+    directory_options _options;
+    path _current;
+    DIR* _dir;
+    struct ::dirent* _entry;
+    directory_entry _dir_entry;
+    std::error_code _ec;
+};
+#endif
+
+// 30.10.13.1 member functions
+GHC_INLINE directory_iterator::directory_iterator() noexcept
+    : _impl(new impl(path(), directory_options::none))
+{
+}
+
+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);
+    }
+}
+
+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;
+}
+
+GHC_INLINE directory_iterator& directory_iterator::operator++()
+{
+    std::error_code ec;
+    _impl->increment(ec);
+    if (ec) {
+        throw filesystem_error(detail::systemErrorText(ec.value()), _impl->_current, ec);
+    }
+    return *this;
+}
+
+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->_current == rhs._impl->_current;
+}
+
+GHC_INLINE bool directory_iterator::operator!=(const directory_iterator& rhs) const
+{
+    return _impl->_current != rhs._impl->_current;
+}
+
+// 30.10.13.2 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();
+}
+
+//-----------------------------------------------------------------------------
+// 30.10.14 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());
+}
+
+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));
+}
+
+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() {}
+
+// 30.10.14.1 observers
+GHC_INLINE directory_options recursive_directory_iterator::options() const
+{
+    return _impl->_options;
+}
+
+GHC_INLINE int recursive_directory_iterator::depth() const
+{
+    return static_cast<int>(_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()));
+}
+
+// 30.10.14.1 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;
+}
+
+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;
+}
+
+GHC_INLINE recursive_directory_iterator& recursive_directory_iterator::increment(std::error_code& ec) noexcept
+{
+    if (recursion_pending() && is_directory((*this)->status()) && (!is_symlink((*this)->symlink_status()) || (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;
+}
+
+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);
+    }
+}
+
+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 27.2.3, 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();
+}
+
+// 30.10.14.2 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