9c6c4fef5c5d708dd18b974dc140bce1f5d34883
[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/Pool.h"
23 #include "zypp/Repository.h"
24 #include "zypp/OnMediaLocation.h"
25
26 using std::endl;
27
28 ///////////////////////////////////////////////////////////////////
29 namespace zypp
30 { /////////////////////////////////////////////////////////////////
31   ///////////////////////////////////////////////////////////////////
32   namespace sat
33   { /////////////////////////////////////////////////////////////////
34
35     Solvable::SplitIdent::SplitIdent( IdString ident_r )
36     : _ident( ident_r )
37     {
38       if ( ! ident_r )
39         return;
40
41       const char * ident = ident_r.c_str();
42       const char * sep = ::strchr( ident, ':' );
43
44       // no ':' in package names (hopefully)
45       if ( ! sep )
46       {
47         _kind = ResKind::package;
48         _name = ident_r;
49         return;
50       }
51
52       // save name
53       _name = IdString( sep+1 );
54       // quick check for well known kinds
55       if ( sep-ident >= 4 )
56       {
57         switch ( ident[3] )
58         {
59 #define OUTS(K,S) if ( !::strncmp( ident, ResKind::K.c_str(), S ) ) _kind = ResKind::K
60           //             ----v
61           case 'c': OUTS( patch, 5 );       return; break;
62           case 'd': OUTS( product, 7 );     return; break;
63           case 'i': OUTS( script, 6 );      return; break;
64           case 'k': OUTS( package, 7 );     return; break;
65           case 'm': OUTS( atom, 4 );        return; break;
66           case 'p': OUTS( srcpackage, 10 ); return; break;
67           case 's': OUTS( message, 7 );     return; break;
68           case 't': OUTS( pattern, 7 );     return; break;
69 #undef OUTS
70         }
71       }
72
73       // an unknown kind
74       _kind = ResKind( std::string( ident, sep-ident ) );
75     }
76
77     Solvable::SplitIdent::SplitIdent( ResKind kind_r, IdString name_r )
78     : _kind( kind_r )
79     , _name( name_r )
80     {
81       if ( kind_r == ResKind::package || kind_r == ResKind::srcpackage )
82         _ident = _name;
83       else
84         _ident = IdString( str::form( "%s:%s", kind_r.c_str(), name_r.c_str() ) );
85     }
86
87     Solvable::SplitIdent::SplitIdent( ResKind kind_r, const C_Str & name_r )
88     : _kind( kind_r )
89     , _name( name_r )
90     {
91       if ( kind_r == ResKind::package || kind_r == ResKind::srcpackage )
92         _ident = _name;
93       else
94         _ident = IdString( str::form( "%s:%s", kind_r.c_str(), name_r.c_str() ) );
95     }
96
97     /////////////////////////////////////////////////////////////////
98
99     const Solvable Solvable::noSolvable;
100
101     /////////////////////////////////////////////////////////////////
102
103     ::_Solvable * Solvable::get() const
104     { return myPool().getSolvable( _id ); }
105
106 #define NO_SOLVABLE_RETURN( VAL ) \
107     ::_Solvable * _solvable( get() ); \
108     if ( ! _solvable ) return VAL
109
110     Solvable Solvable::nextInPool() const
111     { return Solvable( myPool().getNextId( _id ) ); }
112
113     Solvable Solvable::nextInRepo() const
114     {
115       NO_SOLVABLE_RETURN( noSolvable );
116       for ( detail::SolvableIdType next = _id+1; next < unsigned(_solvable->repo->end); ++next )
117       {
118         ::_Solvable * nextS( myPool().getSolvable( next ) );
119         if ( nextS && nextS->repo == _solvable->repo )
120         {
121           return Solvable( next );
122         }
123       }
124       return noSolvable;
125     }
126
127     Repository Solvable::repository() const
128     {
129       NO_SOLVABLE_RETURN( Repository::noRepository );
130       return Repository( _solvable->repo );
131     }
132
133     bool Solvable::isSystem() const
134     {
135       NO_SOLVABLE_RETURN( _id == detail::systemSolvableId );
136       return Repository( _solvable->repo ).isSystemRepo();
137     }
138
139     IdString Solvable::ident() const
140     {
141       NO_SOLVABLE_RETURN( IdString() );
142       return IdString( _solvable->name );
143     }
144
145     std::string Solvable::lookupStrAttribute( const SolvAttr & attr ) const
146     {
147       NO_SOLVABLE_RETURN( std::string() );
148       const char * s = ::solvable_lookup_str( _solvable, attr.id() );
149       return s ? s : std::string();
150     }
151
152     std::string Solvable::lookupStrAttribute( const SolvAttr & attr, const Locale & lang_r ) const
153     {
154       NO_SOLVABLE_RETURN( std::string() );
155       const char * s = 0;
156       if ( lang_r == Locale::noCode )
157       {
158         s = ::solvable_lookup_str_poollang( _solvable, attr.id() );
159       }
160       else
161       {
162         s = ::solvable_lookup_str_lang( _solvable, attr.id(), lang_r.code().c_str() );
163       }
164       return s ? s : std::string();
165    }
166
167     unsigned Solvable::lookupNumAttribute( const SolvAttr & attr ) const
168     {
169       NO_SOLVABLE_RETURN( 0 );
170       return ::solvable_lookup_num( _solvable, attr.id(), 0 );
171     }
172
173     bool Solvable::lookupBoolAttribute( const SolvAttr & attr ) const
174     {
175       NO_SOLVABLE_RETURN( false );
176       return ::solvable_lookup_bool( _solvable, attr.id() );
177     }
178
179     detail::IdType Solvable::lookupIdAttribute( const SolvAttr & attr ) const
180     {
181       NO_SOLVABLE_RETURN( detail::noId );
182       return ::solvable_lookup_id( _solvable, attr.id() );
183     }
184
185     CheckSum Solvable::lookupCheckSumAttribute( const SolvAttr & attr ) const
186     {
187       NO_SOLVABLE_RETURN( CheckSum() );
188       detail::IdType chksumtype = 0;
189       const char * s = ::solvable_lookup_checksum( _solvable, attr.id(), &chksumtype );
190       if ( ! s )
191         return CheckSum();
192       switch ( chksumtype )
193       {
194         case REPOKEY_TYPE_MD5:    return CheckSum::md5( s );
195         case REPOKEY_TYPE_SHA1:   return CheckSum::sha1( s );
196         case REPOKEY_TYPE_SHA256: return CheckSum::sha256( s );
197       }
198       return CheckSum( std::string(), s ); // try to autodetect
199     }
200
201     OnMediaLocation Solvable::lookupLocation() const
202     //std::string Solvable::lookupLocation( unsigned & medianr ) const
203     {
204       NO_SOLVABLE_RETURN( OnMediaLocation() );
205       // medianumber and path
206       unsigned medianr;
207       char * file = ::solvable_get_location( _solvable, &medianr );
208       if ( ! file )
209         return OnMediaLocation();
210
211       OnMediaLocation ret;
212
213       Pathname path;
214       if ( repository().info().type().toEnum() == repo::RepoType::YAST2_e )
215       {
216 #warning STILL HARDCODED /suse PREFIX in location
217         // (ma@) loading a susetags repo search for a solvable with attribute
218         // susetags:datadir. this is the prefix. store it in RepoInfo(?).
219         path = "suse";
220       }
221       ret.setLocation    ( path/file, medianr );
222       ret.setDownloadSize( ByteCount( lookupNumAttribute( SolvAttr::downloadsize ), ByteCount::K ) );
223       ret.setChecksum    ( lookupCheckSumAttribute( SolvAttr::checksum ) );
224       // Not needed/available for solvables?
225       //ret.setOpenSize    ( ByteCount( lookupNumAttribute( SolvAttr::opensize ), ByteCount::K ) );
226       //ret.setOpenChecksum( lookupCheckSumAttribute( SolvAttr::openchecksum ) );
227       return ret;
228     }
229
230     ResKind Solvable::kind() const
231     {
232       NO_SOLVABLE_RETURN( ResKind() );
233       // detect srcpackages by 'arch'
234       switch ( _solvable->arch )
235       {
236         case ARCH_SRC:
237         case ARCH_NOSRC:
238           return ResKind::srcpackage;
239           break;
240       }
241
242       const char * ident = IdString( _solvable->name ).c_str();
243       const char * sep = ::strchr( ident, ':' );
244
245       // no ':' in package names (hopefully)
246       if ( ! sep )
247         return ResKind::package;
248
249       // quick check for well known kinds
250       if ( sep-ident >= 4 )
251       {
252         switch ( ident[3] )
253         {
254 #define OUTS(K,S) if ( !::strncmp( ident, ResKind::K.c_str(), S ) ) return ResKind::K
255           //             ----v
256           case 'c': OUTS( patch, 5 );       break;
257           case 'd': OUTS( product, 7 );     break;
258           case 'i': OUTS( script, 6 );      break;
259           case 'k': OUTS( package, 7 );     break;
260           case 'm': OUTS( atom, 4 );        break;
261           case 'p': OUTS( srcpackage, 10 ); break;
262           case 's': OUTS( message, 7 );     break;
263           case 't': OUTS( pattern, 7 );     break;
264 #undef OUTS
265         }
266       }
267
268       // an unknown kind
269       return ResKind( std::string( ident, sep-ident ) );
270     }
271
272     bool Solvable::isKind( const ResKind & kind_r ) const
273     {
274       NO_SOLVABLE_RETURN( false );
275
276       // detect srcpackages by 'arch'
277       switch ( _solvable->arch )
278       {
279         case ARCH_SRC:
280         case ARCH_NOSRC:
281           return( kind_r == ResKind::srcpackage );
282           break;
283       }
284
285       // no ':' in package names (hopefully)
286       const char * ident = IdString( _solvable->name ).c_str();
287       if ( kind_r == ResKind::package )
288       {
289         return( ::strchr( ident, ':' ) == 0 );
290       }
291
292       // look for a 'kind:' prefix
293       const char * kind = kind_r.c_str();
294       unsigned     ksize = ::strlen( kind );
295       return( ::strncmp( ident, kind, ksize ) == 0
296               && ident[ksize] == ':' );
297     }
298
299     std::string Solvable::name() const
300     {
301       NO_SOLVABLE_RETURN( std::string() );
302       const char * ident = IdString( _solvable->name ).c_str();
303       const char * sep = ::strchr( ident, ':' );
304       return( sep ? sep+1 : ident );
305     }
306
307     Edition Solvable::edition() const
308     {
309       NO_SOLVABLE_RETURN( Edition() );
310       return Edition( _solvable->evr );
311     }
312
313     Arch Solvable::arch() const
314     {
315       NO_SOLVABLE_RETURN( Arch_noarch ); //ArchId() );
316       switch ( _solvable->arch )
317       {
318         case ARCH_SRC:
319         case ARCH_NOSRC:
320           return Arch_noarch; //ArchId( ARCH_NOARCH );
321           break;
322       }
323       return Arch( IdString(_solvable->arch).asString() );
324       //return ArchId( _solvable->arch );
325     }
326
327     IdString Solvable::vendor() const
328     {
329       NO_SOLVABLE_RETURN( IdString() );
330       return IdString( _solvable->vendor );
331     }
332
333     Capabilities Solvable::operator[]( Dep which_r ) const
334     {
335       switch( which_r.inSwitch() )
336       {
337         case Dep::PROVIDES_e:    return provides();    break;
338         case Dep::REQUIRES_e:    return requires();    break;
339         case Dep::CONFLICTS_e:   return conflicts();   break;
340         case Dep::OBSOLETES_e:   return obsoletes();   break;
341         case Dep::RECOMMENDS_e:  return recommends();  break;
342         case Dep::SUGGESTS_e:    return suggests();    break;
343         case Dep::FRESHENS_e:    return freshens();    break;
344         case Dep::ENHANCES_e:    return enhances();    break;
345         case Dep::SUPPLEMENTS_e: return supplements(); break;
346         case Dep::PREREQUIRES_e: return prerequires(); break;
347       }
348       return Capabilities();
349     }
350
351     inline Capabilities _getCapabilities( detail::IdType * idarraydata_r, ::Offset offs_r )
352     {
353       return offs_r ? Capabilities( idarraydata_r + offs_r ) : Capabilities();
354     }
355     Capabilities Solvable::provides() const
356     {
357       NO_SOLVABLE_RETURN( Capabilities() );
358       return _getCapabilities( _solvable->repo->idarraydata, _solvable->provides );
359     }
360     Capabilities Solvable::requires() const
361     {
362       NO_SOLVABLE_RETURN( Capabilities() );
363       return _getCapabilities( _solvable->repo->idarraydata, _solvable->requires );
364     }
365     Capabilities Solvable::conflicts() const
366     {
367       NO_SOLVABLE_RETURN( Capabilities() );
368       return _getCapabilities( _solvable->repo->idarraydata, _solvable->conflicts );
369     }
370     Capabilities Solvable::obsoletes() const
371     {
372       NO_SOLVABLE_RETURN( Capabilities() );
373       return _getCapabilities( _solvable->repo->idarraydata, _solvable->obsoletes );
374     }
375     Capabilities Solvable::recommends() const
376     {
377       NO_SOLVABLE_RETURN( Capabilities() );
378       return _getCapabilities( _solvable->repo->idarraydata, _solvable->recommends );
379     }
380     Capabilities Solvable::suggests() const
381     {
382       NO_SOLVABLE_RETURN( Capabilities() );
383       return _getCapabilities( _solvable->repo->idarraydata, _solvable->suggests );
384     }
385     Capabilities Solvable::freshens() const
386     {
387       NO_SOLVABLE_RETURN( Capabilities() );
388       return _getCapabilities( _solvable->repo->idarraydata, _solvable->freshens );
389     }
390     Capabilities Solvable::enhances() const
391     {
392       NO_SOLVABLE_RETURN( Capabilities() );
393       return _getCapabilities( _solvable->repo->idarraydata, _solvable->enhances );
394     }
395     Capabilities Solvable::supplements() const
396     {
397       NO_SOLVABLE_RETURN( Capabilities() );
398       return _getCapabilities( _solvable->repo->idarraydata, _solvable->supplements );
399     }
400     Capabilities Solvable::prerequires() const
401     {
402       NO_SOLVABLE_RETURN( Capabilities() );
403       // prerequires are a subset of requires
404        ::Offset offs = _solvable->requires;
405        return offs ? Capabilities( _solvable->repo->idarraydata + offs, detail::solvablePrereqMarker )
406                    : Capabilities();
407     }
408
409     ///////////////////////////////////////////////////////////////////
410     namespace
411     { /////////////////////////////////////////////////////////////////
412       /** Expand \ref Capability and call \c fnc_r for each namescpace:language
413        * dependency. Return #invocations of fnc_r, negative if fnc_r returned
414        * false to indicate abort.
415        */
416       int invokeOnEachSupportedLocale( Capability cap_r, function<bool (const Locale &)> fnc_r )
417       {
418         CapDetail detail( cap_r );
419         if ( detail.kind() == CapDetail::EXPRESSION )
420         {
421           switch ( detail.capRel() )
422           {
423             case CapDetail::CAP_AND:
424             case CapDetail::CAP_OR:
425                 // expand
426               {
427                 int res = invokeOnEachSupportedLocale( detail.lhs(), fnc_r );
428                 if ( res < 0 )
429                   return res; // negative on abort.
430                 int res2 = invokeOnEachSupportedLocale( detail.rhs(), fnc_r );
431                 if ( res2 < 0 )
432                   return -res + res2; // negative on abort.
433                 return res + res2;
434               }
435               break;
436
437             case CapDetail::CAP_NAMESPACE:
438               if ( detail.lhs().id() == NAMESPACE_LANGUAGE )
439               {
440                 return ( !fnc_r || fnc_r( Locale( IdString(detail.rhs().id()) ) ) ) ? 1 : -1; // negative on abort.
441               }
442               break;
443
444             case CapDetail::REL_NONE:
445             case CapDetail::CAP_WITH:
446             case CapDetail::CAP_ARCH:
447               break; // unwanted
448           }
449         }
450         return 0;
451       }
452
453        /** Expand \ref Capability and call \c fnc_r for each namescpace:language
454        * dependency. Return #invocations of fnc_r, negative if fnc_r returned
455        * false to indicate abort.
456        */
457       inline int invokeOnEachSupportedLocale( Capabilities cap_r, function<bool (const Locale &)> fnc_r )
458       {
459         int cnt = 0;
460         for_( cit, cap_r.begin(), cap_r.end() )
461         {
462           int res = invokeOnEachSupportedLocale( *cit, fnc_r );
463           if ( res < 0 )
464             return -cnt + res; // negative on abort.
465           cnt += res;
466         }
467         return cnt;
468       }
469       //@}
470
471       // Functor returning false if a Locale is in the set.
472       struct NoMatchIn
473       {
474         NoMatchIn( const LocaleSet & locales_r ) : _locales( locales_r ) {}
475
476         bool operator()( const Locale & locale_r ) const
477         {
478           return _locales.find( locale_r ) == _locales.end();
479         }
480
481         const LocaleSet & _locales;
482       };
483
484     } /////////////////////////////////////////////////////////////////
485
486     bool Solvable::supportsLocales() const
487     {
488       // false_c stops on 1st Locale.
489       return invokeOnEachSupportedLocale( supplements(), functor::false_c() ) < 0;
490     }
491
492     bool Solvable::supportsLocale( const Locale & locale_r ) const
493     {
494       // not_equal_to stops on == Locale.
495       return invokeOnEachSupportedLocale( supplements(), bind( std::not_equal_to<Locale>(), locale_r, _1 ) ) < 0;
496     }
497
498     bool Solvable::supportsLocale( const LocaleSet & locales_r ) const
499     {
500       if ( locales_r.empty() )
501         return false;
502       // NoMatchIn stops if Locale is included.
503       return invokeOnEachSupportedLocale( supplements(), NoMatchIn(locales_r) ) < 0;
504     }
505
506     bool Solvable::supportsRequestedLocales() const
507     { return supportsLocale( myPool().getRequestedLocales() ); }
508
509     void Solvable::getSupportedLocales( LocaleSet & locales_r ) const
510     {
511       invokeOnEachSupportedLocale( supplements(),
512                                    functor::Collector( std::inserter( locales_r, locales_r.begin() ) ) );
513     }
514
515     /******************************************************************
516     **
517     **  FUNCTION NAME : operator<<
518     **  FUNCTION TYPE : std::ostream &
519     */
520     std::ostream & operator<<( std::ostream & str, const Solvable & obj )
521     {
522       if ( ! obj )
523         return str << (obj.isSystem() ? "systemSolvable" : "noSolvable" );
524
525       return str << "(" << obj.id() << ")"
526           << ( obj.isKind( ResKind::srcpackage ) ? "srcpackage:" : "" ) << obj.ident()
527           << '-' << obj.edition() << '.' << obj.arch() << "("
528           << obj.repository().name() << ")";
529     }
530
531     /******************************************************************
532     **
533     **  FUNCTION NAME : dumpOn
534     **  FUNCTION TYPE : std::ostream &
535     */
536     std::ostream & dumpOn( std::ostream & str, const Solvable & obj )
537     {
538       str << obj;
539       if ( obj )
540       {
541 #define OUTS(X) if ( ! obj[Dep::X].empty() ) str << endl << " " #X " " << obj[Dep::X]
542         OUTS(PROVIDES);
543         OUTS(PREREQUIRES);
544         OUTS(REQUIRES);
545         OUTS(CONFLICTS);
546         OUTS(OBSOLETES);
547         OUTS(RECOMMENDS);
548         OUTS(SUGGESTS);
549         OUTS(FRESHENS);
550         OUTS(ENHANCES);
551         OUTS(SUPPLEMENTS);
552 #undef OUTS
553       }
554       return str;
555     }
556
557     /////////////////////////////////////////////////////////////////
558   } // namespace sat
559   ///////////////////////////////////////////////////////////////////
560   /////////////////////////////////////////////////////////////////
561 } // namespace zypp
562 ///////////////////////////////////////////////////////////////////