version checking algorithm

Gary_Lerhaupt@Dell.com Gary_Lerhaupt@Dell.com
Thu Jul 1 11:56:01 2004


As you can see from my post yesterday about MAKE changes, DKMS needs to have 
a better ability to compare version numbers (of kernels).  This is also 
true for needing to know whether to invoke rpmbuild or rpm -ba depending 
on the version of RPM on the user's system for mkrpm.

As such, I've coded up two functions which work in conjunction to compare 
versions.  Since bash has no good concept of return -1, if the first 
value is less than the 2nd, it returns 9.  If they are equal, 10.  If the
first is larger than the 2nd, 11.

The main stumbling point is how to compare non digits in bash.  I decided
the easiest way is to convert any non digit to a consistent digit
by some means and preface it with a decimal point.  So, using `od -b`, 1.4a 
to this equals 1.4.141.  I then recurse through the decimal points to 
determine > or <.  

The first function is responsible for converting the versions to something
bash friendly.  The second function recursively calls itself until it has
a result.

So what's the verdict?  There are probably some cleaner ways to do what
I've accomplished and I'm interested in feedback.  Including this in
DMKS will add a dependency on coreutils, which I don't think should 
be a big deal.

Thanks.

#!/bin/bash

version_checker() {
	local ver1=$1
	while [ `echo $ver1 | egrep -c [^0123456789.]` -gt 0 ]; do
		char=`echo $ver1 | sed 's/.*\([^0123456789.]\).*/\1/'`
		char_dec=`echo -n "$char" | od -b | head -1 | awk {'print $2'}`
		ver1=`echo $ver1 | sed "s/$char/.$char_dec/g"`
	done	
	local ver2=$2
	while [ `echo $ver2 | egrep -c [^0123456789.]` -gt 0 ]; do
		char=`echo $ver2 | sed 's/.*\([^0123456789.]\).*/\1/'`
		char_dec=`echo -n "$char" | od -b | head -1 | awk {'print $2'}`
		ver2=`echo $ver2 | sed "s/$char/.$char_dec/g"`
	done	

	ver1=`echo $ver1 | sed 's/\.\./.0/g'`
	ver2=`echo $ver2 | sed 's/\.\./.0/g'`

	do_version_check "$ver1" "$ver2"
}

do_version_check() {
	
	[ "$1" == "$2" ] && return 10

	ver1front=`echo $1 | cut -d "." -f -1`
	ver1back=`echo $1 | cut -d "." -f 2-`
	ver2front=`echo $2 | cut -d "." -f -1`
	ver2back=`echo $2 | cut -d "." -f 2-`

	if [ "$ver1front" != "$1" ] || [ "$ver2front" != "$2" ]; then
		[ "$ver1front" -gt "$ver2front" ] && return 11
		[ "$ver1front" -lt "$ver2front" ] && return 9

		[ "$ver1front" == "$1" ] || [ -z "$ver1back" ] && ver1back=0
		[ "$ver2front" == "$2" ] || [ -z "$ver2back" ] && ver2back=0
		do_version_check "$ver1back" "$ver2back"
		return $?
	else
		[ "$1" -gt "$2" ] && return 11 || return 9
	fi
}

version_checker "$1" "$2"
echo $?