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