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