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