summaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/libalpm/package.c152
1 files changed, 102 insertions, 50 deletions
diff --git a/lib/libalpm/package.c b/lib/libalpm/package.c
index 14c1df13..0b2bf6d1 100644
--- a/lib/libalpm/package.c
+++ b/lib/libalpm/package.c
@@ -581,102 +581,154 @@ alpm_list_t SYMEXPORT *alpm_pkg_compute_requiredby(pmpkg_t *pkg)
/** @} */
-/* this function was taken from rpm 4.0.4 and rewritten */
+/** 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.
+ *
+ * This function has been adopted from the rpmvercmp function located
+ * at lib/rpmvercmp.c, and was most recently updated against rpm
+ * version 4.4.2.3. Small modifications have been made to make it more
+ * consistent with the libalpm coding style.
+ */
int _alpm_versioncmp(const char *a, const char *b)
{
- char str1[64], str2[64];
+ char oldch1, oldch2;
+ char *str1, *str2;
char *ptr1, *ptr2;
char *one, *two;
- char *rel1 = NULL, *rel2 = NULL;
- char oldch1, oldch2;
- int is1num, is2num;
int rc;
+ int isnum;
+ int ret = 0;
ALPM_LOG_FUNC;
- if(!strcmp(a,b)) {
- return(0);
+ /* libalpm added code. ensure our strings are not null */
+ if(!a) {
+ if(!b) return(0);
+ return(-1);
}
+ if(!b) return(1);
- strncpy(str1, a, 64);
- str1[63] = 0;
- strncpy(str2, b, 64);
- str2[63] = 0;
+ /* easy comparison to see if versions are identical */
+ if(strcmp(a, b) == 0) return(0);
- /* lose the release number */
- for(one = str1; *one && *one != '-'; one++);
- if(one) {
- *one = '\0';
- rel1 = ++one;
- }
- for(two = str2; *two && *two != '-'; two++);
- if(two) {
- *two = '\0';
- rel2 = ++two;
- }
+ str1 = strdup(a);
+ str2 = strdup(b);
one = str1;
two = str2;
- while(*one || *two) {
+ /* 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;
+
ptr1 = one;
ptr2 = two;
- /* find the next segment for each string */
+ /* 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)) {
- is1num = 1;
while(*ptr1 && isdigit((int)*ptr1)) ptr1++;
- } else {
- is1num = 0;
- while(*ptr1 && isalpha((int)*ptr1)) ptr1++;
- }
- if(isdigit((int)*ptr2)) {
- is2num = 1;
while(*ptr2 && isdigit((int)*ptr2)) ptr2++;
+ isnum = 1;
} else {
- is2num = 0;
+ 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';
- /* see if we ran out of segments on one string */
- if(one == ptr1 && two != ptr2) {
- return(is2num ? -1 : 1);
+ /* 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;
}
- if(one != ptr1 && two == ptr2) {
- return(is1num ? 1 : -1);
+
+ /* 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;
}
- /* see if we have a type mismatch (ie, one is alpha and one is digits) */
- if(is1num && !is2num) return(1);
- if(!is1num && is2num) return(-1);
+ 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. */
- if(is1num) while(*one == '0') one++;
- if(is2num) while(*two == '0') two++;
+ /* throw away any leading zeros - it's a number, right? */
+ while (*one == '0') one++;
+ while (*two == '0') two++;
- rc = strverscmp(one, two);
- if(rc) return(rc);
+ /* 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;
- *ptr2 = oldch2;
one = ptr1;
+ *ptr2 = oldch2;
two = ptr2;
}
- if((!*one) && (!*two)) {
- /* compare release numbers */
- if(rel1 && rel2 && strlen(rel1) && strlen(rel2)) return(_alpm_versioncmp(rel1, rel2));
- return(0);
+ /* 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;
+ }
+
+ /* libalpm added code. one version string may have a pkgrel number, the
+ * other may not. unless both have them, we ignore it and return 0. */
+ if( (*one && *one == '-') || (*two && *two == '-') ) {
+ ret = 0;
+ goto cleanup;
}
- return(*one ? 1 : -1);
+ /* whichever version still has characters left over wins */
+ if (!*one) {
+ ret = -1;
+ } else {
+ ret = 1;
+ }
+
+cleanup:
+ free(str1);
+ free(str2);
+ return(ret);
}