#include "zmart-sources.h"
#include "zmart-misc.h"
-using namespace zypp;
+#include <zypp/base/Algorithm.h>
+
using namespace std;
using namespace boost;
+using namespace zypp;
+using namespace zypp::functor;
+using namespace zypp::resfilter;
// TODO get rid of these globals
extern RuntimeData gData;
-extern Settings gSettings;
void ZyppSearchOptions::resolveConflicts() {
if (matchExact()) {
// ??? should we notify user about conflict resolutions?
}
-ZyppSearch::ZyppSearch (const ZyppSearchOptions & options, const vector<string> & qstrings) :
- _options(options), _qstrings(qstrings) {
- init();
-}
-
-// TODO clean this up
-bool ZyppSearch::init () const {
- cond_init_system_sources();
- cond_init_target();
-
- // load additional sources
- for ( std::list<Url>::const_iterator it = gSettings.additional_sources.begin();
- it != gSettings.additional_sources.end(); ++it ) {
- include_source_by_url( *it );
- }
-
- // TODO no sources warning
- if ( gData.sources.empty() ) {
- cerr << "Warning! No sources. Operating only over the installed resolvables."
- " You will not be able to install stuff" << endl;
- }
-
- if (_options.installedFilter() != ZyppSearchOptions::UNINSTALLED_ONLY) {
- cerr_v << "loading target" << endl;
- load_target();
- }
-
- if (_options.installedFilter() != ZyppSearchOptions::INSTALLED_ONLY) {
- cerr_v << "loading sources" << endl;
- load_sources();
+/**
+ * Initializes installation sources, creates search regex, caches installed
+ * packages from RPM database, and populates ResPool with items from
+ * installation sources.
+ */
+ZyppSearch::ZyppSearch (
+ ZYpp::Ptr & zypp,
+ const ZyppSearchOptions & options,
+ const vector<string> qstrings
+ ) :
+ _zypp(zypp), _options(options), _qstrings(qstrings) {
+
+ cond_init_target(); // calls ZYpp::initializeTarget("/");
+ cond_init_system_sources(); // calls manager->restore("/");
+
+ // no sources warning
+ if (gData.sources.empty()) {
+ cerr << "No sources. Zypper currently searches within installation"
+ "sources only." << endl;
+ exit(2); // TODO #define zypper error codes?
}
- return true;
+ setupRegexp();
+ cacheInstalled();
+ load_sources(); // populates ResPool with resolvables from inst. sources
}
-void ZyppSearch::doSearch(const boost::function<void(const PoolItem &)> & f) {
- ResPool pool = getZYpp()->pool();
+/**
+ * Invokes zypp::invokeOnEach() on a subset of pool items restricted by
+ * some search criteria (--type,--match-exact).
+ */
+template <class _Filter, class _Function>
+int ZyppSearch::invokeOnEachSearched(_Filter filter_r, _Function fnc_r) {
+ ResPool pool = _zypp->pool();
// search for specific resolvable type only
if (_options.kind() != Resolvable::Kind()) {
- cerr_vv << "Search by type" << endl;
- setupRegexp();
- for (ResPool::byKind_iterator it = pool.byKindBegin(_options.kind());
- it != pool.byKindEnd(_options.kind()); ++it) {
- if (match(*it)) f(*it);
- }
+ cerr_vv << "invokeOnEachSearched(): search by type" << endl;
+
+ return invokeOnEach(
+ pool.byKindBegin(_options.kind()), pool.byKindEnd(_options.kind()),
+ filter_r, fnc_r);
}
// search for exact package using byName_iterator
// usable only if there is only one query string and if this string
else if (_options.matchExact() && _qstrings.size() == 1 &&
_qstrings[0].find('*') == string::npos &&
_qstrings[0].find('?') == string::npos) {
- cerr_vv << "Exact name match" << endl;
- for (ResPool::byName_iterator it = pool.byNameBegin(_qstrings[0]);
- it != pool.byNameEnd(_qstrings[0]); ++it) {
- f(*it); //table << createRow(*it);
- }
+ cerr_vv << "invokeOnEachSearched(): exact name match" << endl;
+
+ return invokeOnEach(
+ pool.byNameBegin(_qstrings[0]), pool.byNameEnd(_qstrings[0]),
+ filter_r, fnc_r);
}
+
// search among all resolvables
else {
- cerr_vv << "Search among all resolvables" << endl;
- setupRegexp();
- for (ResPool::const_iterator it = pool.begin(); it != pool.end(); ++it) {
- if (match(*it)) f(*it); //table << createRow(*it);
- }
+ cerr_vv << "invokeOnEachSearched(): search among all resolvables" << endl;
+
+ return invokeOnEach(pool.begin(), pool.end(), filter_r, fnc_r);
}
}
+/**
+ * Cache installed packages matching given search criteria into a hash_map.
+ * Assumption made: names of currently installed resolvables + kind
+ * (+version???) are unique.
+ */
+void ZyppSearch::cacheInstalled() {
+ // don't include kind string in hash map key if search is to be restricted
+ // to particular kind (to improve performance a little bit)
+ if (_options.kind() != Resolvable::Kind())
+ _icache.setIncludeKindInKey(false);
+
+ cout_v << "Pre-caching installed resolvables matching given search criteria... " << endl;
+
+ ResStore tgt_resolvables(_zypp->target()->resolvables());
+
+ _zypp->addResolvables(tgt_resolvables, true /*installed*/);
+
+ invokeOnEachSearched(Match(_reg,_options.searchDescriptions()),
+ functorRef<bool,const zypp::PoolItem &>(_icache));
+
+ _zypp->removeResolvables(tgt_resolvables);
+
+ cout_v << _icache.size() << " out of (" << tgt_resolvables.size() << ")"
+ "cached." << endl;
+}
+
+/**
+ * Invokes functor f on each pool item matching search criteria.
+ */
+void ZyppSearch::doSearch(const boost::function<bool(const PoolItem &)> & f) {
+ boost::function<bool (const PoolItem &)> filter;
+
+ switch (_options.installedFilter()) {
+ case ZyppSearchOptions::INSTALLED_ONLY:
+ filter = chain(ByInstalledCache(_icache),Match(_reg,_options.searchDescriptions()));
+ break;
+ case ZyppSearchOptions::UNINSTALLED_ONLY:
+ filter = chain(not_c(ByInstalledCache(_icache)),Match(_reg,_options.searchDescriptions()));
+ break;
+ case ZyppSearchOptions::ALL:
+ default:
+ filter = Match(_reg,_options.searchDescriptions());
+ }
+
+ invokeOnEachSearched(filter, f);
+}
+
//! macro for word boundary tags for regexes
#define WB (_options.matchWords() ? string("\\b") : string())
+// TODO make a regex builder.
/**
- * Creates a regex for searching in resolvable names.
- *
+ * Creates a regex for searching in resolvable names and descriptions.
+ *
* The regex is created according to given search strings and search options.
*
+ * <pre>
* Examples:
* - no search string: .*
* - one search string: .*searchstring.*
* --match-any:
* .*(searchstring1|searchstring2).*
* with --match-words: .*\b(searchstring1|searchstring2)\b.*
+ * </pre>
*/
void ZyppSearch::setupRegexp() {
string regstr;
}
}
- cerr_vv << "using regex: " << regstr << endl;
+ cout_vv << "using regex: " << regstr << endl;
// regex flags
unsigned int flags = boost::regex::normal;
return regexed;
}
-/**
- * Decides whether the pool_item (resolvable) matches the search criteria
- * encoded in regular expression.
- */
-bool ZyppSearch::match(const PoolItem & pool_item) {
- return
- // match resolvable name
- regex_match(pool_item.resolvable()->name(), _reg)
- ||
- // if required, match also summary and description of the resolvable
- (_options.searchDescriptions() ?
- regex_match(pool_item.resolvable()->summary(), _reg) ||
- regex_match(pool_item.resolvable()->description(), _reg)
- :
- false);
-}
-
// Local Variables:
// c-basic-offset: 2
// End:
#define ZYPPERSEARCH_H_
#include <string>
+#include <ext/hash_map>
#include <boost/regex.hpp>
#include <boost/function.hpp>
#include <zypp/ZYpp.h>
+#include "zypper-tabulator.h"
+
/**
- * @brief: search options
+ * Represents zypper search options.
*/
class ZyppSearchOptions {
public:
zypp::Resolvable::Kind _kind;
};
+struct GenericStringHash {
+ size_t operator()(const std::string &str) const {
+ const std::string::size_type size = str.size();
+ size_t h = 0;
+ for(std::string::size_type i = 0; i < size; i++) {
+ h = 5 * h + str[i];
+ }
+ return h;
+ }
+};
+
+typedef __gnu_cxx::hash_map<std::string, zypp::PoolItem, GenericStringHash> PoolItemHash;
+
+/**
+ * Structure for caching installed PoolItems using a hash map.
+ * Name + edition + (if _incl_kind_in_key) kind is used as a key.
+ * The hash map is to be manipulated through addItem() and getItem() methods.
+ */
+struct InstalledCache {
+private:
+ PoolItemHash _items;
+ bool _incl_kind_in_key;
+
+public:
+ InstalledCache(bool incl_kind_in_key = true) :
+ _incl_kind_in_key(incl_kind_in_key)
+ {}
+
+ void setIncludeKindInKey(bool value = true) { _incl_kind_in_key = value; }
+ bool includeKindInKey() { return _incl_kind_in_key; }
+
+ std::string getKey(const zypp::PoolItem & pi) const {
+ return pi.resolvable()->name() + pi.resolvable()->edition().asString() +
+ (_incl_kind_in_key ? pi.resolvable()->kind().asString() : "");
+ }
+
+ void addItem(const zypp::PoolItem & pi) { _items[getKey(pi)] = pi; }
+
+ zypp::PoolItem & getItem(const zypp::PoolItem & pi) {
+ return _items[getKey(pi)];
+ }
+
+ unsigned int size() {
+ return _items.size();
+ }
+
+ /** defined for use as a functor for filling the hashmap in a for_each */
+ bool operator()(const zypp::PoolItem & pi) {
+ addItem(pi);
+ }
+};
+
/**
- *
+ * TODO
*/
class ZyppSearch {
+
public:
- ZyppSearch (const ZyppSearchOptions & options, const std::vector<std::string> & qstrings = std::vector<std::string>());
- void doSearch(const boost::function<void(const zypp::PoolItem &)> & f);
+ ZyppSearch (zypp::ZYpp::Ptr & zypp, const ZyppSearchOptions & options,
+ const std::vector<std::string> qstrings = std::vector<std::string>());
+
+ void doSearch(const boost::function<bool(const zypp::PoolItem &)> & f);
+
+ InstalledCache & installedCache() { return _icache; }
+
+ template <class _Filter, class _Function>
+ int invokeOnEachSearched(_Filter filter_r, _Function fnc_r);
private:
+ zypp::ZYpp::Ptr & _zypp;
const ZyppSearchOptions & _options;
- const std::vector<std::string> & _qstrings;
+ const std::vector<std::string> _qstrings;
boost::regex _reg;
- bool init() const;
+ InstalledCache _icache;
+
void setupRegexp();
+ void cacheInstalled();
std::string wildcards2regex(const std::string & str) const;
- bool match(const zypp::PoolItem & pool_item);
+};
+
+/**
+ * Filter resolvables by their installed status.
+ * This filter does it by comparing resolvables with matching resolvables
+ * from the InstalledCache. A resolvable is considered to be installed if
+ * its name, kind, edition, and architecture matches the one in installed
+ * cache.
+ * <p>
+ * Not an effective filter, surely, but can't find another way to do this.
+ */
+struct ByInstalledCache
+{
+ ByInstalledCache(InstalledCache & icache) :
+ _icache(&icache)
+ {}
+
+ bool operator()(const zypp::PoolItem & pool_item) const {
+ zypp::PoolItem inst_item = _icache->getItem(pool_item);
+ if (inst_item) {
+ // yes, this one is installed, indeed
+ if (inst_item.resolvable()->edition() == pool_item.resolvable()->edition() &&
+ inst_item.resolvable()->arch() == pool_item.resolvable()->arch()) {
+ return true;
+ }
+ // nope, this package is not there on the target (in the InstalledCache)
+ else {
+ return false;
+ }
+ }
+
+ return false;
+ }
+
+ InstalledCache * _icache;
+};
+
+/**
+ * Functor for filling search output table in rug style.
+ */
+struct FillTable
+{
+ FillTable(Table & table, InstalledCache & icache) :
+ _table(&table), _icache(&icache) {
+ TableHeader header;
+ header << "S" << "Catalog" << "Bundle" << "Name" << "Version" << "Arch";
+ *_table << header;
+ }
+
+ bool operator()(const zypp::PoolItem & pool_item) const {
+ TableRow row;
+
+ // add status to the result table
+ zypp::PoolItem inst_item = _icache->getItem(pool_item);
+ if (inst_item) {
+ // check whether the pool item is installed...
+ if (inst_item.resolvable()->edition() == pool_item.resolvable()->edition() &&
+ inst_item.resolvable()->arch() == pool_item.resolvable()->arch())
+ row << "i";
+ // ... or there's just another version of it installed
+ else
+ row << "v";
+ }
+ // or it's not installed at all
+ else {
+ row << "";
+ }
+
+ // add other fields to the result table
+ row << pool_item.resolvable()->source().alias()
+ << "" // TODO what about rug's Bundle?
+ << pool_item.resolvable()->name()
+ << pool_item.resolvable()->edition().asString()
+ << pool_item.resolvable()->arch().asString();
+
+ *_table << row;
+
+ return true;
+ }
+
+ Table * _table;
+
+ InstalledCache * _icache;
+};
+
+/**
+ * Filter functor for Matching PoolItems' names (or also summaries and
+ * descriptions) with a regex created according to search criteria.
+ */
+struct Match {
+ const bool _search_descs;
+ const boost::regex * _regex;
+
+ Match(const boost::regex & regex, bool search_descriptions = false) :
+ _regex(®ex), _search_descs(search_descriptions)
+ {}
+
+ bool operator()(const zypp::PoolItem & pi) const {
+ return
+ // match resolvable name
+ regex_match(pi.resolvable()->name(), *_regex)
+ ||
+ // if required, match also summary and description of the resolvable
+ (_search_descs ?
+ regex_match(pi.resolvable()->summary(), *_regex) ||
+ regex_match(pi.resolvable()->description(), *_regex)
+ :
+ false);
+ }
};
#endif /*ZYPPERSEARCH_H_*/