aboutsummaryrefslogtreecommitdiff
path: root/tier1/kconfig/src/core/kconfigini.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'tier1/kconfig/src/core/kconfigini.cpp')
-rw-r--r--tier1/kconfig/src/core/kconfigini.cpp770
1 files changed, 0 insertions, 770 deletions
diff --git a/tier1/kconfig/src/core/kconfigini.cpp b/tier1/kconfig/src/core/kconfigini.cpp
deleted file mode 100644
index f44b2c39..00000000
--- a/tier1/kconfig/src/core/kconfigini.cpp
+++ /dev/null
@@ -1,770 +0,0 @@
-/*
- This file is part of the KDE libraries
- Copyright (c) 2006, 2007 Thomas Braxton <kde.braxton@gmail.com>
- 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 "kconfigini_p.h"
-
-#include "kconfig.h"
-#include "kconfigbackend.h"
-#include "bufferfragment_p.h"
-#include "kconfigdata.h"
-
-#include <qsavefile.h>
-#include <qlockfile.h>
-#include <qdatetime.h>
-#include <qdir.h>
-#include <qfile.h>
-#include <qfileinfo.h>
-#include <qdebug.h>
-#include <qplatformdefs.h>
-
-#ifndef Q_OS_WIN
-#include <unistd.h> // getuid, close
-#endif
-#include <sys/types.h> // uid_t
-#include <fcntl.h> // open
-
-KCONFIGCORE_EXPORT bool kde_kiosk_exception = false; // flag to disable kiosk restrictions
-
-QString KConfigIniBackend::warningProlog(const QFile &file, int line)
-{
- return QString::fromLatin1("KConfigIni: In file %2, line %1: ")
- .arg(line).arg(file.fileName());
-}
-
-KConfigIniBackend::KConfigIniBackend()
- : KConfigBackend(), lockFile(NULL)
-{
-}
-
-KConfigIniBackend::~KConfigIniBackend()
-{
-}
-
-KConfigBackend::ParseInfo
- KConfigIniBackend::parseConfig(const QByteArray& currentLocale, KEntryMap& entryMap,
- ParseOptions options)
-{
- return parseConfig(currentLocale, entryMap, options, false);
-}
-
-// merging==true is the merging that happens at the beginning of writeConfig:
-// merge changes in the on-disk file with the changes in the KConfig object.
-KConfigBackend::ParseInfo
-KConfigIniBackend::parseConfig(const QByteArray& currentLocale, KEntryMap& entryMap,
- ParseOptions options, bool merging)
-{
- if (filePath().isEmpty() || !QFile::exists(filePath()))
- return ParseOk;
-
- bool bDefault = options&ParseDefaults;
- bool allowExecutableValues = options&ParseExpansions;
-
- QByteArray currentGroup("<default>");
-
- QFile file(filePath());
- if (!file.open(QIODevice::ReadOnly|QIODevice::Text))
- return ParseOpenError;
-
- QList<QByteArray> immutableGroups;
-
- bool fileOptionImmutable = false;
- bool groupOptionImmutable = false;
- bool groupSkip = false;
-
- int lineNo = 0;
- // on systems using \r\n as end of line, \r will be taken care of by
- // trim() below
- QByteArray buffer = file.readAll();
- BufferFragment contents(buffer.data(), buffer.size());
- unsigned int len = contents.length();
- unsigned int startOfLine = 0;
-
- while (startOfLine < len) {
- BufferFragment line = contents.split('\n', &startOfLine);
- line.trim();
- lineNo++;
-
- // skip empty lines and lines beginning with '#'
- if (line.isEmpty() || line.at(0) == '#')
- continue;
-
- if (line.at(0) == '[') { // found a group
- groupOptionImmutable = fileOptionImmutable;
-
- QByteArray newGroup;
- int start = 1, end;
- do {
- end = start;
- for (;;) {
- if (end == line.length()) {
- qWarning() << warningProlog(file, lineNo) << "Invalid group header.";
- // XXX maybe reset the current group here?
- goto next_line;
- }
- if (line.at(end) == ']')
- break;
- end++;
- }
- if (end + 1 == line.length() && start + 2 == end &&
- line.at(start) == '$' && line.at(start + 1) == 'i')
- {
- if (newGroup.isEmpty())
- fileOptionImmutable = !kde_kiosk_exception;
- else
- groupOptionImmutable = !kde_kiosk_exception;
- }
- else {
- if (!newGroup.isEmpty())
- newGroup += '\x1d';
- BufferFragment namePart=line.mid(start, end - start);
- printableToString(&namePart, file, lineNo);
- newGroup += namePart.toByteArray();
- }
- } while ((start = end + 2) <= line.length() && line.at(end + 1) == '[');
- currentGroup = newGroup;
-
- groupSkip = entryMap.getEntryOption(currentGroup, 0, 0, KEntryMap::EntryImmutable);
-
- if (groupSkip && !bDefault)
- continue;
-
- if (groupOptionImmutable)
- // Do not make the groups immutable until the entries from
- // this file have been added.
- immutableGroups.append(currentGroup);
- } else {
- if (groupSkip && !bDefault)
- continue; // skip entry
-
- BufferFragment aKey;
- int eqpos = line.indexOf('=');
- if (eqpos < 0) {
- aKey = line;
- line.clear();
- } else {
- BufferFragment temp = line.left(eqpos);
- temp.trim();
- aKey = temp;
- line.truncateLeft(eqpos + 1);
- }
- if (aKey.isEmpty()) {
- qWarning() << warningProlog(file, lineNo) << "Invalid entry (empty key)";
- continue;
- }
-
- KEntryMap::EntryOptions entryOptions=0;
- if (groupOptionImmutable)
- entryOptions |= KEntryMap::EntryImmutable;
-
- BufferFragment locale;
- int start;
- while ((start = aKey.lastIndexOf('[')) >= 0) {
- int end = aKey.indexOf(']', start);
- if (end < 0) {
- qWarning() << warningProlog(file, lineNo)
- << "Invalid entry (missing ']')";
- goto next_line;
- } else if (end > start + 1 && aKey.at(start + 1) == '$') { // found option(s)
- int i = start + 2;
- while (i < end) {
- switch (aKey.at(i)) {
- case 'i':
- if (!kde_kiosk_exception)
- entryOptions |= KEntryMap::EntryImmutable;
- break;
- case 'e':
- if (allowExecutableValues)
- entryOptions |= KEntryMap::EntryExpansion;
- break;
- case 'd':
- entryOptions |= KEntryMap::EntryDeleted;
- aKey = aKey.left(start);
- printableToString(&aKey, file, lineNo);
- entryMap.setEntry(currentGroup, aKey.toByteArray(), QByteArray(), entryOptions);
- goto next_line;
- default:
- break;
- }
- i++;
- }
- } else { // found a locale
- if (!locale.isNull()) {
- qWarning() << warningProlog(file, lineNo)
- << "Invalid entry (second locale!?)";
- goto next_line;
- }
-
- locale = aKey.mid(start + 1,end - start - 1);
- }
- aKey.truncate(start);
- }
- if (eqpos < 0) { // Do this here after [$d] was checked
- qWarning() << warningProlog(file, lineNo) << "Invalid entry (missing '=')";
- continue;
- }
- printableToString(&aKey, file, lineNo);
- if (!locale.isEmpty()) {
- if (locale != currentLocale) {
- // backward compatibility. C == en_US
- if (locale.at(0) != 'C' || currentLocale != "en_US") {
- if (merging)
- entryOptions |= KEntryMap::EntryRawKey;
- else
- goto next_line; // skip this entry if we're not merging
- }
- }
- }
-
- if (!(entryOptions & KEntryMap::EntryRawKey))
- printableToString(&aKey, file, lineNo);
-
- if (options&ParseGlobal)
- entryOptions |= KEntryMap::EntryGlobal;
- if (bDefault)
- entryOptions |= KEntryMap::EntryDefault;
- if (!locale.isNull())
- entryOptions |= KEntryMap::EntryLocalized;
- printableToString(&line, file, lineNo);
- if (entryOptions & KEntryMap::EntryRawKey) {
- QByteArray rawKey;
- rawKey.reserve(aKey.length() + locale.length() + 2);
- rawKey.append(aKey.toVolatileByteArray());
- rawKey.append('[').append(locale.toVolatileByteArray()).append(']');
- entryMap.setEntry(currentGroup, rawKey, line.toByteArray(), entryOptions);
- } else {
- entryMap.setEntry(currentGroup, aKey.toByteArray(), line.toByteArray(), entryOptions);
- }
- }
-next_line:
- continue;
- }
-
- // now make sure immutable groups are marked immutable
- Q_FOREACH(const QByteArray& group, immutableGroups) {
- entryMap.setEntry(group, QByteArray(), QByteArray(), KEntryMap::EntryImmutable);
- }
-
- return fileOptionImmutable ? ParseImmutable : ParseOk;
-}
-
-void KConfigIniBackend::writeEntries(const QByteArray& locale, QIODevice& file,
- const KEntryMap& map, bool defaultGroup, bool &firstEntry)
-{
- QByteArray currentGroup;
- bool groupIsImmutable = false;
- const KEntryMapConstIterator end = map.constEnd();
- for (KEntryMapConstIterator it = map.constBegin(); it != end; ++it) {
- const KEntryKey& key = it.key();
-
- // Either process the default group or all others
- if ((key.mGroup != "<default>") == defaultGroup)
- continue; // skip
-
- // the only thing we care about groups is, is it immutable?
- if (key.mKey.isNull()) {
- groupIsImmutable = it->bImmutable;
- continue; // skip
- }
-
- const KEntry& currentEntry = *it;
- if (!defaultGroup && currentGroup != key.mGroup) {
- if (!firstEntry)
- file.putChar('\n');
- currentGroup = key.mGroup;
- for (int start = 0, end;; start = end + 1) {
- file.putChar('[');
- end = currentGroup.indexOf('\x1d', start);
- if (end < 0) {
- int cgl = currentGroup.length();
- if (currentGroup.at(start) == '$' && cgl - start <= 10) {
- for (int i = start + 1; i < cgl; i++) {
- char c = currentGroup.at(i);
- if (c < 'a' || c > 'z')
- goto nope;
- }
- file.write("\\x24");
- start++;
- }
- nope:
- file.write(stringToPrintable(currentGroup.mid(start), GroupString));
- file.putChar(']');
- if (groupIsImmutable) {
- file.write("[$i]", 4);
- }
- file.putChar('\n');
- break;
- } else {
- file.write(stringToPrintable(currentGroup.mid(start, end - start), GroupString));
- file.putChar(']');
- }
- }
- }
-
- firstEntry = false;
- // it is data for a group
-
- if (key.bRaw) // unprocessed key with attached locale from merge
- file.write(key.mKey);
- else {
- file.write(stringToPrintable(key.mKey, KeyString)); // Key
- if (key.bLocal && locale != "C") { // 'C' locale == untranslated
- file.putChar('[');
- file.write(locale); // locale tag
- file.putChar(']');
- }
- }
- if (currentEntry.bDeleted) {
- if (currentEntry.bImmutable)
- file.write("[$di]", 5); // Deleted + immutable
- else
- file.write("[$d]", 4); // Deleted
- } else {
- if (currentEntry.bImmutable || currentEntry.bExpand) {
- file.write("[$", 2);
- if (currentEntry.bImmutable)
- file.putChar('i');
- if (currentEntry.bExpand)
- file.putChar('e');
- file.putChar(']');
- }
- file.putChar('=');
- file.write(stringToPrintable(currentEntry.mValue, ValueString));
- }
- file.putChar('\n');
- }
-}
-
-void KConfigIniBackend::writeEntries(const QByteArray& locale, QIODevice& file, const KEntryMap& map)
-{
- bool firstEntry = true;
-
- // write default group
- writeEntries(locale, file, map, true, firstEntry);
-
- // write all other groups
- writeEntries(locale, file, map, false, firstEntry);
-}
-
-bool KConfigIniBackend::writeConfig(const QByteArray& locale, KEntryMap& entryMap,
- WriteOptions options)
-{
- Q_ASSERT(!filePath().isEmpty());
-
- KEntryMap writeMap;
- const bool bGlobal = options & WriteGlobal;
-
- // First, reparse the file on disk, to merge our changes with the ones done by other apps
- // Store the result into writeMap.
- {
- ParseOptions opts = ParseExpansions;
- if (bGlobal)
- opts |= ParseGlobal;
- ParseInfo info = parseConfig(locale, writeMap, opts, true);
- if (info != ParseOk) // either there was an error or the file became immutable
- return false;
- }
-
- const KEntryMapIterator end = entryMap.end();
- for (KEntryMapIterator it=entryMap.begin(); it != end; ++it) {
- if (!it.key().mKey.isEmpty() && !it->bDirty) // not dirty, doesn't overwrite entry in writeMap. skips default entries, too.
- continue;
-
- const KEntryKey& key = it.key();
-
- // only write entries that have the same "globality" as the file
- if (it->bGlobal == bGlobal) {
- if (it->bReverted) {
- writeMap.remove(key);
- } else if (!it->bDeleted) {
- writeMap[key] = *it;
- } else {
- KEntryKey defaultKey = key;
- defaultKey.bDefault = true;
- if (!entryMap.contains(defaultKey)) {
- writeMap.remove(key); // remove the deleted entry if there is no default
- //qDebug() << "Detected as deleted=>removed:" << key.mGroup << key.mKey << "global=" << bGlobal;
- } else {
- writeMap[key] = *it; // otherwise write an explicitly deleted entry
- //qDebug() << "Detected as deleted=>[$d]:" << key.mGroup << key.mKey << "global=" << bGlobal;
- }
- }
- it->bDirty = false;
- }
- }
-
- // now writeMap should contain only entries to be written
- // so write it out to disk
-
- // check if file exists
- QFile::Permissions fileMode = QFile::ReadUser | QFile::WriteUser;
- bool createNew = true;
-
- QFileInfo fi(filePath());
- if (fi.exists())
- {
-#ifdef Q_OS_WIN
- //TODO: getuid does not exist on windows, use GetSecurityInfo and GetTokenInformation instead
- createNew = false;
-#else
- if (fi.ownerId() == ::getuid())
- {
- // Preserve file mode if file exists and is owned by user.
- fileMode = fi.permissions();
- }
- else
- {
- // File is not owned by user:
- // Don't create new file but write to existing file instead.
- createNew = false;
- }
-#endif
- }
-
- if (createNew) {
- QSaveFile file(filePath());
- if (!file.open(QIODevice::WriteOnly)) {
- return false;
- }
-
- file.setTextModeEnabled(true); // to get eol translation
- writeEntries(locale, file, writeMap);
-
- if (!file.size() && (fileMode == (QFile::ReadUser | QFile::WriteUser))) {
- // File is empty and doesn't have special permissions: delete it.
- file.cancelWriting();
-
- if (fi.exists()) {
- // also remove the old file in case it existed. this can happen
- // when we delete all the entries in an existing config file.
- // if we don't do this, then deletions and revertToDefault's
- // will mysteriously fail
- QFile::remove(filePath());
- }
- } else {
- // Normal case: Close the file
- if (file.commit()) {
- QFile::setPermissions(filePath(), fileMode);
- return true;
- }
- // Couldn't write. Disk full?
- qWarning() << "Couldn't write" << filePath() << ". Disk full?";
- return false;
- }
- } else {
- // Open existing file. *DON'T* create it if it suddenly does not exist!
-#ifdef Q_OS_UNIX
- int fd = QT_OPEN(QFile::encodeName(filePath()).constData(), O_WRONLY | O_TRUNC);
- if (fd < 0) {
- return false;
- }
- FILE *fp = ::fdopen(fd, "w");
- if (!fp) {
- QT_CLOSE(fd);
- return false;
- }
- QFile f;
- if (!f.open(fp, QIODevice::WriteOnly)) {
- fclose(fp);
- return false;
- }
- writeEntries(locale, f, writeMap);
- f.close();
- fclose(fp);
-#else
- QFile f( filePath() );
- // XXX This is broken - it DOES create the file if it is suddenly gone.
- if (!f.open( QIODevice::WriteOnly | QIODevice::Truncate )) {
- return false;
- }
- f.setTextModeEnabled(true);
- writeEntries(locale, f, writeMap);
-#endif
- }
- return true;
-}
-
-
-bool KConfigIniBackend::isWritable() const
-{
- const QString filePath = this->filePath();
- if (!filePath.isEmpty()) {
- QFileInfo file(filePath);
- if (!file.exists()) {
- // If the file does not exist, check if the deepest
- // existing dir is writable.
- QFileInfo dir(file.absolutePath());
- while (!dir.exists()) {
- QString parent = dir.absolutePath(); // Go up. Can't use cdUp() on non-existing dirs.
- if (parent == dir.filePath()) {
- // no parent
- return false;
- }
- dir.setFile(parent);
- }
- return dir.isDir() && dir.isWritable();
- } else {
- return file.isWritable();
- }
- }
-
- return false;
-}
-
-QString KConfigIniBackend::nonWritableErrorMessage() const
-{
- return tr("Configuration file \"%1\" not writable.\n").arg(filePath());
-}
-
-void KConfigIniBackend::createEnclosing()
-{
- const QString file = filePath();
- if (file.isEmpty())
- return; // nothing to do
-
- // Create the containing dir, maybe it wasn't there
- QDir dir;
- dir.mkpath(QFileInfo(file).absolutePath());
-}
-
-void KConfigIniBackend::setFilePath(const QString& file)
-{
- if (file.isEmpty())
- return;
-
- Q_ASSERT(QDir::isAbsolutePath(file));
-
- const QFileInfo info(file);
- if (info.exists()) {
- setLocalFilePath(info.canonicalFilePath());
- setLastModified(info.lastModified());
- setSize(info.size());
- } else {
- setLocalFilePath(file);
- setSize(0);
- QDateTime dummy;
- dummy.setTime_t(0);
- setLastModified(dummy);
- }
-}
-
-KConfigBase::AccessMode KConfigIniBackend::accessMode() const
-{
- if (filePath().isEmpty())
- return KConfigBase::NoAccess;
-
- if (isWritable())
- return KConfigBase::ReadWrite;
-
- return KConfigBase::ReadOnly;
-}
-
-bool KConfigIniBackend::lock()
-{
- Q_ASSERT(!filePath().isEmpty());
-
- if (!lockFile) {
- lockFile = new QLockFile(filePath() + QLatin1String(".lock"));
- }
-
- // This is a workaround for current QLockFilePrivate::tryLock_sys
- // which might crash calling qAppName() if sync() is called after
- // the QCoreApplication instance is gone. It might be the case with
- // KSharedConfig instances cleanup.
- if (!lockFile->tryLock(lockFile->staleLockTime())) {
- lockFile->removeStaleLockFile();
- lockFile->lock();
- }
- return lockFile->isLocked();
-}
-
-void KConfigIniBackend::unlock()
-{
- lockFile->unlock();
- delete lockFile;
- lockFile = NULL;
-}
-
-bool KConfigIniBackend::isLocked() const
-{
- return lockFile && lockFile->isLocked();
-}
-
-QByteArray KConfigIniBackend::stringToPrintable(const QByteArray& aString, StringType type)
-{
- static const char nibbleLookup[] = {
- '0', '1', '2', '3', '4', '5', '6', '7',
- '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'
- };
-
- if (aString.isEmpty())
- return aString;
- const int l = aString.length();
-
- QByteArray result; // Guesstimated that it's good to avoid data() initialization for a length of l*4
- result.resize(l * 4); // Maximum 4x as long as source string due to \x<ab> escape sequences
- register const char *s = aString.constData();
- int i = 0;
- char *data = result.data();
- char *start = data;
-
- // Protect leading space
- if (s[0] == ' ' && type != GroupString) {
- *data++ = '\\';
- *data++ = 's';
- i++;
- }
-
- for (; i < l; ++i/*, r++*/) {
- switch (s[i]) {
- default:
- // The \n, \t, \r cases (all < 32) are handled below; we can ignore them here
- if (((unsigned char)s[i]) < 32)
- goto doEscape;
- *data++ = s[i];
- break;
- case '\n':
- *data++ = '\\';
- *data++ = 'n';
- break;
- case '\t':
- *data++ = '\\';
- *data++ = 't';
- break;
- case '\r':
- *data++ = '\\';
- *data++ = 'r';
- break;
- case '\\':
- *data++ = '\\';
- *data++ = '\\';
- break;
- case '=':
- if (type != KeyString) {
- *data++ = s[i];
- break;
- }
- goto doEscape;
- case '[':
- case ']':
- // Above chars are OK to put in *value* strings as plaintext
- if (type == ValueString) {
- *data++ = s[i];
- break;
- }
- doEscape:
- *data++ = '\\';
- *data++ = 'x';
- *data++ = nibbleLookup[((unsigned char)s[i]) >> 4];
- *data++ = nibbleLookup[((unsigned char)s[i]) & 0x0f];
- break;
- }
- }
- *data = 0;
- result.resize(data - start);
-
- // Protect trailing space
- if (result.endsWith(' ') && type != GroupString) {
- result.replace(result.length() - 1, 1, "\\s");
- }
- result.squeeze();
-
- return result;
-}
-
-char KConfigIniBackend::charFromHex(const char *str, const QFile& file, int line)
-{
- unsigned char ret = 0;
- for (int i = 0; i < 2; i++) {
- ret <<= 4;
- quint8 c = quint8(str[i]);
-
- if (c >= '0' && c <= '9') {
- ret |= c - '0';
- } else if (c >= 'a' && c <= 'f') {
- ret |= c - 'a' + 0x0a;
- } else if (c >= 'A' && c <= 'F') {
- ret |= c - 'A' + 0x0a;
- } else {
- QByteArray e(str, 2);
- e.prepend("\\x");
- qWarning() << warningProlog(file, line) << "Invalid hex character " << c
- << " in \\x<nn>-type escape sequence \"" << e.constData() << "\".";
- return 'x';
- }
- }
- return char(ret);
-}
-
-void KConfigIniBackend::printableToString(BufferFragment* aString, const QFile& file, int line)
-{
- if (aString->isEmpty() || aString->indexOf('\\')==-1)
- return;
- aString->trim();
- int l = aString->length();
- char *r = aString->data();
- char *str=r;
-
- for(int i = 0; i < l; i++, r++) {
- if (str[i]!= '\\') {
- *r=str[i];
- } else {
- // Probable escape sequence
- i++;
- if (i >= l) { // Line ends after backslash - stop.
- *r = '\\';
- break;
- }
-
- switch(str[i]) {
- case 's':
- *r = ' ';
- break;
- case 't':
- *r = '\t';
- break;
- case 'n':
- *r = '\n';
- break;
- case 'r':
- *r = '\r';
- break;
- case '\\':
- *r = '\\';
- break;
- case 'x':
- if (i + 2 < l) {
- *r = charFromHex(str + i + 1, file, line);
- i += 2;
- } else {
- *r = 'x';
- i = l - 1;
- }
- break;
- default:
- *r = '\\';
- qWarning() << warningProlog(file, line)
- << QString::fromLatin1("Invalid escape sequence \"\\%1\".").arg(str[i]);
- }
- }
- }
- aString->truncate(r - aString->constData());
-}