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.1.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Sat Jan 22 16:43:49 2005 by doxygen 1.3.9.1 written by Dimitri van Heesch, © 1997-2003