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