219 lines
6.0 KiB
C
219 lines
6.0 KiB
C
/** \ingroup rpmtrans
|
|
* \file lib/rpmvercmp.c
|
|
*/
|
|
|
|
#include "system.h"
|
|
|
|
#include "rpmlib.h"
|
|
|
|
#include "debug.h"
|
|
|
|
/* 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 */
|
|
int rpmvercmp(const char * a, const char * b)
|
|
{
|
|
char oldch1, oldch2;
|
|
char * str1, * str2;
|
|
char * one, * two;
|
|
int rc;
|
|
int isnum;
|
|
|
|
/* easy comparison to see if versions are identical */
|
|
if (!strcmp(a, b)) return 0;
|
|
|
|
str1 = alloca(strlen(a) + 1);
|
|
str2 = alloca(strlen(b) + 1);
|
|
|
|
strcpy(str1, a);
|
|
strcpy(str2, b);
|
|
|
|
one = str1;
|
|
two = str2;
|
|
|
|
/* loop through each version segment of str1 and str2 and compare them */
|
|
while (*one && *two) {
|
|
while (*one && !xisalnum(*one)) one++;
|
|
while (*two && !xisalnum(*two)) two++;
|
|
|
|
if ( !*one && !*two )
|
|
return 0;
|
|
|
|
str1 = one;
|
|
str2 = 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 str1 and str2 to end of segment */
|
|
/* Also take care of the case where the two version segments are */
|
|
/* different types: one numeric and one alpha */
|
|
if (xisdigit(*str1)) {
|
|
if ( xisalpha(*str2) ) return -1;
|
|
while (*str1 && xisdigit(*str1)) str1++;
|
|
while (*str2 && xisdigit(*str2)) str2++;
|
|
isnum = 1;
|
|
} else {
|
|
while (*str1 && xisalpha(*str1)) str1++;
|
|
while (*str2 && xisalpha(*str2)) str2++;
|
|
isnum = 0;
|
|
}
|
|
|
|
/* Again, take care of the case where the two version segments are */
|
|
/* different types: one numeric and one alpha */
|
|
if (one == str1) return -1;
|
|
if (two == str2) return 1;
|
|
|
|
/* save character at the end of the alpha or numeric segment */
|
|
/* so that they can be restored after the comparison */
|
|
oldch1 = *str1;
|
|
*str1 = '\0';
|
|
oldch2 = *str2;
|
|
*str2 = '\0';
|
|
|
|
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)) return 1;
|
|
if (strlen(two) > strlen(one)) return -1;
|
|
}
|
|
|
|
/* 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) return rc;
|
|
|
|
/* restore character that was replaced by null above */
|
|
*str1 = oldch1;
|
|
one = str1;
|
|
*str2 = oldch2;
|
|
two = str2;
|
|
}
|
|
|
|
/* this catches the case where all numeric and alpha segments have */
|
|
/* compared identically but the segment sepparating characters were */
|
|
/* different */
|
|
if ((!*one) && (!*two)) return 0;
|
|
|
|
/* whichever version still has characters left over wins */
|
|
if (!*one) return -1; else return 1;
|
|
}
|
|
|
|
/* Moved from depends.c, because we use it in other places, too. */
|
|
/**
|
|
* 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
|
|
*/
|
|
void parseEVR(char * evr,
|
|
/*@exposed@*/ /*@out@*/ const char ** ep,
|
|
/*@exposed@*/ /*@out@*/ const char ** vp,
|
|
/*@exposed@*/ /*@out@*/ const char ** rp)
|
|
/*@modifies *ep, *vp, *rp @*/
|
|
{
|
|
const char *epoch;
|
|
const char *version; /* assume only version is present */
|
|
const char *release;
|
|
char *s, *se;
|
|
|
|
s = evr;
|
|
while (*s && xisdigit(*s)) s++; /* s points to epoch terminator */
|
|
se = strrchr(s, '-'); /* se points to version terminator */
|
|
|
|
if (*s == ':') {
|
|
epoch = evr;
|
|
*s++ = '\0';
|
|
version = s;
|
|
if (*epoch == '\0') epoch = "0";
|
|
} else {
|
|
epoch = NULL; /* XXX disable epoch compare if missing */
|
|
version = evr;
|
|
}
|
|
if (se) {
|
|
*se++ = '\0';
|
|
release = se;
|
|
} else {
|
|
release = NULL;
|
|
}
|
|
|
|
if (ep) *ep = epoch;
|
|
if (vp) *vp = version;
|
|
if (rp) *rp = release;
|
|
}
|
|
|
|
/* Compare {A,B} [epoch:]version[-release] */
|
|
int
|
|
rpmEVRcmp(const char * const aE, const char * const aV, const char * const aR,
|
|
const char * const aDepend,
|
|
const char * const bE, const char * const bV, const char * const bR,
|
|
const char * const bDepend)
|
|
{
|
|
int sense = 0;
|
|
|
|
rpmMessage(RPMMESS_DEBUG, "cmp e=%s, v=%s, r=%s\n and e=%s, v=%s, r=%s\n ",
|
|
aE, aV, aR, bE, bV, bR);
|
|
|
|
|
|
if (aE && *aE && bE && *bE)
|
|
sense = rpmvercmp(aE, bE);
|
|
else if (aE && *aE && atol(aE) > 0) {
|
|
/* XXX legacy epoch-less requires/conflicts compatibility */
|
|
rpmMessage(RPMMESS_DEBUG, _("the \"B\" dependency needs an epoch (assuming same as \"A\")\n\tA %s\tB %s\n"),
|
|
aDepend, bDepend);
|
|
sense = 0;
|
|
} else if (bE && *bE && atol(bE) > 0)
|
|
sense = -1;
|
|
|
|
if (sense == 0) {
|
|
sense = rpmvercmp(aV, bV);
|
|
if (sense == 0 && aR && *aR && bR && *bR) {
|
|
sense = rpmvercmp(aR, bR);
|
|
}
|
|
}
|
|
|
|
return sense;
|
|
}
|
|
|
|
int isChangeNameMoreFresh(const char * const head,
|
|
const char * const tail[3])
|
|
{
|
|
int result;
|
|
const char * evr[3];
|
|
const char * wordAfterEmail;
|
|
char * copy;
|
|
|
|
rpmMessage(RPMMESS_DEBUG, "test: is '%s' more fresh than e=%s, v=%s, r=%s?\n",
|
|
head, tail[0], tail[1], tail[2]);
|
|
/* find the next to <email> word begin */
|
|
if ( (wordAfterEmail = strrchr(head, '>')) )
|
|
++wordAfterEmail;
|
|
else
|
|
wordAfterEmail = head;
|
|
while ( *wordAfterEmail && xisspace(*wordAfterEmail) )
|
|
++wordAfterEmail;
|
|
/* found. */
|
|
copy = xstrdup(wordAfterEmail);
|
|
parseEVR(copy, &evr[0], &evr[1], &evr[2]);
|
|
/* The order of two argument groups is important:
|
|
if evr[] (passed as B on the second place) has no epoch,
|
|
rpmEVRcmp() assumes the same as in tail[];
|
|
This fits our needs: the epoch may be omitted in a changelog entry (evr[])
|
|
but there are no problems in specifying it in the format (tail[]). */
|
|
result = rpmEVRcmp(tail[0], tail[1], tail[2], "",
|
|
evr[0], evr[1], evr[2], "") < 0;
|
|
_free(copy);
|
|
return result;
|
|
}
|
|
|