/*
* portmgr.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 "databasewrapper.h"
#include "port.h"
#include "portmgr.h"
#include "process.h"
#include <kapplication.h>
#include <kconfig.h>
#include <kdebug.h>
#include <klocale.h>
#include <kmessagebox.h>
#include <knotifyclient.h>
#include <qdir.h>
#include <qregexp.h>
#include <stdlib.h> // for getenv()
// This would be nice in Qt
template <class Container>
Container qUnique( const Container &container )
{
Container c;
typename Container::ConstIterator it = container.begin();
typename Container::ConstIterator end = container.end();
for ( ; it != end; ++it )
if ( c.find( *it ) == c.end() )
c << *it;
return c;
}
using namespace Barry;
PortMgr *PortMgr::m_instance = 0;
PortMgr &PortMgr::self()
{
if ( !m_instance )
m_instance = new PortMgr;
return *m_instance;
}
PortMgr::PortMgr()
: m_portDb( 0 ),
m_pkgDb( 0 )
{
reparseConfig();
}
PortMgr::~PortMgr()
{
delete m_portDb;
delete m_pkgDb;
}
void PortMgr::reparseConfig()
{
KConfig *cfg = kapp->config();
cfg->setGroup( "General" );
#ifdef __NetBSD__
m_portRootDir = QString::fromLatin1( "/usr/opt" );
#else
m_portRootDir = QString::fromLatin1( "/usr/ports" );
#endif
QString portDirDefault = QString::fromLatin1( ::getenv( "PORTSDIR" ) );
if ( portDirDefault.isEmpty() )
portDirDefault = getMkVariable( QString::fromLatin1( "PORTSDIR" ) );
if ( portDirDefault.isEmpty() )
portDirDefault = m_portRootDir;
m_portRootDir = cfg->readEntry( QString::fromLatin1( "Port directory" ), portDirDefault ).stripWhiteSpace();
QString pkgRootDirDefault = QString::fromLatin1( ::getenv( "PKG_DBDIR" ) );
if ( pkgRootDirDefault.isEmpty() )
pkgRootDirDefault = getMkVariable( QString::fromLatin1( "PKG_DBDIR" ) );
if ( pkgRootDirDefault.isEmpty() )
pkgRootDirDefault = QString::fromLatin1( "/var/db/pkg" );
m_pkgRootDir = cfg->readEntry( QString::fromLatin1( "Package directory" ), pkgRootDirDefault).stripWhiteSpace();
}
bool PortMgr::load()
{
checkDatabase();
return loadPortDb() && loadPkgDb();
}
bool PortMgr::loadPortDb()
{
QString portDbFile = m_portRootDir + QString::fromLatin1( "/INDEX.db" );
if ( !QFile::exists( portDbFile ) ) {
KNotifyClient::event( KNotifyClient::fatalError,
i18n( "<qt>The directory %1 seems to be invalid, "
"failed to locate the port database.<br/>"
"<br/>"
"Please make sure that you installed the 'ports' "
"distribution and that the directory %1 was mounted "
"properly, if necessary.<br/>"
"<br/>"
"Also, make sure that the file %1/INDEX.db exists; if "
"it doesn't, run <tt>portsdb -u</tt> as root to generate "
"it.</qt>")
.arg( m_portRootDir ).arg( m_portRootDir ).arg( m_portRootDir ) );
return false;
}
delete m_portDb;
m_portDb = new DatabaseWrapper( portDbFile );
if ( !m_portDb->open() ) {
KNotifyClient::event( KNotifyClient::fatalError,
i18n( "<qt>Failed to load the port database %1!<br/>"
"<br/>"
"Did you specify an invalid port directory by any "
"chance?</qt" ).arg( portDbFile ) );
return false;
}
return true;
}
bool PortMgr::loadPkgDb()
{
QString pkgDbFile = m_pkgRootDir + QString::fromLatin1( "/pkgdb.db" );
if ( !QFile::exists( pkgDbFile ) ) {
KNotifyClient::event( KNotifyClient::fatalError,
i18n( "<qt>The directory %1 seems to be invalid, "
"failed to locate the package database.<br/>"
"<br/>"
"Please make sure that the directory %1 was mounted "
"properly, if necessary.</qt>"
"<br/>"
"Also, make sure that the file %1/pkgdb.db exists; if "
"it doesn't, run <tt>pkgdb -u</tt> as root to generate "
"it.</qt>")
.arg( m_pkgRootDir ).arg( m_pkgRootDir ).arg( m_pkgRootDir ) );
return false;
}
delete m_pkgDb;
m_pkgDb = new DatabaseWrapper( pkgDbFile );
if ( !m_pkgDb->open() ) {
KNotifyClient::event( KNotifyClient::fatalError,
i18n( "<qt>Failed to load the package database %1!<br/>"
"<br/>"
"Did you specify an invalid package directory by any "
"chance?</qt" ).arg( pkgDbFile ) );
return false;
}
return true;
}
QStringList PortMgr::prefixes() const
{
QStringList keys;
keys << QString::fromLatin1( "LOCALBASE" );
keys << QString::fromLatin1( "X11BASE" );
keys << QString::fromLatin1( "LINUXBASE" );
return getMkVariables( keys );
}
QStringList PortMgr::categories() const
{
if ( !m_portDb )
return QStringList();
QString cat = m_portDb->getEntry( QString::fromLatin1( ":categories" ) );
cat += QString::fromLatin1( " " );
cat += m_portDb->getEntry( QString::fromLatin1( ":virtual_categories" ) );
return QStringList::split( ' ', cat );
}
PortList PortMgr::ports( const QString &category ) const
{
if ( !m_portDb )
return PortList();
PortList ports;
const QString origins = m_portDb->getEntry( QString::fromLatin1( "?" ) + category );
const QStringList tokens = QStringList::split( ' ', origins );
QStringList::ConstIterator it = tokens.begin();
QStringList::ConstIterator end = tokens.end();
for ( ; it != end; ++it )
ports += Port( *it );
// Sort out duplicate entries; there is a bug in the way the INDEX file
// gets built :-(
return qUnique( ports );
}
PortList PortMgr::allPorts() const
{
if ( m_allPorts.isEmpty() )
determineAllPorts();
return m_allPorts;
}
void PortMgr::determineAllPorts() const
{
if ( !m_portDb )
return;
const QString origins = m_portDb->getEntry( QString::fromLatin1( ":origins" ) );
const QStringList tokens = QStringList::split( ' ', origins );
QStringList::ConstIterator it = tokens.begin();
QStringList::ConstIterator end = tokens.end();
for ( ; it != end; ++it )
m_allPorts += Port( *it );
}
PortList PortMgr::installedPorts() const
{
if ( m_installedPorts.isEmpty() )
determineInstalledPorts();
return m_installedPorts;
}
void PortMgr::determineInstalledPorts() const
{
if ( !m_pkgDb )
return;
const QString origins = m_pkgDb->getEntry( QString::fromLatin1( ":origins" ) );
const QStringList entries = QStringList::split( ' ', origins );
QStringList::ConstIterator it = entries.begin();
QStringList::ConstIterator end = entries.end();
for ( ; it != end; ++it ) {
const QString pkgName = m_pkgDb->getEntry( QString::fromLatin1( "?" ) + *it );
m_installedPorts += Port::fromPkgName( *it, pkgName );
}
}
QDateTime PortMgr::portDatabaseTimestamp() const
{
const QFileInfo fi( m_portRootDir + QString::fromLatin1( "/INDEX.db" ) );
return fi.lastModified();
}
QDateTime PortMgr::packageDatabaseTimestamp() const
{
const QFileInfo fi( m_pkgRootDir + QString::fromLatin1( "/pkgdb.db" ) );
return fi.lastModified();
}
QString PortMgr::getMkVariable( const QString &key ) const
{
const QString cmd = QString::fromLatin1( "make -f%1/Mk/bsd.port.mk -V%2" )
.arg( m_portRootDir ).arg( key );
return Process::execute( cmd, Process::Stdout );
}
QStringList PortMgr::getMkVariables( const QStringList &keys ) const
{
QString cmd = QString::fromLatin1( "make -f%1/Mk/bsd.port.mk" )
.arg( m_portRootDir );
QStringList::ConstIterator it = keys.begin();
QStringList::ConstIterator end = keys.end();
for ( ; it != end; ++it )
cmd += QString::fromLatin1( " -V%1" ).arg( *it );
const QString output = Process::execute( cmd, Process::Stdout );
return QStringList::split( '\n', output );
}
void PortMgr::checkDatabase() const
{
KConfig *cfg = kapp->config();
cfg->setGroup( QString::fromLatin1( "General" ) );
const QString supfileName = cfg->readEntry( QString::fromLatin1( "CVSup supfile" ) );
QFile supfile( supfileName );
if ( !supfile.open( IO_ReadOnly ) ) {
kdWarning() << "Couldn't open supfile " << supfileName << endl;
return;
}
const QRegExp rx = QString::fromLatin1( "^\\*default\\s+base=" );
QString line;
QTextStream stream( &supfile );
while ( !stream.atEnd() && line.find( rx ) == -1 )
line = stream.readLine();
if ( line.find( rx ) == -1 )
line = QString::fromLatin1( "*default base=/usr/local/etc/cvsup" );
line = line.mid( line.find( '=' ) + 1 ).stripWhiteSpace();
const QString checkoutsFileName = line + QString::fromLatin1( "/sup/ports-all/checkouts.cvs:." );
if ( !QFile::exists( checkoutsFileName ) ) {
kdWarning() << "Could not check checkouts file " << checkoutsFileName << endl;
return;
}
const bool portDbOutdated = PortMgr::self().portDatabaseTimestamp() < QFileInfo( checkoutsFileName ).lastModified();
if ( portDbOutdated ) {
const QString errorMsg =
i18n( "Your port database is not up to date, this means that "
"Barry cannot function properly. It is strongly advised "
"that you quit Barry and update your port database." );
const QString detailedErrorMsg =
i18n( "<qt>Barry uses the database file %1 for a lot of features "
"it provides and detected that this file is not up to date, "
"which means that you updated your ports tree in %2 "
"without running <tt>portsdb -u</tt> afterwards.<br/>"
"<br />"
"You can resolve this by exiting Barry and running (as "
"root) the command <tt>portsdb -u</tt> now. After that, "
"re-start Barry.<br />"
"<br />"
"If you do not update your database, the data shown by "
"Barry (such as the list of available ports, the version "
"numbers, and much more) does not reflect the actual "
"situation on your system!" )
.arg( m_portRootDir + QString::fromLatin1( "/INDEX.db" ) )
.arg( m_portRootDir );
KMessageBox::detailedError( 0, errorMsg, detailedErrorMsg,
i18n( "Database outdated" ) );
}
}
#include "portmgr.moc"
// vim:ts=4:sw=4:noet:list
syntax highlighted by Code2HTML, v. 0.9.1