From b1e39e48e0ef147a3fc307bb7446889081516131 Mon Sep 17 00:00:00 2001
From: Xavier Chantry <shiningxc@gmail.com>
Date: Fri, 18 Jul 2008 14:11:06 +0200
Subject: [PATCH] Revert vercmp code to the old version.

Commit 84283672853350a84d2a71b72dc06e180cad1587 updated the vercmp code by
making it closer to the upstream vercmp one, but it turns out this caused
more problems than it fixed.
The differences between our vercmp and rpm's one were not caused by
evolution in the rpm code, but as far as I can see, they were all here
originally when rpmvercmp was first imported in pacman 2.0, and the code was
still almost identical years later in pacman 3.1.
It makes sense to stay with this well tested code and known behavior of
vercmp.

There is just one small fix I was able to backport from latest rpm code,
which ensures the returned value is always 0, -1 or 1.

Signed-off-by: Xavier Chantry <shiningxc@gmail.com>
---
 doc/pacman.8.txt      |    2 +-
 lib/libalpm/package.c |  160 ++++++++++++++++---------------------------------
 pactest/vercmptest.sh |    6 ++
 3 files changed, 58 insertions(+), 110 deletions(-)

diff --git a/doc/pacman.8.txt b/doc/pacman.8.txt
index 08764de..378ae21 100644
--- a/doc/pacman.8.txt
+++ b/doc/pacman.8.txt
@@ -65,7 +65,7 @@ You can also use `pacman -Su` to upgrade all packages that are out of date. See
 to determine which packages need upgrading. This behavior operates as follows:
 
   Alphanumeric:
-    1.0 < 1.0a < 1.0alpha < 1.0b < 1.0beta < 1.0p < 1.0pre < 1.0rc
+    1.0a < 1.0alpha < 1.0b < 1.0beta < 1.0p < 1.0pre < 1.0rc < 1.0
   Numeric:
     1 < 1.0 < 1.1 < 1.1.1 < 1.2 < 2.0 < 3.0.0
 
diff --git a/lib/libalpm/package.c b/lib/libalpm/package.c
index 124d89a..f43ffdb 100644
--- a/lib/libalpm/package.c
+++ b/lib/libalpm/package.c
@@ -562,10 +562,8 @@ alpm_list_t SYMEXPORT *alpm_pkg_compute_requiredby(pmpkg_t *pkg)
  * if a is newer than b, 0 if a and b are the same version, or -1
  * if b is newer than a.
  *
- * This function has been adopted from the rpmvercmp function located
- * at lib/rpmvercmp.c, and was most recently updated against rpm
- * version 4.4.2.3. Small modifications have been made to make it more
- * consistent with the libalpm coding style.
+ * This function was taken from the rpmvercmp function located at
+ * lib/rpmvercmp.c in rpm 4.0.4 and was rewritten to suit our needs.
  *
  * Keep in mind that the pkgrel is only compared if it is available
  * on both versions handed to this function. For example, comparing
