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