kdecore Library: ksycoca.cpp Source File (kdecore Library)

| # kdecore Library API Documentation
Overview | Class Hierarchy | Classes | Classes (annotated) | Members | Namespaces | Source Files

# 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"

This file is part of the documentation for kdecore Library Version 3.4.1.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Sat Jun 11 20:18:12 2005 by doxygen 1.4.1 written by Dimitri van Heesch, © 1997-2003