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