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