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 
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 /* translate escaped escape sequences to their actual values. */
00052 static QCString printableToString(const char *str, int l)
00053 {
00054   // Strip leading white-space.
00055   while((l>0) &&
00056         ((*str == ' ') || (*str == '\t') || (*str == '\r')))
00057   {
00058      str++; l--;
00059   }
00060 
00061   // Strip trailing white-space.
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) // End of line. (Line ends with single slash)
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); // Maximum 2x as long as source string
00114   register char *r = result.data();
00115   register char *s = str.data();
00116 
00117   if (!s) return QCString("");
00118 
00119   // Escape leading space
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    // Escape trailing space
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   // Check if we can write to the local file.
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         // Create the containing dir, maybe it wasn't there
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   // Parse all desired files from the least to the most specific.
00224   bFileImmutable = false;
00225 
00226   // Parse the general config files
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        // Boot strap language
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       // we can already be sure that this file exists
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        // If the file changed the language, we need to read the file again
00286        // with the new language setting.
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()) // come back, if you have real work for us ;->
00320       return;
00321 
00322    //using kdDebug() here leads to an infinite loop
00323    //remove this for the release, aleXXX
00324    //qWarning("Parsing %s, global = %s default = %s",
00325    //           rFile.name().latin1(), bGlobal ? "true" : "false", bDefault ? "true" : "false");
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++; //skip leading whitespace, shouldn't happen too often
00368 
00369       //skip empty lines, lines starting with #
00370       if ((s < eof) && ((*s == '\n') || (*s == '#')))
00371       {
00372     sktoeol:    //skip till end-of-line
00373          while ((s < eof) && (*s != '\n'))
00374             s++;
00375          continue; // Empty or comment or no keyword
00376       }
00377       const char *startLine = s;
00378 
00379       if (*s == '[')  //group
00380       {
00381          while ((s < eof) && (*s != '\n') && (*s != ']')) s++; // Search till end of group
00382          const char *e = s;
00383          while ((s < eof) && (*s != '\n')) s++; // Search till end of line / end of file
00384          if ((e >= eof) || (*e != ']'))
00385          {
00386             fprintf(stderr, "Invalid group header at %s:%d\n", rFile.name().latin1(), line);
00387             continue;
00388          }
00389          // group found; get the group name by taking everything in
00390          // between the brackets
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          //cout<<"found group ["<<aCurrentGroup<<"]"<<endl;
00401 
00402          // Backwards compatibility
00403          if (aCurrentGroup == "KDE Desktop Entry")
00404             aCurrentGroup = "Desktop Entry";
00405 
00406          groupOptionImmutable = fileOptionImmutable;
00407 
00408          e++;
00409          if ((e+2 < eof) && (*e++ == '[') && (*e++ == '$')) // Option follows
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             // add the special group key indicator
00430             (*pWriteBackMap)[groupKey] = entry;
00431          }
00432 
00433          continue;
00434       }
00435       if (groupSkip)
00436         goto sktoeol; // Skip entry
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 == '=') //find the equal sign
00445          {
00446             if (!endOfKey)
00447                 endOfKey = s;
00448             goto haveeq;
00449          }
00450          if (*s == '[') //find the locale or options.
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               // Locale
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               // Option
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++; // Search till end of line / end of file
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               // backward compatibility. C == en_US
00520               if ( cl != 1 || ll != 5 || memcmp(locale, "C", 1) || memcmp(localeString.data(), "en_US", 5)) {
00521                   //cout<<"mismatched locale '"<<QCString(locale, elocale-locale +1)<<"'"<<endl;
00522                   // We can ignore this one
00523                   if (!pWriteBackMap)
00524                       continue; // We just ignore it
00525                   // We just store it as is to be able to write it back later.
00526                   endOfKey = elocale;
00527                   locale = 0;
00528               }
00529           }
00530       }
00531 
00532       // insert the key/value line
00533       QCString key(startLine, endOfKey - startLine + 2);
00534       QCString val = printableToString(st, s - st);
00535       //qDebug("found key '%s' with value '%s'", key.data(), val.data());
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          // don't insert into the config object but into the temporary
00551          // scratchpad map
00552          pWriteBackMap->insert(aEntryKey, aEntry);
00553       } else {
00554          // directly insert value into config object
00555          // no need to specify localization; if the key we just
00556          // retrieved was localized already, no need to localize it again.
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   // write-sync is only necessary if there are dirty entries
00578   if (!pConfig->isDirty())
00579     return;
00580 
00581   bool bEntriesLeft = true;
00582 
00583   // find out the file to write to (most specific writable file)
00584   // try local app-specific file first
00585 
00586   if (!mfileName.isEmpty()) {
00587     // Create the containing dir if needed
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     // Can we allow the write? We can, if the program
00597     // doesn't run SUID. But if it runs SUID, we must
00598     // check if the user would be allowed to write if
00599     // it wasn't SUID.
00600     if (checkAccess(mLocalFileName, W_OK)) {
00601       // is it writable?
00602       bEntriesLeft = writeConfigFile( mLocalFileName, false, bMerge );
00603     }
00604   }
00605 
00606   // only write out entries to the kdeglobals file if there are any
00607   // entries marked global (indicated by bEntriesLeft) and
00608   // the useKDEGlobals flag is set.
00609   if (bEntriesLeft && useKDEGlobals) {
00610 
00611 
00612     // can we allow the write? (see above)
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   // now write out all other groups.
00623   QCString currentGroup;
00624   for (KEntryMapConstIterator aIt = entryMap.begin();
00625        aIt != entryMap.end(); ++aIt)
00626   {
00627      const KEntryKey &key = aIt.key();
00628 
00629      // Either proces the default group or all others
00630      if ((key.mGroup != "<default>") == defaultGroup)
00631         continue; // Skip
00632 
00633      // Skip default values and group headers.
00634      if ((key.bDefault) || key.mKey.isEmpty())
00635         continue; // Skip
00636 
00637      const KEntry &currentEntry = *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         // Entry had a default value
00656         if ((currentEntry.mValue == (*aTestIt).mValue) &&
00657             (currentEntry.bDeleted == (*aTestIt).bDeleted))
00658            continue; // Same as default, don't write.
00659      }
00660      else
00661      {
00662         // Entry had no default value.
00663         if (currentEntry.bDeleted)
00664            continue; // Don't write deleted entries if there is no default.
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      // it is data for a group
00676      fputs(key.mKey.data(), pStream); // Key
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); // Deleted
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   } // for loop
00707 }
00708 
00709 bool KConfigINIBackEnd::writeConfigFile(QString filename, bool bGlobal,
00710                                         bool bMerge)
00711 {
00712   KEntryMap aTempMap;
00713   bool bEntriesLeft = false;
00714 
00715   // is the config object read-only?
00716   if (pConfig->isReadOnly())
00717     return true; // pretend we wrote it
00718 
00719   bFileImmutable = false;
00720   if (bMerge)
00721   {
00722     // Read entries from disk
00723     QFile rConfigFile( filename );
00724     if (rConfigFile.open(IO_ReadOnly))
00725     {
00726        // fill the temporary structure with entries from the file
00727        parseSingleConfigFile( rConfigFile, &aTempMap, bGlobal, false );
00728        rConfigFile.close();
00729        
00730        if (bFileImmutable) // File has become immutable on disk
00731           return true; // pretend we wrote it
00732     }
00733 
00734     KEntryMap aMap = pConfig->internalEntryMap();
00735 
00736     // augment this structure with the dirty entries from the config object
00737     for (KEntryMapIterator aIt = aMap.begin();
00738           aIt != aMap.end(); ++aIt)
00739     {
00740       const KEntry &currentEntry = *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       // only write back entries that have the same
00751       // "globality" as the file
00752       if (currentEntry.bGlobal != bGlobal)
00753       {
00754         // wrong "globality" - might have to be saved later
00755         bEntriesLeft = true;
00756         continue;
00757       }
00758 
00759       // put this entry from the config object into the
00760       // temporary map, possibly replacing an existing entry
00761       KEntryMapIterator aIt2 = aTempMap.find(aIt.key());
00762       if (aIt2 != aTempMap.end() && (*aIt2).bImmutable)
00763         continue; // Bail out if the on-disk entry is immutable
00764 
00765       aTempMap.insert(aIt.key(), currentEntry, true);
00766     } // loop
00767   }
00768   else
00769   {
00770     // don't merge, just get the regular entry map and use that.
00771     aTempMap = pConfig->internalEntryMap();
00772     bEntriesLeft = true; // maybe not true, but we aren't sure
00773   }
00774 
00775   // OK now the temporary map should be full of ALL entries.
00776   // write it out to disk.
00777 
00778   // Check if file exists:
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         // File is a symlink:
00788         if (stat(QFile::encodeName(filename), &buf) == 0)
00789         {
00790            // Don't create new file but write to existing file instead.
00791            createNew = false;
00792         }
00793      }
00794      else if (buf.st_uid == getuid())
00795      {
00796         // Preserve file mode if file exists and is owned by user.
00797         fileMode = buf.st_mode & 0777;
00798      }
00799      else
00800      {
00801         // File is not owned by user:
00802         // Don't create new file but write to existing file instead.
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      // Open existing file.
00833      // We use open() to ensure that we call without O_CREAT.
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   // Write default group
00848   writeEntries(pStream, aTempMap, true, firstEntry, localeString);
00849 
00850   // Write all other groups
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 { /*BASE::virtual_hook( id, data );*/ }
00869 
00870 void KConfigINIBackEnd::virtual_hook( int id, void* data )
00871 { KConfigBackEnd::virtual_hook( id, data ); }
00872 
KDE Logo
This file is part of the documentation for kdelibs Version 3.1.5.
Documentation copyright © 1996-2002 the KDE developers.
Generated on Wed Jan 28 12:46:23 2004 by doxygen 1.3.4 written by Dimitri van Heesch, © 1997-2001