/* * updatedpackagesdialog.cc * * Copyright (c) 2002, 2003 Frerich Raabe * * 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 #include #include #include // 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