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
00036 #undef Unsorted
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 <kglobal.h>
00045 #include <klocale.h>
00046 #include <kstandarddirs.h>
00047 #include <ksavefile.h>
00048 #include <kurl.h>
00049
00050 extern bool checkAccess(const QString& pathname, int mode);
00051
00052 static QCString printableToString(const char *str, int l)
00053 {
00054
00055 while((l>0) &&
00056 ((*str == ' ') || (*str == '\t') || (*str == '\r')))
00057 {
00058 str++; l--;
00059 }
00060
00061
00062 while((l>0) &&
00063 ((str[l-1] == ' ') || (str[l-1] == '\t') || (str[l-1] == '\r')))
00064 {
00065 l--;
00066 }
00067
00068 QCString result(l + 1);
00069 char *r = result.data();
00070
00071 for(int i = 0; i < l;i++, str++)
00072 {
00073 if (*str == '\\')
00074 {
00075 i++, str++;
00076 if (i >= l)
00077 {
00078 *r++ = '\\';
00079 break;
00080 }
00081 switch(*str)
00082 {
00083 case 's':
00084 *r++ = ' ';
00085 break;
00086 case 't':
00087 *r++ = '\t';
00088 break;
00089 case 'n':
00090 *r++ = '\n';
00091 break;
00092 case 'r':
00093 *r++ = '\r';
00094 break;
00095 case '\\':
00096 *r++ = '\\';
00097 break;
00098 default:
00099 *r++ = '\\';
00100 *r++ = *str;
00101 }
00102 }
00103 else
00104 {
00105 *r++ = *str;
00106 }
00107 }
00108 result.truncate(r-result.data());
00109 return result;
00110 }
00111
00112 static QCString stringToPrintable(const QCString& str){
00113 QCString result(str.length()*2);
00114 register char *r = result.data();
00115 register char *s = str.data();
00116
00117 if (!s) return QCString("");
00118
00119
00120 if (*s == ' ')
00121 {
00122 *r++ = '\\'; *r++ = 's';
00123 s++;
00124 }
00125
00126 if (*s)
00127 {
00128 while(*s)
00129 {
00130 if (*s == '\n')
00131 {
00132 *r++ = '\\'; *r++ = 'n';
00133 }
00134 else if (*s == '\t')
00135 {
00136 *r++ = '\\'; *r++ = 't';
00137 }
00138 else if (*s == '\r')
00139 {
00140 *r++ = '\\'; *r++ = 'r';
00141 }
00142 else if (*s == '\\')
00143 {
00144 *r++ = '\\'; *r++ = '\\';
00145 }
00146 else
00147 {
00148 *r++ = *s;
00149 }
00150 s++;
00151 }
00152
00153 if (*(r-1) == ' ')
00154 {
00155 *(r-1) = '\\'; *r++ = 's';
00156 }
00157 }
00158
00159 result.truncate(r - result.data());
00160 return result;
00161 }
00162
00163 void KConfigBackEnd::changeFileName(const QString &_fileName,
00164 const char * _resType,
00165 bool _useKDEGlobals)
00166 {
00167 mfileName = _fileName;
00168 resType = _resType;
00169 useKDEGlobals = _useKDEGlobals;
00170 if (mfileName.isEmpty())
00171 mLocalFileName = QString::null;
00172 else if (mfileName[0] == '/')
00173 mLocalFileName = mfileName;
00174 else
00175 mLocalFileName = KGlobal::dirs()->saveLocation(resType) + mfileName;
00176
00177 if (useKDEGlobals)
00178 mGlobalFileName = KGlobal::dirs()->saveLocation("config") +
00179 QString::fromLatin1("kdeglobals");
00180 else
00181 mGlobalFileName = QString::null;
00182 }
00183
00184 KConfigBackEnd::KConfigBackEnd(KConfigBase *_config,
00185 const QString &_fileName,
00186 const char * _resType,
00187 bool _useKDEGlobals)
00188 : pConfig(_config), bFileImmutable(false), mConfigState(KConfigBase::NoAccess), mFileMode(-1)
00189 {
00190 changeFileName(_fileName, _resType, _useKDEGlobals);
00191 }
00192
00193 void KConfigBackEnd::setFileWriteMode(int mode)
00194 {
00195 mFileMode = mode;
00196 }
00197
00198 bool KConfigINIBackEnd::parseConfigFiles()
00199 {
00200
00201 mConfigState = KConfigBase::ReadOnly;
00202 if (!mLocalFileName.isEmpty() && !pConfig->isReadOnly())
00203 {
00204 if (checkAccess(mLocalFileName, W_OK))
00205 {
00206 mConfigState = KConfigBase::ReadWrite;
00207 }
00208 else
00209 {
00210
00211 KURL path;
00212 path.setPath(mLocalFileName);
00213 QString dir=path.directory();
00214 KStandardDirs::makeDir(dir);
00215
00216 if (checkAccess(mLocalFileName, W_OK))
00217 {
00218 mConfigState = KConfigBase::ReadWrite;
00219 }
00220 }
00221 }
00222
00223
00224 bFileImmutable = false;
00225
00226
00227 if (useKDEGlobals) {
00228 QStringList kdercs = KGlobal::dirs()->
00229 findAllResources("config", QString::fromLatin1("kdeglobals"));
00230
00231 if (checkAccess(QString::fromLatin1("/etc/kderc"), R_OK))
00232 kdercs += QString::fromLatin1("/etc/kderc");
00233
00234 kdercs += KGlobal::dirs()->
00235 findAllResources("config", QString::fromLatin1("system.kdeglobals"));
00236
00237 QStringList::ConstIterator it;
00238
00239 for (it = kdercs.fromLast(); it != kdercs.end(); --it) {
00240
00241 QFile aConfigFile( *it );
00242 if (!aConfigFile.open( IO_ReadOnly ))
00243 continue;
00244 parseSingleConfigFile( aConfigFile, 0L, true, (*it != mGlobalFileName) );
00245 aConfigFile.close();
00246 if (bFileImmutable)
00247 break;
00248 }
00249 }
00250
00251 bool bReadFile = !mfileName.isEmpty();
00252 while(bReadFile) {
00253 bReadFile = false;
00254 QString bootLanguage;
00255 if (useKDEGlobals && localeString.isEmpty() && !KGlobal::_locale) {
00256
00257 bootLanguage = KLocale::_initLanguage(pConfig);
00258 setLocaleString(bootLanguage.utf8());
00259 }
00260
00261 bFileImmutable = false;
00262 QStringList list = KGlobal::dirs()->
00263 findAllResources(resType, mfileName);
00264
00265 QStringList::ConstIterator it;
00266
00267 for (it = list.fromLast(); it != list.end(); --it) {
00268
00269 QFile aConfigFile( *it );
00270
00271 bool bIsLocal = (*it == mLocalFileName);
00272 if (aConfigFile.open( IO_ReadOnly )) {
00273 parseSingleConfigFile( aConfigFile, 0L, false, !bIsLocal );
00274 aConfigFile.close();
00275 if (bFileImmutable)
00276 break;
00277 }
00278 }
00279 if (KGlobal::dirs()->isRestrictedResource(resType, mfileName))
00280 bFileImmutable = true;
00281 QString currentLanguage;
00282 if (!bootLanguage.isEmpty())
00283 {
00284 currentLanguage = KLocale::_initLanguage(pConfig);
00285
00286
00287 if (bootLanguage != currentLanguage)
00288 {
00289 bReadFile = true;
00290 setLocaleString(currentLanguage.utf8());
00291 }
00292 }
00293 }
00294 if (bFileImmutable)
00295 mConfigState = KConfigBase::ReadOnly;
00296
00297 return true;
00298 }
00299
00300 #ifdef HAVE_MMAP
00301 #ifdef SIGBUS
00302 static const char **mmap_pEof;
00303
00304 static void mmap_sigbus_handler(int)
00305 {
00306 *mmap_pEof = 0;
00307 write(2, "SIGBUS\n", 7);
00308 signal(SIGBUS, mmap_sigbus_handler);
00309 }
00310 #endif
00311 #endif
00312
00313 void KConfigINIBackEnd::parseSingleConfigFile(QFile &rFile,
00314 KEntryMap *pWriteBackMap,
00315 bool bGlobal, bool bDefault)
00316 {
00317 void (*old_sighandler)(int) = 0;
00318
00319 if (!rFile.isOpen())
00320 return;
00321
00322
00323
00324
00325
00326
00327 QCString aCurrentGroup("<default>");
00328
00329 const char *s, *eof;
00330 QByteArray data;
00331
00332 unsigned int ll = localeString.length();
00333
00334 #ifdef HAVE_MMAP
00335 const char *map = ( const char* ) mmap(0, rFile.size(), PROT_READ, MAP_PRIVATE,
00336 rFile.handle(), 0);
00337
00338 if (map)
00339 {
00340 s = map;
00341 eof = s + rFile.size();
00342
00343 #ifdef SIGBUS
00344 mmap_pEof = &eof;
00345 old_sighandler = signal(SIGBUS, mmap_sigbus_handler);
00346 #endif
00347 }
00348 else
00349 #endif
00350 {
00351 rFile.at(0);
00352 data = rFile.readAll();
00353 s = data.data();
00354 eof = s + data.size();
00355 }
00356
00357 bool fileOptionImmutable = false;
00358 bool groupOptionImmutable = false;
00359 bool groupSkip = false;
00360
00361 int line = 0;
00362 for(; s < eof; s++)
00363 {
00364 line++;
00365
00366 while((s < eof) && isspace(*s) && (*s != '\n'))
00367 s++;
00368
00369
00370 if ((s < eof) && ((*s == '\n') || (*s == '#')))
00371 {
00372 sktoeol:
00373 while ((s < eof) && (*s != '\n'))
00374 s++;
00375 continue;
00376 }
00377 const char *startLine = s;
00378
00379 if (*s == '[')
00380 {
00381 while ((s < eof) && (*s != '\n') && (*s != ']')) s++;
00382 const char *e = s;
00383 while ((s < eof) && (*s != '\n')) s++;
00384 if ((e >= eof) || (*e != ']'))
00385 {
00386 fprintf(stderr, "Invalid group header at %s:%d\n", rFile.name().latin1(), line);
00387 continue;
00388 }
00389
00390
00391 if ((e-startLine == 3) &&
00392 (startLine[1] == '$') &&
00393 (startLine[2] == 'i'))
00394 {
00395 fileOptionImmutable = true;
00396 continue;
00397 }
00398
00399 aCurrentGroup = QCString(startLine + 1, e - startLine);
00400
00401
00402
00403 if (aCurrentGroup == "KDE Desktop Entry")
00404 aCurrentGroup = "Desktop Entry";
00405
00406 groupOptionImmutable = fileOptionImmutable;
00407
00408 e++;
00409 if ((e+2 < eof) && (*e++ == '[') && (*e++ == '$'))
00410 {
00411 if (*e == 'i')
00412 {
00413 groupOptionImmutable = true;
00414 }
00415 }
00416
00417 KEntryKey groupKey(aCurrentGroup, 0);
00418 KEntry entry = pConfig->lookupData(groupKey);
00419 groupSkip = entry.bImmutable;
00420
00421 if (groupSkip)
00422 continue;
00423
00424 entry.bImmutable = groupOptionImmutable;
00425 pConfig->putData(groupKey, entry, false);
00426
00427 if (pWriteBackMap)
00428 {
00429
00430 (*pWriteBackMap)[groupKey] = entry;
00431 }
00432
00433 continue;
00434 }
00435 if (groupSkip)
00436 goto sktoeol;
00437
00438 bool optionImmutable = groupOptionImmutable;
00439 bool optionDeleted = false;
00440 bool optionExpand = false;
00441 const char *endOfKey = 0, *locale = 0, *elocale = 0;
00442 for (; (s < eof) && (*s != '\n'); s++)
00443 {
00444 if (*s == '=')
00445 {
00446 if (!endOfKey)
00447 endOfKey = s;
00448 goto haveeq;
00449 }
00450 if (*s == '[')
00451 {
00452 const char *option;
00453 const char *eoption;
00454 endOfKey = s;
00455 option = ++s;
00456 for (;; s++)
00457 {
00458 if ((s >= eof) || (*s == '\n') || (*s == '=')) {
00459 fprintf(stderr, "Invalid entry (missing ']') at %s:%d\n", rFile.name().latin1(), line);
00460 goto sktoeol;
00461 }
00462 if (*s == ']')
00463 break;
00464 }
00465 eoption = s;
00466 if (*option != '$')
00467 {
00468
00469 if (locale) {
00470 fprintf(stderr, "Invalid entry (second locale!?) at %s:%d\n", rFile.name().latin1(), line);
00471 goto sktoeol;
00472 }
00473 locale = option;
00474 elocale = eoption;
00475 }
00476 else
00477 {
00478
00479 while (option < eoption)
00480 {
00481 option++;
00482 if (*option == 'i')
00483 optionImmutable = true;
00484 else if (*option == 'e')
00485 optionExpand = true;
00486 else if (*option == 'd')
00487 {
00488 optionDeleted = true;
00489 goto haveeq;
00490 }
00491 else if (*option == ']')
00492 break;
00493 }
00494 }
00495 }
00496 }
00497 fprintf(stderr, "Invalid entry (missing '=') at %s:%d\n", rFile.name().latin1(), line);
00498 continue;
00499
00500 haveeq:
00501 for (endOfKey--; ; endOfKey--)
00502 {
00503 if (endOfKey < startLine)
00504 {
00505 fprintf(stderr, "Invalid entry (empty key) at %s:%d\n", rFile.name().latin1(), line);
00506 goto sktoeol;
00507 }
00508 if (!isspace(*endOfKey))
00509 break;
00510 }
00511
00512 const char *st = ++s;
00513 while ((s < eof) && (*s != '\n')) s++;
00514
00515 if (locale) {
00516 unsigned int cl = static_cast<unsigned int>(elocale - locale);
00517 if ((ll != cl) || memcmp(locale, localeString.data(), ll))
00518 {
00519
00520 if ( cl != 1 || ll != 5 || memcmp(locale, "C", 1) || memcmp(localeString.data(), "en_US", 5)) {
00521
00522
00523 if (!pWriteBackMap)
00524 continue;
00525
00526 endOfKey = elocale;
00527 locale = 0;
00528 }
00529 }
00530 }
00531
00532
00533 QCString key(startLine, endOfKey - startLine + 2);
00534 QCString val = printableToString(st, s - st);
00535
00536
00537 KEntryKey aEntryKey(aCurrentGroup, key);
00538 aEntryKey.bLocal = (locale != 0);
00539 aEntryKey.bDefault = bDefault;
00540
00541 KEntry aEntry;
00542 aEntry.mValue = val;
00543 aEntry.bGlobal = bGlobal;
00544 aEntry.bImmutable = optionImmutable;
00545 aEntry.bDeleted = optionDeleted;
00546 aEntry.bExpand = optionExpand;
00547 aEntry.bNLS = (locale != 0);
00548
00549 if (pWriteBackMap) {
00550
00551
00552 pWriteBackMap->insert(aEntryKey, aEntry);
00553 } else {
00554
00555
00556
00557 pConfig->putData(aEntryKey, aEntry, false);
00558 }
00559 }
00560 if (fileOptionImmutable)
00561 bFileImmutable = true;
00562
00563 #ifdef HAVE_MMAP
00564 if (map)
00565 {
00566 munmap(( char* )map, rFile.size());
00567 #ifdef SIGBUS
00568 signal(SIGBUS, old_sighandler);
00569 #endif
00570 }
00571 #endif
00572 }
00573
00574
00575 void KConfigINIBackEnd::sync(bool bMerge)
00576 {
00577
00578 if (!pConfig->isDirty())
00579 return;
00580
00581 bool bEntriesLeft = true;
00582
00583
00584
00585
00586 if (!mfileName.isEmpty()) {
00587
00588 if ((resType!="config") && mLocalFileName[0]=='/')
00589 {
00590 KURL path;
00591 path.setPath(mLocalFileName);
00592 QString dir=path.directory();
00593 KStandardDirs::makeDir(dir);
00594 }
00595
00596
00597
00598
00599
00600 if (checkAccess(mLocalFileName, W_OK)) {
00601
00602 bEntriesLeft = writeConfigFile( mLocalFileName, false, bMerge );
00603 }
00604 }
00605
00606
00607
00608
00609 if (bEntriesLeft && useKDEGlobals) {
00610
00611
00612
00613 if (checkAccess ( mGlobalFileName, W_OK )) {
00614 writeConfigFile( mGlobalFileName, true, bMerge );
00615 }
00616 }
00617
00618 }
00619
00620 static void writeEntries(FILE *pStream, const KEntryMap& entryMap, bool defaultGroup, bool &firstEntry, const QCString &localeString)
00621 {
00622
00623 QCString currentGroup;
00624 for (KEntryMapConstIterator aIt = entryMap.begin();
00625 aIt != entryMap.end(); ++aIt)
00626 {
00627 const KEntryKey &key = aIt.key();
00628
00629
00630 if ((key.mGroup != "<default>") == defaultGroup)
00631 continue;
00632
00633
00634 if ((key.bDefault) || key.mKey.isEmpty())
00635 continue;
00636
00637 const KEntry ¤tEntry = *aIt;
00638
00639 KEntryMapConstIterator aTestIt = aIt;
00640 ++aTestIt;
00641 bool hasDefault = (aTestIt != entryMap.end());
00642 if (hasDefault)
00643 {
00644 const KEntryKey &defaultKey = aTestIt.key();
00645 if ((!defaultKey.bDefault) ||
00646 (defaultKey.mKey != key.mKey) ||
00647 (defaultKey.mGroup != key.mGroup) ||
00648 (defaultKey.bLocal != key.bLocal))
00649 hasDefault = false;
00650 }
00651
00652
00653 if (hasDefault)
00654 {
00655
00656 if ((currentEntry.mValue == (*aTestIt).mValue) &&
00657 (currentEntry.bDeleted == (*aTestIt).bDeleted))
00658 continue;
00659 }
00660 else
00661 {
00662
00663 if (currentEntry.bDeleted)
00664 continue;
00665 }
00666
00667 if (!defaultGroup && (currentGroup != key.mGroup)) {
00668 if (!firstEntry)
00669 fprintf(pStream, "\n");
00670 currentGroup = key.mGroup;
00671 fprintf(pStream, "[%s]\n", currentGroup.data());
00672 }
00673
00674 firstEntry = false;
00675
00676 fputs(key.mKey.data(), pStream);
00677
00678 if ( currentEntry.bNLS )
00679 {
00680 fputc('[', pStream);
00681 fputs(localeString.data(), pStream);
00682 fputc(']', pStream);
00683 }
00684
00685 if (currentEntry.bDeleted)
00686 {
00687 fputs("[$d]\n", pStream);
00688 }
00689 else
00690 {
00691 if (currentEntry.bImmutable || currentEntry.bExpand)
00692 {
00693 fputc('[', pStream);
00694 fputc('$', pStream);
00695 if (currentEntry.bImmutable)
00696 fputc('i', pStream);
00697 if (currentEntry.bExpand)
00698 fputc('e', pStream);
00699
00700 fputc(']', pStream);
00701 }
00702 fputc('=', pStream);
00703 fputs(stringToPrintable(currentEntry.mValue).data(), pStream);
00704 fputc('\n', pStream);
00705 }
00706 }
00707 }
00708
00709 bool KConfigINIBackEnd::writeConfigFile(QString filename, bool bGlobal,
00710 bool bMerge)
00711 {
00712 KEntryMap aTempMap;
00713 bool bEntriesLeft = false;
00714
00715
00716 if (pConfig->isReadOnly())
00717 return true;
00718
00719 bFileImmutable = false;
00720 if (bMerge)
00721 {
00722
00723 QFile rConfigFile( filename );
00724 if (rConfigFile.open(IO_ReadOnly))
00725 {
00726
00727 parseSingleConfigFile( rConfigFile, &aTempMap, bGlobal, false );
00728 rConfigFile.close();
00729
00730 if (bFileImmutable)
00731 return true;
00732 }
00733
00734 KEntryMap aMap = pConfig->internalEntryMap();
00735
00736
00737 for (KEntryMapIterator aIt = aMap.begin();
00738 aIt != aMap.end(); ++aIt)
00739 {
00740 const KEntry ¤tEntry = *aIt;
00741 if(aIt.key().bDefault)
00742 {
00743 aTempMap.replace(aIt.key(), currentEntry);
00744 continue;
00745 }
00746
00747 if (!currentEntry.bDirty)
00748 continue;
00749
00750
00751
00752 if (currentEntry.bGlobal != bGlobal)
00753 {
00754
00755 bEntriesLeft = true;
00756 continue;
00757 }
00758
00759
00760
00761 KEntryMapIterator aIt2 = aTempMap.find(aIt.key());
00762 if (aIt2 != aTempMap.end() && (*aIt2).bImmutable)
00763 continue;
00764
00765 aTempMap.insert(aIt.key(), currentEntry, true);
00766 }
00767 }
00768 else
00769 {
00770
00771 aTempMap = pConfig->internalEntryMap();
00772 bEntriesLeft = true;
00773 }
00774
00775
00776
00777
00778
00779 int fileMode = -1;
00780 bool createNew = true;
00781
00782 struct stat buf;
00783 if (lstat(QFile::encodeName(filename), &buf) == 0)
00784 {
00785 if (S_ISLNK(buf.st_mode))
00786 {
00787
00788 if (stat(QFile::encodeName(filename), &buf) == 0)
00789 {
00790
00791 createNew = false;
00792 }
00793 }
00794 else if (buf.st_uid == getuid())
00795 {
00796
00797 fileMode = buf.st_mode & 0777;
00798 }
00799 else
00800 {
00801
00802
00803 createNew = false;
00804 }
00805 }
00806
00807 KSaveFile *pConfigFile = 0;
00808 FILE *pStream = 0;
00809
00810 if (createNew)
00811 {
00812 pConfigFile = new KSaveFile( filename, 0600 );
00813
00814 if (pConfigFile->status() != 0)
00815 {
00816 delete pConfigFile;
00817 return bEntriesLeft;
00818 }
00819
00820 if (!bGlobal && (fileMode == -1))
00821 fileMode = mFileMode;
00822
00823 if (fileMode != -1)
00824 {
00825 fchmod(pConfigFile->handle(), fileMode);
00826 }
00827
00828 pStream = pConfigFile->fstream();
00829 }
00830 else
00831 {
00832
00833
00834 int fd = open( QFile::encodeName(filename), O_WRONLY | O_TRUNC);
00835 if (fd < 0)
00836 return bEntriesLeft;
00837 pStream = fdopen( fd, "w");
00838 if (!pStream)
00839 {
00840 close(fd);
00841 return bEntriesLeft;
00842 }
00843 }
00844
00845 bool firstEntry = true;
00846
00847
00848 writeEntries(pStream, aTempMap, true, firstEntry, localeString);
00849
00850
00851 writeEntries(pStream, aTempMap, false, firstEntry, localeString);
00852
00853 if (pConfigFile)
00854 {
00855 pConfigFile->close();
00856 delete pConfigFile;
00857 }
00858 else
00859 {
00860 fclose(pStream);
00861 }
00862
00863 return bEntriesLeft;
00864 }
00865
00866
00867 void KConfigBackEnd::virtual_hook( int, void* )
00868 { }
00869
00870 void KConfigINIBackEnd::virtual_hook( int id, void* data )
00871 { KConfigBackEnd::virtual_hook( id, data ); }
00872