ksycoca.cpp
00001 
00002 
00003 
00004 
00005 
00006 
00007 
00008 
00009 
00010 
00011 
00012 
00013 
00014 
00015 
00016 
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 
00052 
00053 
00054 
00055 
00056 
00057 
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 
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    
00087    if (kapp && !kapp->dcopClient()->isAttached())
00088    {
00089       kapp->dcopClient()->attach();
00090    }
00091    
00092    
00093    
00094    
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      
00136 
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      
00160      delete database;
00161      database = 0;
00162 
00163      bNoDatabase = true;
00164      if (openDummyIfNotFound)
00165      {
00166         
00167         
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 
00186 KSycoca::KSycoca( bool  )
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   delete KSycoca::_self;
00198 }
00199 
00200 KSycoca * KSycoca::self()
00201 {
00202     if (!_self) {
00203         qAddPostRoutine(delete_ksycoca_self);
00204         _self = new KSycoca();
00205     }
00206   return _self;
00207 }
00208 
00209 KSycoca::~KSycoca()
00210 {
00211    closeDatabase();
00212    delete d;
00213    _self = 0L;
00214 }
00215 
00216 void KSycoca::closeDatabase()
00217 {
00218    QIODevice *device = 0;
00219    if (m_str)
00220       device = m_str->device();
00221 #ifdef HAVE_MMAP
00222    if (device && m_sycoca_mmap)
00223    {
00224       QBuffer *buf = (QBuffer *) device;
00225       buf->buffer().resetRawData(m_sycoca_mmap, m_sycoca_size);
00226       
00227       
00228       munmap((char*) m_sycoca_mmap, m_sycoca_size);
00229       m_sycoca_mmap = 0;
00230    }
00231 #endif
00232 
00233    delete m_str;
00234    m_str = 0;
00235    delete device;
00236    if (d->database != device)
00237       delete d->database;
00238    device = 0;
00239    d->database = 0;
00240    
00241    
00242    delete m_lstFactories;
00243    m_lstFactories = 0L;
00244 }
00245 
00246 void KSycoca::addFactory( KSycocaFactory *factory )
00247 {
00248    assert(m_lstFactories);
00249    m_lstFactories->append(factory);
00250 }
00251 
00252 bool KSycoca::isChanged(const char *type)
00253 {
00254     return self()->d->changeList.contains(type);
00255 }
00256 
00257 void KSycoca::notifyDatabaseChanged(const QStringList &changeList)
00258 {
00259     d->changeList = changeList;
00260     
00261     
00262     
00263     
00264     
00265     closeDatabase();
00266 
00267     
00268     emit databaseChanged();
00269 }
00270 
00271 QDataStream * KSycoca::findEntry(int offset, KSycocaType &type)
00272 {
00273    if ( !m_str )
00274       openDatabase();
00275    
00276    m_str->device()->at(offset);
00277    Q_INT32 aType;
00278    (*m_str) >> aType;
00279    type = (KSycocaType) aType;
00280    
00281    return m_str;
00282 }
00283 
00284 bool KSycoca::checkVersion(bool abortOnError)
00285 {
00286    if ( !m_str )
00287    {
00288       if( !openDatabase(false ) )
00289         return false; 
00290 
00291       
00292       assert(m_str);
00293    }
00294    m_str->device()->at(0);
00295    Q_INT32 aVersion;
00296    (*m_str) >> aVersion;
00297    if ( aVersion < KSYCOCA_VERSION )
00298    {
00299       kdWarning(7011) << "Found version " << aVersion << ", expecting version " << KSYCOCA_VERSION << " or higher." << endl;
00300       if (!abortOnError) return false;
00301       kdError(7011) << "Outdated database ! Stop kded and restart it !" << endl;
00302       abort();
00303    }
00304    return true;
00305 }
00306 
00307 QDataStream * KSycoca::findFactory(KSycocaFactoryId id)
00308 {
00309    
00310    if (bNoDatabase)
00311    {
00312       closeDatabase(); 
00313       
00314       if ( !openDatabase(false ) )
00315       {
00316          static bool triedLaunchingKdeinit = false;
00317          if (!triedLaunchingKdeinit) 
00318          {
00319            triedLaunchingKdeinit = true;
00320            kdDebug(7011) << "findFactory: we have no database.... launching kdeinit" << endl;
00321            KApplication::startKdeinit();
00322            
00323          }
00324          if (!openDatabase(false))
00325             return 0L; 
00326       }
00327    }
00328    
00329    if (!checkVersion(false))
00330    {
00331      kdWarning(7011) << "Outdated database found" << endl;
00332      return 0L;
00333    }
00334    Q_INT32 aId;
00335    Q_INT32 aOffset;
00336    while(true)
00337    {
00338       (*m_str) >> aId;
00339       
00340       if (aId == 0)
00341       {
00342          kdError(7011) << "Error, KSycocaFactory (id = " << int(id) << ") not found!" << endl;
00343          break;
00344       }
00345       (*m_str) >> aOffset;
00346       if (aId == id)
00347       {
00348          
00349          m_str->device()->at(aOffset);
00350          return m_str;
00351       }
00352    }
00353    return 0;
00354 }
00355 
00356 QString KSycoca::kfsstnd_prefixes()
00357 {
00358    if (bNoDatabase) return "";
00359    if (!checkVersion(false)) return "";
00360    Q_INT32 aId;
00361    Q_INT32 aOffset;
00362    
00363    while(true)
00364    {
00365       (*m_str) >> aId;
00366       if ( aId )
00367         (*m_str) >> aOffset;
00368       else
00369         break; 
00370    }
00371    
00372    QString prefixes;
00373    KSycocaEntry::read(*m_str, prefixes);
00374    (*m_str) >> m_timeStamp;
00375    KSycocaEntry::read(*m_str, d->language);
00376    (*m_str) >> d->updateSig;
00377    KSycocaEntry::read(*m_str, d->allResourceDirs);
00378    return prefixes;
00379 }
00380 
00381 Q_UINT32 KSycoca::timeStamp()
00382 {
00383    if (!m_timeStamp)
00384       (void) kfsstnd_prefixes();
00385    return m_timeStamp;
00386 }
00387 
00388 Q_UINT32 KSycoca::updateSignature()
00389 {
00390    if (!m_timeStamp)
00391       (void) kfsstnd_prefixes();
00392    return d->updateSig;
00393 }
00394 
00395 QString KSycoca::language()
00396 {
00397    if (d->language.isEmpty())
00398       (void) kfsstnd_prefixes();
00399    return d->language;
00400 }
00401 
00402 QStringList KSycoca::allResourceDirs()
00403 {
00404    if (!m_timeStamp)
00405       (void) kfsstnd_prefixes();
00406    return d->allResourceDirs;
00407 }
00408 
00409 QString KSycoca::determineRelativePath( const QString & _fullpath, const char *_resource )
00410 {
00411   QString sRelativeFilePath;
00412   QStringList dirs = KGlobal::dirs()->resourceDirs( _resource );
00413   QStringList::ConstIterator dirsit = dirs.begin();
00414   for ( ; dirsit != dirs.end() && sRelativeFilePath.isEmpty(); ++dirsit ) {
00415     
00416     if ( _fullpath.find( *dirsit ) == 0 ) 
00417       sRelativeFilePath = _fullpath.mid( (*dirsit).length() ); 
00418   }
00419   if ( sRelativeFilePath.isEmpty() )
00420     kdFatal(7011) << QString("Couldn't find %1 in any %2 dir !!!").arg( _fullpath ).arg( _resource) << endl;
00421   
00422     
00423     
00424   return sRelativeFilePath;
00425 }
00426 
00427 KSycoca * KSycoca::_self = 0L;
00428 
00429 void KSycoca::flagError()
00430 {
00431    qWarning("ERROR: KSycoca database corruption!");
00432    if (_self)
00433    {
00434       if (_self->d->readError)
00435          return;
00436       _self->d->readError = true;
00437       if (_self->d->autoRebuild)
00438          system("kbuildsycoca"); 
00439    }
00440 }
00441 
00442 void KSycoca::disableAutoRebuild()
00443 {
00444    d->autoRebuild = false;
00445 }
00446 
00447 bool KSycoca::readError()
00448 {
00449    bool b = false;
00450    if (_self)
00451    {
00452       b = _self->d->readError;
00453       _self->d->readError = false;
00454    }
00455    return b;
00456 }
00457 
00458 void KSycocaEntry::read( QDataStream &s, QString &str )
00459 {
00460   Q_UINT32 bytes;
00461   s >> bytes;                          
00462   if ( bytes > 8192 ) {                
00463       if (bytes != 0xffffffff)
00464          KSycoca::flagError();
00465       str = QString::null;
00466   } 
00467   else if ( bytes > 0 ) {              
00468       int bt = bytes/2;
00469       str.setLength( bt );
00470       QChar* ch = (QChar *) str.unicode();
00471       char t[8192];
00472       char *b = t;
00473       s.readRawBytes( b, bytes );
00474       while ( bt-- ) {
00475           *ch++ = (ushort) (((ushort)b[0])<<8) | (uchar)b[1];
00476       b += 2;
00477       }
00478   } else {
00479       str = "";
00480   }
00481 }
00482 
00483 void KSycocaEntry::read( QDataStream &s, QStringList &list )
00484 {
00485   list.clear();
00486   Q_UINT32 count;
00487   s >> count;                          
00488   if (count >= 1024)
00489   {
00490      KSycoca::flagError();
00491      return;
00492   }
00493   for(Q_UINT32 i = 0; i < count; i++)
00494   {
00495      QString str;
00496      read(s, str);
00497      list.append( str );
00498      if (s.atEnd())
00499      {
00500         KSycoca::flagError();
00501         return;
00502      }
00503   }
00504 }
00505 
00506 void KSycoca::virtual_hook( int id, void* data )
00507 { DCOPObject::virtual_hook( id, data ); }
00508 
00509 void KSycocaEntry::virtual_hook( int, void* )
00510 {  }
00511 
00512 #include "ksycoca.moc"
 
This file is part of the documentation for kdecore Library Version 3.2.0.