2 * This code is taken from the RPM package manager.
4 * RPM is Copyright (c) 1998 by Red Hat Software, Inc.,
5 * and may be distributed under the terms of the GPL and LGPL.
6 * See http://rpm.org/gitweb?p=rpm.git;a=blob_plain;f=COPYING;hb=HEAD
8 * The code should follow upstream as closely as possible.
9 * See http://rpm.org/gitweb?p=rpm.git;a=blob_plain;f=lib/rpmvercmp.c;hb=HEAD
11 * Currently the only difference as a policy is that upstream uses C99
12 * features and pkg-config does not require a C99 compiler yet.
23 /* macros to help code look more like upstream */
24 #define rstreq(a, b) (strcmp(a, b) == 0)
25 #define risalnum(c) isalnum((char)(c))
26 #define risdigit(c) isdigit((char)(c))
27 #define risalpha(c) isalpha((char)(c))
29 int rmpvercmp_impl(const char *a
, const char *b
, char *buf1
, char *buf2
);
31 /* compare alpha and numeric segments of two versions */
32 /* return 1: a is newer than b */
33 /* 0: a and b are the same version */
34 /* -1: b is newer than a */
35 int rpmvercmp(const char * a
, const char * b
)
37 /* easy comparison to see if versions are identical */
38 if (rstreq(a
, b
)) return 0;
40 char *buf1
= malloc(strlen(a
) + 1);
41 char *buf2
= malloc(strlen(b
) + 1);
43 if (!buf1
|| !buf2
) return 0; // arbitrary
45 int r
= rmpvercmp_impl(a
, b
, buf1
, buf2
);
53 int rmpvercmp_impl(const char *a
, const char *b
, char *str1
, char *str2
) {
65 /* loop through each version segment of str1 and str2 and compare them */
66 while (*one
&& *two
) {
67 while (*one
&& !risalnum(*one
)) one
++;
68 while (*two
&& !risalnum(*two
)) two
++;
70 /* If we ran to the end of either, we are finished with the loop */
71 if (!(*one
&& *two
)) break;
76 /* grab first completely alpha or completely numeric segment */
77 /* leave one and two pointing to the start of the alpha or numeric */
78 /* segment and walk str1 and str2 to end of segment */
79 if (risdigit(*str1
)) {
80 while (*str1
&& risdigit(*str1
)) str1
++;
81 while (*str2
&& risdigit(*str2
)) str2
++;
84 while (*str1
&& risalpha(*str1
)) str1
++;
85 while (*str2
&& risalpha(*str2
)) str2
++;
89 /* save character at the end of the alpha or numeric segment */
90 /* so that they can be restored after the comparison */
96 /* this cannot happen, as we previously tested to make sure that */
97 /* the first string has a non-null segment */
98 if (one
== str1
) return -1; /* arbitrary */
100 /* take care of the case where the two version segments are */
101 /* different types: one numeric, the other alpha (i.e. empty) */
102 /* numeric segments are always newer than alpha segments */
103 /* XXX See patch #60884 (and details) from bugzilla #50977. */
104 if (two
== str2
) return (isnum
? 1 : -1);
107 /* this used to be done by converting the digit segments */
108 /* to ints using atoi() - it's changed because long */
109 /* digit segments can overflow an int - this should fix that. */
111 /* throw away any leading zeros - it's a number, right? */
112 while (*one
== '0') one
++;
113 while (*two
== '0') two
++;
115 /* whichever number has more digits wins */
116 if (strlen(one
) > strlen(two
)) return 1;
117 if (strlen(two
) > strlen(one
)) return -1;
120 /* strcmp will return which one is greater - even if the two */
121 /* segments are alpha or if they are numeric. don't return */
122 /* if they are equal because there might be more segments to */
124 rc
= strcmp(one
, two
);
125 if (rc
) return (rc
< 1 ? -1 : 1);
127 /* restore character that was replaced by null above */
134 /* this catches the case where all numeric and alpha segments have */
135 /* compared identically but the segment separating characters were */
137 if ((!*one
) && (!*two
)) return 0;
139 /* whichever version still has characters left over wins */
140 if (!*one
) return -1; else return 1;