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