replace boost::replace_all by jano's implementation which is faster (replace in place)
[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 )
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     // if only one repository has been specified, find it in the pool
368     sat::Pool pool(sat::Pool::instance());
369     sat::Pool::RepositoryIterator itr = pool.reposBegin();
370     if (!(_cflags & SEARCH_ALL_REPOS) && _repos.size() == 1)
371     {
372       string theone = *_repos.begin();
373       for (; itr->info().alias() != theone && itr != pool.reposEnd(); ++itr);
374       if (itr == pool.reposEnd())
375       {
376         RepoInfo info; info.setAlias(theone);
377         ERR << "Repository not found in sat pool." <<  endl;
378         ZYPP_THROW(repo::RepoNotFoundException(info));
379       }
380     }
381
382     DBG << "_cflags:" << _cflags << endl;
383
384     scoped_ptr< ::_Dataiterator> _rdit( new ::Dataiterator );
385     // needed while LookupAttr::iterator::dip_equal does ::memcmp:
386     ::memset( _rdit.get(), 0, sizeof(::_Dataiterator) );
387
388     if (_rcattrs.empty())
389     {
390     ::dataiterator_init(_rdit.get(),
391       _cflags & SEARCH_ALL_REPOS ? pool.get()->repos[0] : itr->get(), // repository \todo fix this
392       0,                                           // search all solvables
393       0,                                           // attribute id - only if 1 attr key specified
394       _rcstrings.empty() ? 0 : _rcstrings.c_str(), // compiled search string
395       _cflags);
396     }
397     else if (_rcattrs.size() == 1)
398     {
399       ::dataiterator_init(_rdit.get(),
400         _cflags & SEARCH_ALL_REPOS ? pool.get()->repos[0] : itr->get(), // repository \todo fix this
401         0,                                           // search all solvables
402         _rcattrs.begin()->first.id(),                // keyname - attribute id - only if 1 attr key specified
403         _rcstrings.empty() ? 0 : _rcstrings.c_str(), // compiled search string
404         _cflags);
405     }
406     else
407     {
408       ::dataiterator_init(_rdit.get(),
409         _cflags & SEARCH_ALL_REPOS ? pool.get()->repos[0] : itr->get(), /* repository - switch to next at the end of current one in increment() */
410         0, /*search all resolvables */
411         0, /*keyname - if only 1 attr key specified, pass it here, otherwise do more magic */
412         0, //qs.empty() ? 0 : qs.c_str(), /* create regex, pass it here */
413         _cflags);
414     }
415
416     if ((_cflags & SEARCH_STRINGMASK) == SEARCH_REGEX && _rdit->regex_err != 0)
417       ZYPP_THROW(Exception(str::form(
418           _("Invalid regular expression '%s': regcomp returned %d"),
419           _rcstrings.c_str(), _rdit->regex_err)));
420
421     PoolQuery::const_iterator it(_rdit, this);
422     it.increment();
423     return it;
424   }
425
426   PoolQuery::const_iterator PoolQuery::Impl::end() const
427   {
428     //INT << "end" << endl;
429     return PoolQuery::const_iterator();
430   }
431
432
433   string PoolQuery::Impl::asString() const
434   {
435     ostringstream o;
436
437     o << "compiled: " << _compiled << endl;
438
439     o << "kinds: ";
440     for(Kinds::const_iterator it = _kinds.begin();
441         it != _kinds.end(); ++it)
442       o << *it << " ";
443     o << endl;
444
445     o << "repos: ";
446     for(StrContainer::const_iterator it = _repos.begin();
447         it != _repos.end(); ++it)
448       o << *it << " ";
449     o << endl;
450
451     o << "string match flags:" << endl;
452     o << "* string/substring/glob/regex: " << (_cflags & SEARCH_STRINGMASK) << endl;
453     o << "* SEARCH_NOCASE: " << ((_cflags & SEARCH_NOCASE) ? "yes" : "no") << endl;
454     o << "* SEARCH_ALL_REPOS: " << ((_cflags & SEARCH_ALL_REPOS) ? "yes" : "no") << endl;
455     o << "status filter flags:" << _status_flags << endl;
456
457     // raw
458
459     o << "strings: ";
460     for(StrContainer::const_iterator it = _strings.begin();
461         it != _strings.end(); ++it)
462       o << *it << " ";
463     o << endl;
464
465     o << "attributes: " << endl;
466     for(AttrRawStrMap::const_iterator ai = _attrs.begin(); ai != _attrs.end(); ++ai)
467     {
468       o << "* " << ai->first << ": ";
469       for(StrContainer::const_iterator vi = ai->second.begin();
470           vi != ai->second.end(); ++vi)
471         o << *vi << " ";
472       o << endl;
473     }
474
475     // compiled
476
477     o << "compiled strings: " << _rcstrings << endl;
478     o << "compiled attributes:" << endl;
479     for (AttrCompiledStrMap::const_iterator ai = _rcattrs.begin(); ai != _rcattrs.end(); ++ai)
480       o << "* " << ai->first << ": " << ai->second << endl;
481
482     return o.str();
483   }
484
485   /** \relates PoolQuery::Impl Stream output *//*
486   inline std::ostream & operator<<( std::ostream & str, const PoolQuery::Impl & obj )
487   {
488     return str << "PoolQuery::Impl";
489   }
490   */
491   ///////////////////////////////////////////////////////////////////
492
493   ///////////////////////////////////////////////////////////////////
494   namespace detail
495   { /////////////////////////////////////////////////////////////////
496
497   ///////////////////////////////////////////////////////////////////
498   //
499   //  CLASS NAME : PoolQuery::ResultIterator
500   //
501   ///////////////////////////////////////////////////////////////////
502
503   PoolQueryIterator::PoolQueryIterator()
504     : _sid(0), _has_next(false), _do_matching(false)
505   { this->base_reference() = LookupAttr::iterator(); }
506
507
508   PoolQueryIterator::PoolQueryIterator(
509       scoped_ptr< ::_Dataiterator> & dip_r,
510       const PoolQuery::Impl * pqimpl)
511   : _sid(0)
512   , _has_next(true)
513   , _do_matching(pqimpl->_rcattrs.size() > 1)
514   , _flags(pqimpl->_cflags)
515   , _str(pqimpl->_rcstrings)
516   , _regex(pqimpl->_regex)
517   , _attrs_str(pqimpl->_rcattrs)
518   , _attrs_regex(pqimpl->_rattrs)
519   , _repos(pqimpl->_repos)
520   , _kinds(pqimpl->_kinds)
521   , _status_flags(pqimpl->_status_flags)
522   {
523     this->base_reference() = LookupAttr::iterator(dip_r, true); //!\todo pass chain_repos
524     _has_next = (*base_reference() != sat::detail::noId);
525   }
526
527
528   PoolQueryIterator::PoolQueryIterator(const PoolQueryIterator & rhs)
529     : _sid(rhs._sid)
530     , _has_next(rhs._has_next)
531     , _do_matching(rhs._do_matching)
532     , _flags(rhs._flags)
533     , _str(rhs._str)
534     , _regex(rhs._regex)
535     , _attrs_str(rhs._attrs_str)
536     , _attrs_regex(rhs._attrs_regex)
537     , _repos(rhs._repos)
538     , _kinds(rhs._kinds)
539     , _status_flags(rhs._status_flags)
540   { base_reference() = LookupAttr::iterator(rhs.base()); }
541
542
543   PoolQueryIterator::~PoolQueryIterator()
544   {}
545
546
547   PoolQueryIterator & PoolQueryIterator::operator=( const PoolQueryIterator & rhs )
548   {
549     base_reference() = rhs.base();
550     _sid = rhs._sid;
551     _has_next = rhs._has_next;
552     _do_matching = rhs._do_matching;
553     _flags = rhs._flags;
554     _str = rhs._str;
555     _regex = rhs._regex;
556     _attrs_str = rhs._attrs_str;
557     _attrs_regex = rhs._attrs_regex;
558     _repos = rhs._repos;
559     _kinds = rhs._kinds;
560     _status_flags = rhs._status_flags;
561     return *this;
562   }
563
564
565   void PoolQueryIterator::increment()
566   {
567     if (!base().get())
568       return;
569
570     bool got_match = false;
571     if (_has_next)
572     {
573       DBG << "last: " << _sid << endl;
574       while (_has_next && !(got_match = matchSolvable()));
575     }
576
577     // no more solvables and the last did not match
578     if (!got_match && !_has_next)
579     {
580       base_reference() = LookupAttr::iterator();
581       _sid = 0;
582     }
583
584     DBG << "next: " << _sid << endl;
585   }
586
587   bool PoolQueryIterator::matchSolvable()
588   {
589     _sid = base().get()->solvid;
590
591     bool new_solvable = true;
592     bool matches = !_do_matching;
593     bool drop_by_kind_status = false;
594     bool drop_by_repo = false;
595     do
596     {
597       if (new_solvable)
598       {
599         while(1)
600         {
601           drop_by_repo = false;
602           if(!_repos.empty() &&
603              _repos.find(base().get()->repo->name) == _repos.end())
604           {
605             drop_by_repo = true;
606             break;
607           }
608
609           drop_by_kind_status = false;
610
611           // whether to drop an uninstalled (repo) solvable
612           if ( (_status_flags & PoolQuery::INSTALLED_ONLY) &&
613               base().get()->repo->name != sat::Pool::instance().systemRepoName() )
614           {
615             drop_by_kind_status = true;
616             break;
617           }
618
619           // whether to drop an installed (target) solvable
620           if ((_status_flags & PoolQuery::UNINSTALLED_ONLY) &&
621               base().get()->repo->name == sat::Pool::instance().systemRepoName())
622           {
623             drop_by_kind_status = true;
624             break;
625           }
626
627           // whether to drop unwanted kind
628           if (!_kinds.empty())
629           {
630             sat::Solvable s(_sid);
631             // the user wants to filter by kind.
632             if (_kinds.find(s.kind()) == _kinds.end())
633               drop_by_kind_status = true;
634           }
635
636           break;
637         }
638
639         matches = matches && !drop_by_kind_status && !drop_by_repo;
640       }
641
642       if (_do_matching && !drop_by_kind_status)
643       {
644         if (!matches)
645         {
646           SolvAttr attr(base().get()->key->name);
647           PoolQuery::AttrCompiledStrMap::const_iterator ai = _attrs_str.find(attr);
648           if (ai != _attrs_str.end())
649           {
650             if ((_flags & SEARCH_STRINGMASK) == SEARCH_REGEX)
651             {
652               const regex_t * regex_p;
653               if (_str.empty())
654               {
655                 PoolQuery::AttrRegexMap::iterator rai = _attrs_regex.find(attr);
656                 if (rai != _attrs_regex.end())
657                   regex_p = rai->second.get();
658                 else
659                 {
660                   ERR << "no compiled regex found for " <<  attr << endl;
661                   continue;
662                 }
663               }
664               else
665                 regex_p = _regex.get();
666               matches = ::dataiterator_match(base().get(), _flags, regex_p);
667             }
668             else
669             {
670               const string & sstr =
671                 _str.empty() ? ai->second : _str;
672               matches = ::dataiterator_match(base().get(), _flags, sstr.c_str());
673             }
674
675               // if (matches)
676               /* After calling dataiterator_match (with any string matcher set)
677                  the kv.str member will be filled with something sensible.  */
678               /* INT << "value: " << base().get()->kv.str << endl
679                   << " mstr: " <<  sstr << endl; */
680           }
681         }
682       }
683
684       if (drop_by_repo)
685       {
686         base_reference().nextSkipRepo();
687         drop_by_repo = false;
688       }
689       else if (drop_by_kind_status)
690       {
691         base_reference().nextSkipSolvable();
692         drop_by_kind_status = false;
693       }
694
695       // copy the iterator to forward check for the next attribute ***
696       _tmpit = base_reference();
697       _has_next = (*(++_tmpit) != sat::detail::noId);
698
699       if (_has_next)
700       {
701         // *** now increment. Had it not be done this way,
702         // the LookupAttr::iterator could have reached the end() while
703         // trying to reach a matching attribute or the next solvable
704         // thus resulting to a problem in the equal() method
705         ++base_reference();
706         new_solvable = base().get()->solvid != _sid;
707       }
708       // no more attributes in this repo, return
709       else
710         return matches; // did the last solvable match conditions?
711     }
712     while (!new_solvable);
713
714     return matches;
715   }
716
717   ///////////////////////////////////////////////////////////////////
718   } //namespace detail
719   ///////////////////////////////////////////////////////////////////
720
721   ///////////////////////////////////////////////////////////////////
722   //
723   //    CLASS NAME : PoolQuery
724   //
725   ///////////////////////////////////////////////////////////////////
726
727   PoolQuery::PoolQuery()
728     : _pimpl(new Impl())
729   {}
730
731
732   PoolQuery::~PoolQuery()
733   {}
734
735
736   void PoolQuery::addRepo(const std::string &repoalias)
737   {
738     _pimpl->_repos.insert(repoalias);
739     _pimpl->_flags &= ~SEARCH_ALL_REPOS;
740   }
741
742
743   void PoolQuery::addKind(const ResKind & kind)
744   { _pimpl->_kinds.insert(kind); }
745
746
747   void PoolQuery::addString(const string & value)
748   { _pimpl->_strings.insert(value); }
749
750
751   void PoolQuery::addAttribute(const sat::SolvAttr & attr, const std::string & value)
752   { _pimpl->_attrs[attr].insert(value); }
753
754
755   void PoolQuery::setCaseSensitive(const bool value)
756   {
757     if (value)
758       _pimpl->_flags &= ~SEARCH_NOCASE;
759     else
760       _pimpl->_flags |= SEARCH_NOCASE;
761   }
762
763
764   void PoolQuery::setMatchSubstring()
765   { _pimpl->_flags = (_pimpl->_flags & ~SEARCH_STRINGMASK) | SEARCH_SUBSTRING; }
766   void PoolQuery::setMatchExact()
767   { _pimpl->_flags = (_pimpl->_flags & ~SEARCH_STRINGMASK) | SEARCH_STRING; }
768   void PoolQuery::setMatchRegex()
769   { _pimpl->_flags = (_pimpl->_flags & ~SEARCH_STRINGMASK) | SEARCH_REGEX; }
770   void PoolQuery::setMatchGlob()
771   { _pimpl->_flags = (_pimpl->_flags & ~SEARCH_STRINGMASK) | SEARCH_GLOB; }
772   void PoolQuery::setMatchWord()
773   {
774     _pimpl->_match_word = true;
775     _pimpl->_flags = (_pimpl->_flags & ~SEARCH_STRINGMASK) | SEARCH_REGEX;
776   }
777
778   void PoolQuery::setFlags(int flags)
779   { _pimpl->_flags = flags; }
780
781
782   void PoolQuery::setInstalledOnly()
783   { _pimpl->_status_flags = INSTALLED_ONLY; }
784   void PoolQuery::setUninstalledOnly()
785   { _pimpl->_status_flags = UNINSTALLED_ONLY; }
786   void PoolQuery::setStatusFilterFlags( PoolQuery::StatusFilter flags )
787   { _pimpl->_status_flags = flags; }
788
789
790   void PoolQuery::setRequireAll(const bool require_all)
791   { _pimpl->_require_all = require_all; }
792
793
794   const PoolQuery::StrContainer &
795   PoolQuery::strings() const
796   { return _pimpl->_strings; }
797
798   const PoolQuery::AttrRawStrMap &
799   PoolQuery::attributes() const
800   { return _pimpl->_attrs; }
801
802   const PoolQuery::StrContainer &
803   PoolQuery::attribute(const sat::SolvAttr & attr) const
804   {
805     static const PoolQuery::StrContainer nocontainer;
806     AttrRawStrMap::const_iterator it = _pimpl->_attrs.find(attr);
807     return it != _pimpl->_attrs.end() ? it->second : nocontainer;
808   }
809
810   const PoolQuery::Kinds &
811   PoolQuery::kinds() const
812   { return _pimpl->_kinds; }
813
814   const PoolQuery::StrContainer &
815   PoolQuery::repos() const
816   { return _pimpl->_repos; }
817
818   bool PoolQuery::caseSensitive() const
819   { return _pimpl->_flags & SEARCH_NOCASE; }
820
821   bool PoolQuery::matchExact() const
822   { return (_pimpl->_flags & SEARCH_STRINGMASK) == SEARCH_STRING; }
823   bool PoolQuery::matchSubstring() const
824   { return (_pimpl->_flags & SEARCH_STRINGMASK) == SEARCH_SUBSTRING; }
825   bool PoolQuery::matchGlob() const
826   { return (_pimpl->_flags & SEARCH_STRINGMASK) == SEARCH_GLOB; }
827   bool PoolQuery::matchRegex() const
828   { return (_pimpl->_flags & SEARCH_STRINGMASK) == SEARCH_REGEX; }
829   int PoolQuery::matchType() const
830   { return _pimpl->_flags & SEARCH_STRINGMASK; }
831
832   bool PoolQuery::matchWord() const
833   { return _pimpl->_match_word; }
834
835   bool PoolQuery::requireAll() const
836   { return _pimpl->_require_all; }
837
838   PoolQuery::StatusFilter PoolQuery::statusFilterFlags() const
839   { return _pimpl->_status_flags; }
840
841   PoolQuery::const_iterator PoolQuery::begin() const
842   { return _pimpl->begin(); }
843
844
845   PoolQuery::const_iterator PoolQuery::end() const
846   { return _pimpl->end(); }
847
848
849   bool PoolQuery::empty()
850   { return _pimpl->begin() == _pimpl->end(); }
851
852   //! \todo collect the result, reuse if not dirty
853   PoolQuery::size_type PoolQuery::size()
854   {
855     size_type count = 0;
856     for(const_iterator it = _pimpl->begin(); it != _pimpl->end(); ++it, ++count);
857     return count;
858   }
859
860
861   void PoolQuery::execute(ProcessResolvable fnc)
862   { invokeOnEach(_pimpl->begin(), _pimpl->end(), fnc); }
863
864
865   ///////////////////////////////////////////////////////////////////
866   //
867   //  CLASS NAME : PoolQuery::Attr
868   //
869   /**
870    * represents all atributes in PoolQuery except SolvAtributes, which are
871    * used as is (not needed extend anything if someone adds new solv attr)
872    */
873   struct PoolQueryAttr : public IdStringType<PoolQueryAttr>
874   {
875     private:
876       friend class IdStringType<PoolQueryAttr>;
877       IdString _str;
878     public:
879
880     //noAttr
881     PoolQueryAttr(){}
882
883     explicit PoolQueryAttr( const char* cstr_r )
884         : _str( cstr_r )
885       {}
886
887     explicit PoolQueryAttr( const std::string & str_r )
888         : _str( str_r )
889       {}
890
891     //unknown atributes
892     static const PoolQueryAttr noAttr;
893
894     // own attributes
895     static const PoolQueryAttr repoAttr;
896     static const PoolQueryAttr kindAttr;
897     static const PoolQueryAttr stringAttr;
898     static const PoolQueryAttr stringTypeAttr;
899     static const PoolQueryAttr requireAllAttr;
900     static const PoolQueryAttr caseSensitiveAttr;
901     static const PoolQueryAttr installStatusAttr;
902   };
903
904   const PoolQueryAttr PoolQueryAttr::noAttr;
905
906   const PoolQueryAttr PoolQueryAttr::repoAttr( "repo" );
907   const PoolQueryAttr PoolQueryAttr::kindAttr( "kind" );
908   const PoolQueryAttr PoolQueryAttr::stringAttr( "global_string" );
909   const PoolQueryAttr PoolQueryAttr::stringTypeAttr("string_type");
910   const PoolQueryAttr PoolQueryAttr::requireAllAttr("require_all");
911   const PoolQueryAttr PoolQueryAttr::caseSensitiveAttr("case_sensitive");
912   const PoolQueryAttr PoolQueryAttr::installStatusAttr("install_status");
913
914   class StringTypeAttr : public IdStringType<PoolQueryAttr>
915   {
916     friend class IdStringType<StringTypeAttr>;
917     IdString _str;
918
919   public:
920     StringTypeAttr(){}
921     explicit StringTypeAttr( const char* cstr_r )
922             : _str( cstr_r ){}
923     explicit StringTypeAttr( const std::string & str_r )
924              : _str( str_r ){}
925
926     static const StringTypeAttr noAttr;
927
928     static const StringTypeAttr exactAttr;
929     static const StringTypeAttr substringAttr;
930     static const StringTypeAttr regexAttr;
931     static const StringTypeAttr globAttr;
932     static const StringTypeAttr wordAttr;
933   };
934     const StringTypeAttr StringTypeAttr::noAttr;
935
936     const StringTypeAttr StringTypeAttr::exactAttr("exact");
937     const StringTypeAttr StringTypeAttr::substringAttr("substring");
938     const StringTypeAttr StringTypeAttr::regexAttr("regex");
939     const StringTypeAttr StringTypeAttr::globAttr("glob");
940     const StringTypeAttr StringTypeAttr::wordAttr("word");
941
942   ///////////////////////////////////////////////////////////////////
943
944
945   //\TODO maybe ctor with stream can be usefull
946   bool PoolQuery::recover( istream &str, char delim )
947   {
948     bool finded_something = false; //indicates some atributes is finded
949     string s;
950     do {
951       if ( str.eof() )
952         break;
953
954       getline( str, s, delim );
955
956       if ((!s.empty()) && s[0]=='#') //comment
957       {
958         continue;
959       }
960
961       string::size_type pos = s.find(':');
962       if (s.empty() || pos == s.npos) // some garbage on line... act like blank line
963       {
964         if (finded_something) //is first blank line after record?
965         {
966           break;
967         }
968         else
969         {
970           continue;
971         }
972       }
973
974       finded_something = true;
975
976       string attrName(str::trim(string(s,0,pos))); // trimmed name of atribute
977       string attrValue(str::trim(string(s,pos+1,s.npos))); //trimmed value
978
979       PoolQueryAttr attribute( attrName );
980
981       if ( attribute==PoolQueryAttr::repoAttr )
982       {
983         addRepo( attrValue );
984       }
985       else if ( attribute==PoolQueryAttr::kindAttr )
986       {
987         addKind( ResKind(attrValue) );
988       }
989       else if ( attribute==PoolQueryAttr::stringAttr )
990       {
991         addString( attrValue );
992       }
993       else if ( attribute==PoolQueryAttr::stringTypeAttr )
994       {
995         StringTypeAttr s(attrValue);
996         if( s == StringTypeAttr::regexAttr )
997         {
998           setMatchRegex();
999         }
1000         else if ( s == StringTypeAttr::globAttr )
1001         {
1002           setMatchGlob();
1003         }
1004         else if ( s == StringTypeAttr::exactAttr )
1005         {
1006           setMatchExact();
1007         }
1008         else if ( s == StringTypeAttr::substringAttr )
1009         {
1010           setMatchSubstring();
1011         }
1012         else if ( s == StringTypeAttr::wordAttr )
1013         {
1014           setMatchWord();
1015         }
1016         else if ( s == StringTypeAttr::noAttr )
1017         {
1018           WAR << "unknown string type " << attrValue << endl;
1019         }
1020         else
1021         {
1022           WAR << "forget recover some attribute defined as String type attribute: " << attrValue << endl;
1023         }
1024       }
1025       else if ( attribute==PoolQueryAttr::requireAllAttr )
1026       {
1027         if ( str::strToTrue(attrValue) )
1028         {
1029           setRequireAll(true);
1030         }
1031         else if ( !str::strToFalse(attrValue) )
1032         {
1033           setRequireAll(false);
1034         }
1035         else
1036         {
1037           WAR << "unknown boolean value " << attrValue << endl;
1038         }
1039       }
1040       else if ( attribute==PoolQueryAttr::caseSensitiveAttr )
1041       {
1042         if ( str::strToTrue(attrValue) )
1043         {
1044           setCaseSensitive(true);
1045         }
1046         else if ( !str::strToFalse(attrValue) )
1047         {
1048           setCaseSensitive(false);
1049         }
1050         else
1051         {
1052           WAR << "unknown boolean value " << attrValue << endl;
1053         }
1054       }
1055       else if ( attribute==PoolQueryAttr::installStatusAttr )
1056       {
1057         if( attrValue == "all" )
1058         {
1059           setStatusFilterFlags( ALL );
1060         }
1061         else if( attrValue == "installed" )
1062         {
1063           setInstalledOnly();
1064         }
1065         else if( attrValue == "not-installed" )
1066         {
1067           setUninstalledOnly();
1068         }
1069         else
1070         {
1071           WAR << "Unknown value for install status " << attrValue << endl;
1072         }
1073       }
1074       else if ( attribute==PoolQueryAttr::noAttr )
1075       {
1076         WAR << "empty attribute name" << endl;
1077       }
1078       else
1079       {
1080         string s = attrName;
1081         str::replace_all( s,"_",":" );
1082         SolvAttr a(s);
1083         addAttribute(a,attrValue);
1084       }
1085
1086     } while ( true );
1087
1088     return finded_something;
1089   }
1090
1091   void PoolQuery::serialize( ostream &str, char delim ) const
1092   {
1093     //separating delim
1094     str << delim;
1095     //iterate thrue all settings and write it
1096     static const zypp::PoolQuery q; //not save default options, so create default query example
1097
1098     for_( it, repos().begin(), repos().end() )
1099     {
1100       str << "repo: " << *it << delim ;
1101     }
1102
1103     for_( it, kinds().begin(), kinds().end() )
1104     {
1105       str << "kind: " << it->idStr() << delim ;
1106     }
1107
1108     if (matchType()!=q.matchType())
1109     {
1110       switch( matchType() )
1111       {
1112       case SEARCH_STRING:
1113         str << "string_type: exact" << delim;
1114         break;
1115       case SEARCH_SUBSTRING:
1116         str << "string_type: substring" << delim;
1117         break;
1118       case SEARCH_GLOB:
1119         str << "string_type: glob" << delim;
1120         break;
1121       case SEARCH_REGEX:
1122         str << "string_type: regex" << delim;
1123         break;
1124       default:
1125         WAR << "unknown match type "  << matchType() << endl;
1126       }
1127     }
1128
1129     if( caseSensitive() != q.caseSensitive() )
1130     {
1131       str << "case_sensitive: ";
1132       if (caseSensitive())
1133       {
1134         str << "on" << delim;
1135       }
1136       else
1137       {
1138         str << "off" << delim;
1139       }
1140     }
1141
1142     if( requireAll() != q.requireAll() )
1143     {
1144       str << "require_all: ";
1145       if (requireAll())
1146       {
1147         str << "on" << delim;
1148       }
1149       else
1150       {
1151         str << "off" << delim;
1152       }
1153     }
1154
1155     if( statusFilterFlags() != q.statusFilterFlags() )
1156     {
1157       switch( statusFilterFlags() )
1158       {
1159       case ALL:
1160         str << "install_status: all" << delim;
1161         break;
1162       case INSTALLED_ONLY:
1163         str << "install_status: installed" << delim;
1164         break;
1165       case UNINSTALLED_ONLY:
1166         str << "install_status: not-installed" << delim;
1167         break;
1168       }
1169     }
1170
1171     for_( it, strings().begin(), strings().end() )
1172     {
1173       str << "global_string: " << *it << delim;
1174     }
1175
1176     for_( it, attributes().begin(), attributes().end() )
1177     {
1178       string s = it->first.asString();
1179       str::replace_all(s,":","_");
1180       for_( it2,it->second.begin(),it->second.end() )
1181       {
1182         str << s <<": "<< *it2 << delim;
1183       }
1184     }
1185
1186     //separating delim - protection
1187     str << delim;
1188
1189   }
1190
1191
1192   string PoolQuery::asString() const
1193   { return _pimpl->asString(); }
1194
1195
1196   ostream & operator<<( ostream & str, const PoolQuery & obj )
1197   { return str << obj.asString(); }
1198
1199   //internal matching two containers O(n^2)
1200   template <class Container>
1201   bool equalContainers(const Container& a, const Container& b)
1202   {
1203     if (a.size()!=b.size())
1204       return false;
1205
1206     for_(it,a.begin(),a.end())
1207     {
1208       bool finded = false;
1209       for_( it2, b.begin(),b.end() )
1210       {
1211         if (*it==*it2)
1212         {
1213           finded = true;
1214           break;
1215         }
1216       }
1217
1218       if (!finded)
1219         return false;
1220     }
1221     return true;
1222   }
1223
1224   bool PoolQuery::operator==(const PoolQuery& a) const
1225   {
1226     if (!_pimpl->_compiled)
1227       _pimpl->compile();
1228     if (!a._pimpl->_compiled)
1229       a._pimpl->compile();
1230     if( matchType()!=a.matchType() )
1231       return false;
1232     if( a.matchWord()!=matchWord())
1233       return false;
1234     if( a.requireAll()!=requireAll() )
1235       return false;
1236     if(!equalContainers(a.kinds(), kinds()))
1237       return false;
1238     if(!equalContainers(a.repos(), repos()))
1239       return false;
1240     if(a._pimpl->_rcstrings!=_pimpl->_rcstrings)
1241       return false;
1242     if(!equalContainers(a._pimpl->_rcattrs, _pimpl->_rcattrs))
1243       return false;
1244     if(a._pimpl->_cflags!= _pimpl->_cflags)
1245       return false;
1246
1247     return true;
1248   }
1249
1250
1251   /////////////////////////////////////////////////////////////////
1252 } // namespace zypp
1253 ///////////////////////////////////////////////////////////////////
1254