00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #include <config.h>
00023
00024 #include <unistd.h>
00025 #include <ctype.h>
00026 #ifdef HAVE_SYS_MMAN_H
00027 #include <sys/mman.h>
00028 #endif
00029 #include <sys/types.h>
00030 #ifdef HAVE_SYS_STAT_H
00031 #include <sys/stat.h>
00032 #endif
00033 #include <fcntl.h>
00034 #include <signal.h>
00035 #include <setjmp.h>
00036
00037 #include <qdir.h>
00038 #include <qfileinfo.h>
00039 #include <qtextcodec.h>
00040 #include <qtextstream.h>
00041
00042 #include "kconfigbackend.h"
00043 #include "kconfigbase.h"
00044 #include <kapplication.h>
00045 #include <kglobal.h>
00046 #include <kprocess.h>
00047 #include <klocale.h>
00048 #include <kstandarddirs.h>
00049 #include <ksavefile.h>
00050 #include <kurl.h>
00051 #include <kde_file.h>
00052
00053 extern bool checkAccess(const QString& pathname, int mode);
00054
00055 static QCString printableToString(const char *str, int l)
00056 {
00057
00058 while((l>0) &&
00059 ((*str == ' ') || (*str == '\t') || (*str == '\r')))
00060 {
00061 str++; l--;
00062 }
00063
00064
00065 while((l>0) &&
00066 ((str[l-1] == ' ') || (str[l-1] == '\t') || (str[l-1] == '\r')))
00067 {
00068 l--;
00069 }
00070
00071 QCString result(l + 1);
00072 char *r = result.data();
00073
00074 for(int i = 0; i < l;i++, str++)
00075 {
00076 if (*str == '\\')
00077 {
00078 i++, str++;
00079 if (i >= l)
00080 {
00081 *r++ = '\\';
00082 break;
00083 }
00084 switch(*str)
00085 {
00086 case 's':
00087 *r++ = ' ';
00088 break;
00089 case 't':
00090 *r++ = '\t';
00091 break;
00092 case 'n':
00093 *r++ = '\n';
00094 break;
00095 case 'r':
00096 *r++ = '\r';
00097 break;
00098 case '\\':
00099 *r++ = '\\';
00100 break;
00101 default:
00102 *r++ = '\\';
00103 *r++ = *str;
00104 }
00105 }
00106 else
00107 {
00108 *r++ = *str;
00109 }
00110 }
00111 result.truncate(r-result.data());
00112 return result;
00113 }
00114
00115 static QCString stringToPrintable(const QCString& str){
00116 QCString result(str.length()*2);
00117 register char *r = result.data();
00118 register char *s = str.data();
00119
00120 if (!s) return QCString("");
00121
00122
00123 if (*s == ' ')
00124 {
00125 *r++ = '\\'; *r++ = 's';
00126 s++;
00127 }
00128
00129 if (*s)
00130 {
00131 while(*s)
00132 {
00133 if (*s == '\n')
00134 {
00135 *r++ = '\\'; *r++ = 'n';
00136 }
00137 else if (*s == '\t')
00138 {
00139 *r++ = '\\'; *r++ = 't';
00140 }
00141 else if (*s == '\r')
00142 {
00143 *r++ = '\\'; *r++ = 'r';
00144 }
00145 else if (*s == '\\')
00146 {
00147 *r++ = '\\'; *r++ = '\\';
00148 }
00149 else
00150 {
00151 *r++ = *s;
00152 }
00153 s++;
00154 }
00155
00156 if (*(r-1) == ' ')
00157 {
00158 *(r-1) = '\\'; *r++ = 's';
00159 }
00160 }
00161
00162 result.truncate(r - result.data());
00163 return result;
00164 }
00165
00166 static QCString decodeGroup(const char*s, int l)
00167 {
00168 QCString result(l);
00169 register char *r = result.data();
00170
00171 l--;
00172 while(l)
00173 {
00174 if ((*s == '[') && (l > 1))
00175 {
00176 if ((*(s+1) == '['))
00177 {
00178 l--;
00179 s++;
00180 }
00181 }
00182 if ((*s == ']') && (l > 1))
00183 {
00184 if ((*(s+1) == ']'))
00185 {
00186 l--;
00187 s++;
00188 }
00189 }
00190 *r++ = *s++;
00191 l--;
00192 }
00193 result.truncate(r - result.data());
00194 return result;
00195 }
00196
00197 static QCString encodeGroup(const QCString &str)
00198 {
00199 int l = str.length();
00200 QCString result(l*2+1);
00201 register char *r = result.data();
00202 register char *s = str.data();
00203 while(l)
00204 {
00205 if ((*s == '[') || (*s == ']'))
00206 *r++ = *s;
00207 *r++ = *s++;
00208 l--;
00209 }
00210 result.truncate(r - result.data());
00211 return result;
00212 }
00213
00214 static QCString encodeKey(const char* key)
00215 {
00216 QCString newKey(key);
00217
00218 newKey.replace('[', "%5b");
00219 newKey.replace(']', "%5d");
00220
00221 return newKey;
00222 }
00223
00224 static QCString decodeKey(const char* key)
00225 {
00226 QCString newKey(key);
00227
00228 newKey.replace("%5b", "[");
00229 newKey.replace("%5d", "]");
00230
00231 return newKey;
00232 }
00233
00234 class KConfigBackEnd::KConfigBackEndPrivate
00235 {
00236 public:
00237 QDateTime localLastModified;
00238 uint localLastSize;
00239 KLockFile::Ptr localLockFile;
00240 KLockFile::Ptr globalLockFile;
00241 };
00242
00243 void KConfigBackEnd::changeFileName(const QString &_fileName,
00244 const char * _resType,
00245 bool _useKDEGlobals)
00246 {
00247 mfileName = _fileName;
00248 resType = _resType;
00249 useKDEGlobals = _useKDEGlobals;
00250 if (mfileName.isEmpty())
00251 mLocalFileName = QString::null;
00252 else if (!QDir::isRelativePath(mfileName))
00253 mLocalFileName = mfileName;
00254 else
00255 mLocalFileName = KGlobal::dirs()->saveLocation(resType) + mfileName;
00256
00257 if (useKDEGlobals)
00258 mGlobalFileName = KGlobal::dirs()->saveLocation("config") +
00259 QString::fromLatin1("kdeglobals");
00260 else
00261 mGlobalFileName = QString::null;
00262
00263 d->localLastModified = QDateTime();
00264 d->localLastSize = 0;
00265 d->localLockFile = 0;
00266 d->globalLockFile = 0;
00267 }
00268
00269 KLockFile::Ptr KConfigBackEnd::lockFile(bool bGlobal)
00270 {
00271 if (bGlobal)
00272 {
00273 if (d->globalLockFile)
00274 return d->globalLockFile;
00275
00276 if (!mGlobalFileName.isEmpty())
00277 {
00278 d->globalLockFile = new KLockFile(mGlobalFileName+".lock");
00279 return d->globalLockFile;
00280 }
00281 }
00282 else
00283 {
00284 if (d->localLockFile)
00285 return d->localLockFile;
00286
00287 if (!mLocalFileName.isEmpty())
00288 {
00289 d->localLockFile = new KLockFile(mLocalFileName+".lock");
00290 return d->localLockFile;
00291 }
00292 }
00293 return 0;
00294 }
00295
00296 KConfigBackEnd::KConfigBackEnd(KConfigBase *_config,
00297 const QString &_fileName,
00298 const char * _resType,
00299 bool _useKDEGlobals)
00300 : pConfig(_config), bFileImmutable(false), mConfigState(KConfigBase::NoAccess), mFileMode(-1)
00301 {
00302 d = new KConfigBackEndPrivate;
00303 changeFileName(_fileName, _resType, _useKDEGlobals);
00304 }
00305
00306 KConfigBackEnd::~KConfigBackEnd()
00307 {
00308 delete d;
00309 }
00310
00311 void KConfigBackEnd::setFileWriteMode(int mode)
00312 {
00313 mFileMode = mode;
00314 }
00315
00316 bool KConfigINIBackEnd::parseConfigFiles()
00317 {
00318
00319 mConfigState = KConfigBase::ReadOnly;
00320 if (!mLocalFileName.isEmpty() && !pConfig->isReadOnly())
00321 {
00322 if (checkAccess(mLocalFileName, W_OK))
00323 {
00324 mConfigState = KConfigBase::ReadWrite;
00325 }
00326 else
00327 {
00328
00329 KURL path;
00330 path.setPath(mLocalFileName);
00331 QString dir=path.directory();
00332 KStandardDirs::makeDir(dir);
00333
00334 if (checkAccess(mLocalFileName, W_OK))
00335 {
00336 mConfigState = KConfigBase::ReadWrite;
00337 }
00338 }
00339 QFileInfo info(mLocalFileName);
00340 d->localLastModified = info.lastModified();
00341 d->localLastSize = info.size();
00342 }
00343
00344
00345 bFileImmutable = false;
00346
00347
00348 if (useKDEGlobals) {
00349 QStringList kdercs = KGlobal::dirs()->
00350 findAllResources("config", QString::fromLatin1("kdeglobals"));
00351
00352 #ifdef Q_WS_WIN
00353 QString etc_kderc = QFile::decodeName( QCString(getenv("WINDIR")) + "\\kderc" );
00354 #else
00355 QString etc_kderc = QString::fromLatin1("/etc/kderc");
00356 #endif
00357
00358 if (checkAccess(etc_kderc, R_OK))
00359 kdercs += etc_kderc;
00360
00361 kdercs += KGlobal::dirs()->
00362 findAllResources("config", QString::fromLatin1("system.kdeglobals"));
00363
00364 QStringList::ConstIterator it;
00365
00366 for (it = kdercs.fromLast(); it != kdercs.end(); --it) {
00367
00368 QFile aConfigFile( *it );
00369 if (!aConfigFile.open( IO_ReadOnly ))
00370 continue;
00371 parseSingleConfigFile( aConfigFile, 0L, true, (*it != mGlobalFileName) );
00372 aConfigFile.close();
00373 if (bFileImmutable)
00374 break;
00375 }
00376 }
00377
00378 bool bReadFile = !mfileName.isEmpty();
00379 while(bReadFile) {
00380 bReadFile = false;
00381 QString bootLanguage;
00382 if (useKDEGlobals && localeString.isEmpty() && !KGlobal::_locale) {
00383
00384 bootLanguage = KLocale::_initLanguage(pConfig);
00385 setLocaleString(bootLanguage.utf8());
00386 }
00387
00388 bFileImmutable = false;
00389 QStringList list;
00390 if ( !QDir::isRelativePath(mfileName) )
00391 list << mfileName;
00392 else
00393 list = KGlobal::dirs()->findAllResources(resType, mfileName);
00394
00395 QStringList::ConstIterator it;
00396
00397 for (it = list.fromLast(); it != list.end(); --it) {
00398
00399 QFile aConfigFile( *it );
00400
00401 bool bIsLocal = (*it == mLocalFileName);
00402 if (aConfigFile.open( IO_ReadOnly )) {
00403 parseSingleConfigFile( aConfigFile, 0L, false, !bIsLocal );
00404 aConfigFile.close();
00405 if (bFileImmutable)
00406 break;
00407 }
00408 }
00409 if (KGlobal::dirs()->isRestrictedResource(resType, mfileName))
00410 bFileImmutable = true;
00411 QString currentLanguage;
00412 if (!bootLanguage.isEmpty())
00413 {
00414 currentLanguage = KLocale::_initLanguage(pConfig);
00415
00416
00417 if (bootLanguage != currentLanguage)
00418 {
00419 bReadFile = true;
00420 setLocaleString(currentLanguage.utf8());
00421 }
00422 }
00423 }
00424 if (bFileImmutable)
00425 mConfigState = KConfigBase::ReadOnly;
00426
00427 return true;
00428 }
00429
00430 #ifdef HAVE_MMAP
00431 #ifdef SIGBUS
00432 static sigjmp_buf mmap_jmpbuf;
00433 struct sigaction mmap_old_sigact;
00434
00435 extern "C" {
00436 static void mmap_sigbus_handler(int)
00437 {
00438 siglongjmp (mmap_jmpbuf, 1);
00439 }
00440 }
00441 #endif
00442 #endif
00443
00444 extern bool kde_kiosk_exception;
00445
00446 void KConfigINIBackEnd::parseSingleConfigFile(QFile &rFile,
00447 KEntryMap *pWriteBackMap,
00448 bool bGlobal, bool bDefault)
00449 {
00450 const char *s;
00451 const char *eof;
00452 QByteArray data;
00453
00454 if (!rFile.isOpen())
00455 return;
00456
00457
00458
00459
00460
00461
00462 QCString aCurrentGroup("<default>");
00463
00464 unsigned int ll = localeString.length();
00465
00466 #ifdef HAVE_MMAP
00467 static volatile const char *map;
00468 map = ( const char* ) mmap(0, rFile.size(), PROT_READ, MAP_PRIVATE,
00469 rFile.handle(), 0);
00470
00471 if ( map != MAP_FAILED )
00472 {
00473 s = (const char*) map;
00474 eof = s + rFile.size();
00475
00476 #ifdef SIGBUS
00477 struct sigaction act;
00478 act.sa_handler = mmap_sigbus_handler;
00479 sigemptyset( &act.sa_mask );
00480 #ifdef SA_ONESHOT
00481 act.sa_flags = SA_ONESHOT;
00482 #else
00483 act.sa_flags = SA_RESETHAND;
00484 #endif
00485 sigaction( SIGBUS, &act, &mmap_old_sigact );
00486
00487 if (sigsetjmp (mmap_jmpbuf, 1))
00488 {
00489 qWarning("SIGBUS while reading %s", rFile.name().latin1());
00490 munmap(( char* )map, rFile.size());
00491 sigaction (SIGBUS, &mmap_old_sigact, 0);
00492 return;
00493 }
00494 #endif
00495 }
00496 else
00497 #endif
00498 {
00499 rFile.at(0);
00500 data = rFile.readAll();
00501 s = data.data();
00502 eof = s + data.size();
00503 }
00504
00505 bool fileOptionImmutable = false;
00506 bool groupOptionImmutable = false;
00507 bool groupSkip = false;
00508
00509 int line = 0;
00510 for(; s < eof; s++)
00511 {
00512 line++;
00513
00514 while((s < eof) && isspace(*s) && (*s != '\n'))
00515 s++;
00516
00517
00518 if ((s < eof) && ((*s == '\n') || (*s == '#')))
00519 {
00520 sktoeol:
00521 while ((s < eof) && (*s != '\n'))
00522 s++;
00523 continue;
00524 }
00525 const char *startLine = s;
00526
00527 if (*s == '[')
00528 {
00529
00530 while ((s < eof) && (*s != '\n'))
00531 {
00532 if (*s == ']')
00533 {
00534 if ((s+1 < eof) && (*(s+1) == ']'))
00535 s++;
00536 else
00537 break;
00538 }
00539
00540 s++;
00541 }
00542 const char *e = s;
00543 while ((s < eof) && (*s != '\n')) s++;
00544 if ((e >= eof) || (*e != ']'))
00545 {
00546 fprintf(stderr, "Invalid group header at %s:%d\n", rFile.name().latin1(), line);
00547 continue;
00548 }
00549
00550
00551 if ((e-startLine == 3) &&
00552 (startLine[1] == '$') &&
00553 (startLine[2] == 'i'))
00554 {
00555 if (!kde_kiosk_exception)
00556 fileOptionImmutable = true;
00557 continue;
00558 }
00559
00560 aCurrentGroup = decodeGroup(startLine + 1, e - startLine);
00561
00562
00563
00564 if (aCurrentGroup == "KDE Desktop Entry")
00565 aCurrentGroup = "Desktop Entry";
00566
00567 groupOptionImmutable = fileOptionImmutable;
00568
00569 e++;
00570 if ((e+2 < eof) && (*e++ == '[') && (*e++ == '$'))
00571 {
00572 if ((*e == 'i') && !kde_kiosk_exception)
00573 {
00574 groupOptionImmutable = true;
00575 }
00576 }
00577
00578 KEntryKey groupKey(aCurrentGroup, 0);
00579 KEntry entry = pConfig->lookupData(groupKey);
00580 groupSkip = entry.bImmutable;
00581
00582 if (groupSkip && !bDefault)
00583 continue;
00584
00585 entry.bImmutable |= groupOptionImmutable;
00586 pConfig->putData(groupKey, entry, false);
00587
00588 if (pWriteBackMap)
00589 {
00590
00591 (*pWriteBackMap)[groupKey] = entry;
00592 }
00593
00594 continue;
00595 }
00596 if (groupSkip && !bDefault)
00597 goto sktoeol;
00598
00599 bool optionImmutable = groupOptionImmutable;
00600 bool optionDeleted = false;
00601 bool optionExpand = false;
00602 const char *endOfKey = 0, *locale = 0, *elocale = 0;
00603 for (; (s < eof) && (*s != '\n'); s++)
00604 {
00605 if (*s == '=')
00606 {
00607 if (!endOfKey)
00608 endOfKey = s;
00609 goto haveeq;
00610 }
00611 if (*s == '[')
00612 {
00613 const char *option;
00614 const char *eoption;
00615 endOfKey = s;
00616 option = ++s;
00617 for (;; s++)
00618 {
00619 if ((s >= eof) || (*s == '\n') || (*s == '=')) {
00620 fprintf(stderr, "Invalid entry (missing ']') at %s:%d\n", rFile.name().latin1(), line);
00621 goto sktoeol;
00622 }
00623 if (*s == ']')
00624 break;
00625 }
00626 eoption = s;
00627 if (*option != '$')
00628 {
00629
00630 if (locale) {
00631 fprintf(stderr, "Invalid entry (second locale!?) at %s:%d\n", rFile.name().latin1(), line);
00632 goto sktoeol;
00633 }
00634 locale = option;
00635 elocale = eoption;
00636 }
00637 else
00638 {
00639
00640 while (option < eoption)
00641 {
00642 option++;
00643 if ((*option == 'i') && !kde_kiosk_exception)
00644 optionImmutable = true;
00645 else if (*option == 'e')
00646 optionExpand = true;
00647 else if (*option == 'd')
00648 {
00649 optionDeleted = true;
00650 goto haveeq;
00651 }
00652 else if (*option == ']')
00653 break;
00654 }
00655 }
00656 }
00657 }
00658 fprintf(stderr, "Invalid entry (missing '=') at %s:%d\n", rFile.name().latin1(), line);
00659 continue;
00660
00661 haveeq:
00662 for (endOfKey--; ; endOfKey--)
00663 {
00664 if (endOfKey < startLine)
00665 {
00666 fprintf(stderr, "Invalid entry (empty key) at %s:%d\n", rFile.name().latin1(), line);
00667 goto sktoeol;
00668 }
00669 if (!isspace(*endOfKey))
00670 break;
00671 }
00672
00673 const char *st = ++s;
00674 while ((s < eof) && (*s != '\n')) s++;
00675
00676 if (locale) {
00677 unsigned int cl = static_cast<unsigned int>(elocale - locale);
00678 if ((ll != cl) || memcmp(locale, localeString.data(), ll))
00679 {
00680
00681 if ( cl != 1 || ll != 5 || *locale != 'C' || memcmp(localeString.data(), "en_US", 5)) {
00682
00683
00684 if (!pWriteBackMap)
00685 continue;
00686
00687 endOfKey = elocale;
00688 locale = 0;
00689 }
00690 }
00691 }
00692
00693
00694 QCString key(startLine, endOfKey - startLine + 2);
00695 QCString val = printableToString(st, s - st);
00696
00697
00698 KEntryKey aEntryKey(aCurrentGroup, decodeKey(key));
00699 aEntryKey.bLocal = (locale != 0);
00700 aEntryKey.bDefault = bDefault;
00701
00702 KEntry aEntry;
00703 aEntry.mValue = val;
00704 aEntry.bGlobal = bGlobal;
00705 aEntry.bImmutable = optionImmutable;
00706 aEntry.bDeleted = optionDeleted;
00707 aEntry.bExpand = optionExpand;
00708 aEntry.bNLS = (locale != 0);
00709
00710 if (pWriteBackMap) {
00711
00712
00713 pWriteBackMap->insert(aEntryKey, aEntry);
00714 } else {
00715
00716
00717
00718 pConfig->putData(aEntryKey, aEntry, false);
00719 }
00720 }
00721 if (fileOptionImmutable)
00722 bFileImmutable = true;
00723
00724 #ifdef HAVE_MMAP
00725 if (map)
00726 {
00727 munmap(( char* )map, rFile.size());
00728 #ifdef SIGBUS
00729 sigaction (SIGBUS, &mmap_old_sigact, 0);
00730 #endif
00731 }
00732 #endif
00733 }
00734
00735
00736 void KConfigINIBackEnd::sync(bool bMerge)
00737 {
00738
00739 if (!pConfig->isDirty())
00740 return;
00741
00742 bool bEntriesLeft = true;
00743
00744
00745
00746
00747 if (!mfileName.isEmpty()) {
00748
00749 if ((resType!="config") && !QDir::isRelativePath(mLocalFileName))
00750 {
00751 KURL path;
00752 path.setPath(mLocalFileName);
00753 QString dir=path.directory();
00754 KStandardDirs::makeDir(dir);
00755 }
00756
00757
00758
00759
00760
00761 if (checkAccess(mLocalFileName, W_OK)) {
00762
00763 KLockFile::Ptr lf;
00764
00765 bool mergeLocalFile = bMerge;
00766
00767 if (mergeLocalFile)
00768 {
00769 lf = lockFile(false);
00770 if (lf && lf->isLocked())
00771 lf = 0;
00772
00773 if (lf)
00774 {
00775 lf->lock( KLockFile::LockForce );
00776
00777 }
00778
00779 QFileInfo info(mLocalFileName);
00780 if ((d->localLastSize == info.size()) &&
00781 (d->localLastModified == info.lastModified()))
00782 {
00783
00784 mergeLocalFile = false;
00785 }
00786 else
00787 {
00788
00789 d->localLastModified = QDateTime();
00790 d->localLastSize = 0;
00791 }
00792 }
00793
00794 bEntriesLeft = writeConfigFile( mLocalFileName, false, mergeLocalFile );
00795
00796
00797
00798
00799
00800
00801
00802
00803
00804 if (!mergeLocalFile)
00805 {
00806 QFileInfo info(mLocalFileName);
00807 d->localLastModified = info.lastModified();
00808 d->localLastSize = info.size();
00809 }
00810 if (lf) lf->unlock();
00811 }
00812 }
00813
00814
00815
00816
00817 if (bEntriesLeft && useKDEGlobals) {
00818
00819
00820 if (checkAccess ( mGlobalFileName, W_OK )) {
00821 KLockFile::Ptr lf = lockFile(true);
00822 if (lf && lf->isLocked())
00823 lf = 0;
00824
00825 if (lf)
00826 {
00827 lf->lock( KLockFile::LockForce );
00828
00829 }
00830 writeConfigFile( mGlobalFileName, true, bMerge );
00831 if (lf) lf->unlock();
00832 }
00833 }
00834
00835 }
00836
00837 static void writeEntries(FILE *pStream, const KEntryMap& entryMap, bool defaultGroup, bool &firstEntry, const QCString &localeString)
00838 {
00839
00840 QCString currentGroup;
00841 for (KEntryMapConstIterator aIt = entryMap.begin();
00842 aIt != entryMap.end(); ++aIt)
00843 {
00844 const KEntryKey &key = aIt.key();
00845
00846
00847 if ((key.mGroup != "<default>") == defaultGroup)
00848 continue;
00849
00850
00851 if ((key.bDefault) || key.mKey.isEmpty())
00852 continue;
00853
00854 const KEntry ¤tEntry = *aIt;
00855
00856 KEntryMapConstIterator aTestIt = aIt;
00857 ++aTestIt;
00858 bool hasDefault = (aTestIt != entryMap.end());
00859 if (hasDefault)
00860 {
00861 const KEntryKey &defaultKey = aTestIt.key();
00862 if ((!defaultKey.bDefault) ||
00863 (defaultKey.mKey != key.mKey) ||
00864 (defaultKey.mGroup != key.mGroup) ||
00865 (defaultKey.bLocal != key.bLocal))
00866 hasDefault = false;
00867 }
00868
00869
00870 if (hasDefault)
00871 {
00872
00873 if ((currentEntry.mValue == (*aTestIt).mValue) &&
00874 (currentEntry.bDeleted == (*aTestIt).bDeleted))
00875 continue;
00876 }
00877 else
00878 {
00879
00880 if (currentEntry.bDeleted)
00881 continue;
00882 }
00883
00884 if (!defaultGroup && (currentGroup != key.mGroup)) {
00885 if (!firstEntry)
00886 fprintf(pStream, "\n");
00887 currentGroup = key.mGroup;
00888 fprintf(pStream, "[%s]\n", encodeGroup(currentGroup).data());
00889 }
00890
00891 firstEntry = false;
00892
00893 fputs(encodeKey(key.mKey.data()), pStream);
00894
00895 if ( currentEntry.bNLS )
00896 {
00897 fputc('[', pStream);
00898 fputs(localeString.data(), pStream);
00899 fputc(']', pStream);
00900 }
00901
00902 if (currentEntry.bDeleted)
00903 {
00904 fputs("[$d]\n", pStream);
00905 }
00906 else
00907 {
00908 if (currentEntry.bImmutable || currentEntry.bExpand)
00909 {
00910 fputc('[', pStream);
00911 fputc('$', pStream);
00912 if (currentEntry.bImmutable)
00913 fputc('i', pStream);
00914 if (currentEntry.bExpand)
00915 fputc('e', pStream);
00916
00917 fputc(']', pStream);
00918 }
00919 fputc('=', pStream);
00920 fputs(stringToPrintable(currentEntry.mValue).data(), pStream);
00921 fputc('\n', pStream);
00922 }
00923 }
00924 }
00925
00926 bool KConfigINIBackEnd::getEntryMap(KEntryMap &aTempMap, bool bGlobal,
00927 QFile *mergeFile)
00928 {
00929 bool bEntriesLeft = false;
00930 bFileImmutable = false;
00931
00932
00933 if (mergeFile && mergeFile->open(IO_ReadOnly))
00934 {
00935
00936 parseSingleConfigFile(*mergeFile, &aTempMap, bGlobal, false );
00937
00938 if (bFileImmutable)
00939 return bEntriesLeft;
00940 }
00941
00942 KEntryMap aMap = pConfig->internalEntryMap();
00943
00944
00945 for (KEntryMapIterator aIt = aMap.begin();
00946 aIt != aMap.end(); ++aIt)
00947 {
00948 const KEntry ¤tEntry = *aIt;
00949 if(aIt.key().bDefault)
00950 {
00951 aTempMap.replace(aIt.key(), currentEntry);
00952 continue;
00953 }
00954
00955 if (mergeFile && !currentEntry.bDirty)
00956 continue;
00957
00958
00959
00960 if (currentEntry.bGlobal != bGlobal)
00961 {
00962
00963 bEntriesLeft = true;
00964 continue;
00965 }
00966
00967
00968
00969 KEntryMapIterator aIt2 = aTempMap.find(aIt.key());
00970 if (aIt2 != aTempMap.end() && (*aIt2).bImmutable)
00971 continue;
00972
00973 aTempMap.insert(aIt.key(), currentEntry, true);
00974 }
00975
00976 return bEntriesLeft;
00977 }
00978
00979
00980 bool KConfigINIBackEnd::writeConfigFile(QString filename, bool bGlobal,
00981 bool bMerge)
00982 {
00983
00984 if (pConfig->isReadOnly())
00985 return true;
00986
00987 KEntryMap aTempMap;
00988 QFile *mergeFile = (bMerge ? new QFile(filename) : 0);
00989 bool bEntriesLeft = getEntryMap(aTempMap, bGlobal, mergeFile);
00990 delete mergeFile;
00991 if (bFileImmutable)
00992 return true;
00993
00994
00995
00996
00997
00998 int fileMode = -1;
00999 bool createNew = true;
01000
01001 KDE_struct_stat buf;
01002 if (KDE_stat(QFile::encodeName(filename), &buf) == 0)
01003 {
01004 if (buf.st_uid == getuid())
01005 {
01006
01007 fileMode = buf.st_mode & 0777;
01008 }
01009 else
01010 {
01011
01012
01013 createNew = false;
01014 }
01015 }
01016
01017 KSaveFile *pConfigFile = 0;
01018 FILE *pStream = 0;
01019
01020 if (createNew)
01021 {
01022 pConfigFile = new KSaveFile( filename, 0600 );
01023
01024 if (pConfigFile->status() != 0)
01025 {
01026 delete pConfigFile;
01027 return bEntriesLeft;
01028 }
01029
01030 if (!bGlobal && (fileMode == -1))
01031 fileMode = mFileMode;
01032
01033 if (fileMode != -1)
01034 {
01035 fchmod(pConfigFile->handle(), fileMode);
01036 }
01037
01038 pStream = pConfigFile->fstream();
01039 }
01040 else
01041 {
01042
01043
01044 int fd = KDE_open( QFile::encodeName(filename), O_WRONLY | O_TRUNC );
01045 if (fd < 0)
01046 {
01047 return bEntriesLeft;
01048 }
01049 pStream = KDE_fdopen( fd, "w");
01050 if (!pStream)
01051 {
01052 close(fd);
01053 return bEntriesLeft;
01054 }
01055 }
01056
01057 writeEntries(pStream, aTempMap);
01058
01059 if (pConfigFile)
01060 {
01061 bool bEmptyFile = (ftell(pStream) == 0);
01062 if ( bEmptyFile && ((fileMode == -1) || (fileMode == 0600)) )
01063 {
01064
01065 ::unlink(QFile::encodeName(filename));
01066 pConfigFile->abort();
01067 }
01068 else
01069 {
01070
01071 pConfigFile->close();
01072 }
01073 delete pConfigFile;
01074 }
01075 else
01076 {
01077 fclose(pStream);
01078 }
01079
01080 return bEntriesLeft;
01081 }
01082
01083 void KConfigINIBackEnd::writeEntries(FILE *pStream, const KEntryMap &aTempMap)
01084 {
01085 bool firstEntry = true;
01086
01087
01088 ::writeEntries(pStream, aTempMap, true, firstEntry, localeString);
01089
01090
01091 ::writeEntries(pStream, aTempMap, false, firstEntry, localeString);
01092 }
01093
01094 void KConfigBackEnd::virtual_hook( int, void* )
01095 { }
01096
01097 void KConfigINIBackEnd::virtual_hook( int id, void* data )
01098 { KConfigBackEnd::virtual_hook( id, data ); }
01099
01100 bool KConfigBackEnd::checkConfigFilesWritable(bool warnUser)
01101 {
01102
01103 bool allWritable = true;
01104 QString errorMsg;
01105 if ( !mLocalFileName.isEmpty() && !bFileImmutable && !checkAccess(mLocalFileName,W_OK) )
01106 {
01107 errorMsg = i18n("Will not save configuration.\n");
01108 allWritable = false;
01109 errorMsg += i18n("Configuration file \"%1\" not writable.\n").arg(mLocalFileName);
01110 }
01111
01112
01113 if ( !mGlobalFileName.isEmpty() && useKDEGlobals && !bFileImmutable && !checkAccess(mGlobalFileName,W_OK) )
01114 {
01115 if ( errorMsg.isEmpty() )
01116 errorMsg = i18n("Will not save configuration.\n");
01117 errorMsg += i18n("Configuration file \"%1\" not writable.\n").arg(mGlobalFileName);
01118 allWritable = false;
01119 }
01120
01121 if (warnUser && !allWritable)
01122 {
01123
01124 errorMsg += i18n("Please contact your system administrator.");
01125 QString cmdToExec = KStandardDirs::findExe(QString("kdialog"));
01126 KApplication *app = kapp;
01127 if (!cmdToExec.isEmpty() && app)
01128 {
01129 KProcess lprocess;
01130 lprocess << cmdToExec << "--title" << app->instanceName() << "--msgbox" << errorMsg.local8Bit();
01131 lprocess.start( KProcess::Block );
01132 }
01133 }
01134 return allWritable;
01135 }