kdecore Library: kconfigbackend.cpp Source File (kdecore Library)

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

This file is part of the documentation for kdecore Library Version 3.4.1.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Sat Jun 11 20:18:03 2005 by doxygen 1.4.1 written by Dimitri van Heesch, © 1997-2003