Magellan Linux

Contents of /branches/mage-next/src/highestver.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2629 - (show annotations) (download)
Wed Mar 5 09:05:49 2014 UTC (10 years, 1 month ago) by niro
File MIME type: text/plain
File size: 8398 byte(s)
-use strdup() to fix segfaults
1 /*
2 * Copyright (c) 2006-2011 Pacman Development Team <pacman-dev@archlinux.org>
3 *
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.
8 *
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.
13 *
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/>.
16 */
17
18 #include <stdlib.h>
19 #include <stdio.h> /* printf */
20 #include <string.h>
21 #include <ctype.h>
22
23 /**
24 * Some functions in this file have been adopted from the rpm source, notably
25 * 'rpmvercmp' located at lib/rpmvercmp.c and 'parseEVR' located at
26 * lib/rpmds.c. It was most recently updated against rpm version 4.8.1. Small
27 * modifications have been made to make it more consistent with the libalpm
28 * coding style.
29 */
30
31 /**
32 * Split EVR into epoch, version, and release components.
33 * @param evr [epoch:]version[-release] string
34 * @retval *ep pointer to epoch
35 * @retval *vp pointer to version
36 * @retval *rp pointer to release
37 */
38 static void parseEVR(char *evr, const char **ep, const char **vp,
39 const char **rp)
40 {
41 const char *epoch;
42 const char *version;
43 const char *release;
44 char *s, *se;
45
46 s = evr;
47 /* s points to epoch terminator */
48 while (*s && isdigit(*s)) s++;
49 /* se points to version terminator */
50 se = strrchr(s, '-');
51
52 if(*s == ':') {
53 epoch = evr;
54 *s++ = '\0';
55 version = s;
56 if(*epoch == '\0') {
57 epoch = "0";
58 }
59 } else {
60 /* different from RPM- always assume 0 epoch */
61 epoch = "0";
62 version = evr;
63 }
64 if(se) {
65 *se++ = '\0';
66 release = se;
67 } else {
68 release = NULL;
69 }
70
71 if(ep) *ep = epoch;
72 if(vp) *vp = version;
73 if(rp) *rp = release;
74 }
75
76 /**
77 * Compare alpha and numeric segments of two versions.
78 * return 1: a is newer than b
79 * 0: a and b are the same version
80 * -1: b is newer than a
81 */
82 static int rpmvercmp(const char *a, const char *b)
83 {
84 char oldch1, oldch2;
85 char *str1, *str2;
86 char *ptr1, *ptr2;
87 char *one, *two;
88 int rc;
89 int isnum;
90 int ret = 0;
91
92 /* easy comparison to see if versions are identical */
93 if(strcmp(a, b) == 0) return 0;
94
95 str1 = strdup(a);
96 str2 = strdup(b);
97
98 one = ptr1 = str1;
99 two = ptr2 = str2;
100
101 /* loop through each version segment of str1 and str2 and compare them */
102 while (*one && *two) {
103 while (*one && !isalnum((int)*one)) one++;
104 while (*two && !isalnum((int)*two)) two++;
105
106 /* If we ran to the end of either, we are finished with the loop */
107 if (!(*one && *two)) break;
108
109 /* If the separator lengths were different, we are also finished */
110 if ((one - ptr1) != (two - ptr2)) {
111 return (one - ptr1) < (two - ptr2) ? -1 : 1;
112 }
113
114 ptr1 = one;
115 ptr2 = two;
116
117 /* grab first completely alpha or completely numeric segment */
118 /* leave one and two pointing to the start of the alpha or numeric */
119 /* segment and walk ptr1 and ptr2 to end of segment */
120 if (isdigit((int)*ptr1)) {
121 while (*ptr1 && isdigit((int)*ptr1)) ptr1++;
122 while (*ptr2 && isdigit((int)*ptr2)) ptr2++;
123 isnum = 1;
124 } else {
125 while (*ptr1 && isalpha((int)*ptr1)) ptr1++;
126 while (*ptr2 && isalpha((int)*ptr2)) ptr2++;
127 isnum = 0;
128 }
129
130 /* save character at the end of the alpha or numeric segment */
131 /* so that they can be restored after the comparison */
132 oldch1 = *ptr1;
133 *ptr1 = '\0';
134 oldch2 = *ptr2;
135 *ptr2 = '\0';
136
137 /* this cannot happen, as we previously tested to make sure that */
138 /* the first string has a non-null segment */
139 if (one == ptr1) {
140 ret = -1; /* arbitrary */
141 goto cleanup;
142 }
143
144 /* take care of the case where the two version segments are */
145 /* different types: one numeric, the other alpha (i.e. empty) */
146 /* numeric segments are always newer than alpha segments */
147 /* XXX See patch #60884 (and details) from bugzilla #50977. */
148 if (two == ptr2) {
149 ret = isnum ? 1 : -1;
150 goto cleanup;
151 }
152
153 if (isnum) {
154 /* this used to be done by converting the digit segments */
155 /* to ints using atoi() - it's changed because long */
156 /* digit segments can overflow an int - this should fix that. */
157
158 /* throw away any leading zeros - it's a number, right? */
159 while (*one == '0') one++;
160 while (*two == '0') two++;
161
162 /* whichever number has more digits wins */
163 if (strlen(one) > strlen(two)) {
164 ret = 1;
165 goto cleanup;
166 }
167 if (strlen(two) > strlen(one)) {
168 ret = -1;
169 goto cleanup;
170 }
171 }
172
173 /* strcmp will return which one is greater - even if the two */
174 /* segments are alpha or if they are numeric. don't return */
175 /* if they are equal because there might be more segments to */
176 /* compare */
177 rc = strcmp(one, two);
178 if (rc) {
179 ret = rc < 1 ? -1 : 1;
180 goto cleanup;
181 }
182
183 /* restore character that was replaced by null above */
184 *ptr1 = oldch1;
185 one = ptr1;
186 *ptr2 = oldch2;
187 two = ptr2;
188 }
189
190 /* this catches the case where all numeric and alpha segments have */
191 /* compared identically but the segment separating characters were */
192 /* different */
193 if ((!*one) && (!*two)) {
194 ret = 0;
195 goto cleanup;
196 }
197
198 /* the final showdown. we never want a remaining alpha string to
199 * beat an empty string. the logic is a bit weird, but:
200 * - if one is empty and two is not an alpha, two is newer.
201 * - if one is an alpha, two is newer.
202 * - otherwise one is newer.
203 * */
204 if ( (!*one && !isalpha((int)*two))
205 || isalpha((int)*one) ) {
206 ret = -1;
207 } else {
208 ret = 1;
209 }
210
211 cleanup:
212 free(str1);
213 free(str2);
214 return ret;
215 }
216
217 /** Compare two version strings and determine which one is 'newer'.
218 * Returns a value comparable to the way strcmp works. Returns 1
219 * if a is newer than b, 0 if a and b are the same version, or -1
220 * if b is newer than a.
221 *
222 * Different epoch values for version strings will override any further
223 * comparison. If no epoch is provided, 0 is assumed.
224 *
225 * Keep in mind that the pkgrel is only compared if it is available
226 * on both versions handed to this function. For example, comparing
227 * 1.5-1 and 1.5 will yield 0; comparing 1.5-1 and 1.5-2 will yield
228 * -1 as expected. This is mainly for supporting versioned dependencies
229 * that do not include the pkgrel.
230 */
231 int alpm_pkg_vercmp(const char *a, const char *b)
232 {
233 char *full1, *full2;
234 const char *epoch1, *ver1, *rel1;
235 const char *epoch2, *ver2, *rel2;
236 int ret;
237
238 /* ensure our strings are not null */
239 if(!a && !b) {
240 return 0;
241 } else if(!a) {
242 return -1;
243 } else if(!b) {
244 return 1;
245 }
246 /* another quick shortcut- if full version specs are equal */
247 if(strcmp(a, b) == 0) {
248 return 0;
249 }
250
251 /* Parse both versions into [epoch:]version[-release] triplets. We probably
252 * don't need epoch and release to support all the same magic, but it is
253 * easier to just run it all through the same code. */
254 full1 = strdup(a);
255 full2 = strdup(b);
256
257 /* parseEVR modifies passed in version, so have to dupe it first */
258 parseEVR(full1, &epoch1, &ver1, &rel1);
259 parseEVR(full2, &epoch2, &ver2, &rel2);
260
261 ret = rpmvercmp(epoch1, epoch2);
262 if(ret == 0) {
263 ret = rpmvercmp(ver1, ver2);
264 if(ret == 0 && rel1 && rel2) {
265 ret = rpmvercmp(rel1, rel2);
266 }
267 }
268
269 free(full1);
270 free(full2);
271 return ret;
272 }
273
274 #define BASENAME "highestver"
275
276 static void usage(void)
277 {
278 fprintf(stderr, "usage: %s <ver1> <ver2> ... <verN>\n", BASENAME);
279 fprintf(stderr, "prints the highest version.\n");
280 }
281
282 int main(int argc, char *argv[])
283 {
284 int i;
285 int ret;
286 char *saved;
287
288 if(argc == 1) {
289 usage();
290 return 2;
291 }
292
293 // only one argument given, that is the highest one
294 if(argc == 2) {
295 saved = strdup(argv[1]);
296 }
297
298 // fasten things up
299 if(argc == 3) {
300 ret = alpm_pkg_vercmp(argv[1], argv[2]);
301 if(ret < 0) {
302 saved = strdup(argv[2]);
303 }
304 if(ret == 0) {
305 saved = strdup(argv[1]);
306 }
307 if(ret > 0) {
308 saved = strdup(argv[1]);
309 }
310 }
311
312 if(argc > 3) {
313 for(i=1; i < argc; i++) {
314 if (i == 1) {
315 saved = strdup(argv[i]);
316 }
317 ret = alpm_pkg_vercmp(saved, argv[i+1]);
318 // retval >0 or 0 means saved i the higher on, no need to strcmp
319 if(ret < 0) {
320 saved = strdup(argv[i+1]);
321 }
322 }
323 }
324
325 if(strlen(saved) > 0) {
326 printf("%s\n", saved);
327 }
328
329 return EXIT_SUCCESS;
330 }