1/* 2 * FreeBSD install - a package for the installation and maintenance 3 * of non-core utilities. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions 7 * are met: 8 * 1. Redistributions of source code must retain the above copyright 9 * notice, this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright 11 * notice, this list of conditions and the following disclaimer in the 12 * documentation and/or other materials provided with the distribution. 13 * 14 * Maxim Sobolev 15 * 31 July 2001 16 * 17 */ 18 19#include <sys/cdefs.h> 20__FBSDID("$FreeBSD$"); 21 22#include "lib.h" 23#include <err.h> 24 25/* 26 * Routines to assist with PLIST_FMT_VER numbers in the packing 27 * lists. 28 * 29 * Following is the PLIST_FMT_VER history: 30 * 1.0 - Initial revision; 31 * 1.1 - When recording/checking checksum of symlink use hash of readlink() 32 * value instead of the hash of an object this links points to. 33 * 34 */ 35int 36verscmp(Package *pkg, int major, int minor) 37{ 38 int rval = 0; 39 40 if ((pkg->fmtver_maj < major) || (pkg->fmtver_maj == major && 41 pkg->fmtver_mnr < minor)) 42 rval = -1; 43 else if ((pkg->fmtver_maj > major) || (pkg->fmtver_maj == major && 44 pkg->fmtver_mnr > minor)) 45 rval = 1; 46 47 return rval; 48} 49 50/* 51 * split_version(pkgname, endname, epoch, revision) returns a pointer to 52 * the version portion of a package name and the two special components. 53 * 54 * Syntax is: ${PORTNAME}-${PORTVERSION}[_${PORTREVISION}][,${PORTEPOCH}] 55 * 56 * Written by Oliver Eikemeier 57 * Based on work of Jeremy D. Lea. 58 */ 59static const char * 60split_version(const char *pkgname, const char **endname, unsigned long *epoch, unsigned long *revision) 61{ 62 char *ch; 63 const char *versionstr; 64 const char *endversionstr; 65 66 if (pkgname == NULL) 67 errx(2, "%s: Passed NULL pkgname.", __func__); 68 69 /* Look for the last '-' in the pkgname */ 70 ch = strrchr(pkgname, '-'); 71 /* Cheat if we are just passed a version, not a valid package name */ 72 versionstr = ch ? ch + 1 : pkgname; 73 74 /* Look for the last '_' in the version string, advancing the end pointer */ 75 ch = strrchr(versionstr, '_'); 76 if (revision != NULL) { 77 *revision = ch ? strtoul(ch + 1, NULL, 10) : 0; 78 } 79 endversionstr = ch; 80 81 /* Look for the last ',' in the remaining version string */ 82 ch = strrchr(endversionstr ? endversionstr + 1 : versionstr, ','); 83 if (epoch != NULL) { 84 *epoch = ch ? strtoul(ch + 1, NULL, 10) : 0; 85 } 86 if (ch && !endversionstr) 87 endversionstr = ch; 88 89 /* set the pointer behind the last character of the version without revision or epoch */ 90 if (endname) 91 *endname = endversionstr ? endversionstr : 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 105typedef 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 147static const struct { 148 const char *name; 149 size_t namelen; 150 int value; 151} stage[] = { 152 { "pl", 2, 0 }, 153 { "alpha", 5, 'a'-'a'+1 }, 154 { "beta", 4, 'b'-'a'+1 }, 155 { "pre", 3, 'p'-'a'+1 }, 156 { "rc", 2, 'r'-'a'+1 }, 157 { NULL, 0, -1 } 158}; 159 160static const char * 161get_component(const char *position, version_component *component) 162{ 163 const char *pos = position; 164 int hasstage = 0, haspatchlevel = 0; 165 166 if (!pos) 167 errx(2, "%s: Passed NULL position.", __func__); 168 169 /* handle version number */ 170 if (isdigit(*pos)) { 171 char *endptr; 172#ifdef __LONG_LONG_SUPPORTED 173 component->n = strtoll(pos, &endptr, 10); 174#else 175 component->n = strtol(pos, &endptr, 10); 176#endif 177 /* should we test for errno == ERANGE? */ 178 pos = endptr; 179 } else if (*pos == '*') { 180 component->n = -2; 181 do { 182 pos++; 183 } while(*pos && *pos != '+'); 184 } else { 185 component->n = -1; 186 hasstage = 1; 187 } 188 189 /* handle letter */ 190 if (isalpha(*pos)) { 191 int c = tolower(*pos); 192 haspatchlevel = 1; 193 /* handle special suffixes */ 194 if (isalpha(pos[1])) { 195 int i; 196 for (i = 0; stage[i].name; i++) { 197 if (strncasecmp(pos, stage[i].name, stage[i].namelen) == 0 198 && !isalpha(pos[stage[i].namelen])) { 199 if (hasstage) { 200 /* stage to value */ 201 component->a = stage[i].value; 202 pos += stage[i].namelen; 203 } else { 204 /* insert dot */ 205 component->a = 0; 206 haspatchlevel = 0; 207 } 208 c = 0; 209 break; 210 } 211 } 212 } 213 /* unhandled above */ 214 if (c) { 215 /* use the first letter and skip following */ 216 component->a = c - 'a' + 1; 217 do { 218 ++pos; 219 } while (isalpha(*pos)); 220 } 221 } else { 222 component->a = 0; 223 haspatchlevel = 0; 224 } 225 226 if (haspatchlevel) { 227 /* handle patch number */ 228 if (isdigit(*pos)) { 229 char *endptr; 230#ifdef __LONG_LONG_SUPPORTED 231 component->pl = strtoll(pos, &endptr, 10); 232#else 233 component->pl = strtol(pos, &endptr, 10); 234#endif 235 /* should we test for errno == ERANGE? */ 236 pos = endptr; 237 } else { 238 component->pl = -1; 239 } 240 } else { 241 component->pl = 0; 242 } 243 244 /* skip trailing separators */ 245 while (*pos && !isdigit(*pos) && !isalpha(*pos) && *pos != '+' && *pos != '*') { 246 pos++; 247 } 248 249 return pos; 250} 251 252/* 253 * version_cmp(pkg1, pkg2) returns -1, 0 or 1 depending on if the version 254 * components of pkg1 is less than, equal to or greater than pkg2. No 255 * comparison of the basenames is done. 256 * 257 * The port version is defined by: 258 * ${PORTVERSION}[_${PORTREVISION}][,${PORTEPOCH}] 259 * ${PORTEPOCH} supersedes ${PORTVERSION} supersedes ${PORTREVISION}. 260 * See the commit log for revision 1.349 of ports/Mk/bsd.port.mk 261 * for more information. 262 * 263 * The epoch and revision are defined to be a single number, while the rest 264 * of the version should conform to the porting guidelines. It can contain 265 * multiple components, separated by a period, including letters. 266 * 267 * The tests allow for significantly more latitude in the version numbers 268 * than is allowed in the guidelines. No point in enforcing them here. 269 * That's what portlint is for. 270 * 271 * Jeremy D. Lea. 272 * reimplemented by Oliver Eikemeier 273 */ 274int 275version_cmp(const char *pkg1, const char *pkg2) 276{ 277 const char *v1, *v2, *ve1, *ve2; 278 unsigned long e1, e2, r1, r2; 279 int result = 0; 280 281 v1 = split_version(pkg1, &ve1, &e1, &r1); 282 v2 = split_version(pkg2, &ve2, &e2, &r2); 283 284 /* Check epoch, port version, and port revision, in that order. */ 285 if (e1 != e2) { 286 result = (e1 < e2 ? -1 : 1); 287 } 288 289 /* Shortcut check for equality before invoking the parsing routines. */ 290 if (result == 0 && (ve1 - v1 != ve2 - v2 || strncasecmp(v1, v2, ve1 - v1) != 0)) { 291 /* Loop over different components (the parts separated by dots). 292 * If any component differs, we have the basis for an inequality. */ 293 while(result == 0 && (v1 < ve1 || v2 < ve2)) { 294 int block_v1 = 0; 295 int block_v2 = 0; 296 version_component vc1 = {0, 0, 0}; 297 version_component vc2 = {0, 0, 0}; 298 if (v1 < ve1 && *v1 != '+') { 299 v1 = get_component(v1, &vc1); 300 } else { 301 block_v1 = 1; 302 } 303 if (v2 < ve2 && *v2 != '+') { 304 v2 = get_component(v2, &vc2); 305 } else { 306 block_v2 = 1; 307 } 308 if (block_v1 && block_v2) { 309 if (v1 < ve1) 310 v1++; 311 if (v2 < ve2) 312 v2++; 313 } else if (vc1.n != vc2.n) { 314 result = (vc1.n < vc2.n ? -1 : 1); 315 } else if (vc1.a != vc2.a) { 316 result = (vc1.a < vc2.a ? -1 : 1); 317 } else if (vc1.pl != vc2.pl) { 318 result = (vc1.pl < vc2.pl ? -1 : 1); 319 } 320 } 321 } 322 323 /* Compare FreeBSD revision numbers. */ 324 if (result == 0 && r1 != r2) { 325 result = (r1 < r2 ? -1 : 1); 326 } 327 return result; 328} 329