diff options
-rw-r--r-- | lib/libalpm/package.c | 152 |
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); } |