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
00583 if (groupSkip)
00584 continue;
00585
00586 entry.bImmutable |= groupOptionImmutable;
00587 pConfig->putData(groupKey, entry, false);
00588
00589 if (pWriteBackMap)
00590 {
00591
00592 (*pWriteBackMap)[groupKey] = entry;
00593 }
00594
00595 continue;
00596 }
00597
00598 if (groupSkip)
00599 goto sktoeol;
00600
00601 bool optionImmutable = groupOptionImmutable;
00602 bool optionDeleted = false;
00603 bool optionExpand = false;
00604 const char *endOfKey = 0, *locale = 0, *elocale = 0;
00605 for (; (s < eof) && (*s != '\n'); s++)
00606 {
00607 if (*s == '=')
00608 {
00609 if (!endOfKey)
00610 endOfKey = s;
00611 goto haveeq;
00612 }
00613 if (*s == '[')
00614 {
00615 const char *option;
00616 const char *eoption;
00617 endOfKey = s;
00618 option = ++s;
00619 for (;; s++)
00620 {
00621 if ((s >= eof) || (*s == '\n') || (*s == '=')) {
00622 fprintf(stderr, "Invalid entry (missing ']') at %s:%d\n", rFile.name().latin1(), line);
00623 goto sktoeol;
00624 }
00625 if (*s == ']')
00626 break;
00627 }
00628 eoption = s;
00629 if (*option != '$')
00630 {
00631
00632 if (locale) {
00633 fprintf(stderr, "Invalid entry (second locale!?) at %s:%d\n", rFile.name().latin1(), line);
00634 goto sktoeol;
00635 }
00636 locale = option;
00637 elocale = eoption;
00638 }
00639 else
00640 {
00641
00642 while (option < eoption)
00643 {
00644 option++;
00645 if ((*option == 'i') && !kde_kiosk_exception)
00646 optionImmutable = true;
00647 else if (*option == 'e')
00648 optionExpand = true;
00649 else if (*option == 'd')
00650 {
00651 optionDeleted = true;
00652 goto haveeq;
00653 }
00654 else if (*option == ']')
00655 break;
00656 }
00657 }
00658 }
00659 }
00660 fprintf(stderr, "Invalid entry (missing '=') at %s:%d\n", rFile.name().latin1(), line);
00661 continue;
00662
00663 haveeq:
00664 for (endOfKey--; ; endOfKey--)
00665 {
00666 if (endOfKey < startLine)
00667 {
00668 fprintf(stderr, "Invalid entry (empty key) at %s:%d\n", rFile.name().latin1(), line);
00669 goto sktoeol;
00670 }
00671 if (!isspace(*endOfKey))
00672 break;
00673 }
00674
00675 const char *st = ++s;
00676 while ((s < eof) && (*s != '\n')) s++;
00677
00678 if (locale) {
00679 unsigned int cl = static_cast<unsigned int>(elocale - locale);
00680 if ((ll != cl) || memcmp(locale, localeString.data(), ll))
00681 {
00682
00683 if ( cl != 1 || ll != 5 || *locale != 'C' || memcmp(localeString.data(), "en_US", 5)) {
00684
00685
00686 if (!pWriteBackMap)
00687 continue;
00688
00689 endOfKey = elocale;
00690 locale = 0;
00691 }
00692 }
00693 }
00694
00695
00696 QCString key(startLine, endOfKey - startLine + 2);
00697 QCString val = printableToString(st, s - st);
00698
00699
00700 KEntryKey aEntryKey(aCurrentGroup, decodeKey(key));
00701 aEntryKey.bLocal = (locale != 0);
00702 aEntryKey.bDefault = bDefault;
00703
00704 KEntry aEntry;
00705 aEntry.mValue = val;
00706 aEntry.bGlobal = bGlobal;
00707 aEntry.bImmutable = optionImmutable;
00708 aEntry.bDeleted = optionDeleted;
00709 aEntry.bExpand = optionExpand;
00710 aEntry.bNLS = (locale != 0);
00711
00712 if (pWriteBackMap) {
00713
00714
00715 pWriteBackMap->insert(aEntryKey, aEntry);
00716 } else {
00717
00718
00719
00720 pConfig->putData(aEntryKey, aEntry, false);
00721 }
00722 }
00723 if (fileOptionImmutable)
00724 bFileImmutable = true;
00725
00726 #ifdef HAVE_MMAP
00727 if (map)
00728 {
00729 munmap(( char* )map, rFile.size());
00730 #ifdef SIGBUS
00731 sigaction (SIGBUS, &mmap_old_sigact, 0);
00732 #endif
00733 }
00734 #endif
00735 }
00736
00737
00738 void KConfigINIBackEnd::sync(bool bMerge)
00739 {
00740
00741 if (!pConfig->isDirty())
00742 return;
00743
00744 bool bEntriesLeft = true;
00745
00746
00747
00748
00749 if (!mfileName.isEmpty()) {
00750
00751 if ((resType!="config") && !QDir::isRelativePath(mLocalFileName))
00752 {
00753 KURL path;
00754 path.setPath(mLocalFileName);
00755 QString dir=path.directory();
00756 KStandardDirs::makeDir(dir);
00757 }
00758
00759
00760
00761
00762
00763 if (checkAccess(mLocalFileName, W_OK)) {
00764
00765 KLockFile::Ptr lf;
00766
00767 bool mergeLocalFile = bMerge;
00768
00769 if (mergeLocalFile)
00770 {
00771 lf = lockFile(false);
00772 if (lf && lf->isLocked())
00773 lf = 0;
00774
00775 if (lf)
00776 {
00777 lf->lock( KLockFile::LockForce );
00778
00779 }
00780
00781 QFileInfo info(mLocalFileName);
00782 if ((d->localLastSize == info.size()) &&
00783 (d->localLastModified == info.lastModified()))
00784 {
00785
00786 mergeLocalFile = false;
00787 }
00788 else
00789 {
00790
00791 d->localLastModified = QDateTime();
00792 d->localLastSize = 0;
00793 }
00794 }
00795
00796 bEntriesLeft = writeConfigFile( mLocalFileName, false, mergeLocalFile );
00797
00798
00799
00800
00801
00802
00803
00804
00805
00806 if (!mergeLocalFile)
00807 {
00808 QFileInfo info(mLocalFileName);
00809 d->localLastModified = info.lastModified();
00810 d->localLastSize = info.size();
00811 }
00812 if (lf) lf->unlock();
00813 }
00814 }
00815
00816
00817
00818
00819 if (bEntriesLeft && useKDEGlobals) {
00820
00821
00822 if (checkAccess ( mGlobalFileName, W_OK )) {
00823 KLockFile::Ptr lf = lockFile(true);
00824 if (lf && lf->isLocked())
00825 lf = 0;
00826
00827 if (lf)
00828 {
00829 lf->lock( KLockFile::LockForce );
00830
00831 }
00832 writeConfigFile( mGlobalFileName, true, bMerge );
00833 if (lf) lf->unlock();
00834 }
00835 }
00836
00837 }
00838
00839 static void writeEntries(FILE *pStream, const KEntryMap& entryMap, bool defaultGroup, bool &firstEntry, const QCString &localeString)
00840 {
00841
00842 QCString currentGroup;
00843 for (KEntryMapConstIterator aIt = entryMap.begin();
00844 aIt != entryMap.end(); ++aIt)
00845 {
00846 const KEntryKey &key = aIt.key();
00847
00848
00849 if ((key.mGroup != "<default>") == defaultGroup)
00850 continue;
00851
00852
00853 if ((key.bDefault) || key.mKey.isEmpty())
00854 continue;
00855
00856 const KEntry ¤tEntry = *aIt;
00857
00858 KEntryMapConstIterator aTestIt = aIt;
00859 ++aTestIt;
00860 bool hasDefault = (aTestIt != entryMap.end());
00861 if (hasDefault)
00862 {
00863 const KEntryKey &defaultKey = aTestIt.key();
00864 if ((!defaultKey.bDefault) ||
00865 (defaultKey.mKey != key.mKey) ||
00866 (defaultKey.mGroup != key.mGroup) ||
00867 (defaultKey.bLocal != key.bLocal))
00868 hasDefault = false;
00869 }
00870
00871
00872 if (hasDefault)
00873 {
00874
00875 if ((currentEntry.mValue == (*aTestIt).mValue) &&
00876 (currentEntry.bDeleted == (*aTestIt).bDeleted))
00877 continue;
00878 }
00879 else
00880 {
00881
00882 if (currentEntry.bDeleted)
00883 continue;
00884 }
00885
00886 if (!defaultGroup && (currentGroup != key.mGroup)) {
00887 if (!firstEntry)
00888 fprintf(pStream, "\n");
00889 currentGroup = key.mGroup;
00890 fprintf(pStream, "[%s]\n", encodeGroup(currentGroup).data());
00891 }
00892
00893 firstEntry = false;
00894
00895 fputs(encodeKey(key.mKey.data()), pStream);
00896
00897 if ( currentEntry.bNLS )
00898 {
00899 fputc('[', pStream);
00900 fputs(localeString.data(), pStream);
00901 fputc(']', pStream);
00902 }
00903
00904 if (currentEntry.bDeleted)
00905 {
00906 fputs("[$d]\n", pStream);
00907 }
00908 else
00909 {
00910 if (currentEntry.bImmutable || currentEntry.bExpand)
00911 {
00912 fputc('[', pStream);
00913 fputc('$', pStream);
00914 if (currentEntry.bImmutable)
00915 fputc('i', pStream);
00916 if (currentEntry.bExpand)
00917 fputc('e', pStream);
00918
00919 fputc(']', pStream);
00920 }
00921 fputc('=', pStream);
00922 fputs(stringToPrintable(currentEntry.mValue).data(), pStream);
00923 fputc('\n', pStream);
00924 }
00925 }
00926 }
00927
00928 bool KConfigINIBackEnd::getEntryMap(KEntryMap &aTempMap, bool bGlobal,
00929 QFile *mergeFile)
00930 {
00931 bool bEntriesLeft = false;
00932 bFileImmutable = false;
00933
00934
00935 if (mergeFile && mergeFile->open(IO_ReadOnly))
00936 {
00937
00938 parseSingleConfigFile(*mergeFile, &aTempMap, bGlobal, false );
00939
00940 if (bFileImmutable)
00941 return bEntriesLeft;
00942 }
00943
00944 KEntryMap aMap = pConfig->internalEntryMap();
00945
00946
00947 for (KEntryMapIterator aIt = aMap.begin();
00948 aIt != aMap.end(); ++aIt)
00949 {
00950 const KEntry ¤tEntry = *aIt;
00951 if(aIt.key().bDefault)
00952 {
00953 aTempMap.replace(aIt.key(), currentEntry);
00954 continue;
00955 }
00956
00957 if (mergeFile && !currentEntry.bDirty)
00958 continue;
00959
00960
00961
00962 if (currentEntry.bGlobal != bGlobal)
00963 {
00964
00965 bEntriesLeft = true;
00966 continue;
00967 }
00968
00969
00970
00971 KEntryMapIterator aIt2 = aTempMap.find(aIt.key());
00972 if (aIt2 != aTempMap.end() && (*aIt2).bImmutable)
00973 continue;
00974
00975 aTempMap.insert(aIt.key(), currentEntry, true);
00976 }
00977
00978 return bEntriesLeft;
00979 }
00980
00981
00982 bool KConfigINIBackEnd::writeConfigFile(QString filename, bool bGlobal,
00983 bool bMerge)
00984 {
00985
00986 if (pConfig->isReadOnly())
00987 return true;
00988
00989 KEntryMap aTempMap;
00990 QFile *mergeFile = (bMerge ? new QFile(filename) : 0);
00991 bool bEntriesLeft = getEntryMap(aTempMap, bGlobal, mergeFile);
00992 delete mergeFile;
00993 if (bFileImmutable)
00994 return true;
00995
00996
00997
00998
00999
01000 int fileMode = -1;
01001 bool createNew = true;
01002
01003 KDE_struct_stat buf;
01004 if (KDE_stat(QFile::encodeName(filename), &buf) == 0)
01005 {
01006 if (buf.st_uid == getuid())
01007 {
01008
01009 fileMode = buf.st_mode & 0777;
01010 }
01011 else
01012 {
01013
01014
01015 createNew = false;
01016 }
01017 }
01018
01019 KSaveFile *pConfigFile = 0;
01020 FILE *pStream = 0;
01021
01022 if (createNew)
01023 {
01024 pConfigFile = new KSaveFile( filename, 0600 );
01025
01026 if (pConfigFile->status() != 0)
01027 {
01028 delete pConfigFile;
01029 return bEntriesLeft;
01030 }
01031
01032 if (!bGlobal && (fileMode == -1))
01033 fileMode = mFileMode;
01034
01035 if (fileMode != -1)
01036 {
01037 fchmod(pConfigFile->handle(), fileMode);
01038 }
01039
01040 pStream = pConfigFile->fstream();
01041 }
01042 else
01043 {
01044
01045
01046 int fd = KDE_open( QFile::encodeName(filename), O_WRONLY | O_TRUNC );
01047 if (fd < 0)
01048 {
01049 return bEntriesLeft;
01050 }
01051 pStream = KDE_fdopen( fd, "w");
01052 if (!pStream)
01053 {
01054 close(fd);
01055 return bEntriesLeft;
01056 }
01057 }
01058
01059 writeEntries(pStream, aTempMap);
01060
01061 if (pConfigFile)
01062 {
01063 bool bEmptyFile = (ftell(pStream) == 0);
01064 if ( bEmptyFile && ((fileMode == -1) || (fileMode == 0600)) )
01065 {
01066
01067 ::unlink(QFile::encodeName(filename));
01068 pConfigFile->abort();
01069 }
01070 else
01071 {
01072
01073 pConfigFile->close();
01074 }
01075 delete pConfigFile;
01076 }
01077 else
01078 {
01079 fclose(pStream);
01080 }
01081
01082 return bEntriesLeft;
01083 }
01084
01085 void KConfigINIBackEnd::writeEntries(FILE *pStream, const KEntryMap &aTempMap)
01086 {
01087 bool firstEntry = true;
01088
01089
01090 ::writeEntries(pStream, aTempMap, true, firstEntry, localeString);
01091
01092
01093 ::writeEntries(pStream, aTempMap, false, firstEntry, localeString);
01094 }
01095
01096 void KConfigBackEnd::virtual_hook( int, void* )
01097 { }
01098
01099 void KConfigINIBackEnd::virtual_hook( int id, void* data )
01100 { KConfigBackEnd::virtual_hook( id, data ); }
01101
01102 bool KConfigBackEnd::checkConfigFilesWritable(bool warnUser)
01103 {
01104
01105 bool allWritable = true;
01106 QString errorMsg;
01107 if ( !mLocalFileName.isEmpty() && !bFileImmutable && !checkAccess(mLocalFileName,W_OK) )
01108 {
01109 errorMsg = i18n("Will not save configuration.\n");
01110 allWritable = false;
01111 errorMsg += i18n("Configuration file \"%1\" not writable.\n").arg(mLocalFileName);
01112 }
01113
01114
01115 if ( !mGlobalFileName.isEmpty() && useKDEGlobals && !bFileImmutable && !checkAccess(mGlobalFileName,W_OK) )
01116 {
01117 if ( errorMsg.isEmpty() )
01118 errorMsg = i18n("Will not save configuration.\n");
01119 errorMsg += i18n("Configuration file \"%1\" not writable.\n").arg(mGlobalFileName);
01120 allWritable = false;
01121 }
01122
01123 if (warnUser && !allWritable)
01124 {
01125
01126 errorMsg += i18n("Please contact your system administrator.");
01127 QString cmdToExec = KStandardDirs::findExe(QString("kdialog"));
01128 KApplication *app = kapp;
01129 if (!cmdToExec.isEmpty() && app)
01130 {
01131 KProcess lprocess;
01132 lprocess << cmdToExec << "--title" << app->instanceName() << "--msgbox" << errorMsg.local8Bit();
01133 lprocess.start( KProcess::Block );
01134 }
01135 }
01136 return allWritable;
01137 }