kbuildsycoca.cpp

00001 /*  This file is part of the KDE libraries
00002  *  Copyright (C) 1999 David Faure <faure@kde.org>
00003  *  Copyright (C) 2002-2003 Waldo Bastian <bastian@kde.org>
00004  *
00005  *  This library is free software; you can redistribute it and/or
00006  *  modify it under the terms of the GNU Library General Public
00007  *  License version 2 as published by the Free Software Foundation;
00008  *
00009  *  This library is distributed in the hope that it will be useful,
00010  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012  *  Library General Public License for more details.
00013  *
00014  *  You should have received a copy of the GNU Library General Public License
00015  *  along with this library; see the file COPYING.LIB.  If not, write to
00016  *  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00017  *  Boston, MA 02110-1301, USA.
00018  **/
00019 
00020 #include <qdir.h>
00021 #include <qeventloop.h>
00022 #include <config.h>
00023 
00024 #include "kbuildsycoca.h"
00025 #include "kresourcelist.h"
00026 #include "vfolder_menu.h"
00027 
00028 #include <kservice.h>
00029 #include <kmimetype.h>
00030 #include <kbuildservicetypefactory.h>
00031 #include <kbuildservicefactory.h>
00032 #include <kbuildservicegroupfactory.h>
00033 #include <kbuildimageiofactory.h>
00034 #include <kbuildprotocolinfofactory.h>
00035 #include <kctimefactory.h>
00036 #include <kdatastream.h>
00037 
00038 #include <qdatastream.h>
00039 #include <qfile.h>
00040 #include <qtimer.h>
00041 
00042 #include <assert.h>
00043 #include <kapplication.h>
00044 #include <dcopclient.h>
00045 #include <kglobal.h>
00046 #include <kdebug.h>
00047 #include <kdirwatch.h>
00048 #include <kstandarddirs.h>
00049 #include <ksavefile.h>
00050 #include <klocale.h>
00051 #include <kaboutdata.h>
00052 #include <kcmdlineargs.h>
00053 #include <kcrash.h>
00054 
00055 #ifdef KBUILDSYCOCA_GUI // KBUILDSYCOCA_GUI is used on win32 to build 
00056                         // GUI version of kbuildsycoca, so-called "kbuildsycocaw".
00057 # include <qlabel.h>
00058 # include <kmessagebox.h>
00059   bool silent;
00060   bool showprogress;
00061 #endif
00062 
00063 #include <stdlib.h>
00064 #include <unistd.h>
00065 #include <time.h>
00066 #include <memory> // auto_ptr
00067 
00068 typedef QDict<KSycocaEntry> KBSEntryDict;
00069 typedef QValueList<KSycocaEntry::List> KSycocaEntryListList;
00070 
00071 static Q_UINT32 newTimestamp = 0;
00072 
00073 static KBuildServiceFactory *g_bsf = 0;
00074 static KBuildServiceGroupFactory *g_bsgf = 0;
00075 static KSycocaFactory *g_factory = 0;
00076 static KCTimeInfo *g_ctimeInfo = 0;
00077 static QDict<Q_UINT32> *g_ctimeDict = 0;
00078 static const char *g_resource = 0;
00079 static KBSEntryDict *g_entryDict = 0;
00080 static KBSEntryDict *g_serviceGroupEntryDict = 0;
00081 static KSycocaEntryListList *g_allEntries = 0;
00082 static QStringList *g_changeList = 0;
00083 static QStringList *g_allResourceDirs = 0;
00084 static bool g_changed = false;
00085 static KSycocaEntry::List g_tempStorage;
00086 static VFolderMenu *g_vfolder = 0;
00087 
00088 static const char *cSycocaPath = 0;
00089 
00090 static bool bGlobalDatabase = false;
00091 static bool bMenuTest = false;
00092 
00093 void crashHandler(int)
00094 {
00095    // If we crash while reading sycoca, we delete the database
00096    // in an attempt to recover.
00097    if (cSycocaPath)
00098       unlink(cSycocaPath);
00099 }
00100 
00101 static QString sycocaPath()
00102 {
00103   QString path;
00104 
00105   if (bGlobalDatabase)
00106   {
00107      path = KGlobal::dirs()->saveLocation("services")+"ksycoca";
00108   }
00109   else
00110   {
00111      QCString ksycoca_env = getenv("KDESYCOCA");
00112      if (ksycoca_env.isEmpty())
00113         path = KGlobal::dirs()->saveLocation("cache")+"ksycoca";
00114      else
00115         path = QFile::decodeName(ksycoca_env);
00116   }
00117 
00118   return path;
00119 }
00120 
00121 static QString oldSycocaPath()
00122 {
00123   QCString ksycoca_env = getenv("KDESYCOCA");
00124   if (ksycoca_env.isEmpty())
00125      return KGlobal::dirs()->saveLocation("tmp")+"ksycoca";
00126 
00127   return QString::null;
00128 }
00129 
00130 KBuildSycoca::KBuildSycoca()
00131   : KSycoca( true )
00132 {
00133 }
00134 
00135 KBuildSycoca::~KBuildSycoca()
00136 {
00137 
00138 }
00139 
00140 void KBuildSycoca::processGnomeVfs()
00141 {
00142    QString file = locate("app-reg", "gnome-vfs.applications");
00143    if (file.isEmpty())
00144    {
00145 //      kdDebug(7021) << "gnome-vfs.applications not found." << endl;
00146       return;
00147    }
00148 
00149    QString app;
00150 
00151    char line[1024*64];
00152 
00153    FILE *f = fopen(QFile::encodeName(file), "r");
00154    while (!feof(f))
00155    {
00156       if (!fgets(line, sizeof(line)-1, f))
00157       {
00158         break;
00159       }
00160 
00161       if (line[0] != '\t')
00162       {
00163           app = QString::fromLatin1(line);
00164           app.truncate(app.length()-1);
00165       }
00166       else if (strncmp(line+1, "mime_types=", 11) == 0)
00167       {
00168           QString mimetypes = QString::fromLatin1(line+12);
00169           mimetypes.truncate(mimetypes.length()-1);
00170           mimetypes.replace(QRegExp("\\*"), "all");
00171           KService *s = g_bsf->findServiceByName(app);
00172           if (!s)
00173              continue;
00174 
00175           QStringList &serviceTypes = s->accessServiceTypes();
00176           if (serviceTypes.count() <= 1)
00177           {
00178              serviceTypes += QStringList::split(',', mimetypes);
00179 //             kdDebug(7021) << "Adding gnome mimetypes for '" << app << "'.\n";
00180 //             kdDebug(7021) << "ServiceTypes=" << s->serviceTypes().join(":") << endl;
00181           }
00182       }
00183    }
00184    fclose( f );
00185 }
00186 
00187 KSycocaEntry *KBuildSycoca::createEntry(const QString &file, bool addToFactory)
00188 {
00189    Q_UINT32 timeStamp = g_ctimeInfo->ctime(file);
00190    if (!timeStamp)
00191    {
00192       timeStamp = KGlobal::dirs()->calcResourceHash( g_resource, file, true);
00193    }
00194    KSycocaEntry* entry = 0;
00195    if (g_allEntries)
00196    {
00197       assert(g_ctimeDict);
00198       Q_UINT32 *timeP = (*g_ctimeDict)[file];
00199       Q_UINT32 oldTimestamp = timeP ? *timeP : 0;
00200 
00201       if (timeStamp && (timeStamp == oldTimestamp))
00202       {
00203          // Re-use old entry
00204          if (g_factory == g_bsgf) // Strip .directory from service-group entries
00205          {
00206             entry = g_entryDict->find(file.left(file.length()-10));
00207          }
00208          else if (g_factory == g_bsf)
00209          {
00210             entry = g_entryDict->find(file);
00211          }
00212          else
00213          {
00214             entry = g_entryDict->find(file);
00215          }
00216          // remove from g_ctimeDict; if g_ctimeDict is not empty
00217          // after all files have been processed, it means
00218          // some files were removed since last time
00219          g_ctimeDict->remove( file );
00220       }
00221       else if (oldTimestamp)
00222       {
00223          g_changed = true;
00224          kdDebug(7021) << "modified: " << file << endl;
00225       }
00226       else
00227       {
00228          g_changed = true;
00229          kdDebug(7021) << "new: " << file << endl;
00230       }
00231    }
00232    g_ctimeInfo->addCTime(file, timeStamp );
00233    if (!entry)
00234    {
00235       // Create a new entry
00236       entry = g_factory->createEntry( file, g_resource );
00237    }
00238    if ( entry && entry->isValid() )
00239    {
00240       if (addToFactory)
00241          g_factory->addEntry( entry, g_resource );
00242       else
00243          g_tempStorage.append(entry);
00244       return entry;
00245    }
00246    return 0;
00247 }
00248 
00249 void KBuildSycoca::slotCreateEntry(const QString &file, KService **service)
00250 {
00251    KSycocaEntry *entry = createEntry(file, false);
00252    *service = dynamic_cast<KService *>(entry);
00253 }
00254 
00255 // returns false if the database is up to date
00256 bool KBuildSycoca::build()
00257 {
00258   typedef QPtrList<KBSEntryDict> KBSEntryDictList;
00259   KBSEntryDictList *entryDictList = 0;
00260   KBSEntryDict *serviceEntryDict = 0;
00261 
00262   entryDictList = new KBSEntryDictList();
00263   // Convert for each factory the entryList to a Dict.
00264   int i = 0;
00265   // For each factory
00266   for (KSycocaFactory *factory = m_lstFactories->first();
00267        factory;
00268        factory = m_lstFactories->next() )
00269   {
00270      KBSEntryDict *entryDict = new KBSEntryDict();
00271      if (g_allEntries)
00272      {
00273          KSycocaEntry::List list = (*g_allEntries)[i++];
00274          for( KSycocaEntry::List::Iterator it = list.begin();
00275             it != list.end();
00276             ++it)
00277          {
00278             entryDict->insert( (*it)->entryPath(), static_cast<KSycocaEntry *>(*it));
00279          }
00280      }
00281      if (factory == g_bsf)
00282         serviceEntryDict = entryDict;
00283      else if (factory == g_bsgf)
00284         g_serviceGroupEntryDict = entryDict;
00285      entryDictList->append(entryDict);
00286   }
00287 
00288   QStringList allResources;
00289   // For each factory
00290   for (KSycocaFactory *factory = m_lstFactories->first();
00291        factory;
00292        factory = m_lstFactories->next() )
00293   {
00294     // For each resource the factory deals with
00295     const KSycocaResourceList *list = factory->resourceList();
00296     if (!list) continue;
00297 
00298     for( KSycocaResourceList::ConstIterator it1 = list->begin();
00299          it1 != list->end();
00300          ++it1 )
00301     {
00302       KSycocaResource res = (*it1);
00303       if (!allResources.contains(res.resource))
00304          allResources.append(res.resource);
00305     }
00306   }
00307 
00308   g_ctimeInfo = new KCTimeInfo(); // This is a build factory too, don't delete!!
00309   bool uptodate = true;
00310   // For all resources
00311   for( QStringList::ConstIterator it1 = allResources.begin();
00312        it1 != allResources.end();
00313        ++it1 )
00314   {
00315      g_changed = false;
00316      g_resource = (*it1).ascii();
00317 
00318      QStringList relFiles;
00319 
00320      (void) KGlobal::dirs()->findAllResources( g_resource,
00321                                                QString::null,
00322                                                true, // Recursive!
00323                                                true, // uniq
00324                                                relFiles);
00325 
00326 
00327      // Now find all factories that use this resource....
00328      // For each factory
00329      g_entryDict = entryDictList->first();
00330      for (g_factory = m_lstFactories->first();
00331           g_factory;
00332           g_factory = m_lstFactories->next(),
00333           g_entryDict = entryDictList->next() )
00334      {
00335         // For each resource the factory deals with
00336         const KSycocaResourceList *list = g_factory->resourceList();
00337         if (!list) continue;
00338 
00339         for( KSycocaResourceList::ConstIterator it2 = list->begin();
00340              it2 != list->end();
00341              ++it2 )
00342         {
00343            KSycocaResource res = (*it2);
00344            if (res.resource != (*it1)) continue;
00345 
00346            // For each file in the resource
00347            for( QStringList::ConstIterator it3 = relFiles.begin();
00348                 it3 != relFiles.end();
00349                 ++it3 )
00350            {
00351                // Check if file matches filter
00352                if ((*it3).endsWith(res.extension))
00353                    createEntry(*it3, true);
00354            }
00355         }
00356         if ((g_factory == g_bsf) && (strcmp(g_resource, "services") == 0))
00357            processGnomeVfs();
00358      }
00359      if (g_changed || !g_allEntries)
00360      {
00361         uptodate = false;
00362         g_changeList->append(g_resource);
00363      }
00364   }
00365 
00366   bool result = !uptodate || !g_ctimeDict->isEmpty();
00367 
00368   if (result || bMenuTest)
00369   {
00370      g_resource = "apps";
00371      g_factory = g_bsf;
00372      g_entryDict = serviceEntryDict;
00373      g_changed = false;
00374 
00375      g_vfolder = new VFolderMenu;
00376      if (!m_trackId.isEmpty())
00377         g_vfolder->setTrackId(m_trackId);
00378 
00379      connect(g_vfolder, SIGNAL(newService(const QString &, KService **)),
00380              this, SLOT(slotCreateEntry(const QString &, KService **)));
00381              
00382      VFolderMenu::SubMenu *kdeMenu;
00383      if ( QFile::exists( "/etc/xdg/menus/kde-applications.menu" ) )
00384         kdeMenu = g_vfolder->parseMenu("kde-applications.menu", true);
00385      else
00386         kdeMenu = g_vfolder->parseMenu("applications.menu", true);
00387 
00388      KServiceGroup *entry = g_bsgf->addNew("/", kdeMenu->directoryFile, 0, false);
00389      entry->setLayoutInfo(kdeMenu->layoutList);
00390      createMenu(QString::null, QString::null, kdeMenu);
00391 
00392      KServiceGroup::Ptr g(entry);
00393      createMenuAttribute( g );
00394 
00395      (void) existingResourceDirs();
00396      *g_allResourceDirs += g_vfolder->allDirectories();
00397 
00398      disconnect(g_vfolder, SIGNAL(newService(const QString &, KService **)),
00399              this, SLOT(slotCreateEntry(const QString &, KService **)));
00400 
00401      if (g_changed || !g_allEntries)
00402      {
00403         uptodate = false;
00404         g_changeList->append(g_resource);
00405      }
00406      if (bMenuTest)
00407         return false;
00408   }
00409 
00410   return result;
00411 }
00412 
00413 void KBuildSycoca::createMenuAttribute( KServiceGroup::Ptr entry )
00414 {
00415     KServiceGroup::List list = entry->entries(true, true);
00416     KServiceGroup::List::ConstIterator it = list.begin();
00417     for (; it != list.end(); ++it) {
00418         KSycocaEntry * e = *it;
00419         if (e->isType(KST_KServiceGroup)) {
00420             KServiceGroup::Ptr g(static_cast<KServiceGroup *>(e));
00421             createMenuAttribute( g );
00422         }
00423     }
00424 }
00425 
00426 
00427 void KBuildSycoca::createMenu(QString caption, QString name, VFolderMenu::SubMenu *menu)
00428 {
00429   for(VFolderMenu::SubMenu *subMenu = menu->subMenus.first(); subMenu; subMenu = menu->subMenus.next())
00430   {
00431      QString subName = name+subMenu->name+"/";
00432 
00433      QString directoryFile = subMenu->directoryFile;
00434      if (directoryFile.isEmpty())
00435         directoryFile = subName+".directory";
00436      Q_UINT32 timeStamp = g_ctimeInfo->ctime(directoryFile);
00437      if (!timeStamp)
00438      {
00439         timeStamp = KGlobal::dirs()->calcResourceHash( g_resource, directoryFile, true);
00440      }
00441 
00442      KServiceGroup* entry = 0;
00443      if (g_allEntries)
00444      {
00445         Q_UINT32 *timeP = (*g_ctimeDict)[directoryFile];
00446         Q_UINT32 oldTimestamp = timeP ? *timeP : 0;
00447 
00448         if (timeStamp && (timeStamp == oldTimestamp))
00449         {
00450             entry = dynamic_cast<KServiceGroup *> (g_serviceGroupEntryDict->find(subName));
00451             if (entry && (entry->directoryEntryPath() != directoryFile))
00452                 entry = 0; // Can't reuse this one!
00453         }
00454      }
00455      g_ctimeInfo->addCTime(directoryFile, timeStamp);
00456 
00457      entry = g_bsgf->addNew(subName, subMenu->directoryFile, entry, subMenu->isDeleted);
00458      entry->setLayoutInfo(subMenu->layoutList);
00459      if (! (bMenuTest && entry->noDisplay()) )
00460         createMenu(caption + entry->caption() + "/", subName, subMenu);
00461   }
00462   if (caption.isEmpty())
00463      caption += "/";
00464   if (name.isEmpty())
00465      name += "/";
00466   for(QDictIterator<KService> it(menu->items); it.current(); ++it)
00467   {
00468      if (bMenuTest)
00469      {
00470         if (!menu->isDeleted && !it.current()->noDisplay())
00471           printf("%s\t%s\t%s\n", caption.local8Bit().data(), it.current()->menuId().local8Bit().data(), locate("apps", it.current()->desktopEntryPath()).local8Bit().data());
00472      }
00473      else
00474      {
00475         g_bsf->addEntry( it.current(), g_resource );
00476         g_bsgf->addNewEntryTo(name, it.current());
00477      }
00478   }
00479 }
00480 
00481 bool KBuildSycoca::recreate()
00482 {
00483   QString path(sycocaPath());
00484 #ifdef Q_WS_WIN
00485   printf("kbuildsycoca: path='%s'\n", (const char*)path);
00486 #endif
00487 
00488   // KSaveFile first writes to a temp file.
00489   // Upon close() it moves the stuff to the right place.
00490   std::auto_ptr<KSaveFile> database( new KSaveFile(path) );
00491   if (database->status() == EACCES && QFile::exists(path))
00492   {
00493     QFile::remove( path );
00494     database.reset( new KSaveFile(path) ); // try again
00495   }
00496   if (database->status() != 0)
00497   {
00498     fprintf(stderr, "kbuildsycoca: ERROR creating database '%s'! %s\n", path.local8Bit().data(),strerror(database->status()));
00499 #ifdef KBUILDSYCOCA_GUI // KBUILDSYCOCA_GUI is used on win32 to build 
00500                         // GUI version of kbuildsycoca, so-called "kbuildsycocaw".
00501     if (!silent)
00502       KMessageBox::error(0, i18n("Error creating database '%1'.\nCheck that the permissions are correct on the directory and the disk is not full.\n").arg(path.local8Bit().data()), i18n("KBuildSycoca"));
00503 #endif
00504     return false;
00505   }
00506 
00507   m_str = database->dataStream();
00508 
00509   kdDebug(7021) << "Recreating ksycoca file (" << path << ", version " << KSycoca::version() << ")" << endl;
00510 
00511   // It is very important to build the servicetype one first
00512   // Both are registered in KSycoca, no need to keep the pointers
00513   KSycocaFactory *stf = new KBuildServiceTypeFactory;
00514   g_bsgf = new KBuildServiceGroupFactory();
00515   g_bsf = new KBuildServiceFactory(stf, g_bsgf);
00516   (void) new KBuildImageIOFactory();
00517   (void) new KBuildProtocolInfoFactory();
00518 
00519   if( build()) // Parse dirs
00520   {
00521     save(); // Save database
00522     if (m_str->device()->status())
00523       database->abort(); // Error
00524     m_str = 0L;
00525     if (!database->close())
00526     {
00527       fprintf(stderr, "kbuildsycoca: ERROR writing database '%s'!\n", database->name().local8Bit().data());
00528       fprintf(stderr, "kbuildsycoca: Disk full?\n");
00529 #ifdef KBUILDSYCOCA_GUI
00530       if (!silent)
00531         KMessageBox::error(0, i18n("Error writing database '%1'.\nCheck that the permissions are correct on the directory and the disk is not full.\n").arg(path.local8Bit().data()), i18n("KBuildSycoca"));
00532 #endif
00533       return false;
00534     }
00535   }
00536   else
00537   {
00538     m_str = 0L;
00539     database->abort();
00540     if (bMenuTest)
00541        return true;
00542     kdDebug(7021) << "Database is up to date" << endl;
00543   }
00544 
00545   if (!bGlobalDatabase)
00546   {
00547     // update the timestamp file
00548     QString stamppath = path + "stamp";
00549     QFile ksycocastamp(stamppath);
00550     ksycocastamp.open( IO_WriteOnly );
00551     QDataStream str( &ksycocastamp );
00552     str << newTimestamp;
00553     str << existingResourceDirs();
00554     if (g_vfolder)
00555         str << g_vfolder->allDirectories(); // Extra resource dirs
00556   }
00557   return true;
00558 }
00559 
00560 void KBuildSycoca::save()
00561 {
00562    // Write header (#pass 1)
00563    m_str->device()->at(0);
00564 
00565    (*m_str) << (Q_INT32) KSycoca::version();
00566    KSycocaFactory * servicetypeFactory = 0L;
00567    KSycocaFactory * serviceFactory = 0L;
00568    for(KSycocaFactory *factory = m_lstFactories->first();
00569        factory;
00570        factory = m_lstFactories->next())
00571    {
00572       Q_INT32 aId;
00573       Q_INT32 aOffset;
00574       aId = factory->factoryId();
00575       if ( aId == KST_KServiceTypeFactory )
00576          servicetypeFactory = factory;
00577       else if ( aId == KST_KServiceFactory )
00578          serviceFactory = factory;
00579       aOffset = factory->offset();
00580       (*m_str) << aId;
00581       (*m_str) << aOffset;
00582    }
00583    (*m_str) << (Q_INT32) 0; // No more factories.
00584    // Write KDEDIRS
00585    (*m_str) << KGlobal::dirs()->kfsstnd_prefixes();
00586    (*m_str) << newTimestamp;
00587    (*m_str) << KGlobal::locale()->language();
00588    (*m_str) << KGlobal::dirs()->calcResourceHash("services", "update_ksycoca", true);
00589    (*m_str) << (*g_allResourceDirs);
00590 
00591    // Write factory data....
00592    for(KSycocaFactory *factory = m_lstFactories->first();
00593        factory;
00594        factory = m_lstFactories->next())
00595    {
00596       factory->save(*m_str);
00597       if (m_str->device()->status())
00598          return; // error
00599    }
00600 
00601    int endOfData = m_str->device()->at();
00602 
00603    // Write header (#pass 2)
00604    m_str->device()->at(0);
00605 
00606    (*m_str) << (Q_INT32) KSycoca::version();
00607    for(KSycocaFactory *factory = m_lstFactories->first();
00608        factory;
00609        factory = m_lstFactories->next())
00610    {
00611       Q_INT32 aId;
00612       Q_INT32 aOffset;
00613       aId = factory->factoryId();
00614       aOffset = factory->offset();
00615       (*m_str) << aId;
00616       (*m_str) << aOffset;
00617    }
00618    (*m_str) << (Q_INT32) 0; // No more factories.
00619 
00620    // Jump to end of database
00621    m_str->device()->at(endOfData);
00622 }
00623 
00624 bool KBuildSycoca::checkDirTimestamps( const QString& dirname, const QDateTime& stamp, bool top )
00625 {
00626    if( top )
00627    {
00628       QFileInfo inf( dirname );
00629       if( inf.lastModified() > stamp )
00630          {
00631          kdDebug( 7021 ) << "timestamp changed:" << dirname << endl;
00632          return false;
00633          }
00634    }
00635    QDir dir( dirname );
00636    const QFileInfoList *list = dir.entryInfoList( QDir::DefaultFilter, QDir::Unsorted );
00637    if (!list)
00638       return true;
00639 
00640    for( QFileInfoListIterator it( *list );
00641         it.current() != NULL;
00642         ++it )
00643    {
00644       QFileInfo* fi = it.current();
00645       if( fi->fileName() == "." || fi->fileName() == ".." )
00646          continue;
00647       if( fi->lastModified() > stamp )
00648       {
00649          kdDebug( 7201 ) << "timestamp changed:" << fi->filePath() << endl;
00650          return false;
00651       }
00652       if( fi->isDir() && !checkDirTimestamps( fi->filePath(), stamp, false ))
00653             return false;
00654    }
00655    return true;
00656 }
00657 
00658 // check times of last modification of all files on which ksycoca depens,
00659 // and also their directories
00660 // if all of them all older than the timestamp in file ksycocastamp, this
00661 // means that there's no need to rebuild ksycoca
00662 bool KBuildSycoca::checkTimestamps( Q_UINT32 timestamp, const QStringList &dirs )
00663 {
00664    kdDebug( 7021 ) << "checking file timestamps" << endl;
00665    QDateTime stamp;
00666    stamp.setTime_t( timestamp );
00667    for( QStringList::ConstIterator it = dirs.begin();
00668         it != dirs.end();
00669         ++it )
00670    {
00671       if( !checkDirTimestamps( *it, stamp, true ))
00672             return false;
00673    }
00674    kdDebug( 7021 ) << "timestamps check ok" << endl;
00675    return true;
00676 }
00677 
00678 QStringList KBuildSycoca::existingResourceDirs()
00679 {
00680    static QStringList* dirs = NULL;
00681    if( dirs != NULL )
00682        return *dirs;
00683    dirs = new QStringList;
00684    g_allResourceDirs = new QStringList;
00685    // these are all resources cached by ksycoca
00686    QStringList resources;
00687    resources += KBuildServiceTypeFactory::resourceTypes();
00688    resources += KBuildServiceGroupFactory::resourceTypes();
00689    resources += KBuildServiceFactory::resourceTypes();
00690    resources += KBuildImageIOFactory::resourceTypes();
00691    resources += KBuildProtocolInfoFactory::resourceTypes();
00692    while( !resources.empty())
00693    {
00694       QString res = resources.front();
00695       *dirs += KGlobal::dirs()->resourceDirs( res.latin1());
00696       resources.remove( res ); // remove this 'res' and all its duplicates
00697    }
00698 
00699    *g_allResourceDirs = *dirs;
00700 
00701    for( QStringList::Iterator it = dirs->begin();
00702         it != dirs->end(); )
00703    {
00704       QFileInfo inf( *it );
00705       if( !inf.exists() || !inf.isReadable() )
00706          it = dirs->remove( it );
00707       else
00708          ++it;
00709    }
00710    return *dirs;
00711 }
00712 
00713 static KCmdLineOptions options[] = {
00714    { "nosignal", I18N_NOOP("Do not signal applications to update"), 0 },
00715    { "noincremental", I18N_NOOP("Disable incremental update, re-read everything"), 0 },
00716    { "checkstamps", I18N_NOOP("Check file timestamps"), 0 },
00717    { "nocheckfiles", I18N_NOOP("Disable checking files (dangerous)"), 0 },
00718    { "global", I18N_NOOP("Create global database"), 0 },
00719    { "menutest", I18N_NOOP("Perform menu generation test run only"), 0 },
00720    { "track <menu-id>", I18N_NOOP("Track menu id for debug purposes"), 0 },
00721 #ifdef KBUILDSYCOCA_GUI
00722    { "silent", I18N_NOOP("Silent - work without windows and stderr"), 0 },
00723    { "showprogress", I18N_NOOP("Show progress information (even if 'silent' mode is on)"), 0 },
00724 #endif
00725    KCmdLineLastOption
00726 };
00727 
00728 static const char appName[] = "kbuildsycoca";
00729 static const char appVersion[] = "1.1";
00730 
00731 class WaitForSignal : public QObject
00732 {
00733 public:
00734    ~WaitForSignal() { kapp->eventLoop()->exitLoop(); }
00735 };
00736 
00737 extern "C" KDE_EXPORT int kdemain(int argc, char **argv)
00738 {
00739    KLocale::setMainCatalogue("kdelibs");
00740    KAboutData d(appName, I18N_NOOP("KBuildSycoca"), appVersion,
00741                 I18N_NOOP("Rebuilds the system configuration cache."),
00742                 KAboutData::License_GPL, "(c) 1999-2002 KDE Developers");
00743    d.addAuthor("David Faure", I18N_NOOP("Author"), "faure@kde.org");
00744    d.addAuthor("Waldo Bastian", I18N_NOOP("Author"), "bastian@kde.org");
00745 
00746    KCmdLineArgs::init(argc, argv, &d);
00747    KCmdLineArgs::addCmdLineOptions(options);
00748    KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
00749    bGlobalDatabase = args->isSet("global");
00750    bMenuTest = args->isSet("menutest");
00751 
00752    if (bGlobalDatabase)
00753    {
00754      setenv("KDEHOME", "-", 1);
00755      setenv("KDEROOTHOME", "-", 1);
00756    }
00757 
00758    KApplication::disableAutoDcopRegistration();
00759 #ifdef KBUILDSYCOCA_GUI
00760    KApplication k;
00761 #else
00762    KApplication k(false, false);
00763 #endif
00764    k.disableSessionManagement();
00765 
00766 #ifdef KBUILDSYCOCA_GUI
00767    silent = args->isSet("silent");
00768    showprogress = args->isSet("showprogress");
00769    QLabel progress( QString("<p><br><nobr>    %1    </nobr><br>").arg( i18n("Reloading KDE configuration, please wait...") ), 0, "", Qt::WType_Dialog | Qt::WStyle_DialogBorder  | Qt::WStyle_Customize| Qt::WStyle_Title );
00770    QString capt = i18n("KDE Configuration Manager");
00771    if (!silent) {
00772      if (KMessageBox::No == KMessageBox::questionYesNo(0, i18n("Do you want to reload KDE configuration?"), capt, i18n("Reload"), i18n("Do Not Reload")))
00773        return 0;
00774    }
00775    if (!silent || showprogress) {
00776      progress.setCaption( capt );
00777      progress.show();
00778    }
00779 #endif
00780 
00781    KCrash::setCrashHandler(KCrash::defaultCrashHandler);
00782    KCrash::setEmergencySaveFunction(crashHandler);
00783    KCrash::setApplicationName(QString(appName));
00784 
00785    // this program is in kdelibs so it uses kdelibs as catalog
00786    KLocale::setMainCatalogue("kdelibs");
00787    // force generating of KLocale object. if not, the database will get
00788    // be translated
00789    KGlobal::locale();
00790    KGlobal::dirs()->addResourceType("app-reg", "share/application-registry" );
00791 
00792    DCOPClient *dcopClient = new DCOPClient();
00793 
00794    while(true)
00795    {
00796      QCString registeredName = dcopClient->registerAs(appName, false);
00797      if (registeredName.isEmpty())
00798      {
00799        fprintf(stderr, "Warning: %s is unable to register with DCOP.\n", appName);
00800        break;
00801      }
00802      else if (registeredName == appName)
00803      {
00804        break; // Go
00805      }
00806      fprintf(stderr, "Waiting for already running %s to finish.\n", appName);
00807 
00808      dcopClient->setNotifications( true );
00809      while (dcopClient->isApplicationRegistered(appName))
00810      {
00811        WaitForSignal *obj = new WaitForSignal;
00812        obj->connect(dcopClient, SIGNAL(applicationRemoved(const QCString &)),
00813                SLOT(deleteLater()));
00814        kapp->eventLoop()->enterLoop();
00815      }
00816      dcopClient->setNotifications( false );
00817    }
00818    fprintf(stderr, "%s running...\n", appName);
00819 
00820    bool checkfiles = bGlobalDatabase || args->isSet("checkfiles");
00821 
00822    bool incremental = !bGlobalDatabase && args->isSet("incremental") && checkfiles;
00823    if (incremental || !checkfiles)
00824    {
00825      KSycoca::self()->disableAutoRebuild(); // Prevent deadlock
00826      QString current_language = KGlobal::locale()->language();
00827      QString ksycoca_language = KSycoca::self()->language();
00828      Q_UINT32 current_update_sig = KGlobal::dirs()->calcResourceHash("services", "update_ksycoca", true);
00829      Q_UINT32 ksycoca_update_sig = KSycoca::self()->updateSignature();
00830 
00831      if ((current_update_sig != ksycoca_update_sig) ||
00832          (current_language != ksycoca_language) ||
00833          (KSycoca::self()->timeStamp() == 0))
00834      {
00835         incremental = false;
00836         checkfiles = true;
00837         delete KSycoca::self();
00838      }
00839    }
00840 
00841    g_changeList = new QStringList;
00842 
00843    bool checkstamps = incremental && args->isSet("checkstamps") && checkfiles;
00844    Q_UINT32 filestamp = 0;
00845    QStringList oldresourcedirs;
00846    if( checkstamps && incremental )
00847    {
00848        QString path = sycocaPath()+"stamp";
00849        QCString qPath = QFile::encodeName(path);
00850        cSycocaPath = qPath.data(); // Delete timestamps on crash
00851        QFile ksycocastamp(path);
00852        if( ksycocastamp.open( IO_ReadOnly ))
00853        {
00854            QDataStream str( &ksycocastamp );
00855            if (!str.atEnd())
00856                str >> filestamp;
00857            if (!str.atEnd())
00858            {
00859                str >> oldresourcedirs;
00860                if( oldresourcedirs != KBuildSycoca::existingResourceDirs())
00861                    checkstamps = false;
00862            }
00863            else
00864            {
00865                checkstamps = false;
00866            }
00867            if (!str.atEnd())
00868            {
00869                QStringList extraResourceDirs;
00870                str >> extraResourceDirs;
00871                oldresourcedirs += extraResourceDirs;
00872            }
00873        }
00874        else
00875        {
00876            checkstamps = false;
00877        }
00878        cSycocaPath = 0;
00879    }
00880 
00881    newTimestamp = (Q_UINT32) time(0);
00882 
00883    if( checkfiles && ( !checkstamps || !KBuildSycoca::checkTimestamps( filestamp, oldresourcedirs )))
00884    {
00885       QCString qSycocaPath = QFile::encodeName(sycocaPath());
00886       cSycocaPath = qSycocaPath.data();
00887 
00888       g_allEntries = 0;
00889       g_ctimeDict = 0;
00890       if (incremental)
00891       {
00892          qWarning("Reusing existing ksycoca");
00893          KSycoca *oldSycoca = KSycoca::self();
00894          KSycocaFactoryList *factories = new KSycocaFactoryList;
00895          g_allEntries = new KSycocaEntryListList;
00896          g_ctimeDict = new QDict<Q_UINT32>(523);
00897 
00898          // Must be in same order as in KBuildSycoca::recreate()!
00899          factories->append( new KServiceTypeFactory );
00900          factories->append( new KServiceGroupFactory );
00901          factories->append( new KServiceFactory );
00902          factories->append( new KImageIOFactory );
00903          factories->append( new KProtocolInfoFactory );
00904 
00905          // For each factory
00906          for (KSycocaFactory *factory = factories->first();
00907               factory;
00908               factory = factories->next() )
00909          {
00910              KSycocaEntry::List list;
00911              list = factory->allEntries();
00912              g_allEntries->append( list );
00913          }
00914          delete factories; factories = 0;
00915          KCTimeInfo *ctimeInfo = new KCTimeInfo;
00916          ctimeInfo->fillCTimeDict(*g_ctimeDict);
00917          delete oldSycoca;
00918       }
00919       cSycocaPath = 0;
00920 
00921       KBuildSycoca *sycoca= new KBuildSycoca; // Build data base
00922       if (args->isSet("track"))
00923          sycoca->setTrackId(QString::fromLocal8Bit(args->getOption("track")));
00924       if (!sycoca->recreate()) {
00925 #ifdef KBUILDSYCOCA_GUI
00926         if (!silent || showprogress)
00927           progress.close();
00928 #endif
00929         return -1;
00930       }
00931 
00932       if (bGlobalDatabase)
00933       {
00934         // These directories may have been created with 0700 permission
00935         // better delete them if they are empty
00936         QString applnkDir = KGlobal::dirs()->saveLocation("apps", QString::null, false);
00937         ::rmdir(QFile::encodeName(applnkDir));
00938         QString servicetypesDir = KGlobal::dirs()->saveLocation("servicetypes", QString::null, false);
00939         ::rmdir(QFile::encodeName(servicetypesDir));
00940       }
00941    }
00942 
00943    if (!bGlobalDatabase)
00944    {
00945      // Recreate compatibility symlink
00946      QString oldPath = oldSycocaPath();
00947      if (!oldPath.isEmpty())
00948      {
00949        KTempFile tmp;
00950        if (tmp.status() == 0)
00951        {
00952          QString tmpFile = tmp.name();
00953          tmp.unlink();
00954          symlink(QFile::encodeName(sycocaPath()), QFile::encodeName(tmpFile));
00955          rename(QFile::encodeName(tmpFile), QFile::encodeName(oldPath));
00956        }
00957      }
00958    }
00959 
00960    if (args->isSet("signal"))
00961    {
00962      // Notify ALL applications that have a ksycoca object, using a broadcast
00963      QByteArray data;
00964      QDataStream stream(data, IO_WriteOnly);
00965      stream << *g_changeList;
00966      dcopClient->send( "*", "ksycoca", "notifyDatabaseChanged(QStringList)", data );
00967    }
00968 
00969 #ifdef KBUILDSYCOCA_GUI
00970    if (!silent) {
00971      progress.close();
00972      KMessageBox::information(0, i18n("Configuration information reloaded successfully."), capt);
00973    }
00974 #endif
00975    return 0;
00976 }
00977 
00978 #include "kbuildsycoca.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys