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