[Aptitude-svn-commit] r3663 - in branches/aptitude-0.3/aptitude: . src/generic

Daniel Burrows dburrows at costa.debian.org
Sat Jul 23 19:11:05 UTC 2005


Author: dburrows
Date: Sat Jul 23 19:11:02 2005
New Revision: 3663

Modified:
   branches/aptitude-0.3/aptitude/ChangeLog
   branches/aptitude-0.3/aptitude/src/generic/matchers.cc
   branches/aptitude-0.3/aptitude/src/generic/matchers.h
Log:
Add basic support for retrieving the string(s) corresponding to a pattern match.

Modified: branches/aptitude-0.3/aptitude/ChangeLog
==============================================================================
--- branches/aptitude-0.3/aptitude/ChangeLog	(original)
+++ branches/aptitude-0.3/aptitude/ChangeLog	Sat Jul 23 19:11:02 2005
@@ -1,3 +1,9 @@
+2005-07-23  Daniel Burrows  <dburrows at debian.org>
+
+	* src/generic/matchers.cc, src/generic/matchers.h:
+
+	  Add support for retrieving a string corresponding to a match.
+
 2005-07-22  Daniel Burrows  <dburrows at debian.org>
 
 	* doc/en/aptitude.xml:

Modified: branches/aptitude-0.3/aptitude/src/generic/matchers.cc
==============================================================================
--- branches/aptitude-0.3/aptitude/src/generic/matchers.cc	(original)
+++ branches/aptitude-0.3/aptitude/src/generic/matchers.cc	Sat Jul 23 19:11:02 2005
@@ -36,6 +36,8 @@
 
 #include "../aptitude.h"
 
+#include <stdarg.h>
+
 #include <apt-pkg/error.h>
 #include <apt-pkg/pkgcache.h>
 #include <apt-pkg/pkgrecords.h>
@@ -50,181 +52,491 @@
 
 using namespace std;
 
-inline bool string_matches(regex_t *pattern, string s)
-  // Altering this to support globs or regexps is all that's needed to add
-  // support for those to the program (if that's done, the inline should be
-  // removed :) )
+/** Used to cleanly abort without having to contort the code. */
+class CompilationException
 {
-  regmatch_t amount;
+  string reason;
+public:
+  CompilationException(const char *format,
+		       ...)
+#ifdef __GNUG__
+    __attribute__ ((format (printf, 2, 3)))
+#endif
+  {
+    // eh, I should really move this to common code.
+    char buf[1024];
+    va_list args;
+
+    va_start(args, format);
+
+    vsnprintf(buf, sizeof(buf), format, args);
+
+    reason.assign(buf, 1024);
+  }
+
+  const string &get_msg() {return reason;}
+};
 
-  return !regexec(pattern, s.c_str(), 1, &amount, 0);
+pkg_match_result::~pkg_match_result()
+{
 }
 
