910e62b5创建于 1月15日历史提交
#!/usr/bin/env python
# Copyright 2017 The Chromium Authors
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

import sys

if sys.version_info.major != 2:
    unicode = str


def compare_int(left, right):
    if left == right:
        return 0
    return -1 if left < right else 1


def compare_char(left, right):
    # 'man deb-version' specifies that characters are compared using
    # their ASCII values, except alphabetic characters come before
    # special characters and ~ comes before everything else.
    table = '~$ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz+-.:'
    left = table.find(left)
    right = table.find(right)
    assert left >= 0 and right >= 0
    return compare_int(left, right)


def compare_part(left, right):
    if isinstance(left, int) and isinstance(right, int):
        return compare_int(left, right)
    assert isinstance(left,
                      (str, unicode)) and isinstance(right, (str, unicode))
    # 'man deb-version' specifies that '~' should be matched before the
    # empty string.  Add a '$' to the end of the strings to make this
    # comparison easier.
    left += '$'
    right += '$'
    i = 0
    while i < len(left) and i < len(right):
        comp = compare_char(left[i], right[i])
        if comp != 0:
            return comp
        i += 1
    return compare_int(len(left), len(right))


def get_parts_from_component(component):
    # 'man deb-version' specifies that components should be compared
    # part-by-part, where parts are either strings or numbers.  Strings
    # are compared lexicographically while numbers are compared using
    # magnitude.  Components must start with a string part and end with
    # a number part.  The empty string will be prepended and 0 will be
    # appended to satisfy this requirement.  For example, the component
    # '1.2.3' will be expanded to ['', 1, '.', 2, '.', 3], and the empty
    # component will be expanded to ['', 0].
    part_is_string = True
    parts = []
    part = ''
    for c in component:
        char_is_string = not c.isdigit()
        if char_is_string != part_is_string:
            parts.append(part if part_is_string else int(part))
            part = ''
            part_is_string = char_is_string
        part += c
    if part_is_string:
        parts.append(part)
        part = ''
    parts.append(0 if part == '' else int(part))
    return parts


def compare_component(left, right):
    i = 0
    left = get_parts_from_component(left)
    right = get_parts_from_component(right)
    while i < len(left) and i < len(right):
        comp = compare_part(left[i], right[i])
        if comp != 0:
            return comp
        i += 1
    return compare_int(len(left), len(right))


class DebVersion:

    def __init__(self, version_string):
        self.version_string = version_string
        self.epoch = 0
        self.upstream_version = ''
        self.debian_revision = None

        colon = version_string.find(':')
        if colon >= 0:
            self.epoch = int(version_string[:colon])
        hyphen = version_string.rfind('-')
        if hyphen >= 0:
            self.debian_revision = version_string[hyphen + 1:]
        upstream_version_start = colon + 1
        upstream_version_end = hyphen if hyphen >= 0 else len(version_string)
        self.upstream_version = version_string[
            upstream_version_start:upstream_version_end]

    def __str__(self):
        return self.version_string

    # Comparison algorithm is specified in 'man deb-version'.
    def __cmp__(self, other):
        assert (isinstance(other, DebVersion))

        # Epoch comparison.
        if self.epoch != other.epoch:
            return 1 if self.epoch > other.epoch else -1

        # Upstream version comparison.
        upstream_version_cmp = compare_component(self.upstream_version,
                                                 other.upstream_version)
        if upstream_version_cmp != 0:
            return upstream_version_cmp

        # Debian revision comparison.
        if self.debian_revision == None and other.debian_revision == None:
            return 0
        if self.debian_revision == None:
            return -1
        if other.debian_revision == None:
            return 1
        return compare_component(self.debian_revision, other.debian_revision)

    def __lt__(self, other):
        return self.__cmp__(other) == -1

    def __eq__(self, other):
        return self.__cmp__(other) == 0