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