diff options
author | Christian Mollekopf <chrigi_1@fastmail.fm> | 2012-06-25 18:42:22 (GMT) |
---|---|---|
committer | Christian Mollekopf <chrigi_1@fastmail.fm> | 2012-06-25 18:42:22 (GMT) |
commit | 43d35cfb36c924eede624f4a5c2fcb0dd7d698b6 (patch) | |
tree | b7ac13bcf009d94cabb4c5afeee0825a4ce9da67 /kdecore/sycoca | |
parent | bf385f44f99d73263b1787daac7423599e69f024 (diff) | |
download | libcalendaring-43d35cfb36c924eede624f4a5c2fcb0dd7d698b6.tar.gz |
kdecore import
commit 557c1267cbb5e352e1487c3dd468cd4a4cb63254
Merge: aed99b0 387e3c3
Author: David Faure <faure@kde.org>
Date: Thu Jun 14 17:56:59 2012 +0200
Merge remote-tracking branch 'origin/KDE/4.8'
Diffstat (limited to 'kdecore/sycoca')
-rw-r--r-- | kdecore/sycoca/.kprotocolinfofactory.cpp.kate-swp | bin | 0 -> 63 bytes | |||
-rw-r--r-- | kdecore/sycoca/kmemfile.cpp | 253 | ||||
-rw-r--r-- | kdecore/sycoca/kmemfile.h | 100 | ||||
-rw-r--r-- | kdecore/sycoca/kprotocolinfo.cpp | 462 | ||||
-rw-r--r-- | kdecore/sycoca/kprotocolinfo.h | 378 | ||||
-rw-r--r-- | kdecore/sycoca/kprotocolinfo_p.h | 65 | ||||
-rw-r--r-- | kdecore/sycoca/kprotocolinfofactory.cpp | 122 | ||||
-rw-r--r-- | kdecore/sycoca/kprotocolinfofactory.h | 99 | ||||
-rw-r--r-- | kdecore/sycoca/ksycoca.cpp | 604 | ||||
-rw-r--r-- | kdecore/sycoca/ksycoca.h | 235 | ||||
-rw-r--r-- | kdecore/sycoca/ksycoca_p.h | 79 | ||||
-rw-r--r-- | kdecore/sycoca/ksycocadevices_p.h | 125 | ||||
-rw-r--r-- | kdecore/sycoca/ksycocadict.cpp | 562 | ||||
-rw-r--r-- | kdecore/sycoca/ksycocadict_p.h | 135 | ||||
-rw-r--r-- | kdecore/sycoca/ksycocaentry.cpp | 173 | ||||
-rw-r--r-- | kdecore/sycoca/ksycocaentry.h | 157 | ||||
-rw-r--r-- | kdecore/sycoca/ksycocaentry_p.h | 82 | ||||
-rw-r--r-- | kdecore/sycoca/ksycocafactory.cpp | 248 | ||||
-rw-r--r-- | kdecore/sycoca/ksycocafactory.h | 209 | ||||
-rw-r--r-- | kdecore/sycoca/ksycocatype.h | 57 |
20 files changed, 4145 insertions, 0 deletions
diff --git a/kdecore/sycoca/.kprotocolinfofactory.cpp.kate-swp b/kdecore/sycoca/.kprotocolinfofactory.cpp.kate-swp Binary files differnew file mode 100644 index 0000000..8867a67 --- /dev/null +++ b/kdecore/sycoca/.kprotocolinfofactory.cpp.kate-swp diff --git a/kdecore/sycoca/kmemfile.cpp b/kdecore/sycoca/kmemfile.cpp new file mode 100644 index 0000000..a786e92 --- /dev/null +++ b/kdecore/sycoca/kmemfile.cpp @@ -0,0 +1,253 @@ +/* + This file is part of the KDE libraries + Copyright (C) 2008 Christian Ehrlicher <ch.ehrlicher@gmx.de> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + 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 "kmemfile.h" + +#ifndef QT_NO_SHAREDMEMORY + +#include <QtCore/QSharedMemory> +#include <QtCore/QCryptographicHash> +#include <QtCore/QFile> +#include <QtCore/QDir> + +#include "klocalizedstring.h" + +class KMemFile::Private +{ +public: + struct sharedInfoData { + int shmCounter; + qint64 shmDataSize; + + sharedInfoData() { + memset ( this, 0, sizeof ( *this ) ); + } + }; + Private ( KMemFile *_parent ) : readWritePos ( 0 ), shmDataSize ( 0 ), parent ( _parent ) {} + + QString getShmKey ( int iCounter = -1 ); + static QString getShmKey ( const QString &filename, int iCounter = -1 ); + bool loadContentsFromFile(); + void close(); + + QString filename; + QSharedMemory shmInfo; + QSharedMemory shmData; + qint64 readWritePos; + qint64 shmDataSize; + + KMemFile *parent; +}; + +QString KMemFile::Private::getShmKey ( int iCounter ) +{ + return getShmKey ( filename, iCounter ); +} + +QString KMemFile::Private::getShmKey ( const QString &filename, int iCounter ) +{ + QByteArray tmp = QString ( QDir ( filename ).canonicalPath() + QString::number ( iCounter ) ).toUtf8(); + return QString::fromAscii ( QCryptographicHash::hash ( tmp, QCryptographicHash::Sha1 ) ); +} + +bool KMemFile::Private::loadContentsFromFile() +{ + QFile f ( filename ); + if ( !f.exists() ) { + close(); + parent->setErrorString ( i18n ( "File %1 does not exist" , filename ) ); + return false; + } + if ( !f.open ( QIODevice::ReadOnly ) ) { + close(); + parent->setErrorString ( i18n ( "Cannot open %1 for reading" , filename ) ); + return false; + } + + sharedInfoData *infoPtr = static_cast<sharedInfoData*> ( shmInfo.data() ); + + infoPtr->shmDataSize = f.size(); + shmData.setKey ( getShmKey ( infoPtr->shmCounter ) ); + if ( !shmData.create ( infoPtr->shmDataSize ) ) { + close(); + parent->setErrorString ( i18n ( "Cannot create memory segment for file %1" , filename ) ); + return false; + } + shmData.lock(); + qint64 size = 0; + qint64 bytesRead; + char *data = static_cast<char*> ( shmData.data() ); + bytesRead = f.read ( data, infoPtr->shmDataSize ); + if ( bytesRead != infoPtr->shmDataSize ) { + close(); + parent->setErrorString ( i18n ( "Could not read data from %1 into shm" , filename ) ); + return false; + } + shmDataSize = size; + shmData.unlock(); + return true; +} + +void KMemFile::Private::close() +{ + shmData.unlock(); + shmData.detach(); + shmInfo.unlock(); + shmInfo.detach(); + readWritePos = 0; + shmDataSize = 0; +} + +KMemFile::KMemFile ( const QString &filename, QObject *parent ) + : QIODevice ( parent ), d ( new Private ( this ) ) +{ + d->filename = filename; +} + +KMemFile::~KMemFile() +{ + close(); + delete d; +} + +void KMemFile::close () +{ + QIODevice::close(); + if ( !isOpen() ) + return; + d->close(); +} + +bool KMemFile::isSequential () const +{ + return false; +} + +bool KMemFile::open ( OpenMode mode ) +{ + if ( isOpen() ) { + QIODevice::open ( mode ); + return false; + } + + if ( mode != QIODevice::ReadOnly ) { + setErrorString ( i18n ( "Only 'ReadOnly' allowed" ) ); + return false; + } + + if ( !QFile::exists ( d->filename ) ) { + setErrorString ( i18n ( "File %1 does not exist" , d->filename ) ); + return false; + } + + QSharedMemory lock ( QDir ( d->filename ).canonicalPath() ); + lock.lock(); + + Private::sharedInfoData *infoPtr; + d->shmInfo.setKey ( d->getShmKey() ); + // see if it's already in memory + if ( !d->shmInfo.attach ( QSharedMemory::ReadWrite ) ) { + if ( !d->shmInfo.create ( sizeof ( Private::sharedInfoData ) ) ) { + lock.unlock(); + setErrorString ( i18n ( "Cannot create memory segment for file %1" , d->filename ) ); + return false; + } + d->shmInfo.lock(); + // no -> create it + infoPtr = static_cast<Private::sharedInfoData*> ( d->shmInfo.data() ); + memset ( infoPtr, 0, sizeof ( Private::sharedInfoData ) ); + infoPtr->shmCounter = 1; + if ( !d->loadContentsFromFile() ) { + d->shmInfo.unlock(); + d->shmInfo.detach(); + lock.unlock(); + return false; + } + } else { + d->shmInfo.lock(); + infoPtr = static_cast<Private::sharedInfoData*> ( d->shmInfo.data() ); + d->shmData.setKey ( d->getShmKey ( infoPtr->shmCounter ) ); + if ( !d->shmData.attach ( QSharedMemory::ReadOnly ) ) { + if ( !d->loadContentsFromFile() ) { + d->shmInfo.unlock(); + d->shmInfo.detach(); + lock.unlock(); + return false; + } + } + } + d->shmDataSize = infoPtr->shmDataSize; + d->shmInfo.unlock(); + lock.unlock(); + + setOpenMode ( mode ); + return true; +} + +bool KMemFile::seek ( qint64 pos ) +{ + if ( d->shmDataSize < pos ) { + setErrorString ( i18n ( "Cannot seek past eof" ) ); + return false; + } + d->readWritePos = pos; + QIODevice::seek ( pos ); + return true; +} + +qint64 KMemFile::size () const +{ + return d->shmDataSize; +} + +qint64 KMemFile::readData ( char * data, qint64 maxSize ) +{ + if ( ( openMode() & QIODevice::ReadOnly ) == 0 ) + return -1; + + qint64 maxRead = size() - d->readWritePos; + qint64 bytesToRead = qMin ( maxRead, maxSize ); + const char *src = static_cast<const char*> ( d->shmData.data() ); + memcpy ( data, &src[d->readWritePos], bytesToRead ); + d->readWritePos += bytesToRead; + return bytesToRead; +} + +qint64 KMemFile::writeData ( const char *, qint64 ) +{ + return -1; +} + +void KMemFile::fileContentsChanged ( const QString &filename ) +{ + QSharedMemory lock ( QDir ( filename ).canonicalPath() ); + lock.lock(); + + QSharedMemory shmData ( Private::getShmKey ( filename ) ); + if ( !shmData.attach() ) + return; + shmData.lock(); + Private::sharedInfoData *infoPtr = static_cast<Private::sharedInfoData*> ( shmData.data() ); + infoPtr->shmCounter++; + infoPtr->shmDataSize = 0; + shmData.unlock(); +} + +#endif //QT_NO_SHAREDMEMORY diff --git a/kdecore/sycoca/kmemfile.h b/kdecore/sycoca/kmemfile.h new file mode 100644 index 0000000..5a734a0 --- /dev/null +++ b/kdecore/sycoca/kmemfile.h @@ -0,0 +1,100 @@ +/* + This file is part of the KDE libraries + Copyright (C) 2008 Christian Ehrlicher <ch.ehrlicher@gmx.de> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + 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. +*/ + +#ifndef KMEMFILE_H +#define KMEMFILE_H + +#ifndef QT_NO_SHAREDMEMORY + +#include <QtCore/QIODevice> +#include <kdecore_export.h> + +/** + * @internal + * Simple QIODevice for QSharedMemory to keep ksycoca cache in memory only once + * The first call to open() loads the file into a shm segment. Every + * subsequent call only attaches to this segment. When the file content changed, + * you have to execute KMemFile::fileContentsChanged() to update the internal + * structures. The next call to open() creates a new shm segment. The old one + * is automatically destroyed when the last process closed KMemFile. + */ + +class KDECORE_EXPORT KMemFile : public QIODevice +{ +public: + /** + * ctor + * + * @param filename the file to load into memory + * @param parent our parent + */ + explicit KMemFile ( const QString &filename, QObject *parent = 0 ); + /** + * dtor + */ + virtual ~KMemFile(); + /** + * closes the KMemFile + * + * @reimp + */ + virtual void close (); + /** + * As KMemFile is a random access device, it returns false + * + * @reimp + */ + virtual bool isSequential () const; + /** + * @reimp + * @param mode only QIODevice::ReadOnly is accepted + */ + virtual bool open ( OpenMode mode ); + /** + * Sets the current read/write position to pos + * @reimp + * @param pos the new read/write position + */ + virtual bool seek ( qint64 pos ); + /** + * Returns the size of the file + * @reimp + */ + virtual qint64 size () const; + /** + * This static function updates the internal information about the file + * loaded into shared memory. The next time the file is opened, the file is + * reread from the file system. + */ + static void fileContentsChanged ( const QString &filename ); +protected: + /** @reimp */ + virtual qint64 readData ( char * data, qint64 maxSize ); + /** @reimp */ + virtual qint64 writeData ( const char * data, qint64 maxSize ); +private: + class Private; + friend class Private; + Private * const d; +}; + +#endif //QT_NO_SHAREDMEMORY + +#endif // KMEMFILE_H diff --git a/kdecore/sycoca/kprotocolinfo.cpp b/kdecore/sycoca/kprotocolinfo.cpp new file mode 100644 index 0000000..7b98726 --- /dev/null +++ b/kdecore/sycoca/kprotocolinfo.cpp @@ -0,0 +1,462 @@ +/* This file is part of the KDE libraries + Copyright (C) 1999 Torben Weis <weis@kde.org> + Copyright (C) 2003 Waldo Bastian <bastian@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 "kprotocolinfo.h" +#include "kprotocolinfo_p.h" +#include "kprotocolinfofactory.h" + +#include <kmimetypetrader.h> +#include <kstandarddirs.h> +#include <kconfig.h> +#include <kconfiggroup.h> + +// +// Internal functions: +// +KProtocolInfo::KProtocolInfo(const QString &path) + : KSycocaEntry(*new KProtocolInfoPrivate(path, this)) +{ + Q_D(KProtocolInfo); + QString fullPath = KStandardDirs::locate("services", path); + + KConfig sconfig( fullPath ); + KConfigGroup config(&sconfig, "Protocol" ); + + m_name = config.readEntry( "protocol" ); + m_exec = config.readPathEntry( "exec", QString() ); + m_isSourceProtocol = config.readEntry( "source", true ); + m_isHelperProtocol = config.readEntry( "helper", false ); + m_supportsReading = config.readEntry( "reading", false ); + m_supportsWriting = config.readEntry( "writing", false ); + m_supportsMakeDir = config.readEntry( "makedir", false ); + m_supportsDeleting = config.readEntry( "deleting", false ); + m_supportsLinking = config.readEntry( "linking", false ); + m_supportsMoving = config.readEntry( "moving", false ); + m_supportsOpening = config.readEntry( "opening", false ); + m_canCopyFromFile = config.readEntry( "copyFromFile", false ); + m_canCopyToFile = config.readEntry( "copyToFile", false ); + d->canRenameFromFile = config.readEntry( "renameFromFile", false ); + d->canRenameToFile = config.readEntry( "renameToFile", false ); + d->canDeleteRecursive = config.readEntry( "deleteRecursive", false ); + const QString fnu = config.readEntry( "fileNameUsedForCopying", "FromURL" ); + d->fileNameUsedForCopying = FromUrl; + if (fnu == QLatin1String("Name")) + d->fileNameUsedForCopying = Name; + else if (fnu == QLatin1String("DisplayName")) + d->fileNameUsedForCopying = DisplayName; + + m_listing = config.readEntry( "listing", QStringList() ); + // Many .protocol files say "Listing=false" when they really mean "Listing=" (i.e. unsupported) + if ( m_listing.count() == 1 && m_listing.first() == QLatin1String("false") ) + m_listing.clear(); + m_supportsListing = ( m_listing.count() > 0 ); + m_defaultMimetype = config.readEntry( "defaultMimetype" ); + m_determineMimetypeFromExtension = config.readEntry( "determineMimetypeFromExtension", true ); + d->archiveMimetype = config.readEntry("archiveMimetype", QStringList()); + m_icon = config.readEntry( "Icon" ); + m_config = config.readEntry( "config", m_name ); + m_maxSlaves = config.readEntry( "maxInstances", 1); + d->maxSlavesPerHost = config.readEntry( "maxInstancesPerHost", 0); + + QString tmp = config.readEntry( "input" ); + if ( tmp == QLatin1String("filesystem") ) + m_inputType = KProtocolInfo::T_FILESYSTEM; + else if ( tmp == QLatin1String("stream") ) + m_inputType = KProtocolInfo::T_STREAM; + else + m_inputType = KProtocolInfo::T_NONE; + + tmp = config.readEntry( "output" ); + if ( tmp == QLatin1String("filesystem") ) + m_outputType = KProtocolInfo::T_FILESYSTEM; + else if ( tmp == QLatin1String("stream") ) + m_outputType = KProtocolInfo::T_STREAM; + else + m_outputType = KProtocolInfo::T_NONE; + + d->docPath = config.readPathEntry( "X-DocPath", QString() ); + if (d->docPath.isEmpty()) + d->docPath = config.readPathEntry( "DocPath", QString() ); + d->protClass = config.readEntry( "Class" ).toLower(); + if (d->protClass[0] != QLatin1Char(':')) + d->protClass.prepend(QLatin1Char(':')); + + const QStringList extraNames = config.readEntry( "ExtraNames", QStringList() ); + const QStringList extraTypes = config.readEntry( "ExtraTypes", QStringList() ); + QStringList::const_iterator it = extraNames.begin(); + QStringList::const_iterator typeit = extraTypes.begin(); + for( ; it != extraNames.end() && typeit != extraTypes.end(); ++it, ++typeit ) { + QVariant::Type type = QVariant::nameToType( (*typeit).toLatin1() ); + // currently QVariant::Type and ExtraField::Type use the same subset of values, so we can just cast. + d->extraFields.append( ExtraField( *it, static_cast<ExtraField::Type>(type) ) ); + } + + d->showPreviews = config.readEntry( "ShowPreviews", d->protClass == QLatin1String(":local") ); + + d->capabilities = config.readEntry( "Capabilities", QStringList() ); + d->proxyProtocol = config.readEntry( "ProxiedBy" ); +} + +KProtocolInfo::KProtocolInfo( QDataStream& _str, int offset) : + KSycocaEntry(*new KProtocolInfoPrivate( _str, offset, this) ) +{ + load( _str ); +} + +KProtocolInfo::~KProtocolInfo() +{ +} + +void +KProtocolInfo::load( QDataStream& _str) +{ + Q_D(KProtocolInfo); + // You may add new fields at the end. Make sure to update the version + // number in ksycoca.h + qint32 i_inputType, i_outputType; + qint8 i_isSourceProtocol, i_isHelperProtocol, + i_supportsListing, i_supportsReading, + i_supportsWriting, i_supportsMakeDir, + i_supportsDeleting, i_supportsLinking, + i_supportsMoving, i_supportsOpening, + i_determineMimetypeFromExtension, + i_canCopyFromFile, i_canCopyToFile, i_showPreviews, + i_uriMode, i_canRenameFromFile, i_canRenameToFile, + i_canDeleteRecursive, i_fileNameUsedForCopying; + + _str >> m_name >> m_exec >> m_listing >> m_defaultMimetype + >> i_determineMimetypeFromExtension + >> m_icon + >> i_inputType >> i_outputType + >> i_isSourceProtocol >> i_isHelperProtocol + >> i_supportsListing >> i_supportsReading + >> i_supportsWriting >> i_supportsMakeDir + >> i_supportsDeleting >> i_supportsLinking + >> i_supportsMoving >> i_supportsOpening + >> i_canCopyFromFile >> i_canCopyToFile + >> m_config >> m_maxSlaves >> d->docPath >> d->protClass + >> d->extraFields >> i_showPreviews >> i_uriMode + >> d->capabilities >> d->proxyProtocol + >> i_canRenameFromFile >> i_canRenameToFile + >> i_canDeleteRecursive >> i_fileNameUsedForCopying + >> d->archiveMimetype >> d->maxSlavesPerHost; + + m_inputType = (Type) i_inputType; + m_outputType = (Type) i_outputType; + m_isSourceProtocol = (i_isSourceProtocol != 0); + m_isHelperProtocol = (i_isHelperProtocol != 0); + m_supportsListing = (i_supportsListing != 0); + m_supportsReading = (i_supportsReading != 0); + m_supportsWriting = (i_supportsWriting != 0); + m_supportsMakeDir = (i_supportsMakeDir != 0); + m_supportsDeleting = (i_supportsDeleting != 0); + m_supportsLinking = (i_supportsLinking != 0); + m_supportsMoving = (i_supportsMoving != 0); + m_supportsOpening = (i_supportsOpening != 0); + m_canCopyFromFile = (i_canCopyFromFile != 0); + m_canCopyToFile = (i_canCopyToFile != 0); + d->canRenameFromFile = (i_canRenameFromFile != 0); + d->canRenameToFile = (i_canRenameToFile != 0); + d->canDeleteRecursive = (i_canDeleteRecursive != 0); + d->fileNameUsedForCopying = FileNameUsedForCopying(i_fileNameUsedForCopying); + m_determineMimetypeFromExtension = (i_determineMimetypeFromExtension != 0); + d->showPreviews = (i_showPreviews != 0); +} + +void +KProtocolInfoPrivate::save( QDataStream& _str) +{ + KSycocaEntryPrivate::save( _str ); + + // You may add new fields at the end. Make sure to update the version + // number in ksycoca.h + qint32 i_inputType, i_outputType; + qint8 i_isSourceProtocol, i_isHelperProtocol, + i_supportsListing, i_supportsReading, + i_supportsWriting, i_supportsMakeDir, + i_supportsDeleting, i_supportsLinking, + i_supportsMoving, i_supportsOpening, + i_determineMimetypeFromExtension, + i_canCopyFromFile, i_canCopyToFile, i_showPreviews, + i_uriMode, i_canRenameFromFile, i_canRenameToFile, + i_canDeleteRecursive, i_fileNameUsedForCopying; + + i_inputType = (qint32) q->m_inputType; + i_outputType = (qint32) q->m_outputType; + i_isSourceProtocol = q->m_isSourceProtocol ? 1 : 0; + i_isHelperProtocol = q->m_isHelperProtocol ? 1 : 0; + i_supportsListing = q->m_supportsListing ? 1 : 0; + i_supportsReading = q->m_supportsReading ? 1 : 0; + i_supportsWriting = q->m_supportsWriting ? 1 : 0; + i_supportsMakeDir = q->m_supportsMakeDir ? 1 : 0; + i_supportsDeleting = q->m_supportsDeleting ? 1 : 0; + i_supportsLinking = q->m_supportsLinking ? 1 : 0; + i_supportsMoving = q->m_supportsMoving ? 1 : 0; + i_supportsOpening = q->m_supportsOpening ? 1 : 0; + i_canCopyFromFile = q->m_canCopyFromFile ? 1 : 0; + i_canCopyToFile = q->m_canCopyToFile ? 1 : 0; + i_canRenameFromFile = canRenameFromFile ? 1 : 0; + i_canRenameToFile = canRenameToFile ? 1 : 0; + i_canDeleteRecursive = canDeleteRecursive ? 1 : 0; + i_fileNameUsedForCopying = int(fileNameUsedForCopying); + i_determineMimetypeFromExtension = q->m_determineMimetypeFromExtension ? 1 : 0; + i_showPreviews = showPreviews ? 1 : 0; + i_uriMode = 0; + + _str << q->m_name << q->m_exec << q->m_listing << q->m_defaultMimetype + << i_determineMimetypeFromExtension + << q->m_icon + << i_inputType << i_outputType + << i_isSourceProtocol << i_isHelperProtocol + << i_supportsListing << i_supportsReading + << i_supportsWriting << i_supportsMakeDir + << i_supportsDeleting << i_supportsLinking + << i_supportsMoving << i_supportsOpening + << i_canCopyFromFile << i_canCopyToFile + << q->m_config << q->m_maxSlaves << docPath << protClass + << extraFields << i_showPreviews << i_uriMode + << capabilities << proxyProtocol + << i_canRenameFromFile << i_canRenameToFile + << i_canDeleteRecursive << i_fileNameUsedForCopying + << archiveMimetype << maxSlavesPerHost; +} + + +// +// Static functions: +// + +QStringList KProtocolInfo::protocols() +{ + return KProtocolInfoFactory::self()->protocols(); +} + +bool KProtocolInfo::isFilterProtocol( const QString& _protocol ) +{ + // We call the findProtocol directly (not via KProtocolManager) to bypass any proxy settings. + KProtocolInfo::Ptr prot = KProtocolInfoFactory::self()->findProtocol(_protocol); + if ( !prot ) + return false; + + return !prot->m_isSourceProtocol; +} + +QString KProtocolInfo::icon( const QString& _protocol ) +{ + // We call the findProtocol directly (not via KProtocolManager) to bypass any proxy settings. + KProtocolInfo::Ptr prot = KProtocolInfoFactory::self()->findProtocol(_protocol); + if ( !prot ) + return QString(); + + return prot->m_icon; +} + +QString KProtocolInfo::config( const QString& _protocol ) +{ + // We call the findProtocol directly (not via KProtocolManager) to bypass any proxy settings. + KProtocolInfo::Ptr prot = KProtocolInfoFactory::self()->findProtocol(_protocol); + if ( !prot ) + return QString(); + + return QString::fromLatin1("kio_%1rc").arg(prot->m_config); +} + +int KProtocolInfo::maxSlaves( const QString& _protocol ) +{ + KProtocolInfo::Ptr prot = KProtocolInfoFactory::self()->findProtocol(_protocol); + if ( !prot ) + return 1; + + return prot->m_maxSlaves; +} + +int KProtocolInfo::maxSlavesPerHost( const QString& _protocol ) +{ + KProtocolInfo::Ptr prot = KProtocolInfoFactory::self()->findProtocol(_protocol); + if ( !prot ) + return 0; + + return prot->d_func()->maxSlavesPerHost; +} + +bool KProtocolInfo::determineMimetypeFromExtension( const QString &_protocol ) +{ + KProtocolInfo::Ptr prot = KProtocolInfoFactory::self()->findProtocol( _protocol ); + if ( !prot ) + return true; + + return prot->m_determineMimetypeFromExtension; +} + +QString KProtocolInfo::exec(const QString& protocol) +{ + KProtocolInfo::Ptr prot = KProtocolInfoFactory::self()->findProtocol(protocol); + if ( prot ) { + return prot->m_exec; + } + + // Maybe it's "helper protocol", i.e. launches an app? + const KService::Ptr service = KMimeTypeTrader::self()->preferredService(QString::fromLatin1("x-scheme-handler/") + protocol); + if (service) + return service->exec(); + + return QString(); +} + +KProtocolInfo::ExtraFieldList KProtocolInfo::extraFields( const KUrl &url ) +{ + KProtocolInfo::Ptr prot = KProtocolInfoFactory::self()->findProtocol(url.protocol()); + if ( !prot ) + return ExtraFieldList(); + + return prot->d_func()->extraFields; +} + +QString KProtocolInfo::docPath( const QString& _protocol ) +{ + KProtocolInfo::Ptr prot = KProtocolInfoFactory::self()->findProtocol(_protocol); + if ( !prot ) + return QString(); + + return prot->d_func()->docPath; +} + +QString KProtocolInfo::protocolClass( const QString& _protocol ) +{ + KProtocolInfo::Ptr prot = KProtocolInfoFactory::self()->findProtocol(_protocol); + if ( !prot ) + return QString(); + + return prot->d_func()->protClass; +} + +bool KProtocolInfo::showFilePreview( const QString& _protocol ) +{ + KProtocolInfo::Ptr prot = KProtocolInfoFactory::self()->findProtocol(_protocol); + if ( !prot ) + return false; + + return prot->d_func()->showPreviews; +} + +QStringList KProtocolInfo::capabilities( const QString& _protocol ) +{ + KProtocolInfo::Ptr prot = KProtocolInfoFactory::self()->findProtocol(_protocol); + if ( !prot ) + return QStringList(); + + return prot->d_func()->capabilities; +} + +QString KProtocolInfo::proxiedBy( const QString& _protocol ) +{ + KProtocolInfo::Ptr prot = KProtocolInfoFactory::self()->findProtocol(_protocol); + if ( !prot ) + return QString(); + + return prot->d_func()->proxyProtocol; +} + +QString KProtocolInfo::defaultMimeType() const +{ + return m_defaultMimetype; +} + + +QStringList KProtocolInfo::archiveMimeTypes() const +{ + Q_D(const KProtocolInfo); + return d->archiveMimetype; +} + +bool KProtocolInfo::supportsListing() const +{ + return m_supportsListing; +} + +bool KProtocolInfo::canRenameFromFile() const +{ + Q_D(const KProtocolInfo); + return d->canRenameFromFile; +} + +bool KProtocolInfo::canRenameToFile() const +{ + Q_D(const KProtocolInfo); + return d->canRenameToFile; +} + +bool KProtocolInfo::canDeleteRecursive() const +{ + Q_D(const KProtocolInfo); + return d->canDeleteRecursive; +} + +KProtocolInfo::FileNameUsedForCopying KProtocolInfo::fileNameUsedForCopying() const +{ + Q_D(const KProtocolInfo); + return d->fileNameUsedForCopying; +} + +bool KProtocolInfo::isFilterProtocol( const KUrl &url ) +{ + return isFilterProtocol (url.protocol()); +} + +bool KProtocolInfo::isHelperProtocol( const KUrl &url ) +{ + return isHelperProtocol (url.protocol()); +} + +bool KProtocolInfo::isHelperProtocol( const QString &protocol ) +{ + // We call the findProtocol directly (not via KProtocolManager) to bypass any proxy settings. + KProtocolInfo::Ptr prot = KProtocolInfoFactory::self()->findProtocol(protocol); + if ( prot ) + return prot->m_isHelperProtocol; + + const KService::Ptr service = KMimeTypeTrader::self()->preferredService(QString::fromLatin1("x-scheme-handler/") + protocol); + return !service.isNull(); +} + +bool KProtocolInfo::isKnownProtocol( const KUrl &url ) +{ + return isKnownProtocol (url.protocol()); +} + +bool KProtocolInfo::isKnownProtocol( const QString &protocol ) +{ + // We call the findProtocol (const QString&) to bypass any proxy settings. + KProtocolInfo::Ptr prot = KProtocolInfoFactory::self()->findProtocol(protocol); + return prot || isHelperProtocol(protocol); +} + +QDataStream& operator>>( QDataStream& s, KProtocolInfo::ExtraField& field ) { + s >> field.name; + int type; + s >> type; + field.type = static_cast<KProtocolInfo::ExtraField::Type>( type ); + return s; +} + +QDataStream& operator<<( QDataStream& s, const KProtocolInfo::ExtraField& field ) { + s << field.name; + s << static_cast<int>( field.type ); + return s; +} diff --git a/kdecore/sycoca/kprotocolinfo.h b/kdecore/sycoca/kprotocolinfo.h new file mode 100644 index 0000000..ec42745 --- /dev/null +++ b/kdecore/sycoca/kprotocolinfo.h @@ -0,0 +1,378 @@ +/* This file is part of the KDE libraries + Copyright (C) 1999 Torben Weis <weis@kde.org> + Copyright (C) 2000-2001 Waldo Bastian <bastian@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. +*/ +#ifndef KPROTOCOLINFO_H +#define KPROTOCOLINFO_H + +#include <kglobal.h> + +#include <kurl.h> +#include <ksycocaentry.h> +#include <ksycocatype.h> +#include <QtCore/QVariant> +#include <QtCore/QStringList> + +class QDataStream; +class KProtocolInfoPrivate; + +/** + * \class KProtocolInfo kprotocolinfo.h <KProtocolInfo> + * + * Information about I/O (Internet, etc.) protocols supported by KDE. + + * This class is useful if you want to know which protocols + * KDE supports. In addition you can find out lots of information + * about a certain protocol. A KProtocolInfo instance represents a + * single protocol. Most of the functionality is provided by the static + * methods that scan the *.protocol files of all installed kioslaves to get + * this information. + * + * *.protocol files are installed in the "services" resource. + * + * @author Torben Weis <weis@kde.org> + */ +class KDECORE_EXPORT KProtocolInfo : public KSycocaEntry +{ + friend class KProtocolInfoFactory; + friend class KBuildProtocolInfoFactory; + friend class KProtocolManager; +public: + typedef KSharedPtr<KProtocolInfo> Ptr; + typedef QList<Ptr> List; + +public: + + // + // Static functions: + // + + /** + * Returns list of all known protocols. + * @return a list of all known protocols + */ + static QStringList protocols(); + + /** + * Returns whether a protocol is installed that is able to handle @p url. + * + * @param url the url to check + * @return true if the protocol is known + * @see name() + */ + static bool isKnownProtocol( const KUrl &url ); + + /** + * Same as above except you can supply just the protocol instead of + * the whole URL. + */ + static bool isKnownProtocol( const QString& protocol ); + + /** + * Returns the library / executable to open for the protocol @p protocol + * Example : "kio_ftp", meaning either the executable "kio_ftp" or + * the library "kio_ftp.la" (recommended), whichever is available. + * + * This corresponds to the "exec=" field in the protocol description file. + * @param protocol the protocol to check + * @return the executable of library to open, or QString() for + * unsupported protocols + * @see KUrl::protocol() + */ + static QString exec( const QString& protocol ); + + /** + * Describes the type of a protocol. + * For instance ftp:// appears as a filesystem with folders and files, + * while bzip2:// appears as a single file (a stream of data), + * and telnet:// doesn't output anything. + * @see outputType + */ + enum Type { T_STREAM, ///< stream of data (e.g. single file) + T_FILESYSTEM, ///< structured directory + T_NONE, ///< no information about the type available + T_ERROR ///< used to signal an error + }; + + /** + * Definition of an extra field in the UDS entries, returned by a listDir operation. + * + * The name is the name of the column, translated. + * + * The type name comes from QVariant::typeName() + * Currently supported types: "QString", "QDateTime" (ISO-8601 format) + */ + struct ExtraField { + + enum Type { String = QVariant::String, DateTime = QVariant::DateTime, Invalid = QVariant::Invalid }; + + ExtraField() : type(Invalid) {} + ExtraField(const QString& _name, Type _type ) + : name(_name), type(_type) { + } + QString name; + Type type; + }; + typedef QList<ExtraField> ExtraFieldList; + /** + * Definition of extra fields in the UDS entries, returned by a listDir operation. + * + * This corresponds to the "ExtraNames=" and "ExtraTypes=" fields in the protocol description file. + * Those two lists should be separated with ',' in the protocol description file. + * See ExtraField for details about names and types + */ + static ExtraFieldList extraFields( const KUrl& url ); + + /** + * Returns whether the protocol can act as a helper protocol. + * A helper protocol invokes an external application and does not return + * a file or stream. + * + * This corresponds to the "helper=" field in the protocol description file. + * Valid values for this field are "true" or "false" (default). + * + * @param url the url to check + * @return true if the protocol is a helper protocol (e.g. vnc), false + * if not (e.g. http) + */ + static bool isHelperProtocol( const KUrl &url ); + + /** + * Same as above except you can supply just the protocol instead of + * the whole URL. + */ + static bool isHelperProtocol( const QString& protocol ); + + /** + * Returns whether the protocol can act as a filter protocol. + * + * A filter protocol can operate on data that is passed to it + * but does not retrieve/store data itself, like gzip. + * A filter protocol is the opposite of a source protocol. + * + * The "source=" field in the protocol description file determines + * whether a protocol is a source protocol or a filter protocol. + * Valid values for this field are "true" (default) for source protocol or + * "false" for filter protocol. + * + * @param url the url to check + * @return true if the protocol is a filter (e.g. gzip), false if the + * protocol is a helper or source + */ + static bool isFilterProtocol( const KUrl &url ); + + /** + * Same as above except you can supply just the protocol instead of + * the whole URL. + */ + static bool isFilterProtocol( const QString& protocol ); + + /** + * Returns the name of the icon, associated with the specified protocol. + * + * This corresponds to the "Icon=" field in the protocol description file. + * + * @param protocol the protocol to check + * @return the icon of the protocol, or an empty string if unknown + */ + static QString icon( const QString& protocol ); + + /** + * Returns the name of the config file associated with the + * specified protocol. This is useful if two similar protocols + * need to share a single config file, e.g. http and https. + * + * This corresponds to the "config=" field in the protocol description file. + * The default is the protocol name, see name() + * + * @param protocol the protocol to check + * @return the config file, or an empty string if unknown + */ + static QString config( const QString& protocol ); + + /** + * Returns the soft limit on the number of slaves for this protocol. + * This limits the number of slaves used for a single operation, note + * that multiple operations may result in a number of instances that + * exceeds this soft limit. + * + * This corresponds to the "maxInstances=" field in the protocol description file. + * The default is 1. + * + * @param protocol the protocol to check + * @return the maximum number of slaves, or 1 if unknown + */ + static int maxSlaves( const QString& protocol ); + + + /** + * Returns the limit on the number of slaves for this protocol per host. + * + * This corresponds to the "maxInstancesPerHost=" field in the protocol description file. + * The default is 0 which means there is no per host limit. + * + * @param protocol the protocol to check + * @return the maximum number of slaves, or 1 if unknown + * + * @since 4.4 + */ + static int maxSlavesPerHost( const QString& protocol ); + + /** + * Returns whether mimetypes can be determined based on extension for this + * protocol. For some protocols, e.g. http, the filename extension in the URL + * can not be trusted to truly reflect the file type. + * + * This corresponds to the "determineMimetypeFromExtension=" field in the protocol description file. + * Valid values for this field are "true" (default) or "false". + * + * @param protocol the protocol to check + * @return true if the mime types can be determined by extension + */ + static bool determineMimetypeFromExtension( const QString &protocol ); + + /** + * Returns the documentation path for the specified protocol. + * + * This corresponds to the "X-DocPath=" or "DocPath=" field in the protocol description file. + * + * @param protocol the protocol to check + * @return the docpath of the protocol, or an empty string if unknown + */ + static QString docPath( const QString& protocol ); + + /** + * Returns the protocol class for the specified protocol. + * + * This corresponds to the "Class=" field in the protocol description file. + * + * The following classes are defined: + * @li ":internet" for common internet protocols + * @li ":local" for protocols that access local resources + * + * Protocol classes always start with a ':' so that they can not be confused with + * the protocols themselves. + * + * @param protocol the protocol to check + * @return the class of the protocol, or an empty string if unknown + */ + static QString protocolClass( const QString& protocol ); + + /** + * Returns whether file previews should be shown for the specified protocol. + * + * This corresponds to the "ShowPreviews=" field in the protocol description file. + * + * By default previews are shown if protocolClass is :local. + * + * @param protocol the protocol to check + * @return true if previews should be shown by default, false otherwise + */ + static bool showFilePreview( const QString& protocol ); + + /** + * Returns the list of capabilities provided by the kioslave implementing + * this protocol. + * + * This corresponds to the "Capabilities=" field in the protocol description file. + * + * The capability names are not defined globally, they are up to each + * slave implementation. For example when adding support for a new + * special command for mounting, one would add the string "Mount" to the + * capabilities list, and applications could check for that string + * before sending a special() command that would otherwise do nothing + * on older kioslave implementations. + * + * @param protocol the protocol to check + * @return the list of capabilities. + */ + static QStringList capabilities( const QString& protocol ); + + /** + * Returns the name of the protocol through which the request + * will be routed if proxy support is enabled. + * + * A good example of this is the ftp protocol for which proxy + * support is commonly handled by the http protocol. + * + * This corresponds to the "ProxiedBy=" in the protocol description file. + */ + static QString proxiedBy( const QString& protocol ); + +public: + // Internal functions: + /** + * @internal construct a KProtocolInfo from a stream + */ + KProtocolInfo( QDataStream& _str, int offset); + + virtual ~KProtocolInfo(); + + typedef enum { Name, FromUrl, DisplayName } FileNameUsedForCopying; + + /// @internal. Use KProtocolManager instead. + bool supportsListing() const; + /// @internal. Use KProtocolManager instead. + QString defaultMimeType() const; + /// @internal. Use KProtocolManager instead. + QStringList archiveMimeTypes() const; + +protected: + QString m_name; + QString m_exec; + Type m_inputType; + Type m_outputType; + QStringList m_listing; + bool m_isSourceProtocol; + bool m_isHelperProtocol; + bool m_supportsListing; + bool m_supportsReading; + bool m_supportsWriting; + bool m_supportsMakeDir; + bool m_supportsDeleting; + bool m_supportsLinking; + bool m_supportsMoving; + bool m_supportsOpening; + QString m_defaultMimetype; + bool m_determineMimetypeFromExtension; + QString m_icon; + bool m_canCopyFromFile; + bool m_canCopyToFile; + QString m_config; + int m_maxSlaves; + + bool canRenameFromFile() const; + bool canRenameToFile() const; + bool canDeleteRecursive() const; + FileNameUsedForCopying fileNameUsedForCopying() const; + +private: + /** + * Read a protocol description file + * @param path the path of the description file + */ + KProtocolInfo( const QString & path); + + Q_DECLARE_PRIVATE(KProtocolInfo) + + void load(QDataStream &s); +}; + +KDECORE_EXPORT QDataStream& operator>>( QDataStream& s, KProtocolInfo::ExtraField& field ); +KDECORE_EXPORT QDataStream& operator<<( QDataStream& s, const KProtocolInfo::ExtraField& field ); + +#endif diff --git a/kdecore/sycoca/kprotocolinfo_p.h b/kdecore/sycoca/kprotocolinfo_p.h new file mode 100644 index 0000000..e6ea150 --- /dev/null +++ b/kdecore/sycoca/kprotocolinfo_p.h @@ -0,0 +1,65 @@ +/* This file is part of the KDE libraries + Copyright (C) 1999 Torben Weis <weis@kde.org> + Copyright (C) 2000-2001 Waldo Bastian <bastian@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. +*/ +#ifndef KPROTOCOLINFOPRIVATE_H +#define KPROTOCOLINFOPRIVATE_H + +#include "kprotocolinfo.h" + +#include <ksycocaentry_p.h> + +class KProtocolInfoPrivate : public KSycocaEntryPrivate +{ +public: + K_SYCOCATYPE( KST_KProtocolInfo, KSycocaEntryPrivate ) + + KProtocolInfoPrivate(const QString &path, KProtocolInfo *q_) + : KSycocaEntryPrivate(path), q(q_) + { + } + KProtocolInfoPrivate(QDataStream& _str, int offset, KProtocolInfo *q_) + : KSycocaEntryPrivate(_str, offset), q(q_) + { + } + + virtual void save(QDataStream &s); + + virtual QString name() const + { + return q->m_name; + } + + + KProtocolInfo *q; + QString docPath; + QString protClass; + QStringList archiveMimetype; + KProtocolInfo::ExtraFieldList extraFields; + bool showPreviews : 1; + bool canRenameFromFile : 1; + bool canRenameToFile : 1; + bool canDeleteRecursive : 1; + KProtocolInfo::FileNameUsedForCopying fileNameUsedForCopying; + //KUrl::URIMode uriMode; + QStringList capabilities; + QString proxyProtocol; + int maxSlavesPerHost; +}; + + +#endif diff --git a/kdecore/sycoca/kprotocolinfofactory.cpp b/kdecore/sycoca/kprotocolinfofactory.cpp new file mode 100644 index 0000000..a47af93 --- /dev/null +++ b/kdecore/sycoca/kprotocolinfofactory.cpp @@ -0,0 +1,122 @@ +/* This file is part of the KDE libraries + Copyright (C) 1999 Torben Weis <weis@kde.org> + Copyright (C) 2003 Waldo Bastian <bastian@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 "kprotocolinfofactory.h" + +#include <kstandarddirs.h> +#include <kdebug.h> +#include <ksycoca.h> +#include <ksycocadict_p.h> + +K_GLOBAL_STATIC(KSycocaFactorySingleton<KProtocolInfoFactory>, kProtocolInfoFactoryInstance) + +KProtocolInfoFactory::KProtocolInfoFactory() : KSycocaFactory( KST_KProtocolInfoFactory ) +{ + kProtocolInfoFactoryInstance->instanceCreated(this); +} + +KProtocolInfoFactory::~KProtocolInfoFactory() +{ + if (kProtocolInfoFactoryInstance.exists()) + kProtocolInfoFactoryInstance->instanceDestroyed(this); +} + +KProtocolInfo* +KProtocolInfoFactory::createEntry(int offset) const +{ + KProtocolInfo *info = 0; + KSycocaType type; + QDataStream *str = KSycoca::self()->findEntry(offset, type); + switch (type) + { + case KST_KProtocolInfo: + info = new KProtocolInfo(*str, offset); + break; + default: + return 0; + } + if (!info->isValid()) + { + delete info; + info = 0; + } + return info; +} + + +QStringList KProtocolInfoFactory::protocols() const +{ + QStringList res; + const KSycocaEntry::List list = allEntries(); + for( KSycocaEntry::List::const_iterator it = list.begin(); + it != list.end(); + ++it) { + const KSycocaEntry *entry = (*it).data(); + const KProtocolInfo *info = static_cast<const KProtocolInfo *>(entry); + res.append( info->name() ); + } + return res; +} + +KProtocolInfo::List KProtocolInfoFactory::allProtocols() const +{ + KProtocolInfo::List result; + const KSycocaEntry::List list = allEntries(); + for( KSycocaEntry::List::ConstIterator it = list.begin(); + it != list.end(); + ++it) { + if ((*it)->isType(KST_KProtocolInfo)) { + result.append(KProtocolInfo::Ptr::staticCast(*it)); + } + } + return result; +} + +KProtocolInfo::Ptr KProtocolInfoFactory::findProtocol(const QString &protocol) +{ + if (!sycocaDict()) return KProtocolInfo::Ptr(); // Error! + + QMap<QString,KProtocolInfo::Ptr>::iterator it = m_cache.find(protocol); + if (it != m_cache.end()) + return *it; + + int offset; + + offset = sycocaDict()->find_string( protocol ); + + if (!offset) return KProtocolInfo::Ptr(); // Not found; + + KProtocolInfo::Ptr info(createEntry(offset)); + + if (info && (info->name() != protocol)) + { + // No it wasn't... + return KProtocolInfo::Ptr(); // Not found + } + m_cache.insert(protocol,info); + return info; +} + +void KProtocolInfoFactory::virtual_hook( int id, void* data ) +{ KSycocaFactory::virtual_hook( id, data ); } + +KProtocolInfoFactory* KProtocolInfoFactory::self() +{ + return kProtocolInfoFactoryInstance->self(); +} diff --git a/kdecore/sycoca/kprotocolinfofactory.h b/kdecore/sycoca/kprotocolinfofactory.h new file mode 100644 index 0000000..c0d5a2a --- /dev/null +++ b/kdecore/sycoca/kprotocolinfofactory.h @@ -0,0 +1,99 @@ +/* This file is part of the KDE libraries + Copyright (C) 1999 Torben Weis <weis@kde.org> + Copyright (C) 2000,2003 Waldo Bastian <bastian@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. +*/ +#ifndef kprotocolinfofactory_h +#define kprotocolinfofactory_h + +#include "kprotocolinfo.h" + +#include <QtCore/QMap> +#include <QtCore/QString> +#include <QtCore/QStringList> +#include <kurl.h> +#include <ksycocafactory.h> + + +/** + * @internal + * + * KProtocolInfoFactory is a factory for getting + * KProtocolInfo. The factory is a singleton + * (only one instance can exist). + * + * @short Factory for KProtocolInfo + * + * Exported for kbuildsycoca, but not installed. + */ +class KDECORE_EXPORT KProtocolInfoFactory : public KSycocaFactory +{ + K_SYCOCAFACTORY( KST_KProtocolInfoFactory ) +public: + /** + * The instance of the KProtocolInfoFactory. + * @return the factory instance + */ + static KProtocolInfoFactory* self(); + + /** \internal */ + KProtocolInfoFactory(); + virtual ~KProtocolInfoFactory(); + + /* + * Returns protocol info for @p protocol. + * + * Does not take proxy settings into account. + * @param protocol the protocol to search for + * @return the pointer to the KProtocolInfo, or 0 if not found + */ + KProtocolInfo::Ptr findProtocol(const QString &protocol); + + /** + * @return all protocols + */ + KProtocolInfo::List allProtocols() const; + + /** + * Returns list of all known protocols. + * @return a list of all protocols + */ + QStringList protocols() const; + +protected: + /** + * @internal Not used. + */ + virtual KSycocaEntry *createEntry(const QString &, const char *) const + { return 0; } + + /** + * @internal + */ + virtual KProtocolInfo *createEntry(int offset) const; + +protected: + /** Virtual hook, used to add new "virtual" functions while maintaining + binary compatibility. Unused in this class. + */ + virtual void virtual_hook( int id, void* data ); +private: + QMap<QString,KProtocolInfo::Ptr> m_cache; + class KProtocolInfoFactoryPrivate* d; +}; + +#endif diff --git a/kdecore/sycoca/ksycoca.cpp b/kdecore/sycoca/ksycoca.cpp new file mode 100644 index 0000000..bba13d8 --- /dev/null +++ b/kdecore/sycoca/ksycoca.cpp @@ -0,0 +1,604 @@ +/* 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 "ktoolinvocation.h" +#include "kglobal.h" +#include "kmemfile.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 <config.h> + +#include <stdlib.h> +#include <fcntl.h> + +#include "ksycocadevices_p.h" + +// TODO: remove mmap() from kdewin32 and use QFile::mmap() when needed +#ifdef Q_WS_WIN +#undef HAVE_MMAP +#endif +/** + * Sycoca file version number. + * If the existing file is outdated, it will not get read + * but instead we'll ask kded to regenerate a new one... + */ +#define KSYCOCA_VERSION 205 + +/** + * Sycoca file name, used internally (by kbuildsycoca) + */ +#define KSYCOCA_FILENAME "ksycoca4" + +#if HAVE_MADVISE +#include <sys/mman.h> // This #include was checked when looking for posix_madvise +#endif + +#ifndef MAP_FAILED +#define MAP_FAILED ((void *) -1) +#endif + +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 ), + sycoca_size(0), + sycoca_mmap(0), + m_mmapFile(0), + m_device(0) +{ +#ifdef Q_OS_WIN + /* + on windows we use KMemFile (QSharedMemory) to avoid problems + with mmap (can't delete a mmap'd file) + */ + m_sycocaStrategy = StrategyMemFile; +#else + m_sycocaStrategy = StrategyMmap; +#endif + KConfigGroup config(KGlobal::config(), "KSycoca"); + setStrategyFromString(config.readEntry("strategy")); +} + +void KSycocaPrivate::setStrategyFromString(const QString& strategy) { + if (strategy == QLatin1String("mmap")) + m_sycocaStrategy = StrategyMmap; + else if (strategy == QLatin1String("file")) + m_sycocaStrategy = StrategyFile; + else if (strategy == QLatin1String("sharedmem")) + m_sycocaStrategy = StrategyMemFile; + else if (!strategy.isEmpty()) + kWarning(7011) << "Unknown sycoca strategy:" << strategy; +} + +bool KSycocaPrivate::tryMmap() +{ +#ifdef HAVE_MMAP + Q_ASSERT(!m_databasePath.isEmpty()); + m_mmapFile = new QFile(m_databasePath); + const bool canRead = m_mmapFile->open(QIODevice::ReadOnly); + Q_ASSERT(canRead); + Q_UNUSED(canRead); // no compiler warning in release builds. + fcntl(m_mmapFile->handle(), F_SETFD, FD_CLOEXEC); + sycoca_size = m_mmapFile->size(); + sycoca_mmap = (const char *) mmap(0, sycoca_size, + PROT_READ, MAP_SHARED, + m_mmapFile->handle(), 0); + /* POSIX mandates only MAP_FAILED, but we are paranoid so check for + null pointer too. */ + if (sycoca_mmap == (const char*) MAP_FAILED || sycoca_mmap == 0) { + kDebug(7011) << "mmap failed. (length = " << sycoca_size << ")"; + sycoca_mmap = 0; + return false; + } else { +#ifdef HAVE_MADVISE + (void) posix_madvise((void*)sycoca_mmap, sycoca_size, POSIX_MADV_WILLNEED); +#endif // HAVE_MADVISE + return true; + } +#endif // HAVE_MMAP + return false; +} + +int KSycoca::version() +{ + return KSYCOCA_VERSION; +} + +class KSycocaSingleton +{ +public: + KSycocaSingleton() { } + ~KSycocaSingleton() { } + + bool hasSycoca() const { + return m_threadSycocas.hasLocalData(); + } + KSycoca* sycoca() { + if (!m_threadSycocas.hasLocalData()) + m_threadSycocas.setLocalData(new KSycoca); + return m_threadSycocas.localData(); + } + void setSycoca(KSycoca* s) { + m_threadSycocas.setLocalData(s); + } + +private: + QThreadStorage<KSycoca*> m_threadSycocas; +}; + +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; + QDataStream* str = stream(); + *str << qint32(KSYCOCA_VERSION); + *str << qint32(0); + } 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; + device->device()->open(QIODevice::ReadOnly); // can't fail + } else { +#ifdef HAVE_MMAP + if (m_sycocaStrategy == StrategyMmap && tryMmap()) { + device = new KSycocaMmapDevice(sycoca_mmap, + sycoca_size); + if (!device->device()->open(QIODevice::ReadOnly)) { + delete device; device = 0; + } + } +#endif +#ifndef QT_NO_SHAREDMEMORY + if (!device && m_sycocaStrategy == StrategyMemFile) { + device = new KSycocaMemFileDevice(m_databasePath); + if (!device->device()->open(QIODevice::ReadOnly)) { + delete device; device = 0; + } + } +#endif + 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(); +#ifdef HAVE_MMAP + if (sycoca_mmap) { + //QBuffer *buf = static_cast<QBuffer*>(device); + //buf->buffer().clear(); + // Solaris has munmap(char*, size_t) and everything else should + // be happy with a char* for munmap(void*, size_t) + munmap(const_cast<char*>(sycoca_mmap), sycoca_size); + sycoca_mmap = 0; + } + delete m_mmapFile; m_mmapFile = 0; +#endif + + databaseStatus = DatabaseNotOpen; + timeStamp = 0; +} + +void KSycoca::addFactory( KSycocaFactory *factory ) +{ + d->addFactory(factory); +} + +#ifndef KDE_NO_DEPRECATED +bool KSycoca::isChanged(const char *type) +{ + return self()->d->changeList.contains(QString::fromLatin1(type)); +} +#endif + +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 +#ifndef KDE_NO_DEPRECATED + emit databaseChanged(); +#endif + 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; + *m_str >> aVersion; + if ( aVersion < KSYCOCA_VERSION ) { + kWarning(7011) << "Found version" << aVersion << ", expecting version" << KSYCOCA_VERSION << "or higher."; + databaseStatus = BadVersion; + return false; + } else { + databaseStatus = DatabaseOK; + return true; + } +} + +// If it returns true, we have a valid database and the stream has rewinded to the beginning +// and past the version number. +bool KSycocaPrivate::checkDatabase(BehaviorsIfNotFound ifNotFound) +{ + if (databaseStatus == DatabaseOK) { + if (checkVersion()) // we know the version is ok, but we must rewind the stream anyway + return true; + } + + closeDatabase(); // close the dummy one + + // We can only use the installed ksycoca file if kdeinit+klauncher+kded are running, + // since kded is what keeps the file uptodate. + const bool kdeinitRunning = QDBusConnection::sessionBus().interface()->isServiceRegistered(QString::fromLatin1("org.kde.klauncher")); + + // Check if new database already available + if (kdeinitRunning && openDatabase(ifNotFound & IfNotFoundOpenDummy)) { + if (checkVersion()) { + // Database exists, and version is ok. + return true; + } + } + + if (ifNotFound & IfNotFoundRecreate) { + // Well, if kdeinit is not running we need to launch it, + // but otherwise we simply need to run kbuildsycoca to recreate the sycoca file. + if (!kdeinitRunning) { + kDebug(7011) << "We have no database.... launching kdeinit"; + KToolInvocation::klauncher(); // this calls startKdeinit, and blocks until it returns + // and since kdeinit4 only returns after kbuildsycoca4 is done, we can proceed. + } else { + kDebug(7011) << QThread::currentThread() << "We have no database.... launching" << KBUILDSYCOCA_EXENAME; + if (QProcess::execute(KStandardDirs::findExe(QString::fromLatin1(KBUILDSYCOCA_EXENAME))) != 0) + qWarning("ERROR: 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 + } + 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 + break; // just read 0 + } + // 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; + } + + const QByteArray ksycoca_env = qgetenv("KDESYCOCA"); + if (ksycoca_env.isEmpty()) + return KGlobal::dirs()->saveLocation("cache") + QString::fromLatin1(KSYCOCA_FILENAME); + else + return QFile::decodeName(ksycoca_env); +} + +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) << "ERROR: 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) + qWarning("ERROR: Running %s failed", KBUILDSYCOCA_EXENAME); + // 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... + } +} + +#ifndef KDE_NO_DEPRECATED +bool KSycoca::readError() // KDE5: remove +{ + return false; +} +#endif + +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 "ksycoca.moc" diff --git a/kdecore/sycoca/ksycoca.h b/kdecore/sycoca/ksycoca.h new file mode 100644 index 0000000..1067d70 --- /dev/null +++ b/kdecore/sycoca/ksycoca.h @@ -0,0 +1,235 @@ +/* This file is part of the KDE libraries + * Copyright (C) 1999 Waldo Bastian <bastian@kde.org> + * Copyright (C) 2005-2008 David Faure <faure@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. + **/ + +#ifndef KSYCOCA_H +#define KSYCOCA_H + +#include <kdecore_export.h> +#include <ksycocatype.h> + +#include <QtCore/QObject> +#include <QtCore/QStringList> + +class QDataStream; +class KSycocaFactory; +class KSycocaFactoryList; +class KSycocaPrivate; + +/** + * Executable name of the kbuildsycoca program + */ +#define KBUILDSYCOCA_EXENAME "kbuildsycoca4" + +/** + * @internal + * Read-only SYstem COnfiguration CAche + */ +class KDECORE_EXPORT KSycoca : public QObject +{ + Q_OBJECT + //Q_CLASSINFO("D-Bus Interface", "org.kde.KSycoca") + +protected: + /** + * @internal + * Building database + */ + explicit KSycoca( bool /* buildDatabase */ ); + +public: + /** + * type of database + * @see absoluteFilePath() + */ + typedef enum { LocalDatabase, GlobalDatabase } DatabaseType; + + /** + * Read-only database + */ + KSycoca(); + + /** + * Get or create the only instance of KSycoca (read-only) + */ + static KSycoca *self(); + + virtual ~KSycoca(); + + /** + * @return the compiled-in version, i.e. the one used when writing a new ksycoca + */ + static int version(); + + /** + * @return true if the ksycoca database is available + * This is usually the case, except if KDE isn't installed yet, + * or before kded is started. + */ + static bool isAvailable(); + + /** + * @internal - called by factories in read-only mode + * This is how factories get a stream to an entry + */ + QDataStream *findEntry(int offset, KSycocaType &type); + /** + * @internal - called by factories in read-only mode + * Returns stream(), but positioned for reading this factory, 0 on error. + */ + QDataStream *findFactory(KSycocaFactoryId id); + /** + * @internal - returns kfsstnd stored inside database + */ + QString kfsstnd_prefixes(); + /** + * @internal - returns absolute file path of the database + * + * for global database type the database is searched under + * the 'services' install path. + * Otherwise, the value from the environment variable KDESYCOCA + * is returned if set. If not set the path is build based on + * KStandardDirs cache save location. + */ + static QString absoluteFilePath(DatabaseType type=LocalDatabase); + /** + * @internal - returns language stored inside database + */ + QString language(); + + /** + * @internal - returns timestamp of database + * + * The database contains all changes made _before_ this time and + * _might_ contain changes made after that. + */ + quint32 timeStamp(); + + /** + * @internal - returns update signature of database + * + * Signature that keeps track of changes to + * $KDEDIR/share/services/update_ksycoca + * + * Touching this file causes the database to be recreated + * from scratch. + */ + quint32 updateSignature(); + + /** + * @internal - returns all directories with information + * stored inside sycoca. + */ + QStringList allResourceDirs(); + + /** + * @internal - add a factory + */ + void addFactory( KSycocaFactory * ); + + /** + * @internal + * @return true if building (i.e. if a KBuildSycoca); + */ + virtual bool isBuilding(); + + /** + * @internal - disables launching of kbuildsycoca + */ + static void disableAutoRebuild(); + + /** + * When you receive a "databaseChanged" signal, you can query here if + * a change has occurred in a specific resource type. + * @see KStandardDirs for the various resource types. + * + * This method is meant to be called from the GUI thread only. + * @deprecated use the signal databaseChanged(QStringList) instead. + */ +#ifndef KDE_NO_DEPRECATED + static KDE_DEPRECATED bool isChanged(const char *type); +#endif + + /** + * A read error occurs. + * @internal + */ + static void flagError(); + + /// @deprecated +#ifndef KDE_NO_DEPRECATED + static KDE_DEPRECATED bool readError(); +#endif + +Q_SIGNALS: + /** + * Connect to this to get notified when the database changes + * @deprecated use the databaseChanged(QStringList) signal + */ +#ifndef KDE_NO_DEPRECATED + QT_MOC_COMPAT void databaseChanged(); // KDE5 TODO: remove +#endif + + /** + * Connect to this to get notified when the database changes + * Example: when mimetype definitions have changed, applications showing + * files as icons refresh icons to take into account the new mimetypes. + * Another example: after creating a .desktop file in KOpenWithDialog, + * it must wait until kbuildsycoca4 finishes until the KService::Ptr is available. + * + * @param changedResources List of resources where changes were detected. + * This can include the following resources (as defined in KStandardDirs) : + * apps, xdgdata-apps, services, servicetypes, xdgdata-mime. + */ + void databaseChanged(const QStringList& changedResources); + +protected: + // @internal used by kbuildsycoca + KSycocaFactoryList* factories(); + + // @internal was for kbuildsycoca +#ifndef KDE_NO_DEPRECATED + QDataStream *m_str_deprecated; // KDE5: REMOVE +#endif + // @internal used by factories and kbuildsycoca + QDataStream*& stream(); + friend class KSycocaFactory; + friend class KSycocaDict; + +private Q_SLOTS: + /** + * internal function for receiving kbuildsycoca's signal, when the sycoca file changes + */ + void notifyDatabaseChanged(const QStringList &); + +private: + /** + * Clear all caches related to ksycoca contents. + * @internal only used by kded and kbuildsycoca. + */ + static void clearCaches(); + friend class KBuildSycoca; + friend class Kded; + + Q_DISABLE_COPY(KSycoca) + friend class KSycocaPrivate; + KSycocaPrivate * const d; +}; + +#endif + diff --git a/kdecore/sycoca/ksycoca_p.h b/kdecore/sycoca/ksycoca_p.h new file mode 100644 index 0000000..0d88f10 --- /dev/null +++ b/kdecore/sycoca/ksycoca_p.h @@ -0,0 +1,79 @@ +/* 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. + **/ + +#ifndef KSYCOCA_P_H +#define KSYCOCA_P_H + +#include "ksycocafactory.h" +#include <QStringList> +class QFile; +class QDataStream; +class KSycocaAbstractDevice; + +class KSycocaPrivate +{ +public: + KSycocaPrivate(); + bool checkVersion(); + bool openDatabase(bool openDummyIfNotFound=true); + enum BehaviorIfNotFound { + IfNotFoundDoNothing = 0, + IfNotFoundOpenDummy = 1, + IfNotFoundRecreate = 2 + }; + Q_DECLARE_FLAGS(BehaviorsIfNotFound, BehaviorIfNotFound) + bool checkDatabase(BehaviorsIfNotFound ifNotFound); + void closeDatabase(); + void setStrategyFromString(const QString& strategy); + bool tryMmap(); + + KSycocaAbstractDevice* device(); + QDataStream*& stream(); + + enum { + DatabaseNotOpen, // openDatabase must be called + NoDatabase, // not found, so we opened a dummy one instead + BadVersion, // it's opened, but it's not useable + DatabaseOK } databaseStatus; + bool readError; + + quint32 timeStamp; + enum { StrategyMmap, StrategyMemFile, StrategyFile, StrategyDummyBuffer } m_sycocaStrategy; + QString m_databasePath; + QStringList changeList; + QString language; + quint32 updateSig; + QStringList allResourceDirs; + + void addFactory(KSycocaFactory* factory) { + m_factories.append(factory); + } + KSycocaFactoryList* factories() { return &m_factories; } + +private: + KSycocaFactoryList m_factories; + size_t sycoca_size; + const char *sycoca_mmap; + QFile* m_mmapFile; + KSycocaAbstractDevice* m_device; +}; + +#endif /* KSYCOCA_P_H */ + diff --git a/kdecore/sycoca/ksycocadevices_p.h b/kdecore/sycoca/ksycocadevices_p.h new file mode 100644 index 0000000..47e13cd --- /dev/null +++ b/kdecore/sycoca/ksycocadevices_p.h @@ -0,0 +1,125 @@ +/* This file is part of the KDE project + + Copyright 2009 David Faure <faure@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 as published + by the Free Software Foundation; either version 2 of the License or + ( at your option ) version 3 or, at the discretion of KDE e.V. + ( which shall act as a proxy as in section 14 of the GPLv3 ), any later version. + + 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. +*/ + +#ifndef KSYCOCADEVICES_P_H +#define KSYCOCADEVICES_P_H + +class KSycocaAbstractDevice +{ +public: + KSycocaAbstractDevice() : m_stream(0) + { + } + + virtual ~KSycocaAbstractDevice() { delete m_stream; } + + virtual QIODevice* device() = 0; + + QDataStream* & stream() { + if (!m_stream) { + m_stream = new QDataStream(device()); + m_stream->setVersion(QDataStream::Qt_3_1); + } + return m_stream; + } + +private: + QDataStream* m_stream; +}; + +#ifdef HAVE_MMAP +// Reading from a mmap'ed file +class KSycocaMmapDevice : public KSycocaAbstractDevice +{ +public: + KSycocaMmapDevice(const char* sycoca_mmap, size_t sycoca_size) { + m_buffer = new QBuffer; + m_buffer->setData(QByteArray::fromRawData(sycoca_mmap, sycoca_size)); + } + ~KSycocaMmapDevice() { + delete m_buffer; + } + virtual QIODevice* device() { + return m_buffer; + } +private: + QBuffer* m_buffer; +}; +#endif + +// Reading from a QFile +class KSycocaFileDevice : public KSycocaAbstractDevice +{ +public: + KSycocaFileDevice(const QString& path) { + m_database = new QFile(path); +#ifndef Q_OS_WIN + fcntl(m_database->handle(), F_SETFD, FD_CLOEXEC); +#endif + } + ~KSycocaFileDevice() { + delete m_database; + } + virtual QIODevice* device() { + return m_database; + } +private: + QFile* m_database; +}; + +#ifndef QT_NO_SHAREDMEMORY +// Reading from a KMemFile +class KSycocaMemFileDevice : public KSycocaAbstractDevice +{ +public: + KSycocaMemFileDevice(const QString& path) { + m_database = new KMemFile(path); + } + ~KSycocaMemFileDevice() { + delete m_database; + } + virtual QIODevice* device() { + return m_database; + } +private: + KMemFile* m_database; +}; +#endif + +// Reading from a dummy memory buffer +class KSycocaBufferDevice : public KSycocaAbstractDevice +{ +public: + KSycocaBufferDevice() { + m_buffer = new QBuffer; + } + ~KSycocaBufferDevice() { + delete m_buffer; + } + virtual QIODevice* device() { + return m_buffer; + } +private: + QBuffer* m_buffer; +}; + +#endif /* KSYCOCADEVICES_P_H */ + diff --git a/kdecore/sycoca/ksycocadict.cpp b/kdecore/sycoca/ksycocadict.cpp new file mode 100644 index 0000000..2f9a5b2 --- /dev/null +++ b/kdecore/sycoca/ksycocadict.cpp @@ -0,0 +1,562 @@ +/* This file is part of the KDE libraries + * Copyright (C) 1999 Waldo Bastian <bastian@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 "ksycocadict_p.h" +#include <kservice.h> +#include "ksycocaentry.h" +#include "ksycoca.h" +#include "kdebug.h" + +#include <QtCore/QBitArray> + +namespace +{ +struct string_entry { + string_entry(const QString& _key, const KSycocaEntry::Ptr& _payload) + : hash(0), length(_key.length()), keyStr(_key), key(keyStr.unicode()), payload(_payload) + {} + uint hash; + const int length; + const QString keyStr; + const QChar * const key; // always points to keyStr.unicode(); just an optimization + const KSycocaEntry::Ptr payload; +}; +} + +class KSycocaDictStringList : public QList<string_entry*> +{ +public: + ~KSycocaDictStringList() { + qDeleteAll(*this); + } +}; + +class KSycocaDict::Private +{ +public: + Private() + : stringlist( 0 ), + stream( 0 ), + offset( 0 ) + { + } + + ~Private() + { + delete stringlist; + } + + // Helper for find_string and findMultiString + qint32 offsetForKey(const QString& key) const; + + // Calculate hash - can be used during loading and during saving. + quint32 hashKey(const QString & key) const; + + KSycocaDictStringList *stringlist; + QDataStream *stream; + qint64 offset; + quint32 hashTableSize; + QList<qint32> hashList; +}; + +KSycocaDict::KSycocaDict() + : d( new Private ) +{ +} + +KSycocaDict::KSycocaDict(QDataStream *str, int offset) + : d( new Private ) +{ + d->stream = str; + d->offset = offset; + + quint32 test1, test2; + str->device()->seek(offset); + (*str) >> test1 >> test2; + if ((test1 > 0x000fffff) || (test2 > 1024)) + { + KSycoca::flagError(); + d->hashTableSize = 0; + d->offset = 0; + return; + } + + str->device()->seek(offset); + (*str) >> d->hashTableSize; + (*str) >> d->hashList; + d->offset = str->device()->pos(); // Start of hashtable +} + +KSycocaDict::~KSycocaDict() +{ + delete d; +} + +void +KSycocaDict::add(const QString &key, const KSycocaEntry::Ptr& payload) +{ + if (key.isEmpty()) return; // Not allowed (should never happen) + if (!payload) return; // Not allowed! + if (!d->stringlist) + { + d->stringlist = new KSycocaDictStringList; + } + + string_entry *entry = new string_entry(key, payload); + d->stringlist->append(entry); +} + +void +KSycocaDict::remove(const QString &key) +{ + if (!d || !d->stringlist) { + return; + } + + bool found = false; + for(KSycocaDictStringList::Iterator it = d->stringlist->begin(); it != d->stringlist->end(); ++it) { + string_entry* entry = *it; + if (entry->keyStr == key) { + d->stringlist->erase(it); + delete entry; + found = true; + break; + } + } + if (!found) { + kWarning(7011) << "key not found:" << key; + } +} + +int KSycocaDict::find_string(const QString &key ) const +{ + Q_ASSERT(d); + + //kDebug(7011) << QString("KSycocaDict::find_string(%1)").arg(key); + qint32 offset = d->offsetForKey(key); + + //kDebug(7011) << QString("offset is %1").arg(offset,8,16); + if (offset == 0) + return 0; + + if (offset > 0) + return offset; // Positive ID + + // Lookup duplicate list. + offset = -offset; + + d->stream->device()->seek(offset); + //kDebug(7011) << QString("Looking up duplicate list at %1").arg(offset,8,16); + + while(true) + { + (*d->stream) >> offset; + if (offset == 0) break; + QString dupkey; + (*d->stream) >> dupkey; + //kDebug(7011) << QString(">> %1 %2").arg(offset,8,16).arg(dupkey); + if (dupkey == key) return offset; + } + //kWarning(7011) << "Not found!"; + + return 0; +} + + +QList<int> KSycocaDict::findMultiString(const QString &key ) const +{ + qint32 offset = d->offsetForKey(key); + QList<int> offsetList; + if (offset == 0) + return offsetList; + + if (offset > 0) { // Positive ID: one entry found + offsetList.append(offset); + return offsetList; + } + + // Lookup duplicate list. + offset = -offset; + + d->stream->device()->seek(offset); + //kDebug(7011) << QString("Looking up duplicate list at %1").arg(offset,8,16); + + while(true) + { + (*d->stream) >> offset; + if (offset == 0) break; + QString dupkey; + (*d->stream) >> dupkey; + //kDebug(7011) << QString(">> %1 %2").arg(offset,8,16).arg(dupkey); + if (dupkey == key) + offsetList.append(offset); + } + return offsetList; +} + +uint KSycocaDict::count() const +{ + if ( !d || !d->stringlist ) return 0; + + return d->stringlist->count(); +} + +void +KSycocaDict::clear() +{ + delete d; + d = 0; +} + +uint KSycocaDict::Private::hashKey( const QString &key) const +{ + int len = key.length(); + uint h = 0; + + for(int i = 0; i < hashList.count(); i++) + { + int pos = hashList[i]; + if (pos == 0) { + continue; + } else if (pos < 0) { + pos = -pos; + if (pos < len) + h = ((h * 13) + (key[len-pos].cell() % 29)) & 0x3ffffff; + } else { + pos = pos-1; + if (pos < len) + h = ((h * 13) + (key[pos].cell() % 29)) & 0x3ffffff; + } + } + return h; +} + +// If we have the strings +// hello +// world +// kde +// Then we end up with +// ABCDE +// where A = diversity of 'h' + 'w' + 'k' etc. +// Also, diversity(-2) == 'l'+'l'+'d' (second character from the end) + +// The hasList is used for hashing: +// hashList = (-2, 1, 3) means that the hash key comes from +// the 2nd character from the right, then the 1st from the left, then the 3rd from the left. + +// Calculate the diversity of the strings at position 'pos' +// NOTE: this code is slow, it takes 12% of the _overall_ `kbuildsycoca4 --noincremental` running time +static int +calcDiversity(KSycocaDictStringList *stringlist, int inPos, uint sz) +{ + if (inPos == 0) return 0; + QBitArray matrix(sz); + int pos; + + //static const int s_maxItems = 50; + //int numItem = 0; + + if (inPos < 0) { + pos = -inPos; + for(KSycocaDictStringList::const_iterator it = stringlist->constBegin(), end = stringlist->constEnd(); it != end; ++it) + { + string_entry* entry = *it; + int len = entry->length; + if (pos < len) { + uint hash = ((entry->hash * 13) + (entry->key[len-pos].cell() % 29)) & 0x3ffffff; + matrix.setBit( hash % sz, true ); + } + //if (++numItem == s_maxItems) + // break; + } + } else { + pos = inPos-1; + for(KSycocaDictStringList::const_iterator it = stringlist->constBegin(), end = stringlist->constEnd(); it != end; ++it) + { + string_entry* entry = *it; + if (pos < entry->length) { + uint hash = ((entry->hash * 13) + (entry->key[pos].cell() % 29)) & 0x3ffffff; + matrix.setBit( hash % sz, true ); + } + //if (++numItem == s_maxItems) + // break; + } + } + return matrix.count(true); +} + +// +// Add the diversity of the strings at position 'pos' +static void +addDiversity(KSycocaDictStringList *stringlist, int pos) +{ + if (pos == 0) return; + if (pos < 0) { + pos = -pos; + for(KSycocaDictStringList::const_iterator it = stringlist->constBegin(), end = stringlist->constEnd(); it != end; ++it) + { + string_entry* entry = *it; + int len = entry->length; + if (pos < len) + entry->hash = ((entry->hash * 13) + (entry->key[len-pos].cell() % 29)) & 0x3fffffff; + } + } else { + pos = pos - 1; + for(KSycocaDictStringList::const_iterator it = stringlist->constBegin(), end = stringlist->constEnd(); it != end; ++it) + { + string_entry* entry = *it; + if (pos < entry->length) + entry->hash = ((entry->hash * 13) + (entry->key[pos].cell() % 29)) & 0x3fffffff; + } + } +} + + +void +KSycocaDict::save(QDataStream &str) +{ + if (count() == 0) + { + d->hashTableSize = 0; + d->hashList.clear(); + str << d->hashTableSize; + str << d->hashList; + return; + } + + d->offset = str.device()->pos(); + + //kDebug(7011) << "KSycocaDict:" << count() << "entries."; + + //kDebug(7011) << "Calculating hash keys.."; + + int maxLength = 0; + //kDebug(7011) << "Finding maximum string length"; + for(KSycocaDictStringList::const_iterator it = d->stringlist->constBegin(); it != d->stringlist->constEnd(); ++it) + { + string_entry* entry = *it; + entry->hash = 0; + if (entry->length > maxLength) + maxLength = entry->length; + } + + //kDebug(7011) << "Max string length=" << maxLength << "existing hashList=" << d->hashList; + + // use "almost prime" number for sz (to calculate diversity) and later + // for the table size of big tables + // int sz = d->stringlist->count()*5-1; + register unsigned int sz = count()*4 + 1; + while(!(((sz % 3) && (sz % 5) && (sz % 7) && (sz % 11) && (sz % 13)))) + sz+=2; + + d->hashList.clear(); + + // Times (with warm caches, i.e. after multiple runs) + // kbuildsycoca4 --noincremental 2.83s user 0.20s system 95% cpu 3.187 total + // kbuildsycoca4 --noincremental 2.74s user 0.25s system 93% cpu 3.205 total + // unittest: 0.50-60 msec per iteration / 0.40-50 msec per iteration + + // Now that MimeTypes are not parsed anymore: + // kbuildsycoca4 --noincremental 2.18s user 0.30s system 91% cpu 2.719 total + // kbuildsycoca4 --noincremental 2.07s user 0.34s system 89% cpu 2.681 total + + // If I enabled s_maxItems = 50, it goes down to + // but I don't know if that's a good idea. + // kbuildsycoca4 --noincremental 1.73s user 0.31s system 85% cpu 2.397 total + // kbuildsycoca4 --noincremental 1.84s user 0.29s system 95% cpu 2.230 total + + // try to limit diversity scan by "predicting" positions + // with high diversity + QVector<int> oldvec(maxLength*2+1); + oldvec.fill(0); + int mindiv=0; + int lastDiv = 0; + + while(true) + { + int divsum=0,divnum=0; + + int maxDiv = 0; + int maxPos = 0; + for (int pos = -maxLength; pos <= maxLength; ++pos) { + // cut off + if (oldvec[pos+maxLength] < mindiv) { oldvec[pos+maxLength]=0; continue; } + + const int diversity = calcDiversity(d->stringlist, pos, sz); + if (diversity > maxDiv) { + maxDiv = diversity; + maxPos = pos; + } + oldvec[pos + maxLength] = diversity; + divsum += diversity; + ++divnum; + } + // arbitrary cut-off value 3/4 of average seems to work + if (divnum) + mindiv=(3*divsum)/(4*divnum); + + if (maxDiv <= lastDiv) + break; + //kDebug() << "Max Div=" << maxDiv << "at pos" << maxPos; + lastDiv = maxDiv; + addDiversity(d->stringlist, maxPos); + d->hashList.append(maxPos); + } + + + for(KSycocaDictStringList::Iterator it = d->stringlist->begin(); it != d->stringlist->end(); ++it) { + (*it)->hash = d->hashKey((*it)->keyStr); + } +// fprintf(stderr, "Calculating minimum table size..\n"); + + d->hashTableSize = sz; + + //kDebug() << "hashTableSize=" << sz << "hashList=" << d->hashList << "oldvec=" << oldvec; + + struct hashtable_entry { + string_entry *entry; + QList<string_entry*>* duplicates; + qint64 duplicate_offset; + }; + + hashtable_entry *hashTable = new hashtable_entry[ sz ]; + + //kDebug(7011) << "Clearing hashtable..."; + for (unsigned int i=0; i < sz; i++) + { + hashTable[i].entry = 0; + hashTable[i].duplicates = 0; + } + + //kDebug(7011) << "Filling hashtable..."; + for(KSycocaDictStringList::const_iterator it = d->stringlist->constBegin(); it != d->stringlist->constEnd(); ++it) + { + string_entry* entry = *it; + //kDebug(7011) << "entry keyStr=" << entry->keyStr << entry->payload.data() << entry->payload->entryPath(); + int hash = entry->hash % sz; + if (!hashTable[hash].entry) + { // First entry + hashTable[hash].entry = entry; + } + else + { + if (!hashTable[hash].duplicates) + { // Second entry, build duplicate list. + hashTable[hash].duplicates = new QList<string_entry*>; + hashTable[hash].duplicates->append(hashTable[hash].entry); + hashTable[hash].duplicate_offset = 0; + } + hashTable[hash].duplicates->append(entry); + } + } + + str << d->hashTableSize; + str << d->hashList; + + d->offset = str.device()->pos(); // d->offset points to start of hashTable + //kDebug(7011) << QString("Start of Hash Table, offset = %1").arg(d->offset,8,16); + + // Write the hashtable + the duplicates twice. + // The duplicates are after the normal hashtable, but the offset of each + // duplicate entry is written into the normal hashtable. + for(int pass = 1; pass <= 2; pass++) + { + str.device()->seek(d->offset); + //kDebug(7011) << QString("Writing hash table (pass #%1)").arg(pass); + for(uint i=0; i < d->hashTableSize; i++) + { + qint32 tmpid; + if (!hashTable[i].entry) + tmpid = (qint32) 0; + else if (!hashTable[i].duplicates) + tmpid = (qint32) hashTable[i].entry->payload->offset(); // Positive ID + else + tmpid = (qint32) -hashTable[i].duplicate_offset; // Negative ID + str << tmpid; + //kDebug(7011) << QString("Hash table : %1").arg(tmpid,8,16); + } + //kDebug(7011) << QString("End of Hash Table, offset = %1").arg(str.device()->at(),8,16); + + //kDebug(7011) << QString("Writing duplicate lists (pass #%1)").arg(pass); + for(uint i=0; i < d->hashTableSize; i++) + { + const QList<string_entry*> *dups = hashTable[i].duplicates; + if (dups) + { + hashTable[i].duplicate_offset = str.device()->pos(); + + /*kDebug(7011) << QString("Duplicate lists: Offset = %1 list_size = %2") .arg(hashTable[i].duplicate_offset,8,16).arg(dups->count()); +*/ + for(QList<string_entry*>::ConstIterator dup = dups->begin(); dup != dups->end(); ++dup) + { + const qint32 offset = (*dup)->payload->offset(); + if (!offset) { + const QString storageId = (*dup)->payload->storageId(); + kDebug() << "about to assert! dict=" << this << "storageId=" << storageId << (*dup)->payload.data(); + if ((*dup)->payload->isType(KST_KService)) { + KService::Ptr service = KService::Ptr::staticCast((*dup)->payload); + kDebug() << service->storageId() << service->entryPath(); + } + // save() must have been called on the entry + Q_ASSERT_X( offset, "KSycocaDict::save", + QByteArray("entry offset is 0, save() was not called on " + + (*dup)->payload->storageId().toLatin1() + + " entryPath=" + + (*dup)->payload->entryPath().toLatin1()) + ); + } + str << offset ; // Positive ID + str << (*dup)->keyStr; // Key (QString) + } + str << (qint32) 0; // End of list marker (0) + } + } + //kDebug(7011) << QString("End of Dict, offset = %1").arg(str.device()->at(),8,16); + } + + //kDebug(7011) << "Cleaning up hash table."; + for(uint i=0; i < d->hashTableSize; i++) + { + delete hashTable[i].duplicates; + } + delete [] hashTable; +} + +qint32 KSycocaDict::Private::offsetForKey(const QString& key) const +{ + if ( !stream || !offset ) + { + kError() << "No ksycoca4 database available!" << endl; + return 0; + } + + if (hashTableSize == 0) + return 0; // Unlikely to find anything :-] + + // Read hash-table data + const uint hash = hashKey(key) % hashTableSize; + //kDebug(7011) << "hash is" << hash; + + const qint32 off = offset+sizeof(qint32)*hash; + //kDebug(7011) << QString("off is %1").arg(off,8,16); + stream->device()->seek( off ); + + qint32 retOffset; + (*stream) >> retOffset; + return retOffset; +} diff --git a/kdecore/sycoca/ksycocadict_p.h b/kdecore/sycoca/ksycocadict_p.h new file mode 100644 index 0000000..43b0072 --- /dev/null +++ b/kdecore/sycoca/ksycocadict_p.h @@ -0,0 +1,135 @@ +/* This file is part of the KDE libraries + * Copyright (C) 1999 Waldo Bastian <bastian@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. + **/ + +#ifndef KSYCOCADICT_H +#define KSYCOCADICT_H + +#include <kdecore_export.h> +#include "ksycocaentry.h" + +#include <QList> + +class QString; +class QDataStream; + +/** + * @internal + * Hash table implementation for the sycoca database file + * + * Only exported for the unit test + */ +class KDECORE_EXPORT KSycocaDict //krazy:exclude=dpointer (not const because it gets deleted by clear()) +{ +public: + /** + * Create an empty dict, for building the database + */ + KSycocaDict(); + /** + * Create a dict from an existing database + */ + KSycocaDict(QDataStream *str, int offset); + + ~KSycocaDict(); + + /** + * Adds a 'payload' to the dictionary with key 'key'. + * + * 'payload' should have a valid offset by the time + * the dictionary gets saved. + **/ + void add(const QString &key, const KSycocaEntry::Ptr& payload); + + /** + * Removes the 'payload' from the dictionary with key 'key'. + * + * Not very fast, use with care O(N) + **/ + void remove(const QString &key); + + /** + * Looks up an entry identified by 'key'. + * + * If 0 is returned, no matching entry exists. + * Otherwise, the offset of the entry is returned. + * + * NOTE: It is not guaranteed that this entry is + * indeed the one you were looking for. + * After loading the entry you should check that it + * indeed matches the search key. If it doesn't + * then no matching entry exists. + */ + int find_string(const QString &key ) const; + + /** + * Looks up all entries identified by 'key'. + * This is useful when the dict is used as a multi-hash. + * + * If an empty list is returned, no matching entry exists. + * Otherwise, the offset of the matching entries are returned. + * + * NOTE: It is not guaranteed that each entry is + * indeed among the ones you were looking for. + * After loading each entry you should check that it + * indeed matches the search key. + */ + QList<int> findMultiString(const QString &key ) const; + + /** + * The number of entries in the dictionary. + * + * Only valid when building the database. + */ + uint count() const; + + /** + * Reset the dictionary. + * + * Only valid when building the database. + */ + void clear(); + + /** + * Save the dictionary to the stream + * A reasonable fast hash algorithm will be created. + * + * Typically this will find 90% of the entries directly. + * Average hash table size: nrOfItems * 20 bytes. + * Average duplicate list size: nrOfItms * avgKeyLength / 5. + * + * Unknown keys have an average 20% chance to give a false hit. + * (That's why your program should check the result) + * + * Example: + * Assume 1000 items with an average key length of 60 bytes. + * + * Approx. 900 items will hash directly to the right entry. + * Approx. 100 items require a lookup in the duplicate list. + * + * The hash table size will be approx. 20Kb. + * The duplicate list size will be approx. 12Kb. + **/ + void save(QDataStream &str); + +private: + Q_DISABLE_COPY(KSycocaDict) + class Private; + Private* d; +}; + +#endif diff --git a/kdecore/sycoca/ksycocaentry.cpp b/kdecore/sycoca/ksycocaentry.cpp new file mode 100644 index 0000000..194f9c1 --- /dev/null +++ b/kdecore/sycoca/ksycocaentry.cpp @@ -0,0 +1,173 @@ +/* This file is part of the KDE libraries + * Copyright (C) 1999 Waldo Bastian <bastian@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 "ksycocaentry.h" +#include "ksycocaentry_p.h" +#include <kdebug.h> + +#include <ksycoca.h> + +KSycocaEntryPrivate::KSycocaEntryPrivate(QDataStream &_str, int iOffset) + : offset(iOffset), deleted(false) +{ + KSycocaEntry::read( _str, path ); +} + +KSycocaEntry::KSycocaEntry() + : d_ptr(0) +{ +} + +KSycocaEntry::KSycocaEntry(KSycocaEntryPrivate &d) + : d_ptr(&d) +{ +} + +KSycocaEntry::~KSycocaEntry() +{ + delete d_ptr; +} + +void KSycocaEntry::read( QDataStream &s, QString &str ) +{ + quint32 bytes; + s >> bytes; // read size of string + if ( bytes > 8192 ) { // null string or too big + if (bytes != 0xffffffff) + KSycoca::flagError(); + str.clear(); + } + else if ( bytes > 0 ) { // not empty + int bt = bytes/2; + str.resize( bt ); + QChar* ch = (QChar *) str.unicode(); + char t[8192]; + char *b = t; + s.readRawData( b, bytes ); + while ( bt-- ) { + *ch++ = (ushort) (((ushort)b[0])<<8) | (uchar)b[1]; + b += 2; + } + } else { + str.clear(); + } +} + +void KSycocaEntry::read( QDataStream &s, QStringList &list ) +{ + list.clear(); + quint32 count; + s >> count; // read size of list + if (count >= 1024) + { + KSycoca::flagError(); + return; + } + for(quint32 i = 0; i < count; i++) + { + QString str; + read(s, str); + list.append( str ); + if (s.atEnd()) + { + KSycoca::flagError(); + return; + } + } +} + +bool KSycocaEntry::isType(KSycocaType t) const +{ + return d_ptr->isType(t); +} + +KSycocaType KSycocaEntry::sycocaType() const +{ + return d_ptr->sycocaType(); +} + +QString KSycocaEntry::entryPath() const +{ + Q_D(const KSycocaEntry); + return d->path; +} + +QString KSycocaEntry::storageId() const +{ + Q_D(const KSycocaEntry); + return d->storageId(); +} + +bool KSycocaEntry::isDeleted() const +{ + Q_D(const KSycocaEntry); + return d->deleted; +} + +void KSycocaEntry::setDeleted( bool deleted ) +{ + Q_D(KSycocaEntry); + d->deleted = deleted; +} + +bool KSycocaEntry::isSeparator() const +{ + return d_ptr == 0 || isType(KST_KServiceSeparator); +} + +int KSycocaEntry::offset() const +{ + Q_D(const KSycocaEntry); + return d->offset; +} + +void KSycocaEntryPrivate::save(QDataStream &s) +{ + offset = s.device()->pos(); // store position in member variable + s << qint32(sycocaType()) << path; +} + +void KSycocaEntry::save(QDataStream &s) +{ + Q_D(KSycocaEntry); + d->save(s); +} + +bool KSycocaEntry::isValid() const +{ + Q_D(const KSycocaEntry); + return d && d->isValid(); +} + +QString KSycocaEntry::name() const +{ + Q_D(const KSycocaEntry); + return d->name(); +} + +QStringList KSycocaEntry::propertyNames() const +{ + Q_D(const KSycocaEntry); + return d->propertyNames(); +} + +QVariant KSycocaEntry::property(const QString &name) const +{ + Q_D(const KSycocaEntry); + return d->property(name); +} diff --git a/kdecore/sycoca/ksycocaentry.h b/kdecore/sycoca/ksycocaentry.h new file mode 100644 index 0000000..32f587a --- /dev/null +++ b/kdecore/sycoca/ksycocaentry.h @@ -0,0 +1,157 @@ +/* This file is part of the KDE libraries + * Copyright (C) 1999 Waldo Bastian <bastian@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. + **/ + +#ifndef KSYCOCAENTRY_H +#define KSYCOCAENTRY_H + +#include <kglobal.h> +#include <ksycocatype.h> +#include <ksharedptr.h> + +#include <QtCore/QDataStream> +#include <QtCore/QStringList> +#include <QtCore/QVariant> + +class KSycocaEntryPrivate; + +/** + * Base class for all Sycoca entries. + * + * You can't create an instance of KSycocaEntry, but it provides + * the common functionality for servicetypes and services. + * + * @internal + * @see http://techbase.kde.org/Development/Architecture/KDE3/System_Configuration_Cache + */ +class KDECORE_EXPORT KSycocaEntry : public KShared +{ + +public: + /* + * constructs a invalid KSycocaEntry object + */ + KSycocaEntry(); + + virtual ~KSycocaEntry(); + + /** + * internal + */ + bool isType(KSycocaType t) const; + /** + * internal + */ + KSycocaType sycocaType() const; + + typedef KSharedPtr<KSycocaEntry> Ptr; + typedef QList<Ptr> List; + + /** + * Default constructor + */ +// explicit KSycocaEntry(const QString &path); + + /** + * Safe demarshalling functions. + */ + static void read( QDataStream &s, QString &str ); + static void read( QDataStream &s, QStringList &list ); + + + /** + * @return the name of this entry + */ + QString name() const; + + /** + * @return the path of this entry + * The path can be absolute or relative. + * The corresponding factory should know relative to what. + */ + QString entryPath() const; + + /** + * @return the unique ID for this entry + * In practice, this is storageId() for KService and name() for everything else. + * \since 4.2.1 + */ + QString storageId() const; + + /** + * @return true if valid + */ + bool isValid() const; + + /** + * @return true if deleted + */ + bool isDeleted() const; + + /** + * Returns the requested property. Some often used properties + * have convenience access functions like exec(), + * serviceTypes etc. + * + * @param name the name of the property + * @return the property, or invalid if not found + */ + QVariant property(const QString &name) const; + + /** + * Returns the list of all properties that this service can have. + * That means, that some of these properties may be empty. + * @return the list of supported properties + */ + QStringList propertyNames() const; + + /** + * Sets whether or not this service is deleted + */ + void setDeleted( bool deleted ); + + + /* + * @returns true, if this is a separator + */ + bool isSeparator() const; + + /** + * @internal + * @return the position of the entry in the sycoca file + */ + int offset() const; + + /** + * @internal + * Save ourselves to the database. + */ + void save(QDataStream &s); + +// KSycocaEntry(const KSycocaEntry ©); +// KSycocaEntry &operator=(const KSycocaEntry &right); +protected: + KSycocaEntry(KSycocaEntryPrivate &d); + KSycocaEntryPrivate *d_ptr; + +private: + Q_DISABLE_COPY(KSycocaEntry) + + Q_DECLARE_PRIVATE(KSycocaEntry) +}; + +#endif diff --git a/kdecore/sycoca/ksycocaentry_p.h b/kdecore/sycoca/ksycocaentry_p.h new file mode 100644 index 0000000..496c2cc --- /dev/null +++ b/kdecore/sycoca/ksycocaentry_p.h @@ -0,0 +1,82 @@ +/* This file is part of the KDE libraries + * Copyright (C) 1999 Waldo Bastian <bastian@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. + **/ + +#ifndef KSYCOCAENTRYPRIVATE_H +#define KSYCOCAENTRYPRIVATE_H + +#include "ksycocaentry.h" + +#define K_SYCOCATYPE( type, baseclass ) \ + virtual bool isType(KSycocaType t) const { if (t == type) return true; return baseclass::isType(t);} \ + virtual KSycocaType sycocaType() const { return type; } + + +class KSycocaEntryPrivate +{ +public: + KSycocaEntryPrivate(const QString &path_) + : offset( 0 ), + deleted( false ), path(path_) + {} + + KSycocaEntryPrivate(QDataStream &_str, int iOffset); + + virtual ~KSycocaEntryPrivate() {} + + // Don't forget to call the parent class + // first if you override this function. + virtual void save(QDataStream &s); + + virtual bool isType(KSycocaType t) const + { + return (t == KST_KSycocaEntry); + } + + virtual KSycocaType sycocaType() const + { + return KST_KSycocaEntry; + } + + virtual bool isValid() const + { + return !name().isEmpty(); + } + + virtual QVariant property(const QString &name) const + { + Q_UNUSED(name) + return QVariant(); + } + + virtual QStringList propertyNames() const + { + return QStringList(); + } + + virtual QString name() const = 0; + + virtual QString storageId() const { return name(); } + + int offset; + bool deleted; + QString path; +}; + + + +#endif diff --git a/kdecore/sycoca/ksycocafactory.cpp b/kdecore/sycoca/ksycocafactory.cpp new file mode 100644 index 0000000..24ceca5 --- /dev/null +++ b/kdecore/sycoca/ksycocafactory.cpp @@ -0,0 +1,248 @@ +/* This file is part of the KDE libraries + * Copyright (C) 1999 David Faure <faure@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 "ksycocafactory.h" +#include "ksycoca.h" +#include "ksycocatype.h" +#include "ksycocaentry.h" +#include "ksycocadict_p.h" + +#include <config.h> +#include <kdebug.h> + +#include <QThread> +#include <QtCore/QHash> + +class KSycocaFactory::Private +{ +public: + Private() : mOffset(0), + m_sycocaDictOffset(0), + m_beginEntryOffset(0), + m_endEntryOffset(0) {} + ~Private() + { + delete m_sycocaDict; + } + + int mOffset; + int m_sycocaDictOffset; + int m_beginEntryOffset; + int m_endEntryOffset; + KSycocaDict *m_sycocaDict; +}; + +KSycocaFactory::KSycocaFactory(KSycocaFactoryId factory_id) + : m_resourceList(0), m_entryDict(0), m_str(0), d(new Private) +{ + if (!KSycoca::self()->isBuilding() && (m_str = KSycoca::self()->findFactory(factory_id))) { + // Read position of index tables.... + qint32 i; + (*m_str) >> i; + d->m_sycocaDictOffset = i; + (*m_str) >> i; + d->m_beginEntryOffset = i; + (*m_str) >> i; + d->m_endEntryOffset = i; + + QDataStream* str = stream(); + int saveOffset = str->device()->pos(); + // Init index tables + d->m_sycocaDict = new KSycocaDict(str, d->m_sycocaDictOffset); + saveOffset = str->device()->seek(saveOffset); + } else { + // We are in kbuildsycoca4 -- build new database! + m_entryDict = new KSycocaEntryDict; + d->m_sycocaDict = new KSycocaDict; + d->m_beginEntryOffset = 0; + d->m_endEntryOffset = 0; + + // m_resourceList will be filled in by inherited constructors + } + KSycoca::self()->addFactory(this); +} + +KSycocaFactory::~KSycocaFactory() +{ + delete m_entryDict; + delete d; +} + +void +KSycocaFactory::saveHeader(QDataStream &str) +{ + // Write header + str.device()->seek(d->mOffset); + str << (qint32) d->m_sycocaDictOffset; + str << (qint32) d->m_beginEntryOffset; + str << (qint32) d->m_endEntryOffset; +} + +void +KSycocaFactory::save(QDataStream &str) +{ + if (!m_entryDict) return; // Error! Function should only be called when + // building database + if (!d->m_sycocaDict) return; // Error! + + d->mOffset = str.device()->pos(); // store position in member variable + d->m_sycocaDictOffset = 0; + + // Write header (pass #1) + saveHeader(str); + + d->m_beginEntryOffset = str.device()->pos(); + + // Write all entries. + int entryCount = 0; + for(KSycocaEntryDict::Iterator it = m_entryDict->begin(); + it != m_entryDict->end(); ++it) + { + KSycocaEntry::Ptr entry = *it; + entry->save(str); + entryCount++; + } + + d->m_endEntryOffset = str.device()->pos(); + + // Write indices... + // Linear index + str << (qint32) entryCount; + for(KSycocaEntryDict::Iterator it = m_entryDict->begin(); + it != m_entryDict->end(); ++it) + { + str << qint32(it->data()->offset()); + } + + // Dictionary index + d->m_sycocaDictOffset = str.device()->pos(); + d->m_sycocaDict->save(str); + + int endOfFactoryData = str.device()->pos(); + + // Update header (pass #2) + saveHeader(str); + + // Seek to end. + str.device()->seek(endOfFactoryData); +} + +void +KSycocaFactory::addEntry(const KSycocaEntry::Ptr& newEntry) +{ + if (!m_entryDict) return; // Error! Function should only be called when + // building database + + if (!d->m_sycocaDict) return; // Error! + + KSycocaEntry::Ptr oldEntry = m_entryDict->value(newEntry->storageId()); + if (oldEntry) { + // Already exists -> replace + // We found a more-local override, e.g. ~/.local/share/applications/kde4/foo.desktop + // So forget about the more global file. + // + // This can also happen with two .protocol files using the same protocol= entry. + // If we didn't remove one here, we would end up asserting because save() + // wasn't called on one of the entries. + //kDebug(7021) << "removing" << oldEntry.data() << oldEntry->entryPath() << "because of" << newEntry->entryPath() << "they have the same storageId" << newEntry->storageId(); + removeEntry(newEntry->storageId()); + } + + const QString name = newEntry->storageId(); + m_entryDict->insert( name, newEntry ); + d->m_sycocaDict->add( name, newEntry ); +} + +void +KSycocaFactory::removeEntry(const QString& entryName) +{ + if (!m_entryDict) return; // Error! Function should only be called when + // building database + + if (!d->m_sycocaDict) return; // Error! + + m_entryDict->remove( entryName ); + d->m_sycocaDict->remove( entryName ); // O(N) +} + +KSycocaEntry::List KSycocaFactory::allEntries() const +{ + KSycocaEntry::List list; + + // Assume we're NOT building a database + + QDataStream* str = stream(); + if (!str) return list; + str->device()->seek(d->m_endEntryOffset); + qint32 entryCount; + (*str) >> entryCount; + + if (entryCount > 8192) + { + kDebug(7021) << QThread::currentThread() << "error detected in factory" << this; + KSycoca::flagError(); + return list; + } + + // offsetList is needed because createEntry() modifies the stream position + qint32 *offsetList = new qint32[entryCount]; + for(int i = 0; i < entryCount; i++) + { + (*str) >> offsetList[i]; + } + + for(int i = 0; i < entryCount; i++) + { + KSycocaEntry *newEntry = createEntry(offsetList[i]); + if (newEntry) + { + list.append( KSycocaEntry::Ptr( newEntry ) ); + } + } + delete [] offsetList; + return list; +} + +int KSycocaFactory::offset() const +{ + return d->mOffset; +} + +const KSycocaResourceList * KSycocaFactory::resourceList() const +{ + return m_resourceList; +} + +const KSycocaDict * KSycocaFactory::sycocaDict() const +{ + return d->m_sycocaDict; +} + +bool KSycocaFactory::isEmpty() const +{ + return d->m_beginEntryOffset == d->m_endEntryOffset; +} + +QDataStream* KSycocaFactory::stream() const +{ + return m_str; +} + +void KSycocaFactory::virtual_hook( int /*id*/, void* /*data*/) +{ /*BASE::virtual_hook( id, data );*/ } + diff --git a/kdecore/sycoca/ksycocafactory.h b/kdecore/sycoca/ksycocafactory.h new file mode 100644 index 0000000..8ebcf95 --- /dev/null +++ b/kdecore/sycoca/ksycocafactory.h @@ -0,0 +1,209 @@ +/* This file is part of the KDE libraries + * Copyright (C) 1999 Waldo Bastian <bastian@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. + **/ + +#ifndef KSYCOCAFACTORY_H +#define KSYCOCAFACTORY_H + +#include <ksycocaentry.h> + +class QString; +class KSycocaDict; +class KSycocaResourceList; +template <typename T> class QList; +template <typename KT, typename VT> class QHash; + +typedef QHash<QString, KSycocaEntry::Ptr> KSycocaEntryDict; + +/** + * @internal + * Base class for sycoca factories + */ +class KDECORE_EXPORT KSycocaFactory +{ +public: + virtual KSycocaFactoryId factoryId() const = 0; + +protected: // virtual class + /** + * Create a factory which can be used to lookup from/create a database + * (depending on KSycoca::isBuilding()) + */ + explicit KSycocaFactory( KSycocaFactoryId factory_id ); + +public: + virtual ~KSycocaFactory(); + + /** + * @return the position of the factory in the sycoca file + */ + int offset() const; + + /** + * @return the dict, for special use by KBuildSycoca + */ + KSycocaEntryDict * entryDict() { return m_entryDict; } + + /** + * Construct an entry from a config file. + * To be implemented in the real factories. + */ + virtual KSycocaEntry *createEntry(const QString &file, const char *resource) const = 0; + + /** + * Add an entry + */ + virtual void addEntry(const KSycocaEntry::Ptr& newEntry); + + /** + * Remove all entries with the given name. + * Not very fast (O(N)), use with care. + */ + void removeEntry(const QString& entryName); + + /** + * Read an entry from the database + */ + virtual KSycocaEntry *createEntry(int offset) const = 0; + + /** + * Get a list of all entries from the database. + */ + virtual KSycocaEntry::List allEntries() const; + + /** + * Saves all entries it maintains as well as index files + * for these entries to the stream 'str'. + * + * Also sets mOffset to the starting position. + * + * The stream is positioned at the end of the last index. + * + * Don't forget to call the parent first when you override + * this function. + */ + virtual void save(QDataStream &str); + + /** + * Writes out a header to the stream 'str'. + * The baseclass positions the stream correctly. + * + * Don't forget to call the parent first when you override + * this function. + */ + virtual void saveHeader(QDataStream &str); + + /** + * @return the resources for which this factory is responsible. + * @internal to kbuildsycoca + */ + const KSycocaResourceList * resourceList() const; + + /** + * @return the sycoca dict, for factories to find entries by name. + */ + const KSycocaDict *sycocaDict() const; + + /** + * @return true if the factory is completely empty - no entries defined + */ + bool isEmpty() const; + +protected: + QDataStream* stream() const; + + KSycocaResourceList *m_resourceList; + KSycocaEntryDict *m_entryDict; + +private: + QDataStream *m_str; + class Private; + Private* const d; + +protected: + /** Virtual hook, used to add new "virtual" functions while maintaining + binary compatibility. Unused in this class. + */ + virtual void virtual_hook( int id, void* data ); +}; + +/** + * This, instead of a typedef, allows to declare "class ..." in header files. + * @internal + */ +class KDECORE_EXPORT KSycocaFactoryList : public QList<KSycocaFactory*> //krazy:exclude=dpointer (acts as a typedef) +{ +public: + KSycocaFactoryList() { } +}; + +#include <QThreadStorage> +/** + * Workaround for the lack of QThreadStorage::setAutoDelete(false). + * Container for KSycocaFactory that doesn't delete it when it is deleted. + */ +template <typename F> class KSycocaFactoryContainer +{ +public: + KSycocaFactoryContainer(F* factory) : m_factory(factory) {} + F* factory() { return m_factory; } +private: + F* m_factory; +}; + +/** + * Template for making it easier to define a threadsafe singleton + * for each factory, with support for kbuildsycoca providing a + * subclass of the factory. + * + * @since 4.3 + * @internal + */ +template <typename F> class KSycocaFactorySingleton +{ +public: + typedef KSycocaFactoryContainer<F> C; + KSycocaFactorySingleton() { + } + ~KSycocaFactorySingleton() { + // Do not delete the factory here. + // All factories are owned by KSycoca, and deleted by it. + } + void instanceCreated(F* newFactory) { + // This can also register a subclass created by kbuildsycoca + Q_ASSERT(!m_factories.hasLocalData()); + Q_ASSERT(newFactory); + m_factories.setLocalData(new C(newFactory)); + } + void instanceDestroyed(F* factory) { + if (m_factories.hasLocalData()) { // could be false on thread exit + Q_ASSERT(m_factories.localData()->factory() == factory); Q_UNUSED(factory) + m_factories.setLocalData(0); + } + } + F* self() { + if (!m_factories.hasLocalData()) { + new F; // calls instanceCreated, which calls setLocalData + Q_ASSERT(m_factories.hasLocalData()); + } + return m_factories.localData()->factory(); + } +private: + QThreadStorage<C*> m_factories; +}; + +#endif diff --git a/kdecore/sycoca/ksycocatype.h b/kdecore/sycoca/ksycocatype.h new file mode 100644 index 0000000..9f9ab44 --- /dev/null +++ b/kdecore/sycoca/ksycocatype.h @@ -0,0 +1,57 @@ +/* This file is part of the KDE project + Copyright (C) 1998, 1999 Torben Weis <weis@kde.org> + Waldo Bastian <bastian@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 as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + 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. +*/ + +#ifndef KSYCOCATYPE_H +#define KSYCOCATYPE_H + +/** + * \relates KSycocaEntry + * A KSycocaType is a code (out of the KSycocaType enum) assigned to each + * class type derived from KSycocaEntry . + * To use it, call the macro K_SYCOCATYPE( your_typecode, parent_class ) + * at the top of your class definition. + */ +enum KSycocaType { KST_KSycocaEntry = 0, KST_KService = 1, KST_KServiceType = 2, KST_KMimeType = 3, + KST_KFolderMimeType = 4, KST_KDEDesktopMimeType = 5 /*compat*/, KST_KMimeTypeEntry = 6 /*internal*/, + KST_KServiceGroup = 7, KST_KImageIOFormat = 8, KST_KProtocolInfo = 9, + KST_KServiceSeparator = 10, + KST_KCustom = 1000 }; + +/** + * \relates KSycocaFactory + * A KSycocaFactoryId is a code (out of the KSycocaFactoryId enum) + * assigned to each class type derived from KSycocaFactory. + * To use it, call the macro K_SYCOCAFACTORY( your_factory_id ) + * at the top of your class definition. + */ +enum KSycocaFactoryId { KST_KServiceFactory = 1, + KST_KServiceTypeFactory = 2, + KST_KServiceGroupFactory = 3, + KST_KImageIO = 4, // unused, KDE5: remove + KST_KProtocolInfoFactory = 5, + KST_KMimeTypeFactory = 6, + KST_CTimeInfo = 100 }; + +#define K_SYCOCAFACTORY( factory_id ) \ +public: \ + virtual KSycocaFactoryId factoryId() const { return factory_id; } \ +private: + +#endif |