kconfigbackend.cpp Source File (kdecore Library)

| # kdecore Library API Documentation
Overview | Class Hierarchy | Classes | Classes (annotated) | Members | Source Files

# 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

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