Imported Upstream version 0.7.2
[platform/upstream/libsolv.git] / src / evr.c
index b01adeb..c63878e 100644 (file)
--- a/src/evr.c
+++ b/src/evr.c
  * version compare
  */
 
+#include <ctype.h>
 #include <stdio.h>
 #include <string.h>
 #include "evr.h"
 #include "pool.h"
 
 
-#ifdef DEBIAN_SEMANTICS
 
+#if defined(DEBIAN) || defined(MULTI_SEMANTICS)
+
+/* debian type version compare */
 int
-vercmp(const char *s1, const char *q1, const char *s2, const char *q2)
+solv_vercmp_deb(const char *s1, const char *q1, const char *s2, const char *q2)
 {
   int r, c1, c2;
   while (1)
@@ -58,10 +61,98 @@ vercmp(const char *s1, const char *q1, const char *s2, const char *q2)
     }
 }
 
-#else
+#endif
+
+#if !defined(DEBIAN) || defined(MULTI_SEMANTICS)
+
+/* rpm type version compare */
+/* note: the code assumes that *q1 and *q2 are not alphanumeric! */
+
+int
+solv_vercmp_rpm(const char *s1, const char *q1, const char *s2, const char *q2)
+{
+  int r = 0;
+  const char *e1, *e2;
+
+  for (;;)
+    {
+      while (s1 < q1 && !(*s1 >= '0' && *s1 <= '9') &&
+          !(*s1 >= 'a' && *s1 <= 'z') && !(*s1 >= 'A' && *s1 <= 'Z') && *s1 != '~' && *s1 != '^')
+       s1++;
+      while (s2 < q2 && !(*s2 >= '0' && *s2 <= '9') &&
+          !(*s2 >= 'a' && *s2 <= 'z') && !(*s2 >= 'A' && *s2 <= 'Z') && *s2 != '~' && *s2 != '^')
+       s2++;
+      if (s1 < q1 && *s1 == '~')
+        {
+         if (s2 < q2 && *s2 == '~')
+           {
+             s1++;
+             s2++;
+             continue;
+           }
+         return -1;
+        }
+      if (s2 < q2 && *s2 == '~')
+       return 1;
+      if (s1 < q1 && *s1 == '^')
+       {
+         if (s2 < q2 && *s2 == '^')
+           {
+             s1++;
+             s2++;
+             continue;
+           }
+         return s2 < q2 ? -1 : 1;
+       }
+      if (s2 < q2 && *s2 == '^')
+       return s1 < q1 ? 1 : -1;
+      if (s1 >= q1 || s2 >= q2)
+       break;
+      if ((*s1 >= '0' && *s1 <= '9') || (*s2 >= '0' && *s2 <= '9'))
+       {
+         while (*s1 == '0' && s1[1] >= '0' && s1[1] <= '9')
+           s1++;
+         while (*s2 == '0' && s2[1] >= '0' && s2[1] <= '9')
+           s2++;
+         for (e1 = s1; *e1 >= '0' && *e1 <= '9'; )
+           e1++;
+         for (e2 = s2; *e2 >= '0' && *e2 <= '9'; )
+           e2++;
+         r = (e1 - s1) - (e2 - s2);
+          if (!r)
+           r = strncmp(s1, s2, e1 - s1);
+          if (r)
+           return r > 0 ? 1 : -1;
+       }
+      else
+       {
+         for (e1 = s1; (*e1 >= 'a' && *e1 <= 'z') || (*e1 >= 'A' && *e1 <= 'Z'); )
+           e1++;
+         for (e2 = s2; (*e2 >= 'a' && *e2 <= 'z') || (*e2 >= 'A' && *e2 <= 'Z'); )
+           e2++;
+         r = (e1 - s1) - (e2 - s2);
+          if (r > 0)
+           {
+             r = strncmp(s1, s2, e2 - s2);
+             return r >= 0 ? 1 : -1;
+           }
+          if (r < 0)
+           {
+             r = strncmp(s1, s2, e1 - s1);
+             return r <= 0 ? -1 : 1;
+           }
+         r = strncmp(s1, s2, e1 - s1);
+         if (r)
+           return r > 0 ? 1 : -1;
+       }
+      s1 = e1;
+      s2 = e2;
+    }
+  return s1 < q1 ? 1 : s2 < q2 ? -1 : 0;
+}
 
 int
