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