TDE core libraries
您最多选择25个主题 主题必须以字母或数字开头,可以包含连字符 (-),并且长度不得超过35个字符

tdebuildsycoca.cpp 29KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959
  1. /* This file is part of the KDE libraries
  2. * Copyright (C) 1999 David Faure <faure@kde.org>
  3. * Copyright (C) 2002-2003 Waldo Bastian <bastian@kde.org>
  4. *
  5. * This library is free software; you can redistribute it and/or
  6. * modify it under the terms of the GNU Library General Public
  7. * License version 2 as published by the Free Software Foundation;
  8. *
  9. * This library is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  12. * Library General Public License for more details.
  13. *
  14. * You should have received a copy of the GNU Library General Public License
  15. * along with this library; see the file COPYING.LIB. If not, write to
  16. * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
  17. * Boston, MA 02110-1301, USA.
  18. **/
  19. #include <tqdir.h>
  20. #include <tqeventloop.h>
  21. #include <config.h>
  22. #include "tdebuildsycoca.h"
  23. #include "kresourcelist.h"
  24. #include "vfolder_menu.h"
  25. #include <kservice.h>
  26. #include <kmimetype.h>
  27. #include <kbuildservicetypefactory.h>
  28. #include <kbuildservicefactory.h>
  29. #include <kbuildservicegroupfactory.h>
  30. #include <kbuildimageiofactory.h>
  31. #include <kbuildprotocolinfofactory.h>
  32. #include <kctimefactory.h>
  33. #include <kdatastream.h>
  34. #include <tqdatastream.h>
  35. #include <tqfile.h>
  36. #include <tqtimer.h>
  37. #include <assert.h>
  38. #include <tdeapplication.h>
  39. #include <dcopclient.h>
  40. #include <tdeglobal.h>
  41. #include <kdebug.h>
  42. #include <kdirwatch.h>
  43. #include <kstandarddirs.h>
  44. #include <ksavefile.h>
  45. #include <tdelocale.h>
  46. #include <tdeaboutdata.h>
  47. #include <tdecmdlineargs.h>
  48. #include <kcrash.h>
  49. #ifdef KBUILDSYCOCA_GUI // KBUILDSYCOCA_GUI is used on win32 to build
  50. // GUI version of tdebuildsycoca, so-called "tdebuildsycocaw".
  51. # include <tqlabel.h>
  52. # include <tdemessagebox.h>
  53. bool silent;
  54. bool showprogress;
  55. #endif
  56. #include <stdlib.h>
  57. #include <unistd.h>
  58. #include <time.h>
  59. #include <memory> // auto_ptr
  60. typedef TQDict<KSycocaEntry> KBSEntryDict;
  61. typedef TQValueList<KSycocaEntry::List> KSycocaEntryListList;
  62. static TQ_UINT32 newTimestamp = 0;
  63. static KBuildServiceFactory *g_bsf = 0;
  64. static KBuildServiceGroupFactory *g_bsgf = 0;
  65. static KSycocaFactory *g_factory = 0;
  66. static KCTimeInfo *g_ctimeInfo = 0;
  67. static TQDict<TQ_UINT32> *g_ctimeDict = 0;
  68. static const char *g_resource = 0;
  69. static KBSEntryDict *g_entryDict = 0;
  70. static KBSEntryDict *g_serviceGroupEntryDict = 0;
  71. static KSycocaEntryListList *g_allEntries = 0;
  72. static TQStringList *g_changeList = 0;
  73. static TQStringList *g_allResourceDirs = 0;
  74. static bool g_changed = false;
  75. static KSycocaEntry::List g_tempStorage;
  76. static VFolderMenu *g_vfolder = 0;
  77. static const char *cSycocaPath = 0;
  78. static bool bGlobalDatabase = false;
  79. static bool bMenuTest = false;
  80. void crashHandler(int)
  81. {
  82. // If we crash while reading sycoca, we delete the database
  83. // in an attempt to recover.
  84. if (cSycocaPath)
  85. unlink(cSycocaPath);
  86. }
  87. static TQString sycocaPath()
  88. {
  89. TQString path;
  90. if (bGlobalDatabase)
  91. {
  92. path = TDEGlobal::dirs()->saveLocation("services")+"tdesycoca";
  93. }
  94. else
  95. {
  96. TQCString tdesycoca_env = getenv("TDESYCOCA");
  97. if (tdesycoca_env.isEmpty())
  98. path = TDEGlobal::dirs()->saveLocation("cache")+"tdesycoca";
  99. else
  100. path = TQFile::decodeName(tdesycoca_env);
  101. }
  102. return path;
  103. }
  104. static TQString oldSycocaPath()
  105. {
  106. TQCString tdesycoca_env = getenv("TDESYCOCA");
  107. if (tdesycoca_env.isEmpty())
  108. return TDEGlobal::dirs()->saveLocation("tmp")+"tdesycoca";
  109. return TQString::null;
  110. }
  111. KBuildSycoca::KBuildSycoca()
  112. : KSycoca( true )
  113. {
  114. }
  115. KBuildSycoca::~KBuildSycoca()
  116. {
  117. }
  118. void KBuildSycoca::processGnomeVfs()
  119. {
  120. TQString file = locate("app-reg", "gnome-vfs.applications");
  121. if (file.isEmpty())
  122. {
  123. // kdDebug(7021) << "gnome-vfs.applications not found." << endl;
  124. return;
  125. }
  126. TQString app;
  127. char line[1024*64];
  128. FILE *f = fopen(TQFile::encodeName(file), "r");
  129. while (!feof(f))
  130. {
  131. if (!fgets(line, sizeof(line)-1, f))
  132. {
  133. break;
  134. }
  135. if (line[0] != '\t')
  136. {
  137. app = TQString::fromLatin1(line);
  138. app.truncate(app.length()-1);
  139. }
  140. else if (strncmp(line+1, "mime_types=", 11) == 0)
  141. {
  142. TQString mimetypes = TQString::fromLatin1(line+12);
  143. mimetypes.truncate(mimetypes.length()-1);
  144. mimetypes.replace(TQRegExp("\\*"), "all");
  145. KService *s = g_bsf->findServiceByName(app);
  146. if (!s)
  147. continue;
  148. TQStringList &serviceTypes = s->accessServiceTypes();
  149. if (serviceTypes.count() <= 1)
  150. {
  151. serviceTypes += TQStringList::split(',', mimetypes);
  152. // kdDebug(7021) << "Adding gnome mimetypes for '" << app << "'.\n";
  153. // kdDebug(7021) << "ServiceTypes=" << s->serviceTypes().join(":") << endl;
  154. }
  155. }
  156. }
  157. fclose( f );
  158. }
  159. KSycocaEntry *KBuildSycoca::createEntry(const TQString &file, bool addToFactory)
  160. {
  161. TQ_UINT32 timeStamp = g_ctimeInfo->ctime(file);
  162. if (!timeStamp)
  163. {
  164. timeStamp = TDEGlobal::dirs()->calcResourceHash( g_resource, file, true);
  165. }
  166. KSycocaEntry* entry = 0;
  167. if (g_allEntries)
  168. {
  169. assert(g_ctimeDict);
  170. TQ_UINT32 *timeP = (*g_ctimeDict)[file];
  171. TQ_UINT32 oldTimestamp = timeP ? *timeP : 0;
  172. if (timeStamp && (timeStamp == oldTimestamp))
  173. {
  174. // Re-use old entry
  175. if (g_factory == g_bsgf) // Strip .directory from service-group entries
  176. {
  177. entry = g_entryDict->find(file.left(file.length()-10));
  178. }
  179. else if (g_factory == g_bsf)
  180. {
  181. entry = g_entryDict->find(file);
  182. }
  183. else
  184. {
  185. entry = g_entryDict->find(file);
  186. }
  187. // remove from g_ctimeDict; if g_ctimeDict is not empty
  188. // after all files have been processed, it means
  189. // some files were removed since last time
  190. g_ctimeDict->remove( file );
  191. }
  192. else if (oldTimestamp)
  193. {
  194. g_changed = true;
  195. kdDebug(7021) << "modified: " << file << endl;
  196. }
  197. else
  198. {
  199. g_changed = true;
  200. kdDebug(7021) << "new: " << file << endl;
  201. }
  202. }
  203. g_ctimeInfo->addCTime(file, timeStamp );
  204. if (!entry)
  205. {
  206. // Create a new entry
  207. entry = g_factory->createEntry( file, g_resource );
  208. }
  209. if ( entry && entry->isValid() )
  210. {
  211. if (addToFactory)
  212. g_factory->addEntry( entry, g_resource );
  213. else
  214. g_tempStorage.append(entry);
  215. return entry;
  216. }
  217. return 0;
  218. }
  219. void KBuildSycoca::slotCreateEntry(const TQString &file, KService **service)
  220. {
  221. KSycocaEntry *entry = createEntry(file, false);
  222. *service = dynamic_cast<KService *>(entry);
  223. }
  224. // returns false if the database is up to date
  225. bool KBuildSycoca::build()
  226. {
  227. typedef TQPtrList<KBSEntryDict> KBSEntryDictList;
  228. KBSEntryDictList *entryDictList = 0;
  229. KBSEntryDict *serviceEntryDict = 0;
  230. entryDictList = new KBSEntryDictList();
  231. // Convert for each factory the entryList to a Dict.
  232. int i = 0;
  233. // For each factory
  234. for (KSycocaFactory *factory = m_lstFactories->first();
  235. factory;
  236. factory = m_lstFactories->next() )
  237. {
  238. KBSEntryDict *entryDict = new KBSEntryDict();
  239. if (g_allEntries)
  240. {
  241. KSycocaEntry::List list = (*g_allEntries)[i++];
  242. for( KSycocaEntry::List::Iterator it = list.begin();
  243. it != list.end();
  244. ++it)
  245. {
  246. entryDict->insert( (*it)->entryPath(), static_cast<KSycocaEntry *>(*it));
  247. }
  248. }
  249. if (factory == g_bsf)
  250. serviceEntryDict = entryDict;
  251. else if (factory == g_bsgf)
  252. g_serviceGroupEntryDict = entryDict;
  253. entryDictList->append(entryDict);
  254. }
  255. TQStringList allResources;
  256. // For each factory
  257. for (KSycocaFactory *factory = m_lstFactories->first();
  258. factory;
  259. factory = m_lstFactories->next() )
  260. {
  261. // For each resource the factory deals with
  262. const KSycocaResourceList *list = factory->resourceList();
  263. if (!list) continue;
  264. for( KSycocaResourceList::ConstIterator it1 = list->begin();
  265. it1 != list->end();
  266. ++it1 )
  267. {
  268. KSycocaResource res = (*it1);
  269. if (!allResources.contains(res.resource))
  270. allResources.append(res.resource);
  271. }
  272. }
  273. g_ctimeInfo = new KCTimeInfo(); // This is a build factory too, don't delete!!
  274. bool uptodate = true;
  275. // For all resources
  276. for( TQStringList::ConstIterator it1 = allResources.begin();
  277. it1 != allResources.end();
  278. ++it1 )
  279. {
  280. g_changed = false;
  281. g_resource = (*it1).ascii();
  282. TQStringList relFiles;
  283. (void) TDEGlobal::dirs()->findAllResources( g_resource,
  284. TQString::null,
  285. true, // Recursive!
  286. true, // uniq
  287. relFiles);
  288. // Now find all factories that use this resource....
  289. // For each factory
  290. g_entryDict = entryDictList->first();
  291. for (g_factory = m_lstFactories->first();
  292. g_factory;
  293. g_factory = m_lstFactories->next(),
  294. g_entryDict = entryDictList->next() )
  295. {
  296. // For each resource the factory deals with
  297. const KSycocaResourceList *list = g_factory->resourceList();
  298. if (!list) continue;
  299. for( KSycocaResourceList::ConstIterator it2 = list->begin();
  300. it2 != list->end();
  301. ++it2 )
  302. {
  303. KSycocaResource res = (*it2);
  304. if (res.resource != (*it1)) continue;
  305. // For each file in the resource
  306. for( TQStringList::ConstIterator it3 = relFiles.begin();
  307. it3 != relFiles.end();
  308. ++it3 )
  309. {
  310. // Check if file matches filter
  311. if ((*it3).endsWith(res.extension))
  312. createEntry(*it3, true);
  313. }
  314. }
  315. if ((g_factory == g_bsf) && (strcmp(g_resource, "services") == 0))
  316. processGnomeVfs();
  317. }
  318. if (g_changed || !g_allEntries)
  319. {
  320. uptodate = false;
  321. g_changeList->append(g_resource);
  322. }
  323. }
  324. bool result = !uptodate || !g_ctimeDict->isEmpty();
  325. if (result || bMenuTest)
  326. {
  327. g_resource = "apps";
  328. g_factory = g_bsf;
  329. g_entryDict = serviceEntryDict;
  330. g_changed = false;
  331. g_vfolder = new VFolderMenu;
  332. if (!m_trackId.isEmpty())
  333. g_vfolder->setTrackId(m_trackId);
  334. connect(g_vfolder, TQT_SIGNAL(newService(const TQString &, KService **)),
  335. this, TQT_SLOT(slotCreateEntry(const TQString &, KService **)));
  336. VFolderMenu::SubMenu *kdeMenu = g_vfolder->parseMenu("applications.menu", true);
  337. KServiceGroup *entry = g_bsgf->addNew("/", kdeMenu->directoryFile, 0, false);
  338. entry->setLayoutInfo(kdeMenu->layoutList);
  339. createMenu(TQString::null, TQString::null, kdeMenu);
  340. KServiceGroup::Ptr g(entry);
  341. (void) existingResourceDirs();
  342. *g_allResourceDirs += g_vfolder->allDirectories();
  343. disconnect(g_vfolder, TQT_SIGNAL(newService(const TQString &, KService **)),
  344. this, TQT_SLOT(slotCreateEntry(const TQString &, KService **)));
  345. if (g_changed || !g_allEntries)
  346. {
  347. uptodate = false;
  348. g_changeList->append(g_resource);
  349. }
  350. if (bMenuTest)
  351. return false;
  352. }
  353. return result;
  354. }
  355. void KBuildSycoca::createMenu(TQString caption, TQString name, VFolderMenu::SubMenu *menu)
  356. {
  357. for(VFolderMenu::SubMenu *subMenu = menu->subMenus.first(); subMenu; subMenu = menu->subMenus.next())
  358. {
  359. TQString subName = name+subMenu->name+"/";
  360. TQString directoryFile = subMenu->directoryFile;
  361. if (directoryFile.isEmpty())
  362. directoryFile = subName+".directory";
  363. TQ_UINT32 timeStamp = g_ctimeInfo->ctime(directoryFile);
  364. if (!timeStamp)
  365. {
  366. timeStamp = TDEGlobal::dirs()->calcResourceHash( g_resource, directoryFile, true);
  367. }
  368. KServiceGroup* entry = 0;
  369. if (g_allEntries)
  370. {
  371. TQ_UINT32 *timeP = (*g_ctimeDict)[directoryFile];
  372. TQ_UINT32 oldTimestamp = timeP ? *timeP : 0;
  373. if (timeStamp && (timeStamp == oldTimestamp))
  374. {
  375. entry = dynamic_cast<KServiceGroup *> (g_serviceGroupEntryDict->find(subName));
  376. if (entry && (entry->directoryEntryPath() != directoryFile))
  377. entry = 0; // Can't reuse this one!
  378. }
  379. }
  380. g_ctimeInfo->addCTime(directoryFile, timeStamp);
  381. entry = g_bsgf->addNew(subName, subMenu->directoryFile, entry, subMenu->isDeleted);
  382. entry->setLayoutInfo(subMenu->layoutList);
  383. if (! (bMenuTest && entry->noDisplay()) )
  384. createMenu(caption + entry->caption() + "/", subName, subMenu);
  385. }
  386. if (caption.isEmpty())
  387. caption += "/";
  388. if (name.isEmpty())
  389. name += "/";
  390. for(TQDictIterator<KService> it(menu->items); it.current(); ++it)
  391. {
  392. if (bMenuTest)
  393. {
  394. if (!menu->isDeleted && !it.current()->noDisplay())
  395. printf("%s\t%s\t%s\n", caption.local8Bit().data(), it.current()->menuId().local8Bit().data(), locate("apps", it.current()->desktopEntryPath()).local8Bit().data());
  396. }
  397. else
  398. {
  399. g_bsf->addEntry( it.current(), g_resource );
  400. g_bsgf->addNewEntryTo(name, it.current());
  401. }
  402. }
  403. }
  404. bool KBuildSycoca::recreate()
  405. {
  406. TQString path(sycocaPath());
  407. #ifdef Q_WS_WIN
  408. printf("tdebuildsycoca: path='%s'\n", (const char*)path);
  409. #endif
  410. // KSaveFile first writes to a temp file.
  411. // Upon close() it moves the stuff to the right place.
  412. std::auto_ptr<KSaveFile> database( new KSaveFile(path) );
  413. if (database->status() == EACCES && TQFile::exists(path))
  414. {
  415. TQFile::remove( path );
  416. database.reset( new KSaveFile(path) ); // try again
  417. }
  418. if (database->status() != 0)
  419. {
  420. fprintf(stderr, "[tdebuildsycoca] ERROR creating database '%s'! %s\n", path.local8Bit().data(),strerror(database->status()));
  421. #ifdef KBUILDSYCOCA_GUI // KBUILDSYCOCA_GUI is used on win32 to build
  422. // GUI version of tdebuildsycoca, so-called "tdebuildsycocaw".
  423. if (!silent)
  424. 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"));
  425. #endif
  426. return false;
  427. }
  428. m_str = database->dataStream();
  429. kdDebug(7021) << "Recreating tdesycoca file (" << path << ", version " << KSycoca::version() << ")" << endl;
  430. // It is very important to build the servicetype one first
  431. // Both are registered in KSycoca, no need to keep the pointers
  432. KSycocaFactory *stf = new KBuildServiceTypeFactory;
  433. g_bsgf = new KBuildServiceGroupFactory();
  434. g_bsf = new KBuildServiceFactory(stf, g_bsgf);
  435. (void) new KBuildImageIOFactory();
  436. (void) new KBuildProtocolInfoFactory();
  437. if( build()) // Parse dirs
  438. {
  439. save(); // Save database
  440. if (m_str->device()->status())
  441. database->abort(); // Error
  442. m_str = 0L;
  443. if (!database->close())
  444. {
  445. fprintf(stderr, "[tdebuildsycoca] ERROR writing database '%s'!\n", database->name().local8Bit().data());
  446. fprintf(stderr, "[tdebuildsycoca] Disk full?\n");
  447. #ifdef KBUILDSYCOCA_GUI
  448. if (!silent)
  449. 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"));
  450. #endif
  451. return false;
  452. }
  453. }
  454. else
  455. {
  456. m_str = 0L;
  457. database->abort();
  458. if (bMenuTest)
  459. return true;
  460. kdDebug(7021) << "Database is up to date" << endl;
  461. }
  462. if (!bGlobalDatabase)
  463. {
  464. // update the timestamp file
  465. TQString stamppath = path + "stamp";
  466. TQFile tdesycocastamp(stamppath);
  467. tdesycocastamp.open( IO_WriteOnly );
  468. TQDataStream str( &tdesycocastamp );
  469. str << newTimestamp;
  470. str << existingResourceDirs();
  471. if (g_vfolder)
  472. str << g_vfolder->allDirectories(); // Extra resource dirs
  473. }
  474. return true;
  475. }
  476. void KBuildSycoca::save()
  477. {
  478. // Write header (#pass 1)
  479. m_str->device()->at(0);
  480. (*m_str) << (TQ_INT32) KSycoca::version();
  481. KSycocaFactory * servicetypeFactory = 0L;
  482. KSycocaFactory * serviceFactory = 0L;
  483. for(KSycocaFactory *factory = m_lstFactories->first();
  484. factory;
  485. factory = m_lstFactories->next())
  486. {
  487. TQ_INT32 aId;
  488. TQ_INT32 aOffset;
  489. aId = factory->factoryId();
  490. if ( aId == KST_KServiceTypeFactory )
  491. servicetypeFactory = factory;
  492. else if ( aId == KST_KServiceFactory )
  493. serviceFactory = factory;
  494. aOffset = factory->offset();
  495. (*m_str) << aId;
  496. (*m_str) << aOffset;
  497. }
  498. (*m_str) << (TQ_INT32) 0; // No more factories.
  499. // Write TDEDIRS
  500. (*m_str) << TDEGlobal::dirs()->kfsstnd_prefixes();
  501. (*m_str) << newTimestamp;
  502. (*m_str) << TDEGlobal::locale()->language();
  503. (*m_str) << TDEGlobal::dirs()->calcResourceHash("services", "update_tdesycoca", true);
  504. (*m_str) << (*g_allResourceDirs);
  505. // Write factory data....
  506. for(KSycocaFactory *factory = m_lstFactories->first();
  507. factory;
  508. factory = m_lstFactories->next())
  509. {
  510. factory->save(*m_str);
  511. if (m_str->device()->status())
  512. return; // error
  513. }
  514. int endOfData = m_str->device()->at();
  515. // Write header (#pass 2)
  516. m_str->device()->at(0);
  517. (*m_str) << (TQ_INT32) KSycoca::version();
  518. for(KSycocaFactory *factory = m_lstFactories->first();
  519. factory;
  520. factory = m_lstFactories->next())
  521. {
  522. TQ_INT32 aId;
  523. TQ_INT32 aOffset;
  524. aId = factory->factoryId();
  525. aOffset = factory->offset();
  526. (*m_str) << aId;
  527. (*m_str) << aOffset;
  528. }
  529. (*m_str) << (TQ_INT32) 0; // No more factories.
  530. // Jump to end of database
  531. m_str->device()->at(endOfData);
  532. }
  533. bool KBuildSycoca::checkDirTimestamps( const TQString& dirname, const TQDateTime& stamp, bool top )
  534. {
  535. if( top )
  536. {
  537. TQFileInfo inf( dirname );
  538. if( inf.lastModified() > stamp )
  539. {
  540. kdDebug( 7021 ) << "timestamp changed:" << dirname << endl;
  541. return false;
  542. }
  543. }
  544. TQDir dir( dirname );
  545. const TQFileInfoList *list = dir.entryInfoList( TQDir::DefaultFilter, TQDir::Unsorted );
  546. if (!list)
  547. return true;
  548. for( TQFileInfoListIterator it( *list );
  549. it.current() != NULL;
  550. ++it )
  551. {
  552. TQFileInfo* fi = it.current();
  553. if( fi->fileName() == "." || fi->fileName() == ".." )
  554. continue;
  555. if( fi->lastModified() > stamp )
  556. {
  557. kdDebug( 7201 ) << "timestamp changed:" << fi->filePath() << endl;
  558. return false;
  559. }
  560. if( fi->isDir() && !checkDirTimestamps( fi->filePath(), stamp, false ))
  561. return false;
  562. }
  563. return true;
  564. }
  565. // check times of last modification of all files on which tdesycoca depens,
  566. // and also their directories
  567. // if all of them all older than the timestamp in file tdesycocastamp, this
  568. // means that there's no need to rebuild tdesycoca
  569. bool KBuildSycoca::checkTimestamps( TQ_UINT32 timestamp, const TQStringList &dirs )
  570. {
  571. kdDebug( 7021 ) << "checking file timestamps" << endl;
  572. TQDateTime stamp;
  573. stamp.setTime_t( timestamp );
  574. for( TQStringList::ConstIterator it = dirs.begin();
  575. it != dirs.end();
  576. ++it )
  577. {
  578. if( !checkDirTimestamps( *it, stamp, true ))
  579. return false;
  580. }
  581. kdDebug( 7021 ) << "timestamps check ok" << endl;
  582. return true;
  583. }
  584. TQStringList KBuildSycoca::existingResourceDirs()
  585. {
  586. static TQStringList* dirs = NULL;
  587. if( dirs != NULL )
  588. return *dirs;
  589. dirs = new TQStringList;
  590. g_allResourceDirs = new TQStringList;
  591. // these are all resources cached by tdesycoca
  592. TQStringList resources;
  593. resources += KBuildServiceTypeFactory::resourceTypes();
  594. resources += KBuildServiceGroupFactory::resourceTypes();
  595. resources += KBuildServiceFactory::resourceTypes();
  596. resources += KBuildImageIOFactory::resourceTypes();
  597. resources += KBuildProtocolInfoFactory::resourceTypes();
  598. while( !resources.empty())
  599. {
  600. TQString res = resources.front();
  601. *dirs += TDEGlobal::dirs()->resourceDirs( res.latin1());
  602. resources.remove( res ); // remove this 'res' and all its duplicates
  603. }
  604. *g_allResourceDirs = *dirs;
  605. for( TQStringList::Iterator it = dirs->begin();
  606. it != dirs->end(); )
  607. {
  608. TQFileInfo inf( *it );
  609. if( !inf.exists() || !inf.isReadable() )
  610. it = dirs->remove( it );
  611. else
  612. ++it;
  613. }
  614. return *dirs;
  615. }
  616. static TDECmdLineOptions options[] = {
  617. { "nosignal", I18N_NOOP("Do not signal applications to update"), 0 },
  618. { "noincremental", I18N_NOOP("Disable incremental update, re-read everything"), 0 },
  619. { "checkstamps", I18N_NOOP("Check file timestamps"), 0 },
  620. { "nocheckfiles", I18N_NOOP("Disable checking files (dangerous)"), 0 },
  621. { "global", I18N_NOOP("Create global database"), 0 },
  622. { "menutest", I18N_NOOP("Perform menu generation test run only"), 0 },
  623. { "track <menu-id>", I18N_NOOP("Track menu id for debug purposes"), 0 },
  624. #ifdef KBUILDSYCOCA_GUI
  625. { "silent", I18N_NOOP("Silent - work without windows and stderr"), 0 },
  626. { "showprogress", I18N_NOOP("Show progress information (even if 'silent' mode is on)"), 0 },
  627. #endif
  628. TDECmdLineLastOption
  629. };
  630. static const char appName[] = "tdebuildsycoca";
  631. static const char appVersion[] = "1.1";
  632. class WaitForSignal : public QObject
  633. {
  634. public:
  635. ~WaitForSignal() { kapp->eventLoop()->exitLoop(); }
  636. };
  637. extern "C" KDE_EXPORT int kdemain(int argc, char **argv)
  638. {
  639. TDELocale::setMainCatalogue("tdelibs");
  640. TDEAboutData d(appName, I18N_NOOP("KBuildSycoca"), appVersion,
  641. I18N_NOOP("Rebuilds the system configuration cache."),
  642. TDEAboutData::License_GPL, "(c) 1999-2002 KDE Developers");
  643. d.addAuthor("David Faure", I18N_NOOP("Author"), "faure@kde.org");
  644. d.addAuthor("Waldo Bastian", I18N_NOOP("Author"), "bastian@kde.org");
  645. TDECmdLineArgs::init(argc, argv, &d);
  646. TDECmdLineArgs::addCmdLineOptions(options);
  647. TDECmdLineArgs *args = TDECmdLineArgs::parsedArgs();
  648. bGlobalDatabase = args->isSet("global");
  649. bMenuTest = args->isSet("menutest");
  650. if (bGlobalDatabase)
  651. {
  652. setenv("TDEHOME", "-", 1);
  653. setenv("TDEROOTHOME", "-", 1);
  654. }
  655. TDEApplication::disableAutoDcopRegistration();
  656. #ifdef KBUILDSYCOCA_GUI
  657. TDEApplication k;
  658. #else
  659. TDEApplication k(false, false);
  660. #endif
  661. k.disableSessionManagement();
  662. #ifdef KBUILDSYCOCA_GUI
  663. silent = args->isSet("silent");
  664. showprogress = args->isSet("showprogress");
  665. 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 );
  666. TQString capt = i18n("TDE Configuration Manager");
  667. if (!silent) {
  668. if (KMessageBox::No == KMessageBox::questionYesNo(0, i18n("Do you want to reload TDE configuration?"), capt, i18n("Reload"), i18n("Do Not Reload")))
  669. return 0;
  670. }
  671. if (!silent || showprogress) {
  672. progress.setCaption( capt );
  673. progress.show();
  674. }
  675. #endif
  676. TDECrash::setCrashHandler(TDECrash::defaultCrashHandler);
  677. TDECrash::setEmergencySaveFunction(crashHandler);
  678. TDECrash::setApplicationName(TQString(appName));
  679. // this program is in tdelibs so it uses tdelibs as catalog
  680. TDELocale::setMainCatalogue("tdelibs");
  681. // force generating of TDELocale object. if not, the database will get
  682. // be translated
  683. TDEGlobal::locale();
  684. TDEGlobal::dirs()->addResourceType("app-reg", "share/application-registry" );
  685. DCOPClient *dcopClient = new DCOPClient();
  686. while(true)
  687. {
  688. TQCString registeredName = dcopClient->registerAs(appName, false);
  689. if (registeredName.isEmpty())
  690. {
  691. fprintf(stderr, "[tdebuildsycoca] Warning: %s is unable to register with DCOP.\n", appName);
  692. break;
  693. }
  694. else if (registeredName == appName)
  695. {
  696. break; // Go
  697. }
  698. fprintf(stderr, "[tdebuildsycoca] Waiting for already running %s to finish.\n", appName);
  699. dcopClient->setNotifications( true );
  700. while (dcopClient->isApplicationRegistered(appName))
  701. {
  702. WaitForSignal *obj = new WaitForSignal;
  703. obj->connect(dcopClient, TQT_SIGNAL(applicationRemoved(const TQCString &)),
  704. TQT_SLOT(deleteLater()));
  705. kapp->eventLoop()->enterLoop();
  706. }
  707. dcopClient->setNotifications( false );
  708. }
  709. fprintf(stderr, "[tdebuildsycoca] %s running...\n", appName);
  710. bool checkfiles = bGlobalDatabase || args->isSet("checkfiles");
  711. bool incremental = !bGlobalDatabase && args->isSet("incremental") && checkfiles;
  712. if (incremental || !checkfiles)
  713. {
  714. KSycoca::self()->disableAutoRebuild(); // Prevent deadlock
  715. TQString current_language = TDEGlobal::locale()->language();
  716. TQString tdesycoca_language = KSycoca::self()->language();
  717. TQ_UINT32 current_update_sig = TDEGlobal::dirs()->calcResourceHash("services", "update_tdesycoca", true);
  718. TQ_UINT32 tdesycoca_update_sig = KSycoca::self()->updateSignature();
  719. if ((current_update_sig != tdesycoca_update_sig) ||
  720. (current_language != tdesycoca_language) ||
  721. (KSycoca::self()->timeStamp() == 0))
  722. {
  723. incremental = false;
  724. checkfiles = true;
  725. delete KSycoca::self();
  726. }
  727. }
  728. g_changeList = new TQStringList;
  729. bool checkstamps = incremental && args->isSet("checkstamps") && checkfiles;
  730. TQ_UINT32 filestamp = 0;
  731. TQStringList oldresourcedirs;
  732. if( checkstamps && incremental )
  733. {
  734. TQString path = sycocaPath()+"stamp";
  735. TQCString qPath = TQFile::encodeName(path);
  736. cSycocaPath = qPath.data(); // Delete timestamps on crash
  737. TQFile tdesycocastamp(path);
  738. if( tdesycocastamp.open( IO_ReadOnly ))
  739. {
  740. TQDataStream str( &tdesycocastamp );
  741. if (!str.atEnd())
  742. str >> filestamp;
  743. if (!str.atEnd())
  744. {
  745. str >> oldresourcedirs;
  746. if( oldresourcedirs != KBuildSycoca::existingResourceDirs())
  747. checkstamps = false;
  748. }
  749. else
  750. {
  751. checkstamps = false;
  752. }
  753. if (!str.atEnd())
  754. {
  755. TQStringList extraResourceDirs;
  756. str >> extraResourceDirs;
  757. oldresourcedirs += extraResourceDirs;
  758. }
  759. }
  760. else
  761. {
  762. checkstamps = false;
  763. }
  764. cSycocaPath = 0;
  765. }
  766. newTimestamp = (TQ_UINT32) time(0);
  767. if( checkfiles && ( !checkstamps || !KBuildSycoca::checkTimestamps( filestamp, oldresourcedirs )))
  768. {
  769. TQCString qSycocaPath = TQFile::encodeName(sycocaPath());
  770. cSycocaPath = qSycocaPath.data();
  771. g_allEntries = 0;
  772. g_ctimeDict = 0;
  773. if (incremental)
  774. {
  775. tqWarning("[tdebuildsycoca] Reusing existing tdesycoca.");
  776. KSycoca *oldSycoca = KSycoca::self();
  777. KSycocaFactoryList *factories = new KSycocaFactoryList;
  778. g_allEntries = new KSycocaEntryListList;
  779. g_ctimeDict = new TQDict<TQ_UINT32>(523);
  780. // Must be in same order as in KBuildSycoca::recreate()!
  781. factories->append( new KServiceTypeFactory );
  782. factories->append( new KServiceGroupFactory );
  783. factories->append( new KServiceFactory );
  784. factories->append( new KImageIOFactory );
  785. factories->append( new KProtocolInfoFactory );
  786. // For each factory
  787. for (KSycocaFactory *factory = factories->first();
  788. factory;
  789. factory = factories->next() )
  790. {
  791. KSycocaEntry::List list;
  792. list = factory->allEntries();
  793. g_allEntries->append( list );
  794. }
  795. delete factories; factories = 0;
  796. KCTimeInfo *ctimeInfo = new KCTimeInfo;
  797. ctimeInfo->fillCTimeDict(*g_ctimeDict);
  798. delete oldSycoca;
  799. }
  800. cSycocaPath = 0;
  801. KBuildSycoca *sycoca= new KBuildSycoca; // Build data base
  802. if (args->isSet("track"))
  803. sycoca->setTrackId(TQString::fromLocal8Bit(args->getOption("track")));
  804. if (!sycoca->recreate()) {
  805. #ifdef KBUILDSYCOCA_GUI
  806. if (!silent || showprogress)
  807. progress.close();
  808. #endif
  809. return -1;
  810. }
  811. if (bGlobalDatabase)
  812. {
  813. // These directories may have been created with 0700 permission
  814. // better delete them if they are empty
  815. TQString applnkDir = TDEGlobal::dirs()->saveLocation("apps", TQString::null, false);
  816. ::rmdir(TQFile::encodeName(applnkDir));
  817. TQString servicetypesDir = TDEGlobal::dirs()->saveLocation("servicetypes", TQString::null, false);
  818. ::rmdir(TQFile::encodeName(servicetypesDir));
  819. }
  820. }
  821. if (!bGlobalDatabase)
  822. {
  823. // Recreate compatibility symlink
  824. TQString oldPath = oldSycocaPath();
  825. if (!oldPath.isEmpty())
  826. {
  827. KTempFile tmp;
  828. if (tmp.status() == 0)
  829. {
  830. TQString tmpFile = tmp.name();
  831. tmp.unlink();
  832. symlink(TQFile::encodeName(sycocaPath()), TQFile::encodeName(tmpFile));
  833. rename(TQFile::encodeName(tmpFile), TQFile::encodeName(oldPath));
  834. }
  835. }
  836. }
  837. if (args->isSet("signal"))
  838. {
  839. // Notify ALL applications that have a tdesycoca object, using a broadcast
  840. TQByteArray data;
  841. TQDataStream stream(data, IO_WriteOnly);
  842. stream << *g_changeList;
  843. dcopClient->send( "*", "tdesycoca", "notifyDatabaseChanged(TQStringList)", data );
  844. }
  845. #ifdef KBUILDSYCOCA_GUI
  846. if (!silent) {
  847. progress.close();
  848. KMessageBox::information(0, i18n("[tdebuildsycoca] Configuration information reloaded successfully."), capt);
  849. }
  850. #endif
  851. return 0;
  852. }
  853. #include "tdebuildsycoca.moc"