-vercmp(const char *s1, const char *q1, const char *s2, const char *q2)
+solv_vercmp_rpm_notilde(const char *s1, const char *q1, const char *s2, const char *q2)
 {
   int r = 0;
   const char *e1, *e2;
@@ -84,7 +175,7 @@ vercmp(const char *s1, const char *q1, const char *s2, const char *q2)
            e1++;
          for (e2 = s2; *e2 >= '0' && *e2 <= '9'; )
            e2++;
-         r = e1 - s1 - (e2 - s2);
+         r = (e1 - s1) - (e2 - s2);
           if (!r)
            r = strncmp(s1, s2, e1 - s1);
           if (r)
@@ -96,7 +187,7 @@ vercmp(const char *s1, const char *q1, const char *s2, const char *q2)
            e1++;
          for (e2 = s2; (*e2 >= 'a' && *e2 <= 'z') || (*e2 >= 'A' && *e2 <= 'Z'); )
            e2++;
-         r = e1 - s1 - (e2 - s2);
+         r = (e1 - s1) - (e2 - s2);
           if (r > 0)
            {
              r = strncmp(s1, s2, e2 - s2);
@@ -119,10 +210,133 @@ vercmp(const char *s1, const char *q1, const char *s2, const char *q2)
 
 #endif
 
+#if defined(HAIKU) || defined(MULTI_SEMANTICS)
+
+static int
+solv_cmp_version_part_haiku(const char *s1, const char *q1, const char *s2,
+  const char *q2)
+{
+  while (s1 < q1 && s2 < q2)
+    {
+      int cmp, len1, len2;
+      const char *part1 = s1, *part2 = s2;
+
+      /* compare non-number part */
+      while (s1 < q1 && !isdigit(*s1))
+        s1++;
+      while (s2 < q2 && !isdigit(*s2))
+        s2++;
+
+      if (part1 != s1)
+        {
+          if (part2 == s2)
+            return 1;
+
+          len1 = s1 - part1;
+          len2 = s2 - part2;
+          cmp = strncmp(part1, part2, len1 < len2 ? len1 : len2);
+          if (cmp != 0)
+            return cmp;
+          if (len1 != len2)
+            return len1 - len2;
+       }
+      else if (part2 != s2)
+        return -1;
+
+      /* compare number part */
+      part1 = s1;
+      part2 = s2;
+
+      while (s1 < q1 && isdigit(*s1))
+        s1++;
+      while (s2 < q2 && isdigit(*s2))
+        s2++;
+
+      while (part1 + 1 < s1 && *part1 == '0')
+        part1++;
+      while (part2 + 1 < s2 && *part2 == '0')
+        part2++;
+
+      len1 = s1 - part1;
+      len2 = s2 - part2;
+      if (len1 != len2)
+        return len1 - len2;
+      if (len1 == 0)
+        return 0;
+
+      cmp = strncmp(part1, part2, len1);
+      if (cmp != 0)
+       return cmp;
+    }
+
+  return s1 < q1 ? 1 : s2 < q2 ? -1 : 0;
+}
+
+int
+solv_vercmp_haiku(const char *s1, const char *q1, const char *s2, const char *q2)
+{
+  const char *pre1 = s1;
+  const char *pre2 = s2;
+  int cmp;
+
+  /* find pre-release separator */
+  while (pre1 != q1 && *pre1 != '~')
+    pre1++;
+  while (pre2 != q2 && *pre2 != '~')
+    pre2++;
+
+  /* compare main versions */
+  cmp = solv_cmp_version_part_haiku(s1, pre1, s2, pre2);
+  if (cmp != 0)
+    return cmp < 0 ? -1 : 1; /* must return -1, 0, or 1 */
+
+  /* main versions are equal -- compare pre-release (none is greatest) */
+  if (pre1 == q1)
+    return pre2 == q2 ? 0 : 1;
+  if (pre2 == q2)
+    return -1;
+
+  cmp = solv_cmp_version_part_haiku(pre1 + 1, q1, pre2 + 1, q2);
+  return cmp == 0 ? 0 : cmp < 0 ? -1 : 1; /* must return -1, 0, or 1 */
+}
+
+#endif /* HAIKU */
+
+
+/*
+ * the solv_vercmp variant your system uses.
+ */
+int
+solv_vercmp(const char *s1, const char *q1, const char *s2, const char *q2)
+{
+#if defined(DEBIAN)
+  return solv_vercmp_deb(s1, q1, s2, q2);
+#elif defined(ARCHLINUX)
+  return solv_vercmp_rpm_notilde(s1, q1, s2, q2);
+#elif defined(HAIKU)
+  return solv_vercmp_haiku(s1, q1, s2, q2);
+#else
+  return solv_vercmp_rpm(s1, q1, s2, q2);
+#endif
+}
+
+#if defined(MULTI_SEMANTICS)
+# define solv_vercmp (*(pool->disttype == DISTTYPE_DEB ? &solv_vercmp_deb : \
+                        pool->disttype == DISTTYPE_HAIKU ? solv_vercmp_haiku : \
+                        &solv_ver##cmp_rpm))
+#elif defined(DEBIAN)
+# define solv_vercmp solv_vercmp_deb
+#elif defined(ARCHLINUX)
+# define solv_vercmp solv_vercmp_rpm_notilde
+#elif defined(HAIKU)
+# define solv_vercmp solv_vercmp_haiku
+#else
+# define solv_vercmp solv_vercmp_rpm
+#endif
 
 /* edition (e:v-r) compare */
 int
-evrcmp_str(Pool *pool, const char *evr1, const char *evr2, int mode)
+pool_evrcmp_str(const Pool *pool, const char *evr1, const char *evr2, int mode)
 {
   int r;
   const char *s1, *s2;
@@ -148,13 +362,15 @@ evrcmp_str(Pool *pool, const char *evr1, const char *evr2, int mode)
       s1 = evr1;
       s2 = evr2;
     }
+
+  /* compare the epoch */
   if (s1 == evr1 || *s1 != ':')
     s1 = 0;
   if (s2 == evr2 || *s2 != ':')
     s2 = 0;
   if (s1 && s2)
     {
-      r = vercmp(evr1, s1, evr2, s2);
+      r = solv_vercmp(evr1, s1, evr2, s2);
       if (r)
        return r;
       evr1 = s1 + 1;
@@ -179,16 +395,17 @@ evrcmp_str(Pool *pool, const char *evr1, const char *evr2, int mode)
        return -1;
       evr2 = s2 + 1;
     }
+
+  /* same epoch, now split into version/release */
   for (s1 = evr1, r1 = 0; *s1; s1++)
     if (*s1 == '-')
       r1 = s1;
   for (s2 = evr2, r2 = 0; *s2; s2++)
     if (*s2 == '-')
       r2 = s2;
-
   r = 0;
   if (mode != EVRCMP_MATCH || (evr1 != (r1 ? r1 : s1) && evr2 != (r2 ? r2 : s2)))
-    r = vercmp(evr1, r1 ? r1 : s1, evr2, r2 ? r2 : s2);
+    r = solv_vercmp(evr1, r1 ? r1 : s1, evr2, r2 ? r2 : s2);
   if (r)
     return r;
 
@@ -199,41 +416,77 @@ evrcmp_str(Pool *pool, const char *evr1, const char *evr2, int mode)
       if (r1 && !r2)
        return 1;
     }
+  if (mode == EVRCMP_COMPARE_EVONLY)
+    return 0;
+  if (mode == EVRCMP_MATCH_RELEASE)
+    {
+      /* rpm treats empty releases as missing, i.e "foo = 4-" is the same as "foo = 4" */
+      if (r1 && r1 + 1 == s1)
+       r1 = 0;
+      if (r2 && r2 + 1 == s2)
+       r2 = 0;
+    }
   if (r1 && r2)
     {
-      if (s1 != ++r1 && s2 != ++r2)
-        r = vercmp(r1, s1, r2, s2);
+      r1++;
+      r2++;
+      if (mode != EVRCMP_MATCH || (s1 != r1 && s2 != r2))
+       {
+         if (pool->havedistepoch)
+           {
+             const char *d1, *d2;
+             for (d1 = r1; d1 < s1; d1++)
+               if (*d1 == ':')
+                 break;
+             for (d2 = r2; d2 < s2; d2++)
+               if (*d2 == ':')
+                 break;
+             /* XXX: promote just in one direction? */
+             r = solv_vercmp(r1, d1 ? d1 : s1, r2, d2 ? d2 : s2);
+             if (r == 0 && d1 < s1 && d2 < s2)
+               r = solv_vercmp(d1 + 1, s1, d2 + 1, s2);
+           }
+         else
+            r = solv_vercmp(r1, s1, r2, s2);
+       }
+    }
+  else if (mode == EVRCMP_MATCH_RELEASE)
+    {
+      if (!r1 && r2)
+       return -2;
+      if (r1 && !r2)
+       return 2;
     }
   return r;
 }
 
 int
