diff --git a/autotests/kconfigtest.cpp b/autotests/kconfigtest.cpp index 7bb2cf9..069513d 100644 --- a/autotests/kconfigtest.cpp +++ b/autotests/kconfigtest.cpp @@ -1,1964 +1,1964 @@ /* This file is part of the KDE libraries Copyright (C) 1997 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. */ // Qt5 TODO: re-enable. No point in doing it before, it breaks on QString::fromUtf8(QByteArray), which exists in qt5. #undef QT_NO_CAST_FROM_BYTEARRAY #include "kconfigtest.h" #include "helper.h" #include "config-kconfig.h" #include #include #include #include #include #include #include #ifdef Q_OS_UNIX #include #endif #ifndef Q_OS_WIN #include // getuid #endif KCONFIGGROUP_DECLARE_ENUM_QOBJECT(KConfigTest, Testing) KCONFIGGROUP_DECLARE_FLAGS_QOBJECT(KConfigTest, Flags) QTEST_MAIN(KConfigTest) Q_DECLARE_METATYPE(KConfigGroup) static QString homePath() { #ifdef Q_OS_WIN return QDir::homePath(); #else // Don't use QDir::homePath() on Unix, it removes any trailing slash, while KConfig uses $HOME. return QString::fromLocal8Bit(qgetenv("HOME")); #endif } #define BOOLENTRY1 true #define BOOLENTRY2 false #define STRINGENTRY1 "hello" #define STRINGENTRY2 " hello" #define STRINGENTRY3 "hello " #define STRINGENTRY4 " hello " #define STRINGENTRY5 " " #define STRINGENTRY6 "" #define UTF8BITENTRY "Hello äöü" #define TRANSLATEDSTRINGENTRY1 "bonjour" #define BYTEARRAYENTRY QByteArray( "\x00\xff\x7f\x3c abc\x00\x00", 10 ) #define ESCAPEKEY " []\0017[]==]" #define ESCAPEENTRY "[]\170[]]=3=]\\] " #define DOUBLEENTRY 123456.78912345 #define FLOATENTRY 123.567f #define POINTENTRY QPoint( 4351, 1235 ) #define SIZEENTRY QSize( 10, 20 ) #define RECTENTRY QRect( 10, 23, 5321, 13 ) #define DATETIMEENTRY QDateTime( QDate( 2002, 06, 23 ), QTime( 12, 55, 40 ) ) #define STRINGLISTENTRY (QStringList( "Hello," ) << " World") #define STRINGLISTEMPTYENTRY QStringList() #define STRINGLISTJUSTEMPTYELEMENT QStringList(QString()) #define STRINGLISTEMPTYTRAINLINGELEMENT (QStringList( "Hi" ) << QString()) #define STRINGLISTESCAPEODDENTRY (QStringList( "Hello\\\\\\" ) << "World") #define STRINGLISTESCAPEEVENENTRY (QStringList( "Hello\\\\\\\\" ) << "World") #define STRINGLISTESCAPECOMMAENTRY (QStringList( "Hel\\\\\\,\\\\,\\,\\\\\\\\,lo" ) << "World") #define INTLISTENTRY1 QList() << 1 << 2 << 3 << 4 #define BYTEARRAYLISTENTRY1 QList() << "" << "1,2" << "end" #define VARIANTLISTENTRY (QVariantList() << true << false << QString("joe") << 10023) #define VARIANTLISTENTRY2 (QVariantList() << POINTENTRY << SIZEENTRY) #define HOMEPATH QString(homePath()+"/foo") #define HOMEPATHESCAPE QString(homePath()+"/foo/$HOME") #define DOLLARGROUP "$i" #define TEST_SUBDIR "kconfigtest_subdir/" static inline QString testConfigDir() { return QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + "/" TEST_SUBDIR; } static inline QString kdeGlobalsPath() { return QDir::cleanPath(testConfigDir() + "..") + "/kdeglobals"; } #ifndef Q_OS_WIN void initLocale() { setenv("LC_ALL", "en_US.utf-8", 1); setenv("TZ", "UTC", 1); } Q_CONSTRUCTOR_FUNCTION(initLocale) #endif void KConfigTest::initTestCase() { // ensure we don't use files in the real config directory QStandardPaths::setTestModeEnabled(true); qRegisterMetaType(); // to make sure all files from a previous failed run are deleted cleanupTestCase(); KSharedConfigPtr mainConfig = KSharedConfig::openConfig(); mainConfig->group("Main").writeEntry("Key", "Value"); mainConfig->sync(); KConfig sc(TEST_SUBDIR "kconfigtest"); KConfigGroup cg(&sc, "AAA"); cg.writeEntry("stringEntry1", STRINGENTRY1, KConfig::Persistent | KConfig::Global); cg.deleteEntry("stringEntry2", KConfig::Global); cg = KConfigGroup(&sc, "Hello"); cg.writeEntry("boolEntry1", BOOLENTRY1); cg.writeEntry("boolEntry2", BOOLENTRY2); QByteArray data(UTF8BITENTRY); QCOMPARE(data.size(), 12); // the source file is in utf8 QCOMPARE(QString::fromUtf8(data).length(), 9); cg.writeEntry("Test", data); cg.writeEntry("bytearrayEntry", BYTEARRAYENTRY); cg.writeEntry(ESCAPEKEY, ESCAPEENTRY); cg.writeEntry("emptyEntry", ""); cg.writeEntry("stringEntry1", STRINGENTRY1); cg.writeEntry("stringEntry2", STRINGENTRY2); cg.writeEntry("stringEntry3", STRINGENTRY3); cg.writeEntry("stringEntry4", STRINGENTRY4); cg.writeEntry("stringEntry5", STRINGENTRY5); cg.writeEntry("urlEntry1", QUrl(QStringLiteral("http://qt-project.org"))); cg.writeEntry("keywith=equalsign", STRINGENTRY1); cg.deleteEntry("stringEntry5"); cg.deleteEntry("stringEntry6"); // deleting a nonexistent entry cg.writeEntry("byteArrayEntry1", QByteArray(STRINGENTRY1), KConfig::Global | KConfig::Persistent); cg.writeEntry("doubleEntry1", DOUBLEENTRY); cg.writeEntry("floatEntry1", FLOATENTRY); sc.deleteGroup("deleteMe"); // deleting a nonexistent group cg = KConfigGroup(&sc, "Complex Types"); cg.writeEntry("rectEntry", RECTENTRY); cg.writeEntry("pointEntry", POINTENTRY); cg.writeEntry("sizeEntry", SIZEENTRY); cg.writeEntry("dateTimeEntry", DATETIMEENTRY); cg.writeEntry("dateEntry", DATETIMEENTRY.date()); KConfigGroup ct = cg; cg = KConfigGroup(&ct, "Nested Group 1"); cg.writeEntry("stringentry1", STRINGENTRY1); cg = KConfigGroup(&ct, "Nested Group 2"); cg.writeEntry("stringEntry2", STRINGENTRY2); cg = KConfigGroup(&cg, "Nested Group 2.1"); cg.writeEntry("stringEntry3", STRINGENTRY3); cg = KConfigGroup(&ct, "Nested Group 3"); cg.writeEntry("stringEntry3", STRINGENTRY3); cg = KConfigGroup(&sc, "List Types"); cg.writeEntry("listOfIntsEntry1", INTLISTENTRY1); cg.writeEntry("listOfByteArraysEntry1", BYTEARRAYLISTENTRY1); cg.writeEntry("stringListEntry", STRINGLISTENTRY); cg.writeEntry("stringListEmptyEntry", STRINGLISTEMPTYENTRY); cg.writeEntry("stringListJustEmptyElement", STRINGLISTJUSTEMPTYELEMENT); cg.writeEntry("stringListEmptyTrailingElement", STRINGLISTEMPTYTRAINLINGELEMENT); cg.writeEntry("stringListEscapeOddEntry", STRINGLISTESCAPEODDENTRY); cg.writeEntry("stringListEscapeEvenEntry", STRINGLISTESCAPEEVENENTRY); cg.writeEntry("stringListEscapeCommaEntry", STRINGLISTESCAPECOMMAENTRY); cg.writeEntry("variantListEntry", VARIANTLISTENTRY); cg = KConfigGroup(&sc, "Path Type"); cg.writePathEntry("homepath", HOMEPATH); cg.writePathEntry("homepathescape", HOMEPATHESCAPE); cg = KConfigGroup(&sc, "Enum Types"); #if defined(_MSC_VER) && _MSC_VER == 1600 cg.writeEntry("dummy", 42); #else //Visual C++ 2010 throws an Internal Compiler Error here cg.writeEntry("enum-10", Tens); cg.writeEntry("enum-100", Hundreds); cg.writeEntry("flags-bit0", Flags(bit0)); cg.writeEntry("flags-bit0-bit1", Flags(bit0 | bit1)); #endif cg = KConfigGroup(&sc, "ParentGroup"); KConfigGroup cg1(&cg, "SubGroup1"); cg1.writeEntry("somestring", "somevalue"); cg.writeEntry("parentgrpstring", "somevalue"); KConfigGroup cg2(&cg, "SubGroup2"); cg2.writeEntry("substring", "somevalue"); KConfigGroup cg3(&cg, "SubGroup/3"); cg3.writeEntry("sub3string", "somevalue"); QVERIFY(sc.isDirty()); QVERIFY(sc.sync()); QVERIFY(!sc.isDirty()); QVERIFY2(QFile::exists(testConfigDir() + QStringLiteral("/kconfigtest")), qPrintable(testConfigDir() + QStringLiteral("/kconfigtest must exist"))); QVERIFY2(QFile::exists(kdeGlobalsPath()), qPrintable(kdeGlobalsPath() + QStringLiteral(" must exist"))); KConfig sc1(TEST_SUBDIR "kdebugrc", KConfig::SimpleConfig); KConfigGroup sg0(&sc1, "0"); sg0.writeEntry("AbortFatal", false); sg0.writeEntry("WarnOutput", 0); sg0.writeEntry("FatalOutput", 0); QVERIFY(sc1.sync()); //Setup stuff to test KConfig::addConfigSources() KConfig devcfg(TEST_SUBDIR "specificrc"); KConfigGroup devonlygrp(&devcfg, "Specific Only Group"); devonlygrp.writeEntry("ExistingEntry", "DevValue"); KConfigGroup devandbasegrp(&devcfg, "Shared Group"); devandbasegrp.writeEntry("SomeSharedEntry", "DevValue"); devandbasegrp.writeEntry("SomeSpecificOnlyEntry", "DevValue"); QVERIFY(devcfg.sync()); KConfig basecfg(TEST_SUBDIR "baserc"); KConfigGroup basegrp(&basecfg, "Base Only Group"); basegrp.writeEntry("ExistingEntry", "BaseValue"); KConfigGroup baseanddevgrp(&basecfg, "Shared Group"); baseanddevgrp.writeEntry("SomeSharedEntry", "BaseValue"); baseanddevgrp.writeEntry("SomeBaseOnlyEntry", "BaseValue"); QVERIFY(basecfg.sync()); KConfig gecfg(TEST_SUBDIR "groupescapetest", KConfig::SimpleConfig); cg = KConfigGroup(&gecfg, DOLLARGROUP); cg.writeEntry("entry", "doesntmatter"); } void KConfigTest::cleanupTestCase() { //ensure we don't delete the real directory QDir localConfig(testConfigDir()); //qDebug() << "Erasing" << localConfig; if (localConfig.exists()) { QVERIFY(localConfig.removeRecursively()); } QVERIFY(!localConfig.exists()); if (QFile::exists(kdeGlobalsPath())) { QVERIFY(QFile::remove(kdeGlobalsPath())); } } static QList readLinesFrom(const QString &path) { QFile file(path); const bool opened = file.open(QIODevice::ReadOnly | QIODevice::Text); QList lines; if (!opened) { QWARN(qPrintable(QLatin1String("Failed to open ") + path)); return lines; } QByteArray line; do { line = file.readLine(); if (!line.isEmpty()) { lines.append(line); } } while (!line.isEmpty()); return lines; } static QList readLines(const char *fileName = TEST_SUBDIR "kconfigtest") { const QString path = QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation); Q_ASSERT(!path.isEmpty()); return readLinesFrom(path + '/' + fileName); } // see also testDefaults, which tests reverting with a defaults (global) file available void KConfigTest::testDirtyAfterRevert() { KConfig sc(TEST_SUBDIR "kconfigtest_revert"); KConfigGroup cg(&sc, "Hello"); cg.revertToDefault("does_not_exist"); QVERIFY(!sc.isDirty()); cg.writeEntry("Test", "Correct"); QVERIFY(sc.isDirty()); sc.sync(); QVERIFY(!sc.isDirty()); cg.revertToDefault("Test"); QVERIFY(sc.isDirty()); QVERIFY(sc.sync()); QVERIFY(!sc.isDirty()); cg.revertToDefault("Test"); QVERIFY(!sc.isDirty()); } void KConfigTest::testRevertAllEntries() { // this tests the case were we revert (delete) all entries in a file, // leaving a blank file { KConfig sc(TEST_SUBDIR "konfigtest2", KConfig::SimpleConfig); KConfigGroup cg(&sc, "Hello"); cg.writeEntry("Test", "Correct"); } { KConfig sc(TEST_SUBDIR "konfigtest2", KConfig::SimpleConfig); KConfigGroup cg(&sc, "Hello"); QCOMPARE(cg.readEntry("Test", "Default"), QString("Correct")); cg.revertToDefault("Test"); } KConfig sc(TEST_SUBDIR "konfigtest2", KConfig::SimpleConfig); KConfigGroup cg(&sc, "Hello"); QCOMPARE(cg.readEntry("Test", "Default"), QString("Default")); } void KConfigTest::testSimple() { // kdeglobals (which was created in initTestCase) must be found this way: const QStringList kdeglobals = QStandardPaths::locateAll(QStandardPaths::GenericConfigLocation, QStringLiteral("kdeglobals")); QVERIFY(!kdeglobals.isEmpty()); KConfig sc2(TEST_SUBDIR "kconfigtest"); QCOMPARE(sc2.name(), QString(TEST_SUBDIR "kconfigtest")); // make sure groupList() isn't returning something it shouldn't const QStringList lstGroup = sc2.groupList(); for (const QString &group : lstGroup) { QVERIFY(!group.isEmpty() && group != ""); QVERIFY(!group.contains(QChar(0x1d))); } KConfigGroup sc3(&sc2, "AAA"); QVERIFY(sc3.hasKey("stringEntry1")); // from kdeglobals QVERIFY(!sc3.isEntryImmutable("stringEntry1")); QCOMPARE(sc3.readEntry("stringEntry1"), QString(STRINGENTRY1)); QVERIFY(!sc3.hasKey("stringEntry2")); QCOMPARE(sc3.readEntry("stringEntry2", QString("bla")), QString("bla")); QVERIFY(!sc3.hasDefault("stringEntry1")); sc3 = KConfigGroup(&sc2, "Hello"); QCOMPARE(sc3.readEntry("Test", QByteArray()), QByteArray(UTF8BITENTRY)); QCOMPARE(sc3.readEntry("bytearrayEntry", QByteArray()), BYTEARRAYENTRY); QCOMPARE(sc3.readEntry(ESCAPEKEY), QString(ESCAPEENTRY)); QCOMPARE(sc3.readEntry("Test", QString()), QString::fromUtf8(UTF8BITENTRY)); QCOMPARE(sc3.readEntry("emptyEntry"/*, QString("Fietsbel")*/), QLatin1String("")); QCOMPARE(sc3.readEntry("emptyEntry", QString("Fietsbel")).isEmpty(), true); QCOMPARE(sc3.readEntry("stringEntry1"), QString(STRINGENTRY1)); QCOMPARE(sc3.readEntry("stringEntry2"), QString(STRINGENTRY2)); QCOMPARE(sc3.readEntry("stringEntry3"), QString(STRINGENTRY3)); QCOMPARE(sc3.readEntry("stringEntry4"), QString(STRINGENTRY4)); QVERIFY(!sc3.hasKey("stringEntry5")); QCOMPARE(sc3.readEntry("stringEntry5", QString("test")), QString("test")); QVERIFY(!sc3.hasKey("stringEntry6")); QCOMPARE(sc3.readEntry("stringEntry6", QString("foo")), QString("foo")); QCOMPARE(sc3.readEntry("urlEntry1", QUrl()), QUrl("http://qt-project.org")); QCOMPARE(sc3.readEntry("boolEntry1", BOOLENTRY1), BOOLENTRY1); QCOMPARE(sc3.readEntry("boolEntry2", false), BOOLENTRY2); QCOMPARE(sc3.readEntry("keywith=equalsign", QString("wrong")), QString(STRINGENTRY1)); QCOMPARE(sc3.readEntry("byteArrayEntry1", QByteArray()), QByteArray(STRINGENTRY1)); QCOMPARE(sc3.readEntry("doubleEntry1", 0.0), DOUBLEENTRY); QCOMPARE(sc3.readEntry("floatEntry1", 0.0f), FLOATENTRY); } void KConfigTest::testDefaults() { KConfig config(TEST_SUBDIR "defaulttest", KConfig::NoGlobals); const QString defaultsFile = TEST_SUBDIR "defaulttest.defaults"; KConfig defaults(defaultsFile, KConfig::SimpleConfig); const QString Default(QStringLiteral("Default")); const QString NotDefault(QStringLiteral("Not Default")); const QString Value1(STRINGENTRY1); const QString Value2(STRINGENTRY2); KConfigGroup group = defaults.group("any group"); group.writeEntry("entry1", Default); QVERIFY(group.sync()); group = config.group("any group"); group.writeEntry("entry1", Value1); group.writeEntry("entry2", Value2); QVERIFY(group.sync()); config.addConfigSources(QStringList() << QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + '/' + defaultsFile); config.setReadDefaults(true); QCOMPARE(group.readEntry("entry1", QString()), Default); QCOMPARE(group.readEntry("entry2", NotDefault), NotDefault); // no default for entry2 config.setReadDefaults(false); QCOMPARE(group.readEntry("entry1", Default), Value1); QCOMPARE(group.readEntry("entry2", NotDefault), Value2); group.revertToDefault("entry1"); QCOMPARE(group.readEntry("entry1", QString()), Default); group.revertToDefault("entry2"); QCOMPARE(group.readEntry("entry2", QString()), QString()); // TODO test reverting localized entries Q_ASSERT(config.isDirty()); group.sync(); // Check that everything is OK on disk, too KConfig reader(TEST_SUBDIR "defaulttest", KConfig::NoGlobals); reader.addConfigSources(QStringList() << QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation) + '/' + defaultsFile); KConfigGroup readerGroup = reader.group("any group"); QCOMPARE(readerGroup.readEntry("entry1", QString()), Default); QCOMPARE(readerGroup.readEntry("entry2", QString()), QString()); } void KConfigTest::testLocale() { KConfig config(TEST_SUBDIR "kconfigtest.locales", KConfig::SimpleConfig); const QString Translated(TRANSLATEDSTRINGENTRY1); const QString Untranslated(STRINGENTRY1); KConfigGroup group = config.group("Hello"); group.writeEntry("stringEntry1", Untranslated); config.setLocale(QStringLiteral("fr")); group.writeEntry("stringEntry1", Translated, KConfig::Localized | KConfig::Persistent); QVERIFY(config.sync()); QCOMPARE(group.readEntry("stringEntry1", QString()), Translated); QCOMPARE(group.readEntryUntranslated("stringEntry1"), Untranslated); config.setLocale(QStringLiteral("C")); // strings written in the "C" locale are written as nonlocalized group.writeEntry("stringEntry1", Untranslated, KConfig::Localized | KConfig::Persistent); QVERIFY(config.sync()); QCOMPARE(group.readEntry("stringEntry1", QString()), Untranslated); } void KConfigTest::testEncoding() { QString groupstr = QString::fromUtf8("UTF-8:\xc3\xb6l"); KConfig c(TEST_SUBDIR "kconfigtestencodings"); KConfigGroup cg(&c, groupstr); cg.writeEntry("key", "value"); QVERIFY(c.sync()); QList lines = readLines(TEST_SUBDIR "kconfigtestencodings"); QCOMPARE(lines.count(), 2); QCOMPARE(lines.first(), QByteArray("[UTF-8:\xc3\xb6l]\n")); KConfig c2(TEST_SUBDIR "kconfigtestencodings"); KConfigGroup cg2(&c2, groupstr); QVERIFY(cg2.readEntry("key") == QByteArray("value")); QVERIFY(c2.groupList().contains(groupstr)); } void KConfigTest::testLists() { KConfig sc2(TEST_SUBDIR "kconfigtest"); KConfigGroup sc3(&sc2, "List Types"); QCOMPARE(sc3.readEntry(QString("stringListEntry"), QStringList()), STRINGLISTENTRY); QCOMPARE(sc3.readEntry(QString("stringListEmptyEntry"), QStringList("wrong")), STRINGLISTEMPTYENTRY); QCOMPARE(sc3.readEntry(QString("stringListJustEmptyElement"), QStringList()), STRINGLISTJUSTEMPTYELEMENT); QCOMPARE(sc3.readEntry(QString("stringListEmptyTrailingElement"), QStringList()), STRINGLISTEMPTYTRAINLINGELEMENT); QCOMPARE(sc3.readEntry(QString("stringListEscapeOddEntry"), QStringList()), STRINGLISTESCAPEODDENTRY); QCOMPARE(sc3.readEntry(QString("stringListEscapeEvenEntry"), QStringList()), STRINGLISTESCAPEEVENENTRY); QCOMPARE(sc3.readEntry(QString("stringListEscapeCommaEntry"), QStringList()), STRINGLISTESCAPECOMMAENTRY); QCOMPARE(sc3.readEntry("listOfIntsEntry1"), QString::fromLatin1("1,2,3,4")); QList expectedIntList = INTLISTENTRY1; QVERIFY(sc3.readEntry("listOfIntsEntry1", QList()) == expectedIntList); QCOMPARE(QVariant(sc3.readEntry("variantListEntry", VARIANTLISTENTRY)).toStringList(), QVariant(VARIANTLISTENTRY).toStringList()); QCOMPARE(sc3.readEntry("listOfByteArraysEntry1", QList()), BYTEARRAYLISTENTRY1); } void KConfigTest::testPath() { KConfig sc2(TEST_SUBDIR "kconfigtest"); KConfigGroup sc3(&sc2, "Path Type"); QCOMPARE(sc3.readPathEntry("homepath", QString()), HOMEPATH); QCOMPARE(sc3.readPathEntry("homepathescape", QString()), HOMEPATHESCAPE); QCOMPARE(sc3.entryMap()["homepath"], HOMEPATH); qputenv("WITHSLASH", "/a/"); { QFile file(testConfigDir() + "/pathtest"); file.open(QIODevice::WriteOnly | QIODevice::Text); QTextStream out(&file); out.setCodec("UTF-8"); - out << "[Test Group]" << endl - << "homePath=$HOME/foo" << endl - << "homePath2=file://$HOME/foo" << endl - << "withSlash=$WITHSLASH/foo" << endl - << "withSlash2=$WITHSLASH" << endl - << "withBraces[$e]=file://${HOME}/foo" << endl - << "URL[$e]=file://${HOME}/foo" << endl - << "hostname[$e]=$(hostname)" << endl - << "escapes=aaa,bb/b,ccc\\,ccc" << endl + out << "[Test Group]\n" + << "homePath=$HOME/foo\n" + << "homePath2=file://$HOME/foo\n" + << "withSlash=$WITHSLASH/foo\n" + << "withSlash2=$WITHSLASH\n" + << "withBraces[$e]=file://${HOME}/foo\n" + << "URL[$e]=file://${HOME}/foo\n" + << "hostname[$e]=$(hostname)\n" + << "escapes=aaa,bb/b,ccc\\,ccc\n" << "noeol=foo" // no EOL ; } KConfig cf2(TEST_SUBDIR "pathtest"); KConfigGroup group = cf2.group("Test Group"); QVERIFY(group.hasKey("homePath")); QCOMPARE(group.readPathEntry("homePath", QString()), HOMEPATH); QVERIFY(group.hasKey("homePath2")); QCOMPARE(group.readPathEntry("homePath2", QString()), QString("file://" + HOMEPATH)); QVERIFY(group.hasKey("withSlash")); QCOMPARE(group.readPathEntry("withSlash", QString()), QStringLiteral("/a//foo")); QVERIFY(group.hasKey("withSlash2")); QCOMPARE(group.readPathEntry("withSlash2", QString()), QStringLiteral("/a/")); QVERIFY(group.hasKey("withBraces")); QCOMPARE(group.readPathEntry("withBraces", QString()), QString("file://" + HOMEPATH)); QVERIFY(group.hasKey("URL")); QCOMPARE(group.readEntry("URL", QString()), QString("file://" + HOMEPATH)); QVERIFY(group.hasKey("hostname")); QCOMPARE(group.readEntry("hostname", QString()), QStringLiteral("(hostname)")); // the $ got removed because empty var name QVERIFY(group.hasKey("noeol")); QCOMPARE(group.readEntry("noeol", QString()), QString("foo")); const auto val = QStringList { QStringLiteral("aaa"), QStringLiteral("bb/b"), QStringLiteral("ccc,ccc")}; QCOMPARE(group.readPathEntry("escapes", QStringList()), val); } void KConfigTest::testPersistenceOfExpandFlagForPath() { // This test checks that a path entry starting with $HOME is still flagged // with the expand flag after the config was altered without rewriting the // path entry. // 1st step: Open the config, add a new dummy entry and then sync the config // back to the storage. { KConfig sc2(TEST_SUBDIR "kconfigtest"); KConfigGroup sc3(&sc2, "Path Type"); sc3.writeEntry("dummy", "dummy"); QVERIFY(sc2.sync()); } // 2nd step: Call testPath() again. Rewriting the config must not break // the testPath() test. testPath(); } void KConfigTest::testPathQtHome() { { QFile file(testConfigDir() + "/pathtest"); file.open(QIODevice::WriteOnly | QIODevice::Text); QTextStream out(&file); out.setCodec("UTF-8"); - out << "[Test Group]" << endl - << "dataDir[$e]=$QT_DATA_HOME/kconfigtest" << endl - << "cacheDir[$e]=$QT_CACHE_HOME/kconfigtest" << endl - << "configDir[$e]=$QT_CONFIG_HOME/kconfigtest" << endl; + out << "[Test Group]\n" + << "dataDir[$e]=$QT_DATA_HOME/kconfigtest\n" + << "cacheDir[$e]=$QT_CACHE_HOME/kconfigtest\n" + << "configDir[$e]=$QT_CONFIG_HOME/kconfigtest\n"; } KConfig cf2(TEST_SUBDIR "pathtest"); KConfigGroup group = cf2.group("Test Group"); qunsetenv("QT_DATA_HOME"); qunsetenv("QT_CACHE_HOME"); qunsetenv("QT_CONFIG_HOME"); QVERIFY(group.hasKey("dataDir")); QCOMPARE(group.readEntry("dataDir", QString()), QStandardPaths::writableLocation(QStandardPaths::GenericDataLocation).append(QStringLiteral("/kconfigtest"))); QVERIFY(group.hasKey("cacheDir")); QCOMPARE(group.readEntry("cacheDir", QString()), QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation).append(QStringLiteral("/kconfigtest"))); QVERIFY(group.hasKey("configDir")); QCOMPARE(group.readEntry("configDir", QString()), QStandardPaths::writableLocation(QStandardPaths::GenericConfigLocation).append(QStringLiteral("/kconfigtest"))); qputenv("QT_DATA_HOME","/1"); qputenv("QT_CACHE_HOME","/2"); qputenv("QT_CONFIG_HOME","/3"); QVERIFY(group.hasKey("dataDir")); QCOMPARE(group.readEntry("dataDir", QString()), QStringLiteral("/1/kconfigtest")); QVERIFY(group.hasKey("cacheDir")); QCOMPARE(group.readEntry("cacheDir", QString()), QStringLiteral("/2/kconfigtest")); QVERIFY(group.hasKey("configDir")); QCOMPARE(group.readEntry("configDir", QString()), QStringLiteral("/3/kconfigtest")); } void KConfigTest::testComplex() { KConfig sc2(TEST_SUBDIR "kconfigtest"); KConfigGroup sc3(&sc2, "Complex Types"); QCOMPARE(sc3.readEntry("pointEntry", QPoint()), POINTENTRY); QCOMPARE(sc3.readEntry("sizeEntry", SIZEENTRY), SIZEENTRY); QCOMPARE(sc3.readEntry("rectEntry", QRect(1, 2, 3, 4)), RECTENTRY); QCOMPARE(sc3.readEntry("dateTimeEntry", QDateTime()).toString(Qt::ISODate), DATETIMEENTRY.toString(Qt::ISODate)); QCOMPARE(sc3.readEntry("dateEntry", QDate()).toString(Qt::ISODate), DATETIMEENTRY.date().toString(Qt::ISODate)); QCOMPARE(sc3.readEntry("dateTimeEntry", QDate()), DATETIMEENTRY.date()); } void KConfigTest::testEnums() { //Visual C++ 2010 (compiler version 16.0) throws an Internal Compiler Error //when compiling the code in initTestCase that creates these KConfig entries, //so we can't run this test #if defined(_MSC_VER) && _MSC_VER == 1600 QSKIP("Visual C++ 2010 can't compile this test"); #endif KConfig sc(TEST_SUBDIR "kconfigtest"); KConfigGroup sc3(&sc, "Enum Types"); QCOMPARE(sc3.readEntry("enum-10"), QString("Tens")); QVERIFY(sc3.readEntry("enum-100", Ones) != Ones); QVERIFY(sc3.readEntry("enum-100", Ones) != Tens); QCOMPARE(sc3.readEntry("flags-bit0"), QString("bit0")); QVERIFY(sc3.readEntry("flags-bit0", Flags()) == bit0); int eid = staticMetaObject.indexOfEnumerator("Flags"); QVERIFY(eid != -1); QMetaEnum me = staticMetaObject.enumerator(eid); Flags bitfield = bit0 | bit1; QCOMPARE(sc3.readEntry("flags-bit0-bit1"), QString(me.valueToKeys(bitfield))); QVERIFY(sc3.readEntry("flags-bit0-bit1", Flags()) == bitfield); } void KConfigTest::testEntryMap() { KConfig sc(TEST_SUBDIR "kconfigtest"); KConfigGroup cg(&sc, "Hello"); QMap entryMap = cg.entryMap(); qDebug() << entryMap.keys(); QCOMPARE(entryMap.value("stringEntry1"), QString(STRINGENTRY1)); QCOMPARE(entryMap.value("stringEntry2"), QString(STRINGENTRY2)); QCOMPARE(entryMap.value("stringEntry3"), QString(STRINGENTRY3)); QCOMPARE(entryMap.value("stringEntry4"), QString(STRINGENTRY4)); QVERIFY(!entryMap.contains("stringEntry5")); QVERIFY(!entryMap.contains("stringEntry6")); QCOMPARE(entryMap.value("Test"), QString::fromUtf8(UTF8BITENTRY)); QCOMPARE(entryMap.value("bytearrayEntry"), QString::fromUtf8(BYTEARRAYENTRY.constData())); QCOMPARE(entryMap.value("emptyEntry"), QString()); QVERIFY(entryMap.contains("emptyEntry")); QCOMPARE(entryMap.value("boolEntry1"), QString(BOOLENTRY1 ? "true" : "false")); QCOMPARE(entryMap.value("boolEntry2"), QString(BOOLENTRY2 ? "true" : "false")); QCOMPARE(entryMap.value("keywith=equalsign"), QString(STRINGENTRY1)); QCOMPARE(entryMap.value("byteArrayEntry1"), QString(STRINGENTRY1)); QCOMPARE(entryMap.value("doubleEntry1").toDouble(), DOUBLEENTRY); QCOMPARE(entryMap.value("floatEntry1").toFloat(), FLOATENTRY); } void KConfigTest::testInvalid() { KConfig sc(TEST_SUBDIR "kconfigtest"); // all of these should print a message to the kdebug.dbg file KConfigGroup sc3(&sc, "Invalid Types"); sc3.writeEntry("badList", VARIANTLISTENTRY2); QList list; // 1 element list list << 1; sc3.writeEntry(QStringLiteral("badList"), list); QVERIFY(sc3.readEntry("badList", QPoint()) == QPoint()); QVERIFY(sc3.readEntry("badList", QRect()) == QRect()); QVERIFY(sc3.readEntry("badList", QSize()) == QSize()); QVERIFY(sc3.readEntry("badList", QDate()) == QDate()); QVERIFY(sc3.readEntry("badList", QDateTime()) == QDateTime()); // 2 element list list << 2; sc3.writeEntry("badList", list); QVERIFY(sc3.readEntry("badList", QRect()) == QRect()); QVERIFY(sc3.readEntry("badList", QDate()) == QDate()); QVERIFY(sc3.readEntry("badList", QDateTime()) == QDateTime()); // 3 element list list << 303; sc3.writeEntry("badList", list); QVERIFY(sc3.readEntry("badList", QPoint()) == QPoint()); QVERIFY(sc3.readEntry("badList", QRect()) == QRect()); QVERIFY(sc3.readEntry("badList", QSize()) == QSize()); QVERIFY(sc3.readEntry("badList", QDate()) == QDate()); // out of bounds QVERIFY(sc3.readEntry("badList", QDateTime()) == QDateTime()); // 4 element list list << 4; sc3.writeEntry("badList", list); QVERIFY(sc3.readEntry("badList", QPoint()) == QPoint()); QVERIFY(sc3.readEntry("badList", QSize()) == QSize()); QVERIFY(sc3.readEntry("badList", QDate()) == QDate()); QVERIFY(sc3.readEntry("badList", QDateTime()) == QDateTime()); // 5 element list list[2] = 3; list << 5; sc3.writeEntry("badList", list); QVERIFY(sc3.readEntry("badList", QPoint()) == QPoint()); QVERIFY(sc3.readEntry("badList", QRect()) == QRect()); QVERIFY(sc3.readEntry("badList", QSize()) == QSize()); QVERIFY(sc3.readEntry("badList", QDate()) == QDate()); QVERIFY(sc3.readEntry("badList", QDateTime()) == QDateTime()); // 6 element list list << 6; sc3.writeEntry("badList", list); QVERIFY(sc3.readEntry("badList", QPoint()) == QPoint()); QVERIFY(sc3.readEntry("badList", QRect()) == QRect()); QVERIFY(sc3.readEntry("badList", QSize()) == QSize()); } void KConfigTest::testChangeGroup() { KConfig sc(TEST_SUBDIR "kconfigtest"); KConfigGroup sc3(&sc, "Hello"); QCOMPARE(sc3.name(), QString("Hello")); KConfigGroup newGroup(sc3); #if KCONFIGCORE_ENABLE_DEPRECATED_SINCE(5, 0) newGroup.changeGroup("FooBar"); // deprecated! QCOMPARE(newGroup.name(), QString("FooBar")); QCOMPARE(sc3.name(), QString("Hello")); // unchanged // Write into the "changed group" and check that it works newGroup.writeEntry("InFooBar", "FB"); QCOMPARE(KConfigGroup(&sc, "FooBar").entryMap().value("InFooBar"), QString("FB")); QCOMPARE(KConfigGroup(&sc, "Hello").entryMap().value("InFooBar"), QString()); #endif KConfigGroup rootGroup(sc.group("")); QCOMPARE(rootGroup.name(), QString("")); KConfigGroup sc32(rootGroup.group("Hello")); QCOMPARE(sc32.name(), QString("Hello")); KConfigGroup newGroup2(sc32); #if KCONFIGCORE_ENABLE_DEPRECATED_SINCE(5, 0) newGroup2.changeGroup("FooBar"); // deprecated! QCOMPARE(newGroup2.name(), QString("FooBar")); QCOMPARE(sc32.name(), QString("Hello")); // unchanged #endif } // Simple test for deleteEntry void KConfigTest::testDeleteEntry() { const char *configFile = TEST_SUBDIR "kconfigdeletetest"; { KConfig conf(configFile); conf.group("Hello").writeEntry("DelKey", "ToBeDeleted"); } const QList lines = readLines(configFile); Q_ASSERT(lines.contains("[Hello]\n")); Q_ASSERT(lines.contains("DelKey=ToBeDeleted\n")); KConfig sc(configFile); KConfigGroup group(&sc, "Hello"); group.deleteEntry("DelKey"); QCOMPARE(group.readEntry("DelKey", QString("Fietsbel")), QString("Fietsbel")); QVERIFY(group.sync()); Q_ASSERT(!readLines(configFile).contains("DelKey=ToBeDeleted\n")); QCOMPARE(group.readEntry("DelKey", QString("still deleted")), QString("still deleted")); } void KConfigTest::testDelete() { KConfig sc(TEST_SUBDIR "kconfigtest"); KConfigGroup ct(&sc, "Complex Types"); // First delete a nested group KConfigGroup delgr(&ct, "Nested Group 3"); QVERIFY(delgr.exists()); QVERIFY(ct.hasGroup("Nested Group 3")); delgr.deleteGroup(); QVERIFY(!delgr.exists()); QVERIFY(!ct.hasGroup("Nested Group 3")); QVERIFY(ct.groupList().contains("Nested Group 3")); KConfigGroup ng(&ct, "Nested Group 2"); QVERIFY(sc.hasGroup("Complex Types")); QVERIFY(!sc.hasGroup("Does not exist")); sc.deleteGroup("Complex Types"); QCOMPARE(sc.group("Complex Types").keyList().count(), 0); QVERIFY(!sc.hasGroup("Complex Types")); // #192266 QVERIFY(!sc.group("Complex Types").exists()); QVERIFY(!ct.hasGroup("Nested Group 1")); QCOMPARE(ct.group("Nested Group 1").keyList().count(), 0); QCOMPARE(ct.group("Nested Group 2").keyList().count(), 0); QCOMPARE(ng.group("Nested Group 2.1").keyList().count(), 0); KConfigGroup cg(&sc, "AAA"); cg.deleteGroup(); QVERIFY(sc.entryMap("Complex Types").isEmpty()); QVERIFY(sc.entryMap("AAA").isEmpty()); QVERIFY(!sc.entryMap("Hello").isEmpty()); //not deleted group QVERIFY(sc.entryMap("FooBar").isEmpty()); //inexistant group QVERIFY(cg.sync()); // Check what happens on disk const QList lines = readLines(); //qDebug() << lines; QVERIFY(!lines.contains("[Complex Types]\n")); QVERIFY(!lines.contains("[Complex Types][Nested Group 1]\n")); QVERIFY(!lines.contains("[Complex Types][Nested Group 2]\n")); QVERIFY(!lines.contains("[Complex Types][Nested Group 2.1]\n")); QVERIFY(!lines.contains("[AAA]\n")); QVERIFY(lines.contains("[Hello]\n")); // a group that was not deleted // test for entries that are marked as deleted when there is no default KConfig cf(TEST_SUBDIR "kconfigtest", KConfig::SimpleConfig); // make sure there are no defaults cg = cf.group("Portable Devices"); cg.writeEntry("devices|manual|(null)", "whatever"); cg.writeEntry("devices|manual|/mnt/ipod", "/mnt/ipod"); QVERIFY(cf.sync()); int count = 0; const QList listLines = readLines(); for (const QByteArray &item : listLines) if (item.startsWith("devices|")) { // krazy:exclude=strings count++; } QCOMPARE(count, 2); cg.deleteEntry("devices|manual|/mnt/ipod"); QVERIFY(cf.sync()); const QList listLines2 = readLines(); for (const QByteArray &item : listLines2) { QVERIFY(!item.contains("ipod")); } } void KConfigTest::testDefaultGroup() { KConfig sc(TEST_SUBDIR "kconfigtest"); KConfigGroup defaultGroup(&sc, ""); QCOMPARE(defaultGroup.name(), QString("")); QVERIFY(!defaultGroup.exists()); defaultGroup.writeEntry("TestKey", "defaultGroup"); QVERIFY(defaultGroup.exists()); QCOMPARE(defaultGroup.readEntry("TestKey", QString()), QString("defaultGroup")); QVERIFY(sc.sync()); { // Test reading it KConfig sc2(TEST_SUBDIR "kconfigtest"); KConfigGroup defaultGroup2(&sc2, ""); QCOMPARE(defaultGroup2.name(), QString("")); QVERIFY(defaultGroup2.exists()); QCOMPARE(defaultGroup2.readEntry("TestKey", QString()), QString("defaultGroup")); } { // Test reading it KConfig sc2(TEST_SUBDIR "kconfigtest"); KConfigGroup emptyGroup(&sc2, ""); QCOMPARE(emptyGroup.name(), QString("")); QVERIFY(emptyGroup.exists()); QCOMPARE(emptyGroup.readEntry("TestKey", QString()), QString("defaultGroup")); } QList lines = readLines(); QVERIFY(!lines.contains("[]\n")); QCOMPARE(lines.first(), QByteArray("TestKey=defaultGroup\n")); // Now that the group exists make sure it isn't returned from groupList() const QStringList groupList = sc.groupList(); for (const QString &group : groupList) { QVERIFY(!group.isEmpty() && group != ""); } defaultGroup.deleteGroup(); QVERIFY(sc.sync()); // Test if deleteGroup worked lines = readLines(); QVERIFY(lines.first() != QByteArray("TestKey=defaultGroup\n")); } void KConfigTest::testEmptyGroup() { KConfig sc(TEST_SUBDIR "kconfigtest"); KConfigGroup emptyGroup(&sc, ""); QCOMPARE(emptyGroup.name(), QString("")); // confusing, heh? QVERIFY(!emptyGroup.exists()); emptyGroup.writeEntry("TestKey", "emptyGroup"); QVERIFY(emptyGroup.exists()); QCOMPARE(emptyGroup.readEntry("TestKey", QString()), QString("emptyGroup")); QVERIFY(sc.sync()); { // Test reading it KConfig sc2(TEST_SUBDIR "kconfigtest"); KConfigGroup defaultGroup(&sc2, ""); QCOMPARE(defaultGroup.name(), QString("")); QVERIFY(defaultGroup.exists()); QCOMPARE(defaultGroup.readEntry("TestKey", QString()), QString("emptyGroup")); } { // Test reading it KConfig sc2(TEST_SUBDIR "kconfigtest"); KConfigGroup emptyGroup2(&sc2, ""); QCOMPARE(emptyGroup2.name(), QString("")); QVERIFY(emptyGroup2.exists()); QCOMPARE(emptyGroup2.readEntry("TestKey", QString()), QString("emptyGroup")); } QList lines = readLines(); QVERIFY(!lines.contains("[]\n")); // there's no support for the [] group, in fact. QCOMPARE(lines.first(), QByteArray("TestKey=emptyGroup\n")); // Now that the group exists make sure it isn't returned from groupList() const QStringList groupList = sc.groupList(); for (const QString &group : groupList) { QVERIFY(!group.isEmpty() && group != ""); } emptyGroup.deleteGroup(); QVERIFY(sc.sync()); // Test if deleteGroup worked lines = readLines(); QVERIFY(lines.first() != QByteArray("TestKey=defaultGroup\n")); } #if defined(Q_OS_UNIX) && !defined(Q_OS_MAC) && !defined(Q_OS_BLACKBERRY) && !defined(Q_OS_ANDROID) #define Q_XDG_PLATFORM #endif void KConfigTest::testCascadingWithLocale() { // This test relies on XDG_CONFIG_DIRS, which only has effect on Unix. // Cascading (more than two levels) isn't available at all on Windows. #ifdef Q_XDG_PLATFORM QTemporaryDir middleDir; QTemporaryDir globalDir; qputenv("XDG_CONFIG_DIRS", qPrintable(middleDir.path() + QString(":") + globalDir.path())); const QString globalConfigDir = globalDir.path() + "/" TEST_SUBDIR; QVERIFY(QDir().mkpath(globalConfigDir)); QFile global(globalConfigDir + "foo.desktop"); QVERIFY(global.open(QIODevice::WriteOnly | QIODevice::Text)); QTextStream globalOut(&global); - globalOut << "[Group]" << endl - << "FromGlobal=true" << endl - << "FromGlobal[fr]=vrai" << endl - << "Name=Testing" << endl - << "Name[fr]=FR" << endl - << "Other=Global" << endl - << "Other[fr]=Global_FR" << endl; + globalOut << "[Group]\n" + << "FromGlobal=true\n" + << "FromGlobal[fr]=vrai\n" + << "Name=Testing\n" + << "Name[fr]=FR\n" + << "Other=Global\n" + << "Other[fr]=Global_FR\n"; global.close(); const QString middleConfigDir = middleDir.path() + "/" TEST_SUBDIR; QVERIFY(QDir().mkpath(middleConfigDir)); QFile local(middleConfigDir + "foo.desktop"); QVERIFY(local.open(QIODevice::WriteOnly | QIODevice::Text)); QTextStream out(&local); - out << "[Group]" << endl - << "FromLocal=true" << endl - << "FromLocal[fr]=vrai" << endl - << "Name=Local Testing" << endl - << "Name[fr]=FR" << endl - << "Other=English Only" << endl; + out << "[Group]\n" + << "FromLocal=true\n" + << "FromLocal[fr]=vrai\n" + << "Name=Local Testing\n" + << "Name[fr]=FR\n" + << "Other=English Only\n"; local.close(); KConfig config(TEST_SUBDIR "foo.desktop"); KConfigGroup group = config.group("Group"); QCOMPARE(group.readEntry("FromGlobal"), QString("true")); QCOMPARE(group.readEntry("FromLocal"), QString("true")); QCOMPARE(group.readEntry("Name"), QString("Local Testing")); config.setLocale(QStringLiteral("fr")); QCOMPARE(group.readEntry("FromGlobal"), QString("vrai")); QCOMPARE(group.readEntry("FromLocal"), QString("vrai")); QCOMPARE(group.readEntry("Name"), QString("FR")); QCOMPARE(group.readEntry("Other"), QString("English Only")); // Global_FR is locally overriden #endif } void KConfigTest::testMerge() { DefaultLocale defaultLocale; QLocale::setDefault(QLocale::c()); KConfig config(TEST_SUBDIR "mergetest", KConfig::SimpleConfig); KConfigGroup cg = config.group("some group"); cg.writeEntry("entry", " random entry"); cg.writeEntry("another entry", "blah blah blah"); { // simulate writing by another process QFile file(testConfigDir() + "/mergetest"); file.open(QIODevice::WriteOnly | QIODevice::Text); QTextStream out(&file); out.setCodec("UTF-8"); - out << "[Merged Group]" << endl - << "entry1=Testing" << endl - << "entry2=More Testing" << endl - << "[some group]" << endl - << "entry[fr]=French" << endl - << "entry[es]=Spanish" << endl - << "entry[de]=German" << endl; + out << "[Merged Group]\n" + << "entry1=Testing\n" + << "entry2=More Testing\n" + << "[some group]\n" + << "entry[fr]=French\n" + << "entry[es]=Spanish\n" + << "entry[de]=German\n"; } QVERIFY(config.sync()); { QList lines; // this is what the file should look like lines << "[Merged Group]\n" << "entry1=Testing\n" << "entry2=More Testing\n" << "\n" << "[some group]\n" << "another entry=blah blah blah\n" << "entry=\\srandom entry\n" << "entry[de]=German\n" << "entry[es]=Spanish\n" << "entry[fr]=French\n"; QFile file(testConfigDir() + "/mergetest"); file.open(QIODevice::ReadOnly | QIODevice::Text); for (const QByteArray &line : qAsConst(lines)) { QCOMPARE(line, file.readLine()); } } } void KConfigTest::testImmutable() { { QFile file(testConfigDir() + "/immutabletest"); file.open(QIODevice::WriteOnly | QIODevice::Text); QTextStream out(&file); out.setCodec("UTF-8"); - out << "[$i]" << endl - << "entry1=Testing" << endl - << "[group][$i]" << endl - << "[group][subgroup][$i]" << endl; + out << "[$i]\n" + << "entry1=Testing\n" + << "[group][$i]\n" + << "[group][subgroup][$i]\n"; } KConfig config(TEST_SUBDIR "immutabletest", KConfig::SimpleConfig); QVERIFY(config.isGroupImmutable(QByteArray())); KConfigGroup cg = config.group(QByteArray()); QVERIFY(cg.isEntryImmutable("entry1")); KConfigGroup cg1 = config.group("group"); QVERIFY(cg1.isImmutable()); KConfigGroup cg1a = cg.group("group"); QVERIFY(cg1a.isImmutable()); KConfigGroup cg2 = cg1.group("subgroup"); QVERIFY(cg2.isImmutable()); } void KConfigTest::testOptionOrder() { { QFile file(testConfigDir() + "/doubleattrtest"); file.open(QIODevice::WriteOnly | QIODevice::Text); QTextStream out(&file); out.setCodec("UTF-8"); - out << "[group3]" << endl - << "entry2=unlocalized" << endl - << "entry2[$i][de_DE]=t2" << endl; + out << "[group3]\n" + << "entry2=unlocalized\n" + << "entry2[$i][de_DE]=t2\n"; } KConfig config(TEST_SUBDIR "doubleattrtest", KConfig::SimpleConfig); config.setLocale(QStringLiteral("de_DE")); KConfigGroup cg3 = config.group("group3"); QVERIFY(!cg3.isImmutable()); QCOMPARE(cg3.readEntry("entry2", ""), QString("t2")); QVERIFY(cg3.isEntryImmutable("entry2")); config.setLocale(QStringLiteral("C")); QCOMPARE(cg3.readEntry("entry2", ""), QString("unlocalized")); QVERIFY(!cg3.isEntryImmutable("entry2")); cg3.writeEntry("entry2", "modified"); QVERIFY(config.sync()); { QList lines; // this is what the file should look like lines << "[group3]\n" << "entry2=modified\n" << "entry2[de_DE][$i]=t2\n"; QFile file(testConfigDir() + "/doubleattrtest"); file.open(QIODevice::ReadOnly | QIODevice::Text); for (const QByteArray &line : qAsConst(lines)) { QCOMPARE(line, file.readLine()); } } } void KConfigTest::testGroupEscape() { KConfig config(TEST_SUBDIR "groupescapetest", KConfig::SimpleConfig); QVERIFY(config.group(DOLLARGROUP).exists()); } void KConfigTest::testSubGroup() { KConfig sc(TEST_SUBDIR "kconfigtest"); KConfigGroup cg(&sc, "ParentGroup"); QCOMPARE(cg.readEntry("parentgrpstring", ""), QString("somevalue")); KConfigGroup subcg1(&cg, "SubGroup1"); QCOMPARE(subcg1.name(), QString("SubGroup1")); QCOMPARE(subcg1.readEntry("somestring", ""), QString("somevalue")); KConfigGroup subcg2(&cg, "SubGroup2"); QCOMPARE(subcg2.name(), QString("SubGroup2")); QCOMPARE(subcg2.readEntry("substring", ""), QString("somevalue")); KConfigGroup subcg3(&cg, "SubGroup/3"); QCOMPARE(subcg3.readEntry("sub3string", ""), QString("somevalue")); QCOMPARE(subcg3.name(), QString("SubGroup/3")); KConfigGroup rcg(&sc, ""); KConfigGroup srcg(&rcg, "ParentGroup"); QCOMPARE(srcg.readEntry("parentgrpstring", ""), QString("somevalue")); QStringList groupList = cg.groupList(); groupList.sort(); // comes from QSet, so order is undefined QCOMPARE(groupList, (QStringList() << "SubGroup/3" << "SubGroup1" << "SubGroup2")); const QStringList expectedSubgroup3Keys = (QStringList() << QStringLiteral("sub3string")); QCOMPARE(subcg3.keyList(), expectedSubgroup3Keys); const QStringList expectedParentGroupKeys(QStringList() << QStringLiteral("parentgrpstring")); QCOMPARE(cg.keyList(), expectedParentGroupKeys); QCOMPARE(QStringList(cg.entryMap().keys()), expectedParentGroupKeys); QCOMPARE(QStringList(subcg3.entryMap().keys()), expectedSubgroup3Keys); // Create A group containing only other groups. We want to make sure it // shows up in groupList of sc KConfigGroup neg(&sc, "NoEntryGroup"); KConfigGroup negsub1(&neg, "NEG Child1"); negsub1.writeEntry("entry", "somevalue"); KConfigGroup negsub2(&neg, "NEG Child2"); KConfigGroup negsub3(&neg, "NEG Child3"); KConfigGroup negsub31(&negsub3, "NEG Child3-1"); KConfigGroup negsub4(&neg, "NEG Child4"); KConfigGroup negsub41(&negsub4, "NEG Child4-1"); negsub41.writeEntry("entry", "somevalue"); // A group exists if it has content QVERIFY(negsub1.exists()); // But it doesn't exist if it has no content // Ossi and David say: this is how it's supposed to work. // However you could add a dummy entry for now, or we could add a "Persist" feature to kconfig groups // which would make it written out, much like "immutable" already makes them persistent. QVERIFY(!negsub2.exists()); // A subgroup does not qualify as content if it is also empty QVERIFY(!negsub3.exists()); // A subgroup with content is ok QVERIFY(negsub4.exists()); // Only subgroups with content show up in groupList() //QEXPECT_FAIL("", "Empty subgroups do not show up in groupList()", Continue); //QCOMPARE(neg.groupList(), QStringList() << "NEG Child1" << "NEG Child2" << "NEG Child3" << "NEG Child4"); // This is what happens QStringList groups = neg.groupList(); groups.sort(); // Qt5 made the ordering unreliable, due to QHash QCOMPARE(groups, QStringList() << "NEG Child1" << "NEG Child4"); // make sure groupList() isn't returning something it shouldn't const QStringList listGroup = sc.groupList(); for (const QString &group : listGroup) { QVERIFY(!group.isEmpty() && group != ""); QVERIFY(!group.contains(QChar(0x1d))); QVERIFY(!group.contains("subgroup")); QVERIFY(!group.contains("SubGroup")); } QVERIFY(sc.sync()); // Check that the empty groups are not written out. const QList lines = readLines(); QVERIFY(lines.contains("[NoEntryGroup][NEG Child1]\n")); QVERIFY(!lines.contains("[NoEntryGroup][NEG Child2]\n")); QVERIFY(!lines.contains("[NoEntryGroup][NEG Child3]\n")); QVERIFY(!lines.contains("[NoEntryGroup][NEG Child4]\n")); // implicit group, not written out QVERIFY(lines.contains("[NoEntryGroup][NEG Child4][NEG Child4-1]\n")); } void KConfigTest::testAddConfigSources() { KConfig cf(TEST_SUBDIR "specificrc"); cf.addConfigSources(QStringList() << testConfigDir() + "/baserc"); cf.reparseConfiguration(); KConfigGroup specificgrp(&cf, "Specific Only Group"); QCOMPARE(specificgrp.readEntry("ExistingEntry", ""), QString("DevValue")); KConfigGroup sharedgrp(&cf, "Shared Group"); QCOMPARE(sharedgrp.readEntry("SomeSpecificOnlyEntry", ""), QString("DevValue")); QCOMPARE(sharedgrp.readEntry("SomeBaseOnlyEntry", ""), QString("BaseValue")); QCOMPARE(sharedgrp.readEntry("SomeSharedEntry", ""), QString("DevValue")); KConfigGroup basegrp(&cf, "Base Only Group"); QCOMPARE(basegrp.readEntry("ExistingEntry", ""), QString("BaseValue")); basegrp.writeEntry("New Entry Base Only", "SomeValue"); KConfigGroup newgrp(&cf, "New Group"); newgrp.writeEntry("New Entry", "SomeValue"); QVERIFY(cf.sync()); KConfig plaincfg(TEST_SUBDIR "specificrc"); KConfigGroup newgrp2(&plaincfg, "New Group"); QCOMPARE(newgrp2.readEntry("New Entry", ""), QString("SomeValue")); KConfigGroup basegrp2(&plaincfg, "Base Only Group"); QCOMPARE(basegrp2.readEntry("New Entry Base Only", ""), QString("SomeValue")); } void KConfigTest::testGroupCopyTo() { KConfig cf1(TEST_SUBDIR "kconfigtest"); KConfigGroup original = cf1.group("Enum Types"); KConfigGroup copy = cf1.group("Enum Types Copy"); original.copyTo(©); // copy from one group to another QCOMPARE(copy.entryMap(), original.entryMap()); KConfig cf2(TEST_SUBDIR "copy_of_kconfigtest", KConfig::SimpleConfig); QVERIFY(!cf2.hasGroup(original.name())); QVERIFY(!cf2.hasGroup(copy.name())); KConfigGroup newGroup = cf2.group(original.name()); original.copyTo(&newGroup); // copy from one file to another QVERIFY(cf2.hasGroup(original.name())); QVERIFY(!cf2.hasGroup(copy.name())); // make sure we didn't copy more than we wanted QCOMPARE(newGroup.entryMap(), original.entryMap()); } void KConfigTest::testConfigCopyToSync() { KConfig cf1(TEST_SUBDIR "kconfigtest"); // Prepare source file KConfigGroup group(&cf1, "CopyToTest"); group.writeEntry("Type", "Test"); QVERIFY(cf1.sync()); // Copy to "destination" const QString destination = testConfigDir() + "/kconfigcopytotest"; QFile::remove(destination); KConfig cf2(TEST_SUBDIR "kconfigcopytotest"); KConfigGroup group2(&cf2, "CopyToTest"); group.copyTo(&group2); QString testVal = group2.readEntry("Type"); QCOMPARE(testVal, QString("Test")); // should write to disk the copied data from group QVERIFY(cf2.sync()); QVERIFY(QFile::exists(destination)); } void KConfigTest::testConfigCopyTo() { KConfig cf1(TEST_SUBDIR "kconfigtest"); { // Prepare source file KConfigGroup group(&cf1, "CopyToTest"); group.writeEntry("Type", "Test"); QVERIFY(cf1.sync()); } { // Copy to "destination" const QString destination = testConfigDir() + "/kconfigcopytotest"; QFile::remove(destination); KConfig cf2; cf1.copyTo(destination, &cf2); KConfigGroup group2(&cf2, "CopyToTest"); QString testVal = group2.readEntry("Type"); QCOMPARE(testVal, QString("Test")); QVERIFY(cf2.sync()); QVERIFY(QFile::exists(destination)); } // Check copied config file on disk KConfig cf3(TEST_SUBDIR "kconfigcopytotest"); KConfigGroup group3(&cf3, "CopyToTest"); QString testVal = group3.readEntry("Type"); QCOMPARE(testVal, QString("Test")); } void KConfigTest::testReparent() { KConfig cf(TEST_SUBDIR "kconfigtest"); const QString name(QStringLiteral("Enum Types")); KConfigGroup group = cf.group(name); const QMap originalMap = group.entryMap(); KConfigGroup parent = cf.group("Parent Group"); QVERIFY(!parent.hasGroup(name)); QVERIFY(group.entryMap() == originalMap); group.reparent(&parent); // see if it can be made a sub-group of another group QVERIFY(parent.hasGroup(name)); QCOMPARE(group.entryMap(), originalMap); group.reparent(&cf); // see if it can make it a top-level group again // QVERIFY(!parent.hasGroup(name)); QCOMPARE(group.entryMap(), originalMap); } static void ageTimeStamp(const QString &path, int nsec) { #ifdef Q_OS_UNIX QDateTime mtime = QFileInfo(path).lastModified().addSecs(-nsec); struct utimbuf utbuf; utbuf.actime = mtime.toSecsSinceEpoch(); utbuf.modtime = utbuf.actime; utime(QFile::encodeName(path), &utbuf); #else QTest::qSleep(nsec * 1000); #endif } void KConfigTest::testWriteOnSync() { QDateTime oldStamp, newStamp; KConfig sc(TEST_SUBDIR "kconfigtest", KConfig::IncludeGlobals); // Age the timestamp of global config file a few sec, and collect it. QString globFile = kdeGlobalsPath(); ageTimeStamp(globFile, 2); // age 2 sec oldStamp = QFileInfo(globFile).lastModified(); // Add a local entry and sync the config. // Should not rewrite the global config file. KConfigGroup cgLocal(&sc, "Locals"); cgLocal.writeEntry("someLocalString", "whatever"); QVERIFY(sc.sync()); // Verify that the timestamp of global config file didn't change. newStamp = QFileInfo(globFile).lastModified(); QCOMPARE(newStamp, oldStamp); // Age the timestamp of local config file a few sec, and collect it. QString locFile = testConfigDir() + "/kconfigtest"; ageTimeStamp(locFile, 2); // age 2 sec oldStamp = QFileInfo(locFile).lastModified(); // Add a global entry and sync the config. // Should not rewrite the local config file. KConfigGroup cgGlobal(&sc, "Globals"); cgGlobal.writeEntry("someGlobalString", "whatever", KConfig::Persistent | KConfig::Global); QVERIFY(sc.sync()); // Verify that the timestamp of local config file didn't change. newStamp = QFileInfo(locFile).lastModified(); QCOMPARE(newStamp, oldStamp); } void KConfigTest::testFailOnReadOnlyFileSync() { KConfig sc(TEST_SUBDIR "kconfigfailonreadonlytest"); KConfigGroup cgLocal(&sc, "Locals"); cgLocal.writeEntry("someLocalString", "whatever"); QVERIFY(cgLocal.sync()); QFile f(testConfigDir() + "kconfigfailonreadonlytest"); QVERIFY(f.exists()); QVERIFY(f.setPermissions(QFileDevice::ReadOwner)); #ifndef Q_OS_WIN if (::getuid() == 0) QSKIP("Root can write to read-only files"); #endif cgLocal.writeEntry("someLocalString", "whatever2"); QVERIFY(!cgLocal.sync()); QVERIFY(f.setPermissions(QFileDevice::ReadOwner | QFileDevice::WriteOwner)); QVERIFY(f.remove()); } void KConfigTest::testDirtyOnEqual() { QDateTime oldStamp, newStamp; KConfig sc(TEST_SUBDIR "kconfigtest"); // Initialize value KConfigGroup cgLocal(&sc, "random"); cgLocal.writeEntry("theKey", "whatever"); QVERIFY(sc.sync()); // Age the timestamp of local config file a few sec, and collect it. QString locFile = testConfigDir() + "/kconfigtest"; ageTimeStamp(locFile, 2); // age 2 sec oldStamp = QFileInfo(locFile).lastModified(); // Write exactly the same again cgLocal.writeEntry("theKey", "whatever"); // This should be a no-op QVERIFY(sc.sync()); // Verify that the timestamp of local config file didn't change. newStamp = QFileInfo(locFile).lastModified(); QCOMPARE(newStamp, oldStamp); } void KConfigTest::testDirtyOnEqualOverdo() { QByteArray val1("\0""one", 4); QByteArray val2("\0""two", 4); QByteArray defvalr; KConfig sc(TEST_SUBDIR "kconfigtest"); KConfigGroup cgLocal(&sc, "random"); cgLocal.writeEntry("someKey", val1); QCOMPARE(cgLocal.readEntry("someKey", defvalr), val1); cgLocal.writeEntry("someKey", val2); QCOMPARE(cgLocal.readEntry("someKey", defvalr), val2); } void KConfigTest::testCreateDir() { // Test auto-creating the parent directory when needed (KConfigIniBackend::createEnclosing) QString kdehome = QDir::home().canonicalPath() + "/.kde-unit-test"; QString subdir = kdehome + "/newsubdir"; QString file = subdir + "/foo.desktop"; QFile::remove(file); QDir().rmdir(subdir); QVERIFY(!QDir().exists(subdir)); KDesktopFile desktopFile(file); desktopFile.desktopGroup().writeEntry("key", "value"); QVERIFY(desktopFile.sync()); QVERIFY(QFile::exists(file)); // Cleanup QFile::remove(file); QDir().rmdir(subdir); } void KConfigTest::testSyncOnExit() { // Often, the KGlobalPrivate global static's destructor ends up calling ~KConfig -> // KConfig::sync ... and if that code triggers KGlobal code again then things could crash. // So here's a test for modifying KSharedConfig::openConfig() and not syncing, the process exit will sync. KConfigGroup grp(KSharedConfig::openConfig(TEST_SUBDIR "syncOnExitRc"), "syncOnExit"); grp.writeEntry("key", "value"); } void KConfigTest::testSharedConfig() { // Can I use a KConfigGroup even after the KSharedConfigPtr goes out of scope? KConfigGroup myConfigGroup; { KSharedConfigPtr config = KSharedConfig::openConfig(TEST_SUBDIR "kconfigtest"); myConfigGroup = KConfigGroup(config, "Hello"); } QCOMPARE(myConfigGroup.readEntry("stringEntry1"), QString(STRINGENTRY1)); // Get the main config KSharedConfigPtr mainConfig = KSharedConfig::openConfig(); KConfigGroup mainGroup(mainConfig, "Main"); QCOMPARE(mainGroup.readEntry("Key", QString()), QString("Value")); } void KConfigTest::testLocaleConfig() { // Initialize the testdata QDir dir; QString subdir = testConfigDir(); dir.mkpath(subdir); QString file = subdir + "/localized.test"; QFile::remove(file); QFile f(file); QVERIFY(f.open(QIODevice::WriteOnly)); QTextStream ts(&f); ts << "[Test_Wrong]\n"; ts << "foo[ca]=5\n"; ts << "foostring[ca]=nice\n"; ts << "foobool[ca]=true\n"; ts << "[Test_Right]\n"; ts << "foo=5\n"; ts << "foo[ca]=5\n"; ts << "foostring=primary\n"; ts << "foostring[ca]=nice\n"; ts << "foobool=primary\n"; ts << "foobool[ca]=true\n"; f.close(); // Load the testdata QVERIFY(QFile::exists(file)); KConfig config(file); config.setLocale(QStringLiteral("ca")); // This group has only localized values. That is not supported. The values // should be dropped on loading. KConfigGroup cg(&config, "Test_Wrong"); QEXPECT_FAIL("", "The localized values are not dropped", Continue); QVERIFY(!cg.hasKey("foo")); QEXPECT_FAIL("", "The localized values are not dropped", Continue); QVERIFY(!cg.hasKey("foostring")); QEXPECT_FAIL("", "The localized values are not dropped", Continue); QVERIFY(!cg.hasKey("foobool")); // Now check the correct config group KConfigGroup cg2(&config, "Test_Right"); QCOMPARE(cg2.readEntry("foo"), QString("5")); QCOMPARE(cg2.readEntry("foo", 3), 5); QCOMPARE(cg2.readEntry("foostring"), QString("nice")); QCOMPARE(cg2.readEntry("foostring", "ugly"), QString("nice")); QCOMPARE(cg2.readEntry("foobool"), QString("true")); QCOMPARE(cg2.readEntry("foobool", false), true); // Clean up after the testcase QFile::remove(file); } void KConfigTest::testDeleteWhenLocalized() { // Initialize the testdata QDir dir; QString subdir = QDir::home().canonicalPath() + "/.kde-unit-test/"; dir.mkpath(subdir); QString file = subdir + "/localized_delete.test"; QFile::remove(file); QFile f(file); QVERIFY(f.open(QIODevice::WriteOnly)); QTextStream ts(&f); ts << "[Test4711]\n"; ts << "foo=3\n"; ts << "foo[ca]=5\n"; ts << "foo[de]=7\n"; ts << "foostring=ugly\n"; ts << "foostring[ca]=nice\n"; ts << "foostring[de]=schoen\n"; ts << "foobool=false\n"; ts << "foobool[ca]=true\n"; ts << "foobool[de]=true\n"; f.close(); // Load the testdata. We start in locale "ca". QVERIFY(QFile::exists(file)); KConfig config(file); config.setLocale(QStringLiteral("ca")); KConfigGroup cg(&config, "Test4711"); // Delete a value. Once with localized, once with Normal cg.deleteEntry("foostring", KConfigBase::Persistent | KConfigBase::Localized); cg.deleteEntry("foobool"); QVERIFY(config.sync()); // The value is now gone. The others are still there. Everything correct // here. QVERIFY(!cg.hasKey("foostring")); QVERIFY(!cg.hasKey("foobool")); QVERIFY(cg.hasKey("foo")); // The current state is: (Just return before this comment.) // [...] // foobool[ca]=true // foobool[de]=wahr // foostring=ugly // foostring[de]=schoen // Now switch the locale to "de" and repeat the checks. Results should be // the same. But they currently are not. The localized value are // independent of each other. All values are still there in "de". config.setLocale(QStringLiteral("de")); QEXPECT_FAIL("", "Currently localized values are not deleted correctly", Continue); QVERIFY(!cg.hasKey("foostring")); QEXPECT_FAIL("", "Currently localized values are not deleted correctly", Continue); QVERIFY(!cg.hasKey("foobool")); QVERIFY(cg.hasKey("foo")); // Check where the wrong values come from. // We get the "de" value. QCOMPARE(cg.readEntry("foostring", "nothing"), QString("schoen")); // We get the "de" value. QCOMPARE(cg.readEntry("foobool", false), true); // Now switch the locale back "ca" and repeat the checks. Results are // again different. config.setLocale(QStringLiteral("ca")); // This line worked above. But now it fails. QEXPECT_FAIL("", "Currently localized values are not deleted correctly", Continue); QVERIFY(!cg.hasKey("foostring")); // This line worked above too. QEXPECT_FAIL("", "Currently localized values are not deleted correctly", Continue); QVERIFY(!cg.hasKey("foobool")); QVERIFY(cg.hasKey("foo")); // Check where the wrong values come from. // We get the primary value because the "ca" value was deleted. QCOMPARE(cg.readEntry("foostring", "nothing"), QString("ugly")); // We get the "ca" value. QCOMPARE(cg.readEntry("foobool", false), true); // Now test the deletion of a group. cg.deleteGroup(); QVERIFY(config.sync()); // Current state: [ca] and [de] entries left... oops. //qDebug() << readLinesFrom(file); // Bug: The group still exists [because of the localized entries]... QVERIFY(cg.exists()); QVERIFY(!cg.hasKey("foo")); QVERIFY(!cg.hasKey("foostring")); QEXPECT_FAIL("", "Currently localized values are not deleted correctly", Continue); QVERIFY(!cg.hasKey("foobool")); // Now switch the locale to "de" and repeat the checks. All values // still here because only the primary values are deleted. config.setLocale(QStringLiteral("de")); QEXPECT_FAIL("", "Currently localized values are not deleted correctly", Continue); QVERIFY(!cg.hasKey("foo")); QEXPECT_FAIL("", "Currently localized values are not deleted correctly", Continue); QVERIFY(!cg.hasKey("foostring")); QEXPECT_FAIL("", "Currently localized values are not deleted correctly", Continue); QVERIFY(!cg.hasKey("foobool")); // Check where the wrong values come from. // We get the "de" value. QCOMPARE(cg.readEntry("foostring", "nothing"), QString("schoen")); // We get the "de" value. QCOMPARE(cg.readEntry("foobool", false), true); // We get the "de" value. QCOMPARE(cg.readEntry("foo", 0), 7); // Now switch the locale to "ca" and repeat the checks // "foostring" is now really gone because both the primary value and the // "ca" value are deleted. config.setLocale(QStringLiteral("ca")); QEXPECT_FAIL("", "Currently localized values are not deleted correctly", Continue); QVERIFY(!cg.hasKey("foo")); QVERIFY(!cg.hasKey("foostring")); QEXPECT_FAIL("", "Currently localized values are not deleted correctly", Continue); QVERIFY(!cg.hasKey("foobool")); // Check where the wrong values come from. // We get the "ca" value. QCOMPARE(cg.readEntry("foobool", false), true); // We get the "ca" value. QCOMPARE(cg.readEntry("foo", 0), 5); // Cleanup QFile::remove(file); } void KConfigTest::testKdeGlobals() { { KConfig glob(QStringLiteral("kdeglobals")); KConfigGroup general(&glob, "General"); general.writeEntry("testKG", "1"); QVERIFY(glob.sync()); } KConfig globRead(QStringLiteral("kdeglobals")); const KConfigGroup general(&globRead, "General"); QCOMPARE(general.readEntry("testKG"), QString("1")); // Check we wrote into kdeglobals const QList lines = readLines("kdeglobals"); QVERIFY(lines.contains("[General]\n")); QVERIFY(lines.contains("testKG=1\n")); // Writing using NoGlobals { KConfig glob(QStringLiteral("kdeglobals"), KConfig::NoGlobals); KConfigGroup general(&glob, "General"); general.writeEntry("testKG", "2"); QVERIFY(glob.sync()); } globRead.reparseConfiguration(); QCOMPARE(general.readEntry("testKG"), QString("2")); // Reading using NoGlobals { KConfig globReadNoGlob(QStringLiteral("kdeglobals"), KConfig::NoGlobals); const KConfigGroup generalNoGlob(&globReadNoGlob, "General"); QCOMPARE(generalNoGlob.readEntry("testKG"), QString("2")); } // TODO now use kconfigtest and writeEntry(,Global) -> should go into kdeglobals } void KConfigTest::testAnonymousConfig() { KConfig anonConfig(QString(), KConfig::SimpleConfig); KConfigGroup general(&anonConfig, "General"); QCOMPARE(general.readEntry("testKG"), QString()); // no kdeglobals merging general.writeEntry("Foo", "Bar"); QCOMPARE(general.readEntry("Foo"), QString("Bar")); } void KConfigTest::testQByteArrayUtf8() { QTemporaryFile file; QVERIFY(file.open()); KConfig config(file.fileName(), KConfig::SimpleConfig); KConfigGroup general(&config, "General"); QByteArray bytes(256, '\0'); for (int i = 0; i < 256; i++) { bytes[i] = i; } general.writeEntry("Utf8", bytes); config.sync(); file.flush(); file.close(); QFile readFile(file.fileName()); QVERIFY(readFile.open(QFile::ReadOnly)); #define VALUE "Utf8=\\x00\\x01\\x02\\x03\\x04\\x05\\x06\\x07\\x08\\t\\n\\x0b\\x0c\\r\\x0e\\x0f\\x10\\x11\\x12\\x13\\x14\\x15\\x16\\x17\\x18\\x19\\x1a\\x1b\\x1c\\x1d\\x1e\\x1f !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\\\]^_`abcdefghijklmnopqrstuvwxyz{|}~\\x7f\\x80\\x81\\x82\\x83\\x84\\x85\\x86\\x87\\x88\\x89\\x8a\\x8b\\x8c\\x8d\\x8e\\x8f\\x90\\x91\\x92\\x93\\x94\\x95\\x96\\x97\\x98\\x99\\x9a\\x9b\\x9c\\x9d\\x9e\\x9f\\xa0\\xa1\\xa2\\xa3\\xa4\\xa5\\xa6\\xa7\\xa8\\xa9\\xaa\\xab\\xac\\xad\\xae\\xaf\\xb0\\xb1\\xb2\\xb3\\xb4\\xb5\\xb6\\xb7\\xb8\\xb9\\xba\\xbb\\xbc\\xbd\\xbe\\xbf\\xc0\\xc1\\xc2\\xc3\\xc4\\xc5\\xc6\\xc7\\xc8\\xc9\\xca\\xcb\\xcc\\xcd\\xce\\xcf\\xd0\\xd1\\xd2\\xd3\\xd4\\xd5\\xd6\\xd7\\xd8\\xd9\\xda\\xdb\\xdc\\xdd\\xde\\xdf\\xe0\\xe1\\xe2\\xe3\\xe4\\xe5\\xe6\\xe7\\xe8\\xe9\\xea\\xeb\\xec\\xed\\xee\\xef\\xf0\\xf1\\xf2\\xf3\\xf4\\xf5\\xf6\\xf7\\xf8\\xf9\\xfa\\xfb\\xfc\\xfd\\xfe\\xff" const QByteArray fileBytes = readFile.readAll(); #ifndef Q_OS_WIN QCOMPARE(fileBytes, QByteArrayLiteral("[General]\n" VALUE "\n")); #else QCOMPARE(fileBytes, QByteArrayLiteral("[General]\r\n" VALUE "\r\n")); #endif #undef VALUE // check that reading works KConfig config2(file.fileName(), KConfig::SimpleConfig); KConfigGroup general2(&config2, "General"); QCOMPARE(bytes, general2.readEntry("Utf8", QByteArray())); } void KConfigTest::testQStringUtf8_data() { QTest::addColumn("data"); QTest::newRow("1") << QByteArray("Téléchargements\tTéléchargements"); QTest::newRow("2") << QByteArray("$¢ह€𐍈\t$¢ह€𐍈"); QTest::newRow("3") << QByteArray("\xc2\xe0\xa4\xf0\x90\x8d\t\\xc2\\xe0\\xa4\\xf0\\x90\\x8d"); // 2 byte overlong QTest::newRow("4") << QByteArray("\xc1\xbf\t\\xc1\\xbf"); // 3 byte overlong QTest::newRow("5") << QByteArray("\xe0\x9f\xbf\t\\xe0\\x9f\\xbf"); // 4 byte overlong QTest::newRow("6") << QByteArray("\xf0\x8f\xbf\xbf\t\\xf0\\x8f\\xbf\\xbf"); // outside unicode range QTest::newRow("7") << QByteArray("\xf4\x90\x80\x80\t\\xf4\\x90\\x80\\x80"); // just within range QTest::newRow("8") << QByteArray("\xc2\x80\t\xc2\x80"); QTest::newRow("9") << QByteArray("\xe0\xa0\x80\t\xe0\xa0\x80"); QTest::newRow("10") << QByteArray("\xf0\x90\x80\x80\t\xf0\x90\x80\x80"); QTest::newRow("11") << QByteArray("\xf4\x8f\xbf\xbf\t\xf4\x8f\xbf\xbf"); } void KConfigTest::testQStringUtf8() { QFETCH(QByteArray, data); const QList d = data.split('\t'); const QByteArray value = d[0]; const QByteArray serialized = d[1]; QTemporaryFile file; QVERIFY(file.open()); KConfig config(file.fileName(), KConfig::SimpleConfig); KConfigGroup general(&config, "General"); general.writeEntry("key", value); config.sync(); file.flush(); file.close(); QFile readFile(file.fileName()); QVERIFY(readFile.open(QFile::ReadOnly)); QByteArray fileBytes = readFile.readAll(); #ifdef Q_OS_WIN fileBytes.replace("\r\n", "\n"); #endif QCOMPARE(fileBytes, QByteArrayLiteral("[General]\nkey=") + serialized + QByteArrayLiteral("\n")); // check that reading works KConfig config2(file.fileName(), KConfig::SimpleConfig); KConfigGroup general2(&config2, "General"); QCOMPARE(value, general2.readEntry("key", QByteArray())); } void KConfigTest::testNewlines() { // test that kconfig always uses the native line endings QTemporaryFile file; QVERIFY(file.open()); KConfig anonConfig(file.fileName(), KConfig::SimpleConfig); KConfigGroup general(&anonConfig, "General"); general.writeEntry("Foo", "Bar"); general.writeEntry("Bar", "Foo"); anonConfig.sync(); file.flush(); file.close(); QFile readFile(file.fileName()); QVERIFY(readFile.open(QFile::ReadOnly)); #ifndef Q_OS_WIN QCOMPARE(readFile.readAll(), QByteArrayLiteral("[General]\nBar=Foo\nFoo=Bar\n")); #else QCOMPARE(readFile.readAll(), QByteArrayLiteral("[General]\r\nBar=Foo\r\nFoo=Bar\r\n")); #endif } void KConfigTest::testXdgListEntry() { QTemporaryFile file; QVERIFY(file.open()); QTextStream out(&file); - out << "[General]" << endl - << "Key1=" << endl // empty list + out << "[General]\n" + << "Key1=\n" // empty list // emtpty entries - << "Key2=;" << endl - << "Key3=;;" << endl - << "Key4=;;;" << endl - << "Key5=\\;" << endl - << "Key6=1;2\\;3;;" << endl; + << "Key2=;\n" + << "Key3=;;\n" + << "Key4=;;;\n" + << "Key5=\\;\n" + << "Key6=1;2\\;3;;\n"; out.flush(); file.close(); KConfig anonConfig(file.fileName(), KConfig::SimpleConfig); KConfigGroup grp = anonConfig.group("General"); QStringList invalidList; // use this as a default when an empty list is expected invalidList << QStringLiteral("Error! Default value read!"); QCOMPARE(grp.readXdgListEntry("Key1", invalidList), QStringList()); QCOMPARE(grp.readXdgListEntry("Key2", invalidList), QStringList() << QString()); QCOMPARE(grp.readXdgListEntry("Key3", invalidList), QStringList() << QString() << QString()); QCOMPARE(grp.readXdgListEntry("Key4", invalidList), QStringList()<< QString() << QString() << QString()); QCOMPARE(grp.readXdgListEntry("Key5", invalidList), QStringList() << ";"); QCOMPARE(grp.readXdgListEntry("Key6", invalidList), QStringList() << "1" << "2;3" << QString()); } #include #include // To find multithreading bugs: valgrind --tool=helgrind --track-lockorders=no ./kconfigtest testThreads void KConfigTest::testThreads() { QThreadPool::globalInstance()->setMaxThreadCount(6); QList > futures; // Run in parallel some tests that work on different config files, // otherwise unexpected things might indeed happen. futures << QtConcurrent::run(this, &KConfigTest::testAddConfigSources); futures << QtConcurrent::run(this, &KConfigTest::testSimple); futures << QtConcurrent::run(this, &KConfigTest::testDefaults); futures << QtConcurrent::run(this, &KConfigTest::testSharedConfig); futures << QtConcurrent::run(this, &KConfigTest::testSharedConfig); // QEXPECT_FAIL triggers race conditions, it should be fixed to use QThreadStorage... //futures << QtConcurrent::run(this, &KConfigTest::testDeleteWhenLocalized); //futures << QtConcurrent::run(this, &KConfigTest::testEntryMap); for (QFuture f : qAsConst(futures)) { // krazy:exclude=foreach f.waitForFinished(); } } void KConfigTest::testNotify() { #if !KCONFIG_USE_DBUS QSKIP("KConfig notification requires DBus"); #endif KConfig config(TEST_SUBDIR "kconfigtest"); auto myConfigGroup = KConfigGroup(&config, "TopLevelGroup"); //mimics a config in another process, which is watching for events auto remoteConfig = KSharedConfig::openConfig(TEST_SUBDIR "kconfigtest"); KConfigWatcher::Ptr watcher = KConfigWatcher::create(remoteConfig); //some random config that shouldn't be changing when kconfigtest changes, only on kdeglobals auto otherRemoteConfig = KSharedConfig::openConfig(TEST_SUBDIR "kconfigtest2"); KConfigWatcher::Ptr otherWatcher = KConfigWatcher::create(otherRemoteConfig); QSignalSpy watcherSpy(watcher.data(), &KConfigWatcher::configChanged); QSignalSpy otherWatcherSpy(otherWatcher.data(), &KConfigWatcher::configChanged); //write entries in a group and subgroup myConfigGroup.writeEntry("entryA", "foo", KConfig::Persistent | KConfig::Notify); auto subGroup = myConfigGroup.group("aSubGroup"); subGroup.writeEntry("entry1", "foo", KConfig::Persistent | KConfig::Notify); subGroup.writeEntry("entry2", "foo", KConfig::Persistent | KConfig::Notify); config.sync(); watcherSpy.wait(); QCOMPARE(watcherSpy.count(), 2); std::sort(watcherSpy.begin(), watcherSpy.end(), [] (QList a, QList b) { return a[0].value().name() < b[0].value().name(); }); QCOMPARE(watcherSpy[0][0].value().name(), QStringLiteral("TopLevelGroup")); QCOMPARE(watcherSpy[0][1].value(), QByteArrayList({"entryA"})); QCOMPARE(watcherSpy[1][0].value().name(), QStringLiteral("aSubGroup")); QCOMPARE(watcherSpy[1][0].value().parent().name(), QStringLiteral("TopLevelGroup")); QCOMPARE(watcherSpy[1][1].value(), QByteArrayList({"entry1", "entry2"})); //delete an entry watcherSpy.clear(); myConfigGroup.deleteEntry("entryA", KConfig::Persistent | KConfig::Notify); config.sync(); watcherSpy.wait(); QCOMPARE(watcherSpy.count(), 1); QCOMPARE(watcherSpy[0][0].value().name(), QStringLiteral("TopLevelGroup")); QCOMPARE(watcherSpy[0][1].value(), QByteArrayList({"entryA"})); //revert to default an entry watcherSpy.clear(); myConfigGroup.revertToDefault("entryA", KConfig::Persistent | KConfig::Notify); config.sync(); watcherSpy.wait(); QCOMPARE(watcherSpy.count(), 1); QCOMPARE(watcherSpy[0][0].value().name(), QStringLiteral("TopLevelGroup")); QCOMPARE(watcherSpy[0][1].value(), QByteArrayList({"entryA"})); //deleting a group, should notify that every entry in that group has changed watcherSpy.clear(); myConfigGroup.deleteGroup("aSubGroup", KConfig::Persistent | KConfig::Notify); config.sync(); watcherSpy.wait(); QCOMPARE(watcherSpy.count(), 1); QCOMPARE(watcherSpy[0][0].value().name(), QStringLiteral("aSubGroup")); QCOMPARE(watcherSpy[0][1].value(), QByteArrayList({"entry1", "entry2"})); //global write still triggers our notification watcherSpy.clear(); myConfigGroup.writeEntry("someGlobalEntry", "foo", KConfig::Persistent | KConfig::Notify | KConfig::Global); config.sync(); watcherSpy.wait(); QCOMPARE(watcherSpy.count(), 1); QCOMPARE(watcherSpy[0][0].value().name(), QStringLiteral("TopLevelGroup")); QCOMPARE(watcherSpy[0][1].value(), QByteArrayList({"someGlobalEntry"})); //watching another file should have only triggered from the kdeglobals change QCOMPARE(otherWatcherSpy.count(), 1); QCOMPARE(otherWatcherSpy[0][0].value().name(), QStringLiteral("TopLevelGroup")); QCOMPARE(otherWatcherSpy[0][1].value(), QByteArrayList({"someGlobalEntry"})); } diff --git a/src/kconfig_compiler/KConfigCodeGeneratorBase.cpp b/src/kconfig_compiler/KConfigCodeGeneratorBase.cpp index 574aeff..5a110b2 100644 --- a/src/kconfig_compiler/KConfigCodeGeneratorBase.cpp +++ b/src/kconfig_compiler/KConfigCodeGeneratorBase.cpp @@ -1,277 +1,271 @@ /* m_this file is part of the KDE libraries Copyright (C) 2020 Tomaz Cananbrava (tcanabrava@kde.org) Copyright (c) 2003 Cornelius Schumacher Copyright (c) 2003 Waldo Bastian Copyright (c) 2003 Zack Rusin Copyright (c) 2006 Michaël Larouche Copyright (c) 2008 Allen Winter Copyright (C) 2020 Tomaz Cananbrava (tcanabrava@kde.org) m_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. m_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 "KConfigCodeGeneratorBase.h" #include "KConfigParameters.h" #include "KConfigCommonStructs.h" #include #include #include #include #include -using std::endl; - -namespace -{ -QTextStream cout(stdout); -QTextStream cerr(stderr); -} +#include KConfigCodeGeneratorBase::KConfigCodeGeneratorBase( const QString &inputFile, const QString &baseDir, const QString &fileName, const KConfigParameters ¶meters, ParseResult &parseResult) : m_inputFile(inputFile), m_baseDir(baseDir), m_fileName(fileName), m_cfg(parameters), parseResult(parseResult) { m_file.setFileName(m_fileName); if (!m_file.open(QIODevice::WriteOnly)) { - cerr << "Can not open '" << m_fileName << "for writing." << endl; + std::cerr << "Can not open '" << qPrintable(m_fileName) << "for writing." << std::endl; exit(1); } m_stream.setDevice(&m_file); m_stream.setCodec("utf-8"); if (m_cfg.staticAccessors) { m_this = QStringLiteral("self()->"); } else { m_const = QStringLiteral(" const"); } } KConfigCodeGeneratorBase::~KConfigCodeGeneratorBase() { save(); } void KConfigCodeGeneratorBase::save() { m_file.close(); } //TODO: Remove this weird logic and adapt the testcases void KConfigCodeGeneratorBase::indent() { if (m_indentLevel >= 4) { m_indentLevel += 2; } else { m_indentLevel += 4; } } void KConfigCodeGeneratorBase::unindent() { if (m_indentLevel > 4) { m_indentLevel -= 2; } else { m_indentLevel -= 4; } } QString KConfigCodeGeneratorBase::whitespace() const { QString spaces; for (int i = 0; i < m_indentLevel; i++) { spaces.append(QLatin1Char(' ')); } return spaces; } void KConfigCodeGeneratorBase::startScope() { m_stream << whitespace() << QLatin1Char('{'); - m_stream << endl; + m_stream << '\n'; indent(); } void KConfigCodeGeneratorBase::endScope(ScopeFinalizer finalizer) { unindent(); m_stream << whitespace() << QLatin1Char('}'); if (finalizer == ScopeFinalizer::Semicolon) { m_stream << ';'; } - m_stream << endl; + m_stream << '\n'; } void KConfigCodeGeneratorBase::start() { const QString m_fileName = QFileInfo(m_inputFile).fileName(); - m_stream << "// This file is generated by kconfig_compiler_kf5 from " << m_fileName << ".kcfg" << "." << endl; - m_stream << "// All changes you do to this file will be lost." << endl; + m_stream << "// This file is generated by kconfig_compiler_kf5 from " << m_fileName << ".kcfg" << ".\n"; + m_stream << "// All changes you do to this file will be lost.\n"; } void KConfigCodeGeneratorBase::addHeaders(const QStringList &headerList) { for (auto include : qAsConst(headerList)) { if (include.startsWith(QLatin1Char('"'))) { - m_stream << "#include " << include << endl; + m_stream << "#include " << include << '\n'; } else { - m_stream << "#include <" << include << ">" << endl; + m_stream << "#include <" << include << ">\n"; } } } // adds as many 'namespace foo {' lines to p_out as // there are namespaces in p_ns void KConfigCodeGeneratorBase::beginNamespaces() { if (!m_cfg.nameSpace.isEmpty()) { for (const QString &ns : m_cfg.nameSpace.split(QStringLiteral("::"))) { - m_stream << "namespace " << ns << " {" << endl; + m_stream << "namespace " << ns << " {\n"; } - m_stream << endl; + m_stream << '\n'; } } // adds as many '}' lines to p_out as // there are namespaces in p_ns void KConfigCodeGeneratorBase::endNamespaces() { if (!m_cfg.nameSpace.isEmpty()) { - m_stream << endl; + m_stream << '\n'; const int namespaceCount = m_cfg.nameSpace.count(QStringLiteral("::")) + 1; for (int i = 0; i < namespaceCount; ++i) { - m_stream << "}" << endl; + m_stream << "}\n"; } } } // returns the member accesor implementation // which should go in the h file if inline // or the cpp file if not inline QString KConfigCodeGeneratorBase::memberAccessorBody(const CfgEntry *e, bool globalEnums) const { QString result; QTextStream out(&result, QIODevice::WriteOnly); QString n = e->name; QString t = e->type; bool useEnumType = m_cfg.useEnumTypes && t == QLatin1String("Enum"); out << "return "; if (useEnumType) { out << "static_cast<" << enumType(e, globalEnums) << ">("; } out << m_this << varPath(n, m_cfg); if (!e->param.isEmpty()) { out << "[i]"; } if (useEnumType) { out << ")"; } - out << ";" << endl; + out << ";\n"; return result; } void KConfigCodeGeneratorBase::createIfSetLogic(const CfgEntry *e, const QString &varExpression) { const QString n = e->name; const QString t = e->type; const bool hasBody = !e->signalList.empty() || m_cfg.generateProperties; m_stream << whitespace() << "if ("; if (hasBody) { m_stream << "v != " << varExpression << " && "; } m_stream << "!" << m_this << "isImmutable( QStringLiteral( \""; if (!e->param.isEmpty()) { QString paramName = e->paramName; m_stream << paramName.replace(QStringLiteral("$(") + e->param + QStringLiteral(")"), QLatin1String("%1")) << "\" ).arg( "; if (e->paramType == QLatin1String("Enum")) { m_stream << "QLatin1String( "; if (m_cfg.globalEnums) { m_stream << enumName(e->param) << "ToString[i]"; } else { m_stream << enumName(e->param) << "::enumToString[i]"; } m_stream << " )"; } else { m_stream << "i"; } m_stream << " )"; } else { m_stream << n << "\" )"; } m_stream << " ))"; } void KConfigCodeGeneratorBase::memberMutatorBody(const CfgEntry *e) { QString n = e->name; QString t = e->type; // HACK: Don't open '{' manually, use startScope / endScope to automatically handle whitespace indentation. if (!e->min.isEmpty()) { if (e->min != QLatin1String("0") || !isUnsigned(t)) { // skip writing "if uint<0" (#187579) - m_stream << whitespace() << "if (v < " << e->min << ")" << endl; - m_stream << whitespace() << "{" << endl; + m_stream << whitespace() << "if (v < " << e->min << ")\n"; + m_stream << whitespace() << "{\n"; m_stream << whitespace(); addDebugMethod(m_stream, m_cfg, n); - m_stream << ": value \" << v << \" is less than the minimum value of " << e->min << "\";" << endl; - m_stream << whitespace() << " v = " << e->min << ";" << endl; - m_stream << whitespace() << "}" << endl; + m_stream << ": value \" << v << \" is less than the minimum value of " << e->min << "\";\n"; + m_stream << whitespace() << " v = " << e->min << ";\n"; + m_stream << whitespace() << "}\n"; } } if (!e->max.isEmpty()) { - m_stream << endl; - m_stream << whitespace() << "if (v > " << e->max << ")" << endl; - m_stream << whitespace() << "{" << endl; + m_stream << '\n'; + m_stream << whitespace() << "if (v > " << e->max << ")\n"; + m_stream << whitespace() << "{\n"; m_stream << whitespace(); addDebugMethod(m_stream, m_cfg, n); - m_stream << ": value \" << v << \" is greater than the maximum value of " << e->max << "\";" << endl; - m_stream << whitespace() << " v = " << e->max << ";" << endl; - m_stream << whitespace() << "}" << endl << endl; + m_stream << ": value \" << v << \" is greater than the maximum value of " << e->max << "\";\n"; + m_stream << whitespace() << " v = " << e->max << ";\n"; + m_stream << whitespace() << "}\n\n"; } const QString varExpression = m_this + varPath(n, m_cfg) + (e->param.isEmpty() ? QString() : QStringLiteral("[i]")); // TODO: Remove this `hasBody` logic, always use an '{' for the if. const bool hasBody = !e->signalList.empty() || m_cfg.generateProperties; // m_this call creates an `if (someTest ...) that's just to long to throw over the code. createIfSetLogic(e, varExpression); - m_stream << (hasBody ? " {" : "") << endl; - m_stream << whitespace() << " " << varExpression << " = v;" << endl; + m_stream << (hasBody ? " {" : "") << '\n'; + m_stream << whitespace() << " " << varExpression << " = v;\n"; const auto listSignal = e->signalList; for (const Signal &signal : qAsConst(listSignal)) { if (signal.modify) { - m_stream << whitespace() << " Q_EMIT " << m_this << signal.name << "();" << endl; + m_stream << whitespace() << " Q_EMIT " << m_this << signal.name << "();\n"; } else { m_stream << whitespace() << " " << m_this << varPath(QStringLiteral("settingsChanged"), m_cfg) - << " |= " << signalEnumName(signal.name) << ";" << endl; + << " |= " << signalEnumName(signal.name) << ";\n"; } } if (hasBody) { - m_stream << whitespace() << "}" << endl; + m_stream << whitespace() << "}\n"; } } diff --git a/src/kconfig_compiler/KConfigHeaderGenerator.cpp b/src/kconfig_compiler/KConfigHeaderGenerator.cpp index 5fc1610..47f5358 100644 --- a/src/kconfig_compiler/KConfigHeaderGenerator.cpp +++ b/src/kconfig_compiler/KConfigHeaderGenerator.cpp @@ -1,614 +1,609 @@ /* This file is part of the KDE libraries Copyright (C) 2020 Tomaz Cananbrava (tcanabrava@kde.org) Copyright (c) 2003 Cornelius Schumacher Copyright (c) 2003 Waldo Bastian Copyright (c) 2003 Zack Rusin Copyright (c) 2006 Michaël Larouche Copyright (c) 2008 Allen Winter Copyright (C) 2020 Tomaz Cananbrava (tcanabrava@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 "KConfigHeaderGenerator.h" #include "KConfigCommonStructs.h" #include #include - -namespace -{ -QTextStream cout(stdout); -QTextStream cerr(stderr); -} +#include KConfigHeaderGenerator::KConfigHeaderGenerator( const QString &inputFile, const QString &baseDir, const KConfigParameters &cfg, ParseResult &result) : KConfigCodeGeneratorBase(inputFile, baseDir, baseDir + cfg.baseName + QLatin1Char('.') + cfg.headerExtension, cfg, result) { } void KConfigHeaderGenerator::start() { KConfigCodeGeneratorBase::start(); startHeaderGuards(); createHeaders(); beginNamespaces(); createForwardDeclarations(); doClassDefinition(); endNamespaces(); endHeaderGuards(); } void KConfigHeaderGenerator::doClassDefinition() { - stream() << "class " << cfg().visibility << cfg().className << " : public " << cfg().inherits << endl; + stream() << "class " << cfg().visibility << cfg().className << " : public " << cfg().inherits << '\n'; startScope(); // Add Q_OBJECT macro if the config need signals. if (!parseResult.signalList.isEmpty() || cfg().generateProperties) { - stream() << " Q_OBJECT" << endl; + stream() << " Q_OBJECT\n"; } - stream() << " public:" << endl; + stream() << " public:\n"; implementEnums(); createConstructor(); createDestructor(); for (auto *entry : parseResult.entries) { const QString n = entry->name; const QString t = entry->type; const QString returnType = (cfg().useEnumTypes && t == QLatin1String("Enum")) ? enumType(entry, cfg().globalEnums) : cppType(t); createSetters(entry); createProperties(entry, returnType); createGetters(entry, returnType); createDefaultValueMember(entry); createItemAcessors(entry, returnType); } createSignals(); - stream() << " protected:" << endl; + stream() << " protected:\n"; createSingleton(); // TODO: Move those to functions too. if (parseResult.hasNonModifySignals) { - stream() << whitespace() << "bool usrSave() override;" << endl; + stream() << whitespace() << "bool usrSave() override;\n"; } // Member variables if (!cfg().memberVariables.isEmpty() && cfg().memberVariables != QLatin1String("private") && cfg().memberVariables != QLatin1String("dpointer")) { - stream() << " " << cfg().memberVariables << ":" << endl; + stream() << " " << cfg().memberVariables << ":\n"; } // Class Parameters for (const auto ¶meter : parseResult.parameters) { - stream() << whitespace() << "" << cppType(parameter.type) << " mParam" << parameter.name << ";" << endl; + stream() << whitespace() << "" << cppType(parameter.type) << " mParam" << parameter.name << ";\n"; } createNonDPointerHelpers(); createDPointer(); if (cfg().customAddons) { - stream() << whitespace() << "// Include custom additions" << endl; - stream() << whitespace() << "#include \"" << cfg().baseName << "_addons." << cfg().headerExtension << '"' << endl; + stream() << whitespace() << "// Include custom additions\n"; + stream() << whitespace() << "#include \"" << cfg().baseName << "_addons." << cfg().headerExtension << "\"\n"; } endScope(ScopeFinalizer::Semicolon); } void KConfigHeaderGenerator::createHeaders() { addHeaders(cfg().headerIncludes); if (cfg().headerIncludes.size()) { - stream() << endl; + stream() << '\n'; } if (!cfg().singleton && parseResult.parameters.isEmpty()) { addHeaders({QStringLiteral("qglobal.h")}); } if (cfg().inherits == QLatin1String("KCoreConfigSkeleton")) { addHeaders({QStringLiteral("kcoreconfigskeleton.h")}); } else { addHeaders({QStringLiteral("kconfigskeleton.h")}); } addHeaders({QStringLiteral("QCoreApplication"), QStringLiteral("QDebug")}); - stream() << endl; + stream() << '\n'; addHeaders(parseResult.includes); if (parseResult.includes.size()) { - stream() << endl; + stream() << '\n'; } } void KConfigHeaderGenerator::startHeaderGuards() { const bool hasNamespace = !cfg().nameSpace.isEmpty(); const QString namespaceName = QString(QString(cfg().nameSpace).replace(QLatin1String("::"), QLatin1String("_"))).toUpper(); const QString namespaceStr = hasNamespace ? namespaceName + QLatin1Char('_') : QStringLiteral(""); const QString defineName = namespaceStr + cfg().className.toUpper() + QStringLiteral("_H"); - stream() << "#ifndef " << defineName << endl; - stream() << "#define " << defineName << endl; - stream() << endl; + stream() << "#ifndef " << defineName << '\n'; + stream() << "#define " << defineName << '\n'; + stream() << '\n'; } void KConfigHeaderGenerator::endHeaderGuards() { - stream() << endl; + stream() << '\n'; stream() << "#endif"; - stream() << endl; + stream() << '\n'; // HACK: Original files ended with two last newlines, add them. - stream() << endl; + stream() << '\n'; } void KConfigHeaderGenerator::implementChoiceEnums(const CfgEntry *entry, const CfgEntry::Choices &choices) { const QList chlist = choices.choices; if (chlist.isEmpty()) { return; } QStringList values; for (const auto choice : qAsConst(chlist)) { values.append(choices.prefix + choice.name); } if (choices.name().isEmpty()) { if (cfg().globalEnums) { - stream() << whitespace() << "enum " << enumName(entry->name, entry->choices) << " { " << values.join(QStringLiteral(", ")) << " };" << endl; + stream() << whitespace() << "enum " << enumName(entry->name, entry->choices) << " { " << values.join(QStringLiteral(", ")) << " };\n"; } else { // Create an automatically named enum - stream() << whitespace() << "class " << enumName(entry->name, entry->choices) << endl; - stream() << whitespace() << "{" << endl; - stream() << whitespace() << " public:" << endl; - stream() << whitespace() << " enum type { " << values.join(QStringLiteral(", ")) << ", COUNT };" << endl; - stream() << whitespace() << "};" << endl; + stream() << whitespace() << "class " << enumName(entry->name, entry->choices) << '\n'; + stream() << whitespace() << "{\n"; + stream() << whitespace() << " public:\n"; + stream() << whitespace() << " enum type { " << values.join(QStringLiteral(", ")) << ", COUNT };\n"; + stream() << whitespace() << "};\n"; } } else if (!choices.external()) { // Create a named enum - stream() << whitespace() << "enum " << enumName(entry->name, entry->choices) << " { " << values.join(QStringLiteral(", ")) << " };" << endl; + stream() << whitespace() << "enum " << enumName(entry->name, entry->choices) << " { " << values.join(QStringLiteral(", ")) << " };\n"; } } void KConfigHeaderGenerator::implementValueEnums(const CfgEntry *entry, const QStringList &values) { if (values.isEmpty()) { return; } if (cfg().globalEnums) { // ### FIXME!! // make the following string table an index-based string search! // ### - stream() << whitespace() << "enum " << enumName(entry->param) << " { " << values.join(QStringLiteral(", ")) << " };" << endl; - stream() << whitespace() << "static const char* const " << enumName(entry->param) << "ToString[];" << endl; + stream() << whitespace() << "enum " << enumName(entry->param) << " { " << values.join(QStringLiteral(", ")) << " };\n"; + stream() << whitespace() << "static const char* const " << enumName(entry->param) << "ToString[];\n"; } else { - stream() << whitespace() << "class " << enumName(entry->param) << endl; - stream() << whitespace() << "{" << endl; - stream() << whitespace() << " public:" << endl; - stream() << whitespace() << " enum type { " << values.join(QStringLiteral(", ")) << ", COUNT };" << endl; - stream() << whitespace() << " static const char* const enumToString[];" << endl; - stream() << whitespace() << "};" << endl; + stream() << whitespace() << "class " << enumName(entry->param) << '\n'; + stream() << whitespace() << "{\n"; + stream() << whitespace() << " public:\n"; + stream() << whitespace() << " enum type { " << values.join(QStringLiteral(", ")) << ", COUNT };\n"; + stream() << whitespace() << " static const char* const enumToString[];\n"; + stream() << whitespace() << "};\n"; } } void KConfigHeaderGenerator::implementEnums() { if (!parseResult.entries.size()) { return; } for (const auto entry : parseResult.entries) { const CfgEntry::Choices &choices = entry->choices; const QStringList values = entry->paramValues; implementChoiceEnums(entry, choices); implementValueEnums(entry, values); } - stream() << endl; + stream() << '\n'; } void KConfigHeaderGenerator::createSignals() { // Signal definition. const bool hasSignals = !parseResult.signalList.isEmpty(); unsigned val = 1 << parseResult.signalList.size(); if (!val) { - cerr << "Too many signals to create unique bit masks" << endl; + std::cerr << "Too many signals to create unique bit masks" << std::endl; exit(1); } if (!hasSignals) { return; } - stream() << "\n enum {" << endl; + stream() << "\n enum {\n"; val = 1; // HACK: Use C-Style for add a comma in all but the last element, // just to make the source generated code equal to the old one. // When we are sure, revert this to a range-based-for and just add // a last comma, as it's valid c++. for (int i = 0, end = parseResult.signalList.size(); i < end; i++) { auto signal = parseResult.signalList.at(i); stream() << whitespace() << " " << signalEnumName(signal.name) << " = 0x" << hex << val; if (i != end-1) { - stream() << "," << endl; + stream() << ",\n"; } val <<= 1; } - stream() << endl; - stream() << whitespace() << "};" << dec << endl << endl; + stream() << '\n'; + stream() << whitespace() << "};" << dec << "\n\n"; stream() << " Q_SIGNALS:"; for (const Signal &signal : parseResult.signalList) { - stream() << endl; + stream() << '\n'; if (!signal.label.isEmpty()) { - stream() << whitespace() << "/**" << endl; - stream() << whitespace() << " " << signal.label << endl; - stream() << whitespace() << "*/" << endl; + stream() << whitespace() << "/**\n"; + stream() << whitespace() << " " << signal.label << '\n'; + stream() << whitespace() << "*/\n"; } stream() << whitespace() << "void " << signal.name << "("; QList::ConstIterator it, itEnd = signal.arguments.constEnd(); for (it = signal.arguments.constBegin(); it != itEnd;) { Param argument = *it; QString type = param(argument.type); if (cfg().useEnumTypes && argument.type == QLatin1String("Enum")) { for (auto *entry : parseResult.entries) { if (entry->name == argument.name) { type = enumType(entry, cfg().globalEnums); break; } } } stream() << type << " " << argument.name; if (++it != itEnd) { stream() << ", "; } } - stream() << ");" << endl; + stream() << ");\n"; } - stream() << endl; + stream() << '\n'; - stream() << " private:" << endl; - stream() << whitespace() << "void itemChanged(quint64 flags);" << endl; - stream() << endl; + stream() << " private:\n"; + stream() << whitespace() << "void itemChanged(quint64 flags);\n"; + stream() << '\n'; } void KConfigHeaderGenerator::createDPointer() { if (!cfg().dpointer) { return; } // use a private class for both member variables and items - stream() << " private:" << endl; + stream() << " private:\n"; for (const auto &entry : parseResult.entries) { if (cfg().allDefaultGetters || cfg().defaultGetters.contains(entry->name)) { stream() << whitespace() << ""; if (cfg().staticAccessors) { stream() << "static "; } stream() << cppType(entry->type) << " " << getDefaultFunction(entry->name) << "_helper("; if (!entry->param.isEmpty()) { stream() << " " << cppType(entry->paramType) << " i "; } - stream() << ")" << Const() << ";" << endl; + stream() << ")" << Const() << ";\n"; } } - stream() << whitespace() << "" << cfg().className << "Private *d;" << endl; + stream() << whitespace() << "" << cfg().className << "Private *d;\n"; } void KConfigHeaderGenerator::createConstructor() { if (cfg().singleton) { - stream() << whitespace() << "static " << cfg().className << " *self();" << endl; + stream() << whitespace() << "static " << cfg().className << " *self();\n"; if (parseResult.cfgFileNameArg) { - stream() << whitespace() << "static void instance(const QString& cfgfilename);" << endl; - stream() << whitespace() << "static void instance(KSharedConfig::Ptr config);" << endl; + stream() << whitespace() << "static void instance(const QString& cfgfilename);\n"; + stream() << whitespace() << "static void instance(KSharedConfig::Ptr config);\n"; } return; } stream() << whitespace() << "" << cfg().className << "("; if (parseResult.cfgFileNameArg) { if (cfg().forceStringFilename) stream() << " const QString &cfgfilename" << (parseResult.parameters.isEmpty() ? " = QString()" : ", "); else stream() << " KSharedConfig::Ptr config" << (parseResult.parameters.isEmpty() ? " = KSharedConfig::openConfig()" : ", "); } bool first = true; for (const auto parameter : parseResult.parameters) { if (first) { first = false; } else { stream() << ","; } stream() << " " << param(parameter.type) << " " << parameter.name; } if (cfg().parentInConstructor) { if (parseResult.cfgFileNameArg || !parseResult.parameters.isEmpty()) { stream() << ","; } stream() << " QObject *parent = nullptr"; } - stream() << " );" << endl; + stream() << " );\n"; } void KConfigHeaderGenerator::createDestructor() { - stream() << whitespace() << "~" << cfg().className << "();" << endl << endl; + stream() << whitespace() << "~" << cfg().className << "();\n\n"; } void KConfigHeaderGenerator::createForwardDeclarations() { // Private class declaration if (cfg().dpointer) { - stream() << "class " << cfg().className << "Private;" << endl << endl; + stream() << "class " << cfg().className << "Private;\n\n"; } } void KConfigHeaderGenerator::createProperties(const CfgEntry *entry, const QString& returnType) { if (!cfg().generateProperties) { return; } stream() << whitespace() << "Q_PROPERTY(" << returnType << ' ' << getFunction(entry->name); stream() << " READ " << getFunction(entry->name); if (cfg().allMutators || cfg().mutators.contains(entry->name)) { const QString signal = changeSignalName(entry->name); stream() << " WRITE " << setFunction(entry->name); stream() << " NOTIFY " << signal; //If we have the modified signal, we'll also need //the changed signal as well Signal s; s.name = signal; s.modify = true; parseResult.signalList.append(s); } else { stream() << " CONSTANT"; } - stream() << ")" << endl; + stream() << ")\n"; } void KConfigHeaderGenerator::createSetters(const CfgEntry *entry) { // Manipulator if (!cfg().allMutators && !cfg().mutators.contains(entry->name)) { return; } - stream() << whitespace() << "/**" << endl; - stream() << whitespace() << " Set " << entry->label << endl; - stream() << whitespace() << "*/" << endl; + stream() << whitespace() << "/**\n"; + stream() << whitespace() << " Set " << entry->label << '\n'; + stream() << whitespace() << "*/\n"; if (cfg().staticAccessors) { - stream() << whitespace() << "static" << endl; + stream() << whitespace() << "static\n"; } stream() << whitespace() << "void " << setFunction(entry->name) << "( "; if (!entry->param.isEmpty()) { stream() <paramType) << " i, "; } stream() << (cfg().useEnumTypes && entry->type == QLatin1String("Enum") ? enumType(entry, cfg().globalEnums) : param(entry->type)); stream() << " v )"; // function body inline only if not using dpointer // for BC mode if (!cfg().dpointer) { - stream() << endl; + stream() << '\n'; startScope(); memberMutatorBody(entry); endScope(); - stream() << endl; + stream() << '\n'; } else { - stream() << ";" << endl << endl; + stream() << ";\n\n"; } } void KConfigHeaderGenerator::createGetters(const CfgEntry *entry, const QString &returnType) { // Accessor - stream() << whitespace() << "/**" << endl; - stream() << whitespace() << " Get " << entry->label << endl; - stream() << whitespace() << "*/" << endl; + stream() << whitespace() << "/**\n"; + stream() << whitespace() << " Get " << entry->label << '\n'; + stream() << whitespace() << "*/\n"; if (cfg().staticAccessors) { - stream() << whitespace() << "static" << endl; + stream() << whitespace() << "static\n"; } stream() << whitespace() << ""; stream() << returnType; stream() << " " << getFunction(entry->name) << "("; if (!entry->param.isEmpty()) { stream() << " " << cppType(entry->paramType) << " i "; } stream() << ")" << Const(); // function body inline only if not using dpointer // for BC mode if (!cfg().dpointer) { - stream() << endl; + stream() << '\n'; startScope(); stream() << whitespace() << memberAccessorBody(entry, cfg().globalEnums); endScope(); - stream() << endl; + stream() << '\n'; } else { - stream() << ";" << endl << endl; + stream() << ";\n\n"; } } void KConfigHeaderGenerator::createItemAcessors(const CfgEntry *entry, const QString &returnType) { // Item accessor if (!cfg().itemAccessors) { return; } - stream() << whitespace() << "/**" << endl; + stream() << whitespace() << "/**\n"; stream() << whitespace() << " Get Item object corresponding to " << entry->name << "()" - << endl; - stream() << whitespace() << "*/" << endl; + << '\n'; + stream() << whitespace() << "*/\n"; stream() << whitespace() << "Item" << itemType(entry->type) << " *" << getFunction(entry->name) << "Item("; if (!entry->param.isEmpty()) { stream() << " " << cppType(entry->paramType) << " i "; } stream() << ")"; if (!cfg().dpointer) { - stream() << endl; + stream() << '\n'; startScope(); stream() << whitespace() << itemAccessorBody(entry, cfg()); endScope(); } else { - stream() << ";" << endl; + stream() << ";\n"; } stream() <name)) && !entry->defaultValue.isEmpty())) { return; } - stream() << whitespace() << "/**" << endl; - stream() << whitespace() << " Get " << entry->label << " default value" << endl; - stream() << whitespace() << "*/" << endl; + stream() << whitespace() << "/**\n"; + stream() << whitespace() << " Get " << entry->label << " default value\n"; + stream() << whitespace() << "*/\n"; if (cfg().staticAccessors) { - stream() << whitespace() << "static" << endl; + stream() << whitespace() << "static\n"; } stream() << whitespace() << ""; if (cfg().useEnumTypes && entry->type == QLatin1String("Enum")) { stream() << enumType(entry, cfg().globalEnums); } else { stream() << cppType(entry->type); } stream() << " " << getDefaultFunction(entry->name) << "("; if (!entry->param.isEmpty()) { stream() << " " << cppType(entry->paramType) << " i "; } - stream() << ")" << Const() << endl; - stream() << whitespace() << "{" << endl; + stream() << ")" << Const() << '\n'; + stream() << whitespace() << "{\n"; stream() << whitespace() << " return "; if (cfg().useEnumTypes && entry->type == QLatin1String("Enum")) { stream() << "static_cast<" << enumType(entry, cfg().globalEnums) << ">("; } stream() << getDefaultFunction(entry->name) << "_helper("; if (!entry->param.isEmpty()) { stream() << " i "; } stream() << ")"; if (cfg().useEnumTypes && entry->type == QLatin1String("Enum")) { stream() << ")"; } - stream() << ";" << endl; - stream() << whitespace() << "}" << endl; - stream() << endl; + stream() << ";\n"; + stream() << whitespace() << "}\n"; + stream() << '\n'; } void KConfigHeaderGenerator::createSingleton() { // Private constructor for singleton if (!cfg().singleton) { return; } stream() << whitespace() << "" << cfg().className << "("; if (parseResult.cfgFileNameArg) { stream() << "KSharedConfig::Ptr config"; } if (cfg().parentInConstructor) { if (parseResult.cfgFileNameArg) { stream() << ", "; } stream() << "QObject *parent = nullptr"; } - stream() << ");" << endl; - stream() << whitespace() << "friend class " << cfg().className << "Helper;" << endl << endl; + stream() << ");\n"; + stream() << whitespace() << "friend class " << cfg().className << "Helper;\n\n"; } void KConfigHeaderGenerator::createNonDPointerHelpers() { if (cfg().memberVariables == QLatin1String("dpointer")) { return; } QString group; for (auto *entry : parseResult.entries) { if (entry->group != group) { group = entry->group; - stream() << endl; - stream() << whitespace() << "// " << group << endl; + stream() << '\n'; + stream() << whitespace() << "// " << group << '\n'; } stream() << whitespace() << "" << cppType(entry->type) << " " << varName(entry->name, cfg()); if (!entry->param.isEmpty()) { stream() << QStringLiteral("[%1]").arg(entry->paramMax + 1); } - stream() << ";" << endl; + stream() << ";\n"; if (cfg().allDefaultGetters || cfg().defaultGetters.contains(entry->name)) { stream() << whitespace() << ""; if (cfg().staticAccessors) { stream() << "static "; } stream() << cppType(entry->type) << " " << getDefaultFunction(entry->name) << "_helper("; if (!entry->param.isEmpty()) { stream() << " " << cppType(entry->paramType) << " i "; } - stream() << ")" << Const() << ";" << endl; + stream() << ")" << Const() << ";\n"; } } - stream() << endl << " private:" << endl; + stream() << "\n private:\n"; if (cfg().itemAccessors) { for (auto *entry : parseResult.entries) { stream() << whitespace() << "Item" << itemType(entry->type) << " *" << itemVar(entry, cfg()); if (!entry->param.isEmpty()) { stream() << QStringLiteral("[%1]").arg(entry->paramMax + 1); } - stream() << ";" << endl; + stream() << ";\n"; } } if (parseResult.hasNonModifySignals) { - stream() << whitespace() << "uint " << varName(QStringLiteral("settingsChanged"), cfg()) << ";" << endl; + stream() << whitespace() << "uint " << varName(QStringLiteral("settingsChanged"), cfg()) << ";\n"; } } diff --git a/src/kconfig_compiler/KConfigParameters.cpp b/src/kconfig_compiler/KConfigParameters.cpp index c8046ba..e715818 100644 --- a/src/kconfig_compiler/KConfigParameters.cpp +++ b/src/kconfig_compiler/KConfigParameters.cpp @@ -1,100 +1,96 @@ /* This file is part of the KDE libraries Copyright (C) 2020 Tomaz Cananbrava (tcanabrava@kde.org) Copyright (c) 2003 Cornelius Schumacher Copyright (c) 2003 Waldo Bastian Copyright (c) 2003 Zack Rusin Copyright (c) 2006 Michaël Larouche Copyright (c) 2008 Allen Winter Copyright (C) 2020 Tomaz Cananbrava (tcanabrava@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 "KConfigParameters.h" // TODO: Remove this. #undef QT_NO_CAST_FROM_ASCII #include #include -namespace -{ -QTextStream cout(stdout); -QTextStream cerr(stderr); -} +#include KConfigParameters::KConfigParameters(const QString &codegenFilename) { if (!codegenFilename.endsWith(QLatin1String(".kcfgc"))) { - cerr << "Codegen options file must have extension .kcfgc" << endl; + std::cerr << "Codegen options file must have extension .kcfgc" << std::endl; exit(1); } baseName = QFileInfo(codegenFilename).fileName(); baseName = baseName.left(baseName.length() - 6); // Configure the compiler with some settings QSettings codegenConfig(codegenFilename, QSettings::IniFormat); nameSpace = codegenConfig.value(QStringLiteral("NameSpace")).toString(); className = codegenConfig.value(QStringLiteral("ClassName")).toString(); if (className.isEmpty()) { - cerr << "Class name missing" << endl; + std::cerr << "Class name missing" << std::endl; exit(1); } inherits = codegenConfig.value(QStringLiteral("Inherits")).toString(); if (inherits.isEmpty()) { inherits = QStringLiteral("KConfigSkeleton"); } visibility = codegenConfig.value(QStringLiteral("Visibility")).toString(); if (!visibility.isEmpty()) { visibility += QLatin1Char(' '); } parentInConstructor = codegenConfig.value(QStringLiteral("ParentInConstructor"), false).toBool(); forceStringFilename = codegenConfig.value(QStringLiteral("ForceStringFilename"), false).toBool(); singleton = codegenConfig.value(QStringLiteral("Singleton"), false).toBool(); staticAccessors = singleton; customAddons = codegenConfig.value(QStringLiteral("CustomAdditions"), false).toBool(); memberVariables = codegenConfig.value(QStringLiteral("MemberVariables")).toString(); dpointer = (memberVariables == QLatin1String("dpointer")); headerIncludes = codegenConfig.value(QStringLiteral("IncludeFiles"), QStringList()).toStringList(); sourceIncludes = codegenConfig.value(QStringLiteral("SourceIncludeFiles"), QStringList()).toStringList(); mutators = codegenConfig.value(QStringLiteral("Mutators"), QStringList()).toStringList(); allMutators = ((mutators.count() == 1) && (mutators.at(0).toLower() == QLatin1String("true"))); itemAccessors = codegenConfig.value(QStringLiteral("ItemAccessors"), false).toBool(); setUserTexts = codegenConfig.value(QStringLiteral("SetUserTexts"), false).toBool(); defaultGetters = codegenConfig.value(QStringLiteral("DefaultValueGetters"), QStringList()).toStringList(); allDefaultGetters = (defaultGetters.count() == 1) && (defaultGetters.at(0).toLower() == QLatin1String("true")); notifiers = codegenConfig.value(QStringLiteral("Notifiers"), QStringList()).toStringList(); allNotifiers = ((notifiers.count() == 1) && (notifiers.at(0).toLower() == QLatin1String("true"))); globalEnums = codegenConfig.value(QStringLiteral("GlobalEnums"), false).toBool(); useEnumTypes = codegenConfig.value(QStringLiteral("UseEnumTypes"), false).toBool(); const QString trString = codegenConfig.value(QStringLiteral("TranslationSystem")).toString().toLower(); generateProperties = codegenConfig.value(QStringLiteral("GenerateProperties"), false).toBool(); if (trString == QLatin1String("kde")) { translationSystem = KdeTranslation; translationDomain = codegenConfig.value(QStringLiteral("TranslationDomain")).toString(); } else { if (!trString.isEmpty() && trString != QLatin1String("qt")) { - cerr << "Unknown translation system, falling back to Qt tr()" << endl; + std::cerr << "Unknown translation system, falling back to Qt tr()" << std::endl; } translationSystem = QtTranslation; } qCategoryLoggingName = codegenConfig.value(QStringLiteral("CategoryLoggingName"), QString()).toString(); headerExtension = codegenConfig.value(QStringLiteral("HeaderExtension"), QStringLiteral("h")).toString(); sourceExtension = codegenConfig.value(QStringLiteral("SourceExtension"), QStringLiteral("cpp")).toString(); } diff --git a/src/kconfig_compiler/KConfigSourceGenerator.cpp b/src/kconfig_compiler/KConfigSourceGenerator.cpp index f306693..8a4b921 100644 --- a/src/kconfig_compiler/KConfigSourceGenerator.cpp +++ b/src/kconfig_compiler/KConfigSourceGenerator.cpp @@ -1,657 +1,657 @@ /* This file is part of the KDE libraries Copyright (C) 2020 Tomaz Cananbrava (tcanabrava@kde.org) Copyright (c) 2003 Cornelius Schumacher Copyright (c) 2003 Waldo Bastian Copyright (c) 2003 Zack Rusin Copyright (c) 2006 Michaël Larouche Copyright (c) 2008 Allen Winter Copyright (C) 2020 Tomaz Cananbrava (tcanabrava@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 "KConfigSourceGenerator.h" #include "KConfigCommonStructs.h" KConfigSourceGenerator::KConfigSourceGenerator( const QString &inputFile, const QString &baseDir, const KConfigParameters &cfg, ParseResult &result) : KConfigCodeGeneratorBase(inputFile, baseDir, baseDir + cfg.baseName + QLatin1Char('.') + cfg.sourceExtension, cfg, result) { } void KConfigSourceGenerator::start() { KConfigCodeGeneratorBase::start(); - stream() << endl; + stream() << '\n'; createHeaders(); if (!cfg().nameSpace.isEmpty()) { stream() << "using namespace " << cfg().nameSpace << ";"; - stream() << endl << endl; + stream() << endl << '\n'; } createPrivateDPointerImplementation(); createSingletonImplementation(); createPreamble(); doConstructor(); doGetterSetterDPointerMode(); createDefaultValueGetterSetter(); createDestructor(); createNonModifyingSignalsHelper(); createSignalFlagsHandler(); includeMoc(); } void KConfigSourceGenerator::createHeaders() { QString headerName = cfg().baseName + QLatin1Char('.') + cfg().headerExtension; // TODO: Make addQuotes return a string instead of replacing it inplace. addQuotes(headerName); addHeaders({ headerName }); - stream() << endl; + stream() << '\n'; addHeaders(cfg().sourceIncludes); if (cfg().setUserTexts && cfg().translationSystem == KConfigParameters::KdeTranslation) { addHeaders({QStringLiteral("klocalizedstring.h")}); - stream() << endl; + stream() << '\n'; } // Header required by singleton implementation if (cfg().singleton) { addHeaders({QStringLiteral("qglobal.h"), QStringLiteral("QFile")}); // HACK: Add single line to fix test. if (cfg().singleton && parseResult.cfgFileNameArg) { - stream() << endl; + stream() << '\n'; } } if (cfg().singleton && parseResult.cfgFileNameArg) { addHeaders({QStringLiteral("QDebug")}); } if (cfg().singleton) { - stream() << endl; + stream() << '\n'; } } void KConfigSourceGenerator::createPrivateDPointerImplementation() { // private class implementation if (!cfg().dpointer) { return; } QString group; beginNamespaces(); - stream() << "class " << cfg().className << "Private" << endl; - stream() << "{" << endl; - stream() << " public:" << endl; + stream() << "class " << cfg().className << "Private\n"; + stream() << "{\n"; + stream() << " public:\n"; // Create Members for (auto *entry : parseResult.entries) { if (entry->group != group) { group = entry->group; - stream() << endl; - stream() << " // " << group << endl; + stream() << '\n'; + stream() << " // " << group << '\n'; } stream() << " " << cppType(entry->type) << " " << varName(entry->name, cfg()); if (!entry->param.isEmpty()) { stream() << QStringLiteral("[%1]").arg(entry->paramMax + 1); } - stream() << ";" << endl; + stream() << ";\n"; } - stream() << endl << " // items" << endl; + stream() << endl << " // items\n"; // Create Items. for (auto *entry : parseResult.entries) { const QString declType = entry->signalList.isEmpty() ? QString(cfg().inherits + QStringLiteral("::Item") + itemType(entry->type)) : QStringLiteral("KConfigCompilerSignallingItem"); stream() << " " << declType << " *" << itemVar( entry, cfg() ); if (!entry->param.isEmpty()) { stream() << QStringLiteral("[%1]").arg(entry->paramMax + 1); } - stream() << ";" << endl; + stream() << ";\n"; } if (parseResult.hasNonModifySignals) { - stream() << " uint " << varName(QStringLiteral("settingsChanged"), cfg()) << ";" << endl; + stream() << " uint " << varName(QStringLiteral("settingsChanged"), cfg()) << ";\n"; } - stream() << "};" << endl << endl; + stream() << "};" << endl << '\n'; endNamespaces(); } void KConfigSourceGenerator::createSingletonImplementation() { // Singleton implementation if (!cfg().singleton) { return; } beginNamespaces(); - stream() << "class " << cfg().className << "Helper" << endl; - stream() << '{' << endl; - stream() << " public:" << endl; - stream() << " " << cfg().className << "Helper() : q(nullptr) {}" << endl; - stream() << " ~" << cfg().className << "Helper() { delete q; }" << endl; - stream() << " " << cfg().className << "Helper(const " << cfg().className << "Helper&) = delete;" << endl; - stream() << " " << cfg().className << "Helper& operator=(const " << cfg().className << "Helper&) = delete;" << endl; - stream() << " " << cfg().className << " *q;" << endl; - stream() << "};" << endl; + stream() << "class " << cfg().className << "Helper\n"; + stream() << '{' << '\n'; + stream() << " public:\n"; + stream() << " " << cfg().className << "Helper() : q(nullptr) {}\n"; + stream() << " ~" << cfg().className << "Helper() { delete q; }\n"; + stream() << " " << cfg().className << "Helper(const " << cfg().className << "Helper&) = delete;\n"; + stream() << " " << cfg().className << "Helper& operator=(const " << cfg().className << "Helper&) = delete;\n"; + stream() << " " << cfg().className << " *q;\n"; + stream() << "};\n"; endNamespaces(); - stream() << "Q_GLOBAL_STATIC(" << cfg().className << "Helper, s_global" << cfg().className << ")" << endl; + stream() << "Q_GLOBAL_STATIC(" << cfg().className << "Helper, s_global" << cfg().className << ")\n"; - stream() << cfg().className << " *" << cfg().className << "::self()" << endl; - stream() << "{" << endl; + stream() << cfg().className << " *" << cfg().className << "::self()\n"; + stream() << "{\n"; if (parseResult.cfgFileNameArg) { - stream() << " if (!s_global" << cfg().className << "()->q)" << endl; - stream() << " qFatal(\"you need to call " << cfg().className << "::instance before using\");" << endl; + stream() << " if (!s_global" << cfg().className << "()->q)\n"; + stream() << " qFatal(\"you need to call " << cfg().className << "::instance before using\");\n"; } else { - stream() << " if (!s_global" << cfg().className << "()->q) {" << endl; - stream() << " new " << cfg().className << ';' << endl; - stream() << " s_global" << cfg().className << "()->q->read();" << endl; - stream() << " }" << endl << endl; + stream() << " if (!s_global" << cfg().className << "()->q) {\n"; + stream() << " new " << cfg().className << ';' << '\n'; + stream() << " s_global" << cfg().className << "()->q->read();\n"; + stream() << " }" << endl << '\n'; } - stream() << " return s_global" << cfg().className << "()->q;" << endl; - stream() << "}" << endl << endl; + stream() << " return s_global" << cfg().className << "()->q;\n"; + stream() << "}" << endl << '\n'; if (parseResult.cfgFileNameArg) { auto instance = [this] (const QString &type, const QString &arg, bool isString) { - stream() << "void " << cfg().className << "::instance(" << type << " " << arg << ")" << endl; - stream() << "{" << endl; - stream() << " if (s_global" << cfg().className << "()->q) {" << endl; - stream() << " qDebug() << \"" << cfg().className << "::instance called after the first use - ignoring\";" << endl; - stream() << " return;" << endl; - stream() << " }" << endl; + stream() << "void " << cfg().className << "::instance(" << type << " " << arg << ")\n"; + stream() << "{\n"; + stream() << " if (s_global" << cfg().className << "()->q) {\n"; + stream() << " qDebug() << \"" << cfg().className << "::instance called after the first use - ignoring\";\n"; + stream() << " return;\n"; + stream() << " }\n"; stream() << " new " << cfg().className << "("; if (isString) { stream() << "KSharedConfig::openConfig(" << arg << ")"; } else { stream() << "std::move(" << arg << ")"; } - stream() << ");" << endl; - stream() << " s_global" << cfg().className << "()->q->read();" << endl; - stream() << "}" << endl << endl; + stream() << ");\n"; + stream() << " s_global" << cfg().className << "()->q->read();\n"; + stream() << "}" << endl << '\n'; }; instance(QStringLiteral("const QString&"), QStringLiteral("cfgfilename"), true); instance(QStringLiteral("KSharedConfig::Ptr"), QStringLiteral("config"), false); } } void KConfigSourceGenerator::createPreamble() { QString cppPreamble; for (const auto entry : parseResult.entries) { if (entry->paramValues.isEmpty()) { continue; } cppPreamble += QStringLiteral("const char* const ") + cfg().className + QStringLiteral("::") + enumName(entry->param); cppPreamble += cfg().globalEnums ? QStringLiteral("ToString[] = { \"") + entry->paramValues.join(QStringLiteral("\", \"")) + QStringLiteral("\" };\n") : QStringLiteral("::enumToString[] = { \"") + entry->paramValues.join(QStringLiteral("\", \"")) + QStringLiteral("\" };\n"); } if (!cppPreamble.isEmpty()) { - stream() << cppPreamble << endl; + stream() << cppPreamble << '\n'; } } void KConfigSourceGenerator::createConstructorParameterList() { if (parseResult.cfgFileNameArg) { if (!cfg().forceStringFilename) { stream() << " KSharedConfig::Ptr config"; } else { stream() << " const QString& config"; } stream() << (parseResult.parameters.isEmpty() ? "" : ","); } for (QList::ConstIterator it = parseResult.parameters.constBegin(); it != parseResult.parameters.constEnd(); ++it) { if (it != parseResult.parameters.constBegin()) { stream() << ","; } stream() << " " << param((*it).type) << " " << (*it).name; } if (cfg().parentInConstructor) { if (parseResult.cfgFileNameArg || !parseResult.parameters.isEmpty()) { stream() << ","; } stream() << " QObject *parent"; } } void KConfigSourceGenerator::createParentConstructorCall() { stream() << cfg().inherits << "("; if (!parseResult.cfgFileName.isEmpty()) { stream() << " QStringLiteral( \"" << parseResult.cfgFileName << "\" "; } if (parseResult.cfgFileNameArg) { if (! cfg().forceStringFilename) { stream() << " std::move( config ) "; } else { stream() << " config "; } } if (!parseResult.cfgFileName.isEmpty()) { stream() << ") "; } - stream() << ")" << endl; + stream() << ")\n"; } void KConfigSourceGenerator::createInitializerList() { for (const auto ¶meter : parseResult.parameters) { - stream() << " , mParam" << parameter.name << "(" << parameter.name << ")" << endl; + stream() << " , mParam" << parameter.name << "(" << parameter.name << ")\n"; } if (parseResult.hasNonModifySignals && !cfg().dpointer) { - stream() << " , " << varName(QStringLiteral("settingsChanged"), cfg()) << "(0)" << endl; + stream() << " , " << varName(QStringLiteral("settingsChanged"), cfg()) << "(0)\n"; } } void KConfigSourceGenerator::createEnums(const CfgEntry *entry) { if (entry->type != QLatin1String("Enum")) { return; } - stream() << " QList<" << cfg().inherits << "::ItemEnum::Choice> values" << entry->name << ";" << endl; + stream() << " QList<" << cfg().inherits << "::ItemEnum::Choice> values" << entry->name << ";\n"; for (const auto &choice : qAsConst(entry->choices.choices)) { - stream() << " {" << endl; - stream() << " " << cfg().inherits << "::ItemEnum::Choice choice;" << endl; - stream() << " choice.name = QStringLiteral(\"" << choice.name << "\");" << endl; + stream() << " {\n"; + stream() << " " << cfg().inherits << "::ItemEnum::Choice choice;\n"; + stream() << " choice.name = QStringLiteral(\"" << choice.name << "\");\n"; if (cfg().setUserTexts) { if (!choice.label.isEmpty()) { stream() << " choice.label = " << translatedString(cfg(), choice.label, choice.context) - << ";" << endl; + << ";\n"; } if (!choice.toolTip.isEmpty()) { stream() << " choice.toolTip = " << translatedString(cfg(), choice.toolTip, choice.context) - << ";" << endl; + << ";\n"; } if (!choice.whatsThis.isEmpty()) { stream() << " choice.whatsThis = " << translatedString(cfg(), choice.whatsThis, choice.context) - << ";" << endl; + << ";\n"; } } - stream() << " values" << entry->name << ".append( choice );" << endl; - stream() << " }" << endl; + stream() << " values" << entry->name << ".append( choice );\n"; + stream() << " }\n"; } } void KConfigSourceGenerator::createNormalEntry(const CfgEntry *entry, const QString &key) { stream() << " " << itemPath(entry, cfg()) << " = " - << newItem(entry, key, entry->defaultValue, cfg()) << endl; + << newItem(entry, key, entry->defaultValue, cfg()) << '\n'; if (!entry->min.isEmpty()) { - stream() << " " << itemPath(entry, cfg()) << "->setMinValue(" << entry->min << ");" << endl; + stream() << " " << itemPath(entry, cfg()) << "->setMinValue(" << entry->min << ");\n"; } if (!entry->max.isEmpty()) { - stream() << " " << itemPath(entry, cfg()) << "->setMaxValue(" << entry->max << ");" << endl; + stream() << " " << itemPath(entry, cfg()) << "->setMaxValue(" << entry->max << ");\n"; } if (cfg().setUserTexts) { stream() << userTextsFunctions(entry, cfg()); } if (cfg().allNotifiers || cfg().notifiers.contains(entry->name)) { - stream() << " " << itemPath(entry, cfg()) << "->setWriteFlags(KConfigBase::Notify);" << endl; + stream() << " " << itemPath(entry, cfg()) << "->setWriteFlags(KConfigBase::Notify);\n"; } stream() << " addItem( " << itemPath(entry, cfg()); QString quotedName = entry->name; addQuotes(quotedName); if (quotedName != key) { stream() << ", QStringLiteral( \"" << entry->name << "\" )"; } - stream() << " );" << endl; + stream() << " );\n"; } void KConfigSourceGenerator::createIndexedEntry(const CfgEntry *entry, const QString &key) { for (int i = 0; i <= entry->paramMax; i++) { QString itemVarStr(itemPath(entry, cfg()) + QStringLiteral("[%1]").arg(i)); QString defaultStr = !entry->paramDefaultValues[i].isEmpty() ? entry->paramDefaultValues[i] : !entry->defaultValue.isEmpty() ? paramString(entry->defaultValue, entry, i) : defaultValue(entry->type); stream() << " " << itemVarStr << " = " - << newItem(entry, paramString(key, entry, i), defaultStr, cfg(), QStringLiteral("[%1]").arg(i)) << endl; + << newItem(entry, paramString(key, entry, i), defaultStr, cfg(), QStringLiteral("[%1]").arg(i)) << '\n'; if (cfg().setUserTexts) { stream() << userTextsFunctions(entry, cfg(), itemVarStr, entry->paramName); } // Make mutators for enum parameters work by adding them with $(..) replaced by the // param name. The check for isImmutable in the set* functions doesn't have the param // name available, just the corresponding enum value (int), so we need to store the // param names in a separate static list!. const bool isEnum = entry->paramType == QLatin1String("Enum"); const QString arg = isEnum ? entry->paramValues[i] : QString::number(i); QString paramName = entry->paramName; stream() << " addItem( " << itemVarStr << ", QStringLiteral( \""; stream() << paramName.replace(QStringLiteral("$(") + entry->param + QLatin1Char(')'), QLatin1String("%1")).arg( arg ); - stream() << "\" ) );" << endl; + stream() << "\" ) );\n"; } } void KConfigSourceGenerator::handleCurrentGroupChange(const CfgEntry *entry) { if (entry->group == mCurrentGroup) { return; } // HACK: This fixes one spacing line in the diff. Remove this in the future and adapt the testcases. static bool first = true; if (!entry->group.isEmpty()) { if (!first) { - stream() << endl; + stream() << '\n'; } first = false; } mCurrentGroup = entry->group; stream() << " setCurrentGroup( " << paramString(mCurrentGroup, parseResult.parameters) << " );"; - stream() << endl << endl; + stream() << endl << '\n'; } void KConfigSourceGenerator::doConstructor() { // Constructor stream() << cfg().className << "::" << cfg().className << "("; createConstructorParameterList(); - stream() << " )" << endl; + stream() << " )\n"; stream() << " : "; createParentConstructorCall(); createInitializerList(); - stream() << "{" << endl; + stream() << "{\n"; if (cfg().parentInConstructor) { - stream() << " setParent(parent);" << endl; + stream() << " setParent(parent);\n"; } if (cfg().dpointer) { - stream() << " d = new " << cfg().className << "Private;" << endl; + stream() << " d = new " << cfg().className << "Private;\n"; if (parseResult.hasNonModifySignals) { - stream() << " " << varPath(QStringLiteral("settingsChanged"), cfg()) << " = 0;" << endl; + stream() << " " << varPath(QStringLiteral("settingsChanged"), cfg()) << " = 0;\n"; } } // Needed in case the singleton class is used as baseclass for // another singleton. if (cfg().singleton) { - stream() << " Q_ASSERT(!s_global" << cfg().className << "()->q);" << endl; - stream() << " s_global" << cfg().className << "()->q = this;" << endl; + stream() << " Q_ASSERT(!s_global" << cfg().className << "()->q);\n"; + stream() << " s_global" << cfg().className << "()->q = this;\n"; } if (!parseResult.signalList.isEmpty()) { // this cast to base-class pointer-to-member is valid C++ // https://stackoverflow.com/questions/4272909/is-it-safe-to-upcast-a-method-pointer-and-use-it-with-base-class-pointer/ stream() << " KConfigCompilerSignallingItem::NotifyFunction notifyFunction =" << " static_cast(&" - << cfg().className << "::itemChanged);" << endl; + << cfg().className << "::itemChanged);\n"; - stream() << endl; + stream() << '\n'; } for (auto *entry : parseResult.entries) { handleCurrentGroupChange(entry); const QString key = paramString(entry->key, parseResult.parameters); if (!entry->code.isEmpty()) { - stream() << entry->code << endl; + stream() << entry->code << '\n'; } createEnums(entry); if (!cfg().dpointer) { stream() << itemDeclaration(entry, cfg()); } if (entry->param.isEmpty()) { createNormalEntry(entry, key); } else { createIndexedEntry(entry, key); } } - stream() << "}" << endl << endl; + stream() << "}\n\n"; } void KConfigSourceGenerator::createGetterDPointerMode(const CfgEntry *entry) { // Accessor if (cfg().useEnumTypes && entry->type == QLatin1String("Enum")) { stream() << enumType(entry, cfg().globalEnums); } else { stream() << cppType(entry->type); } stream() << " " << getFunction(entry->name, cfg().className) << "("; if (!entry->param.isEmpty()) { stream() << " " << cppType(entry->paramType) << " i "; } - stream() << ")" << Const() << endl; + stream() << ")" << Const() << '\n'; // function body inline only if not using dpointer // for BC mode startScope(); // HACK: Fix memberAccessorBody stream() << " " << memberAccessorBody(entry, cfg().globalEnums); endScope(); - stream() << endl; + stream() << '\n'; } void KConfigSourceGenerator::createSetterDPointerMode(const CfgEntry *entry) { // Manipulator if (!(cfg().allMutators || cfg().mutators.contains(entry->name))) { return; } stream() << "void " << setFunction(entry->name, cfg().className) << "( "; if (!entry->param.isEmpty()) { stream() << cppType(entry->paramType) << " i, "; } if (cfg().useEnumTypes && entry->type == QLatin1String("Enum")) { stream() << enumType(entry, cfg().globalEnums); } else { stream() << param(entry->type); } - stream() << " v )" << endl; + stream() << " v )\n"; // function body inline only if not using dpointer // for BC mode startScope(); memberMutatorBody(entry); endScope(); - stream() << endl; + stream() << '\n'; } void KConfigSourceGenerator::createItemGetterDPointerMode(const CfgEntry *entry) { // Item accessor if (!cfg().itemAccessors) { return; } - stream() << endl; + stream() << '\n'; stream() << cfg().inherits << "::Item" << itemType(entry->type) << " *" << getFunction(entry->name, cfg().className) << "Item("; if (!entry->param.isEmpty()) { stream() << " " << cppType(entry->paramType) << " i "; } - stream() << ")" << endl; + stream() << ")\n"; startScope(); stream() << " " << itemAccessorBody(entry, cfg()); endScope(); } void KConfigSourceGenerator::doGetterSetterDPointerMode() { if (!cfg().dpointer) { return; } // setters and getters go in Cpp if in dpointer mode for (auto *entry : parseResult.entries) { createSetterDPointerMode(entry); createGetterDPointerMode(entry); createItemGetterDPointerMode(entry); - stream() << endl; + stream() << '\n'; } } void KConfigSourceGenerator::createDefaultValueGetterSetter() { // default value getters always go in Cpp for (auto *entry : parseResult.entries) { QString n = entry->name; QString t = entry->type; // Default value Accessor, as "helper" function if ((cfg().allDefaultGetters || cfg().defaultGetters.contains(n)) && !entry->defaultValue.isEmpty()) { stream() << cppType(t) << " " << getDefaultFunction(n, cfg().className) << "_helper("; if (!entry->param.isEmpty()) { stream() << " " << cppType(entry->paramType) << " i "; } - stream() << ")" << Const() << endl; + stream() << ")" << Const() << '\n'; startScope(); - stream() << memberGetDefaultBody(entry) << endl; + stream() << memberGetDefaultBody(entry) << '\n'; endScope(); - stream() << endl; + stream() << '\n'; } } } void KConfigSourceGenerator::createDestructor() { - stream() << cfg().className << "::~" << cfg().className << "()" << endl; + stream() << cfg().className << "::~" << cfg().className << "()\n"; startScope(); if (cfg().dpointer) { - stream() << " delete d;" << endl; + stream() << " delete d;\n"; } if (cfg().singleton) { - stream() << " s_global" << cfg().className << "()->q = nullptr;" << endl; + stream() << " s_global" << cfg().className << "()->q = nullptr;\n"; } endScope(); - stream() << endl; + stream() << '\n'; } void KConfigSourceGenerator::createNonModifyingSignalsHelper() { if (!parseResult.hasNonModifySignals) { return; } - stream() << "bool " << cfg().className << "::" << "usrSave()" << endl; + stream() << "bool " << cfg().className << "::" << "usrSave()\n"; startScope(); - stream() << " const bool res = " << cfg().inherits << "::usrSave();" << endl; - stream() << " if (!res) return false;" << endl << endl; + stream() << " const bool res = " << cfg().inherits << "::usrSave();\n"; + stream() << " if (!res) return false;\n\n"; for (const Signal &signal : parseResult.signalList) { if (signal.modify) { continue; } - stream() << " if ( " << varPath(QStringLiteral("settingsChanged"), cfg()) << " & " << signalEnumName(signal.name) << " )" << endl; + stream() << " if ( " << varPath(QStringLiteral("settingsChanged"), cfg()) << " & " << signalEnumName(signal.name) << " )\n"; stream() << " Q_EMIT " << signal.name << "("; QList::ConstIterator it, itEnd = signal.arguments.constEnd(); for (it = signal.arguments.constBegin(); it != itEnd;) { Param argument = *it; bool cast = false; if (cfg().useEnumTypes && argument.type == QLatin1String("Enum")) { for (int i = 0, end = parseResult.entries.count(); i < end; ++i) { if (parseResult.entries.at(i)->name == argument.name) { stream() << "static_cast<" << enumType(parseResult.entries.at(i), cfg().globalEnums) << ">("; cast = true; break; } } } stream() << varPath(argument.name, cfg()); if (cast) { stream() << ")"; } if (++it != itEnd) { stream() << ", "; } } - stream() << ");" << endl; + stream() << ");\n"; } - stream() << " " << varPath(QStringLiteral("settingsChanged"), cfg()) << " = 0;" << endl; - stream() << " return true;" << endl; + stream() << " " << varPath(QStringLiteral("settingsChanged"), cfg()) << " = 0;\n"; + stream() << " return true;\n"; endScope(); } void KConfigSourceGenerator::createSignalFlagsHandler() { if (parseResult.signalList.isEmpty()) { return; } - stream() << endl; - stream() << "void " << cfg().className << "::" << "itemChanged(quint64 flags) {" << endl; + stream() << '\n'; + stream() << "void " << cfg().className << "::" << "itemChanged(quint64 flags) {\n"; if (parseResult.hasNonModifySignals) - stream() << " " << varPath(QStringLiteral("settingsChanged"), cfg()) << " |= flags;" << endl; + stream() << " " << varPath(QStringLiteral("settingsChanged"), cfg()) << " |= flags;\n"; if (!parseResult.signalList.isEmpty()) - stream() << endl; + stream() << '\n'; for (const Signal &signal : parseResult.signalList) { if (signal.modify) { - stream() << " if ( flags & " << signalEnumName(signal.name) << " ) {" << endl; - stream() << " Q_EMIT " << signal.name << "();" << endl; - stream() << " }" << endl; + stream() << " if ( flags & " << signalEnumName(signal.name) << " ) {\n"; + stream() << " Q_EMIT " << signal.name << "();\n"; + stream() << " }\n"; } } - stream() << "}" << endl; + stream() << "}\n"; } void KConfigSourceGenerator::includeMoc() { const QString mocFileName = cfg().baseName + QStringLiteral(".moc"); if (parseResult.signalList.count() || cfg().generateProperties) { // Add includemoc if they are signals defined. - stream() << endl; - stream() << "#include \"" << mocFileName << "\"" << endl; - stream() << endl; + stream() << '\n'; + stream() << "#include \"" << mocFileName << "\"\n"; + stream() << '\n'; } } diff --git a/src/kconfig_compiler/KConfigXmlParser.cpp b/src/kconfig_compiler/KConfigXmlParser.cpp index 11c76ab..b403dbd 100644 --- a/src/kconfig_compiler/KConfigXmlParser.cpp +++ b/src/kconfig_compiler/KConfigXmlParser.cpp @@ -1,564 +1,557 @@ /* This file is part of the KDE libraries Copyright (c) 2003 Cornelius Schumacher Copyright (c) 2003 Waldo Bastian Copyright (c) 2003 Zack Rusin Copyright (c) 2006 Michaël Larouche Copyright (c) 2008 Allen Winter Copyright (C) 2020 Tomaz Cananbrava (tcanabrava@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 "KConfigXmlParser.h" #include "KConfigParameters.h" #include #include #include #include #include #include #include - -namespace -{ -QTextStream cout(stdout); -QTextStream cerr(stderr); -} - +#include //TODO: Move preprocessDefault to Header / CPP implementation. // it makes no sense for a parser to process those values and generate code. static void preProcessDefault(QString &defaultValue, const QString &name, const QString &type, const CfgEntry::Choices &choices, QString &code, const KConfigParameters &cfg) { if (type == QLatin1String("String") && !defaultValue.isEmpty()) { defaultValue = literalString(defaultValue); } else if (type == QLatin1String("Path") && !defaultValue.isEmpty()) { defaultValue = literalString(defaultValue); } else if (type == QLatin1String("Url") && !defaultValue.isEmpty()) { // Use fromUserInput in order to support absolute paths and absolute urls, like KDE4's KUrl(QString) did. defaultValue = QLatin1String("QUrl::fromUserInput( ") + literalString(defaultValue) + QLatin1Char(')'); } else if ((type == QLatin1String("UrlList") || type == QLatin1String("StringList") || type == QLatin1String("PathList")) && !defaultValue.isEmpty()) { QTextStream cpp(&code, QIODevice::WriteOnly | QIODevice::Append); if (!code.isEmpty()) { - cpp << endl; + cpp << '\n'; } if (type == QLatin1String("UrlList")) { - cpp << " QList default" << name << ";" << endl; + cpp << " QList default" << name << ";\n"; } else { - cpp << " QStringList default" << name << ";" << endl; + cpp << " QStringList default" << name << ";\n"; } const QStringList defaults = defaultValue.split(QLatin1Char(',')); QStringList::ConstIterator it; for (it = defaults.constBegin(); it != defaults.constEnd(); ++it) { cpp << " default" << name << ".append( "; if (type == QLatin1String("UrlList")) { cpp << "QUrl::fromUserInput("; } cpp << "QString::fromUtf8( \"" << *it << "\" ) "; if (type == QLatin1String("UrlList")) { cpp << ") "; } - cpp << ");" << endl; + cpp << ");\n"; } defaultValue = QLatin1String("default") + name; } else if (type == QLatin1String("Color") && !defaultValue.isEmpty()) { const QRegularExpression colorRe(QRegularExpression::anchoredPattern( QStringLiteral("\\d+,\\s*\\d+,\\s*\\d+(,\\s*\\d+)?"))); if (colorRe.match(defaultValue).hasMatch()) { defaultValue = QLatin1String("QColor( ") + defaultValue + QLatin1String(" )"); } else { defaultValue = QLatin1String("QColor( \"") + defaultValue + QLatin1String("\" )"); } } else if (type == QLatin1String("Enum")) { QList::ConstIterator it; for (it = choices.choices.constBegin(); it != choices.choices.constEnd(); ++it) { if ((*it).name == defaultValue) { if (cfg.globalEnums && choices.name().isEmpty()) { defaultValue.prepend(choices.prefix); } else { defaultValue.prepend(enumTypeQualifier(name, choices) + choices.prefix); } break; } } } else if (type == QLatin1String("IntList")) { QTextStream cpp(&code, QIODevice::WriteOnly | QIODevice::Append); if (!code.isEmpty()) { - cpp << endl; + cpp << '\n'; } - cpp << " QList default" << name << ";" << endl; + cpp << " QList default" << name << ";\n"; if (!defaultValue.isEmpty()) { const QStringList defaults = defaultValue.split(QLatin1Char(',')); QStringList::ConstIterator it; for (it = defaults.constBegin(); it != defaults.constEnd(); ++it) { - cpp << " default" << name << ".append( " << *it << " );" - << endl; + cpp << " default" << name << ".append( " << *it << " );\n"; } } defaultValue = QLatin1String("default") + name; } } static QString dumpNode(const QDomNode &node) { QString msg; QTextStream s(&msg, QIODevice::WriteOnly); node.save(s, 0); msg = msg.simplified(); if (msg.length() > 40) { return msg.left(37) + QLatin1String("..."); } return msg; } void KConfigXmlParser::readParameterFromEntry(CfgEntry &readEntry, const QDomElement &e) { readEntry.param = e.attribute(QStringLiteral("name")); readEntry.paramType = e.attribute(QStringLiteral("type")); if (readEntry.param.isEmpty()) { - cerr << "Parameter must have a name: " << dumpNode(e) << endl; + std::cerr << "Parameter must have a name: " << qPrintable(dumpNode(e)) << std::endl; exit (1); } if (readEntry.paramType.isEmpty()) { - cerr << "Parameter must have a type: " << dumpNode(e) << endl; + std::cerr << "Parameter must have a type: " << qPrintable(dumpNode(e)) << std::endl; exit(1); } if ((readEntry.paramType == QLatin1String("Int")) || (readEntry.paramType == QLatin1String("UInt"))) { bool ok; readEntry.paramMax = e.attribute(QStringLiteral("max")).toInt(&ok); if (!ok) { - cerr << "Integer parameter must have a maximum (e.g. max=\"0\"): " << dumpNode(e) << endl; + std::cerr << "Integer parameter must have a maximum (e.g. max=\"0\"): " << qPrintable(dumpNode(e)) << std::endl; exit(1); } } else if (readEntry.paramType == QLatin1String("Enum")) { for (QDomElement e2 = e.firstChildElement(); !e2.isNull(); e2 = e2.nextSiblingElement()) { if (e2.tagName() == QLatin1String("values")) { for (QDomElement e3 = e2.firstChildElement(); !e3.isNull(); e3 = e3.nextSiblingElement()) { if (e3.tagName() == QLatin1String("value")) { readEntry.paramValues.append(e3.text()); } } break; } } if (readEntry.paramValues.isEmpty()) { - cerr << "No values specified for parameter '" << readEntry.param << "'." << endl; + std::cerr << "No values specified for parameter '" << qPrintable(readEntry.param) << "'." << std::endl; exit(1); } readEntry.paramMax = readEntry.paramValues.count() - 1; } else { - cerr << "Parameter '" << readEntry.param << "' has type " << readEntry.paramType - << " but must be of type int, uint or Enum." << endl; + std::cerr << "Parameter '" << qPrintable(readEntry.param) << "' has type " << qPrintable(readEntry.paramType) + << " but must be of type int, uint or Enum." << std::endl; exit(1); } } bool KConfigXmlParser::hasDefaultCode(CfgEntry &readEntry, const QDomElement &element) { for (QDomElement e = element.firstChildElement(); !e.isNull(); e = e.nextSiblingElement()) { if (e.attribute(QStringLiteral("param")).isEmpty()) { if (e.attribute(QStringLiteral("code")) == QLatin1String("true")) { return true; } } } return false; } void KConfigXmlParser::readChoicesFromEntry(CfgEntry &readEntry, const QDomElement &e) { QList chlist; for (QDomElement e2 = e.firstChildElement(); !e2.isNull(); e2 = e2.nextSiblingElement()) { if (e2.tagName() != QLatin1String("choice")) { continue; } CfgEntry::Choice choice; choice.name = e2.attribute(QStringLiteral("name")); if (choice.name.isEmpty()) { - cerr << "Tag requires attribute 'name'." << endl; + std::cerr << "Tag requires attribute 'name'." << std::endl; } for (QDomElement e3 = e2.firstChildElement(); !e3.isNull(); e3 = e3.nextSiblingElement()) { if (e3.tagName() == QLatin1String("label")) { choice.label = e3.text(); choice.context = e3.attribute(QStringLiteral("context")); } if (e3.tagName() == QLatin1String("tooltip")) { choice.toolTip = e3.text(); choice.context = e3.attribute(QStringLiteral("context")); } if (e3.tagName() == QLatin1String("whatsthis")) { choice.whatsThis = e3.text(); choice.context = e3.attribute(QStringLiteral("context")); } } chlist.append(choice); } QString name = e.attribute(QStringLiteral("name")); QString prefix = e.attribute(QStringLiteral("prefix")); readEntry.choices = CfgEntry::Choices(chlist, name, prefix); } void KConfigXmlParser::readGroupElements(CfgEntry &readEntry, const QDomElement &element) { for (QDomElement e = element.firstChildElement(); !e.isNull(); e = e.nextSiblingElement()) { QString tag = e.tagName(); if (tag == QLatin1String("label")) { readEntry.label = e.text(); readEntry.labelContext = e.attribute(QStringLiteral("context")); } else if (tag == QLatin1String("tooltip")) { readEntry.toolTip = e.text(); readEntry.toolTipContext = e.attribute(QStringLiteral("context")); } else if (tag == QLatin1String("whatsthis")) { readEntry.whatsThis = e.text(); readEntry.whatsThisContext = e.attribute(QStringLiteral("context")); } else if (tag == QLatin1String("min")) { readEntry.min = e.text(); } else if (tag == QLatin1String("max")) { readEntry.max = e.text(); } else if (tag == QLatin1String("code")) { readEntry.code = e.text(); } else if (tag == QLatin1String("parameter")) { readParameterFromEntry(readEntry, e); } else if (tag == QLatin1String("default")) { if (e.attribute(QStringLiteral("param")).isEmpty()) { readEntry.defaultValue = e.text(); } } else if (tag == QLatin1String("choices")) { readChoicesFromEntry(readEntry, e); } else if (tag == QLatin1String("emit")) { Signal signal; signal.name = e.attribute(QStringLiteral("signal")); readEntry.signalList.append(signal); } } } void KConfigXmlParser::createChangedSignal(CfgEntry &readEntry) { if (cfg.generateProperties && (cfg.allMutators || cfg.mutators.contains(readEntry.name))) { Signal s; s.name = changeSignalName(readEntry.name); s.modify = true; readEntry.signalList.append(s); } } void KConfigXmlParser::validateNameAndKey(CfgEntry &readEntry, const QDomElement &element) { bool nameIsEmpty = readEntry.name.isEmpty(); if (nameIsEmpty && readEntry.key.isEmpty()) { - cerr << "Entry must have a name or a key: " << dumpNode(element) << endl; + std::cerr << "Entry must have a name or a key: " << qPrintable(dumpNode(element)) << std::endl; exit (1); } if (readEntry.key.isEmpty()) { readEntry.key = readEntry.name; } if (nameIsEmpty) { readEntry.name = readEntry.key; readEntry.name.remove(QLatin1Char(' ')); } else if (readEntry.name.contains(QLatin1Char(' '))) { - cout << "Entry '" << readEntry.name << "' contains spaces! elements can not contain spaces!" << endl; + std::cout << "Entry '" << qPrintable(readEntry.name) << "' contains spaces! elements can not contain spaces!" << std::endl; readEntry.name.remove(QLatin1Char(' ')); } if (readEntry.name.contains(QStringLiteral("$("))) { if (readEntry.param.isEmpty()) { - cerr << "Name may not be parameterized: " << readEntry.name << endl; + std::cerr << "Name may not be parameterized: " << qPrintable(readEntry.name) << std::endl; exit (1); } } else { if (!readEntry.param.isEmpty()) { - cerr << "Name must contain '$(" << readEntry.param << ")': " << readEntry.name << endl; + std::cerr << "Name must contain '$(" << qPrintable(readEntry.param) << ")': " << qPrintable(readEntry.name) << std::endl; exit (1); } } } void KConfigXmlParser::readParamDefaultValues(CfgEntry &readEntry, const QDomElement &element) { if (readEntry.param.isEmpty()) { return; } // Adjust name readEntry.paramName = readEntry.name; readEntry.name.remove(QStringLiteral("$(") + readEntry.param + QLatin1Char(')')); // Lookup defaults for indexed entries for (int i = 0; i <= readEntry.paramMax; i++) { readEntry.paramDefaultValues.append(QString()); } for (QDomElement e = element.firstChildElement(); !e.isNull(); e = e.nextSiblingElement()) { QString tag = e.tagName(); if (tag != QLatin1String("default")) { continue; } QString index = e.attribute(QStringLiteral("param")); if (index.isEmpty()) { continue; } bool ok; int i = index.toInt(&ok); if (!ok) { i = readEntry.paramValues.indexOf(index); if (i == -1) { - cerr << "Index '" << index << "' for default value is unknown." << endl; + std::cerr << "Index '" << qPrintable(index) << "' for default value is unknown." << std::endl; exit (1); } } if ((i < 0) || (i > readEntry.paramMax)) { - cerr << "Index '" << i << "' for default value is out of range [0, " << readEntry.paramMax << "]." << endl; + std::cerr << "Index '" << i << "' for default value is out of range [0, " << readEntry.paramMax << "]." << std::endl; exit (1); } QString tmpDefaultValue = e.text(); if (e.attribute(QStringLiteral("code")) != QLatin1String("true")) { preProcessDefault(tmpDefaultValue, readEntry.name, readEntry.type, readEntry.choices, readEntry.code, cfg); } readEntry.paramDefaultValues[i] = tmpDefaultValue; } } CfgEntry *KConfigXmlParser::parseEntry(const QString &group, const QDomElement &element) { CfgEntry readEntry; readEntry.type = element.attribute(QStringLiteral("type")); readEntry.name = element.attribute(QStringLiteral("name")); readEntry.key = element.attribute(QStringLiteral("key")); readEntry.hidden = element.attribute(QStringLiteral("hidden")) == QLatin1String("true");; readEntry.group = group; const bool nameIsEmpty = readEntry.name.isEmpty(); readGroupElements(readEntry, element); createChangedSignal(readEntry); validateNameAndKey(readEntry, element); if (readEntry.label.isEmpty()) { readEntry.label = readEntry.key; } if (readEntry.type.isEmpty()) { readEntry.type = QStringLiteral("String"); // XXX : implicit type might be bad } readParamDefaultValues(readEntry, element); if (!mValidNameRegexp.match(readEntry.name).hasMatch()) { if (nameIsEmpty) - cerr << "The key '" << readEntry.key << "' can not be used as name for the entry because " - "it is not a valid name. You need to specify a valid name for this entry." << endl; + std::cerr << "The key '" << qPrintable(readEntry.key) << "' can not be used as name for the entry because " + "it is not a valid name. You need to specify a valid name for this entry." << std::endl; else { - cerr << "The name '" << readEntry.name << "' is not a valid name for an entry." << endl; + std::cerr << "The name '" << qPrintable(readEntry.name) << "' is not a valid name for an entry." << std::endl; } exit (1); } if (mAllNames.contains(readEntry.name)) { if (nameIsEmpty) - cerr << "The key '" << readEntry.key << "' can not be used as name for the entry because " - "it does not result in a unique name. You need to specify a unique name for this entry." << endl; + std::cerr << "The key '" << qPrintable(readEntry.key) << "' can not be used as name for the entry because " + "it does not result in a unique name. You need to specify a unique name for this entry." << std::endl; else { - cerr << "The name '" << readEntry.name << "' is not unique." << endl; + std::cerr << "The name '" << qPrintable(readEntry.name) << "' is not unique." << std::endl; } exit (1); } mAllNames.append(readEntry.name); if (!hasDefaultCode(readEntry, element)) { // TODO: Move all the options to CfgEntry. preProcessDefault(readEntry.defaultValue, readEntry.name, readEntry.type, readEntry.choices, readEntry.code, cfg); } // TODO: Try to Just return the CfgEntry we populated instead of // creating another one to fill the code. CfgEntry *result = new CfgEntry(); result->group = readEntry.group; result->type = readEntry.type; result->key = readEntry.key; result->name = readEntry.name; result->labelContext = readEntry.labelContext; result->label = readEntry.label; result->toolTipContext = readEntry.toolTipContext; result->toolTip = readEntry.toolTip; result->whatsThisContext = readEntry.whatsThisContext; result->whatsThis = readEntry.whatsThis; result->code = readEntry.code; result->defaultValue = readEntry.defaultValue; result->choices = readEntry.choices; result->signalList = readEntry.signalList; result->hidden = readEntry.hidden; if (!readEntry.param.isEmpty()) { result->param = readEntry.param; result->paramName = readEntry.paramName; result->paramType = readEntry.paramType; result->paramValues = readEntry.paramValues; result->paramDefaultValues = readEntry.paramDefaultValues; result->paramMax = readEntry.paramMax; } result->min = readEntry.min; result->max = readEntry.max; return result; } // TODO: Change the name of the config variable. KConfigXmlParser::KConfigXmlParser(const KConfigParameters &cfg, const QString &inputFileName) : cfg(cfg), mInputFileName(inputFileName) { mValidNameRegexp.setPattern(QRegularExpression::anchoredPattern(QStringLiteral("[a-zA-Z_][a-zA-Z0-9_]*"))); } void KConfigXmlParser::start() { QFile input(mInputFileName); QDomDocument doc; QString errorMsg; int errorRow; int errorCol; if (!doc.setContent(&input, &errorMsg, &errorRow, &errorCol)) { - cerr << "Unable to load document." << endl; - cerr << "Parse error in " << mInputFileName << ", line " << errorRow << ", col " << errorCol << ": " << errorMsg << endl; + std::cerr << "Unable to load document." << std::endl; + std::cerr << "Parse error in " << qPrintable(mInputFileName) << ", line " << errorRow << ", col " << errorCol << ": " << qPrintable(errorMsg) << std::endl; exit (1); } QDomElement cfgElement = doc.documentElement(); if (cfgElement.isNull()) { - cerr << "No document in kcfg file" << endl; + std::cerr << "No document in kcfg file" << std::endl; exit (1); } for (QDomElement element = cfgElement.firstChildElement(); !element.isNull(); element = element.nextSiblingElement()) { QString tag = element.tagName(); if (tag == QLatin1String("include")) { readIncludeTag(element); } else if (tag == QLatin1String("kcfgfile")) { readKcfgfileTag(element); } else if (tag == QLatin1String("group")) { readGroupTag(element); } else if (tag == QLatin1String("signal")) { readSignalTag(element); } } } ParseResult KConfigXmlParser::getParseResult() const { return mParseResult; } void KConfigXmlParser::readIncludeTag(const QDomElement &e) { QString includeFile = e.text(); if (!includeFile.isEmpty()) { mParseResult.includes.append(includeFile); } } void KConfigXmlParser::readGroupTag(const QDomElement &e) { QString group = e.attribute(QStringLiteral("name")); if (group.isEmpty()) { - cerr << "Group without name" << endl; + std::cerr << "Group without name" << std::endl; exit (1); } for (QDomElement e2 = e.firstChildElement(); !e2.isNull(); e2 = e2.nextSiblingElement()) { if (e2.tagName() != QLatin1String("entry")) { continue; } CfgEntry *entry = parseEntry(group, e2); if (entry) { mParseResult.entries.append(entry); } else { - cerr << "Can not parse entry." << endl; + std::cerr << "Can not parse entry." << std::endl; exit (1); } } } void KConfigXmlParser::readKcfgfileTag(const QDomElement &e) { mParseResult.cfgFileName = e.attribute(QStringLiteral("name")); mParseResult.cfgFileNameArg = e.attribute(QStringLiteral("arg")).toLower() == QLatin1String("true"); for (QDomElement e2 = e.firstChildElement(); !e2.isNull(); e2 = e2.nextSiblingElement()) { if (e2.tagName() == QLatin1String("parameter")) { Param p; p.name = e2.attribute(QStringLiteral("name")); p.type = e2.attribute(QStringLiteral("type")); if (p.type.isEmpty()) { p.type = QStringLiteral("String"); } mParseResult.parameters.append(p); } } } void KConfigXmlParser::readSignalTag(const QDomElement &e) { QString signalName = e.attribute(QStringLiteral("name")); if (signalName.isEmpty()) { - cerr << "Signal without name." << endl; + std::cerr << "Signal without name." << std::endl; exit (1); } Signal theSignal; theSignal.name = signalName; for (QDomElement e2 = e.firstChildElement(); !e2.isNull(); e2 = e2.nextSiblingElement()) { if (e2.tagName() == QLatin1String("argument")) { Param argument; argument.type = e2.attribute(QStringLiteral("type")); if (argument.type.isEmpty()) { - cerr << "Signal argument without type." << endl; + std::cerr << "Signal argument without type." << std::endl; exit (1); } argument.name= e2.text(); theSignal.arguments.append(argument); } else if (e2.tagName() == QLatin1String("label")) { theSignal.label = e2.text(); } } mParseResult.signalList.append(theSignal); } diff --git a/src/kconfig_compiler/kconfig_compiler.cpp b/src/kconfig_compiler/kconfig_compiler.cpp index 146ff88..11becf5 100644 --- a/src/kconfig_compiler/kconfig_compiler.cpp +++ b/src/kconfig_compiler/kconfig_compiler.cpp @@ -1,782 +1,776 @@ /* This file is part of KDE. Copyright (c) 2003 Cornelius Schumacher Copyright (c) 2003 Waldo Bastian Copyright (c) 2003 Zack Rusin Copyright (c) 2006 Michaël Larouche Copyright (c) 2008 Allen Winter 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. */ // Compiling this file with this flag is just crazy #undef QT_NO_CAST_FROM_ASCII #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../../kconfig_version.h" #include "KConfigParameters.h" #include "KConfigCommonStructs.h" #include "KConfigHeaderGenerator.h" #include "KConfigSourceGenerator.h" #include "KConfigXmlParser.h" -namespace -{ -QTextStream cout(stdout); -QTextStream cerr(stderr); -} - QString varName(const QString &n, const KConfigParameters &cfg) { QString result; if (!cfg.dpointer) { result = QChar::fromLatin1('m') + n; result[1] = result[1].toUpper(); } else { result = n; result[0] = result[0].toLower(); } return result; } QString varPath(const QString &n, const KConfigParameters &cfg) { QString result; if (cfg.dpointer) { result = "d->" + varName(n, cfg); } else { result = varName(n, cfg); } return result; } QString enumName(const QString &n) { QString result = QLatin1String("Enum") + n; result[4] = result[4].toUpper(); return result; } QString enumName(const QString &n, const CfgEntry::Choices &c) { QString result = c.name(); if (result.isEmpty()) { result = QLatin1String("Enum") + n; result[4] = result[4].toUpper(); } return result; } QString enumType(const CfgEntry *e, bool globalEnums) { QString result = e->choices.name(); if (result.isEmpty()) { result = QLatin1String("Enum") + e->name; if (!globalEnums) { result += QLatin1String("::type"); } result[4] = result[4].toUpper(); } return result; } QString enumTypeQualifier(const QString &n, const CfgEntry::Choices &c) { QString result = c.name(); if (result.isEmpty()) { result = QLatin1String("Enum") + n + QLatin1String("::"); result[4] = result[4].toUpper(); } else if (c.external()) { result = c.externalQualifier(); } else { result.clear(); } return result; } QString setFunction(const QString &n, const QString &className) { QString result = QLatin1String("set") + n; result[3] = result[3].toUpper(); if (!className.isEmpty()) { result = className + QLatin1String("::") + result; } return result; } QString changeSignalName(const QString &n) { return n+QStringLiteral("Changed"); } QString getDefaultFunction(const QString &n, const QString &className) { QString result = QLatin1String("default") + n + QLatin1String("Value"); result[7] = result[7].toUpper(); if (!className.isEmpty()) { result = className + QLatin1String("::") + result; } return result; } QString getFunction(const QString &n, const QString &className) { QString result = n; result[0] = result[0].toLower(); if (!className.isEmpty()) { result = className + QLatin1String("::") + result; } return result; } void addQuotes(QString &s) { if (!s.startsWith(QLatin1Char('"'))) { s.prepend(QLatin1Char('"')); } if (!s.endsWith(QLatin1Char('"'))) { s.append(QLatin1Char('"')); } } static QString quoteString(const QString &s) { QString r = s; r.replace(QLatin1Char('\\'), QLatin1String("\\\\")); r.replace(QLatin1Char('\"'), QLatin1String("\\\"")); r.remove(QLatin1Char('\r')); r.replace(QLatin1Char('\n'), QLatin1String("\\n\"\n\"")); return QLatin1Char('\"') + r + QLatin1Char('\"'); } QString literalString(const QString &s) { bool isAscii = true; for (int i = s.length(); i--;) if (s[i].unicode() > 127) { isAscii = false; } if (isAscii) { return QLatin1String("QStringLiteral( ") + quoteString(s) + QLatin1String(" )"); } else { return QLatin1String("QString::fromUtf8( ") + quoteString(s) + QLatin1String(" )"); } } QString signalEnumName(const QString &signalName) { QString result; result = QLatin1String("signal") + signalName; result[6] = result[6].toUpper(); return result; } bool isUnsigned(const QString &type) { if (type == QLatin1String("UInt")) { return true; } if (type == QLatin1String("ULongLong")) { return true; } return false; } /** Return parameter declaration for given type. */ QString param(const QString &t) { const QString type = t.toLower(); if (type == QLatin1String("string")) { return QStringLiteral("const QString &"); } else if (type == QLatin1String("stringlist")) { return QStringLiteral("const QStringList &"); } else if (type == QLatin1String("font")) { return QStringLiteral("const QFont &"); } else if (type == QLatin1String("rect")) { return QStringLiteral("const QRect &"); } else if (type == QLatin1String("size")) { return QStringLiteral("const QSize &"); } else if (type == QLatin1String("color")) { return QStringLiteral("const QColor &"); } else if (type == QLatin1String("point")) { return QStringLiteral("const QPoint &"); } else if (type == QLatin1String("int")) { return QStringLiteral("int"); } else if (type == QLatin1String("uint")) { return QStringLiteral("uint"); } else if (type == QLatin1String("bool")) { return QStringLiteral("bool"); } else if (type == QLatin1String("double")) { return QStringLiteral("double"); } else if (type == QLatin1String("datetime")) { return QStringLiteral("const QDateTime &"); } else if (type == QLatin1String("longlong")) { return QStringLiteral("qint64"); } else if (type == QLatin1String("ulonglong")) { return QStringLiteral("quint64"); } else if (type == QLatin1String("intlist")) { return QStringLiteral("const QList &"); } else if (type == QLatin1String("enum")) { return QStringLiteral("int"); } else if (type == QLatin1String("path")) { return QStringLiteral("const QString &"); } else if (type == QLatin1String("pathlist")) { return QStringLiteral("const QStringList &"); } else if (type == QLatin1String("password")) { return QStringLiteral("const QString &"); } else if (type == QLatin1String("url")) { return QStringLiteral("const QUrl &"); } else if (type == QLatin1String("urllist")) { return QStringLiteral("const QList &"); } else { - cerr << "kconfig_compiler_kf5 does not support type \"" << type << "\"" << endl; + std::cerr << "kconfig_compiler_kf5 does not support type \"" << qPrintable(type) << "\"" << std::endl; return QStringLiteral("QString"); //For now, but an assert would be better } } /** Actual C++ storage type for given type. */ QString cppType(const QString &t) { const QString type = t.toLower(); if (type == QLatin1String("string")) { return QStringLiteral("QString"); } else if (type == QLatin1String("stringlist")) { return QStringLiteral("QStringList"); } else if (type == QLatin1String("font")) { return QStringLiteral("QFont"); } else if (type == QLatin1String("rect")) { return QStringLiteral("QRect"); } else if (type == QLatin1String("size")) { return QStringLiteral("QSize"); } else if (type == QLatin1String("color")) { return QStringLiteral("QColor"); } else if (type == QLatin1String("point")) { return QStringLiteral("QPoint"); } else if (type == QLatin1String("int")) { return QStringLiteral("int"); } else if (type == QLatin1String("uint")) { return QStringLiteral("uint"); } else if (type == QLatin1String("bool")) { return QStringLiteral("bool"); } else if (type == QLatin1String("double")) { return QStringLiteral("double"); } else if (type == QLatin1String("datetime")) { return QStringLiteral("QDateTime"); } else if (type == QLatin1String("longlong")) { return QStringLiteral("qint64"); } else if (type == QLatin1String("ulonglong")) { return QStringLiteral("quint64"); } else if (type == QLatin1String("intlist")) { return QStringLiteral("QList"); } else if (type == QLatin1String("enum")) { return QStringLiteral("int"); } else if (type == QLatin1String("path")) { return QStringLiteral("QString"); } else if (type == QLatin1String("pathlist")) { return QStringLiteral("QStringList"); } else if (type == QLatin1String("password")) { return QStringLiteral("QString"); } else if (type == QLatin1String("url")) { return QStringLiteral("QUrl"); } else if (type == QLatin1String("urllist")) { return QStringLiteral("QList"); } else { - cerr << "kconfig_compiler_kf5 does not support type \"" << type << "\"" << endl; + std::cerr << "kconfig_compiler_kf5 does not support type \"" << qPrintable(type) << "\"" << std::endl; return QStringLiteral("QString"); //For now, but an assert would be better } } QString defaultValue(const QString &t) { const QString type = t.toLower(); if (type == QLatin1String("string")) { return QStringLiteral("\"\""); // Use empty string, not null string! } else if (type == QLatin1String("stringlist")) { return QStringLiteral("QStringList()"); } else if (type == QLatin1String("font")) { return QStringLiteral("QFont()"); } else if (type == QLatin1String("rect")) { return QStringLiteral("QRect()"); } else if (type == QLatin1String("size")) { return QStringLiteral("QSize()"); } else if (type == QLatin1String("color")) { return QStringLiteral("QColor(128, 128, 128)"); } else if (type == QLatin1String("point")) { return QStringLiteral("QPoint()"); } else if (type == QLatin1String("int")) { return QStringLiteral("0"); } else if (type == QLatin1String("uint")) { return QStringLiteral("0"); } else if (type == QLatin1String("bool")) { return QStringLiteral("false"); } else if (type == QLatin1String("double")) { return QStringLiteral("0.0"); } else if (type == QLatin1String("datetime")) { return QStringLiteral("QDateTime()"); } else if (type == QLatin1String("longlong")) { return QStringLiteral("0"); } else if (type == QLatin1String("ulonglong")) { return QStringLiteral("0"); } else if (type == QLatin1String("intlist")) { return QStringLiteral("QList()"); } else if (type == QLatin1String("enum")) { return QStringLiteral("0"); } else if (type == QLatin1String("path")) { return QStringLiteral("\"\""); // Use empty string, not null string! } else if (type == QLatin1String("pathlist")) { return QStringLiteral("QStringList()"); } else if (type == QLatin1String("password")) { return QStringLiteral("\"\""); // Use empty string, not null string! } else if (type == QLatin1String("url")) { return QStringLiteral("QUrl()"); } else if (type == QLatin1String("urllist")) { return QStringLiteral("QList()"); } else { - cerr << "Error, kconfig_compiler_kf5 does not support the \"" << type << "\" type!" << endl; + std::cerr << "Error, kconfig_compiler_kf5 does not support the \"" << qPrintable(type) << "\" type!" << std::endl; return QStringLiteral("QString"); //For now, but an assert would be better } } QString itemType(const QString &type) { QString t; t = type; t.replace(0, 1, t.left(1).toUpper()); return t; } QString itemDeclaration(const CfgEntry *e, const KConfigParameters &cfg) { if (cfg.itemAccessors) { return QString(); } QString type; if (!e->signalList.isEmpty()) { type = QStringLiteral("KConfigCompilerSignallingItem"); } else { type = cfg.inherits + "::Item" + itemType(e->type); } QString fCap = e->name; fCap[0] = fCap[0].toUpper(); return " " + type + " *item" + fCap + ( (!e->param.isEmpty())?(QStringLiteral("[%1]").arg(e->paramMax+1)) : QString()) + ";\n"; } // returns the name of an item variable // use itemPath to know the full path // like using d-> in case of dpointer QString itemVar(const CfgEntry *e, const KConfigParameters &cfg) { QString result; if (cfg.itemAccessors) { if (!cfg.dpointer) { result = 'm' + e->name + "Item"; result[1] = result[1].toUpper(); } else { result = e->name + "Item"; result[0] = result[0].toLower(); } } else { result = "item" + e->name; result[4] = result[4].toUpper(); } return result; } QString itemPath(const CfgEntry *e, const KConfigParameters &cfg) { QString result; if (cfg.dpointer) { result = "d->" + itemVar(e, cfg); } else { result = itemVar(e, cfg); } return result; } QString newItem(const CfgEntry* entry, const QString &key, const QString& defaultValue, const KConfigParameters &cfg, const QString ¶m) { QList sigs = entry->signalList; QString t; if (!sigs.isEmpty()) { t += QLatin1String("new KConfigCompilerSignallingItem("); } t += "new "+ cfg.inherits + "::Item" + itemType(entry->type) + "( currentGroup(), " + key + ", " + varPath( entry->name, cfg ) + param; if (entry->type == QLatin1String("Enum")) { t += ", values" + entry->name; } if (!defaultValue.isEmpty()) { t += QLatin1String(", ") + defaultValue; } t += QLatin1String(" )"); if (!sigs.isEmpty()) { t += QLatin1String(", this, notifyFunction, "); //append the signal flags for (int i = 0; i < sigs.size(); ++i) { if (i != 0) t += QLatin1String(" | "); t += signalEnumName(sigs[i].name); } t += QLatin1String(")"); } t += QLatin1String(";"); return t; } QString paramString(const QString &s, const CfgEntry *e, int i) { QString result = s; QString needle = "$(" + e->param + ')'; if (result.contains(needle)) { QString tmp; if (e->paramType == QLatin1String("Enum")) { tmp = e->paramValues.at(i); } else { tmp = QString::number(i); } result.replace(needle, tmp); } return result; } QString paramString(const QString &group, const QList ¶meters) { QString paramString = group; QString arguments; int i = 1; for (QList::ConstIterator it = parameters.constBegin(); it != parameters.constEnd(); ++it) { if (paramString.contains("$(" + (*it).name + ')')) { const QString tmp = QStringLiteral("%%1").arg(i++); paramString.replace("$(" + (*it).name + ')', tmp); arguments += ".arg( mParam" + (*it).name + " )"; } } if (arguments.isEmpty()) { return "QStringLiteral( \"" + group + "\" )"; } return "QStringLiteral( \"" + paramString + "\" )" + arguments; } QString translatedString(const KConfigParameters &cfg, const QString &string, const QString &context, const QString ¶m, const QString ¶mValue) { QString result; switch (cfg.translationSystem) { case KConfigParameters::QtTranslation: if (!context.isEmpty()) { result += "/*: " + context + " */ QCoreApplication::translate(\""; } else { result += QLatin1String("QCoreApplication::translate(\""); } result += cfg.className + "\", "; break; case KConfigParameters::KdeTranslation: if (!cfg.translationDomain.isEmpty() && !context.isEmpty()) { result += "i18ndc(" + quoteString(cfg.translationDomain) + ", " + quoteString(context) + ", "; } else if (!cfg.translationDomain.isEmpty()) { result += "i18nd(" + quoteString(cfg.translationDomain) + ", "; } else if (!context.isEmpty()) { result += "i18nc(" + quoteString(context) + ", "; } else { result += QLatin1String("i18n("); } break; } if (!param.isEmpty()) { QString resolvedString = string; resolvedString.replace("$(" + param + ')', paramValue); result += quoteString(resolvedString); } else { result += quoteString(string); } result += ')'; return result; } /* int i is the value of the parameter */ QString userTextsFunctions(const CfgEntry *e, const KConfigParameters &cfg, QString itemVarStr, const QString &i) { QString txt; if (itemVarStr.isNull()) { itemVarStr = itemPath(e, cfg); } if (!e->label.isEmpty()) { txt += " " + itemVarStr + "->setLabel( "; txt += translatedString(cfg, e->label, e->labelContext, e->param, i); txt += QLatin1String(" );\n"); } if (!e->toolTip.isEmpty()) { txt += " " + itemVarStr + "->setToolTip( "; txt += translatedString(cfg, e->toolTip, e->toolTipContext, e->param, i); txt += QLatin1String(" );\n"); } if (!e->whatsThis.isEmpty()) { txt += " " + itemVarStr + "->setWhatsThis( "; txt += translatedString(cfg, e->whatsThis, e->whatsThisContext, e->param, i); txt += QLatin1String(" );\n"); } return txt; } // returns the member mutator implementation // which should go in the h file if inline // or the cpp file if not inline //TODO: Fix add Debug Method, it should also take the debug string. void addDebugMethod(QTextStream &out, const KConfigParameters &cfg, const QString &n) { if (cfg.qCategoryLoggingName.isEmpty()) { out << " qDebug() << \"" << setFunction(n); } else { out << " qCDebug(" << cfg.qCategoryLoggingName << ") << \"" << setFunction(n); } } // returns the member get default implementation // which should go in the h file if inline // or the cpp file if not inline QString memberGetDefaultBody(const CfgEntry *e) { QString result = e->code; QTextStream out(&result, QIODevice::WriteOnly); - out << endl; + out << '\n'; if (!e->param.isEmpty()) { - out << " switch (i) {" << endl; + out << " switch (i) {\n"; for (int i = 0; i <= e->paramMax; ++i) { if (!e->paramDefaultValues[i].isEmpty()) { - out << " case " << i << ": return " << e->paramDefaultValues[i] << ';' << endl; + out << " case " << i << ": return " << e->paramDefaultValues[i] << ";\n"; } } QString defaultValue = e->defaultValue; - out << " default:" << endl; - out << " return " << defaultValue.replace("$(" + e->param + ')', QLatin1String("i")) << ';' << endl; - out << " }" << endl; + out << " default:\n"; + out << " return " << defaultValue.replace("$(" + e->param + ')', QLatin1String("i")) << ";\n"; + out << " }\n"; } else { out << " return " << e->defaultValue << ';'; } return result; } // returns the item accesor implementation // which should go in the h file if inline // or the cpp file if not inline QString itemAccessorBody(const CfgEntry *e, const KConfigParameters &cfg) { QString result; QTextStream out(&result, QIODevice::WriteOnly); out << "return " << itemPath(e, cfg); if (!e->param.isEmpty()) { out << "[i]"; } - out << ";" << endl; + out << ";\n"; return result; } //indents text adding X spaces per line QString indent(QString text, int spaces) { QString result; QTextStream out(&result, QIODevice::WriteOnly); QTextStream in(&text, QIODevice::ReadOnly); QString currLine; while (!in.atEnd()) { currLine = in.readLine(); if (!currLine.isEmpty()) for (int i = 0; i < spaces; i++) { out << " "; } - out << currLine << endl; + out << currLine << '\n'; } return result; } bool hasErrors(KConfigXmlParser &parser, const ParseResult& parseResult, const KConfigParameters &cfg) { if (cfg.className.isEmpty()) { - cerr << "Class name missing" << endl; + std::cerr << "Class name missing" << std::endl; return true; } if (cfg.singleton && !parseResult.parameters.isEmpty()) { - cerr << "Singleton class can not have parameters" << endl; + std::cerr << "Singleton class can not have parameters" << std::endl; return true; } if (!parseResult.cfgFileName.isEmpty() && parseResult.cfgFileNameArg) { - cerr << "Having both a fixed filename and a filename as argument is not possible." << endl; + std::cerr << "Having both a fixed filename and a filename as argument is not possible." << std::endl; return true; } /* TODO: For some reason some configuration files prefer to have *no* entries * at all in it, and the generated code is mostly bogus as KConfigXT will not * handle save / load / properties, etc, nothing. * * The first of those files that I came across are qmakebuilderconfig.kcfg from the KDevelop * project. * I think we should remove the possibility of creating configuration classes from configuration * files that don't really have configuration in it. but I'm changing this right now to allow * kdevelop files to pass. * * Remove for KDE 6 * (to make things more interesting, it failed in a code that's never used within KDevelop... ) */ if (parseResult.entries.isEmpty()) { - cerr << "No entries." << endl; + std::cerr << "No entries." << std::endl; return false; } return false; } int main(int argc, char **argv) { QCoreApplication app(argc, argv); app.setApplicationName(QStringLiteral("kconfig_compiler")); app.setApplicationVersion(QStringLiteral(KCONFIG_VERSION_STRING)); QString inputFilename, codegenFilename; QCommandLineOption targetDirectoryOption(QStringList { QStringLiteral("d"), QStringLiteral("directory") }, QCoreApplication::translate("main", "Directory to generate files in [.]"), QCoreApplication::translate("main", "directory"), QStringLiteral(".")); QCommandLineOption licenseOption ( QStringList { QStringLiteral("l"), QStringLiteral("license") }, QCoreApplication::translate("main", "Display software license.")); QCommandLineParser parser; parser.addPositionalArgument(QStringLiteral("file.kcfg"), QStringLiteral("Input kcfg XML file")); parser.addPositionalArgument(QStringLiteral("file.kcfgc"), QStringLiteral("Code generation options file")); parser.addOption(targetDirectoryOption); parser.addOption (licenseOption); parser.addVersionOption(); parser.addHelpOption(); parser.process(app); if (parser.isSet(licenseOption)) { - cout << "Copyright 2003 Cornelius Schumacher, Waldo Bastian, Zack Rusin," << endl; - cout << " Reinhold Kainhofer, Duncan Mac-Vicar P., Harald Fernengel" << endl; - cout << "This program comes with ABSOLUTELY NO WARRANTY." << endl; - cout << "You may redistribute copies of this program" << endl; - cout << "under the terms of the GNU Library Public License." << endl; - cout << "For more information about these matters, see the file named COPYING." << endl; + std::cout << "Copyright 2003 Cornelius Schumacher, Waldo Bastian, Zack Rusin," << std::endl; + std::cout << " Reinhold Kainhofer, Duncan Mac-Vicar P., Harald Fernengel" << std::endl; + std::cout << "This program comes with ABSOLUTELY NO WARRANTY." << std::endl; + std::cout << "You may redistribute copies of this program" << std::endl; + std::cout << "under the terms of the GNU Library Public License." << std::endl; + std::cout << "For more information about these matters, see the file named COPYING." << std::endl; return 0; } const QStringList args = parser.positionalArguments(); if (args.count() < 2) { - cerr << "Too few arguments." << endl; + std::cerr << "Too few arguments." << std::endl; return 1; } if (args.count() > 2) { - cerr << "Too many arguments." << endl; + std::cerr << "Too many arguments." << std::endl; return 1; } inputFilename = args.at(0); codegenFilename = args.at(1); // TODO: Transform baseDir into a helper. QString baseDir = parser.value(targetDirectoryOption); #ifdef Q_OS_WIN if (!baseDir.endsWith('/') && !baseDir.endsWith('\\')) #else if (!baseDir.endsWith('/')) #endif baseDir.append("/"); KConfigParameters cfg(codegenFilename); KConfigXmlParser xmlParser(cfg, inputFilename); // The Xml Parser aborts in the case of an error, so if we get // to parseResult, we have a working Xml file. xmlParser.start(); ParseResult parseResult = xmlParser.getParseResult(); if (hasErrors(xmlParser, parseResult, cfg)) { return 1; } // TODO: Move this to somewhere saner. for (const auto &signal : qAsConst(parseResult.signalList)) { parseResult.hasNonModifySignals |= !signal.modify; } // remove '.kcfg' from the name. const QString baseName = inputFilename.mid(0, inputFilename.size()-5); KConfigHeaderGenerator headerGenerator(baseName, baseDir, cfg, parseResult); headerGenerator.start(); headerGenerator.save(); KConfigSourceGenerator sourceGenerator(baseName, baseDir, cfg, parseResult); sourceGenerator.start(); sourceGenerator.save(); qDeleteAll(parseResult.entries); }