diff --git a/doc/librpm/Doxyheader.h b/doc/librpm/Doxyheader.h index 365075ab2..2c6b09e0d 100644 --- a/doc/librpm/Doxyheader.h +++ b/doc/librpm/Doxyheader.h @@ -37,6 +37,9 @@ /** \defgroup rpmstrpool String Pool API. \brief How to store strings in pools. */ +/** \defgroup rpmver RPM version API. + \brief Rpm version comparison API. + */ /** @} */ /** \defgroup install (un)Installing packages: * diff --git a/lib/rpmtypes.h b/lib/rpmtypes.h index 1948d183b..e8e69b506 100644 --- a/lib/rpmtypes.h +++ b/lib/rpmtypes.h @@ -70,6 +70,7 @@ typedef struct rpmdbMatchIterator_s * rpmdbMatchIterator; typedef struct rpmtsi_s * rpmtsi; typedef struct rpmps_s * rpmps; typedef struct rpmtxn_s * rpmtxn; +typedef struct rpmver_s * rpmver; typedef struct rpmdbIndexIterator_s * rpmdbIndexIterator; typedef const void * fnpyKey; diff --git a/rpmio/Makefile.am b/rpmio/Makefile.am index 30d7161ba..484df6bdd 100644 --- a/rpmio/Makefile.am +++ b/rpmio/Makefile.am @@ -20,7 +20,7 @@ librpmio_la_SOURCES = \ argv.c base64.c digest.h digest.c expression.c macro.c \ rpmhook.c rpmio.c rpmlog.c rpmmalloc.c \ rpmpgp.c rpmsq.c rpmsw.c url.c \ - rpmio_internal.h rpmhook.h rpmvercmp.c \ + rpmio_internal.h rpmhook.h rpmvercmp.c rpmver.c \ rpmstring.c rpmfileutil.c rpmglob.c \ rpmkeyring.c rpmstrpool.c rpmmacro_internal.h diff --git a/rpmio/rpmver.c b/rpmio/rpmver.c new file mode 100644 index 000000000..aac1e8bd2 --- /dev/null +++ b/rpmio/rpmver.c @@ -0,0 +1,212 @@ +#include "system.h" + +#include +#include +#include + +#include "debug.h" + +struct rpmver_s { + const char *e; + const char *v; + const char *r; + char arena[]; +}; + +/** + * 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; /* assume only version is present */ + const char *release; + char *s, *se; + + s = evr; + while (*s && risdigit(*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; +} + +int rpmverOverlap(rpmver v1, rpmsenseFlags f1, rpmver v2, rpmsenseFlags f2) +{ + int sense = 0; + int result = 0; + + /* Compare {A,B} [epoch:]version[-release] */ + if (v1->e && *v1->e && v2->e && *v2->e) + sense = rpmvercmp(v1->e, v2->e); + else if (v1->e && *v1->e && atol(v1->e) > 0) { + sense = 1; + } else if (v2->e && *v2->e && atol(v2->e) > 0) + sense = -1; + + if (sense == 0) { + sense = rpmvercmp(v1->v, v2->v); + if (sense == 0) { + if (v1->r && *v1->r && v2->r && *v2->r) { + sense = rpmvercmp(v1->r, v2->r); + } else { + /* always matches if the side with no release has SENSE_EQUAL */ + if ((v1->r && *v1->r && (f2 & RPMSENSE_EQUAL)) || + (v2->r && *v2->r && (f1 & RPMSENSE_EQUAL))) { + result = 1; + goto exit; + } + } + } + } + + /* Detect overlap of {A,B} range. */ + if (sense < 0 && ((f1 & RPMSENSE_GREATER) || (f2 & RPMSENSE_LESS))) { + result = 1; + } else if (sense > 0 && ((f1 & RPMSENSE_LESS) || (f2 & RPMSENSE_GREATER))) { + result = 1; + } else if (sense == 0 && + (((f1 & RPMSENSE_EQUAL) && (f2 & RPMSENSE_EQUAL)) || + ((f1 & RPMSENSE_LESS) && (f2 & RPMSENSE_LESS)) || + ((f1 & RPMSENSE_GREATER) && (f2 & RPMSENSE_GREATER)))) { + result = 1; + } + +exit: + return result; +} + +static int compare_values(const char *str1, const char *str2) +{ + if (!str1 && !str2) + return 0; + else if (str1 && !str2) + return 1; + else if (!str1 && str2) + return -1; + return rpmvercmp(str1, str2); +} + +int rpmverCmp(rpmver v1, rpmver v2) +{ + const char *e1 = (v1->e != NULL) ? v1->e : "0"; + const char *e2 = (v2->e != NULL) ? v2->e : "0"; + + int rc = compare_values(e1, e2); + if (!rc) { + rc = compare_values(v1->v, v2->v); + if (!rc) + rc = compare_values(v1->r, v2->r); + } + return rc; +} + +uint32_t rpmverEVal(rpmver rv) +{ + return (rv != NULL && rv->e != NULL) ? atol(rv->e) : 0; +} + +const char *rpmverE(rpmver rv) +{ + return (rv != NULL) ? rv->e : NULL; +} + +const char *rpmverV(rpmver rv) +{ + return (rv != NULL) ? rv->v : NULL; +} + +const char *rpmverR(rpmver rv) +{ + return (rv != NULL) ? rv->r : NULL; +} + +char *rpmverEVR(rpmver rv) +{ + char *EVR = NULL; + if (rv) { + rstrscat(&EVR, rv->e ? rv-> e : "", rv->e ? ":" : "", + rv->v, + rv->r ? "-" : "", rv->r ? rv->r : "", NULL); + } + return EVR; +} + +rpmver rpmverParse(const char *evr) +{ + rpmver rv = NULL; + if (evr && *evr) { + size_t evrlen = strlen(evr) + 1; + rv = xmalloc(sizeof(*rv) + evrlen); + memcpy(rv->arena, evr, evrlen); + parseEVR(rv->arena, &rv->e, &rv->v, &rv->r); + } + return rv; +} + +rpmver rpmverNew(const char *e, const char *v, const char *r) +{ + rpmver rv = NULL; + + if (v && *v) { + size_t nb = strlen(v) + 1; + nb += (e != NULL) ? strlen(e) + 1 : 0; + nb += (r != NULL) ? strlen(r) + 1 : 0; + rv = xmalloc(sizeof(*rv) + nb); + + rv->e = NULL; + rv->v = NULL; + rv->r = NULL; + + char *p = rv->arena; + if (e) { + rv->e = p; + p = stpcpy(p, e); + p++; + } + + rv->v = p; + p = stpcpy(p, v); + p++; + + if (r) { + rv->r = p; + p = stpcpy(p, r); + p++; + } + } + return rv; +} + +rpmver rpmverFree(rpmver rv) +{ + if (rv) { + free(rv); + } + return NULL; +} diff --git a/rpmio/rpmver.h b/rpmio/rpmver.h index 14b03ae9b..b8f9950c3 100644 --- a/rpmio/rpmver.h +++ b/rpmio/rpmver.h @@ -1,11 +1,14 @@ #ifndef _RPMVER_H #define _RPMVER_H +#include +#include /* sense flags */ + #ifdef __cplusplus extern "C" { #endif -/** \ingroup rpmtrans +/** \ingroup rpmver * Segmented string compare for version or release strings. * * @param a 1st string @@ -14,6 +17,81 @@ extern "C" { */ int rpmvercmp(const char * a, const char * b); +/** \ingroup rpmver + * Parse rpm version handle from evr string + * + * @param evr [epoch:]version[-release] string + * @return rpm version, NULL on invalid evr + */ +rpmver rpmverParse(const char *evr); + +/** \ingroup rpmver + * Create new rpm version handle from e, v, r components + * + * @param e epoch (or NULL) + * @param v version + * @param r release (or NULL) + * @return rpm version, NULL on invalid + */ +rpmver rpmverNew(const char *e, const char *v, const char *r); + +/** \ingroup rpmver + * Free rpm version handle + * + * @param rv rpm version handle + * @return NULL always + */ +rpmver rpmverFree(rpmver rv); + +/** \ingroup rpmver + * @param rv rpm version handle + * @return numerical value of epoch + */ +uint32_t rpmverEVal(rpmver rv); + +/** \ingroup rpmver + * @param rv rpm version handle + * @return epoch portion + */ +const char *rpmverE(rpmver rv); + +/** \ingroup rpmver + * @param rv rpm version handle + * @return version portion + */ +const char *rpmverV(rpmver rv); + +/** \ingroup rpmver + * @param rv rpm version handle + * @return release portion + */ +const char *rpmverR(rpmver rv); + +/** \ingroup rpmver + * @param rv rpm version handle + * @return formatted [E:]V[-R] string (malloced) + */ +char *rpmverEVR(rpmver rv); + +/** \ingroup rpmver + * Compare two rpm version handles + * + * @param v1 1st version handle + * @param v2 2nd version handle + * @return 0 if equal, -1 if v1 smaller, 1 if greater, than v2 + */ +int rpmverCmp(rpmver v1, rpmver v2); + +/** \ingroup rpmver + * Determine whether two versioned ranges overlap. + * @param v1 1st version + * @param f1 1st sense flags + * @param v2 2nd version + * @param f2 2nd sense flags + * @return 1 if ranges overlap, 0 otherwise + */ +int rpmverOverlap(rpmver v1, rpmsenseFlags f1, rpmver v2, rpmsenseFlags f2); + #ifdef __cplusplus } #endif