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