Edition.cc

Go to the documentation of this file.
00001 /*---------------------------------------------------------------------\
00002 |                          ____ _   __ __ ___                          |
00003 |                         |__  / \ / / . \ . \                         |
00004 |                           / / \ V /|  _/  _/                         |
00005 |                          / /__ | | | | | |                           |
00006 |                         /_____||_| |_| |_|                           |
00007 |                                                                      |
00008 \---------------------------------------------------------------------*/
00012 #include <iostream>
00013 
00014 #include "zypp/base/Logger.h"
00015 #include "base/String.h"
00016 #include "base/Regex.h"
00017 #include "base/Exception.h"
00018 
00019 #include "zypp/Edition.h"
00020 
00021 using namespace std;
00022 
00024 namespace zypp
00025 { 
00026 
00028   //
00029   //    hidden details
00030   //
00032   namespace
00033   {
00040     int rpmverscmp( const std::string & lhs, const std::string & rhs )
00041     {
00042       int  num1, num2;
00043       char oldch1, oldch2;
00044       char * str1, * str2;
00045       char * one, * two;
00046       int  rc;
00047       int  isnum;
00048 
00049       // equal?
00050       if ( lhs == rhs )  return 0;
00051 
00052       // empty is less than anything else:
00053       if ( lhs.empty() ) return -1;
00054       if ( rhs.empty() ) return  1;
00055 
00056       str1 = (char*)alloca( lhs.size() + 1 );
00057       str2 = (char*)alloca( rhs.size() + 1 );
00058 
00059       strcpy( str1, lhs.c_str() );
00060       strcpy( str2, rhs.c_str() );
00061 
00062       one = str1;
00063       two = str2;
00064 
00065       // split strings into segments of alpha or digit
00066       // sequences and compare them accordingly.
00067       while ( *one && *two ) {
00068 
00069         // skip non alphanumerical chars
00070         while ( *one && ! isalnum( *one ) ) ++one;
00071         while ( *two && ! isalnum( *two ) ) ++two;
00072         if ( ! ( *one && *two ) )
00073           break; // reached end of string
00074 
00075         // remember segment start
00076         str1 = one;
00077         str2 = two;
00078 
00079         // jump over segment, type determined by str1
00080         if ( isdigit( *str1 ) ) {
00081           while ( isdigit( *str1 ) ) ++str1;
00082           while ( isdigit( *str2 ) ) ++str2;
00083           isnum = 1;
00084         } else {
00085           while ( isalpha( *str1 ) ) ++str1;
00086           while ( isalpha( *str2 ) ) ++str2;
00087           isnum = 0;
00088         }
00089 
00090         // one == str1 -> can't be as strings are not empty
00091         // two == str2 -> mixed segment types
00092         if ( two == str2 ) return( isnum ? 1 : -1 );
00093 
00094         // compare according to segment type
00095         if ( isnum ) {
00096           // atoi() may overflow on long segments
00097           // skip leading zeros
00098           while ( *one == '0' ) ++one;
00099           while ( *two == '0' ) ++two;
00100           // compare number of digits
00101           num1 = str1 - one;
00102           num2 = str2 - two;
00103           if ( num1 != num2 ) return( num1 < num2 ? -1 : 1 );
00104         }
00105 
00106         // strcmp() compares alpha AND equal sized number segments
00107         // temp. \0-terminate segment
00108         oldch1 = *str1;
00109         *str1 = '\0';
00110         oldch2 = *str2;
00111         *str2 = '\0';
00112 
00113         rc = strcmp( one, two );
00114         if ( rc ) return rc;
00115 
00116         // restore original strings
00117         *str1 = oldch1;
00118         *str2 = oldch2;
00119 
00120         // prepare for next cycle
00121         one = str1;
00122         two = str2;
00123       }
00124 
00125       // check which strings are now empty
00126       if ( !*one ) {
00127         return( !*two ? 0 : -1 );
00128       }
00129       return 1;
00130     }
00131   } // namespace
00133 
00135   //
00136   //    CLASS NAME : Edition::Impl
00137   //
00141   struct Edition::Impl
00142   {
00143     Impl()
00144     : _epoch( noepoch )
00145     {}
00146 
00147     Impl( const std::string & edition_r )
00148     : _epoch( noepoch )
00149     {
00150       //[0-9]+:)?([^-]*)(-([^-]*))?" );
00151       str::smatch what;
00152 
00153       if( str::regex_match( edition_r, what, _rxEdition )
00154           && what[3].size() != 1)
00155         {
00156           if ( what[1].size() > 1 )
00157             _epoch = strtoul( what[1].c_str(), NULL, 10 );
00158           if ( what[2].size() )
00159             _version = what[2];
00160           if (what[3].size() )
00161             _release = what[3].substr(1);
00162         }
00163       else
00164         {
00165           ZYPP_THROW( Exception(string("Invalid Edition: ")+edition_r) );
00166         }
00167     }
00168 
00169     Impl( const std::string & version_r,
00170           const std::string & release_r,
00171           epoch_t epoch_r )
00172     : _epoch( epoch_r )
00173     , _version( validateVR(version_r) )
00174     , _release( validateVR(release_r) )
00175     {}
00176 
00177     Impl( const std::string & version_r,
00178           const std::string & release_r,
00179           const std::string & epoch_r )
00180     : _epoch( validateE(epoch_r) )
00181     , _version( validateVR(version_r) )
00182     , _release( validateVR(release_r) )
00183     {}
00184 
00186     ~Impl()
00187     {}
00188 
00190     static epoch_t validateE( const std::string & epoch_r )
00191     {
00192       if ( epoch_r.empty() )
00193         return noepoch;
00194 
00195       char * endptr = NULL;
00196       epoch_t ret = strtoul( epoch_r.c_str(), &endptr, 10 );
00197       if ( *endptr != '\0' )
00198         ZYPP_THROW( Exception(string("Invalid eopch: ")+epoch_r) );
00199       return ret;
00200     }
00201 
00203     static const std::string & validateVR( const std::string & vr_r )
00204     {
00205       if ( vr_r.find('-') != string::npos )
00206         ZYPP_THROW( Exception(string("Invalid version/release: ")+vr_r) );
00207     
00208       return vr_r;
00209     }
00210 
00211     epoch_t      _epoch;
00212     std::string _version;
00213     std::string _release;
00214 
00215     static const str::regex _rxEdition;
00216   };
00218 
00219   const str::regex Edition::Impl::_rxEdition( "([0-9]+:)?([^-]*)(-[^-]*)?" );
00220 
00222 
00224   //
00225   //    CLASS NAME : Edition
00226   //
00228 
00229   const Edition Edition::noedition;
00230 
00232 
00233   Edition::Edition()
00234   : _pimpl( new Impl )
00235   {}
00236 
00237   Edition::Edition( const std::string & edition_r )
00238   : _pimpl( new Impl( edition_r ) )
00239   {}
00240 
00241   Edition::Edition( const std::string & version_r,
00242                     const std::string & release_r,
00243                     epoch_t epoch_r )
00244   : _pimpl( new Impl( version_r, release_r, epoch_r ) )
00245   {}
00246 
00247   Edition::Edition( const std::string & version_r,
00248                     const std::string & release_r,
00249                     const std::string & epoch_r )
00250   : _pimpl( new Impl( version_r, release_r, epoch_r ) )
00251   {}
00252 
00253   Edition::~Edition()
00254   {}
00255 
00256   Edition::epoch_t Edition::epoch() const
00257   { return _pimpl->_epoch; }
00258 
00259   const std::string & Edition::version() const
00260   { return _pimpl->_version; }
00261 
00262   const std::string & Edition::release() const
00263   { return _pimpl->_release; }
00264 
00265   std::string Edition::asString() const
00266   {
00267     string ret;
00268 
00269     if ( _pimpl->_epoch )
00270       ret += str::form(  "%d:", _pimpl->_epoch );
00271 
00272     ret += _pimpl->_version;
00273 
00274     if ( ! _pimpl->_release.empty() )
00275       {
00276         ret += '-';
00277         ret += _pimpl->_release;
00278       }
00279 
00280     if ( ret.empty() )
00281       return "";
00282 
00283     return ret;
00284   }
00285 
00286   int Edition::compare( const Edition & lhs, const Edition & rhs )
00287   {
00288     // compare epoch
00289     if ( lhs.epoch() != rhs.epoch() )
00290       return lhs.epoch() < rhs.epoch() ? -1 : 1;
00291 
00292     // next compare versions
00293     int res = rpmverscmp( lhs.version(), rhs.version() );
00294     if ( res )
00295       return res; // -1|1: not equal
00296 
00297     return rpmverscmp( lhs.release(), rhs.release() );
00298   }
00299 
00300   int Edition::match( const Edition & lhs, const Edition & rhs )
00301   {
00302     // compare epoch
00303     if ( lhs.epoch() != rhs.epoch() )
00304       return lhs.epoch() < rhs.epoch() ? -1 : 1;
00305 
00306     // next compare versions
00307     if ( lhs.version().empty() || rhs.version().empty() )
00308       return 0; //equal
00309 
00310     int res = rpmverscmp( lhs.version(), rhs.version() );
00311     if ( res )
00312       return res; // -1|1: not equal
00313 
00314     // finaly compare releases
00315     if ( lhs.release().empty() || rhs.release().empty() )
00316       return 0; //equal
00317 
00318     return rpmverscmp( lhs.release(), rhs.release() );
00319   }
00320 
00322 } // namespace zypp
00324 
00325 

Generated on Tue Sep 25 19:23:01 2007 for libzypp by  doxygen 1.5.3