Use string version comparator code from FreeBSD.

This fixes all cases that previous code has with no changes...
sorry pancake.

--HG--
extra : convert_revision : 993d0aa060daf36d1b5ffa63725002dda122c091
This commit is contained in:
Juan RP 2009-02-17 03:49:02 +01:00
parent fb998feacf
commit fda3da1543
4 changed files with 295 additions and 70 deletions

View File

@ -9,21 +9,13 @@
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
if (argc<3) { if (argc < 3) {
printf("Usage: xbps-cmpver [old] [new]\n"); printf("Usage: xbps-cmpver [installed] [required]\n");
printf(" xbpks-cmpver foo-1.2 foo-2.2 # $? = 1\n"); printf(" xbps-cmpver foo-1.2 foo-2.2 # $? = 1\n");
printf(" xbpks-cmpver foo-1.2 foo-1.2 # $? = 0\n"); printf(" xbps-cmpver foo-1.2 foo-1.1.0 # $? = 0\n");
printf(" xbps-cmpver foo-1.2 foo-1.2 # $? = 0\n");
return 1; return 1;
} }
#if UNIT_TEST return xbps_cmpver_packages(argv[1], argv[2]);
printf("1.2 2.2 = %d\n", chkver("1.2", "2.2"));
printf("1.0 10.3 = %d\n", chkver("1.0", "10.3"));
printf("1.0 1.0 = %d\n", chkver("1.0", "1.0"));
printf("1.0 1.2 = %d\n", chkver("1.0", "1.2"));
printf("1.0.1 1.0.2 = %d\n", chkver("1.0.1", "1.0.2"));
printf("1.0beta 1.0rc1 = %d\n", chkver("1.0beta", "1.0rc1"));
#endif
return (xbps_cmpver_packages(argv[1], argv[2]) > 0)?1:0;
} }

View File

@ -10,10 +10,6 @@ xbps-src:
automatically add triggers, shared by src/bin pkgs. [IN PROGRESS] automatically add triggers, shared by src/bin pkgs. [IN PROGRESS]
* Fix the "automatic-install" object in regpkgdb.plist. * Fix the "automatic-install" object in regpkgdb.plist.
xbps-cmpver:
* It fails in some cases: foo-2.0 vs foo-1.99 and foo-2a vs foo-2b.
Another case that fails: 1.7.0 vs 1.6.[56789].
Packages: Packages:
* fix mpfr hardcoded rpath for gmp. * fix mpfr hardcoded rpath for gmp.

View File

