version.go 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  1. package pkgbuild
  2. import (
  3. "fmt"
  4. "strconv"
  5. "strings"
  6. )
  7. // Version string
  8. type Version string
  9. type CompleteVersion struct {
  10. Version Version
  11. Epoch uint8
  12. Pkgrel Version
  13. }
  14. func (c *CompleteVersion) String() string {
  15. return fmt.Sprintf("%d:%s-%s", c.Epoch, c.Version, c.Pkgrel)
  16. }
  17. // NewCompleteVersion creates a CompleteVersion including basic version, epoch
  18. // and rel from string
  19. func NewCompleteVersion(s string) (*CompleteVersion, error) {
  20. var err error
  21. epoch := 0
  22. rel := Version("")
  23. // handle possible epoch
  24. versions := strings.Split(s, ":")
  25. if len(versions) > 2 {
  26. return nil, fmt.Errorf("invalid version format: %s", s)
  27. }
  28. if len(versions) > 1 {
  29. epoch, err = strconv.Atoi(versions[0])
  30. if err != nil {
  31. return nil, err
  32. }
  33. }
  34. // handle possible rel
  35. versions = strings.Split(versions[len(versions)-1], "-")
  36. if len(versions) > 2 {
  37. return nil, fmt.Errorf("invalid version format: %s", s)
  38. }
  39. if len(versions) > 1 {
  40. rel = Version(versions[1])
  41. }
  42. // finally check that the actual version is valid
  43. if validPkgver(versions[0]) {
  44. return &CompleteVersion{
  45. Version: Version(versions[0]),
  46. Epoch: uint8(epoch),
  47. Pkgrel: rel,
  48. }, nil
  49. }
  50. return nil, fmt.Errorf("invalid version format: %s", s)
  51. }
  52. // Older returns true if a is older than the argument version string
  53. func (a *CompleteVersion) Older(v string) bool {
  54. b, err := NewCompleteVersion(v)
  55. if err != nil {
  56. return false
  57. }
  58. return a.cmp(b) == -1
  59. }
  60. // Newer returns true if a is newer than the argument version string
  61. func (a *CompleteVersion) Newer(v string) bool {
  62. b, err := NewCompleteVersion(v)
  63. if err != nil {
  64. return false
  65. }
  66. return a.cmp(b) == 1
  67. }
  68. // Equal returns true if a is equal to the argument version string
  69. func (a *CompleteVersion) Equal(v string) bool {
  70. b, err := NewCompleteVersion(v)
  71. if err != nil {
  72. return false
  73. }
  74. return a.cmp(b) == 0
  75. }
  76. // Compare a to b:
  77. // return 1: a is newer than b
  78. // 0: a and b are the same version
  79. // -1: b is newer than a
  80. func (a *CompleteVersion) cmp(b *CompleteVersion) int8 {
  81. if a.Epoch > b.Epoch {
  82. return 1
  83. }
  84. if a.Epoch < b.Epoch {
  85. return -1
  86. }
  87. if a.Version.bigger(b.Version) {
  88. return 1
  89. }
  90. if b.Version.bigger(a.Version) {
  91. return -1
  92. }
  93. if a.Pkgrel.bigger(b.Pkgrel) {
  94. return 1
  95. }
  96. if b.Pkgrel.bigger(a.Pkgrel) {
  97. return -1
  98. }
  99. return 0
  100. }
  101. // Compare alpha and numeric segments of two versions.
  102. // return 1: a is newer than b
  103. // 0: a and b are the same version
  104. // -1: b is newer than a
  105. //
  106. // This is based on the rpmvercmp function used in libalpm
  107. // https://projects.archlinux.org/pacman.git/tree/lib/libalpm/version.c
  108. func rpmvercmp(a, b Version) int {
  109. if a == b {
  110. return 0
  111. }
  112. var one, two, ptr1, ptr2 int
  113. var isNum bool
  114. one, two, ptr1, ptr2 = 0, 0, 0, 0
  115. // loop through each version segment of a and b and compare them
  116. for len(a) > one && len(b) > two {
  117. for len(a) > one && !isAlphaNumeric(a[one]) {
  118. one++
  119. }
  120. for len(b) > two && !isAlphaNumeric(b[two]) {
  121. two++
  122. }
  123. // if we ran to the end of either, we are finished with the loop
  124. if !(len(a) > one && len(b) > two) {
  125. break
  126. }
  127. // if the seperator lengths were different, we are also finished
  128. if one-ptr1 != two-ptr2 {
  129. if one-ptr1 < two-ptr2 {
  130. return -1
  131. }
  132. return 1
  133. }
  134. ptr1 = one
  135. ptr2 = two
  136. // grab first completely alpha or completely numeric segment
  137. // leave one and two pointing to the start of the alpha or numeric
  138. // segment and walk ptr1 and ptr2 to end of segment
  139. if isDigit(a[ptr1]) {
  140. for len(a) > ptr1 && isDigit(a[ptr1]) {
  141. ptr1++
  142. }
  143. for len(b) > ptr2 && isDigit(b[ptr2]) {
  144. ptr2++
  145. }
  146. isNum = true
  147. } else {
  148. for len(a) > ptr1 && isAlpha(a[ptr1]) {
  149. ptr1++
  150. }
  151. for len(b) > ptr2 && isAlpha(b[ptr2]) {
  152. ptr2++
  153. }
  154. isNum = false
  155. }
  156. // take care of the case where the two version segments are
  157. // different types: one numeric, the other alpha (i.e. empty)
  158. // numeric segments are always newer than alpha segments
  159. if two == ptr2 {
  160. if isNum {
  161. return 1
  162. }
  163. return -1
  164. }
  165. if isNum {
  166. // we know this part of the strings only contains digits
  167. // so we can ignore the error value since it should
  168. // always be nil
  169. as, _ := strconv.ParseInt(string(a[one:ptr1]), 10, 0)
  170. bs, _ := strconv.ParseInt(string(b[two:ptr2]), 10, 0)
  171. // whichever number has more digits wins
  172. if as > bs {
  173. return 1
  174. }
  175. if as < bs {
  176. return -1
  177. }
  178. } else {
  179. cmp := alphaCompare(a[one:ptr1], b[two:ptr2])
  180. if cmp < 0 {
  181. return -1
  182. }
  183. if cmp > 0 {
  184. return 1
  185. }
  186. }
  187. // advance one and two to next segment
  188. one = ptr1
  189. two = ptr2
  190. }
  191. // this catches the case where all numeric and alpha segments have
  192. // compared identically but the segment separating characters were
  193. // different
  194. if len(a) <= one && len(b) <= two {
  195. return 0
  196. }
  197. // the final showdown. we never want a remaining alpha string to
  198. // beat an empty string. the logic is a bit weird, but:
  199. // - if one is empty and two is not an alpha, two is newer.
  200. // - if one is an alpha, two is newer.
  201. // - otherwise one is newer.
  202. if (len(a) <= one && !isAlpha(b[two])) || len(a) > one && isAlpha(a[one]) {
  203. return -1
  204. }
  205. return 1
  206. }
  207. // alphaCompare compares two alpha version segments and will return a positive
  208. // value if a is bigger than b and a negative if b is bigger than a else 0
  209. func alphaCompare(a, b Version) int8 {
  210. if a == b {
  211. return 0
  212. }
  213. i := 0
  214. for len(a) > i && len(b) > i && a[i] == b[i] {
  215. i++
  216. }
  217. if len(a) == i && len(b) > i {
  218. return -1
  219. }
  220. if len(b) == i {
  221. return 1
  222. }
  223. return int8(a[i]) - int8(b[i])
  224. }
  225. // check if version number v is bigger than v2
  226. func (v Version) bigger(v2 Version) bool {
  227. return rpmvercmp(v, v2) == 1
  228. }
  229. // isAlphaNumeric reports whether c is an alpha character or digit
  230. func isAlphaNumeric(c uint8) bool {
  231. return isDigit(c) || isAlpha(c)
  232. }
  233. // isAlpha reports whether c is an alpha character
  234. func isAlpha(c uint8) bool {
  235. return 'a' <= c && c <= 'z' || 'A' <= c && c <= 'Z'
  236. }
  237. // isDigit reports whether d is an ASCII digit
  238. func isDigit(d uint8) bool {
  239. return '0' <= d && d <= '9'
  240. }