xref: /minix/external/bsd/pkg_install/dist/lib/dewey.c (revision a824f5a1)
1 /* $NetBSD: dewey.c,v 1.3 2009/03/08 14:53:16 joerg Exp $ */
2 
3 /*
4  * Copyright � 2002 Alistair G. Crooks.  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  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  * 3. The name of the author may not be used to endorse or promote
15  *    products derived from this software without specific prior written
16  *    permission.
17  *
18  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
19  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
21  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
22  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
23  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
24  * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
26  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
27  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29  */
30 
31 #if HAVE_CONFIG_H
32 #include "config.h"
33 #endif
34 #include <nbcompat.h>
35 
36 #if HAVE_CTYPE_H
37 #include <ctype.h>
38 #endif
39 #if HAVE_STDLIB_H
40 #include <stdlib.h>
41 #endif
42 
43 #include "defs.h"
44 #include "dewey.h"
45 
46 #define PKG_PATTERN_MAX 1024
47 
48 /* do not modify these values, or things will NOT work */
49 enum {
50         Alpha = -3,
51         Beta = -2,
52         RC = -1,
53         Dot = 0,
54         Patch = 1
55 };
56 
57 /* this struct defines a version number */
58 typedef struct arr_t {
59 	unsigned	c;              /* # of version numbers */
60 	unsigned	size;           /* size of array */
61 	int	       *v;              /* array of decimal numbers */
62 	int		netbsd;         /* any "nb" suffix */
63 } arr_t;
64 
65 /* this struct describes a test */
66 typedef struct test_t {
67 	const char     *s;              /* string representation */
68 	unsigned	len;            /* length of string */
69 	int		t;              /* enumerated type of test */
70 } test_t;
71 
72 
73 /* the tests that are recognised. */
74  const test_t   tests[] = {
75         {	"<=",	2,	DEWEY_LE	},
76         {	"<",	1,	DEWEY_LT	},
77         {	">=",	2,	DEWEY_GE	},
78         {	">",	1,	DEWEY_GT	},
79         {	"==",	2,	DEWEY_EQ	},
80         {	"!=",	2,	DEWEY_NE	},
81         {	NULL,	0,	0	}
82 };
83 
84  const test_t	modifiers[] = {
85 	{	"alpha",	5,	Alpha	},
86 	{	"beta",		4,	Beta	},
87 	{	"pre",		3,	RC	},
88 	{	"rc",		2,	RC	},
89 	{	"pl",		2,	Dot	},
90 	{	"_",		1,	Dot	},
91 	{	".",		1,	Dot	},
92 	{	NULL,		0,	0	}
93 };
94 
95 
96 
97 /* locate the test in the tests array */
98 int
dewey_mktest(int * op,const char * test)99 dewey_mktest(int *op, const char *test)
100 {
101 	const test_t *tp;
102 
103 	for (tp = tests ; tp->s ; tp++) {
104 		if (strncasecmp(test, tp->s, tp->len) == 0) {
105 			*op = tp->t;
106 			return tp->len;
107 		}
108 	}
109 	return -1;
110 }
111 
112 /*
113  * make a component of a version number.
114  * '.' encodes as Dot which is '0'
115  * '_' encodes as 'patch level', or 'Dot', which is 0.
116  * 'pl' encodes as 'patch level', or 'Dot', which is 0.
117  * 'alpha' encodes as 'alpha version', or Alpha, which is -3.
118  * 'beta' encodes as 'beta version', or Beta, which is -2.
119  * 'rc' encodes as 'release candidate', or RC, which is -1.
120  * 'nb' encodes as 'netbsd version', which is used after all other tests
121  */
122 static int
mkcomponent(arr_t * ap,const char * num)123 mkcomponent(arr_t *ap, const char *num)
124 {
125 	static const char       alphas[] = "abcdefghijklmnopqrstuvwxyz";
126 	const test_t	       *modp;
127 	int                 n;
128 	const char             *cp;
129 
130 	if (ap->c == ap->size) {
131 		if (ap->size == 0) {
132 			ap->size = 62;
133 			if ((ap->v = malloc(ap->size * sizeof(int))) == NULL)
134 				err(EXIT_FAILURE, "mkver malloc failed");
135 		} else {
136 			ap->size *= 2;
137 			if ((ap->v = realloc(ap->v, ap->size * sizeof(int)))
138 			    == NULL)
139 				err(EXIT_FAILURE, "mkver realloc failed");
140 		}
141 	}
142 	if (isdigit((unsigned char)*num)) {
143 		for (cp = num, n = 0 ; isdigit((unsigned char)*num) ; num++) {
144 			n = (n * 10) + (*num - '0');
145 		}
146 		ap->v[ap->c++] = n;
147 		return (int)(num - cp);
148 	}
149 	for (modp = modifiers ; modp->s ; modp++) {
150 		if (strncasecmp(num, modp->s, modp->len) == 0) {
151 			ap->v[ap->c++] = modp->t;
152 			return modp->len;
153 		}
154 	}
155 	if (strncasecmp(num, "nb", 2) == 0) {
156 		for (cp = num, num += 2, n = 0 ; isdigit((unsigned char)*num) ; num++) {
157 			n = (n * 10) + (*num - '0');
158 		}
159 		ap->netbsd = n;
160 		return (int)(num - cp);
161 	}
162 	if (isalpha((unsigned char)*num)) {
163 		ap->v[ap->c++] = Dot;
164 		cp = strchr(alphas, tolower((unsigned char)*num));
165 		if (ap->c == ap->size) {
166 			ap->size *= 2;
167 			if ((ap->v = realloc(ap->v, ap->size * sizeof(int))) == NULL)
168 				err(EXIT_FAILURE, "mkver realloc failed");
169 		}
170 		ap->v[ap->c++] = (int)(cp - alphas) + 1;
171 		return 1;
172 	}
173 	return 1;
174 }
175 
176 /* make a version number string into an array of comparable ints */
177 static int
mkversion(arr_t * ap,const char * num)178 mkversion(arr_t *ap, const char *num)
179 {
180 	ap->c = 0;
181 	ap->size = 0;
182 	ap->v = NULL;
183 	ap->netbsd = 0;
184 
185 	while (*num) {
186 		num += mkcomponent(ap, num);
187 	}
188 	return 1;
189 }
190 
191 static void
freeversion(arr_t * ap)192 freeversion(arr_t *ap)
193 {
194 	free(ap->v);
195 	ap->v = NULL;
196 	ap->c = 0;
197 	ap->size = 0;
198 }
199 
200 #define DIGIT(v, c, n) (((n) < (c)) ? v[n] : 0)
201 
202 /* compare the result against the test we were expecting */
203 static int
result(int cmp,int tst)204 result(int cmp, int tst)
205 {
206 	switch(tst) {
207 	case DEWEY_LT:
208 		return cmp < 0;
209 	case DEWEY_LE:
210 		return cmp <= 0;
211 	case DEWEY_GT:
212 		return cmp > 0;
213 	case DEWEY_GE:
214 		return cmp >= 0;
215 	case DEWEY_EQ:
216 		return cmp == 0;
217 	case DEWEY_NE:
218 		return cmp != 0;
219 	default:
220 		return 0;
221 	}
222 }
223 
224 /* do the test on the 2 vectors */
225 static int
vtest(arr_t * lhs,int tst,arr_t * rhs)226 vtest(arr_t *lhs, int tst, arr_t *rhs)
227 {
228 	int cmp;
229 	unsigned int c, i;
230 
231 	for (i = 0, c = MAX(lhs->c, rhs->c) ; i < c ; i++) {
232 		if ((cmp = DIGIT(lhs->v, lhs->c, i) - DIGIT(rhs->v, rhs->c, i)) != 0) {
233 			return result(cmp, tst);
234 		}
235 	}
236 	return result(lhs->netbsd - rhs->netbsd, tst);
237 }
238 
239 /*
240  * Compare two dewey decimal numbers
241  */
242 int
dewey_cmp(const char * lhs,int op,const char * rhs)243 dewey_cmp(const char *lhs, int op, const char *rhs)
244 {
245 	arr_t	right;
246 	arr_t	left;
247 	int retval;
248 
249 	if (!mkversion(&left, lhs))
250 		return 0;
251 	if (!mkversion(&right, rhs)) {
252 		freeversion(&left);
253 		return 0;
254 	}
255         retval = vtest(&left, op, &right);
256 	freeversion(&left);
257 	freeversion(&right);
258 	return retval;
259 }
260 
261 /*
262  * Perform dewey match on "pkg" against "pattern".
263  * Return 1 on match, 0 on non-match, -1 on error.
264  */
265 int
dewey_match(const char * pattern,const char * pkg)266 dewey_match(const char *pattern, const char *pkg)
267 {
268 	const char *version;
269 	const char *sep, *sep2;
270 	int op, op2;
271 	int n;
272 
273 	/* compare names */
274 	if ((version=strrchr(pkg, '-')) == NULL) {
275 		return 0;
276 	}
277 	if ((sep = strpbrk(pattern, "<>")) == NULL)
278 		return -1;
279 	/* compare name lengths */
280 	if ((sep-pattern != version-pkg) ||
281 	    strncmp(pkg, pattern, (size_t)(version-pkg)) != 0)
282 		return 0;
283 	version++;
284 
285 	/* extract comparison operator */
286         if ((n = dewey_mktest(&op, sep)) < 0) {
287 		return 0;
288         }
289 	/* skip operator */
290 	sep += n;
291 
292 	/* if greater than, look for less than */
293 	sep2 = NULL;
294 	if (op == DEWEY_GT || op == DEWEY_GE) {
295 		if ((sep2 = strchr(sep, '<')) != NULL) {
296 			if ((n = dewey_mktest(&op2, sep2)) < 0) {
297 				return 0;
298 			}
299 			/* compare upper limit */
300 			if (!dewey_cmp(version, op2, sep2+n))
301 				return 0;
302 		}
303 	}
304 
305 	/* compare only pattern / lower limit */
306 	if (sep2) {
307 		char ver[PKG_PATTERN_MAX];
308 
309 		strlcpy(ver, sep, MIN((ssize_t)sizeof(ver), sep2-sep+1));
310 		if (dewey_cmp(version, op, ver))
311 			return 1;
312 	}
313 	else {
314 		if (dewey_cmp(version, op, sep))
315 			return 1;
316 	}
317 
318 	return 0;
319 }
320 
321