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

much simpler and easier to maintain, also it will be proper D-Bus service now with no sockets, process title hack and whatnot. KCrash and startkde script (in kde-workspace repo) have to be adjusted for it tho note that the internal KIO scheduler already has the functionality to put slaves on hold but now they will be started as detached process. eventually they may become plugins (they are loaded as such by kioslave program) without event loop dispatcher fixes application startup notification (ASN) and cleanup issues Signed-off-by: Ivailo Monev <xakepa10@gmail.com>
810 lines
26 KiB
C++
810 lines
26 KiB
C++
// -*- c-basic-offset: 3 -*-
|
|
/* This file is part of the KDE libraries
|
|
* Copyright (C) 1999 David Faure <faure@kde.org>
|
|
* Copyright (C) 2002-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 "kbuildsycoca.h"
|
|
#include "ksycoca_p.h"
|
|
#include "ksycocaresourcelist.h"
|
|
#include "vfolder_menu.h"
|
|
|
|
#include <config.h>
|
|
#include <config-kded.h>
|
|
|
|
#include <kservice.h>
|
|
#include <kmimetype.h>
|
|
#include "kbuildservicetypefactory.h"
|
|
#include "kbuildmimetypefactory.h"
|
|
#include "kbuildservicefactory.h"
|
|
#include "kbuildservicegroupfactory.h"
|
|
#include "kbuildprotocolinfofactory.h"
|
|
#include "kctimefactory.h"
|
|
#include <ktemporaryfile.h>
|
|
#include <QtCore/QDir>
|
|
#include <QtCore/QFileInfo>
|
|
#include <QtCore/QCoreApplication>
|
|
#include <QtDBus/QDBusConnectionInterface>
|
|
#include <errno.h>
|
|
|
|
#include <assert.h>
|
|
#include <kglobal.h>
|
|
#include <kdebug.h>
|
|
#include <kdirwatch.h>
|
|
#include <kstandarddirs.h>
|
|
#include <ksavefile.h>
|
|
#include <klocale.h>
|
|
#include <kaboutdata.h>
|
|
#include <kcmdlineargs.h>
|
|
#ifndef KBUILDSYCOCA_NO_KCRASH
|
|
#include <kde_file.h>
|
|
#include <kcrash.h>
|
|
#endif
|
|
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <time.h>
|
|
#include <signal.h>
|
|
|
|
typedef QHash<QString, KSycocaEntry::Ptr> KBSEntryDict;
|
|
typedef QList<KSycocaEntry::List> KSycocaEntryListList;
|
|
|
|
static quint32 newTimestamp = 0;
|
|
|
|
static KBuildServiceFactory *g_serviceFactory = 0;
|
|
static KBuildServiceGroupFactory *g_buildServiceGroupFactory = 0;
|
|
static KSycocaFactory *g_currentFactory = 0;
|
|
static KCTimeInfo *g_ctimeInfo = 0; // factory
|
|
static KCTimeDict *g_ctimeDict = 0; // old timestamps
|
|
static QByteArray g_resource = 0;
|
|
static KBSEntryDict *g_currentEntryDict = 0;
|
|
static KBSEntryDict *g_serviceGroupEntryDict = 0;
|
|
static KSycocaEntryListList *g_allEntries = 0; // entries from existing ksycoca
|
|
static QStringList *g_allResourceDirs = 0;
|
|
static bool g_changed = false;
|
|
static KSycocaEntry::List g_tempStorage;
|
|
static VFolderMenu *g_vfolder = 0;
|
|
static QByteArray g_sycocaPath = 0;
|
|
|
|
static bool bGlobalDatabase = false;
|
|
static bool bMenuTest = false;
|
|
|
|
void crashHandler(int sig)
|
|
{
|
|
KDE_signal(sig, SIG_DFL);
|
|
|
|
// If we crash while reading sycoca, we delete the database in an attempt to recover.
|
|
if (!g_sycocaPath.isEmpty()) {
|
|
unlink(g_sycocaPath.constData());
|
|
}
|
|
|
|
::exit(sig);
|
|
}
|
|
|
|
static QString sycocaPath()
|
|
{
|
|
return KSycoca::absoluteFilePath(bGlobalDatabase ? KSycoca::GlobalDatabase : KSycoca::LocalDatabase);
|
|
}
|
|
|
|
KBuildSycoca::KBuildSycoca()
|
|
: KSycoca( true )
|
|
{
|
|
}
|
|
|
|
KBuildSycoca::~KBuildSycoca()
|
|
{
|
|
|
|
}
|
|
|
|
KSycocaEntry::Ptr KBuildSycoca::createEntry(const QString &file, bool addToFactory)
|
|
{
|
|
quint32 timeStamp = g_ctimeInfo->dict()->ctime(file, g_resource);
|
|
if (!timeStamp)
|
|
{
|
|
timeStamp = KGlobal::dirs()->calcResourceHash( g_resource, file,
|
|
KStandardDirs::Recursive);
|
|
}
|
|
KSycocaEntry::Ptr entry;
|
|
if (g_allEntries)
|
|
{
|
|
assert(g_ctimeDict);
|
|
quint32 oldTimestamp = g_ctimeDict->ctime(file, g_resource);
|
|
|
|
if (timeStamp && (timeStamp == oldTimestamp))
|
|
{
|
|
// Re-use old entry
|
|
if (g_currentFactory == g_buildServiceGroupFactory) // Strip .directory from service-group entries
|
|
{
|
|
entry = g_currentEntryDict->value(file.left(file.length()-10));
|
|
} else {
|
|
entry = g_currentEntryDict->value(file);
|
|
}
|
|
// remove from g_ctimeDict; if g_ctimeDict is not empty
|
|
// after all files have been processed, it means
|
|
// some files were removed since last time
|
|
if (file.contains("fake"))
|
|
kDebug(7021) << g_resource << "reusing (and removing) old entry [\"fake\"] for:" << file;
|
|
g_ctimeDict->remove(file, g_resource);
|
|
}
|
|
else if (oldTimestamp)
|
|
{
|
|
g_changed = true;
|
|
g_ctimeDict->remove(file, g_resource);
|
|
kDebug(7021) << "modified:" << file << "in" << g_resource.constData();
|
|
}
|
|
else
|
|
{
|
|
g_changed = true;
|
|
kDebug(7021) << "new:" << file << "in" << g_resource.constData();
|
|
}
|
|
}
|
|
g_ctimeInfo->dict()->addCTime(file, g_resource, timeStamp);
|
|
if (!entry)
|
|
{
|
|
// Create a new entry
|
|
entry = g_currentFactory->createEntry( file, g_resource );
|
|
}
|
|
if ( entry && entry->isValid() )
|
|
{
|
|
if (addToFactory)
|
|
g_currentFactory->addEntry(entry);
|
|
else
|
|
g_tempStorage.append(entry);
|
|
return entry;
|
|
}
|
|
return KSycocaEntry::Ptr();
|
|
}
|
|
|
|
KService::Ptr KBuildSycoca::createService(const QString &path)
|
|
{
|
|
KSycocaEntry::Ptr entry = createEntry(path, false);
|
|
return KService::Ptr::staticCast(entry);
|
|
}
|
|
|
|
// returns false if the database is up to date, true if it needs to be saved
|
|
bool KBuildSycoca::build()
|
|
{
|
|
typedef QList<KBSEntryDict *> KBSEntryDictList;
|
|
KBSEntryDictList entryDictList;
|
|
KBSEntryDict *serviceEntryDict = 0;
|
|
|
|
// Convert for each factory the entryList to a Dict.
|
|
int i = 0;
|
|
// For each factory
|
|
for (KSycocaFactoryList::Iterator factory = factories()->begin();
|
|
factory != factories()->end();
|
|
++factory)
|
|
{
|
|
KBSEntryDict *entryDict = new KBSEntryDict;
|
|
if (g_allEntries)
|
|
{
|
|
const KSycocaEntry::List list = (*g_allEntries)[i++];
|
|
for( KSycocaEntry::List::const_iterator it = list.begin();
|
|
it != list.end();
|
|
++it)
|
|
{
|
|
entryDict->insert( (*it)->entryPath(), *it );
|
|
}
|
|
}
|
|
if ((*factory) == g_serviceFactory)
|
|
serviceEntryDict = entryDict;
|
|
else if ((*factory) == g_buildServiceGroupFactory)
|
|
g_serviceGroupEntryDict = entryDict;
|
|
entryDictList.append(entryDict);
|
|
}
|
|
|
|
QStringList allResources; // we could use QSet<QString> - does order matter?
|
|
// For each factory
|
|
for (KSycocaFactoryList::Iterator factory = factories()->begin();
|
|
factory != factories()->end();
|
|
++factory)
|
|
{
|
|
// For each resource the factory deals with
|
|
const KSycocaResourceList *list = (*factory)->resourceList();
|
|
if (!list) continue;
|
|
|
|
for( KSycocaResourceList::ConstIterator it1 = list->constBegin();
|
|
it1 != list->constEnd();
|
|
++it1 )
|
|
{
|
|
KSycocaResource res = (*it1);
|
|
if (!allResources.contains(res.resource))
|
|
allResources.append(res.resource);
|
|
}
|
|
}
|
|
|
|
g_ctimeInfo = new KCTimeInfo(); // This is a build factory too, don't delete!!
|
|
bool uptodate = true;
|
|
// For all resources
|
|
foreach(const QString &it1, allResources)
|
|
{
|
|
g_changed = false;
|
|
g_resource = it1.toLatin1();
|
|
|
|
QStringList relFiles;
|
|
|
|
(void) KGlobal::dirs()->findAllResources( g_resource,
|
|
QString(),
|
|
KStandardDirs::Recursive |
|
|
KStandardDirs::NoDuplicates,
|
|
relFiles);
|
|
|
|
|
|
// Now find all factories that use this resource....
|
|
// For each factory
|
|
KBSEntryDictList::const_iterator ed_it = entryDictList.begin();
|
|
const KBSEntryDictList::const_iterator ed_end = entryDictList.end();
|
|
KSycocaFactoryList::const_iterator it = factories()->constBegin();
|
|
const KSycocaFactoryList::const_iterator end = factories()->constEnd();
|
|
for ( ; it != end; ++it, ++ed_it )
|
|
{
|
|
g_currentFactory = (*it);
|
|
// g_ctimeInfo gets created after the initial loop, so it has no entryDict.
|
|
g_currentEntryDict = ed_it == ed_end ? 0 : *ed_it;
|
|
// For each resource the factory deals with
|
|
const KSycocaResourceList *list = g_currentFactory->resourceList();
|
|
if (!list) continue;
|
|
|
|
for( KSycocaResourceList::ConstIterator it2 = list->constBegin();
|
|
it2 != list->constEnd();
|
|
++it2 )
|
|
{
|
|
KSycocaResource res = (*it2);
|
|
if (res.resource != it1) continue;
|
|
|
|
// For each file in the resource
|
|
foreach(const QString &it3, relFiles)
|
|
{
|
|
// Check if file matches filter
|
|
if (it3.endsWith(res.extension))
|
|
createEntry(it3, true);
|
|
}
|
|
}
|
|
}
|
|
if (g_changed || !g_allEntries)
|
|
{
|
|
uptodate = false;
|
|
//kDebug() << "CHANGED:" << g_resource;
|
|
m_changedResources.append(g_resource);
|
|
}
|
|
}
|
|
|
|
bool result = !uptodate || (g_ctimeDict && !g_ctimeDict->isEmpty());
|
|
if (g_ctimeDict && !g_ctimeDict->isEmpty()) {
|
|
//kDebug() << "Still in time dict:";
|
|
//g_ctimeDict->dump();
|
|
// ## It seems entries filtered out by vfolder are still in there,
|
|
// so we end up always saving ksycoca, i.e. this method never returns false
|
|
|
|
// Get the list of resources from which some files were deleted
|
|
const QStringList resources = g_ctimeDict->resourceList();
|
|
kDebug() << "Still in the time dict (i.e. deleted files)" << resources;
|
|
m_changedResources += resources;
|
|
}
|
|
|
|
if (result || bMenuTest)
|
|
{
|
|
g_resource = "xdgdata-apps";
|
|
g_currentFactory = g_serviceFactory;
|
|
g_currentEntryDict = serviceEntryDict;
|
|
g_changed = false;
|
|
|
|
g_vfolder = new VFolderMenu(g_serviceFactory, this);
|
|
if (!m_trackId.isEmpty())
|
|
g_vfolder->setTrackId(m_trackId);
|
|
|
|
VFolderMenu::SubMenu *kdeMenu = g_vfolder->parseMenu("applications.menu");
|
|
|
|
KServiceGroup::Ptr entry = g_buildServiceGroupFactory->addNew("/", kdeMenu->directoryFile, KServiceGroup::Ptr(), false);
|
|
entry->setLayoutInfo(kdeMenu->layoutList);
|
|
createMenu(QString(), QString(), kdeMenu);
|
|
|
|
(void) existingResourceDirs();
|
|
*g_allResourceDirs += g_vfolder->allDirectories();
|
|
|
|
if (g_changed || !g_allEntries)
|
|
{
|
|
uptodate = false;
|
|
//kDebug() << "CHANGED:" << g_resource;
|
|
m_changedResources.append(g_resource);
|
|
}
|
|
if (bMenuTest) {
|
|
result = false;
|
|
}
|
|
}
|
|
|
|
qDeleteAll(entryDictList);
|
|
return result;
|
|
}
|
|
|
|
void KBuildSycoca::createMenu(const QString &caption_, const QString &name_, VFolderMenu::SubMenu *menu)
|
|
{
|
|
QString caption = caption_;
|
|
QString name = name_;
|
|
foreach (VFolderMenu::SubMenu *subMenu, menu->subMenus)
|
|
{
|
|
QString subName = name+subMenu->name+'/';
|
|
|
|
QString directoryFile = subMenu->directoryFile;
|
|
if (directoryFile.isEmpty())
|
|
directoryFile = subName+".directory";
|
|
quint32 timeStamp = g_ctimeInfo->dict()->ctime(directoryFile, g_resource);
|
|
if (!timeStamp) {
|
|
timeStamp = KGlobal::dirs()->calcResourceHash( g_resource, directoryFile,
|
|
KStandardDirs::Recursive );
|
|
}
|
|
|
|
KServiceGroup::Ptr entry;
|
|
if (g_allEntries)
|
|
{
|
|
const quint32 oldTimestamp = g_ctimeDict->ctime(directoryFile, g_resource);
|
|
|
|
if (timeStamp && (timeStamp == oldTimestamp))
|
|
{
|
|
KSycocaEntry::Ptr group = g_serviceGroupEntryDict->value(subName);
|
|
if ( group )
|
|
{
|
|
entry = KServiceGroup::Ptr::staticCast( group );
|
|
if (entry->directoryEntryPath() != directoryFile)
|
|
entry = 0; // Can't reuse this one!
|
|
}
|
|
}
|
|
}
|
|
g_ctimeInfo->dict()->addCTime(directoryFile, g_resource, timeStamp);
|
|
|
|
entry = g_buildServiceGroupFactory->addNew(subName, subMenu->directoryFile, entry, subMenu->isDeleted);
|
|
entry->setLayoutInfo(subMenu->layoutList);
|
|
if (! (bMenuTest && entry->noDisplay()) )
|
|
createMenu(caption + entry->caption() + '/', subName, subMenu);
|
|
}
|
|
if (caption.isEmpty())
|
|
caption += '/';
|
|
if (name.isEmpty())
|
|
name += '/';
|
|
foreach (const KService::Ptr &p, menu->items)
|
|
{
|
|
if (bMenuTest)
|
|
{
|
|
if (!menu->isDeleted && !p->noDisplay())
|
|
printf("%s\t%s\t%s\n", qPrintable( caption ), qPrintable( p->menuId() ), qPrintable( KStandardDirs::locate("xdgdata-apps", p->entryPath() ) ) );
|
|
}
|
|
else
|
|
{
|
|
g_buildServiceGroupFactory->addNewEntryTo( name, p );
|
|
}
|
|
}
|
|
}
|
|
|
|
bool KBuildSycoca::recreate()
|
|
{
|
|
QString path(sycocaPath());
|
|
|
|
// KSaveFile first writes to a temp file.
|
|
// Upon finalize() it moves the stuff to the right place.
|
|
KSaveFile database(path);
|
|
bool openedOK = database.open();
|
|
if (!openedOK && database.error() == QFile::PermissionsError && QFile::exists(path))
|
|
{
|
|
QFile::remove( path );
|
|
openedOK = database.open();
|
|
}
|
|
if (!openedOK)
|
|
{
|
|
fprintf(stderr, "kbuildsycoca4: ERROR creating database '%s'! %s\n",
|
|
path.toLocal8Bit().data(), database.errorString().toLocal8Bit().data());
|
|
return false;
|
|
}
|
|
|
|
QDataStream* str = new QDataStream(&database);
|
|
|
|
kDebug(7021).nospace() << "Recreating ksycoca file (" << path << ", version " << KSycoca::version() << ")";
|
|
|
|
// It is very important to build the servicetype one first
|
|
// Both are registered in KSycoca, no need to keep the pointers
|
|
KSycocaFactory *stf = new KBuildServiceTypeFactory;
|
|
KBuildMimeTypeFactory* mimeTypeFactory = new KBuildMimeTypeFactory;
|
|
g_buildServiceGroupFactory = new KBuildServiceGroupFactory();
|
|
g_serviceFactory = new KBuildServiceFactory(stf, mimeTypeFactory, g_buildServiceGroupFactory);
|
|
(void) new KBuildProtocolInfoFactory();
|
|
|
|
if( build()) // Parse dirs
|
|
{
|
|
save(str); // Save database
|
|
if (str->status() != QDataStream::Ok) // ######## TODO: does this detect write errors, e.g. disk full?
|
|
database.abort(); // Error
|
|
delete str;
|
|
str = 0;
|
|
if (!database.finalize())
|
|
{
|
|
fprintf(stderr, "kbuildsycoca4: ERROR writing database '%s'!\n", database.fileName().toLocal8Bit().data());
|
|
fprintf(stderr, "kbuildsycoca4: Disk full?\n");
|
|
return false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
delete str;
|
|
str = 0;
|
|
database.abort();
|
|
if (bMenuTest)
|
|
return true;
|
|
kDebug(7021) << "Database is up to date";
|
|
}
|
|
|
|
if (!bGlobalDatabase)
|
|
{
|
|
// update the timestamp file
|
|
QString stamppath = path + "stamp";
|
|
QFile ksycocastamp(stamppath);
|
|
ksycocastamp.open( QIODevice::WriteOnly );
|
|
QDataStream str( &ksycocastamp );
|
|
str << newTimestamp;
|
|
str << existingResourceDirs();
|
|
if (g_vfolder)
|
|
str << g_vfolder->allDirectories(); // Extra resource dirs
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void KBuildSycoca::save(QDataStream* str)
|
|
{
|
|
// Write header (#pass 1)
|
|
str->device()->seek(0);
|
|
|
|
(*str) << (qint32) KSycoca::version();
|
|
KBuildServiceFactory * serviceFactory = 0;
|
|
for(KSycocaFactoryList::Iterator factory = factories()->begin();
|
|
factory != factories()->end();
|
|
++factory)
|
|
{
|
|
qint32 aId;
|
|
qint32 aOffset;
|
|
aId = (*factory)->factoryId();
|
|
if ( aId == KST_KServiceFactory )
|
|
serviceFactory = static_cast<KBuildServiceFactory *>( *factory );
|
|
aOffset = (*factory)->offset(); // not set yet, so always 0
|
|
(*str) << aId;
|
|
(*str) << aOffset;
|
|
}
|
|
(*str) << (qint32) 0; // No more factories.
|
|
// Write KDEDIRS
|
|
(*str) << KGlobal::dirs()->kfsstnd_prefixes();
|
|
(*str) << newTimestamp;
|
|
(*str) << KGlobal::locale()->language();
|
|
(*str) << KGlobal::dirs()->calcResourceHash("services", "update_ksycoca",
|
|
KStandardDirs::Recursive );
|
|
(*str) << (*g_allResourceDirs);
|
|
|
|
// Calculate per-servicetype/mimetype data
|
|
serviceFactory->postProcessServices();
|
|
|
|
// Here so that it's the last debug message
|
|
kDebug(7021) << "Saving";
|
|
|
|
// Write factory data....
|
|
for(KSycocaFactoryList::Iterator factory = factories()->begin();
|
|
factory != factories()->end();
|
|
++factory)
|
|
{
|
|
(*factory)->save(*str);
|
|
if (str->status() != QDataStream::Ok) // ######## TODO: does this detect write errors, e.g. disk full?
|
|
return; // error
|
|
}
|
|
|
|
int endOfData = str->device()->pos();
|
|
|
|
// Write header (#pass 2)
|
|
str->device()->seek(0);
|
|
|
|
(*str) << (qint32) KSycoca::version();
|
|
for(KSycocaFactoryList::Iterator factory = factories()->begin();
|
|
factory != factories()->end(); ++factory)
|
|
{
|
|
qint32 aId;
|
|
qint32 aOffset;
|
|
aId = (*factory)->factoryId();
|
|
aOffset = (*factory)->offset();
|
|
(*str) << aId;
|
|
(*str) << aOffset;
|
|
}
|
|
(*str) << (qint32) 0; // No more factories.
|
|
|
|
// Jump to end of database
|
|
str->device()->seek(endOfData);
|
|
}
|
|
|
|
bool KBuildSycoca::checkDirTimestamps( const QString& dirname, const QDateTime& stamp, bool top )
|
|
{
|
|
if( top )
|
|
{
|
|
QFileInfo inf( dirname );
|
|
if( inf.lastModified() > stamp ) {
|
|
kDebug( 7021 ) << "timestamp changed:" << dirname;
|
|
return false;
|
|
}
|
|
}
|
|
QDir dir( dirname );
|
|
const QFileInfoList list = dir.entryInfoList( QDir::NoFilter, QDir::Unsorted );
|
|
if (list.isEmpty())
|
|
return true;
|
|
|
|
foreach ( const QFileInfo& fi, list ) {
|
|
if( fi.fileName() == "." || fi.fileName() == ".." )
|
|
continue;
|
|
if( fi.lastModified() > stamp )
|
|
{
|
|
kDebug( 7021 ) << "timestamp changed:" << fi.filePath();
|
|
return false;
|
|
}
|
|
if( fi.isDir() && !checkDirTimestamps( fi.filePath(), stamp, false ))
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// check times of last modification of all files on which ksycoca depens,
|
|
// and also their directories
|
|
// if all of them are older than the timestamp in file ksycocastamp, this
|
|
// means that there's no need to rebuild ksycoca
|
|
bool KBuildSycoca::checkTimestamps( quint32 timestamp, const QStringList &dirs )
|
|
{
|
|
kDebug( 7021 ) << "checking file timestamps";
|
|
QDateTime stamp;
|
|
stamp.setTime_t( timestamp );
|
|
foreach(const QString &it, dirs)
|
|
{
|
|
if( !checkDirTimestamps( it, stamp, true ))
|
|
return false;
|
|
}
|
|
kDebug( 7021 ) << "timestamps check ok";
|
|
return true;
|
|
}
|
|
|
|
QStringList KBuildSycoca::existingResourceDirs()
|
|
{
|
|
static QStringList* dirs = NULL;
|
|
if( dirs != NULL )
|
|
return *dirs;
|
|
dirs = new QStringList;
|
|
g_allResourceDirs = new QStringList;
|
|
// these are all resources cached by ksycoca
|
|
QStringList resources;
|
|
resources += KBuildServiceTypeFactory::resourceTypes();
|
|
resources += KBuildMimeTypeFactory::resourceTypes();
|
|
resources += KBuildServiceGroupFactory::resourceTypes();
|
|
resources += KBuildServiceFactory::resourceTypes();
|
|
resources += KBuildProtocolInfoFactory::resourceTypes();
|
|
while( !resources.empty())
|
|
{
|
|
QString res = resources.front();
|
|
*dirs += KGlobal::dirs()->resourceDirs( res.toLatin1());
|
|
resources.removeAll( res );
|
|
}
|
|
|
|
*g_allResourceDirs = *dirs;
|
|
|
|
for( QStringList::Iterator it = dirs->begin();
|
|
it != dirs->end(); )
|
|
{
|
|
QFileInfo inf( *it );
|
|
if( !inf.exists() || !inf.isReadable() )
|
|
it = dirs->erase( it );
|
|
else
|
|
++it;
|
|
}
|
|
return *dirs;
|
|
}
|
|
|
|
static const char appFullName[] = "org.kde.kbuildsycoca";
|
|
static const char appName[] = "kbuildsycoca4";
|
|
static const char appVersion[] = "1.1";
|
|
|
|
int main(int argc, char **argv)
|
|
{
|
|
KAboutData d(appName, "kdelibs4", ki18n("KBuildSycoca"), appVersion,
|
|
ki18n("Rebuilds the system configuration cache."),
|
|
KAboutData::License_GPL, ki18n("(c) 1999-2002 KDE Developers"));
|
|
d.addAuthor(ki18n("David Faure"), ki18n("Author"), "faure@kde.org");
|
|
d.addAuthor(ki18n("Waldo Bastian"), ki18n("Author"), "bastian@kde.org");
|
|
|
|
KCmdLineOptions options;
|
|
options.add("nosignal", ki18n("Do not signal applications to update"));
|
|
options.add("noincremental", ki18n("Disable incremental update, re-read everything"));
|
|
options.add("checkstamps", ki18n("Check file timestamps"));
|
|
options.add("nocheckfiles", ki18n("Disable checking files (dangerous)"));
|
|
options.add("global", ki18n("Create global database"));
|
|
options.add("menutest", ki18n("Perform menu generation test run only"));
|
|
options.add("track <menu-id>", ki18n("Track menu id for debug purposes"));
|
|
|
|
KCmdLineArgs::init(argc, argv, &d);
|
|
KCmdLineArgs::addCmdLineOptions(options);
|
|
KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
|
|
bGlobalDatabase = args->isSet("global");
|
|
bMenuTest = args->isSet("menutest");
|
|
|
|
if (bGlobalDatabase)
|
|
{
|
|
setenv("KDEHOME", "-", 1);
|
|
setenv("KDEROOTHOME", "-", 1);
|
|
}
|
|
|
|
QCoreApplication k(argc, argv);
|
|
KComponentData mainComponent(d);
|
|
|
|
#ifndef KBUILDSYCOCA_NO_KCRASH
|
|
KCrash::setCrashHandler(crashHandler);
|
|
#endif
|
|
|
|
// force generating of KLocale object. if not, the database will get
|
|
// be translated
|
|
KGlobal::locale();
|
|
KGlobal::dirs()->addResourceType("app-reg", 0, "share/application-registry" );
|
|
|
|
while(QDBusConnection::sessionBus().isConnected())
|
|
{
|
|
// kapp registered already, but with the PID in the name.
|
|
// We need to re-register without it, to detect already-running kbuildsycoca instances.
|
|
if (QDBusConnection::sessionBus().interface()->registerService(appFullName, QDBusConnectionInterface::QueueService)
|
|
!= QDBusConnectionInterface::ServiceQueued)
|
|
{
|
|
break; // Go
|
|
}
|
|
fprintf(stderr, "Waiting for already running %s to finish.\n", appName);
|
|
|
|
QEventLoop eventLoop;
|
|
QObject::connect(QDBusConnection::sessionBus().interface(), SIGNAL(serviceRegistered(QString)),
|
|
&eventLoop, SLOT(quit()));
|
|
eventLoop.exec( QEventLoop::ExcludeUserInputEvents );
|
|
}
|
|
fprintf(stderr, "%s running...\n", appName);
|
|
|
|
bool checkfiles = bGlobalDatabase || args->isSet("checkfiles");
|
|
|
|
bool incremental = !bGlobalDatabase && args->isSet("incremental") && checkfiles;
|
|
if (incremental || !checkfiles)
|
|
{
|
|
KSycoca::disableAutoRebuild(); // Prevent deadlock
|
|
QString current_language = KGlobal::locale()->language();
|
|
QString ksycoca_language = KSycoca::self()->language();
|
|
quint32 current_update_sig = KGlobal::dirs()->calcResourceHash("services", "update_ksycoca",
|
|
KStandardDirs::Recursive );
|
|
quint32 ksycoca_update_sig = KSycoca::self()->updateSignature();
|
|
QString current_prefixes = KGlobal::dirs()->kfsstnd_prefixes();
|
|
QString ksycoca_prefixes = KSycoca::self()->kfsstnd_prefixes();
|
|
|
|
if ((current_update_sig != ksycoca_update_sig) ||
|
|
(current_language != ksycoca_language) ||
|
|
(current_prefixes != ksycoca_prefixes) ||
|
|
(KSycoca::self()->timeStamp() == 0))
|
|
{
|
|
incremental = false;
|
|
checkfiles = true;
|
|
KBuildSycoca::clearCaches();
|
|
}
|
|
}
|
|
|
|
bool checkstamps = incremental && args->isSet("checkstamps") && checkfiles;
|
|
quint32 filestamp = 0;
|
|
QStringList oldresourcedirs;
|
|
if( checkstamps && incremental )
|
|
{
|
|
QString path = sycocaPath()+"stamp";
|
|
g_sycocaPath = QFile::encodeName(path); // Delete timestamps on crash
|
|
QFile ksycocastamp(path);
|
|
if( ksycocastamp.open( QIODevice::ReadOnly ))
|
|
{
|
|
QDataStream str( &ksycocastamp );
|
|
|
|
if (!str.atEnd())
|
|
str >> filestamp;
|
|
if (!str.atEnd())
|
|
{
|
|
str >> oldresourcedirs;
|
|
if( oldresourcedirs != KBuildSycoca::existingResourceDirs())
|
|
checkstamps = false;
|
|
}
|
|
else
|
|
{
|
|
checkstamps = false;
|
|
}
|
|
if (!str.atEnd())
|
|
{
|
|
QStringList extraResourceDirs;
|
|
str >> extraResourceDirs;
|
|
oldresourcedirs += extraResourceDirs;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
checkstamps = false;
|
|
}
|
|
g_sycocaPath.clear();
|
|
}
|
|
|
|
newTimestamp = (quint32) time(0);
|
|
QStringList changedResources;
|
|
|
|
if( checkfiles && ( !checkstamps || !KBuildSycoca::checkTimestamps( filestamp, oldresourcedirs )))
|
|
{
|
|
g_sycocaPath = QFile::encodeName(sycocaPath());
|
|
|
|
g_allEntries = 0;
|
|
g_ctimeDict = 0;
|
|
if (incremental)
|
|
{
|
|
kDebug(7021) << "Reusing existing ksycoca";
|
|
KSycoca::self();
|
|
KSycocaFactoryList *factories = new KSycocaFactoryList;
|
|
g_allEntries = new KSycocaEntryListList;
|
|
g_ctimeDict = new KCTimeDict;
|
|
|
|
// Must be in same order as in KBuildSycoca::recreate()!
|
|
factories->append( new KServiceTypeFactory );
|
|
factories->append( new KMimeTypeFactory );
|
|
factories->append( new KServiceGroupFactory );
|
|
factories->append( new KServiceFactory );
|
|
factories->append( new KProtocolInfoFactory );
|
|
|
|
// For each factory
|
|
for (KSycocaFactoryList::Iterator factory = factories->begin();
|
|
factory != factories->end(); ++factory)
|
|
{
|
|
const KSycocaEntry::List list = (*factory)->allEntries();
|
|
g_allEntries->append( list );
|
|
}
|
|
delete factories; factories = 0;
|
|
KCTimeInfo *ctimeInfo = new KCTimeInfo;
|
|
*g_ctimeDict = ctimeInfo->loadDict();
|
|
}
|
|
g_sycocaPath.clear();
|
|
|
|
KBuildSycoca *sycoca = new KBuildSycoca; // Build data base (deletes oldSycoca)
|
|
if (args->isSet("track"))
|
|
sycoca->setTrackId(args->getOption("track"));
|
|
if (!sycoca->recreate()) {
|
|
return -1;
|
|
}
|
|
changedResources = sycoca->changedResources();
|
|
|
|
if (bGlobalDatabase)
|
|
{
|
|
// These directories may have been created with 0700 permission
|
|
// better delete them if they are empty
|
|
QString servicetypesDir = KGlobal::dirs()->saveLocation("servicetypes", QString(), false);
|
|
::rmdir(QFile::encodeName(servicetypesDir));
|
|
}
|
|
}
|
|
|
|
if (args->isSet("signal"))
|
|
{
|
|
// Notify ALL applications that have a ksycoca object, using a signal
|
|
changedResources.removeDuplicates();
|
|
QDBusMessage signal = QDBusMessage::createSignal("/", "org.kde.KSycoca", "notifyDatabaseChanged" );
|
|
signal << changedResources;
|
|
|
|
if (QDBusConnection::sessionBus().isConnected()) {
|
|
kDebug() << "Emitting notifyDatabaseChanged" << changedResources;
|
|
QDBusConnection::sessionBus().send(signal);
|
|
qApp->processEvents(); // make sure the dbus signal is sent before we quit.
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#include "moc_kbuildsycoca.cpp"
|