| /_____||_| |_| |_| |
| |
\---------------------------------------------------------------------*/
-/** \file zypp/Capability.cc
+/** \file zypp/sat/Capability.cc
*
*/
#include <iostream>
+#include "zypp/base/Logger.h"
+#include "zypp/base/String.h"
+#include "zypp/base/Regex.h"
+#include "zypp/base/Gettext.h"
+#include "zypp/base/Exception.h"
+
+#include "zypp/Arch.h"
+#include "zypp/Rel.h"
+#include "zypp/Edition.h"
#include "zypp/Capability.h"
-#include "zypp/capability/CapabilityImpl.h"
-#include "zypp/SolverContext.h"
+#include "zypp/sat/detail/PoolImpl.h"
+#include "zypp/sat/Pool.h"
+#include "zypp/ResPool.h"
-using namespace std;
+using std::endl;
///////////////////////////////////////////////////////////////////
namespace zypp
{ /////////////////////////////////////////////////////////////////
+ ///////////////////////////////////////////////////////////////////
+ namespace
+ { /////////////////////////////////////////////////////////////////
+
+ /** backward skip whitespace starting at pos_r */
+ inline std::string::size_type backskipWs( const std::string & str_r, std::string::size_type pos_r )
+ {
+ for ( ; pos_r != std::string::npos; --pos_r )
+ {
+ char ch = str_r[pos_r];
+ if ( ch != ' ' && ch != '\t' )
+ break;
+ }
+ return pos_r;
+ }
+
+ /** backward skip non-whitespace starting at pos_r */
+ inline std::string::size_type backskipNWs( const std::string & str_r, std::string::size_type pos_r )
+ {
+ for ( ; pos_r != std::string::npos; --pos_r )
+ {
+ char ch = str_r[pos_r];
+ if ( ch == ' ' || ch == '\t' )
+ break;
+ }
+ return pos_r;
+ }
+
+ /** Split any 'op edition' from str_r */
+ void splitOpEdition( std::string & str_r, Rel & op_r, Edition & ed_r )
+ {
+ if ( str_r.empty() )
+ return;
+ std::string::size_type ch( str_r.size()-1 );
+
+ // check whether the one but last word is a valid Rel:
+ if ( (ch = backskipWs( str_r, ch )) != std::string::npos )
+ {
+ std::string::size_type ee( ch );
+ if ( (ch = backskipNWs( str_r, ch )) != std::string::npos )
+ {
+ std::string::size_type eb( ch );
+ if ( (ch = backskipWs( str_r, ch )) != std::string::npos )
+ {
+ std::string::size_type oe( ch );
+ ch = backskipNWs( str_r, ch ); // now before 'op'? begin
+ if ( op_r.parseFrom( str_r.substr( ch+1, oe-ch ) ) )
+ {
+ // found a legal 'op'
+ ed_r = Edition( str_r.substr( eb+1, ee-eb ) );
+ if ( ch != std::string::npos ) // 'op' is not at str_r begin, so skip WS
+ ch = backskipWs( str_r, ch );
+ str_r.erase( ch+1 );
+ return;
+ }
+ }
+ }
+ }
+ // HERE: Didn't find 'name op edition'
+ // As a convenience we check for an embeded 'op' (not surounded by WS).
+ // But just '[<=>]=?|!=' and not inside '()'.
+ ch = str_r.find_last_of( "<=>)" );
+ if ( ch != std::string::npos && str_r[ch] != ')' )
+ {
+ std::string::size_type oe( ch );
+
+ // do edition first:
+ ch = str_r.find_first_not_of( " \t", oe+1 );
+ if ( ch != std::string::npos )
+ ed_r = Edition( str_r.substr( ch ) );
+
+ // now finish op:
+ ch = oe-1;
+ if ( str_r[oe] != '=' ) // '[<>]'
+ {
+ op_r = ( str_r[oe] == '<' ) ? Rel::LT : Rel::GT;
+ }
+ else
+ { // '?='
+ if ( ch != std::string::npos )
+ {
+ switch ( str_r[ch] )
+ {
+ case '<': --ch; op_r = Rel::LE; break;
+ case '>': --ch; op_r = Rel::GE; break;
+ case '!': --ch; op_r = Rel::NE; break;
+ case '=': --ch; // fall through
+ default: op_r = Rel::EQ; break;
+ }
+ }
+ }
+
+ // finally name:
+ if ( ch != std::string::npos ) // 'op' is not at str_r begin, so skip WS
+ ch = backskipWs( str_r, ch );
+ str_r.erase( ch+1 );
+ return;
+ }
+ // HERE: It's a plain 'name'
+ }
+
+ /** Build \ref Capability from data. No parsing required.
+ */
+ sat::detail::IdType relFromStr( ::_Pool * pool_r,
+ const Arch & arch_r,
+ const std::string & name_r,
+ Rel op_r,
+ const Edition & ed_r,
+ const ResKind & kind_r )
+ {
+ // First build the name, non-packages prefixed by kind
+ sat::Solvable::SplitIdent split( kind_r, name_r );
+ sat::detail::IdType nid( split.ident().id() );
+
+ if ( split.kind() == ResKind::srcpackage )
+ {
+ // map 'kind srcpackage' to 'arch src', the pseudo architecture
+ // libsolv uses.
+ nid = ::pool_rel2id( pool_r, nid, IdString(ARCH_SRC).id(), REL_ARCH, /*create*/true );
+ }
+
+ // Extend name by architecture, if provided and not a srcpackage
+ if ( ! arch_r.empty() && kind_r != ResKind::srcpackage )
+ {
+ nid = ::pool_rel2id( pool_r, nid, arch_r.id(), REL_ARCH, /*create*/true );
+ }
+
+ // Extend 'op edition', if provided
+ if ( op_r != Rel::ANY && ed_r != Edition::noedition )
+ {
+ nid = ::pool_rel2id( pool_r, nid, ed_r.id(), op_r.bits(), /*create*/true );
+ }
+
+ return nid;
+ }
+ /** Build \ref Capability from data, just parsing name for '[.arch]' and detect
+ * 'kind srcpackage' (will be mapped to arch \c src).
+ */
+ sat::detail::IdType relFromStr( ::_Pool * pool_r,
+ const std::string & name_r, Rel op_r, const Edition & ed_r,
+ const ResKind & kind_r )
+ {
+ static const Arch srcArch( IdString(ARCH_SRC).asString() );
+ static const std::string srcKindPrefix( ResKind::srcpackage.asString() + ':' );
+
+ // check for an embedded 'srcpackage:foo' to be mapped to 'foo' and 'ResKind::srcpackage'.
+ if ( kind_r.empty() && str::hasPrefix( name_r, srcKindPrefix ) )
+ {
+ return relFromStr( pool_r, Arch_empty, name_r.substr( srcKindPrefix.size() ), op_r, ed_r, ResKind::srcpackage );
+ }
+
+ Arch arch( Arch_empty );
+ std::string name( name_r );
+
+ std::string::size_type asep( name_r.rfind( "." ) );
+ if ( asep != std::string::npos )
+ {
+ Arch ext( name_r.substr( asep+1 ) );
+ if ( ext.isBuiltIn() || ext == srcArch )
+ {
+ arch = ext;
+ name.erase( asep );
+ }
+ }
+
+ return relFromStr( pool_r, arch, name, op_r, ed_r, kind_r );
+ }
+
+ /** Full parse from string, unless Capability::PARSED.
+ */
+ sat::detail::IdType relFromStr( ::_Pool * pool_r,
+ const Arch & arch_r, // parse from name if empty
+ const std::string & str_r, const ResKind & kind_r,
+ Capability::CtorFlag flag_r )
+ {
+ std::string name( str_r );
+ Rel op;
+ Edition ed;
+ if ( flag_r == Capability::UNPARSED )
+ {
+ splitOpEdition( name, op, ed );
+ }
+
+ if ( arch_r.empty() )
+ return relFromStr( pool_r, name, op, ed, kind_r ); // parses for name[.arch]
+ // else
+ return relFromStr( pool_r, arch_r, name, op, ed, kind_r );
+ }
+
+ /////////////////////////////////////////////////////////////////
+ } // namespace
///////////////////////////////////////////////////////////////////
- //
- // METHOD NAME : Capability::Capability
- // METHOD TYPE : Ctor
- //
- Capability::Capability( ImplPtr impl_r )
- : _pimpl( impl_r )
+
+ const Capability Capability::Null( STRID_NULL );
+ const Capability Capability::Empty( STRID_EMPTY );
+
+ /////////////////////////////////////////////////////////////////
+
+ Capability::Capability( const char * str_r, const ResKind & prefix_r, CtorFlag flag_r )
+ : _id( relFromStr( myPool().getPool(), Arch_empty, str_r, prefix_r, flag_r ) )
+ {}
+
+ Capability::Capability( const std::string & str_r, const ResKind & prefix_r, CtorFlag flag_r )
+ : _id( relFromStr( myPool().getPool(), Arch_empty, str_r.c_str(), prefix_r, flag_r ) )
+ {}
+
+ Capability::Capability( const Arch & arch_r, const char * str_r, const ResKind & prefix_r, CtorFlag flag_r )
+ : _id( relFromStr( myPool().getPool(), arch_r, str_r, prefix_r, flag_r ) )
+ {}
+
+ Capability::Capability( const Arch & arch_r, const std::string & str_r, const ResKind & prefix_r, CtorFlag flag_r )
+ : _id( relFromStr( myPool().getPool(), arch_r, str_r.c_str(), prefix_r, flag_r ) )
+ {}
+
+ Capability::Capability( const char * str_r, CtorFlag flag_r, const ResKind & prefix_r )
+ : _id( relFromStr( myPool().getPool(), Arch_empty, str_r, prefix_r, flag_r ) )
+ {}
+
+ Capability::Capability( const std::string & str_r, CtorFlag flag_r, const ResKind & prefix_r )
+ : _id( relFromStr( myPool().getPool(), Arch_empty, str_r, prefix_r, flag_r ) )
+ {}
+
+ Capability::Capability( const Arch & arch_r, const char * str_r, CtorFlag flag_r, const ResKind & prefix_r )
+ : _id( relFromStr( myPool().getPool(), arch_r, str_r, prefix_r, flag_r ) )
+ {}
+
+ Capability::Capability( const Arch & arch_r, const std::string & str_r, CtorFlag flag_r, const ResKind & prefix_r )
+ : _id( relFromStr( myPool().getPool(), arch_r, str_r, prefix_r, flag_r ) )
{}
///////////////////////////////////////////////////////////////////
- //
- // METHOD NAME : Capability::~Capability
- // METHOD TYPE : Dtor
- //
- Capability::~Capability()
+ // Ctor from <name[.arch] op edition>.
+ ///////////////////////////////////////////////////////////////////
+
+ Capability::Capability( const std::string & name_r, const std::string & op_r, const std::string & ed_r, const ResKind & prefix_r )
+ : _id( relFromStr( myPool().getPool(), name_r, Rel(op_r), Edition(ed_r), prefix_r ) )
+ {}
+ Capability::Capability( const std::string & name_r, Rel op_r, const std::string & ed_r, const ResKind & prefix_r )
+ : _id( relFromStr( myPool().getPool(), name_r, op_r, Edition(ed_r), prefix_r ) )
+ {}
+ Capability::Capability( const std::string & name_r, Rel op_r, const Edition & ed_r, const ResKind & prefix_r )
+ : _id( relFromStr( myPool().getPool(), name_r, op_r, ed_r, prefix_r ) )
+ {}
+
+ ///////////////////////////////////////////////////////////////////
+ // Ctor from <arch name op edition>.
+ ///////////////////////////////////////////////////////////////////
+
+ Capability::Capability( const std::string & arch_r, const std::string & name_r, const std::string & op_r, const std::string & ed_r, const ResKind & prefix_r )
+ : _id( relFromStr( myPool().getPool(), Arch(arch_r), name_r, Rel(op_r), Edition(ed_r), prefix_r ) )
+ {}
+ Capability::Capability( const std::string & arch_r, const std::string & name_r, Rel op_r, const std::string & ed_r, const ResKind & prefix_r )
+ : _id( relFromStr( myPool().getPool(), Arch(arch_r), name_r, op_r, Edition(ed_r), prefix_r ) )
+ {}
+ Capability::Capability( const std::string & arch_r, const std::string & name_r, Rel op_r, const Edition & ed_r, const ResKind & prefix_r )
+ : _id( relFromStr( myPool().getPool(), Arch(arch_r), name_r, op_r, ed_r, prefix_r ) )
+ {}
+ Capability::Capability( const Arch & arch_r, const std::string & name_r, const std::string & op_r, const std::string & ed_r, const ResKind & prefix_r )
+ : _id( relFromStr( myPool().getPool(), arch_r, name_r, Rel(op_r), Edition(ed_r), prefix_r ) )
{}
+ Capability::Capability( const Arch & arch_r, const std::string & name_r, Rel op_r, const std::string & ed_r, const ResKind & prefix_r )
+ : _id( relFromStr( myPool().getPool(), arch_r, name_r, op_r, Edition(ed_r), prefix_r ) )
+ {}
+ Capability::Capability( const Arch & arch_r, const std::string & name_r, Rel op_r, const Edition & ed_r, const ResKind & prefix_r )
+ : _id( relFromStr( myPool().getPool(), arch_r, name_r, op_r, ed_r, prefix_r ) )
+ {}
+
+ const char * Capability::c_str() const
+ { return( _id ? ::pool_dep2str( myPool().getPool(), _id ) : "" ); }
+
+ CapMatch Capability::_doMatch( sat::detail::IdType lhs, sat::detail::IdType rhs )
+ {
+#warning MIGRATE TO SAT
+#warning TESTCASE
+ if ( lhs == rhs )
+ return CapMatch::yes;
+
+ CapDetail l( lhs );
+ CapDetail r( rhs );
+
+ switch ( l.kind() )
+ {
+ case CapDetail::NOCAP:
+ return( r.kind() == CapDetail::NOCAP ); // NOCAP matches NOCAP only
+ break;
+ case CapDetail::EXPRESSION:
+ return CapMatch::irrelevant;
+ break;
+ case CapDetail::NAMED:
+ case CapDetail::VERSIONED:
+ break;
+ }
+
+ switch ( r.kind() )
+ {
+ case CapDetail::NOCAP:
+ return CapMatch::no; // match case handled above
+ break;
+ case CapDetail::EXPRESSION:
+ return CapMatch::irrelevant;
+ break;
+ case CapDetail::NAMED:
+ case CapDetail::VERSIONED:
+ break;
+ }
+ // comparing two simple caps:
+ if ( l.name() != r.name() )
+ return CapMatch::no;
+
+ // if both are arch restricted they must match
+ if ( l.arch() != r.arch()
+ && ! ( l.arch().empty() || r.arch().empty() ) )
+ return CapMatch::no;
+
+ // isNamed matches ANY edition:
+ if ( l.isNamed() || r.isNamed() )
+ return CapMatch::yes;
+
+ // both are versioned:
+ return overlaps( Edition::MatchRange( l.op(), l.ed() ),
+ Edition::MatchRange( r.op(), r.ed() ) );
+ }
+
+ bool Capability::isInterestingFileSpec( const char * name_r )
+ {
+ static str::smatch what;
+ static const str::regex filenameRegex(
+ "/(s?bin|lib(64)?|etc)/|^/usr/(games/|share/(dict/words|magic\\.mime)$)|^/opt/gnome/games/",
+ str::regex::nosubs );
- const Resolvable::Kind & Capability::refers() const
- { return _pimpl->refers(); }
+ return str::regex_match( name_r, what, filenameRegex );
+ }
+
+ Capability Capability::guessPackageSpec( const std::string & str_r, bool & rewrote_r )
+ {
+ Capability cap( str_r );
+ CapDetail detail( cap.detail() );
+
+ // str_r might be the form "libzypp-1.2.3-4.5(.arch)'
+ // correctly parsed as name capability by the ctor.
+ // TODO: Think about allowing glob char in name - for now don't process
+ if ( detail.isNamed() && !::strpbrk( detail.name().c_str(), "*?[{" )
+ && ::strrchr( detail.name().c_str(), '-' ) && sat::WhatProvides( cap ).empty() )
+ {
+ Arch origArch( detail.arch() ); // to support a trailing .arch
+
+ std::string guess( detail.name().asString() );
+ std::string::size_type pos( guess.rfind( '-' ) );
+ guess[pos] = '=';
+
+ Capability guesscap( origArch, guess );
+ detail = guesscap.detail();
+
+ ResPool pool( ResPool::instance() );
+ // require name part matching a pool items name (not just provides!)
+ if ( pool.byIdentBegin( detail.name() ) != pool.byIdentEnd( detail.name() ) )
+ {
+ rewrote_r = true;
+ return guesscap;
+ }
- std::string Capability::asString() const
- { return _pimpl->asString(); }
+ // try the one but last '-'
+ if ( pos )
+ {
+ guess[pos] = '-';
+ if ( (pos = guess.rfind( '-', pos-1 )) != std::string::npos )
+ {
+ guess[pos] = '=';
- bool Capability::matches( Resolvable::constPtr resolvable_r,
- const SolverContext & solverContext_r ) const
- { return _pimpl->matches( resolvable_r, solverContext_r ); }
+ guesscap = Capability( origArch, guess );
+ detail = guesscap.detail();
- bool Capability::matches( Resolvable::constPtr resolvable_r ) const
- { return _pimpl->matches( resolvable_r, SolverContext() ); }
+ // require name part matching a pool items name (not just provides!)
+ if ( pool.byIdentBegin( detail.name() ) != pool.byIdentEnd( detail.name() ) )
+ {
+ rewrote_r = true;
+ return guesscap;
+ }
+ }
+ }
+ }
+
+ rewrote_r = false;
+ return cap;
+ }
+
+ Capability Capability::guessPackageSpec( const std::string & str_r )
+ {
+ bool dummy;
+ return guessPackageSpec( str_r, dummy );
+ }
/******************************************************************
**
*/
std::ostream & operator<<( std::ostream & str, const Capability & obj )
{
- return str << obj._pimpl;
+ return str << obj.detail();
+ }
+
+ std::ostream & dumpOn( std::ostream & str, const Capability & obj )
+ {
+ return str << obj.detail();
+ }
+
+ ///////////////////////////////////////////////////////////////////
+ //
+ // CLASS NAME : CapDetail
+ //
+ ///////////////////////////////////////////////////////////////////
+
+ void CapDetail::_init()
+ {
+ // : _kind( NOCAP ), _lhs( id_r ), _rhs( 0 ), _flag( 0 ), _archIfSimple( 0 )
+
+ if ( _lhs == sat::detail::emptyId || _lhs == sat::detail::noId )
+ return; // NOCAP
+
+ if ( ! ISRELDEP(_lhs) )
+ {
+ // this is name without arch!
+ _kind = NAMED;
+ return;
+ }
+
+ ::Reldep * rd = GETRELDEP( myPool().getPool(), _lhs );
+ _lhs = rd->name;
+ _rhs = rd->evr;
+ _flag = rd->flags;
+
+ if ( Rel::isRel( _flag ) )
+ {
+ _kind = VERSIONED;
+ // Check for name.arch...
+ if ( ! ISRELDEP(_lhs) )
+ return; // this is name without arch!
+ rd = GETRELDEP( myPool().getPool(), _lhs );
+ if ( rd->flags != CAP_ARCH )
+ return; // this is not name.arch
+ // This is name.arch:
+ _lhs = rd->name;
+ _archIfSimple = rd->evr;
+ }
+ else if ( rd->flags == CAP_ARCH )
+ {
+ _kind = NAMED;
+ // This is name.arch:
+ _lhs = rd->name;
+ _archIfSimple = rd->evr;
+ }
+ else
+ {
+ _kind = EXPRESSION;
+ return;
+ }
+ // map back libsolvs pseudo arch 'src' to kind srcpackage
+ if ( _archIfSimple == ARCH_SRC )
+ {
+ _lhs = IdString( (ResKind::srcpackage.asString() + ":" + IdString(_lhs).c_str()).c_str() ).id();
+ _archIfSimple = 0;
+ }
+ }
+
+ /******************************************************************
+ **
+ ** FUNCTION NAME : operator<<
+ ** FUNCTION TYPE : std::ostream &
+ */
+ std::ostream & operator<<( std::ostream & str, const CapDetail & obj )
+ {
+ static const char archsep = '.';
+ switch ( obj.kind() )
+ {
+ case CapDetail::NOCAP:
+ return str << "<NoCap>";
+ break;
+ case CapDetail::NAMED:
+ str << obj.name();
+ if ( obj.hasArch() )
+ str << archsep << obj.arch();
+ return str;
+ break;
+ case CapDetail::VERSIONED:
+ str << obj.name();
+ if ( obj.hasArch() )
+ str << archsep << obj.arch();
+ return str << " " << obj.op() << " " << obj.ed();
+ break;
+ case CapDetail::EXPRESSION:
+ switch ( obj.capRel() )
+ {
+ case CapDetail::REL_NONE:
+ case CapDetail::CAP_AND:
+ case CapDetail::CAP_OR:
+ case CapDetail::CAP_WITH:
+ case CapDetail::CAP_ARCH:
+ return str << obj.lhs().detail() << " " << obj.capRel() << " " << obj.rhs().detail();
+ break;
+ case CapDetail::CAP_NAMESPACE:
+ return str << obj.lhs().detail() << "(" << obj.rhs().detail() << ")";
+ }
+ break;
+ }
+ return str << "<UnknownCap>";
+ }
+
+ std::ostream & operator<<( std::ostream & str, CapDetail::Kind obj )
+ {
+ switch ( obj )
+ {
+ case CapDetail::NOCAP: return str << "NoCap"; break;
+ case CapDetail::NAMED: return str << "NamedCap"; break;
+ case CapDetail::VERSIONED: return str << "VersionedCap"; break;
+ case CapDetail::EXPRESSION: return str << "CapExpression"; break;
+ }
+ return str << "UnknownCap";
+ }
+
+ std::ostream & operator<<( std::ostream & str, CapDetail::CapRel obj )
+ {
+ switch ( obj )
+ {
+ case CapDetail::REL_NONE: return str << "NoCapRel"; break;
+ case CapDetail::CAP_AND: return str << "&"; break; // AND
+ case CapDetail::CAP_OR: return str << "|"; break; // OR
+ case CapDetail::CAP_WITH: return str << "+"; break; // WITH
+ case CapDetail::CAP_NAMESPACE: return str << "NAMESPACE"; break;
+ case CapDetail::CAP_ARCH: return str << "ARCH"; break;
+ }
+ return str << "UnknownCapRel";
}
/////////////////////////////////////////////////////////////////