commit aea67c63efac81f21a28464d044d3f012339c3c6
parent 5d279f837f4bfd6d393d01cd8b8f03289d1fbf28
Author: KatolaZ <katolaz@freaknet.org>
Date:   Tue, 19 Dec 2017 01:05:29 +0100
Implement Debian Policy versioning alrorithm to parse package versions.
Diffstat:
| M | lib/package.py |  |  | 33 | ++------------------------------- | 
| M | lib/parse.py |  |  | 155 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ | 
2 files changed, 157 insertions(+), 31 deletions(-)
diff --git a/lib/package.py b/lib/package.py
@@ -13,7 +13,7 @@ from shutil import copyfile
 import lib.globalvars as globalvars
 from lib.config import mergedir, packages_keys, sources_keys, spooldir
 from lib.log import logtofile
-from lib.parse import parse_dependencies, parse_packages
+from lib.parse import parse_dependencies, parse_packages, cmppkgver
 
 
 def write_packages(packages, filename, sort=True, sources=False):
@@ -116,38 +116,9 @@ def package_newer(pkg1, pkg2):
     if pkg1.get('Package') in _skips:
         return False
 
-    # The choice of dropping [1:] is because we don't care for +deb or +devuan
-    hi_prio = pkg1.get('Version').split('+')[0]
-    lo_prio = pkg2.get('Version').split('+')[0]
-
-    # Epoch check
-    hi_ep = 0
-    lo_ep = 0
-    if ':' in hi_prio:
-        hi_ep = int(hi_prio.split(':')[0])
-        hi_prio = hi_prio.split(':')[1]
-    if ':' in lo_prio:
-        lo_ep = int(lo_prio.split(':')[0])
-        lo_prio = lo_prio.split(':')[1]
-    if lo_ep > hi_ep:
+    if cmppkgver(pkg1.get('Version'), pkg2.get('Version')) < 1:
         return True
 
-    # [0] will be upstream, [1] should be the package build
-    hi_prio = hi_prio.split('-')
-    lo_prio = lo_prio.split('-')
-    if lo_prio[0] > hi_prio[0]:
-        return True
-
-    if len(hi_prio) > 1 and len(lo_prio) > 1:
-        hi_prio[1] = hi_prio[1].replace('.', '')
-        lo_prio[1] = lo_prio[1].replace('.', '')
-        if '~' in hi_prio[1]:
-            hi_prio[1] = hi_prio[1].split('~')[0]
-        if '~' in lo_prio[1]:
-            lo_prio[1] = lo_prio[1].split('~')[0]
-        if int(lo_prio[1]) > int(hi_prio[1]):
-            return True
-
     return False
 
 
diff --git a/lib/parse.py b/lib/parse.py
@@ -6,6 +6,8 @@ Parsing functions/helpers
 
 from time import mktime, strptime
 
+from lib.log import info
+
 
 def get_time(date):
     """
@@ -153,6 +155,159 @@ def parse_dependencies(dependencies):
     return ret
 
 
+def compare_epochs(epo1, epo2):
+    """
+    Compares two given epochs and returns their difference.
+    """
+    return int(epo1) - int(epo2)
+
+
+def get_epoch(ver):
+    """
+    Parses and returns the epoch, and the rest, split of a version string.
+    """
+    if ':' in ver:
+        return ver.split(':', 1)
+    return "0", ver
+
+
+def get_upstream(rest):
+    """
+    Separate upstream_version from debian-version. The latter is whatever is
+    found after the last "-" (hyphen)
+    """
+    split_s = rest.rsplit('-', 1)
+    if len(split_s) < 2:
+        return split_s[0], ""
+    return split_s
+
+
+def get_non_digit(s):
+    """
+    Get a string and return the longest leading substring consisting exclusively
+    of non-digits (or an empty string), and the remaining substring.
+    """
+    if not s:
+        return "", ""
+    head = ""
+    tail = s
+    N = len(s)
+    i = 0
+    while i < N and not s[i].isdigit():
+        head += s[i]
+        tail = tail[1:]
+        i += 1
+    return head, tail
+
+
+def get_digit(s):
+    """
+    Get a string and return the integer value of the longest leading substring
+    consisting exclusively of digit characters (or zero otherwise), and the
+    remaining substring.
+    """
+    if not s:
+        return 0, ""
+    head = ""
+    tail = s
+    N = len(s)
+    i = 0
+    while i < N and s[i].isdigit():
+        head += s[i]
+        tail = tail[1:]
+        i += 1
+    return int(head), tail
+
+
+def char_val(c):
+    """
+    Returns an integer value of a given unicode character. Returns 0 on ~ (since
+    this is in Debian's policy)
+    """
+    if c == '~':
+        return 0
+    elif not c.isalpha():
+        return 256 + ord(c)
+    return ord(c)
+
+
+def compare_deb_str(a1, a2):
+    while len(a1) > 0 and len(a2) > 0:
+        char_diff = char_val(a1[0]) - char_val(a2[0])
+        if char_diff != 0:
+            return char_diff
+        a1 = a1[1:]
+        a2 = a2[1:]
+    if len(a1) == 0:
+        if len(a2) == 0:
+            return 0
+        else:
+            if a2[0] == '~':
+                return 512
+            else:
+                return -ord(a2[0])
+    else:
+        if a1[0] == '~':
+            return -512
+        else:
+            return ord(a1[0])
+
+
+def compare_non_epoch(s1, s2):
+    cont = True
+    while cont:
+        alpha1, tail1 = get_non_digit(s1)
+        alpha2, tail2 = get_non_digit(s2)
+        if alpha1 == alpha2:
+            num1, s1 = get_digit(tail1)
+            num2, s2 = get_digit(tail2)
+            if num1 == num2:
+                cont = True
+            else:
+                diff = num1 - num2
+                cont = False
+        else:
+            cont = False
+            diff = compare_deb_str(alpha1, alpha2)
+
+    return diff
+
+
+def cmppkgver(ver1, ver2):
+    """
+    Main function to compare two package versions. Wraps around other functions
+    to provide a result. It returns an integer < 0 if ver1 is earlier than ver2,
+    0 if ver1 is the same as ver2, and an integer > 0 if ver2 is earlier than
+    ver2.
+
+    WARNING: The function does not induce a total order (i.e., return values
+    MUST NOT be added or subtracted)
+
+    https://www.debian.org/doc/debian-policy/#version
+    """
+    epoch1, rest1 = get_epoch(ver1)
+    epoch2, rest2 = get_epoch(ver2)
+
+    ec = compare_epochs(epoch1, epoch2)
+    if ec != 0:
+        # The two versions differ on epoch
+        info('differing epochs: %d\n' % ec)
+        return ec
+    else:
+        info('equal epochs')
+
+    upst1, rev1 = get_upstream(rest1)
+    upst2, rev2 = get_upstream(rest2)
+
+    info('v1: up: %s deb: %s' % (upst1, rev1))
+    info('v2: up: %s deb: %s' % (upst2, rev2))
+
+    up_diff = compare_deb_str(upst1, upst2)
+    if up_diff == 0:
+        return compare_deb_str(rev1, rev2)
+    return up_diff
+
+
 def compare_dict(dic1, dic2):
     """
     Compares two dicts