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 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 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 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 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 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 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 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 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