tdebuildsycoca.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 <tqdir.h> 00021 #include <tqeventloop.h> 00022 #include <config.h> 00023 00024 #include "tdebuildsycoca.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 <tqdatastream.h> 00039 #include <tqfile.h> 00040 #include <tqtimer.h> 00041 00042 #include <assert.h> 00043 #include <tdeapplication.h> 00044 #include <dcopclient.h> 00045 #include <tdeglobal.h> 00046 #include <kdebug.h> 00047 #include <kdirwatch.h> 00048 #include <kstandarddirs.h> 00049 #include <ksavefile.h> 00050 #include <tdelocale.h> 00051 #include <tdeaboutdata.h> 00052 #include <tdecmdlineargs.h> 00053 #include <kcrash.h> 00054 00055 #ifdef KBUILDSYCOCA_GUI // KBUILDSYCOCA_GUI is used on win32 to build 00056 // GUI version of tdebuildsycoca, so-called "tdebuildsycocaw". 00057 # include <tqlabel.h> 00058 # include <tdemessagebox.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 TQDict<KSycocaEntry> KBSEntryDict; 00069 typedef TQValueList<KSycocaEntry::List> KSycocaEntryListList; 00070 00071 static TQ_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 TQDict<TQ_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 TQStringList *g_changeList = 0; 00083 static TQStringList *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 TQString sycocaPath() 00102 { 00103 TQString path; 00104 00105 if (bGlobalDatabase) 00106 { 00107 path = TDEGlobal::dirs()->saveLocation("services")+"tdesycoca"; 00108 } 00109 else 00110 { 00111 TQCString tdesycoca_env = getenv("TDESYCOCA"); 00112 if (tdesycoca_env.isEmpty()) 00113 path = TDEGlobal::dirs()->saveLocation("cache")+"tdesycoca"; 00114 else 00115 path = TQFile::decodeName(tdesycoca_env); 00116 } 00117 00118 return path; 00119 } 00120 00121 static TQString oldSycocaPath() 00122 { 00123 TQCString tdesycoca_env = getenv("TDESYCOCA"); 00124 if (tdesycoca_env.isEmpty()) 00125 return TDEGlobal::dirs()->saveLocation("tmp")+"tdesycoca"; 00126 00127 return TQString::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 TQString 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 TQString app; 00150 00151 char line[1024*64]; 00152 00153 FILE *f = fopen(TQFile::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 = TQString::fromLatin1(line); 00164 app.truncate(app.length()-1); 00165 } 00166 else if (strncmp(line+1, "mime_types=", 11) == 0) 00167 { 00168 TQString mimetypes = TQString::fromLatin1(line+12); 00169 mimetypes.truncate(mimetypes.length()-1); 00170 mimetypes.replace(TQRegExp("\\*"), "all"); 00171 KService *s = g_bsf->findServiceByName(app); 00172 if (!s) 00173 continue; 00174 00175 TQStringList &serviceTypes = s->accessServiceTypes(); 00176 if (serviceTypes.count() <= 1) 00177 { 00178 serviceTypes += TQStringList::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 TQString &file, bool addToFactory) 00188 { 00189 TQ_UINT32 timeStamp = g_ctimeInfo->ctime(file); 00190 if (!timeStamp) 00191 { 00192 timeStamp = TDEGlobal::dirs()->calcResourceHash( g_resource, file, true); 00193 } 00194 KSycocaEntry* entry = 0; 00195 if (g_allEntries) 00196 { 00197 assert(g_ctimeDict); 00198 TQ_UINT32 *timeP = (*g_ctimeDict)[file]; 00199 TQ_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 TQString &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 TQPtrList<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 TQStringList 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( TQStringList::ConstIterator it1 = allResources.begin(); 00312 it1 != allResources.end(); 00313 ++it1 ) 00314 { 00315 g_changed = false; 00316 g_resource = (*it1).ascii(); 00317 00318 TQStringList relFiles; 00319 00320 (void) TDEGlobal::dirs()->findAllResources( g_resource, 00321 TQString::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( TQStringList::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, TQT_SIGNAL(newService(const TQString &, KService **)), 00380 this, TQT_SLOT(slotCreateEntry(const TQString &, KService **))); 00381 00382 VFolderMenu::SubMenu *kdeMenu = g_vfolder->parseMenu("tde-applications.menu", true); 00383 00384 KServiceGroup *entry = g_bsgf->addNew("/", kdeMenu->directoryFile, 0, false); 00385 entry->setLayoutInfo(kdeMenu->layoutList); 00386 createMenu(TQString::null, TQString::null, kdeMenu); 00387 00388 KServiceGroup::Ptr g(entry); 00389 00390 (void) existingResourceDirs(); 00391 *g_allResourceDirs += g_vfolder->allDirectories(); 00392 00393 disconnect(g_vfolder, TQT_SIGNAL(newService(const TQString &, KService **)), 00394 this, TQT_SLOT(slotCreateEntry(const TQString &, KService **))); 00395 00396 if (g_changed || !g_allEntries) 00397 { 00398 uptodate = false; 00399 g_changeList->append(g_resource); 00400 } 00401 if (bMenuTest) 00402 return false; 00403 } 00404 00405 return result; 00406 } 00407 00408 void KBuildSycoca::createMenu(TQString caption, TQString name, VFolderMenu::SubMenu *menu) 00409 { 00410 for(VFolderMenu::SubMenu *subMenu = menu->subMenus.first(); subMenu; subMenu = menu->subMenus.next()) 00411 { 00412 TQString subName = name+subMenu->name+"/"; 00413 00414 TQString directoryFile = subMenu->directoryFile; 00415 if (directoryFile.isEmpty()) 00416 directoryFile = subName+".directory"; 00417 TQ_UINT32 timeStamp = g_ctimeInfo->ctime(directoryFile); 00418 if (!timeStamp) 00419 { 00420 timeStamp = TDEGlobal::dirs()->calcResourceHash( g_resource, directoryFile, true); 00421 } 00422 00423 KServiceGroup* entry = 0; 00424 if (g_allEntries) 00425 { 00426 TQ_UINT32 *timeP = (*g_ctimeDict)[directoryFile]; 00427 TQ_UINT32 oldTimestamp = timeP ? *timeP : 0; 00428 00429 if (timeStamp && (timeStamp == oldTimestamp)) 00430 { 00431 entry = dynamic_cast<KServiceGroup *> (g_serviceGroupEntryDict->find(subName)); 00432 if (entry && (entry->directoryEntryPath() != directoryFile)) 00433 entry = 0; // Can't reuse this one! 00434 } 00435 } 00436 g_ctimeInfo->addCTime(directoryFile, timeStamp); 00437 00438 entry = g_bsgf->addNew(subName, subMenu->directoryFile, entry, subMenu->isDeleted); 00439 entry->setLayoutInfo(subMenu->layoutList); 00440 if (! (bMenuTest && entry->noDisplay()) ) 00441 createMenu(caption + entry->caption() + "/", subName, subMenu); 00442 } 00443 if (caption.isEmpty()) 00444 caption += "/"; 00445 if (name.isEmpty()) 00446 name += "/"; 00447 for(TQDictIterator<KService> it(menu->items); it.current(); ++it) 00448 { 00449 if (bMenuTest) 00450 { 00451 if (!menu->isDeleted && !it.current()->noDisplay()) 00452 printf("%s\t%s\t%s\n", caption.local8Bit().data(), it.current()->menuId().local8Bit().data(), locate("apps", it.current()->desktopEntryPath()).local8Bit().data()); 00453 } 00454 else 00455 { 00456 g_bsf->addEntry( it.current(), g_resource ); 00457 g_bsgf->addNewEntryTo(name, it.current()); 00458 } 00459 } 00460 } 00461 00462 bool KBuildSycoca::recreate() 00463 { 00464 TQString path(sycocaPath()); 00465 #ifdef Q_WS_WIN 00466 printf("tdebuildsycoca: path='%s'\n", (const char*)path); 00467 #endif 00468 00469 // KSaveFile first writes to a temp file. 00470 // Upon close() it moves the stuff to the right place. 00471 std::auto_ptr<KSaveFile> database( new KSaveFile(path) ); 00472 if (database->status() == EACCES && TQFile::exists(path)) 00473 { 00474 TQFile::remove( path ); 00475 database.reset( new KSaveFile(path) ); // try again 00476 } 00477 if (database->status() != 0) 00478 { 00479 fprintf(stderr, "[tdebuildsycoca] ERROR creating database '%s'! %s\n", path.local8Bit().data(),strerror(database->status())); 00480 #ifdef KBUILDSYCOCA_GUI // KBUILDSYCOCA_GUI is used on win32 to build 00481 // GUI version of tdebuildsycoca, so-called "tdebuildsycocaw". 00482 if (!silent) 00483 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")); 00484 #endif 00485 return false; 00486 } 00487 00488 m_str = database->dataStream(); 00489 00490 kdDebug(7021) << "Recreating tdesycoca file (" << path << ", version " << KSycoca::version() << ")" << endl; 00491 00492 // It is very important to build the servicetype one first 00493 // Both are registered in KSycoca, no need to keep the pointers 00494 KSycocaFactory *stf = new KBuildServiceTypeFactory; 00495 g_bsgf = new KBuildServiceGroupFactory(); 00496 g_bsf = new KBuildServiceFactory(stf, g_bsgf); 00497 (void) new KBuildImageIOFactory(); 00498 (void) new KBuildProtocolInfoFactory(); 00499 00500 if( build()) // Parse dirs 00501 { 00502 save(); // Save database 00503 if (m_str->device()->status()) 00504 database->abort(); // Error 00505 m_str = 0L; 00506 if (!database->close()) 00507 { 00508 fprintf(stderr, "[tdebuildsycoca] ERROR writing database '%s'!\n", database->name().local8Bit().data()); 00509 fprintf(stderr, "[tdebuildsycoca] Disk full?\n"); 00510 #ifdef KBUILDSYCOCA_GUI 00511 if (!silent) 00512 KMessageBox::error(0, i18n("[tdebuildsycoca] 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")); 00513 #endif 00514 return false; 00515 } 00516 } 00517 else 00518 { 00519 m_str = 0L; 00520 database->abort(); 00521 if (bMenuTest) 00522 return true; 00523 kdDebug(7021) << "Database is up to date" << endl; 00524 } 00525 00526 if (!bGlobalDatabase) 00527 { 00528 // update the timestamp file 00529 TQString stamppath = path + "stamp"; 00530 TQFile tdesycocastamp(stamppath); 00531 tdesycocastamp.open( IO_WriteOnly ); 00532 TQDataStream str( &tdesycocastamp ); 00533 str << newTimestamp; 00534 str << existingResourceDirs(); 00535 if (g_vfolder) 00536 str << g_vfolder->allDirectories(); // Extra resource dirs 00537 } 00538 return true; 00539 } 00540 00541 void KBuildSycoca::save() 00542 { 00543 // Write header (#pass 1) 00544 m_str->device()->at(0); 00545 00546 (*m_str) << (TQ_INT32) KSycoca::version(); 00547 KSycocaFactory * servicetypeFactory = 0L; 00548 KSycocaFactory * serviceFactory = 0L; 00549 for(KSycocaFactory *factory = m_lstFactories->first(); 00550 factory; 00551 factory = m_lstFactories->next()) 00552 { 00553 TQ_INT32 aId; 00554 TQ_INT32 aOffset; 00555 aId = factory->factoryId(); 00556 if ( aId == KST_KServiceTypeFactory ) 00557 servicetypeFactory = factory; 00558 else if ( aId == KST_KServiceFactory ) 00559 serviceFactory = factory; 00560 aOffset = factory->offset(); 00561 (*m_str) << aId; 00562 (*m_str) << aOffset; 00563 } 00564 (*m_str) << (TQ_INT32) 0; // No more factories. 00565 // Write TDEDIRS 00566 (*m_str) << TDEGlobal::dirs()->kfsstnd_prefixes(); 00567 (*m_str) << newTimestamp; 00568 (*m_str) << TDEGlobal::locale()->language(); 00569 (*m_str) << TDEGlobal::dirs()->calcResourceHash("services", "update_tdesycoca", true); 00570 (*m_str) << (*g_allResourceDirs); 00571 00572 // Write factory data.... 00573 for(KSycocaFactory *factory = m_lstFactories->first(); 00574 factory; 00575 factory = m_lstFactories->next()) 00576 { 00577 factory->save(*m_str); 00578 if (m_str->device()->status()) 00579 return; // error 00580 } 00581 00582 int endOfData = m_str->device()->at(); 00583 00584 // Write header (#pass 2) 00585 m_str->device()->at(0); 00586 00587 (*m_str) << (TQ_INT32) KSycoca::version(); 00588 for(KSycocaFactory *factory = m_lstFactories->first(); 00589 factory; 00590 factory = m_lstFactories->next()) 00591 { 00592 TQ_INT32 aId; 00593 TQ_INT32 aOffset; 00594 aId = factory->factoryId(); 00595 aOffset = factory->offset(); 00596 (*m_str) << aId; 00597 (*m_str) << aOffset; 00598 } 00599 (*m_str) << (TQ_INT32) 0; // No more factories. 00600 00601 // Jump to end of database 00602 m_str->device()->at(endOfData); 00603 } 00604 00605 bool KBuildSycoca::checkDirTimestamps( const TQString& dirname, const TQDateTime& stamp, bool top ) 00606 { 00607 if( top ) 00608 { 00609 TQFileInfo inf( dirname ); 00610 if( inf.lastModified() > stamp ) 00611 { 00612 kdDebug( 7021 ) << "timestamp changed:" << dirname << endl; 00613 return false; 00614 } 00615 } 00616 TQDir dir( dirname ); 00617 const TQFileInfoList *list = dir.entryInfoList( TQDir::DefaultFilter, TQDir::Unsorted ); 00618 if (!list) 00619 return true; 00620 00621 for( TQFileInfoListIterator it( *list ); 00622 it.current() != NULL; 00623 ++it ) 00624 { 00625 TQFileInfo* fi = it.current(); 00626 if( fi->fileName() == "." || fi->fileName() == ".." ) 00627 continue; 00628 if( fi->lastModified() > stamp ) 00629 { 00630 kdDebug( 7201 ) << "timestamp changed:" << fi->filePath() << endl; 00631 return false; 00632 } 00633 if( fi->isDir() && !checkDirTimestamps( fi->filePath(), stamp, false )) 00634 return false; 00635 } 00636 return true; 00637 } 00638 00639 // check times of last modification of all files on which tdesycoca depens, 00640 // and also their directories 00641 // if all of them all older than the timestamp in file tdesycocastamp, this 00642 // means that there's no need to rebuild tdesycoca 00643 bool KBuildSycoca::checkTimestamps( TQ_UINT32 timestamp, const TQStringList &dirs ) 00644 { 00645 kdDebug( 7021 ) << "checking file timestamps" << endl; 00646 TQDateTime stamp; 00647 stamp.setTime_t( timestamp ); 00648 for( TQStringList::ConstIterator it = dirs.begin(); 00649 it != dirs.end(); 00650 ++it ) 00651 { 00652 if( !checkDirTimestamps( *it, stamp, true )) 00653 return false; 00654 } 00655 kdDebug( 7021 ) << "timestamps check ok" << endl; 00656 return true; 00657 } 00658 00659 TQStringList KBuildSycoca::existingResourceDirs() 00660 { 00661 static TQStringList* dirs = NULL; 00662 if( dirs != NULL ) 00663 return *dirs; 00664 dirs = new TQStringList; 00665 g_allResourceDirs = new TQStringList; 00666 // these are all resources cached by tdesycoca 00667 TQStringList resources; 00668 resources += KBuildServiceTypeFactory::resourceTypes(); 00669 resources += KBuildServiceGroupFactory::resourceTypes(); 00670 resources += KBuildServiceFactory::resourceTypes(); 00671 resources += KBuildImageIOFactory::resourceTypes(); 00672 resources += KBuildProtocolInfoFactory::resourceTypes(); 00673 while( !resources.empty()) 00674 { 00675 TQString res = resources.front(); 00676 *dirs += TDEGlobal::dirs()->resourceDirs( res.latin1()); 00677 resources.remove( res ); // remove this 'res' and all its duplicates 00678 } 00679 00680 *g_allResourceDirs = *dirs; 00681 00682 for( TQStringList::Iterator it = dirs->begin(); 00683 it != dirs->end(); ) 00684 { 00685 TQFileInfo inf( *it ); 00686 if( !inf.exists() || !inf.isReadable() ) 00687 it = dirs->remove( it ); 00688 else 00689 ++it; 00690 } 00691 return *dirs; 00692 } 00693 00694 static TDECmdLineOptions options[] = { 00695 { "nosignal", I18N_NOOP("Do not signal applications to update"), 0 }, 00696 { "noincremental", I18N_NOOP("Disable incremental update, re-read everything"), 0 }, 00697 { "checkstamps", I18N_NOOP("Check file timestamps"), 0 }, 00698 { "nocheckfiles", I18N_NOOP("Disable checking files (dangerous)"), 0 }, 00699 { "global", I18N_NOOP("Create global database"), 0 }, 00700 { "menutest", I18N_NOOP("Perform menu generation test run only"), 0 }, 00701 { "track <menu-id>", I18N_NOOP("Track menu id for debug purposes"), 0 }, 00702 #ifdef KBUILDSYCOCA_GUI 00703 { "silent", I18N_NOOP("Silent - work without windows and stderr"), 0 }, 00704 { "showprogress", I18N_NOOP("Show progress information (even if 'silent' mode is on)"), 0 }, 00705 #endif 00706 TDECmdLineLastOption 00707 }; 00708 00709 static const char appName[] = "tdebuildsycoca"; 00710 static const char appVersion[] = "1.1"; 00711 00712 class WaitForSignal : public QObject 00713 { 00714 public: 00715 ~WaitForSignal() { kapp->eventLoop()->exitLoop(); } 00716 }; 00717 00718 extern "C" KDE_EXPORT int kdemain(int argc, char **argv) 00719 { 00720 TDELocale::setMainCatalogue("tdelibs"); 00721 TDEAboutData d(appName, I18N_NOOP("KBuildSycoca"), appVersion, 00722 I18N_NOOP("Rebuilds the system configuration cache."), 00723 TDEAboutData::License_GPL, "(c) 1999-2002 KDE Developers"); 00724 d.addAuthor("David Faure", I18N_NOOP("Author"), "faure@kde.org"); 00725 d.addAuthor("Waldo Bastian", I18N_NOOP("Author"), "bastian@kde.org"); 00726 00727 TDECmdLineArgs::init(argc, argv, &d); 00728 TDECmdLineArgs::addCmdLineOptions(options); 00729 TDECmdLineArgs *args = TDECmdLineArgs::parsedArgs(); 00730 bGlobalDatabase = args->isSet("global"); 00731 bMenuTest = args->isSet("menutest"); 00732 00733 if (bGlobalDatabase) 00734 { 00735 setenv("TDEHOME", "-", 1); 00736 setenv("TDEROOTHOME", "-", 1); 00737 } 00738 00739 TDEApplication::disableAutoDcopRegistration(); 00740 #ifdef KBUILDSYCOCA_GUI 00741 TDEApplication k; 00742 #else 00743 TDEApplication k(false, false); 00744 #endif 00745 k.disableSessionManagement(); 00746 00747 #ifdef KBUILDSYCOCA_GUI 00748 silent = args->isSet("silent"); 00749 showprogress = args->isSet("showprogress"); 00750 TQLabel progress( TQString("<p><br><nobr> %1 </nobr><br>").arg( i18n("Reloading TDE configuration, please wait...") ), 0, "", Qt::WType_Dialog | Qt::WStyle_DialogBorder | Qt::WStyle_Customize| Qt::WStyle_Title ); 00751 TQString capt = i18n("TDE Configuration Manager"); 00752 if (!silent) { 00753 if (KMessageBox::No == KMessageBox::questionYesNo(0, i18n("Do you want to reload TDE configuration?"), capt, i18n("Reload"), i18n("Do Not Reload"))) 00754 return 0; 00755 } 00756 if (!silent || showprogress) { 00757 progress.setCaption( capt ); 00758 progress.show(); 00759 } 00760 #endif 00761 00762 TDECrash::setCrashHandler(TDECrash::defaultCrashHandler); 00763 TDECrash::setEmergencySaveFunction(crashHandler); 00764 TDECrash::setApplicationName(TQString(appName)); 00765 00766 // this program is in tdelibs so it uses tdelibs as catalog 00767 TDELocale::setMainCatalogue("tdelibs"); 00768 // force generating of TDELocale object. if not, the database will get 00769 // be translated 00770 TDEGlobal::locale(); 00771 TDEGlobal::dirs()->addResourceType("app-reg", "share/application-registry" ); 00772 00773 DCOPClient *dcopClient = new DCOPClient(); 00774 00775 while(true) 00776 { 00777 TQCString registeredName = dcopClient->registerAs(appName, false); 00778 if (registeredName.isEmpty()) 00779 { 00780 fprintf(stderr, "[tdebuildsycoca] Warning: %s is unable to register with DCOP.\n", appName); 00781 break; 00782 } 00783 else if (registeredName == appName) 00784 { 00785 break; // Go 00786 } 00787 fprintf(stderr, "[tdebuildsycoca] Waiting for already running %s to finish.\n", appName); 00788 00789 dcopClient->setNotifications( true ); 00790 while (dcopClient->isApplicationRegistered(appName)) 00791 { 00792 WaitForSignal *obj = new WaitForSignal; 00793 obj->connect(dcopClient, TQT_SIGNAL(applicationRemoved(const TQCString &)), 00794 TQT_SLOT(deleteLater())); 00795 kapp->eventLoop()->enterLoop(); 00796 } 00797 dcopClient->setNotifications( false ); 00798 } 00799 fprintf(stderr, "[tdebuildsycoca] %s running...\n", appName); 00800 00801 bool checkfiles = bGlobalDatabase || args->isSet("checkfiles"); 00802 00803 bool incremental = !bGlobalDatabase && args->isSet("incremental") && checkfiles; 00804 if (incremental || !checkfiles) 00805 { 00806 KSycoca::self()->disableAutoRebuild(); // Prevent deadlock 00807 TQString current_language = TDEGlobal::locale()->language(); 00808 TQString tdesycoca_language = KSycoca::self()->language(); 00809 TQ_UINT32 current_update_sig = TDEGlobal::dirs()->calcResourceHash("services", "update_tdesycoca", true); 00810 TQ_UINT32 tdesycoca_update_sig = KSycoca::self()->updateSignature(); 00811 00812 if ((current_update_sig != tdesycoca_update_sig) || 00813 (current_language != tdesycoca_language) || 00814 (KSycoca::self()->timeStamp() == 0)) 00815 { 00816 incremental = false; 00817 checkfiles = true; 00818 delete KSycoca::self(); 00819 } 00820 } 00821 00822 g_changeList = new TQStringList; 00823 00824 bool checkstamps = incremental && args->isSet("checkstamps") && checkfiles; 00825 TQ_UINT32 filestamp = 0; 00826 TQStringList oldresourcedirs; 00827 if( checkstamps && incremental ) 00828 { 00829 TQString path = sycocaPath()+"stamp"; 00830 TQCString qPath = TQFile::encodeName(path); 00831 cSycocaPath = qPath.data(); // Delete timestamps on crash 00832 TQFile tdesycocastamp(path); 00833 if( tdesycocastamp.open( IO_ReadOnly )) 00834 { 00835 TQDataStream str( &tdesycocastamp ); 00836 if (!str.atEnd()) 00837 str >> filestamp; 00838 if (!str.atEnd()) 00839 { 00840 str >> oldresourcedirs; 00841 if( oldresourcedirs != KBuildSycoca::existingResourceDirs()) 00842 checkstamps = false; 00843 } 00844 else 00845 { 00846 checkstamps = false; 00847 } 00848 if (!str.atEnd()) 00849 { 00850 TQStringList extraResourceDirs; 00851 str >> extraResourceDirs; 00852 oldresourcedirs += extraResourceDirs; 00853 } 00854 } 00855 else 00856 { 00857 checkstamps = false; 00858 } 00859 cSycocaPath = 0; 00860 } 00861 00862 newTimestamp = (TQ_UINT32) time(0); 00863 00864 if( checkfiles && ( !checkstamps || !KBuildSycoca::checkTimestamps( filestamp, oldresourcedirs ))) 00865 { 00866 TQCString qSycocaPath = TQFile::encodeName(sycocaPath()); 00867 cSycocaPath = qSycocaPath.data(); 00868 00869 g_allEntries = 0; 00870 g_ctimeDict = 0; 00871 if (incremental) 00872 { 00873 tqWarning("[tdebuildsycoca] Reusing existing tdesycoca."); 00874 KSycoca *oldSycoca = KSycoca::self(); 00875 KSycocaFactoryList *factories = new KSycocaFactoryList; 00876 g_allEntries = new KSycocaEntryListList; 00877 g_ctimeDict = new TQDict<TQ_UINT32>(523); 00878 00879 // Must be in same order as in KBuildSycoca::recreate()! 00880 factories->append( new KServiceTypeFactory ); 00881 factories->append( new KServiceGroupFactory ); 00882 factories->append( new KServiceFactory ); 00883 factories->append( new KImageIOFactory ); 00884 factories->append( new KProtocolInfoFactory ); 00885 00886 // For each factory 00887 for (KSycocaFactory *factory = factories->first(); 00888 factory; 00889 factory = factories->next() ) 00890 { 00891 KSycocaEntry::List list; 00892 list = factory->allEntries(); 00893 g_allEntries->append( list ); 00894 } 00895 delete factories; factories = 0; 00896 KCTimeInfo *ctimeInfo = new KCTimeInfo; 00897 ctimeInfo->fillCTimeDict(*g_ctimeDict); 00898 delete oldSycoca; 00899 } 00900 cSycocaPath = 0; 00901 00902 KBuildSycoca *sycoca= new KBuildSycoca; // Build data base 00903 if (args->isSet("track")) 00904 sycoca->setTrackId(TQString::fromLocal8Bit(args->getOption("track"))); 00905 if (!sycoca->recreate()) { 00906 #ifdef KBUILDSYCOCA_GUI 00907 if (!silent || showprogress) 00908 progress.close(); 00909 #endif 00910 return -1; 00911 } 00912 00913 if (bGlobalDatabase) 00914 { 00915 // These directories may have been created with 0700 permission 00916 // better delete them if they are empty 00917 TQString applnkDir = TDEGlobal::dirs()->saveLocation("apps", TQString::null, false); 00918 ::rmdir(TQFile::encodeName(applnkDir)); 00919 TQString servicetypesDir = TDEGlobal::dirs()->saveLocation("servicetypes", TQString::null, false); 00920 ::rmdir(TQFile::encodeName(servicetypesDir)); 00921 } 00922 } 00923 00924 if (!bGlobalDatabase) 00925 { 00926 // Recreate compatibility symlink 00927 TQString oldPath = oldSycocaPath(); 00928 if (!oldPath.isEmpty()) 00929 { 00930 KTempFile tmp; 00931 if (tmp.status() == 0) 00932 { 00933 TQString tmpFile = tmp.name(); 00934 tmp.unlink(); 00935 symlink(TQFile::encodeName(sycocaPath()), TQFile::encodeName(tmpFile)); 00936 rename(TQFile::encodeName(tmpFile), TQFile::encodeName(oldPath)); 00937 } 00938 } 00939 } 00940 00941 if (args->isSet("signal")) 00942 { 00943 // Notify ALL applications that have a tdesycoca object, using a broadcast 00944 TQByteArray data; 00945 TQDataStream stream(data, IO_WriteOnly); 00946 stream << *g_changeList; 00947 dcopClient->send( "*", "tdesycoca", "notifyDatabaseChanged(TQStringList)", data ); 00948 } 00949 00950 #ifdef KBUILDSYCOCA_GUI 00951 if (!silent) { 00952 progress.close(); 00953 KMessageBox::information(0, i18n("[tdebuildsycoca] Configuration information reloaded successfully."), capt); 00954 } 00955 #endif 00956 return 0; 00957 } 00958 00959 #include "tdebuildsycoca.moc"