about summary refs log tree commit diff
path: root/src/string
diff options
context:
space:
mode:
Diffstat (limited to 'src/string')
-rw-r--r--src/string/strverscmp.c57
1 files changed, 25 insertions, 32 deletions
diff --git a/src/string/strverscmp.c b/src/string/strverscmp.c
index 6f37cc68..4daf276d 100644
--- a/src/string/strverscmp.c
+++ b/src/string/strverscmp.c
@@ -2,40 +2,33 @@
 #include <ctype.h>
 #include <string.h>
 
-int strverscmp(const char *l, const char *r)
+int strverscmp(const char *l0, const char *r0)
 {
-	int haszero=1;
-	while (*l==*r) {
-		if (!*l) return 0;
+	const unsigned char *l = (const void *)l0;
+	const unsigned char *r = (const void *)r0;
+	size_t i, dp, j;
+	int z = 1;
 
-		if (*l=='0') {
-			if (haszero==1) {
-				haszero=0;
-			}
-		} else if (isdigit(*l)) {
-			if (haszero==1) {
-				haszero=2;
-			}
-		} else {
-			haszero=1;
-		}
-		l++; r++;
+	/* Find maximal matching prefix and track its maximal digit
+	 * suffix and whether those digits are all zeros. */
+	for (dp=i=0; l[i]==r[i]; i++) {
+		int c = l[i];
+		if (!c) return 0;
+		if (!isdigit(c)) dp=i+1, z=1;
+		else if (c!='0') z=0;
 	}
-	if (haszero==1 && (*l=='0' || *r=='0')) {
-		haszero=0;
-	}
-	if ((isdigit(*l) && isdigit(*r) ) && haszero) {
-		size_t lenl=0, lenr=0;
-		while (isdigit(l[lenl]) ) lenl++;
-		while (isdigit(r[lenr]) ) lenr++;
-		if (lenl==lenr) {
-			return (*l -  *r);
-		} else if (lenl>lenr) {
-			return 1;
-		} else {
-			return -1;
-		}
-	} else {
-		return (*l -  *r);
+
+	if (l[dp]!='0' && r[dp]!='0') {
+		/* If we're not looking at a digit sequence that began
+		 * with a zero, longest digit string is greater. */
+		for (j=i; isdigit(l[j]); j++)
+			if (!isdigit(r[j])) return 1;
+		if (isdigit(r[j])) return -1;
+	} else if (z && dp<i && (isdigit(l[i]) || isdigit(r[i]))) {
+		/* Otherwise, if common prefix of digit sequence is
+		 * all zeros, digits order less than non-digits. */
+		return (unsigned char)(l[i]-'0') - (unsigned char)(r[i]-'0');
 	}
+
+	return l[i] - r[i];
 }