TDE core libraries
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

tdeconfigbackend.cpp 32KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190
  1. /*
  2. This file is part of the KDE libraries
  3. Copyright (c) 1999 Preston Brown <pbrown@kde.org>
  4. Copyright (c) 1997-1999 Matthias Kalle Dalheimer <kalle@kde.org>
  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 as published by the Free Software Foundation; either
  8. version 2 of the License, or (at your option) any later version.
  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. You should have received a copy of the GNU Library General Public License
  14. along with this library; see the file COPYING.LIB. If not, write to
  15. the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
  16. Boston, MA 02110-1301, USA.
  17. */
  18. #include <config.h>
  19. #include <unistd.h>
  20. #include <ctype.h>
  21. #ifdef HAVE_SYS_MMAN_H
  22. #include <sys/mman.h>
  23. #endif
  24. #include <sys/types.h>
  25. #ifdef HAVE_SYS_STAT_H
  26. #include <sys/stat.h>
  27. #endif
  28. #include <fcntl.h>
  29. #include <signal.h>
  30. #include <setjmp.h>
  31. #include <tqdir.h>
  32. #include <tqfileinfo.h>
  33. #include <tqtextcodec.h>
  34. #include <tqtextstream.h>
  35. #include "tdeconfigbackend.h"
  36. #include "tdeconfigbase.h"
  37. #include <tdeapplication.h>
  38. #include <tdeglobal.h>
  39. #include <kprocess.h>
  40. #include <tdelocale.h>
  41. #include <kstandarddirs.h>
  42. #include <ksavefile.h>
  43. #include <kurl.h>
  44. #include <kde_file.h>
  45. extern bool checkAccess(const TQString& pathname, int mode);
  46. /* translate escaped escape sequences to their actual values. */
  47. static TQCString printableToString(const char *str, int l)
  48. {
  49. // Strip leading white-space.
  50. while((l>0) &&
  51. ((*str == ' ') || (*str == '\t') || (*str == '\r')))
  52. {
  53. str++; l--;
  54. }
  55. // Strip trailing white-space.
  56. while((l>0) &&
  57. ((str[l-1] == ' ') || (str[l-1] == '\t') || (str[l-1] == '\r')))
  58. {
  59. l--;
  60. }
  61. TQCString result(l + 1);
  62. char *r = result.data();
  63. for(int i = 0; i < l;i++, str++)
  64. {
  65. if (*str == '\\')
  66. {
  67. i++, str++;
  68. if (i >= l) // End of line. (Line ends with single slash)
  69. {
  70. *r++ = '\\';
  71. break;
  72. }
  73. switch(*str)
  74. {
  75. case 's':
  76. *r++ = ' ';
  77. break;
  78. case 't':
  79. *r++ = '\t';
  80. break;
  81. case 'n':
  82. *r++ = '\n';
  83. break;
  84. case 'r':
  85. *r++ = '\r';
  86. break;
  87. case '\\':
  88. *r++ = '\\';
  89. break;
  90. default:
  91. *r++ = '\\';
  92. *r++ = *str;
  93. }
  94. }
  95. else
  96. {
  97. *r++ = *str;
  98. }
  99. }
  100. result.truncate(r-result.data());
  101. return result;
  102. }
  103. static TQCString stringToPrintable(const TQCString& str){
  104. TQCString result(str.length()*2); // Maximum 2x as long as source string
  105. register char *r = const_cast<TQCString&>(result).data();
  106. register char *s = const_cast<TQCString&>(str).data();
  107. if (!s) return TQCString("");
  108. // Escape leading space
  109. if (*s == ' ')
  110. {
  111. *r++ = '\\'; *r++ = 's';
  112. s++;
  113. }
  114. if (*s)
  115. {
  116. while(*s)
  117. {
  118. if (*s == '\n')
  119. {
  120. *r++ = '\\'; *r++ = 'n';
  121. }
  122. else if (*s == '\t')
  123. {
  124. *r++ = '\\'; *r++ = 't';
  125. }
  126. else if (*s == '\r')
  127. {
  128. *r++ = '\\'; *r++ = 'r';
  129. }
  130. else if (*s == '\\')
  131. {
  132. *r++ = '\\'; *r++ = '\\';
  133. }
  134. else
  135. {
  136. *r++ = *s;
  137. }
  138. s++;
  139. }
  140. // Escape trailing space
  141. if (*(r-1) == ' ')
  142. {
  143. *(r-1) = '\\'; *r++ = 's';
  144. }
  145. }
  146. result.truncate(r - result.data());
  147. return result;
  148. }
  149. static TQCString decodeGroup(const char*s, int l)
  150. {
  151. TQCString result(l);
  152. register char *r = result.data();
  153. l--; // Correct for trailing \0
  154. while(l)
  155. {
  156. if ((*s == '[') && (l > 1))
  157. {
  158. if ((*(s+1) == '['))
  159. {
  160. l--;
  161. s++;
  162. }
  163. }
  164. if ((*s == ']') && (l > 1))
  165. {
  166. if ((*(s+1) == ']'))
  167. {
  168. l--;
  169. s++;
  170. }
  171. }
  172. *r++ = *s++;
  173. l--;
  174. }
  175. result.truncate(r - result.data());
  176. return result;
  177. }
  178. static TQCString encodeGroup(const TQCString &str)
  179. {
  180. int l = str.length();
  181. TQCString result(l*2+1);
  182. register char *r = const_cast<TQCString&>(result).data();
  183. register char *s = const_cast<TQCString&>(str).data();
  184. while(l)
  185. {
  186. if ((*s == '[') || (*s == ']'))
  187. *r++ = *s;
  188. *r++ = *s++;
  189. l--;
  190. }
  191. result.truncate(r - result.data());
  192. return result;
  193. }
  194. static TQCString encodeKey(const char* key)
  195. {
  196. TQCString newKey(key);
  197. newKey.replace('[', "%5b");
  198. newKey.replace(']', "%5d");
  199. return newKey;
  200. }
  201. static TQCString decodeKey(const char* key)
  202. {
  203. TQCString newKey(key);
  204. newKey.replace("%5b", "[");
  205. newKey.replace("%5d", "]");
  206. return newKey;
  207. }
  208. class TDEConfigBackEnd::TDEConfigBackEndPrivate
  209. {
  210. public:
  211. TQDateTime localLastModified;
  212. uint localLastSize;
  213. TDELockFile::Ptr localLockFile;
  214. TDELockFile::Ptr globalLockFile;
  215. };
  216. void TDEConfigBackEnd::changeFileName(const TQString &_fileName,
  217. const char * _resType,
  218. bool _useKDEGlobals)
  219. {
  220. mfileName = _fileName;
  221. resType = _resType;
  222. useKDEGlobals = _useKDEGlobals;
  223. if (mfileName.isEmpty()) {
  224. mLocalFileName = TQString::null;
  225. }
  226. else if (!TQDir::isRelativePath(mfileName)) {
  227. mLocalFileName = mfileName;
  228. }
  229. else {
  230. mLocalFileName = TDEGlobal::dirs()->saveLocation(resType, TQString(), false) + mfileName;
  231. }
  232. if (useKDEGlobals) {
  233. mGlobalFileName = TDEGlobal::dirs()->saveLocation("config", TQString(), false) + TQString::fromLatin1("kdeglobals");
  234. }
  235. else {
  236. mGlobalFileName = TQString::null;
  237. }
  238. d->localLastModified = TQDateTime();
  239. d->localLastSize = 0;
  240. d->localLockFile = 0;
  241. d->globalLockFile = 0;
  242. }
  243. TDELockFile::Ptr TDEConfigBackEnd::lockFile(bool bGlobal)
  244. {
  245. if (bGlobal)
  246. {
  247. if (d->globalLockFile)
  248. return d->globalLockFile;
  249. if (!mGlobalFileName.isEmpty())
  250. {
  251. d->globalLockFile = new TDELockFile(mGlobalFileName+".lock");
  252. return d->globalLockFile;
  253. }
  254. }
  255. else
  256. {
  257. if (d->localLockFile)
  258. return d->localLockFile;
  259. if (!mLocalFileName.isEmpty())
  260. {
  261. d->localLockFile = new TDELockFile(mLocalFileName+".lock");
  262. return d->localLockFile;
  263. }
  264. }
  265. return 0;
  266. }
  267. TDEConfigBackEnd::TDEConfigBackEnd(TDEConfigBase *_config,
  268. const TQString &_fileName,
  269. const char * _resType,
  270. bool _useKDEGlobals)
  271. : pConfig(_config), bFileImmutable(false), mConfigState(TDEConfigBase::NoAccess), mFileMode(-1)
  272. {
  273. d = new TDEConfigBackEndPrivate;
  274. changeFileName(_fileName, _resType, _useKDEGlobals);
  275. }
  276. TDEConfigBackEnd::~TDEConfigBackEnd()
  277. {
  278. delete d;
  279. }
  280. void TDEConfigBackEnd::setFileWriteMode(int mode)
  281. {
  282. mFileMode = mode;
  283. }
  284. bool TDEConfigINIBackEnd::parseConfigFiles()
  285. {
  286. // Check if we can write to the local file.
  287. mConfigState = TDEConfigBase::ReadOnly;
  288. if (!mLocalFileName.isEmpty() && !pConfig->isReadOnly())
  289. {
  290. if (checkAccess(mLocalFileName, W_OK))
  291. {
  292. mConfigState = TDEConfigBase::ReadWrite;
  293. }
  294. else
  295. {
  296. // Create the containing dir, maybe it wasn't there
  297. KURL path;
  298. path.setPath(mLocalFileName);
  299. TQString dir=path.directory();
  300. TDEStandardDirs::makeDir(dir);
  301. if (checkAccess(mLocalFileName, W_OK))
  302. {
  303. mConfigState = TDEConfigBase::ReadWrite;
  304. }
  305. }
  306. TQFileInfo info(mLocalFileName);
  307. d->localLastModified = info.lastModified();
  308. d->localLastSize = info.size();
  309. }
  310. // Parse all desired files from the least to the most specific.
  311. bFileImmutable = false;
  312. // Parse the general config files
  313. if (useKDEGlobals) {
  314. TQStringList tdercs = TDEGlobal::dirs()->
  315. findAllResources("config", TQString::fromLatin1("kdeglobals"));
  316. #ifdef Q_WS_WIN
  317. TQString etc_tderc = TQFile::decodeName( TQCString(getenv("WINDIR")) + "\\tderc" );
  318. #else
  319. TQString etc_tderc = TQString::fromLatin1("/etc/tderc");
  320. #endif
  321. if (checkAccess(etc_tderc, R_OK))
  322. tdercs += etc_tderc;
  323. tdercs += TDEGlobal::dirs()->
  324. findAllResources("config", TQString::fromLatin1("system.kdeglobals"));
  325. TQStringList::ConstIterator it;
  326. for (it = tdercs.fromLast(); it != tdercs.end(); --it) {
  327. TQFile aConfigFile( *it );
  328. if (!aConfigFile.open( IO_ReadOnly ))
  329. continue;
  330. parseSingleConfigFile( aConfigFile, 0L, true, (*it != mGlobalFileName) );
  331. aConfigFile.close();
  332. if (bFileImmutable)
  333. break;
  334. }
  335. }
  336. bool bReadFile = !mfileName.isEmpty();
  337. while(bReadFile) {
  338. bReadFile = false;
  339. TQString bootLanguage;
  340. if (useKDEGlobals && localeString.isEmpty() && !TDEGlobal::_locale) {
  341. // Boot strap language
  342. bootLanguage = TDELocale::_initLanguage(pConfig);
  343. setLocaleString(bootLanguage.utf8());
  344. }
  345. bFileImmutable = false;
  346. TQStringList list;
  347. if ( !TQDir::isRelativePath(mfileName) )
  348. list << mfileName;
  349. else
  350. list = TDEGlobal::dirs()->findAllResources(resType, mfileName);
  351. TQStringList::ConstIterator it;
  352. for (it = list.fromLast(); it != list.end(); --it) {
  353. TQFile aConfigFile( *it );
  354. // we can already be sure that this file exists
  355. bool bIsLocal = (*it == mLocalFileName);
  356. if (aConfigFile.open( IO_ReadOnly )) {
  357. parseSingleConfigFile( aConfigFile, 0L, false, !bIsLocal );
  358. aConfigFile.close();
  359. if (bFileImmutable)
  360. break;
  361. }
  362. }
  363. if (TDEGlobal::dirs()->isRestrictedResource(resType, mfileName))
  364. bFileImmutable = true;
  365. TQString currentLanguage;
  366. if (!bootLanguage.isEmpty())
  367. {
  368. currentLanguage = TDELocale::_initLanguage(pConfig);
  369. // If the file changed the language, we need to read the file again
  370. // with the new language setting.
  371. if (bootLanguage != currentLanguage)
  372. {
  373. bReadFile = true;
  374. setLocaleString(currentLanguage.utf8());
  375. }
  376. }
  377. }
  378. if (bFileImmutable)
  379. mConfigState = TDEConfigBase::ReadOnly;
  380. return true;
  381. }
  382. #ifdef HAVE_MMAP
  383. #ifdef SIGBUS
  384. static sigjmp_buf mmap_jmpbuf;
  385. struct sigaction mmap_old_sigact;
  386. extern "C" {
  387. static void mmap_sigbus_handler(int)
  388. {
  389. siglongjmp (mmap_jmpbuf, 1);
  390. }
  391. }
  392. #endif
  393. #endif
  394. extern bool kde_kiosk_exception;
  395. void TDEConfigINIBackEnd::parseSingleConfigFile(TQFile &rFile,
  396. KEntryMap *pWriteBackMap,
  397. bool bGlobal, bool bDefault)
  398. {
  399. const char *s; // May get clobbered by sigsetjump, but we don't use them afterwards.
  400. const char *eof; // May get clobbered by sigsetjump, but we don't use them afterwards.
  401. TQByteArray data;
  402. if (!rFile.isOpen()) // come back, if you have real work for us ;->
  403. return;
  404. //using kdDebug() here leads to an infinite loop
  405. //remove this for the release, aleXXX
  406. //tqWarning("Parsing %s, global = %s default = %s",
  407. // rFile.name().latin1(), bGlobal ? "true" : "false", bDefault ? "true" : "false");
  408. TQCString aCurrentGroup("<default>");
  409. unsigned int ll = localeString.length();
  410. #ifdef HAVE_MMAP
  411. static volatile const char *map;
  412. map = ( const char* ) mmap(0, rFile.size(), PROT_READ, MAP_PRIVATE,
  413. rFile.handle(), 0);
  414. if ( map != MAP_FAILED )
  415. {
  416. s = (const char*) map;
  417. eof = s + rFile.size();
  418. #ifdef SIGBUS
  419. struct sigaction act;
  420. act.sa_handler = mmap_sigbus_handler;
  421. sigemptyset( &act.sa_mask );
  422. #ifdef SA_ONESHOT
  423. act.sa_flags = SA_ONESHOT;
  424. #else
  425. act.sa_flags = SA_RESETHAND;
  426. #endif
  427. sigaction( SIGBUS, &act, &mmap_old_sigact );
  428. if (sigsetjmp (mmap_jmpbuf, 1))
  429. {
  430. tqWarning("SIGBUS while reading %s", rFile.name().latin1());
  431. munmap(( char* )map, rFile.size());
  432. sigaction (SIGBUS, &mmap_old_sigact, 0);
  433. return;
  434. }
  435. #endif
  436. }
  437. else
  438. #endif
  439. {
  440. rFile.at(0);
  441. data = rFile.readAll();
  442. s = data.data();
  443. eof = s + data.size();
  444. }
  445. bool fileOptionImmutable = false;
  446. bool groupOptionImmutable = false;
  447. bool groupSkip = false;
  448. bool foundGettextDomain = false;
  449. TQCString gettextDomain;
  450. int line = 0;
  451. for(; s < eof; s++)
  452. {
  453. line++;
  454. while((s < eof) && isspace(*s) && (*s != '\n'))
  455. s++; //skip leading whitespace, shouldn't happen too often
  456. //skip empty lines, lines starting with #
  457. if ((s < eof) && ((*s == '\n') || (*s == '#')))
  458. {
  459. sktoeol: //skip till end-of-line
  460. while ((s < eof) && (*s != '\n'))
  461. s++;
  462. continue; // Empty or comment or no keyword
  463. }
  464. const char *startLine = s;
  465. if (*s == '[') //group
  466. {
  467. // In a group [[ and ]] have a special meaning
  468. while ((s < eof) && (*s != '\n'))
  469. {
  470. if (*s == ']')
  471. {
  472. if ((s+1 < eof) && (*(s+1) == ']'))
  473. s++; // Skip "]]"
  474. else
  475. break;
  476. }
  477. s++; // Search till end of group
  478. }
  479. const char *e = s;
  480. while ((s < eof) && (*s != '\n')) s++; // Search till end of line / end of file
  481. if ((e >= eof) || (*e != ']'))
  482. {
  483. fprintf(stderr, "Invalid group header at %s:%d\n", rFile.name().latin1(), line);
  484. continue;
  485. }
  486. // group found; get the group name by taking everything in
  487. // between the brackets
  488. if ((e-startLine == 3) &&
  489. (startLine[1] == '$') &&
  490. (startLine[2] == 'i'))
  491. {
  492. if (!kde_kiosk_exception)
  493. fileOptionImmutable = true;
  494. continue;
  495. }
  496. aCurrentGroup = decodeGroup(startLine + 1, e - startLine);
  497. //cout<<"found group ["<<aCurrentGroup<<"]"<<endl;
  498. // Backwards compatibility
  499. if (aCurrentGroup == "KDE Desktop Entry")
  500. aCurrentGroup = "Desktop Entry";
  501. groupOptionImmutable = fileOptionImmutable;
  502. e++;
  503. if ((e+2 < eof) && (*e++ == '[') && (*e++ == '$')) // Option follows
  504. {
  505. if ((*e == 'i') && !kde_kiosk_exception)
  506. {
  507. groupOptionImmutable = true;
  508. }
  509. }
  510. KEntryKey groupKey(aCurrentGroup, 0);
  511. KEntry entry = pConfig->lookupData(groupKey);
  512. groupSkip = entry.bImmutable;
  513. if (groupSkip && !bDefault)
  514. continue;
  515. entry.bImmutable |= groupOptionImmutable;
  516. pConfig->putData(groupKey, entry, false);
  517. if (pWriteBackMap)
  518. {
  519. // add the special group key indicator
  520. (*pWriteBackMap)[groupKey] = entry;
  521. }
  522. continue;
  523. }
  524. if (groupSkip && !bDefault)
  525. goto sktoeol; // Skip entry
  526. bool optionImmutable = groupOptionImmutable;
  527. bool optionDeleted = false;
  528. bool optionExpand = false;
  529. const char *endOfKey = 0, *locale = 0, *elocale = 0;
  530. for (; (s < eof) && (*s != '\n'); s++)
  531. {
  532. if (*s == '=') //find the equal sign
  533. {
  534. if (!endOfKey)
  535. endOfKey = s;
  536. goto haveeq;
  537. }
  538. if (*s == '[') //find the locale or options.
  539. {
  540. const char *option;
  541. const char *eoption;
  542. endOfKey = s;
  543. option = ++s;
  544. for (;; s++)
  545. {
  546. if ((s >= eof) || (*s == '\n') || (*s == '=')) {
  547. fprintf(stderr, "Invalid entry (missing ']') at %s:%d\n", rFile.name().latin1(), line);
  548. goto sktoeol;
  549. }
  550. if (*s == ']')
  551. break;
  552. }
  553. eoption = s;
  554. if (*option != '$')
  555. {
  556. // Locale
  557. if (locale) {
  558. fprintf(stderr, "Invalid entry (second locale!?) at %s:%d\n", rFile.name().latin1(), line);
  559. goto sktoeol;
  560. }
  561. locale = option;
  562. elocale = eoption;
  563. }
  564. else
  565. {
  566. // Option
  567. while (option < eoption)
  568. {
  569. option++;
  570. if ((*option == 'i') && !kde_kiosk_exception)
  571. optionImmutable = true;
  572. else if (*option == 'e')
  573. optionExpand = true;
  574. else if (*option == 'd')
  575. {
  576. optionDeleted = true;
  577. goto haveeq;
  578. }
  579. else if (*option == ']')
  580. break;
  581. }
  582. }
  583. }
  584. }
  585. fprintf(stderr, "Invalid entry (missing '=') at %s:%d\n", rFile.name().latin1(), line);
  586. continue;
  587. haveeq:
  588. for (endOfKey--; ; endOfKey--)
  589. {
  590. if (endOfKey < startLine)
  591. {
  592. fprintf(stderr, "Invalid entry (empty key) at %s:%d\n", rFile.name().latin1(), line);
  593. goto sktoeol;
  594. }
  595. if (!isspace(*endOfKey))
  596. break;
  597. }
  598. const char *st = ++s;
  599. while ((s < eof) && (*s != '\n')) s++; // Search till end of line / end of file
  600. if (locale) {
  601. unsigned int cl = static_cast<unsigned int>(elocale - locale);
  602. if ((ll != cl) || memcmp(locale, localeString.data(), ll))
  603. {
  604. // backward compatibility. C == en_US
  605. if ( cl != 1 || ll != 5 || *locale != 'C' || memcmp(localeString.data(), "en_US", 5)) {
  606. //cout<<"mismatched locale '"<<TQCString(locale, elocale-locale +1)<<"'"<<endl;
  607. // We can ignore this one
  608. if (!pWriteBackMap)
  609. continue; // We just ignore it
  610. // We just store it as is to be able to write it back later.
  611. endOfKey = elocale;
  612. locale = 0;
  613. }
  614. }
  615. }
  616. // insert the key/value line
  617. TQCString key(startLine, endOfKey - startLine + 2);
  618. TQCString val = printableToString(st, s - st);
  619. //tqDebug("found key '%s' with value '%s'", key.data(), val.data());
  620. if (TQString(key.data()) == "X-Ubuntu-Gettext-Domain") {
  621. gettextDomain = val.data();
  622. foundGettextDomain = true;
  623. }
  624. KEntryKey aEntryKey(aCurrentGroup, decodeKey(key));
  625. aEntryKey.bLocal = (locale != 0);
  626. aEntryKey.bDefault = bDefault;
  627. KEntry aEntry;
  628. aEntry.mValue = val;
  629. aEntry.bGlobal = bGlobal;
  630. aEntry.bImmutable = optionImmutable;
  631. aEntry.bDeleted = optionDeleted;
  632. aEntry.bExpand = optionExpand;
  633. aEntry.bNLS = (locale != 0);
  634. if (pWriteBackMap) {
  635. // don't insert into the config object but into the temporary
  636. // scratchpad map
  637. pWriteBackMap->insert(aEntryKey, aEntry);
  638. } else {
  639. // directly insert value into config object
  640. // no need to specify localization; if the key we just
  641. // retrieved was localized already, no need to localize it again.
  642. pConfig->putData(aEntryKey, aEntry, false);
  643. }
  644. }
  645. // Look up translations using TDELocale
  646. // https://launchpad.net/distros/ubuntu/+spec/langpacks-desktopfiles-kde
  647. // This calls TDELocale up to 10 times for each config file (and each TDEConfig has up to 4 files)
  648. // so I'll see how much of a performance hit it is
  649. // it also only acts on the last group in a file
  650. // Ideas: only translate most important fields, only translate "Desktop Entry" files,
  651. // do translation per TDEConfig not per single file
  652. if (!pWriteBackMap) {
  653. TQFile file("file.txt");
  654. if (foundGettextDomain) {
  655. TDELocale locale(gettextDomain);
  656. TQString language = locale.language();
  657. translateKey(locale, aCurrentGroup, TQCString("Name"));
  658. translateKey(locale, aCurrentGroup, TQCString("Comment"));
  659. translateKey(locale, aCurrentGroup, TQCString("Language"));
  660. translateKey(locale, aCurrentGroup, TQCString("Keywords"));
  661. translateKey(locale, aCurrentGroup, TQCString("About"));
  662. translateKey(locale, aCurrentGroup, TQCString("Description"));
  663. translateKey(locale, aCurrentGroup, TQCString("GenericName"));
  664. translateKey(locale, aCurrentGroup, TQCString("Query"));
  665. translateKey(locale, aCurrentGroup, TQCString("ExtraNames"));
  666. translateKey(locale, aCurrentGroup, TQCString("X-TDE-Submenu"));
  667. }
  668. }
  669. if (fileOptionImmutable)
  670. bFileImmutable = true;
  671. #ifdef HAVE_MMAP
  672. if (map)
  673. {
  674. munmap(( char* )map, rFile.size());
  675. #ifdef SIGBUS
  676. sigaction (SIGBUS, &mmap_old_sigact, 0);
  677. #endif
  678. }
  679. #endif
  680. }
  681. void TDEConfigINIBackEnd::translateKey(TDELocale& locale, TQCString currentGroup, TQCString key) {
  682. KEntryKey entryKey = KEntryKey(currentGroup, key);
  683. KEntry entry = pConfig->lookupData(entryKey);
  684. if (TQString(entry.mValue) != "") {
  685. TQString orig = key + "=" + entry.mValue;
  686. TQString translate = locale.translate(key + "=" + entry.mValue);
  687. if (TQString::compare(orig, translate) != 0) {
  688. translate = translate.mid(key.length() + 1);
  689. entry.mValue = translate.utf8();
  690. entryKey.bLocal = true;
  691. entry.bNLS = true;
  692. pConfig->putData(entryKey, entry, false);
  693. }
  694. }
  695. }
  696. void TDEConfigINIBackEnd::sync(bool bMerge)
  697. {
  698. // write-sync is only necessary if there are dirty entries
  699. if (!pConfig->isDirty())
  700. return;
  701. bool bEntriesLeft = true;
  702. // find out the file to write to (most specific writable file)
  703. // try local app-specific file first
  704. if (!mfileName.isEmpty()) {
  705. // Create the containing dir if needed
  706. if ((resType!="config") && !TQDir::isRelativePath(mLocalFileName))
  707. {
  708. KURL path;
  709. path.setPath(mLocalFileName);
  710. TQString dir=path.directory();
  711. TDEStandardDirs::makeDir(dir);
  712. }
  713. // Can we allow the write? We can, if the program
  714. // doesn't run SUID. But if it runs SUID, we must
  715. // check if the user would be allowed to write if
  716. // it wasn't SUID.
  717. if (checkAccess(mLocalFileName, W_OK)) {
  718. // File is writable
  719. TDELockFile::Ptr lf;
  720. bool mergeLocalFile = bMerge;
  721. // Check if the file has been updated since.
  722. if (mergeLocalFile)
  723. {
  724. lf = lockFile(false); // Lock file for local file
  725. if (lf && lf->isLocked())
  726. lf = 0; // Already locked, we don't need to lock/unlock again
  727. if (lf)
  728. {
  729. lf->lock( TDELockFile::LockForce );
  730. // But what if the locking failed? Ignore it for now...
  731. }
  732. TQFileInfo info(mLocalFileName);
  733. if ((d->localLastSize == info.size()) &&
  734. (d->localLastModified == info.lastModified()))
  735. {
  736. // Not changed, don't merge.
  737. mergeLocalFile = false;
  738. }
  739. else
  740. {
  741. // Changed...
  742. d->localLastModified = TQDateTime();
  743. d->localLastSize = 0;
  744. }
  745. }
  746. bEntriesLeft = writeConfigFile( mLocalFileName, false, mergeLocalFile );
  747. // Only if we didn't have to merge anything can we use our in-memory state
  748. // the next time around. Otherwise the config-file may contain entries
  749. // that are different from our in-memory state which means we will have to
  750. // do a merge from then on.
  751. // We do not automatically update the in-memory state with the on-disk
  752. // state when writing the config to disk. We only do so when
  753. // KCOnfig::reparseConfiguration() is called.
  754. // For KDE 4.0 we may wish to reconsider that.
  755. if (!mergeLocalFile)
  756. {
  757. TQFileInfo info(mLocalFileName);
  758. d->localLastModified = info.lastModified();
  759. d->localLastSize = info.size();
  760. }
  761. if (lf) lf->unlock();
  762. }
  763. }
  764. // only write out entries to the kdeglobals file if there are any
  765. // entries marked global (indicated by bEntriesLeft) and
  766. // the useKDEGlobals flag is set.
  767. if (bEntriesLeft && useKDEGlobals) {
  768. // can we allow the write? (see above)
  769. if (checkAccess ( mGlobalFileName, W_OK )) {
  770. TDELockFile::Ptr lf = lockFile(true); // Lock file for global file
  771. if (lf && lf->isLocked())
  772. lf = 0; // Already locked, we don't need to lock/unlock again
  773. if (lf)
  774. {
  775. lf->lock( TDELockFile::LockForce );
  776. // But what if the locking failed? Ignore it for now...
  777. }
  778. writeConfigFile( mGlobalFileName, true, bMerge ); // Always merge
  779. if (lf) lf->unlock();
  780. }
  781. }
  782. }
  783. static void writeEntries(FILE *pStream, const KEntryMap& entryMap, bool defaultGroup, bool &firstEntry, const TQCString &localeString)
  784. {
  785. // now write out all other groups.
  786. TQCString currentGroup;
  787. for (KEntryMapConstIterator aIt = entryMap.begin();
  788. aIt != entryMap.end(); ++aIt)
  789. {
  790. const KEntryKey &key = aIt.key();
  791. // Either proces the default group or all others
  792. if ((key.mGroup != "<default>") == defaultGroup)
  793. continue; // Skip
  794. // Skip default values and group headers.
  795. if ((key.bDefault) || key.mKey.isEmpty())
  796. continue; // Skip
  797. const KEntry &currentEntry = *aIt;
  798. KEntryMapConstIterator aTestIt = aIt;
  799. ++aTestIt;
  800. bool hasDefault = (aTestIt != entryMap.end());
  801. if (hasDefault)
  802. {
  803. const KEntryKey &defaultKey = aTestIt.key();
  804. if ((!defaultKey.bDefault) ||
  805. (defaultKey.mKey != key.mKey) ||
  806. (defaultKey.mGroup != key.mGroup) ||
  807. (defaultKey.bLocal != key.bLocal))
  808. hasDefault = false;
  809. }
  810. if (hasDefault)
  811. {
  812. // Entry had a default value
  813. if ((currentEntry.mValue == (*aTestIt).mValue) &&
  814. (currentEntry.bDeleted == (*aTestIt).bDeleted))
  815. continue; // Same as default, don't write.
  816. }
  817. else
  818. {
  819. // Entry had no default value.
  820. if (currentEntry.bDeleted)
  821. continue; // Don't write deleted entries if there is no default.
  822. }
  823. if (!defaultGroup && (currentGroup != key.mGroup)) {
  824. if (!firstEntry)
  825. fprintf(pStream, "\n");
  826. currentGroup = key.mGroup;
  827. fprintf(pStream, "[%s]\n", encodeGroup(currentGroup).data());
  828. }
  829. firstEntry = false;
  830. // it is data for a group
  831. fputs(encodeKey(key.mKey.data()), pStream); // Key
  832. if ( currentEntry.bNLS )
  833. {
  834. fputc('[', pStream);
  835. fputs(localeString.data(), pStream);
  836. fputc(']', pStream);
  837. }
  838. if (currentEntry.bDeleted)
  839. {
  840. fputs("[$d]\n", pStream); // Deleted
  841. }
  842. else
  843. {
  844. if (currentEntry.bImmutable || currentEntry.bExpand)
  845. {
  846. fputc('[', pStream);
  847. fputc('$', pStream);
  848. if (currentEntry.bImmutable)
  849. fputc('i', pStream);
  850. if (currentEntry.bExpand)
  851. fputc('e', pStream);
  852. fputc(']', pStream);
  853. }
  854. fputc('=', pStream);
  855. fputs(stringToPrintable(currentEntry.mValue).data(), pStream);
  856. fputc('\n', pStream);
  857. }
  858. } // for loop
  859. }
  860. bool TDEConfigINIBackEnd::getEntryMap(KEntryMap &aTempMap, bool bGlobal,
  861. TQFile *mergeFile)
  862. {
  863. bool bEntriesLeft = false;
  864. bFileImmutable = false;
  865. // Read entries from disk
  866. if (mergeFile && mergeFile->open(IO_ReadOnly))
  867. {
  868. // fill the temporary structure with entries from the file
  869. parseSingleConfigFile(*mergeFile, &aTempMap, bGlobal, false );
  870. if (bFileImmutable) // File has become immutable on disk
  871. return bEntriesLeft;
  872. }
  873. KEntryMap aMap = pConfig->internalEntryMap();
  874. // augment this structure with the dirty entries from the config object
  875. for (KEntryMapIterator aIt = aMap.begin();
  876. aIt != aMap.end(); ++aIt)
  877. {
  878. const KEntry &currentEntry = *aIt;
  879. if(aIt.key().bDefault)
  880. {
  881. aTempMap.replace(aIt.key(), currentEntry);
  882. continue;
  883. }
  884. if (mergeFile && !currentEntry.bDirty)
  885. continue;
  886. // only write back entries that have the same
  887. // "globality" as the file
  888. if (currentEntry.bGlobal != bGlobal)
  889. {
  890. // wrong "globality" - might have to be saved later
  891. bEntriesLeft = true;
  892. continue;
  893. }
  894. // put this entry from the config object into the
  895. // temporary map, possibly replacing an existing entry
  896. KEntryMapIterator aIt2 = aTempMap.find(aIt.key());
  897. if (aIt2 != aTempMap.end() && (*aIt2).bImmutable)
  898. continue; // Bail out if the on-disk entry is immutable
  899. aTempMap.insert(aIt.key(), currentEntry, true);
  900. } // loop
  901. return bEntriesLeft;
  902. }
  903. /* antlarr: KDE 4.0: make the first parameter "const TQString &" */
  904. bool TDEConfigINIBackEnd::writeConfigFile(TQString filename, bool bGlobal,
  905. bool bMerge)
  906. {
  907. // is the config object read-only?
  908. if (pConfig->isReadOnly())
  909. return true; // pretend we wrote it
  910. KEntryMap aTempMap;
  911. TQFile *mergeFile = (bMerge ? new TQFile(filename) : 0);
  912. bool bEntriesLeft = getEntryMap(aTempMap, bGlobal, mergeFile);
  913. delete mergeFile;
  914. if (bFileImmutable)
  915. return true; // pretend we wrote it
  916. // OK now the temporary map should be full of ALL entries.
  917. // write it out to disk.
  918. // Check if file exists:
  919. int fileMode = -1;
  920. bool createNew = true;
  921. KDE_struct_stat buf;
  922. if (KDE_stat(TQFile::encodeName(filename), &buf) == 0)
  923. {
  924. if (buf.st_uid == getuid())
  925. {
  926. // Preserve file mode if file exists and is owned by user.
  927. fileMode = buf.st_mode & 0777;
  928. }
  929. else
  930. {
  931. // File is not owned by user:
  932. // Don't create new file but write to existing file instead.
  933. createNew = false;
  934. }
  935. }
  936. KSaveFile *pConfigFile = 0;
  937. FILE *pStream = 0;
  938. if (createNew)
  939. {
  940. pConfigFile = new KSaveFile( filename, 0600 );
  941. if (pConfigFile->status() != 0)
  942. {
  943. delete pConfigFile;
  944. return bEntriesLeft;
  945. }
  946. if (!bGlobal && (fileMode == -1))
  947. fileMode = mFileMode;
  948. if (fileMode != -1)
  949. {
  950. fchmod(pConfigFile->handle(), fileMode);
  951. }
  952. pStream = pConfigFile->fstream();
  953. }
  954. else
  955. {
  956. // Open existing file.
  957. // We use open() to ensure that we call without O_CREAT.
  958. int fd = KDE_open( TQFile::encodeName(filename), O_WRONLY | O_TRUNC );
  959. if (fd < 0)
  960. {
  961. return bEntriesLeft;
  962. }
  963. pStream = KDE_fdopen( fd, "w");
  964. if (!pStream)
  965. {
  966. close(fd);
  967. return bEntriesLeft;
  968. }
  969. }
  970. writeEntries(pStream, aTempMap);
  971. if (pConfigFile)
  972. {
  973. bool bEmptyFile = (ftell(pStream) == 0);
  974. if ( bEmptyFile && ((fileMode == -1) || (fileMode == 0600)) )
  975. {
  976. // File is empty and doesn't have special permissions: delete it.
  977. ::unlink(TQFile::encodeName(filename));
  978. pConfigFile->abort();
  979. }
  980. else
  981. {
  982. // Normal case: Close the file
  983. pConfigFile->close();
  984. }
  985. delete pConfigFile;
  986. }
  987. else
  988. {
  989. fclose(pStream);
  990. }
  991. return bEntriesLeft;
  992. }
  993. void TDEConfigINIBackEnd::writeEntries(FILE *pStream, const KEntryMap &aTempMap)
  994. {
  995. bool firstEntry = true;
  996. // Write default group
  997. ::writeEntries(pStream, aTempMap, true, firstEntry, localeString);
  998. // Write all other groups
  999. ::writeEntries(pStream, aTempMap, false, firstEntry, localeString);
  1000. }
  1001. void TDEConfigBackEnd::virtual_hook( int, void* )
  1002. { /*BASE::virtual_hook( id, data );*/ }
  1003. void TDEConfigINIBackEnd::virtual_hook( int id, void* data )
  1004. { TDEConfigBackEnd::virtual_hook( id, data ); }
  1005. bool TDEConfigBackEnd::checkConfigFilesWritable(bool warnUser)
  1006. {
  1007. // WARNING: Do NOT use the event loop as it may not exist at this time.
  1008. bool allWritable = true;
  1009. TQString errorMsg;
  1010. if ( !mLocalFileName.isEmpty() && !bFileImmutable && !checkAccess(mLocalFileName,W_OK) )
  1011. {
  1012. errorMsg = i18n("Will not save configuration.\n");
  1013. allWritable = false;
  1014. errorMsg += i18n("Configuration file \"%1\" not writable.\n").arg(mLocalFileName);
  1015. }
  1016. // We do not have an immutability flag for kdeglobals. However, making kdeglobals mutable while making
  1017. // the local config file immutable is senseless.
  1018. if ( !mGlobalFileName.isEmpty() && useKDEGlobals && !bFileImmutable && !checkAccess(mGlobalFileName,W_OK) )
  1019. {
  1020. if ( errorMsg.isEmpty() )
  1021. errorMsg = i18n("Will not save configuration.\n");
  1022. errorMsg += i18n("Configuration file \"%1\" not writable.\n").arg(mGlobalFileName);
  1023. allWritable = false;
  1024. }
  1025. if (warnUser && !allWritable)
  1026. {
  1027. // Note: We don't ask the user if we should not ask this question again because we can't save the answer.
  1028. errorMsg += i18n("Please contact your system administrator.");
  1029. TQString cmdToExec = TDEStandardDirs::findExe(TQString("kdialog"));
  1030. TDEApplication *app = kapp;
  1031. if (!cmdToExec.isEmpty() && app)
  1032. {
  1033. TDEProcess lprocess;
  1034. lprocess << cmdToExec << "--title" << app->instanceName() << "--msgbox" << TQCString(errorMsg.local8Bit());
  1035. lprocess.start( TDEProcess::Block );
  1036. }
  1037. }
  1038. return allWritable;
  1039. }