From fe570e000cb4f70c42b7b01e85cd925044c77f5d Mon Sep 17 00:00:00 2001 From: Peter Johnson Date: Tue, 1 Jun 2021 21:50:35 -0700 Subject: [PATCH] [wpiutil] Replace llvm filesystem with C++17 filesystem (#3401) Use ghc::filesystem as fill on older GCC (e.g. RoboRIO). This can be removed once all GCC platforms have upgraded to 8.1 or later. File open functionality has been retained from LLVM but moved to "fs" namespace and tweaked for improved consistency with std::filesystem (e.g. error_code is passed by reference instead of returned). Also update WPILibC's Filesystem functions to return std::string. --- ThirdPartyNotices.txt | 24 + cscore/src/main/native/cpp/Instance.cpp | 4 +- .../src/main/native/linux/UsbCameraImpl.cpp | 28 +- glass/src/lib/native/cpp/other/Field2D.cpp | 9 +- .../main/native/athena/cpp/SerialHelper.cpp | 16 +- hal/src/main/native/sim/Extensions.cpp | 6 +- ntcore/src/main/native/cpp/LoggerImpl.cpp | 9 +- ntcore/src/main/native/cpp/Storage_save.cpp | 6 +- .../main/native/cpp/HALSimHttpConnection.cpp | 66 +- .../src/main/native/cpp/HALSimWeb.cpp | 26 +- wpilibc/src/main/native/cpp/Errors.cpp | 11 +- wpilibc/src/main/native/cpp/Filesystem.cpp | 23 +- .../src/main/native/include/frc/Filesystem.h | 14 +- wpiutil/.styleguide | 3 + wpiutil/src/main/native/cpp/fs.cpp | 326 + .../src/main/native/cpp/llvm/ConvertUTF.cpp | 95 + wpiutil/src/main/native/cpp/llvm/Path.cpp | 833 --- .../src/main/native/cpp/llvm/Unix/Path.inc | 539 -- .../src/main/native/cpp/llvm/Windows/Path.inc | 971 --- .../src/main/native/cpp/llvm/raw_ostream.cpp | 50 +- wpiutil/src/main/native/cpp/raw_istream.cpp | 12 +- .../src/main/native/include/wpi/FileSystem.h | 1023 --- wpiutil/src/main/native/include/wpi/fs.h | 235 + .../native/include/wpi/ghc/filesystem.hpp | 5942 +++++++++++++++++ .../src/main/native/include/wpi/raw_ostream.h | 24 +- 25 files changed, 6757 insertions(+), 3538 deletions(-) create mode 100644 wpiutil/src/main/native/cpp/fs.cpp delete mode 100644 wpiutil/src/main/native/cpp/llvm/Path.cpp delete mode 100644 wpiutil/src/main/native/cpp/llvm/Unix/Path.inc delete mode 100644 wpiutil/src/main/native/cpp/llvm/Windows/Path.inc delete mode 100644 wpiutil/src/main/native/include/wpi/FileSystem.h create mode 100644 wpiutil/src/main/native/include/wpi/fs.h create mode 100644 wpiutil/src/main/native/include/wpi/ghc/filesystem.hpp diff --git a/ThirdPartyNotices.txt b/ThirdPartyNotices.txt index 674ce5c3d9..884361d9a9 100644 --- a/ThirdPartyNotices.txt +++ b/ThirdPartyNotices.txt @@ -43,6 +43,7 @@ Eigen wpimath/src/main/native/eigeninclude/ StackWalker wpiutil/src/main/native/windows/StackWalker.* TCB span wpiutil/src/main/native/include/wpi/span.h wpiutil/src/test/native/cpp/span/ +GHC filesystem wpiutil/src/main/native/include/wpi/ghc/ Team 254 Library wpilibj/src/main/java/edu/wpi/first/wpilibj/spline/SplineParameterizer.java wpilibj/src/main/java/edu/wpi/first/wpilibj/trajectory/TrajectoryParameterizer.java wpilibc/src/main/native/include/spline/SplineParameterizer.h @@ -917,3 +918,26 @@ As an exception, if, as a result of your compiling your source code, portions of this Software are embedded into a machine-executable object form of such source code, you may redistribute such embedded portions in such object form without including the above copyright and permission notices. + +============== +GHC filesystem +============== +Copyright (c) 2018, Steffen Schümann + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/cscore/src/main/native/cpp/Instance.cpp b/cscore/src/main/native/cpp/Instance.cpp index 4233204c7c..a42f7d48f2 100644 --- a/cscore/src/main/native/cpp/Instance.cpp +++ b/cscore/src/main/native/cpp/Instance.cpp @@ -4,9 +4,9 @@ #include "Instance.h" -#include #include #include +#include #include using namespace cs; @@ -31,7 +31,7 @@ static void def_log_func(unsigned int level, const char* file, } else { return; } - oss << "CS: " << levelmsg << msg << " (" << wpi::sys::path::filename(file) + oss << "CS: " << levelmsg << msg << " (" << fs::path{file}.filename().string() << ':' << line << ")\n"; wpi::errs() << oss.str(); } diff --git a/cscore/src/main/native/linux/UsbCameraImpl.cpp b/cscore/src/main/native/linux/UsbCameraImpl.cpp index 31e5792d18..8a05bee765 100644 --- a/cscore/src/main/native/linux/UsbCameraImpl.cpp +++ b/cscore/src/main/native/linux/UsbCameraImpl.cpp @@ -22,10 +22,9 @@ #include #include -#include #include -#include #include +#include #include #include @@ -267,26 +266,20 @@ static bool IsVideoCaptureDevice(const char* cpath) { } static int GetDeviceNum(const char* cpath) { - wpi::StringRef path{cpath}; - std::string pathBuf; + fs::path path{cpath}; // it might be a symlink; if so, find the symlink target (e.g. /dev/videoN), // add that to the list and make it the keypath - if (wpi::sys::fs::is_symlink_file(cpath)) { - char* target = ::realpath(cpath, nullptr); - if (target) { - pathBuf = target; - path = pathBuf; - std::free(target); - } + if (fs::is_symlink(path)) { + path = fs::canonical(path); } - path = wpi::sys::path::filename(path); - if (!path.startswith("video")) { + auto fn = path.filename(); + if (!wpi::StringRef{fn}.startswith("video")) { return -1; } int dev = -1; - if (path.substr(5).getAsInteger(10, dev)) { + if (wpi::StringRef{fn}.substr(5).getAsInteger(10, dev)) { return -1; } return dev; @@ -1688,10 +1681,11 @@ std::vector EnumerateUsbCameras(CS_Status* status) { path += ep->d_name; char* target = ::realpath(path.c_str(), nullptr); if (target) { - wpi::StringRef fname = wpi::sys::path::filename(target); + std::string fname = fs::path{target}.filename(); unsigned int dev = 0; - if (fname.startswith("video") && - !fname.substr(5).getAsInteger(10, dev) && dev < retval.size()) { + if (wpi::StringRef{fname}.startswith("video") && + !wpi::StringRef{fname}.substr(5).getAsInteger(10, dev) && + dev < retval.size()) { retval[dev].otherPaths.emplace_back(path.str()); } std::free(target); diff --git a/glass/src/lib/native/cpp/other/Field2D.cpp b/glass/src/lib/native/cpp/other/Field2D.cpp index a45c37c64f..0fda587ee7 100644 --- a/glass/src/lib/native/cpp/other/Field2D.cpp +++ b/glass/src/lib/native/cpp/other/Field2D.cpp @@ -20,9 +20,9 @@ #include #include #include -#include #include #include +#include #include #include #include @@ -468,10 +468,7 @@ void FieldInfo::LoadJson(const wpi::Twine& jsonfile) { } // the image filename is relative to the json file - wpi::SmallString<128> pathname; - jsonfile.toVector(pathname); - wpi::sys::path::remove_filename(pathname); - wpi::sys::path::append(pathname, image); + auto pathname = fs::path{jsonfile.str()}.replace_filename(image).string(); // load field image if (!LoadImageImpl(pathname.c_str())) { @@ -479,7 +476,7 @@ void FieldInfo::LoadJson(const wpi::Twine& jsonfile) { } // save to field info - *m_pFilename = pathname.str(); + *m_pFilename = pathname; *m_pTop = top; *m_pLeft = left; *m_pBottom = bottom; diff --git a/hal/src/main/native/athena/cpp/SerialHelper.cpp b/hal/src/main/native/athena/cpp/SerialHelper.cpp index 3e0a3d81db..361a0ea1a0 100644 --- a/hal/src/main/native/athena/cpp/SerialHelper.cpp +++ b/hal/src/main/native/athena/cpp/SerialHelper.cpp @@ -8,8 +8,8 @@ #include #include -#include #include +#include #include "hal/Errors.h" #include "visa/visa.h" @@ -220,22 +220,20 @@ void SerialHelper::QueryHubPaths(int32_t* status) { // The directories we need are not symbolic, so we can safely // disable symbolic links. std::error_code ec; - for (auto p = wpi::sys::fs::recursive_directory_iterator( - "/sys/devices/soc0", ec, false); - p != wpi::sys::fs::recursive_directory_iterator(); p.increment(ec)) { + for (auto& p : fs::recursive_directory_iterator("/sys/devices/soc0", ec)) { if (ec) break; - wpi::StringRef path{p->path()}; - if (path.find("amba") == wpi::StringRef::npos) + std::string path = p.path().string(); + if (path.find("amba") == std::string::npos) continue; - if (path.find("usb") == wpi::StringRef::npos) + if (path.find("usb") == std::string::npos) continue; - if (path.find(matchString) == wpi::StringRef::npos) + if (path.find(matchString) == std::string::npos) continue; wpi::SmallVector pathSplitVec; // Split path into individual directories - path.split(pathSplitVec, '/', -1, false); + wpi::StringRef{path}.split(pathSplitVec, '/', -1, false); // Find each individual item index int findusb = -1; diff --git a/hal/src/main/native/sim/Extensions.cpp b/hal/src/main/native/sim/Extensions.cpp index ca5bccd4af..ce9dcbfbea 100644 --- a/hal/src/main/native/sim/Extensions.cpp +++ b/hal/src/main/native/sim/Extensions.cpp @@ -6,9 +6,9 @@ #include -#include #include #include +#include #include #include @@ -54,7 +54,7 @@ extern "C" { int HAL_LoadOneExtension(const char* library) { int rc = 1; // It is expected and reasonable not to find an extra simulation wpi::outs() << "HAL Extensions: Attempting to load: " - << wpi::sys::path::stem(library) << "\n"; + << fs::path{library}.stem().string() << "\n"; wpi::outs().flush(); HTYPE handle = DLOPEN(library); #if !defined(WIN32) && !defined(_WIN32) @@ -68,7 +68,7 @@ int HAL_LoadOneExtension(const char* library) { #endif wpi::outs() << "HAL Extensions: Load failed: " << DLERROR << "\nTrying modified name: " - << wpi::sys::path::stem(libraryName) << "\n"; + << fs::path{libraryName.str()}.stem() << "\n"; wpi::outs().flush(); handle = DLOPEN(libraryName.c_str()); } diff --git a/ntcore/src/main/native/cpp/LoggerImpl.cpp b/ntcore/src/main/native/cpp/LoggerImpl.cpp index 067ec05c58..504b3cd1ad 100644 --- a/ntcore/src/main/native/cpp/LoggerImpl.cpp +++ b/ntcore/src/main/native/cpp/LoggerImpl.cpp @@ -4,9 +4,9 @@ #include "LoggerImpl.h" -#include #include #include +#include #include using namespace nt; @@ -70,13 +70,12 @@ unsigned int LoggerImpl::GetMinLevel() { void LoggerImpl::Log(unsigned int level, const char* file, unsigned int line, const char* msg) { - // this is safe because it's null terminated and always the end - const char* filename = wpi::sys::path::filename(file).data(); + auto filename = fs::path{file}.filename(); { auto thr = GetThread(); if (!thr || thr->m_listeners.empty()) { - DefaultLogger(level, filename, line, msg); + DefaultLogger(level, filename.string().c_str(), line, msg); } } - Send(UINT_MAX, 0, level, filename, line, msg); + Send(UINT_MAX, 0, level, filename.string().c_str(), line, msg); } diff --git a/ntcore/src/main/native/cpp/Storage_save.cpp b/ntcore/src/main/native/cpp/Storage_save.cpp index e6eff1862d..8cd6f9910c 100644 --- a/ntcore/src/main/native/cpp/Storage_save.cpp +++ b/ntcore/src/main/native/cpp/Storage_save.cpp @@ -6,10 +6,10 @@ #include #include -#include #include #include #include +#include #include #include "Log.h" @@ -209,7 +209,7 @@ const char* Storage::SavePersistent(const wpi::Twine& filename, // start by writing to temporary file std::error_code ec; - wpi::raw_fd_ostream os(tmp, ec, wpi::sys::fs::F_Text); + wpi::raw_fd_ostream os(tmp, ec, fs::F_Text); if (ec.value() != 0) { err = "could not open file"; goto done; @@ -266,7 +266,7 @@ const char* Storage::SaveEntries(const wpi::Twine& filename, // start by writing to temporary file std::error_code ec; - wpi::raw_fd_ostream os(tmp, ec, wpi::sys::fs::F_Text); + wpi::raw_fd_ostream os(tmp, ec, fs::F_Text); if (ec.value() != 0) { return "could not open file"; } diff --git a/simulation/halsim_ws_server/src/main/native/cpp/HALSimHttpConnection.cpp b/simulation/halsim_ws_server/src/main/native/cpp/HALSimHttpConnection.cpp index 2e651c2788..d6967b47e9 100644 --- a/simulation/halsim_ws_server/src/main/native/cpp/HALSimHttpConnection.cpp +++ b/simulation/halsim_ws_server/src/main/native/cpp/HALSimHttpConnection.cpp @@ -6,11 +6,10 @@ #include -#include #include -#include #include #include +#include #include #include #include @@ -104,35 +103,26 @@ void HALSimHttpConnection::SendFileResponse(int code, const wpi::Twine& contentType, const wpi::Twine& filename, const wpi::Twine& extraHeader) { - // open file - int infd; - if (wpi::sys::fs::openFileForRead(filename, infd)) { - MySendError(404, "error opening file"); - return; - } + std::string fn = filename.str(); + std::error_code ec; - // get status (to get file size) - wpi::sys::fs::file_status status; - if (wpi::sys::fs::status(infd, status)) { + // get file size + auto size = fs::file_size(fn, ec); + if (ec) { MySendError(404, "error getting file size"); - wpi::sys::fs::file_t file = uv_get_osfhandle(infd); - wpi::sys::fs::closeFile(file); return; } - uv_os_fd_t outfd; - int err = uv_fileno(m_stream.GetRawHandle(), &outfd); - if (err < 0) { - m_stream.GetLoopRef().ReportError(err); - MySendError(404, "error getting fd"); - wpi::sys::fs::file_t file = uv_get_osfhandle(infd); - wpi::sys::fs::closeFile(file); + // open file + wpi::raw_fd_istream is{fn, ec, true}; + if (ec) { + MySendError(404, "error opening file"); return; } wpi::SmallVector toSend; wpi::raw_uv_ostream os{toSend, 4096}; - BuildHeader(os, code, codeText, contentType, status.getSize(), extraHeader); + BuildHeader(os, code, codeText, contentType, size, extraHeader); SendData(os.bufs(), false); Log(code); @@ -141,11 +131,10 @@ void HALSimHttpConnection::SendFileResponse(int code, wpi::SmallVector bodyData; wpi::raw_uv_ostream bodyOs{bodyData, 4096}; - wpi::raw_fd_istream is{infd, true}; std::string fileBuf; size_t oldSize = 0; - while (fileBuf.size() < status.getSize()) { + while (fileBuf.size() < size) { oldSize = fileBuf.size(); fileBuf.resize(oldSize + 1); is.read(&(*fileBuf.begin()) + oldSize, 1); @@ -174,33 +163,28 @@ void HALSimHttpConnection::ProcessRequest() { } if (m_request.GetMethod() == wpi::HTTP_GET && path.startswith("/") && - !path.contains("..")) { + !path.contains("..") && !path.contains("//")) { // convert to fs native representation - wpi::SmallVector nativePath; - wpi::sys::path::native(path, nativePath); - + fs::path nativePath; if (path.startswith("/user/")) { - std::string prefix = (wpi::sys::path::get_separator() + "user" + - wpi::sys::path::get_separator()) - .str(); - wpi::sys::path::replace_path_prefix(nativePath, prefix, - m_server->GetWebrootUser()); + nativePath = fs::path{std::string{m_server->GetWebrootSys()}} / + fs::path{std::string{path.drop_front(6)}, + fs::path::format::generic_format}; } else { - wpi::sys::path::replace_path_prefix(nativePath, - wpi::sys::path::get_separator(), - m_server->GetWebrootSys()); + nativePath = fs::path{std::string{m_server->GetWebrootSys()}} / + fs::path{std::string{path.drop_front(1)}, + fs::path::format::generic_format}; } - if (wpi::sys::fs::is_directory(nativePath)) { - wpi::sys::path::append(nativePath, "index.html"); + if (fs::is_directory(nativePath)) { + nativePath.append("index.html"); } - if (!wpi::sys::fs::exists(nativePath) || - wpi::sys::fs::is_directory(nativePath)) { + if (!fs::exists(nativePath) || fs::is_directory(nativePath)) { MySendError(404, "Resource '" + path + "' not found"); } else { - auto contentType = wpi::MimeTypeFromPath(wpi::Twine(nativePath).str()); - SendFileResponse(200, "OK", contentType, nativePath); + auto contentType = wpi::MimeTypeFromPath(nativePath.string()); + SendFileResponse(200, "OK", contentType, nativePath.string()); } } else { MySendError(404, "Resource not found"); diff --git a/simulation/halsim_ws_server/src/main/native/cpp/HALSimWeb.cpp b/simulation/halsim_ws_server/src/main/native/cpp/HALSimWeb.cpp index b8093a3e20..902916ef52 100644 --- a/simulation/halsim_ws_server/src/main/native/cpp/HALSimWeb.cpp +++ b/simulation/halsim_ws_server/src/main/native/cpp/HALSimWeb.cpp @@ -4,12 +4,11 @@ #include "HALSimWeb.h" -#include -#include #include #include #include #include +#include #include #include #include @@ -42,31 +41,22 @@ bool HALSimWeb::Initialize() { } // determine where to get static content from - // wpi::SmallVector tmp; - wpi::SmallString<64> tmp; - + fs::path path; const char* webroot_sys = std::getenv("HALSIMWS_SYSROOT"); if (webroot_sys != nullptr) { - wpi::StringRef tstr(webroot_sys); - tmp.append(tstr); + path = webroot_sys; } else { - wpi::sys::fs::current_path(tmp); - wpi::sys::path::append(tmp, "sim"); + path = fs::current_path() / "sim"; } - wpi::sys::fs::make_absolute(tmp); - m_webroot_sys = wpi::Twine(tmp).str(); + m_webroot_sys = fs::absolute(path).string(); - tmp.clear(); const char* webroot_user = std::getenv("HALSIMWS_USERROOT"); if (webroot_user != nullptr) { - wpi::StringRef tstr(webroot_user); - tmp.append(tstr); + path = webroot_sys; } else { - wpi::sys::fs::current_path(tmp); - wpi::sys::path::append(tmp, "sim", "user"); + path = fs::current_path() / "sim" / "user"; } - wpi::sys::fs::make_absolute(tmp); - m_webroot_user = wpi::Twine(tmp).str(); + m_webroot_user = fs::absolute(path).string(); const char* uri = std::getenv("HALSIMWS_URI"); if (uri != nullptr) { diff --git a/wpilibc/src/main/native/cpp/Errors.cpp b/wpilibc/src/main/native/cpp/Errors.cpp index ea60b1b15b..01cef389cc 100644 --- a/wpilibc/src/main/native/cpp/Errors.cpp +++ b/wpilibc/src/main/native/cpp/Errors.cpp @@ -8,8 +8,8 @@ #include #include -#include #include +#include using namespace frc; @@ -24,10 +24,11 @@ RuntimeError::RuntimeError(int32_t code, std::string&& loc, std::string&& stack, RuntimeError::RuntimeError(int32_t code, const char* fileName, int lineNumber, const char* funcName, std::string&& stack, std::string&& message) - : RuntimeError{code, - fmt::format("{} [{}:{}]", funcName, - wpi::sys::path::filename(fileName), lineNumber), - std::move(stack), std::move(message)} {} + : RuntimeError{ + code, + fmt::format("{} [{}:{}]", funcName, + fs::path{fileName}.filename().string(), lineNumber), + std::move(stack), std::move(message)} {} void RuntimeError::Report() const { HAL_SendError(m_data->code < 0, m_data->code, 0, what(), m_data->loc.c_str(), diff --git a/wpilibc/src/main/native/cpp/Filesystem.cpp b/wpilibc/src/main/native/cpp/Filesystem.cpp index dc258aa698..d497779888 100644 --- a/wpilibc/src/main/native/cpp/Filesystem.cpp +++ b/wpilibc/src/main/native/cpp/Filesystem.cpp @@ -4,31 +4,26 @@ #include "frc/Filesystem.h" -#include -#include +#include #include "frc/RobotBase.h" -void frc::filesystem::GetLaunchDirectory(wpi::SmallVectorImpl& result) { - wpi::sys::fs::current_path(result); +std::string frc::filesystem::GetLaunchDirectory() { + return fs::current_path().string(); } -void frc::filesystem::GetOperatingDirectory( - wpi::SmallVectorImpl& result) { +std::string frc::filesystem::GetOperatingDirectory() { if constexpr (RobotBase::IsReal()) { - wpi::sys::path::native("/home/lvuser", result); + return "/home/lvuser"; } else { - frc::filesystem::GetLaunchDirectory(result); + return frc::filesystem::GetLaunchDirectory(); } } -void frc::filesystem::GetDeployDirectory(wpi::SmallVectorImpl& result) { - frc::filesystem::GetOperatingDirectory(result); +std::string frc::filesystem::GetDeployDirectory() { if constexpr (RobotBase::IsReal()) { - wpi::sys::path::append(result, "deploy"); + return "/home/lvuser/deploy"; } else { - wpi::sys::path::append(result, "src"); - wpi::sys::path::append(result, "main"); - wpi::sys::path::append(result, "deploy"); + return (fs::current_path() / "src" / "main" / "deploy").string(); } } diff --git a/wpilibc/src/main/native/include/frc/Filesystem.h b/wpilibc/src/main/native/include/frc/Filesystem.h index f743f683cf..fbfc681af4 100644 --- a/wpilibc/src/main/native/include/frc/Filesystem.h +++ b/wpilibc/src/main/native/include/frc/Filesystem.h @@ -4,7 +4,7 @@ #pragma once -#include +#include /** WPILib FileSystem namespace */ namespace frc::filesystem { @@ -13,18 +13,18 @@ namespace frc::filesystem { * Obtains the current working path that the program was launched with. * This is analogous to the `pwd` command on unix. * - * @param result The result of the current working path lookup. + * @return The result of the current working path lookup. */ -void GetLaunchDirectory(wpi::SmallVectorImpl& result); +std::string GetLaunchDirectory(); /** * Obtains the operating directory of the program. On the roboRIO, this * is /home/lvuser. In simulation, it is where the simulation was launched * from (`pwd`). * - * @param result The result of the operating directory lookup. + * @return The result of the operating directory lookup. */ -void GetOperatingDirectory(wpi::SmallVectorImpl& result); +std::string GetOperatingDirectory(); /** * Obtains the deploy directory of the program, which is the remote location @@ -32,8 +32,8 @@ void GetOperatingDirectory(wpi::SmallVectorImpl& result); * /home/lvuser/deploy. In simulation, it is where the simulation was launched * from, in the subdirectory "src/main/deploy" (`pwd`/src/main/deploy). * - * @param result The result of the operating directory lookup + * @return The result of the operating directory lookup */ -void GetDeployDirectory(wpi::SmallVectorImpl& result); +std::string GetDeployDirectory(); } // namespace frc::filesystem diff --git a/wpiutil/.styleguide b/wpiutil/.styleguide index 6393a4d0e1..9d54849a10 100644 --- a/wpiutil/.styleguide +++ b/wpiutil/.styleguide @@ -60,6 +60,9 @@ generatedFileExclude { src/main/native/include/wpi/raw_os_ostream\.h$ src/main/native/include/wpi/raw_ostream\.h$ src/main/native/include/wpi/span\.h$ + src/main/native/include/wpi/fs\.h$ + src/main/native/cpp/fs\.cpp$ + src/main/native/include/wpi/ghc/ src/test/native/cpp/span/ src/main/native/include/wpi/type_traits\.h$ src/main/native/cpp/json diff --git a/wpiutil/src/main/native/cpp/fs.cpp b/wpiutil/src/main/native/cpp/fs.cpp new file mode 100644 index 0000000000..34d6ccad07 --- /dev/null +++ b/wpiutil/src/main/native/cpp/fs.cpp @@ -0,0 +1,326 @@ +// Copyright (c) FIRST and other WPILib contributors. +// Open Source Software; you can modify and/or share it under the terms of +// the WPILib BSD license file in the root directory of this project. + +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#include + +#ifdef _WIN32 +#include +#include +#include +// Require at least Windows 7 API. +#define _WIN32_WINNT 0x0601 +#define _WIN32_IE 0x0800 // MinGW at it again. FIXME: verify if still needed. +#define WIN32_LEAN_AND_MEAN +#ifndef NOMINMAX +#define NOMINMAX +#endif + +#define WIN32_NO_STATUS +#include +#undef WIN32_NO_STATUS +#include +#include + +#include +#include + +#include "wpi/WindowsError.h" + +#else // _WIN32 + +#include +#include + +#endif // _WIN32 + +#if defined(__APPLE__) +#include +#endif +#if ((defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) \ + || (defined(__cplusplus) && __cplusplus >= 201703L)) \ + && defined(__has_include) +#if __has_include() \ + && (!defined(__MAC_OS_X_VERSION_MIN_REQUIRED) \ + || __MAC_OS_X_VERSION_MIN_REQUIRED >= 101500) \ + && (defined(__clang__) || !defined(__GNUC__) || __GNUC__ >= 10 \ + || (__GNUC__ >= 9 && __GNUC_MINOR__ >= 1)) +#define GHC_USE_STD_FS +#endif +#endif +#ifndef GHC_USE_STD_FS +// #define GHC_WIN_DISABLE_WSTRING_STORAGE_TYPE +#define GHC_FILESYSTEM_IMPLEMENTATION +#include "wpi/ghc/filesystem.hpp" +#endif + +#include "wpi/Errno.h" +#include "wpi/ErrorHandling.h" +#include "wpi/WindowsError.h" +#include "wpi/fs.h" + +namespace fs { + +#ifdef _WIN32 + +#ifdef _MSC_VER +#pragma comment(lib, "shell32.lib") +#pragma comment(lib, "ole32.lib") +#pragma warning(push) +#pragma warning(disable : 4244 4267 4146) +#endif + +const file_t kInvalidFile = INVALID_HANDLE_VALUE; + +static DWORD nativeDisposition(CreationDisposition Disp, OpenFlags Flags) { + // This is a compatibility hack. Really we should respect the creation + // disposition, but a lot of old code relied on the implicit assumption that + // OF_Append implied it would open an existing file. Since the disposition is + // now explicit and defaults to CD_CreateAlways, this assumption would cause + // any usage of OF_Append to append to a new file, even if the file already + // existed. A better solution might have two new creation dispositions: + // CD_AppendAlways and CD_AppendNew. This would also address the problem of + // OF_Append being used on a read-only descriptor, which doesn't make sense. + if (Flags & OF_Append) + return OPEN_ALWAYS; + + switch (Disp) { + case CD_CreateAlways: + return CREATE_ALWAYS; + case CD_CreateNew: + return CREATE_NEW; + case CD_OpenAlways: + return OPEN_ALWAYS; + case CD_OpenExisting: + return OPEN_EXISTING; + } + wpi_unreachable("unreachable!"); +} + +static DWORD nativeAccess(FileAccess Access, OpenFlags Flags) { + DWORD Result = 0; + if (Access & FA_Read) + Result |= GENERIC_READ; + if (Access & FA_Write) + Result |= GENERIC_WRITE; + if (Flags & OF_Delete) + Result |= DELETE; + if (Flags & OF_UpdateAtime) + Result |= FILE_WRITE_ATTRIBUTES; + return Result; +} + +static file_t openFileInternal(const path& Path, std::error_code& EC, + DWORD Disp, DWORD Access, DWORD Flags, + bool Inherit = false) { + SECURITY_ATTRIBUTES SA; + SA.nLength = sizeof(SA); + SA.lpSecurityDescriptor = nullptr; + SA.bInheritHandle = Inherit; + + HANDLE H = + ::CreateFileW(Path.c_str(), Access, + FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, &SA, + Disp, Flags, NULL); + if (H == INVALID_HANDLE_VALUE) { + DWORD LastError = ::GetLastError(); + EC = wpi::mapWindowsError(LastError); + // Provide a better error message when trying to open directories. + // This only runs if we failed to open the file, so there is probably + // no performances issues. + if (LastError != ERROR_ACCESS_DENIED) { + return kInvalidFile; + } + if (is_directory(Path)) { + EC = std::make_error_code(std::errc::is_a_directory); + } + return kInvalidFile; + } + EC = std::error_code(); + return H; +} + +static std::error_code setDeleteDisposition(HANDLE Handle, bool Delete) { + FILE_DISPOSITION_INFO Disposition; + Disposition.DeleteFile = Delete; + if (!::SetFileInformationByHandle(Handle, FileDispositionInfo, &Disposition, + sizeof(Disposition))) + return wpi::mapWindowsError(::GetLastError()); + return std::error_code(); +} + +file_t OpenFile(const path& Path, std::error_code& EC, CreationDisposition Disp, + FileAccess Access, OpenFlags Flags, unsigned Mode) { + // Verify that we don't have both "append" and "excl". + assert((!(Disp == CD_CreateNew) || !(Flags & OF_Append)) && + "Cannot specify both 'CreateNew' and 'Append' file creation flags!"); + + DWORD NativeDisp = nativeDisposition(Disp, Flags); + DWORD NativeAccess = nativeAccess(Access, Flags); + + bool Inherit = false; + if (Flags & OF_ChildInherit) { + Inherit = true; + } + + file_t Result = openFileInternal(Path, EC, NativeDisp, NativeAccess, + FILE_ATTRIBUTE_NORMAL, Inherit); + if (EC) { + return Result; + } + + if (Flags & OF_UpdateAtime) { + FILETIME FileTime; + SYSTEMTIME SystemTime; + ::GetSystemTime(&SystemTime); + if (::SystemTimeToFileTime(&SystemTime, &FileTime) == 0 || + ::SetFileTime(Result, NULL, &FileTime, NULL) == 0) { + DWORD LastError = ::GetLastError(); + ::CloseHandle(Result); + EC = wpi::mapWindowsError(LastError); + return kInvalidFile; + } + } + + if (Flags & OF_Delete) { + if ((EC = setDeleteDisposition(Result, true))) { + ::CloseHandle(Result); + return kInvalidFile; + } + } + return Result; +} + +file_t OpenFileForRead(const path& Path, std::error_code& EC, OpenFlags Flags) { + return OpenFile(Path, EC, CD_OpenExisting, FA_Read, Flags); +} + +int FileToFd(file_t& F, std::error_code& EC, OpenFlags Flags) { + if (F == kInvalidFile) { + EC = wpi::mapWindowsError(ERROR_INVALID_HANDLE); + return -1; + } + + int CrtOpenFlags = 0; + if (Flags & OF_Append) { + CrtOpenFlags |= _O_APPEND; + } + + if (Flags & OF_Text) { + CrtOpenFlags |= _O_TEXT; + } + + int ResultFD = ::_open_osfhandle(intptr_t(F), CrtOpenFlags); + if (ResultFD == -1) { + ::CloseHandle(F); + EC = wpi::mapWindowsError(ERROR_INVALID_HANDLE); + return -1; + } + + EC = std::error_code(); + F = kInvalidFile; + return ResultFD; +} + +void CloseFile(file_t& F) { + ::CloseHandle(F); + F = kInvalidFile; +} + +#else // _WIN32 + +const file_t kInvalidFile = -1; + +static int nativeOpenFlags(CreationDisposition Disp, OpenFlags Flags, + FileAccess Access) { + int Result = 0; + if (Access == FA_Read) { + Result |= O_RDONLY; + } else if (Access == FA_Write) { + Result |= O_WRONLY; + } else if (Access == (FA_Read | FA_Write)) { + Result |= O_RDWR; + } + + // This is for compatibility with old code that assumed F_Append implied + // would open an existing file. See Windows/Path.inc for a longer comment. + if (Flags & F_Append) { + Disp = CD_OpenAlways; + } + + if (Disp == CD_CreateNew) { + Result |= O_CREAT; // Create if it doesn't exist. + Result |= O_EXCL; // Fail if it does. + } else if (Disp == CD_CreateAlways) { + Result |= O_CREAT; // Create if it doesn't exist. + Result |= O_TRUNC; // Truncate if it does. + } else if (Disp == CD_OpenAlways) { + Result |= O_CREAT; // Create if it doesn't exist. + } else if (Disp == CD_OpenExisting) { + // Nothing special, just don't add O_CREAT and we get these semantics. + } + + if (Flags & F_Append) { + Result |= O_APPEND; + } + +#ifdef O_CLOEXEC + if (!(Flags & OF_ChildInherit)) { + Result |= O_CLOEXEC; + } +#endif + + return Result; +} + +file_t OpenFile(const path& Path, std::error_code& EC, CreationDisposition Disp, + FileAccess Access, OpenFlags Flags, unsigned Mode) { + int OpenFlags = nativeOpenFlags(Disp, Flags, Access); + file_t ResultFD = kInvalidFile; + + // Call ::open in a lambda to avoid overload resolution in RetryAfterSignal + // when open is overloaded, such as in Bionic. + auto Open = [&]() { return ::open(Path.c_str(), OpenFlags, Mode); }; + if ((ResultFD = wpi::sys::RetryAfterSignal(-1, Open)) < 0) { + EC = std::error_code(errno, std::generic_category()); + return kInvalidFile; + } +#ifndef O_CLOEXEC + if (!(Flags & OF_ChildInherit)) { + int r = fcntl(ResultFD, F_SETFD, FD_CLOEXEC); + (void)r; + assert(r == 0 && "fcntl(F_SETFD, FD_CLOEXEC) failed"); + } +#endif + EC = std::error_code(); + return ResultFD; +} + +file_t OpenFileForRead(const path& Path, std::error_code& EC, OpenFlags Flags) { + return OpenFile(Path, EC, CD_OpenExisting, FA_Read, Flags, 0666); +} + +int FileToFd(file_t& F, std::error_code& EC, OpenFlags Flags) { + int fd = F; + F = kInvalidFile; + EC = std::error_code(); + return fd; +} + +void CloseFile(file_t& F) { + ::close(F); + F = kInvalidFile; +} + +#endif // _WIN32 + +} // namespace fs diff --git a/wpiutil/src/main/native/cpp/llvm/ConvertUTF.cpp b/wpiutil/src/main/native/cpp/llvm/ConvertUTF.cpp index abe3744b87..2594d9a8de 100644 --- a/wpiutil/src/main/native/cpp/llvm/ConvertUTF.cpp +++ b/wpiutil/src/main/native/cpp/llvm/ConvertUTF.cpp @@ -52,6 +52,11 @@ #endif #include +#ifdef _WIN32 +#include "wpi/WindowsError.h" +#include "Windows/WindowsSupport.h" +#endif + /* * This code extensively uses fall-through switches. * Keep the compiler from warning about that. @@ -734,6 +739,96 @@ ConversionResult ConvertUTF8toUTF32(const UTF8 **sourceStart, --------------------------------------------------------------------- */ +#ifdef _WIN32 + +namespace sys { +namespace windows { +std::error_code CodePageToUTF16(unsigned codepage, + wpi::StringRef original, + wpi::SmallVectorImpl &utf16) { + if (!original.empty()) { + int len = ::MultiByteToWideChar(codepage, MB_ERR_INVALID_CHARS, original.begin(), + original.size(), utf16.begin(), 0); + + if (len == 0) { + return mapWindowsError(::GetLastError()); + } + + utf16.reserve(len + 1); + utf16.set_size(len); + + len = ::MultiByteToWideChar(codepage, MB_ERR_INVALID_CHARS, original.begin(), + original.size(), utf16.begin(), utf16.size()); + + if (len == 0) { + return mapWindowsError(::GetLastError()); + } + } + + // Make utf16 null terminated. + utf16.push_back(0); + utf16.pop_back(); + + return std::error_code(); +} + +std::error_code UTF8ToUTF16(wpi::StringRef utf8, + wpi::SmallVectorImpl &utf16) { + return CodePageToUTF16(CP_UTF8, utf8, utf16); +} + +std::error_code CurCPToUTF16(wpi::StringRef curcp, + wpi::SmallVectorImpl &utf16) { + return CodePageToUTF16(CP_ACP, curcp, utf16); +} + +static +std::error_code UTF16ToCodePage(unsigned codepage, const wchar_t *utf16, + size_t utf16_len, + wpi::SmallVectorImpl &converted) { + if (utf16_len) { + // Get length. + int len = ::WideCharToMultiByte(codepage, 0, utf16, utf16_len, converted.begin(), + 0, NULL, NULL); + + if (len == 0) { + return mapWindowsError(::GetLastError()); + } + + converted.reserve(len); + converted.set_size(len); + + // Now do the actual conversion. + len = ::WideCharToMultiByte(codepage, 0, utf16, utf16_len, converted.data(), + converted.size(), NULL, NULL); + + if (len == 0) { + return mapWindowsError(::GetLastError()); + } + } + + // Make the new string null terminated. + converted.push_back(0); + converted.pop_back(); + + return std::error_code(); +} + +std::error_code UTF16ToUTF8(const wchar_t *utf16, size_t utf16_len, + wpi::SmallVectorImpl &utf8) { + return UTF16ToCodePage(CP_UTF8, utf16, utf16_len, utf8); +} + +std::error_code UTF16ToCurCP(const wchar_t *utf16, size_t utf16_len, + wpi::SmallVectorImpl &curcp) { + return UTF16ToCodePage(CP_ACP, utf16, utf16_len, curcp); +} + +} // end namespace windows +} // end namespace sys + +#endif // _WIN32 + } // namespace llvm ConvertUTF_RESTORE_WARNINGS diff --git a/wpiutil/src/main/native/cpp/llvm/Path.cpp b/wpiutil/src/main/native/cpp/llvm/Path.cpp deleted file mode 100644 index 49f92d32d5..0000000000 --- a/wpiutil/src/main/native/cpp/llvm/Path.cpp +++ /dev/null @@ -1,833 +0,0 @@ -//===-- Path.cpp - Implement OS Path Concept ------------------------------===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file implements the operating system Path API. -// -//===----------------------------------------------------------------------===// - -#include "wpi/Path.h" -#include "wpi/ArrayRef.h" -#include "wpi/Endian.h" -#include "wpi/Errc.h" -#include "wpi/ErrorHandling.h" -#include "wpi/FileSystem.h" -#include "wpi/SmallString.h" -#include -#include - -#if !defined(_MSC_VER) && !defined(__MINGW32__) -#include -#else -#include -#endif - -using namespace wpi; -using namespace wpi::support::endian; - -namespace { - using wpi::StringRef; - using wpi::sys::path::is_separator; - using wpi::sys::path::Style; - - inline Style real_style(Style style) { -#ifdef _WIN32 - return (style == Style::posix) ? Style::posix : Style::windows; -#else - return (style == Style::windows) ? Style::windows : Style::posix; -#endif - } - - inline const char *separators(Style style) { - if (real_style(style) == Style::windows) - return "\\/"; - return "/"; - } - - inline char preferred_separator(Style style) { - if (real_style(style) == Style::windows) - return '\\'; - return '/'; - } - - StringRef find_first_component(StringRef path, Style style) { - // Look for this first component in the following order. - // * empty (in this case we return an empty string) - // * either C: or {//,\\}net. - // * {/,\} - // * {file,directory}name - - if (path.empty()) - return path; - - if (real_style(style) == Style::windows) { - // C: - if (path.size() >= 2 && - std::isalpha(static_cast(path[0])) && path[1] == ':') - return path.substr(0, 2); - } - - // //net - if ((path.size() > 2) && is_separator(path[0], style) && - path[0] == path[1] && !is_separator(path[2], style)) { - // Find the next directory separator. - size_t end = path.find_first_of(separators(style), 2); - return path.substr(0, end); - } - - // {/,\} - if (is_separator(path[0], style)) - return path.substr(0, 1); - - // * {file,directory}name - size_t end = path.find_first_of(separators(style)); - return path.substr(0, end); - } - - // Returns the first character of the filename in str. For paths ending in - // '/', it returns the position of the '/'. - size_t filename_pos(StringRef str, Style style) { - if (str.size() > 0 && is_separator(str[str.size() - 1], style)) - return str.size() - 1; - - size_t pos = str.find_last_of(separators(style), str.size() - 1); - - if (real_style(style) == Style::windows) { - if (pos == StringRef::npos) - pos = str.find_last_of(':', str.size() - 2); - } - - if (pos == StringRef::npos || (pos == 1 && is_separator(str[0], style))) - return 0; - - return pos + 1; - } - - // Returns the position of the root directory in str. If there is no root - // directory in str, it returns StringRef::npos. - size_t root_dir_start(StringRef str, Style style) { - // case "c:/" - if (real_style(style) == Style::windows) { - if (str.size() > 2 && str[1] == ':' && is_separator(str[2], style)) - return 2; - } - - // case "//net" - if (str.size() > 3 && is_separator(str[0], style) && str[0] == str[1] && - !is_separator(str[2], style)) { - return str.find_first_of(separators(style), 2); - } - - // case "/" - if (str.size() > 0 && is_separator(str[0], style)) - return 0; - - return StringRef::npos; - } - - // Returns the position past the end of the "parent path" of path. The parent - // path will not end in '/', unless the parent is the root directory. If the - // path has no parent, 0 is returned. - size_t parent_path_end(StringRef path, Style style) { - size_t end_pos = filename_pos(path, style); - - bool filename_was_sep = - path.size() > 0 && is_separator(path[end_pos], style); - - // Skip separators until we reach root dir (or the start of the string). - size_t root_dir_pos = root_dir_start(path, style); - while (end_pos > 0 && - (root_dir_pos == StringRef::npos || end_pos > root_dir_pos) && - is_separator(path[end_pos - 1], style)) - --end_pos; - - if (end_pos == root_dir_pos && !filename_was_sep) { - // We've reached the root dir and the input path was *not* ending in a - // sequence of slashes. Include the root dir in the parent path. - return root_dir_pos + 1; - } - - // Otherwise, just include before the last slash. - return end_pos; - } -} // end unnamed namespace - -namespace wpi { -namespace sys { -namespace path { - -const_iterator begin(StringRef path, Style style) { - const_iterator i; - i.Path = path; - i.Component = find_first_component(path, style); - i.Position = 0; - i.S = style; - return i; -} - -const_iterator end(StringRef path) { - const_iterator i; - i.Path = path; - i.Position = path.size(); - return i; -} - -const_iterator &const_iterator::operator++() { - assert(Position < Path.size() && "Tried to increment past end!"); - - // Increment Position to past the current component - Position += Component.size(); - - // Check for end. - if (Position == Path.size()) { - Component = StringRef(); - return *this; - } - - // Both POSIX and Windows treat paths that begin with exactly two separators - // specially. - bool was_net = Component.size() > 2 && is_separator(Component[0], S) && - Component[1] == Component[0] && !is_separator(Component[2], S); - - // Handle separators. - if (is_separator(Path[Position], S)) { - // Root dir. - if (was_net || - // c:/ - (real_style(S) == Style::windows && Component.endswith(":"))) { - Component = Path.substr(Position, 1); - return *this; - } - - // Skip extra separators. - while (Position != Path.size() && is_separator(Path[Position], S)) { - ++Position; - } - - // Treat trailing '/' as a '.', unless it is the root dir. - if (Position == Path.size() && Component != "/") { - --Position; - Component = "."; - return *this; - } - } - - // Find next component. - size_t end_pos = Path.find_first_of(separators(S), Position); - Component = Path.slice(Position, end_pos); - - return *this; -} - -bool const_iterator::operator==(const const_iterator &RHS) const { - return Path.begin() == RHS.Path.begin() && Position == RHS.Position; -} - -ptrdiff_t const_iterator::operator-(const const_iterator &RHS) const { - return Position - RHS.Position; -} - -reverse_iterator rbegin(StringRef Path, Style style) { - reverse_iterator I; - I.Path = Path; - I.Position = Path.size(); - I.S = style; - return ++I; -} - -reverse_iterator rend(StringRef Path) { - reverse_iterator I; - I.Path = Path; - I.Component = Path.substr(0, 0); - I.Position = 0; - return I; -} - -reverse_iterator &reverse_iterator::operator++() { - size_t root_dir_pos = root_dir_start(Path, S); - - // Skip separators unless it's the root directory. - size_t end_pos = Position; - while (end_pos > 0 && (end_pos - 1) != root_dir_pos && - is_separator(Path[end_pos - 1], S)) - --end_pos; - - // Treat trailing '/' as a '.', unless it is the root dir. - if (Position == Path.size() && !Path.empty() && - is_separator(Path.back(), S) && - (root_dir_pos == StringRef::npos || end_pos - 1 > root_dir_pos)) { - --Position; - Component = "."; - return *this; - } - - // Find next separator. - size_t start_pos = filename_pos(Path.substr(0, end_pos), S); - Component = Path.slice(start_pos, end_pos); - Position = start_pos; - return *this; -} - -bool reverse_iterator::operator==(const reverse_iterator &RHS) const { - return Path.begin() == RHS.Path.begin() && Component == RHS.Component && - Position == RHS.Position; -} - -ptrdiff_t reverse_iterator::operator-(const reverse_iterator &RHS) const { - return Position - RHS.Position; -} - -StringRef root_path(StringRef path, Style style) { - const_iterator b = begin(path, style), pos = b, e = end(path); - if (b != e) { - bool has_net = - b->size() > 2 && is_separator((*b)[0], style) && (*b)[1] == (*b)[0]; - bool has_drive = (real_style(style) == Style::windows) && b->endswith(":"); - - if (has_net || has_drive) { - if ((++pos != e) && is_separator((*pos)[0], style)) { - // {C:/,//net/}, so get the first two components. - return path.substr(0, b->size() + pos->size()); - } else { - // just {C:,//net}, return the first component. - return *b; - } - } - - // POSIX style root directory. - if (is_separator((*b)[0], style)) { - return *b; - } - } - - return StringRef(); -} - -StringRef root_name(StringRef path, Style style) { - const_iterator b = begin(path, style), e = end(path); - if (b != e) { - bool has_net = - b->size() > 2 && is_separator((*b)[0], style) && (*b)[1] == (*b)[0]; - bool has_drive = (real_style(style) == Style::windows) && b->endswith(":"); - - if (has_net || has_drive) { - // just {C:,//net}, return the first component. - return *b; - } - } - - // No path or no name. - return StringRef(); -} - -StringRef root_directory(StringRef path, Style style) { - const_iterator b = begin(path, style), pos = b, e = end(path); - if (b != e) { - bool has_net = - b->size() > 2 && is_separator((*b)[0], style) && (*b)[1] == (*b)[0]; - bool has_drive = (real_style(style) == Style::windows) && b->endswith(":"); - - if ((has_net || has_drive) && - // {C:,//net}, skip to the next component. - (++pos != e) && is_separator((*pos)[0], style)) { - return *pos; - } - - // POSIX style root directory. - if (!has_net && is_separator((*b)[0], style)) { - return *b; - } - } - - // No path or no root. - return StringRef(); -} - -StringRef relative_path(StringRef path, Style style) { - StringRef root = root_path(path, style); - return path.substr(root.size()); -} - -void append(SmallVectorImpl &path, Style style, const Twine &a, - const Twine &b, const Twine &c, const Twine &d) { - SmallString<32> a_storage; - SmallString<32> b_storage; - SmallString<32> c_storage; - SmallString<32> d_storage; - - SmallVector components; - if (!a.isTriviallyEmpty()) components.push_back(a.toStringRef(a_storage)); - if (!b.isTriviallyEmpty()) components.push_back(b.toStringRef(b_storage)); - if (!c.isTriviallyEmpty()) components.push_back(c.toStringRef(c_storage)); - if (!d.isTriviallyEmpty()) components.push_back(d.toStringRef(d_storage)); - - for (auto &component : components) { - bool path_has_sep = - !path.empty() && is_separator(path[path.size() - 1], style); - if (path_has_sep) { - // Strip separators from beginning of component. - size_t loc = component.find_first_not_of(separators(style)); - StringRef c = component.substr(loc); - - // Append it. - path.append(c.begin(), c.end()); - continue; - } - - bool component_has_sep = - !component.empty() && is_separator(component[0], style); - if (!component_has_sep && - !(path.empty() || has_root_name(component, style))) { - // Add a separator. - path.push_back(preferred_separator(style)); - } - - path.append(component.begin(), component.end()); - } -} - -void append(SmallVectorImpl &path, const Twine &a, const Twine &b, - const Twine &c, const Twine &d) { - append(path, Style::native, a, b, c, d); -} - -void append(SmallVectorImpl &path, const_iterator begin, - const_iterator end, Style style) { - for (; begin != end; ++begin) - path::append(path, style, *begin); -} - -StringRef parent_path(StringRef path, Style style) { - size_t end_pos = parent_path_end(path, style); - if (end_pos == StringRef::npos) - return StringRef(); - else - return path.substr(0, end_pos); -} - -void remove_filename(SmallVectorImpl &path, Style style) { - size_t end_pos = parent_path_end(StringRef(path.begin(), path.size()), style); - if (end_pos != StringRef::npos) - path.set_size(end_pos); -} - -void replace_extension(SmallVectorImpl &path, const Twine &extension, - Style style) { - StringRef p(path.begin(), path.size()); - SmallString<32> ext_storage; - StringRef ext = extension.toStringRef(ext_storage); - - // Erase existing extension. - size_t pos = p.find_last_of('.'); - if (pos != StringRef::npos && pos >= filename_pos(p, style)) - path.set_size(pos); - - // Append '.' if needed. - if (ext.size() > 0 && ext[0] != '.') - path.push_back('.'); - - // Append extension. - path.append(ext.begin(), ext.end()); -} - -void replace_path_prefix(SmallVectorImpl &Path, - const StringRef &OldPrefix, const StringRef &NewPrefix, - Style style) { - if (OldPrefix.empty() && NewPrefix.empty()) - return; - - StringRef OrigPath(Path.begin(), Path.size()); - if (!OrigPath.startswith(OldPrefix)) - return; - - // If prefixes have the same size we can simply copy the new one over. - if (OldPrefix.size() == NewPrefix.size()) { - wpi::copy(NewPrefix, Path.begin()); - return; - } - - StringRef RelPath = OrigPath.substr(OldPrefix.size()); - SmallString<256> NewPath; - path::append(NewPath, style, NewPrefix); - path::append(NewPath, style, RelPath); - Path.swap(NewPath); -} - -void native(const Twine &path, SmallVectorImpl &result, Style style) { - assert((!path.isSingleStringRef() || - path.getSingleStringRef().data() != result.data()) && - "path and result are not allowed to overlap!"); - // Clear result. - result.clear(); - path.toVector(result); - native(result, style); -} - -void native(SmallVectorImpl &Path, Style style) { - if (Path.empty()) - return; - if (real_style(style) == Style::windows) { - std::replace(Path.begin(), Path.end(), '/', '\\'); - if (Path[0] == '~' && (Path.size() == 1 || is_separator(Path[1], style))) { - SmallString<128> PathHome; - home_directory(PathHome); - PathHome.append(Path.begin() + 1, Path.end()); - Path = PathHome; - } - } else { - for (auto PI = Path.begin(), PE = Path.end(); PI < PE; ++PI) { - if (*PI == '\\') { - auto PN = PI + 1; - if (PN < PE && *PN == '\\') - ++PI; // increment once, the for loop will move over the escaped slash - else - *PI = '/'; - } - } - } -} - -std::string convert_to_slash(StringRef path, Style style) { - if (real_style(style) != Style::windows) - return path; - - std::string s = path.str(); - std::replace(s.begin(), s.end(), '\\', '/'); - return s; -} - -StringRef filename(StringRef path, Style style) { return *rbegin(path, style); } - -StringRef stem(StringRef path, Style style) { - StringRef fname = filename(path, style); - size_t pos = fname.find_last_of('.'); - if (pos == StringRef::npos) - return fname; - else - if ((fname.size() == 1 && fname == ".") || - (fname.size() == 2 && fname == "..")) - return fname; - else - return fname.substr(0, pos); -} - -StringRef extension(StringRef path, Style style) { - StringRef fname = filename(path, style); - size_t pos = fname.find_last_of('.'); - if (pos == StringRef::npos) - return StringRef(); - else - if ((fname.size() == 1 && fname == ".") || - (fname.size() == 2 && fname == "..")) - return StringRef(); - else - return fname.substr(pos); -} - -bool is_separator(char value, Style style) { - if (value == '/') - return true; - if (real_style(style) == Style::windows) - return value == '\\'; - return false; -} - -StringRef get_separator(Style style) { - if (real_style(style) == Style::windows) - return "\\"; - return "/"; -} - -bool has_root_name(const Twine &path, Style style) { - SmallString<128> path_storage; - StringRef p = path.toStringRef(path_storage); - - return !root_name(p, style).empty(); -} - -bool has_root_directory(const Twine &path, Style style) { - SmallString<128> path_storage; - StringRef p = path.toStringRef(path_storage); - - return !root_directory(p, style).empty(); -} - -bool has_root_path(const Twine &path, Style style) { - SmallString<128> path_storage; - StringRef p = path.toStringRef(path_storage); - - return !root_path(p, style).empty(); -} - -bool has_relative_path(const Twine &path, Style style) { - SmallString<128> path_storage; - StringRef p = path.toStringRef(path_storage); - - return !relative_path(p, style).empty(); -} - -bool has_filename(const Twine &path, Style style) { - SmallString<128> path_storage; - StringRef p = path.toStringRef(path_storage); - - return !filename(p, style).empty(); -} - -bool has_parent_path(const Twine &path, Style style) { - SmallString<128> path_storage; - StringRef p = path.toStringRef(path_storage); - - return !parent_path(p, style).empty(); -} - -bool has_stem(const Twine &path, Style style) { - SmallString<128> path_storage; - StringRef p = path.toStringRef(path_storage); - - return !stem(p, style).empty(); -} - -bool has_extension(const Twine &path, Style style) { - SmallString<128> path_storage; - StringRef p = path.toStringRef(path_storage); - - return !extension(p, style).empty(); -} - -bool is_absolute(const Twine &path, Style style) { - SmallString<128> path_storage; - StringRef p = path.toStringRef(path_storage); - - bool rootDir = has_root_directory(p, style); - bool rootName = - (real_style(style) != Style::windows) || has_root_name(p, style); - - return rootDir && rootName; -} - -bool is_relative(const Twine &path, Style style) { - return !is_absolute(path, style); -} - -StringRef remove_leading_dotslash(StringRef Path, Style style) { - // Remove leading "./" (or ".//" or "././" etc.) - while (Path.size() > 2 && Path[0] == '.' && is_separator(Path[1], style)) { - Path = Path.substr(2); - while (Path.size() > 0 && is_separator(Path[0], style)) - Path = Path.substr(1); - } - return Path; -} - -static SmallString<256> remove_dots(StringRef path, bool remove_dot_dot, - Style style) { - SmallVector components; - - // Skip the root path, then look for traversal in the components. - StringRef rel = path::relative_path(path, style); - for (StringRef C : - wpi::make_range(path::begin(rel, style), path::end(rel))) { - if (C == ".") - continue; - // Leading ".." will remain in the path unless it's at the root. - if (remove_dot_dot && C == "..") { - if (!components.empty() && components.back() != "..") { - components.pop_back(); - continue; - } - if (path::is_absolute(path, style)) - continue; - } - components.push_back(C); - } - - SmallString<256> buffer = path::root_path(path, style); - for (StringRef C : components) - path::append(buffer, style, C); - return buffer; -} - -bool remove_dots(SmallVectorImpl &path, bool remove_dot_dot, - Style style) { - StringRef p(path.data(), path.size()); - - SmallString<256> result = remove_dots(p, remove_dot_dot, style); - if (result == path) - return false; - - path.swap(result); - return true; -} - -} // end namespace path - -namespace fs { - -std::error_code getUniqueID(const Twine Path, UniqueID &Result) { - file_status Status; - std::error_code EC = status(Path, Status); - if (EC) - return EC; - Result = Status.getUniqueID(); - return std::error_code(); -} - -void make_absolute(const Twine ¤t_directory, - SmallVectorImpl &path) { - StringRef p(path.data(), path.size()); - - bool rootDirectory = path::has_root_directory(p); - bool rootName = - (real_style(Style::native) != Style::windows) || path::has_root_name(p); - - // Already absolute. - if (rootName && rootDirectory) - return; - - // All of the following conditions will need the current directory. - SmallString<128> current_dir; - current_directory.toVector(current_dir); - - // Relative path. Prepend the current directory. - if (!rootName && !rootDirectory) { - // Append path to the current directory. - path::append(current_dir, p); - // Set path to the result. - path.swap(current_dir); - return; - } - - if (!rootName && rootDirectory) { - StringRef cdrn = path::root_name(current_dir); - SmallString<128> curDirRootName(cdrn.begin(), cdrn.end()); - path::append(curDirRootName, p); - // Set path to the result. - path.swap(curDirRootName); - return; - } - - if (rootName && !rootDirectory) { - StringRef pRootName = path::root_name(p); - StringRef bRootDirectory = path::root_directory(current_dir); - StringRef bRelativePath = path::relative_path(current_dir); - StringRef pRelativePath = path::relative_path(p); - - SmallString<128> res; - path::append(res, pRootName, bRootDirectory, bRelativePath, pRelativePath); - path.swap(res); - return; - } - - wpi_unreachable("All rootName and rootDirectory combinations should have " - "occurred above!"); -} - -std::error_code make_absolute(SmallVectorImpl &path) { - if (path::is_absolute(path)) - return {}; - - SmallString<128> current_dir; - if (std::error_code ec = current_path(current_dir)) - return ec; - - make_absolute(current_dir, path); - return {}; -} - -bool exists(const basic_file_status &status) { - return status_known(status) && status.type() != file_type::file_not_found; -} - -bool status_known(const basic_file_status &s) { - return s.type() != file_type::status_error; -} - -file_type get_file_type(const Twine &Path, bool Follow) { - file_status st; - if (status(Path, st, Follow)) - return file_type::status_error; - return st.type(); -} - -bool is_directory(const basic_file_status &status) { - return status.type() == file_type::directory_file; -} - -std::error_code is_directory(const Twine &path, bool &result) { - file_status st; - if (std::error_code ec = status(path, st)) - return ec; - result = is_directory(st); - return std::error_code(); -} - -bool is_regular_file(const basic_file_status &status) { - return status.type() == file_type::regular_file; -} - -std::error_code is_regular_file(const Twine &path, bool &result) { - file_status st; - if (std::error_code ec = status(path, st)) - return ec; - result = is_regular_file(st); - return std::error_code(); -} - -bool is_symlink_file(const basic_file_status &status) { - return status.type() == file_type::symlink_file; -} - -std::error_code is_symlink_file(const Twine &path, bool &result) { - file_status st; - if (std::error_code ec = status(path, st, false)) - return ec; - result = is_symlink_file(st); - return std::error_code(); -} - -bool is_other(const basic_file_status &status) { - return exists(status) && - !is_regular_file(status) && - !is_directory(status); -} - -std::error_code is_other(const Twine &Path, bool &Result) { - file_status FileStatus; - if (std::error_code EC = status(Path, FileStatus)) - return EC; - Result = is_other(FileStatus); - return std::error_code(); -} - -void directory_entry::replace_filename(const Twine &Filename, file_type Type, - basic_file_status Status) { - SmallString<128> PathStr = path::parent_path(Path); - path::append(PathStr, Filename); - this->Path = PathStr.str(); - this->Type = Type; - this->Status = Status; -} - -ErrorOr getPermissions(const Twine &Path) { - file_status Status; - if (std::error_code EC = status(Path, Status)) - return EC; - - return Status.permissions(); -} - -} // end namespace fs -} // end namespace sys -} // end namespace wpi - -// Include the truly platform-specific parts. -#ifdef _WIN32 -#include "Windows/Path.inc" -#else -#include "Unix/Path.inc" -#endif diff --git a/wpiutil/src/main/native/cpp/llvm/Unix/Path.inc b/wpiutil/src/main/native/cpp/llvm/Unix/Path.inc deleted file mode 100644 index 4ec9e86a49..0000000000 --- a/wpiutil/src/main/native/cpp/llvm/Unix/Path.inc +++ /dev/null @@ -1,539 +0,0 @@ -//===- llvm/Support/Unix/Path.inc - Unix Path Implementation ----*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file implements the Unix specific implementation of the Path API. -// -//===----------------------------------------------------------------------===// - -//===----------------------------------------------------------------------===// -//=== WARNING: Implementation here must contain only generic UNIX code that -//=== is guaranteed to work on *all* UNIX variants. -//===----------------------------------------------------------------------===// - -#include "wpi/Errno.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -using namespace wpi; - -namespace wpi { -namespace sys { -namespace fs { - -const file_t kInvalidFile = -1; - -TimePoint<> basic_file_status::getLastAccessedTime() const { - return toTimePoint(fs_st_atime, fs_st_atime_nsec); -} - -TimePoint<> basic_file_status::getLastModificationTime() const { - return toTimePoint(fs_st_mtime, fs_st_mtime_nsec); -} - -UniqueID file_status::getUniqueID() const { - return UniqueID(fs_st_dev, fs_st_ino); -} - -uint32_t file_status::getLinkCount() const { - return fs_st_nlinks; -} - -std::error_code current_path(SmallVectorImpl &result) { - result.clear(); - - const char *pwd = ::getenv("PWD"); - wpi::sys::fs::file_status PWDStatus, DotStatus; - if (pwd && wpi::sys::path::is_absolute(pwd) && - !wpi::sys::fs::status(pwd, PWDStatus) && - !wpi::sys::fs::status(".", DotStatus) && - PWDStatus.getUniqueID() == DotStatus.getUniqueID()) { - result.append(pwd, pwd + strlen(pwd)); - return std::error_code(); - } - -#ifdef MAXPATHLEN - result.reserve(MAXPATHLEN); -#else - result.reserve(1024); -#endif - - while (true) { - if (::getcwd(result.data(), result.capacity()) == nullptr) { - // See if there was a real error. - if (errno != ENOMEM) - return std::error_code(errno, std::generic_category()); - // Otherwise there just wasn't enough space. - result.reserve(result.capacity() * 2); - } else - break; - } - - result.set_size(strlen(result.data())); - return std::error_code(); -} - -static int convertAccessMode(AccessMode Mode) { - switch (Mode) { - case AccessMode::Exist: - return F_OK; - case AccessMode::Write: - return W_OK; - case AccessMode::Execute: - return R_OK | X_OK; // scripts also need R_OK. - default: - return F_OK; - } -} - -std::error_code access(const Twine &Path, AccessMode Mode) { - SmallString<128> PathStorage; - StringRef P = Path.toNullTerminatedStringRef(PathStorage); - - if (::access(P.begin(), convertAccessMode(Mode)) == -1) - return std::error_code(errno, std::generic_category()); - - if (Mode == AccessMode::Execute) { - // Don't say that directories are executable. - struct stat buf; - if (0 != stat(P.begin(), &buf)) - return errc::permission_denied; - if (!S_ISREG(buf.st_mode)) - return errc::permission_denied; - } - - return std::error_code(); -} - -bool equivalent(file_status A, file_status B) { - assert(status_known(A) && status_known(B)); - return A.fs_st_dev == B.fs_st_dev && - A.fs_st_ino == B.fs_st_ino; -} - -std::error_code equivalent(const Twine &A, const Twine &B, bool &result) { - file_status fsA, fsB; - if (std::error_code ec = status(A, fsA)) - return ec; - if (std::error_code ec = status(B, fsB)) - return ec; - result = equivalent(fsA, fsB); - return std::error_code(); -} - -static file_type typeForMode(mode_t Mode) { - if (S_ISDIR(Mode)) - return file_type::directory_file; - else if (S_ISREG(Mode)) - return file_type::regular_file; - else if (S_ISBLK(Mode)) - return file_type::block_file; - else if (S_ISCHR(Mode)) - return file_type::character_file; - else if (S_ISFIFO(Mode)) - return file_type::fifo_file; - else if (S_ISSOCK(Mode)) - return file_type::socket_file; - else if (S_ISLNK(Mode)) - return file_type::symlink_file; - return file_type::type_unknown; -} - -static std::error_code fillStatus(int StatRet, const struct stat &Status, - file_status &Result) { - if (StatRet != 0) { - std::error_code EC(errno, std::generic_category()); - if (EC == errc::no_such_file_or_directory) - Result = file_status(file_type::file_not_found); - else - Result = file_status(file_type::status_error); - return EC; - } - - uint32_t atime_nsec, mtime_nsec; -#if defined(__APPLE__) - atime_nsec = Status.st_atimespec.tv_nsec; - mtime_nsec = Status.st_mtimespec.tv_nsec; -#else - atime_nsec = Status.st_atim.tv_nsec; - mtime_nsec = Status.st_mtim.tv_nsec; -#endif - - perms Perms = static_cast(Status.st_mode) & all_perms; - Result = file_status(typeForMode(Status.st_mode), Perms, Status.st_dev, - Status.st_nlink, Status.st_ino, - Status.st_atime, atime_nsec, Status.st_mtime, mtime_nsec, - Status.st_uid, Status.st_gid, Status.st_size); - - return std::error_code(); -} - -std::error_code status(const Twine &Path, file_status &Result, bool Follow) { - SmallString<128> PathStorage; - StringRef P = Path.toNullTerminatedStringRef(PathStorage); - - struct stat Status; - int StatRet = (Follow ? ::stat : ::lstat)(P.begin(), &Status); - return fillStatus(StatRet, Status, Result); -} - -std::error_code status(int FD, file_status &Result) { - struct stat Status; - int StatRet = ::fstat(FD, &Status); - return fillStatus(StatRet, Status, Result); -} - -std::error_code mapped_file_region::init(int FD, uint64_t Offset, - mapmode Mode) { - assert(Size != 0); - - int flags = (Mode == readwrite) ? MAP_SHARED : MAP_PRIVATE; - int prot = (Mode == readonly) ? PROT_READ : (PROT_READ | PROT_WRITE); -#if defined(__APPLE__) - //---------------------------------------------------------------------- - // Newer versions of MacOSX have a flag that will allow us to read from - // binaries whose code signature is invalid without crashing by using - // the MAP_RESILIENT_CODESIGN flag. Also if a file from removable media - // is mapped we can avoid crashing and return zeroes to any pages we try - // to read if the media becomes unavailable by using the - // MAP_RESILIENT_MEDIA flag. These flags are only usable when mapping - // with PROT_READ, so take care not to specify them otherwise. - //---------------------------------------------------------------------- - if (Mode == readonly) { -#if defined(MAP_RESILIENT_CODESIGN) - flags |= MAP_RESILIENT_CODESIGN; -#endif -#if defined(MAP_RESILIENT_MEDIA) - flags |= MAP_RESILIENT_MEDIA; -#endif - } -#endif // #if defined (__APPLE__) - - Mapping = ::mmap(nullptr, Size, prot, flags, FD, Offset); - if (Mapping == MAP_FAILED) - return std::error_code(errno, std::generic_category()); - return std::error_code(); -} - -mapped_file_region::mapped_file_region(int fd, mapmode mode, size_t length, - uint64_t offset, std::error_code &ec) - : Size(length), Mapping(), Mode(mode) { - (void)Mode; - ec = init(fd, offset, mode); - if (ec) - Mapping = nullptr; -} - -mapped_file_region::~mapped_file_region() { - if (Mapping) - ::munmap(Mapping, Size); -} - -size_t mapped_file_region::size() const { - assert(Mapping && "Mapping failed but used anyway!"); - return Size; -} - -char *mapped_file_region::data() const { - assert(Mapping && "Mapping failed but used anyway!"); - return reinterpret_cast(Mapping); -} - -const char *mapped_file_region::const_data() const { - assert(Mapping && "Mapping failed but used anyway!"); - return reinterpret_cast(Mapping); -} - -int mapped_file_region::alignment() { - return ::sysconf(_SC_PAGE_SIZE); -} - -std::error_code detail::directory_iterator_construct(detail::DirIterState &it, - StringRef path, - bool follow_symlinks) { - SmallString<128> path_null(path); - DIR *directory = ::opendir(path_null.c_str()); - if (!directory) - return std::error_code(errno, std::generic_category()); - - it.IterationHandle = reinterpret_cast(directory); - // Add something for replace_filename to replace. - path::append(path_null, "."); - it.CurrentEntry = directory_entry(path_null.str(), follow_symlinks); - return directory_iterator_increment(it); -} - -std::error_code detail::directory_iterator_destruct(detail::DirIterState &it) { - if (it.IterationHandle) - ::closedir(reinterpret_cast(it.IterationHandle)); - it.IterationHandle = 0; - it.CurrentEntry = directory_entry(); - return std::error_code(); -} - -static file_type direntType(dirent* Entry) { - // Most platforms provide the file type in the dirent: Linux/BSD/Mac. - // The DTTOIF macro lets us reuse our status -> type conversion. - return typeForMode(DTTOIF(Entry->d_type)); -} - -std::error_code detail::directory_iterator_increment(detail::DirIterState &It) { - errno = 0; - dirent *CurDir = ::readdir(reinterpret_cast(It.IterationHandle)); - if (CurDir == nullptr && errno != 0) { - return std::error_code(errno, std::generic_category()); - } else if (CurDir != nullptr) { - StringRef Name(CurDir->d_name); - if ((Name.size() == 1 && Name[0] == '.') || - (Name.size() == 2 && Name[0] == '.' && Name[1] == '.')) - return directory_iterator_increment(It); - It.CurrentEntry.replace_filename(Name, direntType(CurDir)); - } else - return directory_iterator_destruct(It); - - return std::error_code(); -} - -ErrorOr directory_entry::status() const { - file_status s; - if (auto EC = fs::status(Path, s, FollowSymlinks)) - return EC; - return s; -} - -#if !defined(F_GETPATH) -static bool hasProcSelfFD() { - // If we have a /proc filesystem mounted, we can quickly establish the - // real name of the file with readlink - static const bool Result = (::access("/proc/self/fd", R_OK) == 0); - return Result; -} -#endif - -static int nativeOpenFlags(CreationDisposition Disp, OpenFlags Flags, - FileAccess Access) { - int Result = 0; - if (Access == FA_Read) - Result |= O_RDONLY; - else if (Access == FA_Write) - Result |= O_WRONLY; - else if (Access == (FA_Read | FA_Write)) - Result |= O_RDWR; - - // This is for compatibility with old code that assumed F_Append implied - // would open an existing file. See Windows/Path.inc for a longer comment. - if (Flags & F_Append) - Disp = CD_OpenAlways; - - if (Disp == CD_CreateNew) { - Result |= O_CREAT; // Create if it doesn't exist. - Result |= O_EXCL; // Fail if it does. - } else if (Disp == CD_CreateAlways) { - Result |= O_CREAT; // Create if it doesn't exist. - Result |= O_TRUNC; // Truncate if it does. - } else if (Disp == CD_OpenAlways) { - Result |= O_CREAT; // Create if it doesn't exist. - } else if (Disp == CD_OpenExisting) { - // Nothing special, just don't add O_CREAT and we get these semantics. - } - - if (Flags & F_Append) - Result |= O_APPEND; - -#ifdef O_CLOEXEC - if (!(Flags & OF_ChildInherit)) - Result |= O_CLOEXEC; -#endif - - return Result; -} - -std::error_code openFile(const Twine &Name, int &ResultFD, - CreationDisposition Disp, FileAccess Access, - OpenFlags Flags, unsigned Mode) { - int OpenFlags = nativeOpenFlags(Disp, Flags, Access); - - SmallString<128> Storage; - StringRef P = Name.toNullTerminatedStringRef(Storage); - // Call ::open in a lambda to avoid overload resolution in RetryAfterSignal - // when open is overloaded, such as in Bionic. - auto Open = [&]() { return ::open(P.begin(), OpenFlags, Mode); }; - if ((ResultFD = sys::RetryAfterSignal(-1, Open)) < 0) - return std::error_code(errno, std::generic_category()); -#ifndef O_CLOEXEC - if (!(Flags & OF_ChildInherit)) { - int r = fcntl(ResultFD, F_SETFD, FD_CLOEXEC); - (void)r; - assert(r == 0 && "fcntl(F_SETFD, FD_CLOEXEC) failed"); - } -#endif - return std::error_code(); -} - -Expected openNativeFile(const Twine &Name, CreationDisposition Disp, - FileAccess Access, OpenFlags Flags, - unsigned Mode) { - - int FD; - std::error_code EC = openFile(Name, FD, Disp, Access, Flags, Mode); - if (EC) - return errorCodeToError(EC); - return FD; -} - -std::error_code openFileForRead(const Twine &Name, int &ResultFD, - OpenFlags Flags, - SmallVectorImpl *RealPath) { - std::error_code EC = - openFile(Name, ResultFD, CD_OpenExisting, FA_Read, Flags, 0666); - if (EC) - return EC; - - // Attempt to get the real name of the file, if the user asked - if(!RealPath) - return std::error_code(); - RealPath->clear(); -#if defined(F_GETPATH) - // When F_GETPATH is available, it is the quickest way to get - // the real path name. - char Buffer[MAXPATHLEN]; - if (::fcntl(ResultFD, F_GETPATH, Buffer) != -1) - RealPath->append(Buffer, Buffer + strlen(Buffer)); -#else - char Buffer[PATH_MAX]; - if (hasProcSelfFD()) { - char ProcPath[64]; - snprintf(ProcPath, sizeof(ProcPath), "/proc/self/fd/%d", ResultFD); - ssize_t CharCount = ::readlink(ProcPath, Buffer, sizeof(Buffer)); - if (CharCount > 0) - RealPath->append(Buffer, Buffer + CharCount); - } else { - SmallString<128> Storage; - StringRef P = Name.toNullTerminatedStringRef(Storage); - - // Use ::realpath to get the real path name - if (::realpath(P.begin(), Buffer) != nullptr) - RealPath->append(Buffer, Buffer + strlen(Buffer)); - } -#endif - return std::error_code(); -} - -Expected openNativeFileForRead(const Twine &Name, OpenFlags Flags, - SmallVectorImpl *RealPath) { - file_t ResultFD; - std::error_code EC = openFileForRead(Name, ResultFD, Flags, RealPath); - if (EC) - return errorCodeToError(EC); - return ResultFD; -} - -void closeFile(file_t &F) { - ::close(F); - F = kInvalidFile; -} - -} // end namespace fs - -namespace path { - -bool home_directory(SmallVectorImpl &result) { - char *RequestedDir = getenv("HOME"); - if (!RequestedDir) { - struct passwd *pw = getpwuid(getuid()); - if (pw && pw->pw_dir) - RequestedDir = pw->pw_dir; - } - if (!RequestedDir) - return false; - - result.clear(); - result.append(RequestedDir, RequestedDir + strlen(RequestedDir)); - return true; -} - -static bool getDarwinConfDir(bool TempDir, SmallVectorImpl &Result) { - #if defined(_CS_DARWIN_USER_TEMP_DIR) && defined(_CS_DARWIN_USER_CACHE_DIR) - // On Darwin, use DARWIN_USER_TEMP_DIR or DARWIN_USER_CACHE_DIR. - // macros defined in on darwin >= 9 - int ConfName = TempDir ? _CS_DARWIN_USER_TEMP_DIR - : _CS_DARWIN_USER_CACHE_DIR; - size_t ConfLen = confstr(ConfName, nullptr, 0); - if (ConfLen > 0) { - do { - Result.resize(ConfLen); - ConfLen = confstr(ConfName, Result.data(), Result.size()); - } while (ConfLen > 0 && ConfLen != Result.size()); - - if (ConfLen > 0) { - assert(Result.back() == 0); - Result.pop_back(); - return true; - } - - Result.clear(); - } - #endif - return false; -} - -static const char *getEnvTempDir() { - // Check whether the temporary directory is specified by an environment - // variable. - const char *EnvironmentVariables[] = {"TMPDIR", "TMP", "TEMP", "TEMPDIR"}; - for (const char *Env : EnvironmentVariables) { - if (const char *Dir = std::getenv(Env)) - return Dir; - } - - return nullptr; -} - -static const char *getDefaultTempDir(bool ErasedOnReboot) { -#ifdef P_tmpdir - if ((bool)P_tmpdir) - return P_tmpdir; -#endif - - if (ErasedOnReboot) - return "/tmp"; - return "/var/tmp"; -} - -void system_temp_directory(bool ErasedOnReboot, SmallVectorImpl &Result) { - Result.clear(); - - if (ErasedOnReboot) { - // There is no env variable for the cache directory. - if (const char *RequestedDir = getEnvTempDir()) { - Result.append(RequestedDir, RequestedDir + strlen(RequestedDir)); - return; - } - } - - if (getDarwinConfDir(ErasedOnReboot, Result)) - return; - - const char *RequestedDir = getDefaultTempDir(ErasedOnReboot); - Result.append(RequestedDir, RequestedDir + strlen(RequestedDir)); -} - -} // end namespace path -} // end namespace sys -} // end namespace wpi diff --git a/wpiutil/src/main/native/cpp/llvm/Windows/Path.inc b/wpiutil/src/main/native/cpp/llvm/Windows/Path.inc deleted file mode 100644 index e9b5280f06..0000000000 --- a/wpiutil/src/main/native/cpp/llvm/Windows/Path.inc +++ /dev/null @@ -1,971 +0,0 @@ -//===- llvm/Support/Windows/Path.inc - Windows Path Impl --------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file implements the Windows specific implementation of the Path API. -// -//===----------------------------------------------------------------------===// - -//===----------------------------------------------------------------------===// -//=== WARNING: Implementation here must contain only generic Windows code that -//=== is guaranteed to work on *all* Windows variants. -//===----------------------------------------------------------------------===// - -#include "wpi/STLExtras.h" -#include "wpi/ConvertUTF.h" -#include "wpi/WindowsError.h" -#include -#include -#include -#include - -// These two headers must be included last, and make sure shlobj is required -// after Windows.h to make sure it picks up our definition of _WIN32_WINNT -#include "WindowsSupport.h" -#include -#include - -#undef max - -// MinGW doesn't define this. -#ifndef _ERRNO_T_DEFINED -#define _ERRNO_T_DEFINED -typedef int errno_t; -#endif - -#ifdef _MSC_VER -# pragma comment(lib, "shell32.lib") -# pragma comment(lib, "ole32.lib") -#pragma warning(push) -#pragma warning(disable: 4244 4267 4146) -#endif - -using namespace wpi; - -using wpi::sys::windows::UTF8ToUTF16; -using wpi::sys::windows::CurCPToUTF16; -using wpi::sys::windows::UTF16ToUTF8; -using wpi::sys::path::widenPath; - -static bool is_separator(const wchar_t value) { - switch (value) { - case L'\\': - case L'/': - return true; - default: - return false; - } -} - -namespace wpi { -namespace sys { -namespace path { - -// Convert a UTF-8 path to UTF-16. Also, if the absolute equivalent of the -// path is longer than CreateDirectory can tolerate, make it absolute and -// prefixed by '\\?\'. -std::error_code widenPath(const Twine &Path8, - SmallVectorImpl &Path16) { - const size_t MaxDirLen = MAX_PATH - 12; // Must leave room for 8.3 filename. - - // Several operations would convert Path8 to SmallString; more efficient to - // do it once up front. - SmallString<128> Path8Str; - Path8.toVector(Path8Str); - - // If we made this path absolute, how much longer would it get? - size_t CurPathLen; - if (wpi::sys::path::is_absolute(Twine(Path8Str))) - CurPathLen = 0; // No contribution from current_path needed. - else { - CurPathLen = ::GetCurrentDirectoryW(0, NULL); - if (CurPathLen == 0) - return mapWindowsError(::GetLastError()); - } - - // Would the absolute path be longer than our limit? - if ((Path8Str.size() + CurPathLen) >= MaxDirLen && - !Path8Str.startswith("\\\\?\\")) { - SmallString<2*MAX_PATH> FullPath("\\\\?\\"); - if (CurPathLen) { - SmallString<80> CurPath; - if (std::error_code EC = wpi::sys::fs::current_path(CurPath)) - return EC; - FullPath.append(CurPath); - } - // Traverse the requested path, canonicalizing . and .. (because the \\?\ - // prefix is documented to treat them as real components). Ignore - // separators, which can be returned from the iterator if the path has a - // drive name. We don't need to call native() on the result since append() - // always attaches preferred_separator. - for (wpi::sys::path::const_iterator I = wpi::sys::path::begin(Path8Str), - E = wpi::sys::path::end(Path8Str); - I != E; ++I) { - if (I->size() == 1 && is_separator((*I)[0])) - continue; - if (I->size() == 1 && *I == ".") - continue; - if (I->size() == 2 && *I == "..") - wpi::sys::path::remove_filename(FullPath); - else - wpi::sys::path::append(FullPath, *I); - } - return UTF8ToUTF16(FullPath, Path16); - } - - // Just use the caller's original path. - return UTF8ToUTF16(Path8Str, Path16); -} -} // end namespace path - -namespace fs { - -const file_t kInvalidFile = INVALID_HANDLE_VALUE; - -UniqueID file_status::getUniqueID() const { - // The file is uniquely identified by the volume serial number along - // with the 64-bit file identifier. - uint64_t FileID = (static_cast(FileIndexHigh) << 32ULL) | - static_cast(FileIndexLow); - - return UniqueID(VolumeSerialNumber, FileID); -} - -TimePoint<> basic_file_status::getLastAccessedTime() const { - FILETIME Time; - Time.dwLowDateTime = LastAccessedTimeLow; - Time.dwHighDateTime = LastAccessedTimeHigh; - return toTimePoint(Time); -} - -TimePoint<> basic_file_status::getLastModificationTime() const { - FILETIME Time; - Time.dwLowDateTime = LastWriteTimeLow; - Time.dwHighDateTime = LastWriteTimeHigh; - return toTimePoint(Time); -} - -uint32_t file_status::getLinkCount() const { - return NumLinks; -} - -std::error_code current_path(SmallVectorImpl &result) { - SmallVector cur_path; - DWORD len = MAX_PATH; - - do { - cur_path.reserve(len); - len = ::GetCurrentDirectoryW(cur_path.capacity(), cur_path.data()); - - // A zero return value indicates a failure other than insufficient space. - if (len == 0) - return mapWindowsError(::GetLastError()); - - // If there's insufficient space, the len returned is larger than the len - // given. - } while (len > cur_path.capacity()); - - // On success, GetCurrentDirectoryW returns the number of characters not - // including the null-terminator. - cur_path.set_size(len); - return UTF16ToUTF8(cur_path.begin(), cur_path.size(), result); -} - -static std::error_code realPathFromHandle(HANDLE H, - SmallVectorImpl &Buffer) { - DWORD CountChars = ::GetFinalPathNameByHandleW( - H, Buffer.begin(), Buffer.capacity() - 1, FILE_NAME_NORMALIZED); - if (CountChars > Buffer.capacity()) { - // The buffer wasn't big enough, try again. In this case the return value - // *does* indicate the size of the null terminator. - Buffer.reserve(CountChars); - CountChars = ::GetFinalPathNameByHandleW( - H, Buffer.data(), Buffer.capacity() - 1, FILE_NAME_NORMALIZED); - } - if (CountChars == 0) - return mapWindowsError(GetLastError()); - Buffer.set_size(CountChars); - return std::error_code(); -} - -static std::error_code realPathFromHandle(HANDLE H, - SmallVectorImpl &RealPath) { - RealPath.clear(); - SmallVector Buffer; - if (std::error_code EC = realPathFromHandle(H, Buffer)) - return EC; - - const wchar_t *Data = Buffer.data(); - DWORD CountChars = Buffer.size(); - if (CountChars >= 4) { - if (0 == ::memcmp(Data, L"\\\\?\\", 8)) { - CountChars -= 4; - Data += 4; - } - } - - // Convert the result from UTF-16 to UTF-8. - return UTF16ToUTF8(Data, CountChars, RealPath); -} - -static std::error_code setDeleteDisposition(HANDLE Handle, bool Delete) { - FILE_DISPOSITION_INFO Disposition; - Disposition.DeleteFile = Delete; - if (!SetFileInformationByHandle(Handle, FileDispositionInfo, &Disposition, - sizeof(Disposition))) - return mapWindowsError(::GetLastError()); - return std::error_code(); -} - -std::error_code access(const Twine &Path, AccessMode Mode) { - SmallVector PathUtf16; - - if (std::error_code EC = widenPath(Path, PathUtf16)) - return EC; - - DWORD Attributes = ::GetFileAttributesW(PathUtf16.begin()); - - if (Attributes == INVALID_FILE_ATTRIBUTES) { - // See if the file didn't actually exist. - DWORD LastError = ::GetLastError(); - if (LastError != ERROR_FILE_NOT_FOUND && - LastError != ERROR_PATH_NOT_FOUND) - return mapWindowsError(LastError); - return errc::no_such_file_or_directory; - } - - if (Mode == AccessMode::Write && (Attributes & FILE_ATTRIBUTE_READONLY)) - return errc::permission_denied; - - return std::error_code(); -} - -bool equivalent(file_status A, file_status B) { - assert(status_known(A) && status_known(B)); - return A.FileIndexHigh == B.FileIndexHigh && - A.FileIndexLow == B.FileIndexLow && - A.FileSizeHigh == B.FileSizeHigh && - A.FileSizeLow == B.FileSizeLow && - A.LastAccessedTimeHigh == B.LastAccessedTimeHigh && - A.LastAccessedTimeLow == B.LastAccessedTimeLow && - A.LastWriteTimeHigh == B.LastWriteTimeHigh && - A.LastWriteTimeLow == B.LastWriteTimeLow && - A.VolumeSerialNumber == B.VolumeSerialNumber; -} - -std::error_code equivalent(const Twine &A, const Twine &B, bool &result) { - file_status fsA, fsB; - if (std::error_code ec = status(A, fsA)) - return ec; - if (std::error_code ec = status(B, fsB)) - return ec; - result = equivalent(fsA, fsB); - return std::error_code(); -} - -static bool isReservedName(StringRef path) { - // This list of reserved names comes from MSDN, at: - // http://msdn.microsoft.com/en-us/library/aa365247%28v=vs.85%29.aspx - static const char *const sReservedNames[] = { "nul", "con", "prn", "aux", - "com1", "com2", "com3", "com4", - "com5", "com6", "com7", "com8", - "com9", "lpt1", "lpt2", "lpt3", - "lpt4", "lpt5", "lpt6", "lpt7", - "lpt8", "lpt9" }; - - // First, check to see if this is a device namespace, which always - // starts with \\.\, since device namespaces are not legal file paths. - if (path.startswith("\\\\.\\")) - return true; - - // Then compare against the list of ancient reserved names. - for (size_t i = 0; i < array_lengthof(sReservedNames); ++i) { - if (path.equals_lower(sReservedNames[i])) - return true; - } - - // The path isn't what we consider reserved. - return false; -} - -static file_type file_type_from_attrs(DWORD Attrs) { - return (Attrs & FILE_ATTRIBUTE_DIRECTORY) ? file_type::directory_file - : file_type::regular_file; -} - -static perms perms_from_attrs(DWORD Attrs) { - return (Attrs & FILE_ATTRIBUTE_READONLY) ? (all_read | all_exe) : all_all; -} - -static std::error_code getStatus(HANDLE FileHandle, file_status &Result) { - if (FileHandle == INVALID_HANDLE_VALUE) - goto handle_status_error; - - switch (::GetFileType(FileHandle)) { - default: - Result = file_status(file_type::type_unknown); - return std::error_code(); - case FILE_TYPE_UNKNOWN: { - DWORD Err = ::GetLastError(); - if (Err != NO_ERROR) - return mapWindowsError(Err); - Result = file_status(file_type::type_unknown); - return std::error_code(); - } - case FILE_TYPE_DISK: - break; - case FILE_TYPE_CHAR: - Result = file_status(file_type::character_file); - return std::error_code(); - case FILE_TYPE_PIPE: - Result = file_status(file_type::fifo_file); - return std::error_code(); - } - - BY_HANDLE_FILE_INFORMATION Info; - if (!::GetFileInformationByHandle(FileHandle, &Info)) - goto handle_status_error; - - Result = file_status( - file_type_from_attrs(Info.dwFileAttributes), - perms_from_attrs(Info.dwFileAttributes), Info.nNumberOfLinks, - Info.ftLastAccessTime.dwHighDateTime, Info.ftLastAccessTime.dwLowDateTime, - Info.ftLastWriteTime.dwHighDateTime, Info.ftLastWriteTime.dwLowDateTime, - Info.dwVolumeSerialNumber, Info.nFileSizeHigh, Info.nFileSizeLow, - Info.nFileIndexHigh, Info.nFileIndexLow); - return std::error_code(); - -handle_status_error: - DWORD LastError = ::GetLastError(); - if (LastError == ERROR_FILE_NOT_FOUND || - LastError == ERROR_PATH_NOT_FOUND) - Result = file_status(file_type::file_not_found); - else if (LastError == ERROR_SHARING_VIOLATION) - Result = file_status(file_type::type_unknown); - else - Result = file_status(file_type::status_error); - return mapWindowsError(LastError); -} - -std::error_code status(const Twine &path, file_status &result, bool Follow) { - SmallString<128> path_storage; - SmallVector path_utf16; - - StringRef path8 = path.toStringRef(path_storage); - if (isReservedName(path8)) { - result = file_status(file_type::character_file); - return std::error_code(); - } - - if (std::error_code ec = widenPath(path8, path_utf16)) - return ec; - - DWORD attr = ::GetFileAttributesW(path_utf16.begin()); - if (attr == INVALID_FILE_ATTRIBUTES) - return getStatus(INVALID_HANDLE_VALUE, result); - - DWORD Flags = FILE_FLAG_BACKUP_SEMANTICS; - // Handle reparse points. - if (!Follow && (attr & FILE_ATTRIBUTE_REPARSE_POINT)) - Flags |= FILE_FLAG_OPEN_REPARSE_POINT; - - ScopedFileHandle h( - ::CreateFileW(path_utf16.begin(), 0, // Attributes only. - FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, - NULL, OPEN_EXISTING, Flags, 0)); - if (!h) - return getStatus(INVALID_HANDLE_VALUE, result); - - return getStatus(h, result); -} - -std::error_code status(int FD, file_status &Result) { - HANDLE FileHandle = reinterpret_cast(_get_osfhandle(FD)); - return getStatus(FileHandle, Result); -} - -std::error_code mapped_file_region::init(int FD, uint64_t Offset, - mapmode Mode) { - this->Mode = Mode; - HANDLE OrigFileHandle = reinterpret_cast(_get_osfhandle(FD)); - if (OrigFileHandle == INVALID_HANDLE_VALUE) - return make_error_code(errc::bad_file_descriptor); - - DWORD flprotect; - switch (Mode) { - case readonly: flprotect = PAGE_READONLY; break; - case readwrite: flprotect = PAGE_READWRITE; break; - case priv: flprotect = PAGE_WRITECOPY; break; - } - - HANDLE FileMappingHandle = - ::CreateFileMappingW(OrigFileHandle, 0, flprotect, - Hi_32(Size), - Lo_32(Size), - 0); - if (FileMappingHandle == NULL) { - std::error_code ec = mapWindowsError(GetLastError()); - return ec; - } - - DWORD dwDesiredAccess; - switch (Mode) { - case readonly: dwDesiredAccess = FILE_MAP_READ; break; - case readwrite: dwDesiredAccess = FILE_MAP_WRITE; break; - case priv: dwDesiredAccess = FILE_MAP_COPY; break; - } - Mapping = ::MapViewOfFile(FileMappingHandle, - dwDesiredAccess, - Offset >> 32, - Offset & 0xffffffff, - Size); - if (Mapping == NULL) { - std::error_code ec = mapWindowsError(GetLastError()); - ::CloseHandle(FileMappingHandle); - return ec; - } - - if (Size == 0) { - MEMORY_BASIC_INFORMATION mbi; - SIZE_T Result = VirtualQuery(Mapping, &mbi, sizeof(mbi)); - if (Result == 0) { - std::error_code ec = mapWindowsError(GetLastError()); - ::UnmapViewOfFile(Mapping); - ::CloseHandle(FileMappingHandle); - return ec; - } - Size = mbi.RegionSize; - } - - // Close the file mapping handle, as it's kept alive by the file mapping. But - // neither the file mapping nor the file mapping handle keep the file handle - // alive, so we need to keep a reference to the file in case all other handles - // are closed and the file is deleted, which may cause invalid data to be read - // from the file. - ::CloseHandle(FileMappingHandle); - if (!::DuplicateHandle(::GetCurrentProcess(), OrigFileHandle, - ::GetCurrentProcess(), &FileHandle, 0, 0, - DUPLICATE_SAME_ACCESS)) { - std::error_code ec = mapWindowsError(GetLastError()); - ::UnmapViewOfFile(Mapping); - return ec; - } - - return std::error_code(); -} - -mapped_file_region::mapped_file_region(int fd, mapmode mode, size_t length, - uint64_t offset, std::error_code &ec) - : Size(length), Mapping() { - ec = init(fd, offset, mode); - if (ec) - Mapping = 0; -} - -static bool hasFlushBufferKernelBug() { - static bool Ret{GetWindowsOSVersion() < wpi::VersionTuple(10, 0, 0, 17763)}; - return Ret; -} - -static bool isEXE(StringRef Magic) { - static const char PEMagic[] = {'P', 'E', '\0', '\0'}; - if (Magic.startswith(StringRef("MZ")) && Magic.size() >= 0x3c + 4) { - uint32_t off = read32le(Magic.data() + 0x3c); - // PE/COFF file, either EXE or DLL. - if (Magic.substr(off).startswith(StringRef(PEMagic, sizeof(PEMagic)))) - return true; - } - return false; -} - -mapped_file_region::~mapped_file_region() { - if (Mapping) { - - bool Exe = isEXE(StringRef((char *)Mapping, Size)); - - ::UnmapViewOfFile(Mapping); - - if (Mode == mapmode::readwrite && Exe && hasFlushBufferKernelBug()) { - // There is a Windows kernel bug, the exact trigger conditions of which - // are not well understood. When triggered, dirty pages are not properly - // flushed and subsequent process's attempts to read a file can return - // invalid data. Calling FlushFileBuffers on the write handle is - // sufficient to ensure that this bug is not triggered. - // The bug only occurs when writing an executable and executing it right - // after, under high I/O pressure. - ::FlushFileBuffers(FileHandle); - } - - ::CloseHandle(FileHandle); - } -} - -size_t mapped_file_region::size() const { - assert(Mapping && "Mapping failed but used anyway!"); - return Size; -} - -char *mapped_file_region::data() const { - assert(Mapping && "Mapping failed but used anyway!"); - return reinterpret_cast(Mapping); -} - -const char *mapped_file_region::const_data() const { - assert(Mapping && "Mapping failed but used anyway!"); - return reinterpret_cast(Mapping); -} - -int mapped_file_region::alignment() { - SYSTEM_INFO SysInfo; - ::GetSystemInfo(&SysInfo); - return SysInfo.dwAllocationGranularity; -} - -static basic_file_status status_from_find_data(WIN32_FIND_DATAW *FindData) { - return basic_file_status(file_type_from_attrs(FindData->dwFileAttributes), - perms_from_attrs(FindData->dwFileAttributes), - FindData->ftLastAccessTime.dwHighDateTime, - FindData->ftLastAccessTime.dwLowDateTime, - FindData->ftLastWriteTime.dwHighDateTime, - FindData->ftLastWriteTime.dwLowDateTime, - FindData->nFileSizeHigh, FindData->nFileSizeLow); -} - -std::error_code detail::directory_iterator_construct(detail::DirIterState &IT, - StringRef Path, - bool FollowSymlinks) { - SmallVector PathUTF16; - - if (std::error_code EC = widenPath(Path, PathUTF16)) - return EC; - - // Convert path to the format that Windows is happy with. - if (PathUTF16.size() > 0 && - !is_separator(PathUTF16[Path.size() - 1]) && - PathUTF16[Path.size() - 1] != L':') { - PathUTF16.push_back(L'\\'); - PathUTF16.push_back(L'*'); - } else { - PathUTF16.push_back(L'*'); - } - - // Get the first directory entry. - WIN32_FIND_DATAW FirstFind; - ScopedFindHandle FindHandle(::FindFirstFileExW( - c_str(PathUTF16), FindExInfoBasic, &FirstFind, FindExSearchNameMatch, - NULL, FIND_FIRST_EX_LARGE_FETCH)); - if (!FindHandle) - return mapWindowsError(::GetLastError()); - - size_t FilenameLen = ::wcslen(FirstFind.cFileName); - while ((FilenameLen == 1 && FirstFind.cFileName[0] == L'.') || - (FilenameLen == 2 && FirstFind.cFileName[0] == L'.' && - FirstFind.cFileName[1] == L'.')) - if (!::FindNextFileW(FindHandle, &FirstFind)) { - DWORD LastError = ::GetLastError(); - // Check for end. - if (LastError == ERROR_NO_MORE_FILES) - return detail::directory_iterator_destruct(IT); - return mapWindowsError(LastError); - } else - FilenameLen = ::wcslen(FirstFind.cFileName); - - // Construct the current directory entry. - SmallString<128> DirectoryEntryNameUTF8; - if (std::error_code EC = - UTF16ToUTF8(FirstFind.cFileName, ::wcslen(FirstFind.cFileName), - DirectoryEntryNameUTF8)) - return EC; - - IT.IterationHandle = intptr_t(FindHandle.take()); - SmallString<128> DirectoryEntryPath(Path); - path::append(DirectoryEntryPath, DirectoryEntryNameUTF8); - IT.CurrentEntry = - directory_entry(DirectoryEntryPath, FollowSymlinks, - file_type_from_attrs(FirstFind.dwFileAttributes), - status_from_find_data(&FirstFind)); - - return std::error_code(); -} - -std::error_code detail::directory_iterator_destruct(detail::DirIterState &IT) { - if (IT.IterationHandle != 0) - // Closes the handle if it's valid. - ScopedFindHandle close(HANDLE(IT.IterationHandle)); - IT.IterationHandle = 0; - IT.CurrentEntry = directory_entry(); - return std::error_code(); -} - -std::error_code detail::directory_iterator_increment(detail::DirIterState &IT) { - WIN32_FIND_DATAW FindData; - if (!::FindNextFileW(HANDLE(IT.IterationHandle), &FindData)) { - DWORD LastError = ::GetLastError(); - // Check for end. - if (LastError == ERROR_NO_MORE_FILES) - return detail::directory_iterator_destruct(IT); - return mapWindowsError(LastError); - } - - size_t FilenameLen = ::wcslen(FindData.cFileName); - if ((FilenameLen == 1 && FindData.cFileName[0] == L'.') || - (FilenameLen == 2 && FindData.cFileName[0] == L'.' && - FindData.cFileName[1] == L'.')) - return directory_iterator_increment(IT); - - SmallString<128> DirectoryEntryPathUTF8; - if (std::error_code EC = - UTF16ToUTF8(FindData.cFileName, ::wcslen(FindData.cFileName), - DirectoryEntryPathUTF8)) - return EC; - - IT.CurrentEntry.replace_filename( - Twine(DirectoryEntryPathUTF8), - file_type_from_attrs(FindData.dwFileAttributes), - status_from_find_data(&FindData)); - return std::error_code(); -} - -ErrorOr directory_entry::status() const { - return Status; -} - -static std::error_code nativeFileToFd(Expected H, int &ResultFD, - OpenFlags Flags) { - int CrtOpenFlags = 0; - if (Flags & OF_Append) - CrtOpenFlags |= _O_APPEND; - - if (Flags & OF_Text) - CrtOpenFlags |= _O_TEXT; - - ResultFD = -1; - if (!H) - return errorToErrorCode(H.takeError()); - - ResultFD = ::_open_osfhandle(intptr_t(*H), CrtOpenFlags); - if (ResultFD == -1) { - ::CloseHandle(*H); - return mapWindowsError(ERROR_INVALID_HANDLE); - } - return std::error_code(); -} - -static DWORD nativeDisposition(CreationDisposition Disp, OpenFlags Flags) { - // This is a compatibility hack. Really we should respect the creation - // disposition, but a lot of old code relied on the implicit assumption that - // OF_Append implied it would open an existing file. Since the disposition is - // now explicit and defaults to CD_CreateAlways, this assumption would cause - // any usage of OF_Append to append to a new file, even if the file already - // existed. A better solution might have two new creation dispositions: - // CD_AppendAlways and CD_AppendNew. This would also address the problem of - // OF_Append being used on a read-only descriptor, which doesn't make sense. - if (Flags & OF_Append) - return OPEN_ALWAYS; - - switch (Disp) { - case CD_CreateAlways: - return CREATE_ALWAYS; - case CD_CreateNew: - return CREATE_NEW; - case CD_OpenAlways: - return OPEN_ALWAYS; - case CD_OpenExisting: - return OPEN_EXISTING; - } - wpi_unreachable("unreachable!"); -} - -static DWORD nativeAccess(FileAccess Access, OpenFlags Flags) { - DWORD Result = 0; - if (Access & FA_Read) - Result |= GENERIC_READ; - if (Access & FA_Write) - Result |= GENERIC_WRITE; - if (Flags & OF_Delete) - Result |= DELETE; - if (Flags & OF_UpdateAtime) - Result |= FILE_WRITE_ATTRIBUTES; - return Result; -} - -static std::error_code openNativeFileInternal(const Twine &Name, - file_t &ResultFile, DWORD Disp, - DWORD Access, DWORD Flags, - bool Inherit = false) { - SmallVector PathUTF16; - if (std::error_code EC = widenPath(Name, PathUTF16)) - return EC; - - SECURITY_ATTRIBUTES SA; - SA.nLength = sizeof(SA); - SA.lpSecurityDescriptor = nullptr; - SA.bInheritHandle = Inherit; - - HANDLE H = - ::CreateFileW(PathUTF16.begin(), Access, - FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, &SA, - Disp, Flags, NULL); - if (H == INVALID_HANDLE_VALUE) { - DWORD LastError = ::GetLastError(); - std::error_code EC = mapWindowsError(LastError); - // Provide a better error message when trying to open directories. - // This only runs if we failed to open the file, so there is probably - // no performances issues. - if (LastError != ERROR_ACCESS_DENIED) - return EC; - if (is_directory(Name)) - return make_error_code(errc::is_a_directory); - return EC; - } - ResultFile = H; - return std::error_code(); -} - -Expected openNativeFile(const Twine &Name, CreationDisposition Disp, - FileAccess Access, OpenFlags Flags, - unsigned Mode) { - // Verify that we don't have both "append" and "excl". - assert((!(Disp == CD_CreateNew) || !(Flags & OF_Append)) && - "Cannot specify both 'CreateNew' and 'Append' file creation flags!"); - - DWORD NativeDisp = nativeDisposition(Disp, Flags); - DWORD NativeAccess = nativeAccess(Access, Flags); - - bool Inherit = false; - if (Flags & OF_ChildInherit) - Inherit = true; - - file_t Result; - std::error_code EC = openNativeFileInternal( - Name, Result, NativeDisp, NativeAccess, FILE_ATTRIBUTE_NORMAL, Inherit); - if (EC) - return errorCodeToError(EC); - - if (Flags & OF_UpdateAtime) { - FILETIME FileTime; - SYSTEMTIME SystemTime; - GetSystemTime(&SystemTime); - if (SystemTimeToFileTime(&SystemTime, &FileTime) == 0 || - SetFileTime(Result, NULL, &FileTime, NULL) == 0) { - DWORD LastError = ::GetLastError(); - ::CloseHandle(Result); - return errorCodeToError(mapWindowsError(LastError)); - } - } - - if (Flags & OF_Delete) { - if ((EC = setDeleteDisposition(Result, true))) { - ::CloseHandle(Result); - return errorCodeToError(EC); - } - } - return Result; -} - -std::error_code openFile(const Twine &Name, int &ResultFD, - CreationDisposition Disp, FileAccess Access, - OpenFlags Flags, unsigned int Mode) { - Expected Result = openNativeFile(Name, Disp, Access, Flags); - if (!Result) - return errorToErrorCode(Result.takeError()); - - return nativeFileToFd(*Result, ResultFD, Flags); -} - -static std::error_code directoryRealPath(const Twine &Name, - SmallVectorImpl &RealPath) { - file_t File; - std::error_code EC = openNativeFileInternal( - Name, File, OPEN_EXISTING, GENERIC_READ, FILE_FLAG_BACKUP_SEMANTICS); - if (EC) - return EC; - - EC = realPathFromHandle(File, RealPath); - ::CloseHandle(File); - return EC; -} - -std::error_code openFileForRead(const Twine &Name, int &ResultFD, - OpenFlags Flags, - SmallVectorImpl *RealPath) { - Expected NativeFile = openNativeFileForRead(Name, Flags, RealPath); - return nativeFileToFd(std::move(NativeFile), ResultFD, OF_None); -} - -Expected openNativeFileForRead(const Twine &Name, OpenFlags Flags, - SmallVectorImpl *RealPath) { - Expected Result = - openNativeFile(Name, CD_OpenExisting, FA_Read, Flags); - - // Fetch the real name of the file, if the user asked - if (Result && RealPath) - realPathFromHandle(*Result, *RealPath); - - return Result; -} - -void closeFile(file_t &F) { - ::CloseHandle(F); - F = kInvalidFile; -} - - -} // end namespace fs - -namespace path { -static bool getKnownFolderPath(KNOWNFOLDERID folderId, - SmallVectorImpl &result) { - wchar_t *path = nullptr; - if (::SHGetKnownFolderPath(folderId, KF_FLAG_CREATE, nullptr, &path) != S_OK) - return false; - - bool ok = !UTF16ToUTF8(path, ::wcslen(path), result); - ::CoTaskMemFree(path); - return ok; -} - -bool home_directory(SmallVectorImpl &result) { - return getKnownFolderPath(FOLDERID_Profile, result); -} - -static bool getTempDirEnvVar(const wchar_t *Var, SmallVectorImpl &Res) { - SmallVector Buf; - size_t Size = 1024; - do { - Buf.reserve(Size); - Size = GetEnvironmentVariableW(Var, Buf.data(), Buf.capacity()); - if (Size == 0) - return false; - - // Try again with larger buffer. - } while (Size > Buf.capacity()); - Buf.set_size(Size); - - return !windows::UTF16ToUTF8(Buf.data(), Size, Res); -} - -static bool getTempDirEnvVar(SmallVectorImpl &Res) { - const wchar_t *EnvironmentVariables[] = {L"TMP", L"TEMP", L"USERPROFILE"}; - for (auto *Env : EnvironmentVariables) { - if (getTempDirEnvVar(Env, Res)) - return true; - } - return false; -} - -void system_temp_directory(bool ErasedOnReboot, SmallVectorImpl &Result) { - (void)ErasedOnReboot; - Result.clear(); - - // Check whether the temporary directory is specified by an environment var. - // This matches GetTempPath logic to some degree. GetTempPath is not used - // directly as it cannot handle evn var longer than 130 chars on Windows 7 - // (fixed on Windows 8). - if (getTempDirEnvVar(Result)) { - assert(!Result.empty() && "Unexpected empty path"); - native(Result); // Some Unix-like shells use Unix path separator in $TMP. - fs::make_absolute(Result); // Make it absolute if not already. - return; - } - - // Fall back to a system default. - const char *DefaultResult = "C:\\Temp"; - Result.append(DefaultResult, DefaultResult + strlen(DefaultResult)); -} -} // end namespace path - -namespace windows { -std::error_code CodePageToUTF16(unsigned codepage, - wpi::StringRef original, - wpi::SmallVectorImpl &utf16) { - if (!original.empty()) { - int len = ::MultiByteToWideChar(codepage, MB_ERR_INVALID_CHARS, original.begin(), - original.size(), utf16.begin(), 0); - - if (len == 0) { - return mapWindowsError(::GetLastError()); - } - - utf16.reserve(len + 1); - utf16.set_size(len); - - len = ::MultiByteToWideChar(codepage, MB_ERR_INVALID_CHARS, original.begin(), - original.size(), utf16.begin(), utf16.size()); - - if (len == 0) { - return mapWindowsError(::GetLastError()); - } - } - - // Make utf16 null terminated. - utf16.push_back(0); - utf16.pop_back(); - - return std::error_code(); -} - -std::error_code UTF8ToUTF16(wpi::StringRef utf8, - wpi::SmallVectorImpl &utf16) { - return CodePageToUTF16(CP_UTF8, utf8, utf16); -} - -std::error_code CurCPToUTF16(wpi::StringRef curcp, - wpi::SmallVectorImpl &utf16) { - return CodePageToUTF16(CP_ACP, curcp, utf16); -} - -static -std::error_code UTF16ToCodePage(unsigned codepage, const wchar_t *utf16, - size_t utf16_len, - wpi::SmallVectorImpl &converted) { - if (utf16_len) { - // Get length. - int len = ::WideCharToMultiByte(codepage, 0, utf16, utf16_len, converted.begin(), - 0, NULL, NULL); - - if (len == 0) { - return mapWindowsError(::GetLastError()); - } - - converted.reserve(len); - converted.set_size(len); - - // Now do the actual conversion. - len = ::WideCharToMultiByte(codepage, 0, utf16, utf16_len, converted.data(), - converted.size(), NULL, NULL); - - if (len == 0) { - return mapWindowsError(::GetLastError()); - } - } - - // Make the new string null terminated. - converted.push_back(0); - converted.pop_back(); - - return std::error_code(); -} - -std::error_code UTF16ToUTF8(const wchar_t *utf16, size_t utf16_len, - wpi::SmallVectorImpl &utf8) { - return UTF16ToCodePage(CP_UTF8, utf16, utf16_len, utf8); -} - -std::error_code UTF16ToCurCP(const wchar_t *utf16, size_t utf16_len, - wpi::SmallVectorImpl &curcp) { - return UTF16ToCodePage(CP_ACP, utf16, utf16_len, curcp); -} - -} // end namespace windows -} // end namespace sys -} // end namespace wpi - -#ifdef _MSC_VER -#pragma warning(pop) -#endif diff --git a/wpiutil/src/main/native/cpp/llvm/raw_ostream.cpp b/wpiutil/src/main/native/cpp/llvm/raw_ostream.cpp index 9f2942c56e..9c7e72dd64 100644 --- a/wpiutil/src/main/native/cpp/llvm/raw_ostream.cpp +++ b/wpiutil/src/main/native/cpp/llvm/raw_ostream.cpp @@ -22,11 +22,11 @@ #include "wpi/StringExtras.h" #include "wpi/Compiler.h" #include "wpi/ErrorHandling.h" -#include "wpi/FileSystem.h" #include "wpi/Format.h" #include "wpi/MathExtras.h" #include "wpi/NativeFormatting.h" #include "wpi/WindowsError.h" +#include "wpi/fs.h" #include #include #include @@ -487,9 +487,9 @@ void format_object_base::home() { //===----------------------------------------------------------------------===// static int getFD(StringRef Filename, std::error_code &EC, - sys::fs::CreationDisposition Disp, sys::fs::FileAccess Access, - sys::fs::OpenFlags Flags) { - assert((Access & sys::fs::FA_Write) && + fs::CreationDisposition Disp, fs::FileAccess Access, + fs::OpenFlags Flags) { + assert((Access & fs::FA_Write) && "Cannot make a raw_ostream from a read-only descriptor!"); // Handle "-" as stdout. Note that when we do this, we consider ourself @@ -498,7 +498,7 @@ static int getFD(StringRef Filename, std::error_code &EC, EC = std::error_code(); // If user requested binary then put stdout into binary mode if // possible. - if (!(Flags & sys::fs::OF_Text)) { + if (!(Flags & fs::OF_Text)) { #if defined(_WIN32) _setmode(_fileno(stdout), _O_BINARY); #endif @@ -506,11 +506,15 @@ static int getFD(StringRef Filename, std::error_code &EC, return STDOUT_FILENO; } - int FD; - if (Access & sys::fs::FA_Read) - EC = sys::fs::openFileForReadWrite(Filename, FD, Disp, Flags); - else - EC = sys::fs::openFileForWrite(Filename, FD, Disp, Flags); + fs::file_t F; + if (Access & fs::FA_Read) { + F = fs::OpenFileForReadWrite(fs::path{std::string_view{Filename.data(), Filename.size()}}, EC, Disp, Flags); + } else { + F = fs::OpenFileForWrite(fs::path{std::string_view{Filename.data(), Filename.size()}}, EC, Disp, Flags); + } + if (EC) + return -1; + int FD = fs::FileToFd(F, EC, Flags); if (EC) return -1; @@ -518,27 +522,27 @@ static int getFD(StringRef Filename, std::error_code &EC, } raw_fd_ostream::raw_fd_ostream(StringRef Filename, std::error_code &EC) - : raw_fd_ostream(Filename, EC, sys::fs::CD_CreateAlways, sys::fs::FA_Write, - sys::fs::OF_None) {} + : raw_fd_ostream(Filename, EC, fs::CD_CreateAlways, fs::FA_Write, + fs::OF_None) {} raw_fd_ostream::raw_fd_ostream(StringRef Filename, std::error_code &EC, - sys::fs::CreationDisposition Disp) - : raw_fd_ostream(Filename, EC, Disp, sys::fs::FA_Write, sys::fs::OF_None) {} + fs::CreationDisposition Disp) + : raw_fd_ostream(Filename, EC, Disp, fs::FA_Write, fs::OF_None) {} raw_fd_ostream::raw_fd_ostream(StringRef Filename, std::error_code &EC, - sys::fs::FileAccess Access) - : raw_fd_ostream(Filename, EC, sys::fs::CD_CreateAlways, Access, - sys::fs::OF_None) {} + fs::FileAccess Access) + : raw_fd_ostream(Filename, EC, fs::CD_CreateAlways, Access, + fs::OF_None) {} raw_fd_ostream::raw_fd_ostream(StringRef Filename, std::error_code &EC, - sys::fs::OpenFlags Flags) - : raw_fd_ostream(Filename, EC, sys::fs::CD_CreateAlways, sys::fs::FA_Write, + fs::OpenFlags Flags) + : raw_fd_ostream(Filename, EC, fs::CD_CreateAlways, fs::FA_Write, Flags) {} raw_fd_ostream::raw_fd_ostream(StringRef Filename, std::error_code &EC, - sys::fs::CreationDisposition Disp, - sys::fs::FileAccess Access, - sys::fs::OpenFlags Flags) + fs::CreationDisposition Disp, + fs::FileAccess Access, + fs::OpenFlags Flags) : raw_fd_ostream(getFD(Filename, EC, Disp, Access, Flags), true) {} /// FD is the file descriptor that this writes to. If ShouldClose is true, this @@ -783,7 +787,7 @@ void raw_fd_ostream::anchor() {} raw_ostream &wpi::outs() { // Set buffer settings to model stdout behavior. std::error_code EC; - static raw_fd_ostream* S = new raw_fd_ostream("-", EC, sys::fs::F_None); + static raw_fd_ostream* S = new raw_fd_ostream("-", EC, fs::F_None); assert(!EC); return *S; } diff --git a/wpiutil/src/main/native/cpp/raw_istream.cpp b/wpiutil/src/main/native/cpp/raw_istream.cpp index 0438ef550d..4cf4dfab28 100644 --- a/wpiutil/src/main/native/cpp/raw_istream.cpp +++ b/wpiutil/src/main/native/cpp/raw_istream.cpp @@ -15,9 +15,9 @@ #include #include -#include "wpi/FileSystem.h" #include "wpi/SmallVector.h" #include "wpi/StringRef.h" +#include "wpi/fs.h" #if defined(_MSC_VER) #ifndef STDIN_FILENO @@ -78,14 +78,14 @@ static int getFD(const Twine& Filename, std::error_code& EC) { return STDIN_FILENO; } - int FD; - - EC = sys::fs::openFileForRead(Filename, FD); + fs::file_t F = fs::OpenFileForRead(Filename.str(), EC); + if (EC) { + return -1; + } + int FD = fs::FileToFd(F, EC, fs::OF_None); if (EC) { return -1; } - - EC = std::error_code(); return FD; } diff --git a/wpiutil/src/main/native/include/wpi/FileSystem.h b/wpiutil/src/main/native/include/wpi/FileSystem.h deleted file mode 100644 index 668eea6325..0000000000 --- a/wpiutil/src/main/native/include/wpi/FileSystem.h +++ /dev/null @@ -1,1023 +0,0 @@ -//===- llvm/Support/FileSystem.h - File System OS Concept -------*- C++ -*-===// -// -// The LLVM Compiler Infrastructure -// -// This file is distributed under the University of Illinois Open Source -// License. See LICENSE.TXT for details. -// -//===----------------------------------------------------------------------===// -// -// This file declares the wpi::sys::fs namespace. It is designed after -// TR2/boost filesystem (v3), but modified to remove exception handling and the -// path class. -// -// All functions return an error_code and their actual work via the last out -// argument. The out argument is defined if and only if errc::success is -// returned. A function may return any error code in the generic or system -// category. However, they shall be equivalent to any error conditions listed -// in each functions respective documentation if the condition applies. [ note: -// this does not guarantee that error_code will be in the set of explicitly -// listed codes, but it does guarantee that if any of the explicitly listed -// errors occur, the correct error_code will be used ]. All functions may -// return errc::not_enough_memory if there is not enough memory to complete the -// operation. -// -//===----------------------------------------------------------------------===// - -#ifndef WPIUTIL_WPI_FILESYSTEM_H -#define WPIUTIL_WPI_FILESYSTEM_H - -#include "wpi/Chrono.h" -#include "wpi/SmallString.h" -#include "wpi/StringRef.h" -#include "wpi/Twine.h" -#include "wpi/Error.h" -#include "wpi/ErrorHandling.h" -#include "wpi/ErrorOr.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -namespace wpi { -namespace sys { -namespace fs { - -#if defined(_WIN32) -// A Win32 HANDLE is a typedef of void* -using file_t = void *; -#else -using file_t = int; -#endif - -extern const file_t kInvalidFile; - -/// An enumeration for the file system's view of the type. -enum class file_type { - status_error, - file_not_found, - regular_file, - directory_file, - symlink_file, - block_file, - character_file, - fifo_file, - socket_file, - type_unknown -}; - -/// space_info - Self explanatory. -struct space_info { - uint64_t capacity; - uint64_t free; - uint64_t available; -}; - -enum perms { - no_perms = 0, - owner_read = 0400, - owner_write = 0200, - owner_exe = 0100, - owner_all = owner_read | owner_write | owner_exe, - group_read = 040, - group_write = 020, - group_exe = 010, - group_all = group_read | group_write | group_exe, - others_read = 04, - others_write = 02, - others_exe = 01, - others_all = others_read | others_write | others_exe, - all_read = owner_read | group_read | others_read, - all_write = owner_write | group_write | others_write, - all_exe = owner_exe | group_exe | others_exe, - all_all = owner_all | group_all | others_all, - set_uid_on_exe = 04000, - set_gid_on_exe = 02000, - sticky_bit = 01000, - all_perms = all_all | set_uid_on_exe | set_gid_on_exe | sticky_bit, - perms_not_known = 0xFFFF -}; - -// Helper functions so that you can use & and | to manipulate perms bits: -inline perms operator|(perms l, perms r) { - return static_cast(static_cast(l) | - static_cast(r)); -} -inline perms operator&(perms l, perms r) { - return static_cast(static_cast(l) & - static_cast(r)); -} -inline perms &operator|=(perms &l, perms r) { - l = l | r; - return l; -} -inline perms &operator&=(perms &l, perms r) { - l = l & r; - return l; -} -inline perms operator~(perms x) { - // Avoid UB by explicitly truncating the (unsigned) ~ result. - return static_cast( - static_cast(~static_cast(x))); -} - -class UniqueID { - uint64_t Device; - uint64_t File; - -public: - UniqueID() = default; - UniqueID(uint64_t Device, uint64_t File) : Device(Device), File(File) {} - - bool operator==(const UniqueID &Other) const { - return Device == Other.Device && File == Other.File; - } - bool operator!=(const UniqueID &Other) const { return !(*this == Other); } - bool operator<(const UniqueID &Other) const { - return std::tie(Device, File) < std::tie(Other.Device, Other.File); - } - - uint64_t getDevice() const { return Device; } - uint64_t getFile() const { return File; } -}; - -/// Represents the result of a call to directory_iterator::status(). This is a -/// subset of the information returned by a regular sys::fs::status() call, and -/// represents the information provided by Windows FileFirstFile/FindNextFile. -class basic_file_status { -protected: - #ifndef _WIN32 - time_t fs_st_atime = 0; - time_t fs_st_mtime = 0; - uint32_t fs_st_atime_nsec = 0; - uint32_t fs_st_mtime_nsec = 0; - uid_t fs_st_uid = 0; - gid_t fs_st_gid = 0; - off_t fs_st_size = 0; - #else - uint32_t LastAccessedTimeHigh = 0; - uint32_t LastAccessedTimeLow = 0; - uint32_t LastWriteTimeHigh = 0; - uint32_t LastWriteTimeLow = 0; - uint32_t FileSizeHigh = 0; - uint32_t FileSizeLow = 0; - #endif - file_type Type = file_type::status_error; - perms Perms = perms_not_known; - -public: - basic_file_status() = default; - - explicit basic_file_status(file_type Type) : Type(Type) {} - - #ifndef _WIN32 - basic_file_status(file_type Type, perms Perms, time_t ATime, - uint32_t ATimeNSec, time_t MTime, uint32_t MTimeNSec, - uid_t UID, gid_t GID, off_t Size) - : fs_st_atime(ATime), fs_st_mtime(MTime), - fs_st_atime_nsec(ATimeNSec), fs_st_mtime_nsec(MTimeNSec), - fs_st_uid(UID), fs_st_gid(GID), - fs_st_size(Size), Type(Type), Perms(Perms) {} - #else - basic_file_status(file_type Type, perms Perms, uint32_t LastAccessTimeHigh, - uint32_t LastAccessTimeLow, uint32_t LastWriteTimeHigh, - uint32_t LastWriteTimeLow, uint32_t FileSizeHigh, - uint32_t FileSizeLow) - : LastAccessedTimeHigh(LastAccessTimeHigh), - LastAccessedTimeLow(LastAccessTimeLow), - LastWriteTimeHigh(LastWriteTimeHigh), - LastWriteTimeLow(LastWriteTimeLow), FileSizeHigh(FileSizeHigh), - FileSizeLow(FileSizeLow), Type(Type), Perms(Perms) {} - #endif - - // getters - file_type type() const { return Type; } - perms permissions() const { return Perms; } - - /// The file access time as reported from the underlying file system. - /// - /// Also see comments on \c getLastModificationTime() related to the precision - /// of the returned value. - TimePoint<> getLastAccessedTime() const; - - /// The file modification time as reported from the underlying file system. - /// - /// The returned value allows for nanosecond precision but the actual - /// resolution is an implementation detail of the underlying file system. - /// There is no guarantee for what kind of resolution you can expect, the - /// resolution can differ across platforms and even across mountpoints on the - /// same machine. - TimePoint<> getLastModificationTime() const; - - #ifndef _WIN32 - uint32_t getUser() const { return fs_st_uid; } - uint32_t getGroup() const { return fs_st_gid; } - uint64_t getSize() const { return fs_st_size; } - #else - uint32_t getUser() const { - return 9999; // Not applicable to Windows, so... - } - - uint32_t getGroup() const { - return 9999; // Not applicable to Windows, so... - } - - uint64_t getSize() const { - return (uint64_t(FileSizeHigh) << 32) + FileSizeLow; - } - #endif - - // setters - void type(file_type v) { Type = v; } - void permissions(perms p) { Perms = p; } -}; - -/// Represents the result of a call to sys::fs::status(). -class file_status : public basic_file_status { - friend bool equivalent(file_status A, file_status B); - - #ifndef _WIN32 - dev_t fs_st_dev = 0; - nlink_t fs_st_nlinks = 0; - ino_t fs_st_ino = 0; - #else - uint32_t NumLinks = 0; - uint32_t VolumeSerialNumber = 0; - uint32_t FileIndexHigh = 0; - uint32_t FileIndexLow = 0; - #endif - -public: - file_status() = default; - - explicit file_status(file_type Type) : basic_file_status(Type) {} - - #ifndef _WIN32 - file_status(file_type Type, perms Perms, dev_t Dev, nlink_t Links, ino_t Ino, - time_t ATime, uint32_t ATimeNSec, - time_t MTime, uint32_t MTimeNSec, - uid_t UID, gid_t GID, off_t Size) - : basic_file_status(Type, Perms, ATime, ATimeNSec, MTime, MTimeNSec, - UID, GID, Size), - fs_st_dev(Dev), fs_st_nlinks(Links), fs_st_ino(Ino) {} - #else - file_status(file_type Type, perms Perms, uint32_t LinkCount, - uint32_t LastAccessTimeHigh, uint32_t LastAccessTimeLow, - uint32_t LastWriteTimeHigh, uint32_t LastWriteTimeLow, - uint32_t VolumeSerialNumber, uint32_t FileSizeHigh, - uint32_t FileSizeLow, uint32_t FileIndexHigh, - uint32_t FileIndexLow) - : basic_file_status(Type, Perms, LastAccessTimeHigh, LastAccessTimeLow, - LastWriteTimeHigh, LastWriteTimeLow, FileSizeHigh, - FileSizeLow), - NumLinks(LinkCount), VolumeSerialNumber(VolumeSerialNumber), - FileIndexHigh(FileIndexHigh), FileIndexLow(FileIndexLow) {} - #endif - - UniqueID getUniqueID() const; - uint32_t getLinkCount() const; -}; - -/// @} -/// @name Physical Operators -/// @{ - -/// Make \a path an absolute path. -/// -/// Makes \a path absolute using the \a current_directory if it is not already. -/// An empty \a path will result in the \a current_directory. -/// -/// /absolute/path => /absolute/path -/// relative/../path => /relative/../path -/// -/// @param path A path that is modified to be an absolute path. -void make_absolute(const Twine ¤t_directory, SmallVectorImpl &path); - -/// Make \a path an absolute path. -/// -/// Makes \a path absolute using the current directory if it is not already. An -/// empty \a path will result in the current directory. -/// -/// /absolute/path => /absolute/path -/// relative/../path => /relative/../path -/// -/// @param path A path that is modified to be an absolute path. -/// @returns errc::success if \a path has been made absolute, otherwise a -/// platform-specific error_code. -std::error_code make_absolute(SmallVectorImpl &path); - -/// Get the current path. -/// -/// @param result Holds the current path on return. -/// @returns errc::success if the current path has been stored in result, -/// otherwise a platform-specific error_code. -std::error_code current_path(SmallVectorImpl &result); - -/// @} -/// @name Physical Observers -/// @{ - -/// Does file exist? -/// -/// @param status A basic_file_status previously returned from stat. -/// @returns True if the file represented by status exists, false if it does -/// not. -bool exists(const basic_file_status &status); - -enum class AccessMode { Exist, Write, Execute }; - -/// Can the file be accessed? -/// -/// @param Path Input path. -/// @returns errc::success if the path can be accessed, otherwise a -/// platform-specific error_code. -std::error_code access(const Twine &Path, AccessMode Mode); - -/// Does file exist? -/// -/// @param Path Input path. -/// @returns True if it exists, false otherwise. -inline bool exists(const Twine &Path) { - return !access(Path, AccessMode::Exist); -} - -/// Can we write this file? -/// -/// @param Path Input path. -/// @returns True if we can write to it, false otherwise. -inline bool can_write(const Twine &Path) { - return !access(Path, AccessMode::Write); -} - -/// Do file_status's represent the same thing? -/// -/// @param A Input file_status. -/// @param B Input file_status. -/// -/// assert(status_known(A) || status_known(B)); -/// -/// @returns True if A and B both represent the same file system entity, false -/// otherwise. -bool equivalent(file_status A, file_status B); - -/// Do paths represent the same thing? -/// -/// assert(status_known(A) || status_known(B)); -/// -/// @param A Input path A. -/// @param B Input path B. -/// @param result Set to true if stat(A) and stat(B) have the same device and -/// inode (or equivalent). -/// @returns errc::success if result has been successfully set, otherwise a -/// platform-specific error_code. -std::error_code equivalent(const Twine &A, const Twine &B, bool &result); - -/// Simpler version of equivalent for clients that don't need to -/// differentiate between an error and false. -inline bool equivalent(const Twine &A, const Twine &B) { - bool result; - return !equivalent(A, B, result) && result; -} - -/// Does status represent a directory? -/// -/// @param Path The path to get the type of. -/// @param Follow For symbolic links, indicates whether to return the file type -/// of the link itself, or of the target. -/// @returns A value from the file_type enumeration indicating the type of file. -file_type get_file_type(const Twine &Path, bool Follow = true); - -/// Does status represent a directory? -/// -/// @param status A basic_file_status previously returned from status. -/// @returns status.type() == file_type::directory_file. -bool is_directory(const basic_file_status &status); - -/// Is path a directory? -/// -/// @param path Input path. -/// @param result Set to true if \a path is a directory (after following -/// symlinks, false if it is not. Undefined otherwise. -/// @returns errc::success if result has been successfully set, otherwise a -/// platform-specific error_code. -std::error_code is_directory(const Twine &path, bool &result); - -/// Simpler version of is_directory for clients that don't need to -/// differentiate between an error and false. -inline bool is_directory(const Twine &Path) { - bool Result; - return !is_directory(Path, Result) && Result; -} - -/// Does status represent a regular file? -/// -/// @param status A basic_file_status previously returned from status. -/// @returns status_known(status) && status.type() == file_type::regular_file. -bool is_regular_file(const basic_file_status &status); - -/// Is path a regular file? -/// -/// @param path Input path. -/// @param result Set to true if \a path is a regular file (after following -/// symlinks), false if it is not. Undefined otherwise. -/// @returns errc::success if result has been successfully set, otherwise a -/// platform-specific error_code. -std::error_code is_regular_file(const Twine &path, bool &result); - -/// Simpler version of is_regular_file for clients that don't need to -/// differentiate between an error and false. -inline bool is_regular_file(const Twine &Path) { - bool Result; - if (is_regular_file(Path, Result)) - return false; - return Result; -} - -/// Does status represent a symlink file? -/// -/// @param status A basic_file_status previously returned from status. -/// @returns status_known(status) && status.type() == file_type::symlink_file. -bool is_symlink_file(const basic_file_status &status); - -/// Is path a symlink file? -/// -/// @param path Input path. -/// @param result Set to true if \a path is a symlink file, false if it is not. -/// Undefined otherwise. -/// @returns errc::success if result has been successfully set, otherwise a -/// platform-specific error_code. -std::error_code is_symlink_file(const Twine &path, bool &result); - -/// Simpler version of is_symlink_file for clients that don't need to -/// differentiate between an error and false. -inline bool is_symlink_file(const Twine &Path) { - bool Result; - if (is_symlink_file(Path, Result)) - return false; - return Result; -} - -/// Does this status represent something that exists but is not a -/// directory or regular file? -/// -/// @param status A basic_file_status previously returned from status. -/// @returns exists(s) && !is_regular_file(s) && !is_directory(s) -bool is_other(const basic_file_status &status); - -/// Is path something that exists but is not a directory, -/// regular file, or symlink? -/// -/// @param path Input path. -/// @param result Set to true if \a path exists, but is not a directory, regular -/// file, or a symlink, false if it does not. Undefined otherwise. -/// @returns errc::success if result has been successfully set, otherwise a -/// platform-specific error_code. -std::error_code is_other(const Twine &path, bool &result); - -/// Get file status as if by POSIX stat(). -/// -/// @param path Input path. -/// @param result Set to the file status. -/// @param follow When true, follows symlinks. Otherwise, the symlink itself is -/// statted. -/// @returns errc::success if result has been successfully set, otherwise a -/// platform-specific error_code. -std::error_code status(const Twine &path, file_status &result, - bool follow = true); - -/// A version for when a file descriptor is already available. -std::error_code status(int FD, file_status &Result); - -/// Is status available? -/// -/// @param s Input file status. -/// @returns True if status() != status_error. -bool status_known(const basic_file_status &s); - -/// Is status available? -/// -/// @param path Input path. -/// @param result Set to true if status() != status_error. -/// @returns errc::success if result has been successfully set, otherwise a -/// platform-specific error_code. -std::error_code status_known(const Twine &path, bool &result); - -enum CreationDisposition : unsigned { - /// CD_CreateAlways - When opening a file: - /// * If it already exists, truncate it. - /// * If it does not already exist, create a new file. - CD_CreateAlways = 0, - - /// CD_CreateNew - When opening a file: - /// * If it already exists, fail. - /// * If it does not already exist, create a new file. - CD_CreateNew = 1, - - /// CD_OpenExisting - When opening a file: - /// * If it already exists, open the file with the offset set to 0. - /// * If it does not already exist, fail. - CD_OpenExisting = 2, - - /// CD_OpenAlways - When opening a file: - /// * If it already exists, open the file with the offset set to 0. - /// * If it does not already exist, create a new file. - CD_OpenAlways = 3, -}; - -enum FileAccess : unsigned { - FA_Read = 1, - FA_Write = 2, -}; - -enum OpenFlags : unsigned { - OF_None = 0, - F_None = 0, // For compatibility - - /// The file should be opened in text mode on platforms that make this - /// distinction. - OF_Text = 1, - F_Text = 1, // For compatibility - - /// The file should be opened in append mode. - OF_Append = 2, - F_Append = 2, // For compatibility - - /// Delete the file on close. Only makes a difference on windows. - OF_Delete = 4, - - /// When a child process is launched, this file should remain open in the - /// child process. - OF_ChildInherit = 8, - - /// Force files Atime to be updated on access. Only makes a difference on windows. - OF_UpdateAtime = 16, -}; - -inline OpenFlags operator|(OpenFlags A, OpenFlags B) { - return OpenFlags(unsigned(A) | unsigned(B)); -} - -inline OpenFlags &operator|=(OpenFlags &A, OpenFlags B) { - A = A | B; - return A; -} - -inline FileAccess operator|(FileAccess A, FileAccess B) { - return FileAccess(unsigned(A) | unsigned(B)); -} - -inline FileAccess &operator|=(FileAccess &A, FileAccess B) { - A = A | B; - return A; -} - -/// @brief Opens a file with the specified creation disposition, access mode, -/// and flags and returns a file descriptor. -/// -/// The caller is responsible for closing the file descriptor once they are -/// finished with it. -/// -/// @param Name The path of the file to open, relative or absolute. -/// @param ResultFD If the file could be opened successfully, its descriptor -/// is stored in this location. Otherwise, this is set to -1. -/// @param Disp Value specifying the existing-file behavior. -/// @param Access Value specifying whether to open the file in read, write, or -/// read-write mode. -/// @param Flags Additional flags. -/// @param Mode The access permissions of the file, represented in octal. -/// @returns errc::success if \a Name has been opened, otherwise a -/// platform-specific error_code. -std::error_code openFile(const Twine &Name, int &ResultFD, - CreationDisposition Disp, FileAccess Access, - OpenFlags Flags, unsigned Mode = 0666); - -/// @brief Opens a file with the specified creation disposition, access mode, -/// and flags and returns a platform-specific file object. -/// -/// The caller is responsible for closing the file object once they are -/// finished with it. -/// -/// @param Name The path of the file to open, relative or absolute. -/// @param Disp Value specifying the existing-file behavior. -/// @param Access Value specifying whether to open the file in read, write, or -/// read-write mode. -/// @param Flags Additional flags. -/// @param Mode The access permissions of the file, represented in octal. -/// @returns errc::success if \a Name has been opened, otherwise a -/// platform-specific error_code. -Expected openNativeFile(const Twine &Name, CreationDisposition Disp, - FileAccess Access, OpenFlags Flags, - unsigned Mode = 0666); - -/// @brief Opens the file with the given name in a write-only or read-write -/// mode, returning its open file descriptor. If the file does not exist, it -/// is created. -/// -/// The caller is responsible for closing the file descriptor once they are -/// finished with it. -/// -/// @param Name The path of the file to open, relative or absolute. -/// @param ResultFD If the file could be opened successfully, its descriptor -/// is stored in this location. Otherwise, this is set to -1. -/// @param Flags Additional flags used to determine whether the file should be -/// opened in, for example, read-write or in write-only mode. -/// @param Mode The access permissions of the file, represented in octal. -/// @returns errc::success if \a Name has been opened, otherwise a -/// platform-specific error_code. -inline std::error_code -openFileForWrite(const Twine &Name, int &ResultFD, - CreationDisposition Disp = CD_CreateAlways, - OpenFlags Flags = OF_None, unsigned Mode = 0666) { - return openFile(Name, ResultFD, Disp, FA_Write, Flags, Mode); -} - -/// @brief Opens the file with the given name in a write-only or read-write -/// mode, returning its open file descriptor. If the file does not exist, it -/// is created. -/// -/// The caller is responsible for closing the freeing the file once they are -/// finished with it. -/// -/// @param Name The path of the file to open, relative or absolute. -/// @param Flags Additional flags used to determine whether the file should be -/// opened in, for example, read-write or in write-only mode. -/// @param Mode The access permissions of the file, represented in octal. -/// @returns a platform-specific file descriptor if \a Name has been opened, -/// otherwise an error object. -inline Expected openNativeFileForWrite(const Twine &Name, - CreationDisposition Disp, - OpenFlags Flags, - unsigned Mode = 0666) { - return openNativeFile(Name, Disp, FA_Write, Flags, Mode); -} - -/// @brief Opens the file with the given name in a write-only or read-write -/// mode, returning its open file descriptor. If the file does not exist, it -/// is created. -/// -/// The caller is responsible for closing the file descriptor once they are -/// finished with it. -/// -/// @param Name The path of the file to open, relative or absolute. -/// @param ResultFD If the file could be opened successfully, its descriptor -/// is stored in this location. Otherwise, this is set to -1. -/// @param Flags Additional flags used to determine whether the file should be -/// opened in, for example, read-write or in write-only mode. -/// @param Mode The access permissions of the file, represented in octal. -/// @returns errc::success if \a Name has been opened, otherwise a -/// platform-specific error_code. -inline std::error_code openFileForReadWrite(const Twine &Name, int &ResultFD, - CreationDisposition Disp, - OpenFlags Flags, - unsigned Mode = 0666) { - return openFile(Name, ResultFD, Disp, FA_Write | FA_Read, Flags, Mode); -} - -/// @brief Opens the file with the given name in a write-only or read-write -/// mode, returning its open file descriptor. If the file does not exist, it -/// is created. -/// -/// The caller is responsible for closing the freeing the file once they are -/// finished with it. -/// -/// @param Name The path of the file to open, relative or absolute. -/// @param Flags Additional flags used to determine whether the file should be -/// opened in, for example, read-write or in write-only mode. -/// @param Mode The access permissions of the file, represented in octal. -/// @returns a platform-specific file descriptor if \a Name has been opened, -/// otherwise an error object. -inline Expected openNativeFileForReadWrite(const Twine &Name, - CreationDisposition Disp, - OpenFlags Flags, - unsigned Mode = 0666) { - return openNativeFile(Name, Disp, FA_Write | FA_Read, Flags, Mode); -} - -/// @brief Opens the file with the given name in a read-only mode, returning -/// its open file descriptor. -/// -/// The caller is responsible for closing the file descriptor once they are -/// finished with it. -/// -/// @param Name The path of the file to open, relative or absolute. -/// @param ResultFD If the file could be opened successfully, its descriptor -/// is stored in this location. Otherwise, this is set to -1. -/// @param RealPath If nonnull, extra work is done to determine the real path -/// of the opened file, and that path is stored in this -/// location. -/// @returns errc::success if \a Name has been opened, otherwise a -/// platform-specific error_code. -std::error_code openFileForRead(const Twine &Name, int &ResultFD, - OpenFlags Flags = OF_None, - SmallVectorImpl *RealPath = nullptr); - -/// @brief Opens the file with the given name in a read-only mode, returning -/// its open file descriptor. -/// -/// The caller is responsible for closing the freeing the file once they are -/// finished with it. -/// -/// @param Name The path of the file to open, relative or absolute. -/// @param RealPath If nonnull, extra work is done to determine the real path -/// of the opened file, and that path is stored in this -/// location. -/// @returns a platform-specific file descriptor if \a Name has been opened, -/// otherwise an error object. -Expected -openNativeFileForRead(const Twine &Name, OpenFlags Flags = OF_None, - SmallVectorImpl *RealPath = nullptr); - -/// @brief Close the file object. This should be used instead of ::close for -/// portability. -/// -/// @param F On input, this is the file to close. On output, the file is -/// set to kInvalidFile. -void closeFile(file_t &F); - -std::error_code getUniqueID(const Twine Path, UniqueID &Result); - -/// This class represents a memory mapped file. It is based on -/// boost::iostreams::mapped_file. -class mapped_file_region { -public: - enum mapmode { - readonly, ///< May only access map via const_data as read only. - readwrite, ///< May access map via data and modify it. Written to path. - priv ///< May modify via data, but changes are lost on destruction. - }; - -private: - /// Platform-specific mapping state. - size_t Size; - void *Mapping; -#ifdef _WIN32 - void *FileHandle; -#endif - mapmode Mode; - - std::error_code init(int FD, uint64_t Offset, mapmode Mode); - -public: - mapped_file_region() = delete; - mapped_file_region(mapped_file_region&) = delete; - mapped_file_region &operator =(mapped_file_region&) = delete; - - /// \param fd An open file descriptor to map. mapped_file_region takes - /// ownership if closefd is true. It must have been opended in the correct - /// mode. - mapped_file_region(int fd, mapmode mode, size_t length, uint64_t offset, - std::error_code &ec); - - ~mapped_file_region(); - - size_t size() const; - char *data() const; - - /// Get a const view of the data. Modifying this memory has undefined - /// behavior. - const char *const_data() const; - - /// \returns The minimum alignment offset must be. - static int alignment(); -}; - -/// @} -/// @name Iterators -/// @{ - -/// directory_entry - A single entry in a directory. Caches the status either -/// from the result of the iteration syscall, or the first time status is -/// called. -class directory_entry { - std::string Path; - file_type Type; // Most platforms can provide this. - bool FollowSymlinks; // Affects the behavior of status(). - basic_file_status Status; // If available. - -public: - explicit directory_entry(const Twine &Path, bool FollowSymlinks = true, - file_type Type = file_type::type_unknown, - basic_file_status Status = basic_file_status()) - : Path(Path.str()), Type(Type), FollowSymlinks(FollowSymlinks), - Status(Status) {} - - directory_entry() = default; - - void replace_filename(const Twine &Filename, file_type Type, - basic_file_status Status = basic_file_status()); - - const std::string &path() const { return Path; } - ErrorOr status() const; - file_type type() const { - if (Type != file_type::type_unknown) - return Type; - auto S = status(); - return S ? S->type() : file_type::type_unknown; - } - - bool operator==(const directory_entry& RHS) const { return Path == RHS.Path; } - bool operator!=(const directory_entry& RHS) const { return !(*this == RHS); } - bool operator< (const directory_entry& RHS) const; - bool operator<=(const directory_entry& RHS) const; - bool operator> (const directory_entry& RHS) const; - bool operator>=(const directory_entry& RHS) const; -}; - -namespace detail { - - struct DirIterState; - - std::error_code directory_iterator_construct(DirIterState &, StringRef, bool); - std::error_code directory_iterator_increment(DirIterState &); - std::error_code directory_iterator_destruct(DirIterState &); - - /// Keeps state for the directory_iterator. - struct DirIterState { - ~DirIterState() { - directory_iterator_destruct(*this); - } - - intptr_t IterationHandle = 0; - directory_entry CurrentEntry; - }; - -} // end namespace detail - -/// directory_iterator - Iterates through the entries in path. There is no -/// operator++ because we need an error_code. If it's really needed we can make -/// it call report_fatal_error on error. -class directory_iterator { - std::shared_ptr State; - bool FollowSymlinks = true; - -public: - explicit directory_iterator(const Twine &path, std::error_code &ec, - bool follow_symlinks = true) - : FollowSymlinks(follow_symlinks) { - State = std::make_shared(); - SmallString<128> path_storage; - ec = detail::directory_iterator_construct( - *State, path.toStringRef(path_storage), FollowSymlinks); - } - - explicit directory_iterator(const directory_entry &de, std::error_code &ec, - bool follow_symlinks = true) - : FollowSymlinks(follow_symlinks) { - State = std::make_shared(); - ec = detail::directory_iterator_construct( - *State, de.path(), FollowSymlinks); - } - - /// Construct end iterator. - directory_iterator() = default; - - // No operator++ because we need error_code. - directory_iterator &increment(std::error_code &ec) { - ec = directory_iterator_increment(*State); - return *this; - } - - const directory_entry &operator*() const { return State->CurrentEntry; } - const directory_entry *operator->() const { return &State->CurrentEntry; } - - bool operator==(const directory_iterator &RHS) const { - if (State == RHS.State) - return true; - if (!RHS.State) - return State->CurrentEntry == directory_entry(); - if (!State) - return RHS.State->CurrentEntry == directory_entry(); - return State->CurrentEntry == RHS.State->CurrentEntry; - } - - bool operator!=(const directory_iterator &RHS) const { - return !(*this == RHS); - } -}; - -namespace detail { - - /// Keeps state for the recursive_directory_iterator. - struct RecDirIterState { - std::stack> Stack; - uint16_t Level = 0; - bool HasNoPushRequest = false; - }; - -} // end namespace detail - -/// recursive_directory_iterator - Same as directory_iterator except for it -/// recurses down into child directories. -class recursive_directory_iterator { - std::shared_ptr State; - bool Follow; - -public: - recursive_directory_iterator() = default; - explicit recursive_directory_iterator(const Twine &path, std::error_code &ec, - bool follow_symlinks = true) - : State(std::make_shared()), - Follow(follow_symlinks) { - State->Stack.push(directory_iterator(path, ec, Follow)); - if (State->Stack.top() == directory_iterator()) - State.reset(); - } - - // No operator++ because we need error_code. - recursive_directory_iterator &increment(std::error_code &ec) { - const directory_iterator end_itr = {}; - - if (State->HasNoPushRequest) - State->HasNoPushRequest = false; - else { - file_type type = State->Stack.top()->type(); - if (type == file_type::symlink_file && Follow) { - // Resolve the symlink: is it a directory to recurse into? - ErrorOr status = State->Stack.top()->status(); - if (status) - type = status->type(); - // Otherwise broken symlink, and we'll continue. - } - if (type == file_type::directory_file) { - State->Stack.push(directory_iterator(*State->Stack.top(), ec, Follow)); - if (State->Stack.top() != end_itr) { - ++State->Level; - return *this; - } - State->Stack.pop(); - } - } - - while (!State->Stack.empty() - && State->Stack.top().increment(ec) == end_itr) { - State->Stack.pop(); - --State->Level; - } - - // Check if we are done. If so, create an end iterator. - if (State->Stack.empty()) - State.reset(); - - return *this; - } - - const directory_entry &operator*() const { return *State->Stack.top(); } - const directory_entry *operator->() const { return &*State->Stack.top(); } - - // observers - /// Gets the current level. Starting path is at level 0. - int level() const { return State->Level; } - - /// Returns true if no_push has been called for this directory_entry. - bool no_push_request() const { return State->HasNoPushRequest; } - - // modifiers - /// Goes up one level if Level > 0. - void pop() { - assert(State && "Cannot pop an end iterator!"); - assert(State->Level > 0 && "Cannot pop an iterator with level < 1"); - - const directory_iterator end_itr = {}; - std::error_code ec; - do { - if (ec) { - //report_fatal_error("Error incrementing directory iterator."); - while (!State->Stack.empty()) State->Stack.pop(); - break; - } - State->Stack.pop(); - --State->Level; - } while (!State->Stack.empty() - && State->Stack.top().increment(ec) == end_itr); - - // Check if we are done. If so, create an end iterator. - if (State->Stack.empty()) - State.reset(); - } - - /// Does not go down into the current directory_entry. - void no_push() { State->HasNoPushRequest = true; } - - bool operator==(const recursive_directory_iterator &RHS) const { - return State == RHS.State; - } - - bool operator!=(const recursive_directory_iterator &RHS) const { - return !(*this == RHS); - } -}; - -/// @} - -} // end namespace fs -} // end namespace sys -} // end namespace wpi - -#endif // LLVM_SUPPORT_FILESYSTEM_H diff --git a/wpiutil/src/main/native/include/wpi/fs.h b/wpiutil/src/main/native/include/wpi/fs.h new file mode 100644 index 0000000000..fca8069d76 --- /dev/null +++ b/wpiutil/src/main/native/include/wpi/fs.h @@ -0,0 +1,235 @@ +// Copyright (c) FIRST and other WPILib contributors. +// Open Source Software; you can modify and/or share it under the terms of +// the WPILib BSD license file in the root directory of this project. + +//===----------------------------------------------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#pragma once + +#include +#include + +#if defined(__APPLE__) +#include +#endif +#if ((defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) \ + || (defined(__cplusplus) && __cplusplus >= 201703L)) \ + && defined(__has_include) +#if __has_include() \ + && (!defined(__MAC_OS_X_VERSION_MIN_REQUIRED) \ + || __MAC_OS_X_VERSION_MIN_REQUIRED >= 101500) \ + && (defined(__clang__) || !defined(__GNUC__) || __GNUC__ >= 10 \ + || (__GNUC__ >= 9 && __GNUC_MINOR__ >= 1)) +#define GHC_USE_STD_FS +#include +namespace fs { +using namespace std::filesystem; +using ifstream = std::ifstream; +using ofstream = std::ofstream; +using fstream = std::fstream; +} // namespace fs +#endif +#endif +#ifndef GHC_USE_STD_FS +// #define GHC_WIN_DISABLE_WSTRING_STORAGE_TYPE +#define GHC_FILESYSTEM_FWD +#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; +} // namespace fs +#endif + +namespace fs { + +#if defined(_WIN32) +// A Win32 HANDLE is a typedef of void* +using file_t = void*; +#else +using file_t = int; +#endif + +extern const file_t kInvalidFile; + +enum CreationDisposition : unsigned { + /// CD_CreateAlways - When opening a file: + /// * If it already exists, truncate it. + /// * If it does not already exist, create a new file. + CD_CreateAlways = 0, + + /// CD_CreateNew - When opening a file: + /// * If it already exists, fail. + /// * If it does not already exist, create a new file. + CD_CreateNew = 1, + + /// CD_OpenExisting - When opening a file: + /// * If it already exists, open the file with the offset set to 0. + /// * If it does not already exist, fail. + CD_OpenExisting = 2, + + /// CD_OpenAlways - When opening a file: + /// * If it already exists, open the file with the offset set to 0. + /// * If it does not already exist, create a new file. + CD_OpenAlways = 3, +}; + +enum FileAccess : unsigned { + FA_Read = 1, + FA_Write = 2, +}; + +enum OpenFlags : unsigned { + OF_None = 0, + F_None = 0, // For compatibility + + /// The file should be opened in text mode on platforms that make this + /// distinction. + OF_Text = 1, + F_Text = 1, // For compatibility + + /// The file should be opened in append mode. + OF_Append = 2, + F_Append = 2, // For compatibility + + /// Delete the file on close. Only makes a difference on windows. + OF_Delete = 4, + + /// When a child process is launched, this file should remain open in the + /// child process. + OF_ChildInherit = 8, + + /// Force files Atime to be updated on access. Only makes a difference on + /// windows. + OF_UpdateAtime = 16, +}; + +inline OpenFlags operator|(OpenFlags A, OpenFlags B) { + return OpenFlags(unsigned(A) | unsigned(B)); +} + +inline OpenFlags& operator|=(OpenFlags& A, OpenFlags B) { + A = A | B; + return A; +} + +inline FileAccess operator|(FileAccess A, FileAccess B) { + return FileAccess(unsigned(A) | unsigned(B)); +} + +inline FileAccess& operator|=(FileAccess& A, FileAccess B) { + A = A | B; + return A; +} + +/** + * Opens a file with the specified creation disposition, access mode, + * and flags and returns a platform-specific file object. + * + * The caller is responsible for closing the file object once they are + * finished with it. + * + * @param Path The path of the file to open, relative or absolute. + * @param EC Error code output, set to non-zero on error + * @param Disp Value specifying the existing-file behavior + * @param Access Value specifying whether to open the file in read, write, or + * read-write mode. + * @param Flags Additional flags. + * @param Mode The access permissions of the file, represented in octal. + * @returns errc::success if \a Name has been opened, otherwise a + * platform-specific error_code. + */ +file_t OpenFile(const path& Path, std::error_code& EC, CreationDisposition Disp, + FileAccess Access, OpenFlags Flags, unsigned Mode = 0666); + +/** + * @brief Opens the file with the given name in a write-only or read-write + * mode, returning its open file descriptor. If the file does not exist, it + * is created. + * + * The caller is responsible for closing the freeing the file once they are + * finished with it. + * + * @param Path The path of the file to open, relative or absolute. + * @param EC Error code output, set to non-zero on error + * @param Disp Value specifying the existing-file behavior + * @param Flags Additional flags used to determine whether the file should be + * opened in, for example, read-write or in write-only mode. + * @param Mode The access permissions of the file, represented in octal. + * @returns a platform-specific file descriptor if \a Name has been opened, + * otherwise kInvalidFile. + */ +inline file_t OpenFileForWrite(const path& Path, std::error_code& EC, + CreationDisposition Disp, OpenFlags Flags, + unsigned Mode = 0666) { + return OpenFile(Path, EC, Disp, FA_Write, Flags, Mode); +} + +/** + * @brief Opens the file with the given name in a write-only or read-write + * mode, returning its open file descriptor. If the file does not exist, it + * is created. + * + * The caller is responsible for closing the freeing the file once they are + * finished with it. + * + * @param Path The path of the file to open, relative or absolute. + * @param EC Error code output, set to non-zero on error + * @param Disp Value specifying the existing-file behavior + * @param Flags Additional flags used to determine whether the file should be + * opened in, for example, read-write or in write-only mode. + * @param Mode The access permissions of the file, represented in octal. + * @return a platform-specific file descriptor if \a Name has been opened, + * otherwise kInvalidFile. + */ +inline file_t OpenFileForReadWrite(const path& Path, std::error_code& EC, + CreationDisposition Disp, OpenFlags Flags, + unsigned Mode = 0666) { + return OpenFile(Path, EC, Disp, FA_Write | FA_Read, Flags, Mode); +} + +/** + * Opens the file with the given name in a read-only mode, returning + * its open file descriptor. + * + * The caller is responsible for closing the freeing the file once they are + * finished with it. + * + * @param Path The path of the file to open, relative or absolute. + * @param EC Error code output, set to non-zero on error + * @param Flags Additional flags + * @return a platform-specific file descriptor if \a Name has been opened, + * otherwise kInvalidFile. + */ +file_t OpenFileForRead(const path& Path, std::error_code& EC, + OpenFlags Flags = OF_None); + +/** + * Converts a file object to a file descriptor. The returned file descriptor + * must be closed with ::close() instead of CloseFile(). + * + * @param F On input, this is the file to convert to a file descriptor. + * On output, the file is set to kInvalidFile. + * @param EC Error code output, set to non-zero on error + * @param Flags Flags passed to the OpenFile function that created file_t + * @return file descriptor, or -1 on error + */ +int FileToFd(file_t& F, std::error_code& EC, OpenFlags Flags); + +/** + * Closes the file object. + * + * @param F On input, this is the file to close. On output, the file is + * set to kInvalidFile. + */ +void CloseFile(file_t& F); + +} // namespace fs diff --git a/wpiutil/src/main/native/include/wpi/ghc/filesystem.hpp b/wpiutil/src/main/native/include/wpi/ghc/filesystem.hpp new file mode 100644 index 0000000000..6cbc89217b --- /dev/null +++ b/wpiutil/src/main/native/include/wpi/ghc/filesystem.hpp @@ -0,0 +1,5942 @@ +//--------------------------------------------------------------------------------------- +// +// ghc::filesystem - A C++17-like filesystem implementation for C++11/C++14/C++17/C++20 +// +//--------------------------------------------------------------------------------------- +// +// Copyright (c) 2018, Steffen Schümann +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +//--------------------------------------------------------------------------------------- +// +// To dynamically select std::filesystem where available on most platforms, +// you could use: +// +// #if ((defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) || (defined(__cplusplus) && __cplusplus >= 201703L)) && defined(__has_include) +// #if __has_include() && (!defined(__MAC_OS_X_VERSION_MIN_REQUIRED) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 101500) +// #define GHC_USE_STD_FS +// #include +// namespace fs = std::filesystem; +// #endif +// #endif +// #ifndef GHC_USE_STD_FS +// #include +// namespace fs = ghc::filesystem; +// #endif +// +//--------------------------------------------------------------------------------------- +#ifndef GHC_FILESYSTEM_H +#define GHC_FILESYSTEM_H + +// #define BSD manifest constant only in +// sys/param.h +#ifndef _WIN32 +#include +#endif + +#ifndef GHC_OS_DETECTED +#if defined(__APPLE__) && defined(__MACH__) +#define GHC_OS_MACOS +#elif defined(__linux__) +#define GHC_OS_LINUX +#if defined(__ANDROID__) +#define GHC_OS_ANDROID +#endif +#elif defined(_WIN64) +#define GHC_OS_WINDOWS +#define GHC_OS_WIN64 +#elif defined(_WIN32) +#define GHC_OS_WINDOWS +#define GHC_OS_WIN32 +#elif defined(__CYGWIN__) +#define GHC_OS_CYGWIN +#elif defined(__svr4__) +#define GHC_OS_SYS5R4 +#elif defined(BSD) +#define GHC_OS_BSD +#elif defined(__EMSCRIPTEN__) +#define GHC_OS_WEB +#include +#elif defined(__QNX__) +#define GHC_OS_QNX +#define GHC_NO_DIRENT_D_TYPE +#else +#error "Operating system currently not supported!" +#endif +#define GHC_OS_DETECTED +#if (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) +#if _MSVC_LANG == 201703L +#define GHC_FILESYSTEM_RUNNING_CPP17 +#else +#define GHC_FILESYSTEM_RUNNING_CPP20 +#endif +#elif (defined(__cplusplus) && __cplusplus >= 201703L) +#if __cplusplus == 201703L +#define GHC_FILESYSTEM_RUNNING_CPP17 +#else +#define GHC_FILESYSTEM_RUNNING_CPP20 +#endif +#endif +#endif + +#if defined(GHC_FILESYSTEM_IMPLEMENTATION) +#define GHC_EXPAND_IMPL +#define GHC_INLINE +#ifdef GHC_OS_WINDOWS +#ifndef GHC_FS_API +#define GHC_FS_API +#endif +#ifndef GHC_FS_API_CLASS +#define GHC_FS_API_CLASS +#endif +#else +#ifndef GHC_FS_API +#define GHC_FS_API __attribute__((visibility("default"))) +#endif +#ifndef GHC_FS_API_CLASS +#define GHC_FS_API_CLASS __attribute__((visibility("default"))) +#endif +#endif +#elif defined(GHC_FILESYSTEM_FWD) +#define GHC_INLINE +#ifdef GHC_OS_WINDOWS +#ifndef GHC_FS_API +#define GHC_FS_API extern +#endif +#ifndef GHC_FS_API_CLASS +#define GHC_FS_API_CLASS +#endif +#else +#ifndef GHC_FS_API +#define GHC_FS_API extern +#endif +#ifndef GHC_FS_API_CLASS +#define GHC_FS_API_CLASS +#endif +#endif +#else +#define GHC_EXPAND_IMPL +#define GHC_INLINE inline +#ifndef GHC_FS_API +#define GHC_FS_API +#endif +#ifndef GHC_FS_API_CLASS +#define GHC_FS_API_CLASS +#endif +#endif + +#ifdef GHC_EXPAND_IMPL + +#ifdef GHC_OS_WINDOWS +#include +// additional includes +#include +#include +#include +#include +#include +#else +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef GHC_OS_ANDROID +#include +#if __ANDROID_API__ < 12 +#include +#endif +#include +#define statvfs statfs +#else +#include +#endif +#ifdef GHC_OS_CYGWIN +#include +#endif +#if !defined(__ANDROID__) || __ANDROID_API__ >= 26 +#include +#endif +#endif +#ifdef GHC_OS_MACOS +#include +#endif + +#if defined(__cpp_impl_three_way_comparison) && defined(__has_include) +#if __has_include() +#define GHC_HAS_THREEWAY_COMP +#include +#endif +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#else // GHC_EXPAND_IMPL + +#if defined(__cpp_impl_three_way_comparison) && defined(__has_include) +#if __has_include() +#define GHC_HAS_THREEWAY_COMP +#include +#endif +#endif +#include +#include +#include +#include +#include +#include +#include +#ifdef GHC_OS_WINDOWS +#include +#endif +#endif // GHC_EXPAND_IMPL + +// After standard library includes. +// Standard library support for std::string_view. +#if defined(__cpp_lib_string_view) +#define GHC_HAS_STD_STRING_VIEW +#elif defined(_LIBCPP_VERSION) && (_LIBCPP_VERSION >= 4000) && (__cplusplus >= 201402) +#define GHC_HAS_STD_STRING_VIEW +#elif defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE >= 7) && (__cplusplus >= 201703) +#define GHC_HAS_STD_STRING_VIEW +#elif defined(_MSC_VER) && (_MSC_VER >= 1910 && _MSVC_LANG >= 201703) +#define GHC_HAS_STD_STRING_VIEW +#endif + +// Standard library support for std::experimental::string_view. +#if defined(_LIBCPP_VERSION) && (_LIBCPP_VERSION >= 3700 && _LIBCPP_VERSION < 7000) && (__cplusplus >= 201402) +#define GHC_HAS_STD_EXPERIMENTAL_STRING_VIEW +#elif defined(__GNUC__) && (((__GNUC__ == 4) && (__GNUC_MINOR__ >= 9)) || (__GNUC__ > 4)) && (__cplusplus >= 201402) +#define GHC_HAS_STD_EXPERIMENTAL_STRING_VIEW +#elif defined(__GLIBCXX__) && defined(_GLIBCXX_USE_DUAL_ABI) && (__cplusplus >= 201402) +// macro _GLIBCXX_USE_DUAL_ABI is always defined in libstdc++ from gcc-5 and newer +#define GHC_HAS_STD_EXPERIMENTAL_STRING_VIEW +#endif + +#if defined(GHC_HAS_STD_STRING_VIEW) +#include +#elif defined(GHC_HAS_STD_EXPERIMENTAL_STRING_VIEW) +#include +#endif + +//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// Behaviour Switches (see README.md, should match the config in test/filesystem_test.cpp): +//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// Enforce C++17 API where possible when compiling for C++20, handles the following cases: +// * fs::path::u8string() returns std::string instead of std::u8string +// #define GHC_FILESYSTEM_ENFORCE_CPP17_API +//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// LWG #2682 disables the since then invalid use of the copy option create_symlinks on directories +// configure LWG conformance () +#define LWG_2682_BEHAVIOUR +//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// LWG #2395 makes crate_directory/create_directories not emit an error if there is a regular +// file with that name, it is superseded by P1164R1, so only activate if really needed +// #define LWG_2935_BEHAVIOUR +//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// LWG #2936 enables new element wise (more expensive) path comparison +// * if this->root_name().native().compare(p.root_name().native()) != 0 return result +// * if this->has_root_directory() and !p.has_root_directory() return -1 +// * if !this->has_root_directory() and p.has_root_directory() return -1 +// * else result of element wise comparison of path iteration where first comparison is != 0 or 0 +// if all comparisons are 0 (on Windows this implementation does case insensitive root_name() +// comparison) +#define LWG_2936_BEHAVIOUR +//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// LWG #2937 enforces that fs::equivalent emits an error, if !fs::exists(p1)||!exists(p2) +#define LWG_2937_BEHAVIOUR +//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// UTF8-Everywhere is the original behaviour of ghc::filesystem. But since v1.5 the windows +// version defaults to std::wstring storage backend. Still all std::string will be interpreted +// as UTF-8 encoded. With this define you can enfoce the old behavior on Windows, using +// std::string as backend and for fs::path::native() and char for fs::path::c_str(). This +// needs more conversions so it is (an was before v1.5) slower, bot might help keeping source +// homogeneous in a multi platform project. +// #define GHC_WIN_DISABLE_WSTRING_STORAGE_TYPE +//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// Raise errors/exceptions when invalid unicode codepoints or UTF-8 sequences are found, +// instead of replacing them with the unicode replacement character (U+FFFD). +// #define GHC_RAISE_UNICODE_ERRORS +//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +// Automatic prefix windows path with "\\?\" if they would break the MAX_PATH length. +// instead of replacing them with the unicode replacement character (U+FFFD). +#ifndef GHC_WIN_DISABLE_AUTO_PREFIXES +#define GHC_WIN_AUTO_PREFIX_LONG_PATH +#endif // GHC_WIN_DISABLE_AUTO_PREFIXES +//- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + +// ghc::filesystem version in decimal (major * 10000 + minor * 100 + patch) +#define GHC_FILESYSTEM_VERSION 10506L + +#if !defined(GHC_WITH_EXCEPTIONS) && (defined(__EXCEPTIONS) || defined(__cpp_exceptions) || defined(_CPPUNWIND)) +#define GHC_WITH_EXCEPTIONS +#endif +#if !defined(GHC_WITH_EXCEPTIONS) && defined(GHC_RAISE_UNICODE_ERRORS) +#error "Can't raise unicode errors with exception support disabled" +#endif + +namespace ghc { +namespace filesystem { + +#if defined(GHC_HAS_CUSTOM_STRING_VIEW) +#define GHC_WITH_STRING_VIEW +#elif defined(GHC_HAS_STD_STRING_VIEW) +#define GHC_WITH_STRING_VIEW +using std::basic_string_view; +#elif defined(GHC_HAS_STD_EXPERIMENTAL_STRING_VIEW) +#define GHC_WITH_STRING_VIEW +using std::experimental::basic_string_view; +#endif + +// temporary existing exception type for yet unimplemented parts +class GHC_FS_API_CLASS not_implemented_exception : public std::logic_error +{ +public: + not_implemented_exception() + : std::logic_error("function not implemented yet.") + { + } +}; + +template +class path_helper_base +{ +public: + using value_type = char_type; +#ifdef GHC_OS_WINDOWS + static constexpr value_type preferred_separator = '\\'; +#else + static constexpr value_type preferred_separator = '/'; +#endif +}; + +#if __cplusplus < 201703L +template +constexpr char_type path_helper_base::preferred_separator; +#endif + +#ifdef GHC_OS_WINDOWS +class path; +namespace detail { +bool has_executable_extension(const path& p); +} +#endif + +// [fs.class.path] class path +class GHC_FS_API_CLASS path +#if defined(GHC_OS_WINDOWS) && !defined(GHC_WIN_DISABLE_WSTRING_STORAGE_TYPE) +#define GHC_USE_WCHAR_T +#define GHC_NATIVEWP(p) p.c_str() +#define GHC_PLATFORM_LITERAL(str) L##str + : private path_helper_base +{ +public: + using path_helper_base::value_type; +#else +#define GHC_NATIVEWP(p) p.wstring().c_str() +#define GHC_PLATFORM_LITERAL(str) str + : private path_helper_base +{ +public: + using path_helper_base::value_type; +#endif + using string_type = std::basic_string; + using path_helper_base::preferred_separator; + + // [fs.enum.path.format] enumeration format + /// The path format in which the constructor argument is given. + enum format { + generic_format, ///< The generic format, internally used by + ///< ghc::filesystem::path with slashes + native_format, ///< The format native to the current platform this code + ///< is build for + auto_format, ///< Try to auto-detect the format, fallback to native + }; + + template + struct _is_basic_string : std::false_type + { + }; + template + struct _is_basic_string> : std::true_type + { + }; + template + struct _is_basic_string, std::allocator>> : std::true_type + { + }; +#ifdef GHC_WITH_STRING_VIEW + template + struct _is_basic_string> : std::true_type + { + }; + template + struct _is_basic_string>> : std::true_type + { + }; +#endif + + template + using path_type = typename std::enable_if::value, path>::type; + template +#if defined(__cpp_lib_char8_t) && !defined(GHC_FILESYSTEM_ENFORCE_CPP17_API) + using path_from_string = + typename std::enable_if<_is_basic_string::value || std::is_same::type>::value || std::is_same::type>::value || std::is_same::type>::value || + std::is_same::type>::value || std::is_same::type>::value || std::is_same::type>::value || + std::is_same::type>::value || std::is_same::type>::value || std::is_same::type>::value || + std::is_same::type>::value, + path>::type; + template + using path_type_EcharT = typename std::enable_if::value || std::is_same::value || std::is_same::value || std::is_same::value || std::is_same::value, path>::type; +#else + using path_from_string = typename std::enable_if<_is_basic_string::value || std::is_same::type>::value || std::is_same::type>::value || + std::is_same::type>::value || std::is_same::type>::value || std::is_same::type>::value || + std::is_same::type>::value || std::is_same::type>::value || std::is_same::type>::value, + path>::type; + template + using path_type_EcharT = typename std::enable_if::value || std::is_same::value || std::is_same::value || std::is_same::value, path>::type; +#endif + // [fs.path.construct] constructors and destructor + path() noexcept; + path(const path& p); + path(path&& p) noexcept; + path(string_type&& source, format fmt = auto_format); + template > + path(const Source& source, format fmt = auto_format); + template + path(InputIterator first, InputIterator last, format fmt = auto_format); +#ifdef GHC_WITH_EXCEPTIONS + template > + path(const Source& source, const std::locale& loc, format fmt = auto_format); + template + path(InputIterator first, InputIterator last, const std::locale& loc, format fmt = auto_format); +#endif + ~path(); + + // [fs.path.assign] assignments + path& operator=(const path& p); + path& operator=(path&& p) noexcept; + path& operator=(string_type&& source); + path& assign(string_type&& source); + template + path& operator=(const Source& source); + template + path& assign(const Source& source); + template + path& assign(InputIterator first, InputIterator last); + + // [fs.path.append] appends + path& operator/=(const path& p); + template + path& operator/=(const Source& source); + template + path& append(const Source& source); + template + path& append(InputIterator first, InputIterator last); + + // [fs.path.concat] concatenation + path& operator+=(const path& x); + path& operator+=(const string_type& x); +#ifdef GHC_WITH_STRING_VIEW + path& operator+=(basic_string_view x); +#endif + path& operator+=(const value_type* x); + path& operator+=(value_type x); + template + path_from_string& operator+=(const Source& x); + template + path_type_EcharT& operator+=(EcharT x); + template + path& concat(const Source& x); + template + path& concat(InputIterator first, InputIterator last); + + // [fs.path.modifiers] modifiers + void clear() noexcept; + path& make_preferred(); + path& remove_filename(); + path& replace_filename(const path& replacement); + path& replace_extension(const path& replacement = path()); + void swap(path& rhs) noexcept; + + // [fs.path.native.obs] native format observers + const string_type& native() const noexcept; + const value_type* c_str() const noexcept; + operator string_type() const; + template , class Allocator = std::allocator> + std::basic_string string(const Allocator& a = Allocator()) const; + std::string string() const; + std::wstring wstring() const; +#if defined(__cpp_lib_char8_t) && !defined(GHC_FILESYSTEM_ENFORCE_CPP17_API) + std::u8string u8string() const; +#else + std::string u8string() const; +#endif + std::u16string u16string() const; + std::u32string u32string() const; + + // [fs.path.generic.obs] generic format observers + template , class Allocator = std::allocator> + std::basic_string generic_string(const Allocator& a = Allocator()) const; + std::string generic_string() const; + std::wstring generic_wstring() const; +#if defined(__cpp_lib_char8_t) && !defined(GHC_FILESYSTEM_ENFORCE_CPP17_API) + std::u8string generic_u8string() const; +#else + std::string generic_u8string() const; +#endif + std::u16string generic_u16string() const; + std::u32string generic_u32string() const; + + // [fs.path.compare] compare + int compare(const path& p) const noexcept; + int compare(const string_type& s) const; +#ifdef GHC_WITH_STRING_VIEW + int compare(basic_string_view s) const; +#endif + int compare(const value_type* s) const; + + // [fs.path.decompose] decomposition + path root_name() const; + path root_directory() const; + path root_path() const; + path relative_path() const; + path parent_path() const; + path filename() const; + path stem() const; + path extension() const; + + // [fs.path.query] query + bool empty() const noexcept; + bool has_root_name() const; + bool has_root_directory() const; + bool has_root_path() const; + bool has_relative_path() const; + bool has_parent_path() const; + bool has_filename() const; + bool has_stem() const; + bool has_extension() const; + bool is_absolute() const; + bool is_relative() const; + + // [fs.path.gen] generation + path lexically_normal() const; + path lexically_relative(const path& base) const; + path lexically_proximate(const path& base) const; + + // [fs.path.itr] iterators + class iterator; + using const_iterator = iterator; + iterator begin() const; + iterator end() const; + +private: + using impl_value_type = value_type; + using impl_string_type = std::basic_string; + friend class directory_iterator; + void append_name(const value_type* name); + static constexpr impl_value_type generic_separator = '/'; + template + class input_iterator_range + { + public: + typedef InputIterator iterator; + typedef InputIterator const_iterator; + typedef typename InputIterator::difference_type difference_type; + + input_iterator_range(const InputIterator& first, const InputIterator& last) + : _first(first) + , _last(last) + { + } + + InputIterator begin() const { return _first; } + InputIterator end() const { return _last; } + + private: + InputIterator _first; + InputIterator _last; + }; + friend void swap(path& lhs, path& rhs) noexcept; + friend size_t hash_value(const path& p) noexcept; + friend path canonical(const path& p, std::error_code& ec); + string_type::size_type root_name_length() const noexcept; + void postprocess_path_with_format(format fmt); + void check_long_path(); + impl_string_type _path; +#ifdef GHC_OS_WINDOWS + void handle_prefixes(); + friend bool detail::has_executable_extension(const path& p); +#ifdef GHC_WIN_AUTO_PREFIX_LONG_PATH + string_type::size_type _prefixLength{0}; +#else // GHC_WIN_AUTO_PREFIX_LONG_PATH + static const string_type::size_type _prefixLength{0}; +#endif // GHC_WIN_AUTO_PREFIX_LONG_PATH +#else + static const string_type::size_type _prefixLength{0}; +#endif +}; + +// [fs.path.nonmember] path non-member functions +GHC_FS_API void swap(path& lhs, path& rhs) noexcept; +GHC_FS_API size_t hash_value(const path& p) noexcept; +#ifdef GHC_HAS_THREEWAY_COMP +GHC_FS_API std::strong_ordering operator<=>(const path& lhs, const path& rhs) noexcept; +#endif +GHC_FS_API bool operator==(const path& lhs, const path& rhs) noexcept; +GHC_FS_API bool operator!=(const path& lhs, const path& rhs) noexcept; +GHC_FS_API bool operator<(const path& lhs, const path& rhs) noexcept; +GHC_FS_API bool operator<=(const path& lhs, const path& rhs) noexcept; +GHC_FS_API bool operator>(const path& lhs, const path& rhs) noexcept; +GHC_FS_API bool operator>=(const path& lhs, const path& rhs) noexcept; +GHC_FS_API path operator/(const path& lhs, const path& rhs); + +// [fs.path.io] path inserter and extractor +template +std::basic_ostream& operator<<(std::basic_ostream& os, const path& p); +template +std::basic_istream& operator>>(std::basic_istream& is, path& p); + +// [pfs.path.factory] path factory functions +template > +#if defined(__cpp_lib_char8_t) && !defined(GHC_FILESYSTEM_ENFORCE_CPP17_API) +[[deprecated("use ghc::filesystem::path::path() with std::u8string instead")]] +#endif +path u8path(const Source& source); +template +#if defined(__cpp_lib_char8_t) && !defined(GHC_FILESYSTEM_ENFORCE_CPP17_API) +[[deprecated("use ghc::filesystem::path::path() with std::u8string instead")]] +#endif +path u8path(InputIterator first, InputIterator last); + +// [fs.class.filesystem_error] class filesystem_error +class GHC_FS_API_CLASS filesystem_error : public std::system_error +{ +public: + filesystem_error(const std::string& what_arg, std::error_code ec); + filesystem_error(const std::string& what_arg, const path& p1, std::error_code ec); + filesystem_error(const std::string& what_arg, const path& p1, const path& p2, std::error_code ec); + const path& path1() const noexcept; + const path& path2() const noexcept; + const char* what() const noexcept override; + +private: + std::string _what_arg; + std::error_code _ec; + path _p1, _p2; +}; + +class GHC_FS_API_CLASS path::iterator +{ +public: + using value_type = const path; + using difference_type = std::ptrdiff_t; + using pointer = const path*; + using reference = const path&; + using iterator_category = std::bidirectional_iterator_tag; + + iterator(); + iterator(const path& p, const impl_string_type::const_iterator& pos); + iterator& operator++(); + iterator operator++(int); + iterator& operator--(); + iterator operator--(int); + bool operator==(const iterator& other) const; + bool operator!=(const iterator& other) const; + reference operator*() const; + pointer operator->() const; + +private: + friend class path; + impl_string_type::const_iterator increment(const impl_string_type::const_iterator& pos) const; + impl_string_type::const_iterator decrement(const impl_string_type::const_iterator& pos) const; + void updateCurrent(); + impl_string_type::const_iterator _first; + impl_string_type::const_iterator _last; + impl_string_type::const_iterator _prefix; + impl_string_type::const_iterator _root; + impl_string_type::const_iterator _iter; + path _current; +}; + +struct space_info +{ + uintmax_t capacity; + uintmax_t free; + uintmax_t available; +}; + +// [fs.enum] enumerations +// [fs.enum.file_type] +enum class file_type { + none, + not_found, + regular, + directory, + symlink, + block, + character, + fifo, + socket, + unknown, +}; + +// [fs.enum.perms] +enum class perms : uint16_t { + none = 0, + + owner_read = 0400, + owner_write = 0200, + owner_exec = 0100, + owner_all = 0700, + + group_read = 040, + group_write = 020, + group_exec = 010, + group_all = 070, + + others_read = 04, + others_write = 02, + others_exec = 01, + others_all = 07, + + all = 0777, + set_uid = 04000, + set_gid = 02000, + sticky_bit = 01000, + + mask = 07777, + unknown = 0xffff +}; + +// [fs.enum.perm.opts] +enum class perm_options : uint16_t { + replace = 3, + add = 1, + remove = 2, + nofollow = 4, +}; + +// [fs.enum.copy.opts] +enum class copy_options : uint16_t { + none = 0, + + skip_existing = 1, + overwrite_existing = 2, + update_existing = 4, + + recursive = 8, + + copy_symlinks = 0x10, + skip_symlinks = 0x20, + + directories_only = 0x40, + create_symlinks = 0x80, +#ifndef GHC_OS_WEB + create_hard_links = 0x100 +#endif +}; + +// [fs.enum.dir.opts] +enum class directory_options : uint16_t { + none = 0, + follow_directory_symlink = 1, + skip_permission_denied = 2, +}; + +// [fs.class.file_status] class file_status +class GHC_FS_API_CLASS file_status +{ +public: + // [fs.file_status.cons] constructors and destructor + file_status() noexcept; + explicit file_status(file_type ft, perms prms = perms::unknown) noexcept; + file_status(const file_status&) noexcept; + file_status(file_status&&) noexcept; + ~file_status(); + // assignments: + file_status& operator=(const file_status&) noexcept; + file_status& operator=(file_status&&) noexcept; + // [fs.file_status.mods] modifiers + void type(file_type ft) noexcept; + void permissions(perms prms) noexcept; + // [fs.file_status.obs] observers + file_type type() const noexcept; + perms permissions() const noexcept; + friend bool operator==(const file_status& lhs, const file_status& rhs) noexcept { return lhs.type() == rhs.type() && lhs.permissions() == rhs.permissions(); } +private: + file_type _type; + perms _perms; +}; + +using file_time_type = std::chrono::time_point; + +// [fs.class.directory_entry] Class directory_entry +class GHC_FS_API_CLASS directory_entry +{ +public: + // [fs.dir.entry.cons] constructors and destructor + directory_entry() noexcept = default; + directory_entry(const directory_entry&) = default; + directory_entry(directory_entry&&) noexcept = default; +#ifdef GHC_WITH_EXCEPTIONS + explicit directory_entry(const path& p); +#endif + directory_entry(const path& p, std::error_code& ec); + ~directory_entry(); + + // assignments: + directory_entry& operator=(const directory_entry&) = default; + directory_entry& operator=(directory_entry&&) noexcept = default; + + // [fs.dir.entry.mods] modifiers +#ifdef GHC_WITH_EXCEPTIONS + void assign(const path& p); + void replace_filename(const path& p); + void refresh(); +#endif + void assign(const path& p, std::error_code& ec); + void replace_filename(const path& p, std::error_code& ec); + void refresh(std::error_code& ec) noexcept; + + // [fs.dir.entry.obs] observers + const filesystem::path& path() const noexcept; + operator const filesystem::path&() const noexcept; +#ifdef GHC_WITH_EXCEPTIONS + bool exists() const; + bool is_block_file() const; + bool is_character_file() const; + bool is_directory() const; + bool is_fifo() const; + bool is_other() const; + bool is_regular_file() const; + bool is_socket() const; + bool is_symlink() const; + uintmax_t file_size() const; + file_time_type last_write_time() const; + file_status status() const; + file_status symlink_status() const; +#endif + bool exists(std::error_code& ec) const noexcept; + bool is_block_file(std::error_code& ec) const noexcept; + bool is_character_file(std::error_code& ec) const noexcept; + bool is_directory(std::error_code& ec) const noexcept; + bool is_fifo(std::error_code& ec) const noexcept; + bool is_other(std::error_code& ec) const noexcept; + bool is_regular_file(std::error_code& ec) const noexcept; + bool is_socket(std::error_code& ec) const noexcept; + bool is_symlink(std::error_code& ec) const noexcept; + uintmax_t file_size(std::error_code& ec) const noexcept; + file_time_type last_write_time(std::error_code& ec) const noexcept; + file_status status(std::error_code& ec) const noexcept; + file_status symlink_status(std::error_code& ec) const noexcept; + +#ifndef GHC_OS_WEB +#ifdef GHC_WITH_EXCEPTIONS + uintmax_t hard_link_count() const; +#endif + uintmax_t hard_link_count(std::error_code& ec) const noexcept; +#endif + +#ifdef GHC_HAS_THREEWAY_COMP + std::strong_ordering operator<=>(const directory_entry& rhs) const noexcept; +#endif + bool operator<(const directory_entry& rhs) const noexcept; + bool operator==(const directory_entry& rhs) const noexcept; + bool operator!=(const directory_entry& rhs) const noexcept; + bool operator<=(const directory_entry& rhs) const noexcept; + bool operator>(const directory_entry& rhs) const noexcept; + bool operator>=(const directory_entry& rhs) const noexcept; + +private: + friend class directory_iterator; +#ifdef GHC_WITH_EXCEPTIONS + file_type status_file_type() const; +#endif + file_type status_file_type(std::error_code& ec) const noexcept; + filesystem::path _path; + file_status _status; + file_status _symlink_status; + uintmax_t _file_size = static_cast(-1); +#ifndef GHC_OS_WINDOWS + uintmax_t _hard_link_count = static_cast(-1); +#endif + time_t _last_write_time = 0; +}; + +// [fs.class.directory.iterator] Class directory_iterator +class GHC_FS_API_CLASS directory_iterator +{ +public: + class GHC_FS_API_CLASS proxy + { + public: + const directory_entry& operator*() const& noexcept { return _dir_entry; } + directory_entry operator*() && noexcept { return std::move(_dir_entry); } + + private: + explicit proxy(const directory_entry& dir_entry) + : _dir_entry(dir_entry) + { + } + friend class directory_iterator; + friend class recursive_directory_iterator; + directory_entry _dir_entry; + }; + using iterator_category = std::input_iterator_tag; + using value_type = directory_entry; + using difference_type = std::ptrdiff_t; + using pointer = const directory_entry*; + using reference = const directory_entry&; + + // [fs.dir.itr.members] member functions + directory_iterator() noexcept; +#ifdef GHC_WITH_EXCEPTIONS + explicit directory_iterator(const path& p); + directory_iterator(const path& p, directory_options options); +#endif + directory_iterator(const path& p, std::error_code& ec) noexcept; + directory_iterator(const path& p, directory_options options, std::error_code& ec) noexcept; + directory_iterator(const directory_iterator& rhs); + directory_iterator(directory_iterator&& rhs) noexcept; + ~directory_iterator(); + directory_iterator& operator=(const directory_iterator& rhs); + directory_iterator& operator=(directory_iterator&& rhs) noexcept; + const directory_entry& operator*() const; + const directory_entry* operator->() const; +#ifdef GHC_WITH_EXCEPTIONS + directory_iterator& operator++(); +#endif + directory_iterator& increment(std::error_code& ec) noexcept; + + // other members as required by [input.iterators] +#ifdef GHC_WITH_EXCEPTIONS + proxy operator++(int) + { + proxy p{**this}; + ++*this; + return p; + } +#endif + bool operator==(const directory_iterator& rhs) const; + bool operator!=(const directory_iterator& rhs) const; + +private: + friend class recursive_directory_iterator; + class impl; + std::shared_ptr _impl; +}; + +// [fs.dir.itr.nonmembers] directory_iterator non-member functions +GHC_FS_API directory_iterator begin(directory_iterator iter) noexcept; +GHC_FS_API directory_iterator end(const directory_iterator&) noexcept; + +// [fs.class.re.dir.itr] class recursive_directory_iterator +class GHC_FS_API_CLASS recursive_directory_iterator +{ +public: + using iterator_category = std::input_iterator_tag; + using value_type = directory_entry; + using difference_type = std::ptrdiff_t; + using pointer = const directory_entry*; + using reference = const directory_entry&; + + // [fs.rec.dir.itr.members] constructors and destructor + recursive_directory_iterator() noexcept; +#ifdef GHC_WITH_EXCEPTIONS + explicit recursive_directory_iterator(const path& p); + recursive_directory_iterator(const path& p, directory_options options); +#endif + recursive_directory_iterator(const path& p, directory_options options, std::error_code& ec) noexcept; + recursive_directory_iterator(const path& p, std::error_code& ec) noexcept; + recursive_directory_iterator(const recursive_directory_iterator& rhs); + recursive_directory_iterator(recursive_directory_iterator&& rhs) noexcept; + ~recursive_directory_iterator(); + + // [fs.rec.dir.itr.members] observers + directory_options options() const; + int depth() const; + bool recursion_pending() const; + + const directory_entry& operator*() const; + const directory_entry* operator->() const; + + // [fs.rec.dir.itr.members] modifiers recursive_directory_iterator& + recursive_directory_iterator& operator=(const recursive_directory_iterator& rhs); + recursive_directory_iterator& operator=(recursive_directory_iterator&& rhs) noexcept; +#ifdef GHC_WITH_EXCEPTIONS + recursive_directory_iterator& operator++(); +#endif + recursive_directory_iterator& increment(std::error_code& ec) noexcept; + +#ifdef GHC_WITH_EXCEPTIONS + void pop(); +#endif + void pop(std::error_code& ec); + void disable_recursion_pending(); + + // other members as required by [input.iterators] +#ifdef GHC_WITH_EXCEPTIONS + directory_iterator::proxy operator++(int) + { + directory_iterator::proxy proxy{**this}; + ++*this; + return proxy; + } +#endif + bool operator==(const recursive_directory_iterator& rhs) const; + bool operator!=(const recursive_directory_iterator& rhs) const; + +private: + struct recursive_directory_iterator_impl + { + directory_options _options; + bool _recursion_pending; + std::stack _dir_iter_stack; + recursive_directory_iterator_impl(directory_options options, bool recursion_pending) + : _options(options) + , _recursion_pending(recursion_pending) + { + } + }; + std::shared_ptr _impl; +}; + +// [fs.rec.dir.itr.nonmembers] directory_iterator non-member functions +GHC_FS_API recursive_directory_iterator begin(recursive_directory_iterator iter) noexcept; +GHC_FS_API recursive_directory_iterator end(const recursive_directory_iterator&) noexcept; + +// [fs.op.funcs] filesystem operations +#ifdef GHC_WITH_EXCEPTIONS +GHC_FS_API path absolute(const path& p); +GHC_FS_API path canonical(const path& p); +GHC_FS_API void copy(const path& from, const path& to); +GHC_FS_API void copy(const path& from, const path& to, copy_options options); +GHC_FS_API bool copy_file(const path& from, const path& to); +GHC_FS_API bool copy_file(const path& from, const path& to, copy_options option); +GHC_FS_API void copy_symlink(const path& existing_symlink, const path& new_symlink); +GHC_FS_API bool create_directories(const path& p); +GHC_FS_API bool create_directory(const path& p); +GHC_FS_API bool create_directory(const path& p, const path& attributes); +GHC_FS_API void create_directory_symlink(const path& to, const path& new_symlink); +GHC_FS_API void create_symlink(const path& to, const path& new_symlink); +GHC_FS_API path current_path(); +GHC_FS_API void current_path(const path& p); +GHC_FS_API bool exists(const path& p); +GHC_FS_API bool equivalent(const path& p1, const path& p2); +GHC_FS_API uintmax_t file_size(const path& p); +GHC_FS_API bool is_block_file(const path& p); +GHC_FS_API bool is_character_file(const path& p); +GHC_FS_API bool is_directory(const path& p); +GHC_FS_API bool is_empty(const path& p); +GHC_FS_API bool is_fifo(const path& p); +GHC_FS_API bool is_other(const path& p); +GHC_FS_API bool is_regular_file(const path& p); +GHC_FS_API bool is_socket(const path& p); +GHC_FS_API bool is_symlink(const path& p); +GHC_FS_API file_time_type last_write_time(const path& p); +GHC_FS_API void last_write_time(const path& p, file_time_type new_time); +GHC_FS_API void permissions(const path& p, perms prms, perm_options opts = perm_options::replace); +GHC_FS_API path proximate(const path& p, const path& base = current_path()); +GHC_FS_API path read_symlink(const path& p); +GHC_FS_API path relative(const path& p, const path& base = current_path()); +GHC_FS_API bool remove(const path& p); +GHC_FS_API uintmax_t remove_all(const path& p); +GHC_FS_API void rename(const path& from, const path& to); +GHC_FS_API void resize_file(const path& p, uintmax_t size); +GHC_FS_API space_info space(const path& p); +GHC_FS_API file_status status(const path& p); +GHC_FS_API file_status symlink_status(const path& p); +GHC_FS_API path temp_directory_path(); +GHC_FS_API path weakly_canonical(const path& p); +#endif +GHC_FS_API path absolute(const path& p, std::error_code& ec); +GHC_FS_API path canonical(const path& p, std::error_code& ec); +GHC_FS_API void copy(const path& from, const path& to, std::error_code& ec) noexcept; +GHC_FS_API void copy(const path& from, const path& to, copy_options options, std::error_code& ec) noexcept; +GHC_FS_API bool copy_file(const path& from, const path& to, std::error_code& ec) noexcept; +GHC_FS_API bool copy_file(const path& from, const path& to, copy_options option, std::error_code& ec) noexcept; +GHC_FS_API void copy_symlink(const path& existing_symlink, const path& new_symlink, std::error_code& ec) noexcept; +GHC_FS_API bool create_directories(const path& p, std::error_code& ec) noexcept; +GHC_FS_API bool create_directory(const path& p, std::error_code& ec) noexcept; +GHC_FS_API bool create_directory(const path& p, const path& attributes, std::error_code& ec) noexcept; +GHC_FS_API void create_directory_symlink(const path& to, const path& new_symlink, std::error_code& ec) noexcept; +GHC_FS_API void create_symlink(const path& to, const path& new_symlink, std::error_code& ec) noexcept; +GHC_FS_API path current_path(std::error_code& ec); +GHC_FS_API void current_path(const path& p, std::error_code& ec) noexcept; +GHC_FS_API bool exists(file_status s) noexcept; +GHC_FS_API bool exists(const path& p, std::error_code& ec) noexcept; +GHC_FS_API bool equivalent(const path& p1, const path& p2, std::error_code& ec) noexcept; +GHC_FS_API uintmax_t file_size(const path& p, std::error_code& ec) noexcept; +GHC_FS_API bool is_block_file(file_status s) noexcept; +GHC_FS_API bool is_block_file(const path& p, std::error_code& ec) noexcept; +GHC_FS_API bool is_character_file(file_status s) noexcept; +GHC_FS_API bool is_character_file(const path& p, std::error_code& ec) noexcept; +GHC_FS_API bool is_directory(file_status s) noexcept; +GHC_FS_API bool is_directory(const path& p, std::error_code& ec) noexcept; +GHC_FS_API bool is_empty(const path& p, std::error_code& ec) noexcept; +GHC_FS_API bool is_fifo(file_status s) noexcept; +GHC_FS_API bool is_fifo(const path& p, std::error_code& ec) noexcept; +GHC_FS_API bool is_other(file_status s) noexcept; +GHC_FS_API bool is_other(const path& p, std::error_code& ec) noexcept; +GHC_FS_API bool is_regular_file(file_status s) noexcept; +GHC_FS_API bool is_regular_file(const path& p, std::error_code& ec) noexcept; +GHC_FS_API bool is_socket(file_status s) noexcept; +GHC_FS_API bool is_socket(const path& p, std::error_code& ec) noexcept; +GHC_FS_API bool is_symlink(file_status s) noexcept; +GHC_FS_API bool is_symlink(const path& p, std::error_code& ec) noexcept; +GHC_FS_API file_time_type last_write_time(const path& p, std::error_code& ec) noexcept; +GHC_FS_API void last_write_time(const path& p, file_time_type new_time, std::error_code& ec) noexcept; +GHC_FS_API void permissions(const path& p, perms prms, std::error_code& ec) noexcept; +GHC_FS_API void permissions(const path& p, perms prms, perm_options opts, std::error_code& ec) noexcept; +GHC_FS_API path proximate(const path& p, std::error_code& ec); +GHC_FS_API path proximate(const path& p, const path& base, std::error_code& ec); +GHC_FS_API path read_symlink(const path& p, std::error_code& ec); +GHC_FS_API path relative(const path& p, std::error_code& ec); +GHC_FS_API path relative(const path& p, const path& base, std::error_code& ec); +GHC_FS_API bool remove(const path& p, std::error_code& ec) noexcept; +GHC_FS_API uintmax_t remove_all(const path& p, std::error_code& ec) noexcept; +GHC_FS_API void rename(const path& from, const path& to, std::error_code& ec) noexcept; +GHC_FS_API void resize_file(const path& p, uintmax_t size, std::error_code& ec) noexcept; +GHC_FS_API space_info space(const path& p, std::error_code& ec) noexcept; +GHC_FS_API file_status status(const path& p, std::error_code& ec) noexcept; +GHC_FS_API bool status_known(file_status s) noexcept; +GHC_FS_API file_status symlink_status(const path& p, std::error_code& ec) noexcept; +GHC_FS_API path temp_directory_path(std::error_code& ec) noexcept; +GHC_FS_API path weakly_canonical(const path& p, std::error_code& ec) noexcept; + +#ifndef GHC_OS_WEB +#ifdef GHC_WITH_EXCEPTIONS +GHC_FS_API void create_hard_link(const path& to, const path& new_hard_link); +GHC_FS_API uintmax_t hard_link_count(const path& p); +#endif +GHC_FS_API void create_hard_link(const path& to, const path& new_hard_link, std::error_code& ec) noexcept; +GHC_FS_API uintmax_t hard_link_count(const path& p, std::error_code& ec) noexcept; +#endif + +// Non-C++17 add-on std::fstream wrappers with path +template > +class basic_filebuf : public std::basic_filebuf +{ +public: + basic_filebuf() {} + ~basic_filebuf() override {} + basic_filebuf(const basic_filebuf&) = delete; + const basic_filebuf& operator=(const basic_filebuf&) = delete; + basic_filebuf* open(const path& p, std::ios_base::openmode mode) + { +#if defined(GHC_OS_WINDOWS) && !defined(__GLIBCXX__) + return std::basic_filebuf::open(p.wstring().c_str(), mode) ? this : 0; +#else + return std::basic_filebuf::open(p.string().c_str(), mode) ? this : 0; +#endif + } +}; + +template > +class basic_ifstream : public std::basic_ifstream +{ +public: + basic_ifstream() {} +#if defined(GHC_OS_WINDOWS) && !defined(__GLIBCXX__) + explicit basic_ifstream(const path& p, std::ios_base::openmode mode = std::ios_base::in) + : std::basic_ifstream(p.wstring().c_str(), mode) + { + } + void open(const path& p, std::ios_base::openmode mode = std::ios_base::in) { std::basic_ifstream::open(p.wstring().c_str(), mode); } +#else + explicit basic_ifstream(const path& p, std::ios_base::openmode mode = std::ios_base::in) + : std::basic_ifstream(p.string().c_str(), mode) + { + } + void open(const path& p, std::ios_base::openmode mode = std::ios_base::in) { std::basic_ifstream::open(p.string().c_str(), mode); } +#endif + basic_ifstream(const basic_ifstream&) = delete; + const basic_ifstream& operator=(const basic_ifstream&) = delete; + ~basic_ifstream() override {} +}; + +template > +class basic_ofstream : public std::basic_ofstream +{ +public: + basic_ofstream() {} +#if defined(GHC_OS_WINDOWS) && !defined(__GLIBCXX__) + explicit basic_ofstream(const path& p, std::ios_base::openmode mode = std::ios_base::out) + : std::basic_ofstream(p.wstring().c_str(), mode) + { + } + void open(const path& p, std::ios_base::openmode mode = std::ios_base::out) { std::basic_ofstream::open(p.wstring().c_str(), mode); } +#else + explicit basic_ofstream(const path& p, std::ios_base::openmode mode = std::ios_base::out) + : std::basic_ofstream(p.string().c_str(), mode) + { + } + void open(const path& p, std::ios_base::openmode mode = std::ios_base::out) { std::basic_ofstream::open(p.string().c_str(), mode); } +#endif + basic_ofstream(const basic_ofstream&) = delete; + const basic_ofstream& operator=(const basic_ofstream&) = delete; + ~basic_ofstream() override {} +}; + +template > +class basic_fstream : public std::basic_fstream +{ +public: + basic_fstream() {} +#if defined(GHC_OS_WINDOWS) && !defined(__GLIBCXX__) + explicit basic_fstream(const path& p, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out) + : std::basic_fstream(p.wstring().c_str(), mode) + { + } + void open(const path& p, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out) { std::basic_fstream::open(p.wstring().c_str(), mode); } +#else + explicit basic_fstream(const path& p, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out) + : std::basic_fstream(p.string().c_str(), mode) + { + } + void open(const path& p, std::ios_base::openmode mode = std::ios_base::in | std::ios_base::out) { std::basic_fstream::open(p.string().c_str(), mode); } +#endif + basic_fstream(const basic_fstream&) = delete; + const basic_fstream& operator=(const basic_fstream&) = delete; + ~basic_fstream() override {} +}; + +typedef basic_filebuf filebuf; +typedef basic_filebuf wfilebuf; +typedef basic_ifstream ifstream; +typedef basic_ifstream wifstream; +typedef basic_ofstream ofstream; +typedef basic_ofstream wofstream; +typedef basic_fstream fstream; +typedef basic_fstream wfstream; + +class GHC_FS_API_CLASS u8arguments +{ +public: + u8arguments(int& argc, char**& argv); + ~u8arguments() + { + _refargc = _argc; + _refargv = _argv; + } + + bool valid() const { return _isvalid; } + +private: + int _argc; + char** _argv; + int& _refargc; + char**& _refargv; + bool _isvalid; +#ifdef GHC_OS_WINDOWS + std::vector _args; + std::vector _argp; +#endif +}; + +//------------------------------------------------------------------------------------------------- +// Implementation +//------------------------------------------------------------------------------------------------- + +namespace detail { +enum utf8_states_t { S_STRT = 0, S_RJCT = 8 }; +GHC_FS_API void appendUTF8(std::string& str, uint32_t unicode); +GHC_FS_API bool is_surrogate(uint32_t c); +GHC_FS_API bool is_high_surrogate(uint32_t c); +GHC_FS_API bool is_low_surrogate(uint32_t c); +GHC_FS_API unsigned consumeUtf8Fragment(const unsigned state, const uint8_t fragment, uint32_t& codepoint); +enum class portable_error { + none = 0, + exists, + not_found, + not_supported, + not_implemented, + invalid_argument, + is_a_directory, +}; +GHC_FS_API std::error_code make_error_code(portable_error err); +#ifdef GHC_OS_WINDOWS +GHC_FS_API std::error_code make_system_error(uint32_t err = 0); +#else +GHC_FS_API std::error_code make_system_error(int err = 0); +#endif +} // namespace detail + +namespace detail { + +#ifdef GHC_EXPAND_IMPL + +GHC_INLINE std::error_code make_error_code(portable_error err) +{ +#ifdef GHC_OS_WINDOWS + switch (err) { + case portable_error::none: + return std::error_code(); + case portable_error::exists: + return std::error_code(ERROR_ALREADY_EXISTS, std::system_category()); + case portable_error::not_found: + return std::error_code(ERROR_PATH_NOT_FOUND, std::system_category()); + case portable_error::not_supported: + return std::error_code(ERROR_NOT_SUPPORTED, std::system_category()); + case portable_error::not_implemented: + return std::error_code(ERROR_CALL_NOT_IMPLEMENTED, std::system_category()); + case portable_error::invalid_argument: + return std::error_code(ERROR_INVALID_PARAMETER, std::system_category()); + case portable_error::is_a_directory: +#ifdef ERROR_DIRECTORY_NOT_SUPPORTED + return std::error_code(ERROR_DIRECTORY_NOT_SUPPORTED, std::system_category()); +#else + return std::error_code(ERROR_NOT_SUPPORTED, std::system_category()); +#endif + } +#else + switch (err) { + case portable_error::none: + return std::error_code(); + case portable_error::exists: + return std::error_code(EEXIST, std::system_category()); + case portable_error::not_found: + return std::error_code(ENOENT, std::system_category()); + case portable_error::not_supported: + return std::error_code(ENOTSUP, std::system_category()); + case portable_error::not_implemented: + return std::error_code(ENOSYS, std::system_category()); + case portable_error::invalid_argument: + return std::error_code(EINVAL, std::system_category()); + case portable_error::is_a_directory: + return std::error_code(EISDIR, std::system_category()); + } +#endif + return std::error_code(); +} + +#ifdef GHC_OS_WINDOWS +GHC_INLINE std::error_code make_system_error(uint32_t err) +{ + return std::error_code(err ? static_cast(err) : static_cast(::GetLastError()), std::system_category()); +} +#else +GHC_INLINE std::error_code make_system_error(int err) +{ + return std::error_code(err ? err : errno, std::system_category()); +} +#endif + +#endif // GHC_EXPAND_IMPL + +template +using EnableBitmask = typename std::enable_if::value || std::is_same::value || std::is_same::value || std::is_same::value, Enum>::type; +} // namespace detail + +template +constexpr detail::EnableBitmask operator&(Enum X, Enum Y) +{ + using underlying = typename std::underlying_type::type; + return static_cast(static_cast(X) & static_cast(Y)); +} + +template +constexpr detail::EnableBitmask operator|(Enum X, Enum Y) +{ + using underlying = typename std::underlying_type::type; + return static_cast(static_cast(X) | static_cast(Y)); +} + +template +constexpr detail::EnableBitmask operator^(Enum X, Enum Y) +{ + using underlying = typename std::underlying_type::type; + return static_cast(static_cast(X) ^ static_cast(Y)); +} + +template +constexpr detail::EnableBitmask operator~(Enum X) +{ + using underlying = typename std::underlying_type::type; + return static_cast(~static_cast(X)); +} + +template +detail::EnableBitmask& operator&=(Enum& X, Enum Y) +{ + X = X & Y; + return X; +} + +template +detail::EnableBitmask& operator|=(Enum& X, Enum Y) +{ + X = X | Y; + return X; +} + +template +detail::EnableBitmask& operator^=(Enum& X, Enum Y) +{ + X = X ^ Y; + return X; +} + +#ifdef GHC_EXPAND_IMPL + +namespace detail { + +GHC_INLINE bool in_range(uint32_t c, uint32_t lo, uint32_t hi) +{ + return (static_cast(c - lo) < (hi - lo + 1)); +} + +GHC_INLINE bool is_surrogate(uint32_t c) +{ + return in_range(c, 0xd800, 0xdfff); +} + +GHC_INLINE bool is_high_surrogate(uint32_t c) +{ + return (c & 0xfffffc00) == 0xd800; +} + +GHC_INLINE bool is_low_surrogate(uint32_t c) +{ + return (c & 0xfffffc00) == 0xdc00; +} + +GHC_INLINE void appendUTF8(std::string& str, uint32_t unicode) +{ + if (unicode <= 0x7f) { + str.push_back(static_cast(unicode)); + } + else if (unicode >= 0x80 && unicode <= 0x7ff) { + str.push_back(static_cast((unicode >> 6) + 192)); + str.push_back(static_cast((unicode & 0x3f) + 128)); + } + else if ((unicode >= 0x800 && unicode <= 0xd7ff) || (unicode >= 0xe000 && unicode <= 0xffff)) { + str.push_back(static_cast((unicode >> 12) + 224)); + str.push_back(static_cast(((unicode & 0xfff) >> 6) + 128)); + str.push_back(static_cast((unicode & 0x3f) + 128)); + } + else if (unicode >= 0x10000 && unicode <= 0x10ffff) { + str.push_back(static_cast((unicode >> 18) + 240)); + str.push_back(static_cast(((unicode & 0x3ffff) >> 12) + 128)); + str.push_back(static_cast(((unicode & 0xfff) >> 6) + 128)); + str.push_back(static_cast((unicode & 0x3f) + 128)); + } + else { +#ifdef GHC_RAISE_UNICODE_ERRORS + throw filesystem_error("Illegal code point for unicode character.", str, std::make_error_code(std::errc::illegal_byte_sequence)); +#else + appendUTF8(str, 0xfffd); +#endif + } +} + +// Thanks to Bjoern Hoehrmann (https://bjoern.hoehrmann.de/utf-8/decoder/dfa/) +// and Taylor R Campbell for the ideas to this DFA approach of UTF-8 decoding; +// Generating debugging and shrinking my own DFA from scratch was a day of fun! +GHC_INLINE unsigned consumeUtf8Fragment(const unsigned state, const uint8_t fragment, uint32_t& codepoint) +{ + static const uint32_t utf8_state_info[] = { + // encoded states + 0x11111111u, 0x11111111u, 0x77777777u, 0x77777777u, 0x88888888u, 0x88888888u, 0x88888888u, 0x88888888u, 0x22222299u, 0x22222222u, 0x22222222u, 0x22222222u, 0x3333333au, 0x33433333u, 0x9995666bu, 0x99999999u, + 0x88888880u, 0x22818108u, 0x88888881u, 0x88888882u, 0x88888884u, 0x88888887u, 0x88888886u, 0x82218108u, 0x82281108u, 0x88888888u, 0x88888883u, 0x88888885u, 0u, 0u, 0u, 0u, + }; + uint8_t category = fragment < 128 ? 0 : (utf8_state_info[(fragment >> 3) & 0xf] >> ((fragment & 7) << 2)) & 0xf; + codepoint = (state ? (codepoint << 6) | (fragment & 0x3fu) : (0xffu >> category) & fragment); + return state == S_RJCT ? static_cast(S_RJCT) : static_cast((utf8_state_info[category + 16] >> (state << 2)) & 0xf); +} + +GHC_INLINE bool validUtf8(const std::string& utf8String) +{ + std::string::const_iterator iter = utf8String.begin(); + unsigned utf8_state = S_STRT; + std::uint32_t codepoint = 0; + while (iter < utf8String.end()) { + if ((utf8_state = consumeUtf8Fragment(utf8_state, static_cast(*iter++), codepoint)) == S_RJCT) { + return false; + } + } + if (utf8_state) { + return false; + } + return true; +} + +} // namespace detail + +#endif + +namespace detail { + +template ::value && (sizeof(typename Utf8String::value_type) == 1) && (sizeof(typename StringType::value_type) == 1)>::type* = nullptr> +inline StringType fromUtf8(const Utf8String& utf8String, const typename StringType::allocator_type& alloc = typename StringType::allocator_type()) +{ + return StringType(utf8String.begin(), utf8String.end(), alloc); +} + +template ::value && (sizeof(typename Utf8String::value_type) == 1) && (sizeof(typename StringType::value_type) == 2)>::type* = nullptr> +inline StringType fromUtf8(const Utf8String& utf8String, const typename StringType::allocator_type& alloc = typename StringType::allocator_type()) +{ + StringType result(alloc); + result.reserve(utf8String.length()); + auto iter = utf8String.cbegin(); + unsigned utf8_state = S_STRT; + std::uint32_t codepoint = 0; + while (iter < utf8String.cend()) { + if ((utf8_state = consumeUtf8Fragment(utf8_state, static_cast(*iter++), codepoint)) == S_STRT) { + if (codepoint <= 0xffff) { + result += static_cast(codepoint); + } + else { + codepoint -= 0x10000; + result += static_cast((codepoint >> 10) + 0xd800); + result += static_cast((codepoint & 0x3ff) + 0xdc00); + } + codepoint = 0; + } + else if (utf8_state == S_RJCT) { +#ifdef GHC_RAISE_UNICODE_ERRORS + throw filesystem_error("Illegal byte sequence for unicode character.", utf8String, std::make_error_code(std::errc::illegal_byte_sequence)); +#else + result += static_cast(0xfffd); + utf8_state = S_STRT; + codepoint = 0; +#endif + } + } + if (utf8_state) { +#ifdef GHC_RAISE_UNICODE_ERRORS + throw filesystem_error("Illegal byte sequence for unicode character.", utf8String, std::make_error_code(std::errc::illegal_byte_sequence)); +#else + result += static_cast(0xfffd); +#endif + } + return result; +} + +template ::value && (sizeof(typename Utf8String::value_type) == 1) && (sizeof(typename StringType::value_type) == 4)>::type* = nullptr> +inline StringType fromUtf8(const Utf8String& utf8String, const typename StringType::allocator_type& alloc = typename StringType::allocator_type()) +{ + StringType result(alloc); + result.reserve(utf8String.length()); + auto iter = utf8String.cbegin(); + unsigned utf8_state = S_STRT; + std::uint32_t codepoint = 0; + while (iter < utf8String.cend()) { + if ((utf8_state = consumeUtf8Fragment(utf8_state, static_cast(*iter++), codepoint)) == S_STRT) { + result += static_cast(codepoint); + codepoint = 0; + } + else if (utf8_state == S_RJCT) { +#ifdef GHC_RAISE_UNICODE_ERRORS + throw filesystem_error("Illegal byte sequence for unicode character.", utf8String, std::make_error_code(std::errc::illegal_byte_sequence)); +#else + result += static_cast(0xfffd); + utf8_state = S_STRT; + codepoint = 0; +#endif + } + } + if (utf8_state) { +#ifdef GHC_RAISE_UNICODE_ERRORS + throw filesystem_error("Illegal byte sequence for unicode character.", utf8String, std::make_error_code(std::errc::illegal_byte_sequence)); +#else + result += static_cast(0xfffd); +#endif + } + return result; +} + +template +inline StringType fromUtf8(const charT (&utf8String)[N]) +{ +#ifdef GHC_WITH_STRING_VIEW + return fromUtf8(basic_string_view(utf8String, N - 1)); +#else + return fromUtf8(std::basic_string(utf8String, N - 1)); +#endif +} + +template ::value && (sizeof(typename strT::value_type) == 1), int>::type size = 1> +inline std::string toUtf8(const strT& unicodeString) +{ + return std::string(unicodeString.begin(), unicodeString.end()); +} + +template ::value && (sizeof(typename strT::value_type) == 2), int>::type size = 2> +inline std::string toUtf8(const strT& unicodeString) +{ + std::string result; + for (auto iter = unicodeString.begin(); iter != unicodeString.end(); ++iter) { + char32_t c = *iter; + if (is_surrogate(c)) { + ++iter; + if (iter != unicodeString.end() && is_high_surrogate(c) && is_low_surrogate(*iter)) { + appendUTF8(result, (char32_t(c) << 10) + *iter - 0x35fdc00); + } + else { +#ifdef GHC_RAISE_UNICODE_ERRORS + throw filesystem_error("Illegal code point for unicode character.", result, std::make_error_code(std::errc::illegal_byte_sequence)); +#else + appendUTF8(result, 0xfffd); + if (iter == unicodeString.end()) { + break; + } +#endif + } + } + else { + appendUTF8(result, c); + } + } + return result; +} + +template ::value && (sizeof(typename strT::value_type) == 4), int>::type size = 4> +inline std::string toUtf8(const strT& unicodeString) +{ + std::string result; + for (auto c : unicodeString) { + appendUTF8(result, static_cast(c)); + } + return result; +} + +template +inline std::string toUtf8(const charT* unicodeString) +{ +#ifdef GHC_WITH_STRING_VIEW + return toUtf8(basic_string_view>(unicodeString)); +#else + return toUtf8(std::basic_string>(unicodeString)); +#endif +} + +#ifdef GHC_USE_WCHAR_T +template ::value && (sizeof(typename WString::value_type) == 2) && (sizeof(typename StringType::value_type) == 1), bool>::type = false> +inline StringType fromWChar(const WString& wString, const typename StringType::allocator_type& alloc = typename StringType::allocator_type()) +{ + auto temp = toUtf8(wString); + return StringType(temp.begin(), temp.end(), alloc); +} + +template ::value && (sizeof(typename WString::value_type) == 2) && (sizeof(typename StringType::value_type) == 2), bool>::type = false> +inline StringType fromWChar(const WString& wString, const typename StringType::allocator_type& alloc = typename StringType::allocator_type()) +{ + return StringType(wString.begin(), wString.end(), alloc); +} + +template ::value && (sizeof(typename WString::value_type) == 2) && (sizeof(typename StringType::value_type) == 4), bool>::type = false> +inline StringType fromWChar(const WString& wString, const typename StringType::allocator_type& alloc = typename StringType::allocator_type()) +{ + auto temp = toUtf8(wString); + return fromUtf8(temp, alloc); +} + +template ::value && (sizeof(typename strT::value_type) == 1), bool>::type = false> +inline std::wstring toWChar(const strT& unicodeString) +{ + return fromUtf8(unicodeString); +} + +template ::value && (sizeof(typename strT::value_type) == 2), bool>::type = false> +inline std::wstring toWChar(const strT& unicodeString) +{ + return std::wstring(unicodeString.begin(), unicodeString.end()); +} + +template ::value && (sizeof(typename strT::value_type) == 4), bool>::type = false> +inline std::wstring toWChar(const strT& unicodeString) +{ + auto temp = toUtf8(unicodeString); + return fromUtf8(temp); +} + +template +inline std::wstring toWChar(const charT* unicodeString) +{ +#ifdef GHC_WITH_STRING_VIEW + return toWChar(basic_string_view>(unicodeString)); +#else + return toWChar(std::basic_string>(unicodeString)); +#endif +} +#endif // GHC_USE_WCHAR_T + +} // namespace detail + +#ifdef GHC_EXPAND_IMPL + +namespace detail { + +template ::value, bool>::type = true> +GHC_INLINE bool startsWith(const strT& what, const strT& with) +{ + return with.length() <= what.length() && equal(with.begin(), with.end(), what.begin()); +} + +template ::value, bool>::type = true> +GHC_INLINE bool endsWith(const strT& what, const strT& with) +{ + return with.length() <= what.length() && what.compare(what.length() - with.length(), with.size(), with) == 0; +} + +} // namespace detail + +GHC_INLINE void path::check_long_path() +{ +#if defined(GHC_OS_WINDOWS) && defined(GHC_WIN_AUTO_PREFIX_LONG_PATH) + if (is_absolute() && _path.length() >= MAX_PATH - 12 && !detail::startsWith(_path, impl_string_type(GHC_PLATFORM_LITERAL("\\\\?\\")))) { + postprocess_path_with_format(native_format); + } +#endif +} + +GHC_INLINE void path::postprocess_path_with_format(path::format fmt) +{ +#ifdef GHC_RAISE_UNICODE_ERRORS + if (!detail::validUtf8(_path)) { + path t; + t._path = _path; + throw filesystem_error("Illegal byte sequence for unicode character.", t, std::make_error_code(std::errc::illegal_byte_sequence)); + } +#endif + switch (fmt) { +#ifdef GHC_OS_WINDOWS + case path::native_format: + case path::auto_format: + case path::generic_format: + for (auto& c : _path) { + if (c == generic_separator) { + c = preferred_separator; + } + } +#ifdef GHC_WIN_AUTO_PREFIX_LONG_PATH + if (is_absolute() && _path.length() >= MAX_PATH - 12 && !detail::startsWith(_path, impl_string_type(GHC_PLATFORM_LITERAL("\\\\?\\")))) { + _path = GHC_PLATFORM_LITERAL("\\\\?\\") + _path; + } +#endif + handle_prefixes(); + break; +#else + case path::auto_format: + case path::native_format: + case path::generic_format: + // nothing to do + break; +#endif + } + if (_path.length() > _prefixLength + 2 && _path[_prefixLength] == preferred_separator && _path[_prefixLength + 1] == preferred_separator && _path[_prefixLength + 2] != preferred_separator) { + impl_string_type::iterator new_end = std::unique(_path.begin() + static_cast(_prefixLength) + 2, _path.end(), [](path::value_type lhs, path::value_type rhs) { return lhs == rhs && lhs == preferred_separator; }); + _path.erase(new_end, _path.end()); + } + else { + impl_string_type::iterator new_end = std::unique(_path.begin() + static_cast(_prefixLength), _path.end(), [](path::value_type lhs, path::value_type rhs) { return lhs == rhs && lhs == preferred_separator; }); + _path.erase(new_end, _path.end()); + } +} + +#endif // GHC_EXPAND_IMPL + +template +inline path::path(const Source& source, format fmt) +#ifdef GHC_USE_WCHAR_T + : _path(detail::toWChar(source)) +#else + : _path(detail::toUtf8(source)) +#endif +{ + postprocess_path_with_format(fmt); +} + +template +inline path u8path(const Source& source) +{ + return path(source); +} +template +inline path u8path(InputIterator first, InputIterator last) +{ + return path(first, last); +} + +template +inline path::path(InputIterator first, InputIterator last, format fmt) + : path(std::basic_string::value_type>(first, last), fmt) +{ + // delegated +} + +#ifdef GHC_EXPAND_IMPL + +namespace detail { + +GHC_INLINE bool equals_simple_insensitive(const path::value_type* str1, const path::value_type* str2) +{ +#ifdef GHC_OS_WINDOWS +#ifdef __GNUC__ + while (::tolower((unsigned char)*str1) == ::tolower((unsigned char)*str2++)) { + if (*str1++ == 0) + return true; + } + return false; +#else // __GNUC__ +#ifdef GHC_USE_WCHAR_T + return 0 == ::_wcsicmp(str1, str2); +#else // GHC_USE_WCHAR_T + return 0 == ::_stricmp(str1, str2); +#endif // GHC_USE_WCHAR_T +#endif // __GNUC__ +#else // GHC_OS_WINDOWS + return 0 == ::strcasecmp(str1, str2); +#endif // GHC_OS_WINDOWS +} + +GHC_INLINE int compare_simple_insensitive(const path::value_type* str1, size_t len1, const path::value_type* str2, size_t len2) +{ + while (len1 > 0 && len2 > 0 && ::tolower(static_cast(*str1)) == ::tolower(static_cast(*str2))) { + --len1; + --len2; + ++str1; + ++str2; + } + if (len1 && len2) { + return *str1 < *str2 ? -1 : 1; + } + if (len1 == 0 && len2 == 0) { + return 0; + } + return len1 == 0 ? -1 : 1; +} + +GHC_INLINE const char* strerror_adapter(char* gnu, char*) +{ + return gnu; +} + +GHC_INLINE const char* strerror_adapter(int posix, char* buffer) +{ + if (posix) { + return "Error in strerror_r!"; + } + return buffer; +} + +template +GHC_INLINE std::string systemErrorText(ErrorNumber code = 0) +{ +#if defined(GHC_OS_WINDOWS) + LPVOID msgBuf; + DWORD dw = code ? static_cast(code) : ::GetLastError(); + FormatMessageW(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, dw, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPWSTR)&msgBuf, 0, NULL); + std::string msg = toUtf8(std::wstring((LPWSTR)msgBuf)); + LocalFree(msgBuf); + return msg; +#else + char buffer[512]; + return strerror_adapter(strerror_r(code ? code : errno, buffer, sizeof(buffer)), buffer); +#endif +} + +#ifdef GHC_OS_WINDOWS +using CreateSymbolicLinkW_fp = BOOLEAN(WINAPI*)(LPCWSTR, LPCWSTR, DWORD); +using CreateHardLinkW_fp = BOOLEAN(WINAPI*)(LPCWSTR, LPCWSTR, LPSECURITY_ATTRIBUTES); + +GHC_INLINE void create_symlink(const path& target_name, const path& new_symlink, bool to_directory, std::error_code& ec) +{ + std::error_code tec; + auto fs = status(target_name, tec); + if ((fs.type() == file_type::directory && !to_directory) || (fs.type() == file_type::regular && to_directory)) { + ec = detail::make_error_code(detail::portable_error::not_supported); + return; + } +#if defined(__GNUC__) && __GNUC__ >= 8 +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wcast-function-type" +#endif + static CreateSymbolicLinkW_fp api_call = reinterpret_cast(GetProcAddress(GetModuleHandleW(L"kernel32.dll"), "CreateSymbolicLinkW")); +#if defined(__GNUC__) && __GNUC__ >= 8 +#pragma GCC diagnostic pop +#endif + if (api_call) { + if (api_call(detail::fromUtf8(new_symlink.u8string()).c_str(), detail::fromUtf8(target_name.u8string()).c_str(), to_directory ? 1 : 0) == 0) { + auto result = ::GetLastError(); + if (result == ERROR_PRIVILEGE_NOT_HELD && api_call(detail::fromUtf8(new_symlink.u8string()).c_str(), detail::fromUtf8(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(GetProcAddress(GetModuleHandleW(L"kernel32.dll"), "CreateHardLinkW")); +#if defined(__GNUC__) && __GNUC__ >= 8 +#pragma GCC diagnostic pop +#endif + if (api_call) { + if (api_call(GHC_NATIVEWP(new_hardlink), GHC_NATIVEWP(target_name), NULL) == 0) { + ec = detail::make_system_error(); + } + } + else { + ec = detail::make_system_error(ERROR_NOT_SUPPORTED); + } +} + +GHC_INLINE path getFullPathName(const wchar_t* p, std::error_code& ec) +{ + ULONG size = ::GetFullPathNameW(p, 0, 0, 0); + if (size) { + std::vector buf(size, 0); + ULONG s2 = GetFullPathNameW(p, size, buf.data(), nullptr); + if (s2 && s2 < size) { + return path(std::wstring(buf.data(), s2)); + } + } + ec = detail::make_system_error(); + return path(); +} + +#else +GHC_INLINE void create_symlink(const path& target_name, const path& new_symlink, bool, std::error_code& ec) +{ + if (::symlink(target_name.c_str(), new_symlink.c_str()) != 0) { + ec = detail::make_system_error(); + } +} + +#ifndef GHC_OS_WEB +GHC_INLINE void create_hardlink(const path& target_name, const path& new_hardlink, std::error_code& ec) +{ + if (::link(target_name.c_str(), new_hardlink.c_str()) != 0) { + ec = detail::make_system_error(); + } +} +#endif +#endif + +template +GHC_INLINE file_status file_status_from_st_mode(T mode) +{ +#ifdef GHC_OS_WINDOWS + file_type ft = file_type::unknown; + if ((mode & _S_IFDIR) == _S_IFDIR) { + ft = file_type::directory; + } + else if ((mode & _S_IFREG) == _S_IFREG) { + ft = file_type::regular; + } + else if ((mode & _S_IFCHR) == _S_IFCHR) { + ft = file_type::character; + } + perms prms = static_cast(mode & 0xfff); + return file_status(ft, prms); +#else + file_type ft = file_type::unknown; + if (S_ISDIR(mode)) { + ft = file_type::directory; + } + else if (S_ISREG(mode)) { + ft = file_type::regular; + } + else if (S_ISCHR(mode)) { + ft = file_type::character; + } + else if (S_ISBLK(mode)) { + ft = file_type::block; + } + else if (S_ISFIFO(mode)) { + ft = file_type::fifo; + } + else if (S_ISLNK(mode)) { + ft = file_type::symlink; + } + else if (S_ISSOCK(mode)) { + ft = file_type::socket; + } + perms prms = static_cast(mode & 0xfff); + return file_status(ft, prms); +#endif +} + +#ifdef GHC_OS_WINDOWS +#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 + +GHC_INLINE std::shared_ptr getReparseData(const path& p, std::error_code& ec) +{ + std::shared_ptr file(CreateFileW(GHC_NATIVEWP(p), 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT | FILE_FLAG_BACKUP_SEMANTICS, 0), CloseHandle); + if (file.get() == INVALID_HANDLE_VALUE) { + ec = detail::make_system_error(); + return nullptr; + } + + std::shared_ptr reparseData((REPARSE_DATA_BUFFER*)std::calloc(1, MAXIMUM_REPARSE_DATA_BUFFER_SIZE), std::free); + ULONG bufferUsed; + if (DeviceIoControl(file.get(), FSCTL_GET_REPARSE_POINT, 0, 0, reparseData.get(), MAXIMUM_REPARSE_DATA_BUFFER_SIZE, &bufferUsed, 0)) { + return reparseData; + } + else { + ec = detail::make_system_error(); + } + return nullptr; +} +#endif + +GHC_INLINE path resolveSymlink(const path& p, std::error_code& ec) +{ +#ifdef GHC_OS_WINDOWS + path result; + auto reparseData = detail::getReparseData(p, ec); + if (!ec) { + if (reparseData && IsReparseTagMicrosoft(reparseData->ReparseTag)) { + switch (reparseData->ReparseTag) { + case IO_REPARSE_TAG_SYMLINK: { + auto printName = std::wstring(&reparseData->SymbolicLinkReparseBuffer.PathBuffer[reparseData->SymbolicLinkReparseBuffer.PrintNameOffset / sizeof(WCHAR)], reparseData->SymbolicLinkReparseBuffer.PrintNameLength / sizeof(WCHAR)); + auto substituteName = + std::wstring(&reparseData->SymbolicLinkReparseBuffer.PathBuffer[reparseData->SymbolicLinkReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)], reparseData->SymbolicLinkReparseBuffer.SubstituteNameLength / sizeof(WCHAR)); + if (detail::endsWith(substituteName, printName) && detail::startsWith(substituteName, std::wstring(L"\\??\\"))) { + result = printName; + } + else { + result = substituteName; + } + if (reparseData->SymbolicLinkReparseBuffer.Flags & 0x1 /*SYMLINK_FLAG_RELATIVE*/) { + result = p.parent_path() / result; + } + break; + } + case IO_REPARSE_TAG_MOUNT_POINT: + result = detail::getFullPathName(GHC_NATIVEWP(p), ec); + //result = std::wstring(&reparseData->MountPointReparseBuffer.PathBuffer[reparseData->MountPointReparseBuffer.SubstituteNameOffset / sizeof(WCHAR)], reparseData->MountPointReparseBuffer.SubstituteNameLength / sizeof(WCHAR)); + break; + default: + break; + } + } + } + return result; +#else + size_t bufferSize = 256; + while (true) { + std::vector buffer(bufferSize, static_cast(0)); + auto rc = ::readlink(p.c_str(), buffer.data(), buffer.size()); + if (rc < 0) { + ec = detail::make_system_error(); + return path(); + } + else if (rc < static_cast(bufferSize)) { + return path(std::string(buffer.data(), static_cast(rc))); + } + bufferSize *= 2; + } + return path(); +#endif +} + +#ifdef GHC_OS_WINDOWS +GHC_INLINE time_t timeFromFILETIME(const FILETIME& ft) +{ + ULARGE_INTEGER ull; + ull.LowPart = ft.dwLowDateTime; + ull.HighPart = ft.dwHighDateTime; + return static_cast(ull.QuadPart / 10000000ULL - 11644473600ULL); +} + +GHC_INLINE void timeToFILETIME(time_t t, FILETIME& ft) +{ + LONGLONG ll; + ll = Int32x32To64(t, 10000000) + 116444736000000000; + ft.dwLowDateTime = static_cast(ll); + ft.dwHighDateTime = static_cast(ll >> 32); +} + +template +GHC_INLINE uintmax_t hard_links_from_INFO(const INFO* info) +{ + return static_cast(-1); +} + +template <> +GHC_INLINE uintmax_t hard_links_from_INFO(const BY_HANDLE_FILE_INFORMATION* info) +{ + return info->nNumberOfLinks; +} + +template +GHC_INLINE DWORD reparse_tag_from_INFO(const INFO*) +{ + return 0; +} + +template <> +GHC_INLINE DWORD reparse_tag_from_INFO(const WIN32_FIND_DATAW* info) +{ + return info->dwReserved0; +} + +template +GHC_INLINE file_status status_from_INFO(const path& p, const INFO* info, std::error_code& ec, uintmax_t* sz = nullptr, time_t* lwt = nullptr) +{ + file_type ft = file_type::unknown; + if (sizeof(INFO) == sizeof(WIN32_FIND_DATAW)) { + if (detail::reparse_tag_from_INFO(info) == IO_REPARSE_TAG_SYMLINK) { + ft = file_type::symlink; + } + } + else { + if ((info->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT)) { + auto reparseData = detail::getReparseData(p, ec); + if (!ec && reparseData && IsReparseTagMicrosoft(reparseData->ReparseTag) && reparseData->ReparseTag == IO_REPARSE_TAG_SYMLINK) { + ft = file_type::symlink; + } + } + } + if (ft == file_type::unknown) { + if ((info->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { + ft = file_type::directory; + } + else { + ft = file_type::regular; + } + } + perms prms = perms::owner_read | perms::group_read | perms::others_read; + if (!(info->dwFileAttributes & FILE_ATTRIBUTE_READONLY)) { + prms = prms | perms::owner_write | perms::group_write | perms::others_write; + } + if (has_executable_extension(p)) { + prms = prms | perms::owner_exec | perms::group_exec | perms::others_exec; + } + if (sz) { + *sz = static_cast(info->nFileSizeHigh) << (sizeof(info->nFileSizeHigh) * 8) | info->nFileSizeLow; + } + if (lwt) { + *lwt = detail::timeFromFILETIME(info->ftLastWriteTime); + } + return file_status(ft, prms); +} + +#endif + +GHC_INLINE bool is_not_found_error(std::error_code& ec) +{ +#ifdef GHC_OS_WINDOWS + return ec.value() == ERROR_FILE_NOT_FOUND || ec.value() == ERROR_PATH_NOT_FOUND || ec.value() == ERROR_INVALID_NAME; +#else + return ec.value() == ENOENT || ec.value() == ENOTDIR; +#endif +} + +GHC_INLINE file_status symlink_status_ex(const path& p, std::error_code& ec, uintmax_t* sz = nullptr, uintmax_t* nhl = nullptr, time_t* lwt = nullptr) noexcept +{ +#ifdef GHC_OS_WINDOWS + file_status fs; + WIN32_FILE_ATTRIBUTE_DATA attr; + if (!GetFileAttributesExW(GHC_NATIVEWP(p), GetFileExInfoStandard, &attr)) { + ec = detail::make_system_error(); + } + else { + ec.clear(); + fs = detail::status_from_INFO(p, &attr, ec, sz, lwt); + if (nhl) { + *nhl = 0; + } + } + if (detail::is_not_found_error(ec)) { + return file_status(file_type::not_found); + } + return ec ? file_status(file_type::none) : fs; +#else + (void)sz; + (void)nhl; + (void)lwt; + struct ::stat fs; + auto result = ::lstat(p.c_str(), &fs); + if (result == 0) { + ec.clear(); + file_status f_s = detail::file_status_from_st_mode(fs.st_mode); + return f_s; + } + ec = detail::make_system_error(); + if (detail::is_not_found_error(ec)) { + return file_status(file_type::not_found, perms::unknown); + } + return file_status(file_type::none); +#endif +} + +GHC_INLINE file_status status_ex(const path& p, std::error_code& ec, file_status* sls = nullptr, uintmax_t* sz = nullptr, uintmax_t* nhl = nullptr, time_t* lwt = nullptr, int recurse_count = 0) noexcept +{ + ec.clear(); +#ifdef GHC_OS_WINDOWS + if (recurse_count > 16) { + ec = detail::make_system_error(0x2A9 /*ERROR_STOPPED_ON_SYMLINK*/); + return file_status(file_type::unknown); + } + WIN32_FILE_ATTRIBUTE_DATA attr; + if (!::GetFileAttributesExW(GHC_NATIVEWP(p), GetFileExInfoStandard, &attr)) { + ec = detail::make_system_error(); + } + else if (attr.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) { + auto reparseData = detail::getReparseData(p, ec); + if (!ec && reparseData && IsReparseTagMicrosoft(reparseData->ReparseTag) && reparseData->ReparseTag == IO_REPARSE_TAG_SYMLINK) { + path target = resolveSymlink(p, ec); + file_status result; + if (!ec && !target.empty()) { + if (sls) { + *sls = status_from_INFO(p, &attr, ec); + } + return detail::status_ex(target, ec, nullptr, sz, nhl, lwt, recurse_count + 1); + } + return file_status(file_type::unknown); + } + } + if (ec) { + if (detail::is_not_found_error(ec)) { + return file_status(file_type::not_found); + } + return file_status(file_type::none); + } + if (nhl) { + *nhl = 0; + } + return detail::status_from_INFO(p, &attr, ec, sz, lwt); +#else + (void)recurse_count; + struct ::stat st; + auto result = ::lstat(p.c_str(), &st); + if (result == 0) { + ec.clear(); + file_status fs = detail::file_status_from_st_mode(st.st_mode); + if (sls) { + *sls = fs; + } + if (fs.type() == file_type::symlink) { + result = ::stat(p.c_str(), &st); + if (result == 0) { + fs = detail::file_status_from_st_mode(st.st_mode); + } + else { + ec = detail::make_system_error(); + if (detail::is_not_found_error(ec)) { + return file_status(file_type::not_found, perms::unknown); + } + return file_status(file_type::none); + } + } + if (sz) { + *sz = static_cast(st.st_size); + } + if (nhl) { + *nhl = st.st_nlink; + } + if (lwt) { + *lwt = st.st_mtime; + } + return fs; + } + else { + ec = detail::make_system_error(); + if (detail::is_not_found_error(ec)) { + return file_status(file_type::not_found, perms::unknown); + } + return file_status(file_type::none); + } +#endif +} + +} // namespace detail + +GHC_INLINE u8arguments::u8arguments(int& argc, char**& argv) + : _argc(argc) + , _argv(argv) + , _refargc(argc) + , _refargv(argv) + , _isvalid(false) +{ +#ifdef GHC_OS_WINDOWS + LPWSTR* p; + p = ::CommandLineToArgvW(::GetCommandLineW(), &argc); + _args.reserve(static_cast(argc)); + _argp.reserve(static_cast(argc)); + for (size_t i = 0; i < static_cast(argc); ++i) { + _args.push_back(detail::toUtf8(std::wstring(p[i]))); + _argp.push_back((char*)_args[i].data()); + } + argv = _argp.data(); + ::LocalFree(p); + _isvalid = true; +#else + std::setlocale(LC_ALL, ""); +#if defined(__ANDROID__) && __ANDROID_API__ < 26 + _isvalid = true; +#else + if (detail::equals_simple_insensitive(::nl_langinfo(CODESET), "UTF-8")) { + _isvalid = true; + } +#endif +#endif +} + +//----------------------------------------------------------------------------- +// [fs.path.construct] constructors and destructor + +GHC_INLINE path::path() noexcept {} + +GHC_INLINE path::path(const path& p) + : _path(p._path) +#if defined(GHC_OS_WINDOWS) && defined(GHC_WIN_AUTO_PREFIX_LONG_PATH) + , _prefixLength(p._prefixLength) +#endif +{ +} + +GHC_INLINE path::path(path&& p) noexcept + : _path(std::move(p._path)) +#if defined(GHC_OS_WINDOWS) && defined(GHC_WIN_AUTO_PREFIX_LONG_PATH) + , _prefixLength(p._prefixLength) +#endif +{ +} + +GHC_INLINE path::path(string_type&& source, format fmt) + : _path(std::move(source)) +{ + postprocess_path_with_format(fmt); +} + +#endif // GHC_EXPAND_IMPL + +#ifdef GHC_WITH_EXCEPTIONS +template +inline path::path(const Source& source, const std::locale& loc, format fmt) + : path(source, fmt) +{ + std::string locName = loc.name(); + if (!(locName.length() >= 5 && (locName.substr(locName.length() - 5) == "UTF-8" || locName.substr(locName.length() - 5) == "utf-8"))) { + throw filesystem_error("This implementation only supports UTF-8 locales!", path(_path), detail::make_error_code(detail::portable_error::not_supported)); + } +} + +template +inline path::path(InputIterator first, InputIterator last, const std::locale& loc, format fmt) + : path(std::basic_string::value_type>(first, last), fmt) +{ + std::string locName = loc.name(); + if (!(locName.length() >= 5 && (locName.substr(locName.length() - 5) == "UTF-8" || locName.substr(locName.length() - 5) == "utf-8"))) { + throw filesystem_error("This implementation only supports UTF-8 locales!", path(_path), detail::make_error_code(detail::portable_error::not_supported)); + } +} +#endif + +#ifdef GHC_EXPAND_IMPL + +GHC_INLINE path::~path() {} + +//----------------------------------------------------------------------------- +// [fs.path.assign] assignments + +GHC_INLINE path& path::operator=(const path& p) +{ + _path = p._path; +#if defined(GHC_OS_WINDOWS) && defined(GHC_WIN_AUTO_PREFIX_LONG_PATH) + _prefixLength = p._prefixLength; +#endif + return *this; +} + +GHC_INLINE path& path::operator=(path&& p) noexcept +{ + _path = std::move(p._path); +#if defined(GHC_OS_WINDOWS) && defined(GHC_WIN_AUTO_PREFIX_LONG_PATH) + _prefixLength = p._prefixLength; +#endif + return *this; +} + +GHC_INLINE path& path::operator=(path::string_type&& source) +{ + return assign(source); +} + +GHC_INLINE path& path::assign(path::string_type&& source) +{ + _path = std::move(source); + postprocess_path_with_format(native_format); + return *this; +} + +#endif // GHC_EXPAND_IMPL + +template +inline path& path::operator=(const Source& source) +{ + return assign(source); +} + +template +inline path& path::assign(const Source& source) +{ +#ifdef GHC_USE_WCHAR_T + _path.assign(detail::toWChar(source)); +#else + _path.assign(detail::toUtf8(source)); +#endif + postprocess_path_with_format(native_format); + return *this; +} + +template <> +inline path& path::assign(const path& source) +{ + _path = source._path; +#if defined(GHC_OS_WINDOWS) && defined(GHC_WIN_AUTO_PREFIX_LONG_PATH) + _prefixLength = source._prefixLength; +#endif + return *this; +} + +template +inline path& path::assign(InputIterator first, InputIterator last) +{ + _path.assign(first, last); + postprocess_path_with_format(native_format); + return *this; +} + +#ifdef GHC_EXPAND_IMPL + +//----------------------------------------------------------------------------- +// [fs.path.append] appends + +GHC_INLINE path& path::operator/=(const path& p) +{ + if (p.empty()) { + // was: if ((!has_root_directory() && is_absolute()) || has_filename()) + if (!_path.empty() && _path[_path.length() - 1] != preferred_separator && _path[_path.length() - 1] != ':') { + _path += preferred_separator; + } + return *this; + } + if ((p.is_absolute() && (_path != root_name()._path || p._path != "/")) || (p.has_root_name() && p.root_name() != root_name())) { + assign(p); + return *this; + } + if (p.has_root_directory()) { + assign(root_name()); + } + else if ((!has_root_directory() && is_absolute()) || has_filename()) { + _path += preferred_separator; + } + auto iter = p.begin(); + bool first = true; + if (p.has_root_name()) { + ++iter; + } + while (iter != p.end()) { + if (!first && !(!_path.empty() && _path[_path.length() - 1] == preferred_separator)) { + _path += preferred_separator; + } + first = false; + _path += (*iter++).native(); + } + check_long_path(); + return *this; +} + +GHC_INLINE void path::append_name(const value_type* name) +{ + if (_path.empty()) { + this->operator/=(path(name)); + } + else { + if (_path.back() != path::preferred_separator) { + _path.push_back(path::preferred_separator); + } + _path += name; + check_long_path(); + } +} + +#endif // GHC_EXPAND_IMPL + +template +inline path& path::operator/=(const Source& source) +{ + return append(source); +} + +template +inline path& path::append(const Source& source) +{ + return this->operator/=(path(source)); +} + +template <> +inline path& path::append(const path& p) +{ + return this->operator/=(p); +} + +template +inline path& path::append(InputIterator first, InputIterator last) +{ + std::basic_string::value_type> part(first, last); + return append(part); +} + +#ifdef GHC_EXPAND_IMPL + +//----------------------------------------------------------------------------- +// [fs.path.concat] concatenation + +GHC_INLINE path& path::operator+=(const path& x) +{ + return concat(x._path); +} + +GHC_INLINE path& path::operator+=(const string_type& x) +{ + return concat(x); +} + +#ifdef GHC_WITH_STRING_VIEW +GHC_INLINE path& path::operator+=(basic_string_view x) +{ + return concat(x); +} +#endif + +GHC_INLINE path& path::operator+=(const value_type* x) +{ +#ifdef GHC_WITH_STRING_VIEW + basic_string_view part(x); +#else + string_type part(x); +#endif + return concat(part); +} + +GHC_INLINE path& path::operator+=(value_type x) +{ +#ifdef GHC_OS_WINDOWS + if (x == generic_separator) { + x = preferred_separator; + } +#endif + if (_path.empty() || _path.back() != preferred_separator) { + _path += x; + } + check_long_path(); + return *this; +} + +#endif // GHC_EXPAND_IMPL + +template +inline path::path_from_string& path::operator+=(const Source& x) +{ + return concat(x); +} + +template +inline path::path_type_EcharT& path::operator+=(EcharT x) +{ +#ifdef GHC_WITH_STRING_VIEW + basic_string_view part(&x, 1); +#else + std::basic_string part(1, x); +#endif + concat(part); + return *this; +} + +template +inline path& path::concat(const Source& x) +{ + path p(x); + _path += p._path; + postprocess_path_with_format(native_format); + return *this; +} +template +inline path& path::concat(InputIterator first, InputIterator last) +{ + _path.append(first, last); + postprocess_path_with_format(native_format); + return *this; +} + +#ifdef GHC_EXPAND_IMPL + +//----------------------------------------------------------------------------- +// [fs.path.modifiers] modifiers +GHC_INLINE void path::clear() noexcept +{ + _path.clear(); +#if defined(GHC_OS_WINDOWS) && defined(GHC_WIN_AUTO_PREFIX_LONG_PATH) + _prefixLength = 0; +#endif +} + +GHC_INLINE path& path::make_preferred() +{ + // as this filesystem implementation only uses generic_format + // internally, this must be a no-op + return *this; +} + +GHC_INLINE path& path::remove_filename() +{ + if (has_filename()) { + _path.erase(_path.size() - filename()._path.size()); + } + return *this; +} + +GHC_INLINE path& path::replace_filename(const path& replacement) +{ + remove_filename(); + return append(replacement); +} + +GHC_INLINE path& path::replace_extension(const path& replacement) +{ + if (has_extension()) { + _path.erase(_path.size() - extension()._path.size()); + } + if (!replacement.empty() && replacement._path[0] != '.') { + _path += '.'; + } + return concat(replacement); +} + +GHC_INLINE void path::swap(path& rhs) noexcept +{ + _path.swap(rhs._path); +#if defined(GHC_OS_WINDOWS) && defined(GHC_WIN_AUTO_PREFIX_LONG_PATH) + std::swap(_prefixLength, rhs._prefixLength); +#endif +} + +//----------------------------------------------------------------------------- +// [fs.path.native.obs] native format observers +GHC_INLINE const path::string_type& path::native() const noexcept +{ + return _path; +} + +GHC_INLINE const path::value_type* path::c_str() const noexcept +{ + return native().c_str(); +} + +GHC_INLINE path::operator path::string_type() const +{ + return native(); +} + +#endif // GHC_EXPAND_IMPL + +template +inline std::basic_string path::string(const Allocator& a) const +{ +#ifdef GHC_USE_WCHAR_T + return detail::fromWChar>(_path, a); +#else + return detail::fromUtf8>(_path, a); +#endif +} + +#ifdef GHC_EXPAND_IMPL + +GHC_INLINE std::string path::string() const +{ +#ifdef GHC_USE_WCHAR_T + return detail::toUtf8(native()); +#else + return native(); +#endif +} + +GHC_INLINE std::wstring path::wstring() const +{ +#ifdef GHC_USE_WCHAR_T + return native(); +#else + return detail::fromUtf8(native()); +#endif +} + +#if defined(__cpp_lib_char8_t) && !defined(GHC_FILESYSTEM_ENFORCE_CPP17_API) +GHC_INLINE std::u8string path::u8string() const +{ +#ifdef GHC_USE_WCHAR_T + return std::u8string(reinterpret_cast(detail::toUtf8(native()).c_str())); +#else + return std::u8string(reinterpret_cast(c_str())); +#endif +} +#else +GHC_INLINE std::string path::u8string() const +{ +#ifdef GHC_USE_WCHAR_T + return detail::toUtf8(native()); +#else + return native(); +#endif +} +#endif + +GHC_INLINE std::u16string path::u16string() const +{ + // TODO: optimize + return detail::fromUtf8(string()); +} + +GHC_INLINE std::u32string path::u32string() const +{ + // TODO: optimize + return detail::fromUtf8(string()); +} + +#endif // GHC_EXPAND_IMPL + +//----------------------------------------------------------------------------- +// [fs.path.generic.obs] generic format observers +template +inline std::basic_string path::generic_string(const Allocator& a) const +{ +#ifdef GHC_OS_WINDOWS +#ifdef GHC_USE_WCHAR_T + auto result = detail::fromWChar, path::string_type>(_path, a); +#else + auto result = detail::fromUtf8>(_path, a); +#endif + for (auto& c : result) { + if (c == preferred_separator) { + c = generic_separator; + } + } + return result; +#else + return detail::fromUtf8>(_path, a); +#endif +} + +#ifdef GHC_EXPAND_IMPL + +GHC_INLINE std::string path::generic_string() const +{ +#ifdef GHC_OS_WINDOWS + return generic_string(); +#else + return _path; +#endif +} + +GHC_INLINE std::wstring path::generic_wstring() const +{ +#ifdef GHC_OS_WINDOWS + return generic_string(); +#else + return detail::fromUtf8(_path); +#endif +} // namespace filesystem + +#if defined(__cpp_lib_char8_t) && !defined(GHC_FILESYSTEM_ENFORCE_CPP17_API) +GHC_INLINE std::u8string path::generic_u8string() const +{ +#ifdef GHC_OS_WINDOWS + return generic_string(); +#else + return std::u8string(reinterpret_cast(_path.c_str())); +#endif +} +#else +GHC_INLINE std::string path::generic_u8string() const +{ +#ifdef GHC_OS_WINDOWS + return generic_string(); +#else + return _path; +#endif +} +#endif + +GHC_INLINE std::u16string path::generic_u16string() const +{ +#ifdef GHC_OS_WINDOWS + return generic_string(); +#else + return detail::fromUtf8(_path); +#endif +} + +GHC_INLINE std::u32string path::generic_u32string() const +{ +#ifdef GHC_OS_WINDOWS + return generic_string(); +#else + return detail::fromUtf8(_path); +#endif +} + +//----------------------------------------------------------------------------- +// [fs.path.compare] compare +GHC_INLINE int path::compare(const path& p) const noexcept +{ +#ifdef LWG_2936_BEHAVIOUR + auto rnl1 = root_name_length(); + auto rnl2 = p.root_name_length(); +#ifdef GHC_OS_WINDOWS + auto rnc = detail::compare_simple_insensitive(_path.c_str(), rnl1, p._path.c_str(), rnl2); +#else + auto rnc = _path.compare(0, rnl1, p._path, 0, (std::min(rnl1, rnl2))); +#endif + if (rnc) { + return rnc; + } + bool hrd1 = has_root_directory(), hrd2 = p.has_root_directory(); + if (hrd1 != hrd2) { + return hrd1 ? 1 : -1; + } + if (hrd1) { + ++rnl1; + ++rnl2; + } + auto iter1 = _path.begin() + static_cast(rnl1); + auto iter2 = p._path.begin() + static_cast(rnl2); + while (iter1 != _path.end() && iter2 != p._path.end() && *iter1 == *iter2) { + ++iter1; + ++iter2; + } + if (iter1 == _path.end()) { + return iter2 == p._path.end() ? 0 : -1; + } + if (iter2 == p._path.end()) { + return 1; + } + if (*iter1 == preferred_separator) { + return -1; + } + if (*iter2 == preferred_separator) { + return 1; + } + return *iter1 < *iter2 ? -1 : 1; +#else // LWG_2936_BEHAVIOUR +#ifdef GHC_OS_WINDOWS + auto rnl1 = root_name_length(); + auto rnl2 = p.root_name_length(); + auto rnc = detail::compare_simple_insensitive(_path.c_str(), rnl1, p._path.c_str(), rnl2); + if (rnc) { + return rnc; + } + return _path.compare(rnl1, std::string::npos, p._path, rnl2, std::string::npos); +#else + return _path.compare(p._path); +#endif +#endif +} + +GHC_INLINE int path::compare(const string_type& s) const +{ + return compare(path(s)); +} + +#ifdef GHC_WITH_STRING_VIEW +GHC_INLINE int path::compare(basic_string_view s) const +{ + return compare(path(s)); +} +#endif + +GHC_INLINE int path::compare(const value_type* s) const +{ + return compare(path(s)); +} + +//----------------------------------------------------------------------------- +// [fs.path.decompose] decomposition +#ifdef GHC_OS_WINDOWS +GHC_INLINE void path::handle_prefixes() +{ +#if defined(GHC_WIN_AUTO_PREFIX_LONG_PATH) + _prefixLength = 0; + if (_path.length() >= 6 && _path[2] == '?' && std::toupper(static_cast(_path[4])) >= 'A' && std::toupper(static_cast(_path[4])) <= 'Z' && _path[5] == ':') { + if (detail::startsWith(_path, impl_string_type(GHC_PLATFORM_LITERAL("\\\\?\\"))) || detail::startsWith(_path, impl_string_type(GHC_PLATFORM_LITERAL("\\??\\")))) { + _prefixLength = 4; + } + } +#endif // GHC_WIN_AUTO_PREFIX_LONG_PATH +} +#endif + +GHC_INLINE path::string_type::size_type path::root_name_length() const noexcept +{ +#ifdef GHC_OS_WINDOWS + if (_path.length() >= _prefixLength + 2 && std::toupper(static_cast(_path[_prefixLength])) >= 'A' && std::toupper(static_cast(_path[_prefixLength])) <= 'Z' && _path[_prefixLength + 1] == ':') { + return 2; + } +#endif + if (_path.length() > _prefixLength + 2 && _path[_prefixLength] == preferred_separator && _path[_prefixLength + 1] == preferred_separator && _path[_prefixLength + 2] != preferred_separator && std::isprint(_path[_prefixLength + 2])) { + impl_string_type::size_type pos = _path.find(preferred_separator, _prefixLength + 3); + if (pos == impl_string_type::npos) { + return _path.length(); + } + else { + return pos; + } + } + return 0; +} + +GHC_INLINE path path::root_name() const +{ + return path(_path.substr(_prefixLength, root_name_length()), native_format); +} + +GHC_INLINE path path::root_directory() const +{ + if (has_root_directory()) { + static const path _root_dir(std::string(1, preferred_separator), native_format); + return _root_dir; + } + return path(); +} + +GHC_INLINE path path::root_path() const +{ + return path(root_name().string() + root_directory().string(), native_format); +} + +GHC_INLINE path path::relative_path() const +{ + auto rootPathLen = _prefixLength + root_name_length() + (has_root_directory() ? 1 : 0); + return path(_path.substr((std::min)(rootPathLen, _path.length())), generic_format); +} + +GHC_INLINE path path::parent_path() const +{ + auto rootPathLen = _prefixLength + root_name_length() + (has_root_directory() ? 1 : 0); + if (rootPathLen < _path.length()) { + if (empty()) { + return path(); + } + else { + auto piter = end(); + auto iter = piter.decrement(_path.end()); + if (iter > _path.begin() + static_cast(rootPathLen) && *iter != preferred_separator) { + --iter; + } + return path(_path.begin(), iter, native_format); + } + } + else { + return *this; + } +} + +GHC_INLINE path path::filename() const +{ + return !has_relative_path() ? path() : path(*--end()); +} + +GHC_INLINE path path::stem() const +{ + impl_string_type fn = filename().native(); + if (fn != "." && fn != "..") { + impl_string_type::size_type pos = fn.rfind('.'); + if (pos != impl_string_type::npos && pos > 0) { + return path{fn.substr(0, pos), native_format}; + } + } + return path{fn, native_format}; +} + +GHC_INLINE path path::extension() const +{ + if (has_relative_path()) { + auto iter = end(); + const auto& fn = *--iter; + impl_string_type::size_type pos = fn._path.rfind('.'); + if (pos != std::string::npos && pos > 0) { + return path(fn._path.substr(pos), native_format); + } + } + return path(); +} + +#ifdef GHC_OS_WINDOWS +namespace detail { +GHC_INLINE bool has_executable_extension(const path& p) +{ + if (p.has_relative_path()) { + auto iter = p.end(); + const auto& fn = *--iter; + auto pos = fn._path.find_last_of('.'); + if (pos == std::string::npos || pos == 0 || fn._path.length() - pos != 3) { + return false; + } + const path::value_type* ext = fn._path.c_str() + pos + 1; + if (detail::equals_simple_insensitive(ext, GHC_PLATFORM_LITERAL("exe")) || detail::equals_simple_insensitive(ext, GHC_PLATFORM_LITERAL("cmd")) || detail::equals_simple_insensitive(ext, GHC_PLATFORM_LITERAL("bat")) || detail::equals_simple_insensitive(ext, GHC_PLATFORM_LITERAL("com"))) { + return true; + } + } + return false; +} +} // namespace detail +#endif + +//----------------------------------------------------------------------------- +// [fs.path.query] query +GHC_INLINE bool path::empty() const noexcept +{ + return _path.empty(); +} + +GHC_INLINE bool path::has_root_name() const +{ + return root_name_length() > 0; +} + +GHC_INLINE bool path::has_root_directory() const +{ + auto rootLen = _prefixLength + root_name_length(); + return (_path.length() > rootLen && _path[rootLen] == preferred_separator); +} + +GHC_INLINE bool path::has_root_path() const +{ + return has_root_name() || has_root_directory(); +} + +GHC_INLINE bool path::has_relative_path() const +{ + auto rootPathLen = _prefixLength + root_name_length() + (has_root_directory() ? 1 : 0); + return rootPathLen < _path.length(); +} + +GHC_INLINE bool path::has_parent_path() const +{ + return !parent_path().empty(); +} + +GHC_INLINE bool path::has_filename() const +{ + return has_relative_path() && !filename().empty(); +} + +GHC_INLINE bool path::has_stem() const +{ + return !stem().empty(); +} + +GHC_INLINE bool path::has_extension() const +{ + return !extension().empty(); +} + +GHC_INLINE bool path::is_absolute() const +{ +#ifdef GHC_OS_WINDOWS + return has_root_name() && has_root_directory(); +#else + return has_root_directory(); +#endif +} + +GHC_INLINE bool path::is_relative() const +{ + return !is_absolute(); +} + +//----------------------------------------------------------------------------- +// [fs.path.gen] generation +GHC_INLINE path path::lexically_normal() const +{ + path dest; + bool lastDotDot = false; + for (string_type s : *this) { + if (s == ".") { + dest /= ""; + continue; + } + else if (s == ".." && !dest.empty()) { + auto root = root_path(); + if (dest == root) { + continue; + } + else if (*(--dest.end()) != "..") { + if (dest._path.back() == preferred_separator) { + dest._path.pop_back(); + } + dest.remove_filename(); + continue; + } + } + if (!(s.empty() && lastDotDot)) { + dest /= s; + } + lastDotDot = s == ".."; + } + if (dest.empty()) { + dest = "."; + } + return dest; +} + +GHC_INLINE path path::lexically_relative(const path& base) const +{ + if (root_name() != base.root_name() || is_absolute() != base.is_absolute() || (!has_root_directory() && base.has_root_directory())) { + return path(); + } + const_iterator a = begin(), b = base.begin(); + while (a != end() && b != base.end() && *a == *b) { + ++a; + ++b; + } + if (a == end() && b == base.end()) { + return path("."); + } + int count = 0; + for (const auto& element : input_iterator_range(b, base.end())) { + if (element != "." && element != "" && element != "..") { + ++count; + } + else if (element == "..") { + --count; + } + } + if (count < 0) { + return path(); + } + path result; + for (int i = 0; i < count; ++i) { + result /= ".."; + } + for (const auto& element : input_iterator_range(a, end())) { + result /= element; + } + return result; +} + +GHC_INLINE path path::lexically_proximate(const path& base) const +{ + path result = lexically_relative(base); + return result.empty() ? *this : result; +} + +//----------------------------------------------------------------------------- +// [fs.path.itr] iterators +GHC_INLINE path::iterator::iterator() {} + +GHC_INLINE path::iterator::iterator(const path& p, const impl_string_type::const_iterator& pos) + : _first(p._path.begin()) + , _last(p._path.end()) + , _prefix(_first + static_cast(p._prefixLength)) + , _root(p.has_root_directory() ? _first + static_cast(p._prefixLength + p.root_name_length()) : _last) + , _iter(pos) +{ + if(pos != _last) { + updateCurrent(); + } +} + +GHC_INLINE path::impl_string_type::const_iterator path::iterator::increment(const path::impl_string_type::const_iterator& pos) const +{ + path::impl_string_type::const_iterator i = pos; + bool fromStart = i == _first || i == _prefix; + if (i != _last) { + if (fromStart && i == _first && _prefix > _first) { + i = _prefix; + } + else if (*i++ == preferred_separator) { + // we can only sit on a slash if it is a network name or a root + if (i != _last && *i == preferred_separator) { + if (fromStart && !(i + 1 != _last && *(i + 1) == preferred_separator)) { + // leadind double slashes detected, treat this and the + // following until a slash as one unit + i = std::find(++i, _last, preferred_separator); + } + else { + // skip redundant slashes + while (i != _last && *i == preferred_separator) { + ++i; + } + } + } + } + else { + if (fromStart && i != _last && *i == ':') { + ++i; + } + else { + i = std::find(i, _last, preferred_separator); + } + } + } + return i; +} + +GHC_INLINE path::impl_string_type::const_iterator path::iterator::decrement(const path::impl_string_type::const_iterator& pos) const +{ + path::impl_string_type::const_iterator i = pos; + if (i != _first) { + --i; + // if this is now the root slash or the trailing slash, we are done, + // else check for network name + if (i != _root && (pos != _last || *i != preferred_separator)) { +#ifdef GHC_OS_WINDOWS + static const impl_string_type seps = GHC_PLATFORM_LITERAL("\\:"); + i = std::find_first_of(std::reverse_iterator(i), std::reverse_iterator(_first), seps.begin(), seps.end()).base(); + if (i > _first && *i == ':') { + i++; + } +#else + i = std::find(std::reverse_iterator(i), std::reverse_iterator(_first), preferred_separator).base(); +#endif + // Now we have to check if this is a network name + if (i - _first == 2 && *_first == preferred_separator && *(_first + 1) == preferred_separator) { + i -= 2; + } + } + } + return i; +} + +GHC_INLINE void path::iterator::updateCurrent() +{ + if ((_iter == _last) || (_iter != _first && _iter != _last && (*_iter == preferred_separator && _iter != _root) && (_iter + 1 == _last))) { + _current.clear(); + } + else { + _current.assign(_iter, increment(_iter)); + } +} + +GHC_INLINE path::iterator& path::iterator::operator++() +{ + _iter = increment(_iter); + while (_iter != _last && // we didn't reach the end + _iter != _root && // this is not a root position + *_iter == preferred_separator && // we are on a separator + (_iter + 1) != _last // the slash is not the last char + ) { + ++_iter; + } + updateCurrent(); + return *this; +} + +GHC_INLINE path::iterator path::iterator::operator++(int) +{ + path::iterator i{*this}; + ++(*this); + return i; +} + +GHC_INLINE path::iterator& path::iterator::operator--() +{ + _iter = decrement(_iter); + updateCurrent(); + return *this; +} + +GHC_INLINE path::iterator path::iterator::operator--(int) +{ + auto i = *this; + --(*this); + return i; +} + +GHC_INLINE bool path::iterator::operator==(const path::iterator& other) const +{ + return _iter == other._iter; +} + +GHC_INLINE bool path::iterator::operator!=(const path::iterator& other) const +{ + return _iter != other._iter; +} + +GHC_INLINE path::iterator::reference path::iterator::operator*() const +{ + return _current; +} + +GHC_INLINE path::iterator::pointer path::iterator::operator->() const +{ + return &_current; +} + +GHC_INLINE path::iterator path::begin() const +{ + return iterator(*this, _path.begin()); +} + +GHC_INLINE path::iterator path::end() const +{ + return iterator(*this, _path.end()); +} + +//----------------------------------------------------------------------------- +// [fs.path.nonmember] path non-member functions +GHC_INLINE void swap(path& lhs, path& rhs) noexcept +{ + swap(lhs._path, rhs._path); +} + +GHC_INLINE size_t hash_value(const path& p) noexcept +{ + return std::hash()(p.generic_string()); +} + +#ifdef GHC_HAS_THREEWAY_COMP +GHC_INLINE std::strong_ordering operator<=>(const path& lhs, const path& rhs) noexcept +{ + return lhs.compare(rhs) <=> 0; +} +#endif + +GHC_INLINE bool operator==(const path& lhs, const path& rhs) noexcept +{ + return lhs.compare(rhs) == 0; +} + +GHC_INLINE bool operator!=(const path& lhs, const path& rhs) noexcept +{ + return !(lhs == rhs); +} + +GHC_INLINE bool operator<(const path& lhs, const path& rhs) noexcept +{ + return lhs.compare(rhs) < 0; +} + +GHC_INLINE bool operator<=(const path& lhs, const path& rhs) noexcept +{ + return lhs.compare(rhs) <= 0; +} + +GHC_INLINE bool operator>(const path& lhs, const path& rhs) noexcept +{ + return lhs.compare(rhs) > 0; +} + +GHC_INLINE bool operator>=(const path& lhs, const path& rhs) noexcept +{ + return lhs.compare(rhs) >= 0; +} + +GHC_INLINE path operator/(const path& lhs, const path& rhs) +{ + path result(lhs); + result /= rhs; + return result; +} + +#endif // GHC_EXPAND_IMPL + +//----------------------------------------------------------------------------- +// [fs.path.io] path inserter and extractor +template +inline std::basic_ostream& operator<<(std::basic_ostream& os, const path& p) +{ + os << "\""; + auto ps = p.string(); + for (auto c : ps) { + if (c == '"' || c == '\\') { + os << '\\'; + } + os << c; + } + os << "\""; + return os; +} + +template +inline std::basic_istream& operator>>(std::basic_istream& is, path& p) +{ + std::basic_string tmp; + charT c; + is >> c; + if (c == '"') { + auto sf = is.flags(); + is >> std::noskipws; + while (is) { + auto c2 = is.get(); + if (is) { + if (c2 == '\\') { + c2 = is.get(); + if (is) { + tmp += static_cast(c2); + } + } + else if (c2 == '"') { + break; + } + else { + tmp += static_cast(c2); + } + } + } + if ((sf & std::ios_base::skipws) == std::ios_base::skipws) { + is >> std::skipws; + } + p = path(tmp); + } + else { + is >> tmp; + p = path(static_cast(c) + tmp); + } + return is; +} + +#ifdef GHC_EXPAND_IMPL + +//----------------------------------------------------------------------------- +// [fs.class.filesystem_error] Class filesystem_error +GHC_INLINE filesystem_error::filesystem_error(const std::string& what_arg, std::error_code ec) + : std::system_error(ec, what_arg) + , _what_arg(what_arg) + , _ec(ec) +{ +} + +GHC_INLINE filesystem_error::filesystem_error(const std::string& what_arg, const path& p1, std::error_code ec) + : std::system_error(ec, what_arg) + , _what_arg(what_arg) + , _ec(ec) + , _p1(p1) +{ + if (!_p1.empty()) { + _what_arg += ": '" + _p1.string() + "'"; + } +} + +GHC_INLINE filesystem_error::filesystem_error(const std::string& what_arg, const path& p1, const path& p2, std::error_code ec) + : std::system_error(ec, what_arg) + , _what_arg(what_arg) + , _ec(ec) + , _p1(p1) + , _p2(p2) +{ + if (!_p1.empty()) { + _what_arg += ": '" + _p1.string() + "'"; + } + if (!_p2.empty()) { + _what_arg += ", '" + _p2.string() + "'"; + } +} + +GHC_INLINE const path& filesystem_error::path1() const noexcept +{ + return _p1; +} + +GHC_INLINE const path& filesystem_error::path2() const noexcept +{ + return _p2; +} + +GHC_INLINE const char* filesystem_error::what() const noexcept +{ + return _what_arg.c_str(); +} + +//----------------------------------------------------------------------------- +// [fs.op.funcs] filesystem operations +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE path absolute(const path& p) +{ + std::error_code ec; + path result = absolute(p, ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), p, ec); + } + return result; +} +#endif + +GHC_INLINE path absolute(const path& p, std::error_code& ec) +{ + ec.clear(); +#ifdef GHC_OS_WINDOWS + if (p.empty()) { + return absolute(current_path(ec), ec) / ""; + } + ULONG size = ::GetFullPathNameW(GHC_NATIVEWP(p), 0, 0, 0); + if (size) { + std::vector buf(size, 0); + ULONG s2 = GetFullPathNameW(GHC_NATIVEWP(p), size, buf.data(), nullptr); + if (s2 && s2 < size) { + path result = path(std::wstring(buf.data(), s2)); + if (p.filename() == ".") { + result /= "."; + } + return result; + } + } + ec = detail::make_system_error(); + return path(); +#else + path base = current_path(ec); + if (!ec) { + if (p.empty()) { + return base / p; + } + if (p.has_root_name()) { + if (p.has_root_directory()) { + return p; + } + else { + return p.root_name() / base.root_directory() / base.relative_path() / p.relative_path(); + } + } + else { + if (p.has_root_directory()) { + return base.root_name() / p; + } + else { + return base / p; + } + } + } + ec = detail::make_system_error(); + return path(); +#endif +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE path canonical(const path& p) +{ + std::error_code ec; + auto result = canonical(p, ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), p, ec); + } + return result; +} +#endif + +GHC_INLINE path canonical(const path& p, std::error_code& ec) +{ + if (p.empty()) { + ec = detail::make_error_code(detail::portable_error::not_found); + return path(); + } + path work = p.is_absolute() ? p : absolute(p, ec); + path result; + + auto fs = status(work, ec); + if (ec) { + return path(); + } + if (fs.type() == file_type::not_found) { + ec = detail::make_error_code(detail::portable_error::not_found); + return path(); + } + bool redo; + do { + auto rootPathLen = work._prefixLength + work.root_name_length() + (work.has_root_directory() ? 1 : 0); + redo = false; + result.clear(); + for (auto pe : work) { + if (pe.empty() || pe == ".") { + continue; + } + else if (pe == "..") { + result = result.parent_path(); + continue; + } + else if ((result / pe).string().length() <= rootPathLen) { + result /= pe; + continue; + } + auto sls = symlink_status(result / pe, ec); + if (ec) { + return path(); + } + if (is_symlink(sls)) { + redo = true; + auto target = read_symlink(result / pe, ec); + if (ec) { + return path(); + } + if (target.is_absolute()) { + result = target; + continue; + } + else { + result /= target; + continue; + } + } + else { + result /= pe; + } + } + work = result; + } while (redo); + ec.clear(); + return result; +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE void copy(const path& from, const path& to) +{ + copy(from, to, copy_options::none); +} + +GHC_INLINE void copy(const path& from, const path& to, copy_options options) +{ + std::error_code ec; + copy(from, to, options, ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), from, to, ec); + } +} +#endif + +GHC_INLINE void copy(const path& from, const path& to, std::error_code& ec) noexcept +{ + copy(from, to, copy_options::none, ec); +} + +GHC_INLINE void copy(const path& from, const path& to, copy_options options, std::error_code& ec) noexcept +{ + std::error_code tec; + file_status fs_from, fs_to; + ec.clear(); + if ((options & (copy_options::skip_symlinks | copy_options::copy_symlinks | copy_options::create_symlinks)) != copy_options::none) { + fs_from = symlink_status(from, ec); + } + else { + fs_from = status(from, ec); + } + if (!exists(fs_from)) { + if (!ec) { + ec = detail::make_error_code(detail::portable_error::not_found); + } + return; + } + if ((options & (copy_options::skip_symlinks | copy_options::create_symlinks)) != copy_options::none) { + fs_to = symlink_status(to, tec); + } + else { + fs_to = status(to, tec); + } + if (is_other(fs_from) || is_other(fs_to) || (is_directory(fs_from) && is_regular_file(fs_to)) || (exists(fs_to) && equivalent(from, to, ec))) { + ec = detail::make_error_code(detail::portable_error::invalid_argument); + } + else if (is_symlink(fs_from)) { + if ((options & copy_options::skip_symlinks) == copy_options::none) { + if (!exists(fs_to) && (options & copy_options::copy_symlinks) != copy_options::none) { + copy_symlink(from, to, ec); + } + else { + ec = detail::make_error_code(detail::portable_error::invalid_argument); + } + } + } + else if (is_regular_file(fs_from)) { + if ((options & copy_options::directories_only) == copy_options::none) { + if ((options & copy_options::create_symlinks) != copy_options::none) { + create_symlink(from.is_absolute() ? from : canonical(from, ec), to, ec); + } +#ifndef GHC_OS_WEB + else if ((options & copy_options::create_hard_links) != copy_options::none) { + create_hard_link(from, to, ec); + } +#endif + else if (is_directory(fs_to)) { + copy_file(from, to / from.filename(), options, ec); + } + else { + copy_file(from, to, options, ec); + } + } + } +#ifdef LWG_2682_BEHAVIOUR + else if (is_directory(fs_from) && (options & copy_options::create_symlinks) != copy_options::none) { + ec = detail::make_error_code(detail::portable_error::is_a_directory); + } +#endif + else if (is_directory(fs_from) && (options == copy_options::none || (options & copy_options::recursive) != copy_options::none)) { + if (!exists(fs_to)) { + create_directory(to, from, ec); + if (ec) { + return; + } + } + for (auto iter = directory_iterator(from, ec); iter != directory_iterator(); iter.increment(ec)) { + if (!ec) { + copy(iter->path(), to / iter->path().filename(), options | static_cast(0x8000), ec); + } + if (ec) { + return; + } + } + } + return; +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE bool copy_file(const path& from, const path& to) +{ + return copy_file(from, to, copy_options::none); +} + +GHC_INLINE bool copy_file(const path& from, const path& to, copy_options option) +{ + std::error_code ec; + auto result = copy_file(from, to, option, ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), from, to, ec); + } + return result; +} +#endif + +GHC_INLINE bool copy_file(const path& from, const path& to, std::error_code& ec) noexcept +{ + return copy_file(from, to, copy_options::none, ec); +} + +GHC_INLINE bool copy_file(const path& from, const path& to, copy_options options, std::error_code& ec) noexcept +{ + std::error_code tecf, tect; + auto sf = status(from, tecf); + auto st = status(to, tect); + bool overwrite = false; + ec.clear(); + if (!is_regular_file(sf)) { + ec = tecf; + return false; + } + if (exists(st) && (!is_regular_file(st) || equivalent(from, to, ec) || (options & (copy_options::skip_existing | copy_options::overwrite_existing | copy_options::update_existing)) == copy_options::none)) { + ec = tect ? tect : detail::make_error_code(detail::portable_error::exists); + return false; + } + if (exists(st)) { + if ((options & copy_options::update_existing) == copy_options::update_existing) { + auto from_time = last_write_time(from, ec); + if (ec) { + ec = detail::make_system_error(); + return false; + } + auto to_time = last_write_time(to, ec); + if (ec) { + ec = detail::make_system_error(); + return false; + } + if (from_time <= to_time) { + return false; + } + } + overwrite = true; + } +#ifdef GHC_OS_WINDOWS + if (!::CopyFileW(GHC_NATIVEWP(from), GHC_NATIVEWP(to), !overwrite)) { + ec = detail::make_system_error(); + return false; + } + return true; +#else + std::vector buffer(16384, '\0'); + int in = -1, out = -1; + if ((in = ::open(from.c_str(), O_RDONLY)) < 0) { + ec = detail::make_system_error(); + return false; + } + int mode = O_CREAT | O_WRONLY | O_TRUNC; + if (!overwrite) { + mode |= O_EXCL; + } + if ((out = ::open(to.c_str(), mode, static_cast(sf.permissions() & perms::all))) < 0) { + ec = detail::make_system_error(); + ::close(in); + return false; + } + ssize_t br, bw; + while ((br = ::read(in, buffer.data(), buffer.size())) > 0) { + ssize_t offset = 0; + do { + if ((bw = ::write(out, buffer.data() + offset, static_cast(br))) > 0) { + br -= bw; + offset += bw; + } + else if (bw < 0) { + ec = detail::make_system_error(); + ::close(in); + ::close(out); + return false; + } + } while (br); + } + ::close(in); + ::close(out); + return true; +#endif +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE void copy_symlink(const path& existing_symlink, const path& new_symlink) +{ + std::error_code ec; + copy_symlink(existing_symlink, new_symlink, ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), existing_symlink, new_symlink, ec); + } +} +#endif + +GHC_INLINE void copy_symlink(const path& existing_symlink, const path& new_symlink, std::error_code& ec) noexcept +{ + ec.clear(); + auto to = read_symlink(existing_symlink, ec); + if (!ec) { + if (exists(to, ec) && is_directory(to, ec)) { + create_directory_symlink(to, new_symlink, ec); + } + else { + create_symlink(to, new_symlink, ec); + } + } +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE bool create_directories(const path& p) +{ + std::error_code ec; + auto result = create_directories(p, ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), p, ec); + } + return result; +} +#endif + +GHC_INLINE bool create_directories(const path& p, std::error_code& ec) noexcept +{ + path current; + ec.clear(); + bool didCreate = false; + 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; +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE bool create_directory(const path& p) +{ + std::error_code ec; + auto result = create_directory(p, path(), ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), p, ec); + } + return result; +} +#endif + +GHC_INLINE bool create_directory(const path& p, std::error_code& ec) noexcept +{ + return create_directory(p, path(), ec); +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE bool create_directory(const path& p, const path& attributes) +{ + std::error_code ec; + auto result = create_directory(p, attributes, ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), p, ec); + } + return result; +} +#endif + +GHC_INLINE bool create_directory(const path& p, const path& attributes, std::error_code& ec) noexcept +{ + std::error_code tec; + ec.clear(); + auto fs = status(p, tec); +#ifdef LWG_2935_BEHAVIOUR + if (status_known(fs) && exists(fs)) { + return false; + } +#else + if (status_known(fs) && exists(fs) && is_directory(fs)) { + return false; + } +#endif +#ifdef GHC_OS_WINDOWS + if (!attributes.empty()) { + if (!::CreateDirectoryExW(GHC_NATIVEWP(attributes), GHC_NATIVEWP(p), NULL)) { + ec = detail::make_system_error(); + return false; + } + } + else if (!::CreateDirectoryW(GHC_NATIVEWP(p), NULL)) { + ec = detail::make_system_error(); + return false; + } +#else + ::mode_t attribs = static_cast(perms::all); + if (!attributes.empty()) { + struct ::stat fileStat; + if (::stat(attributes.c_str(), &fileStat) != 0) { + ec = detail::make_system_error(); + return false; + } + attribs = fileStat.st_mode; + } + if (::mkdir(p.c_str(), attribs) != 0) { + ec = detail::make_system_error(); + return false; + } +#endif + return true; +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE void create_directory_symlink(const path& to, const path& new_symlink) +{ + std::error_code ec; + create_directory_symlink(to, new_symlink, ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), to, new_symlink, ec); + } +} +#endif + +GHC_INLINE void create_directory_symlink(const path& to, const path& new_symlink, std::error_code& ec) noexcept +{ + detail::create_symlink(to, new_symlink, true, ec); +} + +#ifndef GHC_OS_WEB +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE void create_hard_link(const path& to, const path& new_hard_link) +{ + std::error_code ec; + create_hard_link(to, new_hard_link, ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), to, new_hard_link, ec); + } +} +#endif + +GHC_INLINE void create_hard_link(const path& to, const path& new_hard_link, std::error_code& ec) noexcept +{ + detail::create_hardlink(to, new_hard_link, ec); +} +#endif + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE void create_symlink(const path& to, const path& new_symlink) +{ + std::error_code ec; + create_symlink(to, new_symlink, ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), to, new_symlink, ec); + } +} +#endif + +GHC_INLINE void create_symlink(const path& to, const path& new_symlink, std::error_code& ec) noexcept +{ + detail::create_symlink(to, new_symlink, false, ec); +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE path current_path() +{ + std::error_code ec; + auto result = current_path(ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), ec); + } + return result; +} +#endif + +GHC_INLINE path current_path(std::error_code& ec) +{ + ec.clear(); +#ifdef GHC_OS_WINDOWS + DWORD pathlen = ::GetCurrentDirectoryW(0, 0); + std::unique_ptr buffer(new wchar_t[size_t(pathlen) + 1]); + if (::GetCurrentDirectoryW(pathlen, buffer.get()) == 0) { + ec = detail::make_system_error(); + return path(); + } + return path(std::wstring(buffer.get()), path::native_format); +#else + size_t pathlen = static_cast(std::max(int(::pathconf(".", _PC_PATH_MAX)), int(PATH_MAX))); + std::unique_ptr buffer(new char[pathlen + 1]); + if (::getcwd(buffer.get(), pathlen) == nullptr) { + ec = detail::make_system_error(); + return path(); + } + return path(buffer.get()); +#endif +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE void current_path(const path& p) +{ + std::error_code ec; + current_path(p, ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), p, ec); + } +} +#endif + +GHC_INLINE void current_path(const path& p, std::error_code& ec) noexcept +{ + ec.clear(); +#ifdef GHC_OS_WINDOWS + if (!::SetCurrentDirectoryW(GHC_NATIVEWP(p))) { + ec = detail::make_system_error(); + } +#else + if (::chdir(p.string().c_str()) == -1) { + ec = detail::make_system_error(); + } +#endif +} + +GHC_INLINE bool exists(file_status s) noexcept +{ + return status_known(s) && s.type() != file_type::not_found; +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE bool exists(const path& p) +{ + return exists(status(p)); +} +#endif + +GHC_INLINE bool exists(const path& p, std::error_code& ec) noexcept +{ + file_status s = status(p, ec); + if (status_known(s)) { + ec.clear(); + } + return exists(s); +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE bool equivalent(const path& p1, const path& p2) +{ + std::error_code ec; + bool result = equivalent(p1, p2, ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), p1, p2, ec); + } + return result; +} +#endif + +GHC_INLINE bool equivalent(const path& p1, const path& p2, std::error_code& ec) noexcept +{ + ec.clear(); +#ifdef GHC_OS_WINDOWS + std::shared_ptr file1(::CreateFileW(GHC_NATIVEWP(p1), 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0), CloseHandle); + auto e1 = ::GetLastError(); + std::shared_ptr file2(::CreateFileW(GHC_NATIVEWP(p2), 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0), CloseHandle); + if (file1.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 +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE uintmax_t file_size(const path& p) +{ + std::error_code ec; + auto result = file_size(p, ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), p, ec); + } + return result; +} +#endif + +GHC_INLINE uintmax_t file_size(const path& p, std::error_code& ec) noexcept +{ + ec.clear(); +#ifdef GHC_OS_WINDOWS + WIN32_FILE_ATTRIBUTE_DATA attr; + if (!GetFileAttributesExW(GHC_NATIVEWP(p), GetFileExInfoStandard, &attr)) { + ec = detail::make_system_error(); + return static_cast(-1); + } + return static_cast(attr.nFileSizeHigh) << (sizeof(attr.nFileSizeHigh) * 8) | attr.nFileSizeLow; +#else + struct ::stat fileStat; + if (::stat(p.c_str(), &fileStat) == -1) { + ec = detail::make_system_error(); + return static_cast(-1); + } + return static_cast(fileStat.st_size); +#endif +} + +#ifndef GHC_OS_WEB +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE uintmax_t hard_link_count(const path& p) +{ + std::error_code ec; + auto result = hard_link_count(p, ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), p, ec); + } + return result; +} +#endif + +GHC_INLINE uintmax_t hard_link_count(const path& p, std::error_code& ec) noexcept +{ + ec.clear(); +#ifdef GHC_OS_WINDOWS + uintmax_t result = static_cast(-1); + std::shared_ptr file(::CreateFileW(GHC_NATIVEWP(p), 0, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, 0, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, 0), CloseHandle); + BY_HANDLE_FILE_INFORMATION inf; + 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(-1) : result; +#endif +} +#endif + +GHC_INLINE bool is_block_file(file_status s) noexcept +{ + return s.type() == file_type::block; +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE bool is_block_file(const path& p) +{ + return is_block_file(status(p)); +} +#endif + +GHC_INLINE bool is_block_file(const path& p, std::error_code& ec) noexcept +{ + return is_block_file(status(p, ec)); +} + +GHC_INLINE bool is_character_file(file_status s) noexcept +{ + return s.type() == file_type::character; +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE bool is_character_file(const path& p) +{ + return is_character_file(status(p)); +} +#endif + +GHC_INLINE bool is_character_file(const path& p, std::error_code& ec) noexcept +{ + return is_character_file(status(p, ec)); +} + +GHC_INLINE bool is_directory(file_status s) noexcept +{ + return s.type() == file_type::directory; +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE bool is_directory(const path& p) +{ + return is_directory(status(p)); +} +#endif + +GHC_INLINE bool is_directory(const path& p, std::error_code& ec) noexcept +{ + return is_directory(status(p, ec)); +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE bool is_empty(const path& p) +{ + if (is_directory(p)) { + return directory_iterator(p) == directory_iterator(); + } + else { + return file_size(p) == 0; + } +} +#endif + +GHC_INLINE bool is_empty(const path& p, std::error_code& ec) noexcept +{ + auto fs = status(p, ec); + if (ec) { + return false; + } + if (is_directory(fs)) { + directory_iterator iter(p, ec); + if (ec) { + return false; + } + return iter == directory_iterator(); + } + else { + auto sz = file_size(p, ec); + if (ec) { + return false; + } + return sz == 0; + } +} + +GHC_INLINE bool is_fifo(file_status s) noexcept +{ + return s.type() == file_type::fifo; +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE bool is_fifo(const path& p) +{ + return is_fifo(status(p)); +} +#endif + +GHC_INLINE bool is_fifo(const path& p, std::error_code& ec) noexcept +{ + return is_fifo(status(p, ec)); +} + +GHC_INLINE bool is_other(file_status s) noexcept +{ + return exists(s) && !is_regular_file(s) && !is_directory(s) && !is_symlink(s); +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE bool is_other(const path& p) +{ + return is_other(status(p)); +} +#endif + +GHC_INLINE bool is_other(const path& p, std::error_code& ec) noexcept +{ + return is_other(status(p, ec)); +} + +GHC_INLINE bool is_regular_file(file_status s) noexcept +{ + return s.type() == file_type::regular; +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE bool is_regular_file(const path& p) +{ + return is_regular_file(status(p)); +} +#endif + +GHC_INLINE bool is_regular_file(const path& p, std::error_code& ec) noexcept +{ + return is_regular_file(status(p, ec)); +} + +GHC_INLINE bool is_socket(file_status s) noexcept +{ + return s.type() == file_type::socket; +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE bool is_socket(const path& p) +{ + return is_socket(status(p)); +} +#endif + +GHC_INLINE bool is_socket(const path& p, std::error_code& ec) noexcept +{ + return is_socket(status(p, ec)); +} + +GHC_INLINE bool is_symlink(file_status s) noexcept +{ + return s.type() == file_type::symlink; +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE bool is_symlink(const path& p) +{ + return is_symlink(symlink_status(p)); +} +#endif + +GHC_INLINE bool is_symlink(const path& p, std::error_code& ec) noexcept +{ + return is_symlink(symlink_status(p, ec)); +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE file_time_type last_write_time(const path& p) +{ + std::error_code ec; + auto result = last_write_time(p, ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), p, ec); + } + return result; +} +#endif + +GHC_INLINE file_time_type last_write_time(const path& p, std::error_code& ec) noexcept +{ + time_t result = 0; + ec.clear(); + file_status fs = detail::status_ex(p, ec, nullptr, nullptr, nullptr, &result); + return ec ? (file_time_type::min)() : std::chrono::system_clock::from_time_t(result); +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE void last_write_time(const path& p, file_time_type new_time) +{ + std::error_code ec; + last_write_time(p, new_time, ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), p, ec); + } +} +#endif + +GHC_INLINE void last_write_time(const path& p, file_time_type new_time, std::error_code& ec) noexcept +{ + ec.clear(); + auto d = new_time.time_since_epoch(); +#ifdef GHC_OS_WINDOWS + std::shared_ptr file(::CreateFileW(GHC_NATIVEWP(p), FILE_WRITE_ATTRIBUTES, FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL), ::CloseHandle); + FILETIME ft; + auto tt = std::chrono::duration_cast(d).count() * 10 + 116444736000000000; + ft.dwLowDateTime = static_cast(tt); + ft.dwHighDateTime = static_cast(tt >> 32); + if (!::SetFileTime(file.get(), 0, 0, &ft)) { + ec = detail::make_system_error(); + } +#elif defined(GHC_OS_MACOS) +#ifdef __MAC_OS_X_VERSION_MIN_REQUIRED +#if __MAC_OS_X_VERSION_MIN_REQUIRED < 101300 + struct ::stat fs; + if (::stat(p.c_str(), &fs) == 0) { + struct ::timeval tv[2]; + tv[0].tv_sec = fs.st_atimespec.tv_sec; + tv[0].tv_usec = static_cast(fs.st_atimespec.tv_nsec / 1000); + tv[1].tv_sec = std::chrono::duration_cast(d).count(); + tv[1].tv_usec = static_cast(std::chrono::duration_cast(d).count() % 1000000); + if (::utimes(p.c_str(), tv) == 0) { + return; + } + } + ec = detail::make_system_error(); + return; +#else + struct ::timespec times[2]; + times[0].tv_sec = 0; + times[0].tv_nsec = UTIME_OMIT; + times[1].tv_sec = std::chrono::duration_cast(d).count(); + times[1].tv_nsec = 0; // std::chrono::duration_cast(d).count() % 1000000000; + if (::utimensat(AT_FDCWD, p.c_str(), times, AT_SYMLINK_NOFOLLOW) != 0) { + ec = detail::make_system_error(); + } + return; +#endif +#endif +#else +#ifndef UTIME_OMIT +#define UTIME_OMIT ((1l << 30) - 2l) +#endif + struct ::timespec times[2]; + times[0].tv_sec = 0; + times[0].tv_nsec = UTIME_OMIT; + times[1].tv_sec = static_cast(std::chrono::duration_cast(d).count()); + times[1].tv_nsec = static_cast(std::chrono::duration_cast(d).count() % 1000000000); +#if defined(__ANDROID_API__) && __ANDROID_API__ < 12 + if (syscall(__NR_utimensat, AT_FDCWD, p.c_str(), times, AT_SYMLINK_NOFOLLOW) != 0) { +#else + if (::utimensat(AT_FDCWD, p.c_str(), times, AT_SYMLINK_NOFOLLOW) != 0) { +#endif + ec = detail::make_system_error(); + } + return; +#endif +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE void permissions(const path& p, perms prms, perm_options opts) +{ + std::error_code ec; + permissions(p, prms, opts, ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), p, ec); + } +} +#endif + +GHC_INLINE void permissions(const path& p, perms prms, std::error_code& ec) noexcept +{ + permissions(p, prms, perm_options::replace, ec); +} + +GHC_INLINE void permissions(const path& p, perms prms, perm_options opts, std::error_code& ec) noexcept +{ + if (static_cast(opts & (perm_options::replace | perm_options::add | perm_options::remove)) == 0) { + ec = detail::make_error_code(detail::portable_error::invalid_argument); + return; + } + auto fs = symlink_status(p, ec); + if ((opts & perm_options::replace) != perm_options::replace) { + if ((opts & perm_options::add) == perm_options::add) { + prms = fs.permissions() | prms; + } + else { + prms = fs.permissions() & ~prms; + } + } +#ifdef GHC_OS_WINDOWS +#ifdef __GNUC__ + auto oldAttr = GetFileAttributesW(GHC_NATIVEWP(p)); + if (oldAttr != INVALID_FILE_ATTRIBUTES) { + DWORD newAttr = ((prms & perms::owner_write) == perms::owner_write) ? oldAttr & ~(static_cast(FILE_ATTRIBUTE_READONLY)) : oldAttr | FILE_ATTRIBUTE_READONLY; + if (oldAttr == newAttr || SetFileAttributesW(GHC_NATIVEWP(p), newAttr)) { + return; + } + } + ec = detail::make_system_error(); +#else + int mode = 0; + if ((prms & perms::owner_read) == perms::owner_read) { + mode |= _S_IREAD; + } + if ((prms & perms::owner_write) == perms::owner_write) { + mode |= _S_IWRITE; + } + if (::_wchmod(p.wstring().c_str(), mode) != 0) { + ec = detail::make_system_error(); + } +#endif +#else + if ((opts & perm_options::nofollow) != perm_options::nofollow) { + if (::chmod(p.c_str(), static_cast(prms)) != 0) { + ec = detail::make_system_error(); + } + } +#endif +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE path proximate(const path& p, std::error_code& ec) +{ + auto cp = current_path(ec); + if (!ec) { + return proximate(p, cp, ec); + } + return path(); +} +#endif + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE path proximate(const path& p, const path& base) +{ + return weakly_canonical(p).lexically_proximate(weakly_canonical(base)); +} +#endif + +GHC_INLINE path proximate(const path& p, const path& base, std::error_code& ec) +{ + return weakly_canonical(p, ec).lexically_proximate(weakly_canonical(base, ec)); +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE path read_symlink(const path& p) +{ + std::error_code ec; + auto result = read_symlink(p, ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), p, ec); + } + return result; +} +#endif + +GHC_INLINE path read_symlink(const path& p, std::error_code& ec) +{ + file_status fs = symlink_status(p, ec); + if (fs.type() != file_type::symlink) { + ec = detail::make_error_code(detail::portable_error::invalid_argument); + return path(); + } + auto result = detail::resolveSymlink(p, ec); + return ec ? path() : result; +} + +GHC_INLINE path relative(const path& p, std::error_code& ec) +{ + return relative(p, current_path(ec), ec); +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE path relative(const path& p, const path& base) +{ + return weakly_canonical(p).lexically_relative(weakly_canonical(base)); +} +#endif + +GHC_INLINE path relative(const path& p, const path& base, std::error_code& ec) +{ + return weakly_canonical(p, ec).lexically_relative(weakly_canonical(base, ec)); +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE bool remove(const path& p) +{ + std::error_code ec; + auto result = remove(p, ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), p, ec); + } + return result; +} +#endif + +GHC_INLINE bool remove(const path& p, std::error_code& ec) noexcept +{ + ec.clear(); +#ifdef GHC_OS_WINDOWS +#ifdef GHC_USE_WCHAR_T + auto cstr = p.c_str(); +#else + std::wstring np = detail::fromUtf8(p.u8string()); + auto cstr = np.c_str(); +#endif + DWORD attr = GetFileAttributesW(cstr); + if (attr == INVALID_FILE_ATTRIBUTES) { + auto error = ::GetLastError(); + if (error == ERROR_FILE_NOT_FOUND || error == ERROR_PATH_NOT_FOUND) { + return false; + } + ec = detail::make_system_error(error); + } + else if(attr & FILE_ATTRIBUTE_READONLY) { + auto new_attr = attr & ~static_cast(FILE_ATTRIBUTE_READONLY); + if(!SetFileAttributesW(cstr, new_attr)) { + auto error = ::GetLastError(); + ec = detail::make_system_error(error); + } + } + if (!ec) { + if (attr & FILE_ATTRIBUTE_DIRECTORY) { + if (!RemoveDirectoryW(cstr)) { + ec = detail::make_system_error(); + } + } + else { + if (!DeleteFileW(cstr)) { + ec = detail::make_system_error(); + } + } + } +#else + if (::remove(p.c_str()) == -1) { + auto error = errno; + if (error == ENOENT) { + return false; + } + ec = detail::make_system_error(); + } +#endif + return ec ? false : true; +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE uintmax_t remove_all(const path& p) +{ + std::error_code ec; + auto result = remove_all(p, ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), p, ec); + } + return result; +} +#endif + +GHC_INLINE uintmax_t remove_all(const path& p, std::error_code& ec) noexcept +{ + ec.clear(); + uintmax_t count = 0; + if (p == "/") { + ec = detail::make_error_code(detail::portable_error::not_supported); + return static_cast(-1); + } + std::error_code tec; + auto fs = status(p, tec); + if (exists(fs) && is_directory(fs)) { + for (auto iter = directory_iterator(p, ec); iter != directory_iterator(); iter.increment(ec)) { + if (ec && !detail::is_not_found_error(ec)) { + break; + } + bool is_symlink_result = iter->is_symlink(ec); + if (ec) + return static_cast(-1); + if (!is_symlink_result && iter->is_directory(ec)) { + count += remove_all(iter->path(), ec); + if (ec) { + return static_cast(-1); + } + } + else { + if (!ec) { + remove(iter->path(), ec); + } + if (ec) { + return static_cast(-1); + } + ++count; + } + } + } + if (!ec) { + if (remove(p, ec)) { + ++count; + } + } + if (ec) { + return static_cast(-1); + } + return count; +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE void rename(const path& from, const path& to) +{ + std::error_code ec; + rename(from, to, ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), from, to, ec); + } +} +#endif + +GHC_INLINE void rename(const path& from, const path& to, std::error_code& ec) noexcept +{ + ec.clear(); +#ifdef GHC_OS_WINDOWS + if (from != to) { + if (!MoveFileExW(GHC_NATIVEWP(from), GHC_NATIVEWP(to), (DWORD)MOVEFILE_REPLACE_EXISTING)) { + ec = detail::make_system_error(); + } + } +#else + if (from != to) { + if (::rename(from.c_str(), to.c_str()) != 0) { + ec = detail::make_system_error(); + } + } +#endif +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE void resize_file(const path& p, uintmax_t size) +{ + std::error_code ec; + resize_file(p, size, ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), p, ec); + } +} +#endif + +GHC_INLINE void resize_file(const path& p, uintmax_t size, std::error_code& ec) noexcept +{ + ec.clear(); +#ifdef GHC_OS_WINDOWS + LARGE_INTEGER lisize; + lisize.QuadPart = static_cast(size); + if (lisize.QuadPart < 0) { +#ifdef ERROR_FILE_TOO_LARGE + ec = detail::make_system_error(ERROR_FILE_TOO_LARGE); +#else + ec = detail::make_system_error(223); +#endif + return; + } + std::shared_ptr file(CreateFileW(GHC_NATIVEWP(p), 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(size)) != 0) { + ec = detail::make_system_error(); + } +#endif +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE space_info space(const path& p) +{ + std::error_code ec; + auto result = space(p, ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), p, ec); + } + return result; +} +#endif + +GHC_INLINE space_info space(const path& p, std::error_code& ec) noexcept +{ + ec.clear(); +#ifdef GHC_OS_WINDOWS + ULARGE_INTEGER freeBytesAvailableToCaller = {{ 0, 0 }}; + ULARGE_INTEGER totalNumberOfBytes = {{ 0, 0 }}; + ULARGE_INTEGER totalNumberOfFreeBytes = {{ 0, 0 }}; + if (!GetDiskFreeSpaceExW(GHC_NATIVEWP(p), &freeBytesAvailableToCaller, &totalNumberOfBytes, &totalNumberOfFreeBytes)) { + ec = detail::make_system_error(); + return {static_cast(-1), static_cast(-1), static_cast(-1)}; + } + return {static_cast(totalNumberOfBytes.QuadPart), static_cast(totalNumberOfFreeBytes.QuadPart), static_cast(freeBytesAvailableToCaller.QuadPart)}; +#else + struct ::statvfs sfs; + if (::statvfs(p.c_str(), &sfs) != 0) { + ec = detail::make_system_error(); + return {static_cast(-1), static_cast(-1), static_cast(-1)}; + } + return {static_cast(sfs.f_blocks * sfs.f_frsize), static_cast(sfs.f_bfree * sfs.f_frsize), static_cast(sfs.f_bavail * sfs.f_frsize)}; +#endif +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE file_status status(const path& p) +{ + std::error_code ec; + auto result = status(p, ec); + if (result.type() == file_type::none) { + throw filesystem_error(detail::systemErrorText(ec.value()), p, ec); + } + return result; +} +#endif + +GHC_INLINE file_status status(const path& p, std::error_code& ec) noexcept +{ + return detail::status_ex(p, ec); +} + +GHC_INLINE bool status_known(file_status s) noexcept +{ + return s.type() != file_type::none; +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE file_status symlink_status(const path& p) +{ + std::error_code ec; + auto result = symlink_status(p, ec); + if (result.type() == file_type::none) { + throw filesystem_error(detail::systemErrorText(ec.value()), ec); + } + return result; +} +#endif + +GHC_INLINE file_status symlink_status(const path& p, std::error_code& ec) noexcept +{ + return detail::symlink_status_ex(p, ec); +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE path temp_directory_path() +{ + std::error_code ec; + path result = temp_directory_path(ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), ec); + } + return result; +} +#endif + +GHC_INLINE path temp_directory_path(std::error_code& ec) noexcept +{ + ec.clear(); +#ifdef GHC_OS_WINDOWS + wchar_t buffer[512]; + auto rc = GetTempPathW(511, buffer); + if (!rc || rc > 511) { + ec = detail::make_system_error(); + return path(); + } + return path(std::wstring(buffer)); +#else + static const char* temp_vars[] = {"TMPDIR", "TMP", "TEMP", "TEMPDIR", nullptr}; + const char* temp_path = nullptr; + for (auto temp_name = temp_vars; *temp_name != nullptr; ++temp_name) { + temp_path = std::getenv(*temp_name); + if (temp_path) { + return path(temp_path); + } + } + return path("/tmp"); +#endif +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE path weakly_canonical(const path& p) +{ + std::error_code ec; + auto result = weakly_canonical(p, ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), p, ec); + } + return result; +} +#endif + +GHC_INLINE path weakly_canonical(const path& p, std::error_code& ec) noexcept +{ + path result; + ec.clear(); + bool scan = true; + for (auto pe : p) { + if (scan) { + std::error_code tec; + if (exists(result / pe, tec)) { + result /= pe; + } + else { + if (ec) { + return path(); + } + scan = false; + if (!result.empty()) { + result = canonical(result, ec) / pe; + if (ec) { + break; + } + } + else { + result /= pe; + } + } + } + else { + result /= pe; + } + } + if (scan) { + if (!result.empty()) { + result = canonical(result, ec); + } + } + return ec ? path() : result.lexically_normal(); +} + +//----------------------------------------------------------------------------- +// [fs.class.file_status] class file_status +// [fs.file_status.cons] constructors and destructor +GHC_INLINE file_status::file_status() noexcept + : file_status(file_type::none) +{ +} + +GHC_INLINE file_status::file_status(file_type ft, perms prms) noexcept + : _type(ft) + , _perms(prms) +{ +} + +GHC_INLINE file_status::file_status(const file_status& other) noexcept + : _type(other._type) + , _perms(other._perms) +{ +} + +GHC_INLINE file_status::file_status(file_status&& other) noexcept + : _type(other._type) + , _perms(other._perms) +{ +} + +GHC_INLINE file_status::~file_status() {} + +// assignments: +GHC_INLINE file_status& file_status::operator=(const file_status& rhs) noexcept +{ + _type = rhs._type; + _perms = rhs._perms; + return *this; +} + +GHC_INLINE file_status& file_status::operator=(file_status&& rhs) noexcept +{ + _type = rhs._type; + _perms = rhs._perms; + return *this; +} + +// [fs.file_status.mods] modifiers +GHC_INLINE void file_status::type(file_type ft) noexcept +{ + _type = ft; +} + +GHC_INLINE void file_status::permissions(perms prms) noexcept +{ + _perms = prms; +} + +// [fs.file_status.obs] observers +GHC_INLINE file_type file_status::type() const noexcept +{ + return _type; +} + +GHC_INLINE perms file_status::permissions() const noexcept +{ + return _perms; +} + +//----------------------------------------------------------------------------- +// [fs.class.directory_entry] class directory_entry +// [fs.dir.entry.cons] constructors and destructor +// directory_entry::directory_entry() noexcept = default; +// directory_entry::directory_entry(const directory_entry&) = default; +// directory_entry::directory_entry(directory_entry&&) noexcept = default; +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE directory_entry::directory_entry(const filesystem::path& p) + : _path(p) + , _file_size(static_cast(-1)) +#ifndef GHC_OS_WINDOWS + , _hard_link_count(static_cast(-1)) +#endif + , _last_write_time(0) +{ + refresh(); +} +#endif + +GHC_INLINE directory_entry::directory_entry(const filesystem::path& p, std::error_code& ec) + : _path(p) + , _file_size(static_cast(-1)) +#ifndef GHC_OS_WINDOWS + , _hard_link_count(static_cast(-1)) +#endif + , _last_write_time(0) +{ + refresh(ec); +} + +GHC_INLINE directory_entry::~directory_entry() {} + +// assignments: +// directory_entry& directory_entry::operator=(const directory_entry&) = default; +// directory_entry& directory_entry::operator=(directory_entry&&) noexcept = default; + +// [fs.dir.entry.mods] directory_entry modifiers +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE void directory_entry::assign(const filesystem::path& p) +{ + _path = p; + refresh(); +} +#endif + +GHC_INLINE void directory_entry::assign(const filesystem::path& p, std::error_code& ec) +{ + _path = p; + refresh(ec); +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE void directory_entry::replace_filename(const filesystem::path& p) +{ + _path.replace_filename(p); + refresh(); +} +#endif + +GHC_INLINE void directory_entry::replace_filename(const filesystem::path& p, std::error_code& ec) +{ + _path.replace_filename(p); + refresh(ec); +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE void directory_entry::refresh() +{ + std::error_code ec; + refresh(ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), _path, ec); + } +} +#endif + +GHC_INLINE void directory_entry::refresh(std::error_code& ec) noexcept +{ +#ifdef GHC_OS_WINDOWS + _status = detail::status_ex(_path, ec, &_symlink_status, &_file_size, nullptr, &_last_write_time); +#else + _status = detail::status_ex(_path, ec, &_symlink_status, &_file_size, &_hard_link_count, &_last_write_time); +#endif +} + +// [fs.dir.entry.obs] directory_entry observers +GHC_INLINE const filesystem::path& directory_entry::path() const noexcept +{ + return _path; +} + +GHC_INLINE directory_entry::operator const filesystem::path&() const noexcept +{ + return _path; +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE file_type directory_entry::status_file_type() const +{ + return _status.type() != file_type::none ? _status.type() : filesystem::status(path()).type(); +} +#endif + +GHC_INLINE file_type directory_entry::status_file_type(std::error_code& ec) const noexcept +{ + if(_status.type() != file_type::none) { + ec.clear(); + return _status.type(); + } + return filesystem::status(path(), ec).type(); +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE bool directory_entry::exists() const +{ + return status_file_type() != file_type::not_found; +} +#endif + +GHC_INLINE bool directory_entry::exists(std::error_code& ec) const noexcept +{ + return status_file_type(ec) != file_type::not_found; +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE bool directory_entry::is_block_file() const +{ + return status_file_type() == file_type::block; +} +#endif +GHC_INLINE bool directory_entry::is_block_file(std::error_code& ec) const noexcept +{ + return status_file_type(ec) == file_type::block; +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE bool directory_entry::is_character_file() const +{ + return status_file_type() == file_type::character; +} +#endif + +GHC_INLINE bool directory_entry::is_character_file(std::error_code& ec) const noexcept +{ + return status_file_type(ec) == file_type::character; +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE bool directory_entry::is_directory() const +{ + return status_file_type() == file_type::directory; +} +#endif + +GHC_INLINE bool directory_entry::is_directory(std::error_code& ec) const noexcept +{ + return status_file_type(ec) == file_type::directory; +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE bool directory_entry::is_fifo() const +{ + return status_file_type() == file_type::fifo; +} +#endif + +GHC_INLINE bool directory_entry::is_fifo(std::error_code& ec) const noexcept +{ + return status_file_type(ec) == file_type::fifo; +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE bool directory_entry::is_other() const +{ + auto ft = status_file_type(); + return ft != file_type::none && ft != file_type::not_found && ft != file_type::regular && ft != file_type::directory && !is_symlink(); +} +#endif + +GHC_INLINE bool directory_entry::is_other(std::error_code& ec) const noexcept +{ + auto ft = status_file_type(ec); + bool other = ft != file_type::none && ft != file_type::not_found && ft != file_type::regular && ft != file_type::directory && !is_symlink(ec); + return !ec && other; +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE bool directory_entry::is_regular_file() const +{ + return status_file_type() == file_type::regular; +} +#endif + +GHC_INLINE bool directory_entry::is_regular_file(std::error_code& ec) const noexcept +{ + return status_file_type(ec) == file_type::regular; +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE bool directory_entry::is_socket() const +{ + return status_file_type() == file_type::socket; +} +#endif + +GHC_INLINE bool directory_entry::is_socket(std::error_code& ec) const noexcept +{ + return status_file_type(ec) == file_type::socket; +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE bool directory_entry::is_symlink() const +{ + return _symlink_status.type() != file_type::none ? _symlink_status.type() == file_type::symlink : filesystem::is_symlink(symlink_status()); +} +#endif + +GHC_INLINE bool directory_entry::is_symlink(std::error_code& ec) const noexcept +{ + if(_symlink_status.type() != file_type::none) { + ec.clear(); + return _symlink_status.type() == file_type::symlink; + } + return filesystem::is_symlink(symlink_status(ec)); +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE uintmax_t directory_entry::file_size() const +{ + if (_file_size != static_cast(-1)) { + return _file_size; + } + return filesystem::file_size(path()); +} +#endif + +GHC_INLINE uintmax_t directory_entry::file_size(std::error_code& ec) const noexcept +{ + if (_file_size != static_cast(-1)) { + ec.clear(); + return _file_size; + } + return filesystem::file_size(path(), ec); +} + +#ifndef GHC_OS_WEB +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE uintmax_t directory_entry::hard_link_count() const +{ +#ifndef GHC_OS_WINDOWS + if (_hard_link_count != static_cast(-1)) { + return _hard_link_count; + } +#endif + return filesystem::hard_link_count(path()); +} +#endif + +GHC_INLINE uintmax_t directory_entry::hard_link_count(std::error_code& ec) const noexcept +{ +#ifndef GHC_OS_WINDOWS + if (_hard_link_count != static_cast(-1)) { + ec.clear(); + return _hard_link_count; + } +#endif + return filesystem::hard_link_count(path(), ec); +} +#endif + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE file_time_type directory_entry::last_write_time() const +{ + if (_last_write_time != 0) { + return std::chrono::system_clock::from_time_t(_last_write_time); + } + return filesystem::last_write_time(path()); +} +#endif + +GHC_INLINE file_time_type directory_entry::last_write_time(std::error_code& ec) const noexcept +{ + if (_last_write_time != 0) { + ec.clear(); + return std::chrono::system_clock::from_time_t(_last_write_time); + } + return filesystem::last_write_time(path(), ec); +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE file_status directory_entry::status() const +{ + if (_status.type() != file_type::none && _status.permissions() != perms::unknown) { + return _status; + } + return filesystem::status(path()); +} +#endif + +GHC_INLINE file_status directory_entry::status(std::error_code& ec) const noexcept +{ + if (_status.type() != file_type::none && _status.permissions() != perms::unknown) { + ec.clear(); + return _status; + } + return filesystem::status(path(), ec); +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE file_status directory_entry::symlink_status() const +{ + if (_symlink_status.type() != file_type::none && _symlink_status.permissions() != perms::unknown) { + return _symlink_status; + } + return filesystem::symlink_status(path()); +} +#endif + +GHC_INLINE file_status directory_entry::symlink_status(std::error_code& ec) const noexcept +{ + if (_symlink_status.type() != file_type::none && _symlink_status.permissions() != perms::unknown) { + ec.clear(); + return _symlink_status; + } + return filesystem::symlink_status(path(), ec); +} + +#ifdef GHC_HAS_THREEWAY_COMP +GHC_INLINE std::strong_ordering directory_entry::operator<=>(const directory_entry& rhs) const noexcept +{ + return _path <=> rhs._path; +} +#endif + +GHC_INLINE bool directory_entry::operator<(const directory_entry& rhs) const noexcept +{ + return _path < rhs._path; +} + +GHC_INLINE bool directory_entry::operator==(const directory_entry& rhs) const noexcept +{ + return _path == rhs._path; +} + +GHC_INLINE bool directory_entry::operator!=(const directory_entry& rhs) const noexcept +{ + return _path != rhs._path; +} + +GHC_INLINE bool directory_entry::operator<=(const directory_entry& rhs) const noexcept +{ + return _path <= rhs._path; +} + +GHC_INLINE bool directory_entry::operator>(const directory_entry& rhs) const noexcept +{ + return _path > rhs._path; +} + +GHC_INLINE bool directory_entry::operator>=(const directory_entry& rhs) const noexcept +{ + return _path >= rhs._path; +} + +//----------------------------------------------------------------------------- +// [fs.class.directory_iterator] class directory_iterator + +#ifdef GHC_OS_WINDOWS +class directory_iterator::impl +{ +public: + impl(const path& p, directory_options options) + : _base(p) + , _options(options) + , _dirHandle(INVALID_HANDLE_VALUE) + { + if (!_base.empty()) { + ZeroMemory(&_findData, sizeof(WIN32_FIND_DATAW)); + if ((_dirHandle = FindFirstFileW(GHC_NATIVEWP((_base / "*")), &_findData)) != INVALID_HANDLE_VALUE) { + if (std::wstring(_findData.cFileName) == L"." || std::wstring(_findData.cFileName) == L"..") { + increment(_ec); + } + else { + _dir_entry._path = _base / std::wstring(_findData.cFileName); + copyToDirEntry(_ec); + } + } + else { + auto error = ::GetLastError(); + _base = filesystem::path(); + if (error != ERROR_ACCESS_DENIED || (options & directory_options::skip_permission_denied) == directory_options::none) { + _ec = detail::make_system_error(); + } + } + } + } + impl(const impl& other) = delete; + ~impl() + { + if (_dirHandle != INVALID_HANDLE_VALUE) { + FindClose(_dirHandle); + _dirHandle = INVALID_HANDLE_VALUE; + } + } + void increment(std::error_code& ec) + { + if (_dirHandle != INVALID_HANDLE_VALUE) { + do { + if (FindNextFileW(_dirHandle, &_findData)) { + _dir_entry._path = _base; +#ifdef GHC_USE_WCHAR_T + _dir_entry._path.append_name(_findData.cFileName); +#else +#ifdef GHC_RAISE_UNICODE_ERRORS + try { + _dir_entry._path.append_name(detail::toUtf8(_findData.cFileName).c_str()); + } + catch (filesystem_error& fe) { + ec = fe.code(); + return; + } +#else + _dir_entry._path.append_name(detail::toUtf8(_findData.cFileName).c_str()); +#endif +#endif + copyToDirEntry(ec); + } + else { + auto err = ::GetLastError(); + if (err != ERROR_NO_MORE_FILES) { + _ec = ec = detail::make_system_error(err); + } + FindClose(_dirHandle); + _dirHandle = INVALID_HANDLE_VALUE; + _dir_entry._path.clear(); + break; + } + } while (std::wstring(_findData.cFileName) == L"." || std::wstring(_findData.cFileName) == L".."); + } + else { + ec = _ec; + } + } + void copyToDirEntry(std::error_code& ec) + { + if (_findData.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) { + _dir_entry._status = detail::status_ex(_dir_entry._path, ec, &_dir_entry._symlink_status, &_dir_entry._file_size, nullptr, &_dir_entry._last_write_time); + } + else { + _dir_entry._status = detail::status_from_INFO(_dir_entry._path, &_findData, ec, &_dir_entry._file_size, &_dir_entry._last_write_time); + _dir_entry._symlink_status = _dir_entry._status; + } + if (ec) { + if (_dir_entry._status.type() != file_type::none && _dir_entry._symlink_status.type() != file_type::none) { + ec.clear(); + } + else { + _dir_entry._file_size = static_cast(-1); + _dir_entry._last_write_time = 0; + } + } + } + path _base; + directory_options _options; + WIN32_FIND_DATAW _findData; + HANDLE _dirHandle; + directory_entry _dir_entry; + std::error_code _ec; +}; +#else +// POSIX implementation +class directory_iterator::impl +{ +public: + impl(const path& path, directory_options options) + : _base(path) + , _options(options) + , _dir(nullptr) + , _entry(nullptr) + { + if (!path.empty()) { + _dir = ::opendir(path.native().c_str()); + if (!_dir) { + auto error = errno; + _base = filesystem::path(); + if ((error != EACCES && error != EPERM) || (options & directory_options::skip_permission_denied) == directory_options::none) { + _ec = detail::make_system_error(); + } + } + else { + increment(_ec); + } + } + } + impl(const impl& other) = delete; + ~impl() + { + if (_dir) { + ::closedir(_dir); + } + } + void increment(std::error_code& ec) + { + if (_dir) { + bool skip; + do { + skip = false; + errno = 0; + _entry = ::readdir(_dir); + if (_entry) { + _dir_entry._path = _base; + _dir_entry._path.append_name(_entry->d_name); + copyToDirEntry(); + if (ec && (ec.value() == EACCES || ec.value() == EPERM) && (_options & directory_options::skip_permission_denied) == directory_options::skip_permission_denied) { + ec.clear(); + skip = true; + } + } + else { + ::closedir(_dir); + _dir = nullptr; + _dir_entry._path.clear(); + if (errno) { + ec = detail::make_system_error(); + } + break; + } + } while (skip || std::strcmp(_entry->d_name, ".") == 0 || std::strcmp(_entry->d_name, "..") == 0); + } + } + + void copyToDirEntry() + { +#ifdef GHC_NO_DIRENT_D_TYPE + _dir_entry._symlink_status = file_status(); + _dir_entry._status = file_status(); +#else + _dir_entry._symlink_status.permissions(perms::unknown); + switch(_entry->d_type) { + case DT_BLK: _dir_entry._symlink_status.type(file_type::block); break; + case DT_CHR: _dir_entry._symlink_status.type(file_type::character); break; + case DT_DIR: _dir_entry._symlink_status.type(file_type::directory); break; + case DT_FIFO: _dir_entry._symlink_status.type(file_type::fifo); break; + case DT_LNK: _dir_entry._symlink_status.type(file_type::symlink); break; + case DT_REG: _dir_entry._symlink_status.type(file_type::regular); break; + case DT_SOCK: _dir_entry._symlink_status.type(file_type::socket); break; + case DT_UNKNOWN: _dir_entry._symlink_status.type(file_type::none); break; + default: _dir_entry._symlink_status.type(file_type::unknown); break; + } + if (_entry->d_type != DT_LNK) { + _dir_entry._status = _dir_entry._symlink_status; + } + else { + _dir_entry._status.type(file_type::none); + _dir_entry._status.permissions(perms::unknown); + } +#endif + _dir_entry._file_size = static_cast(-1); + _dir_entry._hard_link_count = static_cast(-1); + _dir_entry._last_write_time = 0; + } + path _base; + directory_options _options; + DIR* _dir; + struct ::dirent* _entry; + directory_entry _dir_entry; + std::error_code _ec; +}; +#endif + +// [fs.dir.itr.members] member functions +GHC_INLINE directory_iterator::directory_iterator() noexcept + : _impl(new impl(path(), directory_options::none)) +{ +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE directory_iterator::directory_iterator(const path& p) + : _impl(new impl(p, directory_options::none)) +{ + if (_impl->_ec) { + throw filesystem_error(detail::systemErrorText(_impl->_ec.value()), p, _impl->_ec); + } + _impl->_ec.clear(); +} + +GHC_INLINE directory_iterator::directory_iterator(const path& p, directory_options options) + : _impl(new impl(p, options)) +{ + if (_impl->_ec) { + throw filesystem_error(detail::systemErrorText(_impl->_ec.value()), p, _impl->_ec); + } +} +#endif + +GHC_INLINE directory_iterator::directory_iterator(const path& p, std::error_code& ec) noexcept + : _impl(new impl(p, directory_options::none)) +{ + if (_impl->_ec) { + ec = _impl->_ec; + } +} + +GHC_INLINE directory_iterator::directory_iterator(const path& p, directory_options options, std::error_code& ec) noexcept + : _impl(new impl(p, options)) +{ + if (_impl->_ec) { + ec = _impl->_ec; + } +} + +GHC_INLINE directory_iterator::directory_iterator(const directory_iterator& rhs) + : _impl(rhs._impl) +{ +} + +GHC_INLINE directory_iterator::directory_iterator(directory_iterator&& rhs) noexcept + : _impl(std::move(rhs._impl)) +{ +} + +GHC_INLINE directory_iterator::~directory_iterator() {} + +GHC_INLINE directory_iterator& directory_iterator::operator=(const directory_iterator& rhs) +{ + _impl = rhs._impl; + return *this; +} + +GHC_INLINE directory_iterator& directory_iterator::operator=(directory_iterator&& rhs) noexcept +{ + _impl = std::move(rhs._impl); + return *this; +} + +GHC_INLINE const directory_entry& directory_iterator::operator*() const +{ + return _impl->_dir_entry; +} + +GHC_INLINE const directory_entry* directory_iterator::operator->() const +{ + return &_impl->_dir_entry; +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE directory_iterator& directory_iterator::operator++() +{ + std::error_code ec; + _impl->increment(ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), _impl->_dir_entry._path, ec); + } + return *this; +} +#endif + +GHC_INLINE directory_iterator& directory_iterator::increment(std::error_code& ec) noexcept +{ + _impl->increment(ec); + return *this; +} + +GHC_INLINE bool directory_iterator::operator==(const directory_iterator& rhs) const +{ + return _impl->_dir_entry._path == rhs._impl->_dir_entry._path; +} + +GHC_INLINE bool directory_iterator::operator!=(const directory_iterator& rhs) const +{ + return _impl->_dir_entry._path != rhs._impl->_dir_entry._path; +} + +// [fs.dir.itr.nonmembers] directory_iterator non-member functions + +GHC_INLINE directory_iterator begin(directory_iterator iter) noexcept +{ + return iter; +} + +GHC_INLINE directory_iterator end(const directory_iterator&) noexcept +{ + return directory_iterator(); +} + +//----------------------------------------------------------------------------- +// [fs.class.rec.dir.itr] class recursive_directory_iterator + +GHC_INLINE recursive_directory_iterator::recursive_directory_iterator() noexcept + : _impl(new recursive_directory_iterator_impl(directory_options::none, true)) +{ + _impl->_dir_iter_stack.push(directory_iterator()); +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE recursive_directory_iterator::recursive_directory_iterator(const path& p) + : _impl(new recursive_directory_iterator_impl(directory_options::none, true)) +{ + _impl->_dir_iter_stack.push(directory_iterator(p)); +} + +GHC_INLINE recursive_directory_iterator::recursive_directory_iterator(const path& p, directory_options options) + : _impl(new recursive_directory_iterator_impl(options, true)) +{ + _impl->_dir_iter_stack.push(directory_iterator(p, options)); +} +#endif + +GHC_INLINE recursive_directory_iterator::recursive_directory_iterator(const path& p, directory_options options, std::error_code& ec) noexcept + : _impl(new recursive_directory_iterator_impl(options, true)) +{ + _impl->_dir_iter_stack.push(directory_iterator(p, options, ec)); +} + +GHC_INLINE recursive_directory_iterator::recursive_directory_iterator(const path& p, std::error_code& ec) noexcept + : _impl(new recursive_directory_iterator_impl(directory_options::none, true)) +{ + _impl->_dir_iter_stack.push(directory_iterator(p, ec)); +} + +GHC_INLINE recursive_directory_iterator::recursive_directory_iterator(const recursive_directory_iterator& rhs) + : _impl(rhs._impl) +{ +} + +GHC_INLINE recursive_directory_iterator::recursive_directory_iterator(recursive_directory_iterator&& rhs) noexcept + : _impl(std::move(rhs._impl)) +{ +} + +GHC_INLINE recursive_directory_iterator::~recursive_directory_iterator() {} + +// [fs.rec.dir.itr.members] observers +GHC_INLINE directory_options recursive_directory_iterator::options() const +{ + return _impl->_options; +} + +GHC_INLINE int recursive_directory_iterator::depth() const +{ + return static_cast(_impl->_dir_iter_stack.size() - 1); +} + +GHC_INLINE bool recursive_directory_iterator::recursion_pending() const +{ + return _impl->_recursion_pending; +} + +GHC_INLINE const directory_entry& recursive_directory_iterator::operator*() const +{ + return *(_impl->_dir_iter_stack.top()); +} + +GHC_INLINE const directory_entry* recursive_directory_iterator::operator->() const +{ + return &(*(_impl->_dir_iter_stack.top())); +} + +// [fs.rec.dir.itr.members] modifiers recursive_directory_iterator& +GHC_INLINE recursive_directory_iterator& recursive_directory_iterator::operator=(const recursive_directory_iterator& rhs) +{ + _impl = rhs._impl; + return *this; +} + +GHC_INLINE recursive_directory_iterator& recursive_directory_iterator::operator=(recursive_directory_iterator&& rhs) noexcept +{ + _impl = std::move(rhs._impl); + return *this; +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE recursive_directory_iterator& recursive_directory_iterator::operator++() +{ + std::error_code ec; + increment(ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), _impl->_dir_iter_stack.empty() ? path() : _impl->_dir_iter_stack.top()->path(), ec); + } + return *this; +} +#endif + +GHC_INLINE recursive_directory_iterator& recursive_directory_iterator::increment(std::error_code& ec) noexcept +{ + bool isSymLink = (*this)->is_symlink(ec); + bool isDir = !ec && (*this)->is_directory(ec); + if(isSymLink && detail::is_not_found_error(ec)) { + ec.clear(); + } + if(!ec) { + if (recursion_pending() && isDir && (!isSymLink || (options() & directory_options::follow_directory_symlink) != directory_options::none)) { + _impl->_dir_iter_stack.push(directory_iterator((*this)->path(), _impl->_options, ec)); + } + else { + _impl->_dir_iter_stack.top().increment(ec); + } + if (!ec) { + while (depth() && _impl->_dir_iter_stack.top() == directory_iterator()) { + _impl->_dir_iter_stack.pop(); + _impl->_dir_iter_stack.top().increment(ec); + } + } + else if (!_impl->_dir_iter_stack.empty()) { + _impl->_dir_iter_stack.pop(); + } + _impl->_recursion_pending = true; + } + return *this; +} + +#ifdef GHC_WITH_EXCEPTIONS +GHC_INLINE void recursive_directory_iterator::pop() +{ + std::error_code ec; + pop(ec); + if (ec) { + throw filesystem_error(detail::systemErrorText(ec.value()), _impl->_dir_iter_stack.empty() ? path() : _impl->_dir_iter_stack.top()->path(), ec); + } +} +#endif + +GHC_INLINE void recursive_directory_iterator::pop(std::error_code& ec) +{ + if (depth() == 0) { + *this = recursive_directory_iterator(); + } + else { + do { + _impl->_dir_iter_stack.pop(); + _impl->_dir_iter_stack.top().increment(ec); + } while (depth() && _impl->_dir_iter_stack.top() == directory_iterator()); + } +} + +GHC_INLINE void recursive_directory_iterator::disable_recursion_pending() +{ + _impl->_recursion_pending = false; +} + +// other members as required by [input.iterators] +GHC_INLINE bool recursive_directory_iterator::operator==(const recursive_directory_iterator& rhs) const +{ + return _impl->_dir_iter_stack.top() == rhs._impl->_dir_iter_stack.top(); +} + +GHC_INLINE bool recursive_directory_iterator::operator!=(const recursive_directory_iterator& rhs) const +{ + return _impl->_dir_iter_stack.top() != rhs._impl->_dir_iter_stack.top(); +} + +// [fs.rec.dir.itr.nonmembers] directory_iterator non-member functions +GHC_INLINE recursive_directory_iterator begin(recursive_directory_iterator iter) noexcept +{ + return iter; +} + +GHC_INLINE recursive_directory_iterator end(const recursive_directory_iterator&) noexcept +{ + return recursive_directory_iterator(); +} + +#endif // GHC_EXPAND_IMPL + +} // namespace filesystem +} // namespace ghc + +// cleanup some macros +#undef GHC_INLINE +#undef GHC_EXPAND_IMPL + +#endif // GHC_FILESYSTEM_H diff --git a/wpiutil/src/main/native/include/wpi/raw_ostream.h b/wpiutil/src/main/native/include/wpi/raw_ostream.h index 348dacbf3d..517090979d 100644 --- a/wpiutil/src/main/native/include/wpi/raw_ostream.h +++ b/wpiutil/src/main/native/include/wpi/raw_ostream.h @@ -26,6 +26,12 @@ #include #include +namespace fs { +enum FileAccess : unsigned; +enum OpenFlags : unsigned; +enum CreationDisposition : unsigned; +} // end namespace fs + namespace wpi { class format_object_base; @@ -33,14 +39,6 @@ class FormattedString; class FormattedNumber; class FormattedBytes; -namespace sys { -namespace fs { -enum FileAccess : unsigned; -enum OpenFlags : unsigned; -enum CreationDisposition : unsigned; -} // end namespace fs -} // end namespace sys - /// This class implements an extremely fast bulk output stream that can *only* /// output to a stream. It does not support seeking, reopening, rewinding, line /// buffered disciplines etc. It is a simple buffer that outputs @@ -447,14 +445,14 @@ public: /// descriptor. raw_fd_ostream(StringRef Filename, std::error_code &EC); raw_fd_ostream(StringRef Filename, std::error_code &EC, - sys::fs::CreationDisposition Disp); + fs::CreationDisposition Disp); raw_fd_ostream(StringRef Filename, std::error_code &EC, - sys::fs::FileAccess Access); + fs::FileAccess Access); raw_fd_ostream(StringRef Filename, std::error_code &EC, - sys::fs::OpenFlags Flags); + fs::OpenFlags Flags); raw_fd_ostream(StringRef Filename, std::error_code &EC, - sys::fs::CreationDisposition Disp, sys::fs::FileAccess Access, - sys::fs::OpenFlags Flags); + fs::CreationDisposition Disp, fs::FileAccess Access, + fs::OpenFlags Flags); /// FD is the file descriptor that this writes to. If ShouldClose is true, /// this closes the file when the stream is destroyed. If FD is for stdout or