From a2463c7864985af3df8d19fa011eab9136fda149 Mon Sep 17 00:00:00 2001 From: Michael Andres Date: Wed, 22 Apr 2009 17:44:25 +0200 Subject: [PATCH] Extend LookupAttr to support matching specific string patterns. - May define an AttrMatcher to be used in LookupAttr. - Fix iterator copy and resource handling when compiled regex are involved. --- tests/sat/LookupAttr_test.cc | 15 ++++ zypp/sat/LookupAttr.cc | 163 ++++++++++++++++++++++++++++++------------- zypp/sat/LookupAttr.h | 99 ++++++++++++++++++++++++-- 3 files changed, 222 insertions(+), 55 deletions(-) diff --git a/tests/sat/LookupAttr_test.cc b/tests/sat/LookupAttr_test.cc index 46a45a2..ab8c545 100644 --- a/tests/sat/LookupAttr_test.cc +++ b/tests/sat/LookupAttr_test.cc @@ -1,5 +1,6 @@ #include "TestSetup.h" #include +#include #include static TestSetup test( "/tmp/x", Arch_x86_64 ); @@ -54,6 +55,20 @@ BOOST_AUTO_TEST_CASE(LookupAttr_existingattr) BOOST_CHECK_NE( q.begin(), q.end() ); } +BOOST_AUTO_TEST_CASE(LookupAttr_existingattr_matcher) +{ + sat::LookupAttr q( sat::SolvAttr::name ); + + BOOST_CHECK_THROW( q.setAttrMatcher( sat::AttrMatcher("[]ypper",Match::REGEX) ), MatchInvalidRegexException ); + BOOST_CHECK( ! q.attrMatcher() ); + BOOST_CHECK_NO_THROW( q.setAttrMatcher( sat::AttrMatcher("[zZ]ypper",Match::REGEX) ) ); + BOOST_CHECK( q.attrMatcher() ); + + BOOST_CHECK_EQUAL( q.size(), 9 ); + for_(it,q.begin(),q.end()) + { cout << it << endl;} +} + BOOST_AUTO_TEST_CASE(LookupAttr_iterate_solvables) { // sat::SolvAttr::name query should visit each solvable once. diff --git a/zypp/sat/LookupAttr.cc b/zypp/sat/LookupAttr.cc index 1b43889..8e7961d 100644 --- a/zypp/sat/LookupAttr.cc +++ b/zypp/sat/LookupAttr.cc @@ -17,9 +17,11 @@ #include "zypp/sat/detail/PoolImpl.h" +#include "zypp/sat/Pool.h" #include "zypp/sat/LookupAttr.h" +#include "zypp/sat/AttrMatcher.h" + #include "zypp/CheckSum.h" -#include "zypp/sat/Pool.h" using std::endl; @@ -72,6 +74,15 @@ namespace zypp void setAttr( SolvAttr attr_r ) { _attr = attr_r; } + const AttrMatcher & attrMatcher() const + { return _attrMatcher; } + + void setAttrMatcher( const AttrMatcher & matcher_r ) + { + matcher_r.compile(); + _attrMatcher = matcher_r; + } + public: bool pool() const { return ! (_repo || _solv); } @@ -102,29 +113,16 @@ namespace zypp LookupAttr::iterator begin() const { - if ( _attr == SolvAttr::noAttr ) + if ( _attr == SolvAttr::noAttr || sat::Pool::instance().reposEmpty() ) return end(); -#warning Need to call dataiterator_free, use Autodispose instead of scoped_ptr - scoped_ptr< ::_Dataiterator> dip( new ::Dataiterator ); - // needed while LookupAttr::iterator::dip_equal does ::memcmp: - ::memset( dip.get(), 0, sizeof(::_Dataiterator) ); - + detail::RepoIdType whichRepo = detail::noRepoId; // all repos if ( _solv ) - { - ::dataiterator_init( dip.get(), sat::Pool::instance().get(), _solv.repository().id(), _solv.id(), _attr.id(), 0, 0 ); - } + whichRepo = _solv.repository().id(); else if ( _repo ) - { - ::dataiterator_init( dip.get(), sat::Pool::instance().get(), _repo.id(), _solv.id(), _attr.id(), 0, 0 ); - } - else if ( ! sat::Pool::instance().reposEmpty() ) - { - ::dataiterator_init( dip.get(), sat::Pool::instance().get(), 0, _solv.id(), _attr.id(), 0, 0 ); - } - else - return end(); + whichRepo = _repo.id(); + detail::DIWrap dip( whichRepo, _solv.id(), _attr.id(), _attrMatcher.searchstring(), _attrMatcher.flags().get() ); return iterator( dip ); // iterator takes over ownership! } @@ -135,6 +133,7 @@ namespace zypp SolvAttr _attr; Repository _repo; Solvable _solv; + AttrMatcher _attrMatcher; private: friend Impl * rwcowClone( const Impl * rhs ); @@ -173,6 +172,12 @@ namespace zypp void LookupAttr::setAttr( SolvAttr attr_r ) { _pimpl->setAttr( attr_r ); } + const AttrMatcher & LookupAttr::attrMatcher() const + { return _pimpl->attrMatcher(); } + + void LookupAttr::setAttrMatcher( const AttrMatcher & matcher_r ) + { _pimpl->setAttrMatcher( matcher_r ); } + /////////////////////////////////////////////////////////////////// bool LookupAttr::pool() const @@ -251,6 +256,74 @@ namespace zypp /////////////////////////////////////////////////////////////////// // + // CLASS NAME : detail::DIWrap + // + /////////////////////////////////////////////////////////////////// + + namespace detail + { + DIWrap::DIWrap( RepoIdType repoId_r, SolvableIdType solvId_r, IdType attrId_r, + const std::string & mstring_r, int flags_r ) + : _dip( new ::Dataiterator ) + , _mstring( mstring_r ) + { + ::dataiterator_init( _dip, sat::Pool::instance().get(), repoId_r, solvId_r, attrId_r, + _mstring.empty() ? 0 : _mstring.c_str(), flags_r ); + } + + DIWrap::DIWrap( RepoIdType repoId_r, SolvableIdType solvId_r, IdType attrId_r, + const char * mstring_r, int flags_r ) + : _dip( new ::Dataiterator ) + , _mstring( mstring_r ? mstring_r : "" ) + { + ::dataiterator_init( _dip, sat::Pool::instance().get(), repoId_r, solvId_r, attrId_r, + _mstring.empty() ? 0 : _mstring.c_str(), flags_r ); + } + + DIWrap::DIWrap( const DIWrap & rhs ) + : _dip( 0 ) + , _mstring( rhs._mstring ) + { + if ( rhs._dip ) + { + _dip = new ::Dataiterator; + *_dip = *rhs._dip; + // now we have to manually clone any allocated regex data matcher. + ::Datamatcher & matcher( _dip->matcher ); + if ( matcher.match && ( matcher.flags & SEARCH_STRINGMASK ) == SEARCH_REGEX ) + { + ::datamatcher_init( &matcher, _mstring.c_str(), matcher.flags ); + } + else if ( matcher.match && matcher.match != _mstring.c_str() ) + { + //SEC << "**" << rhs._dip << endl; + SEC << "r " << rhs._dip->matcher.match << endl; + SEC << "r " << rhs._dip->matcher.flags << endl; + SEC << "r " << (const void*)rhs._mstring.c_str() << "'" << rhs._mstring << "'" << endl; + + SEC << "t " << matcher.match << endl; + SEC << "t " << matcher.flags << endl; + SEC << "t " << (const void*)_mstring.c_str() << "'" << _mstring << "'" << endl; + throw( "this cant be!" ); + } + } + } + + DIWrap::~DIWrap() + { + if ( _dip ) + { + ::dataiterator_free( _dip ); + delete _dip; + } + } + + std::ostream & operator<<( std::ostream & str, const DIWrap & obj ) + { return str << obj.get(); } + } + + /////////////////////////////////////////////////////////////////// + // // CLASS NAME : LookupAttr::iterator // /////////////////////////////////////////////////////////////////// @@ -366,13 +439,8 @@ namespace zypp // remember this position ::dataiterator_setpos( _dip.get() ); - // setup the new sub iterator - scoped_ptr< ::_Dataiterator> dip( new ::Dataiterator ); - // needed while LookupAttr::iterator::dip_equal does ::memcmp: - ::memset( dip.get(), 0, sizeof(::_Dataiterator) ); - - ::dataiterator_init( dip.get(), sat::Pool::instance().get(), 0, SOLVID_POS, 0, 0, 0 ); - + // setup the new sub iterator with the remembered position + detail::DIWrap dip( 0, SOLVID_POS, 0, 0, 0 ); return iterator( dip ); // iterator takes over ownership! } @@ -547,50 +615,45 @@ namespace zypp // internal stuff below /////////////////////////////////////////////////////////////////// - LookupAttr::iterator::~iterator() - {} - LookupAttr::iterator::iterator() : iterator_adaptor_( 0 ) {} LookupAttr::iterator::iterator( const iterator & rhs ) - : iterator_adaptor_( cloneFrom( rhs.base() ) ) - , _dip( base() ) + : iterator_adaptor_( 0 ) + , _dip( rhs._dip ) + { + base_reference() = _dip.get(); + } + + LookupAttr::iterator::iterator( detail::DIWrap & dip_r ) + : iterator_adaptor_( 0 ) + { + _dip.swap( dip_r ); // take ownership! + base_reference() = _dip.get(); + increment(); + } + + LookupAttr::iterator::~iterator() {} LookupAttr::iterator & LookupAttr::iterator::operator=( const iterator & rhs ) { if ( &rhs != this ) { - _dip.reset( cloneFrom( rhs.base() ) ); + _dip = rhs._dip; base_reference() = _dip.get(); } return *this; } - LookupAttr::iterator::iterator( scoped_ptr< ::_Dataiterator> & dip_r ) - : iterator_adaptor_( dip_r.get() ) - { - _dip.swap( dip_r ); // take ownership! - increment(); - } - /////////////////////////////////////////////////////////////////// - ::_Dataiterator * LookupAttr::iterator::cloneFrom( const ::_Dataiterator * rhs ) - { - if ( ! rhs ) - return 0; - ::_Dataiterator * ret( new ::_Dataiterator ); - *ret = *rhs; - return ret; - } - bool LookupAttr::iterator::dip_equal( const ::_Dataiterator & lhs, const ::_Dataiterator & rhs ) const { - // requires ::memset in LookupAttr::begin - return ::memcmp( &lhs, &rhs, sizeof(::_Dataiterator) ) == 0; + // Iterator equal is same position in same container. + // Here: same attribute in same solvable. + return( lhs.solvid == rhs.solvid && lhs.key->name == rhs.key->name ); } detail::IdType LookupAttr::iterator::dereference() const diff --git a/zypp/sat/LookupAttr.h b/zypp/sat/LookupAttr.h index 1d052e8..c18a99e 100644 --- a/zypp/sat/LookupAttr.h +++ b/zypp/sat/LookupAttr.h @@ -29,11 +29,15 @@ namespace zypp { ///////////////////////////////////////////////////////////////// class CheckSum; + class Match; + class MatchException; /////////////////////////////////////////////////////////////////// namespace sat { ///////////////////////////////////////////////////////////////// + class AttrMatcher; + /////////////////////////////////////////////////////////////////// // // CLASS NAME : LookupAttr @@ -92,6 +96,9 @@ namespace zypp class LookupAttr { public: + typedef MatchException Exception; + + public: typedef unsigned size_type; /** Specify the where to look for the attribule. */ @@ -147,6 +154,28 @@ namespace zypp void setAttr( SolvAttr attr_r ); //@} + /** \name Restrict attributes to match a pattern. */ + //@{ + /** The pattern to match. + * You can also evaluate \ref AttrMatcher in a boolean context, + * in order to test whether an \ref AttrMatcher is set: + * \code + * LookupAttr q; + * if ( q.attrMatcher() ) + * ...; // an AttrMatcher is set + * \endcode + */ + const AttrMatcher & attrMatcher() const; + + /** Set the pattern to match. + * \throws MatchException Any of the exceptions thrown by \ref AttrMatcher::compile. + */ + void setAttrMatcher( const AttrMatcher & matcher_r ); + + /** Reset the pattern to match. */ + void resetAttrMatcher(); + //@} + public: /** \name Where to search. */ //@{ @@ -227,6 +256,68 @@ namespace zypp }; /////////////////////////////////////////////////////////////////// + namespace detail + { + /** Wrapper around sat \c ::_Dataiterator. + * + * Manages copy and assign, and release of allocated + * resources like datamatcher inside the dataiterator. + * Also maintains a copy of the matchstring in order to + * keep the char* passed to the dataiterator valid. + */ + class DIWrap : private base::SafeBool + { + public: + /** \c NULL \c ::_Dataiterator */ + DIWrap() + : _dip( 0 ) + {} + /** Initializes */ + DIWrap( RepoIdType repoId_r, SolvableIdType solvId_r, IdType attrId_r, + const std::string & mstring_r = std::string(), int flags_r = 0 ); + /** \overload to catch \c NULL \a mstring_r. */ + DIWrap( RepoIdType repoId_r, SolvableIdType solvId_r, IdType attrId_r, + const char * mstring_r, int flags_r = 0 ); + DIWrap( const DIWrap & rhs ); + ~DIWrap(); + public: + void swap( DIWrap & rhs ) + { + if ( &rhs != this ) // prevent self assign! + { + std::swap( _dip, rhs._dip ); + std::swap( _mstring, rhs._mstring ); + } + } + DIWrap & operator=( const DIWrap & rhs ) + { + if ( &rhs != this ) // prevent self assign! + DIWrap( rhs ).swap( *this ); + return *this; + } + void reset() + { DIWrap().swap( *this ); } + public: +#ifndef SWIG // Swig treats it as syntax error + /** Evaluate in a boolean context ( _dip != NULL ). */ + using base::SafeBool::operator bool_type; +#endif + public: + ::_Dataiterator * operator->() const { return _dip; } + ::_Dataiterator * get() const { return _dip; } + const std::string & getstr() const { return _mstring; } + private: + friend base::SafeBool::operator bool_type() const; + bool boolTest() const + { return _dip; } + private: + ::_Dataiterator * _dip; + std::string _mstring; + }; + /** \relates DIWrap Stream output. */ + std::ostream & operator<<( std::ostream & str, const DIWrap & obj ); + } + /////////////////////////////////////////////////////////////////// // // CLASS NAME : LookupAttr::iterator @@ -405,16 +496,14 @@ namespace zypp public: /** - * C-tor taking over ownership of the passed scoped _Dataiterator* + * C-tor taking over ownership of the passed \c ::_Dataiterator * and doing it's first iteration (::dataiterator_step) */ - iterator( scoped_ptr< ::_Dataiterator> & dip_r ); + iterator( detail::DIWrap & dip_r ); private: friend class boost::iterator_core_access; - ::_Dataiterator * cloneFrom( const ::_Dataiterator * rhs ); - template bool equal( const boost::iterator_adaptor & rhs ) const { @@ -433,7 +522,7 @@ namespace zypp ::_Dataiterator * get() const { return _dip.get(); } private: - scoped_ptr< ::_Dataiterator> _dip; + detail::DIWrap _dip; }; /////////////////////////////////////////////////////////////////// -- 2.7.4