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