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