2 * Copyright (c) 2006-2012 Pacman Development Team <pacman-dev@archlinux.org>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
25 * Some functions in this file have been adopted from the rpm source, notably
26 * 'rpmvercmp' located at lib/rpmvercmp.c and 'parseEVR' located at
27 * lib/rpmds.c. It was most recently updated against rpm version 4.8.1. Small
28 * modifications have been made to make it more consistent with the libalpm
33 * Split EVR into epoch, version, and release components.
34 * @param evr [epoch:]version[-release] string
35 * @retval *ep pointer to epoch
36 * @retval *vp pointer to version
37 * @retval *rp pointer to release
39 static void parseEVR(char *evr
, const char **ep
, const char **vp
,
48 /* s points to epoch terminator */
49 while (*s
&& isdigit(*s
)) s
++;
50 /* se points to version terminator */
61 /* different from RPM- always assume 0 epoch */
78 * Compare alpha and numeric segments of two versions.
79 * return 1: a is newer than b
80 * 0: a and b are the same version
81 * -1: b is newer than a
83 static int rpmvercmp(const char *a
, const char *b
)
93 /* easy comparison to see if versions are identical */
94 if(strcmp(a
, b
) == 0) return 0;
102 /* loop through each version segment of str1 and str2 and compare them */
103 while (*one
&& *two
) {
104 while (*one
&& !isalnum((int)*one
)) one
++;
105 while (*two
&& !isalnum((int)*two
)) two
++;
107 /* If we ran to the end of either, we are finished with the loop */
108 if (!(*one
&& *two
)) break;
110 /* If the separator lengths were different, we are also finished */
111 if ((one
- ptr1
) != (two
- ptr2
)) {
112 return (one
- ptr1
) < (two
- ptr2
) ? -1 : 1;
118 /* grab first completely alpha or completely numeric segment */
119 /* leave one and two pointing to the start of the alpha or numeric */
120 /* segment and walk ptr1 and ptr2 to end of segment */
121 if (isdigit((int)*ptr1
)) {
122 while (*ptr1
&& isdigit((int)*ptr1
)) ptr1
++;
123 while (*ptr2
&& isdigit((int)*ptr2
)) ptr2
++;
126 while (*ptr1
&& isalpha((int)*ptr1
)) ptr1
++;
127 while (*ptr2
&& isalpha((int)*ptr2
)) ptr2
++;
131 /* save character at the end of the alpha or numeric segment */
132 /* so that they can be restored after the comparison */
138 /* this cannot happen, as we previously tested to make sure that */
139 /* the first string has a non-null segment */
141 ret
= -1; /* arbitrary */
145 /* take care of the case where the two version segments are */
146 /* different types: one numeric, the other alpha (i.e. empty) */
147 /* numeric segments are always newer than alpha segments */
148 /* XXX See patch #60884 (and details) from bugzilla #50977. */
150 ret
= isnum
? 1 : -1;
155 /* this used to be done by converting the digit segments */
156 /* to ints using atoi() - it's changed because long */
157 /* digit segments can overflow an int - this should fix that. */
159 /* throw away any leading zeros - it's a number, right? */
160 while (*one
== '0') one
++;
161 while (*two
== '0') two
++;
163 /* whichever number has more digits wins */
164 if (strlen(one
) > strlen(two
)) {
168 if (strlen(two
) > strlen(one
)) {
174 /* strcmp will return which one is greater - even if the two */
175 /* segments are alpha or if they are numeric. don't return */
176 /* if they are equal because there might be more segments to */
178 rc
= strcmp(one
, two
);
180 ret
= rc
< 1 ? -1 : 1;
184 /* restore character that was replaced by null above */
191 /* this catches the case where all numeric and alpha segments have */
192 /* compared identically but the segment separating characters were */
194 if ((!*one
) && (!*two
)) {
199 /* the final showdown. we never want a remaining alpha string to
200 * beat an empty string. the logic is a bit weird, but:
201 * - if one is empty and two is not an alpha, two is newer.
202 * - if one is an alpha, two is newer.
203 * - otherwise one is newer.
205 if ( (!*one
&& !isalpha((int)*two
))
206 || isalpha((int)*one
) ) {
218 /** Compare two version strings and determine which one is 'newer'.
219 * Returns a value comparable to the way strcmp works. Returns 1
220 * if a is newer than b, 0 if a and b are the same version, or -1
221 * if b is newer than a.
223 * Different epoch values for version strings will override any further
224 * comparison. If no epoch is provided, 0 is assumed.
226 * Keep in mind that the pkgrel is only compared if it is available
227 * on both versions handed to this function. For example, comparing
228 * 1.5-1 and 1.5 will yield 0; comparing 1.5-1 and 1.5-2 will yield
229 * -1 as expected. This is mainly for supporting versioned dependencies
230 * that do not include the pkgrel.
232 int SYMEXPORT
alpm_pkg_vercmp(const char *a
, const char *b
)
235 const char *epoch1
, *ver1
, *rel1
;
236 const char *epoch2
, *ver2
, *rel2
;
239 /* ensure our strings are not null */
247 /* another quick shortcut- if full version specs are equal */
248 if(strcmp(a
, b
) == 0) {
252 /* Parse both versions into [epoch:]version[-release] triplets. We probably
253 * don't need epoch and release to support all the same magic, but it is
254 * easier to just run it all through the same code. */
258 /* parseEVR modifies passed in version, so have to dupe it first */
259 parseEVR(full1
, &epoch1
, &ver1
, &rel1
);
260 parseEVR(full2
, &epoch2
, &ver2
, &rel2
);
262 ret
= rpmvercmp(epoch1
, epoch2
);
264 ret
= rpmvercmp(ver1
, ver2
);
265 if(ret
== 0 && rel1
&& rel2
) {
266 ret
= rpmvercmp(rel1
, rel2
);
275 /* vim: set ts=2 sw=2 noet: */