kdecore Library API Documentation

ksycoca.cpp

00001 /* This file is part of the KDE libraries 00002 * Copyright (C) 1999-2000 Waldo Bastian <bastian@kde.org> 00003 * 00004 * This library is free software; you can redistribute it and/or 00005 * modify it under the terms of the GNU Library General Public 00006 * License version 2 as published by the Free Software Foundation; 00007 * 00008 * This library is distributed in the hope that it will be useful, 00009 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00010 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00011 * Library General Public License for more details. 00012 * 00013 * You should have received a copy of the GNU Library General Public License 00014 * along with this library; see the file COPYING.LIB. If not, write to 00015 * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, 00016 * Boston, MA 02111-1307, USA. 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 // The following limitations are in place: 00052 // Maximum length of a single string: 8192 bytes 00053 // Maximum length of a string list: 1024 strings 00054 // Maximum number of entries: 8192 00055 // 00056 // The purpose of these limitations is to limit the impact 00057 // of database corruption. 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 // Read-only constructor 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 // Register app as able to receive DCOP messages 00088 if (kapp && !kapp->dcopClient()->isAttached()) 00089 { 00090 kapp->dcopClient()->attach(); 00091 } 00092 // We register with DCOP _before_ we try to open the database. 00093 // This way we can be relative sure that the KDE framework is 00094 // up and running (kdeinit, dcopserver, klaucnher, kded) and 00095 // that the database is up to date. 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 /* POSIX mandates only MAP_FAILED, but we are paranoid so check for 00137 null pointer too. */ 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 QByteArray b_array; 00148 b_array.setRawData(m_sycoca_mmap, m_sycoca_size); 00149 QBuffer *buffer = new QBuffer( b_array ); 00150 buffer->open(IO_ReadWrite); 00151 m_str = new QDataStream( buffer); 00152 } 00153 #endif 00154 bNoDatabase = false; 00155 } 00156 else 00157 { 00158 kdDebug(7011) << "Could not open ksycoca" << endl; 00159 00160 // No database file 00161 delete database; 00162 database = 0; 00163 00164 bNoDatabase = true; 00165 if (openDummyIfNotFound) 00166 { 00167 // We open a dummy database instead. 00168 //kdDebug(7011) << "No database, opening a dummy one." << endl; 00169 QBuffer *buffer = new QBuffer( QByteArray() ); 00170 buffer->open(IO_ReadWrite); 00171 m_str = new QDataStream( buffer); 00172 (*m_str) << (Q_INT32) KSYCOCA_VERSION; 00173 (*m_str) << (Q_INT32) 0; 00174 } 00175 else 00176 { 00177 result = false; 00178 } 00179 } 00180 m_lstFactories = new KSycocaFactoryList(); 00181 m_lstFactories->setAutoDelete( true ); 00182 d->database = database; 00183 return result; 00184 } 00185 00186 // Read-write constructor - only for KBuildSycoca 00187 KSycoca::KSycoca( bool /* dummy */ ) 00188 : DCOPObject("ksycoca_building"), m_lstFactories(0), m_str(0), bNoDatabase(false), 00189 m_sycoca_size(0), m_sycoca_mmap(0) 00190 { 00191 d = new KSycocaPrivate; 00192 m_lstFactories = new KSycocaFactoryList(); 00193 m_lstFactories->setAutoDelete( true ); 00194 _self = this; 00195 } 00196 00197 static void delete_ksycoca_self() { 00198 delete KSycoca::_self; 00199 } 00200 00201 KSycoca * KSycoca::self() 00202 { 00203 if (!_self) { 00204 qAddPostRoutine(delete_ksycoca_self); 00205 _self = new KSycoca(); 00206 } 00207 return _self; 00208 } 00209 00210 KSycoca::~KSycoca() 00211 { 00212 closeDatabase(); 00213 delete d; 00214 _self = 0L; 00215 } 00216 00217 void KSycoca::closeDatabase() 00218 { 00219 QIODevice *device = 0; 00220 if (m_str) 00221 device = m_str->device(); 00222 #ifdef HAVE_MMAP 00223 if (device && m_sycoca_mmap) 00224 { 00225 QBuffer *buf = (QBuffer *) device; 00226 buf->buffer().resetRawData(m_sycoca_mmap, m_sycoca_size); 00227 // Solaris has munmap(char*, size_t) and everything else should 00228 // be happy with a char* for munmap(void*, size_t) 00229 munmap((char*) m_sycoca_mmap, m_sycoca_size); 00230 m_sycoca_mmap = 0; 00231 } 00232 #endif 00233 00234 delete m_str; 00235 m_str = 0; 00236 delete device; 00237 if (d->database != device) 00238 delete d->database; 00239 device = 0; 00240 d->database = 0; 00241 // It is very important to delete all factories here 00242 // since they cache information about the database file 00243 delete m_lstFactories; 00244 m_lstFactories = 0L; 00245 } 00246 00247 void KSycoca::addFactory( KSycocaFactory *factory ) 00248 { 00249 assert(m_lstFactories); 00250 m_lstFactories->append(factory); 00251 } 00252 00253 bool KSycoca::isChanged(const char *type) 00254 { 00255 return self()->d->changeList.contains(type); 00256 } 00257 00258 void KSycoca::notifyDatabaseChanged(const QStringList &changeList) 00259 { 00260 d->changeList = changeList; 00261 //kdDebug(7011) << "got a notifyDatabaseChanged signal !" << endl; 00262 // kded tells us the database file changed 00263 // Close the database and forget all about what we knew 00264 // The next call to any public method will recreate 00265 // everything that's needed. 00266 closeDatabase(); 00267 00268 // Now notify applications 00269 emit databaseChanged(); 00270 } 00271 00272 QDataStream * KSycoca::findEntry(int offset, KSycocaType &type) 00273 { 00274 if ( !m_str ) 00275 openDatabase(); 00276 //kdDebug(7011) << QString("KSycoca::_findEntry(offset=%1)").arg(offset,8,16) << endl; 00277 m_str->device()->at(offset); 00278 Q_INT32 aType; 00279 (*m_str) >> aType; 00280 type = (KSycocaType) aType; 00281 //kdDebug(7011) << QString("KSycoca::found type %1").arg(aType) << endl; 00282 return m_str; 00283 } 00284 00285 bool KSycoca::checkVersion(bool abortOnError) 00286 { 00287 if ( !m_str ) 00288 { 00289 if( !openDatabase(false /* don't open dummy db if not found */) ) 00290 return false; // No database found 00291 00292 // We should never get here... if a database was found then m_str shouldn't be 0L. 00293 assert(m_str); 00294 } 00295 m_str->device()->at(0); 00296 Q_INT32 aVersion; 00297 (*m_str) >> aVersion; 00298 if ( aVersion < KSYCOCA_VERSION ) 00299 { 00300 kdWarning(7011) << "Found version " << aVersion << ", expecting version " << KSYCOCA_VERSION << " or higher." << endl; 00301 if (!abortOnError) return false; 00302 kdError(7011) << "Outdated database ! Stop kded and restart it !" << endl; 00303 abort(); 00304 } 00305 return true; 00306 } 00307 00308 QDataStream * KSycoca::findFactory(KSycocaFactoryId id) 00309 { 00310 // The constructor found no database, but we want one 00311 if (bNoDatabase) 00312 { 00313 closeDatabase(); // close the dummy one 00314 // Check if new database already available 00315 if ( !openDatabase(false /* no dummy one*/) ) 00316 { 00317 static bool triedLaunchingKdeinit = false; 00318 if (!triedLaunchingKdeinit) // try only once 00319 { 00320 triedLaunchingKdeinit = true; 00321 kdDebug(7011) << "findFactory: we have no database.... launching kdeinit" << endl; 00322 KApplication::startKdeinit(); 00323 // Ok, the new database should be here now, open it. 00324 } 00325 if (!openDatabase(false)) 00326 return 0L; // Still no database - uh oh 00327 } 00328 } 00329 // rewind and check 00330 if (!checkVersion(false)) 00331 { 00332 kdWarning(7011) << "Outdated database found" << endl; 00333 return 0L; 00334 } 00335 Q_INT32 aId; 00336 Q_INT32 aOffset; 00337 while(true) 00338 { 00339 (*m_str) >> aId; 00340 //kdDebug(7011) << QString("KSycoca::findFactory : found factory %1").arg(aId) << endl; 00341 if (aId == 0) 00342 { 00343 kdError(7011) << "Error, KSycocaFactory (id = " << int(id) << ") not found!" << endl; 00344 break; 00345 } 00346 (*m_str) >> aOffset; 00347 if (aId == id) 00348 { 00349 //kdDebug(7011) << QString("KSycoca::findFactory(%1) offset %2").arg((int)id).arg(aOffset) << endl; 00350 m_str->device()->at(aOffset); 00351 return m_str; 00352 } 00353 } 00354 return 0; 00355 } 00356 00357 QString KSycoca::kfsstnd_prefixes() 00358 { 00359 if (bNoDatabase) return ""; 00360 if (!checkVersion(false)) return ""; 00361 Q_INT32 aId; 00362 Q_INT32 aOffset; 00363 // skip factories offsets 00364 while(true) 00365 { 00366 (*m_str) >> aId; 00367 if ( aId ) 00368 (*m_str) >> aOffset; 00369 else 00370 break; // just read 0 00371 } 00372 // We now point to the header 00373 QString prefixes; 00374 KSycocaEntry::read(*m_str, prefixes); 00375 (*m_str) >> m_timeStamp; 00376 KSycocaEntry::read(*m_str, d->language); 00377 (*m_str) >> d->updateSig; 00378 KSycocaEntry::read(*m_str, d->allResourceDirs); 00379 return prefixes; 00380 } 00381 00382 Q_UINT32 KSycoca::timeStamp() 00383 { 00384 if (!m_timeStamp) 00385 (void) kfsstnd_prefixes(); 00386 return m_timeStamp; 00387 } 00388 00389 Q_UINT32 KSycoca::updateSignature() 00390 { 00391 if (!m_timeStamp) 00392 (void) kfsstnd_prefixes(); 00393 return d->updateSig; 00394 } 00395 00396 QString KSycoca::language() 00397 { 00398 if (d->language.isEmpty()) 00399 (void) kfsstnd_prefixes(); 00400 return d->language; 00401 } 00402 00403 QStringList KSycoca::allResourceDirs() 00404 { 00405 if (!m_timeStamp) 00406 (void) kfsstnd_prefixes(); 00407 return d->allResourceDirs; 00408 } 00409 00410 QString KSycoca::determineRelativePath( const QString & _fullpath, const char *_resource ) 00411 { 00412 QString sRelativeFilePath; 00413 QStringList dirs = KGlobal::dirs()->resourceDirs( _resource ); 00414 QStringList::ConstIterator dirsit = dirs.begin(); 00415 for ( ; dirsit != dirs.end() && sRelativeFilePath.isEmpty(); ++dirsit ) { 00416 // might need canonicalPath() ... 00417 if ( _fullpath.find( *dirsit ) == 0 ) // path is dirs + relativePath 00418 sRelativeFilePath = _fullpath.mid( (*dirsit).length() ); // skip appsdirs 00419 } 00420 if ( sRelativeFilePath.isEmpty() ) 00421 kdFatal(7011) << QString("Couldn't find %1 in any %2 dir !!!").arg( _fullpath ).arg( _resource) << endl; 00422 //else 00423 // debug code 00424 //kdDebug(7011) << sRelativeFilePath << endl; 00425 return sRelativeFilePath; 00426 } 00427 00428 KSycoca * KSycoca::_self = 0L; 00429 00430 void KSycoca::flagError() 00431 { 00432 qWarning("ERROR: KSycoca database corruption!"); 00433 if (_self) 00434 { 00435 if (_self->d->readError) 00436 return; 00437 _self->d->readError = true; 00438 if (_self->d->autoRebuild) 00439 system("kbuildsycoca"); // Rebuild the damned thing. 00440 } 00441 } 00442 00443 void KSycoca::disableAutoRebuild() 00444 { 00445 d->autoRebuild = false; 00446 } 00447 00448 bool KSycoca::readError() 00449 { 00450 bool b = false; 00451 if (_self) 00452 { 00453 b = _self->d->readError; 00454 _self->d->readError = false; 00455 } 00456 return b; 00457 } 00458 00459 void KSycocaEntry::read( QDataStream &s, QString &str ) 00460 { 00461 Q_UINT32 bytes; 00462 s >> bytes; // read size of string 00463 if ( bytes > 8192 ) { // null string or too big 00464 if (bytes != 0xffffffff) 00465 KSycoca::flagError(); 00466 str = QString::null; 00467 } 00468 else if ( bytes > 0 ) { // not empty 00469 int bt = bytes/2; 00470 str.setLength( bt ); 00471 QChar* ch = (QChar *) str.unicode(); 00472 char t[8192]; 00473 char *b = t; 00474 s.readRawBytes( b, bytes ); 00475 while ( bt-- ) { 00476 *ch++ = (ushort) (((ushort)b[0])<<8) | (uchar)b[1]; 00477 b += 2; 00478 } 00479 } else { 00480 str = ""; 00481 } 00482 } 00483 00484 void KSycocaEntry::read( QDataStream &s, QStringList &list ) 00485 { 00486 list.clear(); 00487 Q_UINT32 count; 00488 s >> count; // read size of list 00489 if (count >= 1024) 00490 { 00491 KSycoca::flagError(); 00492 return; 00493 } 00494 for(Q_UINT32 i = 0; i < count; i++) 00495 { 00496 QString str; 00497 read(s, str); 00498 list.append( str ); 00499 if (s.atEnd()) 00500 { 00501 KSycoca::flagError(); 00502 return; 00503 } 00504 } 00505 } 00506 00507 void KSycoca::virtual_hook( int id, void* data ) 00508 { DCOPObject::virtual_hook( id, data ); } 00509 00510 void KSycocaEntry::virtual_hook( int, void* ) 00511 { /*BASE::virtual_hook( id, data );*/ } 00512 00513 #include "ksycoca.moc"
KDE Logo
This file is part of the documentation for kdecore Library Version 3.4.0.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Thu Apr 14 00:03:34 2005 by doxygen 1.3.7 written by Dimitri van Heesch, © 1997-2003