-evrcmp(Pool *pool, Id evr1id, Id evr2id, int mode)
+pool_evrcmp(const Pool *pool, Id evr1id, Id evr2id, int mode)
 {
   const char *evr1, *evr2;
   if (evr1id == evr2id)
     return 0;
-  evr1 = id2str(pool, evr1id);
-  evr2 = id2str(pool, evr2id);
-  return evrcmp_str(pool, evr1, evr2, mode);
+  evr1 = pool_id2str(pool, evr1id);
+  evr2 = pool_id2str(pool, evr2id);
+  return pool_evrcmp_str(pool, evr1, evr2, mode);
 }
 
 int
-evrmatch(Pool *pool, Id evrid, const char *epoch, const char *version, const char *release)
+pool_evrmatch(const Pool *pool, Id evrid, const char *epoch, const char *version, const char *release)
 {
   const char *evr1;
   const char *s1;
   const char *r1;
   int r;
 
-  evr1 = id2str(pool, evrid);
+  evr1 = pool_id2str(pool, evrid);
   for (s1 = evr1; *s1 >= '0' && *s1 <= '9'; s1++)
     ;
   if (s1 != evr1 && *s1 == ':')
     {
       if (epoch)
        {
-         r = vercmp(evr1, s1, epoch, epoch + strlen(epoch));
+         r = solv_vercmp(evr1, s1, epoch, epoch + strlen(epoch));
          if (r)
            return r;
        }
@@ -251,7 +504,7 @@ evrmatch(Pool *pool, Id evrid, const char *epoch, const char *version, const cha
       r1 = s1;
   if (version)
     {
-      r = vercmp(evr1, r1 ? r1 : s1, version, version + strlen(version));
+      r = solv_vercmp(evr1, r1 ? r1 : s1, version, version + strlen(version));
       if (r)
        return r;
     }
@@ -259,11 +512,10 @@ evrmatch(Pool *pool, Id evrid, const char *epoch, const char *version, const cha
     {
       if (!r1)
        return -1;
-      r = vercmp(r1 + 1, s1, release, release + strlen(release));
+      r = solv_vercmp(r1 + 1, s1, release, release + strlen(release));
       if (r)
        return r;
     }
   return 0;
 }
 
-// EOF