From 86454a33cd6eac04ac22a7ec30d13d5eaa2607db Mon Sep 17 00:00:00 2001 From: Michael Andres Date: Wed, 23 Nov 2005 13:38:08 +0000 Subject: [PATCH] Edition: finalized comarison. --- zypp/Edition.cc | 180 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-- zypp/Edition.h | 35 +++++++---- 2 files changed, 199 insertions(+), 16 deletions(-) diff --git a/zypp/Edition.cc b/zypp/Edition.cc index c8cbae1..a674fd6 100644 --- a/zypp/Edition.cc +++ b/zypp/Edition.cc @@ -12,6 +12,8 @@ #include #include "zypp/base/Logger.h" +#include "base/String.h" + #include "zypp/Edition.h" using namespace std; @@ -22,6 +24,113 @@ namespace zypp /////////////////////////////////////////////////////////////////// // + // hidden details + // + /////////////////////////////////////////////////////////////////// + namespace + { + /** Rpm version comparison. + * \a lhs and \a rhs are expected to be either version or release + * strings. Not both separated by a '-'. + * \return -1,0,1 if version strings are \<,==,\> + * \todo review + */ + int rpmverscmp( const std::string & lhs, const std::string & rhs ) + { + int num1, num2; + char oldch1, oldch2; + char * str1, * str2; + char * one, * two; + int rc; + int isnum; + + // equal? + if ( lhs == rhs ) return 0; + + // empty is less than anything else: + if ( lhs.empty() ) return -1; + if ( rhs.empty() ) return 1; + + str1 = (char*)alloca( lhs.size() + 1 ); + str2 = (char*)alloca( rhs.size() + 1 ); + + strcpy( str1, lhs.c_str() ); + strcpy( str2, rhs.c_str() ); + + one = str1; + two = str2; + + // split strings into segments of alpha or digit + // sequences and compare them accordingly. + while ( *one && *two ) { + + // skip non alphanumerical chars + while ( *one && ! isalnum( *one ) ) ++one; + while ( *two && ! isalnum( *two ) ) ++two; + if ( ! ( *one && *two ) ) + break; // reached end of string + + // remember segment start + str1 = one; + str2 = two; + + // jump over segment, type determined by str1 + if ( isdigit( *str1 ) ) { + while ( isdigit( *str1 ) ) ++str1; + while ( isdigit( *str2 ) ) ++str2; + isnum = 1; + } else { + while ( isalpha( *str1 ) ) ++str1; + while ( isalpha( *str2 ) ) ++str2; + isnum = 0; + } + + // one == str1 -> can't be as strings are not empty + // two == str2 -> mixed segment types + if ( two == str2 ) return( isnum ? 1 : -1 ); + + // compare according to segment type + if ( isnum ) { + // atoi() may overflow on long segments + // skip leading zeros + while ( *one == '0' ) ++one; + while ( *two == '0' ) ++two; + // compare number of digits + num1 = str1 - one; + num2 = str2 - two; + if ( num1 != num2 ) return( num1 < num2 ? -1 : 1 ); + } + + // strcmp() compares alpha AND equal sized number segments + // temp. \0-terminate segment + oldch1 = *str1; + *str1 = '\0'; + oldch2 = *str2; + *str2 = '\0'; + + rc = strcmp( one, two ); + if ( rc ) return rc; + + // restore original strings + *str1 = oldch1; + *str2 = oldch2; + + // prepare for next cycle + one = str1; + two = str2; + } + + // check which strings are now empty + if ( !*one ) { + return( !*two ? 0 : -1 ); + } + return 1; + } + } + /////////////////////////////////////////////////////////////////// + + /////////////////////////////////////////////////////////////////// + // // CLASS NAME : Edition::Impl // /** Edition implementation */ @@ -69,10 +178,6 @@ namespace zypp Edition::~Edition() {} - /** \todo Beautyfy */ - std::string Edition::asString() const - { return _pimpl->_version + "-" + _pimpl->_release; } - Edition::epoch_t Edition::epoch() const { return _pimpl->_epoch; } @@ -82,11 +187,76 @@ namespace zypp const std::string & Edition::release() const { return _pimpl->_release; } - /** \todo copy implementation from Y2PM */ + std::string Edition::asString() const + { + string ret; + + if ( _pimpl->_epoch ) + ret += base::string::form( "%d:", _pimpl->_epoch ); + + ret += _pimpl->_version; + + if ( ! _pimpl->_release.empty() ) + { + ret += '-'; + ret += _pimpl->_release; + } + + if ( ret.empty() ) + return "EDITION-UNSPEC"; + + return ret; + } + bool Edition::compare( Rel op, const Edition & lhs, const Edition & rhs ) { + switch ( op.inSwitch() ) + { + case Rel::EQ_e: + return compare( lhs, rhs ) == 0; + break; + case Rel::NE_e: + return compare( lhs, rhs ) != 0; + break; + case Rel::LT_e: + return compare( lhs, rhs ) == -1; + break; + case Rel::LE_e: + return compare( lhs, rhs ) != 1; + break; + case Rel::GT_e: + return compare( lhs, rhs ) == 1; + break; + case Rel::GE_e: + return compare( lhs, rhs ) != -1; + break; + case Rel::ANY_e: + return true; + break; + case Rel::NONE_e: + return false; + break; + } + // We shouldn't get here. + INT << "Unknown relational opertor '" << op << "' treated as 'NONE'" << endl; return false; } + + int Edition::compare( const Edition & lhs, const Edition & rhs ) + { + // compare epoch + if ( lhs.epoch() != rhs.epoch() ) + return lhs.epoch() < rhs.epoch() ? -1 : 1; + + // next compare versions + int res = rpmverscmp( lhs.version(), rhs.version() ); + if ( res ) + return res; // -1|1: not equal + + // finaly compare releases + return rpmverscmp( lhs.release(), rhs.release() ); + } + ///////////////////////////////////////////////////////////////// } // namespace zypp /////////////////////////////////////////////////////////////////// diff --git a/zypp/Edition.h b/zypp/Edition.h index 06ebf0a..c364062 100644 --- a/zypp/Edition.h +++ b/zypp/Edition.h @@ -27,40 +27,57 @@ namespace zypp // // CLASS NAME : Edition // - /** */ + /** Edition + \todo doc + \todo optimize implementation + \todo implement debian comparison and make choice backend specific + */ class Edition { public: /** Type of an epoch. */ typedef unsigned epoch_t; + /** Value representing \c noepoch. */ + static const epoch_t noepoch = 0; + public: /** Default ctor. */ Edition(); /** Ctor taking \a version_r, \a release_r and optional \a epoch_r */ Edition( const std::string & version_r, const std::string & release_r, - epoch_t epoch_r = 0 ); + epoch_t epoch_r = noepoch ); /** Dtor */ ~Edition(); public: - /** */ + /** Epoch */ epoch_t epoch() const; - /** */ + /** Version */ const std::string & version() const; - /** */ + /** Release */ const std::string & release() const; /** String representation of Edition. */ std::string asString() const; public: - /** Compare Editions by relationam operator \a op. - * \see Rel. + /** Compare two Editions using relational operator \a op. + * \return Result of expression \t ( \a lhs \a op \a rhs \t ).
+ * If \a op is Rel::ANY, the expression is always \c true.
+ * If \a op is Rel::NONE, the expression is always \c false. + * + * \todo optimize impementation. currently a full compare( lhs, rhs ) + * is done and the result evaluated. But step by step would be faster. */ static bool compare( Rel op, const Edition & lhs, const Edition & rhs ); + /** Compare two Editions returning -1,0,1. + * \return -1,0,1 if editions are \<,==,\> + */ + static int compare( const Edition & lhs, const Edition & rhs ); + private: /** Hides implementation */ struct Impl; @@ -100,10 +117,6 @@ namespace zypp inline bool operator>=( const Edition & lhs, const Edition & rhs ) { return Edition::compare( Rel::GE, lhs, rhs ); } - /** \relates Edition */ - inline int compare( const Edition & lhs, const Edition & rhs ) - { return lhs == rhs ? 0 : ( lhs < rhs ? -1 : 1 ); } - //@} ///////////////////////////////////////////////////////////////// -- 2.7.4