-typedef bool (*mfunc)(regex_t *pattern, pkgCache::PkgIterator pkg, pkgCache::VerIterator ver);
+/** 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();}
+};
 
-template<mfunc f>
-class pkg_string_matcher:public pkg_matcher
+class pkg_nonstring_matcher : public pkg_matcher
 {
-  regex_t pattern;
+public:
+  pkg_match_result *get_match(const pkgCache::PkgIterator &pkg,
+			      const pkgCache::VerIterator &ver)
+  {
+    return matches(pkg, ver) ? 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
+{
+  regex_t pattern_nogroup;
+  regex_t pattern_group;
+  bool pattern_nogroup_initialized:1;
+  bool pattern_group_initialized:1;
 
   pkg_string_matcher()
   {
   }
 
-  bool compile(string _pattern, bool flag_errors)
+  void do_compile(const string &_pattern,
+		  regex_t &pattern,
+		  int cflags)
   {
-    int err=regcomp(&pattern, _pattern.c_str(), REG_ICASE|REG_EXTENDED|REG_NOSUB);
+    int err=regcomp(&pattern, _pattern.c_str(), cflags);
     if(err!=0)
       {
-	if(flag_errors)
-	  {
-	    size_t needed=regerror(err, &pattern, NULL, 0);
+	size_t needed=regerror(err, &pattern, NULL, 0);
 
-	    char *buf=new char[needed+1];
+	auto_ptr<char> buf(new char[needed+1]);
 
-	    regerror(err, &pattern, buf, needed+1);
+	regerror(err, &pattern, buf.get(), needed+1);
 
-	    _error->Error("Regex compilation error: %s", buf);
+	throw CompilationException("Regex compilation error: %s", buf.get());
+      }
+  }
 
-	    delete buf;
-	  }
+  void compile(const string &_pattern)
+  {
+    do_compile(_pattern, pattern_nogroup, REG_ICASE|REG_EXTENDED|REG_NOSUB);
+    pattern_nogroup_initialized=true;
 
-	return false;
-      }
+    do_compile(_pattern, pattern_group, REG_ICASE|REG_EXTENDED);
+    pattern_group_initialized=true;
+  }
+
+  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];}
+  };
+public:
+  pkg_string_matcher (const string &_pattern)
+    :pattern_nogroup_initialized(false),
+     pattern_group_initialized(false)
+  {
+    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);
+  }
+
+  pkg_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 true;
+      return new match_result(s, matches,
+			      sizeof(matches)/sizeof(regmatch_t));
   }
+};
+
+typedef pair<bool, const char *> match_target;
+
+class pkg_trivial_string_matcher : public pkg_string_matcher
+{
 public:
-  static pkg_string_matcher<f> *init(string _pattern,
-				     bool flag_errors)
+  pkg_trivial_string_matcher (const string &s) : pkg_string_matcher(s)
   {
-    pkg_string_matcher<f> *rval=new pkg_string_matcher<f>;
+  }
 
-    if(!rval->compile(_pattern, flag_errors))
-      {
-	delete rval;
-	return NULL;
-      }
+  // 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)=0;
+
+  bool matches(const pkgCache::PkgIterator &pkg,
+	       const pkgCache::VerIterator &ver)
+  {
+    match_target v = val(pkg, ver);
+
+    if(!v.first)
+      return false;
     else
-      return rval;
+      return string_matches(v.second);
   }
 
-  ~pkg_string_matcher()
+  pkg_match_result *get_match(const pkgCache::PkgIterator &pkg,
+			      const pkgCache::VerIterator &ver)
   {
-    regfree(&pattern);
+    match_target v = val(pkg, ver);
+
+    if(!v.first)
+      return NULL;
+    else
+      return get_string_match(v.second);
   }
+};
+
+class pkg_name_matcher:public pkg_trivial_string_matcher
+{
+public:
+  pkg_name_matcher(const string &s):pkg_trivial_string_matcher(s) {}
 
-  bool matches(pkgCache::PkgIterator pkg, pkgCache::VerIterator ver)
+  match_target val(const pkgCache::PkgIterator &pkg,
+		   const pkgCache::VerIterator &ver)
   {
-    return f(&pattern, pkg, ver);
+    return match_target(true, pkg.Name());
   }
 };
 
-bool name_matches(regex_t *pattern, pkgCache::PkgIterator pkg, pkgCache::VerIterator ver)
+class pkg_description_matcher:public pkg_trivial_string_matcher
 {
-  return string_matches(pattern, pkg.Name());
-}
-typedef pkg_string_matcher<name_matches> pkg_name_matcher;
+public:
+  pkg_description_matcher(const string &s):pkg_trivial_string_matcher(s) {}
 
-bool description_matches(regex_t *pattern, pkgCache::PkgIterator pkg, pkgCache::VerIterator ver)
-{
-  return !ver.end() && string_matches(pattern, apt_package_records->Lookup(ver.FileList()).LongDesc());
-}
-typedef pkg_string_matcher<description_matches> pkg_description_matcher;
+  match_target val(const pkgCache::PkgIterator &pkg,
+		   const pkgCache::VerIterator &ver)
+  {
+    if(ver.end())
+      return match_target(false, "");
+    else
+      return match_target(true, apt_package_records->Lookup(ver.FileList()).LongDesc().c_str());
+  }
+};
 
-bool maintainer_matches(regex_t *pattern, pkgCache::PkgIterator pkg, pkgCache::VerIterator ver)
+class pkg_maintainer_matcher : public pkg_trivial_string_matcher
 {
-  return !ver.end() && string_matches(pattern, apt_package_records->Lookup(ver.FileList()).Maintainer());
-}
-typedef pkg_string_matcher<maintainer_matches> pkg_maintainer_matcher;
+public:
+  pkg_maintainer_matcher(const string &s):pkg_trivial_string_matcher(s)
+  {
+  }
+
+  match_target val(const pkgCache::PkgIterator &pkg,
+		   const pkgCache::VerIterator &ver)
+  {
+    if(ver.end())
+      return match_target(false, "");
+    else
+      return match_target(true, apt_package_records->Lookup(ver.FileList()).Maintainer().c_str());
+  }
+};
 
-bool section_matches(regex_t *pattern, pkgCache::PkgIterator pkg, pkgCache::VerIterator ver)
+class pkg_section_matcher : public pkg_trivial_string_matcher
 {
-  return !ver.end() && ver.Section() && string_matches(pattern, ver.Section());
-}
-typedef pkg_string_matcher<section_matches> pkg_section_matcher;
+public:
+  pkg_section_matcher(const string &s):pkg_trivial_string_matcher(s)
+  {
+  }
+
+  match_target val(const pkgCache::PkgIterator &pkg,
+		   const pkgCache::VerIterator &ver)
+  {
+    if(ver.end())
+      return match_target(false, "");
 
-bool version_matches(regex_t *pattern, pkgCache::PkgIterator pkg, pkgCache::VerIterator ver)
+    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
 {
-  return !ver.end() && ver.VerStr() && string_matches(pattern, ver.VerStr());
-}
-typedef pkg_string_matcher<version_matches> pkg_version_matcher;
+public:
+  pkg_version_matcher(const string &s) : pkg_trivial_string_matcher(s)
+  {
+  }
+
+  match_target val(const pkgCache::PkgIterator &pkg,
+		   const pkgCache::VerIterator &ver)
+  {
+    if(ver.end())
+      return match_target(false, "");
+
+    const char *s=ver.VerStr();
+
+    if(!s)
+      return match_target(false, "");
+
+    return match_target(true, s);
+  }
+};
 
-bool task_matches(regex_t *pattern, pkgCache::PkgIterator pkg, pkgCache::VerIterator ver)
+class pkg_task_matcher : public pkg_string_matcher
 {
-  list<string> *l=get_tasks(pkg);
+public:
+  pkg_task_matcher(const string &s) : pkg_string_matcher(s)
+  {
+  }
+
+  bool matches(const pkgCache::PkgIterator &pkg,
+	       const pkgCache::VerIterator &v)
+  {
+    list<string> *l=get_tasks(pkg);
+
+    if(!l)
+      return false;
+
+    for(list<string>::iterator i=l->begin();
+	i!=l->end();
+	++i)
+      if(string_matches(i->c_str()))
+	return true;
 
-  if(!l)
     return false;
+  }
 
-  for(list<string>::iterator i=l->begin();
-      i!=l->end();
-      ++i)
-    if(string_matches(pattern, *i))
-      return true;
+  // Uses the fact that the result returns NULL <=> nothing matched
+  pkg_match_result *get_match(pkgCache::PkgIterator &pkg,
+			      pkgCache::VerIterator &ver)
+  {
+    list<string> *l=get_tasks(pkg);
 
-  return false;
-}
-typedef pkg_string_matcher<task_matches> pkg_task_matcher;
+    if(!l)
+      return NULL;
+
+    for(list<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;
+  }
+};
 
 //  Package-file info matchers.  Match a package if any of its
 // available files (for all versions) match the given criteria.
 //
 //  Should I use templates?
-bool origin_matches(regex_t *pattern,
-		    pkgCache::PkgIterator pkg,
-		    pkgCache::VerIterator ver)
+class pkg_origin_matcher : public pkg_string_matcher
 {
-  for(pkgCache::VerIterator v=pkg.VersionList(); !v.end(); ++v)
-    for(pkgCache::VerFileIterator f=v.FileList(); !f.end(); ++f)
-      {
-	pkgCache::PkgFileIterator cur=f.File();
+public:
+  pkg_origin_matcher(const string &s) : pkg_string_matcher(s)
+  {
+  }
 
-	if(!cur.end() && cur.Origin() && string_matches(pattern,
-							cur.Origin()))
-	  return true;
-      }
+  bool matches(const pkgCache::PkgIterator &pkg,
+	       const pkgCache::VerIterator &ver)
+  {
+    for(pkgCache::VerIterator v=pkg.VersionList(); !v.end(); ++v)
+      for(pkgCache::VerFileIterator f=v.FileList(); !f.end(); ++f)
+	{
+	  pkgCache::PkgFileIterator cur=f.File();
 
-  return false;
-}
-typedef pkg_string_matcher<origin_matches> pkg_origin_matcher;
+	  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)
+  {
+    for(pkgCache::VerIterator v=pkg.VersionList(); !v.end(); ++v)
+      for(pkgCache::VerFileIterator f=v.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;
+  }
+};
 
-bool archive_matches(regex_t *pattern,
-		     pkgCache::PkgIterator pkg,
-		     pkgCache::VerIterator ver)
+class pkg_archive_matcher : public pkg_string_matcher
 {
-  if(ver.end() || ver.FileList().end())
+public:
+  pkg_archive_matcher(const string &s) : pkg_string_matcher(s)
+  {
+  }
+
+  bool matches(const pkgCache::PkgIterator &pkg,
+	       const pkgCache::VerIterator &ver)
+  {
+    if(ver.end() || ver.FileList().end())
+      return false;
+
+    for(pkgCache::VerIterator v=pkg.VersionList(); !v.end(); ++v)
+      for(pkgCache::VerFileIterator f=v.FileList(); !f.end(); ++f)
+	{
+	  pkgCache::PkgFileIterator cur=f.File();
+
+	  if(!cur.end() && cur.Archive() && string_matches(cur.Archive()))
+	    return true;
+	}
+
     return false;
+  }
 
-  for(pkgCache::VerIterator v=pkg.VersionList(); !v.end(); ++v)
-    for(pkgCache::VerFileIterator f=v.FileList(); !f.end(); ++f)
-      {
-	pkgCache::PkgFileIterator cur=f.File();
+  pkg_match_result *get_match(const pkgCache::PkgIterator &pkg,
+			      const pkgCache::VerIterator &ver)
+  {
+    if(ver.end() || ver.FileList().end())
+      return NULL;
 
-	if(!cur.end() && cur.Archive() && string_matches(pattern,
-							 cur.Archive()))
-	  return true;
-      }
+    for(pkgCache::VerIterator v=pkg.VersionList(); !v.end(); ++v)
+      for(pkgCache::VerFileIterator f=v.FileList(); !f.end(); ++f)
+	{
+	  pkgCache::PkgFileIterator cur=f.File();
 
-  return false;
-}
-typedef pkg_string_matcher<archive_matches> pkg_archive_matcher;
+	  if(!cur.end() && cur.Archive())
+	    {
+	      pkg_match_result *r=get_string_match(cur.Archive());
+
+	      if(r != NULL)
+		return r;
+	    }
+	}
+
+    return NULL;
+  }
+};
 
 class pkg_auto_matcher:public pkg_matcher
 {
 public:
-  bool matches(pkgCache::PkgIterator pkg, pkgCache::VerIterator ver)
+  bool matches(const pkgCache::PkgIterator &pkg,
+	       const pkgCache::VerIterator &ver)
   {
     return
       (!pkg.CurrentVer().end() || (*apt_cache_file)[pkg].Install()) &&
       ((*apt_cache_file)->get_ext_state(pkg).install_reason!=aptitudeDepCache::manual);
   }
+
+  pkg_match_result *get_match(const pkgCache::PkgIterator &pkg,
+			      const pkgCache::VerIterator &ver)
+  {
+    return matches(pkg, ver) ? new unitary_result(_("Automatically Installed")) : NULL;
+  }
 };
 
 class pkg_broken_matcher:public pkg_matcher
 {
 public:
-  bool matches(pkgCache::PkgIterator pkg, pkgCache::VerIterator ver)
+  bool matches(const pkgCache::PkgIterator &pkg,
+	       const pkgCache::VerIterator &ver)
   {
     if(ver.end())
       return false;
@@ -234,6 +546,12 @@
 	return state.NowBroken() || state.InstBroken();
       }
   }
+
+  pkg_match_result *get_match(const pkgCache::PkgIterator &pkg,
+			      const pkgCache::VerIterator &ver)
+  {
+    return matches(pkg, ver) ? new unitary_result(_("Broken")) : NULL;
+  }
 };
 
 class pkg_priority_matcher:public pkg_matcher
@@ -243,15 +561,59 @@
   pkg_priority_matcher(pkgCache::State::VerPriority _type)
     :type(_type) {}
 
-  bool matches(pkgCache::PkgIterator pkg, pkgCache::VerIterator ver)
+  bool matches(const pkgCache::PkgIterator &pkg,
+	       const pkgCache::VerIterator &ver)
   {
     if(ver.end())
       return false;
     else
       return ver->Priority == type;
   }
+
+  pkg_match_result *get_match(const pkgCache::PkgIterator &pkg,
+			      const pkgCache::VerIterator &ver)
+  {
+    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;
+
+  while(start != end)
+    {
+      if(!first)
+	realization += " | ";
+
+      realization += start.TargetPkg().Name();
+
+      if(start.TargetVer())
+	{
+	  realization += " (";
+	  realization += start.CompType();
+	  realization += " ";
+	  realization += start.TargetVer();
+	  realization += ")";
+	}
+    }
+
+  // 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
 {
@@ -260,7 +622,8 @@
   pkg_broken_type_matcher(pkgCache::Dep::DepType _type)
     :type(_type) {}
 
-  bool matches(pkgCache::PkgIterator pkg, pkgCache::VerIterator ver)
+  bool matches(const pkgCache::PkgIterator &pkg,
+	       const pkgCache::VerIterator &ver)
   {
     if(ver.end())
       return false;
@@ -284,6 +647,33 @@
       }
     return false;
   }
+
+  pkg_match_result * get_match(const pkgCache::PkgIterator &pkg,
+			       const pkgCache::VerIterator &ver)
+  {
+    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 &&
+	       !((*apt_cache_file)[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.
@@ -300,7 +690,8 @@
   {
   }
 
-  bool matches(pkgCache::PkgIterator pkg, pkgCache::VerIterator ver)
+  bool matches(const pkgCache::PkgIterator &pkg,
+	       const pkgCache::VerIterator &ver)
   {
     if(require_purge &&
        ((*apt_cache_file)[pkg].iFlags & pkgDepCache::Purge) == 0)
@@ -332,24 +723,82 @@
 	  }
       }
   }
+
+  pkg_match_result *get_match(const pkgCache::PkgIterator &pkg,
+			      const pkgCache::VerIterator &ver)
+  {
+    if(!matches(pkg, ver))
+      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
 {
-  bool matches(pkgCache::PkgIterator pkg, pkgCache::VerIterator ver)
+  bool matches(const pkgCache::PkgIterator &pkg,
+	       const pkgCache::VerIterator &ver)
   {
     return !pkg.CurrentVer().end() && (*apt_cache_file)[pkg].Keep();
   }
+
+  pkg_match_result *get_match(const pkgCache::PkgIterator &pkg,
+			      const pkgCache::VerIterator &ver)
+  {
+    if(!pkg.CurrentVer().end() && (*apt_cache_file)[pkg].Keep())
+      return new unitary_result(_("Keep"));
+    else
+      return NULL;
+  }
 };
 
 class pkg_virtual_matcher:public pkg_matcher
 // Matches *pure* virtual packages -- ie, those that /have/ no versions.
 {
 public:
-  bool matches(pkgCache::PkgIterator pkg, pkgCache::VerIterator ver)
+  bool matches(const pkgCache::PkgIterator &pkg,
+	       const pkgCache::VerIterator &ver)
   {
     return pkg.VersionList().end();
   }
+
+  pkg_match_result *get_match(const pkgCache::PkgIterator &pkg,
+			      const pkgCache::VerIterator &ver)
+  {
+    if(!pkg.VersionList().end())
+      return NULL;
+    else
+      return new unitary_result(_("Virtual"));
+  }
 };
 
 class pkg_installed_matcher:public pkg_matcher
@@ -357,36 +806,66 @@
 // *any* version of an installed package.
 {
 public:
-  bool matches(pkgCache::PkgIterator pkg, pkgCache::VerIterator ver)
+  bool matches(const pkgCache::PkgIterator &pkg,
+	       const pkgCache::VerIterator &ver)
   {
     return !pkg.CurrentVer().end();
   }
+
+  pkg_match_result *get_match(const pkgCache::PkgIterator &pkg,
+			      const pkgCache::VerIterator &ver)
+  {
+    if(pkg.CurrentVer().end())
+      return NULL;
+    else
+      return new unitary_result(_("Installed"));
+  }
 };
 
 class pkg_essential_matcher:public pkg_matcher
 // Matches essential packages
 {
 public:
-  bool matches(pkgCache::PkgIterator pkg, pkgCache::VerIterator ver)
+  bool matches(const pkgCache::PkgIterator &pkg,
+	       const pkgCache::VerIterator &ver)
   {
     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)
+  {
+    if(!matches(pkg, ver))
+      return NULL;
+    else
+      return new unitary_result(_("Essential"));
+  }
 };
 
 class pkg_configfiles_matcher:public pkg_matcher
 // Matches a package which was removed but has config files remaining
 {
 public:
-  bool matches(pkgCache::PkgIterator pkg, pkgCache::VerIterator ver)
+  bool matches(const pkgCache::PkgIterator &pkg,
+	       const pkgCache::VerIterator &ver)
   {
     return pkg->CurrentState==pkgCache::State::ConfigFiles;
   }
+
+  // ???
+  pkg_match_result *get_match(const pkgCache::PkgIterator &pkg,
+			      const pkgCache::VerIterator &ver)
+  {
+    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.
-// FIXME: make this more general (for recommends, eg?)
 class pkg_dep_matcher:public pkg_matcher
 {
 private:
@@ -401,7 +880,8 @@
   }
   ~pkg_dep_matcher() {delete pattern;}
 
-  bool matches(pkgCache::PkgIterator pkg, pkgCache::VerIterator ver)
+  bool matches(const pkgCache::PkgIterator &pkg,
+	       const pkgCache::VerIterator &ver)
   {
     assert(!pkg.end());
     if(ver.end())
@@ -410,8 +890,8 @@
     for(pkgCache::DepIterator dep=ver.DependsList(); !dep.end(); ++dep)
       {
 	if( (type == dep->Type) ||
-	    (type==pkgCache::Dep::Depends &&
-	     dep->Type==pkgCache::Dep::PreDepends))
+	    (type == pkgCache::Dep::Depends &&
+	     dep->Type == pkgCache::Dep::PreDepends))
 	  {
 	    // See if a versionless match works,.
 	    if(dep.TargetPkg().VersionList().end() &&
@@ -429,41 +909,141 @@
 
     return false;
   }
+
+  pkg_match_result *get_match(const pkgCache::PkgIterator &pkg,
+			      const pkgCache::VerIterator &ver)
+  {
+    assert(!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))
+	  {
+	    // See if a versionless match works,.
+	    if(dep.TargetPkg().VersionList().end())
+	      {
+		pkg_match_result *r=pattern->get_match(dep.TargetPkg(), dep.TargetPkg().VersionList());
+
+		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);
+
+		  if(r)
+		    return new result_pair(r, dep_match(dep));
+		}
+	  }
+      }
+
+    return false;
+  }
 };
 
 class pkg_or_matcher:public pkg_matcher
 {
   pkg_matcher *left,*right;
 public:
-  pkg_or_matcher(pkg_matcher *_left, pkg_matcher *_right):left(_left),right(_right) {}
-  bool matches(pkgCache::PkgIterator pkg, pkgCache::VerIterator ver)
+  pkg_or_matcher(pkg_matcher *_left, pkg_matcher *_right)
+    :left(_left),right(_right)
+  {
+  }
+
+  bool matches(const pkgCache::PkgIterator &pkg,
+	       const pkgCache::VerIterator &ver)
   {
     return left->matches(pkg, ver) || right->matches(pkg, ver);
   }
-  ~pkg_or_matcher() {delete left; delete right;}
+
+  pkg_match_result *get_match(const pkgCache::PkgIterator &pkg,
+			      const pkgCache::VerIterator &ver)
+  {
+    pkg_match_result *lr = left->get_match(pkg, ver);
+
+    if(lr != NULL)
+      return lr;
+    else
+      return right->get_match(pkg, ver);
+  }
+
+  ~pkg_or_matcher()
+  {
+    delete left;
+    delete right;
+  }
 };
 
 class pkg_and_matcher:public pkg_matcher
 {
   pkg_matcher *left,*right;
 public:
-  pkg_and_matcher(pkg_matcher *_left, pkg_matcher *_right):left(_left),right(_right) {}
-  bool matches(pkgCache::PkgIterator pkg, pkgCache::VerIterator ver)
+  pkg_and_matcher(pkg_matcher *_left, pkg_matcher *_right)
+    :left(_left),right(_right)
+  {
+  }
+
+  bool matches(const pkgCache::PkgIterator &pkg,
+	       const pkgCache::VerIterator &ver)
   {
     return left->matches(pkg, ver) && right->matches(pkg, ver);
   }
-  ~pkg_and_matcher() {delete left; delete right;}
+
+  pkg_match_result *get_match(const pkgCache::PkgIterator &pkg,
+			      const pkgCache::VerIterator &ver)
+  {
+    pkg_match_result *r1 = left->get_match(pkg, ver);
+
+    if(r1 == NULL)
+      return NULL;
+
+    pkg_match_result *r2 = right->get_match(pkg, ver);
+
+    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
 {
   pkg_matcher *child;
 public:
-  pkg_not_matcher(pkg_matcher *_child):child(_child) {}
-  bool matches(pkgCache::PkgIterator pkg, pkgCache::VerIterator ver)
+  pkg_not_matcher(pkg_matcher *_child)
+    :child(_child)
+  {
+  }
+
+  bool matches(const pkgCache::PkgIterator &pkg,
+	       const pkgCache::VerIterator &ver)
   {
     return !child->matches(pkg, ver);
   }
+
+  // Eh, there isn't really a good choice about what to return here...
+  // just return what the child does. :(
+  pkg_match_result *get_match(const pkgCache::PkgIterator &pkg,
+			      const pkgCache::VerIterator &ver)
+  {
+    return child->get_match(pkg, ver);
+  }
+
   ~pkg_not_matcher() {delete child;}
 };
 
@@ -473,13 +1053,23 @@
 public:
   pkg_garbage_matcher() {}
 
-  bool matches(pkgCache::PkgIterator pkg, pkgCache::VerIterator ver)
+  bool matches(const pkgCache::PkgIterator &pkg,
+	       const pkgCache::VerIterator &ver)
   {
     if(ver.end())
       return false;
     else
       return (*apt_cache_file)->get_ext_state(pkg).garbage;
   }
+
+  pkg_match_result *get_match(const pkgCache::PkgIterator &pkg,
+			      const pkgCache::VerIterator &ver)
+  {
+    if(!matches(pkg, ver))
+      return NULL;
+    else
+      return new unitary_result(_("Garbage"));
+  }
 };
 
 // A dummy matcher that matches any package.
@@ -488,10 +1078,17 @@
 public:
   pkg_true_matcher() {}
 
-  bool matches(pkgCache::PkgIterator pkg, pkgCache::VerIterator ver)
+  bool matches(const pkgCache::PkgIterator &pkg,
+	       const pkgCache::VerIterator &ver)
   {
     return true;
   }
+
+  pkg_match_result *get_match(const pkgCache::PkgIterator &pkg,
+			      const pkgCache::VerIterator &ver)
+  {
+    return new empty_match_result;
+  }
 };
 
 // A dummy matcher that matches no packages.
@@ -500,10 +1097,17 @@
 public:
   pkg_false_matcher() {}
 
-  bool matches(pkgCache::PkgIterator pkg, pkgCache::VerIterator ver)
+  bool matches(const pkgCache::PkgIterator &pkg,
+	       const pkgCache::VerIterator &ver)
   {
     return false;
   }
+
+  pkg_match_result *get_match(const pkgCache::PkgIterator &pkg,
+			      const pkgCache::VerIterator &ver)
+  {
+    return NULL;
+  }
 };
 
 // Matches packages which have a dependency of the given type declared
@@ -516,7 +1120,8 @@
   pkg_matcher *pattern;
 public:
   pkg_revdep_matcher(pkgCache::Dep::DepType _type,
-		     pkg_matcher *_pattern):type(_type), pattern(_pattern)
+		     pkg_matcher *_pattern)
+    :type(_type), pattern(_pattern)
   {
   }
 
@@ -525,7 +1130,8 @@
     delete pattern;
   }
 
-  bool matches(pkgCache::PkgIterator pkg, pkgCache::VerIterator ver)
+  bool matches(const pkgCache::PkgIterator &pkg,
+	       const pkgCache::VerIterator &ver)
   {
     // Check direct dependencies.
     for(pkgCache::DepIterator d=pkg.RevDependsList(); !d.end(); ++d)
@@ -555,6 +1161,52 @@
 
     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)
+  {
+    // Check direct dependencies.
+    for(pkgCache::DepIterator d=pkg.RevDependsList(); !d.end(); ++d)
+      {
+	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());
+
+	    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())
+		{
+		  pkg_match_result *r = pattern->get_match(d.ParentPkg(),
+							   d.ParentVer());
+		  if(r != NULL)
+		    return new result_pair(r, dep_match(d));
+		}
+	    }
+	}
+
+    return NULL;
+  }
 };
 
 
@@ -565,14 +1217,18 @@
 {
   pkg_matcher *pattern;
 public:
-  pkg_provides_matcher(pkg_matcher *_pattern):pattern(_pattern) {}
+  pkg_provides_matcher(pkg_matcher *_pattern)
+    :pattern(_pattern)
+  {
+  }
 
   ~pkg_provides_matcher() 
   {
     delete pattern;
   }
 
-  bool matches(pkgCache::PkgIterator pkg, pkgCache::VerIterator ver)
+  bool matches(const pkgCache::PkgIterator &pkg,
+	       const pkgCache::VerIterator &ver)
   {
     if(ver.end())
       return false;
@@ -586,6 +1242,23 @@
 
     return false;
   }
+
+  pkg_match_result *get_match(const pkgCache::PkgIterator &pkg,
+			      const pkgCache::VerIterator &ver)
+  {
+    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(*apt_cache_file));
+
+	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
@@ -595,14 +1268,18 @@
 {
   pkg_matcher *pattern;
 public:
-  pkg_revprv_matcher(pkg_matcher *_pattern):pattern(_pattern) {}
+  pkg_revprv_matcher(pkg_matcher *_pattern)
+    :pattern(_pattern) 
+  {
+  }
 
   ~pkg_revprv_matcher() 
   {
     delete pattern;
   }
 
-  bool matches(pkgCache::PkgIterator pkg, pkgCache::VerIterator ver)
+  bool matches(const pkgCache::PkgIterator &pkg,
+	       const pkgCache::VerIterator &ver)
   {
     for(pkgCache::PrvIterator p=pkg.ProvidesList(); !p.end(); ++p)
       {
@@ -612,21 +1289,40 @@
 
     return false;
   }
+
+  pkg_match_result *get_match(const pkgCache::PkgIterator &pkg,
+			      const pkgCache::VerIterator &ver)
+  {
+    for(pkgCache::PrvIterator p=pkg.ProvidesList(); !p.end(); ++p)
+      {
+	pkg_match_result *r = pattern->get_match(p.OwnerPkg(),
+						 p.OwnerVer());
+
+	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 of the given type on.
+// an "important" dependency on.
 //
 // Note that the notion of "importantness" is affected by the current
 // settings!
 class pkg_norevdep_matcher:public pkg_matcher
 {
 public:
-  pkg_norevdep_matcher() {}
+  pkg_norevdep_matcher()
+  {
+  }
 
-  bool matches(pkgCache::PkgIterator pkg, pkgCache::VerIterator ver)
+  bool matches(const pkgCache::PkgIterator &pkg,
+	       const pkgCache::VerIterator &ver)
   {
     if(ver.end())
       return false;
@@ -646,6 +1342,15 @@
         return true;
       }
   }
+
+  pkg_match_result *get_match(const pkgCache::PkgIterator &pkg,
+			      const pkgCache::VerIterator &ver)
+  {
+    if(!matches(pkg, ver))
+      return NULL;
+    else
+      return new unitary_result(_("No reverse dependencies"));
+  }
 };
 
 // Matches (non-virtual) packages which no installed package declares
@@ -655,9 +1360,12 @@
   pkgCache::Dep::DepType type; // Which type to match
 public:
   pkg_norevdep_type_matcher(pkgCache::Dep::DepType _type)
-    :type(_type) {}
+    :type(_type)
+  {
+  }
 
-  bool matches(pkgCache::PkgIterator pkg, pkgCache::VerIterator ver)
+  bool matches(const pkgCache::PkgIterator &pkg,
+	       const pkgCache::VerIterator &ver)
   {
     if(ver.end())
       return false;
@@ -677,12 +1385,22 @@
       }
     return true;
   }
+
+  pkg_match_result *get_match(const pkgCache::PkgIterator &pkg,
+			      const pkgCache::VerIterator &ver)
+  {
+    if(!matches(pkg, ver))
+      return NULL;
+    else
+      return new unitary_result(pkgCache::DepType(type));
+  }
 };
 
 class pkg_new_matcher:public pkg_matcher
 {
 public:
-  bool matches(pkgCache::PkgIterator pkg, pkgCache::VerIterator ver)
+  bool matches(const pkgCache::PkgIterator &pkg,
+	       const pkgCache::VerIterator &ver)
   {
     // Don't match virtual packages.
     if(pkg.VersionList().end())
@@ -690,20 +1408,39 @@
     else
       return (*apt_cache_file)->get_ext_state(pkg).new_package;
   }
+
+  pkg_match_result *get_match(const pkgCache::PkgIterator &pkg,
+			      const pkgCache::VerIterator &ver)
+  {
+    if(!matches(pkg, ver))
+      return NULL;
+    else
+      return new unitary_result(_("New Package"));
+  }
 };
 
 class pkg_upgradable_matcher:public pkg_matcher
 {
 public:
-  bool matches(pkgCache::PkgIterator pkg, pkgCache::VerIterator ver)
+  bool matches(const pkgCache::PkgIterator &pkg,
+	       const pkgCache::VerIterator &ver)
   {
     return !pkg.CurrentVer().end() && (*apt_cache_file)[pkg].Upgradable();
   }
+
+  pkg_match_result *get_match(const pkgCache::PkgIterator &pkg,
+			      const pkgCache::VerIterator &ver)
+  {
+    if(!matches(pkg, ver))
+      return NULL;
+    else
+      return new unitary_result(_("Upgradable"));
+  }
 };
 
 // Parses a dependency type.  Returns (ick) -1 if the type is not
 // recognized.
-pkgCache::Dep::DepType parse_deptype(string s)
+pkgCache::Dep::DepType parse_deptype(const string &s)
 {
   if(!strcasecmp(s.c_str(), "depends"))
     return pkgCache::Dep::Depends;
@@ -721,14 +1458,14 @@
     return (pkgCache::Dep::DepType) -1;
 }
 
-pkg_matcher *parse_condition_list(string s, unsigned int &loc,
-				  bool match_descriptions, bool flag_errors);
+pkg_matcher *parse_condition_list(const string &s, unsigned int &loc,
+				  bool match_descriptions);
 
 // 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 s, unsigned int &loc)
+std::string parse_substr(const string &s, unsigned int &loc)
 {
   std::string rval="";
   bool done=false;
@@ -764,8 +1501,8 @@
   return rval;
 }
 
-pkg_matcher *parse_atom(string s, unsigned int &loc,
-			bool search_descriptions, bool flag_errors)
+pkg_matcher *parse_atom(const string &s, unsigned int &loc,
+			bool search_descriptions)
 {
   std::string substr;
 
@@ -777,32 +1514,21 @@
       if(s[loc]=='!')
 	{
 	  loc++;
-	  pkg_matcher *atom=parse_atom(s, loc, search_descriptions, flag_errors);
-	  if(!atom)
-	    return NULL;
-	  return new pkg_not_matcher(atom);
+	  return new pkg_not_matcher(parse_atom(s, loc, search_descriptions));
 	}
       else if(s[loc]=='(')
 	// Recur into the list
 	{
 	  loc++;
-	  pkg_matcher *lst=parse_condition_list(s, loc,
-						search_descriptions,
-						flag_errors);
-	  if(!lst)
-	    return NULL;
+	  auto_ptr<pkg_matcher> lst(parse_condition_list(s, loc,
+							 search_descriptions));
 
 	  if(!(loc<s.size() && s[loc]==')'))
-	    {
-	      if(flag_errors)
-		_error->Error(_("Unmatched '('"));
-	      delete lst;
-	      return NULL;
-	    }
+	    throw CompilationException(_("Unmatched '('"));
 	  else
 	    {
 	      loc++;
-	      return lst;
+	      return lst.release();
 	    }
 	}
       else if(s[loc]=='~')
@@ -810,7 +1536,16 @@
 	  if(loc+1==s.size())
 	    {
 	      loc++;
-	      return pkg_name_matcher::init("~", flag_errors);
+	      if(!search_descriptions)
+		return new pkg_name_matcher("~");
+	      else
+		{
+		  auto_ptr<pkg_matcher> name(new pkg_name_matcher("~"));
+		  auto_ptr<pkg_matcher> desc(new pkg_description_matcher(substr));
+
+		  return new pkg_or_matcher(name.release(),
+					    desc.release());
+		}
 	    }
 	  else
 	    {
@@ -847,17 +1582,14 @@
 		case 'P':
 		case 'C':
 		  {
-		    pkg_matcher *m=parse_atom(s, loc, search_descriptions, flag_errors);
-
-		    if(!m)
-		      return NULL;
+		    auto_ptr<pkg_matcher> m(parse_atom(s, loc, search_descriptions));
 		    
 		    switch(s[prevloc+1])
 		      {
 		      case 'C':
-			return new pkg_dep_matcher(pkgCache::Dep::Conflicts, m);
+			return new pkg_dep_matcher(pkgCache::Dep::Conflicts, m.release());
 		      case 'P':
-			return new pkg_provides_matcher(m);
+			return new pkg_provides_matcher(m.release());
 		      }
 		  }
 		case 'D':
@@ -883,34 +1615,25 @@
 			    type=parse_deptype(tname.c_str());
 
 			    if(type==-1)
-			      {
-				char buf[512];
-				snprintf(buf, 512, _("Unknown dependency type: %s"),
-					 tname.c_str());
-				if(flag_errors)
-				  _error->Error("%s", buf);
-				return NULL;
-			      }
+			      throw CompilationException(_("Unknown dependency type: %s"),
+							 tname.c_str());
 			  }
 		      }
 
-		    pkg_matcher *m=parse_atom(s, loc, search_descriptions, flag_errors);
-
-		    if(!m)
-		      return NULL;
+		    auto_ptr<pkg_matcher> m(parse_atom(s, loc, search_descriptions));
 
 		    switch(s[prevloc+1])
 		      {
 		      case 'D':
 			if(do_provides)
-			  return new pkg_provides_matcher(m);
+			  return new pkg_provides_matcher(m.release());
 			else
-			  return new pkg_dep_matcher(type, m);
+			  return new pkg_dep_matcher(type, m.release());
 		      case 'R':
 			if(do_provides)
-			  return new pkg_revprv_matcher(m);
+			  return new pkg_revprv_matcher(m.release());
 			else
-			  return new pkg_revdep_matcher(type, m);
+			  return new pkg_revdep_matcher(type, m.release());
 		      }
 		  }
 		default:
@@ -946,17 +1669,11 @@
 			  return new pkg_keep_matcher;
 
 			else
-			  {
-			    char buf[512];
-			    snprintf(buf, 512, _("Unknown action type: %s"),
-				     substr.c_str());
-			    if(flag_errors)
-			      _error->Error("%s", buf);
-			    return NULL;
-			  }
+			  throw CompilationException(_("Unknown action type: %s"),
+						     substr.c_str());
 		      }
 		    case 'A':
-		      return pkg_archive_matcher::init(substr, flag_errors);
+		      return new pkg_archive_matcher(substr);
 		    case 'B':
 		      {
 			pkgCache::Dep::DepType ptype=parse_deptype(substr);
@@ -964,25 +1681,19 @@
 			if(ptype!=-1)
 			  return new pkg_broken_type_matcher(ptype);
 			else
-			  {
-			    char buf[512];
-			    snprintf(buf, 512, _("Unknown dependency type: %s"),
-				     substr.c_str());
-			    if(flag_errors)
-			      _error->Error("%s", buf);
-			    return NULL;
-			  }
+			  throw CompilationException(_("Unknown dependency type: %s"),
+						     substr.c_str());
 		      }
 		    case 'd':
-		      return pkg_description_matcher::init(substr, flag_errors);
+		      return new pkg_description_matcher(substr);
 		    case 'F':
 		      return new pkg_false_matcher;
 		    case 'm':
-		      return pkg_maintainer_matcher::init(substr, flag_errors);
+		      return new pkg_maintainer_matcher(substr);
 		    case 'n':
-		      return pkg_name_matcher::init(substr, flag_errors);
+		      return new pkg_name_matcher(substr);
 		    case 'O':
-		      return pkg_origin_matcher::init(substr, flag_errors);
+		      return new pkg_origin_matcher(substr);
 		    case 'p':
 		      {
 			pkgCache::State::VerPriority type;
@@ -1010,38 +1721,21 @@
 				 strcasecmp(s, (*apt_cache_file)->GetCache().Priority(pkgCache::State::Extra)) == 0))
 			  type=pkgCache::State::Extra;
 			else
-			  {
-			    if(flag_errors)
-			      {
-				char buf[512];
-
-				snprintf(buf, 512, _("Unknown priority %s"),
-					 substr.c_str());
-
-				_error->Error("%s", buf);
-			      }
-
-			    return NULL;
-			  }
+			  throw CompilationException(_("Unknown priority %s"),
+						     substr.c_str());
 
 			return new pkg_priority_matcher(type);
 		      }
 		    case 's':
-		      return pkg_section_matcher::init(substr, flag_errors);
+		      return new pkg_section_matcher(substr);
 		    case 't':
-		      return pkg_task_matcher::init(substr, flag_errors);
+		      return new pkg_task_matcher(substr);
 		    case 'T':
 		      return new pkg_true_matcher;
 		    case 'V':
-		      return pkg_version_matcher::init(substr, flag_errors);
+		      return new pkg_version_matcher(substr);
 		    default:
-		      {
-			char buf[512];
-			snprintf(buf, 512, _("Unknown pattern type: %c"), s[1]);
-			if(flag_errors)
-			  _error->Error("%s", buf);
-			return NULL;
-		      }
+		      throw CompilationException(_("Unknown pattern type: %c"), s[1]);
 		    }
 		}
 	    }
@@ -1049,73 +1743,54 @@
       else
 	{
 	  if(!search_descriptions)
-	    return pkg_name_matcher::init(parse_substr(s, loc),
-					  flag_errors);
+	    return new pkg_name_matcher(parse_substr(s, loc));
 	  else
 	    {
 	      substr=parse_substr(s, loc);
-	      pkg_matcher *name=pkg_name_matcher::init(substr,
-						       flag_errors);
-	      pkg_matcher *desc=pkg_description_matcher::init(substr,
-							      flag_errors);
+	      auto_ptr<pkg_matcher> name(new pkg_name_matcher(substr));
+	      auto_ptr<pkg_matcher> desc(new pkg_description_matcher(substr));
 
-	      if(!name || !desc)
-		{
-		  delete name;
-		  delete desc;
-		  return NULL;
-		}
-	      else
-		return new pkg_or_matcher(name, desc);
+	      return new pkg_or_matcher(name.release(),
+					desc.release());
 	    }
 	}
     }
 
   // If we get here, the string was empty.
-  return NULL;
+  throw CompilationException(_("Can't search for \"\""));
 }
 
-pkg_matcher *parse_and_group(string s, unsigned int &loc,
-			     bool search_descriptions,
-			     bool flag_errors)
+pkg_matcher *parse_and_group(const string &s, unsigned int &loc,
+			     bool search_descriptions)
 {
-  pkg_matcher *rval=NULL;
+  auto_ptr<pkg_matcher> rval(NULL);
   while(loc<s.size() && isspace(s[loc]))
     loc++;
 
   while(loc<s.size() && s[loc]!='|' && s[loc]!=')')
     {
-      pkg_matcher *atom=parse_atom(s, loc,
-				   search_descriptions, flag_errors);
-      if(!atom)
-	{
-	  delete rval;
-	  return NULL;
-	}
+      auto_ptr<pkg_matcher> atom(parse_atom(s, loc,
+					    search_descriptions));
 
-      if(rval==NULL)
-	rval=atom;
+      if(rval.get()==NULL)
+	rval = atom;
       else
-	rval=new pkg_and_matcher(rval, atom);
+	rval = auto_ptr<pkg_matcher>(new pkg_and_matcher(rval.release(), atom.release()));
 
       while(loc<s.size() && isspace(s[loc]))
 	loc++;
     }
-  if(rval==NULL && flag_errors)
-    _error->Error(_("Unexpected empty expression"));
-  return rval;
+
+  if(rval.get()==NULL)
+    throw CompilationException(_("Unexpected empty expression"));
+
+  return rval.release();
 }
 
-pkg_matcher *parse_condition_list(string s, unsigned int &loc,
-				  bool search_descriptions,
-				  bool flag_errors)
-  // This (and the grammer) should be optimized to avoid deep recursion
-  // what does the above line mean? -- DNB 11/21/01
-{
-  pkg_matcher *grp=parse_and_group(s, loc, search_descriptions,
-				   flag_errors);
-  if(!grp)
-    return NULL;
+pkg_matcher *parse_condition_list(const string &s, unsigned int &loc,
+				  bool search_descriptions)
+{
+  auto_ptr<pkg_matcher> grp(parse_and_group(s, loc, search_descriptions));
 
   while(loc<s.size() && isspace(s[loc]))
     loc++;
@@ -1125,33 +1800,25 @@
       if(loc<s.size() && s[loc]=='|')
 	{
 	  loc++;
-	  pkg_matcher *grp2=parse_condition_list(s, loc,
-						 search_descriptions,
-						 flag_errors);
-	  if(!grp2)
-	    {
-	      delete grp;
-	      return NULL;
-	    }
-	  return new pkg_or_matcher(grp, grp2);
+	  auto_ptr<pkg_matcher> grp2(parse_condition_list(s, loc,
+							  search_descriptions));
+
+	  return new pkg_or_matcher(grp.release(), grp2.release());
 	}
       else
-	// We should never come here in a well-formed expression
-	{
-	  delete grp;
-	  if(flag_errors)
-	    _error->Error(_("Badly formed expression"));
-	  return NULL;
-	}
-      // Or here.
+	throw CompilationException(_("Badly formed expression"));
+
+      // Note that this code should never execute:
       while(loc<s.size() && isspace(s[loc]))
 	loc++;
     }
-  // Nothing else?  Well..
-  return grp;
+
+  // If there's no second element in the condition list, return its
+  // head.
+  return grp.release();
 }
 
-pkg_matcher *parse_pattern(string s,
+pkg_matcher *parse_pattern(const string &s,
 			   bool search_descriptions, bool flag_errors)
 {
   unsigned int loc=0;
@@ -1163,19 +1830,21 @@
   if(loc==s.size())
     return NULL;
 
-  pkg_matcher *rval=parse_condition_list(s, loc,
-					 search_descriptions,
-					 flag_errors);
+  try
+    {
+      auto_ptr<pkg_matcher> rval(parse_condition_list(s, loc,
+						      search_descriptions));
 
-  if(rval==NULL)
-    return NULL;
-  else if(loc!=s.size())
+      if(loc!=s.size())
+	throw CompilationException(_("Unexpected ')'"));
+      else
+	return rval.release();
+    }
+  catch(CompilationException e)
     {
       if(flag_errors)
-	_error->Error(_("Unexpected ')'"));
-      delete rval;
+	_error->Error("%s", e.get_msg().c_str());
+
       return NULL;
     }
-  else
-    return rval;
 }

Modified: branches/aptitude-0.3/aptitude/src/generic/matchers.h
==============================================================================
--- branches/aptitude-0.3/aptitude/src/generic/matchers.h	(original)
+++ branches/aptitude-0.3/aptitude/src/generic/matchers.h	Sat Jul 23 19:11:02 2005
@@ -25,13 +25,35 @@
 
 #include <apt-pkg/pkgcache.h>
 
+/* For the cases where you want to investigate just what you matched a
+ * bit more thoroughly.
+ *
+ * A "group" is a string associated with this match.  Matches may have
+ * any nonnegative number of groups, including 0 (in the case that
+ * there's no interesting string associated with the match).
+ */
+class pkg_match_result
+{
+public:
+  virtual unsigned int num_groups()=0;
+  virtual const std::string &group(unsigned int n)=0;
+  virtual ~pkg_match_result();
+};
+
 class pkg_matcher
-// A closure 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)
+/** 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)
+ */
 {
 public:
-  virtual bool matches(pkgCache::PkgIterator pkg, pkgCache::VerIterator ver)=0;
+  virtual bool matches(const pkgCache::PkgIterator &pkg,
+		       const pkgCache::VerIterator &ver)=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);
   virtual ~pkg_matcher() {}
 };
 
@@ -47,9 +69,12 @@
  *
  *  \return the new matcher or \b NULL if an error occurs.
  */
-pkg_matcher *parse_pattern(string s, bool search_descriptions=false,
+pkg_matcher *parse_pattern(const string &s,
+			   bool search_descriptions=false,
 			   bool flag_errors=true);
-inline bool pkg_matches(string s, pkgCache::PkgIterator pkg, pkgCache::VerIterator ver)
+inline bool pkg_matches(const string &s,
+			const pkgCache::PkgIterator &pkg,
+			const pkgCache::VerIterator &ver)
 {
   pkg_matcher *m=parse_pattern(s);
   if(!m)



More information about the Aptitude-svn-commit mailing list