mirror of
https://github.com/jart/cosmopolitan.git
synced 2025-10-04 13:41:02 +00:00
Release Cosmopolitan v3.6.0
This release is an atomic upgrade to GCC 14.1.0 with C23 and C++23
This commit is contained in:
parent
62ace3623a
commit
5660ec4741
1585 changed files with 117353 additions and 271644 deletions
441
third_party/libcxx/fs/path.cpp
vendored
Normal file
441
third_party/libcxx/fs/path.cpp
vendored
Normal file
|
@ -0,0 +1,441 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include <__config>
|
||||
#include <filesystem>
|
||||
#include <vector>
|
||||
|
||||
#include "error.h"
|
||||
#include "path_parser.h"
|
||||
|
||||
_LIBCPP_BEGIN_NAMESPACE_FILESYSTEM
|
||||
|
||||
using detail::ErrorHandler;
|
||||
using parser::createView;
|
||||
using parser::PathParser;
|
||||
using parser::string_view_t;
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// path definitions
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
constexpr path::value_type path::preferred_separator;
|
||||
|
||||
path& path::replace_extension(path const& replacement) {
|
||||
path p = extension();
|
||||
if (not p.empty()) {
|
||||
__pn_.erase(__pn_.size() - p.native().size());
|
||||
}
|
||||
if (!replacement.empty()) {
|
||||
if (replacement.native()[0] != '.') {
|
||||
__pn_ += PATHSTR(".");
|
||||
}
|
||||
__pn_.append(replacement.__pn_);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////
|
||||
// path.decompose
|
||||
|
||||
string_view_t path::__root_name() const {
|
||||
auto PP = PathParser::CreateBegin(__pn_);
|
||||
if (PP.State_ == PathParser::PS_InRootName)
|
||||
return *PP;
|
||||
return {};
|
||||
}
|
||||
|
||||
string_view_t path::__root_directory() const {
|
||||
auto PP = PathParser::CreateBegin(__pn_);
|
||||
if (PP.State_ == PathParser::PS_InRootName)
|
||||
++PP;
|
||||
if (PP.State_ == PathParser::PS_InRootDir)
|
||||
return *PP;
|
||||
return {};
|
||||
}
|
||||
|
||||
string_view_t path::__root_path_raw() const {
|
||||
auto PP = PathParser::CreateBegin(__pn_);
|
||||
if (PP.State_ == PathParser::PS_InRootName) {
|
||||
auto NextCh = PP.peek();
|
||||
if (NextCh && isSeparator(*NextCh)) {
|
||||
++PP;
|
||||
return createView(__pn_.data(), &PP.RawEntry.back());
|
||||
}
|
||||
return PP.RawEntry;
|
||||
}
|
||||
if (PP.State_ == PathParser::PS_InRootDir)
|
||||
return *PP;
|
||||
return {};
|
||||
}
|
||||
|
||||
static bool ConsumeRootName(PathParser* PP) {
|
||||
static_assert(PathParser::PS_BeforeBegin == 1 && PathParser::PS_InRootName == 2, "Values for enums are incorrect");
|
||||
while (PP->State_ <= PathParser::PS_InRootName)
|
||||
++(*PP);
|
||||
return PP->State_ == PathParser::PS_AtEnd;
|
||||
}
|
||||
|
||||
static bool ConsumeRootDir(PathParser* PP) {
|
||||
static_assert(PathParser::PS_BeforeBegin == 1 && PathParser::PS_InRootName == 2 && PathParser::PS_InRootDir == 3,
|
||||
"Values for enums are incorrect");
|
||||
while (PP->State_ <= PathParser::PS_InRootDir)
|
||||
++(*PP);
|
||||
return PP->State_ == PathParser::PS_AtEnd;
|
||||
}
|
||||
|
||||
string_view_t path::__relative_path() const {
|
||||
auto PP = PathParser::CreateBegin(__pn_);
|
||||
if (ConsumeRootDir(&PP))
|
||||
return {};
|
||||
return createView(PP.RawEntry.data(), &__pn_.back());
|
||||
}
|
||||
|
||||
string_view_t path::__parent_path() const {
|
||||
if (empty())
|
||||
return {};
|
||||
// Determine if we have a root path but not a relative path. In that case
|
||||
// return *this.
|
||||
{
|
||||
auto PP = PathParser::CreateBegin(__pn_);
|
||||
if (ConsumeRootDir(&PP))
|
||||
return __pn_;
|
||||
}
|
||||
// Otherwise remove a single element from the end of the path, and return
|
||||
// a string representing that path
|
||||
{
|
||||
auto PP = PathParser::CreateEnd(__pn_);
|
||||
--PP;
|
||||
if (PP.RawEntry.data() == __pn_.data())
|
||||
return {};
|
||||
--PP;
|
||||
return createView(__pn_.data(), &PP.RawEntry.back());
|
||||
}
|
||||
}
|
||||
|
||||
string_view_t path::__filename() const {
|
||||
if (empty())
|
||||
return {};
|
||||
{
|
||||
PathParser PP = PathParser::CreateBegin(__pn_);
|
||||
if (ConsumeRootDir(&PP))
|
||||
return {};
|
||||
}
|
||||
return *(--PathParser::CreateEnd(__pn_));
|
||||
}
|
||||
|
||||
string_view_t path::__stem() const { return parser::separate_filename(__filename()).first; }
|
||||
|
||||
string_view_t path::__extension() const { return parser::separate_filename(__filename()).second; }
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// path.gen
|
||||
|
||||
enum PathPartKind : unsigned char { PK_None, PK_RootSep, PK_Filename, PK_Dot, PK_DotDot, PK_TrailingSep };
|
||||
|
||||
static PathPartKind ClassifyPathPart(string_view_t Part) {
|
||||
if (Part.empty())
|
||||
return PK_TrailingSep;
|
||||
if (Part == PATHSTR("."))
|
||||
return PK_Dot;
|
||||
if (Part == PATHSTR(".."))
|
||||
return PK_DotDot;
|
||||
if (Part == PATHSTR("/"))
|
||||
return PK_RootSep;
|
||||
#if defined(_LIBCPP_WIN32API)
|
||||
if (Part == PATHSTR("\\"))
|
||||
return PK_RootSep;
|
||||
#endif
|
||||
return PK_Filename;
|
||||
}
|
||||
|
||||
path path::lexically_normal() const {
|
||||
if (__pn_.empty())
|
||||
return *this;
|
||||
|
||||
using PartKindPair = pair<string_view_t, PathPartKind>;
|
||||
vector<PartKindPair> Parts;
|
||||
// Guess as to how many elements the path has to avoid reallocating.
|
||||
Parts.reserve(32);
|
||||
|
||||
// Track the total size of the parts as we collect them. This allows the
|
||||
// resulting path to reserve the correct amount of memory.
|
||||
size_t NewPathSize = 0;
|
||||
auto AddPart = [&](PathPartKind K, string_view_t P) {
|
||||
NewPathSize += P.size();
|
||||
Parts.emplace_back(P, K);
|
||||
};
|
||||
auto LastPartKind = [&]() {
|
||||
if (Parts.empty())
|
||||
return PK_None;
|
||||
return Parts.back().second;
|
||||
};
|
||||
|
||||
bool MaybeNeedTrailingSep = false;
|
||||
// Build a stack containing the remaining elements of the path, popping off
|
||||
// elements which occur before a '..' entry.
|
||||
for (auto PP = PathParser::CreateBegin(__pn_); PP; ++PP) {
|
||||
auto Part = *PP;
|
||||
PathPartKind Kind = ClassifyPathPart(Part);
|
||||
switch (Kind) {
|
||||
case PK_Filename:
|
||||
case PK_RootSep: {
|
||||
// Add all non-dot and non-dot-dot elements to the stack of elements.
|
||||
AddPart(Kind, Part);
|
||||
MaybeNeedTrailingSep = false;
|
||||
break;
|
||||
}
|
||||
case PK_DotDot: {
|
||||
// Only push a ".." element if there are no elements preceding the "..",
|
||||
// or if the preceding element is itself "..".
|
||||
auto LastKind = LastPartKind();
|
||||
if (LastKind == PK_Filename) {
|
||||
NewPathSize -= Parts.back().first.size();
|
||||
Parts.pop_back();
|
||||
} else if (LastKind != PK_RootSep)
|
||||
AddPart(PK_DotDot, PATHSTR(".."));
|
||||
MaybeNeedTrailingSep = LastKind == PK_Filename;
|
||||
break;
|
||||
}
|
||||
case PK_Dot:
|
||||
case PK_TrailingSep: {
|
||||
MaybeNeedTrailingSep = true;
|
||||
break;
|
||||
}
|
||||
case PK_None:
|
||||
__libcpp_unreachable();
|
||||
}
|
||||
}
|
||||
// [fs.path.generic]p6.8: If the path is empty, add a dot.
|
||||
if (Parts.empty())
|
||||
return PATHSTR(".");
|
||||
|
||||
// [fs.path.generic]p6.7: If the last filename is dot-dot, remove any
|
||||
// trailing directory-separator.
|
||||
bool NeedTrailingSep = MaybeNeedTrailingSep && LastPartKind() == PK_Filename;
|
||||
|
||||
path Result;
|
||||
Result.__pn_.reserve(Parts.size() + NewPathSize + NeedTrailingSep);
|
||||
for (auto& PK : Parts)
|
||||
Result /= PK.first;
|
||||
|
||||
if (NeedTrailingSep)
|
||||
Result /= PATHSTR("");
|
||||
|
||||
Result.make_preferred();
|
||||
return Result;
|
||||
}
|
||||
|
||||
static int DetermineLexicalElementCount(PathParser PP) {
|
||||
int Count = 0;
|
||||
for (; PP; ++PP) {
|
||||
auto Elem = *PP;
|
||||
if (Elem == PATHSTR(".."))
|
||||
--Count;
|
||||
else if (Elem != PATHSTR(".") && Elem != PATHSTR(""))
|
||||
++Count;
|
||||
}
|
||||
return Count;
|
||||
}
|
||||
|
||||
path path::lexically_relative(const path& base) const {
|
||||
{ // perform root-name/root-directory mismatch checks
|
||||
auto PP = PathParser::CreateBegin(__pn_);
|
||||
auto PPBase = PathParser::CreateBegin(base.__pn_);
|
||||
auto CheckIterMismatchAtBase = [&]() {
|
||||
return PP.State_ != PPBase.State_ && (PP.inRootPath() || PPBase.inRootPath());
|
||||
};
|
||||
if (PP.inRootName() && PPBase.inRootName()) {
|
||||
if (*PP != *PPBase)
|
||||
return {};
|
||||
} else if (CheckIterMismatchAtBase())
|
||||
return {};
|
||||
|
||||
if (PP.inRootPath())
|
||||
++PP;
|
||||
if (PPBase.inRootPath())
|
||||
++PPBase;
|
||||
if (CheckIterMismatchAtBase())
|
||||
return {};
|
||||
}
|
||||
|
||||
// Find the first mismatching element
|
||||
auto PP = PathParser::CreateBegin(__pn_);
|
||||
auto PPBase = PathParser::CreateBegin(base.__pn_);
|
||||
while (PP && PPBase && PP.State_ == PPBase.State_ && *PP == *PPBase) {
|
||||
++PP;
|
||||
++PPBase;
|
||||
}
|
||||
|
||||
// If there is no mismatch, return ".".
|
||||
if (!PP && !PPBase)
|
||||
return ".";
|
||||
|
||||
// Otherwise, determine the number of elements, 'n', which are not dot or
|
||||
// dot-dot minus the number of dot-dot elements.
|
||||
int ElemCount = DetermineLexicalElementCount(PPBase);
|
||||
if (ElemCount < 0)
|
||||
return {};
|
||||
|
||||
// if n == 0 and (a == end() || a->empty()), returns path("."); otherwise
|
||||
if (ElemCount == 0 && (PP.atEnd() || *PP == PATHSTR("")))
|
||||
return PATHSTR(".");
|
||||
|
||||
// return a path constructed with 'n' dot-dot elements, followed by the
|
||||
// elements of '*this' after the mismatch.
|
||||
path Result;
|
||||
// FIXME: Reserve enough room in Result that it won't have to re-allocate.
|
||||
while (ElemCount--)
|
||||
Result /= PATHSTR("..");
|
||||
for (; PP; ++PP)
|
||||
Result /= *PP;
|
||||
return Result;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// path.comparisons
|
||||
static int CompareRootName(PathParser* LHS, PathParser* RHS) {
|
||||
if (!LHS->inRootName() && !RHS->inRootName())
|
||||
return 0;
|
||||
|
||||
auto GetRootName = [](PathParser* Parser) -> string_view_t { return Parser->inRootName() ? **Parser : PATHSTR(""); };
|
||||
int res = GetRootName(LHS).compare(GetRootName(RHS));
|
||||
ConsumeRootName(LHS);
|
||||
ConsumeRootName(RHS);
|
||||
return res;
|
||||
}
|
||||
|
||||
static int CompareRootDir(PathParser* LHS, PathParser* RHS) {
|
||||
if (!LHS->inRootDir() && RHS->inRootDir())
|
||||
return -1;
|
||||
else if (LHS->inRootDir() && !RHS->inRootDir())
|
||||
return 1;
|
||||
else {
|
||||
ConsumeRootDir(LHS);
|
||||
ConsumeRootDir(RHS);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static int CompareRelative(PathParser* LHSPtr, PathParser* RHSPtr) {
|
||||
auto& LHS = *LHSPtr;
|
||||
auto& RHS = *RHSPtr;
|
||||
|
||||
int res;
|
||||
while (LHS && RHS) {
|
||||
if ((res = (*LHS).compare(*RHS)) != 0)
|
||||
return res;
|
||||
++LHS;
|
||||
++RHS;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int CompareEndState(PathParser* LHS, PathParser* RHS) {
|
||||
if (LHS->atEnd() && !RHS->atEnd())
|
||||
return -1;
|
||||
else if (!LHS->atEnd() && RHS->atEnd())
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int path::__compare(string_view_t __s) const {
|
||||
auto LHS = PathParser::CreateBegin(__pn_);
|
||||
auto RHS = PathParser::CreateBegin(__s);
|
||||
int res;
|
||||
|
||||
if ((res = CompareRootName(&LHS, &RHS)) != 0)
|
||||
return res;
|
||||
|
||||
if ((res = CompareRootDir(&LHS, &RHS)) != 0)
|
||||
return res;
|
||||
|
||||
if ((res = CompareRelative(&LHS, &RHS)) != 0)
|
||||
return res;
|
||||
|
||||
return CompareEndState(&LHS, &RHS);
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// path.nonmembers
|
||||
size_t hash_value(const path& __p) noexcept {
|
||||
auto PP = PathParser::CreateBegin(__p.native());
|
||||
size_t hash_value = 0;
|
||||
hash<string_view_t> hasher;
|
||||
while (PP) {
|
||||
hash_value = __hash_combine(hash_value, hasher(*PP));
|
||||
++PP;
|
||||
}
|
||||
return hash_value;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// path.itr
|
||||
path::iterator path::begin() const {
|
||||
auto PP = PathParser::CreateBegin(__pn_);
|
||||
iterator it;
|
||||
it.__path_ptr_ = this;
|
||||
it.__state_ = static_cast<path::iterator::_ParserState>(PP.State_);
|
||||
it.__entry_ = PP.RawEntry;
|
||||
it.__stashed_elem_.__assign_view(*PP);
|
||||
return it;
|
||||
}
|
||||
|
||||
path::iterator path::end() const {
|
||||
iterator it{};
|
||||
it.__state_ = path::iterator::_AtEnd;
|
||||
it.__path_ptr_ = this;
|
||||
return it;
|
||||
}
|
||||
|
||||
path::iterator& path::iterator::__increment() {
|
||||
PathParser PP(__path_ptr_->native(), __entry_, __state_);
|
||||
++PP;
|
||||
__state_ = static_cast<_ParserState>(PP.State_);
|
||||
__entry_ = PP.RawEntry;
|
||||
__stashed_elem_.__assign_view(*PP);
|
||||
return *this;
|
||||
}
|
||||
|
||||
path::iterator& path::iterator::__decrement() {
|
||||
PathParser PP(__path_ptr_->native(), __entry_, __state_);
|
||||
--PP;
|
||||
__state_ = static_cast<_ParserState>(PP.State_);
|
||||
__entry_ = PP.RawEntry;
|
||||
__stashed_elem_.__assign_view(*PP);
|
||||
return *this;
|
||||
}
|
||||
|
||||
#if defined(_LIBCPP_WIN32API)
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
// Windows path conversions
|
||||
size_t __wide_to_char(const wstring& str, char* out, size_t outlen) {
|
||||
if (str.empty())
|
||||
return 0;
|
||||
ErrorHandler<size_t> err("__wide_to_char", nullptr);
|
||||
UINT codepage = AreFileApisANSI() ? CP_ACP : CP_OEMCP;
|
||||
BOOL used_default = FALSE;
|
||||
int ret = WideCharToMultiByte(codepage, 0, str.data(), str.size(), out, outlen, nullptr, &used_default);
|
||||
if (ret <= 0 || used_default)
|
||||
return err.report(errc::illegal_byte_sequence);
|
||||
return ret;
|
||||
}
|
||||
|
||||
size_t __char_to_wide(const string& str, wchar_t* out, size_t outlen) {
|
||||
if (str.empty())
|
||||
return 0;
|
||||
ErrorHandler<size_t> err("__char_to_wide", nullptr);
|
||||
UINT codepage = AreFileApisANSI() ? CP_ACP : CP_OEMCP;
|
||||
int ret = MultiByteToWideChar(codepage, MB_ERR_INVALID_CHARS, str.data(), str.size(), out, outlen);
|
||||
if (ret <= 0)
|
||||
return err.report(errc::illegal_byte_sequence);
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
_LIBCPP_END_NAMESPACE_FILESYSTEM
|
Loading…
Add table
Add a link
Reference in a new issue