mirror of
https://bitbucket.org/smil3y/kdelibs.git
synced 2025-02-23 10:22:48 +00:00
817 lines
26 KiB
C++
817 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 <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 <kglobal.h>
|
|
#include <kdebug.h>
|
|
#include <kdirwatch.h>
|
|
#include <kstandarddirs.h>
|
|
#include <ksavefile.h>
|
|
#include <klocale.h>
|
|
#include <kaboutdata.h>
|
|
#include <kcmdlineargs.h>
|
|
#include <kde_file.h>
|
|
#include <kcrash.h>
|
|
|
|
#include <QDir>
|
|
#include <QFileInfo>
|
|
#include <QCoreApplication>
|
|
#include <QDBusConnectionInterface>
|
|
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <time.h>
|
|
#include <signal.h>
|
|
#include <errno.h>
|
|
#include <assert.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 QLatin1String g_defaultMenu = QLatin1String("kde-applications.menu");
|
|
|
|
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);
|
|
|
|
QString applicationsMenu = g_defaultMenu;
|
|
const QString xdgMenuPrefix = QString::fromLocal8Bit(qgetenv("XDG_MENU_PREFIX"));
|
|
if (!xdgMenuPrefix.isEmpty())
|
|
{
|
|
applicationsMenu = xdgMenuPrefix + QLatin1String("applications.menu");
|
|
const bool isValid = !KStandardDirs::locate("xdgconf-menu", applicationsMenu).isEmpty();
|
|
if (!isValid) {
|
|
kWarning() << "Specified XDG_MENU_PREFIX results in invalid menu" << applicationsMenu;
|
|
applicationsMenu = g_defaultMenu;
|
|
}
|
|
}
|
|
VFolderMenu::SubMenu *kdeMenu = g_vfolder->parseMenu(applicationsMenu);
|
|
|
|
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);
|
|
|
|
KCrash::setCrashHandler(crashHandler);
|
|
|
|
// 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"
|