ignore
[platform/upstream/libzypp.git] / zypp / Edition.cc
1 /*---------------------------------------------------------------------\
2 |                          ____ _   __ __ ___                          |
3 |                         |__  / \ / / . \ . \                         |
4 |                           / / \ V /|  _/  _/                         |
5 |                          / /__ | | | | | |                           |
6 |                         /_____||_| |_| |_|                           |
7 |                                                                      |
8 \---------------------------------------------------------------------*/
9 /** \file zypp/Edition.cc
10  *
11 */
12 #include <iostream>
13
14 #include "zypp/base/Logger.h"
15 #include "base/String.h"
16 #include "base/Exception.h"
17
18 #include "zypp/Edition.h"
19
20 using namespace std;
21
22 ///////////////////////////////////////////////////////////////////
23 namespace zypp
24 { /////////////////////////////////////////////////////////////////
25
26   ///////////////////////////////////////////////////////////////////
27   //
28   //    hidden details
29   //
30   ///////////////////////////////////////////////////////////////////
31   namespace
32   {
33     /** Rpm version comparison.
34      * \a lhs and \a rhs are expected to be either version or release
35      * strings. Not both separated by a '-'.
36      * \return <tt>-1,0,1</tt> if version strings are <tt>\<,==,\></tt>
37      * \todo review
38     */
39     int rpmverscmp( const std::string & lhs, const std::string & rhs )
40     {
41       int  num1, num2;
42       char oldch1, oldch2;
43       char * str1, * str2;
44       char * one, * two;
45       int  rc;
46       int  isnum;
47
48       // equal?
49       if ( lhs == rhs )  return 0;
50
51       // empty is less than anything else:
52       if ( lhs.empty() ) return -1;
53       if ( rhs.empty() ) return  1;
54
55       str1 = (char*)alloca( lhs.size() + 1 );
56       str2 = (char*)alloca( rhs.size() + 1 );
57
58       strcpy( str1, lhs.c_str() );
59       strcpy( str2, rhs.c_str() );
60
61       one = str1;
62       two = str2;
63
64       // split strings into segments of alpha or digit
65       // sequences and compare them accordingly.
66       while ( *one && *two ) {
67
68         // skip non alphanumerical chars
69         while ( *one && ! isalnum( *one ) ) ++one;
70         while ( *two && ! isalnum( *two ) ) ++two;
71         if ( ! ( *one && *two ) )
72           break; // reached end of string
73
74         // remember segment start
75         str1 = one;
76         str2 = two;
77
78         // jump over segment, type determined by str1
79         if ( isdigit( *str1 ) ) {
80           while ( isdigit( *str1 ) ) ++str1;
81           while ( isdigit( *str2 ) ) ++str2;
82           isnum = 1;
83         } else {
84           while ( isalpha( *str1 ) ) ++str1;
85           while ( isalpha( *str2 ) ) ++str2;
86           isnum = 0;
87         }
88
89         // one == str1 -> can't be as strings are not empty
90         // two == str2 -> mixed segment types
91         if ( two == str2 ) return( isnum ? 1 : -1 );
92
93         // compare according to segment type
94         if ( isnum ) {
95           // atoi() may overflow on long segments
96           // skip leading zeros
97           while ( *one == '0' ) ++one;
98           while ( *two == '0' ) ++two;
99           // compare number of digits
100           num1 = str1 - one;
101           num2 = str2 - two;
102           if ( num1 != num2 ) return( num1 < num2 ? -1 : 1 );
103         }
104
105         // strcmp() compares alpha AND equal sized number segments
106         // temp. \0-terminate segment
107         oldch1 = *str1;
108         *str1 = '\0';
109         oldch2 = *str2;
110         *str2 = '\0';
111
112         rc = strcmp( one, two );
113         if ( rc ) return rc;
114
115         // restore original strings
116         *str1 = oldch1;
117         *str2 = oldch2;
118
119         // prepare for next cycle
120         one = str1;
121         two = str2;
122       }
123
124       // check which strings are now empty
125       if ( !*one ) {
126         return( !*two ? 0 : -1 );
127       }
128       return 1;
129     }
130   } // namespace
131   ///////////////////////////////////////////////////////////////////
132
133   ///////////////////////////////////////////////////////////////////
134   //
135   //    CLASS NAME : Edition::Impl
136   //
137   /** Edition implementation.
138    * \todo Unifiy Impl in Edition::noedition and Edition::Edition()
139   */
140   struct Edition::Impl
141   {
142     Impl()
143     : _epoch( noepoch )
144     {}
145
146     Impl( const std::string & edition_r )
147     : _epoch( noepoch )
148     {
149       str::smatch what;
150       if( str::regex_match( edition_r.begin(), edition_r.end(),
151                             what, _rxEdition ) )
152         {
153           // what[2] contains the epoch
154           // what[3] contains the version
155           // what[5] contains the release
156           if ( what[2].matched )
157             _epoch = strtoul( what[2].str().c_str(), NULL, 10 );
158           if ( what[3].matched )
159             _version = what[3].str();
160           if ( what[5].matched )
161             _release = what[5].str();
162         }
163       else
164         {
165           ZYPP_THROW( Exception(string("Invalid Edition: ")+edition_r) );
166         }
167     }
168
169     Impl( const std::string & version_r,
170           const std::string & release_r,
171           epoch_t epoch_r )
172     : _epoch( epoch_r )
173     , _version( validateVR(version_r) )
174     , _release( validateVR(release_r) )
175     {}
176
177     Impl( const std::string & version_r,
178           const std::string & release_r,
179           const std::string & epoch_r )
180     : _epoch( validateE(epoch_r) )
181     , _version( validateVR(version_r) )
182     , _release( validateVR(release_r) )
183     {}
184
185     /** Dtor */
186     ~Impl()
187     {}
188
189     /** return validated epoch ([0-9]*) or throw */
190     static epoch_t validateE( const std::string & epoch_r )
191     {
192       if ( epoch_r.empty() )
193         return noepoch;
194
195       char * endptr = NULL;
196       epoch_t ret = strtoul( epoch_r.c_str(), &endptr, 10 );
197       if ( *endptr != '\0' )
198         ZYPP_THROW( Exception(string("Invalid eopch: ")+epoch_r) );
199       return ret;
200     }
201
202     /** return validated version/release or throw */
203     static const std::string & validateVR( const std::string & vr_r )
204     {
205       str::smatch what;
206       if( ! str::regex_match( vr_r.begin(), vr_r.end(), what, _rxVR ) )
207         ZYPP_THROW( Exception(string("Invalid version/release: ")+vr_r) );
208       return vr_r;
209     }
210
211     epoch_t      _epoch;
212     std::string _version;
213     std::string _release;
214
215     static const str::regex _rxVR;
216     static const str::regex _rxEdition;
217   };
218   ///////////////////////////////////////////////////////////////////
219
220   const str::regex Edition::Impl::_rxVR( "([^-]*)" );
221
222   const str::regex Edition::Impl::_rxEdition( "(([0-9]+):)?([^-]*)(-([^-]*))?" );
223
224   ///////////////////////////////////////////////////////////////////
225
226   ///////////////////////////////////////////////////////////////////
227   //
228   //    CLASS NAME : Edition
229   //
230   ///////////////////////////////////////////////////////////////////
231
232   const Edition Edition::noedition;
233
234   ///////////////////////////////////////////////////////////////////
235
236   Edition::Edition()
237   : _pimpl( new Impl )
238   {}
239
240   Edition::Edition( const std::string & edition_r )
241   : _pimpl( new Impl( edition_r ) )
242   {}
243
244   Edition::Edition( const std::string & version_r,
245                     const std::string & release_r,
246                     epoch_t epoch_r )
247   : _pimpl( new Impl( version_r, release_r, epoch_r ) )
248   {}
249
250   Edition::Edition( const std::string & version_r,
251                     const std::string & release_r,
252                     const std::string & epoch_r )
253   : _pimpl( new Impl( version_r, release_r, epoch_r ) )
254   {}
255
256   Edition::~Edition()
257   {}
258
259   Edition::epoch_t Edition::epoch() const
260   { return _pimpl->_epoch; }
261
262   const std::string & Edition::version() const
263   { return _pimpl->_version; }
264
265   const std::string & Edition::release() const
266   { return _pimpl->_release; }
267
268   std::string Edition::asString() const
269   {
270     string ret;
271
272     if ( _pimpl->_epoch )
273       ret += str::form(  "%d:", _pimpl->_epoch );
274
275     ret += _pimpl->_version;
276
277     if ( ! _pimpl->_release.empty() )
278       {
279         ret += '-';
280         ret += _pimpl->_release;
281       }
282
283     if ( ret.empty() )
284       return "";
285
286     return ret;
287   }
288
289   int Edition::compare( const Edition & lhs, const Edition & rhs )
290   {
291     // compare epoch
292     if ( lhs.epoch() != rhs.epoch() )
293       return lhs.epoch() < rhs.epoch() ? -1 : 1;
294
295     // next compare versions
296     int res = rpmverscmp( lhs.version(), rhs.version() );
297     if ( res )
298       return res; // -1|1: not equal
299
300     return rpmverscmp( lhs.release(), rhs.release() );
301   }
302
303   int Edition::match( const Edition & lhs, const Edition & rhs )
304   {
305     // compare epoch
306     if ( lhs.epoch() != rhs.epoch() )
307       return lhs.epoch() < rhs.epoch() ? -1 : 1;
308
309     // next compare versions
310     if ( lhs.version().empty() || rhs.version().empty() )
311       return 0; //equal
312
313     int res = rpmverscmp( lhs.version(), rhs.version() );
314     if ( res )
315       return res; // -1|1: not equal
316
317     // finaly compare releases
318     if ( lhs.release().empty() || rhs.release().empty() )
319       return 0; //equal
320
321     return rpmverscmp( lhs.release(), rhs.release() );
322   }
323
324   /////////////////////////////////////////////////////////////////
325 } // namespace zypp
326 ///////////////////////////////////////////////////////////////////