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