Imported Upstream version 14.45.0
[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 #include "zypp/base/Xml.h"
20
21 #include "zypp/sat/detail/PoolImpl.h"
22 #include "zypp/sat/Solvable.h"
23 #include "zypp/sat/Pool.h"
24 #include "zypp/sat/LookupAttr.h"
25
26 #include "zypp/Repository.h"
27 #include "zypp/OnMediaLocation.h"
28 #include "zypp/ZConfig.h"
29
30 using std::endl;
31
32 ///////////////////////////////////////////////////////////////////
33 namespace zypp
34 { /////////////////////////////////////////////////////////////////
35   ///////////////////////////////////////////////////////////////////
36   namespace sat
37   { /////////////////////////////////////////////////////////////////
38
39     namespace
40     {
41       void _doSplit( IdString & _ident, ResKind & _kind, IdString & _name )
42       {
43         if ( ! _ident )
44           return;
45
46         ResKind explicitKind = ResKind::explicitBuiltin( _ident.c_str() );
47         // NOTE: kind package and srcpackage do not have namespaced ident!
48         if ( ! explicitKind  )
49         {
50           _name = _ident;
51           // No kind defaults to package
52           if ( !_kind )
53             _kind = ResKind::package;
54           else if ( ! ( _kind == ResKind::package || _kind == ResKind::srcpackage ) )
55             _ident = IdString( str::form( "%s:%s", _kind.c_str(), _ident.c_str() ) );
56         }
57         else
58         {
59           // strip kind spec from name
60           _name = IdString( ::strchr( _ident.c_str(), ':' )+1 );
61           _kind = explicitKind;
62           if ( _kind == ResKind::package || _kind == ResKind::srcpackage )
63             _ident = _name;
64         }
65         return;
66       }
67     }
68
69     Solvable::SplitIdent::SplitIdent( IdString ident_r )
70     : _ident( ident_r )
71     { _doSplit( _ident, _kind, _name ); }
72
73     Solvable::SplitIdent::SplitIdent( const char * ident_r )
74     : _ident( ident_r )
75     { _doSplit( _ident, _kind, _name ); }
76
77     Solvable::SplitIdent::SplitIdent( const std::string & ident_r )
78     : _ident( ident_r )
79     { _doSplit( _ident, _kind, _name ); }
80
81     Solvable::SplitIdent::SplitIdent( ResKind kind_r, IdString name_r )
82     : _ident( name_r )
83     , _kind( kind_r )
84     { _doSplit( _ident, _kind, _name ); }
85
86     Solvable::SplitIdent::SplitIdent( ResKind kind_r, const C_Str & name_r )
87     : _ident( name_r )
88     , _kind( kind_r )
89     { _doSplit( _ident, _kind, _name ); }
90
91     /////////////////////////////////////////////////////////////////
92
93     const Solvable Solvable::noSolvable;
94
95     /////////////////////////////////////////////////////////////////
96
97     ::_Solvable * Solvable::get() const
98     { return myPool().getSolvable( _id ); }
99
100 #define NO_SOLVABLE_RETURN( VAL ) \
101     ::_Solvable * _solvable( get() ); \
102     if ( ! _solvable ) return VAL
103
104     Solvable Solvable::nextInPool() const
105     { return Solvable( myPool().getNextId( _id ) ); }
106
107     Solvable Solvable::nextInRepo() const
108     {
109       NO_SOLVABLE_RETURN( noSolvable );
110       for ( detail::SolvableIdType next = _id+1; next < unsigned(_solvable->repo->end); ++next )
111       {
112         ::_Solvable * nextS( myPool().getSolvable( next ) );
113         if ( nextS && nextS->repo == _solvable->repo )
114         {
115           return Solvable( next );
116         }
117       }
118       return noSolvable;
119     }
120
121     Repository Solvable::repository() const
122     {
123       NO_SOLVABLE_RETURN( Repository::noRepository );
124       return Repository( _solvable->repo );
125     }
126
127     bool Solvable::isSystem() const
128     {
129       NO_SOLVABLE_RETURN( _id == detail::systemSolvableId );
130       return myPool().isSystemRepo( _solvable->repo );
131     }
132
133     bool Solvable::onSystemByUser() const
134     {
135       return isSystem() && myPool().isOnSystemByUser( ident() );
136     }
137
138     IdString Solvable::ident() const
139     {
140       NO_SOLVABLE_RETURN( IdString() );
141       return IdString( _solvable->name );
142     }
143
144     std::string Solvable::lookupStrAttribute( const SolvAttr & attr ) const
145     {
146       NO_SOLVABLE_RETURN( std::string() );
147       const char * s = ::solvable_lookup_str( _solvable, attr.id() );
148       return s ? s : std::string();
149     }
150
151     std::string Solvable::lookupStrAttribute( const SolvAttr & attr, const Locale & lang_r ) const
152     {
153       NO_SOLVABLE_RETURN( std::string() );
154       const char * s = 0;
155       if ( lang_r == Locale::noCode )
156       {
157         s = ::solvable_lookup_str_poollang( _solvable, attr.id() );
158       }
159       else
160       {
161         for ( Locale l( lang_r ); l != Locale::noCode; l = l.fallback() )
162           if ( (s = ::solvable_lookup_str_lang( _solvable, attr.id(), l.code().c_str(), 0 )) )
163             return s;
164           // here: no matching locale, so use default
165           s = ::solvable_lookup_str_lang( _solvable, attr.id(), 0, 0 );
166       }
167       return s ? s : std::string();
168    }
169
170     unsigned long long Solvable::lookupNumAttribute( const SolvAttr & attr ) const
171     {
172       NO_SOLVABLE_RETURN( 0 );
173       return ::solvable_lookup_num( _solvable, attr.id(), 0 );
174     }
175
176     bool Solvable::lookupBoolAttribute( const SolvAttr & attr ) const
177     {
178       NO_SOLVABLE_RETURN( false );
179       return ::solvable_lookup_bool( _solvable, attr.id() );
180     }
181
182     detail::IdType Solvable::lookupIdAttribute( const SolvAttr & attr ) const
183     {
184       NO_SOLVABLE_RETURN( detail::noId );
185       return ::solvable_lookup_id( _solvable, attr.id() );
186     }
187
188     CheckSum Solvable::lookupCheckSumAttribute( const SolvAttr & attr ) const
189     {
190       NO_SOLVABLE_RETURN( CheckSum() );
191       detail::IdType chksumtype = 0;
192       const char * s = ::solvable_lookup_checksum( _solvable, attr.id(), &chksumtype );
193       if ( ! s )
194         return CheckSum();
195       switch ( chksumtype )
196       {
197         case REPOKEY_TYPE_MD5:    return CheckSum::md5( s );
198         case REPOKEY_TYPE_SHA1:   return CheckSum::sha1( s );
199         case REPOKEY_TYPE_SHA224: return CheckSum::sha224( s );
200         case REPOKEY_TYPE_SHA256: return CheckSum::sha256( s );
201         case REPOKEY_TYPE_SHA384: return CheckSum::sha384( s );
202         case REPOKEY_TYPE_SHA512: return CheckSum::sha512( s );
203       }
204       return CheckSum( std::string(), s ); // try to autodetect
205     }
206
207     ///////////////////////////////////////////////////////////////////
208     namespace
209     {
210       inline Pathname lookupDatadirIn( Repository repor_r )
211       {
212         static const sat::SolvAttr susetagsDatadir( "susetags:datadir" );
213         Pathname ret;
214         // First look for repo attribute "susetags:datadir". If not found,
215         // look into the solvables as Code11 libsolv placed it there.
216         sat::LookupRepoAttr datadir( susetagsDatadir, repor_r );
217         if ( ! datadir.empty() )
218           ret = datadir.begin().asString();
219         else
220         {
221           sat::LookupAttr datadir( susetagsDatadir, repor_r );
222           if ( ! datadir.empty() )
223             ret = datadir.begin().asString();
224         }
225         return ret;
226       }
227     }
228     ///////////////////////////////////////////////////////////////////
229
230     OnMediaLocation Solvable::lookupLocation() const
231     {
232       NO_SOLVABLE_RETURN( OnMediaLocation() );
233       // medianumber and path
234       unsigned medianr;
235       const char * file = ::solvable_lookup_location( _solvable, &medianr );
236       if ( ! file )
237         return OnMediaLocation();
238       if ( ! medianr )
239         medianr = 1;
240
241       OnMediaLocation ret;
242
243       Pathname path;
244       switch ( repository().info().type().toEnum() )
245       {
246         case repo::RepoType::NONE_e:
247         {
248           path = lookupDatadirIn( repository() );
249           if ( ! path.empty() )
250             repository().info().setProbedType( repo::RepoType::YAST2_e );
251         }
252         break;
253
254         case repo::RepoType::YAST2_e:
255         {
256           path = lookupDatadirIn( repository() );
257           if ( path.empty() )
258             path = "suse";
259         }
260         break;
261
262         default:
263           break;
264       }
265       ret.setLocation    ( path/file, medianr );
266       ret.setDownloadSize( ByteCount( lookupNumAttribute( SolvAttr::downloadsize ) ) );
267       ret.setChecksum    ( lookupCheckSumAttribute( SolvAttr::checksum ) );
268       // Not needed/available for solvables?
269       //ret.setOpenSize    ( ByteCount( lookupNumAttribute( SolvAttr::opensize ) ) );
270       //ret.setOpenChecksum( lookupCheckSumAttribute( SolvAttr::openchecksum ) );
271       return ret;
272     }
273
274     ResKind Solvable::kind() const
275     {
276       NO_SOLVABLE_RETURN( ResKind() );
277       // detect srcpackages by 'arch'
278       switch ( _solvable->arch )
279       {
280         case ARCH_SRC:
281         case ARCH_NOSRC:
282           return ResKind::srcpackage;
283           break;
284       }
285
286       // either explicitly prefixed...
287       const char * ident = IdString( _solvable->name ).c_str();
288       ResKind knownKind( ResKind::explicitBuiltin( ident ) );
289       if ( knownKind )
290         return knownKind;
291
292       // ...or no ':' in package names (hopefully)...
293       const char * sep = ::strchr( ident, ':' );
294       if ( ! sep )
295         return ResKind::package;
296
297       // ...or something unknown.
298       return ResKind( std::string( ident, sep-ident ) );
299     }
300
301     bool Solvable::isKind( const ResKind & kind_r ) const
302     {
303       NO_SOLVABLE_RETURN( false );
304
305       // detect srcpackages by 'arch'
306       switch ( _solvable->arch )
307       {
308         case ARCH_SRC:
309         case ARCH_NOSRC:
310           return( kind_r == ResKind::srcpackage );
311           break;
312       }
313
314       // no ':' in package names (hopefully)
315       const char * ident = IdString( _solvable->name ).c_str();
316       if ( kind_r == ResKind::package )
317       {
318         return( ::strchr( ident, ':' ) == 0 );
319       }
320
321       // look for a 'kind:' prefix
322       const char * kind = kind_r.c_str();
323       unsigned     ksize = ::strlen( kind );
324       return( ::strncmp( ident, kind, ksize ) == 0
325               && ident[ksize] == ':' );
326     }
327
328     std::string Solvable::name() const
329     {
330       NO_SOLVABLE_RETURN( std::string() );
331       const char * ident = IdString( _solvable->name ).c_str();
332       const char * sep = ::strchr( ident, ':' );
333       return( sep ? sep+1 : ident );
334     }
335
336     Edition Solvable::edition() const
337     {
338       NO_SOLVABLE_RETURN( Edition() );
339       return Edition( _solvable->evr );
340     }
341
342     Arch Solvable::arch() const
343     {
344       NO_SOLVABLE_RETURN( Arch_noarch ); //ArchId() );
345       switch ( _solvable->arch )
346       {
347         case ARCH_SRC:
348         case ARCH_NOSRC:
349           return Arch_noarch; //ArchId( ARCH_NOARCH );
350           break;
351       }
352       return Arch( IdString(_solvable->arch).asString() );
353       //return ArchId( _solvable->arch );
354     }
355
356     bool Solvable::multiversionInstall() const
357     {
358       return myPool().isMultiversion( ident() );
359     }
360
361     IdString Solvable::vendor() const
362     {
363       NO_SOLVABLE_RETURN( IdString() );
364       return IdString( _solvable->vendor );
365     }
366
367     Capabilities Solvable::operator[]( Dep which_r ) const
368     {
369       switch( which_r.inSwitch() )
370       {
371         case Dep::PROVIDES_e:    return provides();    break;
372         case Dep::REQUIRES_e:    return requires();    break;
373         case Dep::CONFLICTS_e:   return conflicts();   break;
374         case Dep::OBSOLETES_e:   return obsoletes();   break;
375         case Dep::RECOMMENDS_e:  return recommends();  break;
376         case Dep::SUGGESTS_e:    return suggests();    break;
377         case Dep::ENHANCES_e:    return enhances();    break;
378         case Dep::SUPPLEMENTS_e: return supplements(); break;
379         case Dep::PREREQUIRES_e: return prerequires(); break;
380       }
381       return Capabilities();
382     }
383
384     inline Capabilities _getCapabilities( detail::IdType * idarraydata_r, ::Offset offs_r )
385     {
386       return offs_r ? Capabilities( idarraydata_r + offs_r ) : Capabilities();
387     }
388     Capabilities Solvable::provides() const
389     {
390       NO_SOLVABLE_RETURN( Capabilities() );
391       return _getCapabilities( _solvable->repo->idarraydata, _solvable->provides );
392     }
393     Capabilities Solvable::requires() const
394     {
395       NO_SOLVABLE_RETURN( Capabilities() );
396       return _getCapabilities( _solvable->repo->idarraydata, _solvable->requires );
397     }
398     Capabilities Solvable::conflicts() const
399     {
400       NO_SOLVABLE_RETURN( Capabilities() );
401       return _getCapabilities( _solvable->repo->idarraydata, _solvable->conflicts );
402     }
403     Capabilities Solvable::obsoletes() const
404     {
405       NO_SOLVABLE_RETURN( Capabilities() );
406       return _getCapabilities( _solvable->repo->idarraydata, _solvable->obsoletes );
407     }
408     Capabilities Solvable::recommends() const
409     {
410       NO_SOLVABLE_RETURN( Capabilities() );
411       return _getCapabilities( _solvable->repo->idarraydata, _solvable->recommends );
412     }
413     Capabilities Solvable::suggests() const
414     {
415       NO_SOLVABLE_RETURN( Capabilities() );
416       return _getCapabilities( _solvable->repo->idarraydata, _solvable->suggests );
417     }
418     Capabilities Solvable::enhances() const
419     {
420       NO_SOLVABLE_RETURN( Capabilities() );
421       return _getCapabilities( _solvable->repo->idarraydata, _solvable->enhances );
422     }
423     Capabilities Solvable::supplements() const
424     {
425       NO_SOLVABLE_RETURN( Capabilities() );
426       return _getCapabilities( _solvable->repo->idarraydata, _solvable->supplements );
427     }
428     Capabilities Solvable::prerequires() const
429     {
430       NO_SOLVABLE_RETURN( Capabilities() );
431       // prerequires are a subset of requires
432        ::Offset offs = _solvable->requires;
433        return offs ? Capabilities( _solvable->repo->idarraydata + offs, detail::solvablePrereqMarker )
434                    : Capabilities();
435     }
436
437     CapabilitySet Solvable::providesNamespace( const std::string & namespace_r ) const
438     {
439       NO_SOLVABLE_RETURN( CapabilitySet() );
440       CapabilitySet ret;
441       Capabilities caps( provides() );
442       for_( it, caps.begin(), caps.end() )
443       {
444         CapDetail caprep( it->detail() );
445         if ( str::hasPrefix( caprep.name().c_str(), namespace_r ) && *(caprep.name().c_str()+namespace_r.size()) == '(' )
446           ret.insert( *it );
447       }
448       return ret;
449    }
450
451     CapabilitySet Solvable::valuesOfNamespace( const std::string & namespace_r ) const
452     {
453       NO_SOLVABLE_RETURN( CapabilitySet() );
454       CapabilitySet ret;
455       Capabilities caps( provides() );
456       for_( it, caps.begin(), caps.end() )
457       {
458         CapDetail caprep( it->detail() );
459         if ( str::hasPrefix( caprep.name().c_str(), namespace_r ) && *(caprep.name().c_str()+namespace_r.size()) == '(' )
460         {
461           std::string value( caprep.name().c_str()+namespace_r.size()+1 );
462           value[value.size()-1] = '\0'; // erase the trailing ')'
463           ret.insert( Capability( value, caprep.op(), caprep.ed() ) );
464         }
465       }
466       return ret;
467     }
468
469
470     std::string Solvable::asString() const
471     {
472       NO_SOLVABLE_RETURN( (_id == detail::systemSolvableId ? "systemSolvable" : "noSolvable") );
473       return str::form( "%s-%s.%s",
474                         IdString( _solvable->name ).c_str(),
475                         IdString( _solvable->evr ).c_str(),
476                         IdString( _solvable->arch ).c_str() );
477     }
478
479     std::string Solvable::asUserString() const\
480     {
481       NO_SOLVABLE_RETURN( (_id == detail::systemSolvableId ? "systemSolvable" : "noSolvable") );
482       return str::form( "%s-%s.%s (%s)",
483                         IdString( _solvable->name ).c_str(),
484                         IdString( _solvable->evr ).c_str(),
485                         IdString( _solvable->arch ).c_str(),
486                         repository().asUserString().c_str() );
487     }
488
489     bool Solvable::identical( Solvable rhs ) const
490     {
491       NO_SOLVABLE_RETURN( ! rhs.get() );
492       ::_Solvable * rhssolvable( rhs.get() );
493       return rhssolvable && ( _solvable == rhssolvable || ::solvable_identical( _solvable, rhssolvable ) );
494     }
495
496     ///////////////////////////////////////////////////////////////////
497     namespace
498     { /////////////////////////////////////////////////////////////////
499       /** Expand \ref Capability and call \c fnc_r for each namescpace:language
500        * dependency. Return #invocations of fnc_r, negative if fnc_r returned
501        * false to indicate abort.
502        */
503       int invokeOnEachSupportedLocale( Capability cap_r, function<bool (const Locale &)> fnc_r )
504       {
505         CapDetail detail( cap_r );
506         if ( detail.kind() == CapDetail::EXPRESSION )
507         {
508           switch ( detail.capRel() )
509           {
510             case CapDetail::CAP_AND:
511             case CapDetail::CAP_OR:
512                 // expand
513               {
514                 int res = invokeOnEachSupportedLocale( detail.lhs(), fnc_r );
515                 if ( res < 0 )
516                   return res; // negative on abort.
517                 int res2 = invokeOnEachSupportedLocale( detail.rhs(), fnc_r );
518                 if ( res2 < 0 )
519                   return -res + res2; // negative on abort.
520                 return res + res2;
521               }
522               break;
523
524             case CapDetail::CAP_NAMESPACE:
525               if ( detail.lhs().id() == NAMESPACE_LANGUAGE )
526               {
527                 return ( !fnc_r || fnc_r( Locale( IdString(detail.rhs().id()) ) ) ) ? 1 : -1; // negative on abort.
528               }
529               break;
530
531             case CapDetail::REL_NONE:
532             case CapDetail::CAP_WITH:
533             case CapDetail::CAP_ARCH:
534               break; // unwanted
535           }
536         }
537         return 0;
538       }
539
540        /** Expand \ref Capability and call \c fnc_r for each namescpace:language
541        * dependency. Return #invocations of fnc_r, negative if fnc_r returned
542        * false to indicate abort.
543        */
544       inline int invokeOnEachSupportedLocale( Capabilities cap_r, function<bool (const Locale &)> fnc_r )
545       {
546         int cnt = 0;
547         for_( cit, cap_r.begin(), cap_r.end() )
548         {
549           int res = invokeOnEachSupportedLocale( *cit, fnc_r );
550           if ( res < 0 )
551             return -cnt + res; // negative on abort.
552           cnt += res;
553         }
554         return cnt;
555       }
556       //@}
557
558       // Functor returning false if a Locale is in the set.
559       struct NoMatchIn
560       {
561         NoMatchIn( const LocaleSet & locales_r ) : _locales( locales_r ) {}
562
563         bool operator()( const Locale & locale_r ) const
564         {
565           return _locales.find( locale_r ) == _locales.end();
566         }
567
568         const LocaleSet & _locales;
569       };
570
571     } /////////////////////////////////////////////////////////////////
572
573     bool Solvable::supportsLocales() const
574     {
575       // false_c stops on 1st Locale.
576       return invokeOnEachSupportedLocale( supplements(), functor::false_c() ) < 0;
577     }
578
579     bool Solvable::supportsLocale( const Locale & locale_r ) const
580     {
581       // not_equal_to stops on == Locale.
582       return invokeOnEachSupportedLocale( supplements(), bind( std::not_equal_to<Locale>(), locale_r, _1 ) ) < 0;
583     }
584
585     bool Solvable::supportsLocale( const LocaleSet & locales_r ) const
586     {
587       if ( locales_r.empty() )
588         return false;
589       // NoMatchIn stops if Locale is included.
590       return invokeOnEachSupportedLocale( supplements(), NoMatchIn(locales_r) ) < 0;
591     }
592
593     bool Solvable::supportsRequestedLocales() const
594     { return supportsLocale( myPool().getRequestedLocales() ); }
595
596     void Solvable::getSupportedLocales( LocaleSet & locales_r ) const
597     {
598       invokeOnEachSupportedLocale( supplements(),
599                                    functor::Collector( std::inserter( locales_r, locales_r.begin() ) ) );
600     }
601
602     /******************************************************************
603     **
604     **  FUNCTION NAME : operator<<
605     **  FUNCTION TYPE : std::ostream &
606     */
607     std::ostream & operator<<( std::ostream & str, const Solvable & obj )
608     {
609       if ( ! obj )
610         return str << (obj.isSystem() ? "systemSolvable" : "noSolvable" );
611
612       return str << "(" << obj.id() << ")"
613           << ( obj.isKind( ResKind::srcpackage ) ? "srcpackage:" : "" ) << obj.ident()
614           << '-' << obj.edition() << '.' << obj.arch() << "("
615           << obj.repository().alias() << ")";
616     }
617
618     /******************************************************************
619     **
620     **  FUNCTION NAME : dumpOn
621     **  FUNCTION TYPE : std::ostream &
622     */
623     std::ostream & dumpOn( std::ostream & str, const Solvable & obj )
624     {
625       str << obj;
626       if ( obj )
627       {
628 #define OUTS(X) if ( ! obj[Dep::X].empty() ) str << endl << " " #X " " << obj[Dep::X]
629         OUTS(PROVIDES);
630         OUTS(PREREQUIRES);
631         OUTS(REQUIRES);
632         OUTS(CONFLICTS);
633         OUTS(OBSOLETES);
634         OUTS(RECOMMENDS);
635         OUTS(SUGGESTS);
636         OUTS(ENHANCES);
637         OUTS(SUPPLEMENTS);
638 #undef OUTS
639       }
640       return str;
641     }
642
643     std::ostream & dumpAsXmlOn( std::ostream & str, const Solvable & obj )
644     {
645       xmlout::Node guard( str, "solvable" );
646
647       dumpAsXmlOn( *guard, obj.kind() );
648       *xmlout::Node( *guard, "name" ) << obj.name();
649       dumpAsXmlOn( *guard, obj.edition() );
650       dumpAsXmlOn( *guard, obj.arch() );
651       dumpAsXmlOn( *guard, obj.repository() );
652       return str;
653     }
654
655     /////////////////////////////////////////////////////////////////
656   } // namespace sat
657   ///////////////////////////////////////////////////////////////////
658   /////////////////////////////////////////////////////////////////
659 } // namespace zypp
660 ///////////////////////////////////////////////////////////////////