added Collector: functor writing elements to an output iterator.
[platform/upstream/libzypp.git] / zypp / sat / Solvable.cc
1 /*---------------------------------------------------------------------\
2 |                          ____ _   __ __ ___                          |
3 |                         |__  / \ / / . \ . \                         |
4 |                           / / \ V /|  _/  _/                         |
5 |                          / /__ | | | | | |                           |
6 |                         /_____||_| |_| |_|                           |
7 |                                                                      |
8 \---------------------------------------------------------------------*/
9 /** \file       zypp/sat/Solvable.cc
10  *
11 */
12 #include <iostream>
13
14 #include "zypp/base/Logger.h"
15 #include "zypp/base/Gettext.h"
16 #include "zypp/base/Exception.h"
17 #include "zypp/base/Functional.h"
18 #include "zypp/base/Collector.h"
19
20 #include "zypp/sat/detail/PoolImpl.h"
21 #include "zypp/sat/Solvable.h"
22 #include "zypp/sat/Repo.h"
23
24 using std::endl;
25
26 ///////////////////////////////////////////////////////////////////
27 namespace zypp
28 { /////////////////////////////////////////////////////////////////
29   ///////////////////////////////////////////////////////////////////
30   namespace sat
31   { /////////////////////////////////////////////////////////////////
32
33     const Solvable Solvable::nosolvable;
34
35     /////////////////////////////////////////////////////////////////
36
37     ::_Solvable * Solvable::get() const
38     { return myPool().getSolvable( _id ); }
39
40 #define NO_SOLVABLE_RETURN( VAL ) \
41     ::_Solvable * _solvable( get() ); \
42     if ( ! _solvable ) return VAL
43
44     Solvable Solvable::nextInPool() const
45     { return Solvable( myPool().getNextId( _id ) ); }
46
47     Solvable Solvable::nextInRepo() const
48     {
49       NO_SOLVABLE_RETURN( nosolvable );
50       for ( detail::SolvableIdType next = _id+1; next < unsigned(_solvable->repo->end); ++next )
51       {
52         ::_Solvable * nextS( myPool().getSolvable( next ) );
53         if ( nextS && nextS->repo == _solvable->repo )
54         {
55           return Solvable( next );
56         }
57       }
58       return nosolvable;
59     }
60
61     Repo Solvable::repo() const
62     {
63       NO_SOLVABLE_RETURN( Repo::norepo );
64       return Repo( _solvable->repo );
65     }
66
67     bool Solvable::isSystem() const
68     { return repo().isSystemRepo(); }
69
70     IdString Solvable::ident() const
71     {
72       NO_SOLVABLE_RETURN( IdString() );
73       return IdString( _solvable->name );
74     }
75
76     std::string Solvable::lookupStrAttribute( const SolvAttr &attr ) const
77     {
78       const char *s = repo_lookup_str(this->get(), attr.idStr().id());
79       return s ? s : std::string();
80     }
81
82     unsigned Solvable::lookupNumAttribute( const SolvAttr &attr ) const
83     {
84       return repo_lookup_num(this->get(), attr.idStr().id());
85     }
86
87     bool Solvable::lookupBoolAttribute( const SolvAttr &attr ) const
88     {
89       return repo_lookup_num(this->get(), attr.idStr().id()) > 0;
90     }
91
92     struct LocCallback
93     {
94       unsigned medianr;
95       const char *mediadir;
96       const char *mediafile;
97       int trivial;
98     };
99
100     static int
101     location_cb (void *vcbdata, ::Solvable *s, ::Repodata *data, ::Repokey *key, ::KeyValue *kv)
102     {
103       LocCallback *lc = (LocCallback *)vcbdata;
104       switch (key->type)
105       {
106         case TYPE_ID:
107         if (key->name == SolvAttr::mediadir.idStr().id())
108         {
109           if (data->localpool)
110             lc->mediadir = stringpool_id2str(&data->spool, kv->id);
111           else
112             lc->mediadir = id2str(data->repo->pool, kv->id);
113         }
114         break;
115         case TYPE_STR:
116         if (key->name == SolvAttr::mediafile.idStr().id())
117           lc->mediafile = kv->str;
118         break;
119           case TYPE_VOID:
120         if (key->name == SolvAttr::mediafile.idStr().id())
121           lc->trivial = 1;
122         break;
123           case TYPE_CONSTANT:
124         if (key->name == SolvAttr::medianr.idStr().id())
125           lc->medianr = kv->num;
126         break;
127       }
128       /* continue walking */
129       return 0;
130     }
131
132     std::string Solvable::lookupLocation(unsigned &medianr) const
133     {
134       NO_SOLVABLE_RETURN( std::string() );
135       ::Repo *repo = _solvable->repo;
136       ::Pool *pool = repo->pool;
137       Id sid = _solvable - pool->solvables;
138       ::Repodata *data;
139       unsigned i;
140       LocCallback lc;
141       lc.medianr = 1;
142       lc.mediadir = 0;
143       lc.mediafile = 0;
144       lc.trivial = 0;
145       for (i = 0, data = repo->repodata; i < repo->nrepodata; i++, data++)
146       {
147         if (data->state == REPODATA_STUB || data->state == REPODATA_ERROR)
148           continue;
149         if (sid < data->start || sid >= data->end)
150           continue;
151         repodata_search(data, sid - data->start, 0, location_cb, &lc);
152       }
153       medianr = lc.medianr;
154       std::string ret;
155
156       if (!lc.trivial)
157       {
158         if (lc.mediafile)
159           ret += lc.mediafile;
160         return ret;
161       }
162
163       if (lc.mediadir)
164       {
165         ret += std::string( lc.mediadir ) + "/";
166       }
167       else
168       {
169         /* If we haven't seen an explicit dirname, then prepend the arch as
170            directory.  */
171         ret += "suse/";
172         ret += IdString(_solvable->arch).asString() + "/";
173       }
174       /* Trivial means that we can construct the rpm name from our
175          solvable data, as name-evr.arch.rpm .  */
176       ret += IdString(_solvable->name).asString();
177       ret += '-';
178       ret += IdString(_solvable->evr).asString();
179       ret += '.';
180       ret += IdString(_solvable->arch).asString();
181       ret += ".rpm";
182       return ret;
183     }
184
185     ResKind Solvable::kind() const
186     {
187       NO_SOLVABLE_RETURN( ResKind() );
188       // detect srcpackages by 'arch'
189       switch ( _solvable->arch )
190       {
191         case ARCH_SRC:
192         case ARCH_NOSRC:
193           return ResKind::srcpackage;
194           break;
195       }
196
197       const char * ident = IdString( _solvable->name ).c_str();
198       const char * sep = ::strchr( ident, ':' );
199
200       // no ':' in package names (hopefully)
201       if ( ! sep )
202         return ResKind::package;
203
204       // quick check for well known kinds
205       if ( sep-ident >= 4 )
206       {
207         switch ( ident[3] )
208         {
209 #define OUTS(K,S) if ( !::strncmp( ident, ResKind::K.c_str(), S ) ) return ResKind::K
210           //             ----v
211           case 'c': OUTS( patch, 5 );       break;
212           case 'd': OUTS( product, 7 );     break;
213           case 'i': OUTS( script, 6 );      break;
214           case 'k': OUTS( package, 7 );     break;
215           case 'm': OUTS( atom, 4 );        break;
216           case 'p': OUTS( srcpackage, 10 ); break;
217           case 's': OUTS( message, 7 );     break;
218           case 't': OUTS( pattern, 7 );     break;
219 #undef OUTS
220         }
221       }
222
223       // an unknown kind
224       return ResKind( std::string( ident, sep-ident ) );
225     }
226
227     bool Solvable::isKind( const ResKind & kind_r ) const
228     {
229       NO_SOLVABLE_RETURN( false );
230
231       // detect srcpackages by 'arch'
232       if ( kind_r == ResKind::srcpackage )
233       {
234         return( _solvable->arch == ARCH_SRC || _solvable->arch == ARCH_NOSRC );
235       }
236
237       // no ':' in package names (hopefully)
238       const char * ident = IdString( _solvable->name ).c_str();
239       if ( kind_r == ResKind::package )
240       {
241         return( ::strchr( ident, ':' ) == 0 );
242       }
243
244       // look for a 'kind:' prefix
245       const char * kind = kind_r.c_str();
246       unsigned     ksize = ::strlen( kind );
247       return( ::strncmp( ident, kind, ksize ) == 0
248               && ident[ksize] == ':' );
249     }
250
251     std::string Solvable::name() const
252     {
253       NO_SOLVABLE_RETURN( std::string() );
254       const char * ident = IdString( _solvable->name ).c_str();
255       const char * sep = ::strchr( ident, ':' );
256       return( sep ? sep+1 : ident );
257     }
258
259     Edition Solvable::edition() const
260     {
261       NO_SOLVABLE_RETURN( Edition() );
262       return Edition( _solvable->evr );
263     }
264
265     Arch Solvable::arch() const
266     {
267       NO_SOLVABLE_RETURN( Arch_noarch ); //ArchId() );
268       switch ( _solvable->arch )
269       {
270         case ARCH_SRC:
271         case ARCH_NOSRC:
272           return Arch_noarch; //ArchId( ARCH_NOARCH );
273           break;
274       }
275       return Arch( IdString(_solvable->arch).asString() );
276       //return ArchId( _solvable->arch );
277     }
278
279     IdString Solvable::vendor() const
280     {
281       NO_SOLVABLE_RETURN( IdString() );
282       return IdString( _solvable->vendor );
283     }
284
285     Capabilities Solvable::operator[]( Dep which_r ) const
286     {
287       switch( which_r.inSwitch() )
288       {
289         case Dep::PROVIDES_e:    return provides();    break;
290         case Dep::REQUIRES_e:    return requires();    break;
291         case Dep::CONFLICTS_e:   return conflicts();   break;
292         case Dep::OBSOLETES_e:   return obsoletes();   break;
293         case Dep::RECOMMENDS_e:  return recommends();  break;
294         case Dep::SUGGESTS_e:    return suggests();    break;
295         case Dep::FRESHENS_e:    return freshens();    break;
296         case Dep::ENHANCES_e:    return enhances();    break;
297         case Dep::SUPPLEMENTS_e: return supplements(); break;
298         case Dep::PREREQUIRES_e: return prerequires(); break;
299       }
300       return Capabilities();
301     }
302
303     inline Capabilities _getCapabilities( detail::IdType * idarraydata_r, ::Offset offs_r )
304     {
305       return offs_r ? Capabilities( idarraydata_r + offs_r ) : Capabilities();
306     }
307     Capabilities Solvable::provides() const
308     {
309       NO_SOLVABLE_RETURN( Capabilities() );
310       return _getCapabilities( _solvable->repo->idarraydata, _solvable->provides );
311     }
312     Capabilities Solvable::requires() const
313     {
314       NO_SOLVABLE_RETURN( Capabilities() );
315       return _getCapabilities( _solvable->repo->idarraydata, _solvable->requires );
316     }
317     Capabilities Solvable::conflicts() const
318     {
319       NO_SOLVABLE_RETURN( Capabilities() );
320       return _getCapabilities( _solvable->repo->idarraydata, _solvable->conflicts );
321     }
322     Capabilities Solvable::obsoletes() const
323     {
324       NO_SOLVABLE_RETURN( Capabilities() );
325       return _getCapabilities( _solvable->repo->idarraydata, _solvable->obsoletes );
326     }
327     Capabilities Solvable::recommends() const
328     {
329       NO_SOLVABLE_RETURN( Capabilities() );
330       return _getCapabilities( _solvable->repo->idarraydata, _solvable->recommends );
331     }
332     Capabilities Solvable::suggests() const
333     {
334       NO_SOLVABLE_RETURN( Capabilities() );
335       return _getCapabilities( _solvable->repo->idarraydata, _solvable->suggests );
336     }
337     Capabilities Solvable::freshens() const
338     {
339       NO_SOLVABLE_RETURN( Capabilities() );
340       return _getCapabilities( _solvable->repo->idarraydata, _solvable->freshens );
341     }
342     Capabilities Solvable::enhances() const
343     {
344       NO_SOLVABLE_RETURN( Capabilities() );
345       return _getCapabilities( _solvable->repo->idarraydata, _solvable->enhances );
346     }
347     Capabilities Solvable::supplements() const
348     {
349       NO_SOLVABLE_RETURN( Capabilities() );
350       return _getCapabilities( _solvable->repo->idarraydata, _solvable->supplements );
351     }
352     Capabilities Solvable::prerequires() const
353     {
354       NO_SOLVABLE_RETURN( Capabilities() );
355       // prerequires are a subset of requires
356        ::Offset offs = _solvable->requires;
357        return offs ? Capabilities( _solvable->repo->idarraydata + offs, detail::solvablePrereqMarker )
358                    : Capabilities();
359     }
360
361     ///////////////////////////////////////////////////////////////////
362     namespace
363     { /////////////////////////////////////////////////////////////////
364       /** Expand \ref Capability and call \c fnc_r for each namescpace:language
365        * dependency. Return #invocations of fnc_r, negative if fnc_r returned
366        * false to indicate abort.
367        */
368       int invokeOnEachSupportedLocale( Capability cap_r, function<bool (const Locale &)> fnc_r )
369       {
370         CapDetail detail( cap_r );
371         if ( detail.kind() == CapDetail::EXPRESSION )
372         {
373           switch ( detail.capRel() )
374           {
375             case CapDetail::CAP_AND:
376             case CapDetail::CAP_OR:
377                 // expand
378             {
379               int res = invokeOnEachSupportedLocale( detail.lhs(), fnc_r );
380               if ( res < 0 )
381                 return res; // negative on abort.
382               int res2 = invokeOnEachSupportedLocale( detail.rhs(), fnc_r );
383               if ( res2 < 0 )
384                 return -res + res2; // negative on abort.
385               return res + res2;
386             }
387             break;
388
389             case CapDetail::CAP_NAMESPACE:
390               if ( detail.lhs().id() == NAMESPACE_LANGUAGE )
391               {
392                 return ( !fnc_r || fnc_r( Locale( IdString(detail.rhs().id()) ) ) ) ? 1 : -1; // negative on abort.
393               }
394               break;
395
396             case CapDetail::REL_NONE:
397             case CapDetail::CAP_WITH:
398               break; // unwanted
399           }
400         }
401         return 0;
402       }
403
404        /** Expand \ref Capability and call \c fnc_r for each namescpace:language
405        * dependency. Return #invocations of fnc_r, negative if fnc_r returned
406        * false to indicate abort.
407        */
408       inline int invokeOnEachSupportedLocale( Capabilities cap_r, function<bool (const Locale &)> fnc_r )
409       {
410         int cnt = 0;
411         for_( cit, cap_r.begin(), cap_r.end() )
412         {
413           int res = invokeOnEachSupportedLocale( *cit, fnc_r );
414           if ( res < 0 )
415             return -cnt + res; // negative on abort.
416           cnt += res;
417         }
418         return cnt;
419       }
420       //@}
421
422       // Functor returning false if a Locale is in the set.
423       struct NoMatchIn
424       {
425         NoMatchIn( const LocaleSet & locales_r ) : _locales( locales_r ) {}
426
427         bool operator()( const Locale & locale_r ) const
428         {
429           return _locales.find( locale_r ) == _locales.end();
430         }
431
432         const LocaleSet & _locales;
433       };
434
435     } /////////////////////////////////////////////////////////////////
436
437     bool Solvable::supportsLocales() const
438     {
439       // false_c stops on 1st Locale.
440       return invokeOnEachSupportedLocale( supplements(), functor::false_c() ) < 0;
441     }
442
443     bool Solvable::supportsLocale( const Locale & locale_r ) const
444     {
445       // not_equal_to stops on == Locale.
446       return invokeOnEachSupportedLocale( supplements(), bind( std::not_equal_to<Locale>(), locale_r, _1 ) ) < 0;
447     }
448
449     bool Solvable::supportsLocale( const LocaleSet & locales_r ) const
450     {
451       if ( locales_r.empty() )
452         return false;
453       // NoMatchIn stops if Locale is included.
454       return invokeOnEachSupportedLocale( supplements(), NoMatchIn(locales_r) ) < 0;
455     }
456
457     bool Solvable::supportsRequestedLocales() const
458     { return supportsLocale( myPool().getRequestedLocales() ); }
459
460     void Solvable::getSupportedLocales( LocaleSet & locales_r ) const
461     {
462       invokeOnEachSupportedLocale( supplements(),
463                                    functor::Collector( std::inserter( locales_r, locales_r.begin() ) ) );
464     }
465
466     /******************************************************************
467     **
468     **  FUNCTION NAME : operator<<
469     **  FUNCTION TYPE : std::ostream &
470     */
471     std::ostream & operator<<( std::ostream & str, const Solvable & obj )
472     {
473       if ( ! obj )
474         return str << "sat::solvable()";
475
476       return str << "(" << obj.id() << ")"
477           << ( obj.isKind( ResKind::srcpackage ) ? "srcpackage:" : "" ) << obj.ident()
478           << '-' << obj.edition() << '.' << obj.arch() << "("
479           << obj.repo().name() << ")";
480     }
481
482     /******************************************************************
483     **
484     **  FUNCTION NAME : dumpOn
485     **  FUNCTION TYPE : std::ostream &
486     */
487     std::ostream & dumpOn( std::ostream & str, const Solvable & obj )
488     {
489       str << obj;
490       if ( obj )
491       {
492 #define OUTS(X) if ( ! obj[Dep::X].empty() ) str << endl << " " #X " " << obj[Dep::X]
493         OUTS(PROVIDES);
494         OUTS(PREREQUIRES);
495         OUTS(REQUIRES);
496         OUTS(CONFLICTS);
497         OUTS(OBSOLETES);
498         OUTS(RECOMMENDS);
499         OUTS(SUGGESTS);
500         OUTS(FRESHENS);
501         OUTS(ENHANCES);
502         OUTS(SUPPLEMENTS);
503 #undef OUTS
504       }
505       return str;
506     }
507
508     /////////////////////////////////////////////////////////////////
509   } // namespace sat
510   ///////////////////////////////////////////////////////////////////
511   /////////////////////////////////////////////////////////////////
512 } // namespace zypp
513 ///////////////////////////////////////////////////////////////////