File indexing completed on 2024-04-21 07:43:54

0001 /*
0002     This file is part of the KDE libraries
0003     SPDX-FileCopyrightText: 1999 David Faure <faure@kde.org>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-only
0006 */
0007 
0008 #include "ksycoca.h"
0009 #include "ksycocadict_p.h"
0010 #include "ksycocaentry.h"
0011 #include "ksycocaentry_p.h"
0012 #include "ksycocafactory_p.h"
0013 #include "ksycocatype.h"
0014 #include "sycocadebug.h"
0015 
0016 #include <QDebug>
0017 #include <QHash>
0018 #include <QIODevice>
0019 #include <QThread>
0020 
0021 class KSycocaFactoryPrivate
0022 {
0023 public:
0024     KSycocaFactoryPrivate()
0025     {
0026     }
0027     ~KSycocaFactoryPrivate()
0028     {
0029         delete m_sycocaDict;
0030     }
0031 
0032     int mOffset = 0;
0033     int m_sycocaDictOffset = 0;
0034     int m_beginEntryOffset = 0;
0035     int m_endEntryOffset = 0;
0036     KSycocaDict *m_sycocaDict = nullptr;
0037 };
0038 
0039 KSycocaFactory::KSycocaFactory(KSycocaFactoryId factory_id, KSycoca *sycoca)
0040     : m_sycoca(sycoca)
0041     , d(new KSycocaFactoryPrivate)
0042 {
0043     if (!m_sycoca->isBuilding() && (m_str = m_sycoca->findFactory(factory_id))) {
0044         // Read position of index tables....
0045         qint32 i;
0046         (*m_str) >> i;
0047         d->m_sycocaDictOffset = i;
0048         (*m_str) >> i;
0049         d->m_beginEntryOffset = i;
0050         (*m_str) >> i;
0051         d->m_endEntryOffset = i;
0052 
0053         QDataStream *str = stream();
0054         qint64 saveOffset = str->device()->pos();
0055         // Init index tables
0056         d->m_sycocaDict = new KSycocaDict(str, d->m_sycocaDictOffset);
0057         saveOffset = str->device()->seek(saveOffset);
0058     } else {
0059         // We are in kbuildsycoca -- build new database!
0060         m_entryDict = new KSycocaEntryDict;
0061         d->m_sycocaDict = new KSycocaDict;
0062         d->m_beginEntryOffset = 0;
0063         d->m_endEntryOffset = 0;
0064 
0065         // m_resourceList will be filled in by inherited constructors
0066     }
0067     m_sycoca->addFactory(this);
0068 }
0069 
0070 KSycocaFactory::~KSycocaFactory()
0071 {
0072     delete m_entryDict;
0073 }
0074 
0075 void KSycocaFactory::saveHeader(QDataStream &str)
0076 {
0077     // Write header
0078     str.device()->seek(d->mOffset);
0079     str << qint32(d->m_sycocaDictOffset);
0080     str << qint32(d->m_beginEntryOffset);
0081     str << qint32(d->m_endEntryOffset);
0082 }
0083 
0084 void KSycocaFactory::save(QDataStream &str)
0085 {
0086     if (!m_entryDict) {
0087         return; // Error! Function should only be called when building database
0088     }
0089     if (!d->m_sycocaDict) {
0090         return; // Error!
0091     }
0092 
0093     d->mOffset = str.device()->pos(); // store position in member variable
0094     d->m_sycocaDictOffset = 0;
0095 
0096     // Write header (pass #1)
0097     saveHeader(str);
0098 
0099     d->m_beginEntryOffset = str.device()->pos();
0100 
0101     // Write all entries.
0102     int entryCount = 0;
0103     for (KSycocaEntry::Ptr entry : std::as_const(*m_entryDict)) {
0104         entry->d_ptr->save(str);
0105         entryCount++;
0106     }
0107 
0108     d->m_endEntryOffset = str.device()->pos();
0109 
0110     // Write indices...
0111     // Linear index
0112     str << qint32(entryCount);
0113     for (const KSycocaEntry::Ptr &entry : std::as_const(*m_entryDict)) {
0114         str << qint32(entry.data()->offset());
0115     }
0116 
0117     // Dictionary index
0118     d->m_sycocaDictOffset = str.device()->pos();
0119     d->m_sycocaDict->save(str);
0120 
0121     qint64 endOfFactoryData = str.device()->pos();
0122 
0123     // Update header (pass #2)
0124     saveHeader(str);
0125 
0126     // Seek to end.
0127     str.device()->seek(endOfFactoryData);
0128 }
0129 
0130 void KSycocaFactory::addEntry(const KSycocaEntry::Ptr &newEntry)
0131 {
0132     if (!m_entryDict) {
0133         return; // Error! Function should only be called when
0134     }
0135     // building database
0136 
0137     if (!d->m_sycocaDict) {
0138         return; // Error!
0139     }
0140 
0141     KSycocaEntry::Ptr oldEntry = m_entryDict->value(newEntry->storageId());
0142     if (oldEntry) {
0143         // Already exists -> replace
0144         // We found a more-local override, e.g. ~/.local/share/applications/kde5/foo.desktop
0145         // So forget about the more global file.
0146         //
0147         // This can also happen with two .protocol files using the same protocol= entry.
0148         // If we didn't remove one here, we would end up asserting because save()
0149         // wasn't called on one of the entries.
0150         // qDebug() << "removing" << oldEntry.data() << oldEntry->entryPath() << "because of" << newEntry->entryPath() << "they have the same storageId" <<
0151         // newEntry->storageId();
0152         removeEntry(newEntry->storageId());
0153     }
0154 
0155     const QString name = newEntry->storageId();
0156     m_entryDict->insert(name, newEntry);
0157     d->m_sycocaDict->add(name, newEntry);
0158 }
0159 
0160 void KSycocaFactory::removeEntry(const QString &entryName)
0161 {
0162     if (!m_entryDict) {
0163         return; // Error! Function should only be called when
0164     }
0165     // building database
0166 
0167     if (!d->m_sycocaDict) {
0168         return; // Error!
0169     }
0170 
0171     m_entryDict->remove(entryName);
0172     d->m_sycocaDict->remove(entryName); // O(N)
0173 }
0174 
0175 KSycocaEntry::List KSycocaFactory::allEntries() const
0176 {
0177     KSycocaEntry::List list;
0178 
0179     // Assume we're NOT building a database
0180 
0181     QDataStream *str = stream();
0182     if (!str) {
0183         return list;
0184     }
0185     str->device()->seek(d->m_endEntryOffset);
0186     qint32 entryCount;
0187     (*str) >> entryCount;
0188 
0189     if (entryCount > 8192) {
0190         qCWarning(SYCOCA) << QThread::currentThread() << "error detected in factory" << this;
0191         KSycoca::flagError();
0192         return list;
0193     }
0194 
0195     // offsetList is needed because createEntry() modifies the stream position
0196     qint32 *offsetList = new qint32[entryCount];
0197     for (int i = 0; i < entryCount; i++) {
0198         (*str) >> offsetList[i];
0199     }
0200 
0201     for (int i = 0; i < entryCount; i++) {
0202         KSycocaEntry *newEntry = createEntry(offsetList[i]);
0203         if (newEntry) {
0204             list.append(KSycocaEntry::Ptr(newEntry));
0205         }
0206     }
0207     delete[] offsetList;
0208     return list;
0209 }
0210 
0211 int KSycocaFactory::offset() const
0212 {
0213     return d->mOffset;
0214 }
0215 
0216 const KSycocaResourceList &KSycocaFactory::resourceList() const
0217 {
0218     return m_resourceList;
0219 }
0220 
0221 const KSycocaDict *KSycocaFactory::sycocaDict() const
0222 {
0223     return d->m_sycocaDict;
0224 }
0225 
0226 bool KSycocaFactory::isEmpty() const
0227 {
0228     return d->m_beginEntryOffset == d->m_endEntryOffset;
0229 }
0230 
0231 QDataStream *KSycocaFactory::stream() const
0232 {
0233     return m_str;
0234 }
0235 
0236 QStringList KSycocaFactory::allDirectories(const QString &subdir)
0237 {
0238     // We don't use QStandardPaths::locateAll() because we want all paths, even those that don't exist yet
0239     QStringList topDirs = QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation);
0240     for (auto &dir : topDirs) {
0241         dir += QLatin1Char('/') + subdir;
0242     }
0243     return topDirs;
0244 }
0245 
0246 void KSycocaFactory::virtual_hook(int /*id*/, void * /*data*/)
0247 {
0248     /*BASE::virtual_hook( id, data );*/
0249 }