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