diff options
Diffstat (limited to 'src/generic/apt/matchers.cc')
-rw-r--r-- | src/generic/apt/matchers.cc | 4343 |
1 files changed, 0 insertions, 4343 deletions
diff --git a/src/generic/apt/matchers.cc b/src/generic/apt/matchers.cc deleted file mode 100644 index a6f567be..00000000 --- a/src/generic/apt/matchers.cc +++ /dev/null @@ -1,4343 +0,0 @@ -// matchers.cc -// -// Copyright 2000-2005, 2007-2008 Daniel Burrows -// -// This program is free software; you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation; either version 2 of the License, or -// (at your option) any later version. -// -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with this program; see the file COPYING. If not, write to -// the Free Software Foundation, Inc., 59 Temple Place - Suite 330, -// Boston, MA 02111-1307, USA. -// -// Grammer for the condition language. (TODO: this is what the -// grammar *will* be, not what it is) -// -// CONDITION := CONDITION-LIST -// CONDITION-LIST := CONDITION-AND-GROUP '|' CONDITION-LIST -// | CONDITION-AND-GROUP -// CONDITION-AND-GROUP := CONDITION-ATOM CONDITION-AND-GROUP -// := CONDITION-ATOM -// CONDITION-ATOM := '(' CONDITION-LIST ')' -// | '!' CONDITION-ATOM -// | '?for' variable-name ':' CONDITION-LIST -// | '?=' variable-name -// | '?' (variable-name ':')? condition-name '(' arguments... ')' -// | '~'field-id <string> -// | <string> -// -// The (arguments...) to a ?function-style matcher are parsed -// according to their expected type. This is unfortunate but -// necessary: since arbitrary strings not containing metacharacters -// are legal condition values, distinguishing conditions from other -// argument types would require the user to type extra punctuation in, -// e.g., ?broken(Depends, ?name(apt.*)). - -#include "matchers.h" - -#include "apt.h" -#include "aptcache.h" -#include "tags.h" -#include "tasks.h" - -#include <aptitude.h> - -#include <generic/util/immset.h> -#include <generic/util/util.h> - -#include <cwidget/generic/util/ssprintf.h> -#include <cwidget/generic/util/transcode.h> - -#include <set> - -#include <stdarg.h> - -#include <apt-pkg/error.h> -#include <apt-pkg/pkgcache.h> -#include <apt-pkg/pkgrecords.h> -#include <apt-pkg/pkgsystem.h> -#include <apt-pkg/version.h> - -#include <cwidget/generic/util/eassert.h> -#include <ctype.h> -#include <stdio.h> -#include <regex.h> -#include <sys/types.h> - -using namespace std; -namespace cw = cwidget; - -namespace aptitude -{ -namespace matching -{ - -pkg_match_result::~pkg_match_result() -{ -} - -pkg_matcher::~pkg_matcher() -{ -} - -namespace -{ -// The condition this represents should never ever happen; this is -// just to provide a well-defined message in case it does so I can -// track it down. -class BadValueTypeException : public cw::util::Exception -{ - std::string msg; -public: - BadValueTypeException(int type, - const std::string &file, - int line_number) - : msg(ssprintf("%s:%d: Internal error: unknown value type %d.", - file.c_str(), line_number, type)) - { - } - - std::string errmsg() const { return msg; } -}; - -#define THROW_BAD_VALUE(type) throw BadValueTypeException((type), __FILE__, __LINE__) - -/** An object describing a matching rule. Note that we match on a - * particular version, not just on the package (this is because some - * attributes are pretty meaningless for only a package) - */ -class pkg_matcher_real : public pkg_matcher -{ -public: - class stack_value; - typedef std::vector<stack_value> match_stack; - - // The type of values stored on the match stack. - // - // This is basically a proto-value-type for the match language; if - // the match language grows into an implementation of the full - // lambda calculus, it will need to include more types (at the - // minimum, it'll need a type for matchers). - class stack_value - { - enum value_type - { - package_value, - version_value - }; - - value_type type; - pkgCache::PkgIterator pkg; - pkgCache::VerIterator ver; - - stack_value(value_type _type, const pkgCache::PkgIterator &_pkg, const pkgCache::VerIterator &_ver) - : type(_type), pkg(_pkg), ver(_ver) - { - } - - public: - static stack_value package(const pkgCache::PkgIterator &pkg) - { - return stack_value(package_value, pkg, pkgCache::VerIterator(*const_cast<pkgCache::PkgIterator &>(pkg).Cache())); - } - - static stack_value version(const pkgCache::PkgIterator &pkg, - const pkgCache::VerIterator &ver) - { - return stack_value(version_value, pkg, ver); - } - - /** \brief Return \b true if this value "matches" the - * given value. - * - * This relation is reflexive and symmetric, but not transitive. - * - * Packages match any of their versions or themselves, versions - * match themselves and their package. - */ - bool is_match_for(const stack_value &other) const - { - switch(type) - { - case package_value: - switch(other.type) - { - case package_value: - return pkg == other.pkg; - case version_value: - return pkg == other.pkg; - default: - THROW_BAD_VALUE(other.type); - } - case version_value: - switch(other.type) - { - case package_value: - return pkg == other.pkg; - case version_value: - return pkg == other.pkg && ver == other.ver; - default: - THROW_BAD_VALUE(other.type); - } - default: - THROW_BAD_VALUE(type); - } - } - - - bool visit_matches(pkg_matcher_real *matcher, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) const; - - pkg_match_result *visit_get_match(pkg_matcher_real *matcher, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) const; - }; - - virtual bool matches(const pkgCache::PkgIterator &pkg, - const pkgCache::VerIterator &ver, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack)=0; - - - - /** \return a match result, or \b NULL if there is no match. It's - * the caller's responsibility to delete this. - */ - virtual pkg_match_result *get_match(const pkgCache::PkgIterator &pkg, - const pkgCache::VerIterator &ver, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack)=0; - - /** See whether this matches a versionless package. This applies - * the matcher to every version of the package and returns \b true - * if any version is matched. - */ - virtual bool matches(const pkgCache::PkgIterator &pkg, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack); - - /** Get a match result for a versionless package. This applies the - * matcher to each version of the package, returning \b NULL if - * none matches or the first match found otherwise. - */ - virtual pkg_match_result *get_match(const pkgCache::PkgIterator &pkg, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack); -}; - -bool pkg_matcher_real::stack_value::visit_matches(pkg_matcher_real *matcher, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) const -{ - switch(type) - { - case package_value: - return matcher->matches(pkg, cache, records, stack); - case version_value: - return matcher->matches(pkg, ver, cache, records, stack); - default: - THROW_BAD_VALUE(type); - } -} - -pkg_match_result *pkg_matcher_real::stack_value::visit_get_match(pkg_matcher_real *matcher, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) const -{ - switch(type) - { - case package_value: - return matcher->get_match(pkg, cache, records, stack); - case version_value: - return matcher->get_match(pkg, ver, cache, records, stack); - default: - THROW_BAD_VALUE(type); - } -} - -typedef imm::map<std::string, int> parse_environment; - -pkg_matcher_real *parse_atom(string::const_iterator &start, - const string::const_iterator &end, - const vector<const char *> &terminators, - bool search_descriptions, - bool wide_context, - const parse_environment &name_context); - -pkg_matcher_real *parse_condition_list(string::const_iterator &start, - const string::const_iterator &end, - const vector<const char *> &terminators, - bool search_descriptions, - bool wide_context, - const parse_environment &name_context); - -/** Used to cleanly abort without having to contort the code. */ -class CompilationException -{ - string reason; -public: - CompilationException(const char *format, - ...) -#ifdef __GNUG__ - __attribute__ ((format (printf, 2, 3))) -#endif - { - va_list args; - - va_start(args, format); - - reason = cw::util::vssprintf(format, args); - } - - const string &get_msg() {return reason;} -}; - -namespace -{ - /** \brief Enumeration containing the known types of - * matchers. - * - * I want to have a table mapping matcher names to matcher types, - * and so the matcher type has to be POD. Well, I could use some - * indirection pattern like grouping policies do, but IMNSHO the - * payoff in grouping-policy land has not made up for the syntactic - * clutter and semantic overhead. I think that if anything it - * would be less valuable here. - * - * Note that matchers for dependencies and for broken dependencies - * are parsed separately below. - */ - enum matcher_type - { - matcher_type_action, - matcher_type_all, - matcher_type_and, - matcher_type_any, - matcher_type_archive, - matcher_type_automatic, - matcher_type_bind, - matcher_type_broken, - matcher_type_config_files, - matcher_type_description, - matcher_type_essential, - matcher_type_false, - matcher_type_for, - matcher_type_garbage, - matcher_type_installed, - matcher_type_maintainer, - matcher_type_name, - matcher_type_narrow, - matcher_type_new, - matcher_type_not, - matcher_type_obsolete, - matcher_type_or, - matcher_type_origin, - matcher_type_priority, - matcher_type_provides, - matcher_type_section, - matcher_type_source_package, - matcher_type_source_version, - matcher_type_tag, - matcher_type_task, - matcher_type_true, - matcher_type_upgradable, - matcher_type_user_tag, - matcher_type_version, - matcher_type_virtual, - matcher_type_widen - }; - - struct matcher_info - { - /** \brief The string used to pick the matcher. - */ - const char *name; - - /** \brief The matcher type indicated by this struct. */ - matcher_type type; - }; - - const matcher_info matcher_types[] = - { - { "action", matcher_type_action }, - { "all-versions", matcher_type_all }, - { "and", matcher_type_and }, - { "any-version", matcher_type_any }, - { "archive", matcher_type_archive }, - { "automatic", matcher_type_automatic }, - { "bind", matcher_type_bind }, - { "broken", matcher_type_broken }, - { "config-files", matcher_type_config_files }, - { "description", matcher_type_description }, - { "essential", matcher_type_essential }, - { "false", matcher_type_false }, - // ForTranslators: As in the sentence "for x = 5, do BLAH". - { "for", matcher_type_for }, - { "garbage", matcher_type_garbage }, - { "installed", matcher_type_installed }, - { "maintainer", matcher_type_maintainer }, - { "name", matcher_type_name }, - /* ForTranslators: Opposite of widen. Search for "widen" in this file for details. */ - { "narrow", matcher_type_narrow }, - { "new", matcher_type_new }, - { "not", matcher_type_not }, - { "obsolete", matcher_type_obsolete }, - { "or", matcher_type_or }, - /* ForTranslators: This specifies who is providing this archive. In the case of Debian the - string will read 'Debian'. Other providers may use their own string, such as - "Ubuntu" or "Xandros". */ - { "origin", matcher_type_origin }, - { "priority", matcher_type_priority }, - { "provides", matcher_type_provides }, - { "section", matcher_type_section }, - { "source-package", matcher_type_source_package }, - { "source-version", matcher_type_source_version }, - { "tag", matcher_type_tag }, - { "task", matcher_type_task }, - { "true", matcher_type_true }, - { "upgradable", matcher_type_upgradable }, - { "user-tag", matcher_type_user_tag }, - { "version", matcher_type_version }, - { "virtual", matcher_type_virtual }, - /* ForTranslators: Opposite of narrow. Search for "widen" in this file for details. */ - { "widen", matcher_type_widen } - }; -} - -bool pkg_matcher_real::matches(const pkgCache::PkgIterator &pkg, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) -{ - for(pkgCache::VerIterator v = pkg.VersionList(); - !v.end(); ++v) - if(matches(pkg, v, cache, records, stack)) - return true; - - if(pkg.VersionList().end()) - return matches(pkg, pkgCache::VerIterator(cache, 0), - cache, records, stack); - else - return false; -} - -pkg_match_result *pkg_matcher_real::get_match(const pkgCache::PkgIterator &pkg, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) -{ - pkg_match_result *rval = NULL; - - for(pkgCache::VerIterator v = pkg.VersionList(); - rval == NULL && !v.end(); ++v) - rval = get_match(pkg, v, cache, records, stack); - - if(pkg.VersionList().end()) - rval = get_match(pkg, pkgCache::VerIterator(cache, 0), - cache, records, stack); - - return rval; -} - -/** A common class to use when there's no interesting result. This is - * distinct from a match failure: an example would be the - * auto_matcher. - */ -class empty_match_result : public pkg_match_result -{ -public: - unsigned int num_groups() {return 0;} - - const string &group(unsigned int n) {abort();} -}; - -class pkg_nonstring_matcher : public pkg_matcher_real -{ -public: - pkg_match_result *get_match(const pkgCache::PkgIterator &pkg, - const pkgCache::VerIterator &ver, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) - { - return matches(pkg, ver, cache, records, stack) ? new empty_match_result : NULL; - } -}; - -class unitary_result : public pkg_match_result -{ - string s; -public: - unitary_result(const string &_s):s(_s) {} - - unsigned int num_groups() {return 1;} - - const string &group(unsigned int n) - { - if(n != 0) - abort(); - - return s; - } -}; - -class result_pair : public pkg_match_result -{ - pkg_match_result *r1, *r2; -public: - result_pair(pkg_match_result *_r1, - pkg_match_result *_r2) - :r1(_r1), r2(_r2) - { - } - - ~result_pair() - { - delete r1; - delete r2; - } - - unsigned int num_groups() - { - return r1->num_groups() + r2->num_groups(); - } - - const string &group(unsigned int n) - { - unsigned int num_groups_r1 = r1->num_groups(); - - if(n < num_groups_r1) - return r1->group(n); - else - return r2->group(n - num_groups_r1); - } -}; - -class pkg_string_matcher : public pkg_matcher_real -{ - regex_t pattern_nogroup; - regex_t pattern_group; - bool pattern_nogroup_initialized:1; - bool pattern_group_initialized:1; - - pkg_string_matcher() - { - } - - void do_compile(const string &_pattern, - regex_t &pattern, - int cflags) - { - int err=regcomp(&pattern, _pattern.c_str(), cflags); - if(err!=0) - { - size_t needed=regerror(err, &pattern, NULL, 0); - - auto_ptr<char> buf(new char[needed+1]); - - regerror(err, &pattern, buf.get(), needed+1); - - throw CompilationException("Regex compilation error: %s", buf.get()); - } - } - - void compile(const string &_pattern) - { - do_compile(_pattern, pattern_nogroup, REG_ICASE|REG_EXTENDED|REG_NOSUB); - pattern_nogroup_initialized=true; - - do_compile(_pattern, pattern_group, REG_ICASE|REG_EXTENDED); - pattern_group_initialized=true; - } - -public: - class match_result : public pkg_match_result - { - // well, it's pretty much necessary to copy all the groups anyway - // :( - vector<string> matches; - public: - match_result(const char *s, regmatch_t *pmatch, int matches_len) - { - for(int i=0; i<matches_len && pmatch[i].rm_so>=0; ++i) - { - matches.push_back(string()); - matches.back().assign(s, pmatch[i].rm_so, - pmatch[i].rm_eo-pmatch[i].rm_so); - } - } - - unsigned int num_groups() {return matches.size();} - const string &group(unsigned int n) {return matches[n];} - }; - - pkg_string_matcher (const string &_pattern) - :pattern_nogroup_initialized(false), - pattern_group_initialized(false) - { - // By convention, empty patterns match anything. (anything, you - // hear me??) That allows you to put "~m" into the pattern - // grouping policy and get a by-maintainer grouping out. - if(_pattern.empty()) - compile(".*"); - else - compile(_pattern); - } - - ~pkg_string_matcher() - { - if(pattern_nogroup_initialized) - regfree(&pattern_nogroup); - if(pattern_group_initialized) - regfree(&pattern_group); - } - - bool string_matches(const char *s) - { - return !regexec(&pattern_nogroup, s, 0, NULL, 0); - } - - match_result *get_string_match(const char *s) - { - // ew. You need a hard limit here. - regmatch_t matches[30]; - - bool matched = (regexec(&pattern_group, s, - sizeof(matches)/sizeof(regmatch_t), - matches, 0) == 0); - - if(!matched) - return NULL; - else - return new match_result(s, matches, - sizeof(matches)/sizeof(regmatch_t)); - } -}; - -typedef pair<bool, std::string> match_target; - -class pkg_trivial_string_matcher : public pkg_string_matcher -{ -public: - pkg_trivial_string_matcher (const string &s) : pkg_string_matcher(s) - { - } - - // If the first element is false, the match fails; otherwise, it - // proceeds using the second element. - virtual match_target val(const pkgCache::PkgIterator &pkg, - const pkgCache::VerIterator &ver, - aptitudeDepCache &cache, - pkgRecords &records)=0; - - bool matches(const pkgCache::PkgIterator &pkg, - const pkgCache::VerIterator &ver, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) - { - match_target v = val(pkg, ver, cache, records); - - if(!v.first) - return false; - else - return string_matches(v.second.c_str()); - } - - pkg_match_result *get_match(const pkgCache::PkgIterator &pkg, - const pkgCache::VerIterator &ver, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) - { - match_target v = val(pkg, ver, cache, records); - - if(!v.first) - return NULL; - else - return get_string_match(v.second.c_str()); - } -}; - -class pkg_name_matcher:public pkg_trivial_string_matcher -{ -public: - pkg_name_matcher(const string &s):pkg_trivial_string_matcher(s) {} - - match_target val(const pkgCache::PkgIterator &pkg, - const pkgCache::VerIterator &ver, - aptitudeDepCache &cache, - pkgRecords &records) - { - return match_target(true, pkg.Name()); - } -}; - -class pkg_description_matcher:public pkg_trivial_string_matcher -{ -public: - pkg_description_matcher(const string &s):pkg_trivial_string_matcher(s) {} - - match_target val(const pkgCache::PkgIterator &pkg, - const pkgCache::VerIterator &ver, - aptitudeDepCache &cache, - pkgRecords &records) - { - if(ver.end()) - return match_target(false, ""); - else - return match_target(true, cw::util::transcode(get_long_description(ver, &records).c_str())); - } -}; - -class pkg_maintainer_matcher : public pkg_trivial_string_matcher -{ -public: - pkg_maintainer_matcher(const string &s):pkg_trivial_string_matcher(s) - { - } - - match_target val(const pkgCache::PkgIterator &pkg, - const pkgCache::VerIterator &ver, - aptitudeDepCache &cache, - pkgRecords &records) - { - if(ver.end()) - return match_target(false, ""); - else - return match_target(true, records.Lookup(ver.FileList()).Maintainer().c_str()); - } -}; - -class pkg_section_matcher : public pkg_trivial_string_matcher -{ -public: - pkg_section_matcher(const string &s):pkg_trivial_string_matcher(s) - { - } - - match_target val(const pkgCache::PkgIterator &pkg, - const pkgCache::VerIterator &ver, - aptitudeDepCache &cache, - pkgRecords &records) - { - if(ver.end()) - return match_target(false, ""); - - const char *s=ver.Section(); - - if(!s) - return match_target(false, ""); - - return match_target(true, s); - } -}; - -class pkg_version_matcher : public pkg_trivial_string_matcher -{ -public: - pkg_version_matcher(const string &s) : pkg_trivial_string_matcher(s) - { - } - - match_target val(const pkgCache::PkgIterator &pkg, - const pkgCache::VerIterator &ver, - aptitudeDepCache &cache, - pkgRecords &records) - { - if(ver.end()) - return match_target(false, ""); - - const char *s=ver.VerStr(); - - if(!s) - return match_target(false, ""); - - return match_target(true, s); - } -}; - -// NOTE: pkg_current_version_matcher, pkg_inst_version_matcher, and -// pkg_cand_version_matcher are all a bit inefficient since they loop -// over all versions when they only match one; if they become a -// performance problem (unlikely), you could (carefully!!) implement -// the version-agnostic match variants to speed things up. -class pkg_curr_version_matcher : public pkg_matcher_real -{ -public: - bool matches(const pkgCache::PkgIterator &pkg, - const pkgCache::VerIterator &ver, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) - { - return !ver.end() && ver == pkg.CurrentVer(); - } - - pkg_match_result *get_match(const pkgCache::PkgIterator &pkg, - const pkgCache::VerIterator &ver, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) - { - if(matches(pkg, ver, cache, records, stack)) - return new unitary_result(ver.VerStr()); - else - return NULL; - } -}; - -class pkg_cand_version_matcher : public pkg_matcher_real -{ -public: - bool matches(const pkgCache::PkgIterator &pkg, - const pkgCache::VerIterator &ver, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) - { - return !ver.end() && - ver == cache[pkg].CandidateVerIter(cache); - } - - pkg_match_result *get_match(const pkgCache::PkgIterator &pkg, - const pkgCache::VerIterator &ver, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) - { - if(matches(pkg, ver, cache, records, stack)) - return new unitary_result(ver.VerStr()); - else - return NULL; - } -}; - -class pkg_inst_version_matcher : public pkg_matcher_real -{ -public: - bool matches(const pkgCache::PkgIterator &pkg, - const pkgCache::VerIterator &ver, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) - { - return !ver.end() && - ver == cache[pkg].InstVerIter(cache); - } - - pkg_match_result *get_match(const pkgCache::PkgIterator &pkg, - const pkgCache::VerIterator &ver, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) - { - if(matches(pkg, ver, cache, records, stack)) - return new unitary_result(ver.VerStr()); - else - return NULL; - } -}; - -pkg_matcher_real *make_package_version_matcher(const string &substr) -{ - if(substr == "CURRENT") - return new pkg_curr_version_matcher; - else if(substr == "TARGET") - return new pkg_inst_version_matcher; - else if(substr == "CANDIDATE") - return new pkg_cand_version_matcher; - else - return new pkg_version_matcher(substr); -} - -class pkg_task_matcher : public pkg_string_matcher -{ -public: - pkg_task_matcher(const string &s) : pkg_string_matcher(s) - { - } - - bool matches(const pkgCache::PkgIterator &pkg, - const pkgCache::VerIterator &v, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) - { - set<string> *l = get_tasks(pkg); - - if(!l) - return false; - - for(set<string>::iterator i=l->begin(); - i!=l->end(); - ++i) - if(string_matches(i->c_str())) - return true; - - return false; - } - - // Uses the fact that the result returns NULL <=> nothing matched - pkg_match_result *get_match(const pkgCache::PkgIterator &pkg, - const pkgCache::VerIterator &ver, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) - { - set<string> *l=get_tasks(pkg); - - if(!l) - return NULL; - - for(set<string>::iterator i=l->begin(); - i!=l->end(); - ++i) - { - pkg_match_result *r=get_string_match(i->c_str()); - - if(r != NULL) - return r; - } - - return NULL; - } -}; - -class pkg_tag_matcher : public pkg_string_matcher -{ -public: - pkg_tag_matcher(const string &s) - : pkg_string_matcher(s) - { - } - - bool matches(const pkgCache::PkgIterator &pkg, - const pkgCache::VerIterator &ver, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) - { -#ifdef HAVE_EPT - typedef ept::debtags::Tag tag; - using aptitude::apt::get_tags; -#endif - -#ifdef HAVE_EPT - const std::set<tag> realTags(get_tags(pkg)); - const std::set<tag> * const tags(&realTags); -#else - const std::set<tag> * const tags(get_tags(pkg)); -#endif - - if(tags == NULL) - return false; - - for(std::set<tag>::const_iterator i=tags->begin(); i!=tags->end(); ++i) - { -#ifdef HAVE_EPT - std::string name(i->fullname()); -#else - const std::string name = i->str().c_str(); -#endif - if(string_matches(name.c_str())) - return true; - } - - return false; - } - - pkg_match_result *get_match(const pkgCache::PkgIterator &pkg, - const pkgCache::VerIterator &ver, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) - { -#ifdef HAVE_EPT - typedef ept::debtags::Tag tag; - using aptitude::apt::get_tags; -#endif - -#ifdef HAVE_EPT - const set<tag> realTags(get_tags(pkg)); - const set<tag> * const tags(&realTags); -#else - const set<tag> * const tags(get_tags(pkg)); -#endif - - if(tags == NULL) - return NULL; - - for(set<tag>::const_iterator i=tags->begin(); i!=tags->end(); ++i) - { -#ifdef HAVE_EPT - std::string name(i->fullname()); -#else - const std::string name = i->str().c_str(); -#endif - - pkg_match_result *res = get_string_match(name.c_str()); - if(res != NULL) - return res; - } - - return NULL; - } -}; - -class pkg_user_tag_matcher : public pkg_string_matcher -{ - std::map<aptitudeDepCache::user_tag, - match_result *> cached_matches; - - match_result *noncopy_get_match(const pkgCache::PkgIterator &pkg, - aptitudeDepCache &cache) - { - const std::set<aptitudeDepCache::user_tag> &user_tags = - cache.get_ext_state(pkg).user_tags; - - for(std::set<aptitudeDepCache::user_tag>::const_iterator it = - user_tags.begin(); it != user_tags.end(); ++it) - { - std::map<aptitudeDepCache::user_tag, match_result *>::const_iterator - found = cached_matches.find(*it); - - if(found != cached_matches.end() && found->second != NULL) - return found->second; - - match_result *result = get_string_match(cache.deref_user_tag(*it).c_str()); - cached_matches[*it] = result; - if(result != NULL) - return result; - } - - return NULL; - } -public: - pkg_user_tag_matcher(const std::string &s) - : pkg_string_matcher(s) - - { - } - - ~pkg_user_tag_matcher() - { - for(std::map<aptitudeDepCache::user_tag, match_result *>::const_iterator it - = cached_matches.begin(); it != cached_matches.end(); ++it) - delete it->second; - } - - bool matches(const pkgCache::PkgIterator &pkg, - const pkgCache::VerIterator &ver, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) - { - return noncopy_get_match(pkg, cache) != NULL; - } - - pkg_match_result *get_match(const pkgCache::PkgIterator &pkg, - const pkgCache::VerIterator &ver, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) - { - match_result *rval = noncopy_get_match(pkg, cache); - if(rval == NULL) - return NULL; - else - return new match_result(*rval); - } -}; - -// Package-file info matchers. Match a package if any of its -// available files (for all versions) match the given criteria. -// -// Should I use templates? -class pkg_origin_matcher : public pkg_string_matcher -{ -public: - pkg_origin_matcher(const string &s) : pkg_string_matcher(s) - { - } - - bool matches(const pkgCache::PkgIterator &pkg, - const pkgCache::VerIterator &ver, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) - { - if(ver.end()) - return false; - - for(pkgCache::VerFileIterator f = ver.FileList(); !f.end(); ++f) - { - pkgCache::PkgFileIterator cur = f.File(); - - if(!cur.end() && cur.Origin() && string_matches(cur.Origin())) - return true; - } - - return false; - } - - pkg_match_result *get_match(const pkgCache::PkgIterator &pkg, - const pkgCache::VerIterator &ver, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) - { - if(ver.end()) - return NULL; - - for(pkgCache::VerFileIterator f = ver.FileList(); !f.end(); ++f) - { - pkgCache::PkgFileIterator cur = f.File(); - - if(!cur.end() && cur.Origin()) - { - pkg_match_result *r = get_string_match(cur.Origin()); - - if(r != NULL) - return r; - } - } - - return NULL; - } -}; - -class pkg_archive_matcher : public pkg_string_matcher -{ -public: - pkg_archive_matcher(const string &s) : pkg_string_matcher(s) - { - } - - bool matches(const pkgCache::PkgIterator &pkg, - const pkgCache::VerIterator &ver, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) - { - if(ver.end() || ver.FileList().end()) - return false; - - for(pkgCache::VerFileIterator f = ver.FileList(); !f.end(); ++f) - { - pkgCache::PkgFileIterator cur = f.File(); - - if(!cur.end() && cur.Archive() && string_matches(cur.Archive())) - return true; - } - - return false; - } - - pkg_match_result *get_match(const pkgCache::PkgIterator &pkg, - const pkgCache::VerIterator &ver, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) - { - if(ver.end() || ver.FileList().end()) - return NULL; - - for(pkgCache::VerFileIterator f = ver.FileList(); !f.end(); ++f) - { - pkgCache::PkgFileIterator cur = f.File(); - - if(!cur.end() && cur.Archive()) - { - pkg_match_result *r = get_string_match(cur.Archive()); - - if(r != NULL) - return r; - } - } - - return NULL; - } -}; - -class pkg_source_package_matcher : public pkg_string_matcher -{ -public: - pkg_source_package_matcher(const string &s) : pkg_string_matcher(s) - { - } - - bool matches(const pkgCache::PkgIterator &pkg, - const pkgCache::VerIterator &ver, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) - { - if(ver.end() || ver.FileList().end()) - return false; - - bool checked_real_package = false; - for(pkgCache::VerFileIterator f = ver.FileList(); !f.end(); ++f) - { - pkgRecords::Parser &p = records.Lookup(f); - - if(p.SourcePkg().empty()) - { - if(!checked_real_package) - { - if(string_matches(pkg.Name())) - return true; - } - } - else if(string_matches(p.SourcePkg().c_str())) - return true; - } - - return false; - } - - pkg_match_result *get_match(const pkgCache::PkgIterator &pkg, - const pkgCache::VerIterator &ver, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) - { - if(ver.end() || ver.FileList().end()) - return false; - - bool checked_real_package = false; - for(pkgCache::VerFileIterator f = ver.FileList(); !f.end(); ++f) - { - pkgRecords::Parser &p = records.Lookup(f); - - if(p.SourcePkg().empty()) - { - if(!checked_real_package) - { - pkg_match_result *r = get_string_match(pkg.Name()); - - if(r != NULL) - return r; - } - } - else - { - pkg_match_result *r = get_string_match(p.SourcePkg().c_str()); - - if(r != NULL) - return r; - } - } - - return NULL; - } -}; - -class pkg_source_version_matcher : public pkg_string_matcher -{ -public: - pkg_source_version_matcher(const string &s) : pkg_string_matcher(s) - { - } - - bool matches(const pkgCache::PkgIterator &pkg, - const pkgCache::VerIterator &ver, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) - { - if(ver.end() || ver.FileList().end()) - return false; - - bool checked_real_package = false; - for(pkgCache::VerFileIterator f = ver.FileList(); !f.end(); ++f) - { - pkgRecords::Parser &p = records.Lookup(f); - - if(p.SourceVer().empty()) - { - if(!checked_real_package) - { - if(string_matches(ver.VerStr())) - return true; - } - } - else if(string_matches(p.SourceVer().c_str())) - return true; - } - - return false; - } - - pkg_match_result *get_match(const pkgCache::PkgIterator &pkg, - const pkgCache::VerIterator &ver, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) - { - if(ver.end() || ver.FileList().end()) - return false; - - bool checked_real_package = false; - for(pkgCache::VerFileIterator f = ver.FileList(); !f.end(); ++f) - { - pkgRecords::Parser &p = records.Lookup(f); - - if(p.SourceVer().empty()) - { - if(!checked_real_package) - { - pkg_match_result *r = get_string_match(ver.VerStr()); - - if(r != NULL) - return r; - } - } - else - { - pkg_match_result *r = get_string_match(p.SourceVer().c_str()); - - if(r != NULL) - return r; - } - } - - return NULL; - } -}; - -class pkg_auto_matcher:public pkg_matcher_real -{ -public: - bool matches(const pkgCache::PkgIterator &pkg, - const pkgCache::VerIterator &ver, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) - { - return - (!pkg.CurrentVer().end() || cache[pkg].Install()) && - (cache[pkg].Flags & pkgCache::Flag::Auto); - } - - pkg_match_result *get_match(const pkgCache::PkgIterator &pkg, - const pkgCache::VerIterator &ver, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) - { - return matches(pkg, ver, cache, records, stack) - ? new unitary_result(_("Automatically Installed")) : NULL; - } -}; - -class pkg_broken_matcher:public pkg_matcher_real -{ -public: - bool matches(const pkgCache::PkgIterator &pkg, - const pkgCache::VerIterator &ver, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) - { - if(ver.end()) - return false; - else - { - aptitudeDepCache::StateCache &state = cache[pkg]; - return state.NowBroken() || state.InstBroken(); - } - } - - pkg_match_result *get_match(const pkgCache::PkgIterator &pkg, - const pkgCache::VerIterator &ver, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) - { - return matches(pkg, ver, cache, records, stack) ? new unitary_result(_("Broken")) : NULL; - } -}; - -class pkg_priority_matcher:public pkg_matcher_real -{ - pkgCache::State::VerPriority type; -public: - pkg_priority_matcher(pkgCache::State::VerPriority _type) - :type(_type) {} - - bool matches(const pkgCache::PkgIterator &pkg, - const pkgCache::VerIterator &ver, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) - { - if(ver.end()) - return false; - else - return ver->Priority == type; - } - - pkg_match_result *get_match(const pkgCache::PkgIterator &pkg, - const pkgCache::VerIterator &ver, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) - { - if(ver.end()) - return NULL; - else if(ver->Priority != type) - return NULL; - else - return new unitary_result(const_cast<pkgCache::VerIterator &>(ver).PriorityType()); - } -}; - -pkg_match_result *dep_match(const pkgCache::DepIterator &dep) -{ - string realization; - - pkgCache::DepIterator start, end; - - surrounding_or(dep, start, end); - - bool first = true; - - while(start != end) - { - if(!first) - realization += " | "; - - first = false; - - realization += start.TargetPkg().Name(); - - if(start.TargetVer()) - { - realization += " ("; - realization += start.CompType(); - realization += " "; - realization += start.TargetVer(); - realization += ")"; - } - - ++start; - } - - // erm... - return new result_pair(new unitary_result(const_cast<pkgCache::DepIterator &>(dep).DepType()), - new unitary_result(realization)); -} - -// Matches packages with unmet dependencies of a particular type. -class pkg_broken_type_matcher:public pkg_matcher_real -{ - pkgCache::Dep::DepType type; // Which type to match -public: - pkg_broken_type_matcher(pkgCache::Dep::DepType _type) - :type(_type) {} - - bool matches(const pkgCache::PkgIterator &pkg, - const pkgCache::VerIterator &ver, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) - { - if(ver.end()) - return false; - else - { - pkgCache::DepIterator dep=ver.DependsList(); - - while(!dep.end()) - { - // Skip to the end of the Or group to check GInstall - while(dep->CompareOp & pkgCache::Dep::Or) - ++dep; - - if(dep->Type==type && - !(cache[dep] & pkgDepCache::DepGInstall)) - // Oops, it's broken.. - return true; - - ++dep; - } - } - return false; - } - - pkg_match_result * get_match(const pkgCache::PkgIterator &pkg, - const pkgCache::VerIterator &ver, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) - { - if(ver.end()) - return NULL; - else - { - pkgCache::DepIterator dep=ver.DependsList(); - - while(!dep.end()) - { - // Skip to the end of the Or group to check GInstall - while(dep->CompareOp & pkgCache::Dep::Or) - ++dep; - - if(dep->Type==type && - !(cache[dep] & pkgDepCache::DepGInstall)) - // Oops, it's broken.. - return dep_match(dep); - - ++dep; - } - } - - return NULL; - } -}; - -// This matches packages based on the action that will be taken with them. -// -// It will treat a request for a non-auto type as also being a request -// for the auto type. -class pkg_action_matcher:public pkg_matcher_real -{ - pkg_action_state type; - bool require_purge; -public: - pkg_action_matcher(pkg_action_state _type, bool _require_purge) - :type(_type), require_purge(_require_purge) - { - } - - bool matches(const pkgCache::PkgIterator &pkg, - const pkgCache::VerIterator &ver, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) - { - if(require_purge && - (cache[pkg].iFlags & pkgDepCache::Purge) == 0) - return false; - else - { - switch(type) - { - case pkg_install: - { - pkg_action_state thetype = find_pkg_state(pkg, cache); - return thetype==pkg_install || thetype==pkg_auto_install; - } - case pkg_hold: - return !pkg.CurrentVer().end() && cache.get_ext_state(pkg).selection_state == pkgCache::State::Hold; - case pkg_remove: - { - pkg_action_state thetype = find_pkg_state(pkg, cache); - - return thetype==pkg_remove || thetype==pkg_auto_remove || - thetype==pkg_unused_remove; - } - default: - { - pkg_action_state thetype = find_pkg_state(pkg, cache); - - return thetype==type; - } - } - } - } - - pkg_match_result *get_match(const pkgCache::PkgIterator &pkg, - const pkgCache::VerIterator &ver, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) - { - if(!matches(pkg, ver, cache, records, stack)) - return NULL; - else - switch(type) - { - case pkg_unchanged: // shouldn't happen (?) - return new unitary_result(_("Unchanged")); - case pkg_broken: - return new unitary_result(_("Broken")); - case pkg_unused_remove: - return new unitary_result(_("Remove [unused]")); - case pkg_auto_hold: - return new unitary_result(_("Hold [auto]")); - case pkg_auto_install: - return new unitary_result(_("Install [auto]")); - case pkg_auto_remove: - return new unitary_result(_("Remove [auto]")); - case pkg_downgrade: - return new unitary_result(_("Downgrade")); - case pkg_hold: - return new unitary_result(_("Hold")); - case pkg_reinstall: - return new unitary_result(_("Reinstall")); - case pkg_install: - return new unitary_result(_("Install")); - case pkg_remove: - return new unitary_result(_("Remove")); - case pkg_upgrade: - return new unitary_result(_("Upgrade")); - default: - // should never happen. - abort(); - } - } -}; - -class pkg_keep_matcher:public pkg_matcher_real -{ - bool matches(const pkgCache::PkgIterator &pkg, - const pkgCache::VerIterator &ver, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) - { - return cache[pkg].Keep(); - } - - pkg_match_result *get_match(const pkgCache::PkgIterator &pkg, - const pkgCache::VerIterator &ver, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) - { - if(cache[pkg].Keep()) - return new unitary_result(_("Keep")); - else - return NULL; - } -}; - -/** Matches package versions that are not associated with a 'real' - * package. Applied to a whole package, this matches virtual - * packages; it also matches package versions corresponding to - * removing a package. - */ -class pkg_virtual_matcher:public pkg_matcher_real -{ -public: - bool matches(const pkgCache::PkgIterator &pkg, - const pkgCache::VerIterator &ver, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) - { - return ver.end(); - } - - pkg_match_result *get_match(const pkgCache::PkgIterator &pkg, - const pkgCache::VerIterator &ver, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) - { - if(!ver.end()) - return NULL; - else - return new unitary_result(_("Virtual")); - } -}; - -/** Matches the currently installed version of a package. - */ -class pkg_installed_matcher:public pkg_matcher_real -{ -public: - bool matches(const pkgCache::PkgIterator &pkg, - const pkgCache::VerIterator &ver, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) - { - return !pkg.CurrentVer().end() && ver == pkg.CurrentVer(); - } - - pkg_match_result *get_match(const pkgCache::PkgIterator &pkg, - const pkgCache::VerIterator &ver, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) - { - if(pkg.CurrentVer().end() || ver != pkg.CurrentVer()) - return NULL; - else - return new unitary_result(_("Installed")); - } -}; - -class pkg_essential_matcher:public pkg_matcher_real -// Matches essential packages -{ -public: - bool matches(const pkgCache::PkgIterator &pkg, - const pkgCache::VerIterator &ver, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) - { - return - (pkg->Flags&pkgCache::Flag::Essential)==pkgCache::Flag::Essential || - (pkg->Flags&pkgCache::Flag::Important)==pkgCache::Flag::Important; - } - - pkg_match_result *get_match(const pkgCache::PkgIterator &pkg, - const pkgCache::VerIterator &ver, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) - { - if(!matches(pkg, ver, cache, records, stack)) - return NULL; - else - return new unitary_result(_("Essential")); - } -}; - -class pkg_configfiles_matcher:public pkg_matcher_real -// Matches a package which was removed but has config files remaining -{ -public: - bool matches(const pkgCache::PkgIterator &pkg, - const pkgCache::VerIterator &ver, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) - { - return pkg->CurrentState==pkgCache::State::ConfigFiles; - } - - // ??? - pkg_match_result *get_match(const pkgCache::PkgIterator &pkg, - const pkgCache::VerIterator &ver, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) - { - if(pkg->CurrentState == pkgCache::State::ConfigFiles) - return new unitary_result(_("Config Files Remain")); - else - return NULL; - } -}; - -// Matches packages with a dependency on the given pattern. -class pkg_dep_matcher:public pkg_matcher_real -{ -private: - pkg_matcher_real *pattern; - pkgCache::Dep::DepType type; - - /** If \b true, only broken dependencies will be matched. */ - bool broken; - -public: - pkg_dep_matcher(pkgCache::Dep::DepType _type, - pkg_matcher_real *_pattern, - bool _broken) - :pattern(_pattern), type(_type), broken(_broken) - { - } - ~pkg_dep_matcher() {delete pattern;} - - bool matches(const pkgCache::PkgIterator &pkg, - const pkgCache::VerIterator &ver, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) - { - eassert(!pkg.end()); - if(ver.end()) - return false; - - for(pkgCache::DepIterator dep=ver.DependsList(); !dep.end(); ++dep) - { - if( (type == dep->Type) || - (type == pkgCache::Dep::Depends && - dep->Type == pkgCache::Dep::PreDepends)) - { - if(broken) - { - pkgCache::DepIterator d2(cache, &*dep); - while(d2->CompareOp & pkgCache::Dep::Or) - ++d2; - if(cache[d2] & pkgDepCache::DepGInstall) - continue; - } - - // See if a versionless match works,. - if(dep.TargetPkg().VersionList().end() && - pattern->matches(dep.TargetPkg(), dep.TargetPkg().VersionList(), cache, records, stack)) - return true; - - for(pkgCache::VerIterator i=dep.TargetPkg().VersionList(); !i.end(); i++) - if(_system->VS->CheckDep(i.VerStr(), dep->CompareOp, dep.TargetVer())) - { - if(pattern->matches(dep.TargetPkg(), i, cache, records, stack)) - return true; - } - } - } - - return false; - } - - pkg_match_result *get_match(const pkgCache::PkgIterator &pkg, - const pkgCache::VerIterator &ver, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) - { - eassert(!pkg.end()); - if(ver.end()) - return NULL; - - for(pkgCache::DepIterator dep=ver.DependsList(); !dep.end(); ++dep) - { - if( (type == dep->Type) || - (type == pkgCache::Dep::Depends && - dep->Type == pkgCache::Dep::PreDepends)) - { - if(broken) - { - pkgCache::DepIterator d2(cache, &*dep); - while(d2->CompareOp & pkgCache::Dep::Or) - ++d2; - if(cache[d2] & pkgDepCache::DepGInstall) - continue; - } - - // See if a versionless match works,. - if(dep.TargetPkg().VersionList().end()) - { - pkg_match_result *r = pattern->get_match(dep.TargetPkg(), dep.TargetPkg().VersionList(), cache, records, stack); - - if(r) - return new result_pair(r, dep_match(dep)); - } - - for(pkgCache::VerIterator i=dep.TargetPkg().VersionList(); !i.end(); i++) - if(_system->VS->CheckDep(i.VerStr(), dep->CompareOp, dep.TargetVer())) - { - pkg_match_result *r = pattern->get_match(dep.TargetPkg(), i, cache, records, stack); - - if(r) - return new result_pair(r, dep_match(dep)); - } - } - } - - return false; - } -}; - -class pkg_or_matcher:public pkg_matcher_real -{ - pkg_matcher_real *left,*right; -public: - pkg_or_matcher(pkg_matcher_real *_left, pkg_matcher_real *_right) - :left(_left),right(_right) - { - } - - bool matches(const pkgCache::PkgIterator &pkg, - const pkgCache::VerIterator &ver, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) - { - return left->matches(pkg, ver, cache, records, stack) || - right->matches(pkg, ver, cache, records, stack); - } - - bool matches(const pkgCache::PkgIterator &pkg, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) - { - return left->matches(pkg, cache, records, stack) || - right->matches(pkg, cache, records, stack); - } - - pkg_match_result *get_match(const pkgCache::PkgIterator &pkg, - const pkgCache::VerIterator &ver, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) - { - pkg_match_result *lr = left->get_match(pkg, ver, cache, records, stack); - - if(lr != NULL) - return lr; - else - return right->get_match(pkg, ver, cache, records, stack); - } - - pkg_match_result *get_match(const pkgCache::PkgIterator &pkg, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) - { - pkg_match_result *lr = left->get_match(pkg, cache, records, stack); - - if(lr != NULL) - return lr; - else - return right->get_match(pkg, cache, records, stack); - } - - ~pkg_or_matcher() - { - delete left; - delete right; - } -}; - -class pkg_and_matcher:public pkg_matcher_real -{ - pkg_matcher_real *left,*right; -public: - pkg_and_matcher(pkg_matcher_real *_left, pkg_matcher_real *_right) - :left(_left),right(_right) - { - } - - bool matches(const pkgCache::PkgIterator &pkg, - const pkgCache::VerIterator &ver, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) - { - return left->matches(pkg, ver, cache, records, stack) && - right->matches(pkg, ver, cache, records, stack); - } - - bool matches(const pkgCache::PkgIterator &pkg, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) - { - return left->matches(pkg, cache, records, stack) && - right->matches(pkg, cache, records, stack); - } - - pkg_match_result *get_match(const pkgCache::PkgIterator &pkg, - const pkgCache::VerIterator &ver, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) - { - pkg_match_result *r1 = left->get_match(pkg, ver, cache, records, stack); - - if(r1 == NULL) - return NULL; - - pkg_match_result *r2 = right->get_match(pkg, ver, cache, records, stack); - - if(r2 == NULL) - { - delete r1; - return NULL; - } - - return new result_pair(r1, r2); - } - - pkg_match_result *get_match(const pkgCache::PkgIterator &pkg, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) - { - pkg_match_result *r1 = left->get_match(pkg, cache, records, stack); - - if(r1 == NULL) - return NULL; - - pkg_match_result *r2 = right->get_match(pkg, cache, records, stack); - - if(r2 == NULL) - { - delete r1; - return NULL; - } - - return new result_pair(r1, r2); - } - - ~pkg_and_matcher() - { - delete left; - delete right; - } -}; - -class pkg_not_matcher:public pkg_matcher_real -{ - pkg_matcher_real *child; -public: - pkg_not_matcher(pkg_matcher_real *_child) - :child(_child) - { - } - - bool matches(const pkgCache::PkgIterator &pkg, - const pkgCache::VerIterator &ver, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) - { - return !child->matches(pkg, ver, cache, records, stack); - } - - bool matches(const pkgCache::PkgIterator &pkg, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) - { - return !child->matches(pkg, cache, records, stack); - } - - // Eh, there isn't really a good choice about what to return here... - // just return an empty result if the child doesn't match. - pkg_match_result *get_match(const pkgCache::PkgIterator &pkg, - const pkgCache::VerIterator &ver, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) - { - pkg_match_result *child_match = child->get_match(pkg, ver, cache, records, stack); - - if(child_match == NULL) - return new empty_match_result; - else - { - delete child_match; - return NULL; - } - } - - pkg_match_result *get_match(const pkgCache::PkgIterator &pkg, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) - { - pkg_match_result *child_match = child->get_match(pkg, cache, records, stack); - - if(child_match == NULL) - return new empty_match_result; - else - { - delete child_match; - return NULL; - } - } - - ~pkg_not_matcher() {delete child;} -}; - -/** Widen the search to include all versions of every package. */ -class pkg_widen_matcher : public pkg_matcher_real -{ - pkg_matcher_real *pattern; -public: - pkg_widen_matcher(pkg_matcher_real *_pattern) - : pattern(_pattern) - { - } - - ~pkg_widen_matcher() - { - } - - bool matches(const pkgCache::PkgIterator &pkg, - const pkgCache::VerIterator &ver, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) - { - return pattern->matches(pkg, cache, records, stack); - } - - bool matches(const pkgCache::PkgIterator &pkg, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) - { - return pattern->matches(pkg, cache, records, stack); - } - - pkg_match_result *get_match(const pkgCache::PkgIterator &pkg, - const pkgCache::VerIterator &ver, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) - { - return pattern->get_match(pkg, cache, records, stack); - } - - pkg_match_result *get_match(const pkgCache::PkgIterator &pkg, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) - { - return pattern->get_match(pkg, cache, records, stack); - } -}; - -/** Narrow the search to versions that match a pattern. */ -class pkg_select_matcher : public pkg_matcher_real -{ - pkg_matcher_real *filter; - pkg_matcher_real *pattern; -public: - pkg_select_matcher(pkg_matcher_real *_filter, - pkg_matcher_real *_pattern) - : filter(_filter), pattern(_pattern) - { - } - - ~pkg_select_matcher() - { - delete filter; - delete pattern; - } - - bool matches(const pkgCache::PkgIterator &pkg, - const pkgCache::VerIterator &ver, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) - { - return filter->matches(pkg, ver, cache, records, stack) && - pattern->matches(pkg, ver, cache, records, stack); - } - - pkg_match_result *get_match(const pkgCache::PkgIterator &pkg, - const pkgCache::VerIterator &ver, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) - { - if(filter->matches(pkg, ver, cache, records, stack)) - return pattern->get_match(pkg, ver, cache, records, stack); - else - return NULL; - } -}; - -// Matches packages that were garbage-collected. -class pkg_garbage_matcher:public pkg_matcher_real -{ -public: - pkg_garbage_matcher() {} - - bool matches(const pkgCache::PkgIterator &pkg, - const pkgCache::VerIterator &ver, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) - { - if(ver.end()) - return false; - else - return cache[pkg].Garbage; - } - - pkg_match_result *get_match(const pkgCache::PkgIterator &pkg, - const pkgCache::VerIterator &ver, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) - { - if(!matches(pkg, ver, cache, records, stack)) - return NULL; - else - return new unitary_result(_("Garbage")); - } -}; - -// A dummy matcher that matches any package. -class pkg_true_matcher:public pkg_matcher_real -{ -public: - pkg_true_matcher() {} - - bool matches(const pkgCache::PkgIterator &pkg, - const pkgCache::VerIterator &ver, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) - { - return true; - } - - pkg_match_result *get_match(const pkgCache::PkgIterator &pkg, - const pkgCache::VerIterator &ver, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) - { - return new empty_match_result; - } -}; - -// A dummy matcher that matches no packages. -class pkg_false_matcher:public pkg_matcher_real -{ -public: - pkg_false_matcher() {} - - bool matches(const pkgCache::PkgIterator &pkg, - const pkgCache::VerIterator &ver, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) - { - return false; - } - - pkg_match_result *get_match(const pkgCache::PkgIterator &pkg, - const pkgCache::VerIterator &ver, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) - { - return NULL; - } -}; - -// Matches packages which have a dependency of the given type declared -// on them by a package matching a given pattern. -// -// Traces through Provided packages as well. -class pkg_revdep_matcher:public pkg_matcher_real -{ - pkgCache::Dep::DepType type; - pkg_matcher_real *pattern; - - /** If \b true, only install-broken dependencies will cause a - * match. - */ - bool broken; - -public: - pkg_revdep_matcher(pkgCache::Dep::DepType _type, - pkg_matcher_real *_pattern, - bool _broken) - :type(_type), pattern(_pattern), broken(_broken) - { - } - - ~pkg_revdep_matcher() - { - delete pattern; - } - - bool matches(const pkgCache::PkgIterator &pkg, - const pkgCache::VerIterator &ver, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) - { - // Check direct dependencies. - for(pkgCache::DepIterator d=pkg.RevDependsList(); !d.end(); ++d) - { - if(broken) - { - pkgCache::DepIterator d2(cache, &*d); - while(d2->CompareOp & pkgCache::Dep::Or) - ++d2; - if(cache[d2] & pkgDepCache::DepGInstall) - continue; - } - - if((d->Type==type || - (type==pkgCache::Dep::Depends && d->Type==pkgCache::Dep::PreDepends)) && - (!d.TargetVer() || (!ver.end() && - _system->VS->CheckDep(ver.VerStr(), d->CompareOp, d.TargetVer()))) && - pattern->matches(d.ParentPkg(), d.ParentVer(), - cache, records, stack)) - return true; - } - - // Check dependencies through virtual packages. ie, things that Depend - // on stuff this package [version] Provides. - if(!ver.end()) - for(pkgCache::PrvIterator p=ver.ProvidesList(); !p.end(); ++p) - { - for(pkgCache::DepIterator d=p.ParentPkg().RevDependsList(); - !d.end(); ++d) - { - if(broken) - { - pkgCache::DepIterator d2(cache, &*d); - while(d2->CompareOp & pkgCache::Dep::Or) - ++d2; - if(cache[d2] & pkgDepCache::DepGInstall) - continue; - } - - // Only unversioned dependencies can match here. - if(d->Type==type && !d.TargetVer() && - pattern->matches(d.ParentPkg(), d.ParentVer(), - cache, records, stack)) - return true; - } - } - - return false; - } - - // Too much duplication, can I factor out the common stuff here? - // C++ doesn't make it easy.. - // - // Maybe I should just forget trying to be efficient and base - // everything on match results.. - pkg_match_result *get_match(const pkgCache::PkgIterator &pkg, - const pkgCache::VerIterator &ver, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) - { - // Check direct dependencies. - for(pkgCache::DepIterator d=pkg.RevDependsList(); !d.end(); ++d) - { - if(broken) - { - pkgCache::DepIterator d2(cache, &*d); - while(d2->CompareOp & pkgCache::Dep::Or) - ++d2; - if(cache[d2] & pkgDepCache::DepGInstall) - continue; - } - - if((d->Type==type || - (type==pkgCache::Dep::Depends && d->Type==pkgCache::Dep::PreDepends)) && - (!d.TargetVer() || (!ver.end() && - _system->VS->CheckDep(ver.VerStr(), d->CompareOp, d.TargetVer())))) - { - pkg_match_result *r = pattern->get_match(d.ParentPkg(), - d.ParentVer(), - cache, records, - stack); - - if(r != NULL) - return new result_pair(r, dep_match(d)); - } - } - - // Check dependencies through virtual packages. ie, things that Depend - // on stuff this package [version] Provides. - if(!ver.end()) - for(pkgCache::PrvIterator p=ver.ProvidesList(); !p.end(); ++p) - { - for(pkgCache::DepIterator d=p.ParentPkg().RevDependsList(); - !d.end(); ++d) - { - // Only unversioned dependencies can match here. - if(d->Type==type && !d.TargetVer()) - { - if(broken) - { - pkgCache::DepIterator d2(cache, &*d); - while(d2->CompareOp & pkgCache::Dep::Or) - ++d2; - if(cache[d2] & pkgDepCache::DepGInstall) - continue; - } - - pkg_match_result *r = pattern->get_match(d.ParentPkg(), - d.ParentVer(), - cache, records, - stack); - if(r != NULL) - return new result_pair(r, dep_match(d)); - } - } - } - - return NULL; - } -}; - - -/** Matches packages that provide a package that matches the given - * pattern. - */ -class pkg_provides_matcher:public pkg_matcher_real -{ - pkg_matcher_real *pattern; -public: - pkg_provides_matcher(pkg_matcher_real *_pattern) - :pattern(_pattern) - { - } - - ~pkg_provides_matcher() - { - delete pattern; - } - - bool matches(const pkgCache::PkgIterator &pkg, - const pkgCache::VerIterator &ver, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) - { - if(ver.end()) - return false; - - for(pkgCache::PrvIterator p=ver.ProvidesList(); !p.end(); ++p) - { - // Assumes no provided version. - if(pattern->matches(p.ParentPkg(), cache, records, stack)) - return true; - } - - return false; - } - - pkg_match_result *get_match(const pkgCache::PkgIterator &pkg, - const pkgCache::VerIterator &ver, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) - { - if(ver.end()) - return NULL; - - for(pkgCache::PrvIterator p=ver.ProvidesList(); !p.end(); ++p) - { - pkg_match_result *r = pattern->get_match(p.ParentPkg(), pkgCache::VerIterator(cache), - cache, records, stack); - - if(r != NULL) - return new result_pair(r, new unitary_result(_("Provides"))); - } - - return false; - } -}; - -/** Matches packages which are provided by a package that fits the - * given pattern. - */ -class pkg_revprv_matcher:public pkg_matcher_real -{ - pkg_matcher_real *pattern; -public: - pkg_revprv_matcher(pkg_matcher_real *_pattern) - :pattern(_pattern) - { - } - - ~pkg_revprv_matcher() - { - delete pattern; - } - - bool matches(const pkgCache::PkgIterator &pkg, - const pkgCache::VerIterator &ver, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) - { - for(pkgCache::PrvIterator p=pkg.ProvidesList(); !p.end(); ++p) - { - if(pattern->matches(p.OwnerPkg(), p.OwnerVer(), cache, records, stack)) - return true; - } - - return false; - } - - pkg_match_result *get_match(const pkgCache::PkgIterator &pkg, - const pkgCache::VerIterator &ver, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) - { - for(pkgCache::PrvIterator p=pkg.ProvidesList(); !p.end(); ++p) - { - pkg_match_result *r = pattern->get_match(p.OwnerPkg(), - p.OwnerVer(), - cache, records, - stack); - - if(r != NULL) - return new result_pair(r, - new unitary_result(_("Provided by"))); - } - - return false; - } -}; - -// Now back from the dead..it seems some people were actually using it ;-) -// -// Matches (non-virtual) packages which no installed package declares -// an "important" dependency on. -// -// Note that the notion of "importantness" is affected by the current -// settings! -class pkg_norevdep_matcher:public pkg_matcher_real -{ -public: - pkg_norevdep_matcher() - { - } - - bool matches(const pkgCache::PkgIterator &pkg, - const pkgCache::VerIterator &ver, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) - { - if(ver.end()) - return false; - else - { - pkgCache::DepIterator dep=pkg.RevDependsList(); - - while(!dep.end()) - { - if(cache.GetPolicy().IsImportantDep(dep) && - !dep.ParentVer().ParentPkg().CurrentVer().end()) - return false; - - ++dep; - } - - return true; - } - } - - pkg_match_result *get_match(const pkgCache::PkgIterator &pkg, - const pkgCache::VerIterator &ver, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) - { - if(!matches(pkg, ver, cache, records, stack)) - return NULL; - else - return new unitary_result(_("No reverse dependencies")); - } -}; - -// Matches (non-virtual) packages which no installed package declares -// a dependency of the given type on. -class pkg_norevdep_type_matcher:public pkg_matcher_real -{ - pkgCache::Dep::DepType type; // Which type to match -public: - pkg_norevdep_type_matcher(pkgCache::Dep::DepType _type) - :type(_type) - { - } - - bool matches(const pkgCache::PkgIterator &pkg, - const pkgCache::VerIterator &ver, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) - { - if(ver.end()) - return false; - else - { - pkgCache::DepIterator dep=pkg.RevDependsList(); - - while(!dep.end()) - { - // Return false if the depender is installed. - if(dep->Type==type && - !dep.ParentVer().ParentPkg().CurrentVer().end()) - return false; - - ++dep; - } - } - return true; - } - - pkg_match_result *get_match(const pkgCache::PkgIterator &pkg, - const pkgCache::VerIterator &ver, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) - { - if(!matches(pkg, ver, cache, records, stack)) - return NULL; - else - return new unitary_result(pkgCache::DepType(type)); - } -}; - -class pkg_new_matcher:public pkg_matcher_real -{ -public: - bool matches(const pkgCache::PkgIterator &pkg, - const pkgCache::VerIterator &ver, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) - { - // Don't match virtual packages. - if(pkg.VersionList().end()) - return false; - else - return cache.get_ext_state(pkg).new_package; - } - - pkg_match_result *get_match(const pkgCache::PkgIterator &pkg, - const pkgCache::VerIterator &ver, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) - { - if(!matches(pkg, ver, cache, records, stack)) - return NULL; - else - return new unitary_result(_("New Package")); - } -}; - -class pkg_upgradable_matcher:public pkg_matcher_real -{ -public: - bool matches(const pkgCache::PkgIterator &pkg, - const pkgCache::VerIterator &ver, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) - { - return !pkg.CurrentVer().end() && cache[pkg].Upgradable(); - } - - pkg_match_result *get_match(const pkgCache::PkgIterator &pkg, - const pkgCache::VerIterator &ver, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) - { - if(!matches(pkg, ver, cache, records, stack)) - return NULL; - else - return new unitary_result(_("Upgradable")); - } -}; - -class pkg_obsolete_matcher : public pkg_matcher_real -{ -public: - bool matches(const pkgCache::PkgIterator &pkg, - const pkgCache::VerIterator &ver, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) - { - return pkg_obsolete(pkg); - } - - pkg_match_result *get_match(const pkgCache::PkgIterator &pkg, - const pkgCache::VerIterator &ver, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) - { - if(!matches(pkg, ver, cache, records, stack)) - return NULL; - else - return new unitary_result(_("Obsolete")); - } -}; - -class pkg_all_matcher : public pkg_matcher_real -{ - pkg_matcher_real *sub_matcher; -public: - pkg_all_matcher(pkg_matcher_real *_sub_matcher) - : sub_matcher(_sub_matcher) - { - } - - ~pkg_all_matcher() - { - delete sub_matcher; - } - - bool matches(const pkgCache::PkgIterator &pkg, - const pkgCache::VerIterator &ver, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) - { - return sub_matcher->matches(pkg, ver, cache, records, stack); - } - - bool matches(const pkgCache::PkgIterator &pkg, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) - { - for(pkgCache::VerIterator ver = pkg.VersionList(); - !ver.end(); ++ver) - { - if(!sub_matcher->matches(pkg, ver, cache, records, stack)) - return false; - } - - return true; - } - - pkg_match_result *get_match(const pkgCache::PkgIterator &pkg, - const pkgCache::VerIterator &ver, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) - { - return sub_matcher->get_match(pkg, ver, cache, records, stack); - } - - // This will somewhat arbitrarily return the string associated with - // the last thing matched. I don't want to return all the strings - // since that would make it impossible to reliably select a string - // later in the search expression. - pkg_match_result *get_match(const pkgCache::PkgIterator &pkg, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) - { - pkg_match_result *tmp = NULL; - - for(pkgCache::VerIterator ver = pkg.VersionList(); - !ver.end(); ++ver) - { - delete tmp; - tmp = sub_matcher->get_match(pkg, ver, cache, records, stack); - if(tmp == NULL) - return tmp; - } - - return tmp; - } -}; - -class pkg_any_matcher : public pkg_matcher_real -{ - pkg_matcher_real *sub_matcher; -public: - pkg_any_matcher(pkg_matcher_real *_sub_matcher) - : sub_matcher(_sub_matcher) - { - } - - ~pkg_any_matcher() - { - delete sub_matcher; - } - - bool matches(const pkgCache::PkgIterator &pkg, - const pkgCache::VerIterator &ver, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) - { - return sub_matcher->matches(pkg, ver, cache, records, stack); - } - - bool matches(const pkgCache::PkgIterator &pkg, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) - { - for(pkgCache::VerIterator ver = pkg.VersionList(); - !ver.end(); ++ver) - { - if(sub_matcher->matches(pkg, ver, cache, records, stack)) - return true; - } - - return false; - } - - pkg_match_result *get_match(const pkgCache::PkgIterator &pkg, - const pkgCache::VerIterator &ver, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) - { - return sub_matcher->get_match(pkg, ver, cache, records, stack); - } - - pkg_match_result *get_match(const pkgCache::PkgIterator &pkg, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) - { - for(pkgCache::VerIterator ver = pkg.VersionList(); - !ver.end(); ++ver) - { - pkg_match_result *tmp = sub_matcher->get_match(pkg, ver, cache, records, stack); - if(tmp != NULL) - return tmp; - } - - return NULL; - } -}; - -// A restricted binding operator reminiscent of lambda. I say -// "restricted" because its argument may only range over packages, -// hence it is not computationally complete. I think that anyone who -// decides not to implement a lambda calculus when it's a natural -// place to go should explain himself, so here are my reasons: -// -// (a) it would significantly complicate the interface to this -// module; the data type accepted by pkg_matcher would probably -// have to become some sort of disjoint sum type (perhaps making -// the pkg_matcher a Visitor on values). -// -// (b) it would raise the possibility of non-terminating searches, -// which would require complexity at the UI level (searches would -// have to be run in a background thread, like the resolver, and -// the user would have to be able to cancel a search that was -// going nowhere). -// -// It's called an "explicit" matcher because it allows the user to -// explicitly specify which package is the target of a matcher. -class pkg_explicit_matcher : public pkg_matcher_real -{ - pkg_matcher_real *sub_matcher; - - class stack_pusher - { - match_stack &stack; - - public: - stack_pusher(match_stack &_stack, const stack_value &val) - : stack(_stack) - { - stack.push_back(val); - } - - ~stack_pusher() - { - // The size should always be non-zero, but avoid blowing up if - // it's not. - if(stack.size() > 0) - stack.pop_back(); - } - }; - -public: - pkg_explicit_matcher(pkg_matcher_real *_sub_matcher) - : sub_matcher(_sub_matcher) - { - } - - ~pkg_explicit_matcher() - { - delete sub_matcher; - } - - bool matches(const pkgCache::PkgIterator &pkg, - const pkgCache::VerIterator &ver, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) - { - stack_pusher pusher(stack, stack_value::version(pkg, ver)); - return sub_matcher->matches(pkg, ver, cache, records, stack); - } - - pkg_match_result *get_match(const pkgCache::PkgIterator &pkg, - const pkgCache::VerIterator &ver, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) - { - stack_pusher pusher(stack, stack_value::version(pkg, ver)); - return sub_matcher->get_match(pkg, cache, records, stack); - } - - - bool matches(const pkgCache::PkgIterator &pkg, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) - { - stack_pusher pusher(stack, stack_value::package(pkg)); - return sub_matcher->matches(pkg, cache, records, stack); - } - - pkg_match_result *get_match(const pkgCache::PkgIterator &pkg, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) - { - stack_pusher pusher(stack, stack_value::package(pkg)); - return sub_matcher->get_match(pkg, cache, records, stack); - } -}; - -/** \brief Bind the first argument of the given matcher. - * - * This returns a matcher that ignores the input value and - * instead uses the value stored at the given location on the - * stack. It's more or less equivalent to - * - * lambda x . lambda f . lambda y . f x - */ -class pkg_bind_matcher : public pkg_matcher_real -{ - pkg_matcher_real *sub_matcher; - match_stack::size_type variable; - - bool matches(aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) const - { - eassert(variable >= 0 && variable < stack.size()); - return stack[variable].visit_matches(sub_matcher, cache, records, stack); - } - - pkg_match_result *get_match(aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) const - { - eassert(variable >= 0 && variable < stack.size()); - return stack[variable].visit_get_match(sub_matcher, cache, records, stack); - } - - -public: - /** \brief Create a new bind matcher. - * - * \param _sub_matcher the matcher whose argument is to be bound. - * \param _variable the stack variable (referred to by its De - * Bruijn numeral) that will be bound to the - * sub-matcher's first argument. - */ - pkg_bind_matcher(pkg_matcher_real *_sub_matcher, - int _variable) - : sub_matcher(_sub_matcher), - variable(_variable) - { - } - - bool matches(const pkgCache::PkgIterator &pkg, - const pkgCache::VerIterator &ver, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) - { - return matches(cache, records, stack); - } - - pkg_match_result *get_match(const pkgCache::PkgIterator &pkg, - const pkgCache::VerIterator &ver, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) - { - return get_match(cache, records, stack); - } - - bool matches(const pkgCache::PkgIterator &pkg, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) - { - return matches(cache, records, stack); - } - - pkg_match_result *get_match(const pkgCache::PkgIterator &pkg, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) - { - return get_match(cache, records, stack); - } -}; - -/** \brief Match packages that correspond to the entry at the - * given stack position. - * - * If the value is a package, match any version of that package or no - * version. If the value is a version, match just that version. - */ -class pkg_equal_matcher : public pkg_matcher_real -{ - match_stack::size_type variable; - -public: - pkg_equal_matcher(match_stack::size_type _variable) - : variable(_variable) - { - } - - bool matches(const pkgCache::PkgIterator &pkg, - const pkgCache::VerIterator &ver, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) - { - eassert(variable >= 0 && variable < stack.size()); - return stack[variable].is_match_for(stack_value::version(pkg, ver)); - } - - pkg_match_result *get_match(const pkgCache::PkgIterator &pkg, - const pkgCache::VerIterator &ver, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) - { - eassert(variable >= 0 && variable < stack.size()); - return stack[variable].is_match_for(stack_value::version(pkg, ver)) - ? new empty_match_result - : NULL; - } -}; - -// Check for terminators. Not terribly efficient, but I expect under -// 3 terminators in any interesting usage (more than that and I really -// should force yacc to do my bidding) -bool terminate(const string::const_iterator &start, - const string::const_iterator &end, - const vector<const char *> &terminators) -{ - for(vector<const char *>::const_iterator i = terminators.begin(); - i != terminators.end(); ++i) - { - string::const_iterator j1 = start; - const char *j2 = *i; - bool matches = true; - - while(j1 != end && *j2 != 0 && matches) - { - if(*j1 != *j2) - matches=false; - - ++j1; - ++j2; - } - - if(matches) - return true; - } - - return false; -} - -// Parses a dependency type. Returns (ick) -1 if the type is not -// recognized. -pkgCache::Dep::DepType parse_deptype(const string &s) -{ - if(!strcasecmp(s.c_str(), "depends")) - return pkgCache::Dep::Depends; - if(!strcasecmp(s.c_str(), "predepends")) - return pkgCache::Dep::PreDepends; - if(!strcasecmp(s.c_str(), "recommends")) - return pkgCache::Dep::Recommends; - else if(!strcasecmp(s.c_str(), "suggests")) - return pkgCache::Dep::Suggests; - else if(!strcasecmp(s.c_str(), "conflicts")) - return pkgCache::Dep::Conflicts; - else if(!strcasecmp(s.c_str(), "breaks")) - return pkgCache::Dep::DpkgBreaks; - else if(!strcasecmp(s.c_str(), "replaces")) - return pkgCache::Dep::Replaces; - else // ewww. - return (pkgCache::Dep::DepType) -1; -} - -// Ideally this would parse the string and return an action type, but -// purging isn't a separate type to the matcher. Maybe instead there -// should be a separate enum for the action matcher's modes? -pkg_matcher_real *make_action_matcher(const std::string &action_str) -{ - // Match packages to be installed - if(!strcasecmp(action_str.c_str(), "install")) - return new pkg_action_matcher(pkg_install, false); - - // Match packages to be upgraded - else if(!strcasecmp(action_str.c_str(), "upgrade")) - return new pkg_action_matcher(pkg_upgrade, false); - - else if(!strcasecmp(action_str.c_str(), "downgrade")) - return new pkg_action_matcher(pkg_downgrade, false); - - // Match packages to be removed OR purged - else if(!strcasecmp(action_str.c_str(), "remove")) - return new pkg_action_matcher(pkg_remove, false); - - // Match packages to be purged - else if(!strcasecmp(action_str.c_str(), "purge")) - return new pkg_action_matcher(pkg_remove, true); - - // Match packages to be reinstalled - else if(!strcasecmp(action_str.c_str(), "reinstall")) - return new pkg_action_matcher(pkg_reinstall, false); - - // Match held packages - else if(!strcasecmp(action_str.c_str(), "hold")) - return new pkg_action_matcher(pkg_hold, false); - else if(!strcasecmp(action_str.c_str(), "keep")) - return new pkg_keep_matcher; - - else - throw CompilationException(_("Unknown action type: %s"), - action_str.c_str()); -} - - -static -std::string parse_literal_string_tail(string::const_iterator &start, - const string::const_iterator end) -{ - std::string rval; - - while(start != end && *start != '"') - { - if(*start == '\\') - { - ++start; - if(start != end) - { - switch(*start) - { - case 'n': - rval += '\n'; - break; - case 't': - rval += '\t'; - break; - default: - rval += *start; - break; - } - ++start; - } - } - else - { - rval += *start; - ++start; - } - } - - if(start == end) - throw CompilationException(_("Unterminated literal string after %s"), rval.c_str()); - - eassert(*start == '"'); - ++start; - - return rval; -} - -// Returns a substring up to the first metacharacter, including escaped -// metacharacters (parentheses, ~, |, and !) -// -// Advances loc to the first character of 's' following the escaped string. -std::string parse_substr(string::const_iterator &start, - const string::const_iterator &end, - const vector<const char *> &terminators, - bool whitespace_breaks) -{ - std::string rval; - bool done=false; - - // Strip leading whitespace. - while(start != end && isspace(*start)) - ++start; - - do - { - while(start != end && - *start != '(' && - *start != ')' && - *start != '!' && - *start != '~' && - *start != '|' && - *start != '"' && - (!whitespace_breaks || !isspace(*start)) && - !terminate(start, end, terminators)) - { - rval += *start; - ++start; - } - - if(start != end && *start == '"') - { - ++start; - - rval += parse_literal_string_tail(start, end); - } - - // We quit because we ran off the end of the string or saw a - // metacharacter. If the latter case and it was a tilde-escape, - // add the escaped character to the string and continue. - if(start != end && start+1 != end && *start == '~') - { - const char next = *(start+1); - - if(next == '(' || next == ')' || - next == '!' || next == '~' || - next == '|' || next == '"' || - (whitespace_breaks && isspace(next))) - { - rval += next; - start += 2; - } - else - done = true; - } - else - done = true; - } while(!done); - - return rval; -} - -pkgCache::State::VerPriority parse_priority(const string &substr) -{ - const char *s=substr.c_str(); - - if(strcasecmp(s, "important") == 0 || - (apt_cache_file && - strcasecmp(s, (*apt_cache_file)->GetCache().Priority(pkgCache::State::Important)) == 0)) - return pkgCache::State::Important; - else if(strcasecmp(s, "required") == 0 || - (apt_cache_file && - strcasecmp(s, (*apt_cache_file)->GetCache().Priority(pkgCache::State::Required)) == 0)) - return pkgCache::State::Required; - else if(strcasecmp(s, "standard") == 0 || - (apt_cache_file && - strcasecmp(s, (*apt_cache_file)->GetCache().Priority(pkgCache::State::Standard)) == 0)) - return pkgCache::State::Standard; - else if(strcasecmp(s, "optional") == 0 || - (apt_cache_file && - strcasecmp(s, (*apt_cache_file)->GetCache().Priority(pkgCache::State::Optional)) == 0)) - return pkgCache::State::Optional; - else if(strcasecmp(s, "extra") == 0 || - (apt_cache_file && - strcasecmp(s, (*apt_cache_file)->GetCache().Priority(pkgCache::State::Extra)) == 0)) - return pkgCache::State::Extra; - else - throw CompilationException(_("Unknown priority %s"), - substr.c_str()); -} - -void parse_whitespace(string::const_iterator &start, - const string::const_iterator &end) -{ - while(start != end && isspace(*start)) - ++start; -} - -void parse_required_character(string::const_iterator &start, - const string::const_iterator &end, - char c) -{ - while(start != end && isspace(*start)) - ++start; - - if(start == end) - throw CompilationException(_("Match pattern ends unexpectedly (expected '%c')."), - c); - else if(*start != c) - throw CompilationException(_("Expected '%c', got '%c'."), - c, *start); - - ++start; -} - -template<typename arg> -struct parse_method; - -void parse_open_paren(string::const_iterator &start, - const string::const_iterator &end) -{ - parse_required_character(start, end, '('); -} - -void parse_close_paren(string::const_iterator &start, - const string::const_iterator &end) -{ - parse_required_character(start, end, ')'); -} - -void parse_comma(string::const_iterator &start, - const string::const_iterator &end) -{ - parse_required_character(start, end, ','); -} - -template<> -struct parse_method<string> -{ - string operator()(string::const_iterator &start, - const string::const_iterator &end, - const std::vector<const char *> &terminators, - bool search_descriptions, - bool wide_context) const - { - return parse_substr(start, end, std::vector<const char *>(), false); - } -}; - -template<> -struct parse_method<pkg_matcher_real *> -{ - pkg_matcher_real *operator()(string::const_iterator &start, - const string::const_iterator &end, - const std::vector<const char *> &terminators, - bool search_descriptions, - bool wide_context, - const parse_environment &name_context) const - { - return parse_condition_list(start, end, terminators, search_descriptions, wide_context, name_context); - } -}; - -template<typename T, typename A1> -T *parse_unary_matcher(string::const_iterator &start, - const string::const_iterator &end, - const std::vector<const char *> &terminators, - bool search_descriptions, - bool wide_context, - const parse_environment &name_context, - const parse_method<A1> &parse1 = parse_method<A1>()) -{ - parse_open_paren(start, end); - A1 a1(parse1(start, end, terminators, search_descriptions, wide_context, name_context)); - parse_close_paren(start, end); - - return new T(a1); -} - -void add_new_terminator(const char *new_terminator, - std::vector<const char *> &terminators) -{ - for(std::vector<const char*>::const_iterator it = terminators.begin(); - it != terminators.end(); ++it) - { - if(strcmp(new_terminator, *it) == 0) - return; - } - - terminators.push_back(new_terminator); -} - -template<typename T, typename A1, typename A2> -T *parse_binary_matcher(string::const_iterator &start, - const string::const_iterator &end, - const std::vector<const char *> &terminators, - bool search_descriptions, - bool wide_context, - const parse_environment &name_context, - const parse_method<A1> &parse1 = parse_method<A1>(), - const parse_method<A2> &parse2 = parse_method<A2>()) -{ - std::vector<const char *> terminators_plus_comma(terminators); - add_new_terminator(",", terminators_plus_comma); - - parse_open_paren(start, end); - A1 a1(parse1(start, end, terminators_plus_comma, search_descriptions, wide_context, name_context)); - parse_comma(start, end); - A2 a2(parse2(start, end, terminators, search_descriptions, wide_context, name_context)); - parse_close_paren(start, end); - - return new T(a1, a2); -} - -string parse_string_match_args(string::const_iterator &start, - const string::const_iterator &end) -{ - parse_open_paren(start, end); - string substr(parse_substr(start, end, std::vector<const char *>(), false)); - parse_close_paren(start, end); - - return substr; -} - -pkg_matcher_real *parse_pkg_matcher_args(string::const_iterator &start, - const string::const_iterator &end, - const std::vector<const char *> &terminators, - bool search_descriptions, bool wide_context, - const parse_environment &name_context) -{ - parse_open_paren(start, end); - auto_ptr<pkg_matcher_real> m(parse_condition_list(start, end, terminators, search_descriptions, wide_context, name_context)); - parse_close_paren(start, end); - - return m.release(); -} - -pkg_matcher_real *parse_optional_pkg_matcher_args(string::const_iterator &start, - const string::const_iterator &end, - const std::vector<const char *> terminators, - bool search_descriptions, - bool wide_context, - const parse_environment &name_context) -{ - while(start != end && isspace(*start)) - ++start; - - if(start != end && *start == '(') - return parse_pkg_matcher_args(start, end, terminators, search_descriptions, wide_context, name_context); - else - return NULL; -} - -/** \brief Find the index of the given bound variable. */ -pkg_matcher_real::match_stack::size_type -get_variable_index(const string &bound_variable, - const parse_environment &name_context) -{ - int idx = name_context.get(bound_variable, -1); - if(idx == -1) - throw CompilationException("Unknown variable \"%s\".", - bound_variable.c_str()); - else - return idx; -} - -/** \brief Parse the tail of a lambda form. - * - * The full lambda form is: - * - * ?for <variable>: CONDITION-LIST - * - * This function assumes that "?for" has been parsed, so it parses: - * - * <variable>: CONDITION-LIST - */ -pkg_matcher_real *parse_explicit_matcher(const std::string &matcher_name, - string::const_iterator &start, - const string::const_iterator &end, - const std::vector<const char *> &terminators, - bool search_descriptions, - bool wide_context, - const parse_environment &name_context) -{ - parse_whitespace(start, end); - - string bound_variable; - while(start != end && *start != '(' && *start != '!' && - *start != '|' && *start != ')' && *start != '?' && - *start != '~' && *start != ':' && !isspace(*start) && - !terminate(start, end, terminators)) - { - bound_variable.push_back(*start); - ++start; - } - - parse_whitespace(start, end); - - if(start == end) - throw CompilationException("Unexpected end of pattern following ?%s %s (expected \":\" followed by a search term).", - matcher_name.c_str(), - bound_variable.c_str()); - else if(*start != ':') - throw CompilationException("Unexpected '%c' following ?%s %s (expected \":\" followed by a search term).", - *start, matcher_name.c_str(), bound_variable.c_str()); - - ++start; - - parse_whitespace(start, end); - - // Variables are case-insensitive and normalized to lower-case. - for(std::string::iterator it = bound_variable.begin(); - it != bound_variable.end(); ++it) - *it = tolower(*it); - - // Bind the name to the index that the variable will have in the - // stack (counting from the bottom of the stack to the top). - parse_environment name_context2(parse_environment::bind(name_context, - bound_variable, - name_context.size())); - std::auto_ptr<pkg_matcher_real> m(parse_condition_list(start, end, - terminators, - search_descriptions, - wide_context, - name_context2)); - - return new pkg_explicit_matcher(m.release()); -} - -/** \brief Return a matcher that may or may not have a rebound - * variable. - * - * If bound_variable is an empty string, just returns matcher. - * Otherwise, looks up bound_variable in the local environment - * (throwing a CompilationException if the lookup fails) and - * generates a pkg_bind_matcher that wraps the given matcher. - */ -pkg_matcher_real *maybe_bind(const string &bound_variable, - pkg_matcher_real *matcher, - const parse_environment &name_context) -{ - if(bound_variable.empty()) - return matcher; - else - return new pkg_bind_matcher(matcher, - get_variable_index(bound_variable, - name_context)); -} - -pkg_matcher_real *parse_matcher_args(const string &matcher_name, - string::const_iterator &start, - const string::const_iterator &end, - const vector<const char *> &terminators, - bool search_descriptions, - bool wide_context, - const parse_environment &name_context) -{ - { - // This block parses the following forms: - // - // ?TYPE(term) - // ?broken-TYPE - // ?broken-TYPE(term) - // ?reverse-TYPE(term) - // ?broken-reverse-TYPE(term) - // ?reverse-broken-TYPE(term) - const std::string broken_prefix("broken-"); - const std::string reverse_prefix("reverse-"); - - bool broken = false; - bool reverse = false; - std::string suffix; - - if(std::string(matcher_name, 0, broken_prefix.size()) == broken_prefix) - { - broken = true; - - if(std::string(matcher_name, broken_prefix.size(), reverse_prefix.size()) == reverse_prefix) - { - reverse = true; - suffix = std::string(matcher_name, broken_prefix.size() + reverse_prefix.size()); - } - else - suffix = std::string(matcher_name, broken_prefix.size()); - } - else if(std::string(matcher_name, 0, reverse_prefix.size()) == reverse_prefix) - { - reverse = true; - - if(std::string(matcher_name, reverse_prefix.size(), broken_prefix.size()) == broken_prefix) - { - broken = true; - suffix = std::string(matcher_name, broken_prefix.size() + reverse_prefix.size()); - } - else - suffix = std::string(matcher_name, reverse_prefix.size()); - } - else - suffix = matcher_name; - - const pkgCache::Dep::DepType deptype = parse_deptype(suffix); - - while(start != end && isspace(*start) && - !terminate(start, end, terminators)) - ++start; - - if(deptype == -1) - { - // Handle the special case of reverse-provides. - if(reverse && suffix == "provides") - return new pkg_revprv_matcher(parse_pkg_matcher_args(start, end, - terminators, - search_descriptions, - false, - name_context)); - else if(broken || reverse) - throw CompilationException(_("Unknown dependency type: %s"), - suffix.c_str()); - - // Otherwise what we have isn't a dep-matcher at all, so just - // don't do anything and try other options. - } - else - { - if(reverse) - { - // broken-reverse-TYPE(term) and reverse-broken-TYPE(term) - pkg_matcher_real *m(parse_pkg_matcher_args(start, end, - terminators, - search_descriptions, - false, - name_context)); - - return new pkg_revdep_matcher(deptype, m, broken); - } - else - { - // broken-TYPE and broken-TYPE(term) in the first branch, - // TYPE(term) in the second. - auto_ptr<pkg_matcher_real> m(broken - ? parse_optional_pkg_matcher_args(start, end, terminators, search_descriptions, false, name_context) - : parse_pkg_matcher_args(start, end, terminators, search_descriptions, false, name_context)); - - if(m.get() != NULL) - return new pkg_dep_matcher(deptype, m.release(), broken); - else - return new pkg_broken_type_matcher(deptype); - } - } - } - - matcher_type type; - bool found = false; - - // Hokey sequential scan. Why? Allocating a static map and - // populating it raises icky issues of thread-safety, when the - // initializer runs, etc...I'd rather just accept some (hopefully - // minor) inefficiency. - for(const matcher_info *it = matcher_types; - !found && (unsigned)(it - matcher_types) < (sizeof(matcher_types) / sizeof(matcher_types[0])); - ++it) - { - if(matcher_name == it->name) - { - type = it->type; - found = true; - } - } - - if(!found) - throw CompilationException(_("Unknown matcher type: \"%s\"."), - matcher_name.c_str()); - - switch(type) - { - case matcher_type_action: - return make_action_matcher(parse_string_match_args(start, end)); - case matcher_type_all: - if(!wide_context) - /* ForTranslators: Question marks ("?") are used as prefix for function names. - Leave the question marks attached to the string placeholders. */ - throw CompilationException(_("The ?%s matcher must be used in a \"wide\" context (a top-level context, or a context enclosed by ?%s)."), - matcher_name.c_str(), - "widen"); - else - return new pkg_all_matcher(parse_pkg_matcher_args(start, end, terminators, search_descriptions, false, name_context)); - case matcher_type_and: - return parse_binary_matcher<pkg_and_matcher, pkg_matcher_real *, pkg_matcher_real *>(start, end, terminators, search_descriptions, wide_context, name_context); - case matcher_type_any: - if(!wide_context) - throw CompilationException(_("The ?%s matcher must be used in a \"wide\" context (a top-level context, or a context enclosed by ?%s)."), - matcher_name.c_str(), - // TODO: should this be the translated - // form of ?widen? - "widen"); - else - return new pkg_any_matcher(parse_pkg_matcher_args(start, end, terminators, search_descriptions, false, name_context)); - case matcher_type_archive: - return new pkg_archive_matcher(parse_string_match_args(start, end)); - case matcher_type_automatic: - return new pkg_auto_matcher; - case matcher_type_bind: - { - parse_whitespace(start, end); - parse_open_paren(start, end); - - std::vector<const char *> new_terminators; - new_terminators.push_back(")"); - new_terminators.push_back(","); - std::string variable_name = parse_substr(start, end, new_terminators, true); - int idx = get_variable_index(variable_name, name_context); - - parse_whitespace(start, end); - parse_comma(start, end); - parse_whitespace(start, end); - - // Remove the comma we pushed at the end of this list, since - // it's no longer a terminator. - new_terminators.pop_back(); - - pkg_matcher_real *m = parse_condition_list(start, end, new_terminators, search_descriptions, wide_context, name_context); - parse_whitespace(start, end); - parse_close_paren(start, end); - - return new pkg_bind_matcher(m, idx); - } - case matcher_type_broken: - return new pkg_broken_matcher; - case matcher_type_config_files: - return new pkg_configfiles_matcher; - case matcher_type_description: - return new pkg_description_matcher(parse_string_match_args(start, end)); - case matcher_type_essential: - return new pkg_essential_matcher; - case matcher_type_false: - return new pkg_false_matcher; - case matcher_type_for: - return parse_explicit_matcher(matcher_name, start, end, terminators, search_descriptions, wide_context, name_context); - case matcher_type_garbage: - return new pkg_garbage_matcher; - case matcher_type_installed: - return new pkg_installed_matcher; - case matcher_type_maintainer: - return new pkg_maintainer_matcher(parse_string_match_args(start, end)); - case matcher_type_name: - return new pkg_name_matcher(parse_string_match_args(start, end)); - case matcher_type_narrow: - return parse_binary_matcher<pkg_select_matcher, pkg_matcher_real *, pkg_matcher_real *>(start, end, terminators, search_descriptions, false, name_context); - case matcher_type_new: - return new pkg_new_matcher; - case matcher_type_not: - return new pkg_not_matcher(parse_pkg_matcher_args(start, end, terminators, search_descriptions, wide_context, name_context)); - case matcher_type_obsolete: - return new pkg_obsolete_matcher; - case matcher_type_or: - return parse_binary_matcher<pkg_or_matcher, pkg_matcher_real *, pkg_matcher_real *>(start, end, terminators, search_descriptions, wide_context, name_context); - case matcher_type_origin: - return new pkg_origin_matcher(parse_string_match_args(start, end)); - case matcher_type_priority: - return new pkg_priority_matcher(parse_priority(parse_string_match_args(start, end))); - case matcher_type_provides: - return parse_unary_matcher<pkg_provides_matcher, pkg_matcher_real *>(start, end, terminators, search_descriptions, false, name_context); - case matcher_type_section: - return new pkg_section_matcher(parse_string_match_args(start, end)); - case matcher_type_source_package: - return new pkg_source_package_matcher(parse_string_match_args(start, end)); - case matcher_type_source_version: - return new pkg_source_version_matcher(parse_string_match_args(start, end)); - case matcher_type_tag: - return new pkg_tag_matcher(parse_string_match_args(start, end)); - case matcher_type_task: - return new pkg_task_matcher(parse_string_match_args(start, end)); - case matcher_type_true: - return new pkg_true_matcher; - case matcher_type_upgradable: - return new pkg_upgradable_matcher; - case matcher_type_user_tag: - return new pkg_user_tag_matcher(parse_string_match_args(start, end)); - case matcher_type_version: - return make_package_version_matcher(parse_string_match_args(start, end)); - case matcher_type_widen: - return new pkg_widen_matcher(parse_pkg_matcher_args(start, end, terminators, search_descriptions, true, name_context)); - case matcher_type_virtual: - return new pkg_virtual_matcher; - default: - // This represents an internal error: it should never happen and - // won't be comprehensible to the user, so there's no point in - // translating it (if it does happen they should report the - // literal message to me). - throw CompilationException("Unexpected matcher type %d encountered.", - type); - } -} - -pkg_matcher_real *parse_function_style_matcher_tail(string::const_iterator &start, - const string::const_iterator &end, - const vector<const char *> &terminators, - bool search_descriptions, - bool wide_context, - const parse_environment &name_context) -{ - if(*start == '=') - { - ++start; - - parse_whitespace(start, end); - - string bound_variable; - while(start != end && *start != '(' && *start != '!' && - *start != '|' && *start != ')' && *start != '?' && - *start != '~' && *start != ':' && !isspace(*start) && - !terminate(start, end, terminators)) - { - bound_variable.push_back(*start); - ++start; - } - - - if(bound_variable.empty()) - throw CompilationException("Unexpected end of pattern following ?=%s (expected a variable name).", - bound_variable.c_str()); - else - return new pkg_equal_matcher(get_variable_index(bound_variable, - name_context)); - } - - // The name is considered to be the next sequence of non-whitespace - // characters that are not an open paren. - - while(start != end && isspace(*start)) - ++start; - - string raw_name; - string lower_case_name; - string bound_variable; - while(start != end && *start != '(' && *start != '!' && - *start != '|' && *start != ')' && *start != '?' && - *start != '~' && !isspace(*start) && - !terminate(start, end, terminators)) - { - if(*start == ':') - { - if(!bound_variable.empty()) - throw CompilationException("Unexpected ':' following \"?%s:%s\".", - bound_variable.c_str(), raw_name.c_str()); - else - { - bound_variable = raw_name; - for(string::iterator it = bound_variable.begin(); - it != bound_variable.end(); ++it) - *it = tolower(*it); - raw_name.clear(); - lower_case_name.clear(); - } - } - else - { - raw_name += *start; - lower_case_name += tolower(*start); - } - ++start; - } - - return maybe_bind(bound_variable, - parse_matcher_args(lower_case_name, - start, - end, - terminators, - search_descriptions, - wide_context, - name_context), - name_context); -} - -pkg_matcher_real *parse_atom(string::const_iterator &start, - const string::const_iterator &end, - const vector<const char *> &terminators, - bool search_descriptions, - bool wide_context, - const parse_environment &name_context) -{ - std::string substr; - - while(start != end && isspace(*start)) - ++start; - - while(start != end && *start != '|' && *start != ')' && - !terminate(start, end, terminators)) - { - if(*start == '!') - { - ++start; - return new pkg_not_matcher(parse_atom(start, end, terminators, - search_descriptions, - wide_context, - name_context)); - } - else if(*start == '(') - // Recur into the list, losing the extra terminators (they are - // treated normally until the closing paren) - { - ++start; - auto_ptr<pkg_matcher_real> lst(parse_condition_list(start, end, - vector<const char *>(), - search_descriptions, - wide_context, - name_context)); - - if(!(start != end && *start == ')')) - throw CompilationException(_("Unmatched '('")); - else - { - ++start; - return lst.release(); - } - } - else if(*start == '?') - { - ++start; - return parse_function_style_matcher_tail(start, end, terminators, search_descriptions, - wide_context, name_context); - } - else if(*start == '~') - { - ++start; - while(start != end && isspace(*start)) - ++start; - - if(start == end) - { - if(!search_descriptions) - return new pkg_name_matcher("~"); - else - { - auto_ptr<pkg_matcher_real> name(new pkg_name_matcher("~")); - auto_ptr<pkg_matcher_real> desc(new pkg_description_matcher(substr)); - - return new pkg_or_matcher(name.release(), - desc.release()); - } - } - else - { - const char search_flag = *start; - - ++start; - - while(start != end && isspace(*start)) - ++start; - - switch(search_flag) - // Nested switch statements, mmmm... - // Ok, there really is a reason here. For all of the match - // types that need a string argument, some prefix code (see - // below) is needed to find the string's end. But this would - // be worse than unnecessary for the others. So I have this - // double check -- first test for anything that doesn't need - // the prefix code, then work out which of the other forms - // we have. - { - case 'v': - return new pkg_virtual_matcher; - case 'b': - return new pkg_broken_matcher; - case 'g': - return new pkg_garbage_matcher; - case 'c': - return new pkg_configfiles_matcher; - case 'i': - return new pkg_installed_matcher; - case 'E': - return new pkg_essential_matcher; - case 'M': - return new pkg_auto_matcher; - case 'N': - return new pkg_new_matcher; - case 'U': - return new pkg_upgradable_matcher; - case 'o': - return new pkg_obsolete_matcher; - case 'P': - case 'C': - case 'W': - { - auto_ptr<pkg_matcher_real> m(parse_atom(start, - end, - terminators, - search_descriptions, - search_flag == 'W', - name_context)); - - switch(search_flag) - { - case 'C': - return new pkg_dep_matcher(pkgCache::Dep::Conflicts, m.release(), false); - case 'P': - return new pkg_provides_matcher(m.release()); - case 'W': - return new pkg_widen_matcher(m.release()); - } - } - case 'S': - { - auto_ptr<pkg_matcher_real> filter(parse_atom(start, - end, - terminators, - search_descriptions, - false, - name_context)); - - auto_ptr<pkg_matcher_real> pattern(parse_atom(start, - end, - terminators, - search_descriptions, - false, - name_context)); - - return new pkg_select_matcher(filter.release(), pattern.release()); - } - case 'D': - case 'R': - { - bool do_provides = false; - bool broken = false; - pkgCache::Dep::DepType type=pkgCache::Dep::Depends; - - if(start != end && *start == 'B') - { - broken = true; - ++start; - } - - string::const_iterator nextstart = start; - - while(nextstart != end && isalpha(*nextstart) && - !terminate(nextstart, end, terminators)) - ++nextstart; - - while(nextstart != end && isspace(*nextstart)) - ++nextstart; - - if(nextstart != end && *nextstart == ':') - { - string tname(start, nextstart); - stripws(tname); - - start = nextstart; - ++start; - - if(!strcasecmp(tname.c_str(), "provides")) - do_provides=true; - else - { - type=parse_deptype(tname.c_str()); - - if(type==-1) - throw CompilationException(_("Unknown dependency type: %s"), - tname.c_str()); - } - } - - if(do_provides && broken) - throw CompilationException(_("Provides: cannot be broken")); - - auto_ptr<pkg_matcher_real> m(parse_atom(start, end, terminators, - search_descriptions, - false, name_context)); - - switch(search_flag) - { - case 'D': - if(do_provides) - return new pkg_provides_matcher(m.release()); - else - return new pkg_dep_matcher(type, m.release(), broken); - case 'R': - if(do_provides) - return new pkg_revprv_matcher(m.release()); - else - return new pkg_revdep_matcher(type, m.release(), broken); - } - } - default: - substr = parse_substr(start, end, terminators, true); - switch(search_flag) - { - case 'a': - { - return make_action_matcher(substr.c_str()); - } - case 'A': - return new pkg_archive_matcher(substr); - case 'B': - { - pkgCache::Dep::DepType ptype=parse_deptype(substr); - - if(ptype!=-1) - return new pkg_broken_type_matcher(ptype); - else - throw CompilationException(_("Unknown dependency type: %s"), - substr.c_str()); - } - case 'd': - return new pkg_description_matcher(substr); - case 'G': - return new pkg_tag_matcher(substr); - case 'F': - return new pkg_false_matcher; - case 'm': - return new pkg_maintainer_matcher(substr); - case 'n': - return new pkg_name_matcher(substr); - case 'O': - return new pkg_origin_matcher(substr); - case 'p': - return new pkg_priority_matcher(parse_priority(substr)); - case 's': - return new pkg_section_matcher(substr); - case 't': - return new pkg_task_matcher(substr); - case 'T': - return new pkg_true_matcher; - case 'V': - return make_package_version_matcher(substr); - default: - throw CompilationException(_("Unknown pattern type: %c"), search_flag); - } - } - } - } - else - { - if(!search_descriptions) - return new pkg_name_matcher(parse_substr(start, end, - terminators, true)); - else - { - substr = parse_substr(start, end, terminators, true); - auto_ptr<pkg_matcher_real> name(new pkg_name_matcher(substr)); - auto_ptr<pkg_matcher_real> desc(new pkg_description_matcher(substr)); - - return new pkg_or_matcher(name.release(), - desc.release()); - } - } - } - - // If we get here, the string was empty. - throw CompilationException(_("Can't search for \"\"")); -} - -pkg_matcher_real *parse_and_group(string::const_iterator &start, - const string::const_iterator &end, - const vector<const char *> &terminators, - bool search_descriptions, - bool wide_context, - const parse_environment &name_context) -{ - auto_ptr<pkg_matcher_real> rval(NULL); - while(start != end && isspace(*start)) - ++start; - - while(start != end && *start != '|' && *start != ')' && - !terminate(start, end, terminators)) - { - auto_ptr<pkg_matcher_real> atom(parse_atom(start, end, terminators, - search_descriptions, - wide_context, - name_context)); - - if(rval.get() == NULL) - rval = atom; - else - rval = auto_ptr<pkg_matcher_real>(new pkg_and_matcher(rval.release(), atom.release())); - - while(start != end && isspace(*start)) - ++start; - } - - if(rval.get() == NULL) - throw CompilationException(_("Unexpected empty expression")); - - return rval.release(); -} - -pkg_matcher_real *parse_condition_list(string::const_iterator &start, - const string::const_iterator &end, - const vector<const char *> &terminators, - bool search_descriptions, - bool wide_context, - const parse_environment &name_context) -{ - auto_ptr<pkg_matcher_real> grp(parse_and_group(start, end, terminators, - search_descriptions, - wide_context, - name_context)); - - while(start != end && isspace(*start)) - ++start; - - while(start != end && *start != ')' && !terminate(start, end, terminators)) - { - if(start != end && *start == '|') - { - ++start; - auto_ptr<pkg_matcher_real> grp2(parse_condition_list(start, end, terminators, - search_descriptions, - wide_context, - name_context)); - - return new pkg_or_matcher(grp.release(), grp2.release()); - } - else - throw CompilationException(_("Badly formed expression")); - - // Note that this code should never execute: - while(start != end && isspace(*start)) - ++start; - } - - // If there's no second element in the condition list, return its - // head. - return grp.release(); -} - -} - -pkg_matcher *parse_pattern(string::const_iterator &start, - const string::const_iterator &end, - const std::vector<const char *> &terminators, - bool search_descriptions, - bool flag_errors, - bool require_full_parse) -{ - // Just filter blank strings out immediately. - while(start != end && isspace(*start) && !terminate(start, end, terminators)) - ++start; - - if(start == end) - return NULL; - - try - { - auto_ptr<pkg_matcher_real> rval(parse_condition_list(start, end, terminators, - search_descriptions, - true, - parse_environment())); - - while(start != end && isspace(*start)) - ++start; - - if(require_full_parse && start != end) - throw CompilationException(_("Unexpected ')'")); - else - return rval.release(); - } - catch(CompilationException e) - { - if(flag_errors) - _error->Error("%s", e.get_msg().c_str()); - - return NULL; - } -} - - -bool apply_matcher(pkg_matcher *matcher, - const pkgCache::PkgIterator &pkg, - const pkgCache::VerIterator &ver, - aptitudeDepCache &cache, - pkgRecords &records) -{ - pkg_matcher_real *real_matcher(dynamic_cast<pkg_matcher_real *>(matcher)); - eassert(real_matcher != NULL); - - pkg_matcher_real::match_stack stack; - return real_matcher->matches(pkg, ver, cache, records, stack); -} - - - -/** \return a match result, or \b NULL if there is no match. It's - * the caller's responsibility to delete this. - */ -pkg_match_result *get_match(pkg_matcher *matcher, - const pkgCache::PkgIterator &pkg, - const pkgCache::VerIterator &ver, - aptitudeDepCache &cache, - pkgRecords &records) -{ - pkg_matcher_real *real_matcher(dynamic_cast<pkg_matcher_real *>(matcher)); - eassert(real_matcher != NULL); - - pkg_matcher_real::match_stack stack; - return real_matcher->get_match(pkg, ver, cache, records, stack); -} - -/** See whether this matches a versionless package. This applies - * the matcher to every version of the package and returns \b true - * if any version is matched. - */ -bool apply_matcher(pkg_matcher *matcher, - const pkgCache::PkgIterator &pkg, - aptitudeDepCache &cache, - pkgRecords &records) -{ - pkg_matcher_real *real_matcher(dynamic_cast<pkg_matcher_real *>(matcher)); - eassert(real_matcher != NULL); - - pkg_matcher_real::match_stack stack; - return real_matcher->matches(pkg, cache, records, stack); -} - -/** Get a match result for a versionless package. This applies the - * matcher to each version of the package, returning \b NULL if - * none matches or the first match found otherwise. - */ -pkg_match_result *get_match(pkg_matcher *matcher, - const pkgCache::PkgIterator &pkg, - aptitudeDepCache &cache, - pkgRecords &records) -{ - pkg_matcher_real *real_matcher(dynamic_cast<pkg_matcher_real *>(matcher)); - eassert(real_matcher != NULL); - - pkg_matcher_real::match_stack stack; - return real_matcher->get_match(pkg, cache, records, stack); -} - -class pkg_const_matcher : public pkg_matcher_real -{ - pkgCache::PkgIterator match_pkg; - - class const_name_result : public pkg_match_result - { - std::string name_group; - public: - const_name_result(const std::string &_name_group) - : name_group(_name_group) - { - } - - unsigned int num_groups() { return 1; } - const std::string &group(unsigned int n) { return name_group; } - }; -public: - pkg_const_matcher(const pkgCache::PkgIterator &_match_pkg) - : match_pkg(_match_pkg) - { - } - - bool matches(const pkgCache::PkgIterator &pkg, - const pkgCache::VerIterator &ver, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) - { - return pkg == match_pkg; - } - - pkg_match_result *get_match(const pkgCache::PkgIterator &pkg, - const pkgCache::VerIterator &ver, - aptitudeDepCache &cache, - pkgRecords &records, - match_stack &stack) - { - if(pkg == match_pkg) - return new const_name_result(pkg.Name()); - else - return NULL; - } -}; - -pkg_matcher *make_const_matcher(const pkgCache::PkgIterator &pkg) -{ - return new pkg_const_matcher(pkg); -} - - -} -} |