Imported Upstream version 17.10.3
[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/LogTools.h"
17 #include "zypp/base/Algorithm.h"
18 #include "zypp/base/String.h"
19 #include "zypp/repo/RepoException.h"
20 #include "zypp/RelCompare.h"
21
22 #include "zypp/sat/Pool.h"
23 #include "zypp/sat/Solvable.h"
24 #include "zypp/base/StrMatcher.h"
25
26 #include "zypp/PoolQuery.h"
27
28 #undef ZYPP_BASE_LOGGER_LOGGROUP
29 #define ZYPP_BASE_LOGGER_LOGGROUP "PoolQuery"
30
31 using namespace std;
32 using namespace zypp::sat;
33
34 ///////////////////////////////////////////////////////////////////
35 namespace zypp
36 { /////////////////////////////////////////////////////////////////
37
38   ///////////////////////////////////////////////////////////////////
39   namespace
40   { /////////////////////////////////////////////////////////////////
41
42     /////////////////////////////////////////////////////////////////
43     // some Helpers and Predicates
44     /////////////////////////////////////////////////////////////////
45
46     bool isDependencyAttribute( sat::SolvAttr attr_r )
47     {
48       static sat::SolvAttr deps[] = {
49         SolvAttr::provides,
50         SolvAttr::requires,
51         SolvAttr::recommends,
52         SolvAttr::obsoletes,
53         SolvAttr::conflicts,
54         SolvAttr::suggests,
55         SolvAttr::supplements,
56         SolvAttr::enhances,
57       };
58       for_( it, arrayBegin(deps), arrayEnd(deps) )
59         if ( *it == attr_r )
60           return true;
61       return false;
62     }
63
64     /** Whether the current capabilities edition range ovelaps and/or its solvables arch matches.
65      * Query asserts \a iter_r points to a capability and we
66      * have to check the range only.
67      */
68     struct EditionRangePredicate
69     {
70       EditionRangePredicate( const Rel & op, const Edition & edition )
71         : _range( op, edition )
72         , _arch( Arch_empty )
73       {}
74       EditionRangePredicate( const Rel & op, const Edition & edition, const Arch & arch )
75         : _range( op, edition )
76         , _arch( arch )
77       {}
78
79       bool operator()( sat::LookupAttr::iterator iter_r )
80       {
81         if ( !_arch.empty() && iter_r.inSolvable().arch() != _arch )
82           return false;
83
84         CapDetail cap( iter_r.id() );
85         if ( ! cap.isSimple() )
86           return false;
87         if ( cap.isNamed() ) // no range to match
88           return true;
89         return overlaps( Edition::MatchRange( cap.op(), cap.ed() ), _range );
90       }
91
92       std::string serialize() const
93       {
94         std::string ret( "EditionRange" );
95         str::appendEscaped( ret, _range.op.asString() );
96         str::appendEscaped( ret, _range.value.asString() );
97         str::appendEscaped( ret, _arch.asString() );
98         return ret;
99       }
100
101       Edition::MatchRange _range;
102       Arch                _arch;
103    };
104
105     /** Whether the current Solvables edition is within a given range and/or its arch matches. */
106     struct SolvableRangePredicate
107     {
108       SolvableRangePredicate( const Rel & op, const Edition & edition )
109         : _range( op, edition )
110         , _arch( Arch_empty )
111       {}
112
113       SolvableRangePredicate( const Rel & op, const Edition & edition, const Arch & arch )
114         : _range( op, edition )
115         , _arch( arch )
116       {}
117
118       bool operator()( sat::LookupAttr::iterator iter_r )
119       {
120         if ( !_arch.empty() && iter_r.inSolvable().arch() != _arch )
121           return false;
122         return overlaps( Edition::MatchRange( Rel::EQ, iter_r.inSolvable().edition() ), _range );
123       }
124
125       std::string serialize() const
126       {
127         std::string ret( "SolvableRange" );
128         str::appendEscaped( ret, _range.op.asString() );
129         str::appendEscaped( ret, _range.value.asString() );
130         str::appendEscaped( ret, _arch.asString() );
131         return ret;
132       }
133
134       Edition::MatchRange _range;
135       Arch                _arch;
136     };
137
138     /** Whether the current capability matches a given one.
139      * Query asserts \a iter_r points to a capability and we
140      * have to check the match only.
141      */
142     struct CapabilityMatchPredicate
143     {
144       CapabilityMatchPredicate( Capability cap_r )
145         : _cap( cap_r )
146       {}
147
148       bool operator()( sat::LookupAttr::iterator iter_r ) const
149       {
150         return _cap.matches( iter_r.asType<Capability>() ) == CapMatch::yes;
151       }
152
153       std::string serialize() const
154       {
155         std::string ret( "CapabilityMatch" );
156         str::appendEscaped( ret, _cap.asString() );
157         return ret;
158       }
159
160       Capability _cap;
161     };
162
163     /////////////////////////////////////////////////////////////////
164     //
165     /////////////////////////////////////////////////////////////////
166     /** Match data per attribtue.
167      *
168      * This includes the attribute itself, an optional \ref StrMatcher
169      * to restrict the query to certain string values, and an optional
170      * boolean \ref Predicate that may apply further restrictions that can
171      * not be expressed by the \ref strMatcher.
172      *
173      * Example for such a \ref predicate would be an additional edition range
174      * check whan looking for dependencies. The \ref strMatcher would
175      * find potential matches by looking at the dependencies name, the
176      * predicate will then check the edition ranges.
177      *
178      * As the \ref predicate takes an iterator pointing to the current
179      * match, it's also suitable for sub-structure (flexarray) inspection
180      * (\see \ref sat::LookupAttr::iterator::solvAttrSubEntry).
181      *
182      * (bsc#1035729) If SolvAttr::name searches for an explicit \c kind:name,
183      * this \c kind is stored in \ref kindPredicate and will overwrite any
184      * 'global' kind restriction applied via \ref PoolQuery::addKind. This
185      * task can't be passed off to a predicate, as \ref PoolQueryMatcher::isAMatch
186      * must accept only explicit-kind-checking predicate matches, in case the
187      * 'global' kind restriction woudl otherwise discard the match.
188      *
189      * \note: \see \ref addPredicate for further constraints.
190      */
191     struct AttrMatchData
192     {
193       typedef function<bool(sat::LookupAttr::iterator)> Predicate;
194
195       static bool always( sat::LookupAttr::iterator ) { return true; }
196       static bool never( sat::LookupAttr::iterator ) { return false; }
197
198       AttrMatchData()
199       {}
200
201       AttrMatchData( sat::SolvAttr attr_r )
202         : attr( attr_r )
203       {}
204
205       AttrMatchData( sat::SolvAttr attr_r, const StrMatcher & strMatcher_r )
206         : attr( attr_r )
207         , strMatcher( strMatcher_r )
208       {}
209
210       AttrMatchData( sat::SolvAttr attr_r, const StrMatcher & strMatcher_r,
211                      const Predicate & predicate_r, const std::string & predicateStr_r )
212         : attr( attr_r )
213         , strMatcher( strMatcher_r )
214         , predicate( predicate_r )
215         , predicateStr( predicateStr_r )
216       {}
217
218       /** A usable Predicate must provide a string serialization.
219        * As there is no \c operator== for \ref Predicate, we compare it's
220        * string representation instead. If you add new predicated, check the
221        * deserialization code in \ref deserialize.
222        */
223       template<class TPredicate>
224       void addPredicate( const TPredicate & predicate_r )
225       {
226         predicate    = predicate_r;
227         predicateStr = predicate_r.serialize();
228       }
229
230       /** Dumb serialization.
231        * \code
232        *   AttrMatchData ATTRIBUTE SEARCHSTRING [C|SEARCHMODE|X] SERIALIZED_PREDICATE
233        * \endcode
234       */
235       std::string serialize() const
236       {
237         std::string ret( "AttrMatchData" );
238         str::appendEscaped( ret, attr.asString() );
239         str::appendEscaped( ret, strMatcher.searchstring() );
240         str::appendEscaped( ret, serializeMode( strMatcher.flags().mode() ) );
241         str::appendEscaped( ret, predicateStr );
242         return ret;
243       }
244
245        /** Dumb restore from serialized string.
246         * \throw Exception on parse error.
247         */
248       static AttrMatchData deserialize( const std::string & str_r )
249       {
250         std::vector<std::string> words;
251         str::splitEscaped( str_r, std::back_inserter(words) );
252         if ( words.empty() || words[0] != "AttrMatchData" )
253           ZYPP_THROW( Exception( str::Str() << "Expecting AttrMatchData: " << str_r ) );
254         if ( words.size() != 5 )
255           ZYPP_THROW( Exception( str::Str() << "Wrong number of words: " << str_r ) );
256
257         AttrMatchData ret;
258         ret.attr = sat::SolvAttr( words[1] );
259         ret.strMatcher = StrMatcher( words[2] );
260         if ( Match::Mode mode = deserializeMode( words[3] ) )
261           ret.strMatcher.setFlags( mode );
262         ret.predicateStr = words[4];
263
264         // now the predicate
265         words.clear();
266         str::splitEscaped( ret.predicateStr, std::back_inserter(words) );
267         if ( ! words.empty() )
268         {
269           if ( words[0] == "EditionRange" )
270           {
271             switch( words.size() )
272             {
273               case 3:
274                 ret.predicate = EditionRangePredicate( Rel(words[1]), Edition(words[2]) );
275                 break;
276               case 4:
277                 ret.predicate = EditionRangePredicate( Rel(words[1]), Edition(words[2]), Arch(words[3]) );
278                 break;
279               default:
280                 ZYPP_THROW( Exception( str::Str() << "Wrong number of words: " << str_r ) );
281                 break;
282             }
283           }
284           else if ( words[0] == "SolvableRange" )
285           {
286             switch( words.size() )
287             {
288               case 3:
289                 ret.predicate = SolvableRangePredicate( Rel(words[1]), Edition(words[2]) );
290                 break;
291               case 4:
292                 ret.predicate = SolvableRangePredicate( Rel(words[1]), Edition(words[2]), Arch(words[3]) );
293                 break;
294               default:
295                 ZYPP_THROW( Exception( str::Str() << "Wrong number of words: " << str_r ) );
296                 break;
297             }
298           }
299           else if ( words[0] == "CapabilityMatch" )
300           {
301             if ( words.size() != 2 )
302               ZYPP_THROW( Exception( str::Str() << "Wrong number of words: " << str_r ) );
303             ret.predicate = CapabilityMatchPredicate( Capability(words[1]) );
304           }
305           else
306             ZYPP_THROW( Exception( str::Str() << "Unknown predicate: " << str_r ) );
307         }
308         return ret;
309      }
310
311       sat::SolvAttr    attr;
312       StrMatcher       strMatcher;
313       Predicate        predicate;
314       std::string      predicateStr;
315       ResKind          kindPredicate = ResKind::nokind; // holds the 'kind' part if SolvAttr:name looks for an explicit 'kind:name'
316
317     private:
318       /** Serialize \ref strMatcher \ref Match::Mode */
319       static std::string serializeMode( Match::Mode mode_r )
320       {
321         // Legacy code used "[C|X]" to differ just between OTHER (need to (C)ompile) and
322         // using the default search mode. As we now allow to specify a SEARCHMODE we
323         // need to serialize it:
324         switch ( mode_r )
325         {
326 #define OUTS(M,S) case Match::M: return #S; break
327           // (C)ompile
328           OUTS( OTHER,          C );
329           // well known modes:
330           OUTS( STRING,         T );
331           OUTS( STRINGSTART,    S );
332           OUTS( STRINGEND,      E );
333           OUTS( SUBSTRING,      B );
334           OUTS( GLOB,           G );
335           OUTS( REGEX,          R );
336 #undef OUTS
337           // everything else use default
338           case Match::NOTHING:
339             break;
340         }
341         return "X";
342       }
343
344       /** Deserialize \ref strMatcher \ref Match::Mode */
345       static Match::Mode deserializeMode( const std::string & str_r )
346       {
347         switch ( str_r[0] )
348         {
349 #define OUTS(M,C) case *#C: return Match::M; break
350           // (C)ompile
351           OUTS( OTHER,          C );
352           // well known modes:
353           OUTS( STRING,         T );
354           OUTS( STRINGSTART,    S );
355           OUTS( STRINGEND,      E );
356           OUTS( SUBSTRING,      B );
357           OUTS( GLOB,           G );
358           OUTS( REGEX,          R );
359 #undef OUTS
360           // everything else use default
361           default:
362             break;
363         }
364         return Match::NOTHING;
365       }
366     };
367
368     /** \relates AttrMatchData */
369     inline std::ostream & operator<<( std::ostream & str, const AttrMatchData & obj )
370     {
371       str << obj.attr << ": " << obj.strMatcher;
372       if ( obj.kindPredicate )
373         str << " +(" << obj.kindPredicate << ")";
374       if ( obj.predicate )
375         str << " +(" << obj.predicateStr << ")";
376       return str;
377     }
378
379     /** \relates AttrMatchData */
380     inline bool operator==( const AttrMatchData & lhs, const AttrMatchData & rhs )
381     {
382       return ( lhs.attr == rhs.attr
383                && lhs.strMatcher == rhs.strMatcher
384                && lhs.predicateStr == rhs.predicateStr );
385     }
386
387     /** \relates AttrMatchData */
388     inline bool operator!=( const AttrMatchData & lhs, const AttrMatchData & rhs )
389     { return !( lhs == rhs ); }
390
391     /** \relates AttrMatchData Arbitrary order for std::container. */
392     inline bool operator<( const AttrMatchData & lhs, const AttrMatchData & rhs )
393     {
394       if ( lhs.attr != rhs.attr )
395         return (  lhs.attr < rhs.attr );
396       if ( lhs.strMatcher != rhs.strMatcher )
397         return (  lhs.strMatcher < rhs.strMatcher );
398       if ( lhs.predicateStr != rhs.predicateStr )
399         return (  lhs.predicateStr < rhs.predicateStr );
400       return false;
401     }
402
403     typedef std::list<AttrMatchData> AttrMatchList;
404
405
406   } /////////////////////////////////////////////////////////////////
407   // namespace
408   ///////////////////////////////////////////////////////////////////
409
410   ///////////////////////////////////////////////////////////////////
411   //
412   //  CLASS NAME : PoolQuery::Impl
413   //
414   /** */
415   class PoolQuery::Impl
416   {
417   public:
418     Impl()
419       : _flags( Match::SUBSTRING | Match::NOCASE | Match::SKIP_KIND )
420       , _match_word(false)
421       , _status_flags(ALL)
422     {}
423
424     ~Impl()
425     {}
426
427   public:
428     /** String representation */
429     string asString() const;
430
431     /** \name Raw query options. */
432     //@{
433     /** Raw search strings. */
434     StrContainer _strings;
435     /** Raw attributes */
436     AttrRawStrMap _attrs;
437     /** Uncompiled attributes with predicate. */
438     std::set<AttrMatchData> _uncompiledPredicated;
439
440     /** Sat solver search flags */
441     Match _flags;
442     bool _match_word;
443
444     /** Sat solver status flags */
445     StatusFilter _status_flags;
446
447     /** Edition condition operand */
448     Edition _edition;
449     /** Operator for edition condition */
450     Rel _op;
451
452     /** Repos to search. */
453     StrContainer _repos;
454
455     /** Kinds to search */
456     Kinds _kinds;
457     //@}
458
459   public:
460
461     bool operator<( const PoolQuery::Impl & rhs ) const
462     {
463 #define OUTS(A) if ( A != rhs.A ) return A < rhs.A;
464       OUTS( _strings );
465       OUTS( _attrs );
466       OUTS( _uncompiledPredicated );
467       OUTS( _flags.get() );
468       OUTS( _match_word );
469       OUTS( _status_flags );
470       OUTS( _edition );
471       OUTS( _op.inSwitch() );
472       OUTS( _repos );
473       OUTS( _kinds );
474 #undef OUTS
475       return false;
476     }
477
478     bool operator==( const PoolQuery::Impl & rhs ) const
479     {
480       if ( _flags == rhs._flags
481         // bnc#792901: while libzypp uses exact match mode for a single
482         // package name lock, zypper always uses glob. :(
483         // We unify those two forms to enable zypper to remove zypp locks
484         // without need to actually evaluate the query (which would require
485         // repos to be loaded).
486         || ( ( ( _flags.isModeString() && rhs._flags.isModeGlob() )
487             || ( _flags.isModeGlob() && rhs._flags.isModeString() ) )
488           && _strings.empty()
489           && _attrs.size() == 1
490           && _attrs.begin()->first == sat::SolvAttr::name ) )
491       {
492         return ( _strings == rhs._strings
493               && _attrs == rhs._attrs
494               && _uncompiledPredicated == rhs._uncompiledPredicated
495               && _match_word == rhs._match_word
496               && _status_flags == rhs._status_flags
497               && _edition == rhs._edition
498               && _op == rhs._op
499               && _repos == rhs._repos
500               && _kinds == rhs._kinds );
501       }
502       return false;
503     }
504
505     bool operator!=( const PoolQuery::Impl & rhs ) const
506     { return ! operator==( rhs ); }
507
508   public:
509     /** Compile the regex.
510      * Basically building the \ref _attrMatchList from strings.
511      * \throws MatchException Any of the exceptions thrown by \ref StrMatcher::compile.
512      */
513     void compile() const;
514
515     /** StrMatcher per attribtue. */
516     mutable AttrMatchList _attrMatchList;
517
518   private:
519     /** Join patterns in \a container_r according to \a flags_r into a single \ref StrMatcher.
520      * The \ref StrMatcher returned will be a REGEX if more than one pattern was passed.
521      */
522     StrMatcher joinedStrMatcher( const StrContainer & container_r, const Match & flags_r ) const;
523
524   private:
525     friend Impl * rwcowClone<Impl>( const Impl * rhs );
526     /** clone for RWCOW_pointer */
527     Impl * clone() const
528     { return new Impl( *this ); }
529   };
530
531   ///////////////////////////////////////////////////////////////////
532
533   struct MyInserter
534   {
535     MyInserter(PoolQuery::StrContainer & cont) : _cont(cont) {}
536
537     bool operator()(const string & str)
538     {
539       _cont.insert(str);
540       return true;
541     }
542
543     PoolQuery::StrContainer & _cont;
544   };
545
546
547   struct EmptyFilter
548   {
549     bool operator()(const string & str)
550     {
551       return !str.empty();
552     }
553   };
554
555   void PoolQuery::Impl::compile() const
556   {
557     _attrMatchList.clear();
558
559     if ( _flags.mode() == Match::OTHER ) // this will never succeed...
560       ZYPP_THROW( MatchUnknownModeException( _flags ) );
561
562     // 'different'         - will have to iterate through all and match by ourselves (slow)
563     // 'same'              - will pass the compiled string to dataiterator_init
564     // 'one-attr'          - will pass it to dataiterator_init
565     // 'one-non-regex-str' - will pass to dataiterator_init, set flag to SEARCH_STRING or SEARCH_SUBSTRING
566
567     // // NO ATTRIBUTE
568     // else
569     //   for all _strings
570     //     create regex; store in rcstrings; if more strings flag regex;
571     if (_attrs.empty())
572     {
573       ; // A default 'query-all' will be added after all sources are processed.
574     }
575
576     // // ONE ATTRIBUTE
577     // else if _attrs is not empty but it contains just one attr
578     //   for all _strings and _attr[key] strings
579     //     create regex; flag 'one-attr'; if more strings flag regex;
580     else if (_attrs.size() == 1)
581     {
582       StrContainer joined;
583       invokeOnEach(_strings.begin(), _strings.end(), EmptyFilter(), MyInserter(joined));
584       invokeOnEach(_attrs.begin()->second.begin(), _attrs.begin()->second.end(), EmptyFilter(), MyInserter(joined));
585
586       _attrMatchList.push_back( AttrMatchData( _attrs.begin()->first, joinedStrMatcher( joined, _flags ) ) );
587     }
588
589     // // MULTIPLE ATTRIBUTES
590     else
591     {
592       // check whether there are any per-attribute strings
593       bool attrvals_empty = true;
594       for_( ai, _attrs.begin(), _attrs.end() )
595       {
596         if ( ai->second.empty() )
597           continue;
598         for_( it, ai->second.begin(), ai->second.end() )
599         {
600           if ( !it->empty() )
601           {
602             attrvals_empty = false;
603             break;
604           }
605         }
606         if ( ! attrvals_empty )
607           break;
608       }
609
610       // chceck whether the per-attribute strings are all the same
611       bool attrvals_thesame = true;
612       AttrRawStrMap::const_iterator ai = _attrs.begin();
613       const StrContainer & set1 = ai->second;
614       ++ai;
615       for (; ai != _attrs.end(); ++ai)
616       {
617         StrContainer result;
618         set_difference(
619           set1.begin(), set1.end(),
620           ai->second.begin(), ai->second.end(),
621           inserter(result, result.begin())/*, ltstr()*/);
622         if (!result.empty())
623         {
624           attrvals_thesame = false;
625           break;
626         }
627       }
628
629       // // THE SAME STRINGS FOR DIFFERENT ATTRS
630       // else if _attrs is not empty but it does not contain strings
631       //   for each key in _attrs take all _strings
632       //     create regex; store in rcstrings; flag 'same'; if more strings flag regex;
633       if (attrvals_empty || attrvals_thesame)
634       {
635         StrContainer joined;
636         if (attrvals_empty)
637         {
638           invokeOnEach(_strings.begin(), _strings.end(), EmptyFilter(), MyInserter(joined));
639         }
640         else
641         {
642           invokeOnEach(_strings.begin(), _strings.end(), EmptyFilter(), MyInserter(joined));
643           invokeOnEach(_attrs.begin()->second.begin(), _attrs.begin()->second.end(), EmptyFilter(), MyInserter(joined));
644         }
645
646         // May use the same StrMatcher for all
647         StrMatcher matcher( joinedStrMatcher( joined, _flags ) );
648         for_( ai, _attrs.begin(), _attrs.end() )
649         {
650           _attrMatchList.push_back( AttrMatchData( ai->first, matcher ) );
651         }
652       }
653
654       // // DIFFERENT STRINGS FOR DIFFERENT ATTRS
655       // if _attrs is not empty and it contains non-empty vectors with non-empty strings
656       //   for each key in _attrs take all _strings + all _attrs[key] strings
657       //     create regex; flag 'different'; if more strings flag regex;
658       else
659       {
660         for_(ai, _attrs.begin(), _attrs.end())
661         {
662           StrContainer joined;
663           invokeOnEach(_strings.begin(), _strings.end(), EmptyFilter(), MyInserter(joined));
664           invokeOnEach(ai->second.begin(), ai->second.end(), EmptyFilter(), MyInserter(joined));
665
666           _attrMatchList.push_back( AttrMatchData( ai->first, joinedStrMatcher( joined, _flags ) ) );
667         }
668       }
669     }
670
671     // Now handle any predicated queries
672     if ( ! _uncompiledPredicated.empty() )
673     {
674       StrContainer global;
675       invokeOnEach( _strings.begin(), _strings.end(), EmptyFilter(), MyInserter(global) );
676       for_( it, _uncompiledPredicated.begin(), _uncompiledPredicated.end() )
677       {
678         if ( it->strMatcher.flags().mode() == Match::OTHER )
679         {
680           // need to compile:
681           StrContainer joined( global );
682           const std::string & mstr( it->strMatcher.searchstring() );
683           if ( ! mstr.empty() )
684             joined.insert( mstr );
685
686           // copy and exchange the StrMatcher
687           AttrMatchData nattr( *it );
688           nattr.strMatcher = joinedStrMatcher( joined, _flags );
689           _attrMatchList.push_back( std::move(nattr) );
690         }
691         else
692         {
693           // copy matcher
694          _attrMatchList.push_back( *it );
695         }
696       }
697     }
698
699     // If no attributes defined at all, then add 'query all'
700     if ( _attrMatchList.empty() )
701     {
702       _attrMatchList.push_back( AttrMatchData( sat::SolvAttr::allAttr, joinedStrMatcher( _strings, _flags ) ) );
703     }
704
705     // Finally check here, whether all involved regex compile.
706     for_( it, _attrMatchList.begin(), _attrMatchList.end() )
707     {
708       it->strMatcher.compile(); // throws on error
709     }
710     //DBG << asString() << endl;
711   }
712
713   ///////////////////////////////////////////////////////////////////
714   namespace
715   {
716     /** Escape \a str_r for use in a regex.
717      * \a flags_r determines whether the input string is interpreted
718      * as regex, glob or plain string.
719      */
720     std::string rxEscape( std::string str_r, const Match & flags_r )
721     {
722       if ( str_r.empty() || flags_r.isModeRegex() )
723         return str_r;
724
725       if ( flags_r.isModeGlob() )
726         return str::rxEscapeGlob( std::move(str_r) );
727
728       return str::rxEscapeStr( std::move(str_r) );
729     }
730   } // namespace
731   ///////////////////////////////////////////////////////////////////
732
733   StrMatcher PoolQuery::Impl::joinedStrMatcher( const StrContainer & container_r, const Match & flags_r ) const
734   {
735     if ( container_r.empty() )
736       return StrMatcher( std::string(), flags_r );
737
738     if ( container_r.size() == 1 && !_match_word )      // use RX to match words
739       return StrMatcher( *container_r.begin(), flags_r );
740
741     // Convert to a regex.
742     // Note: Modes STRING and GLOB match whole strings (anchored ^ $)
743     //       SUBSTRING and REGEX match substrings      (match_word anchores SUBSTRING \b)
744     Match retflags( flags_r );
745     retflags.setModeRegex();
746     str::Str ret;
747
748     if ( flags_r.isModeString() || flags_r.isModeGlob() )
749       ret << "^";
750     else if ( _match_word )
751       ret << "\\b";
752
753     // (..|..|..)
754     char sep = '(';
755     for ( const::std::string & s : container_r )
756     {
757       ret << sep << rxEscape( s, flags_r );
758       if ( sep == '(' )
759         sep = '|';
760     }
761     ret << ')';
762
763     if ( flags_r.isModeString() || flags_r.isModeGlob() )
764       ret << "$";
765     else if ( _match_word )
766       ret << "\\b";
767
768     return StrMatcher( ret, retflags );
769   }
770
771   string PoolQuery::Impl::asString() const
772   {
773     ostringstream o;
774
775     o << "kinds: ";
776     if ( _kinds.empty() )
777       o << "ALL";
778     else
779     {
780       for(Kinds::const_iterator it = _kinds.begin();
781           it != _kinds.end(); ++it)
782         o << *it << " ";
783     }
784     o << endl;
785
786     o << "repos: ";
787     if ( _repos.empty() )
788       o << "ALL";
789     else
790     {
791       for(StrContainer::const_iterator it = _repos.begin();
792           it != _repos.end(); ++it)
793         o << *it << " ";
794     }
795     o << endl;
796
797     o << "version: "<< _op << " " << _edition.asString() << endl;
798     o << "status: " << ( _status_flags ? ( _status_flags == INSTALLED_ONLY ? "INSTALLED_ONLY" : "UNINSTALLED_ONLY" )
799                                        : "ALL" ) << endl;
800
801     o << "string match flags: " << Match(_flags) << endl;
802
803     // raw
804     o << "strings: ";
805     for(StrContainer::const_iterator it = _strings.begin();
806         it != _strings.end(); ++it)
807       o << *it << " ";
808     o << endl;
809
810     o << "attributes: " << endl;
811     for(AttrRawStrMap::const_iterator ai = _attrs.begin(); ai != _attrs.end(); ++ai)
812     {
813       o << "* " << ai->first << ": ";
814       for(StrContainer::const_iterator vi = ai->second.begin();
815           vi != ai->second.end(); ++vi)
816         o << *vi << " ";
817       o << endl;
818     }
819
820     o << "predicated: " << endl;
821     for_( it, _uncompiledPredicated.begin(), _uncompiledPredicated.end() )
822     {
823       o << "* " << *it << endl;
824     }
825
826     // compiled
827     o << "last attribute matcher compiled: " << endl;
828     if ( _attrMatchList.empty() )
829     {
830       o << "not yet compiled" << endl;
831     }
832     else
833     {
834       for_( it, _attrMatchList.begin(), _attrMatchList.end() )
835       {
836         o << "* " << *it << endl;
837       }
838     }
839     return o.str();
840   }
841
842   ///////////////////////////////////////////////////////////////////
843
844   ///////////////////////////////////////////////////////////////////
845   //
846   //    CLASS NAME : PoolQuery
847   //
848   ///////////////////////////////////////////////////////////////////
849
850   PoolQuery::PoolQuery()
851     : _pimpl(new Impl())
852   {}
853
854   PoolQuery::~PoolQuery()
855   {}
856
857   void PoolQuery::addRepo(const std::string &repoalias)
858   {
859     if (repoalias.empty())
860     {
861       WAR << "ignoring an empty repository alias" << endl;
862       return;
863     }
864     _pimpl->_repos.insert(repoalias);
865   }
866
867   void PoolQuery::addKind(const ResKind & kind)
868   { _pimpl->_kinds.insert(kind); }
869
870   void PoolQuery::addString(const string & value)
871   { _pimpl->_strings.insert(value); }
872
873   void PoolQuery::addAttribute(const sat::SolvAttr & attr, const std::string & value)
874   { _pimpl->_attrs[attr].insert(value); }
875
876   void PoolQuery::addDependency( const sat::SolvAttr & attr, const std::string & name, const Rel & op, const Edition & edition )
877   {
878     // Default Match::OTHER indicates need to compile, i.e. to merge name into the global search string and mode.
879     return addDependency( attr, name, op, edition, Arch_empty, Match::OTHER );
880   }
881
882   void PoolQuery::addDependency( const sat::SolvAttr & attr, const std::string & name, const Rel & op, const Edition & edition, const Arch & arch )
883   {
884     // Default Match::OTHER indicates need to compile, i.e. to merge name into the global search string and mode.
885     return addDependency( attr, name, op, edition, arch, Match::OTHER );
886   }
887
888   void PoolQuery::addDependency( const sat::SolvAttr & attr, const std::string & name, const Rel & op, const Edition & edition, const Arch & arch, Match::Mode mode )
889   {
890     if ( op == Rel::NONE )      // will never match.
891       return;
892
893     // SolvAttr::name with explicit 'kind:name' will overwrite the default _kinds
894     ResKind explicitKind;
895     if ( attr == sat::SolvAttr::name )
896       explicitKind = ResKind::explicitBuiltin( name );
897
898     // Legacy: Match::OTHER and no additional constraints on edition/arch/kind
899     //         require addAttribute, otherwise de-serialisation breaks (serialized
900     //         and de-serialized query could be !=).
901     // From the results POV we could also use the predicated case below.
902     if ( op == Rel::ANY && arch.empty() && !explicitKind && mode == Match::OTHER )
903     {
904       addAttribute( attr, name );
905       return;
906     }
907
908     // Match::OTHER indicates need to compile
909     // (merge global search strings into name).
910     AttrMatchData attrMatchData( attr );
911     if ( !explicitKind )
912       attrMatchData.strMatcher = StrMatcher( name, mode );
913     else
914     {
915       // ResKind::explicitBuiltin call above asserts the presence of the ':' in name
916       attrMatchData.strMatcher = StrMatcher( strchr( name.c_str(), ':')+1, mode );
917       attrMatchData.kindPredicate = explicitKind;
918     }
919
920     if ( isDependencyAttribute( attr ) )
921       attrMatchData.addPredicate( EditionRangePredicate( op, edition, arch ) );
922     else
923       attrMatchData.addPredicate( SolvableRangePredicate( op, edition, arch ) );
924
925     _pimpl->_uncompiledPredicated.insert( attrMatchData );
926   }
927
928   void PoolQuery::addDependency( const sat::SolvAttr & attr, Capability cap_r )
929   {
930     CapDetail cap( cap_r );
931     if ( ! cap.isSimple() ) // will never match.
932       return;
933
934     // Matches STRING per default. (won't get compiled!)
935     AttrMatchData attrMatchData( attr, StrMatcher( cap.name().asString() ) );
936
937     if ( isDependencyAttribute( attr ) )
938       attrMatchData.addPredicate( CapabilityMatchPredicate( cap_r ) );
939     else
940       attrMatchData.addPredicate( SolvableRangePredicate( cap.op(), cap.ed() ) );
941
942     _pimpl->_uncompiledPredicated.insert( attrMatchData );
943   }
944
945   void PoolQuery::setEdition(const Edition & edition, const Rel & op)
946   {
947     _pimpl->_edition = edition;
948     _pimpl->_op = op;
949   }
950
951   void PoolQuery::setMatchSubstring()   { _pimpl->_flags.setModeSubstring();    _pimpl->_match_word = false; }
952   void PoolQuery::setMatchExact()       { _pimpl->_flags.setModeString();       _pimpl->_match_word = false; }
953   void PoolQuery::setMatchRegex()       { _pimpl->_flags.setModeRegex();        _pimpl->_match_word = false; }
954   void PoolQuery::setMatchGlob()        { _pimpl->_flags.setModeGlob();         _pimpl->_match_word = false; }
955   void PoolQuery::setMatchWord()        { _pimpl->_flags.setModeSubstring();    _pimpl->_match_word = true; }
956
957   Match PoolQuery::flags() const
958   { return _pimpl->_flags; }
959   void PoolQuery::setFlags( const Match & flags )
960   { _pimpl->_flags = flags; }
961
962
963   void PoolQuery::setInstalledOnly()
964   { _pimpl->_status_flags = INSTALLED_ONLY; }
965   void PoolQuery::setUninstalledOnly()
966   { _pimpl->_status_flags = UNINSTALLED_ONLY; }
967   void PoolQuery::setStatusFilterFlags( PoolQuery::StatusFilter flags )
968   { _pimpl->_status_flags = flags; }
969
970
971   const PoolQuery::StrContainer &
972   PoolQuery::strings() const
973   { return _pimpl->_strings; }
974
975   const PoolQuery::AttrRawStrMap &
976   PoolQuery::attributes() const
977   { return _pimpl->_attrs; }
978
979   const PoolQuery::StrContainer &
980   PoolQuery::attribute(const sat::SolvAttr & attr) const
981   {
982     static const PoolQuery::StrContainer nocontainer;
983     AttrRawStrMap::const_iterator it = _pimpl->_attrs.find(attr);
984     return it != _pimpl->_attrs.end() ? it->second : nocontainer;
985   }
986
987   const Edition PoolQuery::edition() const
988   { return _pimpl->_edition; }
989   const Rel PoolQuery::editionRel() const
990   { return _pimpl->_op; }
991
992
993   const PoolQuery::Kinds &
994   PoolQuery::kinds() const
995   { return _pimpl->_kinds; }
996
997   const PoolQuery::StrContainer &
998   PoolQuery::repos() const
999   { return _pimpl->_repos; }
1000
1001
1002   bool PoolQuery::caseSensitive() const
1003   { return !_pimpl->_flags.test( Match::NOCASE ); }
1004   void PoolQuery::setCaseSensitive( bool value )
1005   { _pimpl->_flags.turn( Match::NOCASE, !value ); }
1006
1007   bool PoolQuery::filesMatchFullPath() const
1008   { return _pimpl->_flags.test( Match::FILES ); }
1009   void PoolQuery::setFilesMatchFullPath( bool value )
1010   { _pimpl->_flags.turn( Match::FILES, value ); }
1011
1012   bool PoolQuery::matchExact() const            { return _pimpl->_flags.isModeString(); }
1013   bool PoolQuery::matchSubstring() const        { return _pimpl->_flags.isModeSubstring() && !_pimpl->_match_word; }
1014   bool PoolQuery::matchGlob() const             { return _pimpl->_flags.isModeGlob(); }
1015   bool PoolQuery::matchRegex() const            { return _pimpl->_flags.isModeRegex(); }
1016   bool PoolQuery::matchWord() const             { return _pimpl->_flags.isModeSubstring() && _pimpl->_match_word; }
1017
1018   PoolQuery::StatusFilter PoolQuery::statusFilterFlags() const
1019   { return _pimpl->_status_flags; }
1020
1021   bool PoolQuery::empty() const
1022   {
1023     try { return begin() == end(); }
1024     catch (const Exception & ex) {}
1025     return true;
1026   }
1027
1028   PoolQuery::size_type PoolQuery::size() const
1029   {
1030     try
1031     {
1032       size_type count = 0;
1033       for_( it, begin(), end() )
1034         ++count;
1035       return count;
1036     }
1037     catch (const Exception & ex) {}
1038     return 0;
1039   }
1040
1041   void PoolQuery::execute(ProcessResolvable fnc)
1042   { invokeOnEach( begin(), end(), fnc); }
1043
1044
1045   /*DEPRECATED LEGACY:*/void PoolQuery::setRequireAll( bool ) {}
1046   /*DEPRECATED LEGACY:*/bool PoolQuery::requireAll() const    { return false; }
1047
1048   ///////////////////////////////////////////////////////////////////
1049   //
1050   //  CLASS NAME : PoolQuery::Attr
1051   //
1052   /**
1053    * represents all atributes in PoolQuery except SolvAtributes, which are
1054    * used as is (not needed extend anything if someone adds new solv attr)
1055    */
1056   struct PoolQueryAttr : public IdStringType<PoolQueryAttr>
1057   {
1058   private:
1059     friend class IdStringType<PoolQueryAttr>;
1060     IdString _str;
1061   public:
1062
1063     //noAttr
1064     PoolQueryAttr(){}
1065
1066     explicit PoolQueryAttr( const char* cstr_r )
1067         : _str( cstr_r )
1068       {}
1069
1070     explicit PoolQueryAttr( const std::string & str_r )
1071         : _str( str_r )
1072       {}
1073
1074     // unknown atributes
1075     static const PoolQueryAttr noAttr;
1076
1077     // PoolQuery's own attributes
1078     static const PoolQueryAttr repoAttr;
1079     static const PoolQueryAttr kindAttr;
1080     static const PoolQueryAttr stringAttr;
1081     static const PoolQueryAttr stringTypeAttr;
1082     static const PoolQueryAttr requireAllAttr;  // LEAGACY: attribute was defined but never implemented.
1083     static const PoolQueryAttr caseSensitiveAttr;
1084     static const PoolQueryAttr installStatusAttr;
1085     static const PoolQueryAttr editionAttr;
1086     static const PoolQueryAttr complexAttr;
1087   };
1088
1089   const PoolQueryAttr PoolQueryAttr::noAttr;
1090
1091   const PoolQueryAttr PoolQueryAttr::repoAttr( "repo" );
1092   const PoolQueryAttr PoolQueryAttr::kindAttr( "type" );
1093   const PoolQueryAttr PoolQueryAttr::stringAttr( "query_string" );
1094   const PoolQueryAttr PoolQueryAttr::stringTypeAttr("match_type");
1095   const PoolQueryAttr PoolQueryAttr::requireAllAttr("require_all");     // LEAGACY: attribute was defined but never implemented.
1096   const PoolQueryAttr PoolQueryAttr::caseSensitiveAttr("case_sensitive");
1097   const PoolQueryAttr PoolQueryAttr::installStatusAttr("install_status");
1098   const PoolQueryAttr PoolQueryAttr::editionAttr("version");
1099   const PoolQueryAttr PoolQueryAttr::complexAttr("complex");
1100
1101   class StringTypeAttr : public IdStringType<PoolQueryAttr>
1102   {
1103     friend class IdStringType<StringTypeAttr>;
1104     IdString _str;
1105
1106   public:
1107     StringTypeAttr(){}
1108     explicit StringTypeAttr( const char* cstr_r )
1109             : _str( cstr_r ){}
1110     explicit StringTypeAttr( const std::string & str_r )
1111              : _str( str_r ){}
1112
1113     static const StringTypeAttr noAttr;
1114
1115     static const StringTypeAttr exactAttr;
1116     static const StringTypeAttr substringAttr;
1117     static const StringTypeAttr regexAttr;
1118     static const StringTypeAttr globAttr;
1119     static const StringTypeAttr wordAttr;
1120   };
1121
1122   const StringTypeAttr StringTypeAttr::noAttr;
1123
1124   const StringTypeAttr StringTypeAttr::exactAttr("exact");
1125   const StringTypeAttr StringTypeAttr::substringAttr("substring");
1126   const StringTypeAttr StringTypeAttr::regexAttr("regex");
1127   const StringTypeAttr StringTypeAttr::globAttr("glob");
1128   const StringTypeAttr StringTypeAttr::wordAttr("word");
1129
1130   ///////////////////////////////////////////////////////////////////
1131
1132
1133   //\TODO maybe ctor with stream can be usefull
1134   //\TODO let it throw, let it throw, let it throw.
1135   bool PoolQuery::recover( istream &str, char delim )
1136   {
1137     bool finded_something = false; //indicates some atributes is finded
1138     string s;
1139     do {
1140       if ( str.eof() )
1141         break;
1142
1143       getline( str, s, delim );
1144
1145       if ((!s.empty()) && s[0]=='#') //comment
1146       {
1147         continue;
1148       }
1149
1150       string::size_type pos = s.find(':');
1151       if (s.empty() || pos == s.npos) // some garbage on line... act like blank line
1152       {
1153         if (finded_something) //is first blank line after record?
1154         {
1155           break;
1156         }
1157         else
1158         {
1159           continue;
1160         }
1161       }
1162
1163       finded_something = true;
1164
1165       string attrName(str::trim(string(s,0,pos))); // trimmed name of atribute
1166       string attrValue(str::trim(string(s,pos+1,s.npos))); //trimmed value
1167
1168       PoolQueryAttr attribute( attrName );
1169
1170       if ( attribute==PoolQueryAttr::repoAttr )
1171       {
1172         addRepo( attrValue );
1173       }
1174       /* some backwards compatibility */
1175       else if ( attribute==PoolQueryAttr::kindAttr || attribute=="kind" )
1176       {
1177         addKind( ResKind(attrValue) );
1178       }
1179       else if ( attribute==PoolQueryAttr::stringAttr
1180         || attribute=="global_string")
1181       {
1182         addString( attrValue );
1183       }
1184       else if ( attribute==PoolQueryAttr::stringTypeAttr
1185         || attribute=="string_type" )
1186       {
1187         StringTypeAttr s(attrValue);
1188         if( s == StringTypeAttr::regexAttr )
1189         {
1190           setMatchRegex();
1191         }
1192         else if ( s == StringTypeAttr::globAttr )
1193         {
1194           setMatchGlob();
1195         }
1196         else if ( s == StringTypeAttr::exactAttr )
1197         {
1198           setMatchExact();
1199         }
1200         else if ( s == StringTypeAttr::substringAttr )
1201         {
1202           setMatchSubstring();
1203         }
1204         else if ( s == StringTypeAttr::wordAttr )
1205         {
1206           setMatchWord();
1207         }
1208         else if ( s == StringTypeAttr::noAttr )
1209         {
1210           WAR << "unknown string type " << attrValue << endl;
1211         }
1212         else
1213         {
1214           WAR << "forget recover some attribute defined as String type attribute: " << attrValue << endl;
1215         }
1216       }
1217       else if ( attribute==PoolQueryAttr::requireAllAttr )
1218       {
1219         // LEAGACY: attribute was defined but never implemented.
1220         // Actually it should not occur outside our testcases.
1221       }
1222       else if ( attribute==PoolQueryAttr::caseSensitiveAttr )
1223       {
1224         if ( str::strToTrue(attrValue) )
1225         {
1226           setCaseSensitive(true);
1227         }
1228         else if ( !str::strToFalse(attrValue) )
1229         {
1230           setCaseSensitive(false);
1231         }
1232         else
1233         {
1234           WAR << "unknown boolean value " << attrValue << endl;
1235         }
1236       }
1237       else if ( attribute==PoolQueryAttr::installStatusAttr )
1238       {
1239         if( attrValue == "all" )
1240         {
1241           setStatusFilterFlags( ALL );
1242         }
1243         else if( attrValue == "installed" )
1244         {
1245           setInstalledOnly();
1246         }
1247         else if( attrValue == "not-installed" )
1248         {
1249           setUninstalledOnly();
1250         }
1251         else
1252         {
1253           WAR << "Unknown value for install status " << attrValue << endl;
1254         }
1255       }
1256       else if ( attribute == PoolQueryAttr::editionAttr)
1257       {
1258         string::size_type pos;
1259         Rel rel("==");
1260         if (attrValue.find_first_of("=<>!") == 0)
1261         {
1262           pos = attrValue.find_last_of("=<>");
1263           rel = Rel(attrValue.substr(0, pos+1));
1264           attrValue = str::trim(attrValue.substr(pos+1, attrValue.npos));
1265         }
1266
1267         setEdition(Edition(attrValue), rel);
1268       }
1269       else if ( attribute == PoolQueryAttr::complexAttr )
1270       {
1271         try
1272         {
1273           _pimpl->_uncompiledPredicated.insert( AttrMatchData::deserialize( attrValue ) );
1274         }
1275         catch ( const Exception & err )
1276         {
1277           WAR << "Unparsable value for complex: " << err.asUserHistory() << endl;
1278
1279         }
1280       }
1281       else if ( attribute==PoolQueryAttr::noAttr )
1282       {
1283         WAR << "empty attribute name" << endl;
1284       }
1285       else
1286       {
1287         string s = attrName;
1288         str::replaceAll( s,"_",":" );
1289         SolvAttr a(s);
1290         if ( a == SolvAttr::name || isDependencyAttribute( a ) )
1291         {
1292           Capability c( attrValue );
1293           CapDetail d( c );
1294           if ( d.isVersioned() )
1295             addDependency( a, d.name().asString(), d.op(), d.ed() );
1296           else
1297             addDependency( a, attrValue );
1298         }
1299         else
1300           addAttribute( a, attrValue );
1301       }
1302
1303     } while ( true );
1304
1305     // OLD STYLE VERSIONED LOCKS:
1306     //  solvable_name: kernel
1307     //  version: > 1
1308     //
1309     // NEW STYLE VERSIONED LOCKS:
1310     //  complex: AttrMatchData solvable:name kernel C SolvableRange\ >\ 1\ \"\"
1311     //   or
1312     //  solvable_name: kernel > 1
1313     //
1314     // Semantically equivalent as locks, but due to the different syntax
1315     // the complex lock is wrongly handled by zypper.
1316     //
1317     // bsc#1112911: Unfortunately all styles are found in real-life locks-files.
1318     // libzypp will try to make sure, when parsing the locks-file, that complex
1319     // locks are rewritten into to OLD STYLE queries zypper can handle.
1320     if ( !_pimpl->_attrs.count(SolvAttr::name) && _pimpl->_uncompiledPredicated.size() == 1 )
1321     {
1322       // No OLD STYLE lock for SolvAttr::name and exactly one complex lock...
1323       const AttrMatchData & attrmatch {  *_pimpl->_uncompiledPredicated.begin() };
1324       if ( attrmatch.attr == SolvAttr::name && attrmatch.strMatcher.flags().mode() == Match::OTHER )
1325       {
1326         // ...for SolvAttr::name and following the global search flags.
1327         // A candidate for a rewrite?
1328
1329         std::vector<std::string> words;
1330         str::splitEscaped( attrmatch.predicateStr, std::back_inserter(words) );
1331         if ( words.size() < 4 || words[3].empty() )
1332         {
1333           // We have _NO_ arch rule in the complex predicate, so we can simplify it.
1334           //
1335           // NOTE: AFAIK it's not possible to create (or have created) a complex lock
1336           // with arch rule with zypper means. Nevertheless, in case such a rule made it
1337           // into a locks file, it's better to have a strange looking 'zypper locks' list
1338           // than to lock the wrong packages.
1339           // (and remember that you can't use "addAttribute( SolvAttr::arch, ... )" because
1340           // attributes are `OR`ed)
1341
1342           // kind
1343           if ( attrmatch.kindPredicate )
1344           {
1345             _pimpl->_kinds.clear();     // an explicit kind overwrites any global one
1346             addKind( attrmatch.kindPredicate );
1347           }
1348
1349           // name
1350           addAttribute( SolvAttr::name, attrmatch.strMatcher.searchstring() );
1351
1352           // edition
1353           std::vector<std::string> words;
1354           str::splitEscaped( attrmatch.predicateStr, std::back_inserter(words) );
1355           if ( ! words.empty() )
1356           {
1357             if ( words[0] == "EditionRange" || words[0] == "SolvableRange" )
1358             {
1359               setEdition( Edition(words[2]), Rel(words[1]) );
1360             }
1361           }
1362
1363           // finally remove the complex lock
1364           _pimpl->_uncompiledPredicated.clear();
1365         }
1366       }
1367     }
1368
1369     return finded_something;
1370   }
1371
1372   void PoolQuery::serialize( ostream &str, char delim ) const
1373   {
1374     //separating delim
1375     str << delim;
1376     //iterate thrue all settings and write it
1377     static const zypp::PoolQuery q; //not save default options, so create default query example
1378
1379     for_( it, repos().begin(), repos().end() )
1380     {
1381       str << "repo: " << *it << delim ;
1382     }
1383
1384     for_( it, kinds().begin(), kinds().end() )
1385     {
1386       str << PoolQueryAttr::kindAttr.asString() << ": "
1387           << it->idStr() << delim ;
1388     }
1389
1390     if (editionRel() != Rel::ANY && edition() != Edition::noedition)
1391       str << PoolQueryAttr::editionAttr.asString() << ": " << editionRel() << " " << edition() << delim;
1392
1393     if (matchMode()!=q.matchMode())
1394     {
1395       switch( matchMode() )
1396       {
1397       case Match::STRING:
1398         str << PoolQueryAttr::stringTypeAttr.asString() << ": exact" << delim;
1399         break;
1400       case Match::SUBSTRING:
1401         str << PoolQueryAttr::stringTypeAttr.asString()
1402             << ": substring" << delim;
1403         break;
1404       case Match::GLOB:
1405         str << PoolQueryAttr::stringTypeAttr.asString() << ": glob" << delim;
1406         break;
1407       case Match::REGEX:
1408         str << PoolQueryAttr::stringTypeAttr.asString() << ": regex" << delim;
1409         break;
1410       default:
1411         WAR << "unknown match type "  << matchMode() << endl;
1412       }
1413     }
1414
1415     if( caseSensitive() != q.caseSensitive() )
1416     {
1417       str << "case_sensitive: ";
1418       if (caseSensitive())
1419       {
1420         str << "on" << delim;
1421       }
1422       else
1423       {
1424         str << "off" << delim;
1425       }
1426     }
1427
1428     if( statusFilterFlags() != q.statusFilterFlags() )
1429     {
1430       switch( statusFilterFlags() )
1431       {
1432       case ALL:
1433         str << "install_status: all" << delim;
1434         break;
1435       case INSTALLED_ONLY:
1436         str << "install_status: installed" << delim;
1437         break;
1438       case UNINSTALLED_ONLY:
1439         str << "install_status: not-installed" << delim;
1440         break;
1441       }
1442     }
1443
1444     for_( it, strings().begin(), strings().end() )
1445     {
1446       str << PoolQueryAttr::stringAttr.asString()<< ": " << *it << delim;
1447     }
1448
1449     for_( it, attributes().begin(), attributes().end() )
1450     {
1451       string s = it->first.asString();
1452       str::replaceAll(s,":","_");
1453       for_( it2,it->second.begin(),it->second.end() )
1454       {
1455         str << s <<": "<< *it2 << delim;
1456       }
1457     }
1458
1459     for_( it, _pimpl->_uncompiledPredicated.begin(), _pimpl->_uncompiledPredicated.end() )
1460     {
1461       str << "complex: "<< it->serialize() << delim;
1462     }
1463
1464     //separating delim - protection
1465     str << delim;
1466   }
1467
1468   string PoolQuery::asString() const
1469   { return _pimpl->asString(); }
1470
1471   ostream & operator<<( ostream & str, const PoolQuery & obj )
1472   { return str << obj.asString(); }
1473
1474   std::ostream & dumpOn( std::ostream & str, const PoolQuery & obj )
1475   { return dumpRange( str << obj, obj.begin(), obj.end() ); }
1476
1477   bool PoolQuery::operator==( const PoolQuery & rhs ) const
1478   { return *_pimpl == *rhs._pimpl; }
1479
1480   bool PoolQuery::operator<( const PoolQuery & rhs ) const
1481   { return *_pimpl < *rhs._pimpl; }
1482
1483   ///////////////////////////////////////////////////////////////////
1484   namespace detail
1485   { /////////////////////////////////////////////////////////////////
1486
1487     ///////////////////////////////////////////////////////////////////
1488     //
1489     //  CLASS NAME : PoolQueryMatcher
1490     //
1491     /** Store \ref PoolQuery settings and assist \ref PoolQueryIterator.
1492      *
1493      * Basically the matcher performs a base query, which should preselect
1494      * candidates for a match. And has some filter conditions on top of it.
1495      * Query and fileter depend on the \ref PoolQuery settings.
1496      *
1497      * Matcher must be stateless, as it is shared between multiple
1498      * \ref PoolQueryIterator instances.
1499      *
1500      * If \ref base_iterator is at the \ref end, \ref advance moves it
1501      * to the first match. Otherwise advance moves to the next match, or
1502      * to the \ref end, if there is no more match.
1503      *
1504      * \note The original implementation treated an empty search string as
1505      * <it>"match always"</it>. We stay compatible.
1506      */
1507     class PoolQueryMatcher
1508     {
1509       public:
1510         typedef sat::LookupAttr::iterator base_iterator;
1511
1512       public:
1513         const base_iterator & end() const
1514         {
1515           static base_iterator _end;
1516           return _end;
1517         }
1518
1519         bool advance( base_iterator & base_r ) const
1520         {
1521           if ( base_r == end() )
1522             base_r = startNewQyery(); // first candidate
1523           else
1524           {
1525             base_r.nextSkipSolvable(); // assert we don't visit this Solvable again
1526             ++base_r; // advance to next candidate
1527           }
1528
1529           while ( base_r != end() )
1530           {
1531             if ( isAMatch( base_r ) )
1532               return true;
1533             // No match: try next
1534             ++base_r;
1535           }
1536           return false;
1537         }
1538
1539         /** Provide all matching attributes within this solvable.
1540          *
1541          */
1542         void matchDetail( const base_iterator & base_r, std::vector<base_iterator> & return_r ) const
1543         {
1544           if ( base_r == end() )
1545             return;
1546
1547           sat::Solvable inSolvable( base_r.inSolvable() );
1548
1549           if ( _attrMatchList.size() == 1 )
1550           {
1551             // base_r is already on the 1st matching attribute!
1552             // String matching is done by the base iterator. We must check the predicate here.
1553             // Let's see if there are more matches for this solvable:
1554             base_iterator base( base_r );
1555             base.stayInThisSolvable(); // avoid discarding matches we found far away from here.
1556             return_r.push_back( base );
1557
1558             const AttrMatchData::Predicate & predicate( _attrMatchList.front().predicate );
1559             for ( ++base; base.inSolvable() == inSolvable; ++base ) // safe even if base == end()
1560             {
1561               if ( ! predicate || predicate( base ) )
1562                 return_r.push_back( base );
1563             }
1564           }
1565           else
1566           {
1567             // Here: search all attributes ;(
1568             for_( mi, _attrMatchList.begin(), _attrMatchList.end() )
1569             {
1570               const AttrMatchData & matchData( *mi );
1571               sat::LookupAttr q( matchData.attr, inSolvable );
1572               if ( matchData.strMatcher ) // an empty searchstring matches always
1573                 q.setStrMatcher( matchData.strMatcher );
1574
1575               if ( ! q.empty() ) // there are matches.
1576               {
1577                 // now check any predicate:
1578                 const AttrMatchData::Predicate & predicate( matchData.predicate );
1579                 for_( it, q.begin(), q.end() )
1580                 {
1581                   if ( ! predicate || predicate( it ) )
1582                     return_r.push_back( it );
1583                 }
1584               }
1585             }
1586           }
1587         }
1588
1589       public:
1590         /** Ctor stores the \ref PoolQuery settings.
1591          * \throw MatchException Any of the exceptions thrown by \ref PoolQuery::Impl::compile.
1592          */
1593         PoolQueryMatcher( const shared_ptr<const PoolQuery::Impl> & query_r )
1594         {
1595           query_r->compile();
1596
1597           // Repo restriction:
1598           sat::Pool satpool( sat::Pool::instance() );
1599
1600           for_( it, query_r->_repos.begin(), query_r->_repos.end() )
1601           {
1602             Repository r( satpool.reposFind( *it ) );
1603             if ( r )
1604               _repos.insert( r );
1605             else
1606               _neverMatchRepo = true;
1607           }
1608           // _neverMatchRepo: we just need to catch the case that no repo
1609           // matched, so we'd interpret the empty list as 'take from all'
1610           if ( _neverMatchRepo && ! _repos.empty() )
1611             _neverMatchRepo = false;
1612
1613           // Kind restriction:
1614           _kinds = query_r->_kinds;
1615           // Edition restriction:
1616           _op      = query_r->_op;
1617           _edition = query_r->_edition;
1618           // Status restriction:
1619           _status_flags = query_r->_status_flags;
1620           // StrMatcher
1621           _attrMatchList = query_r->_attrMatchList;
1622         }
1623
1624         ~PoolQueryMatcher()
1625         {}
1626
1627       private:
1628         /** Initialize a new base query. */
1629         base_iterator startNewQyery() const
1630         {
1631           sat::LookupAttr q;
1632
1633           if ( _neverMatchRepo )
1634             return q.end();
1635
1636           // Repo restriction:
1637           if ( _repos.size() == 1 )
1638             q.setRepo( *_repos.begin() );
1639           // else: handled in isAMatch.
1640
1641           // Attribute restriction:
1642           if ( _attrMatchList.size() == 1 ) // all (SolvAttr::allAttr) or 1 attr
1643           {
1644             const AttrMatchData & matchData( _attrMatchList.front() );
1645             q.setAttr( matchData.attr );
1646             if ( matchData.strMatcher ) // empty searchstring matches always
1647               q.setStrMatcher( matchData.strMatcher );
1648           }
1649           else // more than 1 attr (but not all)
1650           {
1651             // no restriction, it's all handled in isAMatch.
1652             q.setAttr( sat::SolvAttr::allAttr );
1653           }
1654
1655           return q.begin();
1656         }
1657
1658
1659         /** Check whether we are on a match.
1660          *
1661          * The check covers the whole Solvable, not just the current
1662          * attribute \c base_r points to. If there's no match, also
1663          * prepare \c base_r to advance appropriately. If there is
1664          * a match, simply return \c true. \ref advance always moves
1665          * to the next Solvable if there was a match.
1666          *
1667          * \note: Caller asserts we're not at \ref end.
1668         */
1669         bool isAMatch( base_iterator & base_r ) const
1670         {
1671           /////////////////////////////////////////////////////////////////////
1672           Repository inRepo( base_r.inRepo() );
1673           // Status restriction:
1674           if ( _status_flags
1675              && ( (_status_flags == PoolQuery::INSTALLED_ONLY) != inRepo.isSystemRepo() ) )
1676           {
1677             base_r.nextSkipRepo();
1678             return false;
1679           }
1680           // Repo restriction:
1681           if ( _repos.size() > 1 && _repos.find( inRepo ) == _repos.end() )
1682           {
1683             base_r.nextSkipRepo();
1684             return false;
1685           }
1686           /////////////////////////////////////////////////////////////////////
1687           sat::Solvable inSolvable( base_r.inSolvable() );
1688           // Edition restriction:
1689           if ( _op != Rel::ANY && !compareByRel( _op, inSolvable.edition(), _edition, Edition::Match() ) )
1690           {
1691             base_r.nextSkipSolvable();
1692             return false;
1693           }
1694
1695           // Kind restriction:
1696           // Delay the decision to nextSkipSolvable and return false, as there may be
1697           // some explicit kind:name predicate which overrules the global kinds.
1698           bool globalKindOk =( _kinds.empty() || inSolvable.isKind( _kinds.begin(), _kinds.end() ) );
1699
1700           /////////////////////////////////////////////////////////////////////
1701           // string and predicate matching:
1702
1703           if ( _attrMatchList.size() == 1 )
1704           {
1705             // String matching was done by the base iterator.
1706             // Now check any predicate:
1707             const AttrMatchData & matchData( _attrMatchList.front() );
1708
1709             if ( matchData.kindPredicate )
1710             {
1711               if ( matchData.kindPredicate != inSolvable.kind() )
1712               {
1713                 base_r.nextSkipSolvable();      // this matchData will never match in this solvable
1714                 return false;
1715               }
1716             }
1717             else if ( !globalKindOk )
1718               return false;                     // only matching kindPredicate could overwrite this
1719
1720             if ( !matchData.predicate || matchData.predicate( base_r ) )
1721               return true;
1722
1723             return false; // no skip as there may be more occurrences in this solvable of this attr.
1724           }
1725
1726           // Here: search all attributes ;(
1727           for_( mi, _attrMatchList.begin(), _attrMatchList.end() )
1728           {
1729             const AttrMatchData & matchData( *mi );
1730
1731             if ( matchData.kindPredicate )
1732             {
1733               if ( matchData.kindPredicate != inSolvable.kind() )
1734                 continue;                       // this matchData does not apply
1735             }
1736             else if ( !globalKindOk )
1737               continue;                         // only matching kindPredicate could overwrite this
1738
1739             sat::LookupAttr q( matchData.attr, inSolvable );
1740             if ( matchData.strMatcher ) // an empty searchstring matches always
1741               q.setStrMatcher( matchData.strMatcher );
1742
1743             if ( ! q.empty() ) // there are matches.
1744             {
1745               // now check any predicate:
1746               const AttrMatchData::Predicate & predicate( matchData.predicate );
1747               if ( predicate )
1748               {
1749                 for_( it, q.begin(), q.end() )
1750                 {
1751                   if ( predicate( it ) )
1752                     return true;
1753                 }
1754               }
1755               else
1756                 return true;
1757             }
1758           }
1759           base_r.nextSkipSolvable();
1760           return false;
1761         }
1762
1763       private:
1764         /** Repositories include in the search. */
1765         std::set<Repository> _repos;
1766         DefaultIntegral<bool,false> _neverMatchRepo;
1767         /** Resolvable kinds to include. */
1768         std::set<ResKind> _kinds;
1769         /** Edition filter. */
1770         Rel _op;
1771         Edition _edition;
1772         /** Installed status filter flags. \see PoolQuery::StatusFilter */
1773         int _status_flags;
1774         /** StrMatcher per attribtue. */
1775         AttrMatchList _attrMatchList;
1776     };
1777     ///////////////////////////////////////////////////////////////////
1778
1779     void PoolQueryIterator::increment()
1780     {
1781       // matcher restarts if at end! It is called from the ctor
1782       // to get the 1st match. But if the end is reached, it should
1783       // be deleted, otherwise we'd start over again.
1784       if ( !_matcher )
1785         return; // at end
1786       if ( _matches )
1787         _matches.reset(); // invalidate old matches
1788       if ( ! _matcher->advance( base_reference() ) )
1789         _matcher.reset();
1790     }
1791
1792     const PoolQueryIterator::Matches & PoolQueryIterator::matches() const
1793     {
1794       if ( _matches )
1795         return *_matches;
1796
1797       if ( !_matcher )
1798       {
1799         // at end of query:
1800         static const Matches _none;
1801         return _none;
1802       }
1803
1804       _matches.reset( new Matches );
1805       _matcher->matchDetail( base_reference(), *_matches );
1806       return *_matches;
1807     }
1808
1809     std::ostream & dumpOn( std::ostream & str, const PoolQueryIterator & obj )
1810     {
1811       str << *obj;
1812       if ( ! obj.matchesEmpty() )
1813       {
1814         for_( it, obj.matchesBegin(), obj.matchesEnd() )
1815         {
1816           str << endl << "    " << it->inSolvAttr() << "\t" << it->asString();
1817         }
1818       }
1819       return str;
1820     }
1821
1822     ///////////////////////////////////////////////////////////////////
1823   } //namespace detail
1824   ///////////////////////////////////////////////////////////////////
1825
1826   detail::PoolQueryIterator PoolQuery::begin() const
1827   {
1828     return shared_ptr<detail::PoolQueryMatcher>( new detail::PoolQueryMatcher( _pimpl.getPtr() ) );
1829   }
1830
1831   /////////////////////////////////////////////////////////////////
1832 } // namespace zypp
1833 ///////////////////////////////////////////////////////////////////
1834