XBPS Library API  0.19
The X Binary Package System
dewey.c
1 /* $NetBSD: dewey.c,v 1.1.1.3 2009/03/08 14:51:37 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 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <strings.h>
35 #include <ctype.h>
36 
37 #include "xbps_api_impl.h"
38 #ifdef HAVE_CONFIG_H
39 #include "config.h"
40 #endif
41 
42 #define PKG_PATTERN_MAX 1024
43 
44 #ifndef MIN
45 #define MIN(a,b) (((a) < (b)) ? (a) : (b))
46 #endif
47 #ifndef MAX
48 #define MAX(a,b) (((a) > (b)) ? (a) : (b))
49 #endif
50 
51 enum {
52  DEWEY_LT,
53  DEWEY_LE,
54  DEWEY_EQ,
55  DEWEY_GE,
56  DEWEY_GT,
57  DEWEY_NE
58 };
59 
60 /* do not modify these values, or things will NOT work */
61 enum {
62  Alpha = -3,
63  Beta = -2,
64  RC = -1,
65  Dot = 0,
66  Patch = 1
67 };
68 
69 /* this struct defines a version number */
70 typedef struct arr_t {
71  unsigned c; /* # of version numbers */
72  unsigned size; /* size of array */
73  int *v; /* array of decimal numbers */
74  int revision; /* any "_" suffix */
75 } arr_t;
76 
77 /* this struct describes a test */
78 typedef struct test_t {
79  const char *s; /* string representation */
80  unsigned len; /* length of string */
81  int t; /* enumerated type of test */
82 } test_t;
83 
84 
85 /* the tests that are recognised. */
86 const test_t tests[] = {
87  { "<=", 2, DEWEY_LE },
88  { "<", 1, DEWEY_LT },
89  { ">=", 2, DEWEY_GE },
90  { ">", 1, DEWEY_GT },
91  { "==", 2, DEWEY_EQ },
92  { "!=", 2, DEWEY_NE },
93  { NULL, 0, 0 }
94 };
95 
96 const test_t modifiers[] = {
97  { "alpha", 5, Alpha },
98  { "beta", 4, Beta },
99  { "pre", 3, RC },
100  { "rc", 2, RC },
101  { "pl", 2, Dot },
102  { ".", 1, Dot },
103  { NULL, 0, 0 }
104 };
105 
106 
107 
108 /* locate the test in the tests array */
109 static int
110 dewey_mktest(int *op, const char *test)
111 {
112  const test_t *tp;
113 
114  for (tp = tests ; tp->s ; tp++) {
115  if (strncasecmp(test, tp->s, tp->len) == 0) {
116  *op = tp->t;
117  return tp->len;
118  }
119  }
120  return -1;
121 }
122 
123 /*
124  * make a component of a version number.
125  * '.' encodes as Dot which is '0'
126  * 'pl' encodes as 'patch level', or 'Dot', which is 0.
127  * 'alpha' encodes as 'alpha version', or Alpha, which is -3.
128  * 'beta' encodes as 'beta version', or Beta, which is -2.
129  * 'rc' encodes as 'release candidate', or RC, which is -1.
130  * '_' encodes as 'xbps revision', which is used after all other tests
131  */
132 static int
133 mkcomponent(arr_t *ap, const char *num)
134 {
135  static const char alphas[] = "abcdefghijklmnopqrstuvwxyz";
136  const test_t *modp;
137  int n;
138  const char *cp;
139 
140  if (ap->c == ap->size) {
141  if (ap->size == 0) {
142  ap->size = 62;
143  ap->v = malloc(ap->size * sizeof(int));
144  assert(ap->v != NULL);
145  } else {
146  ap->size *= 2;
147  ap->v = realloc(ap->v, ap->size * sizeof(int));
148  assert(ap->v != NULL);
149  }
150  }
151  if (isdigit((unsigned char)*num)) {
152  for (cp = num, n = 0 ; isdigit((unsigned char)*num) ; num++) {
153  n = (n * 10) + (*num - '0');
154  }
155  ap->v[ap->c++] = n;
156  return (int)(num - cp);
157  }
158  for (modp = modifiers ; modp->s ; modp++) {
159  if (strncasecmp(num, modp->s, modp->len) == 0) {
160  ap->v[ap->c++] = modp->t;
161  return modp->len;
162  }
163  }
164  if (strncasecmp(num, "_", 1) == 0) {
165  for (cp = num, num += 1, n = 0 ; isdigit((unsigned char)*num) ; num++) {
166  n = (n * 10) + (*num - '0');
167  }
168  ap->revision = n;
169  return (int)(num - cp);
170  }
171  if (isalpha((unsigned char)*num)) {
172  ap->v[ap->c++] = Dot;
173  cp = strchr(alphas, tolower((unsigned char)*num));
174  if (ap->c == ap->size) {
175  ap->size *= 2;
176  ap->v = realloc(ap->v, ap->size * sizeof(int));
177  assert(ap->v != NULL);
178  }
179  ap->v[ap->c++] = (int)(cp - alphas) + 1;
180  return 1;
181  }
182  return 1;
183 }
184 
185 /* make a version number string into an array of comparable ints */
186 static int
187 mkversion(arr_t *ap, const char *num)
188 {
189  ap->c = 0;
190  ap->size = 0;
191  ap->v = NULL;
192  ap->revision = 0;
193 
194  while (*num) {
195  num += mkcomponent(ap, num);
196  }
197  return 1;
198 }
199 
200 static void
201 freeversion(arr_t *ap)
202 {
203  free(ap->v);
204  ap->v = NULL;
205  ap->c = 0;
206  ap->size = 0;
207 }
208 
209 #define DIGIT(v, c, n) (((n) < (c)) ? v[n] : 0)
210 
211 /* compare the result against the test we were expecting */
212 static int
213 result(int cmp, int tst)
214 {
215  switch(tst) {
216  case DEWEY_LT:
217  return cmp < 0;
218  case DEWEY_LE:
219  return cmp <= 0;
220  case DEWEY_GT:
221  return cmp > 0;
222  case DEWEY_GE:
223  return cmp >= 0;
224  case DEWEY_EQ:
225  return cmp == 0;
226  case DEWEY_NE:
227  return cmp != 0;
228  default:
229  return 0;
230  }
231 }
232 
233 /* do the test on the 2 vectors */
234 static int
235 vtest(arr_t *lhs, int tst, arr_t *rhs)
236 {
237  int cmp;
238  unsigned int c, i;
239 
240  for (i = 0, c = MAX(lhs->c, rhs->c) ; i < c ; i++) {
241  if ((cmp = DIGIT(lhs->v, lhs->c, i) - DIGIT(rhs->v, rhs->c, i)) != 0) {
242  return result(cmp, tst);
243  }
244  }
245  return result(lhs->revision - rhs->revision, tst);
246 }
247 
248 /*
249  * Compare two dewey decimal numbers.
250  */
251 static int
252 dewey_cmp(const char *lhs, int op, const char *rhs)
253 {
254  arr_t right;
255  arr_t left;
256  int retval;
257 
258  if (!mkversion(&left, lhs))
259  return 0;
260  if (!mkversion(&right, rhs)) {
261  freeversion(&left);
262  return 0;
263  }
264  retval = vtest(&left, op, &right);
265  freeversion(&left);
266  freeversion(&right);
267  return retval;
268 }
269 
270 /*
271  * Returns -1, 0 or 1 depending on if the version components of
272  * pkg1 is less than, equal to or greater than pkg2. No comparison
273  * comparison of the basenames is done.
274  */
275 int
276 xbps_cmpver(const char *pkg1, const char *pkg2)
277 {
278  if (dewey_cmp(pkg1, DEWEY_LT, pkg2))
279  return -1;
280  else if (dewey_cmp(pkg1, DEWEY_GT, pkg2))
281  return 1;
282  else
283  return 0;
284 }
285 
286 /*
287  * Perform dewey match on "pkg" against "pattern".
288  * Return 1 on match, 0 on non-match, -1 on error.
289  */
290 int HIDDEN
291 dewey_match(const char *pattern, const char *pkg)
292 {
293  const char *version;
294  const char *sep, *sep2;
295  int op, op2;
296  int n;
297 
298  /* compare names */
299  if ((version=strrchr(pkg, '-')) == NULL) {
300  return 0;
301  }
302  if ((sep = strpbrk(pattern, "<>")) == NULL)
303  return -1;
304  /* compare name lengths */
305  if ((sep-pattern != version-pkg) ||
306  strncmp(pkg, pattern, (size_t)(version-pkg)) != 0)
307  return 0;
308  version++;
309 
310  /* extract comparison operator */
311  if ((n = dewey_mktest(&op, sep)) < 0) {
312  return 0;
313  }
314  /* skip operator */
315  sep += n;
316 
317  /* if greater than, look for less than */
318  sep2 = NULL;
319  if (op == DEWEY_GT || op == DEWEY_GE) {
320  if ((sep2 = strchr(sep, '<')) != NULL) {
321  if ((n = dewey_mktest(&op2, sep2)) < 0) {
322  return 0;
323  }
324  /* compare upper limit */
325  if (!dewey_cmp(version, op2, sep2+n))
326  return 0;
327  }
328  }
329 
330  /* compare only pattern / lower limit */
331  if (sep2) {
332  char ver[PKG_PATTERN_MAX];
333 
334  strlcpy(ver, sep, MIN((ssize_t)sizeof(ver), sep2-sep+1));
335  if (dewey_cmp(version, op, ver))
336  return 1;
337  } else {
338  if (dewey_cmp(version, op, sep))
339  return 1;
340  }
341 
342  return 0;
343 }
344