@ -1,66 +1,303 @@
/* /*
* Compare package and version strings * FreeBSD install - a package for the installation and maintenance
* @ 2008 * of non-core utilities.
* Author: pancake <youterm.com> *
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* Maxim Sobolev
* 31 July 2001
*
*/ */
#include <stdio.h> #include <stdio.h>
#include <stdlib.h>
#include <string.h> #include <string.h>
#include <ctype.h>
#include <xbps_api.h> #include <xbps_api.h>
static int chkchr(const char *ch) /*
* split_version(pkgname, endname, epoch, revision) returns a pointer to
* the version portion of a package name and the two special components.
*
* Syntax is: ${PORTNAME}-${PORTVERSION}[_${PORTREVISION}][,${PORTEPOCH}]
*
* Written by Oliver Eikemeier
* Based on work of Jeremy D. Lea.
*/
static const char *
split_version(const char *pkgname, const char **endname, unsigned long *epoch,
unsigned long *revision)
{ {
if (*ch>='0' && *ch<='9') char *ch;
return *ch-'0'; const char *versionstr;
if (ch[1]=='\0') { const char *endversionstr;
if (*ch>='a'&&*ch<='z'){
return *ch-'a'; assert(pkgname != NULL);
}
} /* Look for the last '-' the the pkgname */
switch(*ch) { ch = strrchr(pkgname, '-');
case 'a': if (ch[1]=='l') /* Cheat if we are just passed a version, not a valid package name */
return 0xa; versionstr = ch ? ch + 1 : pkgname;
return -1;
case 'b': return 0xb; /* Look for the last '_' in the version string, advancing the end pointer */
case 'r': return 0xc; ch = strrchr(versionstr, '_');
} if (revision != NULL) {
return -1; *revision = ch ? strtoul(ch + 1, NULL, 10) : 0;
}
endversionstr = ch;
/* Look for the last ',' in the remaining version string */
ch = strrchr(endversionstr ? endversionstr + 1 : versionstr, ',');
if (epoch != NULL) {
*epoch = ch ? strtoul(ch + 1, NULL, 10) : 0;
}
if (ch && !endversionstr)
endversionstr = ch;
/*
* set the pointer behind the last character of the version without
* revision or epoch
*/
if (endname)
*endname = endversionstr ? endversionstr : strrchr(versionstr, '\0');
return versionstr;
} }
static int ver2int(const char *a0, int *pow, int mpow) /*
* PORTVERSIONs are composed of components separated by dots. A component
* consists of a version number, a letter and a patchlevel number. This does
* not conform to the porter's handbook, but let us formulate rules that
* fit the current practice and are far simpler than to make decisions
* based on the order of netters and lumbers. Besides, people use versions
* like 10b2 in the ports...
*/
typedef struct {
long n;
long pl;
int a;
} version_component;
/*
* get_component(position, component) gets the value of the next component
* (number - letter - number triple) and returns a pointer to the next character
* after any leading separators
*
* - components are separated by dots
* - characters !~ [a-zA-Z0-9.+*] are treated as separators
* (1.0:2003.09.16 = 1.0.2003.09.16), this may not be what you expect:
* 1.0.1:2003.09.16 < 1.0:2003.09.16
* - consecutive separators are collapsed (10..1 = 10.1)
* - missing separators are inserted, essentially
* letter number letter => letter number . letter (10a1b2 = 10a1.b2)
* - missing components are assumed to be equal to 0 (10 = 10.0 = 10.0.0)
* - the letter sort order is: [none], a, b, ..., z; numbers without letters
* sort first (10 < 10a < 10b)
* - missing version numbers (in components starting with a letter) sort as -1
* (a < 0, 10.a < 10)
* - a separator is inserted before the special strings "pl", "alpha", "beta",
* "pre" and "rc".
* - "pl" sorts before every other letter, "alpha", "beta", "pre" and "rc"
* sort as a, b, p and r. (10alpha = 10.a < 10, but 10 < 10a; pl11 < alpha3
* < 0.1beta2 = 0.1.b2 < 0.1)
* - other strings use only the first letter for sorting, case is ignored
* (1.d2 = 1.dev2 = 1.Development2)
* - The special component `*' is guaranteed to be the smallest possible
* component (2.* < 2pl1 < 2alpha3 < 2.9f7 < 3.*)
* - components separated by `+' are handled by version_cmp below
*
* Oliver Eikemeier
*/
static const struct {
const char *name;
size_t namelen;
int value;
} stage[] = {
{ "pl", 2, 0 },
{ "alpha", 5, 'a'-'a'+1 },
{ "beta", 4, 'b'-'a'+1 },
{ "pre", 3, 'p'-'a'+1 },
{ "rc", 2, 'r'-'a'+1 },
{ NULL, 0, -1 }
};
static const char *
get_component(const char *position, version_component *component)
{ {
int r,ret = 0; const char *pos = position;
int pos = 0; int hasstage = 0, haspatchlevel = 0;
const char *a = a0+strlen(a0)-1;
for(*pow=0;a>=a0;a=a-1) { assert(pos != NULL);
if (*a=='.') {
*pow=*pow+1; /* handle version number */
} else { if (isdigit((unsigned char)*pos)) {
r = chkchr(a); char *endptr;
if (r != -1) component->n = strtol(pos, &endptr, 10);
ret+=((r+1)*((*pow)+1))<<pos++; /* should we test for errno == ERANGE? */
pos = endptr;
} else if (*pos == '*') {
component->n = -2;
do {
pos++;
} while(*pos && *pos != '+');
} else {
component->n = -1;
hasstage = 1;
}
/* handle letter */
if (isalpha((unsigned char)*pos)) {
int c = tolower((unsigned char)*pos);
haspatchlevel = 1;
/* handle special suffixes */
if (isalpha((unsigned char)pos[1])) {
int i;
for (i = 0; stage[i].name; i++) {
if (strncasecmp(pos, stage[i].name, stage[i].namelen) == 0
&& !isalpha((unsigned char)pos[stage[i].namelen])) {
if (hasstage) {
/* stage to value */
component->a = stage[i].value;
pos += stage[i].namelen;
} else {
/* insert dot */
component->a = 0;
haspatchlevel = 0;
}
c = 0;
break;
} }
if (mpow>0 && *pow > mpow) }
break;
} }
return ret; /* unhandled above */
if (c) {
/* use the first letter and skip following */
component->a = c - 'a' + 1;
do {
++pos;
} while (isalpha((unsigned char)*pos));
}
} else {
component->a = 0;
haspatchlevel = 0;
}
if (haspatchlevel) {
/* handle patch number */
if (isdigit((unsigned char)*pos)) {
char *endptr;
component->pl = strtol(pos, &endptr, 10);
/* should we test for errno == ERANGE? */
pos = endptr;
} else {
component->pl = -1;
}
} else {
component->pl = 0;
}
/* skip trailing separators */
while (*pos && !isdigit((unsigned char)*pos) &&
!isalpha((unsigned char)*pos) &&
*pos != '+' && *pos != '*') {
pos++;
}
return pos;
}
/*
* version_cmp(pkg1, pkg2) returns -1, 0 or 1 depending on if the version
* components of pkg1 is less than, equal to or greater than pkg2. No
* comparison of the basenames is done.
*
* The port version is defined by:
* ${PORTVERSION}[_${PORTREVISION}][,${PORTEPOCH}]
* ${PORTEPOCH} supersedes ${PORTVERSION} supersedes ${PORTREVISION}.
* See the commit log for revision 1.349 of ports/Mk/bsd.port.mk
* for more information.
*
* The epoch and revision are defined to be a single number, while the rest
* of the version should conform to the porting guidelines. It can contain
* multiple components, separated by a period, including letters.
*
* The tests allow for significantly more latitude in the version numbers
* than is allowed in the guidelines. No point in enforcing them here.
* That's what portlint is for.
*
* Jeremy D. Lea.
* reimplemented by Oliver Eikemeier
*/
int
xbps_cmpver_packages(const char *pkg1, const char *pkg2)
{
const char *v1, *v2, *ve1, *ve2;
unsigned long e1, e2, r1, r2;
int result = 0;
v1 = split_version(pkg1, &ve1, &e1, &r1);
v2 = split_version(pkg2, &ve2, &e2, &r2);
/* Check epoch, port version, and port revision, in that order. */
if (e1 != e2) {
result = (e1 < e2 ? -1 : 1);
}
/* Shortcut check for equality before invoking the parsing routines. */
if (result == 0 && (ve1 - v1 != ve2 - v2 || strncasecmp(v1, v2, ve1 - v1) != 0)) {
/* Loop over different components (the parts separated by dots).
* If any component differs, we have the basis for an inequality. */
while(result == 0 && (v1 < ve1 || v2 < ve2)) {
int block_v1 = 0;
int block_v2 = 0;
version_component vc1 = {0, 0, 0};
version_component vc2 = {0, 0, 0};
if (v1 < ve1 && *v1 != '+') {
v1 = get_component(v1, &vc1);
} else {
block_v1 = 1;
}
if (v2 < ve2 && *v2 != '+') {
v2 = get_component(v2, &vc2);
} else {
block_v2 = 1;
}
if (block_v1 && block_v2) {
if (v1 < ve1)
v1++;
if (v2 < ve2)
v2++;
} else if (vc1.n != vc2.n) {
result = (vc1.n < vc2.n ? -1 : 1);
} else if (vc1.a != vc2.a) {
result = (vc1.a < vc2.a ? -1 : 1);
} else if (vc1.pl != vc2.pl) {
result = (vc1.pl < vc2.pl ? -1 : 1);
}
}
}
/* Compare FreeBSD revision numbers. */
if (result == 0 && r1 != r2) {
result = (r1 < r2 ? -1 : 1);
}
return result == -1 ? 1 : 0;
} }
int int
xbps_cmpver_versions(const char *a0, const char *a1) xbps_cmpver_versions(const char *inst, const char *req)
{ {
int p0,p1; int res = xbps_cmpver_packages(inst, req);
int v0 = ver2int(a0, &p0, 0);
int v1 = ver2int(a1, &p1, p0); return res == -1 ? 1 : 0;
return v1-v0;
}
int
xbps_cmpver_packages(const char *a0, const char *b0)
{
char *a = strrchr(a0, '-');
char *b = strrchr(b0, '-');
assert(a != NULL || b != NULL);
return xbps_cmpver_versions(a+1, b+1);
} }

View File

@ -48,7 +48,7 @@ xbps_check_is_installed_pkg(const char *pkg)
plist = xbps_append_full_path(true, NULL, XBPS_REGPKGDB); plist = xbps_append_full_path(true, NULL, XBPS_REGPKGDB);
if (plist == NULL) if (plist == NULL)
return EINVAL; return -1;
pkgname = xbps_get_pkg_name(pkg); pkgname = xbps_get_pkg_name(pkg);
reqver = xbps_get_pkg_version(pkg); reqver = xbps_get_pkg_version(pkg);
@ -77,7 +77,7 @@ xbps_check_is_installed_pkg(const char *pkg)
assert(instver != NULL); assert(instver != NULL);
/* Compare installed and required version. */ /* Compare installed and required version. */
rv = xbps_cmpver_versions(instver, reqver) > 0 ? 1 : 0; rv = xbps_cmpver_versions(instver, reqver);
free(pkgname); free(pkgname);
free(plist); free(plist);