version.go 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339
  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 string
  62. func (a *CompleteVersion) Older(v string) bool {
  63. b, err := NewCompleteVersion(v)
  64. if err != nil {
  65. return false
  66. }
  67. return a.cmp(b) == -1
  68. }
  69. // Newer returns true if a is newer than the argument version string
  70. func (a *CompleteVersion) Newer(v string) bool {
  71. b, err := NewCompleteVersion(v)
  72. if err != nil {
  73. return false
  74. }
  75. return a.cmp(b) == 1
  76. }
  77. // Equal returns true if a is equal to the argument version string
  78. func (a *CompleteVersion) Equal(v string) bool {
  79. b, err := NewCompleteVersion(v)
  80. if err != nil {
  81. return false
  82. }
  83. return a.cmp(b) == 0
  84. }
  85. // Satisfies tests whether or not version fits inside the bounds specified by
  86. // dep
  87. func (version *CompleteVersion) Satisfies(dep *Dependency) bool {
  88. var cmpMax int8
  89. var cmpMin int8
  90. if dep.MaxVer != nil {
  91. cmpMax = version.cmp(dep.MaxVer)
  92. if cmpMax == 1 {
  93. return false
  94. }
  95. if cmpMax == 0 && dep.slt {
  96. return false
  97. }
  98. }
  99. if dep.MinVer != nil {
  100. if dep.MaxVer == dep.MinVer {
  101. cmpMin = cmpMax
  102. } else {
  103. cmpMin = version.cmp(dep.MinVer)
  104. }
  105. if cmpMin == -1 {
  106. return false
  107. }
  108. if cmpMin == 0 && dep.sgt {
  109. return false
  110. }
  111. }
  112. return true
  113. }
  114. // Compare a to b:
  115. // return 1: a is newer than b
  116. // 0: a and b are the same version
  117. // -1: b is newer than a
  118. func (a *CompleteVersion) cmp(b *CompleteVersion) int8 {
  119. if a.Epoch > b.Epoch {
  120. return 1
  121. }
  122. if a.Epoch < b.Epoch {
  123. return -1
  124. }
  125. if a.Version.bigger(b.Version) {
  126. return 1
  127. }
  128. if b.Version.bigger(a.Version) {
  129. return -1
  130. }
  131. if a.Pkgrel == "" || b.Pkgrel == "" {
  132. return 0
  133. }
  134. if a.Pkgrel.bigger(b.Pkgrel) {
  135. return 1
  136. }
  137. if b.Pkgrel.bigger(a.Pkgrel) {
  138. return -1
  139. }
  140. return 0
  141. }
  142. // Compare alpha and numeric segments of two versions.
  143. // return 1: a is newer than b
  144. // 0: a and b are the same version
  145. // -1: b is newer than a
  146. //
  147. // This is based on the rpmvercmp function used in libalpm
  148. // https://projects.archlinux.org/pacman.git/tree/lib/libalpm/version.c
  149. func rpmvercmp(av, bv Version) int {
  150. if av == bv {
  151. return 0
  152. }
  153. a, b := []rune(string(av)), []rune(string(bv))
  154. var one, two, ptr1, ptr2 int
  155. var isNum bool
  156. one, two, ptr1, ptr2 = 0, 0, 0, 0
  157. // loop through each version segment of a and b and compare them
  158. for len(a) > one && len(b) > two {
  159. for len(a) > one && !isAlphaNumeric(a[one]) {
  160. one++
  161. }
  162. for len(b) > two && !isAlphaNumeric(b[two]) {
  163. two++
  164. }
  165. // if we ran to the end of either, we are finished with the loop
  166. if !(len(a) > one && len(b) > two) {
  167. break
  168. }
  169. // if the seperator lengths were different, we are also finished
  170. if one-ptr1 != two-ptr2 {
  171. if one-ptr1 < two-ptr2 {
  172. return -1
  173. }
  174. return 1
  175. }
  176. ptr1 = one
  177. ptr2 = two
  178. // grab first completely alpha or completely numeric segment
  179. // leave one and two pointing to the start of the alpha or numeric
  180. // segment and walk ptr1 and ptr2 to end of segment
  181. if isDigit(a[ptr1]) {
  182. for len(a) > ptr1 && isDigit(a[ptr1]) {
  183. ptr1++
  184. }
  185. for len(b) > ptr2 && isDigit(b[ptr2]) {
  186. ptr2++
  187. }
  188. isNum = true
  189. } else {
  190. for len(a) > ptr1 && isAlpha(a[ptr1]) {
  191. ptr1++
  192. }
  193. for len(b) > ptr2 && isAlpha(b[ptr2]) {
  194. ptr2++
  195. }
  196. isNum = false
  197. }
  198. // take care of the case where the two version segments are
  199. // different types: one numeric, the other alpha (i.e. empty)
  200. // numeric segments are always newer than alpha segments
  201. if two == ptr2 {
  202. if isNum {
  203. return 1
  204. }
  205. return -1
  206. }
  207. if isNum {
  208. // we know this part of the strings only contains digits
  209. // so we can ignore the error value since it should
  210. // always be nil
  211. as, _ := strconv.ParseInt(string(a[one:ptr1]), 10, 0)
  212. bs, _ := strconv.ParseInt(string(b[two:ptr2]), 10, 0)
  213. // whichever number has more digits wins
  214. if as > bs {
  215. return 1
  216. }
  217. if as < bs {
  218. return -1
  219. }
  220. } else {
  221. cmp := alphaCompare(a[one:ptr1], b[two:ptr2])
  222. if cmp < 0 {
  223. return -1
  224. }
  225. if cmp > 0 {
  226. return 1
  227. }
  228. }
  229. // advance one and two to next segment
  230. one = ptr1
  231. two = ptr2
  232. }
  233. // this catches the case where all numeric and alpha segments have
  234. // compared identically but the segment separating characters were
  235. // different
  236. if len(a) <= one && len(b) <= two {
  237. return 0
  238. }
  239. // the final showdown. we never want a remaining alpha string to
  240. // beat an empty string. the logic is a bit weird, but:
  241. // - if one is empty and two is not an alpha, two is newer.
  242. // - if one is an alpha, two is newer.
  243. // - otherwise one is newer.
  244. if (len(a) <= one && !isAlpha(b[two])) || len(a) > one && isAlpha(a[one]) {
  245. return -1
  246. }
  247. return 1
  248. }
  249. // alphaCompare compares two alpha version segments and will return a positive
  250. // value if a is bigger than b and a negative if b is bigger than a else 0
  251. func alphaCompare(a, b []rune) int8 {
  252. if string(a) == string(b) {
  253. return 0
  254. }
  255. i := 0
  256. for len(a) > i && len(b) > i && a[i] == b[i] {
  257. i++
  258. }
  259. if len(a) == i && len(b) > i {
  260. return -1
  261. }
  262. if len(b) == i {
  263. return 1
  264. }
  265. return int8(a[i]) - int8(b[i])
  266. }
  267. // check if version number v is bigger than v2
  268. func (v Version) bigger(v2 Version) bool {
  269. return rpmvercmp(v, v2) == 1
  270. }
  271. // isAlphaNumeric reports whether c is an alpha character or digit
  272. func isAlphaNumeric(c rune) bool {
  273. return isDigit(c) || isAlpha(c)
  274. }
  275. // isAlpha reports whether c is an alpha character
  276. func isAlpha(c rune) bool {
  277. return unicode.IsLetter(c)
  278. }
  279. // isDigit reports whether d is an ASCII digit
  280. func isDigit(d rune) bool {
  281. return unicode.IsDigit(d)
  282. }