1 /*---------------------------------------------------------------------\
3 | |__ / \ / / . \ . \ |
8 \---------------------------------------------------------------------*/
9 /** \file zypp/PoolQuery.cc
18 #include "zypp/base/Logger.h"
19 #include "zypp/base/PtrTypes.h"
20 #include "zypp/base/DefaultIntegral.h"
21 #include "zypp/base/Regex.h"
22 #include "zypp/base/Algorithm.h"
23 #include "zypp/base/UserRequestException.h"
24 #include "zypp/repo/RepoException.h"
26 #include "zypp/sat/Pool.h"
27 #include "zypp/sat/Solvable.h"
28 #include "zypp/sat/SolvAttr.h"
29 #include "zypp/sat/detail/PoolImpl.h"
31 #include "zypp/PoolQuery.h"
34 using namespace zypp::sat;
36 ///////////////////////////////////////////////////////////////////
38 { /////////////////////////////////////////////////////////////////
40 ///////////////////////////////////////////////////////////////////
42 // CLASS NAME : PoolQuery::Impl
48 : _flags( SEARCH_ALL_REPOS | SEARCH_NOCASE | SEARCH_SUBSTRING )
50 , _match_word(false), _use_wildcards(false)
60 static int repo_search_cb(void *cbdata, ::Solvable *s, ::Repodata *data, ::Repokey *key, ::KeyValue *kv)
62 PoolQuery *me = (PoolQuery*) cbdata;
66 sat::Solvable solvable(s - sat::Pool::instance().get()->solvables);
68 // now filter by kind here (we cant do it before)
69 if ( ! me->_pimpl->_kinds.empty() )
71 // the user wants to filter by kind.
72 if ( find( me->_pimpl->_kinds.begin(),
73 me->_pimpl->_kinds.end(),
75 == me->_pimpl->_kinds.end() )
77 // we did not find the kind in the list
78 // so this is not a result.
79 return SEARCH_NEXT_SOLVABLE;
84 r = me->_pimpl->_fnc( solvable );//makeResObject(solvable) );
88 return SEARCH_NEXT_SOLVABLE;
91 ResultIterator begin();
94 string asString() const;
98 string createRegex(vector<string> & container);
101 /** Raw search strings. */
102 vector<string> _strings;
103 /** Regex-compiled search strings. */
105 /** Raw attributes */
107 /** Regex-compiled attributes */
108 CompiledAttrMap _rcattrs;
110 /** Repos to search. */
111 vector<string> _repos;
112 /** Kinds to search */
113 vector<Resolvable::Kind> _kinds;
115 /** Sat solver search flags */
117 /** Backup of search flags. compile() may change the flags if needed, so
118 * in order to reuse the query, the original flags need to be stored
119 * at the start of compile() */
121 /** Sat solver status flags */
129 /** Sat solver Dataiterator structure */
130 RepoDataIterator _rdit;
134 /** Function for processing found solvables. Used in execute(). */
135 mutable PoolQuery::ProcessResolvable _fnc;
137 friend Impl * rwcowClone<Impl>( const Impl * rhs );
138 /** clone for RWCOW_pointer */
140 { return new Impl( *this ); }
143 template <class _OutputIterator>
144 struct CollectNonEmpty
146 CollectNonEmpty( _OutputIterator iter_r ) : _iter( iter_r ) {}
149 bool operator()( const _Tp & value_r ) const
158 mutable _OutputIterator _iter;
161 void PoolQuery::Impl::compile()
166 // 'different' - will have to iterate through all and match by ourselves (slow)
167 // 'same' - will pass the compiled string to dataiterator_init
168 // 'one-attr' - will pass it to dataiterator_init
169 // 'one-non-regex-str' - will pass to dataiterator_init, set flag to SEARCH_STRING or SEARCH_SUBSTRING
174 // create regex; store in _rcstrings; if more strings flag regex;
177 _rcstrings = createRegex(_strings);
178 if (_strings.size() > 1)
179 _flags = (_flags & ~SEARCH_STRINGMASK) | SEARCH_REGEX;//setMatchRegex();
183 // else if _attrs is not empty but it contains just one attr
184 // for all _strings and _attr[key] strings
185 // create regex; store in _rcattrs; flag 'one-attr'; if more strings flag regex;
186 else if (_attrs.size() == 1)
188 vector<string> joined;
189 for(vector<string>::const_iterator it = _strings.begin(); it != _strings.end(); ++it)
191 joined.push_back(*it);
192 for(vector<string>::const_iterator it = _attrs.begin()->second.begin(); it != _attrs.begin()->second.end(); ++it)
194 joined.push_back(*it);
195 _rcstrings = createRegex(joined);
196 _rcattrs.insert(pair<sat::SolvAttr, string>(_attrs.begin()->first, string()));
200 // // MULTIPLE ATTRIBUTES
203 bool attrvals_empty = true;
204 for (AttrMap::const_iterator ai = _attrs.begin(); ai != _attrs.end(); ++ai)
205 if (!ai->second.empty())
206 for(vector<string>::const_iterator it = ai->second.begin();
207 it != ai->second.end(); it++)
210 attrvals_empty = false;
211 goto attremptycheckend;
216 bool attrvals_thesame = true;
217 for (AttrMap::const_iterator ai = _attrs.begin(); ai != _attrs.end(); ++ai)
222 // // THE SAME STRINGS FOR DIFFERENT ATTRS
223 // else if _attrs is not empty but it does not contain strings
224 // for each key in _attrs take all _strings
225 // create regex; store in _rcattrs and _rcstrings; flag 'same'; if more strings flag regex;
226 if (attrvals_empty || attrvals_thesame)
230 // compile the search string
231 vector<string> joined;
232 for(vector<string>::const_iterator it = _strings.begin(); it != _strings.end(); ++it)
234 joined.push_back(*it);
235 _rcstrings = createRegex(joined);
237 // copy the _attrs keys to _rcattrs
238 for (AttrMap::const_iterator ai = _attrs.begin(); ai != _attrs.end(); ++ai)
239 _rcattrs.insert(pair<sat::SolvAttr, string>(ai->first, string()));
242 // // DIFFERENT STRINGS FOR DIFFERENT ATTRS
243 // if _attrs is not empty and it contains non-empty vectors with non-empty strings
244 // for each key in _attrs take all _strings + all _attrs[key] strings
245 // create regex; store in _rcattrs; flag 'different'; if more strings flag regex;
252 // tell the Dataiterator to search only in one repo if only one specified
253 if (_repos.size() == 1)
254 _flags &= ~SEARCH_ALL_REPOS;
258 DBG << asString() << endl;
262 * Converts '*' and '?' wildcards within str into their regex equivalents.
264 static string wildcards2regex(const string & str)
266 string regexed = str;
268 str::regex all("\\*"); // regex to search for '*'
269 str::regex one("\\?"); // regex to search for '?'
270 string r_all(".*"); // regex equivalent of '*'
271 string r_one("."); // regex equivalent of '?'
272 string::size_type pos;
274 // replace all "*" in input with ".*"
275 for (pos = 0; (pos = regexed.find("*", pos)) != std::string::npos; pos+=2)
276 regexed = regexed.replace(pos, 1, r_all);
278 // replace all "?" in input with "."
279 for (pos = 0; (pos = regexed.find('?', pos)) != std::string::npos; ++pos)
280 regexed = regexed.replace(pos, 1, r_one);
282 DBG << " -> " << regexed << endl;
287 //! macro for word boundary tags for regexes
288 #define WB (_match_word ? string("\\b") : string())
290 string PoolQuery::Impl::createRegex(vector<string> & container)
294 if (container.empty())
297 if (container.size() == 1)
299 rstr = *container.begin();
301 if (!_use_wildcards && ((_flags & SEARCH_STRINGMASK) != SEARCH_REGEX))
306 rstr = wildcards2regex(rstr);
308 if (_flags & SEARCH_STRING) // match exact
309 rstr = "^" + rstr + "$";
312 rstr = ".*" + WB + rstr + WB + ".*";
319 vector<string>::const_iterator it = container.begin();
323 tmp = wildcards2regex(*it);
327 if (!(_flags & SEARCH_STRING)) // not match exact
328 tmp += ".*" + WB + tmp;
329 rstr = "(?=" + tmp + ")";
333 if (_flags & SEARCH_STRING) // match exact
343 for (; it != container.end(); ++it)
346 tmp = wildcards2regex(*it);
350 if (!(_flags & SEARCH_STRING)) // not match exact
351 tmp += ".*" + WB + tmp;
352 rstr += "(?=" + tmp + ")";
362 if (!(_flags & SEARCH_STRING)) // not match exact
368 if (_flags & SEARCH_STRING) // match exact
378 PoolQuery::ResultIterator PoolQuery::Impl::begin()
382 // if only one repository has been specified, find it in the pool
383 sat::Pool pool(sat::Pool::instance());
384 sat::Pool::RepositoryIterator itr = pool.reposBegin();
385 if (!(_flags & SEARCH_ALL_REPOS) && _repos.size() == 1)
387 string theone = *_repos.begin();
388 for (; itr->info().alias() != theone && itr != pool.reposEnd(); ++itr);
389 if (itr == pool.reposEnd())
391 RepoInfo info; info.setAlias(theone);
392 ERR << "Repository not found in sat pool." << endl;
393 ZYPP_THROW(repo::RepoNotFoundException(info));
397 DBG << "_flags:" << _flags << endl;
399 if (_rcattrs.empty())
401 ::dataiterator_init(&_rdit,
402 _flags & SEARCH_ALL_REPOS ? pool.get()->repos[0] : itr->get(), // repository \todo fix this
403 0, // search all solvables
404 0, // attribute id - only if 1 attr key specified
405 _rcstrings.empty() ? 0 : _rcstrings.c_str(), // compiled search string
408 else if (_rcattrs.size() == 1)
410 ::dataiterator_init(&_rdit,
411 _flags & SEARCH_ALL_REPOS ? pool.get()->repos[0] : itr->get(), // repository \todo fix this
412 0, // search all solvables
413 _rcattrs.begin()->first.id(), // keyname - attribute id - only if 1 attr key specified
414 _rcstrings.empty() ? 0 : _rcstrings.c_str(), // compiled search string
419 ::dataiterator_init(&_rdit,
420 _flags & SEARCH_ALL_REPOS ? pool.get()->repos[0] : itr->get(), /* repository - switch to next at the end of current one in increment() */
421 0, /*search all resolvables */
422 0, /*keyname - if only 1 attr key specified, pass it here, otherwise do more magic */
423 0, //qs.empty() ? 0 : qs.c_str(), /* create regex, pass it here */
428 PoolQuery::ResultIterator it(p);
433 PoolQuery::ResultIterator PoolQuery::Impl::end()
435 INT << "end" << endl;
436 return PoolQuery::ResultIterator();
440 string PoolQuery::Impl::asString() const
444 o << "compiled: " << _compiled << endl;
446 o << "match flags:" << endl;
447 o << "* sat: " << (_flags & SEARCH_STRINGMASK) << endl;
448 o << "* SEARCH_REGEX: " << ((_flags & SEARCH_STRINGMASK) == SEARCH_REGEX ? "yes" : "no") << endl;
453 for(vector<string>::const_iterator it = _strings.begin();
454 it != _strings.end(); ++it)
458 o << "attributes: " << endl;
459 for(AttrMap::const_iterator ai = _attrs.begin(); ai != _attrs.end(); ++ai)
461 o << "* " << ai->first << ": ";
462 for(vector<string>::const_iterator vi = ai->second.begin();
463 vi != ai->second.end(); ++vi)
470 o << "regex compiled strings: " << _rcstrings << endl;
471 o << "regex compiled attributes:" << endl;
472 for (CompiledAttrMap::const_iterator ai = _rcattrs.begin(); ai != _rcattrs.end(); ++ai)
473 o << "* " << ai->first << ": " << ai->second << endl;
478 /** \relates PoolQuery::Impl Stream output *//*
479 inline std::ostream & operator<<( std::ostream & str, const PoolQuery::Impl & obj )
481 return str << "PoolQuery::Impl";
484 ///////////////////////////////////////////////////////////////////
486 ///////////////////////////////////////////////////////////////////
488 // CLASS NAME : PoolQuery::ResultIterator
490 ///////////////////////////////////////////////////////////////////
492 PoolQuery::ResultIterator::ResultIterator(ImplPtr pqimpl)
493 : PoolQuery::ResultIterator::iterator_adaptor_(0)
496 , _attrs(pqimpl->_rcattrs)
497 , _do_matching(false)
498 , _pool((sat::Pool::instance()))
500 _rdit = &_pqimpl->_rdit;
501 base_reference() = _rdit;
502 _sid = 0; /*rdit > ID_EMPTY ? rdit->solvid : 0;*/
504 if (_attrs.size() > 1)
508 void PoolQuery::ResultIterator::increment()
513 bool got_match = false;
516 DBG << "last: " << _sid << endl;
517 while (_has_next && !(got_match = matchSolvable()));
522 base_reference() = 0;
526 DBG << "next: " << _sid << endl;
529 bool PoolQuery::ResultIterator::matchSolvable()
531 _sid = _rdit->solvid;
533 bool matches = !_do_matching;
537 //! \todo FIXME Dataiterator returning resolvables belonging to current repo?
538 in_repo = _sid >= _rdit->repo->start;
542 if ( !matches && in_repo /*_sid >= 2 *//*_rdit->repo->start*/)
544 SolvAttr attr(_rdit->key->name);
546 CompiledAttrMap::const_iterator ai = _attrs.find(attr);
547 if (ai != _attrs.end())
550 //matches = (ai->second == IdString(_rdit->kv.id).asString());
553 IdString(_rdit->kv.id).asString().find
554 (_pqimpl->_rcstrings.empty() ? ai->second : _pqimpl->_rcstrings)
557 INT << "value: " << IdString(_rdit->kv.id).asString() << endl
558 << " mstr: " << (_pqimpl->_rcstrings.empty() ? ai->second : _pqimpl->_rcstrings) << endl;
564 if ((_has_next = ::dataiterator_step(_rdit)))
566 if (!in_repo /*_sid < 2 *//*_rdit->repo->start*/)
568 INT << "repo start: " << _rdit->repo->start << endl;
569 _sid = _rdit->solvid;
577 while (_rdit->solvid == _sid || !in_repo /*_sid < 2 *//*_rdit->repo->start*/);
582 ///////////////////////////////////////////////////////////////////
584 // CLASS NAME : PoolQuery
586 ///////////////////////////////////////////////////////////////////
588 PoolQuery::PoolQuery()
593 PoolQuery::~PoolQuery()
597 void PoolQuery::addRepo(const std::string &repoalias)
599 _pimpl->_repos.push_back(repoalias);
600 _pimpl->_flags &= ~SEARCH_ALL_REPOS;
604 void PoolQuery::addKind(const Resolvable::Kind &kind)
605 { _pimpl->_kinds.push_back(kind); }
608 void PoolQuery::addString(const string & value)
609 { _pimpl->_strings.push_back(value); }
612 void PoolQuery::addAttribute(const sat::SolvAttr & attr, const std::string & value)
613 { _pimpl->_attrs[attr].push_back(value); }
616 void PoolQuery::setCaseSensitive(const bool value)
619 _pimpl->_flags &= ~SEARCH_NOCASE;
621 _pimpl->_flags |= SEARCH_NOCASE;
625 void PoolQuery::setMatchSubstring()
626 { _pimpl->_flags = (_pimpl->_flags & ~SEARCH_STRINGMASK) | SEARCH_SUBSTRING; }
627 void PoolQuery::setMatchExact()
628 { _pimpl->_flags = (_pimpl->_flags & ~SEARCH_STRINGMASK) | SEARCH_STRING; }
629 void PoolQuery::setMatchRegex()
630 { _pimpl->_flags = (_pimpl->_flags & ~SEARCH_STRINGMASK) | SEARCH_REGEX; }
631 void PoolQuery::setMatchWord()
633 _pimpl->_match_word = true;
634 _pimpl->_flags = (_pimpl->_flags & ~SEARCH_STRINGMASK) | SEARCH_REGEX;
638 void PoolQuery::setFlags(int flags)
639 { _pimpl->_flags = flags; }
642 void PoolQuery::setInstalledOnly()
643 { _pimpl->_status_flags |= INSTALLED_ONLY; }
644 void PoolQuery::setUninstalledOnly()
645 { _pimpl->_status_flags |= UNINSTALLED_ONLY; }
646 void PoolQuery::setStatusFilterFlags( int flags )
647 { _pimpl->_status_flags |= flags; }
650 void PoolQuery::requireAll(const bool require_all)
651 { _pimpl->_require_all = require_all; }
654 PoolQuery::ResultIterator PoolQuery::begin()
655 { return _pimpl->begin(); }
658 PoolQuery::ResultIterator PoolQuery::end()
659 { return _pimpl->end(); }
662 bool PoolQuery::empty()
663 { return _pimpl->begin() == _pimpl->end(); }
665 //! \todo collect the result, reuse if not dirty
666 PoolQuery::size_type PoolQuery::size()
669 for(ResultIterator it = _pimpl->begin(); it != _pimpl->end(); ++it, ++count);
674 void PoolQuery::execute(ProcessResolvable fnc)
676 invokeOnEach(_pimpl->begin(), _pimpl->end(), fnc);
680 if (!_pimpl->_strings.empty())
681 term = *_pimpl->_strings.begin();
683 sat::Pool pool(sat::Pool::instance());
684 for ( sat::Pool::RepositoryIterator itr = pool.reposBegin();
685 itr != pool.reposEnd();
688 // filter by installed uninstalled
689 if ( ( _pimpl->_status_flags & INSTALLED_ONLY ) && (itr->name() != sat::Pool::instance().systemRepoName()) )
692 if ( ( _pimpl->_status_flags & UNINSTALLED_ONLY ) && (itr->name() == sat::Pool::instance().systemRepoName()) )
695 // is this repo in users repos?
696 bool included = ( find(_pimpl->_repos.begin(), _pimpl->_repos.end(), itr->name()) != _pimpl->_repos.end() );
698 // only look in user repos filter if the filter is not empty
699 // in this case we search in all
700 if ( _pimpl->_repos.empty() || included )
702 repo_search( itr->get(), 0, 0, term.c_str(), _pimpl->_flags, Impl::repo_search_cb, (void*) (this));
709 ///////////////////////////////////////////////////////////////////
711 // CLASS NAME : PoolQuery::Impl
714 * represents all atributes in PoolQuery except SolvAtributes, which are
715 * used as is (not needed extend anything if someone adds new solv attr)
717 struct PoolQueryAttr : public IdStringType<PoolQueryAttr>
720 friend class IdStringType<PoolQueryAttr>;
726 PoolQueryAttr():isSolvAttr(false){}
728 explicit PoolQueryAttr( const char* cstr_r )
729 : _str( cstr_r ),isSolvAttr(false){}
731 explicit PoolQueryAttr( const std::string & str_r )
732 : _str( str_r ),isSolvAttr(false)
735 sat::SolvAttr sa(str_r);
736 if( sa != sat::SolvAttr::noAttr )
744 static const PoolQueryAttr noAttr;
747 static const PoolQueryAttr nameAttr;
748 static const PoolQueryAttr repoAttr;
749 static const PoolQueryAttr kindAttr;
751 // exported attributes from SolvAtributes
755 const PoolQueryAttr PoolQueryAttr::noAttr;
757 const PoolQueryAttr PoolQueryAttr::nameAttr( "name" );
758 const PoolQueryAttr PoolQueryAttr::repoAttr( "repo" );
759 const PoolQueryAttr PoolQueryAttr::kindAttr( "kind" );
761 ///////////////////////////////////////////////////////////////////
764 //\TODO maybe ctor with stream can be usefull
765 bool PoolQuery::recover( istream &str, char delim )
767 bool finded_something = false; //indicates some atributes is finded
773 getline( str, s, delim );
775 if ((!s.empty()) && s[0]=='#') //comment
780 string::size_type pos = s.find(':');
781 if (s.empty() || pos == s.npos) // some garbage on line... act like blank line
783 if (finded_something) //is first blank line after record?
793 finded_something = true;
795 string atrName(str::trim(string(s,0,pos))); // trimmed name of atribute
796 string atrValue(str::trim(string(s,pos+1,s.npos))); //trimmed value
798 PoolQueryAttr attribute( atrName );
800 if ( attribute==PoolQueryAttr::nameAttr)
802 //setName...maybe some regex test
805 else if ( attribute==PoolQueryAttr::repoAttr )
809 else if ( attribute==PoolQueryAttr::kindAttr )
811 addKind( Resolvable::Kind(atrValue) );
813 else if ( attribute==PoolQueryAttr::noAttr )
815 if (attribute.isSolvAttr)
821 //log unknwon atribute
826 //some forget handle new atribute
832 return finded_something;
836 void PoolQuery::serialize( ostream &str, char delim ) const
840 //iterate thrue all settings and write it
842 for_( it, _pimpl->_repos.begin(), _pimpl->_repos.end() )
844 str << "repo: " << *it << delim ;
847 for_( it, _pimpl->_kinds.begin(), _pimpl->_kinds.end() )
849 str << "kind: " << it->idStr() << delim ;
852 //separating delim - protection
858 string PoolQuery::asString() const
859 { return _pimpl->asString(); }
862 ostream & operator<<( ostream & str, const PoolQuery & obj )
863 { return str << obj.asString(); }
866 /////////////////////////////////////////////////////////////////
868 ///////////////////////////////////////////////////////////////////