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

KDED

  • sources
  • kde-4.14
  • kdelibs
  • kded
kbuildsycoca.cpp
Go to the documentation of this file.
1 // -*- c-basic-offset: 3 -*-
2 /* This file is part of the KDE libraries
3  * Copyright (C) 1999 David Faure <faure@kde.org>
4  * Copyright (C) 2002-2003 Waldo Bastian <bastian@kde.org>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Library General Public
8  * License version 2 as published by the Free Software Foundation;
9  *
10  * This library is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13  * Library General Public License for more details.
14  *
15  * You should have received a copy of the GNU Library General Public License
16  * along with this library; see the file COPYING.LIB. If not, write to
17  * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
18  * Boston, MA 02110-1301, USA.
19  **/
20 
21 #include "kbuildsycoca.h"
22 #include "ksycoca_p.h"
23 #include "ksycocaresourcelist.h"
24 #include "vfolder_menu.h"
25 
26 #include <config.h>
27 #include <config-kded.h>
28 
29 #include <kservice.h>
30 #include <kmimetype.h>
31 #include "kbuildservicetypefactory.h"
32 #include "kbuildmimetypefactory.h"
33 #include "kbuildservicefactory.h"
34 #include "kbuildservicegroupfactory.h"
35 #include "kbuildprotocolinfofactory.h"
36 #include "kctimefactory.h"
37 #include <ktemporaryfile.h>
38 #include <QtCore/QDataStream>
39 #include <QtCore/QDir>
40 #include <QtCore/QEventLoop>
41 #include <QtCore/QFile>
42 #include <QtCore/QTimer>
43 #include <QtDBus/QtDBus>
44 #include <errno.h>
45 
46 #include <assert.h>
47 #include <kapplication.h>
48 #include <kglobal.h>
49 #include <kdebug.h>
50 #include <kdirwatch.h>
51 #include <kstandarddirs.h>
52 #include <ksavefile.h>
53 #include <klocale.h>
54 #include <kaboutdata.h>
55 #include <kcmdlineargs.h>
56 #ifndef KBUILDSYCOCA_NO_KCRASH
57 #include <kcrash.h>
58 #endif
59 #include <kmemfile.h>
60 
61 #include <stdlib.h>
62 #include <unistd.h>
63 #include <time.h>
64 #include <memory> // auto_ptr
65 
66 typedef QHash<QString, KSycocaEntry::Ptr> KBSEntryDict;
67 typedef QList<KSycocaEntry::List> KSycocaEntryListList;
68 
69 static quint32 newTimestamp = 0;
70 
71 static KBuildServiceFactory *g_serviceFactory = 0;
72 static KBuildServiceGroupFactory *g_buildServiceGroupFactory = 0;
73 static KSycocaFactory *g_currentFactory = 0;
74 static KCTimeInfo *g_ctimeInfo = 0; // factory
75 static KCTimeDict *g_ctimeDict = 0; // old timestamps
76 static QByteArray g_resource = 0;
77 static KBSEntryDict *g_currentEntryDict = 0;
78 static KBSEntryDict *g_serviceGroupEntryDict = 0;
79 static KSycocaEntryListList *g_allEntries = 0; // entries from existing ksycoca
80 static QStringList *g_allResourceDirs = 0;
81 static bool g_changed = false;
82 static KSycocaEntry::List g_tempStorage;
83 static VFolderMenu *g_vfolder = 0;
84 
85 static const char *cSycocaPath = 0;
86 
87 static bool bGlobalDatabase = false;
88 static bool bMenuTest = false;
89 
90 void crashHandler(int)
91 {
92  // If we crash while reading sycoca, we delete the database
93  // in an attempt to recover.
94  if (cSycocaPath)
95  unlink(cSycocaPath);
96 }
97 
98 static QString sycocaPath()
99 {
100  return KSycoca::absoluteFilePath(bGlobalDatabase ? KSycoca::GlobalDatabase : KSycoca::LocalDatabase);
101 }
102 
103 KBuildSycoca::KBuildSycoca()
104  : KSycoca( true )
105 {
106 }
107 
108 KBuildSycoca::~KBuildSycoca()
109 {
110 
111 }
112 
113 KSycocaEntry::Ptr KBuildSycoca::createEntry(const QString &file, bool addToFactory)
114 {
115  quint32 timeStamp = g_ctimeInfo->dict()->ctime(file, g_resource);
116  if (!timeStamp)
117  {
118  timeStamp = KGlobal::dirs()->calcResourceHash( g_resource, file,
119  KStandardDirs::Recursive);
120  }
121  KSycocaEntry::Ptr entry;
122  if (g_allEntries)
123  {
124  assert(g_ctimeDict);
125  quint32 oldTimestamp = g_ctimeDict->ctime(file, g_resource);
126 
127  if (timeStamp && (timeStamp == oldTimestamp))
128  {
129  // Re-use old entry
130  if (g_currentFactory == g_buildServiceGroupFactory) // Strip .directory from service-group entries
131  {
132  entry = g_currentEntryDict->value(file.left(file.length()-10));
133  } else {
134  entry = g_currentEntryDict->value(file);
135  }
136  // remove from g_ctimeDict; if g_ctimeDict is not empty
137  // after all files have been processed, it means
138  // some files were removed since last time
139  if (file.contains("fake"))
140  kDebug(7021) << g_resource << "reusing (and removing) old entry [\"fake\"] for:" << file;
141  g_ctimeDict->remove(file, g_resource);
142  }
143  else if (oldTimestamp)
144  {
145  g_changed = true;
146  g_ctimeDict->remove(file, g_resource);
147  kDebug(7021) << "modified:" << file << "in" << g_resource.constData();
148  }
149  else
150  {
151  g_changed = true;
152  kDebug(7021) << "new:" << file << "in" << g_resource.constData();
153  }
154  }
155  g_ctimeInfo->dict()->addCTime(file, g_resource, timeStamp);
156  if (!entry)
157  {
158  // Create a new entry
159  entry = g_currentFactory->createEntry( file, g_resource );
160  }
161  if ( entry && entry->isValid() )
162  {
163  if (addToFactory)
164  g_currentFactory->addEntry(entry);
165  else
166  g_tempStorage.append(entry);
167  return entry;
168  }
169  return KSycocaEntry::Ptr();
170 }
171 
172 KService::Ptr KBuildSycoca::createService(const QString &path)
173 {
174  KSycocaEntry::Ptr entry = createEntry(path, false);
175  return KService::Ptr::staticCast(entry);
176 }
177 
178 // returns false if the database is up to date, true if it needs to be saved
179 bool KBuildSycoca::build()
180 {
181  typedef QLinkedList<KBSEntryDict *> KBSEntryDictList;
182  KBSEntryDictList entryDictList;
183  KBSEntryDict *serviceEntryDict = 0;
184 
185  // Convert for each factory the entryList to a Dict.
186  int i = 0;
187  // For each factory
188  for (KSycocaFactoryList::Iterator factory = factories()->begin();
189  factory != factories()->end();
190  ++factory)
191  {
192  KBSEntryDict *entryDict = new KBSEntryDict;
193  if (g_allEntries)
194  {
195  const KSycocaEntry::List list = (*g_allEntries)[i++];
196  for( KSycocaEntry::List::const_iterator it = list.begin();
197  it != list.end();
198  ++it)
199  {
200  entryDict->insert( (*it)->entryPath(), *it );
201  }
202  }
203  if ((*factory) == g_serviceFactory)
204  serviceEntryDict = entryDict;
205  else if ((*factory) == g_buildServiceGroupFactory)
206  g_serviceGroupEntryDict = entryDict;
207  entryDictList.append(entryDict);
208  }
209 
210  QStringList allResources; // we could use QSet<QString> - does order matter?
211  // For each factory
212  for (KSycocaFactoryList::Iterator factory = factories()->begin();
213  factory != factories()->end();
214  ++factory)
215  {
216  // For each resource the factory deals with
217  const KSycocaResourceList *list = (*factory)->resourceList();
218  if (!list) continue;
219 
220  for( KSycocaResourceList::ConstIterator it1 = list->constBegin();
221  it1 != list->constEnd();
222  ++it1 )
223  {
224  KSycocaResource res = (*it1);
225  if (!allResources.contains(res.resource))
226  allResources.append(res.resource);
227  }
228  }
229 
230  g_ctimeInfo = new KCTimeInfo(); // This is a build factory too, don't delete!!
231  bool uptodate = true;
232  // For all resources
233  for( QStringList::ConstIterator it1 = allResources.constBegin();
234  it1 != allResources.constEnd();
235  ++it1 )
236  {
237  g_changed = false;
238  g_resource = (*it1).toLatin1();
239 
240  QStringList relFiles;
241 
242  (void) KGlobal::dirs()->findAllResources( g_resource,
243  QString(),
244  KStandardDirs::Recursive |
245  KStandardDirs::NoDuplicates,
246  relFiles);
247 
248 
249  // Now find all factories that use this resource....
250  // For each factory
251  KBSEntryDictList::const_iterator ed_it = entryDictList.begin();
252  const KBSEntryDictList::const_iterator ed_end = entryDictList.end();
253  KSycocaFactoryList::const_iterator it = factories()->constBegin();
254  const KSycocaFactoryList::const_iterator end = factories()->constEnd();
255  for ( ; it != end; ++it, ++ed_it )
256  {
257  g_currentFactory = (*it);
258  // g_ctimeInfo gets created after the initial loop, so it has no entryDict.
259  g_currentEntryDict = ed_it == ed_end ? 0 : *ed_it;
260  // For each resource the factory deals with
261  const KSycocaResourceList *list = g_currentFactory->resourceList();
262  if (!list) continue;
263 
264  for( KSycocaResourceList::ConstIterator it2 = list->constBegin();
265  it2 != list->constEnd();
266  ++it2 )
267  {
268  KSycocaResource res = (*it2);
269  if (res.resource != (*it1)) continue;
270 
271  // For each file in the resource
272  for( QStringList::ConstIterator it3 = relFiles.constBegin();
273  it3 != relFiles.constEnd();
274  ++it3 )
275  {
276  // Check if file matches filter
277  if ((*it3).endsWith(res.extension))
278  createEntry(*it3, true);
279  }
280  }
281  }
282  if (g_changed || !g_allEntries)
283  {
284  uptodate = false;
285  //kDebug() << "CHANGED:" << g_resource;
286  m_changedResources.append(g_resource);
287  }
288  }
289 
290  bool result = !uptodate || (g_ctimeDict && !g_ctimeDict->isEmpty());
291  if (g_ctimeDict && !g_ctimeDict->isEmpty()) {
292  //kDebug() << "Still in time dict:";
293  //g_ctimeDict->dump();
294  // ## It seems entries filtered out by vfolder are still in there,
295  // so we end up always saving ksycoca, i.e. this method never returns false
296 
297  // Get the list of resources from which some files were deleted
298  const QStringList resources = g_ctimeDict->resourceList();
299  kDebug() << "Still in the time dict (i.e. deleted files)" << resources;
300  m_changedResources += resources;
301  }
302 
303  if (result || bMenuTest)
304  {
305  g_resource = "apps";
306  g_currentFactory = g_serviceFactory;
307  g_currentEntryDict = serviceEntryDict;
308  g_changed = false;
309 
310  g_vfolder = new VFolderMenu(g_serviceFactory, this);
311  if (!m_trackId.isEmpty())
312  g_vfolder->setTrackId(m_trackId);
313 
314  VFolderMenu::SubMenu *kdeMenu = g_vfolder->parseMenu("applications.menu", true);
315 
316  KServiceGroup::Ptr entry = g_buildServiceGroupFactory->addNew("/", kdeMenu->directoryFile, KServiceGroup::Ptr(), false);
317  entry->setLayoutInfo(kdeMenu->layoutList);
318  createMenu(QString(), QString(), kdeMenu);
319 
320  (void) existingResourceDirs();
321  *g_allResourceDirs += g_vfolder->allDirectories();
322 
323  if (g_changed || !g_allEntries)
324  {
325  uptodate = false;
326  //kDebug() << "CHANGED:" << g_resource;
327  m_changedResources.append(g_resource);
328  }
329  if (bMenuTest) {
330  result = false;
331  }
332  }
333 
334  qDeleteAll(entryDictList);
335  return result;
336 }
337 
338 void KBuildSycoca::createMenu(const QString &caption_, const QString &name_, VFolderMenu::SubMenu *menu)
339 {
340  QString caption = caption_;
341  QString name = name_;
342  foreach (VFolderMenu::SubMenu *subMenu, menu->subMenus)
343  {
344  QString subName = name+subMenu->name+'/';
345 
346  QString directoryFile = subMenu->directoryFile;
347  if (directoryFile.isEmpty())
348  directoryFile = subName+".directory";
349  quint32 timeStamp = g_ctimeInfo->dict()->ctime(directoryFile, g_resource);
350  if (!timeStamp) {
351  timeStamp = KGlobal::dirs()->calcResourceHash( g_resource, directoryFile,
352  KStandardDirs::Recursive );
353  }
354 
355  KServiceGroup::Ptr entry;
356  if (g_allEntries)
357  {
358  const quint32 oldTimestamp = g_ctimeDict->ctime(directoryFile, g_resource);
359 
360  if (timeStamp && (timeStamp == oldTimestamp))
361  {
362  KSycocaEntry::Ptr group = g_serviceGroupEntryDict->value(subName);
363  if ( group )
364  {
365  entry = KServiceGroup::Ptr::staticCast( group );
366  if (entry->directoryEntryPath() != directoryFile)
367  entry = 0; // Can't reuse this one!
368  }
369  }
370  }
371  g_ctimeInfo->dict()->addCTime(directoryFile, g_resource, timeStamp);
372 
373  entry = g_buildServiceGroupFactory->addNew(subName, subMenu->directoryFile, entry, subMenu->isDeleted);
374  entry->setLayoutInfo(subMenu->layoutList);
375  if (! (bMenuTest && entry->noDisplay()) )
376  createMenu(caption + entry->caption() + '/', subName, subMenu);
377  }
378  if (caption.isEmpty())
379  caption += '/';
380  if (name.isEmpty())
381  name += '/';
382  foreach (const KService::Ptr &p, menu->items)
383  {
384  if (bMenuTest)
385  {
386  if (!menu->isDeleted && !p->noDisplay())
387  printf("%s\t%s\t%s\n", qPrintable( caption ), qPrintable( p->menuId() ), qPrintable( KStandardDirs::locate("apps", p->entryPath() ) ) );
388  }
389  else
390  {
391  g_buildServiceGroupFactory->addNewEntryTo( name, p );
392  }
393  }
394 }
395 
396 bool KBuildSycoca::recreate()
397 {
398  QString path(sycocaPath());
399 
400  // KSaveFile first writes to a temp file.
401  // Upon finalize() it moves the stuff to the right place.
402  KSaveFile database(path);
403  bool openedOK = database.open();
404  if (!openedOK && database.error() == QFile::PermissionsError && QFile::exists(path))
405  {
406  QFile::remove( path );
407  openedOK = database.open();
408  }
409  if (!openedOK)
410  {
411  fprintf(stderr, "kbuildsycoca4: ERROR creating database '%s'! %s\n",
412  path.toLocal8Bit().data(), database.errorString().toLocal8Bit().data());
413  return false;
414  }
415 
416  QDataStream* str = new QDataStream(&database);
417  str->setVersion(QDataStream::Qt_3_1);
418 
419  kDebug(7021).nospace() << "Recreating ksycoca file (" << path << ", version " << KSycoca::version() << ")";
420 
421  // It is very important to build the servicetype one first
422  // Both are registered in KSycoca, no need to keep the pointers
423  KSycocaFactory *stf = new KBuildServiceTypeFactory;
424  KBuildMimeTypeFactory* mimeTypeFactory = new KBuildMimeTypeFactory;
425  g_buildServiceGroupFactory = new KBuildServiceGroupFactory();
426  g_serviceFactory = new KBuildServiceFactory(stf, mimeTypeFactory, g_buildServiceGroupFactory);
427  (void) new KBuildProtocolInfoFactory();
428 
429  if( build()) // Parse dirs
430  {
431  save(str); // Save database
432  if (str->status() != QDataStream::Ok) // ######## TODO: does this detect write errors, e.g. disk full?
433  database.abort(); // Error
434  delete str;
435  str = 0;
436  if (!database.finalize())
437  {
438  fprintf(stderr, "kbuildsycoca4: ERROR writing database '%s'!\n", database.fileName().toLocal8Bit().data());
439  fprintf(stderr, "kbuildsycoca4: Disk full?\n");
440  return false;
441  }
442  }
443  else
444  {
445  delete str;
446  str = 0;
447  database.abort();
448  if (bMenuTest)
449  return true;
450  kDebug(7021) << "Database is up to date";
451  }
452 
453  if (!bGlobalDatabase)
454  {
455  // update the timestamp file
456  QString stamppath = path + "stamp";
457  QFile ksycocastamp(stamppath);
458  ksycocastamp.open( QIODevice::WriteOnly );
459  QDataStream str( &ksycocastamp );
460  str.setVersion(QDataStream::Qt_3_1);
461  str << newTimestamp;
462  str << existingResourceDirs();
463  if (g_vfolder)
464  str << g_vfolder->allDirectories(); // Extra resource dirs
465  }
466  if (d->m_sycocaStrategy == KSycocaPrivate::StrategyMemFile)
467  KMemFile::fileContentsChanged(path);
468 
469  return true;
470 }
471 
472 void KBuildSycoca::save(QDataStream* str)
473 {
474  // Write header (#pass 1)
475  str->device()->seek(0);
476 
477  (*str) << (qint32) KSycoca::version();
478  KSycocaFactory * servicetypeFactory = 0;
479  KBuildMimeTypeFactory * mimeTypeFactory = 0;
480  KBuildServiceFactory * serviceFactory = 0;
481  for(KSycocaFactoryList::Iterator factory = factories()->begin();
482  factory != factories()->end();
483  ++factory)
484  {
485  qint32 aId;
486  qint32 aOffset;
487  aId = (*factory)->factoryId();
488  if ( aId == KST_KServiceTypeFactory )
489  servicetypeFactory = *factory;
490  else if ( aId == KST_KMimeTypeFactory )
491  mimeTypeFactory = static_cast<KBuildMimeTypeFactory *>( *factory );
492  else if ( aId == KST_KServiceFactory )
493  serviceFactory = static_cast<KBuildServiceFactory *>( *factory );
494  aOffset = (*factory)->offset(); // not set yet, so always 0
495  (*str) << aId;
496  (*str) << aOffset;
497  }
498  (*str) << (qint32) 0; // No more factories.
499  // Write KDEDIRS
500  (*str) << KGlobal::dirs()->kfsstnd_prefixes();
501  (*str) << newTimestamp;
502  (*str) << KGlobal::locale()->language();
503  (*str) << KGlobal::dirs()->calcResourceHash("services", "update_ksycoca",
504  KStandardDirs::Recursive );
505  (*str) << (*g_allResourceDirs);
506 
507  // Calculate per-servicetype/mimetype data
508  serviceFactory->postProcessServices();
509 
510  // Here so that it's the last debug message
511  kDebug(7021) << "Saving";
512 
513  // Write factory data....
514  for(KSycocaFactoryList::Iterator factory = factories()->begin();
515  factory != factories()->end();
516  ++factory)
517  {
518  (*factory)->save(*str);
519  if (str->status() != QDataStream::Ok) // ######## TODO: does this detect write errors, e.g. disk full?
520  return; // error
521  }
522 
523  int endOfData = str->device()->pos();
524 
525  // Write header (#pass 2)
526  str->device()->seek(0);
527 
528  (*str) << (qint32) KSycoca::version();
529  for(KSycocaFactoryList::Iterator factory = factories()->begin();
530  factory != factories()->end(); ++factory)
531  {
532  qint32 aId;
533  qint32 aOffset;
534  aId = (*factory)->factoryId();
535  aOffset = (*factory)->offset();
536  (*str) << aId;
537  (*str) << aOffset;
538  }
539  (*str) << (qint32) 0; // No more factories.
540 
541  // Jump to end of database
542  str->device()->seek(endOfData);
543 }
544 
545 bool KBuildSycoca::checkDirTimestamps( const QString& dirname, const QDateTime& stamp, bool top )
546 {
547  if( top )
548  {
549  QFileInfo inf( dirname );
550  if( inf.lastModified() > stamp ) {
551  kDebug( 7021 ) << "timestamp changed:" << dirname;
552  return false;
553  }
554  }
555  QDir dir( dirname );
556  const QFileInfoList list = dir.entryInfoList( QDir::NoFilter, QDir::Unsorted );
557  if (list.isEmpty())
558  return true;
559 
560  foreach ( const QFileInfo& fi, list ) {
561  if( fi.fileName() == "." || fi.fileName() == ".." )
562  continue;
563  if( fi.lastModified() > stamp )
564  {
565  kDebug( 7201 ) << "timestamp changed:" << fi.filePath();
566  return false;
567  }
568  if( fi.isDir() && !checkDirTimestamps( fi.filePath(), stamp, false ))
569  return false;
570  }
571  return true;
572 }
573 
574 // check times of last modification of all files on which ksycoca depens,
575 // and also their directories
576 // if all of them are older than the timestamp in file ksycocastamp, this
577 // means that there's no need to rebuild ksycoca
578 bool KBuildSycoca::checkTimestamps( quint32 timestamp, const QStringList &dirs )
579 {
580  kDebug( 7021 ) << "checking file timestamps";
581  QDateTime stamp;
582  stamp.setTime_t( timestamp );
583  for( QStringList::ConstIterator it = dirs.begin();
584  it != dirs.end();
585  ++it )
586  {
587  if( !checkDirTimestamps( *it, stamp, true ))
588  return false;
589  }
590  kDebug( 7021 ) << "timestamps check ok";
591  return true;
592 }
593 
594 QStringList KBuildSycoca::existingResourceDirs()
595 {
596  static QStringList* dirs = NULL;
597  if( dirs != NULL )
598  return *dirs;
599  dirs = new QStringList;
600  g_allResourceDirs = new QStringList;
601  // these are all resources cached by ksycoca
602  QStringList resources;
603  resources += KBuildServiceTypeFactory::resourceTypes();
604  resources += KBuildMimeTypeFactory::resourceTypes();
605  resources += KBuildServiceGroupFactory::resourceTypes();
606  resources += KBuildServiceFactory::resourceTypes();
607  resources += KBuildProtocolInfoFactory::resourceTypes();
608  while( !resources.empty())
609  {
610  QString res = resources.front();
611  *dirs += KGlobal::dirs()->resourceDirs( res.toLatin1());
612  resources.removeAll( res );
613  }
614 
615  *g_allResourceDirs = *dirs;
616 
617  for( QStringList::Iterator it = dirs->begin();
618  it != dirs->end(); )
619  {
620  QFileInfo inf( *it );
621  if( !inf.exists() || !inf.isReadable() )
622  it = dirs->erase( it );
623  else
624  ++it;
625  }
626  return *dirs;
627 }
628 
629 static const char appFullName[] = "org.kde.kbuildsycoca";
630 static const char appName[] = "kbuildsycoca4";
631 static const char appVersion[] = "1.1";
632 
633 extern "C" KDE_EXPORT int kdemain(int argc, char **argv)
634 {
635  KAboutData d(appName, "kdelibs4", ki18n("KBuildSycoca"), appVersion,
636  ki18n("Rebuilds the system configuration cache."),
637  KAboutData::License_GPL, ki18n("(c) 1999-2002 KDE Developers"));
638  d.addAuthor(ki18n("David Faure"), ki18n("Author"), "faure@kde.org");
639  d.addAuthor(ki18n("Waldo Bastian"), ki18n("Author"), "bastian@kde.org");
640 
641  KCmdLineOptions options;
642  options.add("nosignal", ki18n("Do not signal applications to update"));
643  options.add("noincremental", ki18n("Disable incremental update, re-read everything"));
644  options.add("checkstamps", ki18n("Check file timestamps"));
645  options.add("nocheckfiles", ki18n("Disable checking files (dangerous)"));
646  options.add("global", ki18n("Create global database"));
647  options.add("menutest", ki18n("Perform menu generation test run only"));
648  options.add("track <menu-id>", ki18n("Track menu id for debug purposes"));
649 
650  KCmdLineArgs::init(argc, argv, &d);
651  KCmdLineArgs::addCmdLineOptions(options);
652  KCmdLineArgs *args = KCmdLineArgs::parsedArgs();
653  bGlobalDatabase = args->isSet("global");
654  bMenuTest = args->isSet("menutest");
655 
656  if (bGlobalDatabase)
657  {
658  setenv("KDEHOME", "-", 1);
659  setenv("KDEROOTHOME", "-", 1);
660  }
661 
662  QCoreApplication k(argc, argv);
663  KComponentData mainComponent(d);
664 
665 #ifndef KBUILDSYCOCA_NO_KCRASH
666  KCrash::setCrashHandler(KCrash::defaultCrashHandler);
667  KCrash::setEmergencySaveFunction(crashHandler);
668  KCrash::setApplicationName(QString(appName));
669 #endif
670 
671  // force generating of KLocale object. if not, the database will get
672  // be translated
673  KGlobal::locale();
674  KGlobal::dirs()->addResourceType("app-reg", 0, "share/application-registry" );
675 
676  while(QDBusConnection::sessionBus().isConnected())
677  {
678  // kapp registered already, but with the PID in the name.
679  // We need to re-register without it, to detect already-running kbuildsycoca instances.
680  if (QDBusConnection::sessionBus().interface()->registerService(appFullName, QDBusConnectionInterface::QueueService)
681  != QDBusConnectionInterface::ServiceQueued)
682  {
683  break; // Go
684  }
685  fprintf(stderr, "Waiting for already running %s to finish.\n", appName);
686 
687  QEventLoop eventLoop;
688  QObject::connect(QDBusConnection::sessionBus().interface(), SIGNAL(serviceRegistered(QString)),
689  &eventLoop, SLOT(quit()));
690  eventLoop.exec( QEventLoop::ExcludeUserInputEvents );
691  }
692  fprintf(stderr, "%s running...\n", appName);
693 
694  bool checkfiles = bGlobalDatabase || args->isSet("checkfiles");
695 
696  bool incremental = !bGlobalDatabase && args->isSet("incremental") && checkfiles;
697  if (incremental || !checkfiles)
698  {
699  KSycoca::disableAutoRebuild(); // Prevent deadlock
700  QString current_language = KGlobal::locale()->language();
701  QString ksycoca_language = KSycoca::self()->language();
702  quint32 current_update_sig = KGlobal::dirs()->calcResourceHash("services", "update_ksycoca",
703  KStandardDirs::Recursive );
704  quint32 ksycoca_update_sig = KSycoca::self()->updateSignature();
705  QString current_prefixes = KGlobal::dirs()->kfsstnd_prefixes();
706  QString ksycoca_prefixes = KSycoca::self()->kfsstnd_prefixes();
707 
708  if ((current_update_sig != ksycoca_update_sig) ||
709  (current_language != ksycoca_language) ||
710  (current_prefixes != ksycoca_prefixes) ||
711  (KSycoca::self()->timeStamp() == 0))
712  {
713  incremental = false;
714  checkfiles = true;
715  KBuildSycoca::clearCaches();
716  }
717  }
718 
719  bool checkstamps = incremental && args->isSet("checkstamps") && checkfiles;
720  quint32 filestamp = 0;
721  QStringList oldresourcedirs;
722  if( checkstamps && incremental )
723  {
724  QString path = sycocaPath()+"stamp";
725  QByteArray qPath = QFile::encodeName(path);
726  cSycocaPath = qPath.data(); // Delete timestamps on crash
727  QFile ksycocastamp(path);
728  if( ksycocastamp.open( QIODevice::ReadOnly ))
729  {
730  QDataStream str( &ksycocastamp );
731  str.setVersion(QDataStream::Qt_3_1);
732 
733  if (!str.atEnd())
734  str >> filestamp;
735  if (!str.atEnd())
736  {
737  str >> oldresourcedirs;
738  if( oldresourcedirs != KBuildSycoca::existingResourceDirs())
739  checkstamps = false;
740  }
741  else
742  {
743  checkstamps = false;
744  }
745  if (!str.atEnd())
746  {
747  QStringList extraResourceDirs;
748  str >> extraResourceDirs;
749  oldresourcedirs += extraResourceDirs;
750  }
751  }
752  else
753  {
754  checkstamps = false;
755  }
756  cSycocaPath = 0;
757  }
758 
759  newTimestamp = (quint32) time(0);
760  QStringList changedResources;
761 
762  if( checkfiles && ( !checkstamps || !KBuildSycoca::checkTimestamps( filestamp, oldresourcedirs )))
763  {
764  QByteArray qSycocaPath = QFile::encodeName(sycocaPath());
765  cSycocaPath = qSycocaPath.data();
766 
767  g_allEntries = 0;
768  g_ctimeDict = 0;
769  if (incremental)
770  {
771  kDebug(7021) << "Reusing existing ksycoca";
772  KSycoca::self();
773  KSycocaFactoryList *factories = new KSycocaFactoryList;
774  g_allEntries = new KSycocaEntryListList;
775  g_ctimeDict = new KCTimeDict;
776 
777  // Must be in same order as in KBuildSycoca::recreate()!
778  factories->append( new KServiceTypeFactory );
779  factories->append( new KMimeTypeFactory );
780  factories->append( new KServiceGroupFactory );
781  factories->append( new KServiceFactory );
782  factories->append( new KProtocolInfoFactory );
783 
784  // For each factory
785  for (KSycocaFactoryList::Iterator factory = factories->begin();
786  factory != factories->end(); ++factory)
787  {
788  const KSycocaEntry::List list = (*factory)->allEntries();
789  g_allEntries->append( list );
790  }
791  delete factories; factories = 0;
792  KCTimeInfo *ctimeInfo = new KCTimeInfo;
793  *g_ctimeDict = ctimeInfo->loadDict();
794  }
795  cSycocaPath = 0;
796 
797  KBuildSycoca *sycoca = new KBuildSycoca; // Build data base (deletes oldSycoca)
798  if (args->isSet("track"))
799  sycoca->setTrackId(args->getOption("track"));
800  if (!sycoca->recreate()) {
801  return -1;
802  }
803  changedResources = sycoca->changedResources();
804 
805  if (bGlobalDatabase)
806  {
807  // These directories may have been created with 0700 permission
808  // better delete them if they are empty
809  QString applnkDir = KGlobal::dirs()->saveLocation("apps", QString(), false);
810  ::rmdir(QFile::encodeName(applnkDir));
811  QString servicetypesDir = KGlobal::dirs()->saveLocation("servicetypes", QString(), false);
812  ::rmdir(QFile::encodeName(servicetypesDir));
813  }
814  }
815 
816  if (args->isSet("signal"))
817  {
818  // Notify ALL applications that have a ksycoca object, using a signal
819  QDBusMessage signal = QDBusMessage::createSignal("/", "org.kde.KSycoca", "notifyDatabaseChanged" );
820  signal << changedResources;
821 
822  if (QDBusConnection::sessionBus().isConnected()) {
823  kDebug() << "Emitting notifyDatabaseChanged" << changedResources;
824  QDBusConnection::sessionBus().send(signal);
825  qApp->processEvents(); // make sure the dbus signal is sent before we quit.
826  }
827  }
828 
829  return 0;
830 }
831 
832 #include "kbuildsycoca.moc"
KStandardDirs::saveLocation
QString saveLocation(const char *type, const QString &suffix=QString(), bool create=true) const
caption
QString caption()
KSycocaFactoryList
QFileInfo::isReadable
bool isReadable() const
VFolderMenu::SubMenu::layoutList
QStringList layoutList
Definition: vfolder_menu.h:54
KSharedPtr< KService >
VFolderMenu::SubMenu::isDeleted
bool isDeleted
Definition: vfolder_menu.h:53
KCmdLineArgs::addCmdLineOptions
static void addCmdLineOptions(const KCmdLineOptions &options, const KLocalizedString &name=KLocalizedString(), const QByteArray &id=QByteArray(), const QByteArray &afterId=QByteArray())
KCTimeDict::resourceList
QStringList resourceList() const
Definition: kctimefactory.cpp:52
KSycoca::factories
KSycocaFactoryList * factories()
appVersion
static const char appVersion[]
Definition: kbuildsycoca.cpp:631
QHash::insert
iterator insert(const Key &key, const T &value)
g_currentFactory
static KSycocaFactory * g_currentFactory
Definition: kbuildsycoca.cpp:73
KBuildSycoca::existingResourceDirs
static QStringList existingResourceDirs()
Definition: kbuildsycoca.cpp:594
KBuildSycoca::recreate
bool recreate()
Recreate the database file.
Definition: kbuildsycoca.cpp:396
kbuildservicegroupfactory.h
KService::noDisplay
bool noDisplay() const
KAboutData::addAuthor
KAboutData & addAuthor(const KLocalizedString &name, const KLocalizedString &task=KLocalizedString(), const QByteArray &emailAddress=QByteArray(), const QByteArray &webAddress=QByteArray())
cSycocaPath
static const char * cSycocaPath
Definition: kbuildsycoca.cpp:85
QEventLoop
KSaveFile::error
QFile::FileError error() const
VFolderMenu::SubMenu
Definition: vfolder_menu.h:40
kdebug.h
kmimetype.h
kapplication.h
KStandardDirs::addResourceType
bool addResourceType(const char *type, const QString &relativename, bool priority=true)
KCmdLineOptions::add
KCmdLineOptions & add(const QByteArray &name, const KLocalizedString &description=KLocalizedString(), const QByteArray &defaultValue=QByteArray())
QIODevice::seek
virtual bool seek(qint64 pos)
QByteArray
g_ctimeDict
static KCTimeDict * g_ctimeDict
Definition: kbuildsycoca.cpp:75
group
appName
static const char appName[]
Definition: kbuildsycoca.cpp:630
QFile::remove
bool remove()
kdirwatch.h
KSycoca
ki18n
KLocalizedString ki18n(const char *msg)
QDataStream
kbuildprotocolinfofactory.h
g_vfolder
static VFolderMenu * g_vfolder
Definition: kbuildsycoca.cpp:83
KCmdLineArgs::parsedArgs
static KCmdLineArgs * parsedArgs(const QByteArray &id=QByteArray())
QCoreApplication
KStandardDirs::locate
static QString locate(const char *type, const QString &filename, const KComponentData &cData=KGlobal::mainComponent())
QLinkedList< KSycocaResource >::ConstIterator
typedef ConstIterator
ksavefile.h
KBuildServiceGroupFactory::resourceTypes
static QStringList resourceTypes()
Returns all resource types for this service factory.
Definition: kbuildservicegroupfactory.cpp:43
KCmdLineArgs
KBuildSycoca::KBuildSycoca
KBuildSycoca()
Definition: kbuildsycoca.cpp:103
KSaveFile
KSaveFile::open
virtual bool open(OpenMode flags=QIODevice::ReadWrite)
kdemain
int kdemain(int argc, char **argv)
Definition: kbuildsycoca.cpp:633
KGlobal::dirs
KStandardDirs * dirs()
QStringList::contains
bool contains(const QString &str, Qt::CaseSensitivity cs) const
kctimefactory.h
KMimeTypeFactory
KCrash::defaultCrashHandler
void defaultCrashHandler(int signal)
QDebug::nospace
QDebug & nospace()
KCrash::setApplicationName
void setApplicationName(const QString &name)
KSycocaResourceList
Definition: ksycocaresourcelist.h:32
g_buildServiceGroupFactory
static KBuildServiceGroupFactory * g_buildServiceGroupFactory
Definition: kbuildsycoca.cpp:72
QList::erase
iterator erase(iterator pos)
QDBusConnection::sessionBus
QDBusConnection sessionBus()
KSycocaPrivate::StrategyMemFile
KSycoca::disableAutoRebuild
static void disableAutoRebuild()
quint32
KSycocaFactory::createEntry
virtual KSycocaEntry * createEntry(const QString &file, const char *resource) const =0
QFile::exists
bool exists() const
KServiceTypeFactory
KSycocaFactory
KSaveFile::abort
void abort()
kDebug
static QDebug kDebug(bool cond, int area=KDE_DEFAULT_DEBUG_AREA)
klocale.h
VFolderMenu::parseMenu
SubMenu * parseMenu(const QString &file, bool forceLegacyLoad=false)
Parses VFolder menu definition and generates a menu layout.
Definition: vfolder_menu.cpp:1590
QDataStream::status
Status status() const
KCTimeInfo
Internal factory for storing the timestamp of each file in ksycoca.
Definition: kctimefactory.h:49
KSycocaResource::extension
QString extension
Definition: ksycocaresourcelist.h:29
vfolder_menu.h
QFile
KCmdLineArgs::isSet
bool isSet(const QByteArray &option) const
KSycoca::language
QString language()
KBuildSycoca::~KBuildSycoca
virtual ~KBuildSycoca()
Definition: kbuildsycoca.cpp:108
KBuildServiceFactory::resourceTypes
static QStringList resourceTypes()
Returns all resource types for this service factory.
Definition: kbuildservicefactory.cpp:60
KBuildProtocolInfoFactory
Protocol Info factory for building ksycoca.
Definition: kbuildprotocolinfofactory.h:30
QIODevice::pos
virtual qint64 pos() const
KCTimeInfo::dict
KCTimeDict * dict()
Definition: kctimefactory.h:78
KCTimeDict::isEmpty
bool isEmpty() const
Definition: kctimefactory.h:35
g_allEntries
static KSycocaEntryListList * g_allEntries
Definition: kbuildsycoca.cpp:79
QFileInfo::filePath
QString filePath() const
QLinkedList
KSycoca::GlobalDatabase
QObject::name
const char * name() const
KSaveFile::fileName
QString fileName() const
kglobal.h
kbuildservicetypefactory.h
KSaveFile::errorString
QString errorString() const
bGlobalDatabase
static bool bGlobalDatabase
Definition: kbuildsycoca.cpp:87
QList::append
void append(const T &value)
KSycoca::version
static int version()
KSycoca::LocalDatabase
QList::empty
bool empty() const
newTimestamp
static quint32 newTimestamp
Definition: kbuildsycoca.cpp:69
VFolderMenu
Definition: vfolder_menu.h:35
KStandardDirs::Recursive
QHash
QEventLoop::exec
int exec(QFlags< QEventLoop::ProcessEventsFlag > flags)
QFileInfo::isDir
bool isDir() const
KSycocaEntryListList
QList< KSycocaEntry::List > KSycocaEntryListList
Definition: kbuildsycoca.cpp:67
QFileInfo::fileName
QString fileName() const
sycocaPath
static QString sycocaPath()
Definition: kbuildsycoca.cpp:98
VFolderMenu::SubMenu::subMenus
QList< SubMenu * > subMenus
Definition: vfolder_menu.h:48
KBuildSycoca
Definition: kbuildsycoca.h:39
KCrash::setEmergencySaveFunction
void setEmergencySaveFunction(HandlerType saveFunction=0)
QDBusMessage::createSignal
QDBusMessage createSignal(const QString &path, const QString &interface, const QString &name)
KSycoca::kfsstnd_prefixes
QString kfsstnd_prefixes()
KServiceGroupFactory
kmemfile.h
kcmdlineargs.h
VFolderMenu::setTrackId
void setTrackId(const QString &id)
Debug function to enable tracking of what happens with a specific menu item id.
Definition: vfolder_menu.cpp:1651
QDBusConnection::send
bool send(const QDBusMessage &message) const
QString::isEmpty
bool isEmpty() const
KBuildSycoca::setTrackId
void setTrackId(const QString &id)
Definition: kbuildsycoca.h:55
QList::removeAll
int removeAll(const T &value)
QByteArray::constData
const char * constData() const
KService::menuId
QString menuId() const
KMemFile::fileContentsChanged
static void fileContentsChanged(const QString &filename)
kbuildsycoca.h
kbuildservicefactory.h
QList< KSycocaFactory * >::Iterator
typedef Iterator
KAboutData
VFolderMenu::SubMenu::directoryFile
QString directoryFile
Definition: vfolder_menu.h:47
QList::front
T & front()
KStandardDirs::calcResourceHash
quint32 calcResourceHash(const char *type, const QString &filename, SearchOptions options=NoSearchOptions) const
KLocale::language
QString language() const
KSycoca::updateSignature
quint32 updateSignature()
kcrash.h
QString
QList
QFileInfo::lastModified
QDateTime lastModified() const
VFolderMenu::allDirectories
QStringList allDirectories()
Returns a list of all directories involved in the last call to parseMenu(), excluding the KDE Legacy ...
Definition: vfolder_menu.cpp:88
KBuildMimeTypeFactory::resourceTypes
static QStringList resourceTypes()
Returns all resource types for this factory.
Definition: kbuildmimetypefactory.cpp:43
QFile::open
virtual bool open(QFlags< QIODevice::OpenModeFlag > mode)
QDataStream::atEnd
bool atEnd() const
g_allResourceDirs
static QStringList * g_allResourceDirs
Definition: kbuildsycoca.cpp:80
VFolderMenu::SubMenu::items
QHash< QString, KService::Ptr > items
Definition: vfolder_menu.h:49
KStandardDirs::kfsstnd_prefixes
QString kfsstnd_prefixes()
KBuildServiceGroupFactory
Service group factory for building ksycoca.
Definition: kbuildservicegroupfactory.h:30
KProtocolInfoFactory
QStringList
kservice.h
KBuildServiceGroupFactory::addNew
KServiceGroup::Ptr addNew(const QString &menuName, const QString &file, KServiceGroup::Ptr entry, bool isDeleted)
Add new menu menuName defined by file When entry is non-null it is re-used, otherwise a new group is ...
Definition: kbuildservicegroupfactory.cpp:78
QDataStream::setVersion
void setVersion(int v)
KSycoca::timeStamp
quint32 timeStamp()
KBuildProtocolInfoFactory::resourceTypes
static QStringList resourceTypes()
Returns all resource types for this service factory.
Definition: kbuildprotocolinfofactory.cpp:39
QFileInfo
QList::end
iterator end()
QHash::value
const T value(const Key &key) const
QString::toLocal8Bit
QByteArray toLocal8Bit() const
QFileInfo::exists
bool exists() const
QString::contains
bool contains(QChar ch, Qt::CaseSensitivity cs) const
KBuildServiceFactory
Service factory for building ksycoca.
Definition: kbuildservicefactory.h:37
KSaveFile::finalize
bool finalize()
KBuildSycoca::clearCaches
static void clearCaches()
Definition: kbuildsycoca.h:60
KCTimeDict::ctime
quint32 ctime(const QString &path, const QByteArray &resource) const
Definition: kctimefactory.cpp:37
QLinkedList::constBegin
const_iterator constBegin() const
KStandardDirs::NoDuplicates
KStandardDirs::resourceDirs
QStringList resourceDirs(const char *type) const
crashHandler
void crashHandler(int)
Definition: kbuildsycoca.cpp:90
QLinkedList::constEnd
const_iterator constEnd() const
KGlobal::locale
KLocale * locale()
appFullName
static const char appFullName[]
Definition: kbuildsycoca.cpp:629
QDir
KCTimeInfo::loadDict
KCTimeDict loadDict() const
Definition: kctimefactory.cpp:124
KCTimeDict::addCTime
void addCTime(const QString &path, const QByteArray &resource, quint32 ctime)
Definition: kctimefactory.cpp:31
ktemporaryfile.h
KBuildMimeTypeFactory
Mime-type factory for building ksycoca.
Definition: kbuildmimetypefactory.h:32
QString::toLatin1
QByteArray toLatin1() const
KCTimeDict::remove
void remove(const QString &path, const QByteArray &resource)
Definition: kctimefactory.cpp:42
QDBusMessage
dir
QString dir(const QString &fileClass)
bMenuTest
static bool bMenuTest
Definition: kbuildsycoca.cpp:88
kstandarddirs.h
KSharedPtr< KService >::staticCast
static KSharedPtr< KService > staticCast(const KSharedPtr< U > &o)
KBuildServiceFactory::postProcessServices
void postProcessServices()
Definition: kbuildservicefactory.cpp:192
KBuildServiceTypeFactory::resourceTypes
static QStringList resourceTypes()
Returns all resource types for this factory.
Definition: kbuildservicetypefactory.cpp:42
KSycocaResource
Definition: ksycocaresourcelist.h:26
QList::ConstIterator
typedef ConstIterator
g_serviceFactory
static KBuildServiceFactory * g_serviceFactory
Definition: kbuildsycoca.cpp:71
mainComponent
const KComponentData & mainComponent()
KCmdLineArgs::init
static void init(int argc, char **argv, const QByteArray &appname, const QByteArray &catalog, const KLocalizedString &programName, const QByteArray &version, const KLocalizedString &description=KLocalizedString(), StdCmdLineArgs stdargs=StdCmdLineArgs(CmdLineArgQt|CmdLineArgKDE))
QString::length
int length() const
g_serviceGroupEntryDict
static KBSEntryDict * g_serviceGroupEntryDict
Definition: kbuildsycoca.cpp:78
KSycocaResource::resource
QString resource
Definition: ksycocaresourcelist.h:28
QByteArray::data
char * data()
kbuildmimetypefactory.h
QString::left
QString left(int n) const
KAboutData::License_GPL
qint32
ksycocaresourcelist.h
KBuildSycoca::changedResources
QStringList changedResources() const
Definition: kbuildsycoca.h:57
KCmdLineArgs::getOption
QString getOption(const QByteArray &option) const
g_currentEntryDict
static KBSEntryDict * g_currentEntryDict
Definition: kbuildsycoca.cpp:77
quit
KAction * quit(const QObject *recvr, const char *slot, QObject *parent)
KServiceFactory
kaboutdata.h
KStandardDirs::findAllResources
QStringList findAllResources(const char *type, const QString &filter=QString(), SearchOptions options=NoSearchOptions) const
QDataStream::device
QIODevice * device() const
QList::constEnd
const_iterator constEnd() const
QList::constBegin
const_iterator constBegin() const
ksycoca_p.h
QObject::connect
bool connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
KBSEntryDict
QHash< QString, KSycocaEntry::Ptr > KBSEntryDict
Definition: kbuildsycoca.cpp:66
end
const KShortcut & end()
KCmdLineOptions
VFolderMenu::SubMenu::name
QString name
Definition: vfolder_menu.h:46
KSycoca::self
static KSycoca * self()
QList::begin
iterator begin()
KCTimeDict
Simple dict for assocating a timestamp with each file in ksycoca.
Definition: kctimefactory.h:28
g_ctimeInfo
static KCTimeInfo * g_ctimeInfo
Definition: kbuildsycoca.cpp:74
KComponentData
KSycoca::absoluteFilePath
static QString absoluteFilePath(DatabaseType type=LocalDatabase)
g_tempStorage
static KSycocaEntry::List g_tempStorage
Definition: kbuildsycoca.cpp:82
KBuildSycoca::checkTimestamps
static bool checkTimestamps(quint32 timestamp, const QStringList &dirs)
Definition: kbuildsycoca.cpp:578
KBuildServiceGroupFactory::addNewEntryTo
void addNewEntryTo(const QString &menuName, const KService::Ptr &newEntry)
Adds the entry newEntry to the menu menuName.
Definition: kbuildservicegroupfactory.cpp:62
QFile::encodeName
QByteArray encodeName(const QString &fileName)
g_resource
static QByteArray g_resource
Definition: kbuildsycoca.cpp:76
KCrash::setCrashHandler
void setCrashHandler(HandlerType handler=defaultCrashHandler)
QDateTime
QDateTime::setTime_t
void setTime_t(uint seconds)
g_changed
static bool g_changed
Definition: kbuildsycoca.cpp:81
begin
const KShortcut & begin()
KDE_EXPORT
#define KDE_EXPORT
list
QStringList list(const QString &fileClass)
KBuildServiceTypeFactory
Service-type factory for building ksycoca.
Definition: kbuildservicetypefactory.h:30
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Mon Jun 22 2020 13:26:11 by doxygen 1.8.7 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

KDED

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

kdelibs API Reference

Skip menu "kdelibs API Reference"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • 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

Search



Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal