-
Notifications
You must be signed in to change notification settings - Fork 9
/
rpm-versioning.ss
151 lines (130 loc) · 5.04 KB
/
rpm-versioning.ss
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
;;; Comparing
(export #t)
(import
:std/assert :std/misc/list :std/pregexp :std/srfi/13 :std/sugar :std/values :std/text/char-set
./base ./order ./pred)
(def (valid-rpm-version-component? string start: (start 0) end: (end (string-length string)))
(and
(string-index string char-ascii-numeric? start end)
(not (string-index string (cut string-index <> "-~/") start end))
(not (string-contains string ".." start end))))
(def (valid-rpm-architecture-component? string start: (start 0) end: (end (string-length string)))
(and
(string-index string char-ascii-alphabetic? start end)
(not (string-index string (complement char-ascii-alphanumeric-or-underscore?) start end))))
(def (parse-rpm-versioned-name string start: (start 0) end: (end (string-length string)))
(def (err) (error "No valid RPM version in package name" string))
(def (split-at n) (values (substring string start n)
(substring string (1+ n) end)))
(def pos (string-index-right string #\- start end))
(unless (and pos (valid-rpm-version-component? string start: (1+ pos) end: end))
(err))
(def pos2 (string-index-right string #\- start pos))
(split-at (if (and pos2 (valid-rpm-version-component? string start: (1+ pos2) end: pos))
pos2 pos)))
(def (rpm-versioned-name-basename string)
(nth-value 0 (parse-rpm-versioned-name string)))
(def (rpm-versioned-name-version string)
(nth-value 1 (parse-rpm-versioned-name string)))
(def (parse-rpm-path path)
(def directory (path-directory path))
(def extension (path-extension path))
(def basename (path-strip-extension (path-strip-directory path)))
(def dotpos (string-index-right basename #\.))
(assert! (equal? extension "rpm"))
(assert! dotpos)
(assert! (valid-rpm-architecture-component? basename start: (1+ dotpos)))
(def architecture (substring basename (1+ dotpos) (string-length basename)))
(defvalues (name version) (parse-rpm-versioned-name basename end: dotpos))
(values directory name version architecture))
(def (rpm-path-packagename path)
(nth-value 1 (parse-rpm-path path)))
(def (rpm-pathname-version path)
(nth-value 2 (parse-rpm-path path)))
;; For version comparison, I followed
;; https://twiki.cern.ch/twiki/bin/view/Main/RPMAndDebVersioning
;; Given a version or release component of a RPM, parse it into a list
;; of numbers and letters, e.g. "0.99p7" => (0 99 "p" 7)
(def (parse-rpm-version-component v)
(def r [])
(def len (string-length v))
(def i 0)
(def (handle-component predicate f)
(when (and (< i len) (predicate (string-ref v i)))
(let (j (or (string-index v (complement predicate) (1+ i)) len))
(when f (push! (f (substring v i j)) r))
(set! i j))))
(while (< i len)
(handle-component char-ascii-numeric? string->number)
(handle-component char-ascii-alphabetic? identity)
(handle-component (complement char-ascii-alphanumeric?) #f))
(reverse r))
;; Given the first chunks of two respective version numbers,
;; return the symbol < = > depending on which of predicates hold,
;; or #f is none does
(def (compare-rpm-version-chunks ch1 ch2)
(assert! (or (integer? ch1) (string? ch1)))
(assert! (or (integer? ch2) (string? ch2)))
(cond
((and (integer? ch1) (integer? ch2))
(cond
((< ch1 ch2) '<)
((> ch1 ch2) '>)
(else '=)))
;; RPM: integer block beats alphanumeric, so 1.4.1 > 1.4p8
((integer? ch1) '>)
((integer? ch2) '<)
(else
(cond
((string<? ch1 ch2) '<)
((string>? ch1 ch2) '>)
(else '=)))))
(def (compare-rpm-version-components v1 v2)
(def l1 (parse-rpm-version-component v1))
(def l2 (parse-rpm-version-component v2))
(let/cc return
(until (or (null? l1) (null? l2))
(let (r (compare-rpm-version-chunks (pop! l1) (pop! l2)))
(case r
((< > #f) (return r))
((=) #f))))
(cond
((null? l1) (return '<))
((null? l2) (return '>))
(else (return '=)))))
(def (parse-rpm-version x)
(match (pregexp-match "^(?:([0-9]+):)?([^-/~]+)(?:-([^-/~]+))?$" x) ;; TODO: also forbid ..
([_ epoch version release]
(values (if (string-empty? epoch) 0 (string->number epoch))
version release))
(error "bad rpm version" x)))
(def (compare-rpm-versions v1 v2)
(defvalues (epoch1 version1 release1) (parse-rpm-version v1))
(defvalues (epoch2 version2 release2) (parse-rpm-version v2))
(cond
((> epoch1 epoch2) '>)
((< epoch1 epoch2) '<)
(else
(let (r (compare-rpm-version-components version1 version2))
(case r ((< > #f) r)
((=) (compare-rpm-version-components release1 release2)))))))
(def (rpm-version<= v1 v2)
(case (compare-rpm-versions v1 v2)
((< =) #t)
((>) #f)))
(def (rpm-version>= v1 v2)
(case (compare-rpm-versions v1 v2)
((> =) #t)
((<) #f)))
(def (rpm-version< v1 v2)
(case (compare-rpm-versions v1 v2)
((<) #t)
((> =) #f)))
(def (rpm-version> v1 v2)
(case (compare-rpm-versions v1 v2)
((>) #t)
((< =) #f)))
(def (rpm-version= v1 v2)
(case (compare-rpm-versions v1 v2)
((=) #t)
((< >) #f)))