Always evaluate locale fallbacks when retrieving translated strings.
[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/sat/LookupAttr.h"
24
25 #include "zypp/Repository.h"
26 #include "zypp/OnMediaLocation.h"
27 #include "zypp/ZConfig.h"
28
29 using std::endl;
30
31 ///////////////////////////////////////////////////////////////////
32 namespace zypp
33 { /////////////////////////////////////////////////////////////////
34   ///////////////////////////////////////////////////////////////////
35   namespace sat
36   { /////////////////////////////////////////////////////////////////
37
38     namespace
39     {
40       void _doSplit( IdString & _ident, ResKind & _kind, IdString & _name )
41       {
42         if ( ! _ident )
43           return;
44
45         const char * ident = _ident.c_str();
46         const char * sep = ::strchr( ident, ':' );
47
48         // no ':' in package names (hopefully)
49         if ( ! sep )
50         {
51           _kind = ResKind::package;
52           _name = _ident;
53           return;
54         }
55
56         // save name
57         _name = IdString( sep+1 );
58
59         // Quick check for well known kinds.
60         // NOTE: kind package and srcpackage do not
61         //       have namespaced ident!
62         if ( sep-ident >= 4 )
63         {
64           switch ( ident[3] )
65           {
66 #define OUTS(K,S) if ( !::strncmp( ident, ResKind::K.c_str(), S ) ) _kind = ResKind::K
67           //             ----v
68             case 'c': OUTS( patch, 5 );       return; break;
69             case 'd': OUTS( product, 7 );     return; break;
70             case 'k': OUTS( package, 7 );     _ident = _name; return; break;
71             case 'p': OUTS( srcpackage, 10 ); _ident = _name; return; break;
72             case 't': OUTS( pattern, 7 );     return; break;
73 #undef OUTS
74           }
75         }
76
77         // an unknown kind
78         _kind = ResKind( std::string( ident, sep-ident ) );
79       }
80     }
81
82     Solvable::SplitIdent::SplitIdent( IdString ident_r )
83     : _ident( ident_r )
84     { _doSplit( _ident, _kind, _name ); }
85
86     Solvable::SplitIdent::SplitIdent( const char * ident_r )
87     : _ident( ident_r )
88     { _doSplit( _ident, _kind, _name ); }
89
90     Solvable::SplitIdent::SplitIdent( const std::string & ident_r )
91     : _ident( ident_r )
92     { _doSplit( _ident, _kind, _name ); }
93
94     Solvable::SplitIdent::SplitIdent( ResKind kind_r, IdString name_r )
95     : _kind( kind_r )
96     , _name( name_r )
97     {
98       if ( kind_r == ResKind::package || kind_r == ResKind::srcpackage )
99         _ident = _name;
100       else
101         _ident = IdString( str::form( "%s:%s", kind_r.c_str(), name_r.c_str() ) );
102     }
103
104     Solvable::SplitIdent::SplitIdent( ResKind kind_r, const C_Str & name_r )
105     : _kind( kind_r )
106     , _name( name_r )
107     {
108       if ( kind_r == ResKind::package || kind_r == ResKind::srcpackage )
109         _ident = _name;
110       else
111         _ident = IdString( str::form( "%s:%s", kind_r.c_str(), name_r.c_str() ) );
112     }
113
114     /////////////////////////////////////////////////////////////////
115
116     const Solvable Solvable::noSolvable;
117
118     /////////////////////////////////////////////////////////////////
119
120     ::_Solvable * Solvable::get() const
121     { return myPool().getSolvable( _id ); }
122
123 #define NO_SOLVABLE_RETURN( VAL ) \
124     ::_Solvable * _solvable( get() ); \
125     if ( ! _solvable ) return VAL
126
127     Solvable Solvable::nextInPool() const
128     { return Solvable( myPool().getNextId( _id ) ); }
129
130     Solvable Solvable::nextInRepo() const
131     {
132       NO_SOLVABLE_RETURN( noSolvable );
133       for ( detail::SolvableIdType next = _id+1; next < unsigned(_solvable->repo->end); ++next )
134       {
135         ::_Solvable * nextS( myPool().getSolvable( next ) );
136         if ( nextS && nextS->repo == _solvable->repo )
137         {
138           return Solvable( next );
139         }
140       }
141       return noSolvable;
142     }
143
144     Repository Solvable::repository() const
145     {
146       NO_SOLVABLE_RETURN( Repository::noRepository );
147       return Repository( _solvable->repo );
148     }
149
150     bool Solvable::isSystem() const
151     {
152       NO_SOLVABLE_RETURN( _id == detail::systemSolvableId );
153       return myPool().isSystemRepo( _solvable->repo );
154     }
155
156     IdString Solvable::ident() const
157     {
158       NO_SOLVABLE_RETURN( IdString() );
159       return IdString( _solvable->name );
160     }
161
162     std::string Solvable::lookupStrAttribute( const SolvAttr & attr ) const
163     {
164       NO_SOLVABLE_RETURN( std::string() );
165       const char * s = ::solvable_lookup_str( _solvable, attr.id() );
166       return s ? s : std::string();
167     }
168
169     std::string Solvable::lookupStrAttribute( const SolvAttr & attr, const Locale & lang_r ) const
170     {
171       NO_SOLVABLE_RETURN( std::string() );
172       const char * s = 0;
173       if ( lang_r == Locale::noCode )
174       {
175         s = ::solvable_lookup_str_poollang( _solvable, attr.id() );
176       }
177       else
178       {
179         for ( Locale l( lang_r ); l != Locale::noCode; l = l.fallback() )
180           if ( (s = ::solvable_lookup_str_lang( _solvable, attr.id(), l.code().c_str(), 0 )) )
181             return s;
182           // here: no matching locale, so use default
183           s = ::solvable_lookup_str_lang( _solvable, attr.id(), 0, 0 );
184       }
185       return s ? s : std::string();
186    }
187
188     unsigned Solvable::lookupNumAttribute( const SolvAttr & attr ) const
189     {
190       NO_SOLVABLE_RETURN( 0 );
191       return ::solvable_lookup_num( _solvable, attr.id(), 0 );
192     }
193
194     bool Solvable::lookupBoolAttribute( const SolvAttr & attr ) const
195     {
196       NO_SOLVABLE_RETURN( false );
197       return ::solvable_lookup_bool( _solvable, attr.id() );
198     }
199
200     detail::IdType Solvable::lookupIdAttribute( const SolvAttr & attr ) const
201     {
202       NO_SOLVABLE_RETURN( detail::noId );
203       return ::solvable_lookup_id( _solvable, attr.id() );
204     }
205
206     CheckSum Solvable::lookupCheckSumAttribute( const SolvAttr & attr ) const
207     {
208       NO_SOLVABLE_RETURN( CheckSum() );
209       detail::IdType chksumtype = 0;
210       const char * s = ::solvable_lookup_checksum( _solvable, attr.id(), &chksumtype );
211       if ( ! s )
212         return CheckSum();
213       switch ( chksumtype )
214       {
215         case REPOKEY_TYPE_MD5:    return CheckSum::md5( s );
216         case REPOKEY_TYPE_SHA1:   return CheckSum::sha1( s );
217         case REPOKEY_TYPE_SHA256: return CheckSum::sha256( s );
218       }
219       return CheckSum( std::string(), s ); // try to autodetect
220     }
221
222     ///////////////////////////////////////////////////////////////////
223     namespace
224     {
225       inline Pathname lookupDatadirIn( Repository repor_r )
226       {
227         static const sat::SolvAttr susetagsDatadir( "susetags:datadir" );
228         Pathname ret;
229         // First look for repo attribute "susetags:datadir". If not found,
230         // look into the solvables as Code11 satsolver placed it there.
231         sat::LookupRepoAttr datadir( susetagsDatadir, repor_r );
232         if ( ! datadir.empty() )
233           ret = datadir.begin().asString();
234         else
235         {
236           sat::LookupAttr datadir( susetagsDatadir, repor_r );
237           if ( ! datadir.empty() )
238             ret = datadir.begin().asString();
239         }
240         return ret;
241       }
242     }
243     ///////////////////////////////////////////////////////////////////
244
245     OnMediaLocation Solvable::lookupLocation() const
246     {
247       NO_SOLVABLE_RETURN( OnMediaLocation() );
248       // medianumber and path
249       unsigned medianr;
250       char * file = ::solvable_get_location( _solvable, &medianr );
251       if ( ! file )
252         return OnMediaLocation();
253
254       OnMediaLocation ret;
255
256       Pathname path;
257       switch ( repository().info().type().toEnum() )
258       {
259         case repo::RepoType::NONE_e:
260         {
261           path = lookupDatadirIn( repository() );
262           if ( ! path.empty() )
263             repository().info().setProbedType( repo::RepoType::YAST2_e );
264         }
265         break;
266
267         case repo::RepoType::YAST2_e:
268         {
269           path = lookupDatadirIn( repository() );
270           if ( path.empty() )
271             path = "suse";
272         }
273         break;
274
275         default:
276           break;
277       }
278       ret.setLocation    ( path/file, medianr );
279       ret.setDownloadSize( ByteCount( lookupNumAttribute( SolvAttr::downloadsize ), ByteCount::K ) );
280       ret.setChecksum    ( lookupCheckSumAttribute( SolvAttr::checksum ) );
281       // Not needed/available for solvables?
282       //ret.setOpenSize    ( ByteCount( lookupNumAttribute( SolvAttr::opensize ), ByteCount::K ) );
283       //ret.setOpenChecksum( lookupCheckSumAttribute( SolvAttr::openchecksum ) );
284       return ret;
285     }
286
287     ResKind Solvable::kind() const
288     {
289       NO_SOLVABLE_RETURN( ResKind() );
290       // detect srcpackages by 'arch'
291       switch ( _solvable->arch )
292       {
293         case ARCH_SRC:
294         case ARCH_NOSRC:
295           return ResKind::srcpackage;
296           break;
297       }
298
299       const char * ident = IdString( _solvable->name ).c_str();
300       const char * sep = ::strchr( ident, ':' );
301
302       // no ':' in package names (hopefully)
303       if ( ! sep )
304         return ResKind::package;
305
306       // quick check for well known kinds
307       if ( sep-ident >= 4 )
308       {
309         switch ( ident[3] )
310         {
311 #define OUTS(K,S) if ( !::strncmp( ident, ResKind::K.c_str(), S ) ) return ResKind::K
312           //             ----v
313           case 'c': OUTS( patch, 5 );       break;
314           case 'd': OUTS( product, 7 );     break;
315           case 'k': OUTS( package, 7 );     break;
316           case 'p': OUTS( srcpackage, 10 ); break;
317           case 't': OUTS( pattern, 7 );     break;
318 #undef OUTS
319         }
320       }
321
322       // an unknown kind
323       return ResKind( std::string( ident, sep-ident ) );
324     }
325
326     bool Solvable::isKind( const ResKind & kind_r ) const
327     {
328       NO_SOLVABLE_RETURN( false );
329
330       // detect srcpackages by 'arch'
331       switch ( _solvable->arch )
332       {
333         case ARCH_SRC:
334         case ARCH_NOSRC:
335           return( kind_r == ResKind::srcpackage );
336           break;
337       }
338
339       // no ':' in package names (hopefully)
340       const char * ident = IdString( _solvable->name ).c_str();
341       if ( kind_r == ResKind::package )
342       {
343         return( ::strchr( ident, ':' ) == 0 );
344       }
345
346       // look for a 'kind:' prefix
347       const char * kind = kind_r.c_str();
348       unsigned     ksize = ::strlen( kind );
349       return( ::strncmp( ident, kind, ksize ) == 0
350               && ident[ksize] == ':' );
351     }
352
353     std::string Solvable::name() const
354     {
355       NO_SOLVABLE_RETURN( std::string() );
356       const char * ident = IdString( _solvable->name ).c_str();
357       const char * sep = ::strchr( ident, ':' );
358       return( sep ? sep+1 : ident );
359     }
360
361     Edition Solvable::edition() const
362     {
363       NO_SOLVABLE_RETURN( Edition() );
364       return Edition( _solvable->evr );
365     }
366
367     Arch Solvable::arch() const
368     {
369       NO_SOLVABLE_RETURN( Arch_noarch ); //ArchId() );
370       switch ( _solvable->arch )
371       {
372         case ARCH_SRC:
373         case ARCH_NOSRC:
374           return Arch_noarch; //ArchId( ARCH_NOARCH );
375           break;
376       }
377       return Arch( IdString(_solvable->arch).asString() );
378       //return ArchId( _solvable->arch );
379     }
380
381     bool Solvable::multiversionInstall() const
382     {
383       return myPool().isMultiversion( ident() );
384     }
385
386     bool Solvable::installOnly() const { return multiversionInstall(); }
387
388     IdString Solvable::vendor() const
389     {
390       NO_SOLVABLE_RETURN( IdString() );
391       return IdString( _solvable->vendor );
392     }
393
394     Capabilities Solvable::operator[]( Dep which_r ) const
395     {
396       switch( which_r.inSwitch() )
397       {
398         case Dep::PROVIDES_e:    return provides();    break;
399         case Dep::REQUIRES_e:    return requires();    break;
400         case Dep::CONFLICTS_e:   return conflicts();   break;
401         case Dep::OBSOLETES_e:   return obsoletes();   break;
402         case Dep::RECOMMENDS_e:  return recommends();  break;
403         case Dep::SUGGESTS_e:    return suggests();    break;
404         case Dep::ENHANCES_e:    return enhances();    break;
405         case Dep::SUPPLEMENTS_e: return supplements(); break;
406         case Dep::PREREQUIRES_e: return prerequires(); break;
407       }
408       return Capabilities();
409     }
410
411     inline Capabilities _getCapabilities( detail::IdType * idarraydata_r, ::Offset offs_r )
412     {
413       return offs_r ? Capabilities( idarraydata_r + offs_r ) : Capabilities();
414     }
415     Capabilities Solvable::provides() const
416     {
417       NO_SOLVABLE_RETURN( Capabilities() );
418       return _getCapabilities( _solvable->repo->idarraydata, _solvable->provides );
419     }
420     Capabilities Solvable::requires() const
421     {
422       NO_SOLVABLE_RETURN( Capabilities() );
423       return _getCapabilities( _solvable->repo->idarraydata, _solvable->requires );
424     }
425     Capabilities Solvable::conflicts() const
426     {
427       NO_SOLVABLE_RETURN( Capabilities() );
428       return _getCapabilities( _solvable->repo->idarraydata, _solvable->conflicts );
429     }
430     Capabilities Solvable::obsoletes() const
431     {
432       NO_SOLVABLE_RETURN( Capabilities() );
433       return _getCapabilities( _solvable->repo->idarraydata, _solvable->obsoletes );
434     }
435     Capabilities Solvable::recommends() const
436     {
437       NO_SOLVABLE_RETURN( Capabilities() );
438       return _getCapabilities( _solvable->repo->idarraydata, _solvable->recommends );
439     }
440     Capabilities Solvable::suggests() const
441     {
442       NO_SOLVABLE_RETURN( Capabilities() );
443       return _getCapabilities( _solvable->repo->idarraydata, _solvable->suggests );
444     }
445     Capabilities Solvable::enhances() const
446     {
447       NO_SOLVABLE_RETURN( Capabilities() );
448       return _getCapabilities( _solvable->repo->idarraydata, _solvable->enhances );
449     }
450     Capabilities Solvable::supplements() const
451     {
452       NO_SOLVABLE_RETURN( Capabilities() );
453       return _getCapabilities( _solvable->repo->idarraydata, _solvable->supplements );
454     }
455     Capabilities Solvable::prerequires() const
456     {
457       NO_SOLVABLE_RETURN( Capabilities() );
458       // prerequires are a subset of requires
459        ::Offset offs = _solvable->requires;
460        return offs ? Capabilities( _solvable->repo->idarraydata + offs, detail::solvablePrereqMarker )
461                    : Capabilities();
462     }
463
464     CapabilitySet Solvable::providesNamespace( const std::string & namespace_r ) const
465     {
466       NO_SOLVABLE_RETURN( CapabilitySet() );
467       CapabilitySet ret;
468       Capabilities caps( provides() );
469       for_( it, caps.begin(), caps.end() )
470       {
471         CapDetail caprep( it->detail() );
472         if ( str::hasPrefix( caprep.name().c_str(), namespace_r ) && *(caprep.name().c_str()+namespace_r.size()) == '(' )
473           ret.insert( *it );
474       }
475       return ret;
476    }
477
478     CapabilitySet Solvable::valuesOfNamespace( const std::string & namespace_r ) const
479     {
480       NO_SOLVABLE_RETURN( CapabilitySet() );
481       CapabilitySet ret;
482       Capabilities caps( provides() );
483       for_( it, caps.begin(), caps.end() )
484       {
485         CapDetail caprep( it->detail() );
486         if ( str::hasPrefix( caprep.name().c_str(), namespace_r ) && *(caprep.name().c_str()+namespace_r.size()) == '(' )
487         {
488           std::string value( caprep.name().c_str()+namespace_r.size()+1 );
489           value[value.size()-1] = '\0'; // erase the trailing ')'
490           ret.insert( Capability( value, caprep.op(), caprep.ed() ) );
491         }
492       }
493       return ret;
494     }
495
496
497     std::string Solvable::asString() const
498     {
499       NO_SOLVABLE_RETURN( (_id == detail::systemSolvableId ? "systemSolvable" : "noSolvable") );
500       return str::form( "%s-%s.%s",
501                         IdString( _solvable->name ).c_str(),
502                         IdString( _solvable->evr ).c_str(),
503                         IdString( _solvable->arch ).c_str() );
504     }
505
506     bool Solvable::identical( Solvable rhs ) const
507     {
508       NO_SOLVABLE_RETURN( ! rhs.get() );
509       ::_Solvable * rhssolvable( rhs.get() );
510       return rhssolvable && ( _solvable == rhssolvable || ::solvable_identical( _solvable, rhssolvable ) );
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 (const 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
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     void Solvable::getSupportedLocales( LocaleSet & locales_r ) const
614     {
615       invokeOnEachSupportedLocale( supplements(),
616                                    functor::Collector( std::inserter( locales_r, locales_r.begin() ) ) );
617     }
618
619     /******************************************************************
620     **
621     **  FUNCTION NAME : operator<<
622     **  FUNCTION TYPE : std::ostream &
623     */
624     std::ostream & operator<<( std::ostream & str, const Solvable & obj )
625     {
626       if ( ! obj )
627         return str << (obj.isSystem() ? "systemSolvable" : "noSolvable" );
628
629       return str << "(" << obj.id() << ")"
630           << ( obj.isKind( ResKind::srcpackage ) ? "srcpackage:" : "" ) << obj.ident()
631           << '-' << obj.edition() << '.' << obj.arch() << "("
632           << obj.repository().alias() << ")";
633     }
634
635     /******************************************************************
636     **
637     **  FUNCTION NAME : dumpOn
638     **  FUNCTION TYPE : std::ostream &
639     */
640     std::ostream & dumpOn( std::ostream & str, const Solvable & obj )
641     {
642       str << obj;
643       if ( obj )
644       {
645 #define OUTS(X) if ( ! obj[Dep::X].empty() ) str << endl << " " #X " " << obj[Dep::X]
646         OUTS(PROVIDES);
647         OUTS(PREREQUIRES);
648         OUTS(REQUIRES);
649         OUTS(CONFLICTS);
650         OUTS(OBSOLETES);
651         OUTS(RECOMMENDS);
652         OUTS(SUGGESTS);
653         OUTS(ENHANCES);
654         OUTS(SUPPLEMENTS);
655 #undef OUTS
656       }
657       return str;
658     }
659
660     /////////////////////////////////////////////////////////////////
661   } // namespace sat
662   ///////////////////////////////////////////////////////////////////
663   /////////////////////////////////////////////////////////////////
664 } // namespace zypp
665 ///////////////////////////////////////////////////////////////////