summaryrefslogtreecommitdiff
path: root/src/generic/apt/matching/match.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/generic/apt/matching/match.cc')
-rw-r--r--src/generic/apt/matching/match.cc153
1 files changed, 141 insertions, 12 deletions
diff --git a/src/generic/apt/matching/match.cc b/src/generic/apt/matching/match.cc
index d5d0c482..a42a6b81 100644
--- a/src/generic/apt/matching/match.cc
+++ b/src/generic/apt/matching/match.cc
@@ -1,6 +1,6 @@
// match.cc
//
-// Copyright (C) 2008-2010 Daniel Burrows
+// Copyright (C) 2008-2011 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
@@ -25,6 +25,7 @@
#include <generic/apt/tags.h>
#include <generic/apt/tasks.h>
#include <generic/util/progress_info.h>
+#include <generic/util/util.h>
#include <apt-pkg/error.h>
#include <apt-pkg/pkgrecords.h>
@@ -48,10 +49,12 @@
#include <algorithm>
#include <boost/scoped_ptr.hpp>
+#include <boost/unordered_map.hpp>
#include "serialize.h"
using aptitude::util::progress_info;
+using boost::unordered_map;
using cwidget::util::transcode;
using cwidget::util::ref_ptr;
@@ -340,6 +343,60 @@ namespace aptitude
// sorted list of the packages it matches.
std::map<std::string, std::vector<Xapian::docid> > matched_term_prefixes;
+ // Stores compiled regular expressions that mimic Xapian's term
+ // matching (specifically, they only match at word boundaries).
+ // Only used if the Xapian database failed to load.
+ unordered_map<std::string, ref_ptr<regex> > term_regexes;
+
+ // Stores compiled regular expressions that mimic Xapian's term
+ // matching (specifically, they only match at word boundaries).
+ // Only used if the Xapian database failed to load.
+ unordered_map<std::string, ref_ptr<regex> > term_prefix_regexes;
+
+ /** \brief Get a regular expression that matches the given
+ * string as a "term".
+ *
+ * Memoizes its return value in term_regexes.
+ */
+ const ref_ptr<regex> &get_term_regex(const std::string &term)
+ {
+ ref_ptr<regex> &result = term_regexes[term];
+
+ if(!result.valid())
+ {
+ const std::string term_regex =
+ term.empty()
+ ? ".*"
+ : "(^|[^[:alnum:]])" + backslash_escape_nonalnum(term) + "($|[^[:alnum:]])";
+
+ result = ref_ptr<regex>(new regex(term_regex, REG_ICASE | REG_EXTENDED | REG_NOSUB));
+ }
+
+ return result;
+ }
+
+ /** \brief Get a regular expression that matches the given
+ * string as a "term prefix".
+ *
+ * Memoizes its return value in term_prefix_regexes.
+ */
+ const ref_ptr<regex> &get_term_prefix_regex(const std::string &term)
+ {
+ ref_ptr<regex> &result = term_regexes[term];
+
+ if(!result.valid())
+ {
+ const std::string term_regex =
+ term.empty()
+ ? ".*"
+ : "(^|[^[:alnum:]])" + backslash_escape_nonalnum(term) + ".*";
+
+ result = ref_ptr<regex>(new regex(term_regex, REG_ICASE | REG_EXTENDED | REG_NOSUB));
+ }
+
+ return result;
+ }
+
public:
implementation()
{
@@ -393,9 +450,42 @@ namespace aptitude
return cached_match->second;
}
- bool term_prefix_matches(const pkgCache::PkgIterator &pkg,
- const std::string &prefix,
- bool debug)
+ bool term_prefix_matches(const matchable &target,
+ const std::string &prefix,
+ aptitudeDepCache &cache,
+ pkgRecords &records,
+ bool debug)
+ {
+ pkgCache::PkgIterator pkg(target.get_package_iterator(cache));
+ if(db.get() != NULL)
+ return xapian_term_prefix_matches(pkg, prefix, debug);
+
+
+ // If we don't have a Xapian database, fake it by checking the
+ // package's name and description.
+ //
+ // Note that this is not a perfectly faithful representation
+ // of what Xapian would do: most notably, it omits special
+ // prefix handling (e.g., XP for package names).
+
+ const ref_ptr<regex> &term_prefix_regex = get_term_prefix_regex(prefix);
+
+ if(term_prefix_regex->exec(pkg.Name()))
+ return true;
+ else if(!target.get_has_version())
+ return false;
+ else
+ {
+ pkgCache::VerIterator ver = target.get_version_iterator(cache);
+
+ return term_prefix_regex->exec(transcode(get_long_description(ver, &records)));
+ }
+ }
+
+ private:
+ bool xapian_term_prefix_matches(const pkgCache::PkgIterator &pkg,
+ const std::string &prefix,
+ bool debug)
{
if(debug)
std::cout << "Searching for " << prefix << " as a term pefix." << std::endl;
@@ -463,10 +553,43 @@ namespace aptitude
}
}
- bool term_matches(const pkgCache::PkgIterator &pkg,
+ public:
+ bool term_matches(const matchable &target,
const std::string &term,
+ aptitudeDepCache &cache,
+ pkgRecords &records,
bool debug)
{
+ pkgCache::PkgIterator pkg(target.get_package_iterator(cache));
+ if(db.get() != NULL)
+ return xapian_term_matches(pkg, term, debug);
+
+ // If we don't have a Xapian database, fake it by checking the
+ // package's name and description.
+ //
+ // Note that this is not a perfectly faithful representation
+ // of what Xapian would do: most notably, it omits special
+ // prefix handling (e.g., XP for package names).
+
+ const ref_ptr<regex> &term_regex = get_term_regex(term);
+
+ if(term_regex->exec(pkg.Name()))
+ return true;
+ else if(!target.get_has_version())
+ return false;
+ else
+ {
+ pkgCache::VerIterator ver = target.get_version_iterator(cache);
+
+ return term_regex->exec(transcode(get_long_description(ver, &records)));
+ }
+ }
+
+ private:
+ bool xapian_term_matches(const pkgCache::PkgIterator &pkg,
+ const std::string &term,
+ bool debug)
+ {
Xapian::docid pkg_docid(get_docid_by_name(*db, pkg.Name()));
const std::map<std::string, std::vector<Xapian::docid> >::iterator
@@ -520,6 +643,8 @@ namespace aptitude
}
}
+ public:
+
const xapian_info &get_toplevel_xapian_info(const ref_ptr<pattern> &toplevel,
bool debug)
{
@@ -1443,7 +1568,7 @@ namespace aptitude
#endif
if(tags == NULL)
- return false;
+ return NULL;
for(std::set<tag>::const_iterator i=tags->begin(); i!=tags->end(); ++i)
{
@@ -1495,9 +1620,11 @@ namespace aptitude
case pattern::term:
{
- pkgCache::PkgIterator pkg(target.get_package_iterator(cache));
-
- if(search_info->term_matches(pkg, p->get_term_term(), debug))
+ if(search_info->term_matches(target,
+ p->get_term_term(),
+ cache,
+ records,
+ debug))
return match::make_atomic(p);
else
return NULL;
@@ -1506,9 +1633,11 @@ namespace aptitude
case pattern::term_prefix:
{
- pkgCache::PkgIterator pkg(target.get_package_iterator(cache));
-
- if(search_info->term_prefix_matches(pkg, p->get_term_prefix_term(), debug))
+ if(search_info->term_prefix_matches(target,
+ p->get_term_prefix_term(),
+ cache,
+ records,
+ debug))
return match::make_atomic(p);
else
return NULL;