]> vaikene.ee Git - evaf/blobdiff - src/libs/Common/inifile.cpp
Warning fixes and copyright update.
[evaf] / src / libs / Common / inifile.cpp
index 14a47ee5fda19cb3c82b2bdbab383f13cfe3d9a6..ebdfd73affa70de1f0c75be772a31c541613d1f3 100644 (file)
@@ -3,7 +3,7 @@
  * @brief Internal implementation of the class for reading and writing parameter values in INI files.
  * @author Enar Vaikene
  *
- * Copyright (c) 2011 Enar Vaikene
+ * Copyright (c) 2011-2019 Enar Vaikene
  *
  * This file is part of the eVaf C++ cross-platform application development framework.
  *
@@ -37,13 +37,13 @@ using namespace eVaf::Common;
 //-------------------------------------------------------------------
 
 IniFile::IniFile(QString const & fileName, QIODevice::OpenMode mode)
+    : d(new Internal::IniFileImpl(fileName, mode))
 {
-    d = new Internal::IniFileImpl(fileName, mode);
 }
 
 IniFile::~IniFile()
 {
-    delete d;
+    d.reset();
 }
 
 bool IniFile::isValid() const
@@ -56,12 +56,12 @@ QString IniFile::errorString() const
     return d->errorString();
 }
 
-QVariant IniFile::getValue(QString const & paramName, QVariant const & defaultValue)
+QVariant IniFile::getValue(QByteArray const & paramName, QVariant const & defaultValue)
 {
     return d->getValue(paramName, defaultValue);
 }
 
-bool IniFile::setValue(QString const & paramName, QVariant const & value)
+bool IniFile::setValue(QByteArray const & paramName, QVariant const & value)
 {
     return d->setValue(paramName, value);
 }
@@ -96,10 +96,10 @@ IniFileImpl::~IniFileImpl()
     mCache.clear();
 }
 
