1 /*
2  * Copyright (c) 2007-2009, Novell Inc.
3  *
4  * This program is licensed under the BSD license, read LICENSE.BSD
5  * for further information
6  */
7 
8 /*
9  * evr.c
10  *
11  * version compare
12  */
13 
14 #include <ctype.h>
15 #include <stdio.h>
16 #include <string.h>
17 #include "evr.h"
18 #include "pool.h"
19 
20 #ifdef ENABLE_CONDA
21 #include "conda.h"
22 #endif
23 
24 
25 #if defined(DEBIAN) || defined(MULTI_SEMANTICS)
26 
27 /* debian type version compare */
28 int
solv_vercmp_deb(const char * s1,const char * q1,const char * s2,const char * q2)29 solv_vercmp_deb(const char *s1, const char *q1, const char *s2, const char *q2)
30 {
31   int r, c1, c2;
32   while (1)
33     {
34       c1 = s1 < q1 ? *(const unsigned char *)s1++ : 0;
35       c2 = s2 < q2 ? *(const unsigned char *)s2++ : 0;
36       if ((c1 >= '0' && c1 <= '9') && (c2 >= '0' && c2 <= '9'))
37 	{
38 	  while (c1 == '0')
39             c1 = s1 < q1 ? *(const unsigned char *)s1++ : 0;
40 	  while (c2 == '0')
41             c2 = s2 < q2 ? *(const unsigned char *)s2++ : 0;
42 	  r = 0;
43 	  while ((c1 >= '0' && c1 <= '9') && (c2 >= '0' && c2 <= '9'))
44 	    {
45 	      if (!r)
46 		r = c1 - c2;
47               c1 = s1 < q1 ? *(const unsigned char *)s1++ : 0;
48               c2 = s2 < q2 ? *(const unsigned char *)s2++ : 0;
49 	    }
50 	  if (c1 >= '0' && c1 <= '9')
51 	    return 1;
52 	  if (c2 >= '0' && c2 <= '9')
53 	    return -1;
54 	  if (r)
55 	    return r < 0 ? -1 : 1;
56 	}
57       c1 = c1 == '~' ? -1 : !c1 || (c1 >= '0' && c1 <= '9') || (c1 >= 'A' && c1 <= 'Z') || (c1 >= 'a' && c1 <= 'z')  ? c1 : c1 + 256;
58       c2 = c2 == '~' ? -1 : !c2 || (c2 >= '0' && c2 <= '9') || (c2 >= 'A' && c2 <= 'Z') || (c2 >= 'a' && c2 <= 'z')  ? c2 : c2 + 256;
59       r = c1 - c2;
60       if (r)
61 	return r < 0 ? -1 : 1;
62       if (!c1)
63 	return 0;
64     }
65 }
66 
67 #endif
68 
69 #if !defined(DEBIAN) || defined(MULTI_SEMANTICS)
70 
71 /* rpm type version compare */
72 /* note: the code assumes that *q1 and *q2 are not alphanumeric! */
73 
74 int
solv_vercmp_rpm(const char * s1,const char * q1,const char * s2,const char * q2)75 solv_vercmp_rpm(const char *s1, const char *q1, const char *s2, const char *q2)
76 {
77   int r = 0;
78   const char *e1, *e2;
79 
80   for (;;)
81     {
82       while (s1 < q1 && !(*s1 >= '0' && *s1 <= '9') &&
83           !(*s1 >= 'a' && *s1 <= 'z') && !(*s1 >= 'A' && *s1 <= 'Z') && *s1 != '~' && *s1 != '^')
84 	s1++;
85       while (s2 < q2 && !(*s2 >= '0' && *s2 <= '9') &&
86           !(*s2 >= 'a' && *s2 <= 'z') && !(*s2 >= 'A' && *s2 <= 'Z') && *s2 != '~' && *s2 != '^')
87 	s2++;
88       if (s1 < q1 && *s1 == '~')
89         {
90 	  if (s2 < q2 && *s2 == '~')
91 	    {
92 	      s1++;
93 	      s2++;
94 	      continue;
95 	    }
96 	  return -1;
97         }
98       if (s2 < q2 && *s2 == '~')
99 	return 1;
100       if (s1 < q1 && *s1 == '^')
101 	{
102 	  if (s2 < q2 && *s2 == '^')
103 	    {
104 	      s1++;
105 	      s2++;
106 	      continue;
107 	    }
108 	  return s2 < q2 ? -1 : 1;
109 	}
110       if (s2 < q2 && *s2 == '^')
111 	return s1 < q1 ? 1 : -1;
112       if (s1 >= q1 || s2 >= q2)
113 	break;
114       if ((*s1 >= '0' && *s1 <= '9') || (*s2 >= '0' && *s2 <= '9'))
115 	{
116 	  while (*s1 == '0' && s1[1] >= '0' && s1[1] <= '9')
117 	    s1++;
118 	  while (*s2 == '0' && s2[1] >= '0' && s2[1] <= '9')
119 	    s2++;
120 	  for (e1 = s1; *e1 >= '0' && *e1 <= '9'; )
121 	    e1++;
122 	  for (e2 = s2; *e2 >= '0' && *e2 <= '9'; )
123 	    e2++;
124 	  r = (e1 - s1) - (e2 - s2);
125           if (!r)
126 	    r = strncmp(s1, s2, e1 - s1);
127           if (r)
128 	    return r > 0 ? 1 : -1;
129 	}
130       else
131 	{
132 	  for (e1 = s1; (*e1 >= 'a' && *e1 <= 'z') || (*e1 >= 'A' && *e1 <= 'Z'); )
133 	    e1++;
134 	  for (e2 = s2; (*e2 >= 'a' && *e2 <= 'z') || (*e2 >= 'A' && *e2 <= 'Z'); )
135 	    e2++;
136 	  r = (e1 - s1) - (e2 - s2);
137           if (r > 0)
138 	    {
139 	      r = strncmp(s1, s2, e2 - s2);
140 	      return r >= 0 ? 1 : -1;
141 	    }
142           if (r < 0)
143 	    {
144 	      r = strncmp(s1, s2, e1 - s1);
145 	      return r <= 0 ? -1 : 1;
146 	    }
147 	  r = strncmp(s1, s2, e1 - s1);
148 	  if (r)
149 	    return r > 0 ? 1 : -1;
150 	}
151       s1 = e1;
152       s2 = e2;
153     }
154   return s1 < q1 ? 1 : s2 < q2 ? -1 : 0;
155 }
156 
157 int
solv_vercmp_rpm_notilde(const char * s1,const char * q1,const char * s2,const char * q2)158 solv_vercmp_rpm_notilde(const char *s1, const char *q1, const char *s2, const char *q2)
159 {
160   int r = 0;
161   const char *e1, *e2;
162 
163   while (s1 < q1 && s2 < q2)
164     {
165       while (s1 < q1 && !(*s1 >= '0' && *s1 <= '9') &&
166           !(*s1 >= 'a' && *s1 <= 'z') && !(*s1 >= 'A' && *s1 <= 'Z'))
167 	s1++;
168       while (s2 < q2 && !(*s2 >= '0' && *s2 <= '9') &&
169           !(*s2 >= 'a' && *s2 <= 'z') && !(*s2 >= 'A' && *s2 <= 'Z'))
170 	s2++;
171       if ((*s1 >= '0' && *s1 <= '9') || (*s2 >= '0' && *s2 <= '9'))
172 	{
173 	  while (*s1 == '0' && s1[1] >= '0' && s1[1] <= '9')
174 	    s1++;
175 	  while (*s2 == '0' && s2[1] >= '0' && s2[1] <= '9')
176 	    s2++;
177 	  for (e1 = s1; *e1 >= '0' && *e1 <= '9'; )
178 	    e1++;
179 	  for (e2 = s2; *e2 >= '0' && *e2 <= '9'; )
180 	    e2++;
181 	  r = (e1 - s1) - (e2 - s2);
182           if (!r)
183 	    r = strncmp(s1, s2, e1 - s1);
184           if (r)
185 	    return r > 0 ? 1 : -1;
186 	}
187       else
188 	{
189 	  for (e1 = s1; (*e1 >= 'a' && *e1 <= 'z') || (*e1 >= 'A' && *e1 <= 'Z'); )
190 	    e1++;
191 	  for (e2 = s2; (*e2 >= 'a' && *e2 <= 'z') || (*e2 >= 'A' && *e2 <= 'Z'); )
192 	    e2++;
193 	  r = (e1 - s1) - (e2 - s2);
194           if (r > 0)
195 	    {
196 	      r = strncmp(s1, s2, e2 - s2);
197 	      return r >= 0 ? 1 : -1;
198 	    }
199           if (r < 0)
200 	    {
201 	      r = strncmp(s1, s2, e1 - s1);
202 	      return r <= 0 ? -1 : 1;
203 	    }
204 	  r = strncmp(s1, s2, e1 - s1);
205 	  if (r)
206 	    return r > 0 ? 1 : -1;
207 	}
208       s1 = e1;
209       s2 = e2;
210     }
211   return s1 < q1 ? 1 : s2 < q2 ? -1 : 0;
212 }
213 
214 #endif
215 
216 #if defined(HAIKU) || defined(MULTI_SEMANTICS)
217 
218 static int
solv_cmp_version_part_haiku(const char * s1,const char * q1,const char * s2,const char * q2)219 solv_cmp_version_part_haiku(const char *s1, const char *q1, const char *s2,
220   const char *q2)
221 {
222   while (s1 < q1 && s2 < q2)
223     {
224       int cmp, len1, len2;
225       const char *part1 = s1, *part2 = s2;
226 
227       /* compare non-number part */
228       while (s1 < q1 && !isdigit(*s1))
229         s1++;
230       while (s2 < q2 && !isdigit(*s2))
231         s2++;
232 
233       if (part1 != s1)
234         {
235           if (part2 == s2)
236             return 1;
237 
238           len1 = s1 - part1;
239           len2 = s2 - part2;
240           cmp = strncmp(part1, part2, len1 < len2 ? len1 : len2);
241           if (cmp != 0)
242             return cmp;
243           if (len1 != len2)
244             return len1 - len2;
245        }
246       else if (part2 != s2)
247         return -1;
248 
249       /* compare number part */
250       part1 = s1;
251       part2 = s2;
252 
253       while (s1 < q1 && isdigit(*s1))
254         s1++;
255       while (s2 < q2 && isdigit(*s2))
256         s2++;
257 
258       while (part1 + 1 < s1 && *part1 == '0')
259         part1++;
260       while (part2 + 1 < s2 && *part2 == '0')
261         part2++;
262 
263       len1 = s1 - part1;
264       len2 = s2 - part2;
265       if (len1 != len2)
266         return len1 - len2;
267       if (len1 == 0)
268         return 0;
269 
270       cmp = strncmp(part1, part2, len1);
271       if (cmp != 0)
272        return cmp;
273     }
274 
275   return s1 < q1 ? 1 : s2 < q2 ? -1 : 0;
276 }
277 
278 int
solv_vercmp_haiku(const char * s1,const char * q1,const char * s2,const char * q2)279 solv_vercmp_haiku(const char *s1, const char *q1, const char *s2, const char *q2)
280 {
281   const char *pre1 = s1;
282   const char *pre2 = s2;
283   int cmp;
284 
285   /* find pre-release separator */
286   while (pre1 != q1 && *pre1 != '~')
287     pre1++;
288   while (pre2 != q2 && *pre2 != '~')
289     pre2++;
290 
291   /* compare main versions */
292   cmp = solv_cmp_version_part_haiku(s1, pre1, s2, pre2);
293   if (cmp != 0)
294     return cmp < 0 ? -1 : 1; /* must return -1, 0, or 1 */
295 
296   /* main versions are equal -- compare pre-release (none is greatest) */
297   if (pre1 == q1)
298     return pre2 == q2 ? 0 : 1;
299   if (pre2 == q2)
300     return -1;
301 
302   cmp = solv_cmp_version_part_haiku(pre1 + 1, q1, pre2 + 1, q2);
303   return cmp == 0 ? 0 : cmp < 0 ? -1 : 1; /* must return -1, 0, or 1 */
304 }
305 
306 #endif /* HAIKU */
307 
308 
309 /*
310  * the solv_vercmp variant your system uses.
311  */
312 int
solv_vercmp(const char * s1,const char * q1,const char * s2,const char * q2)313 solv_vercmp(const char *s1, const char *q1, const char *s2, const char *q2)
314 {
315 #if defined(DEBIAN)
316   return solv_vercmp_deb(s1, q1, s2, q2);
317 #elif defined(ARCHLINUX)
318   return solv_vercmp_rpm_notilde(s1, q1, s2, q2);
319 #elif defined(HAIKU)
320   return solv_vercmp_haiku(s1, q1, s2, q2);
321 #else
322   return solv_vercmp_rpm(s1, q1, s2, q2);
323 #endif
324 }
325 
326 #if defined(MULTI_SEMANTICS)
327 # define solv_vercmp (*(pool->disttype == DISTTYPE_DEB ? &solv_vercmp_deb : \
328                         pool->disttype == DISTTYPE_HAIKU ? &solv_vercmp_haiku : \
329                         &solv_ver##cmp_rpm))
330 #elif defined(DEBIAN)
331 # define solv_vercmp solv_vercmp_deb
332 #elif defined(ARCHLINUX)
333 # define solv_vercmp solv_vercmp_rpm_notilde
334 #elif defined(HAIKU)
335 # define solv_vercmp solv_vercmp_haiku
336 #else
337 # define solv_vercmp solv_vercmp_rpm
338 #endif
339 
340 /* edition (e:v-r) compare */
341 int
pool_evrcmp_str(const Pool * pool,const char * evr1,const char * evr2,int mode)342 pool_evrcmp_str(const Pool *pool, const char *evr1, const char *evr2, int mode)
343 {
344   int r;
345   const char *s1, *s2;
346   const char *r1, *r2;
347 
348   if (evr1 == evr2)
349     return 0;
350 
351 #ifdef ENABLE_CONDA
352   if (pool->disttype == DISTTYPE_CONDA)
353     return pool_evrcmp_conda(pool, evr1, evr2, mode);
354 #endif
355 
356 #if 0
357   POOL_DEBUG(DEBUG_EVRCMP, "evrcmp %s %s mode=%d\n", evr1, evr2, mode);
358 #endif
359   for (s1 = evr1; *s1 >= '0' && *s1 <= '9'; s1++)
360     ;
361   for (s2 = evr2; *s2 >= '0' && *s2 <= '9'; s2++)
362     ;
363   if (mode == EVRCMP_MATCH && (*evr1 == ':' || *evr2 == ':'))
364     {
365       /* empty epoch, skip epoch check */
366       if (*s1 == ':')
367 	evr1 = s1 + 1;
368       if (*s2 == ':')
369 	evr2 = s2 + 1;
370       s1 = evr1;
371       s2 = evr2;
372     }
373 
374   /* compare the epoch */
375   if (s1 == evr1 || *s1 != ':')
376     s1 = 0;
377   if (s2 == evr2 || *s2 != ':')
378     s2 = 0;
379   if (s1 && s2)
380     {
381       r = solv_vercmp(evr1, s1, evr2, s2);
382       if (r)
383 	return r;
384       evr1 = s1 + 1;
385       evr2 = s2 + 1;
386     }
387   else if (s1)
388     {
389       if (!pool->promoteepoch)
390 	{
391 	  while (*evr1 == '0')
392 	    evr1++;
393 	  if (*evr1 != ':')
394 	    return 1;
395 	}
396       evr1 = s1 + 1;
397     }
398   else if (s2)
399     {
400       while (*evr2 == '0')
401 	evr2++;
402       if (*evr2 != ':')
403 	return -1;
404       evr2 = s2 + 1;
405     }
406 
407   /* same epoch, now split into version/release */
408   for (s1 = evr1, r1 = 0; *s1; s1++)
409     if (*s1 == '-')
410       r1 = s1;
411   for (s2 = evr2, r2 = 0; *s2; s2++)
412     if (*s2 == '-')
413       r2 = s2;
414   r = 0;
415   if (mode != EVRCMP_MATCH || (evr1 != (r1 ? r1 : s1) && evr2 != (r2 ? r2 : s2)))
416     r = solv_vercmp(evr1, r1 ? r1 : s1, evr2, r2 ? r2 : s2);
417   if (r)
418     return r;
419 
420   if (mode == EVRCMP_COMPARE)
421     {
422       if (!r1 && r2)
423 	return -1;
424       if (r1 && !r2)
425 	return 1;
426     }
427   if (mode == EVRCMP_COMPARE_EVONLY)
428     return 0;
429   if (mode == EVRCMP_MATCH_RELEASE)
430     {
431       /* rpm treats empty releases as missing, i.e "foo = 4-" is the same as "foo = 4" */
432       if (r1 && r1 + 1 == s1)
433 	r1 = 0;
434       if (r2 && r2 + 1 == s2)
435 	r2 = 0;
436     }
437   if (r1 && r2)
438     {
439       r1++;
440       r2++;
441       if (mode != EVRCMP_MATCH || (s1 != r1 && s2 != r2))
442 	{
443 	  if (pool->havedistepoch)
444 	    {
445 	      const char *d1, *d2;
446 	      for (d1 = r1; d1 < s1; d1++)
447 		if (*d1 == ':')
448 		  break;
449 	      for (d2 = r2; d2 < s2; d2++)
450 		if (*d2 == ':')
451 		  break;
452 	      /* XXX: promote just in one direction? */
453 	      r = solv_vercmp(r1, d1 ? d1 : s1, r2, d2 ? d2 : s2);
454 	      if (r == 0 && d1 < s1 && d2 < s2)
455 		r = solv_vercmp(d1 + 1, s1, d2 + 1, s2);
456 	    }
457 	  else
458             r = solv_vercmp(r1, s1, r2, s2);
459 	}
460     }
461   else if (mode == EVRCMP_MATCH_RELEASE)
462     {
463       if (!r1 && r2)
464 	return -2;
465       if (r1 && !r2)
466 	return 2;
467     }
468   return r;
469 }
470 
471 int
pool_evrcmp(const Pool * pool,Id evr1id,Id evr2id,int mode)472 pool_evrcmp(const Pool *pool, Id evr1id, Id evr2id, int mode)
473 {
474   const char *evr1, *evr2;
475   if (evr1id == evr2id)
476     return 0;
477   evr1 = pool_id2str(pool, evr1id);
478   evr2 = pool_id2str(pool, evr2id);
479   return pool_evrcmp_str(pool, evr1, evr2, mode);
480 }
481 
482 int
pool_evrmatch(const Pool * pool,Id evrid,const char * epoch,const char * version,const char * release)483 pool_evrmatch(const Pool *pool, Id evrid, const char *epoch, const char *version, const char *release)
484 {
485   const char *evr1;
486   const char *s1;
487   const char *r1;
488   int r;
489 
490   evr1 = pool_id2str(pool, evrid);
491   for (s1 = evr1; *s1 >= '0' && *s1 <= '9'; s1++)
492     ;
493   if (s1 != evr1 && *s1 == ':')
494     {
495       if (epoch)
496 	{
497 	  r = solv_vercmp(evr1, s1, epoch, epoch + strlen(epoch));
498 	  if (r)
499 	    return r;
500 	}
501       evr1 = s1 + 1;
502     }
503   else if (epoch)
504     {
505       while (*epoch == '0')
506 	epoch++;
507       if (*epoch)
508 	return -1;
509     }
510   for (s1 = evr1, r1 = 0; *s1; s1++)
511     if (*s1 == '-')
512       r1 = s1;
513   if (version)
514     {
515       r = solv_vercmp(evr1, r1 ? r1 : s1, version, version + strlen(version));
516       if (r)
517 	return r;
518     }
519   if (release)
520     {
521       if (!r1)
522 	return -1;
523       r = solv_vercmp(r1 + 1, s1, release, release + strlen(release));
524       if (r)
525 	return r;
526     }
527   return 0;
528 }
529 
530