kdecore Library API Documentation

kconfigbackend.cpp

00001 /* 00002 This file is part of the KDE libraries 00003 Copyright (c) 1999 Preston Brown <pbrown@kde.org> 00004 Copyright (c) 1997-1999 Matthias Kalle Dalheimer <kalle@kde.org> 00005 00006 This library is free software; you can redistribute it and/or 00007 modify it under the terms of the GNU Library General Public 00008 License as published by the Free Software Foundation; either 00009 version 2 of the License, or (at your option) any later version. 00010 00011 This library is distributed in the hope that it will be useful, 00012 but WITHOUT ANY WARRANTY; without even the implied warranty of 00013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00014 Library General Public License for more details. 00015 00016 You should have received a copy of the GNU Library General Public License 00017 along with this library; see the file COPYING.LIB. If not, write to 00018 the Free Software Foundation, Inc., 59 Temple Place - Suite 330, 00019 Boston, MA 02111-1307, USA. 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 00052 extern bool checkAccess(const QString& pathname, int mode); 00053 /* translate escaped escape sequences to their actual values. */ 00054 static QCString printableToString(const char *str, int l) 00055 { 00056 // Strip leading white-space. 00057 while((l>0) && 00058 ((*str == ' ') || (*str == '\t') || (*str == '\r'))) 00059 { 00060 str++; l--; 00061 } 00062 00063 // Strip trailing white-space. 00064 while((l>0) && 00065 ((str[l-1] == ' ') || (str[l-1] == '\t') || (str[l-1] == '\r'))) 00066 { 00067 l--; 00068 } 00069 00070 QCString result(l + 1); 00071 char *r = result.data(); 00072 00073 for(int i = 0; i < l;i++, str++) 00074 { 00075 if (*str == '\\') 00076 { 00077 i++, str++; 00078 if (i >= l) // End of line. (Line ends with single slash) 00079 { 00080 *r++ = '\\'; 00081 break; 00082 } 00083 switch(*str) 00084 { 00085 case 's': 00086 *r++ = ' '; 00087 break; 00088 case 't': 00089 *r++ = '\t'; 00090 break; 00091 case 'n': 00092 *r++ = '\n'; 00093 break; 00094 case 'r': 00095 *r++ = '\r'; 00096 break; 00097 case '\\': 00098 *r++ = '\\'; 00099 break; 00100 default: 00101 *r++ = '\\'; 00102 *r++ = *str; 00103 } 00104 } 00105 else 00106 { 00107 *r++ = *str; 00108 } 00109 } 00110 result.truncate(r-result.data()); 00111 return result; 00112 } 00113 00114 static QCString stringToPrintable(const QCString& str){ 00115 QCString result(str.length()*2); // Maximum 2x as long as source string 00116 register char *r = result.data(); 00117 register char *s = str.data(); 00118 00119 if (!s) return QCString(""); 00120 00121 // Escape leading space 00122 if (*s == ' ') 00123 { 00124 *r++ = '\\'; *r++ = 's'; 00125 s++; 00126 } 00127 00128 if (*s) 00129 { 00130 while(*s) 00131 { 00132 if (*s == '\n') 00133 { 00134 *r++ = '\\'; *r++ = 'n'; 00135 } 00136 else if (*s == '\t') 00137 { 00138 *r++ = '\\'; *r++ = 't'; 00139 } 00140 else if (*s == '\r') 00141 { 00142 *r++ = '\\'; *r++ = 'r'; 00143 } 00144 else if (*s == '\\') 00145 { 00146 *r++ = '\\'; *r++ = '\\'; 00147 } 00148 else 00149 { 00150 *r++ = *s; 00151 } 00152 s++; 00153 } 00154 // Escape trailing space 00155 if (*(r-1) == ' ') 00156 { 00157 *(r-1) = '\\'; *r++ = 's'; 00158 } 00159 } 00160 00161 result.truncate(r - result.data()); 00162 return result; 00163 } 00164 00165 static QCString decodeGroup(const char*s, int l) 00166 { 00167 QCString result(l); 00168 register char *r = result.data(); 00169 00170 l--; // Correct for trailing \0 00171 while(l) 00172 { 00173 if ((*s == '[') && (l > 1)) 00174 { 00175 if ((*(s+1) == '[')) 00176 { 00177 l--; 00178 s++; 00179 } 00180 } 00181 if ((*s == ']') && (l > 1)) 00182 { 00183 if ((*(s+1) == ']')) 00184 { 00185 l--; 00186 s++; 00187 } 00188 } 00189 *r++ = *s++; 00190 l--; 00191 } 00192 result.truncate(r - result.data()); 00193 return result; 00194 } 00195 00196 static QCString encodeGroup(const QCString &str) 00197 { 00198 int l = str.length(); 00199 QCString result(l*2+1); 00200 register char *r = result.data(); 00201 register char *s = str.data(); 00202 while(l) 00203 { 00204 if ((*s == '[') || (*s == ']')) 00205 *r++ = *s; 00206 *r++ = *s++; 00207 l--; 00208 } 00209 result.truncate(r - result.data()); 00210 return result; 00211 } 00212 00213 class KConfigBackEnd::KConfigBackEndPrivate 00214 { 00215 public: 00216 QDateTime localLastModified; 00217 uint localLastSize; 00218 }; 00219 00220 void KConfigBackEnd::changeFileName(const QString &_fileName, 00221 const char * _resType, 00222 bool _useKDEGlobals) 00223 { 00224 mfileName = _fileName; 00225 resType = _resType; 00226 useKDEGlobals = _useKDEGlobals; 00227 if (mfileName.isEmpty()) 00228 mLocalFileName = QString::null; 00229 else if (mfileName[0] == '/') 00230 mLocalFileName = mfileName; 00231 else 00232 mLocalFileName = KGlobal::dirs()->saveLocation(resType) + mfileName; 00233 00234 if (useKDEGlobals) 00235 mGlobalFileName = KGlobal::dirs()->saveLocation("config") + 00236 QString::fromLatin1("kdeglobals"); 00237 else 00238 mGlobalFileName = QString::null; 00239 00240 d->localLastModified = QDateTime(); 00241 d->localLastSize = 0; 00242 } 00243 00244 KConfigBackEnd::KConfigBackEnd(KConfigBase *_config, 00245 const QString &_fileName, 00246 const char * _resType, 00247 bool _useKDEGlobals) 00248 : pConfig(_config), bFileImmutable(false), mConfigState(KConfigBase::NoAccess), mFileMode(-1) 00249 { 00250 d = new KConfigBackEndPrivate; 00251 changeFileName(_fileName, _resType, _useKDEGlobals); 00252 } 00253 00254 KConfigBackEnd::~KConfigBackEnd() 00255 { 00256 delete d; 00257 } 00258 00259 void KConfigBackEnd::setFileWriteMode(int mode) 00260 { 00261 mFileMode = mode; 00262 } 00263 00264 bool KConfigINIBackEnd::parseConfigFiles() 00265 { 00266 // Check if we can write to the local file. 00267 mConfigState = KConfigBase::ReadOnly; 00268 if (!mLocalFileName.isEmpty() && !pConfig->isReadOnly()) 00269 { 00270 if (checkAccess(mLocalFileName, W_OK)) 00271 { 00272 mConfigState = KConfigBase::ReadWrite; 00273 } 00274 else 00275 { 00276 // Create the containing dir, maybe it wasn't there 00277 KURL path; 00278 path.setPath(mLocalFileName); 00279 QString dir=path.directory(); 00280 KStandardDirs::makeDir(dir); 00281 00282 if (checkAccess(mLocalFileName, W_OK)) 00283 { 00284 mConfigState = KConfigBase::ReadWrite; 00285 } 00286 } 00287 QFileInfo info(mLocalFileName); 00288 d->localLastModified = info.lastModified(); 00289 d->localLastSize = info.size(); 00290 } 00291 00292 // Parse all desired files from the least to the most specific. 00293 bFileImmutable = false; 00294 00295 // Parse the general config files 00296 if (useKDEGlobals) { 00297 QStringList kdercs = KGlobal::dirs()-> 00298 findAllResources("config", QString::fromLatin1("kdeglobals")); 00299 00300 if (checkAccess(QString::fromLatin1("/etc/kderc"), R_OK)) 00301 kdercs += QString::fromLatin1("/etc/kderc"); 00302 00303 kdercs += KGlobal::dirs()-> 00304 findAllResources("config", QString::fromLatin1("system.kdeglobals")); 00305 00306 QStringList::ConstIterator it; 00307 00308 for (it = kdercs.fromLast(); it != kdercs.end(); --it) { 00309 00310 QFile aConfigFile( *it ); 00311 if (!aConfigFile.open( IO_ReadOnly )) 00312 continue; 00313 parseSingleConfigFile( aConfigFile, 0L, true, (*it != mGlobalFileName) ); 00314 aConfigFile.close(); 00315 if (bFileImmutable) 00316 break; 00317 } 00318 } 00319 00320 bool bReadFile = !mfileName.isEmpty(); 00321 while(bReadFile) { 00322 bReadFile = false; 00323 QString bootLanguage; 00324 if (useKDEGlobals && localeString.isEmpty() && !KGlobal::_locale) { 00325 // Boot strap language 00326 bootLanguage = KLocale::_initLanguage(pConfig); 00327 setLocaleString(bootLanguage.utf8()); 00328 } 00329 00330 bFileImmutable = false; 00331 QStringList list; 00332 if ( mfileName[0] == '/' ) 00333 list << mfileName; 00334 else 00335 list = KGlobal::dirs()->findAllResources(resType, mfileName); 00336 00337 QStringList::ConstIterator it; 00338 00339 for (it = list.fromLast(); it != list.end(); --it) { 00340 00341 QFile aConfigFile( *it ); 00342 // we can already be sure that this file exists 00343 bool bIsLocal = (*it == mLocalFileName); 00344 if (aConfigFile.open( IO_ReadOnly )) { 00345 parseSingleConfigFile( aConfigFile, 0L, false, !bIsLocal ); 00346 aConfigFile.close(); 00347 if (bFileImmutable) 00348 break; 00349 } 00350 } 00351 if (KGlobal::dirs()->isRestrictedResource(resType, mfileName)) 00352 bFileImmutable = true; 00353 QString currentLanguage; 00354 if (!bootLanguage.isEmpty()) 00355 { 00356 currentLanguage = KLocale::_initLanguage(pConfig); 00357 // If the file changed the language, we need to read the file again 00358 // with the new language setting. 00359 if (bootLanguage != currentLanguage) 00360 { 00361 bReadFile = true; 00362 setLocaleString(currentLanguage.utf8()); 00363 } 00364 } 00365 } 00366 if (bFileImmutable) 00367 mConfigState = KConfigBase::ReadOnly; 00368 00369 return true; 00370 } 00371 00372 #ifdef HAVE_MMAP 00373 #ifdef SIGBUS 00374 static sigjmp_buf mmap_jmpbuf; 00375 struct sigaction mmap_old_sigact; 00376 00377 extern "C" { 00378 static void mmap_sigbus_handler(int) 00379 { 00380 siglongjmp (mmap_jmpbuf, 1); 00381 } 00382 } 00383 #endif 00384 #endif 00385 00386 extern bool kde_kiosk_exception; 00387 00388 void KConfigINIBackEnd::parseSingleConfigFile(QFile &rFile, 00389 KEntryMap *pWriteBackMap, 00390 bool bGlobal, bool bDefault) 00391 { 00392 const char *s; // May get clobbered by sigsetjump, but we don't use them afterwards. 00393 const char *eof; // May get clobbered by sigsetjump, but we don't use them afterwards. 00394 QByteArray data; 00395 00396 if (!rFile.isOpen()) // come back, if you have real work for us ;-> 00397 return; 00398 00399 //using kdDebug() here leads to an infinite loop 00400 //remove this for the release, aleXXX 00401 //qWarning("Parsing %s, global = %s default = %s", 00402 // rFile.name().latin1(), bGlobal ? "true" : "false", bDefault ? "true" : "false"); 00403 00404 QCString aCurrentGroup("<default>"); 00405 00406 unsigned int ll = localeString.length(); 00407 00408 #ifdef HAVE_MMAP 00409 static volatile const char *map; 00410 map = ( const char* ) mmap(0, rFile.size(), PROT_READ, MAP_PRIVATE, 00411 rFile.handle(), 0); 00412 00413 if (map) 00414 { 00415 s = (const char*) map; 00416 eof = s + rFile.size(); 00417 00418 #ifdef SIGBUS 00419 struct sigaction act; 00420 act.sa_handler = mmap_sigbus_handler; 00421 sigemptyset( &act.sa_mask ); 00422 #ifdef SA_ONESHOT 00423 act.sa_flags = SA_ONESHOT; 00424 #else 00425 act.sa_flags = SA_RESETHAND; 00426 #endif 00427 sigaction( SIGBUS, &act, &mmap_old_sigact ); 00428 00429 if (sigsetjmp (mmap_jmpbuf, 1)) 00430 { 00431 munmap(( char* )map, rFile.size()); 00432 sigaction (SIGBUS, &mmap_old_sigact, 0); 00433 return; 00434 } 00435 #endif 00436 } 00437 else 00438 #endif 00439 { 00440 rFile.at(0); 00441 data = rFile.readAll(); 00442 s = data.data(); 00443 eof = s + data.size(); 00444 } 00445 00446 bool fileOptionImmutable = false; 00447 bool groupOptionImmutable = false; 00448 bool groupSkip = false; 00449 00450 int line = 0; 00451 for(; s < eof; s++) 00452 { 00453 line++; 00454 00455 while((s < eof) && isspace(*s) && (*s != '\n')) 00456 s++; //skip leading whitespace, shouldn't happen too often 00457 00458 //skip empty lines, lines starting with # 00459 if ((s < eof) && ((*s == '\n') || (*s == '#'))) 00460 { 00461 sktoeol: //skip till end-of-line 00462 while ((s < eof) && (*s != '\n')) 00463 s++; 00464 continue; // Empty or comment or no keyword 00465 } 00466 const char *startLine = s; 00467 00468 if (*s == '[') //group 00469 { 00470 // In a group [[ and ]] have a special meaning 00471 while ((s < eof) && (*s != '\n')) 00472 { 00473 if (*s == ']') 00474 { 00475 if ((s+1 < eof) && (*(s+1) == ']')) 00476 s++; // Skip "]]" 00477 else 00478 break; 00479 } 00480 00481 s++; // Search till end of group 00482 } 00483 const char *e = s; 00484 while ((s < eof) && (*s != '\n')) s++; // Search till end of line / end of file 00485 if ((e >= eof) || (*e != ']')) 00486 { 00487 fprintf(stderr, "Invalid group header at %s:%d\n", rFile.name().latin1(), line); 00488 continue; 00489 } 00490 // group found; get the group name by taking everything in 00491 // between the brackets 00492 if ((e-startLine == 3) && 00493 (startLine[1] == '$') && 00494 (startLine[2] == 'i')) 00495 { 00496 if (!kde_kiosk_exception) 00497 fileOptionImmutable = true; 00498 continue; 00499 } 00500 00501 aCurrentGroup = decodeGroup(startLine + 1, e - startLine); 00502 //cout<<"found group ["<<aCurrentGroup<<"]"<<endl; 00503 00504 // Backwards compatibility 00505 if (aCurrentGroup == "KDE Desktop Entry") 00506 aCurrentGroup = "Desktop Entry"; 00507 00508 groupOptionImmutable = fileOptionImmutable; 00509 00510 e++; 00511 if ((e+2 < eof) && (*e++ == '[') && (*e++ == '$')) // Option follows 00512 { 00513 if ((*e == 'i') && !kde_kiosk_exception) 00514 { 00515 groupOptionImmutable = true; 00516 } 00517 } 00518 00519 KEntryKey groupKey(aCurrentGroup, 0); 00520 KEntry entry = pConfig->lookupData(groupKey); 00521 groupSkip = entry.bImmutable; 00522 00523 if (groupSkip && !bDefault) 00524 continue; 00525 00526 entry.bImmutable |= groupOptionImmutable; 00527 pConfig->putData(groupKey, entry, false); 00528 00529 if (pWriteBackMap) 00530 { 00531 // add the special group key indicator 00532 (*pWriteBackMap)[groupKey] = entry; 00533 } 00534 00535 continue; 00536 } 00537 if (groupSkip && !bDefault) 00538 goto sktoeol; // Skip entry 00539 00540 bool optionImmutable = groupOptionImmutable; 00541 bool optionDeleted = false; 00542 bool optionExpand = false; 00543 const char *endOfKey = 0, *locale = 0, *elocale = 0; 00544 for (; (s < eof) && (*s != '\n'); s++) 00545 { 00546 if (*s == '=') //find the equal sign 00547 { 00548 if (!endOfKey) 00549 endOfKey = s; 00550 goto haveeq; 00551 } 00552 if (*s == '[') //find the locale or options. 00553 { 00554 const char *option; 00555 const char *eoption; 00556 endOfKey = s; 00557 option = ++s; 00558 for (;; s++) 00559 { 00560 if ((s >= eof) || (*s == '\n') || (*s == '=')) { 00561 fprintf(stderr, "Invalid entry (missing ']') at %s:%d\n", rFile.name().latin1(), line); 00562 goto sktoeol; 00563 } 00564 if (*s == ']') 00565 break; 00566 } 00567 eoption = s; 00568 if (*option != '$') 00569 { 00570 // Locale 00571 if (locale) { 00572 fprintf(stderr, "Invalid entry (second locale!?) at %s:%d\n", rFile.name().latin1(), line); 00573 goto sktoeol; 00574 } 00575 locale = option; 00576 elocale = eoption; 00577 } 00578 else 00579 { 00580 // Option 00581 while (option < eoption) 00582 { 00583 option++; 00584 if ((*option == 'i') && !kde_kiosk_exception) 00585 optionImmutable = true; 00586 else if (*option == 'e') 00587 optionExpand = true; 00588 else if (*option == 'd') 00589 { 00590 optionDeleted = true; 00591 goto haveeq; 00592 } 00593 else if (*option == ']') 00594 break; 00595 } 00596 } 00597 } 00598 } 00599 fprintf(stderr, "Invalid entry (missing '=') at %s:%d\n", rFile.name().latin1(), line); 00600 continue; 00601 00602 haveeq: 00603 for (endOfKey--; ; endOfKey--) 00604 { 00605 if (endOfKey < startLine) 00606 { 00607 fprintf(stderr, "Invalid entry (empty key) at %s:%d\n", rFile.name().latin1(), line); 00608 goto sktoeol; 00609 } 00610 if (!isspace(*endOfKey)) 00611 break; 00612 } 00613 00614 const char *st = ++s; 00615 while ((s < eof) && (*s != '\n')) s++; // Search till end of line / end of file 00616 00617 if (locale) { 00618 unsigned int cl = static_cast<unsigned int>(elocale - locale); 00619 if ((ll != cl) || memcmp(locale, localeString.data(), ll)) 00620 { 00621 // backward compatibility. C == en_US 00622 if ( cl != 1 || ll != 5 || *locale != 'C' || memcmp(localeString.data(), "en_US", 5)) { 00623 //cout<<"mismatched locale '"<<QCString(locale, elocale-locale +1)<<"'"<<endl; 00624 // We can ignore this one 00625 if (!pWriteBackMap) 00626 continue; // We just ignore it 00627 // We just store it as is to be able to write it back later. 00628 endOfKey = elocale; 00629 locale = 0; 00630 } 00631 } 00632 } 00633 00634 // insert the key/value line 00635 QCString key(startLine, endOfKey - startLine + 2); 00636 QCString val = printableToString(st, s - st); 00637 //qDebug("found key '%s' with value '%s'", key.data(), val.data()); 00638 00639 KEntryKey aEntryKey(aCurrentGroup, key); 00640 aEntryKey.bLocal = (locale != 0); 00641 aEntryKey.bDefault = bDefault; 00642 00643 KEntry aEntry; 00644 aEntry.mValue = val; 00645 aEntry.bGlobal = bGlobal; 00646 aEntry.bImmutable = optionImmutable; 00647 aEntry.bDeleted = optionDeleted; 00648 aEntry.bExpand = optionExpand; 00649 aEntry.bNLS = (locale != 0); 00650 00651 if (pWriteBackMap) { 00652 // don't insert into the config object but into the temporary 00653 // scratchpad map 00654 pWriteBackMap->insert(aEntryKey, aEntry); 00655 } else { 00656 // directly insert value into config object 00657 // no need to specify localization; if the key we just 00658 // retrieved was localized already, no need to localize it again. 00659 pConfig->putData(aEntryKey, aEntry, false); 00660 } 00661 } 00662 if (fileOptionImmutable) 00663 bFileImmutable = true; 00664 00665 #ifdef HAVE_MMAP 00666 if (map) 00667 { 00668 munmap(( char* )map, rFile.size()); 00669 #ifdef SIGBUS 00670 sigaction (SIGBUS, &mmap_old_sigact, 0); 00671 #endif 00672 } 00673 #endif 00674 } 00675 00676 00677 void KConfigINIBackEnd::sync(bool bMerge) 00678 { 00679 // write-sync is only necessary if there are dirty entries 00680 if (!pConfig->isDirty()) 00681 return; 00682 00683 bool bEntriesLeft = true; 00684 00685 // find out the file to write to (most specific writable file) 00686 // try local app-specific file first 00687 00688 if (!mfileName.isEmpty()) { 00689 // Create the containing dir if needed 00690 if ((resType!="config") && mLocalFileName[0]=='/') 00691 { 00692 KURL path; 00693 path.setPath(mLocalFileName); 00694 QString dir=path.directory(); 00695 KStandardDirs::makeDir(dir); 00696 } 00697 00698 // Can we allow the write? We can, if the program 00699 // doesn't run SUID. But if it runs SUID, we must 00700 // check if the user would be allowed to write if 00701 // it wasn't SUID. 00702 if (checkAccess(mLocalFileName, W_OK)) { 00703 // File is writable 00704 00705 bool mergeLocalFile = bMerge; 00706 // Check if the file has been updated since. 00707 if (mergeLocalFile) 00708 { 00709 QFileInfo info(mLocalFileName); 00710 if ((d->localLastSize == info.size()) && 00711 (d->localLastModified == info.lastModified())) 00712 { 00713 // Not changed, don't merge. 00714 mergeLocalFile = false; 00715 } 00716 else 00717 { 00718 // Changed... 00719 d->localLastModified = QDateTime(); 00720 d->localLastSize = 0; 00721 } 00722 } 00723 00724 bEntriesLeft = writeConfigFile( mLocalFileName, false, mergeLocalFile ); 00725 00726 // Only if we didn't have to merge anything can we use our in-memory state 00727 // the next time around. Otherwise the config-file may contain entries 00728 // that are different from our in-memory state which means we will have to 00729 // do a merge from then on. 00730 // We do not automatically update the in-memory state with the on-disk 00731 // state when writing the config to disk. We only do so when 00732 // KCOnfig::reparseConfiguration() is called. 00733 // For KDE 4.0 we may wish to reconsider that. 00734 if (!mergeLocalFile) 00735 { 00736 QFileInfo info(mLocalFileName); 00737 d->localLastModified = info.lastModified(); 00738 d->localLastSize = info.size(); 00739 } 00740 } 00741 } 00742 00743 // only write out entries to the kdeglobals file if there are any 00744 // entries marked global (indicated by bEntriesLeft) and 00745 // the useKDEGlobals flag is set. 00746 if (bEntriesLeft && useKDEGlobals) { 00747 00748 // can we allow the write? (see above) 00749 if (checkAccess ( mGlobalFileName, W_OK )) { 00750 writeConfigFile( mGlobalFileName, true, bMerge ); // Always merge 00751 } 00752 } 00753 00754 } 00755 00756 static void writeEntries(FILE *pStream, const KEntryMap& entryMap, bool defaultGroup, bool &firstEntry, const QCString &localeString) 00757 { 00758 // now write out all other groups. 00759 QCString currentGroup; 00760 for (KEntryMapConstIterator aIt = entryMap.begin(); 00761 aIt != entryMap.end(); ++aIt) 00762 { 00763 const KEntryKey &key = aIt.key(); 00764 00765 // Either proces the default group or all others 00766 if ((key.mGroup != "<default>") == defaultGroup) 00767 continue; // Skip 00768 00769 // Skip default values and group headers. 00770 if ((key.bDefault) || key.mKey.isEmpty()) 00771 continue; // Skip 00772 00773 const KEntry &currentEntry = *aIt; 00774 00775 KEntryMapConstIterator aTestIt = aIt; 00776 ++aTestIt; 00777 bool hasDefault = (aTestIt != entryMap.end()); 00778 if (hasDefault) 00779 { 00780 const KEntryKey &defaultKey = aTestIt.key(); 00781 if ((!defaultKey.bDefault) || 00782 (defaultKey.mKey != key.mKey) || 00783 (defaultKey.mGroup != key.mGroup) || 00784 (defaultKey.bLocal != key.bLocal)) 00785 hasDefault = false; 00786 } 00787 00788 00789 if (hasDefault) 00790 { 00791 // Entry had a default value 00792 if ((currentEntry.mValue == (*aTestIt).mValue) && 00793 (currentEntry.bDeleted == (*aTestIt).bDeleted)) 00794 continue; // Same as default, don't write. 00795 } 00796 else 00797 { 00798 // Entry had no default value. 00799 if (currentEntry.bDeleted) 00800 continue; // Don't write deleted entries if there is no default. 00801 } 00802 00803 if (!defaultGroup && (currentGroup != key.mGroup)) { 00804 if (!firstEntry) 00805 fprintf(pStream, "\n"); 00806 currentGroup = key.mGroup; 00807 fprintf(pStream, "[%s]\n", encodeGroup(currentGroup).data()); 00808 } 00809 00810 firstEntry = false; 00811 // it is data for a group 00812 fputs(key.mKey.data(), pStream); // Key 00813 00814 if ( currentEntry.bNLS ) 00815 { 00816 fputc('[', pStream); 00817 fputs(localeString.data(), pStream); 00818 fputc(']', pStream); 00819 } 00820 00821 if (currentEntry.bDeleted) 00822 { 00823 fputs("[$d]\n", pStream); // Deleted 00824 } 00825 else 00826 { 00827 if (currentEntry.bImmutable || currentEntry.bExpand) 00828 { 00829 fputc('[', pStream); 00830 fputc('$', pStream); 00831 if (currentEntry.bImmutable) 00832 fputc('i', pStream); 00833 if (currentEntry.bExpand) 00834 fputc('e', pStream); 00835 00836 fputc(']', pStream); 00837 } 00838 fputc('=', pStream); 00839 fputs(stringToPrintable(currentEntry.mValue).data(), pStream); 00840 fputc('\n', pStream); 00841 } 00842 } // for loop 00843 } 00844 00845 bool KConfigINIBackEnd::getEntryMap(KEntryMap &aTempMap, bool bGlobal, 00846 QFile *mergeFile) 00847 { 00848 bool bEntriesLeft = false; 00849 bFileImmutable = false; 00850 00851 // Read entries from disk 00852 if (mergeFile && mergeFile->open(IO_ReadOnly)) 00853 { 00854 // fill the temporary structure with entries from the file 00855 parseSingleConfigFile(*mergeFile, &aTempMap, bGlobal, false ); 00856 00857 if (bFileImmutable) // File has become immutable on disk 00858 return bEntriesLeft; 00859 } 00860 00861 KEntryMap aMap = pConfig->internalEntryMap(); 00862 00863 // augment this structure with the dirty entries from the config object 00864 for (KEntryMapIterator aIt = aMap.begin(); 00865 aIt != aMap.end(); ++aIt) 00866 { 00867 const KEntry &currentEntry = *aIt; 00868 if(aIt.key().bDefault) 00869 { 00870 aTempMap.replace(aIt.key(), currentEntry); 00871 continue; 00872 } 00873 00874 if (mergeFile && !currentEntry.bDirty) 00875 continue; 00876 00877 // only write back entries that have the same 00878 // "globality" as the file 00879 if (currentEntry.bGlobal != bGlobal) 00880 { 00881 // wrong "globality" - might have to be saved later 00882 bEntriesLeft = true; 00883 continue; 00884 } 00885 00886 // put this entry from the config object into the 00887 // temporary map, possibly replacing an existing entry 00888 KEntryMapIterator aIt2 = aTempMap.find(aIt.key()); 00889 if (aIt2 != aTempMap.end() && (*aIt2).bImmutable) 00890 continue; // Bail out if the on-disk entry is immutable 00891 00892 aTempMap.insert(aIt.key(), currentEntry, true); 00893 } // loop 00894 00895 return bEntriesLeft; 00896 } 00897 00898 /* antlarr: KDE 4.0: make the first parameter "const QString &" */ 00899 bool KConfigINIBackEnd::writeConfigFile(QString filename, bool bGlobal, 00900 bool bMerge) 00901 { 00902 // is the config object read-only? 00903 if (pConfig->isReadOnly()) 00904 return true; // pretend we wrote it 00905 00906 KEntryMap aTempMap; 00907 QFile *mergeFile = (bMerge ? new QFile(filename) : 0); 00908 bool bEntriesLeft = getEntryMap(aTempMap, bGlobal, mergeFile); 00909 delete mergeFile; 00910 if (bFileImmutable) return true; // pretend we wrote it 00911 00912 // OK now the temporary map should be full of ALL entries. 00913 // write it out to disk. 00914 00915 // Check if file exists: 00916 int fileMode = -1; 00917 bool createNew = true; 00918 00919 struct stat buf; 00920 if (lstat(QFile::encodeName(filename), &buf) == 0) 00921 { 00922 if (S_ISLNK(buf.st_mode)) 00923 { 00924 // File is a symlink: 00925 if (stat(QFile::encodeName(filename), &buf) == 0) 00926 { 00927 // Don't create new file but write to existing file instead. 00928 createNew = false; 00929 } 00930 } 00931 else if (buf.st_uid == getuid()) 00932 { 00933 // Preserve file mode if file exists and is owned by user. 00934 fileMode = buf.st_mode & 0777; 00935 } 00936 else 00937 { 00938 // File is not owned by user: 00939 // Don't create new file but write to existing file instead. 00940 createNew = false; 00941 } 00942 } 00943 00944 KSaveFile *pConfigFile = 0; 00945 FILE *pStream = 0; 00946 00947 if (createNew) 00948 { 00949 pConfigFile = new KSaveFile( filename, 0600 ); 00950 00951 if (pConfigFile->status() != 0) 00952 { 00953 delete pConfigFile; 00954 return bEntriesLeft; 00955 } 00956 00957 if (!bGlobal && (fileMode == -1)) 00958 fileMode = mFileMode; 00959 00960 if (fileMode != -1) 00961 { 00962 fchmod(pConfigFile->handle(), fileMode); 00963 } 00964 00965 pStream = pConfigFile->fstream(); 00966 } 00967 else 00968 { 00969 // Open existing file. 00970 // We use open() to ensure that we call without O_CREAT. 00971 int fd = open( QFile::encodeName(filename), O_WRONLY | O_TRUNC); 00972 if (fd < 0) 00973 return bEntriesLeft; 00974 pStream = fdopen( fd, "w"); 00975 if (!pStream) 00976 { 00977 close(fd); 00978 return bEntriesLeft; 00979 } 00980 } 00981 00982 writeEntries(pStream, aTempMap); 00983 00984 if (pConfigFile) 00985 { 00986 bool bEmptyFile = (ftell(pStream) == 0); 00987 if ( bEmptyFile && ((fileMode == -1) || (fileMode == 0600)) ) 00988 { 00989 // File is empty and doesn't have special permissions: delete it. 00990 ::unlink(QFile::encodeName(filename)); 00991 pConfigFile->abort(); 00992 } 00993 else 00994 { 00995 // Normal case: Close the file 00996 pConfigFile->close(); 00997 } 00998 delete pConfigFile; 00999 } 01000 else 01001 { 01002 fclose(pStream); 01003 } 01004 01005 return bEntriesLeft; 01006 } 01007 01008 void KConfigINIBackEnd::writeEntries(FILE *pStream, const KEntryMap &aTempMap) 01009 { 01010 bool firstEntry = true; 01011 01012 // Write default group 01013 ::writeEntries(pStream, aTempMap, true, firstEntry, localeString); 01014 01015 // Write all other groups 01016 ::writeEntries(pStream, aTempMap, false, firstEntry, localeString); 01017 } 01018 01019 void KConfigBackEnd::virtual_hook( int, void* ) 01020 { /*BASE::virtual_hook( id, data );*/ } 01021 01022 void KConfigINIBackEnd::virtual_hook( int id, void* data ) 01023 { KConfigBackEnd::virtual_hook( id, data ); } 01024 01025 bool KConfigBackEnd::checkConfigFilesWritable(bool warnUser) 01026 { 01027 // WARNING: Do NOT use the event loop as it may not exist at this time. 01028 bool allWritable = true; 01029 QString errorMsg( i18n("Will not save configuration.\n") ); 01030 if ( !mLocalFileName.isEmpty() && !bFileImmutable && !checkAccess(mLocalFileName,W_OK) ) 01031 { 01032 allWritable = false; 01033 errorMsg += i18n("Configuration file \"%1\" not writable.\n").arg(mLocalFileName); 01034 } 01035 // We do not have an immutability flag for kdeglobals. However, making kdeglobals mutable while making 01036 // the local config file immutable is senseless. 01037 if ( !mGlobalFileName.isEmpty() && useKDEGlobals && !bFileImmutable && !checkAccess(mGlobalFileName,W_OK) ) 01038 { 01039 errorMsg += i18n("Configuration file \"%1\" not writable.\n").arg(mGlobalFileName); 01040 allWritable = false; 01041 } 01042 01043 if (warnUser && !allWritable) 01044 { 01045 // Note: We don't ask the user if we should not ask this question again because we can't save the answer. 01046 errorMsg += i18n("Please contact your system administrator."); 01047 QString cmdToExec = KStandardDirs::findExe(QString("kdialog")); 01048 KApplication *app = kapp; 01049 if (!cmdToExec.isEmpty() && app) 01050 { 01051 KProcess lprocess; 01052 lprocess << cmdToExec << "--title" << app->instanceName() << "--msgbox" << errorMsg.local8Bit(); 01053 lprocess.start( KProcess::Block ); 01054 } 01055 } 01056 return allWritable; 01057 }
KDE Logo
This file is part of the documentation for kdecore Library Version 3.2.3.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Fri Aug 20 09:48:25 2004 by doxygen 1.3.7 written by Dimitri van Heesch, © 1997-2003