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

Reply via email to