• Skip to content
  • Skip to link menu
KDE 4.6 API Reference
  • KDE API Reference
  • kdelibs
  • KDE Home
  • Contact Us
 

KDED

kbuildsycoca.cpp

Go to the documentation of this file.
00001 // -*- c-basic-offset: 3 -*-
00002 /*  This file is part of the KDE libraries
00003  *  Copyright (C) 1999 David Faure <faure@kde.org>
00004  *  Copyright (C) 2002-2003 Waldo Bastian <bastian@kde.org>
00005  *
00006  *  This library is free software; you can redistribute it and/or
00007  *  modify it under the terms of the GNU Library General Public
00008  *  License version 2 as published by the Free Software Foundation;
00009  *
00010  *  This library is distributed in the hope that it will be useful,
00011  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00012  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013  *  Library General Public License for more details.
00014  *
00015  *  You should have received a copy of the GNU Library General Public License
00016  *  along with this library; see the file COPYING.LIB.  If not, write to
00017  *  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00018  *  Boston, MA 02110-1301, USA.
00019  **/
00020 
00021 #include "kbuildsycoca.h"
00022 #include "ksycoca_p.h"
00023 #include "kresourcelist.h"
00024 #include "vfolder_menu.h"
00025 
00026 #include <config.h>
00027 #include <config-kded.h>
00028 
00029 #include <kservice.h>
00030 #include <kmimetype.h>
00031 #include "kbuildservicetypefactory.h"
00032 #include "kbuildmimetypefactory.h"
00033 #include "kbuildservicefactory.h"
00034 #include "kbuildservicegroupfactory.h"
00035 #include "kbuildprotocolinfofactory.h"
00036 #include "kctimefactory.h"
00037 #include <ktemporaryfile.h>
00038 #include <QtCore/QDataStream>
00039 #include <QtCore/QDir>
00040 #include <QtCore/QEventLoop>
00041 #include <QtCore/QFile>
00042 #include <QtCore/QTimer>
00043 #include <QtDBus/QtDBus>
00044 #include <errno.h>
00045 
00046 #include <assert.h>
00047 #include <kapplication.h>
00048 #include <kglobal.h>
00049 #include <kdebug.h>
00050 #include <kdirwatch.h>
00051 #include <kstandarddirs.h>
00052 #include <ksavefile.h>
00053 #include <klocale.h>
00054 #include <kaboutdata.h>
00055 #include <kcmdlineargs.h>
00056 #ifndef KBUILDSYCOCA_NO_KCRASH
00057 #include <kcrash.h>
00058 #endif
00059 #include <kmemfile.h>
00060 
00061 #include <stdlib.h>
00062 #include <unistd.h>
00063 #include <time.h>
00064 #include <memory> // auto_ptr
00065 
00066 typedef QHash<QString, KSycocaEntry::Ptr> KBSEntryDict;
00067 typedef QList<KSycocaEntry::List> KSycocaEntryListList;
00068 
00069 static quint32 newTimestamp = 0;
00070 
00071 static KBuildServiceFactory *g_serviceFactory = 0;
00072 static KBuildServiceGroupFactory *g_buildServiceGroupFactory = 0;
00073 static KSycocaFactory *g_currentFactory = 0;
00074 static KCTimeInfo *g_ctimeInfo = 0; // factory
00075 static KCTimeDict *g_ctimeDict = 0; // old timestamps
00076 static QByteArray g_resource = 0;
00077 static KBSEntryDict *g_currentEntryDict = 0;
00078 static KBSEntryDict *g_serviceGroupEntryDict = 0;
00079 static KSycocaEntryListList *g_allEntries = 0; // entries from existing ksycoca
00080 static QStringList *g_allResourceDirs = 0;
00081 static bool g_changed = false;
00082 static KSycocaEntry::List g_tempStorage;
00083 static VFolderMenu *g_vfolder = 0;
00084 
00085 static const char *cSycocaPath = 0;
00086 
00087 static bool bGlobalDatabase = false;
00088 static bool bMenuTest = false;
00089 
00090 void crashHandler(int)
00091 {
00092    // If we crash while reading sycoca, we delete the database
00093    // in an attempt to recover.
00094    if (cSycocaPath)
00095       unlink(cSycocaPath);
00096 }
00097 
00098 static QString sycocaPath()
00099 {
00100   return KSycoca::absoluteFilePath(bGlobalDatabase ? KSycoca::GlobalDatabase : KSycoca::LocalDatabase);
00101 }
00102 
00103 KBuildSycoca::KBuildSycoca()
00104   : KSycoca( true )
00105 {
00106 }
00107 
00108 KBuildSycoca::~KBuildSycoca()
00109 {
00110 
00111 }
00112 
00113 KSycocaEntry::Ptr KBuildSycoca::createEntry(const QString &file, bool addToFactory)
00114 {
00115    quint32 timeStamp = g_ctimeInfo->dict()->ctime(file, g_resource);
00116    if (!timeStamp)
00117    {
00118       timeStamp = KGlobal::dirs()->calcResourceHash( g_resource, file,
00119                                                      KStandardDirs::Recursive);
00120    }
00121    KSycocaEntry::Ptr entry;
00122    if (g_allEntries)
00123    {
00124       assert(g_ctimeDict);
00125       quint32 oldTimestamp = g_ctimeDict->ctime(file, g_resource);
00126 
00127       if (timeStamp && (timeStamp == oldTimestamp))
00128       {
00129          // Re-use old entry
00130          if (g_currentFactory == g_buildServiceGroupFactory) // Strip .directory from service-group entries
00131          {
00132             entry = g_currentEntryDict->value(file.left(file.length()-10));
00133          } else {
00134             entry = g_currentEntryDict->value(file);
00135          }
00136          // remove from g_ctimeDict; if g_ctimeDict is not empty
00137          // after all files have been processed, it means
00138          // some files were removed since last time
00139          if (file.contains("fake"))
00140             kDebug(7021) << g_resource << "reusing (and removing) old entry [\"fake\"] for:" << file;
00141          g_ctimeDict->remove(file, g_resource);
00142       }
00143       else if (oldTimestamp)
00144       {
00145          g_changed = true;
00146          g_ctimeDict->remove(file, g_resource);
00147          kDebug(7021) << "modified:" << file << "in" << g_resource.constData();
00148       }
00149       else
00150       {
00151          g_changed = true;
00152          kDebug(7021) << "new:" << file << "in" << g_resource.constData();
00153       }
00154    }
00155    g_ctimeInfo->dict()->addCTime(file, g_resource, timeStamp);
00156    if (!entry)
00157    {
00158       // Create a new entry
00159       entry = g_currentFactory->createEntry( file, g_resource );
00160    }
00161    if ( entry && entry->isValid() )
00162    {
00163       if (addToFactory)
00164          g_currentFactory->addEntry(entry);
00165       else
00166          g_tempStorage.append(entry);
00167       return entry;
00168    }
00169    return KSycocaEntry::Ptr();
00170 }
00171 
00172 KService::Ptr KBuildSycoca::createService(const QString &path)
00173 {
00174    KSycocaEntry::Ptr entry = createEntry(path, false);
00175    return KService::Ptr::staticCast(entry);
00176 }
00177 
00178 // returns false if the database is up to date, true if it needs to be saved
00179 bool KBuildSycoca::build()
00180 {
00181   typedef QLinkedList<KBSEntryDict *> KBSEntryDictList;
00182   KBSEntryDictList entryDictList;
00183   KBSEntryDict *serviceEntryDict = 0;
00184 
00185   // Convert for each factory the entryList to a Dict.
00186   int i = 0;
00187   // For each factory
00188   for (KSycocaFactoryList::Iterator factory = factories()->begin();
00189        factory != factories()->end();
00190        ++factory)
00191   {
00192      KBSEntryDict *entryDict = new KBSEntryDict;
00193      if (g_allEntries)
00194      {
00195          const KSycocaEntry::List list = (*g_allEntries)[i++];
00196          for( KSycocaEntry::List::const_iterator it = list.begin();
00197             it != list.end();
00198             ++it)
00199          {
00200             entryDict->insert( (*it)->entryPath(), *it );
00201          }
00202      }
00203      if ((*factory) == g_serviceFactory)
00204         serviceEntryDict = entryDict;
00205      else if ((*factory) == g_buildServiceGroupFactory)
00206         g_serviceGroupEntryDict = entryDict;
00207      entryDictList.append(entryDict);
00208   }
00209 
00210   QStringList allResources; // we could use QSet<QString> - does order matter?
00211   // For each factory
00212   for (KSycocaFactoryList::Iterator factory = factories()->begin();
00213        factory != factories()->end();
00214        ++factory)
00215   {
00216     // For each resource the factory deals with
00217     const KSycocaResourceList *list = (*factory)->resourceList();
00218     if (!list) continue;
00219 
00220     for( KSycocaResourceList::ConstIterator it1 = list->constBegin();
00221          it1 != list->constEnd();
00222          ++it1 )
00223     {
00224       KSycocaResource res = (*it1);
00225       if (!allResources.contains(res.resource))
00226          allResources.append(res.resource);
00227     }
00228   }
00229 
00230   g_ctimeInfo = new KCTimeInfo(); // This is a build factory too, don't delete!!
00231   bool uptodate = true;
00232   // For all resources
00233   for( QStringList::ConstIterator it1 = allResources.constBegin();
00234        it1 != allResources.constEnd();
00235        ++it1 )
00236   {
00237      g_changed = false;
00238      g_resource = (*it1).toLatin1();
00239 
00240      QStringList relFiles;
00241 
00242      (void) KGlobal::dirs()->findAllResources( g_resource,
00243                                                QString(),
00244                                                KStandardDirs::Recursive |
00245                                                KStandardDirs::NoDuplicates,
00246                                                relFiles);
00247 
00248 
00249      // Now find all factories that use this resource....
00250      // For each factory
00251      KBSEntryDictList::const_iterator ed_it = entryDictList.begin();
00252      const KBSEntryDictList::const_iterator ed_end = entryDictList.end();
00253      KSycocaFactoryList::const_iterator it = factories()->constBegin();
00254      const KSycocaFactoryList::const_iterator end = factories()->constEnd();
00255      for ( ; it != end; ++it, ++ed_it )
00256      {
00257         g_currentFactory = (*it);
00258         // g_ctimeInfo gets created after the initial loop, so it has no entryDict.
00259         g_currentEntryDict = ed_it == ed_end ? 0 : *ed_it;
00260     // For each resource the factory deals with
00261         const KSycocaResourceList *list = g_currentFactory->resourceList();
00262         if (!list) continue;
00263 
00264         for( KSycocaResourceList::ConstIterator it2 = list->constBegin();
00265              it2 != list->constEnd();
00266              ++it2 )
00267         {
00268            KSycocaResource res = (*it2);
00269            if (res.resource != (*it1)) continue;
00270 
00271            // For each file in the resource
00272            for( QStringList::ConstIterator it3 = relFiles.constBegin();
00273                 it3 != relFiles.constEnd();
00274                 ++it3 )
00275            {
00276                // Check if file matches filter
00277                if ((*it3).endsWith(res.extension))
00278                    createEntry(*it3, true);
00279            }
00280         }
00281      }
00282      if (g_changed || !g_allEntries)
00283      {
00284         uptodate = false;
00285         //kDebug() << "CHANGED:" << g_resource;
00286         m_changedResources.append(g_resource);
00287      }
00288   }
00289 
00290   bool result = !uptodate || (g_ctimeDict && !g_ctimeDict->isEmpty());
00291   if (g_ctimeDict && !g_ctimeDict->isEmpty()) {
00292       //kDebug() << "Still in time dict:";
00293       //g_ctimeDict->dump();
00294       // ## It seems entries filtered out by vfolder are still in there,
00295       // so we end up always saving ksycoca, i.e. this method never returns false
00296 
00297       // Get the list of resources from which some files were deleted
00298       const QStringList resources = g_ctimeDict->resourceList();
00299       kDebug() << "Still in the time dict (i.e. deleted files)" << resources;
00300       m_changedResources += resources;
00301   }
00302 
00303   if (result || bMenuTest)
00304   {
00305      g_resource = "apps";
00306      g_currentFactory = g_serviceFactory;
00307      g_currentEntryDict = serviceEntryDict;
00308      g_changed = false;
00309 
00310      g_vfolder = new VFolderMenu(g_serviceFactory, this);
00311      if (!m_trackId.isEmpty())
00312         g_vfolder->setTrackId(m_trackId);
00313 
00314      VFolderMenu::SubMenu *kdeMenu = g_vfolder->parseMenu("applications.menu", true);
00315 
00316      KServiceGroup::Ptr entry = g_buildServiceGroupFactory->addNew("/", kdeMenu->directoryFile, KServiceGroup::Ptr(), false);
00317      entry->setLayoutInfo(kdeMenu->layoutList);
00318      createMenu(QString(), QString(), kdeMenu);
00319 
00320      (void) existingResourceDirs();
00321      *g_allResourceDirs += g_vfolder->allDirectories();
00322 
00323      if (g_changed || !g_allEntries)
00324      {
00325         uptodate = false;
00326         //kDebug() << "CHANGED:" << g_resource;
00327         m_changedResources.append(g_resource);
00328      }
00329      if (bMenuTest) {
00330          result = false;
00331      }
00332   }
00333 
00334   qDeleteAll(entryDictList);
00335   return result;
00336 }
00337 
00338 void KBuildSycoca::createMenu(const QString &caption_, const QString &name_, VFolderMenu::SubMenu *menu)
00339 {
00340   QString caption = caption_;
00341   QString name = name_;
00342   foreach (VFolderMenu::SubMenu *subMenu, menu->subMenus)
00343   {
00344      QString subName = name+subMenu->name+'/';
00345 
00346      QString directoryFile = subMenu->directoryFile;
00347      if (directoryFile.isEmpty())
00348         directoryFile = subName+".directory";
00349      quint32 timeStamp = g_ctimeInfo->dict()->ctime(directoryFile, g_resource);
00350      if (!timeStamp) {
00351         timeStamp = KGlobal::dirs()->calcResourceHash( g_resource, directoryFile,
00352                                                        KStandardDirs::Recursive );
00353      }
00354 
00355      KServiceGroup::Ptr entry;
00356      if (g_allEntries)
00357      {
00358         const quint32 oldTimestamp = g_ctimeDict->ctime(directoryFile, g_resource);
00359 
00360         if (timeStamp && (timeStamp == oldTimestamp))
00361         {
00362             KSycocaEntry::Ptr group = g_serviceGroupEntryDict->value(subName);
00363             if ( group )
00364             {
00365                 entry = KServiceGroup::Ptr::staticCast( group );
00366                 if (entry->directoryEntryPath() != directoryFile)
00367                     entry = 0; // Can't reuse this one!
00368             }
00369         }
00370      }
00371      g_ctimeInfo->dict()->addCTime(directoryFile, g_resource, timeStamp);
00372 
00373      entry = g_buildServiceGroupFactory->addNew(subName, subMenu->directoryFile, entry, subMenu->isDeleted);
00374      entry->setLayoutInfo(subMenu->layoutList);
00375      if (! (bMenuTest && entry->noDisplay()) )
00376         createMenu(caption + entry->caption() + '/', subName, subMenu);
00377   }
00378   if (caption.isEmpty())
00379      caption += '/';
00380   if (name.isEmpty())
00381      name += '/';
00382   foreach (const KService::Ptr &p, menu->items)
00383   {
00384      if (bMenuTest)
00385      {
00386         if (!menu->isDeleted && !p->noDisplay())
00387            printf("%s\t%s\t%s\n", qPrintable( caption ), qPrintable( p->menuId() ), qPrintable( KStandardDirs::locate("apps", p->entryPath() ) ) );
00388      }
00389      else
00390      {
00391         g_buildServiceGroupFactory->addNewEntryTo( name, p );
00392      }
00393   }
00394 }
00395 
00396 bool KBuildSycoca::recreate()
00397 {
00398   QString path(sycocaPath());
00399 
00400   // KSaveFile first writes to a temp file.
00401   // Upon finalize() it moves the stuff to the right place.
00402   KSaveFile database(path);
00403   bool openedOK = database.open();
00404   if (!openedOK && database.error() == QFile::PermissionsError && QFile::exists(path))
00405   {
00406     QFile::remove( path );
00407     openedOK = database.open();
00408   }
00409   if (!openedOK)
00410   {
00411     fprintf(stderr, "kbuildsycoca4: ERROR creating database '%s'! %s\n",
00412       path.toLocal8Bit().data(), database.errorString().toLocal8Bit().data());
00413     return false;
00414   }
00415 
00416   QDataStream* str = new QDataStream(&database);
00417   str->setVersion(QDataStream::Qt_3_1);
00418 
00419   kDebug(7021).nospace() << "Recreating ksycoca file (" << path << ", version " << KSycoca::version() << ")";
00420 
00421   // It is very important to build the servicetype one first
00422   // Both are registered in KSycoca, no need to keep the pointers
00423   KSycocaFactory *stf = new KBuildServiceTypeFactory;
00424   KBuildMimeTypeFactory* mimeTypeFactory = new KBuildMimeTypeFactory;
00425   g_buildServiceGroupFactory = new KBuildServiceGroupFactory();
00426   g_serviceFactory = new KBuildServiceFactory(stf, mimeTypeFactory, g_buildServiceGroupFactory);
00427   (void) new KBuildProtocolInfoFactory();
00428 
00429   if( build()) // Parse dirs
00430   {
00431     save(str); // Save database
00432     if (str->status() != QDataStream::Ok) // ######## TODO: does this detect write errors, e.g. disk full?
00433       database.abort(); // Error
00434     delete str;
00435     str = 0;
00436     if (!database.finalize())
00437     {
00438       fprintf(stderr, "kbuildsycoca4: ERROR writing database '%s'!\n", database.fileName().toLocal8Bit().data());
00439       fprintf(stderr, "kbuildsycoca4: Disk full?\n");
00440       return false;
00441     }
00442   }
00443   else
00444   {
00445     delete str;
00446     str = 0;
00447     database.abort();
00448     if (bMenuTest)
00449        return true;
00450     kDebug(7021) << "Database is up to date";
00451   }
00452 
00453   if (!bGlobalDatabase)
00454   {
00455     // update the timestamp file
00456     QString stamppath = path + "stamp";
00457     QFile ksycocastamp(stamppath);
00458     ksycocastamp.open( QIODevice::WriteOnly );
00459     QDataStream str( &ksycocastamp );
00460     str.setVersion(QDataStream::Qt_3_1);
00461     str << newTimestamp;
00462     str << existingResourceDirs();
00463     if (g_vfolder)
00464         str << g_vfolder->allDirectories(); // Extra resource dirs
00465   }
00466   if (d->m_sycocaStrategy == KSycocaPrivate::StrategyMemFile)
00467      KMemFile::fileContentsChanged(path);
00468 
00469   return true;
00470 }
00471 
00472 void KBuildSycoca::save(QDataStream* str)
00473 {
00474    // Write header (#pass 1)
00475    str->device()->seek(0);
00476 
00477    (*str) << (qint32) KSycoca::version();
00478    KSycocaFactory * servicetypeFactory = 0;
00479    KBuildMimeTypeFactory * mimeTypeFactory = 0;
00480    KBuildServiceFactory * serviceFactory = 0;
00481    for(KSycocaFactoryList::Iterator factory = factories()->begin();
00482        factory != factories()->end();
00483        ++factory)
00484    {
00485       qint32 aId;
00486       qint32 aOffset;
00487       aId = (*factory)->factoryId();
00488       if ( aId == KST_KServiceTypeFactory )
00489          servicetypeFactory = *factory;
00490       else if ( aId == KST_KMimeTypeFactory )
00491          mimeTypeFactory = static_cast<KBuildMimeTypeFactory *>( *factory );
00492       else if ( aId == KST_KServiceFactory )
00493          serviceFactory = static_cast<KBuildServiceFactory *>( *factory );
00494       aOffset = (*factory)->offset(); // not set yet, so always 0
00495       (*str) << aId;
00496       (*str) << aOffset;
00497    }
00498    (*str) << (qint32) 0; // No more factories.
00499    // Write KDEDIRS
00500    (*str) << KGlobal::dirs()->kfsstnd_prefixes();
00501    (*str) << newTimestamp;
00502    (*str) << KGlobal::locale()->language();
00503    (*str) << KGlobal::dirs()->calcResourceHash("services", "update_ksycoca",
00504                                                  KStandardDirs::Recursive );
00505    (*str) << (*g_allResourceDirs);
00506 
00507    // Calculate per-servicetype/mimetype data
00508    serviceFactory->postProcessServices();
00509 
00510    // Here so that it's the last debug message
00511    kDebug(7021) << "Saving";
00512 
00513    // Write factory data....
00514    for(KSycocaFactoryList::Iterator factory = factories()->begin();
00515        factory != factories()->end();
00516        ++factory)
00517    {
00518       (*factory)->save(*str);
00519       if (str->status() != QDataStream::Ok) // ######## TODO: does this detect write errors, e.g. disk full?
00520          return; // error
00521    }
00522 
00523    int endOfData = str->device()->pos();
00524 
00525    // Write header (#pass 2)
00526    str->device()->seek(0);
00527 
00528    (*str) << (qint32) KSycoca::version();
00529    for(KSycocaFactoryList::Iterator factory = factories()->begin();
00530        factory != factories()->end(); ++factory)
00531    {
00532       qint32 aId;
00533       qint32 aOffset;
00534       aId = (*factory)->factoryId();
00535       aOffset = (*factory)->offset();
00536       (*str) << aId;
00537       (*str) << aOffset;
00538    }
00539    (*str) << (qint32) 0; // No more factories.
00540 
00541    // Jump to end of database
00542    str->device()->seek(endOfData);
00543 }
00544 
00545 bool KBuildSycoca::checkDirTimestamps( const QString& dirname, const QDateTime& stamp, bool top )
00546 {
00547    if( top )
00548    {
00549       QFileInfo inf( dirname );
00550       if( inf.lastModified() > stamp ) {
00551          kDebug( 7021 ) << "timestamp changed:" << dirname;
00552          return false;
00553       }
00554    }
00555    QDir dir( dirname );
00556    const QFileInfoList list = dir.entryInfoList( QDir::NoFilter, QDir::Unsorted );
00557    if (list.isEmpty())
00558       return true;
00559 
00560    foreach ( const QFileInfo& fi, list ) {
00561       if( fi.fileName() == "." || fi.fileName() == ".." )
00562          continue;
00563       if( fi.lastModified() > stamp )
00564       {
00565          kDebug( 7201 ) << "timestamp changed:" << fi.filePath();
00566          return false;
00567       }
00568       if( fi.isDir() && !checkDirTimestamps( fi.filePath(), stamp, false ))
00569             return false;
00570    }
00571    return true;
00572 }
00573 
00574 // check times of last modification of all files on which ksycoca depens,
00575 // and also their directories
00576 // if all of them are older than the timestamp in file ksycocastamp, this
00577 // means that there's no need to rebuild ksycoca
00578 bool KBuildSycoca::checkTimestamps( quint32 timestamp, const QStringList &dirs )
00579 {
00580    kDebug( 7021 ) << "checking file timestamps";
00581    QDateTime stamp;
00582    stamp.setTime_t( timestamp );
00583    for( QStringList::ConstIterator it = dirs.begin();
00584         it != dirs.end();
00585         ++it )
00586    {
00587       if( !checkDirTimestamps( *it, stamp, true ))
00588             return false;
00589    }
00590    kDebug( 7021 ) << "timestamps check ok";
00591    return true;
00592 }
00593 
00594 QStringList KBuildSycoca::existingResourceDirs()
00595 {
00596    static QStringList* dirs = NULL;
00597    if( dirs != NULL )
00598        return *dirs;
00599    dirs = new QStringList;
00600    g_allResourceDirs = new QStringList;
00601    // these are all resources cached by ksycoca
00602    QStringList resources;
00603    resources += KBuildServiceTypeFactory::resourceTypes();
00604    resources += KBuildMimeTypeFactory::resourceTypes();
00605    resources += KBuildServiceGroupFactory::resourceTypes();
00606    resources += KBuildServiceFactory::resourceTypes();
00607    resources += KBuildProtocolInfoFactory::resourceTypes();
00608    while( !resources.empty())
00609    {
00610       QString res = resources.front();
00611       *dirs += KGlobal::dirs()->resourceDirs( res.toLatin1());
00612       resources.removeAll( res );
00613    }
00614 
00615    *g_allResourceDirs = *dirs;
00616 
00617    for( QStringList::Iterator it = dirs->begin();
00618         it != dirs->end(); )
00619    {
00620       QFileInfo inf( *it );
00621       if( !inf.exists() || !inf.isReadable() )
00622          it = dirs->erase( it );
00623       else
00624          ++it;
00625    }
00626    return *dirs;
00627 }
00628 
00629 static const char appFullName[] = "org.kde.kbuildsycoca";
00630 static const char appName[] = "kbuildsycoca4";
00631 static const char appVersion[] = "1.1";
00632 
00633 extern "C" KDE_EXPORT int kdemain(int argc, char **argv)
00634 {
00635    KAboutData d(appName, "kdelibs4", ki18n("KBuildSycoca"), appVersion,
00636                 ki18n("Rebuilds the system configuration cache."),
00637                 KAboutData::License_GPL, ki18n("(c) 1999-2002 KDE Developers"));
00638    d.addAuthor(ki18n("David Faure"), ki18n("Author"), "faure@kde.org");
00639    d.addAuthor(ki18n("Waldo Bastian"), ki18n("Author"), "bastian@kde.org");
00640 
00641    KCmdLineOptions options;
00642    options.add("nosignal", ki18n("Do not signal applications to update"));
00643    options.add("noincremental", ki18n("Disable incremental update, re-read everything"));
00644    options.add("checkstamps", ki18n("Check file timestamps"));
00645    options.add("nocheckfiles", ki18n("Disable checking files (dangerous)"));
00646    options.add("global", ki18n("Create global database"));
00647    options.add("menutest", ki18n("Perform menu generation test run only"));
00648    options.add("track <menu-id>", ki18n("Track menu id for debug purposes"));
00649 
00650    KCmdLineArgs::init(argc, argv, &d);
00651    KCmdLineArgs::addCmdLineOptions(options);
00652    KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
00653    bGlobalDatabase = args->isSet("global");
00654    bMenuTest = args->isSet("menutest");
00655 
00656    if (bGlobalDatabase)
00657    {
00658      setenv("KDEHOME", "-", 1);
00659      setenv("KDEROOTHOME", "-", 1);
00660    }
00661 
00662    QCoreApplication k(argc, argv);
00663    KComponentData mainComponent(d);
00664 
00665 #ifndef KBUILDSYCOCA_NO_KCRASH
00666    KCrash::setCrashHandler(KCrash::defaultCrashHandler);
00667    KCrash::setEmergencySaveFunction(crashHandler);
00668    KCrash::setApplicationName(QString(appName));
00669 #endif
00670 
00671    // force generating of KLocale object. if not, the database will get
00672    // be translated
00673    KGlobal::locale();
00674    KGlobal::dirs()->addResourceType("app-reg", 0, "share/application-registry" );
00675 
00676    while(QDBusConnection::sessionBus().isConnected())
00677    {
00678      // kapp registered already, but with the PID in the name.
00679      // We need to re-register without it, to detect already-running kbuildsycoca instances.
00680      if (QDBusConnection::sessionBus().interface()->registerService(appFullName, QDBusConnectionInterface::QueueService)
00681          != QDBusConnectionInterface::ServiceQueued)
00682      {
00683        break; // Go
00684      }
00685      fprintf(stderr, "Waiting for already running %s to finish.\n", appName);
00686 
00687      QEventLoop eventLoop;
00688      QObject::connect(QDBusConnection::sessionBus().interface(), SIGNAL(serviceRegistered(QString)),
00689                       &eventLoop, SLOT(quit()));
00690      eventLoop.exec( QEventLoop::ExcludeUserInputEvents );
00691    }
00692    fprintf(stderr, "%s running...\n", appName);
00693 
00694    bool checkfiles = bGlobalDatabase || args->isSet("checkfiles");
00695 
00696    bool incremental = !bGlobalDatabase && args->isSet("incremental") && checkfiles;
00697    if (incremental || !checkfiles)
00698    {
00699      KSycoca::disableAutoRebuild(); // Prevent deadlock
00700      QString current_language = KGlobal::locale()->language();
00701      QString ksycoca_language = KSycoca::self()->language();
00702      quint32 current_update_sig = KGlobal::dirs()->calcResourceHash("services", "update_ksycoca",
00703                                                                     KStandardDirs::Recursive );
00704      quint32 ksycoca_update_sig = KSycoca::self()->updateSignature();
00705      QString current_prefixes = KGlobal::dirs()->kfsstnd_prefixes();
00706      QString ksycoca_prefixes = KSycoca::self()->kfsstnd_prefixes();
00707 
00708      if ((current_update_sig != ksycoca_update_sig) ||
00709          (current_language != ksycoca_language) ||
00710          (current_prefixes != ksycoca_prefixes) ||
00711          (KSycoca::self()->timeStamp() == 0))
00712      {
00713         incremental = false;
00714         checkfiles = true;
00715         KBuildSycoca::clearCaches();
00716      }
00717    }
00718 
00719    bool checkstamps = incremental && args->isSet("checkstamps") && checkfiles;
00720    quint32 filestamp = 0;
00721    QStringList oldresourcedirs;
00722    if( checkstamps && incremental )
00723    {
00724        QString path = sycocaPath()+"stamp";
00725        QByteArray qPath = QFile::encodeName(path);
00726        cSycocaPath = qPath.data(); // Delete timestamps on crash
00727        QFile ksycocastamp(path);
00728        if( ksycocastamp.open( QIODevice::ReadOnly ))
00729        {
00730            QDataStream str( &ksycocastamp );
00731            str.setVersion(QDataStream::Qt_3_1);
00732 
00733            if (!str.atEnd())
00734                str >> filestamp;
00735            if (!str.atEnd())
00736            {
00737                str >> oldresourcedirs;
00738                if( oldresourcedirs != KBuildSycoca::existingResourceDirs())
00739                    checkstamps = false;
00740            }
00741            else
00742            {
00743                checkstamps = false;
00744            }
00745            if (!str.atEnd())
00746            {
00747                QStringList extraResourceDirs;
00748                str >> extraResourceDirs;
00749                oldresourcedirs += extraResourceDirs;
00750            }
00751        }
00752        else
00753        {
00754            checkstamps = false;
00755        }
00756        cSycocaPath = 0;
00757    }
00758 
00759    newTimestamp = (quint32) time(0);
00760    QStringList changedResources;
00761 
00762    if( checkfiles && ( !checkstamps || !KBuildSycoca::checkTimestamps( filestamp, oldresourcedirs )))
00763    {
00764       QByteArray qSycocaPath = QFile::encodeName(sycocaPath());
00765       cSycocaPath = qSycocaPath.data();
00766 
00767       g_allEntries = 0;
00768       g_ctimeDict = 0;
00769       if (incremental)
00770       {
00771          kDebug(7021) << "Reusing existing ksycoca";
00772          KSycoca::self();
00773          KSycocaFactoryList *factories = new KSycocaFactoryList;
00774          g_allEntries = new KSycocaEntryListList;
00775          g_ctimeDict = new KCTimeDict;
00776 
00777          // Must be in same order as in KBuildSycoca::recreate()!
00778          factories->append( new KServiceTypeFactory );
00779          factories->append( new KMimeTypeFactory );
00780          factories->append( new KServiceGroupFactory );
00781          factories->append( new KServiceFactory );
00782          factories->append( new KProtocolInfoFactory );
00783 
00784          // For each factory
00785      for (KSycocaFactoryList::Iterator factory = factories->begin();
00786           factory != factories->end(); ++factory)
00787          {
00788              const KSycocaEntry::List list = (*factory)->allEntries();
00789              g_allEntries->append( list );
00790          }
00791          delete factories; factories = 0;
00792          KCTimeInfo *ctimeInfo = new KCTimeInfo;
00793          *g_ctimeDict = ctimeInfo->loadDict();
00794       }
00795       cSycocaPath = 0;
00796 
00797       KBuildSycoca *sycoca = new KBuildSycoca; // Build data base (deletes oldSycoca)
00798       if (args->isSet("track"))
00799          sycoca->setTrackId(args->getOption("track"));
00800       if (!sycoca->recreate()) {
00801         return -1;
00802       }
00803       changedResources = sycoca->changedResources();
00804 
00805       if (bGlobalDatabase)
00806       {
00807         // These directories may have been created with 0700 permission
00808         // better delete them if they are empty
00809         QString applnkDir = KGlobal::dirs()->saveLocation("apps", QString(), false);
00810         ::rmdir(QFile::encodeName(applnkDir));
00811         QString servicetypesDir = KGlobal::dirs()->saveLocation("servicetypes", QString(), false);
00812         ::rmdir(QFile::encodeName(servicetypesDir));
00813       }
00814    }
00815 
00816    if (args->isSet("signal"))
00817    {
00818      // Notify ALL applications that have a ksycoca object, using a signal
00819      QDBusMessage signal = QDBusMessage::createSignal("/", "org.kde.KSycoca", "notifyDatabaseChanged" );
00820      signal << changedResources;
00821 
00822      if (QDBusConnection::sessionBus().isConnected()) {
00823          kDebug() << "Emitting notifyDatabaseChanged" << changedResources;
00824        QDBusConnection::sessionBus().send(signal);
00825        qApp->processEvents(); // make sure the dbus signal is sent before we quit.
00826      }
00827    }
00828 
00829    return 0;
00830 }
00831 
00832 #include "kbuildsycoca.moc"

KDED

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

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDEWebKit
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUnitConversion
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.7.3
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal