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