#include <stdarg.h>
#include <unistd.h>
#include <string.h>
+#include <sys/types.h>
+#include <regex.h>
#include "pool.h"
+#include "repo.h"
+#include "util.h"
#include "conda.h"
static const char *
}
/* C implementation of the version comparison code in conda/models/version.py */
+/* startswith == 1 : check if s1 starts with s2 */
static int
-solv_vercmp_conda(const char *s1, const char *q1, const char *s2, const char *q2)
+solv_vercmp_conda(const char *s1, const char *q1, const char *s2, const char *q2, int startswith)
{
const char *s1p, *s2p;
const char *s1e, *s2e;
int r, isfirst;
+ const char *q2end = 0;
+ if (startswith)
+ {
+ for (q2end = q2; q2end > s2; q2end--)
+ if (q2end[-1] != '.' && q2end[-1] != '-' && q2end[-1] != '_')
+ break;
+ }
for (;;)
{
while (s1 < q1 && (*s1 == '.' || *s1 == '-' || *s1 == '_'))
s2++;
if (s1 == q1 && s2 == q2)
return 0;
+ if (startswith && s2 == q2)
+ return 0;
/* find end of component */
s1e = endseg(s1, q1);
s2e = endseg(s2, q2);
-
+
for (isfirst = 1; ; isfirst = 0)
{
if (s1 == s1e && s2 == s2e)
break;
+ if (s2 == q2end)
+ return 0;
s1p = endpart(s1, s1e);
s2p = endpart(s2, s2e);
/* prepend 0 if not numeric */
}
}
-int
-pool_evrcmp_conda(const Pool *pool, const char *evr1, const char *evr2, int mode)
+static int
+pool_evrcmp_conda_int(const char *evr1, const char *evr1e, const char *evr2, const char *evr2e, int startswith)
{
static char zero[2] = {'0', 0};
- int r;
const char *s1, *s2;
const char *r1, *r2;
-
- if (evr1 == evr2)
- return 0;
+ int r;
/* split and compare epoch */
- for (s1 = evr1; *s1 >= '0' && *s1 <= '9'; s1++)
+ for (s1 = evr1; s1 < evr1e && *s1 >= '0' && *s1 <= '9'; s1++)
;
- for (s2 = evr2; *s2 >= '0' && *s2 <= '9'; s2++)
+ for (s2 = evr2; s2 < evr2e && *s2 >= '0' && *s2 <= '9'; s2++)
;
- if (s1 == evr1 || *s1 != '!')
+ if (s1 == evr1 || s1 == evr1e || *s1 != '!')
s1 = 0;
- if (s2 == evr1 || *s2 != '!')
+ if (s2 == evr1 || s2 == evr2e || *s2 != '!')
s2 = 0;
if (s1 || s2)
{
r = solv_vercmp_conda(s1 ? evr1 : zero, s1 ? s1 : zero + 1,
- s2 ? evr2 : zero, s2 ? s2 : zero + 1);
+ s2 ? evr2 : zero, s2 ? s2 : zero + 1, 0);
if (r)
return r;
if (s1)
evr2 = s2 + 1;
}
/* split into version/localversion */
- for (s1 = evr1, r1 = 0; *s1; s1++)
+ for (s1 = evr1, r1 = 0; s1 < evr1e; s1++)
if (*s1 == '+')
r1 = s1;
- for (s2 = evr2, r2 = 0; *s2; s2++)
+ for (s2 = evr2, r2 = 0; s2 < evr2e; s2++)
if (*s2 == '+')
r2 = s2;
- r = solv_vercmp_conda(evr1, r1 ? r1 : s1, evr2, r2 ? r2 : s2);
+ r = solv_vercmp_conda(evr1, r1 ? r1 : s1, evr2, r2 ? r2 : s2, r2 ? 0 : startswith);
if (r)
return r;
if (!r1 && !r2)
return -1;
if (r1 && !r2)
return 1;
- return solv_vercmp_conda(r1 + 1, s1, r2 + 1, s2);
+ return solv_vercmp_conda(r1 + 1, s1, r2 + 1, s2, startswith);
}
-#if 0
-/* return true if solvable s matches the spec */
+int
+pool_evrcmp_conda(const Pool *pool, const char *evr1, const char *evr2, int mode)
+{
+ if (evr1 == evr2)
+ return 0;
+ return pool_evrcmp_conda_int(evr1, evr1 + strlen(evr1), evr2, evr2 + strlen(evr2), 0);
+}
+
+static int
+regexmatch(const char *evr, const char *version, size_t versionlen)
+{
+ regex_t reg;
+ char *buf = solv_malloc(versionlen + 1);
+ int r;
+
+ memcpy(buf, version, versionlen);
+ buf[versionlen] = 0;
+ if (regcomp(®, buf, REG_EXTENDED | REG_NOSUB))
+ return 0;
+ r = regexec(®, evr, 0, NULL, 0);
+ regfree(®);
+ return r == 0;
+}
+
+static int
+globmatch(const char *evr, const char *version, size_t versionlen)
+{
+ regex_t reg;
+ char *buf = solv_malloc(2 * versionlen + 3);
+ size_t i, j;
+ int r;
+
+ buf[0] = '^';
+ j = 1;
+ for (i = 0, j = 1; i < versionlen; i++)
+ {
+ if (version[i] == '.' || version[i] == '+' || version[i] == '*')
+ buf[j++] = version[i] == '*' ? '.' : '\\';
+ buf[j++] = version[i];
+ }
+ buf[j++] = '$';
+ buf[j] = 0;
+ if (regcomp(®, buf, REG_EXTENDED | REG_NOSUB))
+ return 0;
+ r = regexec(®, evr, 0, NULL, 0);
+ regfree(®);
+ return r == 0;
+}
+
+/* return true if solvable s matches the version */
+/* see conda/models/version.py */
+static int
+solvable_conda_matchversion_single(Solvable *s, const char *version, size_t versionlen)
+{
+ const char *evr;
+ size_t i;
+ int r;
+
+ if (versionlen == 0 || (versionlen == 1 && *version == '*'))
+ return 1; /* matches every version */
+ evr = pool_id2str(s->repo->pool, s->evr);
+ if (versionlen >= 2 && version[0] == '^' && version[versionlen - 1] == '$')
+ return regexmatch(evr, version, versionlen);
+ if (version[0] == '=' || version[0] == '<' || version[0] == '>' || version[0] == '!' || version[0] == '~')
+ {
+ int flags = 0;
+ int oplen;
+ if (version[0] == '=')
+ flags = version[1] == '=' ? REL_EQ : 8;
+ else if (version[0] == '!' || version[0] == '~')
+ {
+ if (version[1] != '=')
+ return 0;
+ flags = version[0] == '!' ? REL_LT | REL_GT : 9;
+ }
+ else if (version[0] == '<' || version[0] == '>')
+ {
+ flags = version[0] == '<' ? REL_LT : REL_GT;
+ if (version[1] == '=')
+ flags |= REL_EQ;
+ }
+ else
+ return 0;
+ oplen = flags == 8 || flags == REL_LT || flags == REL_GT ? 1 : 2;
+ if (versionlen < oplen + 1)
+ return 0;
+ version += oplen;
+ versionlen -= oplen;
+ if (version[0] == '=' || version[0] == '<' || version[0] == '>' || version[0] == '!' || version[0] == '~')
+ return 0; /* bad chars after op */
+ if (versionlen >= 2 && version[versionlen - 2] == '.' && version[versionlen - 1] == '*')
+ {
+ if (flags == 8 || flags == (REL_GT | REL_EQ))
+ versionlen -= 2;
+ else if (flags == (REL_LT | REL_GT))
+ {
+ versionlen -= 2;
+ flags = 10;
+ }
+ else
+ return 0;
+ }
+ if (flags < 8)
+ {
+ /* we now have an op and a version */
+ r = pool_evrcmp_conda_int(evr, evr + strlen(evr), version, version + versionlen, 0);
+ if (r < 0)
+ return (flags & REL_LT) ? 1 : 0;
+ if (r == 0)
+ return (flags & REL_EQ) ? 1 : 0;
+ if (r > 0)
+ return (flags & REL_GT) ? 1 : 0;
+ return 0;
+ }
+ if (flags == 8 || flags == 10) /* startswith, not-startswith */
+ {
+ r = pool_evrcmp_conda_int(evr, evr + strlen(evr), version, version + versionlen, 1);
+ return flags == 8 ? r == 0 : r != 0;
+ }
+ else if (flags == 9) /* compatible release op */
+ {
+ r = pool_evrcmp_conda_int(evr, evr + strlen(evr), version, version + versionlen, 0);
+ if (r < 0)
+ return 0;
+ /* split off last component */
+ while (versionlen > 0 && version[versionlen - 1] != '.')
+ versionlen--;
+ if (versionlen < 2)
+ return 0;
+ versionlen--;
+ r = pool_evrcmp_conda_int(evr, evr + strlen(evr), version, version + versionlen, 1);
+ return r == 0 ? 1 : 0;
+ }
+ return 0;
+ }
+
+ /* do we have a '*' in the version */
+ for (i = 0; i < versionlen; i++)
+ if (version[i] == '*')
+ {
+ for (i++; i < versionlen; i++)
+ if (version[i] != '*')
+ break;
+ if (i < versionlen)
+ return globmatch(evr, version, versionlen);
+ }
+
+ if (versionlen > 1 && version[versionlen - 1] == '*')
+ {
+ /* startswith */
+ while (versionlen > 0 && version[versionlen - 1] == '*')
+ versionlen--;
+ while (versionlen > 0 && version[versionlen - 1] == '.')
+ versionlen--;
+ r = pool_evrcmp_conda_int(evr, evr + strlen(evr), version, version + versionlen, 1);
+ return r == 0 ? 1 : 0;
+ }
+ /* do we have a '@' in the version? */
+ for (i = 0; i < versionlen; i++)
+ if (version[i] == '@')
+ return strncmp(evr, version, versionlen) == 0 && evr[versionlen] == 0;
+ r = pool_evrcmp_conda_int(evr, evr + strlen(evr), version, version + versionlen, 0);
+ return r == 0 ? 1 : 0;
+}
+
+
+/* return true if solvable s matches the version */
/* see conda/models/match_spec.py */
int
-solvable_conda_matchspec(Solvable *s, const char *spec)
+solvable_conda_matchversion(Solvable *s, const char *version)
{
+ const char *build;
+ size_t vl;
+ /* split off build */
+ if ((build = strchr(version, ' ')) != 0)
+ {
+ vl = build - version;
+ while (*build == ' ')
+ build++;
+ }
+ else
+ vl = strlen(version);
+ return solvable_conda_matchversion_single(s, version, vl);
}
-#endif