-void IniFileImpl::updateCache(quint64 pos, qint64 diff)
+void IniFileImpl::updateCache(qint64 pos, qint64 diff)
 {
     // Walk through all the sections in the cache
-    QHash<QString, QExplicitlySharedDataPointer<IniFileSection> >::const_iterator it;
+    QHash<QByteArray, QExplicitlySharedDataPointer<IniFileSection> >::const_iterator it;
     for (it = mCache.constBegin(); it != mCache.constEnd(); ++it) {
         QExplicitlySharedDataPointer<IniFileSection> sectionObject = *it;
 
@@ -108,7 +108,7 @@ void IniFileImpl::updateCache(quint64 pos, qint64 diff)
             sectionObject->filePos += diff;
 
         // Update individual values in the section that come after the current file offset
-        QHash<QString, QExplicitlySharedDataPointer<IniFileValue> >::const_iterator it1;
+        QHash<QByteArray, QExplicitlySharedDataPointer<IniFileValue> >::const_iterator it1;
         for (it1 = sectionObject->values.constBegin(); it1 != sectionObject->values.constEnd(); ++it1) {
             QExplicitlySharedDataPointer<IniFileValue> valueObject = *it1;
             if (valueObject->filePos > pos)
@@ -117,7 +117,7 @@ void IniFileImpl::updateCache(quint64 pos, qint64 diff)
     }
 }
 
-QExplicitlySharedDataPointer<IniFileSection> IniFileImpl::getSection(QFile & file, QString const & sectionName)
+QExplicitlySharedDataPointer<IniFileSection> IniFileImpl::getSection(QFile & file, QByteArray const & sectionName)
 {
     // Check for external modifications
     QFileInfo fi(file);
@@ -128,7 +128,7 @@ QExplicitlySharedDataPointer<IniFileSection> IniFileImpl::getSection(QFile & fil
     }
 
     // Look for the section in the cache first
-    QHash<QString, QExplicitlySharedDataPointer<IniFileSection> >::const_iterator it = mCache.constFind(sectionName.toLower());
+    QHash<QByteArray, QExplicitlySharedDataPointer<IniFileSection> >::const_iterator it = mCache.constFind(sectionName.toLower());
     if (it != mCache.constEnd()) {
         // Found in the cache
         if (mValid)
@@ -138,7 +138,7 @@ QExplicitlySharedDataPointer<IniFileSection> IniFileImpl::getSection(QFile & fil
 
     // Read the INI file and look for the section
     while (mValid && !file.atEnd()) {
-        QString line = file.readLine().trimmed();
+        QByteArray line = file.readLine().trimmed();
 
         // Ignore the line if it is empty, a comment or not a section name
         if (line.isEmpty() || line.startsWith(';') || line.startsWith('#') || !line.startsWith('['))
@@ -150,7 +150,7 @@ QExplicitlySharedDataPointer<IniFileSection> IniFileImpl::getSection(QFile & fil
             continue;
 
         // Is this the section that we are looking for?
-        if (line.mid(1, idx - 1).compare(sectionName, Qt::CaseInsensitive) == 0) {
+        if (qstricmp(line.mid(1, idx - 1).constData(), sectionName.constData()) == 0) {
             // Create the section object and add to the cache
             QExplicitlySharedDataPointer<IniFileSection> sectionObject(new IniFileSection(file.pos()));
             sectionObject->name = sectionName.toLower();
@@ -166,10 +166,10 @@ QExplicitlySharedDataPointer<IniFileSection> IniFileImpl::getSection(QFile & fil
     return QExplicitlySharedDataPointer<IniFileSection>();
 }
 
-QExplicitlySharedDataPointer<IniFileValue> IniFileImpl::getParameter(QFile & file, IniFileSection & section, QString const & paramName)
+QExplicitlySharedDataPointer<IniFileValue> IniFileImpl::getParameter(QFile & file, IniFileSection & section, QByteArray const & paramName)
 {
     // Look for the parameter in the cache first
-    QHash<QString, QExplicitlySharedDataPointer<IniFileValue> >::const_iterator it = section.values.constFind(paramName.toLower());
+    QHash<QByteArray, QExplicitlySharedDataPointer<IniFileValue> >::const_iterator it = section.values.constFind(paramName.toLower());
     if (it != section.values.constEnd()) {
         // Found it in the cache
         if (mValid)
@@ -181,9 +181,9 @@ QExplicitlySharedDataPointer<IniFileValue> IniFileImpl::getParameter(QFile & fil
     while (mValid && !file.atEnd()) {
 
         // Current file position
-        quint64 currentPos = file.pos();
+        qint64 currentPos = file.pos();
 
-        QString line = file.readLine().trimmed();
+        QByteArray line = file.readLine().trimmed();
 
         // Ignore the line if it is empty or a comment
         if (line.isEmpty() || line.startsWith(';') || line.startsWith('#'))
@@ -202,12 +202,12 @@ QExplicitlySharedDataPointer<IniFileValue> IniFileImpl::getParameter(QFile & fil
         if (idx == -1)
             continue;
 
-        QString name = line.mid(0, idx).trimmed().toLower();
-        QString value = line.mid(idx + 1).trimmed();
+        QByteArray name = line.mid(0, idx).trimmed().toLower();
+        QByteArray value = line.mid(idx + 1).trimmed();
 
         // Check for the 'windows:' or 'linux:' prefix in the parameter name
         bool thisOsOnly = false;
-#ifdef Q_OS_LINUX
+#if defined(Q_OS_LINUX) || defined(Q_OS_MACOS)
         if (name.startsWith("windows:"))
             continue;
         if (name.startsWith("linux:")) {
@@ -226,7 +226,7 @@ QExplicitlySharedDataPointer<IniFileValue> IniFileImpl::getParameter(QFile & fil
 
         // If the parameter value is not in the cache, add it to the cache
         QExplicitlySharedDataPointer<IniFileValue> valueObject;
-        QHash<QString, QExplicitlySharedDataPointer<IniFileValue> >::const_iterator it = section.values.constFind(name);
+        QHash<QByteArray, QExplicitlySharedDataPointer<IniFileValue> >::const_iterator it = section.values.constFind(name);
         if (it == section.values.constEnd()) {
             valueObject = new IniFileValue(currentPos);
             valueObject->name = name;
@@ -243,7 +243,7 @@ QExplicitlySharedDataPointer<IniFileValue> IniFileImpl::getParameter(QFile & fil
         }
 
         // Is this the parameter vwe are looking for?
-        if (name.compare(paramName, Qt::CaseInsensitive) == 0) {
+        if (qstricmp(name.constData(), paramName.constData()) == 0) {
             // Rewind to the beginning of the line
             file.seek(currentPos);
 
@@ -256,7 +256,7 @@ QExplicitlySharedDataPointer<IniFileValue> IniFileImpl::getParameter(QFile & fil
     return QExplicitlySharedDataPointer<IniFileValue>();
 }
 
-QVariant IniFileImpl::getValue(QString const & paramName, QVariant const & defaultValue)
+QVariant IniFileImpl::getValue(QByteArray const & paramName, QVariant const & defaultValue)
 {
     // Locate the '/' character that separates section names from key names
     int idx = paramName.lastIndexOf('/');
@@ -264,8 +264,8 @@ QVariant IniFileImpl::getValue(QString const & paramName, QVariant const & defau
         return defaultValue;
 
     // Separate section and key names
-    QString section = paramName.left(idx);
-    QString key = paramName.mid(idx + 1);
+    QByteArray section = paramName.left(idx);
+    QByteArray key = paramName.mid(idx + 1);
 
     // Open the file
     QFile f(mFileName);
@@ -289,10 +289,38 @@ QVariant IniFileImpl::getValue(QString const & paramName, QVariant const & defau
     if (f.isOpen())
         f.close();
 
-    return toVariant(valueObject->paramValue, defaultValue);
+    // Return the cached value if it is already set and the type is the same than the default value type
+    if (valueObject->value.isValid() && (!defaultValue.isValid() || valueObject->value.type() == defaultValue.type()))
+        return valueObject->value;
+
+    // Convert to the proper type
+    if (defaultValue.type() == QVariant::ByteArray || defaultValue.type() == QVariant::String) {
+        // Remove single and double quotes
+        QByteArray v = valueObject->paramValue;
+        if (v.startsWith('\"')) {
+            v.remove(0, 1);
+            if (v.endsWith('\"'))
+                v.remove(v.size() - 1, 1);
+        }
+        else if (v.startsWith('\'')) {
+            v.remove(0, 1);
+            if (v.endsWith('\''))
+                v.remove(v.size() - 1, 1);
+        }
+
+        // Convert from the escaped character array
+        if (defaultValue.type() == QVariant::String)
+            valueObject->value = QVariant(strFromEscapedCharArray(v));
+        else
+            valueObject->value = QVariant(binFromEscapedCharArray(v));
+    }
+    else
+        valueObject->value = toVariant(valueObject->paramValue, defaultValue);
+
+    return valueObject->value;
 }
 
-bool IniFileImpl::setValue(QString const & paramName, QVariant const & value)
+bool IniFileImpl::setValue(QByteArray const & paramName, QVariant const & value)
 {
     // Locate the '/' character that separates section names from key names
     int idx = paramName.lastIndexOf('/', -1);
@@ -300,29 +328,49 @@ bool IniFileImpl::setValue(QString const & paramName, QVariant const & value)
         return false;
 
     // Separate section and key names
-    QString section = paramName.left(idx).toLower();
-    QString key = paramName.mid(idx + 1).toLower();
+    QByteArray section = paramName.left(idx).toLower();
+    QByteArray key = paramName.mid(idx + 1).toLower();
 
     // Format the value depending on the type
-    QString valueString;
+    QByteArray valueString;
     switch (value.type()) {
         case QVariant::UInt:
-            valueString = "0x" + QString::number(value.toUInt(), 16);
+            valueString = QByteArray("0x").append(QByteArray::number(value.toUInt(), 16));
             break;
         case QVariant::Int:
-            valueString = QString::number(value.toInt());
+            valueString = QByteArray::number(value.toInt());
             break;
         case QVariant::Double:
-            valueString = QString::number(value.toDouble(), 'f');
+            valueString = QByteArray::number(value.toDouble(), 'f');
             break;
         case QVariant::Bool:
             valueString = value.toBool() ? "true" : "false";
             break;
-        case QVariant::Char:
-            valueString = value.toChar();
+        case QVariant::Char: {
+            QChar c = value.toChar();
+            printf("c.unicode() = %u\n", c.unicode());
+            if (c.unicode() < 32 || c.unicode() >= 127)
+                valueString = QByteArray("\\0x").append(QByteArray::number(c.unicode(), 16));
+            else
+                valueString = QByteArray(1, static_cast<char const>(c.unicode()));
+            break;
+        }
+        case QVariant::ByteArray:
+            valueString = binToEscapedCharArray(value.toByteArray());
+            if (valueString.startsWith(' ') || valueString.endsWith(' ')) {
+                valueString.insert(0, '\"');
+                valueString.append('\"');
+            }
+            break;
+        case QVariant::String:
+            valueString = strToEscapedCharArray(value.toString());
+            if (valueString.startsWith(' ') || valueString.endsWith(' ')) {
+                valueString.insert(0, '\"');
+                valueString.append('\"');
+            }
             break;
         default:
-            valueString = value.toString();
+            valueString = value.toString().toLatin1();
     }
 
     // Open the file
@@ -343,7 +391,7 @@ bool IniFileImpl::setValue(QString const & paramName, QVariant const & value)
     if (!sectionObject) {
 
         // Write the new section to the INI file (the file is already positioned at the end)
-        if (f.write(QString("[%1]" EOL).arg(section).toLocal8Bit()) == -1) {
+        if (f.write(QString("[%1]" EOL).arg(section.constData()).toLatin1()) == -1) {
             mErrorString = f.errorString();
             mValid = false;
             EVAF_ERROR("Failed to write to the INI file %s : %s", qPrintable(mFileName), qPrintable(mErrorString));
@@ -351,14 +399,14 @@ bool IniFileImpl::setValue(QString const & paramName, QVariant const & value)
         }
 
         // Current file position
-        quint64 currentPos = f.pos();
+        qint64 currentPos = f.pos();
 
         // Add the new section to the internal cache
         sectionObject = new IniFileSection(currentPos);
         mCache.insert(section.toLower(), sectionObject);
 
         // Write the parameter value to the INI file
-        if (f.write(QString("%1 = %2" EOL).arg(key).arg(valueString).toLocal8Bit()) == -1) {
+        if (f.write(QString("%1 = %2" EOL).arg(key.constData()).arg(valueString.constData()).toLatin1()) == -1) {
             mErrorString = f.errorString();
             mValid = false;
             EVAF_ERROR("Failed to write to the INI file %s : %s", qPrintable(mFileName), qPrintable(mErrorString));
@@ -369,13 +417,14 @@ bool IniFileImpl::setValue(QString const & paramName, QVariant const & value)
         QExplicitlySharedDataPointer<IniFileValue> valueObject(new IniFileValue(currentPos));
         valueObject->name = key;
         valueObject->paramValue = valueString;
+        valueObject->value = value;
         sectionObject->values.insert(key, valueObject);
     }
 
     // If the section is found, use the existing section object from the cache
     else {
-        quint64 currentPos;
-        quint64 oldPos = f.pos();
+        qint64 currentPos;
+        qint64 oldPos = f.pos();
         QString prefix; // Platform-specific prefix
 
         // Locate the parameter value
@@ -423,7 +472,7 @@ bool IniFileImpl::setValue(QString const & paramName, QVariant const & value)
 
         // Rewind to the original position and write the new parameter value
         f.seek(currentPos);
-        if (f.write(QString("%1%2 = %3" EOL).arg(prefix).arg(key).arg(valueString).toLocal8Bit()) == -1) {
+        if (f.write(QString("%1%2 = %3" EOL).arg(prefix).arg(key.constData()).arg(valueString.constData()).toLatin1()) == -1) {
             mErrorString = f.errorString();
             mValid = false;
             EVAF_ERROR("Failed to write to the INI file %s : %s", qPrintable(mFileName), qPrintable(mErrorString));
@@ -459,6 +508,7 @@ bool IniFileImpl::setValue(QString const & paramName, QVariant const & value)
 
         // Update the parameter value in the internal cache
         valueObject->paramValue = valueString;
+        valueObject->value = value;
 
     }