libzypp  11.13.5
PoolQuery.cc
Go to the documentation of this file.
1 /*---------------------------------------------------------------------\
2 | ____ _ __ __ ___ |
3 | |__ / \ / / . \ . \ |
4 | / / \ V /| _/ _/ |
5 | / /__ | | | | | | |
6 | /_____||_| |_| |_| |
7 | |
8 \---------------------------------------------------------------------*/
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"
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 
35 namespace zypp
36 {
37 
39  namespace
40  {
41 
43  // some Helpers and Predicates
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 
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 
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 
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 
164  //
166 
184  struct AttrMatchData
185  {
186  typedef function<bool(sat::LookupAttr::iterator)> Predicate;
187 
188  static bool always( sat::LookupAttr::iterator ) { return true; }
189  static bool never( sat::LookupAttr::iterator ) { return false; }
190 
191  AttrMatchData()
192  {}
193 
194  AttrMatchData( sat::SolvAttr attr_r, const StrMatcher & strMatcher_r )
195  : attr( attr_r )
196  , strMatcher( strMatcher_r )
197  {}
198 
199  AttrMatchData( sat::SolvAttr attr_r, const StrMatcher & strMatcher_r,
200  const Predicate & predicate_r, const std::string & predicateStr_r )
201  : attr( attr_r )
202  , strMatcher( strMatcher_r )
203  , predicate( predicate_r )
204  , predicateStr( predicateStr_r )
205  {}
206 
212  template<class _Predicate>
213  void addPredicate( const _Predicate & predicate_r )
214  {
215  predicate = predicate_r;
216  predicateStr = predicate_r.serialize();
217  }
218 
224  std::string serialize() const
225  {
226  std::string ret( "AttrMatchData" );
227  str::appendEscaped( ret, attr.asString() );
228  str::appendEscaped( ret, strMatcher.searchstring() );
229  // TODO: Actually the flag should be serialized too, but for PoolQuery
230  // it's by now sufficient to differ between mode OTHER and others,
231  // i.e. whether to compile or not compile.
232  str::appendEscaped( ret, strMatcher.flags().mode() == Match::OTHER ? "C" : "X" );
234  return ret;
235  }
236 
240  static AttrMatchData deserialize( const std::string & str_r )
241  {
242  std::vector<std::string> words;
243  str::splitEscaped( str_r, std::back_inserter(words) );
244  if ( words.empty() || words[0] != "AttrMatchData" )
245  ZYPP_THROW( Exception( str::Str() << "Expecting AttrMatchData: " << str_r ) );
246  if ( words.size() != 5 )
247  ZYPP_THROW( Exception( str::Str() << "Wrong number of words: " << str_r ) );
248 
249  AttrMatchData ret;
250  ret.attr = sat::SolvAttr( words[1] );
251  ret.strMatcher = StrMatcher( words[2] );
252  if ( words[3] == "C" )
253  ret.strMatcher.setFlags( Match::OTHER );
254  ret.predicateStr = words[4];
255 
256  // now the predicate
257  words.clear();
258  str::splitEscaped( ret.predicateStr, std::back_inserter(words) );
259  if ( ! words.empty() )
260  {
261  if ( words[0] == "EditionRange" )
262  {
263  switch( words.size() )
264  {
265  case 3:
266  ret.predicate = EditionRangePredicate( Rel(words[1]), Edition(words[2]) );
267  break;
268  case 4:
269  ret.predicate = EditionRangePredicate( Rel(words[1]), Edition(words[2]), Arch(words[3]) );
270  break;
271  default:
272  ZYPP_THROW( Exception( str::Str() << "Wrong number of words: " << str_r ) );
273  break;
274  }
275  }
276  else if ( words[0] == "SolvableRange" )
277  {
278  switch( words.size() )
279  {
280  case 3:
281  ret.predicate = SolvableRangePredicate( Rel(words[1]), Edition(words[2]) );
282  break;
283  case 4:
284  ret.predicate = SolvableRangePredicate( Rel(words[1]), Edition(words[2]), Arch(words[3]) );
285  break;
286  default:
287  ZYPP_THROW( Exception( str::Str() << "Wrong number of words: " << str_r ) );
288  break;
289  }
290  }
291  else if ( words[0] == "CapabilityMatch" )
292  {
293  if ( words.size() != 2 )
294  ZYPP_THROW( Exception( str::Str() << "Wrong number of words: " << str_r ) );
295  ret.predicate = CapabilityMatchPredicate( Capability(words[1]) );
296  }
297  else
298  ZYPP_THROW( Exception( str::Str() << "Unknown predicate: " << str_r ) );
299  }
300  return ret;
301  }
302 
303  sat::SolvAttr attr;
304  StrMatcher strMatcher;
305  Predicate predicate;
306  std::string predicateStr;
307  };
308 
310  inline std::ostream & operator<<( std::ostream & str, const AttrMatchData & obj )
311  {
312  str << obj.attr << ": " << obj.strMatcher;
313  if ( obj.predicate )
314  str << " +(" << obj.predicateStr << ")";
315  return str;
316  }
317 
319  inline bool operator==( const AttrMatchData & lhs, const AttrMatchData & rhs )
320  {
321  return ( lhs.attr == rhs.attr
322  && lhs.strMatcher == rhs.strMatcher
323  && lhs.predicateStr == rhs.predicateStr );
324  }
325 
327  inline bool operator!=( const AttrMatchData & lhs, const AttrMatchData & rhs )
328  { return !( lhs == rhs ); }
329 
331  inline bool operator<( const AttrMatchData & lhs, const AttrMatchData & rhs )
332  {
333  if ( lhs.attr != rhs.attr )
334  return ( lhs.attr < rhs.attr );
335  if ( lhs.strMatcher != rhs.strMatcher )
336  return ( lhs.strMatcher < rhs.strMatcher );
337  if ( lhs.predicateStr != rhs.predicateStr )
338  return ( lhs.predicateStr < rhs.predicateStr );
339  return false;
340  }
341 
342  typedef std::list<AttrMatchData> AttrMatchList;
343 
344 
345  }
346  // namespace
348 
350  //
351  // CLASS NAME : PoolQuery::Impl
352  //
355  {
356  public:
358  : _flags( Match::SUBSTRING | Match::NOCASE | Match::SKIP_KIND )
359  , _match_word(false)
360  , _require_all(false)
361  , _status_flags(ALL)
362  {}
363 
364  ~Impl()
365  {}
366 
367  public:
369  string asString() const;
370 
378  std::set<AttrMatchData> _uncompiledPredicated;
379 
384 
387 
392 
395 
399 
400  public:
401 
402  bool operator==( const PoolQuery::Impl & rhs ) const
403  {
404  return ( _strings == rhs._strings
405  && _attrs == rhs._attrs
406  && _uncompiledPredicated == rhs._uncompiledPredicated
407  && _flags == rhs._flags
408  && _match_word == rhs._match_word
409  && _require_all == rhs._require_all
410  && _status_flags == rhs._status_flags
411  && _edition == rhs._edition
412  && _op == rhs._op
413  && _repos == rhs._repos
414  && _kinds == rhs._kinds );
415  }
416 
417  bool operator!=( const PoolQuery::Impl & rhs ) const
418  { return ! operator==( rhs ); }
419 
420  public:
425  void compile() const;
426 
428  mutable AttrMatchList _attrMatchList;
429 
430  private:
432  string createRegex( const StrContainer & container, const Match & flags ) const;
433 
434  private:
435  friend Impl * rwcowClone<Impl>( const Impl * rhs );
437  Impl * clone() const
438  { return new Impl( *this ); }
439  };
440 
442 
443  struct MyInserter
444  {
445  MyInserter(PoolQuery::StrContainer & cont) : _cont(cont) {}
446 
447  bool operator()(const string & str)
448  {
449  _cont.insert(str);
450  return true;
451  }
452 
454  };
455 
456 
457  struct EmptyFilter
458  {
459  bool operator()(const string & str)
460  {
461  return !str.empty();
462  }
463  };
464 
465  void PoolQuery::Impl::compile() const
466  {
467  _attrMatchList.clear();
468 
469  Match cflags( _flags );
470  if ( cflags.mode() == Match::OTHER ) // this will never succeed...
472 
474  string rcstrings;
475 
476 
477  // 'different' - will have to iterate through all and match by ourselves (slow)
478  // 'same' - will pass the compiled string to dataiterator_init
479  // 'one-attr' - will pass it to dataiterator_init
480  // 'one-non-regex-str' - will pass to dataiterator_init, set flag to SEARCH_STRING or SEARCH_SUBSTRING
481 
482  // // NO ATTRIBUTE
483  // else
484  // for all _strings
485  // create regex; store in rcstrings; if more strings flag regex;
486  if (_attrs.empty())
487  {
488  ; // A default 'query-all' will be added after all sources are processed.
489  }
490 
491  // // ONE ATTRIBUTE
492  // else if _attrs is not empty but it contains just one attr
493  // for all _strings and _attr[key] strings
494  // create regex; flag 'one-attr'; if more strings flag regex;
495  else if (_attrs.size() == 1)
496  {
497  StrContainer joined;
498  invokeOnEach(_strings.begin(), _strings.end(), EmptyFilter(), MyInserter(joined));
499  invokeOnEach(_attrs.begin()->second.begin(), _attrs.begin()->second.end(), EmptyFilter(), MyInserter(joined));
500  rcstrings = createRegex(joined, cflags);
501  if (joined.size() > 1) // switch to regex for multiple strings
502  cflags.setModeRegex();
503  _attrMatchList.push_back( AttrMatchData( _attrs.begin()->first,
504  StrMatcher( rcstrings, cflags ) ) );
505  }
506 
507  // // MULTIPLE ATTRIBUTES
508  else
509  {
510  // check whether there are any per-attribute strings
511  bool attrvals_empty = true;
512  for (AttrRawStrMap::const_iterator ai = _attrs.begin(); ai != _attrs.end(); ++ai)
513  if (!ai->second.empty())
514  for(StrContainer::const_iterator it = ai->second.begin();
515  it != ai->second.end(); it++)
516  if (!it->empty())
517  {
518  attrvals_empty = false;
519  goto attremptycheckend;
520  }
521 attremptycheckend:
522 
523  // chceck whether the per-attribute strings are all the same
524  bool attrvals_thesame = true;
525  AttrRawStrMap::const_iterator ai = _attrs.begin();
526  const StrContainer & set1 = ai->second;
527  ++ai;
528  for (; ai != _attrs.end(); ++ai)
529  {
530  StrContainer result;
531  set_difference(
532  set1.begin(), set1.end(),
533  ai->second.begin(), ai->second.end(),
534  inserter(result, result.begin())/*, ltstr()*/);
535  if (!result.empty())
536  {
537  attrvals_thesame = false;
538  break;
539  }
540  }
541 
542  // // THE SAME STRINGS FOR DIFFERENT ATTRS
543  // else if _attrs is not empty but it does not contain strings
544  // for each key in _attrs take all _strings
545  // create regex; store in rcstrings; flag 'same'; if more strings flag regex;
546  if (attrvals_empty || attrvals_thesame)
547  {
548  StrContainer joined;
549  if (attrvals_empty)
550  {
551  invokeOnEach(_strings.begin(), _strings.end(), EmptyFilter(), MyInserter(joined));
552  rcstrings = createRegex(joined, cflags);
553  }
554  else
555  {
556  invokeOnEach(_strings.begin(), _strings.end(), EmptyFilter(), MyInserter(joined));
557  invokeOnEach(_attrs.begin()->second.begin(), _attrs.begin()->second.end(), EmptyFilter(), MyInserter(joined));
558  rcstrings = createRegex(joined, cflags);
559  }
560  if (joined.size() > 1) // switch to regex for multiple strings
561  cflags.setModeRegex();
562  // May use the same StrMatcher for all
563  StrMatcher matcher( rcstrings, cflags );
564  for_( ai, _attrs.begin(), _attrs.end() )
565  {
566  _attrMatchList.push_back( AttrMatchData( ai->first, matcher ) );
567  }
568  }
569 
570  // // DIFFERENT STRINGS FOR DIFFERENT ATTRS
571  // if _attrs is not empty and it contains non-empty vectors with non-empty strings
572  // for each key in _attrs take all _strings + all _attrs[key] strings
573  // create regex; flag 'different'; if more strings flag regex;
574  else
575  {
576  for_(ai, _attrs.begin(), _attrs.end())
577  {
578  StrContainer joined;
579  invokeOnEach(_strings.begin(), _strings.end(), EmptyFilter(), MyInserter(joined));
580  invokeOnEach(ai->second.begin(), ai->second.end(), EmptyFilter(), MyInserter(joined));
581  string s = createRegex(joined, cflags);
582  if (joined.size() > 1) // switch to regex for multiple strings
583  cflags.setModeRegex();
584  _attrMatchList.push_back( AttrMatchData( ai->first,
585  StrMatcher( s, cflags ) ) );
586  }
587  }
588  }
589 
590  // Now handle any predicated queries
591  if ( ! _uncompiledPredicated.empty() )
592  {
593  StrContainer global;
594  invokeOnEach( _strings.begin(), _strings.end(), EmptyFilter(), MyInserter(global) );
595  for_( it, _uncompiledPredicated.begin(), _uncompiledPredicated.end() )
596  {
597  if ( it->strMatcher.flags().mode() == Match::OTHER )
598  {
599  // need to compile:
600  StrContainer joined( global );
601  const std::string & mstr( it->strMatcher.searchstring() );
602  if ( ! mstr.empty() )
603  joined.insert( mstr );
604 
605  cflags = _flags;
606  rcstrings = createRegex( joined, cflags );
607  if ( joined.size() > 1 ) // switch to regex for multiple strings
608  cflags.setModeRegex();
609 
610  _attrMatchList.push_back( AttrMatchData( it->attr,
611  StrMatcher( rcstrings, cflags ),
612  it->predicate, it->predicateStr ) );
613  }
614  else
615  {
616  // copy matcher
617  _attrMatchList.push_back( *it );
618  }
619  }
620  }
621 
622  // If no attributes defined at all, then add 'query all'
623  if ( _attrMatchList.empty() )
624  {
625  cflags = _flags;
626  rcstrings = createRegex( _strings, cflags );
627  if ( _strings.size() > 1 ) // switch to regex for multiple strings
628  cflags.setModeRegex();
629  _attrMatchList.push_back( AttrMatchData( sat::SolvAttr::allAttr,
630  StrMatcher( rcstrings, cflags ) ) );
631  }
632 
633  // Finally check here, whether all involved regex compile.
634  for_( it, _attrMatchList.begin(), _attrMatchList.end() )
635  {
636  it->strMatcher.compile(); // throws on error
637  }
638  //DBG << asString() << endl;
639  }
640 
641 
645  static string wildcards2regex(const string & str)
646  {
647  string regexed = str;
648 
649  string r_all(".*"); // regex equivalent of '*'
650  string r_one("."); // regex equivalent of '?'
651  string::size_type pos;
652 
653  // replace all "*" in input with ".*"
654  for (pos = 0; (pos = regexed.find("*", pos)) != std::string::npos; pos+=2)
655  regexed = regexed.replace(pos, 1, r_all);
656 
657  // replace all "?" in input with "."
658  for (pos = 0; (pos = regexed.find('?', pos)) != std::string::npos; ++pos)
659  regexed = regexed.replace(pos, 1, r_one);
660 
661  return regexed;
662  }
663 
664  string PoolQuery::Impl::createRegex( const StrContainer & container, const Match & flags ) const
665  {
667 #define WB (_match_word ? string("\\b") : string())
668  string rstr;
669 
670  if (container.empty())
671  return rstr;
672 
673  if (container.size() == 1)
674  {
675  return WB + *container.begin() + WB;
676  }
677 
678  // multiple strings
679 
680  bool use_wildcards = flags.isModeGlob();
681  StrContainer::const_iterator it = container.begin();
682  string tmp;
683 
684  if (use_wildcards)
685  tmp = wildcards2regex(*it);
686  else
687  tmp = *it;
688 
689  if (_require_all)
690  {
691  if ( ! flags.isModeString() ) // not match exact
692  tmp += ".*" + WB + tmp;
693  rstr = "(?=" + tmp + ")";
694  }
695  else
696  {
697  if ( flags.isModeString() || flags.isModeGlob() )
698  rstr = "^";
699  rstr += WB + "(" + tmp;
700  }
701 
702  ++it;
703 
704  for (; it != container.end(); ++it)
705  {
706  if (use_wildcards)
707  tmp = wildcards2regex(*it);
708  else
709  tmp = *it;
710 
711  if (_require_all)
712  {
713  if ( ! flags.isModeString() ) // not match exact
714  tmp += ".*" + WB + tmp;
715  rstr += "(?=" + tmp + ")";
716  }
717  else
718  {
719  rstr += "|" + tmp;
720  }
721  }
722 
723  if (_require_all)
724  {
725  if ( ! flags.isModeString() ) // not match exact
726  rstr += WB + ".*";
727  }
728  else
729  {
730  rstr += ")" + WB;
731  if ( flags.isModeString() || flags.isModeGlob() )
732  rstr += "$";
733  }
734 
735  return rstr;
736 #undef WB
737  }
738 
740  {
741  ostringstream o;
742 
743  o << "kinds: ";
744  if ( _kinds.empty() )
745  o << "ALL";
746  else
747  {
748  for(Kinds::const_iterator it = _kinds.begin();
749  it != _kinds.end(); ++it)
750  o << *it << " ";
751  }
752  o << endl;
753 
754  o << "repos: ";
755  if ( _repos.empty() )
756  o << "ALL";
757  else
758  {
759  for(StrContainer::const_iterator it = _repos.begin();
760  it != _repos.end(); ++it)
761  o << *it << " ";
762  }
763  o << endl;
764 
765  o << "version: "<< _op << " " << _edition.asString() << endl;
766  o << "status: " << ( _status_flags ? ( _status_flags == INSTALLED_ONLY ? "INSTALLED_ONLY" : "UNINSTALLED_ONLY" )
767  : "ALL" ) << endl;
768 
769  o << "string match flags: " << Match(_flags) << endl;
770 
771  // raw
772  o << "strings: ";
773  for(StrContainer::const_iterator it = _strings.begin();
774  it != _strings.end(); ++it)
775  o << *it << " ";
776  o << endl;
777 
778  o << "attributes: " << endl;
779  for(AttrRawStrMap::const_iterator ai = _attrs.begin(); ai != _attrs.end(); ++ai)
780  {
781  o << "* " << ai->first << ": ";
782  for(StrContainer::const_iterator vi = ai->second.begin();
783  vi != ai->second.end(); ++vi)
784  o << *vi << " ";
785  o << endl;
786  }
787 
788  o << "predicated: " << endl;
789  for_( it, _uncompiledPredicated.begin(), _uncompiledPredicated.end() )
790  {
791  o << "* " << *it << endl;
792  }
793 
794  // compiled
795  o << "last attribute matcher compiled: " << endl;
796  if ( _attrMatchList.empty() )
797  {
798  o << "not yet compiled" << endl;
799  }
800  else
801  {
802  for_( it, _attrMatchList.begin(), _attrMatchList.end() )
803  {
804  o << "* " << *it << endl;
805  }
806  }
807  return o.str();
808  }
809 
811 
813  //
814  // CLASS NAME : PoolQuery
815  //
817 
818  PoolQuery::PoolQuery()
819  : _pimpl(new Impl())
820  {}
821 
823  {}
824 
825  void PoolQuery::addRepo(const std::string &repoalias)
826  {
827  if (repoalias.empty())
828  {
829  WAR << "ignoring an empty repository alias" << endl;
830  return;
831  }
832  _pimpl->_repos.insert(repoalias);
833  }
834 
835  void PoolQuery::addKind(const ResKind & kind)
836  { _pimpl->_kinds.insert(kind); }
837 
838  void PoolQuery::addString(const string & value)
839  { _pimpl->_strings.insert(value); }
840 
841  void PoolQuery::addAttribute(const sat::SolvAttr & attr, const std::string & value)
842  { _pimpl->_attrs[attr].insert(value); }
843 
844  void PoolQuery::addDependency( const sat::SolvAttr & attr, const std::string & name, const Rel & op, const Edition & edition )
845  { return addDependency( attr, name, op, edition, Arch_empty ); }
846 
847  void PoolQuery::addDependency( const sat::SolvAttr & attr, const std::string & name, const Rel & op, const Edition & edition, const Arch & arch )
848  {
849  switch ( op.inSwitch() )
850  {
851  case Rel::ANY_e: // no additional constraint on edition.
852  if ( arch.empty() ) // no additional constraint on arch.
853  {
854  addAttribute( attr, name );
855  return;
856  }
857  break;
858 
859  case Rel::NONE_e: // will never match.
860  return;
861 
862  default: // go and add the predicated query (uncompiled)
863  break;
864  }
865 
866  // Match::OTHER indicates need to compile
867  // (merge global search strings into name).
868  AttrMatchData attrMatchData( attr, StrMatcher( name, Match::OTHER ) );
869 
870  if ( isDependencyAttribute( attr ) )
871  attrMatchData.addPredicate( EditionRangePredicate( op, edition, arch ) );
872  else
873  attrMatchData.addPredicate( SolvableRangePredicate( op, edition, arch ) );
874 
875  _pimpl->_uncompiledPredicated.insert( attrMatchData );
876  }
877 
879  {
880  CapDetail cap( cap_r );
881  if ( ! cap.isSimple() ) // will never match.
882  return;
883 
884  // Matches STRING per default. (won't get compiled!)
885  AttrMatchData attrMatchData( attr, StrMatcher( cap.name().asString() ) );
886 
887  if ( isDependencyAttribute( attr ) )
888  attrMatchData.addPredicate( CapabilityMatchPredicate( cap_r ) );
889  else
890  attrMatchData.addPredicate( SolvableRangePredicate( cap.op(), cap.ed() ) );
891 
892  _pimpl->_uncompiledPredicated.insert( attrMatchData );
893  }
894 
895  void PoolQuery::setEdition(const Edition & edition, const Rel & op)
896  {
898  _pimpl->_op = op;
899  }
900 
906  {
907  _pimpl->_match_word = true;
909  }
910 
912  { return _pimpl->_flags; }
913  void PoolQuery::setFlags( const Match & flags )
914  { _pimpl->_flags = flags; }
915 
916 
922  { _pimpl->_status_flags = flags; }
923 
924 
925  void PoolQuery::setRequireAll(bool require_all)
926  { _pimpl->_require_all = require_all; }
927 
928 
931  { return _pimpl->_strings; }
932 
935  { return _pimpl->_attrs; }
936 
939  {
940  static const PoolQuery::StrContainer nocontainer;
941  AttrRawStrMap::const_iterator it = _pimpl->_attrs.find(attr);
942  return it != _pimpl->_attrs.end() ? it->second : nocontainer;
943  }
944 
946  { return _pimpl->_edition; }
948  { return _pimpl->_op; }
949 
950 
951  const PoolQuery::Kinds &
953  { return _pimpl->_kinds; }
954 
957  { return _pimpl->_repos; }
958 
959 
961  { return !_pimpl->_flags.test( Match::NOCASE ); }
962  void PoolQuery::setCaseSensitive( bool value )
963  { _pimpl->_flags.turn( Match::NOCASE, !value ); }
964 
966  { return _pimpl->_flags.test( Match::FILES ); }
968  { _pimpl->_flags.turn( Match::FILES, value ); }
969 
970  bool PoolQuery::matchExact() const { return _pimpl->_flags.isModeString(); }
972  bool PoolQuery::matchGlob() const { return _pimpl->_flags.isModeGlob(); }
973  bool PoolQuery::matchRegex() const { return _pimpl->_flags.isModeRegex(); }
974 
975  bool PoolQuery::matchWord() const
976  { return _pimpl->_match_word; }
977 
979  { return _pimpl->_require_all; }
980 
982  { return _pimpl->_status_flags; }
983 
984  bool PoolQuery::empty() const
985  {
986  try { return begin() == end(); }
987  catch (const Exception & ex) {}
988  return true;
989  }
990 
992  {
993  try
994  {
995  size_type count = 0;
996  for_( it, begin(), end() )
997  ++count;
998  return count;
999  }
1000  catch (const Exception & ex) {}
1001  return 0;
1002  }
1003 
1005  { invokeOnEach( begin(), end(), fnc); }
1006 
1007 
1009  //
1010  // CLASS NAME : PoolQuery::Attr
1011  //
1016  struct PoolQueryAttr : public IdStringType<PoolQueryAttr>
1017  {
1018  private:
1021  public:
1022 
1023  //noAttr
1025 
1026  explicit PoolQueryAttr( const char* cstr_r )
1027  : _str( cstr_r )
1028  {}
1029 
1030  explicit PoolQueryAttr( const std::string & str_r )
1031  : _str( str_r )
1032  {}
1033 
1034  // unknown atributes
1035  static const PoolQueryAttr noAttr;
1036 
1037  // PoolQuery's own attributes
1038  static const PoolQueryAttr repoAttr;
1039  static const PoolQueryAttr kindAttr;
1047  };
1048 
1050 
1051  const PoolQueryAttr PoolQueryAttr::repoAttr( "repo" );
1052  const PoolQueryAttr PoolQueryAttr::kindAttr( "type" );
1053  const PoolQueryAttr PoolQueryAttr::stringAttr( "query_string" );
1054  const PoolQueryAttr PoolQueryAttr::stringTypeAttr("match_type");
1055  const PoolQueryAttr PoolQueryAttr::requireAllAttr("require_all");
1056  const PoolQueryAttr PoolQueryAttr::caseSensitiveAttr("case_sensitive");
1057  const PoolQueryAttr PoolQueryAttr::installStatusAttr("install_status");
1058  const PoolQueryAttr PoolQueryAttr::editionAttr("version");
1059  const PoolQueryAttr PoolQueryAttr::complexAttr("complex");
1060 
1061  class StringTypeAttr : public IdStringType<PoolQueryAttr>
1062  {
1065 
1066  public:
1068  explicit StringTypeAttr( const char* cstr_r )
1069  : _str( cstr_r ){}
1070  explicit StringTypeAttr( const std::string & str_r )
1071  : _str( str_r ){}
1072 
1073  static const StringTypeAttr noAttr;
1074 
1078  static const StringTypeAttr globAttr;
1079  static const StringTypeAttr wordAttr;
1080  };
1081 
1083 
1085  const StringTypeAttr StringTypeAttr::substringAttr("substring");
1089 
1091 
1092 
1093  //\TODO maybe ctor with stream can be usefull
1094  //\TODO let it throw, let it throw, let it throw.
1095  bool PoolQuery::recover( istream &str, char delim )
1096  {
1097  bool finded_something = false; //indicates some atributes is finded
1098  string s;
1099  do {
1100  if ( str.eof() )
1101  break;
1102 
1103  getline( str, s, delim );
1104 
1105  if ((!s.empty()) && s[0]=='#') //comment
1106  {
1107  continue;
1108  }
1109 
1110  string::size_type pos = s.find(':');
1111  if (s.empty() || pos == s.npos) // some garbage on line... act like blank line
1112  {
1113  if (finded_something) //is first blank line after record?
1114  {
1115  break;
1116  }
1117  else
1118  {
1119  continue;
1120  }
1121  }
1122 
1123  finded_something = true;
1124 
1125  string attrName(str::trim(string(s,0,pos))); // trimmed name of atribute
1126  string attrValue(str::trim(string(s,pos+1,s.npos))); //trimmed value
1127 
1128  PoolQueryAttr attribute( attrName );
1129 
1130  MIL << "attribute name: " << attrName << endl;
1131 
1132  if ( attribute==PoolQueryAttr::repoAttr )
1133  {
1134  addRepo( attrValue );
1135  }
1136  /* some backwards compatibility */
1137  else if ( attribute==PoolQueryAttr::kindAttr || attribute=="kind" )
1138  {
1139  addKind( ResKind(attrValue) );
1140  }
1141  else if ( attribute==PoolQueryAttr::stringAttr
1142  || attribute=="global_string")
1143  {
1144  addString( attrValue );
1145  }
1146  else if ( attribute==PoolQueryAttr::stringTypeAttr
1147  || attribute=="string_type" )
1148  {
1149  StringTypeAttr s(attrValue);
1150  if( s == StringTypeAttr::regexAttr )
1151  {
1152  setMatchRegex();
1153  }
1154  else if ( s == StringTypeAttr::globAttr )
1155  {
1156  setMatchGlob();
1157  }
1158  else if ( s == StringTypeAttr::exactAttr )
1159  {
1160  setMatchExact();
1161  }
1162  else if ( s == StringTypeAttr::substringAttr )
1163  {
1165  }
1166  else if ( s == StringTypeAttr::wordAttr )
1167  {
1168  setMatchWord();
1169  }
1170  else if ( s == StringTypeAttr::noAttr )
1171  {
1172  WAR << "unknown string type " << attrValue << endl;
1173  }
1174  else
1175  {
1176  WAR << "forget recover some attribute defined as String type attribute: " << attrValue << endl;
1177  }
1178  }
1179  else if ( attribute==PoolQueryAttr::requireAllAttr )
1180  {
1181  if ( str::strToTrue(attrValue) )
1182  {
1183  setRequireAll(true);
1184  }
1185  else if ( !str::strToFalse(attrValue) )
1186  {
1187  setRequireAll(false);
1188  }
1189  else
1190  {
1191  WAR << "unknown boolean value " << attrValue << endl;
1192  }
1193  }
1194  else if ( attribute==PoolQueryAttr::caseSensitiveAttr )
1195  {
1196  if ( str::strToTrue(attrValue) )
1197  {
1198  setCaseSensitive(true);
1199  }
1200  else if ( !str::strToFalse(attrValue) )
1201  {
1202  setCaseSensitive(false);
1203  }
1204  else
1205  {
1206  WAR << "unknown boolean value " << attrValue << endl;
1207  }
1208  }
1209  else if ( attribute==PoolQueryAttr::installStatusAttr )
1210  {
1211  if( attrValue == "all" )
1212  {
1214  }
1215  else if( attrValue == "installed" )
1216  {
1217  setInstalledOnly();
1218  }
1219  else if( attrValue == "not-installed" )
1220  {
1222  }
1223  else
1224  {
1225  WAR << "Unknown value for install status " << attrValue << endl;
1226  }
1227  }
1228  else if ( attribute == PoolQueryAttr::editionAttr)
1229  {
1230  string::size_type pos;
1231  Rel rel("==");
1232  if (attrValue.find_first_of("=<>!") == 0)
1233  {
1234  pos = attrValue.find_last_of("=<>");
1235  rel = Rel(attrValue.substr(0, pos+1));
1236  attrValue = str::trim(attrValue.substr(pos+1, attrValue.npos));
1237  }
1238 
1239  setEdition(Edition(attrValue), rel);
1240  }
1241  else if ( attribute == PoolQueryAttr::complexAttr )
1242  {
1243  try
1244  {
1246  }
1247  catch ( const Exception & err )
1248  {
1249  WAR << "Unparsable value for complex: " << err.asUserHistory() << endl;
1250 
1251  }
1252  }
1253  else if ( attribute==PoolQueryAttr::noAttr )
1254  {
1255  WAR << "empty attribute name" << endl;
1256  }
1257  else
1258  {
1259  string s = attrName;
1260  str::replaceAll( s,"_",":" );
1261  SolvAttr a(s);
1262  if ( a == SolvAttr::name || isDependencyAttribute( a ) )
1263  {
1264  Capability c( attrValue );
1265  CapDetail d( c );
1266  if ( d.isVersioned() )
1267  addDependency( a, d.name().asString(), d.op(), d.ed() );
1268  else
1269  addDependency( a, attrValue );
1270  }
1271  else
1272  addAttribute( a, attrValue );
1273  }
1274 
1275  } while ( true );
1276 
1277  return finded_something;
1278  }
1279 
1280  void PoolQuery::serialize( ostream &str, char delim ) const
1281  {
1282  //separating delim
1283  str << delim;
1284  //iterate thrue all settings and write it
1285  static const zypp::PoolQuery q; //not save default options, so create default query example
1286 
1287  for_( it, repos().begin(), repos().end() )
1288  {
1289  str << "repo: " << *it << delim ;
1290  }
1291 
1292  for_( it, kinds().begin(), kinds().end() )
1293  {
1294  str << PoolQueryAttr::kindAttr.asString() << ": "
1295  << it->idStr() << delim ;
1296  }
1297 
1298  if (editionRel() != Rel::ANY && edition() != Edition::noedition)
1299  str << PoolQueryAttr::editionAttr.asString() << ": " << editionRel() << " " << edition() << delim;
1300 
1301  if (matchMode()!=q.matchMode())
1302  {
1303  switch( matchMode() )
1304  {
1305  case Match::STRING:
1306  str << PoolQueryAttr::stringTypeAttr.asString() << ": exact" << delim;
1307  break;
1308  case Match::SUBSTRING:
1310  << ": substring" << delim;
1311  break;
1312  case Match::GLOB:
1313  str << PoolQueryAttr::stringTypeAttr.asString() << ": glob" << delim;
1314  break;
1315  case Match::REGEX:
1316  str << PoolQueryAttr::stringTypeAttr.asString() << ": regex" << delim;
1317  break;
1318  default:
1319  WAR << "unknown match type " << matchMode() << endl;
1320  }
1321  }
1322 
1323  if( caseSensitive() != q.caseSensitive() )
1324  {
1325  str << "case_sensitive: ";
1326  if (caseSensitive())
1327  {
1328  str << "on" << delim;
1329  }
1330  else
1331  {
1332  str << "off" << delim;
1333  }
1334  }
1335 
1336  if( requireAll() != q.requireAll() )
1337  {
1338  str << "require_all: ";
1339  if (requireAll())
1340  {
1341  str << "on" << delim;
1342  }
1343  else
1344  {
1345  str << "off" << delim;
1346  }
1347  }
1348 
1349  if( statusFilterFlags() != q.statusFilterFlags() )
1350  {
1351  switch( statusFilterFlags() )
1352  {
1353  case ALL:
1354  str << "install_status: all" << delim;
1355  break;
1356  case INSTALLED_ONLY:
1357  str << "install_status: installed" << delim;
1358  break;
1359  case UNINSTALLED_ONLY:
1360  str << "install_status: not-installed" << delim;
1361  break;
1362  }
1363  }
1364 
1365  for_( it, strings().begin(), strings().end() )
1366  {
1367  str << PoolQueryAttr::stringAttr.asString()<< ": " << *it << delim;
1368  }
1369 
1370  for_( it, attributes().begin(), attributes().end() )
1371  {
1372  string s = it->first.asString();
1373  str::replaceAll(s,":","_");
1374  for_( it2,it->second.begin(),it->second.end() )
1375  {
1376  str << s <<": "<< *it2 << delim;
1377  }
1378  }
1379 
1381  {
1382  str << "complex: "<< it->serialize() << delim;
1383  }
1384 
1385  //separating delim - protection
1386  str << delim;
1387  }
1388 
1389  string PoolQuery::asString() const
1390  { return _pimpl->asString(); }
1391 
1392  ostream & operator<<( ostream & str, const PoolQuery & obj )
1393  { return str << obj.asString(); }
1394 
1395  std::ostream & dumpOn( std::ostream & str, const PoolQuery & obj )
1396  { return dumpRange( str << obj, obj.begin(), obj.end() ); }
1397 
1398  bool PoolQuery::operator==( const PoolQuery & rhs ) const
1399  { return *_pimpl == *rhs._pimpl; }
1400 
1402  namespace detail
1403  {
1404 
1406  //
1407  // CLASS NAME : PoolQueryMatcher
1408  //
1426  {
1427  public:
1429 
1430  public:
1431  const base_iterator & end() const
1432  {
1433  static base_iterator _end;
1434  return _end;
1435  }
1436 
1437  bool advance( base_iterator & base_r ) const
1438  {
1439  if ( base_r == end() )
1440  base_r = startNewQyery(); // first candidate
1441  else
1442  {
1443  base_r.nextSkipSolvable(); // assert we don't visit this Solvable again
1444  ++base_r; // advance to next candidate
1445  }
1446 
1447  while ( base_r != end() )
1448  {
1449  if ( isAMatch( base_r ) )
1450  return true;
1451  // No match: try next
1452  ++base_r;
1453  }
1454  return false;
1455  }
1456 
1460  void matchDetail( const base_iterator & base_r, std::vector<base_iterator> & return_r ) const
1461  {
1462  if ( base_r == end() )
1463  return;
1464 
1465  sat::Solvable inSolvable( base_r.inSolvable() );
1466 
1467  if ( _attrMatchList.size() == 1 )
1468  {
1469  // base_r is already on the 1st matching attribute!
1470  // String matching is done by the base iterator. We must check the predicate here.
1471  // Let's see if there are more matches for this solvable:
1472  base_iterator base( base_r );
1473  base.stayInThisSolvable(); // avoid discarding matches we found far away from here.
1474  return_r.push_back( base );
1475 
1476  const AttrMatchData::Predicate & predicate( _attrMatchList.front().predicate );
1477  for ( ++base; base.inSolvable() == inSolvable; ++base ) // safe even if base == end()
1478  {
1479  if ( ! predicate || predicate( base ) )
1480  return_r.push_back( base );
1481  }
1482  }
1483  else
1484  {
1485  // Here: search all attributes ;(
1486  for_( mi, _attrMatchList.begin(), _attrMatchList.end() )
1487  {
1488  const AttrMatchData & matchData( *mi );
1489  sat::LookupAttr q( matchData.attr, inSolvable );
1490  if ( matchData.strMatcher ) // an empty searchstring matches always
1491  q.setStrMatcher( matchData.strMatcher );
1492 
1493  if ( ! q.empty() ) // there are matches.
1494  {
1495  // now check any predicate:
1496  const AttrMatchData::Predicate & predicate( matchData.predicate );
1497  for_( it, q.begin(), q.end() )
1498  {
1499  if ( ! predicate || predicate( it ) )
1500  return_r.push_back( it );
1501  }
1502  }
1503  }
1504  }
1505  }
1506 
1507  public:
1511  PoolQueryMatcher( const shared_ptr<const PoolQuery::Impl> & query_r )
1512  {
1513  query_r->compile();
1514 
1515  // Repo restriction:
1516  sat::Pool satpool( sat::Pool::instance() );
1517 
1518  for_( it, query_r->_repos.begin(), query_r->_repos.end() )
1519  {
1520  Repository r( satpool.reposFind( *it ) );
1521  if ( r )
1522  _repos.insert( r );
1523  else
1524  _neverMatchRepo = true;
1525  }
1526  // _neverMatchRepo: we just need to catch the case that no repo
1527  // matched, so we'd interpret the empty list as 'take from all'
1528  if ( _neverMatchRepo && ! _repos.empty() )
1529  _neverMatchRepo = false;
1530 
1531  // Kind restriction:
1532  _kinds = query_r->_kinds;
1533  // Edition restriction:
1534  _op = query_r->_op;
1535  _edition = query_r->_edition;
1536  // Status restriction:
1537  _status_flags = query_r->_status_flags;
1538  // StrMatcher
1539  _attrMatchList = query_r->_attrMatchList;
1540  }
1541 
1543  {}
1544 
1545  private:
1548  {
1549  sat::LookupAttr q;
1550 
1551  if ( _neverMatchRepo )
1552  return q.end();
1553 
1554  // Repo restriction:
1555  if ( _repos.size() == 1 )
1556  q.setRepo( *_repos.begin() );
1557  // else: handled in isAMatch.
1558 
1559  // Attribute restriction:
1560  if ( _attrMatchList.size() == 1 ) // all (SolvAttr::allAttr) or 1 attr
1561  {
1562  const AttrMatchData & matchData( _attrMatchList.front() );
1563  q.setAttr( matchData.attr );
1564  if ( matchData.strMatcher ) // empty searchstring matches always
1565  q.setStrMatcher( matchData.strMatcher );
1566  }
1567  else // more than 1 attr (but not all)
1568  {
1569  // no restriction, it's all handled in isAMatch.
1571  }
1572 
1573  return q.begin();
1574  }
1575 
1576 
1587  bool isAMatch( base_iterator & base_r ) const
1588  {
1590  Repository inRepo( base_r.inRepo() );
1591  // Status restriction:
1592  if ( _status_flags
1593  && ( (_status_flags == PoolQuery::INSTALLED_ONLY) != inRepo.isSystemRepo() ) )
1594  {
1595  base_r.nextSkipRepo();
1596  return false;
1597  }
1598  // Repo restriction:
1599  if ( _repos.size() > 1 && _repos.find( inRepo ) == _repos.end() )
1600  {
1601  base_r.nextSkipRepo();
1602  return false;
1603  }
1605  sat::Solvable inSolvable( base_r.inSolvable() );
1606  // Kind restriction:
1607  if ( ! _kinds.empty() && ! inSolvable.isKind( _kinds.begin(), _kinds.end() ) )
1608  {
1609  base_r.nextSkipSolvable();
1610  return false;
1611  }
1612 
1613  // Edition restriction:
1614  if ( _op != Rel::ANY && !compareByRel( _op, inSolvable.edition(), _edition, Edition::Match() ) )
1615  {
1616  base_r.nextSkipSolvable();
1617  return false;
1618  }
1620  // string and predicate matching:
1621 
1622  if ( _attrMatchList.size() == 1 )
1623  {
1624  // String matching was done by the base iterator.
1625  // Now check any predicate:
1626  const AttrMatchData::Predicate & predicate( _attrMatchList.front().predicate );
1627  if ( ! predicate || predicate( base_r ) )
1628  return true;
1629 
1630  return false; // no skip as there may be more occurrences od this attr.
1631  }
1632 
1633  // Here: search all attributes ;(
1634  for_( mi, _attrMatchList.begin(), _attrMatchList.end() )
1635  {
1636  const AttrMatchData & matchData( *mi );
1637  sat::LookupAttr q( matchData.attr, inSolvable );
1638  if ( matchData.strMatcher ) // an empty searchstring matches always
1639  q.setStrMatcher( matchData.strMatcher );
1640 
1641  if ( ! q.empty() ) // there are matches.
1642  {
1643  // now check any predicate:
1644  const AttrMatchData::Predicate & predicate( matchData.predicate );
1645  if ( predicate )
1646  {
1647  for_( it, q.begin(), q.end() )
1648  {
1649  if ( predicate( it ) )
1650  return true;
1651  }
1652  }
1653  else
1654  return true;
1655  }
1656  }
1657  base_r.nextSkipSolvable();
1658  return false;
1659  }
1660 
1661  private:
1663  std::set<Repository> _repos;
1666  std::set<ResKind> _kinds;
1673  AttrMatchList _attrMatchList;
1674  };
1676 
1678  {
1679  // matcher restarts if at end! It is called from the ctor
1680  // to get the 1st match. But if the end is reached, it should
1681  // be deleted, otherwise we'd start over again.
1682  if ( !_matcher )
1683  return; // at end
1684  if ( _matches )
1685  _matches.reset(); // invalidate old matches
1686  if ( ! _matcher->advance( base_reference() ) )
1687  _matcher.reset();
1688  }
1689 
1691  {
1692  if ( _matches )
1693  return *_matches;
1694 
1695  if ( !_matcher )
1696  {
1697  // at end of query:
1698  static const Matches _none;
1699  return _none;
1700  }
1701 
1702  _matches.reset( new Matches );
1703  _matcher->matchDetail( base_reference(), *_matches );
1704  return *_matches;
1705  }
1706 
1707  std::ostream & dumpOn( std::ostream & str, const PoolQueryIterator & obj )
1708  {
1709  str << *obj;
1710  if ( ! obj.matchesEmpty() )
1711  {
1712  for_( it, obj.matchesBegin(), obj.matchesEnd() )
1713  {
1714  str << endl << " " << it->inSolvAttr() << "\t" << it->asString();
1715  }
1716  }
1717  return str;
1718  }
1719 
1721  } //namespace detail
1723 
1725  {
1726  return shared_ptr<detail::PoolQueryMatcher>( new detail::PoolQueryMatcher( _pimpl.getPtr() ) );
1727  }
1728 
1730 } // namespace zypp
1732