version.go 6.0 KB

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