184750Ssobomax/*
2101677Sschweikh * FreeBSD install - a package for the installation and maintenance
384750Ssobomax * of non-core utilities.
484750Ssobomax *
584750Ssobomax * Redistribution and use in source and binary forms, with or without
684750Ssobomax * modification, are permitted provided that the following conditions
784750Ssobomax * are met:
884750Ssobomax * 1. Redistributions of source code must retain the above copyright
984750Ssobomax *    notice, this list of conditions and the following disclaimer.
1084750Ssobomax * 2. Redistributions in binary form must reproduce the above copyright
1184750Ssobomax *    notice, this list of conditions and the following disclaimer in the
1284750Ssobomax *    documentation and/or other materials provided with the distribution.
1384750Ssobomax *
1484750Ssobomax * Maxim Sobolev
1584750Ssobomax * 31 July 2001
1684750Ssobomax *
1798766Smarkm */
1898766Smarkm
1998766Smarkm#include <sys/cdefs.h>
2098766Smarkm__FBSDID("$FreeBSD$");
2198766Smarkm
2298766Smarkm#include "lib.h"
2398766Smarkm#include <err.h>
2498766Smarkm
2598766Smarkm/*
2684750Ssobomax * Routines to assist with PLIST_FMT_VER numbers in the packing
2784750Ssobomax * lists.
2884750Ssobomax *
2984750Ssobomax * Following is the PLIST_FMT_VER history:
3084750Ssobomax * 1.0 - Initial revision;
3184750Ssobomax * 1.1 - When recording/checking checksum of symlink use hash of readlink()
32101677Sschweikh *	 value instead of the hash of an object this links points to.
3384750Ssobomax *
3484750Ssobomax */
3584750Ssobomaxint
3684750Ssobomaxverscmp(Package *pkg, int major, int minor)
3784750Ssobomax{
3884750Ssobomax    int rval = 0;
3984750Ssobomax
4084750Ssobomax    if ((pkg->fmtver_maj < major) || (pkg->fmtver_maj == major &&
4184750Ssobomax	pkg->fmtver_mnr < minor))
4284750Ssobomax	rval = -1;
4384750Ssobomax    else if ((pkg->fmtver_maj > major) || (pkg->fmtver_maj == major &&
4484750Ssobomax	     pkg->fmtver_mnr > minor))
4584750Ssobomax	rval = 1;
4684750Ssobomax
4784750Ssobomax    return rval;
4884750Ssobomax}
4998766Smarkm
5098766Smarkm/*
51131274Seik * split_version(pkgname, endname, epoch, revision) returns a pointer to
52131274Seik * the version portion of a package name and the two special components.
5398766Smarkm *
54131274Seik * Syntax is:  ${PORTNAME}-${PORTVERSION}[_${PORTREVISION}][,${PORTEPOCH}]
55131274Seik *
56131274Seik * Written by Oliver Eikemeier
57131274Seik * Based on work of Jeremy D. Lea.
5898766Smarkm */
59131274Seikstatic const char *
60131274Seiksplit_version(const char *pkgname, const char **endname, unsigned long *epoch, unsigned long *revision)
6198766Smarkm{
6298766Smarkm    char *ch;
63131274Seik    const char *versionstr;
64131274Seik    const char *endversionstr;
6598766Smarkm
6698766Smarkm    if (pkgname == NULL)
6798766Smarkm	errx(2, "%s: Passed NULL pkgname.", __func__);
68131274Seik
69227458Seadler    /* Look for the last '-' in the pkgname */
70131274Seik    ch = strrchr(pkgname, '-');
71131274Seik    /* Cheat if we are just passed a version, not a valid package name */
72131274Seik    versionstr = ch ? ch + 1 : pkgname;
73131274Seik
74131274Seik    /* Look for the last '_' in the version string, advancing the end pointer */
75131274Seik    ch = strrchr(versionstr, '_');
76131274Seik    if (revision != NULL) {
77131274Seik	*revision = ch ? strtoul(ch + 1, NULL, 10) : 0;
78131274Seik    }
79131274Seik    endversionstr = ch;
80131274Seik
81131274Seik    /* Look for the last ',' in the remaining version string */
82131274Seik    ch = strrchr(endversionstr ? endversionstr + 1 : versionstr, ',');
8398766Smarkm    if (epoch != NULL) {
84131274Seik	*epoch = ch ? strtoul(ch + 1, NULL, 10) : 0;
8598766Smarkm    }
86131274Seik    if (ch && !endversionstr)
87131274Seik	endversionstr = ch;
88131274Seik
89131274Seik    /* set the pointer behind the last character of the version without revision or epoch */
90131274Seik    if (endname)
91131274Seik	*endname = endversionstr ? endversionstr : strrchr(versionstr, '\0');
92131274Seik
93131274Seik    return versionstr;
94131274Seik}
95131274Seik
96131274Seik/*
97131274Seik * PORTVERSIONs are composed of components separated by dots. A component
98131274Seik * consists of a version number, a letter and a patchlevel number. This does
99131274Seik * not conform to the porter's handbook, but let us formulate rules that
100131274Seik * fit the current practice and are far simpler than to make decisions
101131274Seik * based on the order of netters and lumbers. Besides, people use versions
102131274Seik * like 10b2 in the ports...
103131274Seik */
104131274Seik
105131274Seiktypedef struct {
106131274Seik#ifdef __LONG_LONG_SUPPORTED
107131274Seik    long long n;
108131274Seik    long long pl;
109131274Seik#else
110131274Seik    long n;
111131274Seik    long pl;
112131274Seik#endif
113131274Seik    int a;
114131274Seik} version_component;
115131274Seik
116131274Seik/*
117131274Seik * get_component(position, component) gets the value of the next component
118131274Seik * (number - letter - number triple) and returns a pointer to the next character
119131274Seik * after any leading separators
120131274Seik *
121131274Seik * - components are separated by dots
122131274Seik * - characters !~ [a-zA-Z0-9.+*] are treated as separators
123131274Seik *   (1.0:2003.09.16 = 1.0.2003.09.16), this may not be what you expect:
124131274Seik *   1.0.1:2003.09.16 < 1.0:2003.09.16
125131274Seik * - consecutive separators are collapsed (10..1 = 10.1)
126131274Seik * - missing separators are inserted, essentially
127131274Seik *   letter number letter => letter number . letter (10a1b2 = 10a1.b2)
128131274Seik * - missing components are assumed to be equal to 0 (10 = 10.0 = 10.0.0)
129131274Seik * - the letter sort order is: [none], a, b, ..., z; numbers without letters
130131274Seik *   sort first (10 < 10a < 10b)
131131274Seik * - missing version numbers (in components starting with a letter) sort as -1
132131274Seik *   (a < 0, 10.a < 10)
133131274Seik * - a separator is inserted before the special strings "pl", "alpha", "beta",
134131274Seik *   "pre" and "rc".
135131274Seik * - "pl" sorts before every other letter, "alpha", "beta", "pre" and "rc"
136131274Seik *   sort as a, b, p and r. (10alpha = 10.a < 10, but 10 < 10a; pl11 < alpha3
137131274Seik *   < 0.1beta2 = 0.1.b2 < 0.1)
138131274Seik * - other strings use only the first letter for sorting, case is ignored
139131274Seik *   (1.d2 = 1.dev2 = 1.Development2)
140131274Seik * - The special component `*' is guaranteed to be the smallest possible
141131274Seik *   component (2.* < 2pl1 < 2alpha3 < 2.9f7 < 3.*)
142131274Seik * - components separated by `+' are handled by version_cmp below
143131274Seik *
144131274Seik * Oliver Eikemeier
145131274Seik */
146131274Seik
147131274Seikstatic const struct {
148131274Seik    const char *name;
149131274Seik    size_t namelen;
150131274Seik    int value;
151131274Seik} stage[] = {
152131274Seik    { "pl",    2,  0        },
153131274Seik    { "alpha", 5, 'a'-'a'+1 },
154131274Seik    { "beta",  4, 'b'-'a'+1 },
155131274Seik    { "pre",   3, 'p'-'a'+1 },
156131274Seik    { "rc",    2, 'r'-'a'+1 },
157131274Seik    { NULL,    0,  -1       }
158131274Seik};
159131274Seik
160131274Seikstatic const char *
161131274Seikget_component(const char *position, version_component *component)
162131274Seik{
163131274Seik    const char *pos = position;
164131274Seik    int hasstage = 0, haspatchlevel = 0;
165131274Seik
166131274Seik    if (!pos)
167131274Seik	errx(2, "%s: Passed NULL position.", __func__);
168131274Seik
169131274Seik    /* handle version number */
170131274Seik    if (isdigit(*pos)) {
171131274Seik	char *endptr;
172131274Seik#ifdef __LONG_LONG_SUPPORTED
173131274Seik	component->n = strtoll(pos, &endptr, 10);
174131274Seik#else
175131274Seik	component->n = strtol(pos, &endptr, 10);
176131274Seik#endif
177131274Seik	/* should we test for errno == ERANGE? */
178131274Seik	pos = endptr;
179131274Seik    } else if (*pos == '*') {
180131274Seik	component->n = -2;
181131274Seik	do {
182131274Seik	    pos++;
183131274Seik	} while(*pos && *pos != '+');
184131274Seik    } else {
185131274Seik	component->n = -1;
186131274Seik	hasstage = 1;
18798766Smarkm    }
188131274Seik
189131274Seik    /* handle letter */
190131274Seik    if (isalpha(*pos)) {
191131274Seik	int c = tolower(*pos);
192131274Seik	haspatchlevel = 1;
193131274Seik	/* handle special suffixes */
194131274Seik	if (isalpha(pos[1])) {
195131274Seik	    int i;
196131274Seik	    for (i = 0; stage[i].name; i++) {
197131274Seik		if (strncasecmp(pos, stage[i].name, stage[i].namelen) == 0
198131274Seik                    && !isalpha(pos[stage[i].namelen])) {
199131274Seik		    if (hasstage) {
200131274Seik			/* stage to value */
201131274Seik			component->a = stage[i].value;
202131274Seik			pos += stage[i].namelen;
203131274Seik		    } else {
204131274Seik			/* insert dot */
205131274Seik			component->a = 0;
206131274Seik			haspatchlevel = 0;
207131274Seik		    }
208131274Seik		    c = 0;
209131274Seik		    break;
210131274Seik		}
211131274Seik	    }
212131274Seik	}
213131274Seik	/* unhandled above */
214131274Seik	if (c) {
215131274Seik	    /* use the first letter and skip following */
216131274Seik	    component->a = c - 'a' + 1;
217131274Seik	    do {
218131274Seik		++pos;
219131274Seik	    } while (isalpha(*pos));
220131274Seik	}
221131274Seik    } else {
222131274Seik	component->a = 0;
223131274Seik	haspatchlevel = 0;
224131274Seik    }
225131274Seik
226131274Seik    if (haspatchlevel) {
227131274Seik	/* handle patch number */
228131274Seik	if (isdigit(*pos)) {
229131274Seik	    char *endptr;
230131274Seik#ifdef __LONG_LONG_SUPPORTED
231131274Seik	    component->pl = strtoll(pos, &endptr, 10);
232131274Seik#else
233131274Seik	    component->pl = strtol(pos, &endptr, 10);
234131274Seik#endif
235131274Seik	    /* should we test for errno == ERANGE? */
236131274Seik	    pos = endptr;
237131274Seik	} else {
238131274Seik	    component->pl = -1;
239131274Seik	}
240131274Seik    } else {
241131274Seik	component->pl = 0;
242131274Seik    }
243131274Seik
244131274Seik    /* skip trailing separators */
245131274Seik    while (*pos && !isdigit(*pos) && !isalpha(*pos) && *pos != '+' && *pos != '*') {
246131274Seik	pos++;
247131274Seik    }
248131274Seik
249131274Seik    return pos;
25098766Smarkm}
25198766Smarkm
25298766Smarkm/*
25398766Smarkm * version_cmp(pkg1, pkg2) returns -1, 0 or 1 depending on if the version
25498766Smarkm * components of pkg1 is less than, equal to or greater than pkg2. No
255101677Sschweikh * comparison of the basenames is done.
25698766Smarkm *
257101677Sschweikh * The port version is defined by:
25898766Smarkm * ${PORTVERSION}[_${PORTREVISION}][,${PORTEPOCH}]
259101677Sschweikh * ${PORTEPOCH} supersedes ${PORTVERSION} supersedes ${PORTREVISION}.
26098766Smarkm * See the commit log for revision 1.349 of ports/Mk/bsd.port.mk
26198766Smarkm * for more information.
26298766Smarkm *
26398766Smarkm * The epoch and revision are defined to be a single number, while the rest
26498766Smarkm * of the version should conform to the porting guidelines. It can contain
265101677Sschweikh * multiple components, separated by a period, including letters.
26698766Smarkm *
267131274Seik * The tests allow for significantly more latitude in the version numbers
268131274Seik * than is allowed in the guidelines. No point in enforcing them here.
269131274Seik * That's what portlint is for.
27098766Smarkm *
27198766Smarkm * Jeremy D. Lea.
272131274Seik * reimplemented by Oliver Eikemeier
27398766Smarkm */
27498766Smarkmint
27598766Smarkmversion_cmp(const char *pkg1, const char *pkg2)
27698766Smarkm{
277131274Seik    const char *v1, *v2, *ve1, *ve2;
278131274Seik    unsigned long e1, e2, r1, r2;
279131274Seik    int result = 0;
28098766Smarkm
281131274Seik    v1 = split_version(pkg1, &ve1, &e1, &r1);
282131274Seik    v2 = split_version(pkg2, &ve2, &e2, &r2);
283131274Seik
284131274Seik    /* Check epoch, port version, and port revision, in that order. */
285131274Seik    if (e1 != e2) {
286131274Seik	result = (e1 < e2 ? -1 : 1);
287131274Seik    }
288131274Seik
289131274Seik    /* Shortcut check for equality before invoking the parsing routines. */
290131274Seik    if (result == 0 && (ve1 - v1 != ve2 - v2 || strncasecmp(v1, v2, ve1 - v1) != 0)) {
291131274Seik	/* Loop over different components (the parts separated by dots).
292131274Seik	 * If any component differs, we have the basis for an inequality. */
293131274Seik	while(result == 0 && (v1 < ve1 || v2 < ve2)) {
294131274Seik	    int block_v1 = 0;
295131274Seik	    int block_v2 = 0;
296131274Seik	    version_component vc1 = {0, 0, 0};
297131274Seik	    version_component vc2 = {0, 0, 0};
298131274Seik	    if (v1 < ve1 && *v1 != '+') {
299131274Seik		v1 = get_component(v1, &vc1);
30098766Smarkm	    } else {
301131274Seik		block_v1 = 1;
30298766Smarkm	    }
303131274Seik	    if (v2 < ve2 && *v2 != '+') {
304131274Seik		v2 = get_component(v2, &vc2);
305131274Seik	    } else {
306131274Seik		block_v2 = 1;
307131274Seik	    }
308131274Seik	    if (block_v1 && block_v2) {
309131274Seik		if (v1 < ve1)
310131274Seik		    v1++;
311131274Seik		if (v2 < ve2)
312131274Seik		    v2++;
313131274Seik	    } else if (vc1.n != vc2.n) {
314131274Seik		result = (vc1.n < vc2.n ? -1 : 1);
315131274Seik	    } else if (vc1.a != vc2.a) {
316131274Seik		result = (vc1.a < vc2.a ? -1 : 1);
317131274Seik	    } else if (vc1.pl != vc2.pl) {
318131274Seik		result = (vc1.pl < vc2.pl ? -1 : 1);
319131274Seik	    }
32098766Smarkm	}
32198766Smarkm    }
322131274Seik
323131274Seik    /* Compare FreeBSD revision numbers. */
324131274Seik    if (result == 0 && r1 != r2) {
325131274Seik	result = (r1 < r2 ? -1 : 1);
326131274Seik    }
327131274Seik    return result;
32898766Smarkm}
329