@@ -575,155 +573,99 @@ alpm_list_t SYMEXPORT *alpm_pkg_compute_requiredby(pmpkg_t *pkg)
  */
 int SYMEXPORT alpm_pkg_vercmp(const char *a, const char *b)
 {
-	char oldch1, oldch2;
-	char *str1, *str2;
+	char str1[64], str2[64];
 	char *ptr1, *ptr2;
 	char *one, *two;
+	char *rel1 = NULL, *rel2 = NULL;
+	char oldch1, oldch2;
+	int is1num, is2num;
 	int rc;
-	int isnum;
-	int ret = 0;
 
 	ALPM_LOG_FUNC;
 
-	/* libalpm added code. ensure our strings are not null */
-	if(!a) {
-		if(!b) return(0);
-		return(-1);
+	if(!strcmp(a,b)) {
+		return(0);
 	}
-	if(!b) return(1);
 
-	/* easy comparison to see if versions are identical */
-	if(strcmp(a, b) == 0) return(0);
+	strncpy(str1, a, 64);
+	str1[63] = 0;
+	strncpy(str2, b, 64);
+	str2[63] = 0;
 
-	str1 = strdup(a);
-	str2 = strdup(b);
+	/* lose the release number */
+	for(one = str1; *one && *one != '-'; one++);
+	if(one) {
+		*one = '\0';
+		rel1 = ++one;
+	}
+	for(two = str2; *two && *two != '-'; two++);
+	if(two) {
+		*two = '\0';
+		rel2 = ++two;
+	}
 
 	one = str1;
 	two = str2;
 
-	/* loop through each version segment of str1 and str2 and compare them */
-	while(*one && *two) {
+	while(*one || *two) {
 		while(*one && !isalnum((int)*one)) one++;
 		while(*two && !isalnum((int)*two)) two++;
 
-		/* If we ran to the end of either, we are finished with the loop */
-		if(!(*one && *two)) break;
-
 		ptr1 = one;
 		ptr2 = two;
 
-		/* grab first completely alpha or completely numeric segment */
-		/* leave one and two pointing to the start of the alpha or numeric */
-		/* segment and walk ptr1 and ptr2 to end of segment */
+		/* find the next segment for each string */
 		if(isdigit((int)*ptr1)) {
+			is1num = 1;
 			while(*ptr1 && isdigit((int)*ptr1)) ptr1++;
-			while(*ptr2 && isdigit((int)*ptr2)) ptr2++;
-			isnum = 1;
 		} else {
+			is1num = 0;
 			while(*ptr1 && isalpha((int)*ptr1)) ptr1++;
+		}
+		if(isdigit((int)*ptr2)) {
+			is2num = 1;
+			while(*ptr2 && isdigit((int)*ptr2)) ptr2++;
+		} else {
+			is2num = 0;
 			while(*ptr2 && isalpha((int)*ptr2)) ptr2++;
-			isnum = 0;
 		}
 
-		/* save character at the end of the alpha or numeric segment */
-		/* so that they can be restored after the comparison */
 		oldch1 = *ptr1;
 		*ptr1 = '\0';
 		oldch2 = *ptr2;
 		*ptr2 = '\0';
 
-		/* this cannot happen, as we previously tested to make sure that */
-		/* the first string has a non-null segment */
-		if (one == ptr1) {
-			ret = -1;	/* arbitrary */
-			goto cleanup;
+		/* see if we ran out of segments on one string */
+		if(one == ptr1 && two != ptr2) {
+			return(is2num ? -1 : 1);
 		}
-
-		/* take care of the case where the two version segments are */
-		/* different types: one numeric, the other alpha (i.e. empty) */
-		/* numeric segments are always newer than alpha segments */
-		/* XXX See patch #60884 (and details) from bugzilla #50977. */
-		if (two == ptr2) {
-			ret = isnum ? 1 : -1;
-			goto cleanup;
+		if(one != ptr1 && two == ptr2) {
+			return(is1num ? 1 : -1);
 		}
 
-		if (isnum) {
-			/* this used to be done by converting the digit segments */
-			/* to ints using atoi() - it's changed because long  */
-			/* digit segments can overflow an int - this should fix that. */
-
-			/* throw away any leading zeros - it's a number, right? */
-			while (*one == '0') one++;
-			while (*two == '0') two++;
+		/* see if we have a type mismatch (ie, one is alpha and one is digits) */
+		if(is1num && !is2num) return(1);
+		if(!is1num && is2num) return(-1);
 
-			/* whichever number has more digits wins */
-			if (strlen(one) > strlen(two)) {
-				ret = 1;
-				goto cleanup;
-			}
-			if (strlen(two) > strlen(one)) {
-				ret = -1;
-				goto cleanup;
-			}
-		}
+		if(is1num) while(*one == '0') one++;
+		if(is2num) while(*two == '0') two++;
 
-		/* strcmp will return which one is greater - even if the two */
-		/* segments are alpha or if they are numeric.  don't return  */
-		/* if they are equal because there might be more segments to */
-		/* compare */
-		rc = strcmp(one, two);
-		if (rc) {
-			ret = rc < 1 ? -1 : 1;
-			goto cleanup;
-		}
+		rc = strverscmp(one, two);
+		if (rc) return (rc < 1 ? -1 : 1);
 
-		/* restore character that was replaced by null above */
 		*ptr1 = oldch1;
-		one = ptr1;
 		*ptr2 = oldch2;
+		one = ptr1;
 		two = ptr2;
-
-		/* libalpm added code. check if version strings have hit the pkgrel
-		 * portion. depending on which strings have hit, take correct action.
-		 * this is all based on the premise that we only have one dash in
-		 * the version string, and it separates pkgver from pkgrel. */
-		if(*ptr1 == '-' && *ptr2 == '-') {
-			/* no-op, continue comparing since we are equivalent throughout */
-		} else if(*ptr1 == '-') {
-			/* ptr1 has hit the pkgrel and ptr2 has not.
-			 * version 2 is newer iff we are not at the end of ptr2;
-			 * if we are at end then one version had pkgrel and one did not */
-			ret = *ptr2 ? -1 : 0;
-			goto cleanup;
-		} else if(*ptr2 == '-') {
-			/* ptr2 has hit the pkgrel and ptr1 has not.
-			 * version 1 is newer iff we are not at the end of ptr1;
-			 * if we are at end then one version had pkgrel and one did not */
-			ret = *ptr1 ? 1 : 0;
-			goto cleanup;
-		}
-	}
-
-	/* this catches the case where all numeric and alpha segments have */
-	/* compared identically but the segment separating characters were */
-	/* different */
-	if ((!*one) && (!*two)) {
-		ret = 0;
-		goto cleanup;
 	}
 
-	/* whichever version still has characters left over wins */
-	if (!*one) {
-		ret = -1;
-	} else {
-		ret = 1;
+	if((!*one) && (!*two)) {
+		/* compare release numbers */
+		if(rel1 && rel2 && strlen(rel1) && strlen(rel2)) return(alpm_pkg_vercmp(rel1, rel2));
+		return(0);
 	}
 
-cleanup:
-	free(str1);
-	free(str2);
-	return(ret);
+	return(*one ? 1 : -1);
 }
 
 /** @} */
diff --git a/pactest/vercmptest.sh b/pactest/vercmptest.sh
index f8d457e..43ea767 100755
--- a/pactest/vercmptest.sh
+++ b/pactest/vercmptest.sh
@@ -88,6 +88,12 @@ runtest 1.1-1 1.1   0
 runtest 1.0-1 1.1  -1
 runtest 1.1-1 1.0   1
 
+# alphanumeric versions
+runtest 1.5b-1  1.5-1  -1
+runtest 1.5b    1.5    -1
+runtest 1.5b-1  1.5    -1
+runtest 1.5b    1.5.1  -1
+
 #END TESTS
 
 echo
-- 
1.5.6

