Branch data Line data Source code
1 : : /*-
2 : : * Copyright (c) 2011 Philippe Pepiot <phil@philpep.org>
3 : : * Copyright (c) 2014 Vsevolod Stakhov <vsevolod@FreeBSD.org>
4 : : * All rights reserved.
5 : : *
6 : : * Redistribution and use in source and binary forms, with or without
7 : : * modification, are permitted provided that the following conditions
8 : : * are met:
9 : : * 1. Redistributions of source code must retain the above copyright
10 : : * notice, this list of conditions and the following disclaimer
11 : : * in this position and unchanged.
12 : : * 2. Redistributions in binary form must reproduce the above copyright
13 : : * notice, this list of conditions and the following disclaimer in the
14 : : * documentation and/or other materials provided with the distribution.
15 : : *
16 : : * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
17 : : * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 : : * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 : : * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
20 : : * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 : : * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 : : * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 : : * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 : : * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 : : * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 : : */
27 : :
28 : :
29 : : #include <assert.h>
30 : : #include <string.h>
31 : : #include <stdlib.h>
32 : : #include <ctype.h>
33 : :
34 : : #include "pkg.h"
35 : : #include "private/event.h"
36 : : #include "private/pkg.h"
37 : :
38 : : /*
39 : : * split_version(pkgname, endname, epoch, revision) returns a pointer to
40 : : * the version portion of a package name and the two special components.
41 : : *
42 : : * Syntax is: ${PORTNAME}-${PORTVERSION}[_${PORTREVISION}][,${PORTEPOCH}]
43 : : *
44 : : * Written by Oliver Eikemeier
45 : : * Based on work of Jeremy D. Lea.
46 : : */
47 : : static const char *
48 : 1948 : split_version(const char *pkgname, const char **endname,
49 : : unsigned long *epoch, unsigned long *revision)
50 : : {
51 : : char *ch;
52 : : const char *versionstr;
53 : : const char *endversionstr;
54 : :
55 [ + - ]: 1948 : if (pkgname == NULL) {
56 : 0 : pkg_emit_error("%s: Passed NULL pkgname.", __func__);
57 : 0 : return (NULL);
58 : : }
59 : :
60 : : /* Look for the last '-' the the pkgname */
61 : 1948 : ch = strrchr(pkgname, '-');
62 : : /* Cheat if we are just passed a version, not a valid package name */
63 [ - + ]: 1948 : versionstr = ch ? ch + 1 : pkgname;
64 : :
65 : : /*
66 : : * Look for the last '_' in the version string, advancing the
67 : : * end pointer.
68 : : */
69 : 1948 : ch = strrchr(versionstr, '_');
70 : :
71 [ - + ]: 1948 : if (revision != NULL)
72 [ + + ]: 1948 : *revision = ch ? strtoul(ch + 1, NULL, 10) : 0;
73 : :
74 : 1948 : endversionstr = ch;
75 : :
76 : : /* Look for the last ',' in the remaining version string */
77 [ + + ]: 1948 : ch = strrchr(endversionstr ? endversionstr + 1 : versionstr, ',');
78 : :
79 [ - + ]: 1948 : if (epoch != NULL)
80 [ + + ]: 1948 : *epoch = ch ? strtoul(ch + 1, NULL, 10) : 0;
81 : :
82 [ + + + - ]: 1948 : if (ch && !endversionstr)
83 : 4 : endversionstr = ch;
84 : :
85 : : /*
86 : : * set the pointer behind the last character of the version without
87 : : * revision or epoch
88 : : */
89 [ - + ]: 1948 : if (endname)
90 [ + + ]: 1948 : *endname = endversionstr ? endversionstr :
91 : 1596 : strrchr(versionstr, '\0');
92 : :
93 : 1948 : return versionstr;
94 : 1948 : }
95 : :
96 : : /*
97 : : * PORTVERSIONs are composed of components separated by dots. A component
98 : : * consists of a version number, a letter and a patchlevel number. This does
99 : : * not conform to the porter's handbook, but let us formulate rules that
100 : : * fit the current practice and are far simpler than to make decisions
101 : : * based on the order of netters and lumbers. Besides, people use versions
102 : : * like 10b2 in the ports...
103 : : */
104 : :
105 : : typedef struct {
106 : : #ifdef __LONG_LONG_SUPPORTED
107 : : long long n;
108 : : long long pl;
109 : : #else
110 : : long n;
111 : : long pl;
112 : : #endif
113 : : int a;
114 : : } version_component;
115 : :
116 : : /*
117 : : * get_component(position, component) gets the value of the next component
118 : : * (number - letter - number triple) and returns a pointer to the next character
119 : : * after any leading separators
120 : : *
121 : : * - components are separated by dots
122 : : * - characters !~ [a-zA-Z0-9.+*] are treated as separators
123 : : * (1.0:2003.09.16 = 1.0.2003.09.16), this may not be what you expect:
124 : : * 1.0.1:2003.09.16 < 1.0:2003.09.16
125 : : * - consecutive separators are collapsed (10..1 = 10.1)
126 : : * - missing separators are inserted, essentially
127 : : * letter number letter => letter number . letter (10a1b2 = 10a1.b2)
128 : : * - missing components are assumed to be equal to 0 (10 = 10.0 = 10.0.0)
129 : : * - the letter sort order is: [none], a, b, ..., z; numbers without letters
130 : : * sort first (10 < 10a < 10b)
131 : : * - missing version numbers (in components starting with a letter) sort as -1
132 : : * (a < 0, 10.a < 10)
133 : : * - a separator is inserted before the special strings "pl", "alpha", "beta",
134 : : * "pre" and "rc".
135 : : * - "pl" sorts before every other letter, "alpha", "beta", "pre" and "rc"
136 : : * sort as a, b, p and r. (10alpha = 10.a < 10, but 10 < 10a; pl11 < alpha3
137 : : * < 0.1beta2 = 0.1.b2 < 0.1)
138 : : * - other strings use only the first letter for sorting, case is ignored
139 : : * (1.d2 = 1.dev2 = 1.Development2)
140 : : * - The special component `*' is guaranteed to be the smallest possible
141 : : * component (2.* < 2pl1 < 2alpha3 < 2.9f7 < 3.*)
142 : : * - components separated by `+' are handled by version_cmp below
143 : : *
144 : : * Oliver Eikemeier
145 : : */
146 : :
147 : : static const struct stage {
148 : : const char *name;
149 : : size_t namelen;
150 : : int value;
151 : : } stages[] = {
152 : : { "pl", 2, 0 },
153 : : { "snap", 4, 1 },
154 : : #define ABASE 2 /* Last special early-sorted prefix + 1 */
155 : : { "alpha", 5, 'a'-'a'+ABASE },
156 : : { "beta", 4, 'b'-'a'+ABASE },
157 : : { "pre", 3, 'p'-'a'+ABASE },
158 : : { "rc", 2, 'r'-'a'+ABASE },
159 : : };
160 : :
161 : : static const char *
162 : 1438 : get_component(const char *position, version_component *component)
163 : : {
164 : 1438 : const char *pos = position;
165 : 1438 : int hasstage = 0, haspatchlevel = 0;
166 : :
167 [ + - ]: 1438 : if (!pos) {
168 : 0 : pkg_emit_error("%s: Passed NULL position.", __func__);
169 : 0 : return (NULL);
170 : : }
171 : :
172 : : /* handle version number */
173 [ + + ]: 1438 : if (isdigit(*pos)) {
174 : : char *endptr;
175 : : #ifdef __LONG_LONG_SUPPORTED
176 : 1386 : component->n = strtoll(pos, &endptr, 10);
177 : : #else
178 : : component->n = strtol(pos, &endptr, 10);
179 : : #endif
180 : : /* should we test for errno == ERANGE? */
181 : 1386 : pos = endptr;
182 [ - + ]: 1438 : } else if (*pos == '*') {
183 : 0 : component->n = -2;
184 : 0 : do {
185 : 0 : pos++;
186 [ # # # # ]: 0 : } while(*pos && *pos != '+');
187 : 0 : } else {
188 : 52 : component->n = -1;
189 : 52 : hasstage = 1;
190 : : }
191 : :
192 : : /* handle letter */
193 [ + + ]: 1438 : if (isalpha(*pos)) {
194 : 52 : int c = tolower(*pos);
195 : 52 : haspatchlevel = 1;
196 : : /* handle special suffixes */
197 [ - + ]: 52 : if (isalpha(pos[1])) {
198 : : unsigned int i;
199 [ - + ]: 180 : for (i = 0; i < nitems(stages); i++) {
200 : 180 : const struct stage *stage = &stages[i];
201 : 180 : size_t len = stage->namelen;
202 [ + + - + ]: 180 : if (strncasecmp(pos, stage->name, len) == 0 &&
203 : 52 : !isalpha(pos[stage->namelen])) {
204 [ - + ]: 52 : if (hasstage) {
205 : : /* stage to value */
206 : 52 : component->a = stage->value;
207 : 52 : pos += stage->namelen;
208 : 52 : } else {
209 : : /* insert dot */
210 : 0 : component->a = 0;
211 : 0 : haspatchlevel = 0;
212 : : }
213 : 52 : c = 0;
214 : 52 : break;
215 : : }
216 : 128 : }
217 : 52 : }
218 : : /* unhandled above */
219 [ + - ]: 52 : if (c) {
220 : : /* use the first letter and skip following */
221 : 0 : component->a = c - 'a' + ABASE;
222 : 0 : do {
223 : 0 : ++pos;
224 [ # # ]: 0 : } while (isalpha(*pos));
225 : 0 : }
226 : 52 : } else {
227 : 1386 : component->a = 0;
228 : 1386 : haspatchlevel = 0;
229 : : }
230 : :
231 [ + + ]: 1438 : if (haspatchlevel) {
232 : : /* handle patch number */
233 [ + - ]: 52 : if (isdigit(*pos)) {
234 : : char *endptr;
235 : : #ifdef __LONG_LONG_SUPPORTED
236 : 52 : component->pl = strtoll(pos, &endptr, 10);
237 : : #else
238 : : component->pl = strtol(pos, &endptr, 10);
239 : : #endif
240 : : /* should we test for errno == ERANGE? */
241 : 52 : pos = endptr;
242 : 52 : } else {
243 : 0 : component->pl = -1;
244 : : }
245 : 52 : } else {
246 : 1386 : component->pl = 0;
247 : : }
248 : :
249 : : /* skip trailing separators */
250 [ + + + + : 2730 : while (*pos && !isdigit(*pos) && !isalpha(*pos)
+ + ]
251 [ + + - + ]: 698 : && *pos != '+' && *pos != '*')
252 : 646 : pos++;
253 : :
254 : 1438 : return pos;
255 : 1438 : }
256 : :
257 : : /*
258 : : * version_cmp(pkg1, pkg2) returns -1, 0 or 1 depending on if the version
259 : : * components of pkg1 is less than, equal to or greater than pkg2. No
260 : : * comparison of the basenames is done.
261 : : *
262 : : * The port version is defined by:
263 : : * ${PORTVERSION}[_${PORTREVISION}][,${PORTEPOCH}]
264 : : * ${PORTEPOCH} supersedes ${PORTVERSION} supersedes ${PORTREVISION}.
265 : : * See the commit log for revision 1.349 of ports/Mk/bsd.port.mk
266 : : * for more information.
267 : : *
268 : : * The epoch and revision are defined to be a single number, while the rest
269 : : * of the version should conform to the porting guidelines. It can contain
270 : : * multiple components, separated by a period, including letters.
271 : : *
272 : : * The tests allow for significantly more latitude in the version numbers
273 : : * than is allowed in the guidelines. No point in enforcing them here.
274 : : * That's what portlint is for.
275 : : *
276 : : * Jeremy D. Lea.
277 : : * reimplemented by Oliver Eikemeier
278 : : */
279 : : int
280 : 974 : pkg_version_cmp(const char * const pkg1, const char * const pkg2)
281 : : {
282 : : const char *v1, *v2, *ve1, *ve2;
283 : : unsigned long e1, e2, r1, r2;
284 : 974 : int result = 0;
285 : :
286 : 974 : v1 = split_version(pkg1, &ve1, &e1, &r1);
287 : 974 : v2 = split_version(pkg2, &ve2, &e2, &r2);
288 : :
289 [ + - ]: 974 : assert (v1 != NULL && v2 != NULL);
290 : :
291 : : /* Check epoch, port version, and port revision, in that order. */
292 [ + + ]: 974 : if (e1 != e2)
293 : 4 : result = (e1 < e2 ? -1 : 1);
294 : :
295 : : /* Shortcut check for equality before invoking the parsing routines. */
296 [ + + + + ]: 1738 : if (result == 0 &&
297 [ + + ]: 970 : (ve1 - v1 != ve2 - v2 || strncasecmp(v1, v2, ve1 - v1) != 0)) {
298 : : /*
299 : : * Loop over different components (the parts separated by dots).
300 : : * If any component differs, there is an inequality.
301 : : */
302 [ + + + + : 2074 : while (result == 0 && (v1 < ve1 || v2 < ve2)) {
+ + ]
303 : 784 : int block_v1 = 0;
304 : 784 : int block_v2 = 0;
305 : 784 : version_component vc1 = {0, 0, 0};
306 : 784 : version_component vc2 = {0, 0, 0};
307 [ + + - + ]: 784 : if (v1 < ve1 && *v1 != '+') {
308 : 719 : v1 = get_component(v1, &vc1);
309 [ + - ]: 719 : assert (v1 != NULL);
310 : 719 : } else {
311 : 65 : block_v1 = 1;
312 : : }
313 [ + + - + ]: 784 : if (v2 < ve2 && *v2 != '+') {
314 : 719 : v2 = get_component(v2, &vc2);
315 [ + - ]: 719 : assert (v2 != NULL);
316 : 719 : } else {
317 : 65 : block_v2 = 1;
318 : : }
319 [ + + + - ]: 784 : if (block_v1 && block_v2) {
320 [ # # ]: 0 : if (v1 < ve1)
321 : 0 : v1++;
322 [ # # ]: 0 : if (v2 < ve2)
323 : 0 : v2++;
324 [ + + ]: 784 : } else if (vc1.n != vc2.n) {
325 : 482 : result = (vc1.n < vc2.n ? -1 : 1);
326 [ + + ]: 784 : } else if (vc1.a != vc2.a) {
327 : 24 : result = (vc1.a < vc2.a ? -1 : 1);
328 [ + - ]: 302 : } else if (vc1.pl != vc2.pl) {
329 : 0 : result = (vc1.pl < vc2.pl ? -1 : 1);
330 : 0 : }
331 : : }
332 : 506 : }
333 : :
334 : : /* Compare FreeBSD revision numbers. */
335 [ + + + + ]: 974 : if (result == 0 && r1 != r2)
336 : 140 : result = (r1 < r2 ? -1 : 1);
337 : :
338 : 974 : return (result);
339 : : }
340 : :
341 : : pkg_change_t
342 : 0 : pkg_version_change(const struct pkg * restrict pkg)
343 : : {
344 : :
345 [ # # ]: 0 : if (pkg->old_version == NULL)
346 : 0 : return (PKG_REINSTALL);
347 : :
348 [ # # # # ]: 0 : switch (pkg_version_cmp(pkg->old_version, pkg->version)) {
349 : : case -1:
350 : 0 : return (PKG_UPGRADE);
351 : : default: /* placate the compiler */
352 : : case 0:
353 : 0 : return (PKG_REINSTALL);
354 : : case 1:
355 : 0 : return (PKG_DOWNGRADE);
356 : : }
357 : 0 : }
358 : :
359 : : pkg_change_t
360 : 633 : pkg_version_change_between(const struct pkg * pkg1, const struct pkg *pkg2)
361 : : {
362 [ + - ]: 633 : if (pkg2 == NULL)
363 : 0 : return PKG_REINSTALL;
364 : :
365 [ + - + + ]: 633 : switch (pkg_version_cmp(pkg2->version, pkg1->version)) {
366 : : case -1:
367 : 310 : return (PKG_UPGRADE);
368 : : default: /* placate the compiler */
369 : : case 0:
370 : 236 : return (PKG_REINSTALL);
371 : : case 1:
372 : 87 : return (PKG_DOWNGRADE);
373 : : }
374 : 633 : }
|