184750Ssobomax/* 2101677Sschweikh * FreeBSD install - a package for the installation and maintenance 384750Ssobomax * of non-core utilities. 484750Ssobomax * 584750Ssobomax * Redistribution and use in source and binary forms, with or without 684750Ssobomax * modification, are permitted provided that the following conditions 784750Ssobomax * are met: 884750Ssobomax * 1. Redistributions of source code must retain the above copyright 984750Ssobomax * notice, this list of conditions and the following disclaimer. 1084750Ssobomax * 2. Redistributions in binary form must reproduce the above copyright 1184750Ssobomax * notice, this list of conditions and the following disclaimer in the 1284750Ssobomax * documentation and/or other materials provided with the distribution. 1384750Ssobomax * 1484750Ssobomax * Maxim Sobolev 1584750Ssobomax * 31 July 2001 1684750Ssobomax * 1798766Smarkm */ 1898766Smarkm 1998766Smarkm#include <sys/cdefs.h> 2098766Smarkm__FBSDID("$FreeBSD$"); 2198766Smarkm 2298766Smarkm#include "lib.h" 2398766Smarkm#include <err.h> 2498766Smarkm 2598766Smarkm/* 2684750Ssobomax * Routines to assist with PLIST_FMT_VER numbers in the packing 2784750Ssobomax * lists. 2884750Ssobomax * 2984750Ssobomax * Following is the PLIST_FMT_VER history: 3084750Ssobomax * 1.0 - Initial revision; 3184750Ssobomax * 1.1 - When recording/checking checksum of symlink use hash of readlink() 32101677Sschweikh * value instead of the hash of an object this links points to. 3384750Ssobomax * 3484750Ssobomax */ 3584750Ssobomaxint 3684750Ssobomaxverscmp(Package *pkg, int major, int minor) 3784750Ssobomax{ 3884750Ssobomax int rval = 0; 3984750Ssobomax 4084750Ssobomax if ((pkg->fmtver_maj < major) || (pkg->fmtver_maj == major && 4184750Ssobomax pkg->fmtver_mnr < minor)) 4284750Ssobomax rval = -1; 4384750Ssobomax else if ((pkg->fmtver_maj > major) || (pkg->fmtver_maj == major && 4484750Ssobomax pkg->fmtver_mnr > minor)) 4584750Ssobomax rval = 1; 4684750Ssobomax 4784750Ssobomax return rval; 4884750Ssobomax} 4998766Smarkm 5098766Smarkm/* 51131274Seik * split_version(pkgname, endname, epoch, revision) returns a pointer to 52131274Seik * the version portion of a package name and the two special components. 5398766Smarkm * 54131274Seik * Syntax is: ${PORTNAME}-${PORTVERSION}[_${PORTREVISION}][,${PORTEPOCH}] 55131274Seik * 56131274Seik * Written by Oliver Eikemeier 57131274Seik * Based on work of Jeremy D. Lea. 5898766Smarkm */ 59131274Seikstatic const char * 60131274Seiksplit_version(const char *pkgname, const char **endname, unsigned long *epoch, unsigned long *revision) 6198766Smarkm{ 6298766Smarkm char *ch; 63131274Seik const char *versionstr; 64131274Seik const char *endversionstr; 6598766Smarkm 6698766Smarkm if (pkgname == NULL) 6798766Smarkm errx(2, "%s: Passed NULL pkgname.", __func__); 68131274Seik 69227458Seadler /* Look for the last '-' in the pkgname */ 70131274Seik ch = strrchr(pkgname, '-'); 71131274Seik /* Cheat if we are just passed a version, not a valid package name */ 72131274Seik versionstr = ch ? ch + 1 : pkgname; 73131274Seik 74131274Seik /* Look for the last '_' in the version string, advancing the end pointer */ 75131274Seik ch = strrchr(versionstr, '_'); 76131274Seik if (revision != NULL) { 77131274Seik *revision = ch ? strtoul(ch + 1, NULL, 10) : 0; 78131274Seik } 79131274Seik endversionstr = ch; 80131274Seik 81131274Seik /* Look for the last ',' in the remaining version string */ 82131274Seik ch = strrchr(endversionstr ? endversionstr + 1 : versionstr, ','); 8398766Smarkm if (epoch != NULL) { 84131274Seik *epoch = ch ? strtoul(ch + 1, NULL, 10) : 0; 8598766Smarkm } 86131274Seik if (ch && !endversionstr) 87131274Seik endversionstr = ch; 88131274Seik 89131274Seik /* set the pointer behind the last character of the version without revision or epoch */ 90131274Seik if (endname) 91131274Seik *endname = endversionstr ? endversionstr : strrchr(versionstr, '\0'); 92131274Seik 93131274Seik return versionstr; 94131274Seik} 95131274Seik 96131274Seik/* 97131274Seik * PORTVERSIONs are composed of components separated by dots. A component 98131274Seik * consists of a version number, a letter and a patchlevel number. This does 99131274Seik * not conform to the porter's handbook, but let us formulate rules that 100131274Seik * fit the current practice and are far simpler than to make decisions 101131274Seik * based on the order of netters and lumbers. Besides, people use versions 102131274Seik * like 10b2 in the ports... 103131274Seik */ 104131274Seik 105131274Seiktypedef struct { 106131274Seik#ifdef __LONG_LONG_SUPPORTED 107131274Seik long long n; 108131274Seik long long pl; 109131274Seik#else 110131274Seik long n; 111131274Seik long pl; 112131274Seik#endif 113131274Seik int a; 114131274Seik} version_component; 115131274Seik 116131274Seik/* 117131274Seik * get_component(position, component) gets the value of the next component 118131274Seik * (number - letter - number triple) and returns a pointer to the next character 119131274Seik * after any leading separators 120131274Seik * 121131274Seik * - components are separated by dots 122131274Seik * - characters !~ [a-zA-Z0-9.+*] are treated as separators 123131274Seik * (1.0:2003.09.16 = 1.0.2003.09.16), this may not be what you expect: 124131274Seik * 1.0.1:2003.09.16 < 1.0:2003.09.16 125131274Seik * - consecutive separators are collapsed (10..1 = 10.1) 126131274Seik * - missing separators are inserted, essentially 127131274Seik * letter number letter => letter number . letter (10a1b2 = 10a1.b2) 128131274Seik * - missing components are assumed to be equal to 0 (10 = 10.0 = 10.0.0) 129131274Seik * - the letter sort order is: [none], a, b, ..., z; numbers without letters 130131274Seik * sort first (10 < 10a < 10b) 131131274Seik * - missing version numbers (in components starting with a letter) sort as -1 132131274Seik * (a < 0, 10.a < 10) 133131274Seik * - a separator is inserted before the special strings "pl", "alpha", "beta", 134131274Seik * "pre" and "rc". 135131274Seik * - "pl" sorts before every other letter, "alpha", "beta", "pre" and "rc" 136131274Seik * sort as a, b, p and r. (10alpha = 10.a < 10, but 10 < 10a; pl11 < alpha3 137131274Seik * < 0.1beta2 = 0.1.b2 < 0.1) 138131274Seik * - other strings use only the first letter for sorting, case is ignored 139131274Seik * (1.d2 = 1.dev2 = 1.Development2) 140131274Seik * - The special component `*' is guaranteed to be the smallest possible 141131274Seik * component (2.* < 2pl1 < 2alpha3 < 2.9f7 < 3.*) 142131274Seik * - components separated by `+' are handled by version_cmp below 143131274Seik * 144131274Seik * Oliver Eikemeier 145131274Seik */ 146131274Seik 147131274Seikstatic const struct { 148131274Seik const char *name; 149131274Seik size_t namelen; 150131274Seik int value; 151131274Seik} stage[] = { 152131274Seik { "pl", 2, 0 }, 153131274Seik { "alpha", 5, 'a'-'a'+1 }, 154131274Seik { "beta", 4, 'b'-'a'+1 }, 155131274Seik { "pre", 3, 'p'-'a'+1 }, 156131274Seik { "rc", 2, 'r'-'a'+1 }, 157131274Seik { NULL, 0, -1 } 158131274Seik}; 159131274Seik 160131274Seikstatic const char * 161131274Seikget_component(const char *position, version_component *component) 162131274Seik{ 163131274Seik const char *pos = position; 164131274Seik int hasstage = 0, haspatchlevel = 0; 165131274Seik 166131274Seik if (!pos) 167131274Seik errx(2, "%s: Passed NULL position.", __func__); 168131274Seik 169131274Seik /* handle version number */ 170131274Seik if (isdigit(*pos)) { 171131274Seik char *endptr; 172131274Seik#ifdef __LONG_LONG_SUPPORTED 173131274Seik component->n = strtoll(pos, &endptr, 10); 174131274Seik#else 175131274Seik component->n = strtol(pos, &endptr, 10); 176131274Seik#endif 177131274Seik /* should we test for errno == ERANGE? */ 178131274Seik pos = endptr; 179131274Seik } else if (*pos == '*') { 180131274Seik component->n = -2; 181131274Seik do { 182131274Seik pos++; 183131274Seik } while(*pos && *pos != '+'); 184131274Seik } else { 185131274Seik component->n = -1; 186131274Seik hasstage = 1; 18798766Smarkm } 188131274Seik 189131274Seik /* handle letter */ 190131274Seik if (isalpha(*pos)) { 191131274Seik int c = tolower(*pos); 192131274Seik haspatchlevel = 1; 193131274Seik /* handle special suffixes */ 194131274Seik if (isalpha(pos[1])) { 195131274Seik int i; 196131274Seik for (i = 0; stage[i].name; i++) { 197131274Seik if (strncasecmp(pos, stage[i].name, stage[i].namelen) == 0 198131274Seik && !isalpha(pos[stage[i].namelen])) { 199131274Seik if (hasstage) { 200131274Seik /* stage to value */ 201131274Seik component->a = stage[i].value; 202131274Seik pos += stage[i].namelen; 203131274Seik } else { 204131274Seik /* insert dot */ 205131274Seik component->a = 0; 206131274Seik haspatchlevel = 0; 207131274Seik } 208131274Seik c = 0; 209131274Seik break; 210131274Seik } 211131274Seik } 212131274Seik } 213131274Seik /* unhandled above */ 214131274Seik if (c) { 215131274Seik /* use the first letter and skip following */ 216131274Seik component->a = c - 'a' + 1; 217131274Seik do { 218131274Seik ++pos; 219131274Seik } while (isalpha(*pos)); 220131274Seik } 221131274Seik } else { 222131274Seik component->a = 0; 223131274Seik haspatchlevel = 0; 224131274Seik } 225131274Seik 226131274Seik if (haspatchlevel) { 227131274Seik /* handle patch number */ 228131274Seik if (isdigit(*pos)) { 229131274Seik char *endptr; 230131274Seik#ifdef __LONG_LONG_SUPPORTED 231131274Seik component->pl = strtoll(pos, &endptr, 10); 232131274Seik#else 233131274Seik component->pl = strtol(pos, &endptr, 10); 234131274Seik#endif 235131274Seik /* should we test for errno == ERANGE? */ 236131274Seik pos = endptr; 237131274Seik } else { 238131274Seik component->pl = -1; 239131274Seik } 240131274Seik } else { 241131274Seik component->pl = 0; 242131274Seik } 243131274Seik 244131274Seik /* skip trailing separators */ 245131274Seik while (*pos && !isdigit(*pos) && !isalpha(*pos) && *pos != '+' && *pos != '*') { 246131274Seik pos++; 247131274Seik } 248131274Seik 249131274Seik return pos; 25098766Smarkm} 25198766Smarkm 25298766Smarkm/* 25398766Smarkm * version_cmp(pkg1, pkg2) returns -1, 0 or 1 depending on if the version 25498766Smarkm * components of pkg1 is less than, equal to or greater than pkg2. No 255101677Sschweikh * comparison of the basenames is done. 25698766Smarkm * 257101677Sschweikh * The port version is defined by: 25898766Smarkm * ${PORTVERSION}[_${PORTREVISION}][,${PORTEPOCH}] 259101677Sschweikh * ${PORTEPOCH} supersedes ${PORTVERSION} supersedes ${PORTREVISION}. 26098766Smarkm * See the commit log for revision 1.349 of ports/Mk/bsd.port.mk 26198766Smarkm * for more information. 26298766Smarkm * 26398766Smarkm * The epoch and revision are defined to be a single number, while the rest 26498766Smarkm * of the version should conform to the porting guidelines. It can contain 265101677Sschweikh * multiple components, separated by a period, including letters. 26698766Smarkm * 267131274Seik * The tests allow for significantly more latitude in the version numbers 268131274Seik * than is allowed in the guidelines. No point in enforcing them here. 269131274Seik * That's what portlint is for. 27098766Smarkm * 27198766Smarkm * Jeremy D. Lea. 272131274Seik * reimplemented by Oliver Eikemeier 27398766Smarkm */ 27498766Smarkmint 27598766Smarkmversion_cmp(const char *pkg1, const char *pkg2) 27698766Smarkm{ 277131274Seik const char *v1, *v2, *ve1, *ve2; 278131274Seik unsigned long e1, e2, r1, r2; 279131274Seik int result = 0; 28098766Smarkm 281131274Seik v1 = split_version(pkg1, &ve1, &e1, &r1); 282131274Seik v2 = split_version(pkg2, &ve2, &e2, &r2); 283131274Seik 284131274Seik /* Check epoch, port version, and port revision, in that order. */ 285131274Seik if (e1 != e2) { 286131274Seik result = (e1 < e2 ? -1 : 1); 287131274Seik } 288131274Seik 289131274Seik /* Shortcut check for equality before invoking the parsing routines. */ 290131274Seik if (result == 0 && (ve1 - v1 != ve2 - v2 || strncasecmp(v1, v2, ve1 - v1) != 0)) { 291131274Seik /* Loop over different components (the parts separated by dots). 292131274Seik * If any component differs, we have the basis for an inequality. */ 293131274Seik while(result == 0 && (v1 < ve1 || v2 < ve2)) { 294131274Seik int block_v1 = 0; 295131274Seik int block_v2 = 0; 296131274Seik version_component vc1 = {0, 0, 0}; 297131274Seik version_component vc2 = {0, 0, 0}; 298131274Seik if (v1 < ve1 && *v1 != '+') { 299131274Seik v1 = get_component(v1, &vc1); 30098766Smarkm } else { 301131274Seik block_v1 = 1; 30298766Smarkm } 303131274Seik if (v2 < ve2 && *v2 != '+') { 304131274Seik v2 = get_component(v2, &vc2); 305131274Seik } else { 306131274Seik block_v2 = 1; 307131274Seik } 308131274Seik if (block_v1 && block_v2) { 309131274Seik if (v1 < ve1) 310131274Seik v1++; 311131274Seik if (v2 < ve2) 312131274Seik v2++; 313131274Seik } else if (vc1.n != vc2.n) { 314131274Seik result = (vc1.n < vc2.n ? -1 : 1); 315131274Seik } else if (vc1.a != vc2.a) { 316131274Seik result = (vc1.a < vc2.a ? -1 : 1); 317131274Seik } else if (vc1.pl != vc2.pl) { 318131274Seik result = (vc1.pl < vc2.pl ? -1 : 1); 319131274Seik } 32098766Smarkm } 32198766Smarkm } 322131274Seik 323131274Seik /* Compare FreeBSD revision numbers. */ 324131274Seik if (result == 0 && r1 != r2) { 325131274Seik result = (r1 < r2 ? -1 : 1); 326131274Seik } 327131274Seik return result; 32898766Smarkm} 329