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 delete KSycoca::_self;
00176 }
00177
00178 KSycoca * KSycoca::self()
00179 {
00180 if (!_self) {
00181 qAddPostRoutine(delete_ksycoca_self);
00182 _self = new KSycoca();
00183 }
00184 return _self;
00185 }
00186
00187 KSycoca::~KSycoca()
00188 {
00189 closeDatabase();
00190 delete d;
00191 _self = 0L;
00192 }
00193
00194 void KSycoca::closeDatabase()
00195 {
00196 QIODevice *device = 0;
00197 if (m_str)
00198 device = m_str->device();
00199 #ifdef HAVE_MMAP
00200 if (device && m_sycoca_mmap)
00201 {
00202 QBuffer *buf = (QBuffer *) device;
00203 buf->buffer().resetRawData(m_sycoca_mmap, m_sycoca_size);
00204
00205
00206 munmap((char*) m_sycoca_mmap, m_sycoca_size);
00207 m_sycoca_mmap = 0;
00208 }
00209 #endif
00210
00211 delete m_str;
00212 m_str = 0;
00213 delete device;
00214 if (d->database != device)
00215 delete d->database;
00216 device = 0;
00217 d->database = 0;
00218
00219
00220 delete m_lstFactories;
00221 m_lstFactories = 0L;
00222 }
00223
00224 void KSycoca::addFactory( KSycocaFactory *factory )
00225 {
00226 assert(m_lstFactories);
00227 m_lstFactories->append(factory);
00228 }
00229
00230 bool KSycoca::isChanged(const char *type)
00231 {
00232 return self()->d->changeList.contains(type);
00233 }
00234
00235 void KSycoca::notifyDatabaseChanged(const QStringList &changeList)
00236 {
00237 d->changeList = changeList;
00238
00239
00240
00241
00242
00243 closeDatabase();
00244
00245
00246 emit databaseChanged();
00247 }
00248
00249 QDataStream * KSycoca::findEntry(int offset, KSycocaType &type)
00250 {
00251 if ( !m_str )
00252 openDatabase();
00253
00254 m_str->device()->at(offset);
00255 Q_INT32 aType;
00256 (*m_str) >> aType;
00257 type = (KSycocaType) aType;
00258
00259 return m_str;
00260 }
00261
00262 bool KSycoca::checkVersion(bool abortOnError)
00263 {
00264 if ( !m_str )
00265 {
00266 if( !openDatabase(false ) )
00267 return false;
00268
00269
00270 assert(m_str);
00271 }
00272 m_str->device()->at(0);
00273 Q_INT32 aVersion;
00274 (*m_str) >> aVersion;
00275 if ( aVersion < KSYCOCA_VERSION )
00276 {
00277 kdWarning(7011) << "Found version " << aVersion << ", expecting version " << KSYCOCA_VERSION << " or higher." << endl;
00278 if (!abortOnError) return false;
00279 kdError(7011) << "Outdated database ! Stop kded and restart it !" << endl;
00280 abort();
00281 }
00282 return true;
00283 }
00284
00285 QDataStream * KSycoca::findFactory(KSycocaFactoryId id)
00286 {
00287
00288 if (bNoDatabase)
00289 {
00290 closeDatabase();
00291
00292 if ( !openDatabase(false ) )
00293 {
00294 static bool triedLaunchingKdeinit = false;
00295 if (!triedLaunchingKdeinit)
00296 {
00297 triedLaunchingKdeinit = true;
00298 kdDebug(7011) << "findFactory: we have no database.... launching kdeinit" << endl;
00299 KApplication::startKdeinit();
00300
00301 }
00302 if (!openDatabase(false))
00303 return 0L;
00304 }
00305 }
00306
00307 if (!checkVersion(false))
00308 {
00309 kdWarning(7011) << "Outdated database found" << endl;
00310 return 0L;
00311 }
00312 Q_INT32 aId;
00313 Q_INT32 aOffset;
00314 while(true)
00315 {
00316 (*m_str) >> aId;
00317
00318 if (aId == 0)
00319 {
00320 kdError(7011) << "Error, KSycocaFactory (id = " << int(id) << ") not found!" << endl;
00321 break;
00322 }
00323 (*m_str) >> aOffset;
00324 if (aId == id)
00325 {
00326
00327 m_str->device()->at(aOffset);
00328 return m_str;
00329 }
00330 }
00331 return 0;
00332 }
00333
00334 QString KSycoca::kfsstnd_prefixes()
00335 {
00336 if (bNoDatabase) return "";
00337 if (!checkVersion(false)) return "";
00338 Q_INT32 aId;
00339 Q_INT32 aOffset;
00340
00341 while(true)
00342 {
00343 (*m_str) >> aId;
00344 if ( aId )
00345 (*m_str) >> aOffset;
00346 else
00347 break;
00348 }
00349
00350 QString prefixes;
00351 KSycocaEntry::read(*m_str, prefixes);
00352 (*m_str) >> m_timeStamp;
00353 KSycocaEntry::read(*m_str, d->language);
00354 (*m_str) >> d->updateSig;
00355 return prefixes;
00356 }
00357
00358 Q_UINT32 KSycoca::timeStamp()
00359 {
00360 if (!m_timeStamp)
00361 (void) kfsstnd_prefixes();
00362 return m_timeStamp;
00363 }
00364
00365 Q_UINT32 KSycoca::updateSignature()
00366 {
00367 if (!m_timeStamp)
00368 (void) kfsstnd_prefixes();
00369 return d->updateSig;
00370 }
00371
00372 QString KSycoca::language()
00373 {
00374 if (d->language.isEmpty())
00375 (void) kfsstnd_prefixes();
00376 return d->language;
00377 }
00378
00379 QString KSycoca::determineRelativePath( const QString & _fullpath, const char *_resource )
00380 {
00381 QString sRelativeFilePath;
00382 QStringList dirs = KGlobal::dirs()->resourceDirs( _resource );
00383 QStringList::ConstIterator dirsit = dirs.begin();
00384 for ( ; dirsit != dirs.end() && sRelativeFilePath.isEmpty(); ++dirsit ) {
00385
00386 if ( _fullpath.find( *dirsit ) == 0 )
00387 sRelativeFilePath = _fullpath.mid( (*dirsit).length() );
00388 }
00389 if ( sRelativeFilePath.isEmpty() )
00390 kdFatal(7011) << QString("Couldn't find %1 in any %2 dir !!!").arg( _fullpath ).arg( _resource) << endl;
00391
00392
00393
00394 return sRelativeFilePath;
00395 }
00396
00397 KSycoca * KSycoca::_self = 0L;
00398
00399 void KSycoca::flagError()
00400 {
00401 qWarning("ERROR: KSycoca database corruption!");
00402 if (_self)
00403 {
00404 if (_self->d->readError)
00405 return;
00406 _self->d->readError = true;
00407 if (_self->d->autoRebuild)
00408 system("kbuildsycoca");
00409 }
00410 }
00411
00412 void KSycoca::disableAutoRebuild()
00413 {
00414 d->autoRebuild = false;
00415 }
00416
00417 bool KSycoca::readError()
00418 {
00419 bool b = false;
00420 if (_self)
00421 {
00422 b = _self->d->readError;
00423 _self->d->readError = false;
00424 }
00425 return b;
00426 }
00427
00428 void KSycocaEntry::read( QDataStream &s, QString &str )
00429 {
00430 Q_UINT32 bytes;
00431 s >> bytes;
00432 if ( bytes > 8192 ) {
00433 if (bytes != 0xffffffff)
00434 KSycoca::flagError();
00435 str = QString::null;
00436 }
00437 else if ( bytes > 0 ) {
00438 int bt = bytes/2;
00439 str.setLength( bt );
00440 QChar* ch = (QChar *) str.unicode();
00441 char t[8192];
00442 char *b = t;
00443 s.readRawBytes( b, bytes );
00444 while ( bt-- ) {
00445 *ch++ = (ushort) (((ushort)b[0])<<8) | (uchar)b[1];
00446 b += 2;
00447 }
00448 } else {
00449 str = "";
00450 }
00451 }
00452
00453 void KSycocaEntry::read( QDataStream &s, QStringList &list )
00454 {
00455 list.clear();
00456 Q_UINT32 count;
00457 s >> count;
00458 if (count >= 1024)
00459 {
00460 KSycoca::flagError();
00461 return;
00462 }
00463 for(Q_UINT32 i = 0; i < count; i++)
00464 {
00465 QString str;
00466 read(s, str);
00467 list.append( str );
00468 if (s.atEnd())
00469 {
00470 KSycoca::flagError();
00471 return;
00472 }
00473 }
00474 }
00475
00476 void KSycoca::virtual_hook( int id, void* data )
00477 { DCOPObject::virtual_hook( id, data ); }
00478
00479 void KSycocaEntry::virtual_hook( int, void* )
00480 { }
00481
00482 #include "ksycoca.moc"
This file is part of the documentation for kdelibs Version 3.1.5.