9#include "kbuildsycoca_p.h"
11#include "ksycocaresourcelist_p.h"
12#include "ksycocautils_p.h"
13#include "sycocadebug.h"
14#include "vfolder_menu_p.h"
16#include "kbuildmimetypefactory_p.h"
17#include "kbuildservicefactory_p.h"
18#include "kbuildservicegroupfactory_p.h"
19#include "kctimefactory_p.h"
24#include <QDirIterator>
30#include <config-ksycoca.h>
32#include <kservicegroup.h>
34#include <kmemfile_p.h>
37#include <QStandardPaths>
38#include <qplatformdefs.h>
40static const char *s_cSycocaPath =
nullptr;
42KBuildSycocaInterface::~KBuildSycocaInterface()
46KBuildSycoca::KBuildSycoca()
48 , m_allEntries(nullptr)
49 , m_ctimeFactory(nullptr)
50 , m_ctimeDict(nullptr)
51 , m_currentEntryDict(nullptr)
52 , m_serviceGroupEntryDict(nullptr)
60KBuildSycoca::~KBuildSycoca()
63 qDeleteAll(*factories());
69 quint32 timeStamp = m_ctimeFactory->dict()->ctime(file, m_resource);
71 timeStamp = calcResourceHash(m_resourceSubdir, file);
78 Q_ASSERT(m_ctimeDict);
79 quint32 oldTimestamp = m_ctimeDict->ctime(file, m_resource);
81 qCDebug(SYCOCA) <<
"m_ctimeDict->ctime(" << file <<
") = " << oldTimestamp <<
"compared with" << timeStamp;
84 if (timeStamp && (timeStamp == oldTimestamp)) {
86 if (currentFactory == d->m_serviceFactory) {
87 entry = m_currentEntryDict->value(file.
left(file.
length() - 10));
89 entry = m_currentEntryDict->value(file);
95 qCDebug(SYCOCA) <<
"reusing (and removing) old entry for:" << file <<
"entry=" << entry;
97 m_ctimeDict->remove(file, m_resource);
98 }
else if (oldTimestamp) {
100 m_ctimeDict->remove(file, m_resource);
101 qCDebug(SYCOCA) <<
"modified:" << file;
104 qCDebug(SYCOCA) <<
"new:" << file;
107 m_ctimeFactory->dict()->addCTime(file, m_resource, timeStamp);
110 entry = currentFactory->createEntry(file);
112 if (entry && entry->isValid()) {
122 m_tempStorage.append(entry);
128bool KBuildSycoca::build()
131 KBSEntryDictList entryDictList;
132 KBSEntryDict *serviceEntryDict =
nullptr;
135 entryDictList.
reserve(factories()->size());
138 const auto &factoryList = *factories();
139 for (KSycocaFactory *factory : factoryList) {
140 KBSEntryDict *entryDict =
new KBSEntryDict;
145 entryDict->insert(entry->entryPath(), entry);
148 if (factory == d->m_serviceFactory) {
149 serviceEntryDict = entryDict;
150 }
else if (factory == m_buildServiceGroupFactory) {
151 m_serviceGroupEntryDict = entryDict;
153 entryDictList.append(entryDict);
158 const auto lstDirs = factoryResourceDirs();
159 for (
const QString &dir : lstDirs) {
161 KSycocaUtilsPrivate::visitResourceDirectory(dir, [&stamp](
const QFileInfo &info) {
165 m_allResourceDirs.insert(dir, stamp);
168 const auto lstFiles = factoryExtraFiles();
169 for (
const QString &file : lstFiles) {
170 m_extraFiles.insert(file,
QFileInfo(file).lastModified().toMSecsSinceEpoch());
175 for (KSycocaFactory *factory : factoryList) {
177 const KSycocaResourceList resourceList = factory->resourceList();
178 for (
const KSycocaResource &res : resourceList) {
182 allResourcesSubDirs.
insert(res.subdir, res.resource);
186 m_ctimeFactory =
new KCTimeFactory(
this);
187 for (
auto it1 = allResourcesSubDirs.
cbegin(); it1 != allResourcesSubDirs.
cend(); ++it1) {
189 m_resourceSubdir = it1.key();
190 m_resource = it1.value();
194 qCDebug(SYCOCA) <<
"Looking for subdir" << m_resourceSubdir <<
"=>" << dirs;
195 for (
const QString &dir : dirs) {
197 while (it.hasNext()) {
198 const QString filePath = it.next();
206 for (
int f = 0; f < factoryList.count(); ++f) {
207 KSycocaFactory *currentFactory = factoryList.at(f);
209 m_currentEntryDict = f == entryDictList.size() ? nullptr : entryDictList.at(f);
211 const KSycocaResourceList &resourceList = currentFactory->resourceList();
212 for (
const KSycocaResource &res : resourceList) {
213 if (res.resource != m_resource) {
218 for (
const QString &entryPath : std::as_const(relFiles)) {
220 if (entryPath.endsWith(res.extension)) {
223 currentFactory->addEntry(entry);
232 const bool createVFolder =
true;
233 if (createVFolder || m_menuTest) {
235 m_resourceSubdir = QStringLiteral(
"applications");
236 m_currentEntryDict = serviceEntryDict;
239 m_vfolder =
new VFolderMenu(d->m_serviceFactory,
this);
240 if (!m_trackId.isEmpty()) {
241 m_vfolder->setTrackId(m_trackId);
244 VFolderMenu::SubMenu *kdeMenu = m_vfolder->parseMenu(QStringLiteral(
"applications.menu"));
247 entry->setLayoutInfo(kdeMenu->layoutList);
252 const auto allDirectories = m_vfolder->allDirectories();
253 for (
QString dir : allDirectories) {
257 if (!m_allResourceDirs.contains(dir)) {
259 KSycocaUtilsPrivate::visitResourceDirectory(dir, [&stamp](
const QFileInfo &info) {
263 m_allResourceDirs.insert(dir, stamp);
272 if (m_ctimeDict && !m_ctimeDict->isEmpty()) {
273 qCDebug(SYCOCA) <<
"Still in time dict:";
277 qDeleteAll(entryDictList);
281void KBuildSycoca::createMenu(
const QString &caption_,
const QString &name_, VFolderMenu::SubMenu *menu)
285 for (VFolderMenu::SubMenu *subMenu : std::as_const(menu->subMenus)) {
288 QString directoryFile = subMenu->directoryFile;
292 quint32 timeStamp = m_ctimeFactory->dict()->ctime(directoryFile, m_resource);
294 timeStamp = calcResourceHash(m_resourceSubdir, directoryFile);
299 const quint32 oldTimestamp = m_ctimeDict->ctime(directoryFile, m_resource);
301 if (timeStamp && (timeStamp == oldTimestamp)) {
305 if (entry->directoryEntryPath() != directoryFile) {
312 m_ctimeFactory->dict()->addCTime(directoryFile, m_resource, timeStamp);
315 entry = m_buildServiceGroupFactory->addNew(subName, subMenu->directoryFile, entry, subMenu->isDeleted);
316 entry->setLayoutInfo(subMenu->layoutList);
317 if (!(m_menuTest && entry->noDisplay())) {
329 if (!menu->isDeleted && !p->noDisplay()) {
330 printf(
"%s\t%s\t%s\n",
332 qPrintable(p->menuId()),
336 m_buildServiceGroupFactory->addNewEntryTo(name, p);
341bool KBuildSycoca::recreate(
bool incremental)
345 qCWarning(SYCOCA) <<
"Couldn't create" << fi.absolutePath();
351 if (!lockFile.tryLock()) {
352 qCDebug(SYCOCA) <<
"Waiting for already running" << KBUILDSYCOCA_EXENAME <<
"to finish.";
353 if (!lockFile.lock()) {
357 if (!needsRebuild()) {
364 s_cSycocaPath = qSycocaPath.
data();
366 m_allEntries =
nullptr;
367 m_ctimeDict =
nullptr;
368 if (incremental && checkGlobalHeader()) {
369 qCDebug(SYCOCA) <<
"Reusing existing ksycoca";
371 m_allEntries =
new KSycocaEntryListList;
372 m_ctimeDict =
new KCTimeDict;
375 m_allEntries->append(KSycocaPrivate::self()->mimeTypeFactory()->allEntries());
376 m_allEntries->append(KSycocaPrivate::self()->serviceGroupFactory()->allEntries());
377 m_allEntries->append(KSycocaPrivate::self()->serviceFactory()->allEntries());
379 KCTimeFactory *ctimeInfo =
new KCTimeFactory(oldSycoca);
380 *m_ctimeDict = ctimeInfo->loadDict();
382 s_cSycocaPath =
nullptr;
392 qCWarning(SYCOCA) <<
"ERROR creating database" <<
path <<
":" << database.errorString();
400 qCDebug(SYCOCA).nospace() <<
"Recreating ksycoca file (" <<
path <<
", version " <<
KSycoca::version() <<
")";
402 KBuildMimeTypeFactory *buildMimeTypeFactory =
new KBuildMimeTypeFactory(
this);
403 d->m_mimeTypeFactory = buildMimeTypeFactory;
404 m_buildServiceGroupFactory =
new KBuildServiceGroupFactory(
this);
405 d->m_serviceGroupFactory = m_buildServiceGroupFactory;
406 d->m_serviceFactory =
new KBuildServiceFactory(buildMimeTypeFactory);
411 database.cancelWriting();
419 if (qEnvironmentVariableIsSet(
"SUDO_UID")) {
420 const int uid = qEnvironmentVariableIntValue(
"SUDO_UID");
421 const int gid = qEnvironmentVariableIntValue(
"SUDO_GID");
423 fchown(database.handle(), uid, gid);
428 if (!database.commit()) {
429 qCWarning(SYCOCA) <<
"ERROR writing database" << database.fileName() << database.errorString();
435 database.cancelWriting();
439 qCDebug(SYCOCA) <<
"Database is up to date";
442#ifndef QT_NO_SHAREDMEMORY
443 if (d->m_sycocaStrategy == KSycocaPrivate::StrategyMemFile) {
444 KMemFile::fileContentsChanged(path);
463 KBuildServiceFactory *serviceFactory =
nullptr;
464 auto lst = *factories();
465 for (KSycocaFactory *factory : std::as_const(lst)) {
468 aId = factory->factoryId();
473 if (aId == KST_KServiceFactory) {
474 serviceFactory =
static_cast<KBuildServiceFactory *
>(factory);
476 aOffset = factory->offset();
483 (*str) << m_newTimestamp;
486 (*str) << calcResourceHash(QStringLiteral(
"kservices6"), QStringLiteral(
"update_ksycoca"));
487 (*str) << m_allResourceDirs.keys();
488 for (
auto it = m_allResourceDirs.constBegin(); it != m_allResourceDirs.constEnd(); ++it) {
489 (*str) << it.value();
491 (*str) << m_extraFiles.keys();
492 for (
auto it = m_extraFiles.constBegin(); it != m_extraFiles.constEnd(); ++it) {
493 (*str) << it.value();
497 if (serviceFactory) {
498 serviceFactory->postProcessServices();
502 qCDebug(SYCOCA) <<
"Saving";
506 for (KSycocaFactory *factory : std::as_const(lst)) {
520 for (KSycocaFactory *factory : std::as_const(lst)) {
523 aId = factory->factoryId();
524 aOffset = factory->offset();
537 if (dirs !=
nullptr) {
542 *dirs += KMimeTypeFactory::resourceDirs();
543 *dirs += KServiceFactory::resourceDirs();
553 files += KMimeAssociations::mimeAppsFiles();
561 if (dirs !=
nullptr) {
566 auto checkDir = [](
const QString &str) {
570 dirs->
erase(std::remove_if(dirs->
begin(), dirs->
end(), checkDir), dirs->
end());
575static quint32 updateHash(
const QString &file, quint32 hash)
578 if (fi.isReadable() && fi.isFile()) {
581 qint64 timestamp = fi.lastModified().toSecsSinceEpoch();
585 if (timestamp == 0) {
594quint32 KBuildSycoca::calcResourceHash(
const QString &resourceSubDir,
const QString &filename)
598 return updateHash(filename, hash);
601 const QString qrcFilePath = QStringLiteral(
":/") + filePath;
604 for (
const QString &file : files) {
605 hash = updateHash(file, hash);
607 if (hash == 0 && !filename.endsWith(
QLatin1String(
"update_ksycoca"))
610 if (files.isEmpty()) {
612 qCDebug(SYCOCA) <<
"File not found anymore:" << filename <<
" -- probably deleted meanwhile";
615 qCDebug(SYCOCA) <<
"File(s) found but not readable (or disappeared meanwhile)" << files;
621bool KBuildSycoca::checkGlobalHeader()
625 const quint32 current_update_sig = KBuildSycoca::calcResourceHash(QStringLiteral(
"kservices6"), QStringLiteral(
"update_ksycoca"));
628 const KSycocaHeader header = KSycocaPrivate::self()->readSycocaHeader();
631 return (current_update_sig == header.updateSignature)
632 && (current_language == header.language)
633 && (current_prefixes == header.prefixes)
634 && (header.timeStamp != 0);
637const char *KBuildSycoca::sycocaPath()
639 return s_cSycocaPath;
642#include "moc_kbuildsycoca_p.cpp"
KServiceGroup represents a group of service, for example screensavers.
QExplicitlySharedDataPointer< KServiceGroup > Ptr
A shared data pointer for KServiceGroup.
Represents an installed application.
QExplicitlySharedDataPointer< KService > Ptr
A shared data pointer for KService.
QExplicitlySharedDataPointer< KSycocaEntry > Ptr
A shared data pointer for KSycocaEntry.
static KSycoca * self()
Get or create the only instance of KSycoca (read-only)
static QString absoluteFilePath()
KIOCORE_EXPORT MkpathJob * mkpath(const QUrl &url, const QUrl &baseUrl=QUrl(), JobFlags flags=DefaultFlags)
QString path(const QString &relativePath)
KIOCORE_EXPORT QString dir(const QString &fileClass)
QString name(StandardAction id)
QAction * save(const QObject *recvr, const char *slot, QObject *parent)
QIODevice * device() const const
Status status() const const
QDateTime currentDateTimeUtc()
qint64 currentMSecsSinceEpoch()
qint64 toMSecsSinceEpoch() const const
qint64 toSecsSinceEpoch() const const
bool isRelativePath(const QString &path)
QByteArray encodeName(const QString &fileName)
bool exists() const const
bool exists() const const
bool exists(const QString &path)
bool isReadable() const const
QDateTime lastModified() const const
virtual qint64 pos() const const
virtual bool seek(qint64 pos)
iterator erase(const_iterator begin, const_iterator end)
void reserve(qsizetype size)
QString bcp47Name() const const
const_iterator cbegin() const const
const_iterator cend() const const
iterator insert(const Key &key, const T &value)
iterator insert(const T &value)
QString locate(StandardLocation type, const QString &fileName, LocateOptions options)
QStringList locateAll(StandardLocation type, const QString &fileName, LocateOptions options)
QStringList standardLocations(StandardLocation type)
bool contains(QChar ch, Qt::CaseSensitivity cs) const const
bool endsWith(QChar c, Qt::CaseSensitivity cs) const const
bool isEmpty() const const
QString left(qsizetype n) const const
qsizetype length() const const
QString mid(qsizetype position, qsizetype n) const const
bool startsWith(QChar c, Qt::CaseSensitivity cs) const const
QString join(QChar separator) const const