[Telepathy-commits] [telepathy-qt4/master] Added KeyFile class.

Andre Moreira Magalhaes (andrunko) andre.magalhaes at collabora.co.uk
Mon Dec 22 08:47:20 PST 2008


Added KeyFile class, used to read key-pair files such as INI and/or .desktop
files.
---
 TelepathyQt4/KeyFile      |    6 +
 TelepathyQt4/Makefile.am  |    3 +
 TelepathyQt4/key-file.cpp |  453 +++++++++++++++++++++++++++++++++++++++++++++
 TelepathyQt4/key-file.h   |   71 +++++++
 4 files changed, 533 insertions(+), 0 deletions(-)
 create mode 100644 TelepathyQt4/KeyFile
 create mode 100644 TelepathyQt4/key-file.cpp
 create mode 100644 TelepathyQt4/key-file.h

diff --git a/TelepathyQt4/KeyFile b/TelepathyQt4/KeyFile
new file mode 100644
index 0000000..0d4adc0
--- /dev/null
+++ b/TelepathyQt4/KeyFile
@@ -0,0 +1,6 @@
+#ifndef _TelepathyQt4_KeyFile_HEADER_GUARD_
+#define _TelepathyQt4_KeyFile_HEADER_GUARD_
+
+#include <TelepathyQt4/key-file.h>
+
+#endif
diff --git a/TelepathyQt4/Makefile.am b/TelepathyQt4/Makefile.am
index cdd0d43..8d8d834 100644
--- a/TelepathyQt4/Makefile.am
+++ b/TelepathyQt4/Makefile.am
@@ -50,6 +50,7 @@ libtelepathy_qt4_la_SOURCES = \
     cli-properties.cpp \
     debug.cpp \
     debug-internal.hpp \
+    key-file.cpp \
     types.cpp \
     header-compile-test.cpp
 
@@ -84,6 +85,7 @@ nodist_libtelepathy_qt4_la_SOURCES = \
 tpqt4include_HEADERS = \
     Constants \
     Debug \
+    KeyFile \
     Types \
     cli-account.h \
     cli-account-manager.h \
@@ -101,6 +103,7 @@ tpqt4include_HEADERS = \
     cli-simple-pending-operations.h \
     constants.h \
     debug.h \
+    key-file.h \
     types.h
 
 tpqt4clientinclude_HEADERS = \
