ksycoca.cpp
00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019 #include "config.h"
00020
00021 #include "ksycoca.h"
00022 #include "ksycocatype.h"
00023 #include "ksycocafactory.h"
00024
00025 #include <qdatastream.h>
00026 #include <qfile.h>
00027 #include <qbuffer.h>
00028
00029 #include <kapplication.h>
00030 #include <dcopclient.h>
00031 #include <kglobal.h>
00032 #include <kdebug.h>
00033 #include <kprocess.h>
00034 #include <kstandarddirs.h>
00035
00036 #include <assert.h>
00037 #include <stdlib.h>
00038 #include <unistd.h>
00039 #include <fcntl.h>
00040
00041 #ifdef HAVE_SYS_MMAN_H
00042 #include <sys/mman.h>
00043 #endif
00044
00045 #ifndef MAP_FAILED
00046 #define MAP_FAILED ((void *) -1)
00047 #endif
00048
00049 template class QPtrList<KSycocaFactory>;
00050
00051
00052
00053
00054
00055
00056
00057
00058
00059 struct KSycocaPrivate {
00060 KSycocaPrivate() {
00061 database = 0;
00062 readError = false;
00063 updateSig = 0;
00064 autoRebuild = true;
00065 }
00066 QFile *database;
00067 QStringList changeList;
00068 QString language;
00069 bool readError;
00070 bool autoRebuild;
00071 Q_UINT32 updateSig;
00072 QStringList allResourceDirs;
00073 };
00074
00075 int KSycoca::version()
00076 {
00077 return KSYCOCA_VERSION;
00078 }
00079
00080
00081 KSycoca::KSycoca()
00082 : DCOPObject("ksycoca"), m_lstFactories(0), m_str(0), bNoDatabase(false),
00083 m_sycoca_size(0), m_sycoca_mmap(0), m_timeStamp(0)
00084 {
00085 d = new KSycocaPrivate;
00086
00087 if (kapp && !kapp->dcopClient()->isAttached())
00088 {
00089 kapp->dcopClient()->attach();
00090 }
00091
00092
00093
00094
00095 openDatabase();
00096 _self = this;
00097 }
00098
00099 bool KSycoca::openDatabase( bool openDummyIfNotFound )
00100 {
00101 bool result = true;
00102
00103 m_sycoca_mmap = 0;
00104 m_str = 0;
00105 QString path;
00106 QCString ksycoca_env = getenv("KDESYCOCA");
00107 if (ksycoca_env.isEmpty())
00108 path = KGlobal::dirs()->saveLocation("cache") + "ksycoca";
00109 else
00110 path = QFile::decodeName(ksycoca_env);
00111
00112 kdDebug(7011) << "Trying to open ksycoca from " << path << endl;
00113 QFile *database = new QFile(path);
00114 bool bOpen = database->open( IO_ReadOnly );
00115 if (!bOpen)
00116 {
00117 path = locate("services", "ksycoca");
00118 if (!path.isEmpty())
00119 {
00120 kdDebug(7011) << "Trying to open global ksycoca from " << path << endl;
00121 delete database;
00122 database = new QFile(path);
00123 bOpen = database->open( IO_ReadOnly );
00124 }
00125 }
00126
00127 if (bOpen)
00128 {
00129 fcntl(database->handle(), F_SETFD, FD_CLOEXEC);
00130 m_sycoca_size = database->size();
00131 #ifdef HAVE_MMAP
00132 m_sycoca_mmap = (const char *) mmap(0, m_sycoca_size,
00133 PROT_READ, MAP_SHARED,
00134 database->handle(), 0);
00135
00136
00137 if (m_sycoca_mmap == (const char*) MAP_FAILED || m_sycoca_mmap == 0)
00138 {
00139 kdDebug(7011) << "mmap failed. (length = " << m_sycoca_size << ")" << endl;
00140 #endif
00141 m_str = new QDataStream(database);
00142 #ifdef HAVE_MMAP
00143 }
00144 else
00145 {
00146 QByteArray b_array;
00147 b_array.setRawData(m_sycoca_mmap, m_sycoca_size);
00148 QBuffer *buffer = new QBuffer( b_array );
00149 buffer->open(IO_ReadWrite);
00150 m_str = new QDataStream( buffer);
00151 }
00152 #endif
00153 bNoDatabase = false;
00154 }
00155 else
00156 {
00157 kdDebug(7011) << "Could not open ksycoca" << endl;
00158
00159
00160 delete database;
00161 database = 0;
00162
00163 bNoDatabase = true;
00164 if (openDummyIfNotFound)
00165 {
00166
00167
00168 QBuffer *buffer = new QBuffer( QByteArray() );
00169 buffer->open(IO_ReadWrite);
00170 m_str = new QDataStream( buffer);
00171 (*m_str) << (Q_INT32) KSYCOCA_VERSION;
00172 (*m_str) << (Q_INT32) 0;
00173 }
00174 else
00175 {
00176 result = false;
00177 }
00178 }
00179 m_lstFactories = new KSycocaFactoryList();
00180 m_lstFactories->setAutoDelete( true );
00181 d->database = database;
00182 return result;
00183 }
00184
00185
00186 KSycoca::KSycoca( bool )
00187 : DCOPObject("ksycoca_building"), m_lstFactories(0), m_str(0), bNoDatabase(false),
00188 m_sycoca_size(0), m_sycoca_mmap(0)
00189 {
00190 d = new KSycocaPrivate;
00191 m_lstFactories = new KSycocaFactoryList();
00192 m_lstFactories->setAutoDelete( true );
00193 _self = this;
00194 }
00195
00196 static void delete_ksycoca_self() {
00197 if (KSycoca::_checkSelf())
00198 delete KSycoca::_self;
00199
00200 }
00201
00202 bool KSycoca::_checkSelf() {
00203 return (_self ? true : false);
00204 }
00205
00206 KSycoca * KSycoca::self()
00207 {
00208 if (!_self) {
00209 qAddPostRoutine(delete_ksycoca_self);
00210 _self = new KSycoca();
00211 }
00212 return _self;
00213 }
00214
00215 KSycoca::~KSycoca()
00216 {
00217 closeDatabase();
00218 delete d;
00219 _self = 0L;
00220 }
00221
00222 void KSycoca::closeDatabase()
00223 {
00224 QIODevice *device = 0;
00225 if (m_str)
00226 device = m_str->device();
00227 #ifdef HAVE_MMAP
00228 if (device && m_sycoca_mmap)
00229 {
00230 QBuffer *buf = (QBuffer *) device;
00231 buf->buffer().resetRawData(m_sycoca_mmap, m_sycoca_size);
00232
00233
00234 munmap((char*) m_sycoca_mmap, m_sycoca_size);
00235 m_sycoca_mmap = 0;
00236 }
00237 #endif
00238
00239 delete m_str;
00240 m_str = 0;
00241 delete device;
00242 if (d->database != device)
00243 delete d->database;
00244 device = 0;
00245 d->database = 0;
00246
00247
00248 delete m_lstFactories;
00249 m_lstFactories = 0L;
00250 }
00251
00252 void KSycoca::addFactory( KSycocaFactory *factory )
00253 {
00254 assert(m_lstFactories);
00255 m_lstFactories->append(factory);
00256 }
00257
00258 bool KSycoca::isChanged(const char *type)
00259 {
00260 return self()->d->changeList.contains(type);
00261 }
00262
00263 void KSycoca::notifyDatabaseChanged(const QStringList &changeList)
00264 {
00265 d->changeList = changeList;
00266
00267
00268
00269
00270
00271 closeDatabase();
00272
00273
00274 emit databaseChanged();
00275 }
00276
00277 QDataStream * KSycoca::findEntry(int offset, KSycocaType &type)
00278 {
00279 if ( !m_str )
00280 openDatabase();
00281
00282 m_str->device()->at(offset);
00283 Q_INT32 aType;
00284 (*m_str) >> aType;
00285 type = (KSycocaType) aType;
00286
00287 return m_str;
00288 }
00289
00290 bool KSycoca::checkVersion(bool abortOnError)
00291 {
00292 if ( !m_str )
00293 {
00294 if( !openDatabase(false ) )
00295 return false;
00296
00297
00298 assert(m_str);
00299 }
00300 m_str->device()->at(0);
00301 Q_INT32 aVersion;
00302 (*m_str) >> aVersion;
00303 if ( aVersion < KSYCOCA_VERSION )
00304 {
00305 kdWarning(7011) << "Found version " << aVersion << ", expecting version " << KSYCOCA_VERSION << " or higher." << endl;
00306 if (!abortOnError) return false;
00307 kdError(7011) << "Outdated database ! Stop kded and restart it !" << endl;
00308 abort();
00309 }
00310 return true;
00311 }
00312
00313 QDataStream * KSycoca::findFactory(KSycocaFactoryId id)
00314 {
00315
00316 if (bNoDatabase)
00317 {
00318 closeDatabase();
00319
00320 if ( !openDatabase(false ) )
00321 {
00322 static bool triedLaunchingKdeinit = false;
00323 if (!triedLaunchingKdeinit)
00324 {
00325 triedLaunchingKdeinit = true;
00326 kdDebug(7011) << "findFactory: we have no database.... launching kdeinit" << endl;
00327 KApplication::startKdeinit();
00328
00329 }
00330 if (!openDatabase(false))
00331 return 0L;
00332 }
00333 }
00334
00335 if (!checkVersion(false))
00336 {
00337 kdWarning(7011) << "Outdated database found" << endl;
00338 return 0L;
00339 }
00340 Q_INT32 aId;
00341 Q_INT32 aOffset;
00342 while(true)
00343 {
00344 (*m_str) >> aId;
00345
00346 if (aId == 0)
00347 {
00348 kdError(7011) << "Error, KSycocaFactory (id = " << int(id) << ") not found!" << endl;
00349 break;
00350 }
00351 (*m_str) >> aOffset;
00352 if (aId == id)
00353 {
00354
00355 m_str->device()->at(aOffset);
00356 return m_str;
00357 }
00358 }
00359 return 0;
00360 }
00361
00362 QString KSycoca::kfsstnd_prefixes()
00363 {
00364 if (bNoDatabase) return "";
00365 if (!checkVersion(false)) return "";
00366 Q_INT32 aId;
00367 Q_INT32 aOffset;
00368
00369 while(true)
00370 {
00371 (*m_str) >> aId;
00372 if ( aId )
00373 (*m_str) >> aOffset;
00374 else
00375 break;
00376 }
00377
00378 QString prefixes;
00379 KSycocaEntry::read(*m_str, prefixes);
00380 (*m_str) >> m_timeStamp;
00381 KSycocaEntry::read(*m_str, d->language);
00382 (*m_str) >> d->updateSig;
00383 KSycocaEntry::read(*m_str, d->allResourceDirs);
00384 return prefixes;
00385 }
00386
00387 Q_UINT32 KSycoca::timeStamp()
00388 {
00389 if (!m_timeStamp)
00390 (void) kfsstnd_prefixes();
00391 return m_timeStamp;
00392 }
00393
00394 Q_UINT32 KSycoca::updateSignature()
00395 {
00396 if (!m_timeStamp)
00397 (void) kfsstnd_prefixes();
00398 return d->updateSig;
00399 }
00400
00401 QString KSycoca::language()
00402 {
00403 if (d->language.isEmpty())
00404 (void) kfsstnd_prefixes();
00405 return d->language;
00406 }
00407
00408 QStringList KSycoca::allResourceDirs()
00409 {
00410 if (!m_timeStamp)
00411 (void) kfsstnd_prefixes();
00412 return d->allResourceDirs;
00413 }
00414
00415 QString KSycoca::determineRelativePath( const QString & _fullpath, const char *_resource )
00416 {
00417 QString sRelativeFilePath;
00418 QStringList dirs = KGlobal::dirs()->resourceDirs( _resource );
00419 QStringList::ConstIterator dirsit = dirs.begin();
00420 for ( ; dirsit != dirs.end() && sRelativeFilePath.isEmpty(); ++dirsit ) {
00421
00422 if ( _fullpath.find( *dirsit ) == 0 )
00423 sRelativeFilePath = _fullpath.mid( (*dirsit).length() );
00424 }
00425 if ( sRelativeFilePath.isEmpty() )
00426 kdFatal(7011) << QString("Couldn't find %1 in any %2 dir !!!").arg( _fullpath ).arg( _resource) << endl;
00427
00428
00429
00430 return sRelativeFilePath;
00431 }
00432
00433 KSycoca * KSycoca::_self = 0L;
00434
00435 void KSycoca::flagError()
00436 {
00437 qWarning("ERROR: KSycoca database corruption!");
00438 if (_self)
00439 {
00440 if (_self->d->readError)
00441 return;
00442 _self->d->readError = true;
00443 if (_self->d->autoRebuild)
00444 system("kbuildsycoca");
00445 }
00446 }
00447
00448 void KSycoca::disableAutoRebuild()
00449 {
00450 d->autoRebuild = false;
00451 }
00452
00453 bool KSycoca::readError()
00454 {
00455 bool b = false;
00456 if (_self)
00457 {
00458 b = _self->d->readError;
00459 _self->d->readError = false;
00460 }
00461 return b;
00462 }
00463
00464 void KSycocaEntry::read( QDataStream &s, QString &str )
00465 {
00466 Q_UINT32 bytes;
00467 s >> bytes;
00468 if ( bytes > 8192 ) {
00469 if (bytes != 0xffffffff)
00470 KSycoca::flagError();
00471 str = QString::null;
00472 }
00473 else if ( bytes > 0 ) {
00474 int bt = bytes/2;
00475 str.setLength( bt );
00476 QChar* ch = (QChar *) str.unicode();
00477 char t[8192];
00478 char *b = t;
00479 s.readRawBytes( b, bytes );
00480 while ( bt-- ) {
00481 *ch++ = (ushort) (((ushort)b[0])<<8) | (uchar)b[1];
00482 b += 2;
00483 }
00484 } else {
00485 str = "";
00486 }
00487 }
00488
00489 void KSycocaEntry::read( QDataStream &s, QStringList &list )
00490 {
00491 list.clear();
00492 Q_UINT32 count;
00493 s >> count;
00494 if (count >= 1024)
00495 {
00496 KSycoca::flagError();
00497 return;
00498 }
00499 for(Q_UINT32 i = 0; i < count; i++)
00500 {
00501 QString str;
00502 read(s, str);
00503 list.append( str );
00504 if (s.atEnd())
00505 {
00506 KSycoca::flagError();
00507 return;
00508 }
00509 }
00510 }
00511
00512 void KSycoca::virtual_hook( int id, void* data )
00513 { DCOPObject::virtual_hook( id, data ); }
00514
00515 void KSycocaEntry::virtual_hook( int, void* )
00516 { }
00517
00518 #include "ksycoca.moc"
This file is part of the documentation for kdecore Library Version 3.2.0.