1 # Copyright 1999-2020 Gentoo Authors
2 # Distributed under the terms of the GNU General Public License v2
4 # @ECLASS: eapi7-ver.eclass
6 # PMS team <pms@gentoo.org>
8 # Ulrich Müller <ulm@gentoo.org>
9 # Michał Górny <mgorny@gentoo.org>
10 # @SUPPORTED_EAPIS: 0 1 2 3 4 5 6
11 # @BLURB: Testing implementation of EAPI 7 version manipulators
13 # A stand-alone implementation of the version manipulation functions
14 # aimed for EAPI 7. Intended to be used for wider testing of
15 # the proposed functions and to allow ebuilds to switch to the new
16 # model early, with minimal change needed for actual EAPI 7.
18 # https://bugs.gentoo.org/482170
20 # @SUBSECTION Version strings
22 # The functions support arbitrary version strings consisting of version
23 # components interspersed with (possibly empty) version separators.
25 # A version component can either consist purely of digits ([0-9]+)
26 # or purely of uppercase and lowercase letters ([A-Za-z]+). A version
27 # separator is either a string of any other characters ([^A-Za-z0-9]+),
28 # or it occurs at the transition between a sequence of letters
29 # and a sequence of digits, or vice versa. In the latter case,
30 # the version separator is an empty string.
32 # The version is processed left-to-right, and each successive component
33 # is assigned numbers starting with 1. The components are either split
34 # on version separators or on boundaries between digits and letters
35 # (in which case the separator between the components is empty).
36 # Version separators are assigned numbers starting with 1 for
37 # the separator between 1st and 2nd components. As a special case,
38 # if the version string starts with a separator, it is assigned index 0.
43 # 1.2b-alpha4 -> 1 . 2 '' b - alpha '' 4
54 # A range can be specified as 'm' for m-th version component, 'm-'
55 # for all components starting with m-th or 'm-n' for components starting
56 # at m-th and ending at n-th (inclusive). If the range spans outside
57 # the version string, it is truncated silently.
61 7) die "${ECLASS}: EAPI=${EAPI} includes all functions from this eclass" ;;
62 *) die "${ECLASS}: EAPI=${EAPI} unknown" ;;
65 # @FUNCTION: _ver_parse_range
66 # @USAGE: <range> <max>
69 # Parse the range string <range>, setting 'start' and 'end' variables
70 # to the appropriate bounds. <max> specifies the appropriate upper
71 # bound for the range; the user-specified value is truncated to this.
76 [[ ${range} == [0-9]* ]] \
77 || die "${FUNCNAME}: range must start with a number"
79 [[ ${range} == *-* ]] && end=${range#*-} || end=${start}
81 [[ ${start} -le ${end} ]] \
82 || die "${FUNCNAME}: end of range must be >= start"
83 [[ ${end} -le ${max} ]] || end=${max}
89 # @FUNCTION: _ver_split
93 # Split the version string <version> into separator-component array.
94 # Sets 'comp' to an array of the form: ( s_0 c_1 s_1 c_2 s_2 c_3... )
95 # where s_i are separators and c_i are components.
101 # get separators and components
107 # cut the next component; it can be either digits or letters
108 [[ ${v} == [0-9]* ]] && c=${v%%[^0-9]*} || c=${v%%[^a-zA-Z]*}
111 comp+=( "${s}" "${c}" )
116 # @USAGE: <range> [<version>]
118 # Print the substring of the version string containing components
119 # defined by the <range> and the version separators between them.
120 # Processes <version> if specified, ${PV} otherwise.
122 # For the syntax of versions and ranges, please see the eclass
131 local max=$((${#comp[@]}/2))
132 _ver_parse_range "${range}" "${max}"
134 if [[ ${start} -gt 0 ]]; then
135 start=$(( start*2 - 1 ))
137 # Work around a bug in bash-3.2, where "${comp[*]:start:end*2-start}"
138 # inserts stray 0x7f characters for empty array elements
139 printf "%s" "${comp[@]:start:end*2-start}" $'\n'
143 # @USAGE: <range> <repl> [<range> <repl>...] [<version>]
145 # Print the version string after substituting the specified version
146 # separators at <range> with <repl> (string). Multiple '<range> <repl>'
147 # pairs can be specified. Processes <version> if specified,
150 # For the syntax of versions and ranges, please see the eclass
154 (( ${#} & 1 )) && v=${@: -1} || v=${PV}
159 local max=$((${#comp[@]}/2 - 1))
161 while [[ ${#} -ge 2 ]]; do
162 _ver_parse_range "${1}" "${max}"
163 for (( i = start*2; i <= end*2; i+=2 )); do
164 [[ ${i} -eq 0 && -z ${comp[i]} ]] && continue
174 # @FUNCTION: _ver_compare_int
176 # @RETURN: 0 if <a> -eq <b>, 1 if <a> -lt <b>, 3 if <a> -gt <b>
179 # Compare two non-negative integers <a> and <b>, of arbitrary length.
180 # If <a> is equal to, less than, or greater than <b>, return 0, 1, or 3
181 # as exit status, respectively.
183 local a=$1 b=$2 d=$(( ${#1}-${#2} ))
185 # Zero-pad to equal length if necessary.
186 if [[ ${d} -gt 0 ]]; then
187 printf -v b "%0${d}d%s" 0 "${b}"
188 elif [[ ${d} -lt 0 ]]; then
189 printf -v a "%0$(( -d ))d%s" 0 "${a}"
192 [[ ${a} > ${b} ]] && return 3
196 # @FUNCTION: _ver_compare
198 # @RETURN: 1 if <va> < <vb>, 2 if <va> = <vb>, 3 if <va> > <vb>
201 # Compare two versions <va> and <vb>. If <va> is less than, equal to,
202 # or greater than <vb>, return 1, 2, or 3 as exit status, respectively.
204 local va=${1} vb=${2} a an al as ar b bn bl bs br re LC_ALL=C
206 re="^([0-9]+(\.[0-9]+)*)([a-z]?)((_(alpha|beta|pre|rc|p)[0-9]*)*)(-r[0-9]+)?$"
208 [[ ${va} =~ ${re} ]] || die "${FUNCNAME}: invalid version: ${va}"
209 an=${BASH_REMATCH[1]}
210 al=${BASH_REMATCH[3]}
211 as=${BASH_REMATCH[4]}
212 ar=${BASH_REMATCH[7]}
214 [[ ${vb} =~ ${re} ]] || die "${FUNCNAME}: invalid version: ${vb}"
215 bn=${BASH_REMATCH[1]}
216 bl=${BASH_REMATCH[3]}
217 bs=${BASH_REMATCH[4]}
218 br=${BASH_REMATCH[7]}
220 # Compare numeric components (PMS algorithm 3.2)
222 _ver_compare_int "${an%%.*}" "${bn%%.*}" || return
224 while [[ ${an} == *.* && ${bn} == *.* ]]; do
225 # Other components (PMS algorithm 3.3)
230 if [[ ${a} == 0* || ${b} == 0* ]]; then
231 # Remove any trailing zeros
232 [[ ${a} =~ 0+$ ]] && a=${a%"${BASH_REMATCH[0]}"}
233 [[ ${b} =~ 0+$ ]] && b=${b%"${BASH_REMATCH[0]}"}
234 [[ ${a} > ${b} ]] && return 3
235 [[ ${a} < ${b} ]] && return 1
237 _ver_compare_int "${a}" "${b}" || return
240 [[ ${an} == *.* ]] && return 3
241 [[ ${bn} == *.* ]] && return 1
243 # Compare letter components (PMS algorithm 3.4)
244 [[ ${al} > ${bl} ]] && return 3
245 [[ ${al} < ${bl} ]] && return 1
247 # Compare suffixes (PMS algorithm 3.5)
250 while [[ -n ${as} && -n ${bs} ]]; do
251 # Compare each suffix (PMS algorithm 3.6)
254 if [[ ${a%%[0-9]*} == "${b%%[0-9]*}" ]]; then
255 _ver_compare_int "${a##*[a-z]}" "${b##*[a-z]}" || return
258 [[ ${a%%[0-9]*} == p ]] && return 3
259 [[ ${b%%[0-9]*} == p ]] && return 1
260 # Hack: Use that alpha < beta < pre < rc alphabetically
261 [[ ${a} > ${b} ]] && return 3 || return 1
266 if [[ -n ${as} ]]; then
267 [[ ${as} == p[_0-9]* ]] && return 3 || return 1
268 elif [[ -n ${bs} ]]; then
269 [[ ${bs} == p[_0-9]* ]] && return 1 || return 3
272 # Compare revision components (PMS algorithm 3.7)
273 _ver_compare_int "${ar#-r}" "${br#-r}" || return
278 # @FUNCTION: ver_test
279 # @USAGE: [<v1>] <op> <v2>
281 # Check if the relation <v1> <op> <v2> is true. If <v1> is not specified,
282 # default to ${PVR}. <op> can be -gt, -ge, -eq, -ne, -le, -lt.
283 # Both versions must conform to the PMS version syntax (with optional
284 # revision parts), and the comparison is performed according to
285 # the algorithm specified in the PMS.
289 if [[ $# -eq 3 ]]; then
296 [[ $# -eq 2 ]] || die "${FUNCNAME}: bad number of arguments"
302 -eq|-ne|-lt|-le|-gt|-ge) ;;
303 *) die "${FUNCNAME}: invalid operator: ${op}" ;;
306 _ver_compare "${va}" "${vb}"