version.go 6.6 KB

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