On Monday 01 October 2007 22:59:40 Roy Marples wrote:
> This version, I believe, is more readable and maintainable then the one
> currently in portage. It also uses a lot less code and has the bonus of
> being pure sh.
It should be noted that that first draft was developed on bash only.
Attached is a patch that fixes quoting on all shells and shift errors on
ash/dash/busybox variants.
Also attached is the patched versionater.eclass again.
Thanks
Roy
# Copyright 2007 Gentoo Foundation
# Distributed under the terms of the GNU General Public License v2
# sh version of versionator.eclass
# get_last_version_component_index implementation is not implemented because
# 1) The name encourages the use of bash arrarys
# 2) User could do $(($(get_version_component_count) - 1)) to achieve the
# same result
_get_all_version_components() {
local dosep=$1 ver=${2:-${PV}} x= last= this=
while [ -n "${ver}" ]; do
x=$(printf "%c" "${ver}")
case "${x}" in
[0-9]) this="digit";;
-|.|_) this="sep";;
*) this="alpha";;
esac
if ${dosep} || [ "${this}" != "sep" ]; then
[ -n "${last}" -a "${this}" != "${last}" ] && printf " "
printf "%c" "${x}"
fi
last=${this}
ver=${ver#${x}}
done
}
# Split up a version string into its component parts. If no parameter is
# supplied, defaults to $PV.
# 0.8.3 -> 0 . 8 . 3
# 7c -> 7 c
# 3.0_p2 -> 3 . 0 _ p2
# 20040905 -> 20040905
# 3.0c-r1 -> 3 . 0 c - r1
get_all_version_components() {
_get_all_version_components true "$@"
}
# Get the important version components, excluding '.', '-' and '_'. Defaults to
# $PV if no parameter is supplied.
# 0.8.3 -> 0 8 3
# 7c -> 7 c
# 3.0_p2 -> 3 0 p2
# 20040905 -> 20040905
# 3.0c-r1 -> 3 0 c r1
get_version_components() {
_get_all_version_components false "$@"
}
# Get the major version of a value. Defaults to $PV if no parameter is supplied.
# 0.8.3 -> 0
# 7c -> 7
# 3.0_p2 -> 3
# 20040905 -> 20040905
# 3.0c-r1 -> 3
get_major_version() {
set -- $(get_version_components "$@")
printf "%s" "$1"
}
# Get everything after the major version and its separator (if present) of a
# value. Defaults to $PV if no parameter is supplied.
# 0.8.3 -> 8.3
# 7c -> c
# 3.0_p2 -> 0_p2
# 20040905 -> (empty string)
# 3.0c-r1 -> 0c-r1
get_after_major_version() {
printf "%s" "[EMAIL PROTECTED](get_major_version "$@")}"
}
_replace_version_separator_n() {
local n=$1 sep=$2 i=0
set -- $(get_all_version_components "$3")
while [ -n "$1" ]; do
case "$1" in
-|.|_)
i=$((${i} + 1))
if [ "${i}" = "${n}" ]; then
printf "%s" "${sep}"
else
printf "%s" "$1"
fi
;;
*)
printf "%s" "$1"
;;
esac
shift
done
}
_replace_version_separator_a() {
local n=$1 sep=$2 i=0
set -- $(get_all_version_components "$3")
while [ -n "$1" ]; do
if [ "${n}" = "$1" ]; then
printf "%s" "${sep}"
n=
else
printf "%s" "$1"
fi
shift
done
}
# Replace the $1th separator with $2 in $3 (defaults to $PV if $3 is not
# supplied). If there are fewer than $1 separators, don't change anything.
# 1 '_' 1.2.3 -> 1_2.3
# 2 '_' 1.2.3 -> 1.2_3
# 1 '_' 1b-2.3 -> 1b_2.3
# Rather than being a number, $1 can be a separator character such as '-', '.'
# or '_'. In this case, the first separator of this kind is selected.
replace_version_separator() {
case "$1" in
[0-9]*) _replace_version_separator_n "$@";;
*) _replace_version_separator_a "$@";;
esac
}
# Replace all version separators in $2 (defaults to $PV) with $1.
# '_' 1b.2.3 -> 1b_2_3
replace_all_version_separators() {
local sep=$1
set -- $(get_all_version_components "$2")
while [ -n "$1" ]; do
case "$1" in
-|.|_) printf "%s" "${sep}";;
*) printf "%s" "$1";;
esac
shift
done
}
# Delete the $1th separator in $2 (defaults to $PV if $2 is not supplied). If
# there are fewer than $1 separators, don't change anything.
# 1 1.2.3 -> 12.3
# 2 1.2.3 -> 1.23
# 1 1b-2.3 -> 1b2.3
# Rather than being a number, $1 can be a separator character such as '-', '.'
# or '_'. In this case, the first separator of this kind is deleted.
delete_version_separator() {
replace_version_separator "$1" "" "$2"
}
# Delete all version separators in $1 (defaults to $PV).
# 1b.2.3 -> 1b23
delete_all_version_separators() {
replace_all_version_separators "" "$@"
}
# How many version components are there in $1 (defaults to $PV)?
# 1.0.1 -> 3
# 3.0c-r1 -> 4
get_version_component_count() {
set -- $(get_version_components "$@")
printf "%s" "$#"
}
# Get a particular component or range of components from the version. If no
# version parameter is supplied, defaults to $PV.
# 1 1.2.3 -> 1
# 1-2 1.2.3 -> 1.2
# 2- 1.2.3 -> 2.3
get_version_component_range() {
[ -z "$1" ] && return 1
local range="$(get_all_version_components "$1")"
shift
local vers="$(get_all_version_components "$@")"
set -- ${range}
local one=$1 two=$2 three=$3
set -- ${vers}
local i=1
while [ ${i} -lt ${one} ]; do
shift; shift
i=$((${i} + 1))
done
printf "%s" "$1"
[ "${two}" != "-" ] && return
shift
[ -z "${three}" ] && three=$(get_version_component_count "${vers}")
while [ ${i} -lt ${three} ]; do
printf "%s" "$1$2"
shift; shift
i=$((${i} + 1))
done
}
_version_getn() {
local v=$1
case "$1" in
0*) v=${1##*0}; v=${v:-0};;
-|.|_) v=0;;
esac
printf "%s" "${v}"
}
_version_is_prefix() {
case "$1" in
alpha|beta|pre|rc) return 0;;
esac
return 1
}
# Takes two parameters (a, b) which are versions. If a is an earlier version
# than b, returns 1. If a is identical to b, return 2. If b is later than a,
# return 3. You probably want version_is_at_least rather than this function.
# May not be very reliable. Test carefully before using this.
version_compare() {
# Don't beat around the bush
[ "$1" = "$2" ] && return 2
local ver1="$(get_all_version_components "$1")" ver11= ver11n=
local ver2="$(get_all_version_components "$2")" ver21= ver21n=
while [ -n "${ver1}" -o -n "${ver2}" ]; do
# Grab the components and trim leading 0's
set -- ${ver1}
ver11=$(_version_getn "$@")
[ -n "$1" ] && shift
ver1="$@"
set -- ${ver2}
ver21=$(_version_getn "$@")
[ -n "$1" ] && shift
ver2="$@"
if _version_is_prefix "${ver11}"; then
_version_is_prefix "${ver21}" || return 1
else
_version_is_prefix "${ver21}" && return 3
fi
[ -z "${ver11}" ] && ver11=0
[ -z "${ver21}" ] && ver21=0
[ "${ver11}" = "${ver21}" ] && continue
case "${ver11}" in
[0-9]*) ver11n=true;;
*) ver11n=false;;
esac
case "${ver21}" in
[0-9]*) ver21n=true;;
*) ver21n=false;;
esac
if ${ver11n} && ${ver21n}; then
# Both are numbers
[ "${ver11}" -lt "${ver21}" ] && return 1
[ "${ver11}" -gt "${ver21}" ] && return 3
fi
# Either is not a number, so lexical comparison
[ "${ver11}" "<" "${ver21}" ] && return 1
[ "${ver11}" ">" "${ver21}" ] && return 3
done
# All equal then
return 2
}
# Is $2 (defaults to $PVR) at least version $1? Intended for use in eclasses
# only. May not be reliable, be sure to do very careful testing before actually
# using this. Prod ciaranm if you find something it can't handle.
version_is_at_least() {
version_compare "$1" "${2:-${PVR}}"
case $? in
1|2) return 0;;
3) return 1;;
*) die "versionator compare bug";;
esac
}
# Returns its parameters sorted, highest version last.
version_sort() {
local sorted= left="$@" item=
while [ -n "${left}" ]; do
set -- ${left}
item=$1
shift
left="$@"
set -- ${sorted}
sorted=
local inserted=false
while [ -n "$1" ]; do
version_compare "${item}" "$1"
if [ "$?" = "1" ]; then
sorted="${sorted}${sorted:+ }${item} $*"
continue 2
fi
sorted="${sorted}${sorted:+ }$1"
shift
done
sorted="${sorted}${sorted:+ }${item}"
done
printf "%s" "${sorted}"
}
__versionator__test_version_compare() {
local lt=1 eq=2 gt=3 p= q=
__versionator__test_version_compare_t() {
version_compare "$1" "$3"
local r=$?
[ "${r}" != "$2" ] && echo "FAIL: [EMAIL PROTECTED] (got ${r}
exp $2)"
}
echo "
0 $lt 1
1 $lt 2
2 $gt 1
2 $eq 2
0 $eq 0
10 $lt 20
68 $eq 068
068 $gt 67
068 $lt 69
1.0 $lt 2.0
2.0 $eq 2.0
2.0 $gt 1.0
1.0 $gt 0.0
0.0 $eq 0.0
0.0 $lt 1.0
0.1 $lt 0.2
0.2 $eq 0.2
0.3 $gt 0.2
1.2 $lt 2.1
2.1 $gt 1.2
1.2.3 $lt 1.2.4
1.2.4 $gt 1.2.3
1.2.0 $eq 1.2
1.2.1 $gt 1.2
1.2 $lt 1.2.1
1.2b $eq 1.2b
1.2b $lt 1.2c
1.2b $gt 1.2a
1.2b $gt 1.2
1.2 $lt 1.2a
1.3 $gt 1.2a
1.3 $lt 1.3a
1.0_alpha7 $lt 1.0_beta7
1.0_beta $lt 1.0_pre
1.0_pre5 $lt 1.0_rc2
1.0_rc2 $lt 1.0
1.0_p1 $gt 1.0
1.0_p1-r1 $gt 1.0_p1
1.0_alpha6-r1 $gt 1.0_alpha6
1.0_beta6-r1 $gt 1.0_alpha6-r2
1.0_pre1 $lt 1.0-p1
1.0p $gt 1.0_p1
1.0r $gt 1.0-r1
1.6.15 $gt 1.6.10-r2
1.6.10-r2 $lt 1.6.15
" | while read a b c ; do
[ -z "${a}${b}${c}" ] && continue;
__versionator__test_version_compare_t "${a}" "${b}" "${c}"
done
for q in "alpha beta pre rc=${lt};${gt}" "p r=${gt};${lt}" ; do
for p in ${q%%=*} ; do
local c=${q##*=}
local alt=${c%%;*} agt=${c##*;}
__versionator__test_version_compare_t "1.0" $agt
"1.0_${p}"
__versionator__test_version_compare_t "1.0" $agt
"1.0_${p}1"
__versionator__test_version_compare_t "1.0" $agt
"1.0_${p}068"
__versionator__test_version_compare_t "2.0_${p}"
$alt "2.0"
__versionator__test_version_compare_t "2.0_${p}1"
$alt "2.0"
__versionator__test_version_compare_t "2.0_${p}068"
$alt "2.0"
__versionator__test_version_compare_t "1.0_${p}" $eq
"1.0_${p}"
__versionator__test_version_compare_t "0.0_${p}" $lt
"0.0_${p}1"
__versionator__test_version_compare_t "666_${p}3" $gt
"666_${p}"
__versionator__test_version_compare_t "1_${p}7" $lt
"1_${p}8"
__versionator__test_version_compare_t "1_${p}7" $eq
"1_${p}7"
__versionator__test_version_compare_t "1_${p}7" $gt
"1_${p}6"
__versionator__test_version_compare_t "1_${p}09" $eq
"1_${p}9"
done
done
for p in "-r" "_p" ; do
__versionator__test_version_compare_t "7.2${p}1" $lt "7.2${p}2"
__versionator__test_version_compare_t "7.2${p}2" $gt "7.2${p}1"
__versionator__test_version_compare_t "7.2${p}3" $gt "7.2${p}2"
__versionator__test_version_compare_t "7.2${p}2" $lt "7.2${p}3"
done
}
173c173
< local range=$(get_all_version_components "$1")
---
> local range="$(get_all_version_components "$1")"
175c175
< local vers=$(get_all_version_components "$@")
---
> local vers="$(get_all_version_components "$@")"
223,224c223,224
< local ver1=$(get_all_version_components "$1") ver11= ver11n=
< local ver2=$(get_all_version_components "$2") ver21= ver21n=
---
> local ver1="$(get_all_version_components "$1")" ver11= ver11n=
> local ver2="$(get_all_version_components "$2")" ver21= ver21n=
230c230
< shift
---
> [ -n "$1" ] && shift
234c234
< shift
---
> [ -n "$1" ] && shift