From 690ff4c3e44e7063ebde2557b7c0087ab720b894 Mon Sep 17 00:00:00 2001 From: Julian Andres Klode Date: Wed, 14 Aug 2019 14:38:26 +0200 Subject: Add initial support for parsing patterns into parse trees Introduce a parser for patterns that generates a parse tree. The language understood by the parser is: pattern = '?'TERM | '?'TERM '(' pattern (',' pattern)* ','? ')' | WORD | QUOTED-WORD TERM = [0-9a-zA-Z-] WORD = [0-9a-ZA-Z-.*^$\[\]_\\] QUOTED_WORD = "..." # you know what I mean This language is context free, which is a massive simplification from aptitude's language, where ?foo(bar) could have two different meanings depending on whether ?foo takes an argument or not. --- apt-pkg/cachefilter-patterns.h | 103 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 apt-pkg/cachefilter-patterns.h (limited to 'apt-pkg/cachefilter-patterns.h') diff --git a/apt-pkg/cachefilter-patterns.h b/apt-pkg/cachefilter-patterns.h new file mode 100644 index 000000000..dfbcd66a5 --- /dev/null +++ b/apt-pkg/cachefilter-patterns.h @@ -0,0 +1,103 @@ +/* + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +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> 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 parseTop(); + + private: + std::unique_ptr parse(); + std::unique_ptr parsePattern(); + std::unique_ptr parseWord(); + std::unique_ptr parseQuotedWord(); +}; + +} // namespace Internal +} // namespace APT +#endif -- cgit v1.2.3-70-g09d2 From 08b61197f418883ea20563e2251fb60779c0ba87 Mon Sep 17 00:00:00 2001 From: Julian Andres Klode Date: Thu, 15 Aug 2019 11:47:00 +0200 Subject: Add pattern tree parser infra and connect with cacheset and apt list This adds a transformation from parse tree into a CacheFilter and connects it with cachesets and the apt list command. --- apt-pkg/cachefilter-patterns.cc | 51 +++++++++++++++++++++++++++++++ apt-pkg/cachefilter-patterns.h | 15 ++++++++++ apt-pkg/cachefilter.h | 4 +++ apt-pkg/cacheset.cc | 31 ++++++++++++++++++- apt-pkg/cacheset.h | 6 +++- apt-private/private-list.cc | 23 +++++++++----- test/integration/test-apt-patterns | 61 ++++++++++++++++++++++++++++++++++++++ 7 files changed, 182 insertions(+), 9 deletions(-) create mode 100755 test/integration/test-apt-patterns (limited to 'apt-pkg/cachefilter-patterns.h') diff --git a/apt-pkg/cachefilter-patterns.cc b/apt-pkg/cachefilter-patterns.cc index 3c958ebae..ea4cf3cd8 100644 --- a/apt-pkg/cachefilter-patterns.cc +++ b/apt-pkg/cachefilter-patterns.cc @@ -201,5 +201,56 @@ bool PatternTreeParser::PatternNode::matches(APT::StringView name, int min, int return true; } +std::unique_ptr PatternParser::aPattern(std::unique_ptr &nodeP) +{ + assert(nodeP != nullptr); + auto node = dynamic_cast(nodeP.get()); + if (node == nullptr) + nodeP->error("Expected a pattern"); + + node->error(rstrprintf("Unrecognized pattern '%s'", node->term.to_string().c_str())); + + return nullptr; +} + +std::string PatternParser::aWord(std::unique_ptr &nodeP) +{ + assert(nodeP != nullptr); + auto node = dynamic_cast(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::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 index dfbcd66a5..3097ba94b 100644 --- a/apt-pkg/cachefilter-patterns.h +++ b/apt-pkg/cachefilter-patterns.h @@ -98,6 +98,21 @@ struct PatternTreeParser std::unique_ptr 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 aPattern(std::unique_ptr &nodeP); + std::string aWord(std::unique_ptr &nodeP); +}; + } // 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 +#include +#include #include #include @@ -145,6 +147,8 @@ public: }; /*}}}*/ +/// \brief Parse a pattern, return nullptr or pattern +std::unique_ptr 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; @@ -840,6 +863,12 @@ void CacheSetHelper::showRegExSelection(pkgCache::PkgIterator const &/*pkg*/, // showFnmatchSelection /*{{{*/ void CacheSetHelper::showFnmatchSelection(pkgCache::PkgIterator const &/*pkg*/, std::string const &/*pattern*/) { +} + /*}}}*/ +// showPatternSelection /*{{{*/ +void CacheSetHelper::showPatternSelection(pkgCache::PkgIterator const & /*pkg*/, + std::string const & /*pattern*/) +{ } /*}}}*/ /*}}}*/ 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, diff --git a/apt-private/private-list.cc b/apt-private/private-list.cc index 7c8c89777..6071129a7 100644 --- a/apt-private/private-list.cc +++ b/apt-private/private-list.cc @@ -39,17 +39,26 @@ struct PackageSortAlphabetic /*{{{*/ class PackageNameMatcher : public Matcher { + pkgCacheFile &cacheFile; public: - explicit PackageNameMatcher(const char **patterns) + explicit PackageNameMatcher(pkgCacheFile &cacheFile, const char **patterns) + : cacheFile(cacheFile) { for(int i=0; patterns[i] != NULL; ++i) { std::string pattern = patterns[i]; - APT::CacheFilter::PackageMatcher *cachefilter = NULL; - if(_config->FindB("APT::Cmd::Use-Regexp", false) == true) + APT::CacheFilter::Matcher *cachefilter = NULL; + if (pattern.size() > 0 && pattern[0] == '?') + cachefilter = APT::CacheFilter::ParsePattern(pattern, &cacheFile).release(); + else if(_config->FindB("APT::Cmd::Use-Regexp", false) == true) cachefilter = new APT::CacheFilter::PackageNameMatchesRegEx(pattern); else cachefilter = new APT::CacheFilter::PackageNameMatchesFnmatch(pattern); + + if (cachefilter == nullptr) { + return; + filters.clear(); + } filters.push_back(cachefilter); } } @@ -62,7 +71,7 @@ class PackageNameMatcher : public Matcher { for(J=filters.begin(); J != filters.end(); ++J) { - APT::CacheFilter::PackageMatcher *cachefilter = *J; + APT::CacheFilter::Matcher *cachefilter = *J; if((*cachefilter)(P)) return true; } @@ -70,8 +79,8 @@ class PackageNameMatcher : public Matcher } private: - std::vector filters; - std::vector::const_iterator J; + std::vector filters; + std::vector::const_iterator J; #undef PackageMatcher }; /*}}}*/ @@ -111,7 +120,7 @@ bool DoList(CommandLine &Cmd) if (_config->FindB("APT::Cmd::List-Include-Summary", false) == true) format += "\n ${Description}\n"; - PackageNameMatcher matcher(patterns); + PackageNameMatcher matcher(CacheFile, patterns); LocalitySortedVersionSet bag; OpTextProgress progress(*_config); progress.OverallProgress(0, diff --git a/test/integration/test-apt-patterns b/test/integration/test-apt-patterns new file mode 100755 index 000000000..c33fa1bf8 --- /dev/null +++ b/test/integration/test-apt-patterns @@ -0,0 +1,61 @@ +#!/bin/sh +TESTDIR="$(readlink -f "$(dirname "$0")")" +. "$TESTDIR/framework" + +setupenvironment +configarchitecture 'i386' 'amd64' + +insertpackage 'unstable' 'available' 'all' '1.0' + +insertinstalledpackage 'manual1' 'i386' '1.0' 'Depends: automatic1' +insertinstalledpackage 'manual2' 'i386' '1.0' + +insertinstalledpackage 'automatic1' 'i386' '1.0' +insertinstalledpackage 'automatic2' 'i386' '1.0' + +insertinstalledpackage 'essential' 'i386' '1.0' 'Essential: yes' +insertinstalledpackage 'conf-only' 'i386' '1.0' '' '' 'deinstall ok config-files' +insertinstalledpackage 'broken' 'i386' '1.0' 'Depends: does-not-exist' + +insertinstalledpackage 'not-obsolete' 'i386' '1.0' +insertpackage 'unstable' 'not-obsolete' 'all' '2.0' + +insertpackage 'unstable' 'foreign' 'amd64' '2.0' + +setupaptarchive + +testsuccess aptmark auto automatic1 automatic2 + +msgmsg "Check that commands understand patterns" + +testfailureequal "E: input:0-14: error: Unrecognized pattern '?not-a-pattern' + ?not-a-pattern + ^^^^^^^^^^^^^^ +N: Unable to locate package ?not-a-pattern +N: Couldn't find any package by glob '?not-a-pattern' +E: Regex compilation error - Invalid preceding regular expression +N: Couldn't find any package by regex '?not-a-pattern' +E: input:0-14: error: Unrecognized pattern '?not-a-pattern' + ?not-a-pattern + ^^^^^^^^^^^^^^ +N: Unable to locate package ?not-a-pattern +N: Couldn't find any package by glob '?not-a-pattern' +E: Regex compilation error - Invalid preceding regular expression +N: Couldn't find any package by regex '?not-a-pattern' +E: No packages found" apt show '?not-a-pattern' + +testfailureequal "Listing... +E: input:0-14: error: Unrecognized pattern '?not-a-pattern' + ?not-a-pattern + ^^^^^^^^^^^^^^" apt list '?not-a-pattern' + +testfailureequal "Reading package lists... +Building dependency tree... +Reading state information... +E: input:0-14: error: Unrecognized pattern '?not-a-pattern' + ?not-a-pattern + ^^^^^^^^^^^^^^ +E: Unable to locate package ?not-a-pattern +E: Couldn't find any package by glob '?not-a-pattern' +E: Regex compilation error - Invalid preceding regular expression +E: Couldn't find any package by regex '?not-a-pattern'" apt install -s '?not-a-pattern' -- cgit v1.2.3-70-g09d2 From 083e72a5939d33dc1fd7596aa441a9982332f776 Mon Sep 17 00:00:00 2001 From: Julian Andres Klode Date: Thu, 15 Aug 2019 13:12:43 +0200 Subject: Add ?automatic and ?garbage patterns These patterns allow you to identify automatically installed packages, as well as automatically installed packages that are no longer reachable from the manually installed ones. --- apt-pkg/cachefilter-patterns.cc | 4 ++++ apt-pkg/cachefilter-patterns.h | 26 ++++++++++++++++++++++++++ doc/apt-patterns.7.xml | 11 +++++++++++ test/integration/test-apt-patterns | 6 ++++++ 4 files changed, 47 insertions(+) (limited to 'apt-pkg/cachefilter-patterns.h') diff --git a/apt-pkg/cachefilter-patterns.cc b/apt-pkg/cachefilter-patterns.cc index dd5d741d3..6b506b740 100644 --- a/apt-pkg/cachefilter-patterns.cc +++ b/apt-pkg/cachefilter-patterns.cc @@ -210,8 +210,12 @@ std::unique_ptr PatternParser::aPattern(std::unique_p if (node->matches("?architecture", 1, 1)) return std::make_unique(aWord(node->arguments[0])); + if (node->matches("?automatic", 0, 0)) + return std::make_unique(file); if (node->matches("?false", 0, 0)) return std::make_unique(); + if (node->matches("?garbage", 0, 0)) + return std::make_unique(file); if (node->matches("?name", 1, 1)) return std::make_unique(aWord(node->arguments[0])); if (node->matches("?not", 1, 1)) diff --git a/apt-pkg/cachefilter-patterns.h b/apt-pkg/cachefilter-patterns.h index 3097ba94b..4c2ef48df 100644 --- a/apt-pkg/cachefilter-patterns.h +++ b/apt-pkg/cachefilter-patterns.h @@ -113,6 +113,32 @@ struct PatternParser std::string aWord(std::unique_ptr &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 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; + } +}; +} // namespace Patterns } // namespace Internal } // namespace APT #endif diff --git a/doc/apt-patterns.7.xml b/doc/apt-patterns.7.xml index 5aa352f03..327ea17d5 100644 --- a/doc/apt-patterns.7.xml +++ b/doc/apt-patterns.7.xml @@ -69,6 +69,12 @@ ?architecture(WILDCARD) Selects packages matching the specified architecture, which may contain wildcards using any. + ?automatic + Selects packages that were installed automatically. + + ?garbage + Selects packages that can be removed automatically. + ?name(REGEX) Selects packages where the name matches the given regular expression. @@ -77,6 +83,11 @@ Examples + + apt remove ?garbage + Remove all packages that are automatically installed and no longer needed - same as apt autoremove + + Migrating from aptitude diff --git a/test/integration/test-apt-patterns b/test/integration/test-apt-patterns index 0d7b1540d..9a2c74f83 100755 --- a/test/integration/test-apt-patterns +++ b/test/integration/test-apt-patterns @@ -112,6 +112,12 @@ foreign/unstable 2.0 amd64" apt list '?architecture(amd64)' testsuccessequal "Listing..." apt list '?architecture(foreign)' testsuccessequal "Listing..." apt list '?architecture(native)' +testsuccessequal "Listing... +automatic1/now 1.0 i386 [installed,local] +automatic2/now 1.0 i386 [installed,local]" apt list '?automatic' + +testsuccessequal "Listing... +automatic2/now 1.0 i386 [installed,local]" apt list '?garbage' testsuccessequal "Listing... automatic1/now 1.0 i386 [installed,local] -- cgit v1.2.3-70-g09d2 From 89790c11799c4144e37a6a1f0dbe0f6fff89ea89 Mon Sep 17 00:00:00 2001 From: Julian Andres Klode Date: Thu, 15 Aug 2019 13:23:55 +0200 Subject: Add ?obsolete and ?upgradable patterns These match packages that have no version in a repository, or where an upgrade is available. Notably, ?and(?obsolete,?upgradable) == ?false because an upgradable package is by definition not obsolete. --- apt-pkg/cachefilter-patterns.cc | 4 ++++ apt-pkg/cachefilter-patterns.h | 35 +++++++++++++++++++++++++++++++++++ doc/apt-patterns.7.xml | 6 ++++++ test/integration/test-apt-patterns | 10 ++++++++++ 4 files changed, 55 insertions(+) (limited to 'apt-pkg/cachefilter-patterns.h') diff --git a/apt-pkg/cachefilter-patterns.cc b/apt-pkg/cachefilter-patterns.cc index 6b506b740..b97d65a03 100644 --- a/apt-pkg/cachefilter-patterns.cc +++ b/apt-pkg/cachefilter-patterns.cc @@ -220,8 +220,12 @@ std::unique_ptr PatternParser::aPattern(std::unique_p return std::make_unique(aWord(node->arguments[0])); if (node->matches("?not", 1, 1)) return std::make_unique(aPattern(node->arguments[0]).release()); + if (node->matches("?obsolete", 0, 0)) + return std::make_unique(); if (node->matches("?true", 0, 0)) return std::make_unique(); + if (node->matches("?upgradable", 0, 0)) + return std::make_unique(file); if (node->matches("?x-name-fnmatch", 1, 1)) return std::make_unique(aWord(node->arguments[0])); diff --git a/apt-pkg/cachefilter-patterns.h b/apt-pkg/cachefilter-patterns.h index 4c2ef48df..68ad32f9a 100644 --- a/apt-pkg/cachefilter-patterns.h +++ b/apt-pkg/cachefilter-patterns.h @@ -138,6 +138,41 @@ struct PackageIsGarbage : public PackageMatcher return (*Cache)[Pkg].Garbage; } }; + +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(); + } +}; } // namespace Patterns } // namespace Internal } // namespace APT diff --git a/doc/apt-patterns.7.xml b/doc/apt-patterns.7.xml index 327ea17d5..6058d7a74 100644 --- a/doc/apt-patterns.7.xml +++ b/doc/apt-patterns.7.xml @@ -78,6 +78,12 @@ ?name(REGEX) Selects packages where the name matches the given regular expression. + ?obsolete + Selects packages that no longer exist in repositories. + + ?upgradable + Selects packages that can be upgraded (have a newer candidate). + diff --git a/test/integration/test-apt-patterns b/test/integration/test-apt-patterns index 9a2c74f83..549089d8a 100755 --- a/test/integration/test-apt-patterns +++ b/test/integration/test-apt-patterns @@ -123,6 +123,16 @@ testsuccessequal "Listing... automatic1/now 1.0 i386 [installed,local] automatic2/now 1.0 i386 [installed,local]" apt list '?name(^automatic)' +testsuccessequal "Listing... +available/unstable 1.0 all +conf-only/now 1.0 i386 [residual-config] +foreign/unstable 2.0 amd64 +not-obsolete/unstable 2.0 i386 [upgradable from: 1.0]" apt list '?not(?obsolete)' + +testsuccessequal "Listing... +not-obsolete/unstable 2.0 i386 [upgradable from: 1.0] +N: There is 1 additional version. Please use the '-a' switch to see it" apt list '?upgradable' + testsuccessequal "Listing..." apt list '?x-name-fnmatch(1)' testsuccessequal "Listing... automatic1/now 1.0 i386 [installed,local] -- cgit v1.2.3-70-g09d2 From a3e9ab661b2d670f7d0da53a5f56c52af0763c5b Mon Sep 17 00:00:00 2001 From: Julian Andres Klode Date: Thu, 15 Aug 2019 13:31:30 +0200 Subject: Add ?config-files and ?installed patterns These two are mutually exclusive states of installed-ness. And ?installed package is fully unpacked and configured; a ?config-files package only has config files left. --- apt-pkg/cachefilter-patterns.cc | 4 ++++ apt-pkg/cachefilter-patterns.h | 19 +++++++++++++++++++ doc/apt-patterns.7.xml | 9 +++++++++ test/integration/test-apt-patterns | 18 ++++++++++++++++++ 4 files changed, 50 insertions(+) (limited to 'apt-pkg/cachefilter-patterns.h') diff --git a/apt-pkg/cachefilter-patterns.cc b/apt-pkg/cachefilter-patterns.cc index b97d65a03..0f23890b4 100644 --- a/apt-pkg/cachefilter-patterns.cc +++ b/apt-pkg/cachefilter-patterns.cc @@ -212,10 +212,14 @@ std::unique_ptr PatternParser::aPattern(std::unique_p return std::make_unique(aWord(node->arguments[0])); if (node->matches("?automatic", 0, 0)) return std::make_unique(file); + if (node->matches("?config-files", 0, 0)) + return std::make_unique(); if (node->matches("?false", 0, 0)) return std::make_unique(); if (node->matches("?garbage", 0, 0)) return std::make_unique(file); + if (node->matches("?installed", 0, 0)) + return std::make_unique(file); if (node->matches("?name", 1, 1)) return std::make_unique(aWord(node->arguments[0])); if (node->matches("?not", 1, 1)) diff --git a/apt-pkg/cachefilter-patterns.h b/apt-pkg/cachefilter-patterns.h index 68ad32f9a..7770385c9 100644 --- a/apt-pkg/cachefilter-patterns.h +++ b/apt-pkg/cachefilter-patterns.h @@ -128,6 +128,14 @@ struct PackageIsAutomatic : public PackageMatcher } }; +struct PackageIsConfigFiles : public PackageMatcher +{ + bool operator()(pkgCache::PkgIterator const &Pkg) override + { + return Pkg->CurrentState == pkgCache::State::ConfigFiles; + } +}; + struct PackageIsGarbage : public PackageMatcher { pkgCacheFile *Cache; @@ -139,6 +147,17 @@ struct PackageIsGarbage : public PackageMatcher } }; +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 diff --git a/doc/apt-patterns.7.xml b/doc/apt-patterns.7.xml index 6058d7a74..64dad4dd4 100644 --- a/doc/apt-patterns.7.xml +++ b/doc/apt-patterns.7.xml @@ -72,9 +72,15 @@ ?automatic Selects packages that were installed automatically. + ?config-files + Selects packages that are not fully installed, but have solely residual configuration files left. + ?garbage Selects packages that can be removed automatically. + ?installed + Selects packages that are currently installed. + ?name(REGEX) Selects packages where the name matches the given regular expression. @@ -93,6 +99,9 @@ apt remove ?garbage Remove all packages that are automatically installed and no longer needed - same as apt autoremove + apt purge ?config-files + Purge all packages that only have configuration files left + diff --git a/test/integration/test-apt-patterns b/test/integration/test-apt-patterns index 549089d8a..b84605256 100755 --- a/test/integration/test-apt-patterns +++ b/test/integration/test-apt-patterns @@ -116,9 +116,27 @@ testsuccessequal "Listing... automatic1/now 1.0 i386 [installed,local] automatic2/now 1.0 i386 [installed,local]" apt list '?automatic' +testsuccessequal "Listing... +conf-only/now 1.0 i386 [residual-config]" apt list '?config-files' + testsuccessequal "Listing... automatic2/now 1.0 i386 [installed,local]" apt list '?garbage' +testsuccessequal "Listing... +automatic1/now 1.0 i386 [installed,local] +automatic2/now 1.0 i386 [installed,local] +broken/now 1.0 i386 [installed,local] +dpkg/now 1.16.2+fake all [installed,local] +essential/now 1.0 i386 [installed,local] +manual1/now 1.0 i386 [installed,local] +manual2/now 1.0 i386 [installed,local] +not-obsolete/unstable 2.0 i386 [upgradable from: 1.0]" apt list '?installed' + +testsuccessequal "Listing... +available/unstable 1.0 all +conf-only/now 1.0 i386 [residual-config] +foreign/unstable 2.0 amd64" apt list '?not(?installed)' + testsuccessequal "Listing... automatic1/now 1.0 i386 [installed,local] automatic2/now 1.0 i386 [installed,local]" apt list '?name(^automatic)' -- cgit v1.2.3-70-g09d2 From 9282c9094ab3e77cebe90e97b2f9a5fc21463e2f Mon Sep 17 00:00:00 2001 From: Julian Andres Klode Date: Thu, 15 Aug 2019 13:32:54 +0200 Subject: Add ?broken pattern This matches all packages that have broken dependencies in the installed version or the version selected for install. --- apt-pkg/cachefilter-patterns.cc | 2 ++ apt-pkg/cachefilter-patterns.h | 12 ++++++++++++ doc/apt-patterns.7.xml | 3 +++ test/integration/test-apt-patterns | 3 +++ 4 files changed, 20 insertions(+) (limited to 'apt-pkg/cachefilter-patterns.h') diff --git a/apt-pkg/cachefilter-patterns.cc b/apt-pkg/cachefilter-patterns.cc index 0f23890b4..a180cf78e 100644 --- a/apt-pkg/cachefilter-patterns.cc +++ b/apt-pkg/cachefilter-patterns.cc @@ -212,6 +212,8 @@ std::unique_ptr PatternParser::aPattern(std::unique_p return std::make_unique(aWord(node->arguments[0])); if (node->matches("?automatic", 0, 0)) return std::make_unique(file); + if (node->matches("?broken", 0, 0)) + return std::make_unique(file); if (node->matches("?config-files", 0, 0)) return std::make_unique(); if (node->matches("?false", 0, 0)) diff --git a/apt-pkg/cachefilter-patterns.h b/apt-pkg/cachefilter-patterns.h index 7770385c9..c219e5bcd 100644 --- a/apt-pkg/cachefilter-patterns.h +++ b/apt-pkg/cachefilter-patterns.h @@ -128,6 +128,18 @@ struct PackageIsAutomatic : public PackageMatcher } }; +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 diff --git a/doc/apt-patterns.7.xml b/doc/apt-patterns.7.xml index 64dad4dd4..e1c64de8e 100644 --- a/doc/apt-patterns.7.xml +++ b/doc/apt-patterns.7.xml @@ -72,6 +72,9 @@ ?automatic Selects packages that were installed automatically. + ?broken + Selects packages that have broken dependencies. + ?config-files Selects packages that are not fully installed, but have solely residual configuration files left. diff --git a/test/integration/test-apt-patterns b/test/integration/test-apt-patterns index b84605256..e566c6bd2 100755 --- a/test/integration/test-apt-patterns +++ b/test/integration/test-apt-patterns @@ -116,6 +116,9 @@ testsuccessequal "Listing... automatic1/now 1.0 i386 [installed,local] automatic2/now 1.0 i386 [installed,local]" apt list '?automatic' +testsuccessequal "Listing... +broken/now 1.0 i386 [installed,local]" apt list '?broken' + testsuccessequal "Listing... conf-only/now 1.0 i386 [residual-config]" apt list '?config-files' -- cgit v1.2.3-70-g09d2 From 08762e0e4e2923360339eeb4d8ed26a00d7f1de5 Mon Sep 17 00:00:00 2001 From: Julian Andres Klode Date: Thu, 15 Aug 2019 13:35:01 +0200 Subject: Add ?essential pattern This matches all packages where at least one of the versions is marked essential; or well, whenver apt considers a package essential. --- apt-pkg/cachefilter-patterns.cc | 2 ++ apt-pkg/cachefilter-patterns.h | 7 +++++++ doc/apt-patterns.7.xml | 3 +++ test/integration/test-apt-patterns | 3 +++ 4 files changed, 15 insertions(+) (limited to 'apt-pkg/cachefilter-patterns.h') diff --git a/apt-pkg/cachefilter-patterns.cc b/apt-pkg/cachefilter-patterns.cc index a180cf78e..ea35b9c46 100644 --- a/apt-pkg/cachefilter-patterns.cc +++ b/apt-pkg/cachefilter-patterns.cc @@ -216,6 +216,8 @@ std::unique_ptr PatternParser::aPattern(std::unique_p return std::make_unique(file); if (node->matches("?config-files", 0, 0)) return std::make_unique(); + if (node->matches("?essential", 0, 0)) + return std::make_unique(); if (node->matches("?false", 0, 0)) return std::make_unique(); if (node->matches("?garbage", 0, 0)) diff --git a/apt-pkg/cachefilter-patterns.h b/apt-pkg/cachefilter-patterns.h index c219e5bcd..17f1fc752 100644 --- a/apt-pkg/cachefilter-patterns.h +++ b/apt-pkg/cachefilter-patterns.h @@ -158,6 +158,13 @@ struct PackageIsGarbage : public PackageMatcher return (*Cache)[Pkg].Garbage; } }; +struct PackageIsEssential : public PackageMatcher +{ + bool operator()(pkgCache::PkgIterator const &Pkg) override + { + return (Pkg->Flags & pkgCache::Flag::Essential) != 0; + } +}; struct PackageIsInstalled : public PackageMatcher { diff --git a/doc/apt-patterns.7.xml b/doc/apt-patterns.7.xml index e1c64de8e..11031be95 100644 --- a/doc/apt-patterns.7.xml +++ b/doc/apt-patterns.7.xml @@ -78,6 +78,9 @@ ?config-files Selects packages that are not fully installed, but have solely residual configuration files left. + ?essential + Selects packages that have Essential: yes set in their control file. + ?garbage Selects packages that can be removed automatically. diff --git a/test/integration/test-apt-patterns b/test/integration/test-apt-patterns index e566c6bd2..1efb10d2d 100755 --- a/test/integration/test-apt-patterns +++ b/test/integration/test-apt-patterns @@ -122,6 +122,9 @@ broken/now 1.0 i386 [installed,local]" apt list '?broken' testsuccessequal "Listing... conf-only/now 1.0 i386 [residual-config]" apt list '?config-files' +testsuccessequal "Listing... +essential/now 1.0 i386 [installed,local]" apt list '?essential' + testsuccessequal "Listing... automatic2/now 1.0 i386 [installed,local]" apt list '?garbage' -- cgit v1.2.3-70-g09d2 From af674d82d5cd36e22223ec49675d32adad07e0a9 Mon Sep 17 00:00:00 2001 From: Julian Andres Klode Date: Thu, 15 Aug 2019 13:36:04 +0200 Subject: Add the ?exact-name pattern The ?exact-name pattern matches the name exactly, there is no substring matching going on, or any regular expression or fnmatch magic. --- apt-pkg/cachefilter-patterns.cc | 2 ++ apt-pkg/cachefilter-patterns.h | 10 ++++++++++ doc/apt-patterns.7.xml | 3 +++ test/integration/test-apt-patterns | 4 ++++ 4 files changed, 19 insertions(+) (limited to 'apt-pkg/cachefilter-patterns.h') diff --git a/apt-pkg/cachefilter-patterns.cc b/apt-pkg/cachefilter-patterns.cc index ea35b9c46..f078924ff 100644 --- a/apt-pkg/cachefilter-patterns.cc +++ b/apt-pkg/cachefilter-patterns.cc @@ -218,6 +218,8 @@ std::unique_ptr PatternParser::aPattern(std::unique_p return std::make_unique(); if (node->matches("?essential", 0, 0)) return std::make_unique(); + if (node->matches("?exact-name", 1, 1)) + return std::make_unique(aWord(node->arguments[0])); if (node->matches("?false", 0, 0)) return std::make_unique(); if (node->matches("?garbage", 0, 0)) diff --git a/apt-pkg/cachefilter-patterns.h b/apt-pkg/cachefilter-patterns.h index 17f1fc752..993839425 100644 --- a/apt-pkg/cachefilter-patterns.h +++ b/apt-pkg/cachefilter-patterns.h @@ -166,6 +166,16 @@ struct PackageIsEssential : public PackageMatcher } }; +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; diff --git a/doc/apt-patterns.7.xml b/doc/apt-patterns.7.xml index 11031be95..2eda77b55 100644 --- a/doc/apt-patterns.7.xml +++ b/doc/apt-patterns.7.xml @@ -81,6 +81,9 @@ ?essential Selects packages that have Essential: yes set in their control file. + ?exact-name(NAME) + Selects packages with the exact specified name. + ?garbage Selects packages that can be removed automatically. diff --git a/test/integration/test-apt-patterns b/test/integration/test-apt-patterns index 1efb10d2d..c75793e55 100755 --- a/test/integration/test-apt-patterns +++ b/test/integration/test-apt-patterns @@ -125,6 +125,10 @@ conf-only/now 1.0 i386 [residual-config]" apt list '?config-files' testsuccessequal "Listing... essential/now 1.0 i386 [installed,local]" apt list '?essential' +testsuccessequal "Listing..." apt list '?exact-name(automatic)' +testsuccessequal "Listing... +automatic1/now 1.0 i386 [installed,local]" apt list '?exact-name(automatic1)' + testsuccessequal "Listing... automatic2/now 1.0 i386 [installed,local]" apt list '?garbage' -- cgit v1.2.3-70-g09d2 From c94b507b92b3d833761f3e416d4d5bba709bf87f Mon Sep 17 00:00:00 2001 From: Julian Andres Klode Date: Thu, 15 Aug 2019 13:40:31 +0200 Subject: Add ?virtual pattern This matches any package that does not have versions. --- apt-pkg/cachefilter-patterns.cc | 2 ++ apt-pkg/cachefilter-patterns.h | 8 ++++++++ doc/apt-patterns.7.xml | 5 +++++ test/integration/test-apt-patterns | 6 ++++++ 4 files changed, 21 insertions(+) (limited to 'apt-pkg/cachefilter-patterns.h') diff --git a/apt-pkg/cachefilter-patterns.cc b/apt-pkg/cachefilter-patterns.cc index f078924ff..986faaf52 100644 --- a/apt-pkg/cachefilter-patterns.cc +++ b/apt-pkg/cachefilter-patterns.cc @@ -236,6 +236,8 @@ std::unique_ptr PatternParser::aPattern(std::unique_p return std::make_unique(); if (node->matches("?upgradable", 0, 0)) return std::make_unique(file); + if (node->matches("?virtual", 0, 0)) + return std::make_unique(); if (node->matches("?x-name-fnmatch", 1, 1)) return std::make_unique(aWord(node->arguments[0])); diff --git a/apt-pkg/cachefilter-patterns.h b/apt-pkg/cachefilter-patterns.h index 993839425..d37da815f 100644 --- a/apt-pkg/cachefilter-patterns.h +++ b/apt-pkg/cachefilter-patterns.h @@ -221,6 +221,14 @@ struct PackageIsUpgradable : public PackageMatcher 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 diff --git a/doc/apt-patterns.7.xml b/doc/apt-patterns.7.xml index 2eda77b55..efd4293dc 100644 --- a/doc/apt-patterns.7.xml +++ b/doc/apt-patterns.7.xml @@ -99,6 +99,11 @@ ?upgradable Selects packages that can be upgraded (have a newer candidate). + ?virtual + Selects all virtual packages; that is packages without a version. + These exist when they are referenced somewhere in the archive, + for example because something depends on that name. + diff --git a/test/integration/test-apt-patterns b/test/integration/test-apt-patterns index c75793e55..92c76edd1 100755 --- a/test/integration/test-apt-patterns +++ b/test/integration/test-apt-patterns @@ -161,6 +161,12 @@ testsuccessequal "Listing... not-obsolete/unstable 2.0 i386 [upgradable from: 1.0] N: There is 1 additional version. Please use the '-a' switch to see it" apt list '?upgradable' +testsuccessequal "Package: does-not-exist +State: not a real package (virtual) +N: Can't select candidate version from package does-not-exist as it has no candidate +N: Can't select versions from package 'does-not-exist' as it is purely virtual +N: No packages found" apt show '?virtual' + testsuccessequal "Listing..." apt list '?x-name-fnmatch(1)' testsuccessequal "Listing... automatic1/now 1.0 i386 [installed,local] -- cgit v1.2.3-70-g09d2