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