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