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