Fix failed package download due to unkown repository type (bnc #386386)
[platform/upstream/libzypp.git] / zypp / PoolQuery.cc
1 /*---------------------------------------------------------------------\
2 |                          ____ _   __ __ ___                          |
3 |                         |__  / \ / / . \ . \                         |
4 |                           / / \ V /|  _/  _/                         |
5 |                          / /__ | | | | | |                           |
6 |                         /_____||_| |_| |_|                           |
7 |                                                                      |
8 \---------------------------------------------------------------------*/
9 /** \file       zypp/PoolQuery.cc
10  *
11 */
12 #include <iostream>
13 #include <sstream>
14
15 #include "zypp/base/Gettext.h"
16 #include "zypp/base/Logger.h"
17 #include "zypp/base/Algorithm.h"
18 #include "zypp/base/String.h"
19 #include "zypp/repo/RepoException.h"
20
21 #include "zypp/sat/Pool.h"
22 #include "zypp/sat/Solvable.h"
23
24 #include "zypp/PoolQuery.h"
25
26 extern "C"
27 {
28 #include "satsolver/repo.h"
29 }
30
31 #undef ZYPP_BASE_LOGGER_LOGGROUP
32 #define ZYPP_BASE_LOGGER_LOGGROUP "PoolQuery"
33
34 using namespace std;
35 using namespace zypp::sat;
36
37 ///////////////////////////////////////////////////////////////////
38 namespace zypp
39 { /////////////////////////////////////////////////////////////////
40
41   ///////////////////////////////////////////////////////////////////
42   //
43   //  CLASS NAME : PoolQuery::Impl
44   //
45   class PoolQuery::Impl
46   {
47   public:
48     Impl()
49       : _flags( SEARCH_ALL_REPOS | SEARCH_NOCASE | SEARCH_SUBSTRING | SEARCH_SKIP_KIND )
50       , _status_flags(ALL)
51       , _match_word(false)
52       , _require_all(false)
53       , _compiled(false)
54     {}
55
56     ~Impl()
57     {}
58
59   public:
60     const_iterator begin() const;
61     const_iterator end() const;
62
63     string asString() const;
64
65     void compile() const;
66   private:
67     string createRegex(const StrContainer & container) const;
68
69   public:
70     /** Raw search strings. */
71     StrContainer _strings;
72     /** Compiled search strings. */
73     mutable string _rcstrings;
74     /** Compiled regex struct */
75     mutable str::regex _regex;
76     /** Raw attributes */
77     AttrRawStrMap _attrs;
78     /** Regex-compiled attributes */
79     mutable AttrCompiledStrMap _rcattrs;
80     mutable AttrRegexMap _rattrs;
81
82     /** Repos to search. */
83     StrContainer _repos;
84     /** Kinds to search */
85     Kinds _kinds;
86
87     /** Sat solver search flags */
88     int _flags;
89     /** Backup of search flags. compile() may change the flags if needed, so
90      * in order to reuse the query, the original flags need to be stored
91      * at the start of compile() */
92     mutable int _cflags;
93     /** Sat solver status flags */
94     StatusFilter _status_flags;
95
96     bool _match_word;
97
98     bool _require_all;
99
100     mutable bool _compiled;
101
102   private:
103     friend Impl * rwcowClone<Impl>( const Impl * rhs );
104     /** clone for RWCOW_pointer */
105     Impl * clone() const
106     { return new Impl( *this ); }
107   };
108
109
110   struct MyInserter
111   {
112     MyInserter(PoolQuery::StrContainer & cont) : _cont(cont) {}
113
114     bool operator()(const string & str)
115     {
116       _cont.insert(str);
117       return true;
118     }
119
120     PoolQuery::StrContainer & _cont;
121   };
122
123
124   struct EmptyFilter
125   {
126     bool operator()(const string & str)
127     {
128       return !str.empty();
129     }
130   };
131
132
133   void PoolQuery::Impl::compile() const
134   {
135     _cflags = _flags;
136
137     // 'different'         - will have to iterate through all and match by ourselves (slow)
138     // 'same'              - will pass the compiled string to dataiterator_init
139     // 'one-attr'          - will pass it to dataiterator_init
140     // 'one-non-regex-str' - will pass to dataiterator_init, set flag to SEARCH_STRING or SEARCH_SUBSTRING
141
142     // // NO ATTRIBUTE
143     // else
144     //   for all _strings
145     //     create regex; store in _rcstrings; if more strings flag regex;
146     if (_attrs.empty())
147     {
148       _rcstrings = createRegex(_strings);
149       if (_strings.size() > 1)
150         _cflags = (_cflags & ~SEARCH_STRINGMASK) | SEARCH_REGEX;//setMatchRegex();
151     }
152
153     // // ONE ATTRIBUTE
154     // else if _attrs is not empty but it contains just one attr
155     //   for all _strings and _attr[key] strings
156     //     create regex; store in _rcattrs; flag 'one-attr'; if more strings flag regex;
157     else if (_attrs.size() == 1)
158     {
159       StrContainer joined;
160       invokeOnEach(_strings.begin(), _strings.end(), EmptyFilter(), MyInserter(joined));
161       invokeOnEach(_attrs.begin()->second.begin(), _attrs.begin()->second.end(), EmptyFilter(), MyInserter(joined));
162       _rcstrings = createRegex(joined);
163       _rcattrs.insert(pair<sat::SolvAttr, string>(_attrs.begin()->first, string()));
164       // switch to regex for multiple strings
165       if (joined.size() > 1)
166         _cflags = (_cflags & ~SEARCH_STRINGMASK) | SEARCH_REGEX;
167       if ((_cflags & SEARCH_STRINGMASK) == SEARCH_REGEX)
168         /* We feed multiple lines eventually (e.g. authors or descriptions), so set REG_NEWLINE. */
169         _regex = str::regex(_rcstrings, REG_EXTENDED | REG_NOSUB | REG_NEWLINE | (_cflags & SEARCH_NOCASE ? REG_ICASE : 0));
170     }
171
172     // // MULTIPLE ATTRIBUTES
173     else
174     {
175       // check whether there are any per-attribute strings
176       bool attrvals_empty = true;
177       for (AttrRawStrMap::const_iterator ai = _attrs.begin(); ai != _attrs.end(); ++ai)
178         if (!ai->second.empty())
179           for(StrContainer::const_iterator it = ai->second.begin();
180               it != ai->second.end(); it++)
181             if (!it->empty())
182             {
183               attrvals_empty = false;
184               goto attremptycheckend;
185             }
186 attremptycheckend:
187
188       // chceck whether the per-attribute strings are all the same
189       bool attrvals_thesame = true;
190       AttrRawStrMap::const_iterator ai = _attrs.begin();
191       const StrContainer & set1 = ai->second;
192       ++ai;
193       for (; ai != _attrs.end(); ++ai)
194       {
195         StrContainer result;
196         set_difference(
197           set1.begin(), set1.end(),
198           ai->second.begin(), ai->second.end(),
199           inserter(result, result.begin())/*, ltstr()*/);
200         if (!result.empty())
201         {
202           attrvals_thesame = false;
203           break;
204         }
205       }
206
207       // // THE SAME STRINGS FOR DIFFERENT ATTRS
208       // else if _attrs is not empty but it does not contain strings
209       //   for each key in _attrs take all _strings
210       //     create regex; store in _rcattrs and _rcstrings; flag 'same'; if more strings flag regex;
211       if (attrvals_empty || attrvals_thesame)
212       {
213         StrContainer joined;
214         if (attrvals_empty)
215         {
216           invokeOnEach(_strings.begin(), _strings.end(), EmptyFilter(), MyInserter(joined));
217           _rcstrings = createRegex(joined);
218         }
219         else
220         {
221           invokeOnEach(_strings.begin(), _strings.end(), EmptyFilter(), MyInserter(joined));
222           invokeOnEach(_attrs.begin()->second.begin(), _attrs.begin()->second.end(), EmptyFilter(), MyInserter(joined));
223           _rcstrings = createRegex(joined);
224         }
225         // copy the _attrs keys to _rcattrs
226         for_(ai, _attrs.begin(), _attrs.end())
227           _rcattrs.insert(pair<sat::SolvAttr, string>(ai->first, string()));
228
229         // switch to regex for multiple strings
230         if (joined.size() > 1)
231           _cflags = (_cflags & ~SEARCH_STRINGMASK) | SEARCH_REGEX;
232
233         if ((_cflags & SEARCH_STRINGMASK) == SEARCH_REGEX)
234           /* We feed multiple lines eventually (e.g. authors or descriptions), so set REG_NEWLINE. */
235           _regex = str::regex(_rcstrings, REG_EXTENDED | REG_NOSUB | REG_NEWLINE | (_cflags & SEARCH_NOCASE ? REG_ICASE : 0));
236       }
237
238       // // DIFFERENT STRINGS FOR DIFFERENT ATTRS
239       // if _attrs is not empty and it contains non-empty vectors with non-empty strings
240       //   for each key in _attrs take all _strings + all _attrs[key] strings
241       //     create regex; store in _rcattrs; flag 'different'; if more strings flag regex;
242       else
243       {
244         for_(ai, _attrs.begin(), _attrs.end())
245         {
246           StrContainer joined;
247           invokeOnEach(_strings.begin(), _strings.end(), EmptyFilter(), MyInserter(joined));
248           invokeOnEach(ai->second.begin(), ai->second.end(), EmptyFilter(), MyInserter(joined));
249           string s = createRegex(joined);
250           _rcattrs.insert(pair<sat::SolvAttr, string>(ai->first, s));
251           
252           // switch to regex for multiple strings
253           if (joined.size() > 1)
254             _cflags = (_cflags & ~SEARCH_STRINGMASK) | SEARCH_REGEX;
255           if ((_cflags & SEARCH_STRINGMASK) == SEARCH_REGEX)
256           {
257             str::regex regex(s, REG_EXTENDED | REG_NOSUB | REG_NEWLINE | (_cflags & SEARCH_NOCASE ? REG_ICASE : 0));
258             _rattrs.insert(pair<sat::SolvAttr, str::regex>(ai->first, regex));
259           }
260         }
261       }
262     }
263
264     // tell the Dataiterator to search only in one repo if only one specified
265     if (_repos.size() == 1)
266       _cflags &= ~SEARCH_ALL_REPOS;
267
268     _compiled = true;
269
270     DBG << asString() << endl;
271   }
272
273
274   /**
275    * Converts '*' and '?' wildcards within str into their regex equivalents.
276    */
277   static string wildcards2regex(const string & str)
278   {
279     string regexed = str;
280
281     string r_all(".*"); // regex equivalent of '*'
282     string r_one(".");  // regex equivalent of '?'
283     string::size_type pos;
284
285     // replace all "*" in input with ".*"
286     for (pos = 0; (pos = regexed.find("*", pos)) != std::string::npos; pos+=2)
287       regexed = regexed.replace(pos, 1, r_all);
288
289     // replace all "?" in input with "."
290     for (pos = 0; (pos = regexed.find('?', pos)) != std::string::npos; ++pos)
291       regexed = regexed.replace(pos, 1, r_one);
292
293     return regexed;
294   }
295
296 //! macro for word boundary tags for regexes
297 #define WB (_match_word ? string("\\b") : string())
298
299   string PoolQuery::Impl::createRegex(const StrContainer & container) const
300   {
301     string rstr;
302
303     if (container.empty())
304       return rstr;
305
306     if (container.size() == 1)
307     {
308       return WB + *container.begin() + WB;
309     }
310
311     // multiple strings
312
313     bool use_wildcards = (_cflags & SEARCH_STRINGMASK) == SEARCH_GLOB;
314     StrContainer::const_iterator it = container.begin();
315     string tmp;
316
317     if (use_wildcards)
318       tmp = wildcards2regex(*it);
319     else
320       tmp = *it;
321
322     if (_require_all)
323     {
324       if ((_cflags & SEARCH_STRINGMASK) != SEARCH_STRING) // not match exact
325         tmp += ".*" + WB + tmp;
326       rstr = "(?=" + tmp + ")";
327     }
328     else
329     {
330       if ((_cflags & SEARCH_STRINGMASK) == SEARCH_STRING || // match exact
331           (_cflags & SEARCH_STRINGMASK) == SEARCH_GLOB)     // match glob
332         rstr = "^";
333
334       rstr += WB + "(" + tmp;
335     }
336
337     ++it;
338
339     for (; it != container.end(); ++it)
340     {
341       if (use_wildcards)
342         tmp = wildcards2regex(*it);
343       else
344         tmp = *it;
345
346       if (_require_all)
347       {
348         if ((_cflags & SEARCH_STRINGMASK) != SEARCH_STRING) // not match exact
349           tmp += ".*" + WB + tmp;
350         rstr += "(?=" + tmp + ")";
351       }
352       else
353       {
354         rstr += "|" + tmp;
355       }
356     }
357
358     if (_require_all)
359     {
360       if ((_cflags & SEARCH_STRINGMASK) != SEARCH_STRING) // not match exact
361         rstr += WB + ".*";
362     }
363     else
364     {
365       rstr += ")" + WB;
366       if ((_cflags & SEARCH_STRINGMASK) == SEARCH_STRING || // match exact
367           (_cflags & SEARCH_STRINGMASK) == SEARCH_GLOB)     // match glob
368         rstr += "$";
369     }
370
371     return rstr;
372   }
373
374
375   PoolQuery::const_iterator PoolQuery::Impl::begin() const
376   {
377     compile();
378
379     sat::Pool pool(sat::Pool::instance());
380     // no pool or no repos
381     if (!pool.get() || pool.reposEmpty())
382       return end();
383
384     // if only one repository has been specified, find it in the pool
385     Repository repo;
386     if (!(_cflags & SEARCH_ALL_REPOS) && _repos.size() == 1)
387     {
388       string theone = *_repos.begin();
389       repo = pool.reposFind(theone);
390       if (repo == Repository::noRepository)
391       {
392         DBG << "Repository '" << theone << "' not found in sat pool." << endl;
393         return end();
394       }
395     }
396
397     if ((_cflags & SEARCH_ALL_REPOS) || repo == Repository::noRepository)
398       repo = *pool.reposBegin();
399
400     DBG << "_cflags:" << _cflags << endl;
401
402     scoped_ptr< ::_Dataiterator> _rdit( new ::Dataiterator );
403     // needed while LookupAttr::iterator::dip_equal does ::memcmp:
404     ::memset( _rdit.get(), 0, sizeof(::_Dataiterator) );
405
406     // initialize the Dataiterator for different cases
407     if (_rcattrs.empty())
408     {
409       ::dataiterator_init(_rdit.get(),
410         repo.get(),                                  // either the first repo or the repo to search
411         0,                                           // search all solvables
412         0,                                           // attribute id - only if 1 attr key specified
413         _rcstrings.empty() ? 0 : _rcstrings.c_str(), // compiled search string
414         _cflags);
415     }
416     else if (_rcattrs.size() == 1)
417     {
418       ::dataiterator_init(_rdit.get(),
419         repo.get(),                                  // either the first repo or the repo to search
420         0,                                           // search all solvables
421         _rcattrs.begin()->first.id(),                // keyname - attribute id - only if 1 attr key specified
422         _rcstrings.empty() ? 0 : _rcstrings.c_str(), // compiled search string
423         _cflags);
424     }
425     else
426     {
427       ::dataiterator_init(_rdit.get(),
428         repo.get(),                                  // either the first repo or the repo to search
429         0, /*search all resolvables */
430         0, /*keyname - if only 1 attr key specified, pass it here, otherwise do more magic */
431         0, //qs.empty() ? 0 : qs.c_str(), /* create regex, pass it here */
432         _cflags);
433     }
434
435     if ((_cflags & SEARCH_STRINGMASK) == SEARCH_REGEX && _rdit->regex_err != 0)
436       ZYPP_THROW(Exception(str::form(
437           _("Invalid regular expression '%s': regcomp returned %d"),
438           _rcstrings.c_str(), _rdit->regex_err)));
439
440     PoolQuery::const_iterator it(_rdit, this);
441     it.increment();
442     return it;
443   }
444
445
446   PoolQuery::const_iterator PoolQuery::Impl::end() const
447   {
448     return PoolQuery::const_iterator();
449   }
450
451
452   string PoolQuery::Impl::asString() const
453   {
454     ostringstream o;
455
456     o << "compiled: " << _compiled << endl;
457
458     o << "kinds: ";
459     for(Kinds::const_iterator it = _kinds.begin();
460         it != _kinds.end(); ++it)
461       o << *it << " ";
462     o << endl;
463
464     o << "repos: ";
465     for(StrContainer::const_iterator it = _repos.begin();
466         it != _repos.end(); ++it)
467       o << *it << " ";
468     o << endl;
469
470     o << "string match flags:" << endl;
471     o << "* string/substring/glob/regex: " << (_cflags & SEARCH_STRINGMASK) << endl;
472     o << "* SEARCH_NOCASE: " << ((_cflags & SEARCH_NOCASE) ? "yes" : "no") << endl;
473     o << "* SEARCH_ALL_REPOS: " << ((_cflags & SEARCH_ALL_REPOS) ? "yes" : "no") << endl;
474     o << "status filter flags:" << _status_flags << endl;
475
476     // raw
477
478     o << "strings: ";
479     for(StrContainer::const_iterator it = _strings.begin();
480         it != _strings.end(); ++it)
481       o << *it << " ";
482     o << endl;
483
484     o << "attributes: " << endl;
485     for(AttrRawStrMap::const_iterator ai = _attrs.begin(); ai != _attrs.end(); ++ai)
486     {
487       o << "* " << ai->first << ": ";
488       for(StrContainer::const_iterator vi = ai->second.begin();
489           vi != ai->second.end(); ++vi)
490         o << *vi << " ";
491       o << endl;
492     }
493
494     // compiled
495
496     o << "compiled strings: " << _rcstrings << endl;
497     o << "compiled attributes:" << endl;
498     for (AttrCompiledStrMap::const_iterator ai = _rcattrs.begin(); ai != _rcattrs.end(); ++ai)
499       o << "* " << ai->first << ": " << ai->second << endl;
500
501     return o.str();
502   }
503
504   /** \relates PoolQuery::Impl Stream output *//*
505   inline std::ostream & operator<<( std::ostream & str, const PoolQuery::Impl & obj )
506   {
507     return str << "PoolQuery::Impl";
508   }
509   */
510   ///////////////////////////////////////////////////////////////////
511
512   ///////////////////////////////////////////////////////////////////
513   namespace detail
514   { /////////////////////////////////////////////////////////////////
515
516   ///////////////////////////////////////////////////////////////////
517   //
518   //  CLASS NAME : PoolQuery::ResultIterator
519   //
520   ///////////////////////////////////////////////////////////////////
521
522   PoolQueryIterator::PoolQueryIterator()
523     : _sid(0), _has_next(false), _do_matching(false)
524   { this->base_reference() = LookupAttr::iterator(); }
525
526
527   PoolQueryIterator::PoolQueryIterator(
528       scoped_ptr< ::_Dataiterator> & dip_r,
529       const PoolQuery::Impl * pqimpl)
530   : _sid(0)
531   , _has_next(true)
532   , _do_matching(pqimpl->_rcattrs.size() > 1)
533   , _flags(pqimpl->_cflags)
534   , _str(pqimpl->_rcstrings)
535   , _regex(pqimpl->_regex)
536   , _attrs_str(pqimpl->_rcattrs)
537   , _attrs_regex(pqimpl->_rattrs)
538   , _repos(pqimpl->_repos)
539   , _kinds(pqimpl->_kinds)
540   , _status_flags(pqimpl->_status_flags)
541   {
542     this->base_reference() = LookupAttr::iterator(dip_r, true); //!\todo pass chain_repos
543     _has_next = (*base_reference() != sat::detail::noId);
544   }
545
546
547   PoolQueryIterator::PoolQueryIterator(const PoolQueryIterator & rhs)
548     : _sid(rhs._sid)
549     , _has_next(rhs._has_next)
550     , _do_matching(rhs._do_matching)
551     , _flags(rhs._flags)
552     , _str(rhs._str)
553     , _regex(rhs._regex)
554     , _attrs_str(rhs._attrs_str)
555     , _attrs_regex(rhs._attrs_regex)
556     , _repos(rhs._repos)
557     , _kinds(rhs._kinds)
558     , _status_flags(rhs._status_flags)
559   { base_reference() = LookupAttr::iterator(rhs.base()); }
560
561
562   PoolQueryIterator::~PoolQueryIterator()
563   {}
564
565
566   PoolQueryIterator & PoolQueryIterator::operator=( const PoolQueryIterator & rhs )
567   {
568     base_reference() = rhs.base();
569     _sid = rhs._sid;
570     _has_next = rhs._has_next;
571     _do_matching = rhs._do_matching;
572     _flags = rhs._flags;
573     _str = rhs._str;
574     _regex = rhs._regex;
575     _attrs_str = rhs._attrs_str;
576     _attrs_regex = rhs._attrs_regex;
577     _repos = rhs._repos;
578     _kinds = rhs._kinds;
579     _status_flags = rhs._status_flags;
580     return *this;
581   }
582
583
584   void PoolQueryIterator::increment()
585   {
586     if (!base().get())
587       return;
588
589     bool got_match = false;
590     if (_has_next)
591       while (_has_next && !(got_match = matchSolvable()));
592
593     // no more solvables and the last did not match
594     if (!got_match && !_has_next)
595     {
596       base_reference() = LookupAttr::iterator();
597       _sid = 0;
598     }
599   }
600
601   bool PoolQueryIterator::matchSolvable()
602   {
603     _sid = base().get()->solvid;
604
605     bool new_solvable = true;
606     bool matches = !_do_matching;
607     bool drop_by_kind_status = false;
608     bool drop_by_repo = false;
609     do
610     {
611       if (new_solvable)
612       {
613         while(1)
614         {
615           drop_by_repo = false;
616           if(!_repos.empty() &&
617              _repos.find(base().get()->repo->name) == _repos.end())
618           {
619             drop_by_repo = true;
620             break;
621           }
622
623           drop_by_kind_status = false;
624
625           // whether to drop an uninstalled (repo) solvable
626           if ( (_status_flags & PoolQuery::INSTALLED_ONLY) &&
627               base().get()->repo->name != sat::Pool::instance().systemRepoName() )
628           {
629             drop_by_kind_status = true;
630             break;
631           }
632
633           // whether to drop an installed (target) solvable
634           if ((_status_flags & PoolQuery::UNINSTALLED_ONLY) &&
635               base().get()->repo->name == sat::Pool::instance().systemRepoName())
636           {
637             drop_by_kind_status = true;
638             break;
639           }
640
641           // whether to drop unwanted kind
642           if (!_kinds.empty())
643           {
644             sat::Solvable s(_sid);
645             // the user wants to filter by kind.
646             if (_kinds.find(s.kind()) == _kinds.end())
647               drop_by_kind_status = true;
648           }
649
650           break;
651         }
652
653         matches = matches && !drop_by_kind_status && !drop_by_repo;
654       }
655
656       if (_do_matching && !drop_by_kind_status)
657       {
658         if (!matches)
659         {
660           SolvAttr attr(base().get()->key->name);
661           PoolQuery::AttrCompiledStrMap::const_iterator ai = _attrs_str.find(attr);
662           if (ai != _attrs_str.end())
663           {
664             if ((_flags & SEARCH_STRINGMASK) == SEARCH_REGEX)
665             {
666               const regex_t * regex_p;
667               if (_str.empty())
668               {
669                 PoolQuery::AttrRegexMap::iterator rai = _attrs_regex.find(attr);
670                 if (rai != _attrs_regex.end())
671                   regex_p = rai->second.get();
672                 else
673                 {
674                   ERR << "no compiled regex found for " <<  attr << endl;
675                   continue;
676                 }
677               }
678               else
679                 regex_p = _regex.get();
680
681               matches = ::dataiterator_match(base().get(), _flags, regex_p);
682             }
683             else
684             {
685               const string & sstr =
686                 _str.empty() ? ai->second : _str;
687               matches = ::dataiterator_match(base().get(), _flags, sstr.c_str());
688             }
689
690               // if (matches)
691               /* After calling dataiterator_match (with any string matcher set)
692                  the kv.str member will be filled with something sensible.  */
693                /*INT << "value: " << base().get()->kv.str << endl
694                   << " str: " <<  _str << endl;*/
695           }
696         }
697       }
698
699       if (drop_by_repo)
700       {
701         base_reference().nextSkipRepo();
702         drop_by_repo = false;
703       }
704       else if (drop_by_kind_status)
705       {
706         base_reference().nextSkipSolvable();
707         drop_by_kind_status = false;
708       }
709
710       // copy the iterator to forward check for the next attribute ***
711       _tmpit = base_reference();
712       _has_next = (*(++_tmpit) != sat::detail::noId);
713
714       if (_has_next)
715       {
716         // *** now increment. Had it not be done this way,
717         // the LookupAttr::iterator could have reached the end() while
718         // trying to reach a matching attribute or the next solvable
719         // thus resulting to a problem in the equal() method
720         ++base_reference();
721         new_solvable = base().get()->solvid != _sid;
722       }
723       // no more attributes in this repo, return
724       else
725         return matches; // did the last solvable match conditions?
726     }
727     while (!new_solvable);
728
729     return matches;
730   }
731
732   ///////////////////////////////////////////////////////////////////
733   } //namespace detail
734   ///////////////////////////////////////////////////////////////////
735
736   ///////////////////////////////////////////////////////////////////
737   //
738   //    CLASS NAME : PoolQuery
739   //
740   ///////////////////////////////////////////////////////////////////
741
742   PoolQuery::PoolQuery()
743     : _pimpl(new Impl())
744   {}
745
746
747   PoolQuery::~PoolQuery()
748   {}
749
750
751   void PoolQuery::addRepo(const std::string &repoalias)
752   {
753     _pimpl->_repos.insert(repoalias);
754     _pimpl->_flags &= ~SEARCH_ALL_REPOS;
755   }
756
757
758   void PoolQuery::addKind(const ResKind & kind)
759   { _pimpl->_kinds.insert(kind); }
760
761
762   void PoolQuery::addString(const string & value)
763   { _pimpl->_strings.insert(value); }
764
765
766   void PoolQuery::addAttribute(const sat::SolvAttr & attr, const std::string & value)
767   { _pimpl->_attrs[attr].insert(value); }
768
769
770   void PoolQuery::setCaseSensitive(const bool value)
771   {
772     if (value)
773       _pimpl->_flags &= ~SEARCH_NOCASE;
774     else
775       _pimpl->_flags |= SEARCH_NOCASE;
776   }
777
778
779   void PoolQuery::setMatchSubstring()
780   { _pimpl->_flags = (_pimpl->_flags & ~SEARCH_STRINGMASK) | SEARCH_SUBSTRING; }
781   void PoolQuery::setMatchExact()
782   { _pimpl->_flags = (_pimpl->_flags & ~SEARCH_STRINGMASK) | SEARCH_STRING; }
783   void PoolQuery::setMatchRegex()
784   { _pimpl->_flags = (_pimpl->_flags & ~SEARCH_STRINGMASK) | SEARCH_REGEX; }
785   void PoolQuery::setMatchGlob()
786   { _pimpl->_flags = (_pimpl->_flags & ~SEARCH_STRINGMASK) | SEARCH_GLOB; }
787   void PoolQuery::setMatchWord()
788   {
789     _pimpl->_match_word = true;
790     _pimpl->_flags = (_pimpl->_flags & ~SEARCH_STRINGMASK) | SEARCH_REGEX;
791   }
792
793   void PoolQuery::setFlags(int flags)
794   { _pimpl->_flags = flags; }
795
796
797   void PoolQuery::setInstalledOnly()
798   { _pimpl->_status_flags = INSTALLED_ONLY; }
799   void PoolQuery::setUninstalledOnly()
800   { _pimpl->_status_flags = UNINSTALLED_ONLY; }
801   void PoolQuery::setStatusFilterFlags( PoolQuery::StatusFilter flags )
802   { _pimpl->_status_flags = flags; }
803
804
805   void PoolQuery::setRequireAll(const bool require_all)
806   { _pimpl->_require_all = require_all; }
807
808
809   const PoolQuery::StrContainer &
810   PoolQuery::strings() const
811   { return _pimpl->_strings; }
812
813   const PoolQuery::AttrRawStrMap &
814   PoolQuery::attributes() const
815   { return _pimpl->_attrs; }
816
817   const PoolQuery::StrContainer &
818   PoolQuery::attribute(const sat::SolvAttr & attr) const
819   {
820     static const PoolQuery::StrContainer nocontainer;
821     AttrRawStrMap::const_iterator it = _pimpl->_attrs.find(attr);
822     return it != _pimpl->_attrs.end() ? it->second : nocontainer;
823   }
824
825   const PoolQuery::Kinds &
826   PoolQuery::kinds() const
827   { return _pimpl->_kinds; }
828
829   const PoolQuery::StrContainer &
830   PoolQuery::repos() const
831   { return _pimpl->_repos; }
832
833   bool PoolQuery::caseSensitive() const
834   { return !(_pimpl->_flags & SEARCH_NOCASE); }
835
836   bool PoolQuery::matchExact() const
837   { return (_pimpl->_flags & SEARCH_STRINGMASK) == SEARCH_STRING; }
838   bool PoolQuery::matchSubstring() const
839   { return (_pimpl->_flags & SEARCH_STRINGMASK) == SEARCH_SUBSTRING; }
840   bool PoolQuery::matchGlob() const
841   { return (_pimpl->_flags & SEARCH_STRINGMASK) == SEARCH_GLOB; }
842   bool PoolQuery::matchRegex() const
843   { return (_pimpl->_flags & SEARCH_STRINGMASK) == SEARCH_REGEX; }
844   int PoolQuery::matchType() const
845   { return _pimpl->_flags & SEARCH_STRINGMASK; }
846
847   bool PoolQuery::matchWord() const
848   { return _pimpl->_match_word; }
849
850   bool PoolQuery::requireAll() const
851   { return _pimpl->_require_all; }
852
853   PoolQuery::StatusFilter PoolQuery::statusFilterFlags() const
854   { return _pimpl->_status_flags; }
855
856   PoolQuery::const_iterator PoolQuery::begin() const
857   { return _pimpl->begin(); }
858
859
860   PoolQuery::const_iterator PoolQuery::end() const
861   { return _pimpl->end(); }
862
863
864   bool PoolQuery::empty() const
865   {
866     try { return _pimpl->begin() == _pimpl->end(); }
867     catch (const Exception & ex) {}
868     return true;
869   }
870
871
872   PoolQuery::size_type PoolQuery::size() const
873   {
874     size_type count = 0;
875     try
876     {
877       for(const_iterator it = _pimpl->begin(); it != _pimpl->end(); ++it, ++count);
878     }
879     catch (const Exception & ex) {}
880
881     return count;
882   }
883
884
885   void PoolQuery::execute(ProcessResolvable fnc)
886   { invokeOnEach(_pimpl->begin(), _pimpl->end(), fnc); }
887
888
889   ///////////////////////////////////////////////////////////////////
890   //
891   //  CLASS NAME : PoolQuery::Attr
892   //
893   /**
894    * represents all atributes in PoolQuery except SolvAtributes, which are
895    * used as is (not needed extend anything if someone adds new solv attr)
896    */
897   struct PoolQueryAttr : public IdStringType<PoolQueryAttr>
898   {
899     private:
900       friend class IdStringType<PoolQueryAttr>;
901       IdString _str;
902     public:
903
904     //noAttr
905     PoolQueryAttr(){}
906
907     explicit PoolQueryAttr( const char* cstr_r )
908         : _str( cstr_r )
909       {}
910
911     explicit PoolQueryAttr( const std::string & str_r )
912         : _str( str_r )
913       {}
914
915     //unknown atributes
916     static const PoolQueryAttr noAttr;
917
918     // own attributes
919     static const PoolQueryAttr repoAttr;
920     static const PoolQueryAttr kindAttr;
921     static const PoolQueryAttr stringAttr;
922     static const PoolQueryAttr stringTypeAttr;
923     static const PoolQueryAttr requireAllAttr;
924     static const PoolQueryAttr caseSensitiveAttr;
925     static const PoolQueryAttr installStatusAttr;
926   };
927
928   const PoolQueryAttr PoolQueryAttr::noAttr;
929
930   const PoolQueryAttr PoolQueryAttr::repoAttr( "repo" );
931   const PoolQueryAttr PoolQueryAttr::kindAttr( "type" );
932   const PoolQueryAttr PoolQueryAttr::stringAttr( "query_string" );
933   const PoolQueryAttr PoolQueryAttr::stringTypeAttr("match_type");
934   const PoolQueryAttr PoolQueryAttr::requireAllAttr("require_all");
935   const PoolQueryAttr PoolQueryAttr::caseSensitiveAttr("case_sensitive");
936   const PoolQueryAttr PoolQueryAttr::installStatusAttr("install_status");
937
938   class StringTypeAttr : public IdStringType<PoolQueryAttr>
939   {
940     friend class IdStringType<StringTypeAttr>;
941     IdString _str;
942
943   public:
944     StringTypeAttr(){}
945     explicit StringTypeAttr( const char* cstr_r )
946             : _str( cstr_r ){}
947     explicit StringTypeAttr( const std::string & str_r )
948              : _str( str_r ){}
949
950     static const StringTypeAttr noAttr;
951
952     static const StringTypeAttr exactAttr;
953     static const StringTypeAttr substringAttr;
954     static const StringTypeAttr regexAttr;
955     static const StringTypeAttr globAttr;
956     static const StringTypeAttr wordAttr;
957   };
958     const StringTypeAttr StringTypeAttr::noAttr;
959
960     const StringTypeAttr StringTypeAttr::exactAttr("exact");
961     const StringTypeAttr StringTypeAttr::substringAttr("substring");
962     const StringTypeAttr StringTypeAttr::regexAttr("regex");
963     const StringTypeAttr StringTypeAttr::globAttr("glob");
964     const StringTypeAttr StringTypeAttr::wordAttr("word");
965
966   ///////////////////////////////////////////////////////////////////
967
968
969   //\TODO maybe ctor with stream can be usefull
970   bool PoolQuery::recover( istream &str, char delim )
971   {
972     bool finded_something = false; //indicates some atributes is finded
973     string s;
974     do {
975       if ( str.eof() )
976         break;
977
978       getline( str, s, delim );
979
980       if ((!s.empty()) && s[0]=='#') //comment
981       {
982         continue;
983       }
984
985       string::size_type pos = s.find(':');
986       if (s.empty() || pos == s.npos) // some garbage on line... act like blank line
987       {
988         if (finded_something) //is first blank line after record?
989         {
990           break;
991         }
992         else
993         {
994           continue;
995         }
996       }
997
998       finded_something = true;
999
1000       string attrName(str::trim(string(s,0,pos))); // trimmed name of atribute
1001       string attrValue(str::trim(string(s,pos+1,s.npos))); //trimmed value
1002
1003       PoolQueryAttr attribute( attrName );
1004
1005       if ( attribute==PoolQueryAttr::repoAttr )
1006       {
1007         addRepo( attrValue );
1008       }
1009       else if ( attribute==PoolQueryAttr::kindAttr )
1010       {
1011         addKind( ResKind(attrValue) );
1012       }
1013       else if ( attribute==PoolQueryAttr::stringAttr )
1014       {
1015         addString( attrValue );
1016       }
1017       else if ( attribute==PoolQueryAttr::stringTypeAttr )
1018       {
1019         StringTypeAttr s(attrValue);
1020         if( s == StringTypeAttr::regexAttr )
1021         {
1022           setMatchRegex();
1023         }
1024         else if ( s == StringTypeAttr::globAttr )
1025         {
1026           setMatchGlob();
1027         }
1028         else if ( s == StringTypeAttr::exactAttr )
1029         {
1030           setMatchExact();
1031         }
1032         else if ( s == StringTypeAttr::substringAttr )
1033         {
1034           setMatchSubstring();
1035         }
1036         else if ( s == StringTypeAttr::wordAttr )
1037         {
1038           setMatchWord();
1039         }
1040         else if ( s == StringTypeAttr::noAttr )
1041         {
1042           WAR << "unknown string type " << attrValue << endl;
1043         }
1044         else
1045         {
1046           WAR << "forget recover some attribute defined as String type attribute: " << attrValue << endl;
1047         }
1048       }
1049       else if ( attribute==PoolQueryAttr::requireAllAttr )
1050       {
1051         if ( str::strToTrue(attrValue) )
1052         {
1053           setRequireAll(true);
1054         }
1055         else if ( !str::strToFalse(attrValue) )
1056         {
1057           setRequireAll(false);
1058         }
1059         else
1060         {
1061           WAR << "unknown boolean value " << attrValue << endl;
1062         }
1063       }
1064       else if ( attribute==PoolQueryAttr::caseSensitiveAttr )
1065       {
1066         if ( str::strToTrue(attrValue) )
1067         {
1068           setCaseSensitive(true);
1069         }
1070         else if ( !str::strToFalse(attrValue) )
1071         {
1072           setCaseSensitive(false);
1073         }
1074         else
1075         {
1076           WAR << "unknown boolean value " << attrValue << endl;
1077         }
1078       }
1079       else if ( attribute==PoolQueryAttr::installStatusAttr )
1080       {
1081         if( attrValue == "all" )
1082         {
1083           setStatusFilterFlags( ALL );
1084         }
1085         else if( attrValue == "installed" )
1086         {
1087           setInstalledOnly();
1088         }
1089         else if( attrValue == "not-installed" )
1090         {
1091           setUninstalledOnly();
1092         }
1093         else
1094         {
1095           WAR << "Unknown value for install status " << attrValue << endl;
1096         }
1097       }
1098       else if ( attribute==PoolQueryAttr::noAttr )
1099       {
1100         WAR << "empty attribute name" << endl;
1101       }
1102       else
1103       {
1104         string s = attrName;
1105         str::replace_all( s,"_",":" );
1106         SolvAttr a(s);
1107         addAttribute(a,attrValue);
1108       }
1109
1110     } while ( true );
1111
1112     return finded_something;
1113   }
1114
1115   void PoolQuery::serialize( ostream &str, char delim ) const
1116   {
1117     //separating delim
1118     str << delim;
1119     //iterate thrue all settings and write it
1120     static const zypp::PoolQuery q; //not save default options, so create default query example
1121
1122     for_( it, repos().begin(), repos().end() )
1123     {
1124       str << "repo: " << *it << delim ;
1125     }
1126
1127     for_( it, kinds().begin(), kinds().end() )
1128     {
1129       str << PoolQueryAttr::kindAttr.asString() << ": " 
1130           << it->idStr() << delim ;
1131     }
1132
1133     if (matchType()!=q.matchType())
1134     {
1135       switch( matchType() )
1136       {
1137       case SEARCH_STRING:
1138         str << PoolQueryAttr::stringTypeAttr.asString() << ": exact" << delim;
1139         break;
1140       case SEARCH_SUBSTRING:
1141         str << PoolQueryAttr::stringTypeAttr.asString() 
1142             << ": substring" << delim;
1143         break;
1144       case SEARCH_GLOB:
1145         str << PoolQueryAttr::stringTypeAttr.asString() << ": glob" << delim;
1146         break;
1147       case SEARCH_REGEX:
1148         str << PoolQueryAttr::stringTypeAttr.asString() << ": regex" << delim;
1149         break;
1150       default:
1151         WAR << "unknown match type "  << matchType() << endl;
1152       }
1153     }
1154
1155     if( caseSensitive() != q.caseSensitive() )
1156     {
1157       str << "case_sensitive: ";
1158       if (caseSensitive())
1159       {
1160         str << "on" << delim;
1161       }
1162       else
1163       {
1164         str << "off" << delim;
1165       }
1166     }
1167
1168     if( requireAll() != q.requireAll() )
1169     {
1170       str << "require_all: ";
1171       if (requireAll())
1172       {
1173         str << "on" << delim;
1174       }
1175       else
1176       {
1177         str << "off" << delim;
1178       }
1179     }
1180
1181     if( statusFilterFlags() != q.statusFilterFlags() )
1182     {
1183       switch( statusFilterFlags() )
1184       {
1185       case ALL:
1186         str << "install_status: all" << delim;
1187         break;
1188       case INSTALLED_ONLY:
1189         str << "install_status: installed" << delim;
1190         break;
1191       case UNINSTALLED_ONLY:
1192         str << "install_status: not-installed" << delim;
1193         break;
1194       }
1195     }
1196
1197     for_( it, strings().begin(), strings().end() )
1198     {
1199       str << PoolQueryAttr::stringAttr.asString()<< ": " << *it << delim;
1200     }
1201
1202     for_( it, attributes().begin(), attributes().end() )
1203     {
1204       string s = it->first.asString();
1205       str::replace_all(s,":","_");
1206       for_( it2,it->second.begin(),it->second.end() )
1207       {
1208         str << s <<": "<< *it2 << delim;
1209       }
1210     }
1211
1212     //separating delim - protection
1213     str << delim;
1214
1215   }
1216
1217
1218   string PoolQuery::asString() const
1219   { return _pimpl->asString(); }
1220
1221
1222   ostream & operator<<( ostream & str, const PoolQuery & obj )
1223   { return str << obj.asString(); }
1224
1225   //internal matching two containers O(n^2)
1226   template <class Container>
1227   bool equalContainers(const Container& a, const Container& b)
1228   {
1229     if (a.size()!=b.size())
1230       return false;
1231
1232     for_(it,a.begin(),a.end())
1233     {
1234       bool finded = false;
1235       for_( it2, b.begin(),b.end() )
1236       {
1237         if (*it==*it2)
1238         {
1239           finded = true;
1240           break;
1241         }
1242       }
1243
1244       if (!finded)
1245         return false;
1246     }
1247     return true;
1248   }
1249
1250   bool PoolQuery::operator==(const PoolQuery& a) const
1251   {
1252     if (!_pimpl->_compiled)
1253       _pimpl->compile();
1254     if (!a._pimpl->_compiled)
1255       a._pimpl->compile();
1256     if( matchType()!=a.matchType() )
1257       return false;
1258     if( a.matchWord()!=matchWord())
1259       return false;
1260     if( a.requireAll()!=requireAll() )
1261       return false;
1262     if(!equalContainers(a.kinds(), kinds()))
1263       return false;
1264     if(!equalContainers(a.repos(), repos()))
1265       return false;
1266     if(a._pimpl->_rcstrings!=_pimpl->_rcstrings)
1267       return false;
1268     if(!equalContainers(a._pimpl->_rcattrs, _pimpl->_rcattrs))
1269       return false;
1270     if(a._pimpl->_cflags!= _pimpl->_cflags)
1271       return false;
1272
1273     return true;
1274   }
1275
1276
1277   /////////////////////////////////////////////////////////////////
1278 } // namespace zypp
1279 ///////////////////////////////////////////////////////////////////
1280