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