diff --git a/TelepathyQt4/key-file.cpp b/TelepathyQt4/key-file.cpp
new file mode 100644
index 0000000..431a228
--- /dev/null
+++ b/TelepathyQt4/key-file.cpp
@@ -0,0 +1,453 @@
+/*
+ * This file is part of TelepathyQt4
+ *
+ * Copyright (C) 2008 Collabora Ltd. <http://www.collabora.co.uk/>
+ * Copyright (C) 2008 Nokia Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#include "key-file.h"
+
+#include "debug-internal.hpp"
+
+#include <QtCore/QByteArray>
+#include <QtCore/QFile>
+#include <QtCore/QHash>
+
+namespace Telepathy
+{
+
+struct KeyFile::Private
+{
+    QString fileName;
+    KeyFile::Status status;
+    QHash<QString, QHash<QString, QString> > groups;
+    QString currentGroup;
+
+    Private();
+    Private(const QString &fName);
+
+    void setFileName(const QString &fName);
+    void setError(KeyFile::Status status, const QString &reason);
+    bool read();
+
+    bool escapedKey(const QByteArray &data, int from, int to, QString &result);
+    bool escapedString(const QByteArray &data, int from, int to, QString &result);
+
+    QStringList allGroups() const;
+    QStringList allKeys() const;
+    QStringList keys() const;
+    bool contains(const QString &key) const;
+    QString value(const QString &key) const;
+};
+
+KeyFile::Private::Private()
+    : status(KeyFile::None)
+{
+}
+
+KeyFile::Private::Private(const QString &fName)
+    : fileName(fName),
+      status(KeyFile::NoError),
+      currentGroup("general")
+{
+    read();
+}
+
+void KeyFile::Private::setFileName(const QString &fName)
+{
+    fileName = fName;
+    status = KeyFile::NoError;
+    currentGroup = "general";
+    groups.clear();
+    read();
+}
+
+void KeyFile::Private::setError(KeyFile::Status status, const QString &reason)
+{
+    warning() << QString("ERROR: filename(%1) reason(%2)")
+                         .arg(fileName).arg(reason);
+    status = status;
+    groups.clear();
+}
+
+bool KeyFile::Private::read()
+{
+    QFile file(fileName);
+    if (!file.exists()) {
+        setError(KeyFile::NotFoundError,
+                 "file does not exist");
+        return false;
+    }
+
+    if (!file.open(QFile::ReadOnly)) {
+        setError(KeyFile::AccessError,
+                 "cannot open file for readonly access");
+        return false;
+    }
+
+    QByteArray data;
+    QByteArray group;
+    QString currentGroup("general");
+    QHash<QString, QString> groupMap;
+    int line = 0;
+    int idx;
+    while (!file.atEnd()) {
+        data = file.readLine().trimmed();
+        line++;
+
+        if (data.size() == 0) {
+            // skip empty lines
+            continue;
+        }
+
+        char ch = data.at(0);
+        if (ch == '#') {
+            // skip comments
+            continue;
+        }
+        else if (ch == '[') {
+            if (groupMap.size()) {
+                groups[currentGroup] = groupMap;
+                groupMap.clear();
+            }
+
+            idx = data.indexOf(']');
+            if (idx == -1) {
+                // line starts with [ and it's not a group
+                setError(KeyFile::FormatError,
+                         QString("invalid group at line %2 - missing ']'")
+                                 .arg(line));
+                return false;
+            }
+
+            group = data.mid(1, idx - 1).trimmed();
+            if (groups.contains(group)) {
+                setError(KeyFile::FormatError,
+                         QString("duplicated group '%1' at line %2")
+                                 .arg(group.constData()).arg(line));
+                return false;
+            }
+
+            currentGroup = "";
+            if (!escapedString(group, 0, group.size(), currentGroup)) {
+                setError(KeyFile::FormatError,
+                         QString("invalid group '%1' at line %2")
+                                 .arg(currentGroup).arg(line));
+                return false;
+            }
+        }
+        else {
+            idx = data.indexOf('=');
+            if (idx == 0) {
+                setError(KeyFile::FormatError,
+                         QString("format error at line %1 - missing '='")
+                                 .arg(line));
+                return false;
+            }
+
+            // remove trailing spaces
+            char ch;
+            while ((ch = data.at(idx - 1)) == ' ' || ch == '\t') {
+                --idx;
+            }
+
+            QString key;
+            if (!escapedKey(data, 0, idx, key)) {
+                setError(KeyFile::FormatError,
+                         QString("invalid key '%1' at line %2")
+                                 .arg(key).arg(line));
+                return false;
+            }
+
+            data = data.mid(idx + 1).trimmed();
+            QString value;
+            if (!escapedString(data, 0, data.size(), value)) {
+                setError(KeyFile::FormatError,
+                         QString("invalid key value for key '%2' at line %3")
+                                 .arg(key).arg(line));
+                return false;
+            }
+
+            if (groupMap.contains(key)) {
+                setError(KeyFile::FormatError,
+                         QString("duplicated key '%1' on group '%2' at line %3")
+                                 .arg(key).arg(currentGroup).arg(line));
+                return false;
+            }
+            groupMap[key] = value;
+        }
+    }
+
+    if (groupMap.size()) {
+        groups[currentGroup] = groupMap;
+        groupMap.clear();
+    }
+
+    return true;
+}
+
+bool KeyFile::Private::escapedKey(const QByteArray &data, int from, int to, QString &result)
+{
+    int i = from;
+    bool ret = true;
+    while (i < to) {
+        uint ch = data.at(i++);
+        if (!((ch >= 'a' && ch <= 'z') ||
+              (ch >= 'A' && ch <= 'Z') ||
+              (ch >= '0' && ch <= '9') ||
+              (ch == '-'))) {
+            ret = false;
+        }
+        result += ch;
+    }
+    return ret;
+}
+
+bool KeyFile::Private::escapedString(const QByteArray &data, int from, int to, QString &result)
+{
+    int i = from;
+    while (i < to) {
+        uint ch = data.at(i++);
+
+        if (ch == '\\') {
+            char nextCh = data.at(i++);
+            switch (nextCh) {
+                case 's':
+                    result += ' ';
+                    break;
+                case 'n':
+                    result += '\n';
+                    break;
+                case 't':
+                    result += '\t';
+                    break;
+                case 'r':
+                    result += '\r';
+                    break;
+                case ';':
+                    // keep \; there so we can split lists properly
+                    result += "\\;";
+                    break;
+                case '\\':
+                    result += '\\';
+                    break;
+                default:
+                    return false;
+            }
+        }
+        else {
+            result += ch;
+        }
+    }
+
+    return true;
+}
+
+QStringList KeyFile::Private::allGroups() const
+{
+    return groups.keys();
+}
+
+QStringList KeyFile::Private::allKeys() const
+{
+    QStringList keys;
+    QHash<QString, QHash<QString, QString> >::const_iterator itrGroups = groups.begin();
+    while (itrGroups != groups.end()) {
+        keys << itrGroups.value().keys();
+        ++itrGroups;
+    }
+    return keys;
+}
+
+QStringList KeyFile::Private::keys() const
+{
+    QHash<QString, QString> groupMap = groups[currentGroup];
+    return groupMap.keys();
+}
+
+bool KeyFile::Private::contains(const QString &key) const
+{
+    QHash<QString, QString> groupMap = groups[currentGroup];
+    return groupMap.contains(key);
+}
+
+QString KeyFile::Private::value(const QString &key) const
+{
+    QHash<QString, QString> groupMap = groups[currentGroup];
+    return groupMap.value(key);
+}
+
+
+/**
+ * \class KeyFile
+ * \headerfile <TelepathyQt4/key-file.h> <TelepathyQt4/KeyFile>
+ *
+ * The KeyFile class provides an easy way to read key-pair files such as INI
+ * style files and .desktop files.
+ *
+ * It follows the rules regarding string escaping as defined in
+ * http://standards.freedesktop.org/desktop-entry-spec/latest/index.html
+ */
+
+/**
+ * Creates a KeyFile object used to read (key-pair) compliant files.
+ *
+ * The status will be #KeyFile::None
+ * \sa setFileName()
+ */
+KeyFile::KeyFile()
+    : mPriv(new Private())
+{
+}
+
+/**
+ * Creates a KeyFile object used to read (key-pair) compliant files.
+ *
+ * \param fileName Name of the file to be read.
+ */
+KeyFile::KeyFile(const QString &fileName)
+    : mPriv(new Private(fileName))
+{
+}
+
+/**
+ * Class destructor.
+ */
+KeyFile::~KeyFile()
+{
+    delete mPriv;
+}
+
+/**
+ * Sets the name of the file to be read.
+ *
+ * \param fileName Name of the file to be read.
+ */
+void KeyFile::setFileName(const QString &fileName)
+{
+    mPriv->setFileName(fileName);
+}
+
+/**
+ * Returns the name of the file associated with this object.
+ *
+ * \return Name of the file associated with this object.
+ */
+QString KeyFile::fileName() const
+{
+    return mPriv->fileName;
+}
+
+/**
+ * Return a status code indicating the first error that was met by #KeyFile,
+ * or KeyFile::NoError if no error occurred.
+ *
+ * Make sure to use this method if you set the filename to be read using
+ * setFileName().
+ *
+ * \return Status code.
+ * \sa setFileName()
+ */
+KeyFile::Status KeyFile::status() const
+{
+    return mPriv->status;
+}
+
+/**
+ * Set the current group to be used while reading keys.
+ *
+ * Query functions such as keys(), contains() and value() are based on this
+ * group.
+ *
+ * By default a group named "general" is used as the group for global
+ * keys and is used as the default group if none is set.
+ *
+ * \param group Name of the group to be used.
+ * \sa group()
+ */
+void KeyFile::setGroup(const QString &group)
+{
+    mPriv->currentGroup = group;
+}
+
+/**
+ * Returns the current group.
+ *
+ * \return Name of the current group.
+ * \sa setGroup()
+ */
+QString KeyFile::group() const
+{
+    return mPriv->currentGroup;
+}
+
+/**
+ * Returns all groups in the desktop file.
+ *
+ * Global keys will be added to a group named "general".
+ *
+ * \return List of all groups in the desktop file.
+ */
+QStringList KeyFile::allGroups() const
+{
+    return mPriv->allGroups();
+}
+
+/**
+ * Returns all keys described in the desktop file.
+ *
+ * \return List of all keys in the desktop file.
+ */
+QStringList KeyFile::allKeys() const
+{
+    return mPriv->allKeys();
+}
+
+/**
+ * Returns a list of keys in the current group.
+ *
+ * \return List of all keys in the current group.
+ * \sa group(), setGroup()
+ */
+QStringList KeyFile::keys() const
+{
+    return mPriv->keys();
+}
+
+/**
+ * Check if the current group contains a key named \a key.
+ *
+ * \return true if \a key exists, false otherwise.
+ * \sa group(), setGroup()
+ */
+bool KeyFile::contains(const QString &key) const
+{
+    return mPriv->contains(key);
+}
+
+/**
+ * Get the value for the key in the current group named \a key.
+ *
+ * \return Value of \a key, empty string if not found.
+ * \sa group(), setGroup()
+ */
+QString KeyFile::value(const QString &key) const
+{
+    return mPriv->value(key);
+}
+
+}
diff --git a/TelepathyQt4/key-file.h b/TelepathyQt4/key-file.h
new file mode 100644
index 0000000..2870c14
--- /dev/null
+++ b/TelepathyQt4/key-file.h
@@ -0,0 +1,71 @@
+/*
+ * This file is part of TelepathyQt4
+ *
+ * Copyright (C) 2008 Collabora Ltd. <http://www.collabora.co.uk/>
+ * Copyright (C) 2008 Nokia Corporation
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+ */
+
+#ifndef _TelepathyQt4_key_file_h_HEADER_GUARD_
+#define _TelepathyQt4_key_file_h_HEADER_GUARD_
+
+#include <QtCore/QString>
+#include <QtCore/QStringList>
+
+namespace Telepathy
+{
+
+class KeyFile
+{
+public:
+    enum Status {
+        None = 0,
+        NoError,
+        NotFoundError,
+        AccessError,
+        FormatError,
+    };
+
+    KeyFile();
+    KeyFile(const QString &fileName);
+    ~KeyFile();
+
+    void setFileName(const QString &fileName);
+    QString fileName() const;
+
+    Status status() const;
+
+    void setGroup(const QString &group);
+    QString group() const;
+
+    QStringList allGroups() const;
+    QStringList allKeys() const;
+    QStringList keys() const;
+    bool contains(const QString &key) const;
+
+    QString value(const QString &key) const;
+
+private:
+    Q_DISABLE_COPY(KeyFile);
+
+    struct Private;
+    friend struct Private;
+    Private *mPriv;
+};
+
+}
+
+#endif
-- 
1.5.6.5




More information about the Telepathy-commits mailing list