20 |
#include <string.h> |
#include <string.h> |
21 |
#include <ctype.h> |
#include <ctype.h> |
22 |
|
|
23 |
/** |
#include "package.h" |
|
* Some functions in this file have been adopted from the rpm source, notably |
|
|
* 'rpmvercmp' located at lib/rpmvercmp.c and 'parseEVR' located at |
|
|
* lib/rpmds.c. It was most recently updated against rpm version 4.8.1. Small |
|
|
* modifications have been made to make it more consistent with the libalpm |
|
|
* coding style. |
|
|
*/ |
|
|
|
|
|
/** |
|
|
* Split EVR into epoch, version, and release components. |
|
|
* @param evr [epoch:]version[-release] string |
|
|
* @retval *ep pointer to epoch |
|
|
* @retval *vp pointer to version |
|
|
* @retval *rp pointer to release |
|
|
*/ |
|
|
static void parseEVR(char *evr, const char **ep, const char **vp, |
|
|
const char **rp) |
|
|
{ |
|
|
const char *epoch; |
|
|
const char *version; |
|
|
const char *release; |
|
|
char *s, *se; |
|
|
|
|
|
s = evr; |
|
|
/* s points to epoch terminator */ |
|
|
while (*s && isdigit(*s)) s++; |
|
|
/* se points to version terminator */ |
|
|
se = strrchr(s, '-'); |
|
|
|
|
|
if(*s == ':') { |
|
|
epoch = evr; |
|
|
*s++ = '\0'; |
|
|
version = s; |
|
|
if(*epoch == '\0') { |
|
|
epoch = "0"; |
|
|
} |
|
|
} else { |
|
|
/* different from RPM- always assume 0 epoch */ |
|
|
epoch = "0"; |
|
|
version = evr; |
|
|
} |
|
|
if(se) { |
|
|
*se++ = '\0'; |
|
|
release = se; |
|
|
} else { |
|
|
release = NULL; |
|
|
} |
|
|
|
|
|
if(ep) *ep = epoch; |
|
|
if(vp) *vp = version; |
|
|
if(rp) *rp = release; |
|
|
} |
|
|
|
|
|
/** |
|
|
* Compare alpha and numeric segments of two versions. |
|
|
* return 1: a is newer than b |
|
|
* 0: a and b are the same version |
|
|
* -1: b is newer than a |
|
|
*/ |
|
|
static int rpmvercmp(const char *a, const char *b) |
|
|
{ |
|
|
char oldch1, oldch2; |
|
|
char *str1, *str2; |
|
|
char *ptr1, *ptr2; |
|
|
char *one, *two; |
|
|
int rc; |
|
|
int isnum; |
|
|
int ret = 0; |
|
|
|
|
|
/* easy comparison to see if versions are identical */ |
|
|
if(strcmp(a, b) == 0) return 0; |
|
|
|
|
|
str1 = strdup(a); |
|
|
str2 = strdup(b); |
|
|
|
|
|
one = ptr1 = str1; |
|
|
two = ptr2 = str2; |
|
|
|
|
|
/* loop through each version segment of str1 and str2 and compare them */ |
|
|
while (*one && *two) { |
|
|
while (*one && !isalnum((int)*one)) one++; |
|
|
while (*two && !isalnum((int)*two)) two++; |
|
|
|
|
|
/* If we ran to the end of either, we are finished with the loop */ |
|
|
if (!(*one && *two)) break; |
|
|
|
|
|
/* If the separator lengths were different, we are also finished */ |
|
|
if ((one - ptr1) != (two - ptr2)) { |
|
|
return (one - ptr1) < (two - ptr2) ? -1 : 1; |
|
|
} |
|
|
|
|
|
ptr1 = one; |
|
|
ptr2 = two; |
|
|
|
|
|
/* grab first completely alpha or completely numeric segment */ |
|
|
/* leave one and two pointing to the start of the alpha or numeric */ |
|
|
/* segment and walk ptr1 and ptr2 to end of segment */ |
|
|
if (isdigit((int)*ptr1)) { |
|
|
while (*ptr1 && isdigit((int)*ptr1)) ptr1++; |
|
|
while (*ptr2 && isdigit((int)*ptr2)) ptr2++; |
|
|
isnum = 1; |
|
|
} else { |
|
|
while (*ptr1 && isalpha((int)*ptr1)) ptr1++; |
|
|
while (*ptr2 && isalpha((int)*ptr2)) ptr2++; |
|
|
isnum = 0; |
|
|
} |
|
|
|
|
|
/* save character at the end of the alpha or numeric segment */ |
|
|
/* so that they can be restored after the comparison */ |
|
|
oldch1 = *ptr1; |
|
|
*ptr1 = '\0'; |
|
|
oldch2 = *ptr2; |
|
|
*ptr2 = '\0'; |
|
|
|
|
|
/* this cannot happen, as we previously tested to make sure that */ |
|
|
/* the first string has a non-null segment */ |
|
|
if (one == ptr1) { |
|
|
ret = -1; /* arbitrary */ |
|
|
goto cleanup; |
|
|
} |
|
|
|
|
|
/* take care of the case where the two version segments are */ |
|
|
/* different types: one numeric, the other alpha (i.e. empty) */ |
|
|
/* numeric segments are always newer than alpha segments */ |
|
|
/* XXX See patch #60884 (and details) from bugzilla #50977. */ |
|
|
if (two == ptr2) { |
|
|
ret = isnum ? 1 : -1; |
|
|
goto cleanup; |
|
|
} |
|
|
|
|
|
if (isnum) { |
|
|
/* this used to be done by converting the digit segments */ |
|
|
/* to ints using atoi() - it's changed because long */ |
|
|
/* digit segments can overflow an int - this should fix that. */ |
|
|
|
|
|
/* throw away any leading zeros - it's a number, right? */ |
|
|
while (*one == '0') one++; |
|
|
while (*two == '0') two++; |
|
|
|
|
|
/* whichever number has more digits wins */ |
|
|
if (strlen(one) > strlen(two)) { |
|
|
ret = 1; |
|
|
goto cleanup; |
|
|
} |
|
|
if (strlen(two) > strlen(one)) { |
|
|
ret = -1; |
|
|
goto cleanup; |
|
|
} |
|
|
} |
|
|
|
|
|
/* strcmp will return which one is greater - even if the two */ |
|
|
/* segments are alpha or if they are numeric. don't return */ |
|
|
/* if they are equal because there might be more segments to */ |
|
|
/* compare */ |
|
|
rc = strcmp(one, two); |
|
|
if (rc) { |
|
|
ret = rc < 1 ? -1 : 1; |
|
|
goto cleanup; |
|
|
} |
|
|
|
|
|
/* restore character that was replaced by null above */ |
|
|
*ptr1 = oldch1; |
|
|
one = ptr1; |
|
|
*ptr2 = oldch2; |
|
|
two = ptr2; |
|
|
} |
|
|
|
|
|
/* this catches the case where all numeric and alpha segments have */ |
|
|
/* compared identically but the segment separating characters were */ |
|
|
/* different */ |
|
|
if ((!*one) && (!*two)) { |
|
|
ret = 0; |
|
|
goto cleanup; |
|
|
} |
|
|
|
|
|
/* the final showdown. we never want a remaining alpha string to |
|
|
* beat an empty string. the logic is a bit weird, but: |
|
|
* - if one is empty and two is not an alpha, two is newer. |
|
|
* - if one is an alpha, two is newer. |
|
|
* - otherwise one is newer. |
|
|
* */ |
|
|
if ( (!*one && !isalpha((int)*two)) |
|
|
|| isalpha((int)*one) ) { |
|
|
ret = -1; |
|
|
} else { |
|
|
ret = 1; |
|
|
} |
|
|
|
|
|
cleanup: |
|
|
free(str1); |
|
|
free(str2); |
|
|
return ret; |
|
|
} |
|
|
|
|
|
/** Compare two version strings and determine which one is 'newer'. |
|
|
* Returns a value comparable to the way strcmp works. Returns 1 |
|
|
* if a is newer than b, 0 if a and b are the same version, or -1 |
|
|
* if b is newer than a. |
|
|
* |
|
|
* Different epoch values for version strings will override any further |
|
|
* comparison. If no epoch is provided, 0 is assumed. |
|
|
* |
|
|
* Keep in mind that the pkgrel is only compared if it is available |
|
|
* on both versions handed to this function. For example, comparing |
|
|
* 1.5-1 and 1.5 will yield 0; comparing 1.5-1 and 1.5-2 will yield |
|
|
* -1 as expected. This is mainly for supporting versioned dependencies |
|
|
* that do not include the pkgrel. |
|
|
*/ |
|
|
int alpm_pkg_vercmp(const char *a, const char *b) |
|
|
{ |
|
|
char *full1, *full2; |
|
|
const char *epoch1, *ver1, *rel1; |
|
|
const char *epoch2, *ver2, *rel2; |
|
|
int ret; |
|
|
|
|
|
/* ensure our strings are not null */ |
|
|
if(!a && !b) { |
|
|
return 0; |
|
|
} else if(!a) { |
|
|
return -1; |
|
|
} else if(!b) { |
|
|
return 1; |
|
|
} |
|
|
/* another quick shortcut- if full version specs are equal */ |
|
|
if(strcmp(a, b) == 0) { |
|
|
return 0; |
|
|
} |
|
|
|
|
|
/* Parse both versions into [epoch:]version[-release] triplets. We probably |
|
|
* don't need epoch and release to support all the same magic, but it is |
|
|
* easier to just run it all through the same code. */ |
|
|
full1 = strdup(a); |
|
|
full2 = strdup(b); |
|
|
|
|
|
/* parseEVR modifies passed in version, so have to dupe it first */ |
|
|
parseEVR(full1, &epoch1, &ver1, &rel1); |
|
|
parseEVR(full2, &epoch2, &ver2, &rel2); |
|
|
|
|
|
ret = rpmvercmp(epoch1, epoch2); |
|
|
if(ret == 0) { |
|
|
ret = rpmvercmp(ver1, ver2); |
|
|
if(ret == 0 && rel1 && rel2) { |
|
|
ret = rpmvercmp(rel1, rel2); |
|
|
} |
|
|
} |
|
|
|
|
|
free(full1); |
|
|
free(full2); |
|
|
return ret; |
|
|
} |
|
24 |
|
|
25 |
#define BASENAME "vercmp" |
#define BASENAME "vercmp" |
26 |
|
|