/*
* updatedpackagesdialog.cc
*
* Copyright (c) 2002, 2003 Frerich Raabe <raabe@kde.org>
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. For licensing and distribution details, check the
* accompanying file 'COPYING'.
*/
#include "portlistview.h"
#include "portmgr.h"
#include "updatedpackagesdialog.h"
#include <klocale.h>
#include <ctype.h>
#include <err.h>
#include <stdlib.h>
// The following two C functions for comparing version numbers have been
// taken from a FreeBSD 5.0-p7 system's
// /usr/src/usr.sbin/pkg_install/lib/version.c file.
namespace {
/*
* version_of(pkgname, epoch, revision) returns a pointer to the version
* portion of a package name and the two special components.
*
* Jeremy D. Lea.
*/
const char *
version_of(const char *pkgname, int *epoch, int *revision)
{
char *ch;
if (pkgname == NULL)
errx(2, "%s: Passed NULL pkgname.", __func__);
if (epoch != NULL) {
if ((ch = strrchr(pkgname, ',')) == NULL)
*epoch = 0;
else
*epoch = atoi(&ch[1]);
}
if (revision != NULL) {
if ((ch = strrchr(pkgname, '_')) == NULL)
*revision = 0;
else
*revision = atoi(&ch[1]);
}
/* Cheat if we are just passed a version, not a valid package name */
if ((ch = strrchr(pkgname, '-')) == NULL)
return pkgname;
else
return &ch[1];
}
/*
* 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 below allow for significantly more latitude in the version
* numbers than is allowed in the guidelines. No point in wasting user's
* time enforcing them here. That's what flamewars are for.
*
* Jeremy D. Lea.
*/
int
version_cmp(const char *pkg1, const char *pkg2)
{
const char *c1, *c2, *v1, *v2;
char *t1, *t2;
int e1, e2, r1, r2, n1, n2;
v1 = version_of(pkg1, &e1, &r1);
v2 = version_of(pkg2, &e2, &r2);
/* Minor optimisation. */
if (strcmp(v1, v2) == 0)
return 0;
/* First compare epoch. */
if (e1 != e2)
return (e1 < e2 ? -1 : 1);
else {
/*
* We walk down the versions, trying to convert to numbers.
* We terminate when we reach an underscore, a comma or the
* string terminator, thanks to a nasty trick with strchr().
* strtol() conveniently gobbles up the chars it converts.
*/
c1 = strchr("_,", v1[0]);
c2 = strchr("_,", v2[0]);
while (c1 == NULL && c2 == NULL) {
n1 = strtol(v1, &t1, 10);
n2 = strtol(v2, &t2, 10);
if (n1 != n2)
return (n1 < n2 ? -1 : 1);
/*
* The numbers are equal, check for letters. Assume they're
* letters purely because strtol() didn't chomp them.
*/
c1 = strchr("_,.", t1[0]);
c2 = strchr("_,.", t2[0]);
if (c1 == NULL && c2 == NULL) {
/* Both have letters. Compare them. */
if (t1[0] != t2[0])
return (t1[0] < t2[0] ? -1 : 1);
/* Boring. The letters are equal. Carry on. */
v1 = &t1[1], v2 = &t2[1];
} else if (c1 == NULL) {
/*
* Letters are strange. After a number, a letter counts
* as greater, but after a period it's less.
*/
return (isdigit(v1[0]) ? 1 : -1);
} else if (c2 == NULL) {
return (isdigit(v2[0]) ? -1 : 1);
} else {
/* Neither were letters. Advance over the period. */
v1 = (t1[0] == '.' ? &t1[1] : t1);
v2 = (t2[0] == '.' ? &t2[1] : t2);
}
c1 = strchr("_,", v1[0]);
c2 = strchr("_,", v2[0]);
}
/* If we got here, check if one version has something left. */
if (c1 == NULL)
return (isdigit(v1[0]) ? 1 : -1);
if (c2 == NULL)
return (isdigit(v2[0]) ? -1 : 1);
/* We've run out of version. Try the revision... */
if (r1 != r2)
return (r1 < r2 ? -1 : 1);
else
return 0;
}
}
} // Anonymous namespace
using namespace Barry;
UpdatedPackagesProgressDialog::UpdatedPackagesProgressDialog(
QWidget *parent, const char *name )
: ActionProgressDialog( parent, name )
{
addColumn( i18n( "Name" ) );
addColumn( i18n( "Current" ) );
addColumn( i18n( "Installed" ) );
search( PortMgr::self().installedPorts() );
}
void UpdatedPackagesProgressDialog::processPort( const Port &port )
{
const QString pkgScheme = port.name() + QString::fromLatin1( "-" );
const QString pkg1 = pkgScheme + port.installedVersion();
const QString pkg2 = pkgScheme + port.version();
if ( ::version_cmp( pkg1.latin1(), pkg2.latin1() ) == -1 )
enlistPort( port );
}
void UpdatedPackagesProgressDialog::setupItem( PortItem *item )
{
item->setText( 2, item->port().installedVersion() );
}
#include "updatedpackagesdialog.moc"
// vim:ts=4:sw=4:noet:list
syntax highlighted by Code2HTML, v. 0.9.1