2010-04-05 15:56:32 -04:00
|
|
|
/*
|
2011-01-05 23:45:15 -05:00
|
|
|
* Copyright (c) 2006-2011 Pacman Development Team <pacman-dev@archlinux.org>
|
2010-04-05 15:56:32 -04:00
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or modify
|
|
|
|
* it under the terms of the GNU General Public License as published by
|
|
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
|
|
* (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "config.h"
|
|
|
|
|
|
|
|
#include <string.h>
|
|
|
|
#include <ctype.h>
|
|
|
|
|
|
|
|
/* libalpm */
|
|
|
|
#include "util.h"
|
|
|
|
|
2011-01-10 11:44:25 -05:00
|
|
|
/**
|
|
|
|
* 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.
|
2010-04-05 15:56:32 -04:00
|
|
|
*/
|
2011-01-10 11:44:25 -05:00
|
|
|
|
|
|
|
/**
|
|
|
|
* 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)
|
2010-04-05 15:56:32 -04:00
|
|
|
{
|
|
|
|
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 */
|
2011-03-20 20:45:57 -04:00
|
|
|
if(strcmp(a, b) == 0) return 0;
|
2010-04-05 15:56:32 -04:00
|
|
|
|
|
|
|
str1 = strdup(a);
|
|
|
|
str2 = strdup(b);
|
|
|
|
|
|
|
|
one = str1;
|
|
|
|
two = 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;
|
|
|
|
|
|
|
|
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.
|
|
|
|
* */
|
2011-01-10 11:44:25 -05:00
|
|
|
if ( (!*one && !isalpha((int)*two))
|
2010-04-05 15:56:32 -04:00
|
|
|
|| isalpha((int)*one) ) {
|
|
|
|
ret = -1;
|
|
|
|
} else {
|
|
|
|
ret = 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
cleanup:
|
|
|
|
free(str1);
|
|
|
|
free(str2);
|
2011-03-20 20:45:57 -04:00
|
|
|
return ret;
|
2010-04-05 15:56:32 -04:00
|
|
|
}
|
|
|
|
|
2011-01-10 11:44:25 -05:00
|
|
|
/** 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 SYMEXPORT 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) {
|
2011-03-20 20:45:57 -04:00
|
|
|
return 0;
|
2011-01-10 11:44:25 -05:00
|
|
|
} else if(!a) {
|
2011-03-20 20:45:57 -04:00
|
|
|
return -1;
|
2011-01-10 11:44:25 -05:00
|
|
|
} else if(!b) {
|
2011-03-20 20:45:57 -04:00
|
|
|
return 1;
|
2011-01-10 11:44:25 -05:00
|
|
|
}
|
|
|
|
/* another quick shortcut- if full version specs are equal */
|
|
|
|
if(strcmp(a, b) == 0) {
|
2011-03-20 20:45:57 -04:00
|
|
|
return 0;
|
2011-01-10 11:44:25 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
/* 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);
|
2011-03-20 20:45:57 -04:00
|
|
|
return ret;
|
2011-01-10 11:44:25 -05:00
|
|
|
}
|
|
|
|
|
2010-04-05 15:56:32 -04:00
|
|
|
/* vim: set ts=2 sw=2 noet: */
|