Rename pmconflict_t to alpm_conflict_t
[pacman-ng.git] / lib / libalpm / version.c
blob9f3a9b71bbcd9a12d09cf342624ac594be8043f0
1 /*
2 * Copyright (c) 2006-2011 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/>.
18 #include "config.h"
20 #include <string.h>
21 #include <ctype.h>
23 /* libalpm */
24 #include "util.h"
26 /**
27 * Some functions in this file have been adopted from the rpm source, notably
28 * 'rpmvercmp' located at lib/rpmvercmp.c and 'parseEVR' located at
29 * lib/rpmds.c. It was most recently updated against rpm version 4.8.1. Small
30 * modifications have been made to make it more consistent with the libalpm
31 * coding style.
34 /**
35 * Split EVR into epoch, version, and release components.
36 * @param evr [epoch:]version[-release] string
37 * @retval *ep pointer to epoch
38 * @retval *vp pointer to version
39 * @retval *rp pointer to release
41 static void parseEVR(char *evr, const char **ep, const char **vp,
42 const char **rp)
44 const char *epoch;
45 const char *version;
46 const char *release;
47 char *s, *se;
49 s = evr;
50 /* s points to epoch terminator */
51 while (*s && isdigit(*s)) s++;
52 /* se points to version terminator */
53 se = strrchr(s, '-');
55 if(*s == ':') {
56 epoch = evr;
57 *s++ = '\0';
58 version = s;
59 if(*epoch == '\0') {
60 epoch = "0";
62 } else {
63 /* different from RPM- always assume 0 epoch */
64 epoch = "0";
65 version = evr;
67 if(se) {
68 *se++ = '\0';
69 release = se;
70 } else {
71 release = NULL;
74 if(ep) *ep = epoch;
75 if(vp) *vp = version;
76 if(rp) *rp = release;
79 /**
80 * Compare alpha and numeric segments of two versions.
81 * return 1: a is newer than b
82 * 0: a and b are the same version
83 * -1: b is newer than a
85 static int rpmvercmp(const char *a, const char *b)
87 char oldch1, oldch2;
88 char *str1, *str2;
89 char *ptr1, *ptr2;
90 char *one, *two;
91 int rc;
92 int isnum;
93 int ret = 0;
95 /* easy comparison to see if versions are identical */
96 if(strcmp(a, b) == 0) return 0;
98 str1 = strdup(a);
99 str2 = strdup(b);
101 one = str1;
102 two = str2;
104 /* loop through each version segment of str1 and str2 and compare them */
105 while(*one && *two) {
106 while(*one && !isalnum((int)*one)) one++;
107 while(*two && !isalnum((int)*two)) two++;
109 /* If we ran to the end of either, we are finished with the loop */
110 if(!(*one && *two)) break;
112 ptr1 = one;
113 ptr2 = two;
115 /* grab first completely alpha or completely numeric segment */
116 /* leave one and two pointing to the start of the alpha or numeric */
117 /* segment and walk ptr1 and ptr2 to end of segment */
118 if(isdigit((int)*ptr1)) {
119 while(*ptr1 && isdigit((int)*ptr1)) ptr1++;
120 while(*ptr2 && isdigit((int)*ptr2)) ptr2++;
121 isnum = 1;
122 } else {
123 while(*ptr1 && isalpha((int)*ptr1)) ptr1++;
124 while(*ptr2 && isalpha((int)*ptr2)) ptr2++;
125 isnum = 0;
128 /* save character at the end of the alpha or numeric segment */
129 /* so that they can be restored after the comparison */
130 oldch1 = *ptr1;
131 *ptr1 = '\0';
132 oldch2 = *ptr2;
133 *ptr2 = '\0';
135 /* this cannot happen, as we previously tested to make sure that */
136 /* the first string has a non-null segment */
137 if (one == ptr1) {
138 ret = -1; /* arbitrary */
139 goto cleanup;
142 /* take care of the case where the two version segments are */
143 /* different types: one numeric, the other alpha (i.e. empty) */
144 /* numeric segments are always newer than alpha segments */
145 /* XXX See patch #60884 (and details) from bugzilla #50977. */
146 if (two == ptr2) {
147 ret = isnum ? 1 : -1;
148 goto cleanup;
151 if (isnum) {
152 /* this used to be done by converting the digit segments */
153 /* to ints using atoi() - it's changed because long */
154 /* digit segments can overflow an int - this should fix that. */
156 /* throw away any leading zeros - it's a number, right? */
157 while (*one == '0') one++;
158 while (*two == '0') two++;
160 /* whichever number has more digits wins */
161 if (strlen(one) > strlen(two)) {
162 ret = 1;
163 goto cleanup;
165 if (strlen(two) > strlen(one)) {
166 ret = -1;
167 goto cleanup;
171 /* strcmp will return which one is greater - even if the two */
172 /* segments are alpha or if they are numeric. don't return */
173 /* if they are equal because there might be more segments to */
174 /* compare */
175 rc = strcmp(one, two);
176 if (rc) {
177 ret = rc < 1 ? -1 : 1;
178 goto cleanup;
181 /* restore character that was replaced by null above */
182 *ptr1 = oldch1;
183 one = ptr1;
184 *ptr2 = oldch2;
185 two = ptr2;
188 /* this catches the case where all numeric and alpha segments have */
189 /* compared identically but the segment separating characters were */
190 /* different */
191 if ((!*one) && (!*two)) {
192 ret = 0;
193 goto cleanup;
196 /* the final showdown. we never want a remaining alpha string to
197 * beat an empty string. the logic is a bit weird, but:
198 * - if one is empty and two is not an alpha, two is newer.
199 * - if one is an alpha, two is newer.
200 * - otherwise one is newer.
201 * */
202 if ( (!*one && !isalpha((int)*two))
203 || isalpha((int)*one) ) {
204 ret = -1;
205 } else {
206 ret = 1;
209 cleanup:
210 free(str1);
211 free(str2);
212 return ret;
215 /** Compare two version strings and determine which one is 'newer'.
216 * Returns a value comparable to the way strcmp works. Returns 1
217 * if a is newer than b, 0 if a and b are the same version, or -1
218 * if b is newer than a.
220 * Different epoch values for version strings will override any further
221 * comparison. If no epoch is provided, 0 is assumed.
223 * Keep in mind that the pkgrel is only compared if it is available
224 * on both versions handed to this function. For example, comparing
225 * 1.5-1 and 1.5 will yield 0; comparing 1.5-1 and 1.5-2 will yield
226 * -1 as expected. This is mainly for supporting versioned dependencies
227 * that do not include the pkgrel.
229 int SYMEXPORT alpm_pkg_vercmp(const char *a, const char *b)
231 char *full1, *full2;
232 const char *epoch1, *ver1, *rel1;
233 const char *epoch2, *ver2, *rel2;
234 int ret;
236 /* ensure our strings are not null */
237 if(!a && !b) {
238 return 0;
239 } else if(!a) {
240 return -1;
241 } else if(!b) {
242 return 1;
244 /* another quick shortcut- if full version specs are equal */
245 if(strcmp(a, b) == 0) {
246 return 0;
249 /* Parse both versions into [epoch:]version[-release] triplets. We probably
250 * don't need epoch and release to support all the same magic, but it is
251 * easier to just run it all through the same code. */
252 full1 = strdup(a);
253 full2 = strdup(b);
255 /* parseEVR modifies passed in version, so have to dupe it first */
256 parseEVR(full1, &epoch1, &ver1, &rel1);
257 parseEVR(full2, &epoch2, &ver2, &rel2);
259 ret = rpmvercmp(epoch1, epoch2);
260 if(ret == 0) {
261 ret = rpmvercmp(ver1, ver2);
262 if(ret == 0 && rel1 && rel2) {
263 ret = rpmvercmp(rel1, rel2);
267 free(full1);
268 free(full2);
269 return ret;
272 /* vim: set ts=2 sw=2 noet: */