diff --git a/Makefile-lib.am b/Makefile-lib.am index bef049e1..f9d67f5c 100644 --- a/Makefile-lib.am +++ b/Makefile-lib.am @@ -28,6 +28,7 @@ librpmostree_1_la_SOURCES = \ src/lib/rpmostree.c \ src/lib/rpmostree-db.c \ src/lib/rpmostree-package.c \ + src/lib/rpmver-private.c \ $(NULL) librpmostree_1_la_CFLAGS = $(AM_CFLAGS) -I$(srcdir)/libglnx -I$(srcdir)/src/libpriv -I$(srcdir)/src/lib \ diff --git a/configure.ac b/configure.ac index f9c253a0..bab7e9b8 100644 --- a/configure.ac +++ b/configure.ac @@ -53,6 +53,12 @@ dnl RHEL8.1 has old libarchive AS_IF([pkg-config --atleast-version=3.3.3 libarchive], [AC_DEFINE([HAVE_LIBARCHIVE_ZSTD], 1, [Define if we have libarchive with zstd])]) +# rpmver is available in rpm >= 4.16, el8 is still on 4.14 +AC_SEARCH_LIBS([rpmverCmp], [rpm], + AC_DEFINE([BUILDOPT_HAVE_RPMVER], 1, [Whether rpmver API is available in librpmio]), + AC_DEFINE([BUILDOPT_HAVE_RPMVER], 0, [Whether rpmver API is available in librpmio]) +) + dnl We don't *actually* use this ourself, but librepo does, and libdnf gets confused dnl if librepo doesn't support it. have_zchunk=no diff --git a/src/lib/rpmostree-package.c b/src/lib/rpmostree-package.c index 8575c4a5..3bb03bfe 100644 --- a/src/lib/rpmostree-package.c +++ b/src/lib/rpmostree-package.c @@ -32,12 +32,17 @@ #include #include -#include #include "libglnx.h" #include "rpmostree-shlib-ipc-private.h" #include "rpmostree-package-priv.h" +#if BUILDOPT_HAVE_RPMVER +#include +#else +#include "rpmver-private.h" +#endif + typedef GObjectClass RpmOstreePackageClass; /* Since the class is small, we perform a poor man's polymorphism; RpmOstreePackage objects diff --git a/src/lib/rpmver-private.c b/src/lib/rpmver-private.c new file mode 100644 index 00000000..117df792 --- /dev/null +++ b/src/lib/rpmver-private.c @@ -0,0 +1,223 @@ +// Temporary forked internal copy of: +// https://github.com/rpm-software-management/rpm/blob/rpm-4.16.1.3/rpmio/rpmver.c +// SPDX-License-Identifier: LGPL-2.1-or-later + +/* NOTE(lucab): compatibility changes to avoid touching the source. + * The *malloc functions have different behaviors when size=0, but this is not + * a concern here because all calls in this file have strictly-positive size. + */ +#include "config.h" +#if !BUILDOPT_HAVE_RPMVER +#include +#define xmalloc(_size) g_malloc((_size)) +#define free(_mem) g_free((_mem)) +#include "rpmver-private.h" + +#include +#include + +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; +} +#endif diff --git a/src/lib/rpmver-private.h b/src/lib/rpmver-private.h new file mode 100644 index 00000000..f875c2f9 --- /dev/null +++ b/src/lib/rpmver-private.h @@ -0,0 +1,108 @@ +// Temporary forked internal copy of +// https://github.com/rpm-software-management/rpm/blob/rpm-4.16.1.3/rpmio/rpmver.h +// SPDX-License-Identifier: LGPL-2.1-or-later + +#ifndef _RPMVER_H +#define _RPMVER_H + +#include +#include /* sense flags */ + +#ifdef __cplusplus +extern "C" { +#endif + +// NOTE(lucab): Backported from . +#ifndef rpmver +typedef struct rpmver_s * rpmver; +#endif + +/** \ingroup rpmver + * Segmented string compare for version or release strings. + * + * @param a 1st string + * @param b 2nd string + * @return +1 if a is "newer", 0 if equal, -1 if b is "newer" + */ +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 + +#endif /* _RPMVER_H */