pkg_version.c
1 /*- 2 * Copyright (c) 2011 Philippe Pepiot <phil@philpep.org> 3 * Copyright (c) 2014 Vsevolod Stakhov <vsevolod@FreeBSD.org> 4 * 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 * in this position and unchanged. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT, 20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28 29 #include <assert.h> 30 #include <string.h> 31 #include <stdlib.h> 32 #include <ctype.h> 33 34 #include "pkg.h" 35 #include "private/event.h" 36 #include "private/pkg.h" 37 38 /* 39 * split_version(pkgname, endname, epoch, revision) returns a pointer to 40 * the version portion of a package name and the two special components. 41 * 42 * Syntax is: ${PORTNAME}-${PORTVERSION}[_${PORTREVISION}][,${PORTEPOCH}] 43 * 44 * Written by Oliver Eikemeier 45 * Based on work of Jeremy D. Lea. 46 */ 47 static const char * 48 split_version(const char *pkgname, const char **endname, 49 unsigned long *epoch, unsigned long *revision) 50 { 51 char *ch; 52 const char *versionstr; 53 const char *endversionstr; 54 55 if (pkgname == NULL) { 56 pkg_emit_error("%s: Passed NULL pkgname.", __func__); 57 return (NULL); 58 } 59 60 /* Look for the last '-' the the pkgname */ 61 ch = strrchr(pkgname, '-'); 62 /* Cheat if we are just passed a version, not a valid package name */ 63 versionstr = ch ? ch + 1 : pkgname; 64 65 /* 66 * Look for the last '_' in the version string, advancing the 67 * end pointer. 68 */ 69 ch = strrchr(versionstr, '_'); 70 71 if (revision != NULL) 72 *revision = ch ? strtoul(ch + 1, NULL, 10) : 0; 73 74 endversionstr = ch; 75 76 /* Look for the last ',' in the remaining version string */ 77 ch = strrchr(endversionstr ? endversionstr + 1 : versionstr, ','); 78 79 if (epoch != NULL) 80 *epoch = ch ? strtoul(ch + 1, NULL, 10) : 0; 81 82 if (ch && !endversionstr) 83 endversionstr = ch; 84 85 /* 86 * set the pointer behind the last character of the version without 87 * revision or epoch 88 */ 89 if (endname) 90 *endname = endversionstr ? endversionstr : 91 strrchr(versionstr, '\0'); 92 93 return versionstr; 94 } 95 96 /* 97 * PORTVERSIONs are composed of components separated by dots. A component 98 * consists of a version number, a letter and a patchlevel number. This does 99 * not conform to the porter's handbook, but let us formulate rules that 100 * fit the current practice and are far simpler than to make decisions 101 * based on the order of netters and lumbers. Besides, people use versions 102 * like 10b2 in the ports... 103 */ 104 105 typedef struct { 106 #ifdef __LONG_LONG_SUPPORTED 107 long long n; 108 long long pl; 109 #else 110 long n; 111 long pl; 112 #endif 113 int a; 114 } version_component; 115 116 /* 117 * get_component(position, component) gets the value of the next component 118 * (number - letter - number triple) and returns a pointer to the next character 119 * after any leading separators 120 * 121 * - components are separated by dots 122 * - characters !~ [a-zA-Z0-9.+*] are treated as separators 123 * (1.0:2003.09.16 = 1.0.2003.09.16), this may not be what you expect: 124 * 1.0.1:2003.09.16 < 1.0:2003.09.16 125 * - consecutive separators are collapsed (10..1 = 10.1) 126 * - missing separators are inserted, essentially 127 * letter number letter => letter number . letter (10a1b2 = 10a1.b2) 128 * - missing components are assumed to be equal to 0 (10 = 10.0 = 10.0.0) 129 * - the letter sort order is: [none], a, b, ..., z; numbers without letters 130 * sort first (10 < 10a < 10b) 131 * - missing version numbers (in components starting with a letter) sort as -1 132 * (a < 0, 10.a < 10) 133 * - a separator is inserted before the special strings "pl", "alpha", "beta", 134 * "pre" and "rc". 135 * - "pl" sorts before every other letter, "alpha", "beta", "pre" and "rc" 136 * sort as a, b, p and r. (10alpha = 10.a < 10, but 10 < 10a; pl11 < alpha3 137 * < 0.1beta2 = 0.1.b2 < 0.1) 138 * - other strings use only the first letter for sorting, case is ignored 139 * (1.d2 = 1.dev2 = 1.Development2) 140 * - The special component `*' is guaranteed to be the smallest possible 141 * component (2.* < 2pl1 < 2alpha3 < 2.9f7 < 3.*) 142 * - components separated by `+' are handled by version_cmp below 143 * 144 * Oliver Eikemeier 145 */ 146 147 static const struct stage { 148 const char *name; 149 size_t namelen; 150 int value; 151 } stages[] = { 152 { "pl", 2, 0 }, 153 { "snap", 6, 1 }, 154 #define ABASE 2 /* Last special early-sorted prefix + 1 */ 155 { "alpha", 5, 'a'-'a'+ABASE }, 156 { "beta", 4, 'b'-'a'+ABASE }, 157 { "pre", 3, 'p'-'a'+ABASE }, 158 { "rc", 2, 'r'-'a'+ABASE }, 159 }; 160 161 static const char * 162 get_component(const char *position, version_component *component) 163 { 164 const char *pos = position; 165 int hasstage = 0, haspatchlevel = 0; 166 167 if (!pos) { 168 pkg_emit_error("%s: Passed NULL position.", __func__); 169 return (NULL); 170 } 171 172 /* handle version number */ 173 if (isdigit(*pos)) { 174 char *endptr; 175 #ifdef __LONG_LONG_SUPPORTED 176 component->n = strtoll(pos, &endptr, 10); 177 #else 178 component->n = strtol(pos, &endptr, 10); 179 #endif 180 /* should we test for errno == ERANGE? */ 181 pos = endptr; 182 } else if (*pos == '*') { 183 component->n = -2; 184 do { 185 pos++; 186 } while(*pos && *pos != '+'); 187 } else { 188 component->n = -1; 189 hasstage = 1; 190 } 191 192 /* handle letter */ 193 if (isalpha(*pos)) { 194 int c = tolower(*pos); 195 haspatchlevel = 1; 196 /* handle special suffixes */ 197 if (isalpha(pos[1])) { 198 unsigned int i; 199 for (i = 0; i < NELEM(stages); i++) { 200 const struct stage *stage = &stages[i]; 201 size_t len = stage->namelen; 202 if (strncasecmp(pos, stage->name, len) == 0 && 203 !isalpha(pos[stage->namelen])) { 204 if (hasstage) { 205 /* stage to value */ 206 component->a = stage->value; 207 pos += stage->namelen; 208 } else { 209 /* insert dot */ 210 component->a = 0; 211 haspatchlevel = 0; 212 } 213 c = 0; 214 break; 215 } 216 } 217 } 218 /* unhandled above */ 219 if (c) { 220 /* use the first letter and skip following */ 221 component->a = c - 'a' + ABASE; 222 do { 223 ++pos; 224 } while (isalpha(*pos)); 225 } 226 } else { 227 component->a = 0; 228 haspatchlevel = 0; 229 } 230 231 if (haspatchlevel) { 232 /* handle patch number */ 233 if (isdigit(*pos)) { 234 char *endptr; 235 #ifdef __LONG_LONG_SUPPORTED 236 component->pl = strtoll(pos, &endptr, 10); 237 #else 238 component->pl = strtol(pos, &endptr, 10); 239 #endif 240 /* should we test for errno == ERANGE? */ 241 pos = endptr; 242 } else { 243 component->pl = -1; 244 } 245 } else { 246 component->pl = 0; 247 } 248 249 /* skip trailing separators */ 250 while (*pos && !isdigit(*pos) && !isalpha(*pos) 251 && *pos != '+' && *pos != '*') 252 pos++; 253 254 return pos; 255 } 256 257 /* 258 * version_cmp(pkg1, pkg2) returns -1, 0 or 1 depending on if the version 259 * components of pkg1 is less than, equal to or greater than pkg2. No 260 * comparison of the basenames is done. 261 * 262 * The port version is defined by: 263 * ${PORTVERSION}[_${PORTREVISION}][,${PORTEPOCH}] 264 * ${PORTEPOCH} supersedes ${PORTVERSION} supersedes ${PORTREVISION}. 265 * See the commit log for revision 1.349 of ports/Mk/bsd.port.mk 266 * for more information. 267 * 268 * The epoch and revision are defined to be a single number, while the rest 269 * of the version should conform to the porting guidelines. It can contain 270 * multiple components, separated by a period, including letters. 271 * 272 * The tests allow for significantly more latitude in the version numbers 273 * than is allowed in the guidelines. No point in enforcing them here. 274 * That's what portlint is for. 275 * 276 * Jeremy D. Lea. 277 * reimplemented by Oliver Eikemeier 278 */ 279 int 280 pkg_version_cmp(const char * const pkg1, const char * const pkg2) 281 { 282 const char *v1, *v2, *ve1, *ve2; 283 unsigned long e1, e2, r1, r2; 284 int result = 0; 285 286 v1 = split_version(pkg1, &ve1, &e1, &r1); 287 v2 = split_version(pkg2, &ve2, &e2, &r2); 288 289 assert (v1 != NULL && v2 != NULL); 290 291 /* Check epoch, port version, and port revision, in that order. */ 292 if (e1 != e2) 293 result = (e1 < e2 ? -1 : 1); 294 295 /* Shortcut check for equality before invoking the parsing routines. */ 296 if (result == 0 && 297 (ve1 - v1 != ve2 - v2 || strncasecmp(v1, v2, ve1 - v1) != 0)) { 298 /* 299 * Loop over different components (the parts separated by dots). 300 * If any component differs, there is an inequality. 301 */ 302 while (result == 0 && (v1 < ve1 || v2 < ve2)) { 303 int block_v1 = 0; 304 int block_v2 = 0; 305 version_component vc1 = {0, 0, 0}; 306 version_component vc2 = {0, 0, 0}; 307 if (v1 < ve1 && *v1 != '+') { 308 v1 = get_component(v1, &vc1); 309 assert (v1 != NULL); 310 } else { 311 block_v1 = 1; 312 } 313 if (v2 < ve2 && *v2 != '+') { 314 v2 = get_component(v2, &vc2); 315 assert (v2 != NULL); 316 } else { 317 block_v2 = 1; 318 } 319 if (block_v1 && block_v2) { 320 if (v1 < ve1) 321 v1++; 322 if (v2 < ve2) 323 v2++; 324 } else if (vc1.n != vc2.n) { 325 result = (vc1.n < vc2.n ? -1 : 1); 326 } else if (vc1.a != vc2.a) { 327 result = (vc1.a < vc2.a ? -1 : 1); 328 } else if (vc1.pl != vc2.pl) { 329 result = (vc1.pl < vc2.pl ? -1 : 1); 330 } 331 } 332 } 333 334 /* Compare FreeBSD revision numbers. */ 335 if (result == 0 && r1 != r2) 336 result = (r1 < r2 ? -1 : 1); 337 338 return (result); 339 } 340 341 pkg_change_t 342 pkg_version_change_between(const struct pkg * pkg1, const struct pkg *pkg2) 343 { 344 if (pkg2 == NULL) 345 return PKG_REINSTALL; 346 347 switch (pkg_version_cmp(pkg2->version, pkg1->version)) { 348 case -1: 349 return (PKG_UPGRADE); 350 default: /* placate the compiler */ 351 case 0: 352 return (PKG_REINSTALL); 353 case 1: 354 return (PKG_DOWNGRADE); 355 } 356 }