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