• Skip to content
  • Skip to link menu
Trinity API Reference
  • Trinity API Reference
  • tdecore
 

tdecore

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

tdecore

Skip menu "tdecore"
  • Main Page
  • Modules
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

tdecore

Skip menu "tdecore"
  • arts
  • dcop
  • dnssd
  • interfaces
  •   kspeech
  •     interface
  •     library
  •   tdetexteditor
  • kate
  • kded
  • kdoctools
  • kimgio
  • kjs
  • libtdemid
  • libtdescreensaver
  • tdeabc
  • tdecmshell
  • tdecore
  • tdefx
  • tdehtml
  • tdeinit
  • tdeio
  •   bookmarks
  •   httpfilter
  •   kpasswdserver
  •   kssl
  •   tdefile
  •   tdeio
  •   tdeioexec
  • tdeioslave
  •   http
  • tdemdi
  •   tdemdi
  • tdenewstuff
  • tdeparts
  • tdeprint
  • tderandr
  • tderesources
  • tdespell2
  • tdesu
  • tdeui
  • tdeunittest
  • tdeutils
  • tdewallet
Generated for tdecore by doxygen 1.7.1
This website is maintained by Timothy Pearson.