| /* Distributed under the OSI-approved BSD 3-Clause License. See accompanying |
| file Copyright.txt or https://cmake.org/licensing for details. */ |
| |
| #include "cmConfigure.h" // IWYU pragma: keep |
| |
| #include "cmList.h" |
| |
| #include <algorithm> |
| #include <cstddef> |
| #include <functional> |
| #include <iterator> |
| #include <set> |
| #include <stdexcept> |
| #include <utility> |
| |
| #include <cm/memory> |
| |
| #include "cmsys/RegularExpression.hxx" |
| |
| #include "cmAlgorithms.h" |
| #include "cmGeneratorExpression.h" |
| #include "cmListFileCache.h" |
| #include "cmRange.h" |
| #include "cmStringAlgorithms.h" |
| #include "cmStringReplaceHelper.h" |
| #include "cmSystemTools.h" |
| |
| cm::string_view cmList::element_separator{ ";" }; |
| |
| cmList cmList::sublist(size_type pos, size_type length) const |
| { |
| if (pos >= this->Values.size()) { |
| throw std::out_of_range(cmStrCat( |
| "begin index: ", pos, " is out of range 0 - ", this->Values.size() - 1)); |
| } |
| |
| size_type count = (length == npos || pos + length > this->size()) |
| ? this->size() |
| : pos + length; |
| return this->sublist(this->begin() + pos, this->begin() + count); |
| } |
| |
| cmList::size_type cmList::find(cm::string_view value) const |
| { |
| auto res = std::find(this->Values.begin(), this->Values.end(), value); |
| if (res == this->Values.end()) { |
| return npos; |
| } |
| |
| return std::distance(this->Values.begin(), res); |
| } |
| |
| cmList& cmList::remove_duplicates() |
| { |
| auto newEnd = cmRemoveDuplicates(this->Values); |
| this->Values.erase(newEnd, this->Values.end()); |
| |
| return *this; |
| } |
| |
| namespace { |
| class MatchesRegex |
| { |
| public: |
| MatchesRegex(cmsys::RegularExpression& regex, cmList::FilterMode mode) |
| : Regex(regex) |
| , IncludeMatches(mode == cmList::FilterMode::INCLUDE) |
| { |
| } |
| |
| bool operator()(const std::string& target) |
| { |
| return this->Regex.find(target) ^ this->IncludeMatches; |
| } |
| |
| private: |
| cmsys::RegularExpression& Regex; |
| const bool IncludeMatches; |
| }; |
| } |
| |
| cmList& cmList::filter(cm::string_view pattern, FilterMode mode) |
| { |
| cmsys::RegularExpression regex(std::string{ pattern }); |
| if (!regex.is_valid()) { |
| throw std::invalid_argument( |
| cmStrCat("sub-command FILTER, mode REGEX failed to compile regex \"", |
| pattern, "\".")); |
| } |
| |
| auto it = std::remove_if(this->Values.begin(), this->Values.end(), |
| MatchesRegex{ regex, mode }); |
| this->Values.erase(it, this->Values.end()); |
| |
| return *this; |
| } |
| |
| namespace { |
| class StringSorter |
| { |
| protected: |
| using StringFilter = std::function<std::string(const std::string&)>; |
| |
| using OrderMode = cmList::SortConfiguration::OrderMode; |
| using CompareMethod = cmList::SortConfiguration::CompareMethod; |
| using CaseSensitivity = cmList::SortConfiguration::CaseSensitivity; |
| |
| StringFilter GetCompareFilter(CompareMethod compare) |
| { |
| return (compare == CompareMethod::FILE_BASENAME) |
| ? cmSystemTools::GetFilenameName |
| : nullptr; |
| } |
| |
| StringFilter GetCaseFilter(CaseSensitivity sensitivity) |
| { |
| return (sensitivity == CaseSensitivity::INSENSITIVE) |
| ? cmSystemTools::LowerCase |
| : nullptr; |
| } |
| |
| using ComparisonFunction = |
| std::function<bool(const std::string&, const std::string&)>; |
| ComparisonFunction GetComparisonFunction(CompareMethod compare) |
| { |
| if (compare == CompareMethod::NATURAL) { |
| return std::function<bool(const std::string&, const std::string&)>( |
| [](const std::string& x, const std::string& y) { |
| return cmSystemTools::strverscmp(x, y) < 0; |
| }); |
| } |
| return std::function<bool(const std::string&, const std::string&)>( |
| [](const std::string& x, const std::string& y) { return x < y; }); |
| } |
| |
| public: |
| StringSorter(cmList::SortConfiguration const& config) |
| : Filters{ this->GetCompareFilter(config.Compare), |
| this->GetCaseFilter(config.Case) } |
| , SortMethod(this->GetComparisonFunction(config.Compare)) |
| , Descending(config.Order == OrderMode::DESCENDING) |
| { |
| } |
| |
| std::string ApplyFilter(const std::string& argument) |
| { |
| std::string result = argument; |
| for (auto const& filter : this->Filters) { |
| if (filter) { |
| result = filter(result); |
| } |
| } |
| return result; |
| } |
| |
| bool operator()(const std::string& a, const std::string& b) |
| { |
| std::string af = this->ApplyFilter(a); |
| std::string bf = this->ApplyFilter(b); |
| bool result; |
| if (this->Descending) { |
| result = this->SortMethod(bf, af); |
| } else { |
| result = this->SortMethod(af, bf); |
| } |
| return result; |
| } |
| |
| private: |
| StringFilter Filters[2] = { nullptr, nullptr }; |
| ComparisonFunction SortMethod; |
| bool Descending; |
| }; |
| } |
| |
| cmList::SortConfiguration::SortConfiguration() = default; |
| |
| cmList& cmList::sort(const SortConfiguration& cfg) |
| { |
| SortConfiguration config{ cfg }; |
| |
| if (config.Order == SortConfiguration::OrderMode::DEFAULT) { |
| config.Order = SortConfiguration::OrderMode::ASCENDING; |
| } |
| if (config.Compare == SortConfiguration::CompareMethod::DEFAULT) { |
| config.Compare = SortConfiguration::CompareMethod::STRING; |
| } |
| if (config.Case == SortConfiguration::CaseSensitivity::DEFAULT) { |
| config.Case = SortConfiguration::CaseSensitivity::SENSITIVE; |
| } |
| |
| if ((config.Compare == SortConfiguration::CompareMethod::STRING) && |
| (config.Case == SortConfiguration::CaseSensitivity::SENSITIVE) && |
| (config.Order == SortConfiguration::OrderMode::ASCENDING)) { |
| std::sort(this->Values.begin(), this->Values.end()); |
| } else { |
| StringSorter sorter(config); |
| std::sort(this->Values.begin(), this->Values.end(), sorter); |
| } |
| |
| return *this; |
| } |
| |
| namespace { |
| using transform_type = std::function<std::string(const std::string&)>; |
| using transform_error = cmList::transform_error; |
| |
| class TransformSelector : public cmList::TransformSelector |
| { |
| public: |
| ~TransformSelector() override = default; |
| |
| std::string Tag; |
| |
| const std::string& GetTag() override { return this->Tag; } |
| |
| virtual bool Validate(std::size_t count = 0) = 0; |
| |
| virtual bool InSelection(const std::string&) = 0; |
| |
| virtual void Transform(cmList::container_type& list, |
| const transform_type& transform) |
| { |
| std::transform(list.begin(), list.end(), list.begin(), transform); |
| } |
| |
| protected: |
| TransformSelector(std::string&& tag) |
| : Tag(std::move(tag)) |
| { |
| } |
| }; |
| |
| class TransformNoSelector : public TransformSelector |
| { |
| public: |
| TransformNoSelector() |
| : TransformSelector("NO SELECTOR") |
| { |
| } |
| |
| bool Validate(std::size_t) override { return true; } |
| |
| bool InSelection(const std::string&) override { return true; } |
| }; |
| class TransformSelectorRegex : public TransformSelector |
| { |
| public: |
| TransformSelectorRegex(const std::string& regex) |
| : TransformSelector("REGEX") |
| , Regex(regex) |
| { |
| } |
| TransformSelectorRegex(std::string&& regex) |
| : TransformSelector("REGEX") |
| , Regex(regex) |
| { |
| } |
| |
| bool Validate(std::size_t) override { return this->Regex.is_valid(); } |
| |
| bool InSelection(const std::string& value) override |
| { |
| return this->Regex.find(value); |
| } |
| |
| cmsys::RegularExpression Regex; |
| }; |
| class TransformSelectorIndexes : public TransformSelector |
| { |
| public: |
| std::vector<index_type> Indexes; |
| |
| bool InSelection(const std::string&) override { return true; } |
| |
| void Transform(std::vector<std::string>& list, |
| const transform_type& transform) override |
| { |
| this->Validate(list.size()); |
| |
| for (auto index : this->Indexes) { |
| list[index] = transform(list[index]); |
| } |
| } |
| |
| protected: |
| TransformSelectorIndexes(std::string&& tag) |
| : TransformSelector(std::move(tag)) |
| { |
| } |
| TransformSelectorIndexes(std::string&& tag, |
| std::vector<index_type> const& indexes) |
| : TransformSelector(std::move(tag)) |
| , Indexes(indexes) |
| { |
| } |
| TransformSelectorIndexes(std::string&& tag, |
| std::vector<index_type>&& indexes) |
| : TransformSelector(std::move(tag)) |
| , Indexes(indexes) |
| { |
| } |
| |
| index_type NormalizeIndex(index_type index, std::size_t count) |
| { |
| if (index < 0) { |
| index = static_cast<index_type>(count) + index; |
| } |
| if (index < 0 || count <= static_cast<std::size_t>(index)) { |
| throw transform_error(cmStrCat( |
| "sub-command TRANSFORM, selector ", this->Tag, ", index: ", index, |
| " out of range (-", count, ", ", count - 1, ").")); |
| } |
| return index; |
| } |
| }; |
| class TransformSelectorAt : public TransformSelectorIndexes |
| { |
| public: |
| TransformSelectorAt(std::vector<index_type> const& indexes) |
| : TransformSelectorIndexes("AT", indexes) |
| { |
| } |
| TransformSelectorAt(std::vector<index_type>&& indexes) |
| : TransformSelectorIndexes("AT", std::move(indexes)) |
| { |
| } |
| |
| bool Validate(std::size_t count) override |
| { |
| decltype(this->Indexes) indexes; |
| |
| for (auto index : this->Indexes) { |
| indexes.push_back(this->NormalizeIndex(index, count)); |
| } |
| this->Indexes = std::move(indexes); |
| |
| return true; |
| } |
| }; |
| class TransformSelectorFor : public TransformSelectorIndexes |
| { |
| public: |
| TransformSelectorFor(index_type start, index_type stop, index_type step) |
| : TransformSelectorIndexes("FOR") |
| , Start(start) |
| , Stop(stop) |
| , Step(step) |
| { |
| } |
| |
| bool Validate(std::size_t count) override |
| { |
| this->Start = this->NormalizeIndex(this->Start, count); |
| this->Stop = this->NormalizeIndex(this->Stop, count); |
| |
| // Does stepping move us further from the end? |
| if (this->Start > this->Stop) { |
| throw transform_error( |
| cmStrCat("sub-command TRANSFORM, selector FOR " |
| "expects <start> to be no greater than <stop> (", |
| this->Start, " > ", this->Stop, ")")); |
| } |
| |
| // compute indexes |
| auto size = (this->Stop - this->Start + 1) / this->Step; |
| if ((this->Stop - this->Start + 1) % this->Step != 0) { |
| size += 1; |
| } |
| |
| this->Indexes.resize(size); |
| auto start = this->Start; |
| auto step = this->Step; |
| std::generate(this->Indexes.begin(), this->Indexes.end(), |
| [&start, step]() -> index_type { |
| auto r = start; |
| start += step; |
| return r; |
| }); |
| |
| return true; |
| } |
| |
| private: |
| index_type Start, Stop, Step; |
| }; |
| |
| class TransformAction |
| { |
| public: |
| virtual ~TransformAction() = default; |
| |
| void Initialize(TransformSelector* selector) { this->Selector = selector; } |
| virtual void Initialize(TransformSelector*, const std::string&) {} |
| virtual void Initialize(TransformSelector*, const std::string&, |
| const std::string&) |
| { |
| } |
| virtual void Initialize(TransformSelector* selector, |
| const std::vector<std::string>&) |
| { |
| this->Initialize(selector); |
| } |
| |
| virtual std::string operator()(const std::string& s) = 0; |
| |
| protected: |
| TransformSelector* Selector; |
| }; |
| class TransformActionAppend : public TransformAction |
| { |
| public: |
| using TransformAction::Initialize; |
| |
| void Initialize(TransformSelector* selector, |
| const std::string& append) override |
| { |
| TransformAction::Initialize(selector); |
| this->Append = append; |
| } |
| void Initialize(TransformSelector* selector, |
| const std::vector<std::string>& append) override |
| { |
| this->Initialize(selector, append.front()); |
| } |
| |
| std::string operator()(const std::string& s) override |
| { |
| if (this->Selector->InSelection(s)) { |
| return cmStrCat(s, this->Append); |
| } |
| |
| return s; |
| } |
| |
| private: |
| std::string Append; |
| }; |
| class TransformActionPrepend : public TransformAction |
| { |
| public: |
| using TransformAction::Initialize; |
| |
| void Initialize(TransformSelector* selector, |
| const std::string& prepend) override |
| { |
| TransformAction::Initialize(selector); |
| this->Prepend = prepend; |
| } |
| void Initialize(TransformSelector* selector, |
| const std::vector<std::string>& prepend) override |
| { |
| this->Initialize(selector, prepend.front()); |
| } |
| |
| std::string operator()(const std::string& s) override |
| { |
| if (this->Selector->InSelection(s)) { |
| return cmStrCat(this->Prepend, s); |
| } |
| |
| return s; |
| } |
| |
| private: |
| std::string Prepend; |
| }; |
| class TransformActionToUpper : public TransformAction |
| { |
| public: |
| std::string operator()(const std::string& s) override |
| { |
| if (this->Selector->InSelection(s)) { |
| return cmSystemTools::UpperCase(s); |
| } |
| |
| return s; |
| } |
| }; |
| class TransformActionToLower : public TransformAction |
| { |
| public: |
| std::string operator()(const std::string& s) override |
| { |
| if (this->Selector->InSelection(s)) { |
| return cmSystemTools::LowerCase(s); |
| } |
| |
| return s; |
| } |
| }; |
| class TransformActionStrip : public TransformAction |
| { |
| public: |
| std::string operator()(const std::string& s) override |
| { |
| if (this->Selector->InSelection(s)) { |
| return cmTrimWhitespace(s); |
| } |
| |
| return s; |
| } |
| }; |
| class TransformActionGenexStrip : public TransformAction |
| { |
| public: |
| std::string operator()(const std::string& s) override |
| { |
| if (this->Selector->InSelection(s)) { |
| return cmGeneratorExpression::Preprocess( |
| s, cmGeneratorExpression::StripAllGeneratorExpressions); |
| } |
| |
| return s; |
| } |
| }; |
| class TransformActionReplace : public TransformAction |
| { |
| public: |
| using TransformAction::Initialize; |
| |
| void Initialize(TransformSelector* selector, const std::string& regex, |
| const std::string& replace) override |
| { |
| TransformAction::Initialize(selector); |
| this->ReplaceHelper = |
| cm::make_unique<cmStringReplaceHelper>(regex, replace); |
| |
| if (!this->ReplaceHelper->IsRegularExpressionValid()) { |
| throw transform_error( |
| cmStrCat("sub-command TRANSFORM, action REPLACE: Failed to compile " |
| "regex \"", |
| regex, "\".")); |
| } |
| if (!this->ReplaceHelper->IsReplaceExpressionValid()) { |
| throw transform_error(cmStrCat("sub-command TRANSFORM, action REPLACE: ", |
| this->ReplaceHelper->GetError(), ".")); |
| } |
| } |
| void Initialize(TransformSelector* selector, |
| const std::vector<std::string>& args) override |
| { |
| this->Initialize(selector, args[0], args[1]); |
| } |
| |
| std::string operator()(const std::string& s) override |
| { |
| if (this->Selector->InSelection(s)) { |
| // Scan through the input for all matches. |
| std::string output; |
| |
| if (!this->ReplaceHelper->Replace(s, output)) { |
| throw transform_error( |
| cmStrCat("sub-command TRANSFORM, action REPLACE: ", |
| this->ReplaceHelper->GetError(), ".")); |
| } |
| |
| return output; |
| } |
| |
| return s; |
| } |
| |
| private: |
| std::unique_ptr<cmStringReplaceHelper> ReplaceHelper; |
| }; |
| |
| // Descriptor of action |
| // Arity: number of arguments required for the action |
| // Transform: Object implementing the action |
| struct ActionDescriptor |
| { |
| ActionDescriptor(cmList::TransformAction action) |
| : Action(action) |
| { |
| } |
| ActionDescriptor(cmList::TransformAction action, std::string name, |
| std::size_t arity, |
| std::unique_ptr<TransformAction> transform) |
| : Action(action) |
| , Name(std::move(name)) |
| , Arity(arity) |
| , Transform(std::move(transform)) |
| { |
| } |
| |
| operator cmList::TransformAction() const { return this->Action; } |
| |
| cmList::TransformAction Action; |
| std::string Name; |
| std::size_t Arity = 0; |
| std::unique_ptr<TransformAction> Transform; |
| }; |
| |
| // Build a set of supported actions. |
| using ActionDescriptorSet = std::set< |
| ActionDescriptor, |
| std::function<bool(cmList::TransformAction, cmList::TransformAction)>>; |
| |
| ActionDescriptorSet Descriptors([](cmList::TransformAction x, |
| cmList::TransformAction y) { |
| return x < y; |
| }); |
| |
| ActionDescriptorSet::iterator TransformConfigure( |
| cmList::TransformAction action, |
| std::unique_ptr<cmList::TransformSelector>& selector, std::size_t arity) |
| { |
| if (Descriptors.empty()) { |
| Descriptors.emplace(cmList::TransformAction::APPEND, "APPEND", 1, |
| cm::make_unique<TransformActionAppend>()); |
| Descriptors.emplace(cmList::TransformAction::PREPEND, "PREPEND", 1, |
| cm::make_unique<TransformActionPrepend>()); |
| Descriptors.emplace(cmList::TransformAction::TOUPPER, "TOUPPER", 0, |
| cm::make_unique<TransformActionToUpper>()); |
| Descriptors.emplace(cmList::TransformAction::TOLOWER, "TOLOWER", 0, |
| cm::make_unique<TransformActionToLower>()); |
| Descriptors.emplace(cmList::TransformAction::STRIP, "STRIP", 0, |
| cm::make_unique<TransformActionStrip>()); |
| Descriptors.emplace(cmList::TransformAction::GENEX_STRIP, "GENEX_STRIP", 0, |
| cm::make_unique<TransformActionGenexStrip>()); |
| Descriptors.emplace(cmList::TransformAction::REPLACE, "REPLACE", 2, |
| cm::make_unique<TransformActionReplace>()); |
| } |
| |
| auto descriptor = Descriptors.find(action); |
| if (descriptor == Descriptors.end()) { |
| throw transform_error(cmStrCat(" sub-command TRANSFORM, ", |
| std::to_string(static_cast<int>(action)), |
| " invalid action.")); |
| } |
| |
| if (descriptor->Arity != arity) { |
| throw transform_error(cmStrCat("sub-command TRANSFORM, action ", |
| descriptor->Name, " expects ", |
| descriptor->Arity, " argument(s).")); |
| } |
| if (!selector) { |
| selector = cm::make_unique<TransformNoSelector>(); |
| } |
| |
| return descriptor; |
| } |
| } |
| |
| std::unique_ptr<cmList::TransformSelector> cmList::TransformSelector::NewAT( |
| std::initializer_list<index_type> indexes) |
| { |
| return cm::make_unique<TransformSelectorAt>( |
| std::vector<index_type>{ indexes.begin(), indexes.end() }); |
| ; |
| } |
| std::unique_ptr<cmList::TransformSelector> cmList::TransformSelector::NewAT( |
| std::vector<index_type> const& indexes) |
| { |
| return cm::make_unique<TransformSelectorAt>(indexes); |
| } |
| std::unique_ptr<cmList::TransformSelector> cmList::TransformSelector::NewAT( |
| std::vector<index_type>&& indexes) |
| { |
| return cm::make_unique<TransformSelectorAt>(std::move(indexes)); |
| } |
| |
| std::unique_ptr<cmList::TransformSelector> cmList::TransformSelector::NewFOR( |
| std::initializer_list<index_type> indexes) |
| { |
| if (indexes.size() < 2 || indexes.size() > 3) { |
| throw transform_error("sub-command TRANSFORM, selector FOR " |
| "expects 2 or 3 arguments"); |
| } |
| if (indexes.size() == 3 && *(indexes.begin() + 2) < 0) { |
| throw transform_error("sub-command TRANSFORM, selector FOR expects " |
| "positive numeric value for <step>."); |
| } |
| |
| return cm::make_unique<TransformSelectorFor>( |
| *indexes.begin(), *(indexes.begin() + 1), |
| indexes.size() == 3 ? *(indexes.begin() + 2) : 1); |
| } |
| std::unique_ptr<cmList::TransformSelector> cmList::TransformSelector::NewFOR( |
| std::vector<index_type> const& indexes) |
| { |
| if (indexes.size() < 2 || indexes.size() > 3) { |
| throw transform_error("sub-command TRANSFORM, selector FOR " |
| "expects 2 or 3 arguments"); |
| } |
| if (indexes.size() == 3 && indexes[2] < 0) { |
| throw transform_error("sub-command TRANSFORM, selector FOR expects " |
| "positive numeric value for <step>."); |
| } |
| |
| return cm::make_unique<TransformSelectorFor>( |
| indexes[0], indexes[1], indexes.size() == 3 ? indexes[2] : 1); |
| } |
| std::unique_ptr<cmList::TransformSelector> cmList::TransformSelector::NewFOR( |
| std::vector<index_type>&& indexes) |
| { |
| if (indexes.size() < 2 || indexes.size() > 3) { |
| throw transform_error("sub-command TRANSFORM, selector FOR " |
| "expects 2 or 3 arguments"); |
| } |
| if (indexes.size() == 3 && indexes[2] < 0) { |
| throw transform_error("sub-command TRANSFORM, selector FOR expects " |
| "positive numeric value for <step>."); |
| } |
| |
| return cm::make_unique<TransformSelectorFor>( |
| indexes[0], indexes[1], indexes.size() == 3 ? indexes[2] : 1); |
| } |
| |
| std::unique_ptr<cmList::TransformSelector> cmList::TransformSelector::NewREGEX( |
| std::string const& regex) |
| { |
| std::unique_ptr<::TransformSelector> selector = |
| cm::make_unique<TransformSelectorRegex>(regex); |
| if (!selector->Validate()) { |
| throw transform_error( |
| cmStrCat("sub-command TRANSFORM, selector REGEX failed to compile " |
| "regex \"", |
| regex, "\".")); |
| } |
| // weird construct to please all compilers |
| return std::unique_ptr<cmList::TransformSelector>(selector.release()); |
| } |
| std::unique_ptr<cmList::TransformSelector> cmList::TransformSelector::NewREGEX( |
| std::string&& regex) |
| { |
| std::unique_ptr<::TransformSelector> selector = |
| cm::make_unique<TransformSelectorRegex>(std::move(regex)); |
| if (!selector->Validate()) { |
| throw transform_error( |
| cmStrCat("sub-command TRANSFORM, selector REGEX failed to compile " |
| "regex \"", |
| regex, "\".")); |
| } |
| // weird construct to please all compilers |
| return std::unique_ptr<cmList::TransformSelector>(selector.release()); |
| } |
| |
| cmList& cmList::transform(TransformAction action, |
| std::unique_ptr<TransformSelector> selector) |
| { |
| auto descriptor = TransformConfigure(action, selector, 0); |
| |
| descriptor->Transform->Initialize( |
| static_cast<::TransformSelector*>(selector.get())); |
| |
| static_cast<::TransformSelector&>(*selector).Transform( |
| this->Values, [&descriptor](const std::string& s) -> std::string { |
| return (*descriptor->Transform)(s); |
| }); |
| |
| return *this; |
| } |
| |
| cmList& cmList::transform(TransformAction action, std::string const& arg, |
| std::unique_ptr<TransformSelector> selector) |
| { |
| auto descriptor = TransformConfigure(action, selector, 1); |
| |
| descriptor->Transform->Initialize( |
| static_cast<::TransformSelector*>(selector.get()), arg); |
| |
| static_cast<::TransformSelector&>(*selector).Transform( |
| this->Values, [&descriptor](const std::string& s) -> std::string { |
| return (*descriptor->Transform)(s); |
| }); |
| |
| return *this; |
| } |
| |
| cmList& cmList::transform(TransformAction action, std::string const& arg1, |
| std::string const& arg2, |
| std::unique_ptr<TransformSelector> selector) |
| { |
| auto descriptor = TransformConfigure(action, selector, 2); |
| |
| descriptor->Transform->Initialize( |
| static_cast<::TransformSelector*>(selector.get()), arg1, arg2); |
| |
| static_cast<::TransformSelector&>(*selector).Transform( |
| this->Values, [&descriptor](const std::string& s) -> std::string { |
| return (*descriptor->Transform)(s); |
| }); |
| |
| return *this; |
| } |
| |
| cmList& cmList::transform(TransformAction action, |
| std::vector<std::string> const& args, |
| std::unique_ptr<TransformSelector> selector) |
| { |
| auto descriptor = TransformConfigure(action, selector, args.size()); |
| |
| descriptor->Transform->Initialize( |
| static_cast<::TransformSelector*>(selector.get()), args); |
| |
| static_cast<::TransformSelector&>(*selector).Transform( |
| this->Values, [&descriptor](const std::string& s) -> std::string { |
| return (*descriptor->Transform)(s); |
| }); |
| |
| return *this; |
| } |
| |
| std::string& cmList::append(std::string& list, std::string&& value) |
| { |
| if (list.empty()) { |
| list = std::move(value); |
| } else { |
| list += cmStrCat(cmList::element_separator, value); |
| } |
| |
| return list; |
| } |
| std::string& cmList::append(std::string& list, cm::string_view value) |
| { |
| return cmList::append(list, std::string{ value }); |
| } |
| |
| std::string& cmList::prepend(std::string& list, std::string&& value) |
| { |
| if (list.empty()) { |
| list = std::move(value); |
| } else { |
| list.insert(0, cmStrCat(value, cmList::element_separator)); |
| } |
| |
| return list; |
| } |
| std::string& cmList::prepend(std::string& list, cm::string_view value) |
| { |
| return cmList::prepend(list, std::string{ value }); |
| } |
| |
| cmList::size_type cmList::ComputeIndex(index_type pos, bool boundCheck) const |
| { |
| if (boundCheck) { |
| if (this->Values.empty()) { |
| throw std::out_of_range( |
| cmStrCat("index: ", pos, " out of range (0, 0)")); |
| } |
| |
| auto index = pos; |
| if (!this->Values.empty()) { |
| auto length = this->Values.size(); |
| if (index < 0) { |
| index = static_cast<index_type>(length) + index; |
| } |
| if (index < 0 || length <= static_cast<size_type>(index)) { |
| throw std::out_of_range(cmStrCat("index: ", pos, " out of range (-", |
| this->Values.size(), ", ", |
| this->Values.size() - 1, ")")); |
| } |
| } |
| return index; |
| } |
| |
| return pos < 0 ? this->Values.size() + pos : pos; |
| } |
| cmList::size_type cmList::ComputeInsertIndex(index_type pos, |
| bool boundCheck) const |
| { |
| if (boundCheck) { |
| if (this->Values.empty() && pos != 0) { |
| throw std::out_of_range( |
| cmStrCat("index: ", pos, " out of range (0, 0)")); |
| } |
| |
| auto index = pos; |
| if (!this->Values.empty()) { |
| auto length = this->Values.size(); |
| if (index < 0) { |
| index = static_cast<index_type>(length) + index; |
| } |
| if (index < 0 || length < static_cast<size_type>(index)) { |
| throw std::out_of_range(cmStrCat("index: ", pos, " out of range (-", |
| this->Values.size(), ", ", |
| this->Values.size(), ")")); |
| } |
| } |
| return index; |
| } |
| |
| return pos < 0 ? this->Values.size() + pos : pos; |
| } |
| |
| cmList cmList::GetItems(std::vector<index_type>&& indexes) const |
| { |
| cmList listItems; |
| |
| for (auto index : indexes) { |
| listItems.emplace_back(this->get_item(index)); |
| } |
| |
| return listItems; |
| } |
| |
| cmList& cmList::RemoveItems(std::vector<index_type>&& indexes) |
| { |
| if (indexes.empty()) { |
| return *this; |
| } |
| |
| // compute all indexes |
| std::vector<size_type> idx(indexes.size()); |
| std::transform(indexes.cbegin(), indexes.cend(), idx.begin(), |
| [this](const index_type& index) -> size_type { |
| return this->ComputeIndex(index); |
| }); |
| |
| std::sort(idx.begin(), idx.end(), |
| [](size_type l, size_type r) { return l > r; }); |
| auto newEnd = std::unique(idx.begin(), idx.end()); |
| idx.erase(newEnd, idx.end()); |
| |
| for (auto index : idx) { |
| this->erase(this->begin() + index); |
| } |
| |
| return *this; |
| } |
| |
| cmList& cmList::RemoveItems(std::vector<std::string>&& items) |
| { |
| std::sort(items.begin(), items.end()); |
| auto last = std::unique(items.begin(), items.end()); |
| auto first = items.begin(); |
| |
| auto newEnd = cmRemoveMatching(this->Values, cmMakeRange(first, last)); |
| this->Values.erase(newEnd, this->Values.end()); |
| |
| return *this; |
| } |
| |
| cmList::container_type::iterator cmList::Insert( |
| container_type& container, container_type::const_iterator pos, |
| std::string&& value, ExpandElements expandElements, |
| EmptyElements emptyElements) |
| { |
| auto delta = std::distance(container.cbegin(), pos); |
| auto insertPos = container.begin() + delta; |
| |
| if (expandElements == ExpandElements::Yes) { |
| // If argument is empty, it is an empty list. |
| if (emptyElements == EmptyElements::No && value.empty()) { |
| return insertPos; |
| } |
| |
| // if there are no ; in the name then just copy the current string |
| if (value.find(';') == std::string::npos) { |
| return container.insert(insertPos, std::move(value)); |
| } |
| |
| std::string newValue; |
| // Break the string at non-escaped semicolons not nested in []. |
| int squareNesting = 0; |
| auto last = value.begin(); |
| auto const cend = value.end(); |
| for (auto c = last; c != cend; ++c) { |
| switch (*c) { |
| case '\\': { |
| // We only want to allow escaping of semicolons. Other |
| // escapes should not be processed here. |
| auto cnext = c + 1; |
| if ((cnext != cend) && *cnext == ';') { |
| newValue.append(last, c); |
| // Skip over the escape character |
| last = cnext; |
| c = cnext; |
| } |
| } break; |
| case '[': { |
| ++squareNesting; |
| } break; |
| case ']': { |
| --squareNesting; |
| } break; |
| case ';': { |
| // brackets. |
| if (squareNesting == 0) { |
| newValue.append(last, c); |
| // Skip over the semicolon |
| last = c + 1; |
| if (!newValue.empty() || emptyElements == EmptyElements::Yes) { |
| // Add the last argument. |
| insertPos = container.insert(insertPos, newValue); |
| insertPos++; |
| newValue.clear(); |
| } |
| } |
| } break; |
| default: { |
| // Just append this character. |
| } break; |
| } |
| } |
| newValue.append(last, cend); |
| if (!newValue.empty() || emptyElements == EmptyElements::Yes) { |
| // Add the last argument. |
| container.insert(insertPos, std::move(newValue)); |
| } |
| } else if (!value.empty() || emptyElements == EmptyElements::Yes) { |
| return container.insert(insertPos, std::move(value)); |
| } |
| return container.begin() + delta; |
| } |
| |
| std::string const& cmList::ToString(BT<std::string> const& s) |
| { |
| return s.Value; |
| } |