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