summaryrefslogtreecommitdiff
path: root/apt-pkg/cachefilter-patterns.cc
diff options
context:
space:
mode:
Diffstat (limited to 'apt-pkg/cachefilter-patterns.cc')
-rw-r--r--apt-pkg/cachefilter-patterns.cc301
1 files changed, 272 insertions, 29 deletions
diff --git a/apt-pkg/cachefilter-patterns.cc b/apt-pkg/cachefilter-patterns.cc
index bf6166ee4..5a58a9767 100644
--- a/apt-pkg/cachefilter-patterns.cc
+++ b/apt-pkg/cachefilter-patterns.cc
@@ -6,13 +6,43 @@
* SPDX-License-Identifier: GPL-2.0+
*/
+#include <config.h>
+
#include <apt-pkg/cachefilter-patterns.h>
+#include <apti18n.h>
+
namespace APT
{
namespace Internal
{
+static const constexpr struct
+{
+ APT::StringView shortName;
+ APT::StringView longName;
+ bool takesArgument;
+} shortPatterns[] = {
+ {"r"_sv, "?architecture"_sv, true},
+ {"A"_sv, "?archive"_sv, true},
+ {"M"_sv, "?automatic"_sv, false},
+ {"b"_sv, "?broken"_sv, false},
+ {"c"_sv, "?config-files"_sv, false},
+ {"E"_sv, "?essential"_sv, false},
+ {"F"_sv, "?false"_sv, false},
+ {"g"_sv, "?garbage"_sv, false},
+ {"i"_sv, "?installed"_sv, false},
+ {"n"_sv, "?name"_sv, true},
+ {"o"_sv, "?obsolete"_sv, false},
+ {"O"_sv, "?origin"_sv, true},
+ {"s"_sv, "?section"_sv, true},
+ {"e"_sv, "?source-package"_sv, true},
+ {"T"_sv, "?true"_sv, false},
+ {"U"_sv, "?upgradable"_sv, false},
+ {"V"_sv, "?version"_sv, true},
+ {"v"_sv, "?virtual"_sv, false},
+};
+
template <class... Args>
std::string rstrprintf(Args... args)
{
@@ -28,41 +58,197 @@ std::unique_ptr<PatternTreeParser::Node> PatternTreeParser::parseTop()
auto node = parse();
skipSpace();
+ if (node == nullptr)
+ throw Error{Node{0, sentence.size()}, "Expected pattern"};
+
if (node->end != sentence.size())
+ throw Error{Node{node->end, sentence.size()}, "Expected end of file"};
+
+ return node;
+}
+
+// Parse any pattern
+std::unique_ptr<PatternTreeParser::Node> PatternTreeParser::parse()
+{
+ return parseOr();
+}
+
+std::unique_ptr<PatternTreeParser::Node> PatternTreeParser::parseOr()
+{
+ auto start = state.offset;
+ std::vector<std::unique_ptr<PatternTreeParser::Node>> nodes;
+
+ auto firstNode = parseAnd();
+
+ if (firstNode == nullptr)
+ return nullptr;
+
+ nodes.push_back(std::move(firstNode));
+ for (skipSpace(); sentence[state.offset] == '|'; skipSpace())
+ {
+ state.offset++;
+ skipSpace();
+ auto node = parseAnd();
+
+ if (node == nullptr)
+ throw Error{Node{state.offset, sentence.size()}, "Expected pattern after |"};
+
+ nodes.push_back(std::move(node));
+ }
+
+ if (nodes.size() == 0)
+ return nullptr;
+ if (nodes.size() == 1)
+ return std::move(nodes[0]);
+
+ auto node = std::make_unique<PatternNode>();
+ node->start = start;
+ node->end = nodes[nodes.size() - 1]->end;
+ node->term = "?or";
+ node->arguments = std::move(nodes);
+ node->haveArgumentList = true;
+
+ return node;
+}
+
+std::unique_ptr<PatternTreeParser::Node> PatternTreeParser::parseAnd()
+{
+ auto start = state.offset;
+ std::vector<std::unique_ptr<PatternTreeParser::Node>> nodes;
+
+ for (skipSpace(); state.offset < sentence.size(); skipSpace())
{
- Node node2;
+ auto node = parseUnary();
- node2.start = node->end;
- node2.end = sentence.size();
- throw Error{node2, "Expected end of file"};
+ if (node == nullptr)
+ break;
+
+ nodes.push_back(std::move(node));
}
+ if (nodes.size() == 0)
+ return nullptr;
+ if (nodes.size() == 1)
+ return std::move(nodes[0]);
+
+ auto node = std::make_unique<PatternNode>();
+ node->start = start;
+ node->end = nodes[nodes.size() - 1]->end;
+ node->term = "?and";
+ node->arguments = std::move(nodes);
+ node->haveArgumentList = true;
+
return node;
}
-// Parse any pattern
-std::unique_ptr<PatternTreeParser::Node> PatternTreeParser::parse()
+std::unique_ptr<PatternTreeParser::Node> PatternTreeParser::parseUnary()
+{
+
+ if (sentence[state.offset] != '!')
+ return parsePrimary();
+
+ auto start = ++state.offset;
+ auto primary = parsePrimary();
+
+ if (primary == nullptr)
+ throw Error{Node{start, sentence.size()}, "Expected pattern"};
+
+ auto node = std::make_unique<PatternNode>();
+ node->start = start;
+ node->end = primary->end;
+ node->term = "?not";
+ node->arguments.push_back(std::move(primary));
+ node->haveArgumentList = true;
+ return node;
+}
+
+std::unique_ptr<PatternTreeParser::Node> PatternTreeParser::parsePrimary()
{
std::unique_ptr<Node> node;
+ if ((node = parseShortPattern()) != nullptr)
+ return node;
if ((node = parsePattern()) != nullptr)
return node;
+ if ((node = parseGroup()) != nullptr)
+ return node;
+
+ return nullptr;
+}
+
+std::unique_ptr<PatternTreeParser::Node> PatternTreeParser::parseGroup()
+{
+ if (sentence[state.offset] != '(')
+ return nullptr;
+
+ auto start = state.offset++;
+
+ skipSpace();
+ auto node = parse();
+ if (node == nullptr)
+ throw Error{Node{state.offset, sentence.size()},
+ "Expected pattern after '('"};
+ skipSpace();
+
+ if (sentence[state.offset] != ')')
+ throw Error{Node{state.offset, sentence.size()},
+ "Expected closing parenthesis"};
+
+ auto end = ++state.offset;
+ node->start = start;
+ node->end = end;
+ return node;
+}
+
+std::unique_ptr<PatternTreeParser::Node> PatternTreeParser::parseArgument(bool shrt)
+{
+ std::unique_ptr<Node> node;
if ((node = parseQuotedWord()) != nullptr)
return node;
- if ((node = parseWord()) != nullptr)
+ if ((node = parseWord(shrt)) != nullptr)
+ return node;
+ if ((node = parse()) != nullptr)
+ return node;
+
+ throw Error{Node{state.offset, sentence.size()},
+ "Expected pattern, quoted word, or word"};
+}
+
+// Parse a short pattern
+std::unique_ptr<PatternTreeParser::Node> PatternTreeParser::parseShortPattern()
+{
+ if (sentence[state.offset] != '~')
+ return nullptr;
+
+ for (auto &sp : shortPatterns)
+ {
+ if (sentence.substr(state.offset + 1, sp.shortName.size()) != sp.shortName)
+ continue;
+
+ auto node = std::make_unique<PatternNode>();
+ node->end = node->start = state.offset;
+ node->term = sp.longName;
+
+ state.offset += sp.shortName.size() + 1;
+ if (sp.takesArgument)
+ {
+ node->arguments.push_back(parseArgument(true));
+ node->haveArgumentList = true;
+ }
+ node->end = state.offset;
+
return node;
+ }
- Node eNode;
- eNode.end = eNode.start = state.offset;
- throw Error{eNode, "Expected pattern, quoted word, or word"};
+ throw Error{Node{state.offset, sentence.size()}, "Unknown short pattern"};
}
// Parse a list pattern (or function call pattern)
std::unique_ptr<PatternTreeParser::Node> PatternTreeParser::parsePattern()
{
- static const APT::StringView CHARS("0123456789"
- "abcdefghijklmnopqrstuvwxyz"
- "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
- "-");
+ static constexpr auto CHARS = ("0123456789"
+ "abcdefghijklmnopqrstuvwxyz"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "-"_sv);
if (sentence[state.offset] != '?')
return nullptr;
@@ -77,6 +263,9 @@ std::unique_ptr<PatternTreeParser::Node> PatternTreeParser::parsePattern()
node->term = sentence.substr(node->start, state.offset - node->start);
+ if (node->term.size() <= 1)
+ throw Error{*node, "Pattern must have a term/name"};
+
node->end = skipSpace();
// We don't have any arguments, return node;
if (sentence[state.offset] != '(')
@@ -93,7 +282,7 @@ std::unique_ptr<PatternTreeParser::Node> PatternTreeParser::parsePattern()
return node;
}
- node->arguments.push_back(parse());
+ node->arguments.push_back(parseArgument(false));
skipSpace();
while (sentence[state.offset] == ',')
{
@@ -102,7 +291,7 @@ std::unique_ptr<PatternTreeParser::Node> PatternTreeParser::parsePattern()
// This was a trailing comma - allow it and break the loop
if (sentence[state.offset] == ')')
break;
- node->arguments.push_back(parse());
+ node->arguments.push_back(parseArgument(false));
skipSpace();
}
@@ -142,10 +331,13 @@ std::unique_ptr<PatternTreeParser::Node> PatternTreeParser::parseQuotedWord()
}
// Parse a bare word atom
-std::unique_ptr<PatternTreeParser::Node> PatternTreeParser::parseWord()
+std::unique_ptr<PatternTreeParser::Node> PatternTreeParser::parseWord(bool shrt)
{
- static const APT::StringView DISALLOWED_START("?~,()\0", 6);
- static const APT::StringView DISALLOWED(",()\0", 4);
+ static const constexpr auto DISALLOWED_START = "!?~|,() \0"_sv;
+ static const constexpr auto DISALLOWED_LONG = "|,()\0"_sv;
+ static const constexpr auto DISALLOWED_SHRT = "|,() ?\0"_sv;
+ const auto DISALLOWED = shrt ? DISALLOWED_SHRT : DISALLOWED_LONG;
+
if (DISALLOWED_START.find(sentence[state.offset]) != APT::StringView::npos)
return nullptr;
@@ -163,20 +355,21 @@ std::unique_ptr<PatternTreeParser::Node> PatternTreeParser::parseWord()
// 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";
+
+ os << term.to_string();
+ if (haveArgumentList)
+ {
+ os << "(";
+ for (auto &node : arguments)
+ node->render(os) << ",";
+ os << ")";
+ }
return os;
}
std::ostream &PatternTreeParser::WordNode::render(std::ostream &os)
{
- os << '"' << word.to_string() << '"';
- return os;
+ return quoted ? os << '"' << word.to_string() << '"' : os << word.to_string();
}
std::nullptr_t PatternTreeParser::Node::error(std::string message)
@@ -210,6 +403,12 @@ std::unique_ptr<APT::CacheFilter::Matcher> PatternParser::aPattern(std::unique_p
if (node->matches("?architecture", 1, 1))
return std::make_unique<APT::CacheFilter::PackageArchitectureMatchesSpecification>(aWord(node->arguments[0]));
+ if (node->matches("?archive", 1, 1))
+ return std::make_unique<Patterns::VersionIsArchive>(aWord(node->arguments[0]));
+ if (node->matches("?all-versions", 1, 1))
+ return std::make_unique<Patterns::VersionIsAllVersions>(aPattern(node->arguments[0]));
+ if (node->matches("?any-version", 1, 1))
+ return std::make_unique<Patterns::VersionIsAnyVersion>(aPattern(node->arguments[0]));
if (node->matches("?automatic", 0, 0))
return std::make_unique<Patterns::PackageIsAutomatic>(file);
if (node->matches("?broken", 0, 0))
@@ -232,21 +431,33 @@ std::unique_ptr<APT::CacheFilter::Matcher> PatternParser::aPattern(std::unique_p
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("?origin", 1, 1))
+ return std::make_unique<Patterns::VersionIsOrigin>(aWord(node->arguments[0]));
+ if (node->matches("?section", 1, 1))
+ return std::make_unique<Patterns::VersionIsSection>(aWord(node->arguments[0]));
+ if (node->matches("?source-package", 1, 1))
+ return std::make_unique<Patterns::VersionIsSourcePackage>(aWord(node->arguments[0]));
+ if (node->matches("?source-version", 1, 1))
+ return std::make_unique<Patterns::VersionIsSourceVersion>(aWord(node->arguments[0]));
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("?version", 1, 1))
+ return std::make_unique<Patterns::VersionIsVersion>(aWord(node->arguments[0]));
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))
+ if (node->matches("?and", 0, -1) || node->matches("?narrow", 0, -1))
{
auto pattern = std::make_unique<APT::CacheFilter::ANDMatcher>();
for (auto &arg : node->arguments)
pattern->AND(aPattern(arg).release());
+ if (node->term == "?narrow")
+ return std::make_unique<Patterns::VersionIsAnyVersion>(std::move(pattern));
return pattern;
}
if (node->matches("?or", 0, -1))
@@ -272,6 +483,38 @@ std::string PatternParser::aWord(std::unique_ptr<PatternTreeParser::Node> &nodeP
return node->word.to_string();
}
+namespace Patterns
+{
+
+BaseRegexMatcher::BaseRegexMatcher(std::string const &Pattern)
+{
+ pattern = new regex_t;
+ int const Res = regcomp(pattern, Pattern.c_str(), REG_EXTENDED | REG_ICASE | REG_NOSUB);
+ if (Res == 0)
+ return;
+
+ delete pattern;
+ pattern = NULL;
+ char Error[300];
+ regerror(Res, pattern, Error, sizeof(Error));
+ _error->Error(_("Regex compilation error - %s"), Error);
+}
+bool BaseRegexMatcher::operator()(const char *string)
+{
+ if (unlikely(pattern == NULL))
+ return false;
+ else
+ return regexec(pattern, string, 0, 0, 0) == 0;
+}
+BaseRegexMatcher::~BaseRegexMatcher()
+{
+ if (pattern == NULL)
+ return;
+ regfree(pattern);
+ delete pattern;
+}
+} // namespace Patterns
+
} // namespace Internal
// The bridge into the public world