mirror of
https://github.com/wpilibsuite/allwpilib
synced 2026-06-19 00:41:43 +00:00
[wpiutil] Replace llvm filesystem with C++17 filesystem (#3401)
Use ghc::filesystem as fill on older GCC (e.g. RoboRIO). This can be removed once all GCC platforms have upgraded to 8.1 or later. File open functionality has been retained from LLVM but moved to "fs" namespace and tweaked for improved consistency with std::filesystem (e.g. error_code is passed by reference instead of returned). Also update WPILibC's Filesystem functions to return std::string.
This commit is contained in:
@@ -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 <s.schuemann@pobox.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
|
||||
#include "Instance.h"
|
||||
|
||||
#include <wpi/Path.h>
|
||||
#include <wpi/SmallString.h>
|
||||
#include <wpi/StringRef.h>
|
||||
#include <wpi/fs.h>
|
||||
#include <wpi/raw_ostream.h>
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
@@ -22,10 +22,9 @@
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
|
||||
#include <wpi/FileSystem.h>
|
||||
#include <wpi/MemAlloc.h>
|
||||
#include <wpi/Path.h>
|
||||
#include <wpi/SmallString.h>
|
||||
#include <wpi/fs.h>
|
||||
#include <wpi/raw_ostream.h>
|
||||
#include <wpi/timestamp.h>
|
||||
|
||||
@@ -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<UsbCameraInfo> 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);
|
||||
|
||||
@@ -20,9 +20,9 @@
|
||||
#include <portable-file-dialogs.h>
|
||||
#include <units/angle.h>
|
||||
#include <units/length.h>
|
||||
#include <wpi/Path.h>
|
||||
#include <wpi/SmallString.h>
|
||||
#include <wpi/StringMap.h>
|
||||
#include <wpi/fs.h>
|
||||
#include <wpi/json.h>
|
||||
#include <wpi/raw_istream.h>
|
||||
#include <wpi/raw_ostream.h>
|
||||
@@ -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;
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
#include <cstdio>
|
||||
#include <cstring>
|
||||
|
||||
#include <wpi/FileSystem.h>
|
||||
#include <wpi/StringRef.h>
|
||||
#include <wpi/fs.h>
|
||||
|
||||
#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<wpi::StringRef, 16> 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;
|
||||
|
||||
@@ -6,9 +6,9 @@
|
||||
|
||||
#include <vector>
|
||||
|
||||
#include <wpi/Path.h>
|
||||
#include <wpi/SmallString.h>
|
||||
#include <wpi/StringRef.h>
|
||||
#include <wpi/fs.h>
|
||||
#include <wpi/raw_ostream.h>
|
||||
#include <wpi/spinlock.h>
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
|
||||
@@ -4,9 +4,9 @@
|
||||
|
||||
#include "LoggerImpl.h"
|
||||
|
||||
#include <wpi/Path.h>
|
||||
#include <wpi/SmallString.h>
|
||||
#include <wpi/StringRef.h>
|
||||
#include <wpi/fs.h>
|
||||
#include <wpi/raw_ostream.h>
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -6,10 +6,10 @@
|
||||
#include <string>
|
||||
|
||||
#include <wpi/Base64.h>
|
||||
#include <wpi/FileSystem.h>
|
||||
#include <wpi/Format.h>
|
||||
#include <wpi/SmallString.h>
|
||||
#include <wpi/StringExtras.h>
|
||||
#include <wpi/fs.h>
|
||||
#include <wpi/raw_ostream.h>
|
||||
|
||||
#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";
|
||||
}
|
||||
|
||||
@@ -6,11 +6,10 @@
|
||||
|
||||
#include <uv.h>
|
||||
|
||||
#include <wpi/FileSystem.h>
|
||||
#include <wpi/MimeTypes.h>
|
||||
#include <wpi/Path.h>
|
||||
#include <wpi/SmallVector.h>
|
||||
#include <wpi/UrlParser.h>
|
||||
#include <wpi/fs.h>
|
||||
#include <wpi/raw_istream.h>
|
||||
#include <wpi/raw_ostream.h>
|
||||
#include <wpi/raw_uv_ostream.h>
|
||||
@@ -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<uv::Buffer, 4> 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<uv::Buffer, 4> 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<char, 32> 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");
|
||||
|
||||
@@ -4,12 +4,11 @@
|
||||
|
||||
#include "HALSimWeb.h"
|
||||
|
||||
#include <wpi/FileSystem.h>
|
||||
#include <wpi/Path.h>
|
||||
#include <wpi/SmallString.h>
|
||||
#include <wpi/Twine.h>
|
||||
#include <wpi/UrlParser.h>
|
||||
#include <wpi/WebSocketServer.h>
|
||||
#include <wpi/fs.h>
|
||||
#include <wpi/raw_uv_ostream.h>
|
||||
#include <wpi/uv/Loop.h>
|
||||
#include <wpi/uv/Tcp.h>
|
||||
@@ -42,31 +41,22 @@ bool HALSimWeb::Initialize() {
|
||||
}
|
||||
|
||||
// determine where to get static content from
|
||||
// wpi::SmallVector<char, 64> 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) {
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
|
||||
#include <hal/DriverStation.h>
|
||||
#include <hal/HALBase.h>
|
||||
#include <wpi/Path.h>
|
||||
#include <wpi/StackTrace.h>
|
||||
#include <wpi/fs.h>
|
||||
|
||||
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(),
|
||||
|
||||
@@ -4,31 +4,26 @@
|
||||
|
||||
#include "frc/Filesystem.h"
|
||||
|
||||
#include <wpi/FileSystem.h>
|
||||
#include <wpi/Path.h>
|
||||
#include <wpi/fs.h>
|
||||
|
||||
#include "frc/RobotBase.h"
|
||||
|
||||
void frc::filesystem::GetLaunchDirectory(wpi::SmallVectorImpl<char>& result) {
|
||||
wpi::sys::fs::current_path(result);
|
||||
std::string frc::filesystem::GetLaunchDirectory() {
|
||||
return fs::current_path().string();
|
||||
}
|
||||
|
||||
void frc::filesystem::GetOperatingDirectory(
|
||||
wpi::SmallVectorImpl<char>& 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<char>& 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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <wpi/SmallVector.h>
|
||||
#include <string>
|
||||
|
||||
/** 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<char>& 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<char>& result);
|
||||
std::string GetOperatingDirectory();
|
||||
|
||||
/**
|
||||
* Obtains the deploy directory of the program, which is the remote location
|
||||
@@ -32,8 +32,8 @@ void GetOperatingDirectory(wpi::SmallVectorImpl<char>& 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<char>& result);
|
||||
std::string GetDeployDirectory();
|
||||
|
||||
} // namespace frc::filesystem
|
||||
|
||||
@@ -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
|
||||
|
||||
326
wpiutil/src/main/native/cpp/fs.cpp
Normal file
326
wpiutil/src/main/native/cpp/fs.cpp
Normal file
@@ -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 <cassert>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <fcntl.h>
|
||||
#include <io.h>
|
||||
#include <sys/types.h>
|
||||
// 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 <windows.h>
|
||||
#undef WIN32_NO_STATUS
|
||||
#include <winternl.h>
|
||||
#include <ntstatus.h>
|
||||
|
||||
#include <shellapi.h>
|
||||
#include <shlobj.h>
|
||||
|
||||
#include "wpi/WindowsError.h"
|
||||
|
||||
#else // _WIN32
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#endif // _WIN32
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#include <Availability.h>
|
||||
#endif
|
||||
#if ((defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) \
|
||||
|| (defined(__cplusplus) && __cplusplus >= 201703L)) \
|
||||
&& defined(__has_include)
|
||||
#if __has_include(<filesystem>) \
|
||||
&& (!defined(__MAC_OS_X_VERSION_MIN_REQUIRED) \
|
||||
|| __MAC_OS_X_VERSION_MIN_REQUIRED >= 101500) \
|
||||
&& (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
|
||||
@@ -52,6 +52,11 @@
|
||||
#endif
|
||||
#include <assert.h>
|
||||
|
||||
#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<wchar_t> &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<wchar_t> &utf16) {
|
||||
return CodePageToUTF16(CP_UTF8, utf8, utf16);
|
||||
}
|
||||
|
||||
std::error_code CurCPToUTF16(wpi::StringRef curcp,
|
||||
wpi::SmallVectorImpl<wchar_t> &utf16) {
|
||||
return CodePageToUTF16(CP_ACP, curcp, utf16);
|
||||
}
|
||||
|
||||
static
|
||||
std::error_code UTF16ToCodePage(unsigned codepage, const wchar_t *utf16,
|
||||
size_t utf16_len,
|
||||
wpi::SmallVectorImpl<char> &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<char> &utf8) {
|
||||
return UTF16ToCodePage(CP_UTF8, utf16, utf16_len, utf8);
|
||||
}
|
||||
|
||||
std::error_code UTF16ToCurCP(const wchar_t *utf16, size_t utf16_len,
|
||||
wpi::SmallVectorImpl<char> &curcp) {
|
||||
return UTF16ToCodePage(CP_ACP, utf16, utf16_len, curcp);
|
||||
}
|
||||
|
||||
} // end namespace windows
|
||||
} // end namespace sys
|
||||
|
||||
#endif // _WIN32
|
||||
|
||||
} // namespace llvm
|
||||
|
||||
ConvertUTF_RESTORE_WARNINGS
|
||||
|
||||
@@ -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 <cctype>
|
||||
#include <cstring>
|
||||
|
||||
#if !defined(_MSC_VER) && !defined(__MINGW32__)
|
||||
#include <unistd.h>
|
||||
#else
|
||||
#include <io.h>
|
||||
#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<unsigned char>(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<char> &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<StringRef, 4> 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<char> &path, const Twine &a, const Twine &b,
|
||||
const Twine &c, const Twine &d) {
|
||||
append(path, Style::native, a, b, c, d);
|
||||
}
|
||||
|
||||
void append(SmallVectorImpl<char> &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<char> &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<char> &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<char> &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<char> &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<char> &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<StringRef, 16> 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<char> &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<char> &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<char> &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<perms> 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
|
||||
@@ -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 <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/mman.h>
|
||||
#include <dirent.h>
|
||||
#include <pwd.h>
|
||||
#include <sys/param.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
|
||||
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<char> &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<perms>(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<char*>(Mapping);
|
||||
}
|
||||
|
||||
const char *mapped_file_region::const_data() const {
|
||||
assert(Mapping && "Mapping failed but used anyway!");
|
||||
return reinterpret_cast<const char*>(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<intptr_t>(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<DIR *>(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<DIR *>(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<basic_file_status> 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<int> 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<char> *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<file_t> openNativeFileForRead(const Twine &Name, OpenFlags Flags,
|
||||
SmallVectorImpl<char> *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<char> &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<char> &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 <unistd.h> 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<char> &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
|
||||
@@ -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 <fcntl.h>
|
||||
#include <io.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
// 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 <shellapi.h>
|
||||
#include <shlobj.h>
|
||||
|
||||
#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<wchar_t> &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<uint64_t>(FileIndexHigh) << 32ULL) |
|
||||
static_cast<uint64_t>(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<char> &result) {
|
||||
SmallVector<wchar_t, MAX_PATH> 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<wchar_t> &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<char> &RealPath) {
|
||||
RealPath.clear();
|
||||
SmallVector<wchar_t, MAX_PATH> 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<wchar_t, 128> 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<wchar_t, 128> 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<HANDLE>(_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<HANDLE>(_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<char*>(Mapping);
|
||||
}
|
||||
|
||||
const char *mapped_file_region::const_data() const {
|
||||
assert(Mapping && "Mapping failed but used anyway!");
|
||||
return reinterpret_cast<const char*>(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<wchar_t, 128> 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<basic_file_status> directory_entry::status() const {
|
||||
return Status;
|
||||
}
|
||||
|
||||
static std::error_code nativeFileToFd(Expected<HANDLE> 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<wchar_t, 128> 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<file_t> 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<file_t> 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<char> &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<char> *RealPath) {
|
||||
Expected<HANDLE> NativeFile = openNativeFileForRead(Name, Flags, RealPath);
|
||||
return nativeFileToFd(std::move(NativeFile), ResultFD, OF_None);
|
||||
}
|
||||
|
||||
Expected<file_t> openNativeFileForRead(const Twine &Name, OpenFlags Flags,
|
||||
SmallVectorImpl<char> *RealPath) {
|
||||
Expected<file_t> 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<char> &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<char> &result) {
|
||||
return getKnownFolderPath(FOLDERID_Profile, result);
|
||||
}
|
||||
|
||||
static bool getTempDirEnvVar(const wchar_t *Var, SmallVectorImpl<char> &Res) {
|
||||
SmallVector<wchar_t, 1024> 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<char> &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<char> &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<wchar_t> &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<wchar_t> &utf16) {
|
||||
return CodePageToUTF16(CP_UTF8, utf8, utf16);
|
||||
}
|
||||
|
||||
std::error_code CurCPToUTF16(wpi::StringRef curcp,
|
||||
wpi::SmallVectorImpl<wchar_t> &utf16) {
|
||||
return CodePageToUTF16(CP_ACP, curcp, utf16);
|
||||
}
|
||||
|
||||
static
|
||||
std::error_code UTF16ToCodePage(unsigned codepage, const wchar_t *utf16,
|
||||
size_t utf16_len,
|
||||
wpi::SmallVectorImpl<char> &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<char> &utf8) {
|
||||
return UTF16ToCodePage(CP_UTF8, utf16, utf16_len, utf8);
|
||||
}
|
||||
|
||||
std::error_code UTF16ToCurCP(const wchar_t *utf16, size_t utf16_len,
|
||||
wpi::SmallVectorImpl<char> &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
|
||||
@@ -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 <algorithm>
|
||||
#include <cctype>
|
||||
#include <cerrno>
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -15,9 +15,9 @@
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
235
wpiutil/src/main/native/include/wpi/fs.h
Normal file
235
wpiutil/src/main/native/include/wpi/fs.h
Normal file
@@ -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 <string_view>
|
||||
#include <system_error>
|
||||
|
||||
#if defined(__APPLE__)
|
||||
#include <Availability.h>
|
||||
#endif
|
||||
#if ((defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) \
|
||||
|| (defined(__cplusplus) && __cplusplus >= 201703L)) \
|
||||
&& defined(__has_include)
|
||||
#if __has_include(<filesystem>) \
|
||||
&& (!defined(__MAC_OS_X_VERSION_MIN_REQUIRED) \
|
||||
|| __MAC_OS_X_VERSION_MIN_REQUIRED >= 101500) \
|
||||
&& (defined(__clang__) || !defined(__GNUC__) || __GNUC__ >= 10 \
|
||||
|| (__GNUC__ >= 9 && __GNUC_MINOR__ >= 1))
|
||||
#define GHC_USE_STD_FS
|
||||
#include <filesystem>
|
||||
namespace fs {
|
||||
using namespace std::filesystem;
|
||||
using ifstream = std::ifstream;
|
||||
using ofstream = std::ofstream;
|
||||
using fstream = std::fstream;
|
||||
} // 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
|
||||
5942
wpiutil/src/main/native/include/wpi/ghc/filesystem.hpp
Normal file
5942
wpiutil/src/main/native/include/wpi/ghc/filesystem.hpp
Normal file
File diff suppressed because it is too large
Load Diff
@@ -26,6 +26,12 @@
|
||||
#include <vector>
|
||||
#include <system_error>
|
||||
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user