summaryrefslogtreecommitdiff
path: root/apt-pkg
diff options
context:
space:
mode:
authorJulian Andres Klode <jak@debian.org>2019-08-19 14:15:49 +0000
committerJulian Andres Klode <jak@debian.org>2019-08-19 14:15:49 +0000
commitde951e5619f55c8281389a5c5986792f5453e602 (patch)
treefe9664d72c563e00e63e4acd83e6c1b21f8fa12c /apt-pkg
parent7c724251fd8c24e89dc8cb813eee20aa0a4ad793 (diff)
parentd18b6095862e8268b4d2cd8c0b3140829a1e4950 (diff)
Merge branch 'pu/patterns' into 'master'
Package patterns See merge request apt-team/apt!74
Diffstat (limited to 'apt-pkg')
-rw-r--r--apt-pkg/cachefilter-patterns.cc306
-rw-r--r--apt-pkg/cachefilter-patterns.h235
-rw-r--r--apt-pkg/cachefilter.h4
-rw-r--r--apt-pkg/cacheset.cc31
-rw-r--r--apt-pkg/cacheset.h6
5 files changed, 580 insertions, 2 deletions
diff --git a/apt-pkg/cachefilter-patterns.cc b/apt-pkg/cachefilter-patterns.cc
new file mode 100644
index 000000000..bf6166ee4
--- /dev/null
+++ b/apt-pkg/cachefilter-patterns.cc
@@ -0,0 +1,306 @@
+/*
+ * cachefilter-patterns.cc - Parser for aptitude-style patterns
+ *
+ * Copyright (c) 2019 Canonical Ltd
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <apt-pkg/cachefilter-patterns.h>
+
+namespace APT
+{
+namespace Internal
+{
+
+template <class... Args>
+std::string rstrprintf(Args... args)
+{
+ std::string str;
+ strprintf(str, std::forward<Args>(args)...);
+ return str;
+}
+
+// Parse a complete pattern, make sure it's the entire input
+std::unique_ptr<PatternTreeParser::Node> PatternTreeParser::parseTop()
+{
+ skipSpace();
+ auto node = parse();
+ skipSpace();
+
+ if (node->end != sentence.size())
+ {
+ Node node2;
+
+ node2.start = node->end;
+ node2.end = sentence.size();
+ throw Error{node2, "Expected end of file"};
+ }
+
+ return node;
+}
+
+// Parse any pattern
+std::unique_ptr<PatternTreeParser::Node> PatternTreeParser::parse()
+{
+ std::unique_ptr<Node> node;
+ if ((node = parsePattern()) != nullptr)
+ return node;
+ if ((node = parseQuotedWord()) != nullptr)
+ return node;
+ if ((node = parseWord()) != nullptr)
+ return node;
+
+ Node eNode;
+ eNode.end = eNode.start = state.offset;
+ throw Error{eNode, "Expected pattern, quoted word, or word"};
+}
+
+// Parse a list pattern (or function call pattern)
+std::unique_ptr<PatternTreeParser::Node> PatternTreeParser::parsePattern()
+{
+ static const APT::StringView CHARS("0123456789"
+ "abcdefghijklmnopqrstuvwxyz"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "-");
+ if (sentence[state.offset] != '?')
+ return nullptr;
+
+ auto node = std::make_unique<PatternNode>();
+ node->end = node->start = state.offset;
+ state.offset++;
+
+ while (CHARS.find(sentence[state.offset]) != APT::StringView::npos)
+ {
+ ++state.offset;
+ }
+
+ node->term = sentence.substr(node->start, state.offset - node->start);
+
+ node->end = skipSpace();
+ // We don't have any arguments, return node;
+ if (sentence[state.offset] != '(')
+ return node;
+ node->end = ++state.offset;
+ skipSpace();
+
+ node->haveArgumentList = true;
+
+ // Empty argument list, return
+ if (sentence[state.offset] == ')')
+ {
+ node->end = ++state.offset;
+ return node;
+ }
+
+ node->arguments.push_back(parse());
+ skipSpace();
+ while (sentence[state.offset] == ',')
+ {
+ ++state.offset;
+ skipSpace();
+ // This was a trailing comma - allow it and break the loop
+ if (sentence[state.offset] == ')')
+ break;
+ node->arguments.push_back(parse());
+ skipSpace();
+ }
+
+ node->end = state.offset;
+ if (sentence[state.offset] != ')')
+ throw Error{node->arguments.empty() ? *node : *node->arguments[node->arguments.size() - 1],
+ rstrprintf("Expected closing parenthesis or comma after last argument, received %c", sentence[state.offset])};
+
+ node->end = ++state.offset;
+ return node;
+}
+
+// Parse a quoted word atom
+std::unique_ptr<PatternTreeParser::Node> PatternTreeParser::parseQuotedWord()
+{
+ if (sentence[state.offset] != '"')
+ return nullptr;
+
+ auto node = std::make_unique<WordNode>();
+ node->start = state.offset;
+
+ // Eat beginning of string
+ state.offset++;
+
+ while (sentence[state.offset] != '"' && sentence[state.offset] != '\0')
+ state.offset++;
+
+ // End of string
+ if (sentence[state.offset] != '"')
+ throw Error{*node, "Could not find end of string"};
+ state.offset++;
+
+ node->end = state.offset;
+ node->word = sentence.substr(node->start + 1, node->end - node->start - 2);
+
+ return node;
+}
+
+// Parse a bare word atom
+std::unique_ptr<PatternTreeParser::Node> PatternTreeParser::parseWord()
+{
+ static const APT::StringView DISALLOWED_START("?~,()\0", 6);
+ static const APT::StringView DISALLOWED(",()\0", 4);
+ if (DISALLOWED_START.find(sentence[state.offset]) != APT::StringView::npos)
+ return nullptr;
+
+ auto node = std::make_unique<WordNode>();
+ node->start = state.offset;
+
+ while (DISALLOWED.find(sentence[state.offset]) == APT::StringView::npos)
+ state.offset++;
+
+ node->end = state.offset;
+ node->word = sentence.substr(node->start, node->end - node->start);
+ return node;
+}
+
+// Rendering of the tree in JSON for debugging
+std::ostream &PatternTreeParser::PatternNode::render(std::ostream &os)
+{
+ os << "{"
+ << "\"term\": \"" << term.to_string() << "\",\n"
+ << "\"arguments\": [\n";
+ for (auto &node : arguments)
+ node->render(os) << "," << std::endl;
+ os << "null]\n";
+ os << "}\n";
+ return os;
+}
+
+std::ostream &PatternTreeParser::WordNode::render(std::ostream &os)
+{
+ os << '"' << word.to_string() << '"';
+ return os;
+}
+
+std::nullptr_t PatternTreeParser::Node::error(std::string message)
+{
+ throw Error{*this, message};
+}
+
+bool PatternTreeParser::PatternNode::matches(APT::StringView name, int min, int max)
+{
+ if (name != term)
+ return false;
+ if (max != 0 && !haveArgumentList)
+ error(rstrprintf("%s expects an argument list", term.to_string().c_str()));
+ if (max == 0 && haveArgumentList)
+ error(rstrprintf("%s does not expect an argument list", term.to_string().c_str()));
+ if (min >= 0 && min == max && (arguments.size() != size_t(min)))
+ error(rstrprintf("%s expects %d arguments, but received %d arguments", term.to_string().c_str(), min, arguments.size()));
+ if (min >= 0 && arguments.size() < size_t(min))
+ error(rstrprintf("%s expects at least %d arguments, but received %d arguments", term.to_string().c_str(), min, arguments.size()));
+ if (max >= 0 && arguments.size() > size_t(max))
+ error(rstrprintf("%s expects at most %d arguments, but received %d arguments", term.to_string().c_str(), max, arguments.size()));
+ return true;
+}
+
+std::unique_ptr<APT::CacheFilter::Matcher> PatternParser::aPattern(std::unique_ptr<PatternTreeParser::Node> &nodeP)
+{
+ assert(nodeP != nullptr);
+ auto node = dynamic_cast<PatternTreeParser::PatternNode *>(nodeP.get());
+ if (node == nullptr)
+ nodeP->error("Expected a pattern");
+
+ if (node->matches("?architecture", 1, 1))
+ return std::make_unique<APT::CacheFilter::PackageArchitectureMatchesSpecification>(aWord(node->arguments[0]));
+ if (node->matches("?automatic", 0, 0))
+ return std::make_unique<Patterns::PackageIsAutomatic>(file);
+ if (node->matches("?broken", 0, 0))
+ return std::make_unique<Patterns::PackageIsBroken>(file);
+ if (node->matches("?config-files", 0, 0))
+ return std::make_unique<Patterns::PackageIsConfigFiles>();
+ if (node->matches("?essential", 0, 0))
+ return std::make_unique<Patterns::PackageIsEssential>();
+ if (node->matches("?exact-name", 1, 1))
+ return std::make_unique<Patterns::PackageHasExactName>(aWord(node->arguments[0]));
+ if (node->matches("?false", 0, 0))
+ return std::make_unique<APT::CacheFilter::FalseMatcher>();
+ if (node->matches("?garbage", 0, 0))
+ return std::make_unique<Patterns::PackageIsGarbage>(file);
+ if (node->matches("?installed", 0, 0))
+ return std::make_unique<Patterns::PackageIsInstalled>(file);
+ if (node->matches("?name", 1, 1))
+ return std::make_unique<APT::CacheFilter::PackageNameMatchesRegEx>(aWord(node->arguments[0]));
+ if (node->matches("?not", 1, 1))
+ return std::make_unique<APT::CacheFilter::NOTMatcher>(aPattern(node->arguments[0]).release());
+ if (node->matches("?obsolete", 0, 0))
+ return std::make_unique<Patterns::PackageIsObsolete>();
+ if (node->matches("?true", 0, 0))
+ return std::make_unique<APT::CacheFilter::TrueMatcher>();
+ if (node->matches("?upgradable", 0, 0))
+ return std::make_unique<Patterns::PackageIsUpgradable>(file);
+ if (node->matches("?virtual", 0, 0))
+ return std::make_unique<Patterns::PackageIsVirtual>();
+ if (node->matches("?x-name-fnmatch", 1, 1))
+ return std::make_unique<APT::CacheFilter::PackageNameMatchesFnmatch>(aWord(node->arguments[0]));
+
+ // Variable argument patterns
+ if (node->matches("?and", 0, -1))
+ {
+ auto pattern = std::make_unique<APT::CacheFilter::ANDMatcher>();
+ for (auto &arg : node->arguments)
+ pattern->AND(aPattern(arg).release());
+ return pattern;
+ }
+ if (node->matches("?or", 0, -1))
+ {
+ auto pattern = std::make_unique<APT::CacheFilter::ORMatcher>();
+
+ for (auto &arg : node->arguments)
+ pattern->OR(aPattern(arg).release());
+ return pattern;
+ }
+
+ node->error(rstrprintf("Unrecognized pattern '%s'", node->term.to_string().c_str()));
+
+ return nullptr;
+}
+
+std::string PatternParser::aWord(std::unique_ptr<PatternTreeParser::Node> &nodeP)
+{
+ assert(nodeP != nullptr);
+ auto node = dynamic_cast<PatternTreeParser::WordNode *>(nodeP.get());
+ if (node == nullptr)
+ nodeP->error("Expected a word");
+ return node->word.to_string();
+}
+
+} // namespace Internal
+
+// The bridge into the public world
+std::unique_ptr<APT::CacheFilter::Matcher> APT::CacheFilter::ParsePattern(APT::StringView pattern, pkgCacheFile *file)
+{
+ if (file != nullptr && !file->BuildDepCache())
+ return nullptr;
+
+ try
+ {
+ auto top = APT::Internal::PatternTreeParser(pattern).parseTop();
+ APT::Internal::PatternParser parser{file};
+ return parser.aPattern(top);
+ }
+ catch (APT::Internal::PatternTreeParser::Error &e)
+ {
+ std::stringstream ss;
+ ss << "input:" << e.location.start << "-" << e.location.end << ": error: " << e.message << "\n";
+ ss << pattern.to_string() << "\n";
+ for (size_t i = 0; i < e.location.start; i++)
+ ss << " ";
+ for (size_t i = e.location.start; i < e.location.end; i++)
+ ss << "^";
+
+ ss << "\n";
+
+ _error->Error("%s", ss.str().c_str());
+ return nullptr;
+ }
+}
+
+} // namespace APT
diff --git a/apt-pkg/cachefilter-patterns.h b/apt-pkg/cachefilter-patterns.h
new file mode 100644
index 000000000..d37da815f
--- /dev/null
+++ b/apt-pkg/cachefilter-patterns.h
@@ -0,0 +1,235 @@
+/*
+ * cachefilter-patterns.h - Pattern parser and additional patterns as matchers
+ *
+ * Copyright (c) 2019 Canonical Ltd
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#ifndef APT_CACHEFILTER_PATTERNS_H
+#define APT_CACHEFILTER_PATTERNS_H
+#include <apt-pkg/cachefile.h>
+#include <apt-pkg/cachefilter.h>
+#include <apt-pkg/error.h>
+#include <apt-pkg/string_view.h>
+#include <apt-pkg/strutl.h>
+#include <iostream>
+#include <memory>
+#include <sstream>
+#include <string>
+#include <vector>
+#include <assert.h>
+namespace APT
+{
+
+namespace Internal
+{
+/**
+ * \brief PatternTreeParser parses the given sentence into a parse tree.
+ *
+ * The parse tree consists of nodes:
+ * - Word nodes which contains words or quoted words
+ * - Patterns, which represent ?foo and ?foo(...) patterns
+ */
+struct PatternTreeParser
+{
+
+ struct Node
+ {
+ size_t start = 0;
+ size_t end = 0;
+
+ virtual std::ostream &render(std::ostream &os) { return os; };
+ std::nullptr_t error(std::string message);
+ };
+
+ struct Error : public std::exception
+ {
+ Node location;
+ std::string message;
+
+ Error(Node location, std::string message) : location(location), message(message) {}
+ const char *what() const throw() override { return message.c_str(); }
+ };
+
+ struct PatternNode : public Node
+ {
+ APT::StringView term;
+ std::vector<std::unique_ptr<Node>> arguments;
+ bool haveArgumentList = false;
+
+ std::ostream &render(std::ostream &stream) override;
+ bool matches(APT::StringView name, int min, int max);
+ };
+
+ struct WordNode : public Node
+ {
+ APT::StringView word;
+ bool quoted = false;
+ std::ostream &render(std::ostream &stream) override;
+ };
+
+ struct State
+ {
+ off_t offset = 0;
+ };
+
+ APT::StringView sentence;
+ State state;
+
+ PatternTreeParser(APT::StringView sentence) : sentence(sentence){};
+ off_t skipSpace()
+ {
+ while (sentence[state.offset] == ' ' || sentence[state.offset] == '\t' || sentence[state.offset] == '\r' || sentence[state.offset] == '\n')
+ state.offset++;
+ return state.offset;
+ };
+
+ /// \brief Parse a complete pattern
+ ///
+ /// There may not be anything before or after the pattern, except for
+ /// whitespace.
+ std::unique_ptr<Node> parseTop();
+
+ private:
+ std::unique_ptr<Node> parse();
+ std::unique_ptr<Node> parsePattern();
+ std::unique_ptr<Node> parseWord();
+ std::unique_ptr<Node> parseQuotedWord();
+};
+
+/**
+ * \brief PatternParser parses the given sentence into a parse tree.
+ *
+ * The parse tree consists of nodes:
+ * - Word nodes which contains words or quoted words
+ * - Patterns, which represent ?foo and ?foo(...) patterns
+ */
+struct PatternParser
+{
+ pkgCacheFile *file;
+
+ std::unique_ptr<APT::CacheFilter::Matcher> aPattern(std::unique_ptr<PatternTreeParser::Node> &nodeP);
+ std::string aWord(std::unique_ptr<PatternTreeParser::Node> &nodeP);
+};
+
+namespace Patterns
+{
+using namespace APT::CacheFilter;
+
+struct PackageIsAutomatic : public PackageMatcher
+{
+ pkgCacheFile *Cache;
+ explicit PackageIsAutomatic(pkgCacheFile *Cache) : Cache(Cache) {}
+ bool operator()(pkgCache::PkgIterator const &Pkg) override
+ {
+ assert(Cache != nullptr);
+ return ((*Cache)[Pkg].Flags & pkgCache::Flag::Auto) != 0;
+ }
+};
+
+struct PackageIsBroken : public PackageMatcher
+{
+ pkgCacheFile *Cache;
+ explicit PackageIsBroken(pkgCacheFile *Cache) : Cache(Cache) {}
+ bool operator()(pkgCache::PkgIterator const &Pkg) override
+ {
+ assert(Cache != nullptr);
+ auto state = (*Cache)[Pkg];
+ return state.InstBroken() || state.NowBroken();
+ }
+};
+
+struct PackageIsConfigFiles : public PackageMatcher
+{
+ bool operator()(pkgCache::PkgIterator const &Pkg) override
+ {
+ return Pkg->CurrentState == pkgCache::State::ConfigFiles;
+ }
+};
+
+struct PackageIsGarbage : public PackageMatcher
+{
+ pkgCacheFile *Cache;
+ explicit PackageIsGarbage(pkgCacheFile *Cache) : Cache(Cache) {}
+ bool operator()(pkgCache::PkgIterator const &Pkg) override
+ {
+ assert(Cache != nullptr);
+ return (*Cache)[Pkg].Garbage;
+ }
+};
+struct PackageIsEssential : public PackageMatcher
+{
+ bool operator()(pkgCache::PkgIterator const &Pkg) override
+ {
+ return (Pkg->Flags & pkgCache::Flag::Essential) != 0;
+ }
+};
+
+struct PackageHasExactName : public PackageMatcher
+{
+ std::string name;
+ explicit PackageHasExactName(std::string name) : name(name) {}
+ bool operator()(pkgCache::PkgIterator const &Pkg) override
+ {
+ return Pkg.Name() == name;
+ }
+};
+
+struct PackageIsInstalled : public PackageMatcher
+{
+ pkgCacheFile *Cache;
+ explicit PackageIsInstalled(pkgCacheFile *Cache) : Cache(Cache) {}
+ bool operator()(pkgCache::PkgIterator const &Pkg) override
+ {
+ assert(Cache != nullptr);
+ return Pkg->CurrentVer != 0;
+ }
+};
+
+struct PackageIsObsolete : public PackageMatcher
+{
+ bool operator()(pkgCache::PkgIterator const &pkg) override
+ {
+ // This code can be written without loops, as aptitude does, but it
+ // is far less readable.
+ if (pkg.CurrentVer().end())
+ return false;
+
+ // See if there is any version that exists in a repository,
+ // if so return false
+ for (auto ver = pkg.VersionList(); !ver.end(); ver++)
+ {
+ for (auto file = ver.FileList(); !file.end(); file++)
+ {
+ if ((file.File()->Flags & pkgCache::Flag::NotSource) == 0)
+ return false;
+ }
+ }
+
+ return true;
+ }
+};
+
+struct PackageIsUpgradable : public PackageMatcher
+{
+ pkgCacheFile *Cache;
+ explicit PackageIsUpgradable(pkgCacheFile *Cache) : Cache(Cache) {}
+ bool operator()(pkgCache::PkgIterator const &Pkg) override
+ {
+ assert(Cache != nullptr);
+ return Pkg->CurrentVer != 0 && (*Cache)[Pkg].Upgradable();
+ }
+};
+
+struct PackageIsVirtual : public PackageMatcher
+{
+ bool operator()(pkgCache::PkgIterator const &Pkg) override
+ {
+ return Pkg->VersionList == 0;
+ }
+};
+} // namespace Patterns
+} // namespace Internal
+} // namespace APT
+#endif
diff --git a/apt-pkg/cachefilter.h b/apt-pkg/cachefilter.h
index 8a6c01341..3c6e1559d 100644
--- a/apt-pkg/cachefilter.h
+++ b/apt-pkg/cachefilter.h
@@ -7,7 +7,9 @@
#define APT_CACHEFILTER_H
// Include Files /*{{{*/
#include <apt-pkg/pkgcache.h>
+#include <apt-pkg/string_view.h>
+#include <memory>
#include <string>
#include <vector>
@@ -145,6 +147,8 @@ public:
};
/*}}}*/
+/// \brief Parse a pattern, return nullptr or pattern
+std::unique_ptr<APT::CacheFilter::Matcher> ParsePattern(APT::StringView pattern, pkgCacheFile *file);
}
}
#endif
diff --git a/apt-pkg/cacheset.cc b/apt-pkg/cacheset.cc
index 789727266..dd55edb4e 100644
--- a/apt-pkg/cacheset.cc
+++ b/apt-pkg/cacheset.cc
@@ -46,6 +46,7 @@ bool CacheSetHelper::PackageFrom(enum PkgSelector const select, PackageContainer
case FNMATCH: return PackageFromFnmatch(pci, Cache, pattern);
case PACKAGENAME: return PackageFromPackageName(pci, Cache, pattern);
case STRING: return PackageFromString(pci, Cache, pattern);
+ case PATTERN: return PackageFromPattern(pci, Cache, pattern);
}
return false;
}
@@ -281,13 +282,33 @@ bool CacheSetHelper::PackageFromPackageName(PackageContainerInterface * const pc
pci->insert(Pkg);
return true;
}
+
+bool CacheSetHelper::PackageFromPattern(PackageContainerInterface *const pci, pkgCacheFile &Cache, std::string const &pattern)
+{
+ if (pattern.size() < 1 || pattern[0] != '?')
+ return false;
+
+ auto compiledPattern = APT::CacheFilter::ParsePattern(pattern, &Cache);
+ if (!compiledPattern)
+ return false;
+
+ for (pkgCache::PkgIterator Pkg = Cache->PkgBegin(); Pkg.end() == false; ++Pkg)
+ {
+ if ((*compiledPattern)(Pkg) == false)
+ continue;
+
+ pci->insert(Pkg);
+ }
+ return true;
+}
/*}}}*/
// PackageFromString - Return all packages matching a specific string /*{{{*/
bool CacheSetHelper::PackageFromString(PackageContainerInterface * const pci, pkgCacheFile &Cache, std::string const &str) {
bool found = true;
_error->PushToStack();
- if (PackageFrom(CacheSetHelper::PACKAGENAME, pci, Cache, str) == false &&
+ if (PackageFrom(CacheSetHelper::PATTERN, pci, Cache, str) == false &&
+ PackageFrom(CacheSetHelper::PACKAGENAME, pci, Cache, str) == false &&
PackageFrom(CacheSetHelper::TASK, pci, Cache, str) == false &&
// FIXME: hm, hm, regexp/fnmatch incompatible?
PackageFrom(CacheSetHelper::FNMATCH, pci, Cache, str) == false &&
@@ -686,6 +707,7 @@ void CacheSetHelper::canNotFindPackage(enum PkgSelector const select,
case FNMATCH: canNotFindFnmatch(pci, Cache, pattern); break;
case PACKAGENAME: canNotFindPackage(pci, Cache, pattern); break;
case STRING: canNotFindPackage(pci, Cache, pattern); break;
+ case PATTERN: canNotFindPackage(pci, Cache, pattern); break;
case UNKNOWN: break;
}
}
@@ -822,6 +844,7 @@ void CacheSetHelper::showPackageSelection(pkgCache::PkgIterator const &pkg, enum
case REGEX: showRegExSelection(pkg, pattern); break;
case TASK: showTaskSelection(pkg, pattern); break;
case FNMATCH: showFnmatchSelection(pkg, pattern); break;
+ case PATTERN: showPatternSelection(pkg, pattern); break;
case PACKAGENAME: /* no surprises here */ break;
case STRING: /* handled by the special cases */ break;
case UNKNOWN: break;
@@ -842,6 +865,12 @@ void CacheSetHelper::showFnmatchSelection(pkgCache::PkgIterator const &/*pkg*/,
std::string const &/*pattern*/) {
}
/*}}}*/
+// showPatternSelection /*{{{*/
+void CacheSetHelper::showPatternSelection(pkgCache::PkgIterator const & /*pkg*/,
+ std::string const & /*pattern*/)
+{
+}
+ /*}}}*/
/*}}}*/
// showVersionSelection /*{{{*/
void CacheSetHelper::showVersionSelection(pkgCache::PkgIterator const &Pkg,
diff --git a/apt-pkg/cacheset.h b/apt-pkg/cacheset.h
index 489fb6220..6023b861d 100644
--- a/apt-pkg/cacheset.h
+++ b/apt-pkg/cacheset.h
@@ -52,7 +52,7 @@ public: /*{{{*/
GlobalError::MsgType ErrorType = GlobalError::ERROR);
virtual ~CacheSetHelper();
- enum PkgSelector { UNKNOWN, REGEX, TASK, FNMATCH, PACKAGENAME, STRING };
+ enum PkgSelector { UNKNOWN, REGEX, TASK, FNMATCH, PACKAGENAME, STRING, PATTERN };
virtual bool PackageFrom(enum PkgSelector const select, PackageContainerInterface * const pci, pkgCacheFile &Cache, std::string const &pattern);
@@ -172,10 +172,12 @@ protected:
bool PackageFromFnmatch(PackageContainerInterface * const pci, pkgCacheFile &Cache, std::string pattern);
bool PackageFromPackageName(PackageContainerInterface * const pci, pkgCacheFile &Cache, std::string pattern);
bool PackageFromString(PackageContainerInterface * const pci, pkgCacheFile &Cache, std::string const &pattern);
+ bool PackageFromPattern(PackageContainerInterface * const pci, pkgCacheFile &Cache, std::string const &pattern);
private:
void showTaskSelection(pkgCache::PkgIterator const &pkg, std::string const &pattern);
void showRegExSelection(pkgCache::PkgIterator const &pkg, std::string const &pattern);
void showFnmatchSelection(pkgCache::PkgIterator const &pkg, std::string const &pattern);
+ void showPatternSelection(pkgCache::PkgIterator const &pkg, std::string const &pattern);
void canNotFindTask(PackageContainerInterface *const pci, pkgCacheFile &Cache, std::string pattern);
void canNotFindRegEx(PackageContainerInterface *const pci, pkgCacheFile &Cache, std::string pattern);
void canNotFindFnmatch(PackageContainerInterface *const pci, pkgCacheFile &Cache, std::string pattern);
@@ -739,6 +741,8 @@ public:
std::string pkg, CacheSetHelper::VerSelector const fallback, CacheSetHelper &helper,
bool const onlyFromName = false);
+ static bool FromPattern(VersionContainerInterface *const vci, pkgCacheFile &Cache,
+ std::string pkg, CacheSetHelper::VerSelector const fallback, CacheSetHelper &helper);
static bool FromPackage(VersionContainerInterface * const vci, pkgCacheFile &Cache,
pkgCache::PkgIterator const &P, CacheSetHelper::VerSelector const fallback,