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