gitee.com/mysnapcore/mysnapd@v0.1.0/strutil/version.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2014-2017 Canonical Ltd
     5   *
     6   * This program is free software: you can redistribute it and/or modify
     7   * it under the terms of the GNU General Public License version 3 as
     8   * published by the Free Software Foundation.
     9   *
    10   * This program is distributed in the hope that it will be useful,
    11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
    12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13   * GNU General Public License for more details.
    14   *
    15   * You should have received a copy of the GNU General Public License
    16   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    17   *
    18   */
    19  
    20  package strutil
    21  
    22  import (
    23  	"fmt"
    24  	"strings"
    25  )
    26  
    27  // golang: seriously? that's sad!
    28  func max(a, b int) int {
    29  	if a < b {
    30  		return b
    31  	}
    32  	return a
    33  }
    34  
    35  //go:generate go run $GOINVOKEFLAGS ./chrorder/main.go -package=strutil -output=chrorder.go
    36  
    37  func cmpString(as, bs string) int {
    38  	for i := 0; i < max(len(as), len(bs)); i++ {
    39  		var a uint8
    40  		var b uint8
    41  		if i < len(as) {
    42  			a = as[i]
    43  		}
    44  		if i < len(bs) {
    45  			b = bs[i]
    46  		}
    47  		if chOrder[a] < chOrder[b] {
    48  			return -1
    49  		}
    50  		if chOrder[a] > chOrder[b] {
    51  			return +1
    52  		}
    53  	}
    54  	return 0
    55  }
    56  
    57  func trimLeadingZeroes(a string) string {
    58  	for i := 0; i < len(a); i++ {
    59  		if a[i] != '0' {
    60  			return a[i:]
    61  		}
    62  	}
    63  	return ""
    64  }
    65  
    66  // a and b both match /[0-9]+/
    67  func cmpNumeric(a, b string) int {
    68  	a = trimLeadingZeroes(a)
    69  	b = trimLeadingZeroes(b)
    70  
    71  	switch d := len(a) - len(b); {
    72  	case d > 0:
    73  		return 1
    74  	case d < 0:
    75  		return -1
    76  	}
    77  	for i := 0; i < len(a); i++ {
    78  		switch {
    79  		case a[i] > b[i]:
    80  			return 1
    81  		case a[i] < b[i]:
    82  			return -1
    83  		}
    84  	}
    85  	return 0
    86  }
    87  
    88  func matchEpoch(a string) bool {
    89  	if len(a) == 0 {
    90  		return false
    91  	}
    92  	if a[0] < '0' || a[0] > '9' {
    93  		return false
    94  	}
    95  	var i int
    96  	for i = 1; i < len(a) && a[i] >= '0' && a[i] <= '9'; i++ {
    97  	}
    98  	return i < len(a) && a[i] == ':'
    99  }
   100  
   101  // versionIsValid returns true if the given string is a valid
   102  // version number according to the debian policy
   103  func versionIsValid(a string) bool {
   104  	if matchEpoch(a) {
   105  		return false
   106  	}
   107  	return true
   108  }
   109  
   110  func nextFrag(s string) (frag, rest string, numeric bool) {
   111  	if len(s) == 0 {
   112  		return "", "", false
   113  	}
   114  
   115  	var i int
   116  	if s[0] >= '0' && s[0] <= '9' {
   117  		// is digit
   118  		for i = 1; i < len(s) && s[i] >= '0' && s[i] <= '9'; i++ {
   119  		}
   120  		numeric = true
   121  	} else {
   122  		// not digit
   123  		for i = 1; i < len(s) && (s[i] < '0' || s[i] > '9'); i++ {
   124  		}
   125  	}
   126  	return s[:i], s[i:], numeric
   127  }
   128  
   129  func compareSubversion(va, vb string) int {
   130  	var a, b string
   131  	var anum, bnum bool
   132  	var res int
   133  	for res == 0 {
   134  		a, va, anum = nextFrag(va)
   135  		b, vb, bnum = nextFrag(vb)
   136  		if a == "" && b == "" {
   137  			break
   138  		}
   139  		if anum && bnum {
   140  			res = cmpNumeric(a, b)
   141  		} else {
   142  			res = cmpString(a, b)
   143  		}
   144  	}
   145  	return res
   146  }
   147  
   148  // VersionCompare compare two version strings that follow the debian
   149  // version policy and
   150  // Returns:
   151  //   -1 if a is smaller than b
   152  //    0 if a equals b
   153  //   +1 if a is bigger than b
   154  func VersionCompare(va, vb string) (res int, err error) {
   155  	// FIXME: return err here instead
   156  	if !versionIsValid(va) {
   157  		return 0, fmt.Errorf("invalid version %q", va)
   158  	}
   159  	if !versionIsValid(vb) {
   160  		return 0, fmt.Errorf("invalid version %q", vb)
   161  	}
   162  
   163  	var sa, sb string
   164  	if ia := strings.LastIndexByte(va, '-'); ia < 0 {
   165  		sa = "0"
   166  	} else {
   167  		va, sa = va[:ia], va[ia+1:]
   168  	}
   169  	if ib := strings.LastIndexByte(vb, '-'); ib < 0 {
   170  		sb = "0"
   171  	} else {
   172  		vb, sb = vb[:ib], vb[ib+1:]
   173  	}
   174  
   175  	// the main version number (before the "-")
   176  	res = compareSubversion(va, vb)
   177  	if res != 0 {
   178  		return res, nil
   179  	}
   180  
   181  	// the subversion revision behind the "-"
   182  	return compareSubversion(sa, sb), nil
   183  }