]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - lib/libpkg/version.c
* Fix compilation when using SCTP_AUDITING_ENABLED.
[FreeBSD/FreeBSD.git] / lib / libpkg / version.c
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$");
21
22 #include "pkg.h"
23 #include <err.h>
24
25 /*
26  * This routine could easily go somewhere else.
27  *
28  */
29 int
30 libpkg_version(void)
31 {
32         return LIBPKG_VERSION;
33 }
34
35 /*
36  * Routines to assist with PLIST_FMT_VER numbers in the packing
37  * lists.
38  *
39  * Following is the PLIST_FMT_VER history:
40  * 1.0 - Initial revision;
41  * 1.1 - When recording/checking checksum of symlink use hash of readlink()
42  *       value instead of the hash of an object this links points to.
43  *
44  */
45 int
46 verscmp(Package *pkg, int major, int minor)
47 {
48     int rval = 0;
49
50     if ((pkg->fmtver_maj < major) || (pkg->fmtver_maj == major &&
51         pkg->fmtver_mnr < minor))
52         rval = -1;
53     else if ((pkg->fmtver_maj > major) || (pkg->fmtver_maj == major &&
54              pkg->fmtver_mnr > minor))
55         rval = 1;
56
57     return rval;
58 }
59
60 /*
61  * split_version(pkgname, endname, epoch, revision) returns a pointer to
62  * the version portion of a package name and the two special components.
63  *
64  * Syntax is:  ${PORTNAME}-${PORTVERSION}[_${PORTREVISION}][,${PORTEPOCH}]
65  *
66  * Written by Oliver Eikemeier
67  * Based on work of Jeremy D. Lea.
68  */
69 static const char *
70 split_version(const char *pkgname, const char **endname, unsigned long *epoch, unsigned long *revision)
71 {
72     char *ch;
73     const char *versionstr;
74     const char *endversionstr;
75
76     if (pkgname == NULL)
77         errx(2, "%s: Passed NULL pkgname.", __func__);
78
79     /* Look for the last '-' the the pkgname */
80     ch = strrchr(pkgname, '-');
81     /* Cheat if we are just passed a version, not a valid package name */
82     versionstr = ch ? ch + 1 : pkgname;
83
84     /* Look for the last '_' in the version string, advancing the end pointer */
85     ch = strrchr(versionstr, '_');
86     if (revision != NULL) {
87         *revision = ch ? strtoul(ch + 1, NULL, 10) : 0;
88     }
89     endversionstr = ch;
90
91     /* Look for the last ',' in the remaining version string */
92     ch = strrchr(endversionstr ? endversionstr + 1 : versionstr, ',');
93     if (epoch != NULL) {
94         *epoch = ch ? strtoul(ch + 1, NULL, 10) : 0;
95     }
96     if (ch && !endversionstr)
97         endversionstr = ch;
98
99     /* set the pointer behind the last character of the version without revision or epoch */
100     if (endname)
101         *endname = endversionstr ? endversionstr : strrchr(versionstr, '\0');
102
103     return versionstr;
104 }
105
106 /*
107  * PORTVERSIONs are composed of components separated by dots. A component
108  * consists of a version number, a letter and a patchlevel number. This does
109  * not conform to the porter's handbook, but let us formulate rules that
110  * fit the current practice and are far simpler than to make decisions
111  * based on the order of netters and lumbers. Besides, people use versions
112  * like 10b2 in the ports...
113  */
114
115 typedef struct {
116 #ifdef __LONG_LONG_SUPPORTED
117     long long n;
118     long long pl;
119 #else
120     long n;
121     long pl;
122 #endif
123     int a;
124 } version_component;
125
126 /*
127  * get_component(position, component) gets the value of the next component
128  * (number - letter - number triple) and returns a pointer to the next character
129  * after any leading separators
130  *
131  * - components are separated by dots
132  * - characters !~ [a-zA-Z0-9.+*] are treated as separators
133  *   (1.0:2003.09.16 = 1.0.2003.09.16), this may not be what you expect:
134  *   1.0.1:2003.09.16 < 1.0:2003.09.16
135  * - consecutive separators are collapsed (10..1 = 10.1)
136  * - missing separators are inserted, essentially
137  *   letter number letter => letter number . letter (10a1b2 = 10a1.b2)
138  * - missing components are assumed to be equal to 0 (10 = 10.0 = 10.0.0)
139  * - the letter sort order is: [none], a, b, ..., z; numbers without letters
140  *   sort first (10 < 10a < 10b)
141  * - missing version numbers (in components starting with a letter) sort as -1
142  *   (a < 0, 10.a < 10)
143  * - a separator is inserted before the special strings "pl", "alpha", "beta", 
144  *   "pre" and "rc".
145  * - "pl" sorts before every other letter, "alpha", "beta", "pre" and "rc"
146  *   sort as a, b, p and r. (10alpha = 10.a < 10, but 10 < 10a; pl11 < alpha3
147  *   < 0.1beta2 = 0.1.b2 < 0.1)
148  * - other strings use only the first letter for sorting, case is ignored
149  *   (1.d2 = 1.dev2 = 1.Development2)
150  * - The special component `*' is guaranteed to be the smallest possible
151  *   component (2.* < 2pl1 < 2alpha3 < 2.9f7 < 3.*)
152  * - components separated by `+' are handled by version_cmp below
153  *
154  * Oliver Eikemeier
155  */
156
157 static const struct {
158     const char *name;
159     size_t namelen;
160     int value;
161 } stage[] = {
162     { "pl",    2,  0        },
163     { "alpha", 5, 'a'-'a'+1 },
164     { "beta",  4, 'b'-'a'+1 },
165     { "pre",   3, 'p'-'a'+1 },
166     { "rc",    2, 'r'-'a'+1 },
167     { NULL,    0,  -1       }
168 };
169
170 static const char *
171 get_component(const char *position, version_component *component)
172 {
173     const char *pos = position;
174     int hasstage = 0, haspatchlevel = 0;
175
176     if (!pos)
177         errx(2, "%s: Passed NULL position.", __func__);
178
179     /* handle version number */
180     if (isdigit(*pos)) {
181         char *endptr;
182 #ifdef __LONG_LONG_SUPPORTED
183         component->n = strtoll(pos, &endptr, 10);
184 #else
185         component->n = strtol(pos, &endptr, 10);
186 #endif
187         /* should we test for errno == ERANGE? */
188         pos = endptr;
189     } else if (*pos == '*') {
190         component->n = -2;
191         do {
192             pos++;
193         } while(*pos && *pos != '+');
194     } else {
195         component->n = -1;
196         hasstage = 1;
197     }
198
199     /* handle letter */
200     if (isalpha(*pos)) {
201         int c = tolower(*pos);
202         haspatchlevel = 1;
203         /* handle special suffixes */
204         if (isalpha(pos[1])) {
205             int i;
206             for (i = 0; stage[i].name; i++) {
207                 if (strncasecmp(pos, stage[i].name, stage[i].namelen) == 0
208                     && !isalpha(pos[stage[i].namelen])) {
209                     if (hasstage) {
210                         /* stage to value */
211                         component->a = stage[i].value;
212                         pos += stage[i].namelen;
213                     } else {
214                         /* insert dot */
215                         component->a = 0;
216                         haspatchlevel = 0;
217                     }
218                     c = 0;
219                     break;
220                 }
221             }
222         }
223         /* unhandled above */
224         if (c) {
225             /* use the first letter and skip following */
226             component->a = c - 'a' + 1;
227             do {
228                 ++pos;
229             } while (isalpha(*pos));
230         }
231     } else {
232         component->a = 0;
233         haspatchlevel = 0;
234     }
235
236     if (haspatchlevel) {
237         /* handle patch number */
238         if (isdigit(*pos)) {
239             char *endptr;
240 #ifdef __LONG_LONG_SUPPORTED
241             component->pl = strtoll(pos, &endptr, 10);
242 #else
243             component->pl = strtol(pos, &endptr, 10);
244 #endif
245             /* should we test for errno == ERANGE? */
246             pos = endptr;
247         } else {
248             component->pl = -1;
249         }
250     } else {
251         component->pl = 0;
252     }
253
254     /* skip trailing separators */
255     while (*pos && !isdigit(*pos) && !isalpha(*pos) && *pos != '+' && *pos != '*') {
256         pos++;
257     }
258
259     return pos;
260 }
261
262 /*
263  * version_cmp(pkg1, pkg2) returns -1, 0 or 1 depending on if the version
264  * components of pkg1 is less than, equal to or greater than pkg2. No
265  * comparison of the basenames is done.
266  *
267  * The port version is defined by:
268  * ${PORTVERSION}[_${PORTREVISION}][,${PORTEPOCH}]
269  * ${PORTEPOCH} supersedes ${PORTVERSION} supersedes ${PORTREVISION}.
270  * See the commit log for revision 1.349 of ports/Mk/bsd.port.mk
271  * for more information.
272  *
273  * The epoch and revision are defined to be a single number, while the rest
274  * of the version should conform to the porting guidelines. It can contain
275  * multiple components, separated by a period, including letters.
276  *
277  * The tests allow for significantly more latitude in the version numbers
278  * than is allowed in the guidelines. No point in enforcing them here.
279  * That's what portlint is for.
280  *
281  * Jeremy D. Lea.
282  * reimplemented by Oliver Eikemeier
283  */
284 int
285 version_cmp(const char *pkg1, const char *pkg2)
286 {
287     const char *v1, *v2, *ve1, *ve2;
288     unsigned long e1, e2, r1, r2;
289     int result = 0;
290
291     v1 = split_version(pkg1, &ve1, &e1, &r1);
292     v2 = split_version(pkg2, &ve2, &e2, &r2);
293
294     /* Check epoch, port version, and port revision, in that order. */
295     if (e1 != e2) {
296         result = (e1 < e2 ? -1 : 1);
297     }
298
299     /* Shortcut check for equality before invoking the parsing routines. */
300     if (result == 0 && (ve1 - v1 != ve2 - v2 || strncasecmp(v1, v2, ve1 - v1) != 0)) {
301         /* Loop over different components (the parts separated by dots).
302          * If any component differs, we have the basis for an inequality. */
303         while(result == 0 && (v1 < ve1 || v2 < ve2)) {
304             int block_v1 = 0;
305             int block_v2 = 0;
306             version_component vc1 = {0, 0, 0};
307             version_component vc2 = {0, 0, 0};
308             if (v1 < ve1 && *v1 != '+') {
309                 v1 = get_component(v1, &vc1);
310             } else {
311                 block_v1 = 1;
312             }
313             if (v2 < ve2 && *v2 != '+') {
314                 v2 = get_component(v2, &vc2);
315             } else {
316                 block_v2 = 1;
317             }
318             if (block_v1 && block_v2) {
319                 if (v1 < ve1)
320                     v1++;
321                 if (v2 < ve2)
322                     v2++;
323             } else if (vc1.n != vc2.n) {
324                 result = (vc1.n < vc2.n ? -1 : 1);
325             } else if (vc1.a != vc2.a) {
326                 result = (vc1.a < vc2.a ? -1 : 1);
327             } else if (vc1.pl != vc2.pl) {
328                 result = (vc1.pl < vc2.pl ? -1 : 1);
329             }
330         }
331     }
332
333     /* Compare FreeBSD revision numbers. */
334     if (result == 0 && r1 != r2) {
335         result = (r1 < r2 ? -1 : 1);
336     }
337     return result;
338 }