• Skip to content
  • Skip to link menu
Trinity API Reference
  • Trinity API Reference
  • kdecore
 

kdecore

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

kdecore

Skip menu "kdecore"
  • Main Page
  • Modules
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

kdecore

Skip menu "kdecore"
  • arts
  • dcop
  • dnssd
  • interfaces
  •     interface
  •     library
  •   kspeech
  •   ktexteditor
  • kabc
  • kate
  • kcmshell
  • kdecore
  • kded
  • kdefx
  • kdeprint
  • kdesu
  • kdeui
  • kdoctools
  • khtml
  • kimgio
  • kinit
  • kio
  •   bookmarks
  •   httpfilter
  •   kfile
  •   kio
  •   kioexec
  •   kpasswdserver
  •   kssl
  • kioslave
  •   http
  • kjs
  • kmdi
  •   kmdi
  • knewstuff
  • kparts
  • krandr
  • kresources
  • kspell2
  • kunittest
  • kutils
  • kwallet
  • libkmid
  • libkscreensaver
Generated for kdecore by doxygen 1.7.6.1
This website is maintained by Timothy Pearson.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. |