Deleted Added
full compact
version.c (222035) version.c (226436)
1/*
2 * FreeBSD install - a package for the installation and maintenance
3 * of non-core utilities.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * Maxim Sobolev
15 * 31 July 2001
16 *
17 */
18
19#include <sys/cdefs.h>
1/*
2 * FreeBSD install - a package for the installation and maintenance
3 * of non-core utilities.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * Maxim Sobolev
15 * 31 July 2001
16 *
17 */
18
19#include <sys/cdefs.h>
20__FBSDID("$FreeBSD: head/usr.sbin/pkg_install/lib/version.c 131274 2004-06-29 18:52:13Z eik $");
20__FBSDID("$FreeBSD: head/usr.sbin/pkg_install/lib/version.c 226436 2011-10-16 14:30:28Z eadler $");
21
22#include "lib.h"
23#include <err.h>
24
25/*
26 * Routines to assist with PLIST_FMT_VER numbers in the packing
27 * lists.
28 *
29 * Following is the PLIST_FMT_VER history:
30 * 1.0 - Initial revision;
31 * 1.1 - When recording/checking checksum of symlink use hash of readlink()
32 * value instead of the hash of an object this links points to.
33 *
34 */
35int
36verscmp(Package *pkg, int major, int minor)
37{
38 int rval = 0;
39
40 if ((pkg->fmtver_maj < major) || (pkg->fmtver_maj == major &&
41 pkg->fmtver_mnr < minor))
42 rval = -1;
43 else if ((pkg->fmtver_maj > major) || (pkg->fmtver_maj == major &&
44 pkg->fmtver_mnr > minor))
45 rval = 1;
46
47 return rval;
48}
49
50/*
51 * split_version(pkgname, endname, epoch, revision) returns a pointer to
52 * the version portion of a package name and the two special components.
53 *
54 * Syntax is: ${PORTNAME}-${PORTVERSION}[_${PORTREVISION}][,${PORTEPOCH}]
55 *
56 * Written by Oliver Eikemeier
57 * Based on work of Jeremy D. Lea.
58 */
59static const char *
60split_version(const char *pkgname, const char **endname, unsigned long *epoch, unsigned long *revision)
61{
62 char *ch;
63 const char *versionstr;
64 const char *endversionstr;
65
66 if (pkgname == NULL)
67 errx(2, "%s: Passed NULL pkgname.", __func__);
68
21
22#include "lib.h"
23#include <err.h>
24
25/*
26 * Routines to assist with PLIST_FMT_VER numbers in the packing
27 * lists.
28 *
29 * Following is the PLIST_FMT_VER history:
30 * 1.0 - Initial revision;
31 * 1.1 - When recording/checking checksum of symlink use hash of readlink()
32 * value instead of the hash of an object this links points to.
33 *
34 */
35int
36verscmp(Package *pkg, int major, int minor)
37{
38 int rval = 0;
39
40 if ((pkg->fmtver_maj < major) || (pkg->fmtver_maj == major &&
41 pkg->fmtver_mnr < minor))
42 rval = -1;
43 else if ((pkg->fmtver_maj > major) || (pkg->fmtver_maj == major &&
44 pkg->fmtver_mnr > minor))
45 rval = 1;
46
47 return rval;
48}
49
50/*
51 * split_version(pkgname, endname, epoch, revision) returns a pointer to
52 * the version portion of a package name and the two special components.
53 *
54 * Syntax is: ${PORTNAME}-${PORTVERSION}[_${PORTREVISION}][,${PORTEPOCH}]
55 *
56 * Written by Oliver Eikemeier
57 * Based on work of Jeremy D. Lea.
58 */
59static const char *
60split_version(const char *pkgname, const char **endname, unsigned long *epoch, unsigned long *revision)
61{
62 char *ch;
63 const char *versionstr;
64 const char *endversionstr;
65
66 if (pkgname == NULL)
67 errx(2, "%s: Passed NULL pkgname.", __func__);
68
69 /* Look for the last '-' the the pkgname */
69 /* Look for the last '-' the pkgname */
70 ch = strrchr(pkgname, '-');
71 /* Cheat if we are just passed a version, not a valid package name */
72 versionstr = ch ? ch + 1 : pkgname;
73
74 /* Look for the last '_' in the version string, advancing the end pointer */
75 ch = strrchr(versionstr, '_');
76 if (revision != NULL) {
77 *revision = ch ? strtoul(ch + 1, NULL, 10) : 0;
78 }
79 endversionstr = ch;
80
81 /* Look for the last ',' in the remaining version string */
82 ch = strrchr(endversionstr ? endversionstr + 1 : versionstr, ',');
83 if (epoch != NULL) {
84 *epoch = ch ? strtoul(ch + 1, NULL, 10) : 0;
85 }
86 if (ch && !endversionstr)
87 endversionstr = ch;
88
89 /* set the pointer behind the last character of the version without revision or epoch */
90 if (endname)
91 *endname = endversionstr ? endversionstr : strrchr(versionstr, '\0');
92
93 return versionstr;
94}
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
105typedef 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
147static const struct {
148 const char *name;
149 size_t namelen;
150 int value;
151} stage[] = {
152 { "pl", 2, 0 },
153 { "alpha", 5, 'a'-'a'+1 },
154 { "beta", 4, 'b'-'a'+1 },
155 { "pre", 3, 'p'-'a'+1 },
156 { "rc", 2, 'r'-'a'+1 },
157 { NULL, 0, -1 }
158};
159
160static const char *
161get_component(const char *position, version_component *component)
162{
163 const char *pos = position;
164 int hasstage = 0, haspatchlevel = 0;
165
166 if (!pos)
167 errx(2, "%s: Passed NULL position.", __func__);
168
169 /* handle version number */
170 if (isdigit(*pos)) {
171 char *endptr;
172#ifdef __LONG_LONG_SUPPORTED
173 component->n = strtoll(pos, &endptr, 10);
174#else
175 component->n = strtol(pos, &endptr, 10);
176#endif
177 /* should we test for errno == ERANGE? */
178 pos = endptr;
179 } else if (*pos == '*') {
180 component->n = -2;
181 do {
182 pos++;
183 } while(*pos && *pos != '+');
184 } else {
185 component->n = -1;
186 hasstage = 1;
187 }
188
189 /* handle letter */
190 if (isalpha(*pos)) {
191 int c = tolower(*pos);
192 haspatchlevel = 1;
193 /* handle special suffixes */
194 if (isalpha(pos[1])) {
195 int i;
196 for (i = 0; stage[i].name; i++) {
197 if (strncasecmp(pos, stage[i].name, stage[i].namelen) == 0
198 && !isalpha(pos[stage[i].namelen])) {
199 if (hasstage) {
200 /* stage to value */
201 component->a = stage[i].value;
202 pos += stage[i].namelen;
203 } else {
204 /* insert dot */
205 component->a = 0;
206 haspatchlevel = 0;
207 }
208 c = 0;
209 break;
210 }
211 }
212 }
213 /* unhandled above */
214 if (c) {
215 /* use the first letter and skip following */
216 component->a = c - 'a' + 1;
217 do {
218 ++pos;
219 } while (isalpha(*pos));
220 }
221 } else {
222 component->a = 0;
223 haspatchlevel = 0;
224 }
225
226 if (haspatchlevel) {
227 /* handle patch number */
228 if (isdigit(*pos)) {
229 char *endptr;
230#ifdef __LONG_LONG_SUPPORTED
231 component->pl = strtoll(pos, &endptr, 10);
232#else
233 component->pl = strtol(pos, &endptr, 10);
234#endif
235 /* should we test for errno == ERANGE? */
236 pos = endptr;
237 } else {
238 component->pl = -1;
239 }
240 } else {
241 component->pl = 0;
242 }
243
244 /* skip trailing separators */
245 while (*pos && !isdigit(*pos) && !isalpha(*pos) && *pos != '+' && *pos != '*') {
246 pos++;
247 }
248
249 return pos;
250}
251
252/*
253 * version_cmp(pkg1, pkg2) returns -1, 0 or 1 depending on if the version
254 * components of pkg1 is less than, equal to or greater than pkg2. No
255 * comparison of the basenames is done.
256 *
257 * The port version is defined by:
258 * ${PORTVERSION}[_${PORTREVISION}][,${PORTEPOCH}]
259 * ${PORTEPOCH} supersedes ${PORTVERSION} supersedes ${PORTREVISION}.
260 * See the commit log for revision 1.349 of ports/Mk/bsd.port.mk
261 * for more information.
262 *
263 * The epoch and revision are defined to be a single number, while the rest
264 * of the version should conform to the porting guidelines. It can contain
265 * multiple components, separated by a period, including letters.
266 *
267 * The tests allow for significantly more latitude in the version numbers
268 * than is allowed in the guidelines. No point in enforcing them here.
269 * That's what portlint is for.
270 *
271 * Jeremy D. Lea.
272 * reimplemented by Oliver Eikemeier
273 */
274int
275version_cmp(const char *pkg1, const char *pkg2)
276{
277 const char *v1, *v2, *ve1, *ve2;
278 unsigned long e1, e2, r1, r2;
279 int result = 0;
280
281 v1 = split_version(pkg1, &ve1, &e1, &r1);
282 v2 = split_version(pkg2, &ve2, &e2, &r2);
283
284 /* Check epoch, port version, and port revision, in that order. */
285 if (e1 != e2) {
286 result = (e1 < e2 ? -1 : 1);
287 }
288
289 /* Shortcut check for equality before invoking the parsing routines. */
290 if (result == 0 && (ve1 - v1 != ve2 - v2 || strncasecmp(v1, v2, ve1 - v1) != 0)) {
291 /* Loop over different components (the parts separated by dots).
292 * If any component differs, we have the basis for an inequality. */
293 while(result == 0 && (v1 < ve1 || v2 < ve2)) {
294 int block_v1 = 0;
295 int block_v2 = 0;
296 version_component vc1 = {0, 0, 0};
297 version_component vc2 = {0, 0, 0};
298 if (v1 < ve1 && *v1 != '+') {
299 v1 = get_component(v1, &vc1);
300 } else {
301 block_v1 = 1;
302 }
303 if (v2 < ve2 && *v2 != '+') {
304 v2 = get_component(v2, &vc2);
305 } else {
306 block_v2 = 1;
307 }
308 if (block_v1 && block_v2) {
309 if (v1 < ve1)
310 v1++;
311 if (v2 < ve2)
312 v2++;
313 } else if (vc1.n != vc2.n) {
314 result = (vc1.n < vc2.n ? -1 : 1);
315 } else if (vc1.a != vc2.a) {
316 result = (vc1.a < vc2.a ? -1 : 1);
317 } else if (vc1.pl != vc2.pl) {
318 result = (vc1.pl < vc2.pl ? -1 : 1);
319 }
320 }
321 }
322
323 /* Compare FreeBSD revision numbers. */
324 if (result == 0 && r1 != r2) {
325 result = (r1 < r2 ? -1 : 1);
326 }
327 return result;
328}
70 ch = strrchr(pkgname, '-');
71 /* Cheat if we are just passed a version, not a valid package name */
72 versionstr = ch ? ch + 1 : pkgname;
73
74 /* Look for the last '_' in the version string, advancing the end pointer */
75 ch = strrchr(versionstr, '_');
76 if (revision != NULL) {
77 *revision = ch ? strtoul(ch + 1, NULL, 10) : 0;
78 }
79 endversionstr = ch;
80
81 /* Look for the last ',' in the remaining version string */
82 ch = strrchr(endversionstr ? endversionstr + 1 : versionstr, ',');
83 if (epoch != NULL) {
84 *epoch = ch ? strtoul(ch + 1, NULL, 10) : 0;
85 }
86 if (ch && !endversionstr)
87 endversionstr = ch;
88
89 /* set the pointer behind the last character of the version without revision or epoch */
90 if (endname)
91 *endname = endversionstr ? endversionstr : strrchr(versionstr, '\0');
92
93 return versionstr;
94}
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
105typedef 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
147static const struct {
148 const char *name;
149 size_t namelen;
150 int value;
151} stage[] = {
152 { "pl", 2, 0 },
153 { "alpha", 5, 'a'-'a'+1 },
154 { "beta", 4, 'b'-'a'+1 },
155 { "pre", 3, 'p'-'a'+1 },
156 { "rc", 2, 'r'-'a'+1 },
157 { NULL, 0, -1 }
158};
159
160static const char *
161get_component(const char *position, version_component *component)
162{
163 const char *pos = position;
164 int hasstage = 0, haspatchlevel = 0;
165
166 if (!pos)
167 errx(2, "%s: Passed NULL position.", __func__);
168
169 /* handle version number */
170 if (isdigit(*pos)) {
171 char *endptr;
172#ifdef __LONG_LONG_SUPPORTED
173 component->n = strtoll(pos, &endptr, 10);
174#else
175 component->n = strtol(pos, &endptr, 10);
176#endif
177 /* should we test for errno == ERANGE? */
178 pos = endptr;
179 } else if (*pos == '*') {
180 component->n = -2;
181 do {
182 pos++;
183 } while(*pos && *pos != '+');
184 } else {
185 component->n = -1;
186 hasstage = 1;
187 }
188
189 /* handle letter */
190 if (isalpha(*pos)) {
191 int c = tolower(*pos);
192 haspatchlevel = 1;
193 /* handle special suffixes */
194 if (isalpha(pos[1])) {
195 int i;
196 for (i = 0; stage[i].name; i++) {
197 if (strncasecmp(pos, stage[i].name, stage[i].namelen) == 0
198 && !isalpha(pos[stage[i].namelen])) {
199 if (hasstage) {
200 /* stage to value */
201 component->a = stage[i].value;
202 pos += stage[i].namelen;
203 } else {
204 /* insert dot */
205 component->a = 0;
206 haspatchlevel = 0;
207 }
208 c = 0;
209 break;
210 }
211 }
212 }
213 /* unhandled above */
214 if (c) {
215 /* use the first letter and skip following */
216 component->a = c - 'a' + 1;
217 do {
218 ++pos;
219 } while (isalpha(*pos));
220 }
221 } else {
222 component->a = 0;
223 haspatchlevel = 0;
224 }
225
226 if (haspatchlevel) {
227 /* handle patch number */
228 if (isdigit(*pos)) {
229 char *endptr;
230#ifdef __LONG_LONG_SUPPORTED
231 component->pl = strtoll(pos, &endptr, 10);
232#else
233 component->pl = strtol(pos, &endptr, 10);
234#endif
235 /* should we test for errno == ERANGE? */
236 pos = endptr;
237 } else {
238 component->pl = -1;
239 }
240 } else {
241 component->pl = 0;
242 }
243
244 /* skip trailing separators */
245 while (*pos && !isdigit(*pos) && !isalpha(*pos) && *pos != '+' && *pos != '*') {
246 pos++;
247 }
248
249 return pos;
250}
251
252/*
253 * version_cmp(pkg1, pkg2) returns -1, 0 or 1 depending on if the version
254 * components of pkg1 is less than, equal to or greater than pkg2. No
255 * comparison of the basenames is done.
256 *
257 * The port version is defined by:
258 * ${PORTVERSION}[_${PORTREVISION}][,${PORTEPOCH}]
259 * ${PORTEPOCH} supersedes ${PORTVERSION} supersedes ${PORTREVISION}.
260 * See the commit log for revision 1.349 of ports/Mk/bsd.port.mk
261 * for more information.
262 *
263 * The epoch and revision are defined to be a single number, while the rest
264 * of the version should conform to the porting guidelines. It can contain
265 * multiple components, separated by a period, including letters.
266 *
267 * The tests allow for significantly more latitude in the version numbers
268 * than is allowed in the guidelines. No point in enforcing them here.
269 * That's what portlint is for.
270 *
271 * Jeremy D. Lea.
272 * reimplemented by Oliver Eikemeier
273 */
274int
275version_cmp(const char *pkg1, const char *pkg2)
276{
277 const char *v1, *v2, *ve1, *ve2;
278 unsigned long e1, e2, r1, r2;
279 int result = 0;
280
281 v1 = split_version(pkg1, &ve1, &e1, &r1);
282 v2 = split_version(pkg2, &ve2, &e2, &r2);
283
284 /* Check epoch, port version, and port revision, in that order. */
285 if (e1 != e2) {
286 result = (e1 < e2 ? -1 : 1);
287 }
288
289 /* Shortcut check for equality before invoking the parsing routines. */
290 if (result == 0 && (ve1 - v1 != ve2 - v2 || strncasecmp(v1, v2, ve1 - v1) != 0)) {
291 /* Loop over different components (the parts separated by dots).
292 * If any component differs, we have the basis for an inequality. */
293 while(result == 0 && (v1 < ve1 || v2 < ve2)) {
294 int block_v1 = 0;
295 int block_v2 = 0;
296 version_component vc1 = {0, 0, 0};
297 version_component vc2 = {0, 0, 0};
298 if (v1 < ve1 && *v1 != '+') {
299 v1 = get_component(v1, &vc1);
300 } else {
301 block_v1 = 1;
302 }
303 if (v2 < ve2 && *v2 != '+') {
304 v2 = get_component(v2, &vc2);
305 } else {
306 block_v2 = 1;
307 }
308 if (block_v1 && block_v2) {
309 if (v1 < ve1)
310 v1++;
311 if (v2 < ve2)
312 v2++;
313 } else if (vc1.n != vc2.n) {
314 result = (vc1.n < vc2.n ? -1 : 1);
315 } else if (vc1.a != vc2.a) {
316 result = (vc1.a < vc2.a ? -1 : 1);
317 } else if (vc1.pl != vc2.pl) {
318 result = (vc1.pl < vc2.pl ? -1 : 1);
319 }
320 }
321 }
322
323 /* Compare FreeBSD revision numbers. */
324 if (result == 0 && r1 != r2) {
325 result = (r1 < r2 ? -1 : 1);
326 }
327 return result;
328}