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 struct KSycocaPrivate { 00060 KSycocaPrivate() { 00061 database = 0; 00062 readError = false; 00063 updateSig = 0; 00064 autoRebuild = true; 00065 } 00066 QFile *database; 00067 QStringList changeList; 00068 QString language; 00069 bool readError; 00070 bool autoRebuild; 00071 Q_UINT32 updateSig; 00072 QStringList allResourceDirs; 00073 }; 00074 00075 int KSycoca::version() 00076 { 00077 return KSYCOCA_VERSION; 00078 } 00079 00080 // Read-only constructor 00081 KSycoca::KSycoca() 00082 : DCOPObject("ksycoca"), m_lstFactories(0), m_str(0), bNoDatabase(false), 00083 m_sycoca_size(0), m_sycoca_mmap(0), m_timeStamp(0) 00084 { 00085 d = new KSycocaPrivate; 00086 // Register app as able to receive DCOP messages 00087 if (kapp && !kapp->dcopClient()->isAttached()) 00088 { 00089 kapp->dcopClient()->attach(); 00090 } 00091 // We register with DCOP _before_ we try to open the database. 00092 // This way we can be relative sure that the KDE framework is 00093 // up and running (kdeinit, dcopserver, klaucnher, kded) and 00094 // that the database is up to date. 00095 openDatabase(); 00096 _self = this; 00097 } 00098 00099 bool KSycoca::openDatabase( bool openDummyIfNotFound ) 00100 { 00101 bool result = true; 00102 00103 m_sycoca_mmap = 0; 00104 m_str = 0; 00105 QString path; 00106 QCString ksycoca_env = getenv("KDESYCOCA"); 00107 if (ksycoca_env.isEmpty()) 00108 path = KGlobal::dirs()->saveLocation("cache") + "ksycoca"; 00109 else 00110 path = QFile::decodeName(ksycoca_env); 00111 00112 kdDebug(7011) << "Trying to open ksycoca from " << path << endl; 00113 QFile *database = new QFile(path); 00114 bool bOpen = database->open( IO_ReadOnly ); 00115 if (!bOpen) 00116 { 00117 path = locate("services", "ksycoca"); 00118 if (!path.isEmpty()) 00119 { 00120 kdDebug(7011) << "Trying to open global ksycoca from " << path << endl; 00121 delete database; 00122 database = new QFile(path); 00123 bOpen = database->open( IO_ReadOnly ); 00124 } 00125 } 00126 00127 if (bOpen) 00128 { 00129 fcntl(database->handle(), F_SETFD, FD_CLOEXEC); 00130 m_sycoca_size = database->size(); 00131 #ifdef HAVE_MMAP 00132 m_sycoca_mmap = (const char *) mmap(0, m_sycoca_size, 00133 PROT_READ, MAP_SHARED, 00134 database->handle(), 0); 00135 /* POSIX mandates only MAP_FAILED, but we are paranoid so check for 00136 null pointer too. */ 00137 if (m_sycoca_mmap == (const char*) MAP_FAILED || m_sycoca_mmap == 0) 00138 { 00139 kdDebug(7011) << "mmap failed. (length = " << m_sycoca_size << ")" << endl; 00140 #endif 00141 m_str = new QDataStream(database); 00142 #ifdef HAVE_MMAP 00143 } 00144 else 00145 { 00146 QByteArray b_array; 00147 b_array.setRawData(m_sycoca_mmap, m_sycoca_size); 00148 QBuffer *buffer = new QBuffer( b_array ); 00149 buffer->open(IO_ReadWrite); 00150 m_str = new QDataStream( buffer); 00151 } 00152 #endif 00153 bNoDatabase = false; 00154 } 00155 else 00156 { 00157 kdDebug(7011) << "Could not open ksycoca" << endl; 00158 00159 // No database file 00160 delete database; 00161 database = 0; 00162 00163 bNoDatabase = true; 00164 if (openDummyIfNotFound) 00165 { 00166 // We open a dummy database instead. 00167 //kdDebug(7011) << "No database, opening a dummy one." << endl; 00168 QBuffer *buffer = new QBuffer( QByteArray() ); 00169 buffer->open(IO_ReadWrite); 00170 m_str = new QDataStream( buffer); 00171 (*m_str) << (Q_INT32) KSYCOCA_VERSION; 00172 (*m_str) << (Q_INT32) 0; 00173 } 00174 else 00175 { 00176 result = false; 00177 } 00178 } 00179 m_lstFactories = new KSycocaFactoryList(); 00180 m_lstFactories->setAutoDelete( true ); 00181 d->database = database; 00182 return result; 00183 } 00184 00185 // Read-write constructor - only for KBuildSycoca 00186 KSycoca::KSycoca( bool /* dummy */ ) 00187 : DCOPObject("ksycoca_building"), m_lstFactories(0), m_str(0), bNoDatabase(false), 00188 m_sycoca_size(0), m_sycoca_mmap(0) 00189 { 00190 d = new KSycocaPrivate; 00191 m_lstFactories = new KSycocaFactoryList(); 00192 m_lstFactories->setAutoDelete( true ); 00193 _self = this; 00194 } 00195 00196 static void delete_ksycoca_self() { 00197 delete KSycoca::_self; 00198 } 00199 00200 KSycoca * KSycoca::self() 00201 { 00202 if (!_self) { 00203 qAddPostRoutine(delete_ksycoca_self); 00204 _self = new KSycoca(); 00205 } 00206 return _self; 00207 } 00208 00209 KSycoca::~KSycoca() 00210 { 00211 closeDatabase(); 00212 delete d; 00213 _self = 0L; 00214 } 00215 00216 void KSycoca::closeDatabase() 00217 { 00218 QIODevice *device = 0; 00219 if (m_str) 00220 device = m_str->device(); 00221 #ifdef HAVE_MMAP 00222 if (device && m_sycoca_mmap) 00223 { 00224 QBuffer *buf = (QBuffer *) device; 00225 buf->buffer().resetRawData(m_sycoca_mmap, m_sycoca_size); 00226 // Solaris has munmap(char*, size_t) and everything else should 00227 // be happy with a char* for munmap(void*, size_t) 00228 munmap((char*) m_sycoca_mmap, m_sycoca_size); 00229 m_sycoca_mmap = 0; 00230 } 00231 #endif 00232 00233 delete m_str; 00234 m_str = 0; 00235 delete device; 00236 if (d->database != device) 00237 delete d->database; 00238 device = 0; 00239 d->database = 0; 00240 // It is very important to delete all factories here 00241 // since they cache information about the database file 00242 delete m_lstFactories; 00243 m_lstFactories = 0L; 00244 } 00245 00246 void KSycoca::addFactory( KSycocaFactory *factory ) 00247 { 00248 assert(m_lstFactories); 00249 m_lstFactories->append(factory); 00250 } 00251 00252 bool KSycoca::isChanged(const char *type) 00253 { 00254 return self()->d->changeList.contains(type); 00255 } 00256 00257 void KSycoca::notifyDatabaseChanged(const QStringList &changeList) 00258 { 00259 d->changeList = changeList; 00260 //kdDebug(7011) << "got a notifyDatabaseChanged signal !" << endl; 00261 // kded tells us the database file changed 00262 // Close the database and forget all about what we knew 00263 // The next call to any public method will recreate 00264 // everything that's needed. 00265 closeDatabase(); 00266 00267 // Now notify applications 00268 emit databaseChanged(); 00269 } 00270 00271 QDataStream * KSycoca::findEntry(int offset, KSycocaType &type) 00272 { 00273 if ( !m_str ) 00274 openDatabase(); 00275 //kdDebug(7011) << QString("KSycoca::_findEntry(offset=%1)").arg(offset,8,16) << endl; 00276 m_str->device()->at(offset); 00277 Q_INT32 aType; 00278 (*m_str) >> aType; 00279 type = (KSycocaType) aType; 00280 //kdDebug(7011) << QString("KSycoca::found type %1").arg(aType) << endl; 00281 return m_str; 00282 } 00283 00284 bool KSycoca::checkVersion(bool abortOnError) 00285 { 00286 if ( !m_str ) 00287 { 00288 if( !openDatabase(false /* don't open dummy db if not found */) ) 00289 return false; // No database found 00290 00291 // We should never get here... if a database was found then m_str shouldn't be 0L. 00292 assert(m_str); 00293 } 00294 m_str->device()->at(0); 00295 Q_INT32 aVersion; 00296 (*m_str) >> aVersion; 00297 if ( aVersion < KSYCOCA_VERSION ) 00298 { 00299 kdWarning(7011) << "Found version " << aVersion << ", expecting version " << KSYCOCA_VERSION << " or higher." << endl; 00300 if (!abortOnError) return false; 00301 kdError(7011) << "Outdated database ! Stop kded and restart it !" << endl; 00302 abort(); 00303 } 00304 return true; 00305 } 00306 00307 QDataStream * KSycoca::findFactory(KSycocaFactoryId id) 00308 { 00309 // The constructor found no database, but we want one 00310 if (bNoDatabase) 00311 { 00312 closeDatabase(); // close the dummy one 00313 // Check if new database already available 00314 if ( !openDatabase(false /* no dummy one*/) ) 00315 { 00316 static bool triedLaunchingKdeinit = false; 00317 if (!triedLaunchingKdeinit) // try only once 00318 { 00319 triedLaunchingKdeinit = true; 00320 kdDebug(7011) << "findFactory: we have no database.... launching kdeinit" << endl; 00321 KApplication::startKdeinit(); 00322 // Ok, the new database should be here now, open it. 00323 } 00324 if (!openDatabase(false)) 00325 return 0L; // Still no database - uh oh 00326 } 00327 } 00328 // rewind and check 00329 if (!checkVersion(false)) 00330 { 00331 kdWarning(7011) << "Outdated database found" << endl; 00332 return 0L; 00333 } 00334 Q_INT32 aId; 00335 Q_INT32 aOffset; 00336 while(true) 00337 { 00338 (*m_str) >> aId; 00339 //kdDebug(7011) << QString("KSycoca::findFactory : found factory %1").arg(aId) << endl; 00340 if (aId == 0) 00341 { 00342 kdError(7011) << "Error, KSycocaFactory (id = " << int(id) << ") not found!" << endl; 00343 break; 00344 } 00345 (*m_str) >> aOffset; 00346 if (aId == id) 00347 { 00348 //kdDebug(7011) << QString("KSycoca::findFactory(%1) offset %2").arg((int)id).arg(aOffset) << endl; 00349 m_str->device()->at(aOffset); 00350 return m_str; 00351 } 00352 } 00353 return 0; 00354 } 00355 00356 QString KSycoca::kfsstnd_prefixes() 00357 { 00358 if (bNoDatabase) return ""; 00359 if (!checkVersion(false)) return ""; 00360 Q_INT32 aId; 00361 Q_INT32 aOffset; 00362 // skip factories offsets 00363 while(true) 00364 { 00365 (*m_str) >> aId; 00366 if ( aId ) 00367 (*m_str) >> aOffset; 00368 else 00369 break; // just read 0 00370 } 00371 // We now point to the header 00372 QString prefixes; 00373 KSycocaEntry::read(*m_str, prefixes); 00374 (*m_str) >> m_timeStamp; 00375 KSycocaEntry::read(*m_str, d->language); 00376 (*m_str) >> d->updateSig; 00377 KSycocaEntry::read(*m_str, d->allResourceDirs); 00378 return prefixes; 00379 } 00380 00381 Q_UINT32 KSycoca::timeStamp() 00382 { 00383 if (!m_timeStamp) 00384 (void) kfsstnd_prefixes(); 00385 return m_timeStamp; 00386 } 00387 00388 Q_UINT32 KSycoca::updateSignature() 00389 { 00390 if (!m_timeStamp) 00391 (void) kfsstnd_prefixes(); 00392 return d->updateSig; 00393 } 00394 00395 QString KSycoca::language() 00396 { 00397 if (d->language.isEmpty()) 00398 (void) kfsstnd_prefixes(); 00399 return d->language; 00400 } 00401 00402 QStringList KSycoca::allResourceDirs() 00403 { 00404 if (!m_timeStamp) 00405 (void) kfsstnd_prefixes(); 00406 return d->allResourceDirs; 00407 } 00408 00409 QString KSycoca::determineRelativePath( const QString & _fullpath, const char *_resource ) 00410 { 00411 QString sRelativeFilePath; 00412 QStringList dirs = KGlobal::dirs()->resourceDirs( _resource ); 00413 QStringList::ConstIterator dirsit = dirs.begin(); 00414 for ( ; dirsit != dirs.end() && sRelativeFilePath.isEmpty(); ++dirsit ) { 00415 // might need canonicalPath() ... 00416 if ( _fullpath.find( *dirsit ) == 0 ) // path is dirs + relativePath 00417 sRelativeFilePath = _fullpath.mid( (*dirsit).length() ); // skip appsdirs 00418 } 00419 if ( sRelativeFilePath.isEmpty() ) 00420 kdFatal(7011) << QString("Couldn't find %1 in any %2 dir !!!").arg( _fullpath ).arg( _resource) << endl; 00421 //else 00422 // debug code 00423 //kdDebug(7011) << sRelativeFilePath << endl; 00424 return sRelativeFilePath; 00425 } 00426 00427 KSycoca * KSycoca::_self = 0L; 00428 00429 void KSycoca::flagError() 00430 { 00431 qWarning("ERROR: KSycoca database corruption!"); 00432 if (_self) 00433 { 00434 if (_self->d->readError) 00435 return; 00436 _self->d->readError = true; 00437 if (_self->d->autoRebuild) 00438 system("kbuildsycoca"); // Rebuild the damned thing. 00439 } 00440 } 00441 00442 void KSycoca::disableAutoRebuild() 00443 { 00444 d->autoRebuild = false; 00445 } 00446 00447 bool KSycoca::readError() 00448 { 00449 bool b = false; 00450 if (_self) 00451 { 00452 b = _self->d->readError; 00453 _self->d->readError = false; 00454 } 00455 return b; 00456 } 00457 00458 void KSycocaEntry::read( QDataStream &s, QString &str ) 00459 { 00460 Q_UINT32 bytes; 00461 s >> bytes; // read size of string 00462 if ( bytes > 8192 ) { // null string or too big 00463 if (bytes != 0xffffffff) 00464 KSycoca::flagError(); 00465 str = QString::null; 00466 } 00467 else if ( bytes > 0 ) { // not empty 00468 int bt = bytes/2; 00469 str.setLength( bt ); 00470 QChar* ch = (QChar *) str.unicode(); 00471 char t[8192]; 00472 char *b = t; 00473 s.readRawBytes( b, bytes ); 00474 while ( bt-- ) { 00475 *ch++ = (ushort) (((ushort)b[0])<<8) | (uchar)b[1]; 00476 b += 2; 00477 } 00478 } else { 00479 str = ""; 00480 } 00481 } 00482 00483 void KSycocaEntry::read( QDataStream &s, QStringList &list ) 00484 { 00485 list.clear(); 00486 Q_UINT32 count; 00487 s >> count; // read size of list 00488 if (count >= 1024) 00489 { 00490 KSycoca::flagError(); 00491 return; 00492 } 00493 for(Q_UINT32 i = 0; i < count; i++) 00494 { 00495 QString str; 00496 read(s, str); 00497 list.append( str ); 00498 if (s.atEnd()) 00499 { 00500 KSycoca::flagError(); 00501 return; 00502 } 00503 } 00504 } 00505 00506 void KSycoca::virtual_hook( int id, void* data ) 00507 { DCOPObject::virtual_hook( id, data ); } 00508 00509 void KSycocaEntry::virtual_hook( int, void* ) 00510 { /*BASE::virtual_hook( id, data );*/ } 00511 00512 #include "ksycoca.moc"
KDE Logo
This file is part of the documentation for kdecore Library Version 3.2.3.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Sun Oct 10 18:54:56 2004 by doxygen 1.3.7 written by Dimitri van Heesch, © 1997-2003