brintos

brintos / llvm-project-archived public Read only

0
0
Text · 12.7 KiB · 400b6e8 Raw
448 lines · cpp
1//===----------------------------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8 9#include <__config>10#include <filesystem>11#include <vector>12 13#include "error.h"14#include "path_parser.h"15 16_LIBCPP_BEGIN_NAMESPACE_FILESYSTEM17 18using detail::ErrorHandler;19using parser::createView;20using parser::PathParser;21using parser::string_view_t;22 23///////////////////////////////////////////////////////////////////////////////24//                            path definitions25///////////////////////////////////////////////////////////////////////////////26 27_LIBCPP_DIAGNOSTIC_PUSH28_LIBCPP_CLANG_DIAGNOSTIC_IGNORED("-Wdeprecated")29constexpr path::value_type path::preferred_separator;30_LIBCPP_DIAGNOSTIC_POP31 32path& path::replace_extension(path const& replacement) {33  path p = extension();34  if (not p.empty()) {35    __pn_.erase(__pn_.size() - p.native().size());36  }37  if (!replacement.empty()) {38    if (replacement.native()[0] != '.') {39      __pn_ += PATHSTR(".");40    }41    __pn_.append(replacement.__pn_);42  }43  return *this;44}45 46///////////////////////////////////////////////////////////////////////////////47// path.decompose48 49string_view_t path::__root_name() const {50  auto PP = PathParser::CreateBegin(__pn_);51  if (PP.State_ == PathParser::PS_InRootName)52    return *PP;53  return {};54}55 56string_view_t path::__root_directory() const {57  auto PP = PathParser::CreateBegin(__pn_);58  if (PP.State_ == PathParser::PS_InRootName)59    ++PP;60  if (PP.State_ == PathParser::PS_InRootDir)61    return *PP;62  return {};63}64 65string_view_t path::__root_path_raw() const {66  auto PP = PathParser::CreateBegin(__pn_);67  if (PP.State_ == PathParser::PS_InRootName) {68    auto NextCh = PP.peek();69    if (NextCh && isSeparator(*NextCh)) {70      ++PP;71      return createView(__pn_.data(), &PP.RawEntry.back());72    }73    return PP.RawEntry;74  }75  if (PP.State_ == PathParser::PS_InRootDir)76    return *PP;77  return {};78}79 80static bool ConsumeRootName(PathParser* PP) {81  static_assert(PathParser::PS_BeforeBegin == 1 && PathParser::PS_InRootName == 2, "Values for enums are incorrect");82  while (PP->State_ <= PathParser::PS_InRootName)83    ++(*PP);84  return PP->State_ == PathParser::PS_AtEnd;85}86 87static bool ConsumeRootDir(PathParser* PP) {88  static_assert(PathParser::PS_BeforeBegin == 1 && PathParser::PS_InRootName == 2 && PathParser::PS_InRootDir == 3,89                "Values for enums are incorrect");90  while (PP->State_ <= PathParser::PS_InRootDir)91    ++(*PP);92  return PP->State_ == PathParser::PS_AtEnd;93}94 95string_view_t path::__relative_path() const {96  auto PP = PathParser::CreateBegin(__pn_);97  if (ConsumeRootDir(&PP))98    return {};99  return createView(PP.RawEntry.data(), &__pn_.back());100}101 102string_view_t path::__parent_path() const {103  if (empty())104    return {};105  // Determine if we have a root path but not a relative path. In that case106  // return *this.107  {108    auto PP = PathParser::CreateBegin(__pn_);109    if (ConsumeRootDir(&PP))110      return __pn_;111  }112  // Otherwise remove a single element from the end of the path, and return113  // a string representing that path114  {115    auto PP = PathParser::CreateEnd(__pn_);116    --PP;117    if (PP.RawEntry.data() == __pn_.data())118      return {};119    --PP;120    return createView(__pn_.data(), &PP.RawEntry.back());121  }122}123 124string_view_t path::__filename() const {125  if (empty())126    return {};127  {128    PathParser PP = PathParser::CreateBegin(__pn_);129    if (ConsumeRootDir(&PP))130      return {};131  }132  return *(--PathParser::CreateEnd(__pn_));133}134 135string_view_t path::__stem() const { return parser::separate_filename(__filename()).first; }136 137string_view_t path::__extension() const { return parser::separate_filename(__filename()).second; }138 139////////////////////////////////////////////////////////////////////////////140// path.gen141 142enum PathPartKind : unsigned char { PK_None, PK_RootSep, PK_Filename, PK_Dot, PK_DotDot, PK_TrailingSep };143 144static PathPartKind ClassifyPathPart(string_view_t Part) {145  if (Part.empty())146    return PK_TrailingSep;147  if (Part == PATHSTR("."))148    return PK_Dot;149  if (Part == PATHSTR(".."))150    return PK_DotDot;151  if (Part == PATHSTR("/"))152    return PK_RootSep;153#if defined(_LIBCPP_WIN32API)154  if (Part == PATHSTR("\\"))155    return PK_RootSep;156#endif157  return PK_Filename;158}159 160path path::lexically_normal() const {161  if (__pn_.empty())162    return *this;163 164  using PartKindPair = pair<string_view_t, PathPartKind>;165  vector<PartKindPair> Parts;166  // Guess as to how many elements the path has to avoid reallocating.167  Parts.reserve(32);168 169  // Track the total size of the parts as we collect them. This allows the170  // resulting path to reserve the correct amount of memory.171  size_t NewPathSize = 0;172  auto AddPart       = [&](PathPartKind K, string_view_t P) {173    NewPathSize += P.size();174    Parts.emplace_back(P, K);175  };176  auto LastPartKind = [&]() {177    if (Parts.empty())178      return PK_None;179    return Parts.back().second;180  };181 182  bool MaybeNeedTrailingSep = false;183  // Build a stack containing the remaining elements of the path, popping off184  // elements which occur before a '..' entry.185  for (auto PP = PathParser::CreateBegin(__pn_); PP; ++PP) {186    auto Part         = *PP;187    PathPartKind Kind = ClassifyPathPart(Part);188    switch (Kind) {189    case PK_Filename:190    case PK_RootSep: {191      // Add all non-dot and non-dot-dot elements to the stack of elements.192      AddPart(Kind, Part);193      MaybeNeedTrailingSep = false;194      break;195    }196    case PK_DotDot: {197      // Only push a ".." element if there are no elements preceding the "..",198      // or if the preceding element is itself "..".199      auto LastKind = LastPartKind();200      if (LastKind == PK_Filename) {201        NewPathSize -= Parts.back().first.size();202        Parts.pop_back();203      } else if (LastKind != PK_RootSep)204        AddPart(PK_DotDot, PATHSTR(".."));205      MaybeNeedTrailingSep = LastKind == PK_Filename;206      break;207    }208    case PK_Dot:209    case PK_TrailingSep: {210      MaybeNeedTrailingSep = true;211      break;212    }213    case PK_None:214      __libcpp_unreachable();215    }216  }217  // [fs.path.generic]p6.8: If the path is empty, add a dot.218  if (Parts.empty())219    return PATHSTR(".");220 221  // [fs.path.generic]p6.7: If the last filename is dot-dot, remove any222  // trailing directory-separator.223  bool NeedTrailingSep = MaybeNeedTrailingSep && LastPartKind() == PK_Filename;224 225  path Result;226  Result.__pn_.reserve(Parts.size() + NewPathSize + NeedTrailingSep);227  for (auto& PK : Parts)228    Result /= PK.first;229 230  if (NeedTrailingSep)231    Result /= PATHSTR("");232 233  Result.make_preferred();234  return Result;235}236 237static int DetermineLexicalElementCount(PathParser PP) {238  int Count = 0;239  for (; PP; ++PP) {240    auto Elem = *PP;241    if (Elem == PATHSTR(".."))242      --Count;243    else if (Elem != PATHSTR(".") && Elem != PATHSTR(""))244      ++Count;245  }246  return Count;247}248 249path path::lexically_relative(const path& base) const {250  { // perform root-name/root-directory mismatch checks251    auto PP                      = PathParser::CreateBegin(__pn_);252    auto PPBase                  = PathParser::CreateBegin(base.__pn_);253    auto CheckIterMismatchAtBase = [&]() {254      return PP.State_ != PPBase.State_ && (PP.inRootPath() || PPBase.inRootPath());255    };256    if (PP.inRootName() && PPBase.inRootName()) {257      if (*PP != *PPBase)258        return {};259    } else if (CheckIterMismatchAtBase())260      return {};261 262    if (PP.inRootPath())263      ++PP;264    if (PPBase.inRootPath())265      ++PPBase;266    if (CheckIterMismatchAtBase())267      return {};268  }269 270  // Find the first mismatching element271  auto PP     = PathParser::CreateBegin(__pn_);272  auto PPBase = PathParser::CreateBegin(base.__pn_);273  while (PP && PPBase && PP.State_ == PPBase.State_ && (*PP == *PPBase || PP.inRootDir())) {274    ++PP;275    ++PPBase;276  }277 278  // If there is no mismatch, return ".".279  if (!PP && !PPBase)280    return ".";281 282  // Otherwise, determine the number of elements, 'n', which are not dot or283  // dot-dot minus the number of dot-dot elements.284  int ElemCount = DetermineLexicalElementCount(PPBase);285  if (ElemCount < 0)286    return {};287 288  // if n == 0 and (a == end() || a->empty()), returns path("."); otherwise289  if (ElemCount == 0 && (PP.atEnd() || *PP == PATHSTR("")))290    return PATHSTR(".");291 292  // return a path constructed with 'n' dot-dot elements, followed by the293  // elements of '*this' after the mismatch.294  path Result;295  constexpr size_t ElemSize      = 2; // ".."296  constexpr size_t SeparatorSize = 1; // separator is always a single char297  Result.__reserve(ElemCount * (ElemSize + SeparatorSize) + SeparatorSize + PP.Path.size());298  while (ElemCount--)299    Result /= PATHSTR("..");300  for (; PP; ++PP)301    Result /= *PP;302  return Result;303}304 305////////////////////////////////////////////////////////////////////////////306// path.comparisons307static int CompareRootName(PathParser* LHS, PathParser* RHS) {308  if (!LHS->inRootName() && !RHS->inRootName())309    return 0;310 311  auto GetRootName = [](PathParser* Parser) -> string_view_t { return Parser->inRootName() ? **Parser : PATHSTR(""); };312  int res          = GetRootName(LHS).compare(GetRootName(RHS));313  ConsumeRootName(LHS);314  ConsumeRootName(RHS);315  return res;316}317 318static int CompareRootDir(PathParser* LHS, PathParser* RHS) {319  if (!LHS->inRootDir() && RHS->inRootDir())320    return -1;321  else if (LHS->inRootDir() && !RHS->inRootDir())322    return 1;323  else {324    ConsumeRootDir(LHS);325    ConsumeRootDir(RHS);326    return 0;327  }328}329 330static int CompareRelative(PathParser* LHSPtr, PathParser* RHSPtr) {331  auto& LHS = *LHSPtr;332  auto& RHS = *RHSPtr;333 334  int res;335  while (LHS && RHS) {336    if ((res = (*LHS).compare(*RHS)) != 0)337      return res;338    ++LHS;339    ++RHS;340  }341  return 0;342}343 344static int CompareEndState(PathParser* LHS, PathParser* RHS) {345  if (LHS->atEnd() && !RHS->atEnd())346    return -1;347  else if (!LHS->atEnd() && RHS->atEnd())348    return 1;349  return 0;350}351 352int path::__compare(string_view_t __s) const {353  auto LHS = PathParser::CreateBegin(__pn_);354  auto RHS = PathParser::CreateBegin(__s);355  int res;356 357  if ((res = CompareRootName(&LHS, &RHS)) != 0)358    return res;359 360  if ((res = CompareRootDir(&LHS, &RHS)) != 0)361    return res;362 363  if ((res = CompareRelative(&LHS, &RHS)) != 0)364    return res;365 366  return CompareEndState(&LHS, &RHS);367}368 369////////////////////////////////////////////////////////////////////////////370// path.nonmembers371size_t hash_value(const path& __p) noexcept {372  auto PP           = PathParser::CreateBegin(__p.native());373  size_t hash_value = 0;374  hash<string_view_t> hasher;375  while (PP) {376    string_view_t Part = PP.inRootDir() ? PATHSTR("/") : *PP;377    hash_value         = __hash_combine(hash_value, hasher(Part));378    ++PP;379  }380  return hash_value;381}382 383////////////////////////////////////////////////////////////////////////////384// path.itr385path::iterator path::begin() const {386  auto PP = PathParser::CreateBegin(__pn_);387  iterator it;388  it.__path_ptr_ = this;389  it.__state_    = static_cast<path::iterator::_ParserState>(PP.State_);390  it.__entry_    = PP.RawEntry;391  it.__stashed_elem_.__assign_view(*PP);392  return it;393}394 395path::iterator path::end() const {396  iterator it{};397  it.__state_    = path::iterator::_AtEnd;398  it.__path_ptr_ = this;399  return it;400}401 402path::iterator& path::iterator::__increment() {403  PathParser PP(__path_ptr_->native(), __entry_, __state_);404  ++PP;405  __state_ = static_cast<_ParserState>(PP.State_);406  __entry_ = PP.RawEntry;407  __stashed_elem_.__assign_view(*PP);408  return *this;409}410 411path::iterator& path::iterator::__decrement() {412  PathParser PP(__path_ptr_->native(), __entry_, __state_);413  --PP;414  __state_ = static_cast<_ParserState>(PP.State_);415  __entry_ = PP.RawEntry;416  __stashed_elem_.__assign_view(*PP);417  return *this;418}419 420#if defined(_LIBCPP_WIN32API)421////////////////////////////////////////////////////////////////////////////422// Windows path conversions423size_t __wide_to_char(const wstring& str, char* out, size_t outlen) {424  if (str.empty())425    return 0;426  ErrorHandler<size_t> err("__wide_to_char", nullptr);427  UINT codepage     = AreFileApisANSI() ? CP_ACP : CP_OEMCP;428  BOOL used_default = FALSE;429  int ret           = WideCharToMultiByte(codepage, 0, str.data(), str.size(), out, outlen, nullptr, &used_default);430  if (ret <= 0 || used_default)431    return err.report(errc::illegal_byte_sequence);432  return ret;433}434 435size_t __char_to_wide(const string& str, wchar_t* out, size_t outlen) {436  if (str.empty())437    return 0;438  ErrorHandler<size_t> err("__char_to_wide", nullptr);439  UINT codepage = AreFileApisANSI() ? CP_ACP : CP_OEMCP;440  int ret       = MultiByteToWideChar(codepage, MB_ERR_INVALID_CHARS, str.data(), str.size(), out, outlen);441  if (ret <= 0)442    return err.report(errc::illegal_byte_sequence);443  return ret;444}445#endif446 447_LIBCPP_END_NAMESPACE_FILESYSTEM448