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