tImplement Debian Policy versioning alrorithm to parse package versions. - amprolla - devuan's apt repo merger
 (HTM) git clone git://parazyd.org/amprolla.git
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) README
 (DIR) LICENSE
       ---
 (DIR) commit aea67c63efac81f21a28464d044d3f012339c3c6
 (DIR) parent 5d279f837f4bfd6d393d01cd8b8f03289d1fbf28
 (HTM) 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(-)
       ---
 (DIR) diff --git a/lib/package.py b/lib/package.py
       t@@ -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):
       t@@ -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
        
        
 (DIR) diff --git a/lib/parse.py b/lib/parse.py
       t@@ -6,6 +6,8 @@ Parsing functions/helpers
        
        from time import mktime, strptime
        
       +from lib.log import info
       +
        
        def get_time(date):
            """
       t@@ -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