kconfigbackend.cpp
/*
This file is part of the KDE libraries
Copyright (c) 1999 Preston Brown <pbrown@kde.org>
Copyright (c) 1997-1999 Matthias Kalle Dalheimer <kalle@kde.org>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include <config.h>
#include <unistd.h>
#include <ctype.h>
#ifdef HAVE_SYS_MMAN_H
#include <sys/mman.h>
#endif
#include <sys/types.h>
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
#endif
#include <fcntl.h>
#include <signal.h>
#include <setjmp.h>
#include <qdir.h>
#include <qfileinfo.h>
#include <qtextcodec.h>
#include <qtextstream.h>
#include "kconfigbackend.h"
#include "kconfigbase.h"
#include <kapplication.h>
#include <kglobal.h>
#include <kprocess.h>
#include <klocale.h>
#include <kstandarddirs.h>
#include <ksavefile.h>
#include <kurl.h>
#include <kde_file.h>
extern bool checkAccess(const QString& pathname, int mode);
/* translate escaped escape sequences to their actual values. */
static QCString printableToString(const char *str, int l)
{
// Strip leading white-space.
while((l>0) &&
((*str == ' ') || (*str == '\t') || (*str == '\r')))
{
str++; l--;
}
// Strip trailing white-space.
while((l>0) &&
((str[l-1] == ' ') || (str[l-1] == '\t') || (str[l-1] == '\r')))
{
l--;
}
QCString result(l + 1);
char *r = result.data();
for(int i = 0; i < l;i++, str++)
{
if (*str == '\\')
{
i++, str++;
if (i >= l) // End of line. (Line ends with single slash)
{
*r++ = '\\';
break;
}
switch(*str)
{
case 's':
*r++ = ' ';
break;
case 't':
*r++ = '\t';
break;
case 'n':
*r++ = '\n';
break;
case 'r':
*r++ = '\r';
break;
case '\\':
*r++ = '\\';
break;
default:
*r++ = '\\';
*r++ = *str;
}
}
else
{
*r++ = *str;
}
}
result.truncate(r-result.data());
return result;
}
static QCString stringToPrintable(const QCString& str){
QCString result(str.length()*2); // Maximum 2x as long as source string
register char *r = result.data();
register char *s = str.data();
if (!s) return QCString("");
// Escape leading space
if (*s == ' ')
{
*r++ = '\\'; *r++ = 's';
s++;
}
if (*s)
{
while(*s)
{
if (*s == '\n')
{
*r++ = '\\'; *r++ = 'n';
}
else if (*s == '\t')
{
*r++ = '\\'; *r++ = 't';
}
else if (*s == '\r')
{
*r++ = '\\'; *r++ = 'r';
}
else if (*s == '\\')
{
*r++ = '\\'; *r++ = '\\';
}
else
{
*r++ = *s;
}
s++;
}
// Escape trailing space
if (*(r-1) == ' ')
{
*(r-1) = '\\'; *r++ = 's';
}
}
result.truncate(r - result.data());
return result;
}
static QCString decodeGroup(const char*s, int l)
{
QCString result(l);
register char *r = result.data();
l--; // Correct for trailing \0
while(l)
{
if ((*s == '[') && (l > 1))
{
if ((*(s+1) == '['))
{
l--;
s++;
}
}
if ((*s == ']') && (l > 1))
{
if ((*(s+1) == ']'))
{
l--;
s++;
}
}
*r++ = *s++;
l--;
}
result.truncate(r - result.data());
return result;
}
static QCString encodeGroup(const QCString &str)
{
int l = str.length();
QCString result(l*2+1);
register char *r = result.data();
register char *s = str.data();
while(l)
{
if ((*s == '[') || (*s == ']'))
*r++ = *s;
*r++ = *s++;
l--;
}
result.truncate(r - result.data());
return result;
}
static QCString encodeKey(const char* key)
{
QCString newKey(key);
newKey.replace('[', "%5b");
newKey.replace(']', "%5d");
return newKey;
}
static QCString decodeKey(const char* key)
{
QCString newKey(key);
newKey.replace("%5b", "[");
newKey.replace("%5d", "]");
return newKey;
}
class KConfigBackEnd::KConfigBackEndPrivate
{
public:
QDateTime localLastModified;
uint localLastSize;
KLockFile::Ptr localLockFile;
KLockFile::Ptr globalLockFile;
};
void KConfigBackEnd::changeFileName(const QString &_fileName,
const char * _resType,
bool _useKDEGlobals)
{
mfileName = _fileName;
resType = _resType;
useKDEGlobals = _useKDEGlobals;
if (mfileName.isEmpty())
mLocalFileName = QString::null;
else if (!QDir::isRelativePath(mfileName))
mLocalFileName = mfileName;
else
mLocalFileName = KGlobal::dirs()->saveLocation(resType) + mfileName;
if (useKDEGlobals)
mGlobalFileName = KGlobal::dirs()->saveLocation("config") +
QString::fromLatin1("kdeglobals");
else
mGlobalFileName = QString::null;
d->localLastModified = QDateTime();
d->localLastSize = 0;
d->localLockFile = 0;
d->globalLockFile = 0;
}
KLockFile::Ptr KConfigBackEnd::lockFile(bool bGlobal)
{
if (bGlobal)
{
if (d->globalLockFile)
return d->globalLockFile;
if (!mGlobalFileName.isEmpty())
{
d->globalLockFile = new KLockFile(mGlobalFileName+".lock");
return d->globalLockFile;
}
}
else
{
if (d->localLockFile)
return d->localLockFile;
if (!mLocalFileName.isEmpty())
{
d->localLockFile = new KLockFile(mLocalFileName+".lock");
return d->localLockFile;
}
}
return 0;
}
KConfigBackEnd::KConfigBackEnd(KConfigBase *_config,
const QString &_fileName,
const char * _resType,
bool _useKDEGlobals)
: pConfig(_config), bFileImmutable(false), mConfigState(KConfigBase::NoAccess), mFileMode(-1)
{
d = new KConfigBackEndPrivate;
changeFileName(_fileName, _resType, _useKDEGlobals);
}
KConfigBackEnd::~KConfigBackEnd()
{
delete d;
}
void KConfigBackEnd::setFileWriteMode(int mode)
{
mFileMode = mode;
}
bool KConfigINIBackEnd::parseConfigFiles()
{
// Check if we can write to the local file.
mConfigState = KConfigBase::ReadOnly;
if (!mLocalFileName.isEmpty() && !pConfig->isReadOnly())
{
if (checkAccess(mLocalFileName, W_OK))
{
mConfigState = KConfigBase::ReadWrite;
}
else
{
// Create the containing dir, maybe it wasn't there
KURL path;
path.setPath(mLocalFileName);
QString dir=path.directory();
KStandardDirs::makeDir(dir);
if (checkAccess(mLocalFileName, W_OK))
{
mConfigState = KConfigBase::ReadWrite;
}
}
QFileInfo info(mLocalFileName);
d->localLastModified = info.lastModified();
d->localLastSize = info.size();
}
// Parse all desired files from the least to the most specific.
bFileImmutable = false;
// Parse the general config files
if (useKDEGlobals) {
QStringList kdercs = KGlobal::dirs()->
findAllResources("config", QString::fromLatin1("kdeglobals"));
#ifdef Q_WS_WIN
QString etc_kderc = QFile::decodeName( QCString(getenv("WINDIR")) + "\\kderc" );
#else
QString etc_kderc = QString::fromLatin1("/etc/kderc");
#endif
if (checkAccess(etc_kderc, R_OK))
kdercs += etc_kderc;
kdercs += KGlobal::dirs()->
findAllResources("config", QString::fromLatin1("system.kdeglobals"));
QStringList::ConstIterator it;
for (it = kdercs.fromLast(); it != kdercs.end(); --it) {
QFile aConfigFile( *it );
if (!aConfigFile.open( IO_ReadOnly ))
continue;
parseSingleConfigFile( aConfigFile, 0L, true, (*it != mGlobalFileName) );
aConfigFile.close();
if (bFileImmutable)
break;
}
}
bool bReadFile = !mfileName.isEmpty();
while(bReadFile) {
bReadFile = false;
QString bootLanguage;
if (useKDEGlobals && localeString.isEmpty() && !KGlobal::_locale) {
// Boot strap language
bootLanguage = KLocale::_initLanguage(pConfig);
setLocaleString(bootLanguage.utf8());
}
bFileImmutable = false;
QStringList list;
if ( !QDir::isRelativePath(mfileName) )
list << mfileName;
else
list = KGlobal::dirs()->findAllResources(resType, mfileName);
QStringList::ConstIterator it;
for (it = list.fromLast(); it != list.end(); --it) {
QFile aConfigFile( *it );
// we can already be sure that this file exists
bool bIsLocal = (*it == mLocalFileName);
if (aConfigFile.open( IO_ReadOnly )) {
parseSingleConfigFile( aConfigFile, 0L, false, !bIsLocal );
aConfigFile.close();
if (bFileImmutable)
break;
}
}
if (KGlobal::dirs()->isRestrictedResource(resType, mfileName))
bFileImmutable = true;
QString currentLanguage;
if (!bootLanguage.isEmpty())
{
currentLanguage = KLocale::_initLanguage(pConfig);
// If the file changed the language, we need to read the file again
// with the new language setting.
if (bootLanguage != currentLanguage)
{
bReadFile = true;
setLocaleString(currentLanguage.utf8());
}
}
}
if (bFileImmutable)
mConfigState = KConfigBase::ReadOnly;
return true;
}
#ifdef HAVE_MMAP
#ifdef SIGBUS
static sigjmp_buf mmap_jmpbuf;
struct sigaction mmap_old_sigact;
extern "C" {
static void mmap_sigbus_handler(int)
{
siglongjmp (mmap_jmpbuf, 1);
}
}
#endif
#endif
extern bool kde_kiosk_exception;
void KConfigINIBackEnd::parseSingleConfigFile(QFile &rFile,
KEntryMap *pWriteBackMap,
bool bGlobal, bool bDefault)
{
const char *s; // May get clobbered by sigsetjump, but we don't use them afterwards.
const char *eof; // May get clobbered by sigsetjump, but we don't use them afterwards.
QByteArray data;
if (!rFile.isOpen()) // come back, if you have real work for us ;->
return;
//using kdDebug() here leads to an infinite loop
//remove this for the release, aleXXX
//qWarning("Parsing %s, global = %s default = %s",
// rFile.name().latin1(), bGlobal ? "true" : "false", bDefault ? "true" : "false");
QCString aCurrentGroup("<default>");
unsigned int ll = localeString.length();
#ifdef HAVE_MMAP
static volatile const char *map;
map = ( const char* ) mmap(0, rFile.size(), PROT_READ, MAP_PRIVATE,
rFile.handle(), 0);
if ( map != MAP_FAILED )
{
s = (const char*) map;
eof = s + rFile.size();
#ifdef SIGBUS
struct sigaction act;
act.sa_handler = mmap_sigbus_handler;
sigemptyset( &act.sa_mask );
#ifdef SA_ONESHOT
act.sa_flags = SA_ONESHOT;
#else
act.sa_flags = SA_RESETHAND;
#endif
sigaction( SIGBUS, &act, &mmap_old_sigact );
if (sigsetjmp (mmap_jmpbuf, 1))
{
qWarning("SIGBUS while reading %s", rFile.name().latin1());
munmap(( char* )map, rFile.size());
sigaction (SIGBUS, &mmap_old_sigact, 0);
return;
}
#endif
}
else
#endif
{
rFile.at(0);
data = rFile.readAll();
s = data.data();
eof = s + data.size();
}
bool fileOptionImmutable = false;
bool groupOptionImmutable = false;
bool groupSkip = false;
int line = 0;
for(; s < eof; s++)
{
line++;
while((s < eof) && isspace(*s) && (*s != '\n'))
s++; //skip leading whitespace, shouldn't happen too often
//skip empty lines, lines starting with #
if ((s < eof) && ((*s == '\n') || (*s == '#')))
{
sktoeol: //skip till end-of-line
while ((s < eof) && (*s != '\n'))
s++;
continue; // Empty or comment or no keyword
}
const char *startLine = s;
if (*s == '[') //group
{
// In a group [[ and ]] have a special meaning
while ((s < eof) && (*s != '\n'))
{
if (*s == ']')
{
if ((s+1 < eof) && (*(s+1) == ']'))
s++; // Skip "]]"
else
break;
}
s++; // Search till end of group
}
const char *e = s;
while ((s < eof) && (*s != '\n')) s++; // Search till end of line / end of file
if ((e >= eof) || (*e != ']'))
{
fprintf(stderr, "Invalid group header at %s:%d\n", rFile.name().latin1(), line);
continue;
}
// group found; get the group name by taking everything in
// between the brackets
if ((e-startLine == 3) &&
(startLine[1] == '$') &&
(startLine[2] == 'i'))
{
if (!kde_kiosk_exception)
fileOptionImmutable = true;
continue;
}
aCurrentGroup = decodeGroup(startLine + 1, e - startLine);
//cout<<"found group ["<<aCurrentGroup<<"]"<<endl;
// Backwards compatibility
if (aCurrentGroup == "KDE Desktop Entry")
aCurrentGroup = "Desktop Entry";
groupOptionImmutable = fileOptionImmutable;
e++;
if ((e+2 < eof) && (*e++ == '[') && (*e++ == '$')) // Option follows
{
if ((*e == 'i') && !kde_kiosk_exception)
{
groupOptionImmutable = true;
}
}
KEntryKey groupKey(aCurrentGroup, 0);
KEntry entry = pConfig->lookupData(groupKey);
groupSkip = entry.bImmutable;
if (groupSkip && !bDefault)
continue;
entry.bImmutable |= groupOptionImmutable;
pConfig->putData(groupKey, entry, false);
if (pWriteBackMap)
{
// add the special group key indicator
(*pWriteBackMap)[groupKey] = entry;
}
continue;
}
if (groupSkip && !bDefault)
goto sktoeol; // Skip entry
bool optionImmutable = groupOptionImmutable;
bool optionDeleted = false;
bool optionExpand = false;
const char *endOfKey = 0, *locale = 0, *elocale = 0;
for (; (s < eof) && (*s != '\n'); s++)
{
if (*s == '=') //find the equal sign
{
if (!endOfKey)
endOfKey = s;
goto haveeq;
}
if (*s == '[') //find the locale or options.
{
const char *option;
const char *eoption;
endOfKey = s;
option = ++s;
for (;; s++)
{
if ((s >= eof) || (*s == '\n') || (*s == '=')) {
fprintf(stderr, "Invalid entry (missing ']') at %s:%d\n", rFile.name().latin1(), line);
goto sktoeol;
}
if (*s == ']')
break;
}
eoption = s;
if (*option != '$')
{
// Locale
if (locale) {
fprintf(stderr, "Invalid entry (second locale!?) at %s:%d\n", rFile.name().latin1(), line);
goto sktoeol;
}
locale = option;
elocale = eoption;
}
else
{
// Option
while (option < eoption)
{
option++;
if ((*option == 'i') && !kde_kiosk_exception)
optionImmutable = true;
else if (*option == 'e')
optionExpand = true;
else if (*option == 'd')
{
optionDeleted = true;
goto haveeq;
}
else if (*option == ']')
break;
}
}
}
}
fprintf(stderr, "Invalid entry (missing '=') at %s:%d\n", rFile.name().latin1(), line);
continue;
haveeq:
for (endOfKey--; ; endOfKey--)
{
if (endOfKey < startLine)
{
fprintf(stderr, "Invalid entry (empty key) at %s:%d\n", rFile.name().latin1(), line);
goto sktoeol;
}
if (!isspace(*endOfKey))
break;
}
const char *st = ++s;
while ((s < eof) && (*s != '\n')) s++; // Search till end of line / end of file
if (locale) {
unsigned int cl = static_cast<unsigned int>(elocale - locale);
if ((ll != cl) || memcmp(locale, localeString.data(), ll))
{
// backward compatibility. C == en_US
if ( cl != 1 || ll != 5 || *locale != 'C' || memcmp(localeString.data(), "en_US", 5)) {
//cout<<"mismatched locale '"<<QCString(locale, elocale-locale +1)<<"'"<<endl;
// We can ignore this one
if (!pWriteBackMap)
continue; // We just ignore it
// We just store it as is to be able to write it back later.
endOfKey = elocale;
locale = 0;
}
}
}
// insert the key/value line
QCString key(startLine, endOfKey - startLine + 2);
QCString val = printableToString(st, s - st);
//qDebug("found key '%s' with value '%s'", key.data(), val.data());
KEntryKey aEntryKey(aCurrentGroup, decodeKey(key));
aEntryKey.bLocal = (locale != 0);
aEntryKey.bDefault = bDefault;
KEntry aEntry;
aEntry.mValue = val;
aEntry.bGlobal = bGlobal;
aEntry.bImmutable = optionImmutable;
aEntry.bDeleted = optionDeleted;
aEntry.bExpand = optionExpand;
aEntry.bNLS = (locale != 0);
if (pWriteBackMap) {
// don't insert into the config object but into the temporary
// scratchpad map
pWriteBackMap->insert(aEntryKey, aEntry);
} else {
// directly insert value into config object
// no need to specify localization; if the key we just
// retrieved was localized already, no need to localize it again.
pConfig->putData(aEntryKey, aEntry, false);
}
}
if (fileOptionImmutable)
bFileImmutable = true;
#ifdef HAVE_MMAP
if (map)
{
munmap(( char* )map, rFile.size());
#ifdef SIGBUS
sigaction (SIGBUS, &mmap_old_sigact, 0);
#endif
}
#endif
}
void KConfigINIBackEnd::sync(bool bMerge)
{
// write-sync is only necessary if there are dirty entries
if (!pConfig->isDirty())
return;
bool bEntriesLeft = true;
// find out the file to write to (most specific writable file)
// try local app-specific file first
if (!mfileName.isEmpty()) {
// Create the containing dir if needed
if ((resType!="config") && !QDir::isRelativePath(mLocalFileName))
{
KURL path;
path.setPath(mLocalFileName);
QString dir=path.directory();
KStandardDirs::makeDir(dir);
}
// Can we allow the write? We can, if the program
// doesn't run SUID. But if it runs SUID, we must
// check if the user would be allowed to write if
// it wasn't SUID.
if (checkAccess(mLocalFileName, W_OK)) {
// File is writable
KLockFile::Ptr lf;
bool mergeLocalFile = bMerge;
// Check if the file has been updated since.
if (mergeLocalFile)
{
lf = lockFile(false); // Lock file for local file
if (lf && lf->isLocked())
lf = 0; // Already locked, we don't need to lock/unlock again
if (lf)
{
lf->lock( KLockFile::LockForce );
// But what if the locking failed? Ignore it for now...
}
QFileInfo info(mLocalFileName);
if ((d->localLastSize == info.size()) &&
(d->localLastModified == info.lastModified()))
{
// Not changed, don't merge.
mergeLocalFile = false;
}
else
{
// Changed...
d->localLastModified = QDateTime();
d->localLastSize = 0;
}
}
bEntriesLeft = writeConfigFile( mLocalFileName, false, mergeLocalFile );
// Only if we didn't have to merge anything can we use our in-memory state
// the next time around. Otherwise the config-file may contain entries
// that are different from our in-memory state which means we will have to
// do a merge from then on.
// We do not automatically update the in-memory state with the on-disk
// state when writing the config to disk. We only do so when
// KCOnfig::reparseConfiguration() is called.
// For KDE 4.0 we may wish to reconsider that.
if (!mergeLocalFile)
{
QFileInfo info(mLocalFileName);
d->localLastModified = info.lastModified();
d->localLastSize = info.size();
}
if (lf) lf->unlock();
}
}
// only write out entries to the kdeglobals file if there are any
// entries marked global (indicated by bEntriesLeft) and
// the useKDEGlobals flag is set.
if (bEntriesLeft && useKDEGlobals) {
// can we allow the write? (see above)
if (checkAccess ( mGlobalFileName, W_OK )) {
KLockFile::Ptr lf = lockFile(true); // Lock file for global file
if (lf && lf->isLocked())
lf = 0; // Already locked, we don't need to lock/unlock again
if (lf)
{
lf->lock( KLockFile::LockForce );
// But what if the locking failed? Ignore it for now...
}
writeConfigFile( mGlobalFileName, true, bMerge ); // Always merge
if (lf) lf->unlock();
}
}
}
static void writeEntries(FILE *pStream, const KEntryMap& entryMap, bool defaultGroup, bool &firstEntry, const QCString &localeString)
{
// now write out all other groups.
QCString currentGroup;
for (KEntryMapConstIterator aIt = entryMap.begin();
aIt != entryMap.end(); ++aIt)
{
const KEntryKey &key = aIt.key();
// Either proces the default group or all others
if ((key.mGroup != "<default>") == defaultGroup)
continue; // Skip
// Skip default values and group headers.
if ((key.bDefault) || key.mKey.isEmpty())
continue; // Skip
const KEntry &currentEntry = *aIt;
KEntryMapConstIterator aTestIt = aIt;
++aTestIt;
bool hasDefault = (aTestIt != entryMap.end());
if (hasDefault)
{
const KEntryKey &defaultKey = aTestIt.key();
if ((!defaultKey.bDefault) ||
(defaultKey.mKey != key.mKey) ||
(defaultKey.mGroup != key.mGroup) ||
(defaultKey.bLocal != key.bLocal))
hasDefault = false;
}
if (hasDefault)
{
// Entry had a default value
if ((currentEntry.mValue == (*aTestIt).mValue) &&
(currentEntry.bDeleted == (*aTestIt).bDeleted))
continue; // Same as default, don't write.
}
else
{
// Entry had no default value.
if (currentEntry.bDeleted)
continue; // Don't write deleted entries if there is no default.
}
if (!defaultGroup && (currentGroup != key.mGroup)) {
if (!firstEntry)
fprintf(pStream, "\n");
currentGroup = key.mGroup;
fprintf(pStream, "[%s]\n", encodeGroup(currentGroup).data());
}
firstEntry = false;
// it is data for a group
fputs(encodeKey(key.mKey.data()), pStream); // Key
if ( currentEntry.bNLS )
{
fputc('[', pStream);
fputs(localeString.data(), pStream);
fputc(']', pStream);
}
if (currentEntry.bDeleted)
{
fputs("[$d]\n", pStream); // Deleted
}
else
{
if (currentEntry.bImmutable || currentEntry.bExpand)
{
fputc('[', pStream);
fputc('$', pStream);
if (currentEntry.bImmutable)
fputc('i', pStream);
if (currentEntry.bExpand)
fputc('e', pStream);
fputc(']', pStream);
}
fputc('=', pStream);
fputs(stringToPrintable(currentEntry.mValue).data(), pStream);
fputc('\n', pStream);
}
} // for loop
}
bool KConfigINIBackEnd::getEntryMap(KEntryMap &aTempMap, bool bGlobal,
QFile *mergeFile)
{
bool bEntriesLeft = false;
bFileImmutable = false;
// Read entries from disk
if (mergeFile && mergeFile->open(IO_ReadOnly))
{
// fill the temporary structure with entries from the file
parseSingleConfigFile(*mergeFile, &aTempMap, bGlobal, false );
if (bFileImmutable) // File has become immutable on disk
return bEntriesLeft;
}
KEntryMap aMap = pConfig->internalEntryMap();
// augment this structure with the dirty entries from the config object
for (KEntryMapIterator aIt = aMap.begin();
aIt != aMap.end(); ++aIt)
{
const KEntry &currentEntry = *aIt;
if(aIt.key().bDefault)
{
aTempMap.replace(aIt.key(), currentEntry);
continue;
}
if (mergeFile && !currentEntry.bDirty)
continue;
// only write back entries that have the same
// "globality" as the file
if (currentEntry.bGlobal != bGlobal)
{
// wrong "globality" - might have to be saved later
bEntriesLeft = true;
continue;
}
// put this entry from the config object into the
// temporary map, possibly replacing an existing entry
KEntryMapIterator aIt2 = aTempMap.find(aIt.key());
if (aIt2 != aTempMap.end() && (*aIt2).bImmutable)
continue; // Bail out if the on-disk entry is immutable
aTempMap.insert(aIt.key(), currentEntry, true);
} // loop
return bEntriesLeft;
}
/* antlarr: KDE 4.0: make the first parameter "const QString &" */
bool KConfigINIBackEnd::writeConfigFile(QString filename, bool bGlobal,
bool bMerge)
{
// is the config object read-only?
if (pConfig->isReadOnly())
return true; // pretend we wrote it
KEntryMap aTempMap;
QFile *mergeFile = (bMerge ? new QFile(filename) : 0);
bool bEntriesLeft = getEntryMap(aTempMap, bGlobal, mergeFile);
delete mergeFile;
if (bFileImmutable)
return true; // pretend we wrote it
// OK now the temporary map should be full of ALL entries.
// write it out to disk.
// Check if file exists:
int fileMode = -1;
bool createNew = true;
KDE_struct_stat buf;
if (KDE_stat(QFile::encodeName(filename), &buf) == 0)
{
if (buf.st_uid == getuid())
{
// Preserve file mode if file exists and is owned by user.
fileMode = buf.st_mode & 0777;
}
else
{
// File is not owned by user:
// Don't create new file but write to existing file instead.
createNew = false;
}
}
KSaveFile *pConfigFile = 0;
FILE *pStream = 0;
if (createNew)
{
pConfigFile = new KSaveFile( filename, 0600 );
if (pConfigFile->status() != 0)
{
delete pConfigFile;
return bEntriesLeft;
}
if (!bGlobal && (fileMode == -1))
fileMode = mFileMode;
if (fileMode != -1)
{
fchmod(pConfigFile->handle(), fileMode);
}
pStream = pConfigFile->fstream();
}
else
{
// Open existing file.
// We use open() to ensure that we call without O_CREAT.
int fd = KDE_open( QFile::encodeName(filename), O_WRONLY | O_TRUNC );
if (fd < 0)
{
return bEntriesLeft;
}
pStream = KDE_fdopen( fd, "w");
if (!pStream)
{
close(fd);
return bEntriesLeft;
}
}
writeEntries(pStream, aTempMap);
if (pConfigFile)
{
bool bEmptyFile = (ftell(pStream) == 0);
if ( bEmptyFile && ((fileMode == -1) || (fileMode == 0600)) )
{
// File is empty and doesn't have special permissions: delete it.
::unlink(QFile::encodeName(filename));
pConfigFile->abort();
}
else
{
// Normal case: Close the file
pConfigFile->close();
}
delete pConfigFile;
}
else
{
fclose(pStream);
}
return bEntriesLeft;
}
void KConfigINIBackEnd::writeEntries(FILE *pStream, const KEntryMap &aTempMap)
{
bool firstEntry = true;
// Write default group
::writeEntries(pStream, aTempMap, true, firstEntry, localeString);
// Write all other groups
::writeEntries(pStream, aTempMap, false, firstEntry, localeString);
}
void KConfigBackEnd::virtual_hook( int, void* )
{ /*BASE::virtual_hook( id, data );*/ }
void KConfigINIBackEnd::virtual_hook( int id, void* data )
{ KConfigBackEnd::virtual_hook( id, data ); }
bool KConfigBackEnd::checkConfigFilesWritable(bool warnUser)
{
// WARNING: Do NOT use the event loop as it may not exist at this time.
bool allWritable = true;
QString errorMsg;
if ( !mLocalFileName.isEmpty() && !bFileImmutable && !checkAccess(mLocalFileName,W_OK) )
{
errorMsg = i18n("Will not save configuration.\n");
allWritable = false;
errorMsg += i18n("Configuration file \"%1\" not writable.\n").arg(mLocalFileName);
}
// We do not have an immutability flag for kdeglobals. However, making kdeglobals mutable while making
// the local config file immutable is senseless.
if ( !mGlobalFileName.isEmpty() && useKDEGlobals && !bFileImmutable && !checkAccess(mGlobalFileName,W_OK) )
{
if ( errorMsg.isEmpty() )
errorMsg = i18n("Will not save configuration.\n");
errorMsg += i18n("Configuration file \"%1\" not writable.\n").arg(mGlobalFileName);
allWritable = false;
}
if (warnUser && !allWritable)
{
// Note: We don't ask the user if we should not ask this question again because we can't save the answer.
errorMsg += i18n("Please contact your system administrator.");
QString cmdToExec = KStandardDirs::findExe(QString("kdialog"));
KApplication *app = kapp;
if (!cmdToExec.isEmpty() && app)
{
KProcess lprocess;
lprocess << cmdToExec << "--title" << app->instanceName() << "--msgbox" << errorMsg.local8Bit();
lprocess.start( KProcess::Block );
}
}
return allWritable;
}
KConfigBase::putData
virtual void putData(const KEntryKey &_key, const KEntry &_data, bool _checkGroup=true)=0
Inserts a (key/value) pair into the internal storage mechanism of the configuration object...
KURL
Represents and parses a URL.
Definition: kurl.h:127
KSharedPtr< KLockFile >
KApplication
Controls and provides information to all KDE applications.
Definition: kapplication.h:94
KSaveFile::status
int status() const
Returns the status of the file based on errno.
Definition: ksavefile.h:68
KProcess
Child process invocation, monitoring and control.
Definition: kprocess.h:125
KInstance::instanceName
QCString instanceName() const
Returns the name of the instance.
Definition: kinstance.cpp:263
KSaveFile::handle
int handle() const
An integer file descriptor open for writing to the file.
Definition: ksavefile.h:83
KEntry::bDeleted
bool bDeleted
Entry has been deleted.
Definition: kconfigdata.h:57
KConfigBackEnd::changeFileName
void changeFileName(const QString &_fileName, const char *_resType, bool _useKDEGlobals)
Changes the filenames associated with this back end.
Definition: kconfigbackend.cpp:243
KEntry
map/dict/list config node entry.
Definition: kconfigdata.h:32
KConfigBase::isReadOnly
bool isReadOnly() const
Returns the read-only status of the config object.
Definition: kconfigbase.h:1738
KEntryKey::bDefault
bool bDefault
Entry indicates if this is a default value.
Definition: kconfigdata.h:90
KSaveFile
The KSaveFile class has been made to write out changes to an existing file atomically.
Definition: ksavefile.h:41
KProcess::start
virtual bool start(RunMode runmode=NotifyOnExit, Communication comm=NoCommunication)
Starts the process.
Definition: kprocess.cpp:298
KSaveFile::abort
void abort()
Aborts the write operation and removes any intermediate files This implies a close.
Definition: ksavefile.cpp:100
klocale.h
KStandardDirs::findExe
static QString findExe(const QString &appname, const QString &pathstr=QString::null, bool ignoreExecBit=false)
Finds the executable in the system path.
Definition: kstandarddirs.cpp:916
KConfigBackEnd::checkConfigFilesWritable
bool checkConfigFilesWritable(bool warnUser)
Check whether the config files are writable.
Definition: kconfigbackend.cpp:1100
KConfigBackEnd::~KConfigBackEnd
virtual ~KConfigBackEnd()
Destructs the configuration backend.
Definition: kconfigbackend.cpp:306
KSaveFile::fstream
FILE * fstream()
A FILE* stream open for writing to the file.
Definition: ksavefile.h:91
KStandardDirs::makeDir
static bool makeDir(const QString &dir, int mode=0755)
Recursively creates still-missing directories in the given path.
Definition: kstandarddirs.cpp:1156
KSaveFile::close
bool close()
Closes the file and makes the changes definitive.
Definition: ksavefile.cpp:107
KLockFile
The KLockFile class provides NFS safe lockfiles.
Definition: klockfile.h:33
KLockFile::LockForce
Automatically remove a lock when a lock is detected that is stale for more than staleTime() seconds...
Definition: klockfile.h:80
KEntry::bImmutable
bool bImmutable
Entry can not be modified.
Definition: kconfigdata.h:53
KConfigBase::isDirty
bool isDirty() const
Checks whether the config file has any dirty (modified) entries.
Definition: kconfigbase.h:1722
KGlobal::dirs
static KStandardDirs * dirs()
Returns the application standard dirs object.
Definition: kglobal.cpp:54
KEntryKey
key structure holding both the actual key and the the group to which it belongs.
Definition: kconfigdata.h:69
KConfigBackEnd::setLocaleString
void setLocaleString(const QCString &_localeString)
Set the locale string that defines the current language.
Definition: kconfigbackend.h:132
KLockFile::lock
LockResult lock(int options=0)
Attempt to acquire the lock.
Definition: klockfile.cpp:240
KEntryKey::mGroup
QCString mGroup
The "group" to which this EntryKey belongs.
Definition: kconfigdata.h:78
KStandardDirs::saveLocation
QString saveLocation(const char *type, const QString &suffix=QString::null, bool create=true) const
Finds a location to save files into for the given type in the user's home directory.
Definition: kstandarddirs.cpp:1077
KEntryKey::bLocal
bool bLocal
Entry is localised or not.
Definition: kconfigdata.h:86
KConfigBase::lookupData
virtual KEntry lookupData(const KEntryKey &_key) const =0
Looks up an entry in the config object's internal structure.
KConfigBase
KDE Configuration Management abstract base class.
Definition: kconfigbase.h:70
KEntry::KEntryMapConstIterator
QMap< KEntryKey, KEntry >::ConstIterator KEntryMapConstIterator
Definition: kconfigdata.h:144
KConfigINIBackEnd::writeEntries
void writeEntries(FILE *pStream, const KEntryMap &aTempMap)
Write the entries in aTempMap to the file stream.
Definition: kconfigbackend.cpp:1083
KURL::directory
QString directory(bool _strip_trailing_slash_from_result=true, bool _ignore_trailing_slash_in_path=true) const
Returns the directory of the path.
Definition: kurl.cpp:1766
KConfigBackEnd::KConfigBackEnd
KConfigBackEnd(KConfigBase *_config, const QString &_fileName, const char *_resType, bool _useKDEGlobals)
Constructs a configuration back end.
Definition: kconfigbackend.cpp:296
KStandardDirs::findAllResources
QStringList findAllResources(const char *type, const QString &filter=QString::null, bool recursive=false, bool unique=false) const
Tries to find all resources with the specified type.
Definition: kstandarddirs.cpp:676
KURL::setPath
void setPath(const QString &path)
Sets the decoded path of the URL.
Definition: kurl.cpp:1990
KConfigBase::internalEntryMap
virtual KEntryMap internalEntryMap(const QString &pGroup) const =0
Returns a map (tree) of the entries in the specified group.
KConfigBackEnd::lockFile
KLockFile::Ptr lockFile(bool bGlobal=false)
Returns a lock file object for the configuration file.
Definition: kconfigbackend.cpp:269
KEntryKey::mKey
QCString mKey
The actual key of the entry in question.
Definition: kconfigdata.h:82
KEntry::bGlobal
bool bGlobal
Entry should be written to the global config file.
Definition: kconfigdata.h:49
KLockFile::isLocked
bool isLocked() const
Returns whether the lock is held or not.
Definition: klockfile.cpp:354
KProcess::Block
The application is suspended until the started process is finished.
Definition: kprocess.h:177
KConfigINIBackEnd::sync
virtual void sync(bool bMerge=true)
Writes configuration data to file(s).
Definition: kconfigbackend.cpp:736
KEntry::bNLS
bool bNLS
Entry should be written with locale tag.
Definition: kconfigdata.h:45
KEntry::KEntryMap
QMap< KEntryKey, KEntry > KEntryMap
Definition: kconfigdata.h:128
KConfigINIBackEnd::parseConfigFiles
bool parseConfigFiles()
Parses all INI-style configuration files for a config object.
Definition: kconfigbackend.cpp:316
KLockFile::unlock
void unlock()
Release the lock.
Definition: klockfile.cpp:359
KConfigINIBackEnd::getEntryMap
bool getEntryMap(KEntryMap &map, bool bGlobal, QFile *mergeFile)
Get the entry map.
Definition: kconfigbackend.cpp:926
KConfigINIBackEnd::writeConfigFile
bool writeConfigFile(QString filename, bool bGlobal=false, bool bMerge=true)
Writes configuration file back.
Definition: kconfigbackend.cpp:980
KGlobal::checkAccess
bool checkAccess(const QString &pathname, int mode)
Definition: kapplication.cpp:2854
KConfigINIBackEnd::parseSingleConfigFile
void parseSingleConfigFile(QFile &rFile, KEntryMap *pWriteBackMap=0L, bool bGlobal=false, bool bDefault=false)
Parses one configuration file.
Definition: kconfigbackend.cpp:446
KEntry::bExpand
bool bExpand
Whether to apply dollar expansion or not.
Definition: kconfigdata.h:61
KEntry::bDirty
bool bDirty
Must the entry be written back to disk?
Definition: kconfigdata.h:41
KConfigBackEnd::setFileWriteMode
void setFileWriteMode(int mode)
Set the file mode for newly created files.
Definition: kconfigbackend.cpp:311

KDE Home | KDE Accessibility Home | Description of Access Keys