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
solv_vercmp_deb(const char * s1,const char * q1,const char * s2,const char * q2)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
solv_vercmp_rpm(const char * s1,const char * q1,const char * s2,const char * q2)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
solv_vercmp_rpm_notilde(const char * s1,const char * q1,const char * s2,const char * q2)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
solv_cmp_version_part_haiku(const char * s1,const char * q1,const char * s2,const char * q2)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
solv_vercmp_haiku(const char * s1,const char * q1,const char * s2,const char * q2)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
solv_vercmp(const char * s1,const char * q1,const char * s2,const char * q2)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
pool_evrcmp_str(const Pool * pool,const char * evr1,const char * evr2,int mode)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
pool_evrcmp(const Pool * pool,Id evr1id,Id evr2id,int mode)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
pool_evrmatch(const Pool * pool,Id evrid,const char * epoch,const char * version,const char * release)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