mirror of
https://bitbucket.org/smil3y/kdelibs.git
synced 2025-02-23 18:32:49 +00:00

automatic sycoca database was disabled while working on
70f9b2f953
for testing purpose
Signed-off-by: Ivailo Monev <xakepa10@gmail.com>
505 lines
14 KiB
C++
505 lines
14 KiB
C++
/* This file is part of the KDE libraries
|
|
* Copyright (C) 1999-2000 Waldo Bastian <bastian@kde.org>
|
|
* Copyright (C) 2005-2009 David Faure <faure@kde.org>
|
|
* Copyright (C) 2008 Hamish Rodda <rodda@kde.org>
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Library General Public
|
|
* License version 2 as published by the Free Software Foundation;
|
|
*
|
|
* This library 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. See the GNU
|
|
* Library General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Library General Public License
|
|
* along with this library; see the file COPYING.LIB. If not, write to
|
|
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
|
|
* Boston, MA 02110-1301, USA.
|
|
**/
|
|
|
|
#include "ksycoca.h"
|
|
#include "ksycoca_p.h"
|
|
#include "ksycocatype.h"
|
|
#include "ksycocafactory.h"
|
|
#include "kglobal.h"
|
|
#include "kde_file.h"
|
|
#include "kconfiggroup.h"
|
|
#include "ksharedconfig.h"
|
|
#include "kdebug.h"
|
|
#include "kstandarddirs.h"
|
|
|
|
#include <QtCore/QDataStream>
|
|
#include <QtCore/QCoreApplication>
|
|
#include <QtCore/QFile>
|
|
#include <QtCore/QBuffer>
|
|
#include <QProcess>
|
|
#include <QtDBus/QtDBus>
|
|
#include <QtCore/qthread.h>
|
|
|
|
#include <config.h>
|
|
|
|
#include <stdlib.h>
|
|
#include <fcntl.h>
|
|
|
|
#include "ksycocadevices_p.h"
|
|
|
|
static bool s_autoRebuild = true;
|
|
|
|
// The following limitations are in place:
|
|
// Maximum length of a single string: 8192 bytes
|
|
// Maximum length of a string list: 1024 strings
|
|
// Maximum number of entries: 8192
|
|
//
|
|
// The purpose of these limitations is to limit the impact
|
|
// of database corruption.
|
|
|
|
Q_DECLARE_OPERATORS_FOR_FLAGS(KSycocaPrivate::BehaviorsIfNotFound)
|
|
|
|
KSycocaPrivate::KSycocaPrivate()
|
|
: databaseStatus(DatabaseNotOpen),
|
|
readError(false),
|
|
timeStamp(0),
|
|
m_databasePath(),
|
|
updateSig(0),
|
|
m_device(0)
|
|
{
|
|
m_sycocaStrategy = StrategyFile;
|
|
KConfigGroup config(KGlobal::config(), "KSycoca");
|
|
setStrategyFromString(config.readEntry("strategy"));
|
|
}
|
|
|
|
void KSycocaPrivate::setStrategyFromString(const QString& strategy) {
|
|
if (strategy == QLatin1String("file")) {
|
|
m_sycocaStrategy = StrategyFile;
|
|
} else if (!strategy.isEmpty()) {
|
|
kWarning(7011) << "Unknown sycoca strategy:" << strategy;
|
|
}
|
|
}
|
|
|
|
int KSycoca::version()
|
|
{
|
|
return KSYCOCA_VERSION;
|
|
}
|
|
|
|
class KSycocaSingleton
|
|
{
|
|
public:
|
|
KSycocaSingleton() { }
|
|
~KSycocaSingleton()
|
|
{
|
|
if (m_threadSycocas) {
|
|
delete m_threadSycocas;
|
|
m_threadSycocas = 0;
|
|
}
|
|
}
|
|
|
|
bool hasSycoca() const {
|
|
return (m_threadSycocas != 0);
|
|
}
|
|
KSycoca* sycoca() {
|
|
if (!m_threadSycocas) {
|
|
m_threadSycocas = new KSycoca();
|
|
}
|
|
return m_threadSycocas;
|
|
}
|
|
void setSycoca(KSycoca* s) {
|
|
m_threadSycocas = s;
|
|
}
|
|
|
|
private:
|
|
static thread_local KSycoca* m_threadSycocas;
|
|
};
|
|
thread_local KSycoca* KSycocaSingleton::m_threadSycocas = 0;
|
|
|
|
K_GLOBAL_STATIC(KSycocaSingleton, ksycocaInstance)
|
|
|
|
// Read-only constructor
|
|
KSycoca::KSycoca()
|
|
: d(new KSycocaPrivate())
|
|
{
|
|
QDBusConnection::sessionBus().connect(
|
|
QString(), QString(),
|
|
QString::fromLatin1("org.kde.KSycoca"),
|
|
QString::fromLatin1("notifyDatabaseChanged"),
|
|
this, SLOT(notifyDatabaseChanged(QStringList))
|
|
);
|
|
}
|
|
|
|
bool KSycocaPrivate::openDatabase(bool openDummyIfNotFound)
|
|
{
|
|
Q_ASSERT(databaseStatus == DatabaseNotOpen);
|
|
|
|
delete m_device; m_device = 0;
|
|
QString path = KSycoca::absoluteFilePath();
|
|
|
|
bool canRead = KDE::access(path, R_OK) == 0;
|
|
kDebug(7011) << "Trying to open ksycoca from" << path;
|
|
if (!canRead) {
|
|
path = KSycoca::absoluteFilePath(KSycoca::GlobalDatabase);
|
|
if (!path.isEmpty()) {
|
|
kDebug(7011) << "Trying to open global ksycoca from " << path;
|
|
canRead = KDE::access(path, R_OK) == 0;
|
|
}
|
|
}
|
|
|
|
bool result = true;
|
|
if (canRead) {
|
|
m_databasePath = path;
|
|
checkVersion();
|
|
} else { // No database file
|
|
kDebug(7011) << "Could not open ksycoca";
|
|
m_databasePath.clear();
|
|
databaseStatus = NoDatabase;
|
|
if (openDummyIfNotFound) {
|
|
// We open a dummy database instead.
|
|
//kDebug(7011) << "No database, opening a dummy one.";
|
|
|
|
m_sycocaStrategy = StrategyDummyBuffer;
|
|
(void)stream();
|
|
} else {
|
|
result = false;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
KSycocaAbstractDevice* KSycocaPrivate::device()
|
|
{
|
|
if (m_device) {
|
|
return m_device;
|
|
}
|
|
|
|
Q_ASSERT(!m_databasePath.isEmpty());
|
|
|
|
KSycocaAbstractDevice* device = m_device;
|
|
if (m_sycocaStrategy == StrategyDummyBuffer) {
|
|
device = new KSycocaBufferDevice();
|
|
} else {
|
|
if (!device) {
|
|
device = new KSycocaFileDevice(m_databasePath);
|
|
if (!device->device()->open(QIODevice::ReadOnly)) {
|
|
kError(7011) << "Couldn't open" << m_databasePath << "even though it is readable? Impossible.";
|
|
//delete device; device = 0; // this would crash in the return statement...
|
|
}
|
|
}
|
|
}
|
|
if (device) {
|
|
m_device = device;
|
|
}
|
|
return m_device;
|
|
}
|
|
|
|
QDataStream*& KSycocaPrivate::stream()
|
|
{
|
|
if (!m_device) {
|
|
if (databaseStatus == DatabaseNotOpen) {
|
|
checkDatabase(KSycocaPrivate::IfNotFoundRecreate | KSycocaPrivate::IfNotFoundOpenDummy);
|
|
}
|
|
|
|
device(); // create m_device
|
|
}
|
|
|
|
return m_device->stream();
|
|
}
|
|
|
|
// Read-write constructor - only for KBuildSycoca
|
|
KSycoca::KSycoca(bool /* dummy */)
|
|
: d(new KSycocaPrivate)
|
|
{
|
|
// This instance was not created by the singleton, but by a direct call to new!
|
|
ksycocaInstance->setSycoca(this);
|
|
}
|
|
|
|
KSycoca* KSycoca::self()
|
|
{
|
|
KSycoca* s = ksycocaInstance->sycoca();
|
|
Q_ASSERT(s);
|
|
return s;
|
|
}
|
|
|
|
KSycoca::~KSycoca()
|
|
{
|
|
d->closeDatabase();
|
|
delete d;
|
|
//if (ksycocaInstance.exists()
|
|
// && ksycocaInstance->self == this)
|
|
// ksycocaInstance->self = 0;
|
|
}
|
|
|
|
bool KSycoca::isAvailable()
|
|
{
|
|
return self()->d->checkDatabase(KSycocaPrivate::IfNotFoundDoNothing/* don't open dummy db if not found */);
|
|
}
|
|
|
|
void KSycocaPrivate::closeDatabase()
|
|
{
|
|
delete m_device;
|
|
m_device = 0;
|
|
|
|
// It is very important to delete all factories here
|
|
// since they cache information about the database file
|
|
// But other threads might be using them, so this class is
|
|
// refcounted, and deleted when the last thread is done with them
|
|
qDeleteAll(m_factories);
|
|
m_factories.clear();
|
|
|
|
databaseStatus = DatabaseNotOpen;
|
|
timeStamp = 0;
|
|
}
|
|
|
|
void KSycoca::addFactory(KSycocaFactory *factory)
|
|
{
|
|
d->addFactory(factory);
|
|
}
|
|
|
|
void KSycoca::notifyDatabaseChanged(const QStringList &changeList)
|
|
{
|
|
d->changeList = changeList;
|
|
//kDebug(7011) << QThread::currentThread() << "got a notifyDatabaseChanged signal" << changeList;
|
|
// kbuildsycoca tells us the database file changed
|
|
// Close the database and forget all about what we knew
|
|
// The next call to any public method will recreate
|
|
// everything that's needed.
|
|
d->closeDatabase();
|
|
|
|
// Now notify applications
|
|
emit databaseChanged(changeList);
|
|
}
|
|
|
|
QDataStream * KSycoca::findEntry(int offset, KSycocaType &type)
|
|
{
|
|
QDataStream* str = stream();
|
|
Q_ASSERT(str);
|
|
//kDebug(7011) << QString("KSycoca::_findEntry(offset=%1)").arg(offset,8,16);
|
|
str->device()->seek(offset);
|
|
qint32 aType;
|
|
*str >> aType;
|
|
type = KSycocaType(aType);
|
|
//kDebug(7011) << QString("KSycoca::found type %1").arg(aType);
|
|
return str;
|
|
}
|
|
|
|
KSycocaFactoryList* KSycoca::factories()
|
|
{
|
|
return d->factories();
|
|
}
|
|
|
|
// Warning, checkVersion rewinds to the beginning of stream().
|
|
bool KSycocaPrivate::checkVersion()
|
|
{
|
|
QDataStream *m_str = device()->stream();
|
|
Q_ASSERT(m_str);
|
|
m_str->device()->seek(0);
|
|
qint32 aVersion = 0;
|
|
*m_str >> aVersion;
|
|
if (aVersion < KSYCOCA_VERSION) {
|
|
kWarning(7011) << "Found version" << aVersion << ", expecting version" << KSYCOCA_VERSION << "or higher.";
|
|
databaseStatus = BadVersion;
|
|
return false;
|
|
}
|
|
databaseStatus = DatabaseOK;
|
|
return true;
|
|
}
|
|
|
|
// If it returns true, the database is valid and the stream has rewinded to the beginning
|
|
// and past the version number.
|
|
bool KSycocaPrivate::checkDatabase(BehaviorsIfNotFound ifNotFound)
|
|
{
|
|
if (databaseStatus == DatabaseOK) {
|
|
if (checkVersion()) {
|
|
// the version is ok, but must rewind the stream anyway
|
|
return true;
|
|
}
|
|
}
|
|
|
|
closeDatabase(); // close the dummy one
|
|
|
|
// Check if new database already available
|
|
if (openDatabase(ifNotFound & IfNotFoundOpenDummy)) {
|
|
if (checkVersion()) {
|
|
// Database exists, and version is ok.
|
|
return true;
|
|
}
|
|
}
|
|
|
|
if (ifNotFound & IfNotFoundRecreate) {
|
|
// We simply need to run kbuildsycoca to recreate the sycoca file.
|
|
kDebug(7011) << QThread::currentThread() << "We have no database.... launching" << KBUILDSYCOCA_EXENAME;
|
|
if (QProcess::execute(KStandardDirs::findExe(QString::fromLatin1(KBUILDSYCOCA_EXENAME))) != 0) {
|
|
kWarning(7011) << "Running KSycoca failed.";
|
|
}
|
|
|
|
closeDatabase(); // close the dummy one
|
|
|
|
// Ok, the new database should be here now, open it.
|
|
if (!openDatabase(ifNotFound & IfNotFoundOpenDummy)) {
|
|
kDebug(7011) << "Still no database...";
|
|
return false; // Still no database - uh oh
|
|
}
|
|
if (!checkVersion()) {
|
|
kDebug(7011) << "Still outdated...";
|
|
return false; // Still outdated - uh oh
|
|
}
|
|
|
|
// If kded is not running we need to launch it as it monitors for changes
|
|
static const QString kdedInterface = QString::fromLatin1("org.kde.kded");
|
|
QDBusConnectionInterface* sessionInterface = QDBusConnection::sessionBus().interface();
|
|
const bool kdedRunning = sessionInterface->isServiceRegistered(kdedInterface);
|
|
if (!kdedRunning) {
|
|
kDebug(7011) << "Launching kded";
|
|
sessionInterface->startService(kdedInterface);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
QDataStream * KSycoca::findFactory(KSycocaFactoryId id)
|
|
{
|
|
// Ensure we have a valid database (right version, and rewinded to beginning)
|
|
if (!d->checkDatabase(KSycocaPrivate::IfNotFoundRecreate)) {
|
|
return 0;
|
|
}
|
|
|
|
QDataStream* str = stream();
|
|
Q_ASSERT(str);
|
|
|
|
qint32 aId;
|
|
qint32 aOffset;
|
|
while (true) {
|
|
*str >> aId;
|
|
if (aId == 0) {
|
|
kError(7011) << "Error, KSycocaFactory (id =" << int(id) << ") not found!";
|
|
break;
|
|
}
|
|
*str >> aOffset;
|
|
if (aId == id) {
|
|
//kDebug(7011) << "KSycoca::findFactory(" << id << ") offset " << aOffset;
|
|
str->device()->seek(aOffset);
|
|
return str;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
QString KSycoca::kfsstnd_prefixes()
|
|
{
|
|
// do not try to launch kbuildsycoca from here; this code is also called by kbuildsycoca.
|
|
if (!d->checkDatabase(KSycocaPrivate::IfNotFoundDoNothing)) {
|
|
return QString();
|
|
}
|
|
QDataStream* str = stream();
|
|
Q_ASSERT(str);
|
|
qint32 aId;
|
|
qint32 aOffset;
|
|
// skip factories offsets
|
|
while(true)
|
|
{
|
|
*str >> aId;
|
|
if (aId) {
|
|
*str >> aOffset;
|
|
} else {
|
|
// just read 0
|
|
break;
|
|
}
|
|
}
|
|
// We now point to the header
|
|
QString prefixes;
|
|
KSycocaEntry::read(*str, prefixes);
|
|
*str >> d->timeStamp;
|
|
KSycocaEntry::read(*str, d->language);
|
|
*str >> d->updateSig;
|
|
KSycocaEntry::read(*str, d->allResourceDirs);
|
|
return prefixes;
|
|
}
|
|
|
|
quint32 KSycoca::timeStamp()
|
|
{
|
|
if (!d->timeStamp) {
|
|
(void) kfsstnd_prefixes();
|
|
}
|
|
return d->timeStamp;
|
|
}
|
|
|
|
quint32 KSycoca::updateSignature()
|
|
{
|
|
if (!d->timeStamp) {
|
|
(void) kfsstnd_prefixes();
|
|
}
|
|
return d->updateSig;
|
|
}
|
|
|
|
QString KSycoca::absoluteFilePath(DatabaseType type)
|
|
{
|
|
if (type == GlobalDatabase) {
|
|
QString path = KGlobal::dirs()->findResource("services", QString::fromLatin1(KSYCOCA_FILENAME));
|
|
if (path.isEmpty())
|
|
return KGlobal::dirs()->saveLocation("services") + QString::fromLatin1(KSYCOCA_FILENAME);
|
|
return path;
|
|
}
|
|
|
|
return KGlobal::dirs()->saveLocation("cache") + QString::fromLatin1(KSYCOCA_FILENAME);
|
|
}
|
|
|
|
QString KSycoca::language()
|
|
{
|
|
if (d->language.isEmpty()) {
|
|
(void) kfsstnd_prefixes();
|
|
}
|
|
return d->language;
|
|
}
|
|
|
|
QStringList KSycoca::allResourceDirs()
|
|
{
|
|
if (!d->timeStamp) {
|
|
(void) kfsstnd_prefixes();
|
|
}
|
|
return d->allResourceDirs;
|
|
}
|
|
|
|
void KSycoca::flagError()
|
|
{
|
|
kWarning(7011) << "KSycoca database corruption!";
|
|
KSycocaPrivate* d = ksycocaInstance->sycoca()->d;
|
|
if (d->readError) {
|
|
return;
|
|
}
|
|
d->readError = true;
|
|
if (s_autoRebuild) {
|
|
// Rebuild the damned thing.
|
|
if (QProcess::execute(KStandardDirs::findExe(QString::fromLatin1(KBUILDSYCOCA_EXENAME))) != 0) {
|
|
kWarning(7011) << "Running KSycoca failed.";
|
|
}
|
|
// Old comment, maybe not true anymore:
|
|
// Do not wait until the DBUS signal from kbuildsycoca here.
|
|
// It deletes m_str which is a problem when flagError is called during the KSycocaFactory ctor...
|
|
}
|
|
}
|
|
|
|
|
|
bool KSycoca::isBuilding()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
void KSycoca::disableAutoRebuild()
|
|
{
|
|
s_autoRebuild = false;
|
|
}
|
|
|
|
QDataStream*& KSycoca::stream()
|
|
{
|
|
return d->stream();
|
|
}
|
|
|
|
void KSycoca::clearCaches()
|
|
{
|
|
if (ksycocaInstance.exists() && ksycocaInstance->hasSycoca()) {
|
|
ksycocaInstance->sycoca()->d->closeDatabase();
|
|
}
|
|
}
|
|
|
|
#include "moc_ksycoca.cpp"
|