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 if (KSycoca::_checkSelf()) 00198 delete KSycoca::_self; 00199 00200 } 00201 00202 bool KSycoca::_checkSelf() { 00203 return (_self ? true : false); 00204 } 00205 00206 KSycoca * KSycoca::self() 00207 { 00208 if (!_self) { 00209 qAddPostRoutine(delete_ksycoca_self); 00210 _self = new KSycoca(); 00211 } 00212 return _self; 00213 } 00214 00215 KSycoca::~KSycoca() 00216 { 00217 closeDatabase(); 00218 delete d; 00219 _self = 0L; 00220 } 00221 00222 void KSycoca::closeDatabase() 00223 { 00224 QIODevice *device = 0; 00225 if (m_str) 00226 device = m_str->device(); 00227 #ifdef HAVE_MMAP 00228 if (device && m_sycoca_mmap) 00229 { 00230 QBuffer *buf = (QBuffer *) device; 00231 buf->buffer().resetRawData(m_sycoca_mmap, m_sycoca_size); 00232 // Solaris has munmap(char*, size_t) and everything else should 00233 // be happy with a char* for munmap(void*, size_t) 00234 munmap((char*) m_sycoca_mmap, m_sycoca_size); 00235 m_sycoca_mmap = 0; 00236 } 00237 #endif 00238 00239 delete m_str; 00240 m_str = 0; 00241 delete device; 00242 if (d->database != device) 00243 delete d->database; 00244 device = 0; 00245 d->database = 0; 00246 // It is very important to delete all factories here 00247 // since they cache information about the database file 00248 delete m_lstFactories; 00249 m_lstFactories = 0L; 00250 } 00251 00252 void KSycoca::addFactory( KSycocaFactory *factory ) 00253 { 00254 assert(m_lstFactories); 00255 m_lstFactories->append(factory); 00256 } 00257 00258 bool KSycoca::isChanged(const char *type) 00259 { 00260 return self()->d->changeList.contains(type); 00261 } 00262 00263 void KSycoca::notifyDatabaseChanged(const QStringList &changeList) 00264 { 00265 d->changeList = changeList; 00266 //kdDebug(7011) << "got a notifyDatabaseChanged signal !" << endl; 00267 // kded tells us the database file changed 00268 // Close the database and forget all about what we knew 00269 // The next call to any public method will recreate 00270 // everything that's needed. 00271 closeDatabase(); 00272 00273 // Now notify applications 00274 emit databaseChanged(); 00275 } 00276 00277 QDataStream * KSycoca::findEntry(int offset, KSycocaType &type) 00278 { 00279 if ( !m_str ) 00280 openDatabase(); 00281 //kdDebug(7011) << QString("KSycoca::_findEntry(offset=%1)").arg(offset,8,16) << endl; 00282 m_str->device()->at(offset); 00283 Q_INT32 aType; 00284 (*m_str) >> aType; 00285 type = (KSycocaType) aType; 00286 //kdDebug(7011) << QString("KSycoca::found type %1").arg(aType) << endl; 00287 return m_str; 00288 } 00289 00290 bool KSycoca::checkVersion(bool abortOnError) 00291 { 00292 if ( !m_str ) 00293 { 00294 if( !openDatabase(false /* don't open dummy db if not found */) ) 00295 return false; // No database found 00296 00297 // We should never get here... if a database was found then m_str shouldn't be 0L. 00298 assert(m_str); 00299 } 00300 m_str->device()->at(0); 00301 Q_INT32 aVersion; 00302 (*m_str) >> aVersion; 00303 if ( aVersion < KSYCOCA_VERSION ) 00304 { 00305 kdWarning(7011) << "Found version " << aVersion << ", expecting version " << KSYCOCA_VERSION << " or higher." << endl; 00306 if (!abortOnError) return false; 00307 kdError(7011) << "Outdated database ! Stop kded and restart it !" << endl; 00308 abort(); 00309 } 00310 return true; 00311 } 00312 00313 QDataStream * KSycoca::findFactory(KSycocaFactoryId id) 00314 { 00315 // The constructor found no database, but we want one 00316 if (bNoDatabase) 00317 { 00318 closeDatabase(); // close the dummy one 00319 // Check if new database already available 00320 if ( !openDatabase(false /* no dummy one*/) ) 00321 { 00322 static bool triedLaunchingKdeinit = false; 00323 if (!triedLaunchingKdeinit) // try only once 00324 { 00325 triedLaunchingKdeinit = true; 00326 kdDebug(7011) << "findFactory: we have no database.... launching kdeinit" << endl; 00327 KApplication::startKdeinit(); 00328 // Ok, the new database should be here now, open it. 00329 } 00330 if (!openDatabase(false)) 00331 return 0L; // Still no database - uh oh 00332 } 00333 } 00334 // rewind and check 00335 if (!checkVersion(false)) 00336 { 00337 kdWarning(7011) << "Outdated database found" << endl; 00338 return 0L; 00339 } 00340 Q_INT32 aId; 00341 Q_INT32 aOffset; 00342 while(true) 00343 { 00344 (*m_str) >> aId; 00345 //kdDebug(7011) << QString("KSycoca::findFactory : found factory %1").arg(aId) << endl; 00346 if (aId == 0) 00347 { 00348 kdError(7011) << "Error, KSycocaFactory (id = " << int(id) << ") not found!" << endl; 00349 break; 00350 } 00351 (*m_str) >> aOffset; 00352 if (aId == id) 00353 { 00354 //kdDebug(7011) << QString("KSycoca::findFactory(%1) offset %2").arg((int)id).arg(aOffset) << endl; 00355 m_str->device()->at(aOffset); 00356 return m_str; 00357 } 00358 } 00359 return 0; 00360 } 00361 00362 QString KSycoca::kfsstnd_prefixes() 00363 { 00364 if (bNoDatabase) return ""; 00365 if (!checkVersion(false)) return ""; 00366 Q_INT32 aId; 00367 Q_INT32 aOffset; 00368 // skip factories offsets 00369 while(true) 00370 { 00371 (*m_str) >> aId; 00372 if ( aId ) 00373 (*m_str) >> aOffset; 00374 else 00375 break; // just read 0 00376 } 00377 // We now point to the header 00378 QString prefixes; 00379 KSycocaEntry::read(*m_str, prefixes); 00380 (*m_str) >> m_timeStamp; 00381 KSycocaEntry::read(*m_str, d->language); 00382 (*m_str) >> d->updateSig; 00383 KSycocaEntry::read(*m_str, d->allResourceDirs); 00384 return prefixes; 00385 } 00386 00387 Q_UINT32 KSycoca::timeStamp() 00388 { 00389 if (!m_timeStamp) 00390 (void) kfsstnd_prefixes(); 00391 return m_timeStamp; 00392 } 00393 00394 Q_UINT32 KSycoca::updateSignature() 00395 { 00396 if (!m_timeStamp) 00397 (void) kfsstnd_prefixes(); 00398 return d->updateSig; 00399 } 00400 00401 QString KSycoca::language() 00402 { 00403 if (d->language.isEmpty()) 00404 (void) kfsstnd_prefixes(); 00405 return d->language; 00406 } 00407 00408 QStringList KSycoca::allResourceDirs() 00409 { 00410 if (!m_timeStamp) 00411 (void) kfsstnd_prefixes(); 00412 return d->allResourceDirs; 00413 } 00414 00415 QString KSycoca::determineRelativePath( const QString & _fullpath, const char *_resource ) 00416 { 00417 QString sRelativeFilePath; 00418 QStringList dirs = KGlobal::dirs()->resourceDirs( _resource ); 00419 QStringList::ConstIterator dirsit = dirs.begin(); 00420 for ( ; dirsit != dirs.end() && sRelativeFilePath.isEmpty(); ++dirsit ) { 00421 // might need canonicalPath() ... 00422 if ( _fullpath.find( *dirsit ) == 0 ) // path is dirs + relativePath 00423 sRelativeFilePath = _fullpath.mid( (*dirsit).length() ); // skip appsdirs 00424 } 00425 if ( sRelativeFilePath.isEmpty() ) 00426 kdFatal(7011) << QString("Couldn't find %1 in any %2 dir !!!").arg( _fullpath ).arg( _resource) << endl; 00427 //else 00428 // debug code 00429 //kdDebug(7011) << sRelativeFilePath << endl; 00430 return sRelativeFilePath; 00431 } 00432 00433 KSycoca * KSycoca::_self = 0L; 00434 00435 void KSycoca::flagError() 00436 { 00437 qWarning("ERROR: KSycoca database corruption!"); 00438 if (_self) 00439 { 00440 if (_self->d->readError) 00441 return; 00442 _self->d->readError = true; 00443 if (_self->d->autoRebuild) 00444 system("kbuildsycoca"); // Rebuild the damned thing. 00445 } 00446 } 00447 00448 void KSycoca::disableAutoRebuild() 00449 { 00450 d->autoRebuild = false; 00451 } 00452 00453 bool KSycoca::readError() 00454 { 00455 bool b = false; 00456 if (_self) 00457 { 00458 b = _self->d->readError; 00459 _self->d->readError = false; 00460 } 00461 return b; 00462 } 00463 00464 void KSycocaEntry::read( QDataStream &s, QString &str ) 00465 { 00466 Q_UINT32 bytes; 00467 s >> bytes; // read size of string 00468 if ( bytes > 8192 ) { // null string or too big 00469 if (bytes != 0xffffffff) 00470 KSycoca::flagError(); 00471 str = QString::null; 00472 } 00473 else if ( bytes > 0 ) { // not empty 00474 int bt = bytes/2; 00475 str.setLength( bt ); 00476 QChar* ch = (QChar *) str.unicode(); 00477 char t[8192]; 00478 char *b = t; 00479 s.readRawBytes( b, bytes ); 00480 while ( bt-- ) { 00481 *ch++ = (ushort) (((ushort)b[0])<<8) | (uchar)b[1]; 00482 b += 2; 00483 } 00484 } else { 00485 str = ""; 00486 } 00487 } 00488 00489 void KSycocaEntry::read( QDataStream &s, QStringList &list ) 00490 { 00491 list.clear(); 00492 Q_UINT32 count; 00493 s >> count; // read size of list 00494 if (count >= 1024) 00495 { 00496 KSycoca::flagError(); 00497 return; 00498 } 00499 for(Q_UINT32 i = 0; i < count; i++) 00500 { 00501 QString str; 00502 read(s, str); 00503 list.append( str ); 00504 if (s.atEnd()) 00505 { 00506 KSycoca::flagError(); 00507 return; 00508 } 00509 } 00510 } 00511 00512 void KSycoca::virtual_hook( int id, void* data ) 00513 { DCOPObject::virtual_hook( id, data ); } 00514 00515 void KSycocaEntry::virtual_hook( int, void* ) 00516 { /*BASE::virtual_hook( id, data );*/ } 00517 00518 #include "ksycoca.moc"
KDE Logo
This file is part of the documentation for kdecore Library Version 3.3.0.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Wed Sep 29 09:43:11 2004 by doxygen 1.3.8 written by Dimitri van Heesch, © 1997-2003