File indexing completed on 2024-05-12 15:34:15

0001 /*
0002     This file is part of KDE.
0003 
0004     SPDX-FileCopyrightText: 2003 Cornelius Schumacher <schumacher@kde.org>
0005     SPDX-FileCopyrightText: 2003 Waldo Bastian <bastian@kde.org>
0006     SPDX-FileCopyrightText: 2003 Zack Rusin <zack@kde.org>
0007     SPDX-FileCopyrightText: 2006 Michaƫl Larouche <michael.larouche@kdemail.net>
0008     SPDX-FileCopyrightText: 2008 Allen Winter <winter@kde.org>
0009 
0010     SPDX-License-Identifier: LGPL-2.0-or-later
0011 */
0012 
0013 #include <QCommandLineOption>
0014 #include <QCommandLineParser>
0015 #include <QCoreApplication>
0016 #include <QDomAttr>
0017 #include <QFile>
0018 #include <QFileInfo>
0019 #include <QRegularExpression>
0020 #include <QSettings>
0021 #include <QStringList>
0022 #include <QTextStream>
0023 
0024 #include <algorithm>
0025 #include <iostream>
0026 #include <ostream>
0027 #include <stdlib.h>
0028 
0029 #include "../../kconfig_version.h"
0030 #include "KConfigCommonStructs.h"
0031 #include "KConfigHeaderGenerator.h"
0032 #include "KConfigParameters.h"
0033 #include "KConfigSourceGenerator.h"
0034 #include "KConfigXmlParser.h"
0035 
0036 QString varName(const QString &n, const KConfigParameters &cfg)
0037 {
0038     QString result;
0039     if (!cfg.dpointer) {
0040         result = QChar::fromLatin1('m') + n;
0041         result[1] = result.at(1).toUpper();
0042     } else {
0043         result = n;
0044         result[0] = result.at(0).toLower();
0045     }
0046     return result;
0047 }
0048 
0049 QString varPath(const QString &n, const KConfigParameters &cfg)
0050 {
0051     QString result;
0052     if (cfg.dpointer) {
0053         result = QLatin1String{"d->"} + varName(n, cfg);
0054     } else {
0055         result = varName(n, cfg);
0056     }
0057     return result;
0058 }
0059 
0060 QString enumName(const QString &n)
0061 {
0062     QString result = QLatin1String("Enum") + n;
0063     result[4] = result.at(4).toUpper();
0064     return result;
0065 }
0066 
0067 QString enumName(const QString &n, const CfgEntry::Choices &c)
0068 {
0069     QString result = c.name();
0070     if (result.isEmpty()) {
0071         result = QLatin1String("Enum") + n;
0072         result[4] = result.at(4).toUpper();
0073     }
0074     return result;
0075 }
0076 
0077 QString enumType(const CfgEntry *e, bool globalEnums)
0078 {
0079     QString result = e->choices.name();
0080     if (result.isEmpty()) {
0081         result = QLatin1String("Enum") + e->name;
0082         if (!globalEnums) {
0083             result += QLatin1String("::type");
0084         }
0085         result[4] = result.at(4).toUpper();
0086     }
0087     return result;
0088 }
0089 
0090 QString enumTypeQualifier(const QString &n, const CfgEntry::Choices &c)
0091 {
0092     QString result = c.name();
0093     if (result.isEmpty()) {
0094         result = QLatin1String("Enum") + n + QLatin1String("::");
0095         result[4] = result.at(4).toUpper();
0096     } else if (c.external()) {
0097         result = c.externalQualifier();
0098     } else {
0099         result.clear();
0100     }
0101     return result;
0102 }
0103 
0104 QString setFunction(const QString &n, const QString &className)
0105 {
0106     QString result = QLatin1String("set") + n;
0107     result[3] = result.at(3).toUpper();
0108 
0109     if (!className.isEmpty()) {
0110         result = className + QLatin1String("::") + result;
0111     }
0112     return result;
0113 }
0114 
0115 QString changeSignalName(const QString &n)
0116 {
0117     return n + QLatin1String{"Changed"};
0118 }
0119 
0120 QString getDefaultFunction(const QString &n, const QString &className)
0121 {
0122     QString result = QLatin1String("default%1Value").arg(n);
0123     result[7] = result.at(7).toUpper();
0124 
0125     if (!className.isEmpty()) {
0126         result.prepend(className + QLatin1String("::"));
0127     }
0128     return result;
0129 }
0130 
0131 QString getFunction(const QString &n, const QString &className)
0132 {
0133     QString result = n;
0134     result[0] = result.at(0).toLower();
0135 
0136     if (!className.isEmpty()) {
0137         result.prepend(className + QLatin1String("::"));
0138     }
0139     return result;
0140 }
0141 
0142 QString immutableFunction(const QString &n, const QString &className)
0143 {
0144     QString result = QLatin1String("is") + n;
0145     result[2] = result.at(2).toUpper();
0146     result += QLatin1String{"Immutable"};
0147 
0148     if (!className.isEmpty()) {
0149         result.prepend(className + QLatin1String("::"));
0150     }
0151     return result;
0152 }
0153 
0154 void addQuotes(QString &s)
0155 {
0156     if (!s.startsWith(QLatin1Char('"'))) {
0157         s.prepend(QLatin1Char('"'));
0158     }
0159     if (!s.endsWith(QLatin1Char('"'))) {
0160         s.append(QLatin1Char('"'));
0161     }
0162 }
0163 
0164 static QString quoteString(const QString &s)
0165 {
0166     QString r = s;
0167     r.replace(QLatin1Char('\\'), QLatin1String("\\\\"));
0168     r.replace(QLatin1Char('\"'), QLatin1String("\\\""));
0169     r.remove(QLatin1Char('\r'));
0170     r.replace(QLatin1Char('\n'), QLatin1String("\\n\"\n\""));
0171     return QLatin1Char('\"') + r + QLatin1Char('\"');
0172 }
0173 
0174 QString literalString(const QString &str)
0175 {
0176     const bool isAscii = std::none_of(str.cbegin(), str.cend(), [](const QChar ch) {
0177         return ch.unicode() > 127;
0178     });
0179 
0180     if (isAscii) {
0181         return QLatin1String("QStringLiteral( %1 )").arg(quoteString(str));
0182     } else {
0183         return QLatin1String("QString::fromUtf8( %1 )").arg(quoteString(str));
0184     }
0185 }
0186 
0187 QString signalEnumName(const QString &signalName)
0188 {
0189     QString result;
0190     result = QLatin1String("signal") + signalName;
0191     result[6] = result.at(6).toUpper();
0192 
0193     return result;
0194 }
0195 
0196 bool isUnsigned(const QString &type)
0197 {
0198     return type == QLatin1String("UInt") || type == QLatin1String("ULongLong");
0199 }
0200 
0201 /**
0202   Return parameter declaration for given type.
0203 */
0204 QString param(const QString &t)
0205 {
0206     const QString type = t.toLower();
0207     if (type == QLatin1String("string")) {
0208         return QStringLiteral("const QString &");
0209     } else if (type == QLatin1String("stringlist")) {
0210         return QStringLiteral("const QStringList &");
0211     } else if (type == QLatin1String("font")) {
0212         return QStringLiteral("const QFont &");
0213     } else if (type == QLatin1String("rect")) {
0214         return QStringLiteral("const QRect &");
0215     } else if (type == QLatin1String("size")) {
0216         return QStringLiteral("const QSize &");
0217     } else if (type == QLatin1String("color")) {
0218         return QStringLiteral("const QColor &");
0219     } else if (type == QLatin1String("point")) {
0220         return QStringLiteral("const QPoint &");
0221     } else if (type == QLatin1String("int")) {
0222         return QStringLiteral("int");
0223     } else if (type == QLatin1String("uint")) {
0224         return QStringLiteral("uint");
0225     } else if (type == QLatin1String("bool")) {
0226         return QStringLiteral("bool");
0227     } else if (type == QLatin1String("double")) {
0228         return QStringLiteral("double");
0229     } else if (type == QLatin1String("datetime")) {
0230         return QStringLiteral("const QDateTime &");
0231     } else if (type == QLatin1String("longlong")) {
0232         return QStringLiteral("qint64");
0233     } else if (type == QLatin1String("ulonglong")) {
0234         return QStringLiteral("quint64");
0235     } else if (type == QLatin1String("intlist")) {
0236         return QStringLiteral("const QList<int> &");
0237     } else if (type == QLatin1String("enum")) {
0238         return QStringLiteral("int");
0239     } else if (type == QLatin1String("path")) {
0240         return QStringLiteral("const QString &");
0241     } else if (type == QLatin1String("pathlist")) {
0242         return QStringLiteral("const QStringList &");
0243     } else if (type == QLatin1String("password")) {
0244         return QStringLiteral("const QString &");
0245     } else if (type == QLatin1String("url")) {
0246         return QStringLiteral("const QUrl &");
0247     } else if (type == QLatin1String("urllist")) {
0248         return QStringLiteral("const QList<QUrl> &");
0249     } else {
0250         std::cerr << "kconfig_compiler_kf5 does not support type \"" << qPrintable(type) << "\"" << std::endl;
0251         return QStringLiteral("QString"); // For now, but an assert would be better
0252     }
0253 }
0254 
0255 /**
0256   Actual C++ storage type for given type.
0257 */
0258 QString cppType(const QString &t)
0259 {
0260     const QString type = t.toLower();
0261     if (type == QLatin1String("string")) {
0262         return QStringLiteral("QString");
0263     } else if (type == QLatin1String("stringlist")) {
0264         return QStringLiteral("QStringList");
0265     } else if (type == QLatin1String("font")) {
0266         return QStringLiteral("QFont");
0267     } else if (type == QLatin1String("rect")) {
0268         return QStringLiteral("QRect");
0269     } else if (type == QLatin1String("size")) {
0270         return QStringLiteral("QSize");
0271     } else if (type == QLatin1String("color")) {
0272         return QStringLiteral("QColor");
0273     } else if (type == QLatin1String("point")) {
0274         return QStringLiteral("QPoint");
0275     } else if (type == QLatin1String("int")) {
0276         return QStringLiteral("int");
0277     } else if (type == QLatin1String("uint")) {
0278         return QStringLiteral("uint");
0279     } else if (type == QLatin1String("bool")) {
0280         return QStringLiteral("bool");
0281     } else if (type == QLatin1String("double")) {
0282         return QStringLiteral("double");
0283     } else if (type == QLatin1String("datetime")) {
0284         return QStringLiteral("QDateTime");
0285     } else if (type == QLatin1String("longlong")) {
0286         return QStringLiteral("qint64");
0287     } else if (type == QLatin1String("ulonglong")) {
0288         return QStringLiteral("quint64");
0289     } else if (type == QLatin1String("intlist")) {
0290         return QStringLiteral("QList<int>");
0291     } else if (type == QLatin1String("enum")) {
0292         return QStringLiteral("int");
0293     } else if (type == QLatin1String("path")) {
0294         return QStringLiteral("QString");
0295     } else if (type == QLatin1String("pathlist")) {
0296         return QStringLiteral("QStringList");
0297     } else if (type == QLatin1String("password")) {
0298         return QStringLiteral("QString");
0299     } else if (type == QLatin1String("url")) {
0300         return QStringLiteral("QUrl");
0301     } else if (type == QLatin1String("urllist")) {
0302         return QStringLiteral("QList<QUrl>");
0303     } else {
0304         std::cerr << "kconfig_compiler_kf5 does not support type \"" << qPrintable(type) << "\"" << std::endl;
0305         return QStringLiteral("QString"); // For now, but an assert would be better
0306     }
0307 }
0308 
0309 QString defaultValue(const QString &t)
0310 {
0311     const QString type = t.toLower();
0312     if (type == QLatin1String("string")) {
0313         return QStringLiteral("\"\""); // Use empty string, not null string!
0314     } else if (type == QLatin1String("stringlist")) {
0315         return QStringLiteral("QStringList()");
0316     } else if (type == QLatin1String("font")) {
0317         return QStringLiteral("QFont()");
0318     } else if (type == QLatin1String("rect")) {
0319         return QStringLiteral("QRect()");
0320     } else if (type == QLatin1String("size")) {
0321         return QStringLiteral("QSize()");
0322     } else if (type == QLatin1String("color")) {
0323         return QStringLiteral("QColor(128, 128, 128)");
0324     } else if (type == QLatin1String("point")) {
0325         return QStringLiteral("QPoint()");
0326     } else if (type == QLatin1String("int")) {
0327         return QStringLiteral("0");
0328     } else if (type == QLatin1String("uint")) {
0329         return QStringLiteral("0");
0330     } else if (type == QLatin1String("bool")) {
0331         return QStringLiteral("false");
0332     } else if (type == QLatin1String("double")) {
0333         return QStringLiteral("0.0");
0334     } else if (type == QLatin1String("datetime")) {
0335         return QStringLiteral("QDateTime()");
0336     } else if (type == QLatin1String("longlong")) {
0337         return QStringLiteral("0");
0338     } else if (type == QLatin1String("ulonglong")) {
0339         return QStringLiteral("0");
0340     } else if (type == QLatin1String("intlist")) {
0341         return QStringLiteral("QList<int>()");
0342     } else if (type == QLatin1String("enum")) {
0343         return QStringLiteral("0");
0344     } else if (type == QLatin1String("path")) {
0345         return QStringLiteral("\"\""); // Use empty string, not null string!
0346     } else if (type == QLatin1String("pathlist")) {
0347         return QStringLiteral("QStringList()");
0348     } else if (type == QLatin1String("password")) {
0349         return QStringLiteral("\"\""); // Use empty string, not null string!
0350     } else if (type == QLatin1String("url")) {
0351         return QStringLiteral("QUrl()");
0352     } else if (type == QLatin1String("urllist")) {
0353         return QStringLiteral("QList<QUrl>()");
0354     } else {
0355         std::cerr << "Error, kconfig_compiler_kf5 does not support the \"" << qPrintable(type) << "\" type!" << std::endl;
0356         return QStringLiteral("QString"); // For now, but an assert would be better
0357     }
0358 }
0359 
0360 QString itemType(const QString &type)
0361 {
0362     if (type.isEmpty()) {
0363         return QString{};
0364     }
0365 
0366     QString str = type;
0367     str[0] = str.at(0).toUpper();
0368 
0369     return str;
0370 }
0371 
0372 QString itemDeclaration(const CfgEntry *e, const KConfigParameters &cfg)
0373 {
0374     if (e->name.isEmpty()) {
0375         return QString{};
0376     }
0377 
0378     const QString type = cfg.inherits + QLatin1String{"::Item"} + itemType(e->type);
0379 
0380     QString fCap = e->name;
0381     fCap[0] = fCap.at(0).toUpper();
0382     const QString argSuffix = (!e->param.isEmpty()) ? (QStringLiteral("[%1]").arg(e->paramMax + 1)) : QString();
0383     QString result;
0384 
0385     if (!cfg.itemAccessors && !cfg.dpointer) {
0386         result += QLatin1String{"  "} + (!e->signalList.isEmpty() ? QStringLiteral("KConfigCompilerSignallingItem") : type);
0387         result += QLatin1String("  *item%1;\n").arg(fCap + argSuffix);
0388     }
0389 
0390     if (!e->signalList.isEmpty()) {
0391         result += QLatin1String("  %1  *%2;\n").arg(type, innerItemVar(e, cfg) + argSuffix);
0392     }
0393 
0394     return result;
0395 }
0396 
0397 // returns the name of an item variable
0398 // use itemPath to know the full path
0399 // like using d-> in case of dpointer
0400 QString itemVar(const CfgEntry *e, const KConfigParameters &cfg)
0401 {
0402     QString result;
0403     if (cfg.itemAccessors) {
0404         if (!cfg.dpointer) {
0405             result = QLatin1String("m%1Item").arg(e->name);
0406             result[1] = result.at(1).toUpper();
0407         } else {
0408             result = e->name + QLatin1String{"Item"};
0409             result[0] = result.at(0).toLower();
0410         }
0411     } else {
0412         result = QLatin1String{"item"} + e->name;
0413         result[4] = result.at(4).toUpper();
0414     }
0415     return result;
0416 }
0417 
0418 // returns the name of the local inner item if there is one
0419 // (before wrapping with KConfigCompilerSignallingItem)
0420 // Otherwise return itemVar()
0421 QString innerItemVar(const CfgEntry *e, const KConfigParameters &cfg)
0422 {
0423     if (e->signalList.isEmpty()) {
0424         return itemPath(e, cfg);
0425     }
0426 
0427     QString result = QLatin1String{"innerItem"} + e->name;
0428     result[9] = result.at(9).toUpper();
0429     return result;
0430 }
0431 
0432 QString itemPath(const CfgEntry *e, const KConfigParameters &cfg)
0433 {
0434     return cfg.dpointer ? QLatin1String{"d->"} + itemVar(e, cfg) : itemVar(e, cfg);
0435 }
0436 
0437 QString newInnerItem(const CfgEntry *entry, const QString &key, const QString &defaultValue, const KConfigParameters &cfg, const QString &param)
0438 {
0439     QString str = QLatin1String("new %1::Item%2").arg(cfg.inherits, itemType(entry->type));
0440     str += QLatin1String("( currentGroup(), %1, %2").arg(key, varPath(entry->name, cfg) + param);
0441 
0442     if (entry->type == QLatin1String("Enum")) {
0443         str += QLatin1String{", values"} + entry->name;
0444     }
0445     if (!defaultValue.isEmpty()) {
0446         str += QLatin1String(", ") + defaultValue;
0447     }
0448     str += QLatin1String(" );");
0449 
0450     return str;
0451 }
0452 
0453 QString newItem(const CfgEntry *entry, const QString &key, const QString &defaultValue, const KConfigParameters &cfg, const QString &param)
0454 {
0455     const QList<Signal> sigs = entry->signalList;
0456     if (sigs.isEmpty()) {
0457         return newInnerItem(entry, key, defaultValue, cfg, param);
0458     }
0459 
0460     QString str;
0461     str += QLatin1String("new KConfigCompilerSignallingItem(%1, this, notifyFunction, ").arg(innerItemVar(entry, cfg) + param);
0462     // Append the signal flags
0463     const int listSize = sigs.size();
0464     for (int i = 0; i < listSize; ++i) {
0465         if (i != 0) {
0466             str += QLatin1String(" | ");
0467         }
0468         str += signalEnumName(sigs[i].name);
0469     }
0470     str += QLatin1String(");");
0471 
0472     return str;
0473 }
0474 
0475 QString paramString(const QString &s, const CfgEntry *e, int i)
0476 {
0477     QString result = s;
0478     const QString needle = QLatin1String("$(%1)").arg(e->param);
0479     if (result.contains(needle)) {
0480         const QString tmp = e->paramType == QLatin1String{"Enum"} ? e->paramValues.at(i) : QString::number(i);
0481 
0482         result.replace(needle, tmp);
0483     }
0484     return result;
0485 }
0486 
0487 QString paramString(const QString &group, const QList<Param> &parameters)
0488 {
0489     QString paramString = group;
0490     QString arguments;
0491     int i = 1;
0492     bool firstArg = true;
0493     for (const auto &param : parameters) {
0494         const QString paramName = param.name;
0495         const QString str = QLatin1String("$(%1)").arg(paramName);
0496         if (paramString.contains(str)) {
0497             const QString tmp = QStringLiteral("%%1").arg(i++);
0498             paramString.replace(str, tmp);
0499 
0500             if (firstArg) {
0501                 arguments += QLatin1String{".arg( "};
0502                 firstArg = false;
0503             }
0504 
0505             arguments += QLatin1String("mParam%1, ").arg(paramName);
0506         }
0507     }
0508 
0509     if (!arguments.isEmpty()) {
0510         // Remove the last ", "
0511         arguments.chop(2);
0512 
0513         // Close the ".arg( "
0514         arguments += QLatin1String{" )"};
0515     } else {
0516         return QLatin1String("QStringLiteral( \"%1\" )").arg(group);
0517     }
0518 
0519     return QLatin1String("QStringLiteral( \"%1\" )%2").arg(paramString, arguments);
0520 }
0521 
0522 QString translatedString(const KConfigParameters &cfg, const QString &string, const QString &context, const QString &param, const QString &paramValue)
0523 {
0524     QString result;
0525 
0526     switch (cfg.translationSystem) {
0527     case KConfigParameters::QtTranslation:
0528         if (!context.isEmpty()) {
0529             result += QLatin1String("/*: %1 */ QCoreApplication::translate(\"").arg(context);
0530         } else {
0531             result += QLatin1String{"QCoreApplication::translate(\""};
0532         }
0533         result += QLatin1String("%1\", ").arg(cfg.className);
0534         break;
0535 
0536     case KConfigParameters::KdeTranslation:
0537         if (!cfg.translationDomain.isEmpty() && !context.isEmpty()) {
0538             result += QLatin1String("i18ndc(%1, %2, ").arg(quoteString(cfg.translationDomain), quoteString(context));
0539         } else if (!cfg.translationDomain.isEmpty()) {
0540             result += QLatin1String("i18nd(%1, ").arg(quoteString(cfg.translationDomain));
0541         } else if (!context.isEmpty()) {
0542             result += QLatin1String("i18nc(%1, ").arg(quoteString(context));
0543         } else {
0544             result += QLatin1String{"i18n("};
0545         }
0546         break;
0547     }
0548 
0549     if (!param.isEmpty()) {
0550         QString resolvedString = string;
0551         resolvedString.replace(QLatin1String("$(%1)").arg(param), paramValue);
0552         result += quoteString(resolvedString);
0553     } else {
0554         result += quoteString(string);
0555     }
0556 
0557     result += QLatin1Char{')'};
0558 
0559     return result;
0560 }
0561 
0562 /* int i is the value of the parameter */
0563 QString userTextsFunctions(const CfgEntry *e, const KConfigParameters &cfg, QString itemVarStr, const QString &i)
0564 {
0565     QString txt;
0566     if (itemVarStr.isNull()) {
0567         itemVarStr = itemPath(e, cfg);
0568     }
0569     if (!e->label.isEmpty()) {
0570         txt += QLatin1String("  %1->setLabel( %2 );\n").arg(itemVarStr, translatedString(cfg, e->label, e->labelContext, e->param, i));
0571     }
0572     if (!e->toolTip.isEmpty()) {
0573         txt += QLatin1String("  %1->setToolTip( %2 );\n").arg(itemVarStr, translatedString(cfg, e->toolTip, e->toolTipContext, e->param, i));
0574     }
0575     if (!e->whatsThis.isEmpty()) {
0576         txt += QLatin1String("  %1->setWhatsThis( %2 );\n").arg(itemVarStr, translatedString(cfg, e->whatsThis, e->whatsThisContext, e->param, i));
0577     }
0578     return txt;
0579 }
0580 
0581 // returns the member mutator implementation
0582 // which should go in the h file if inline
0583 // or the cpp file if not inline
0584 // TODO: Fix add Debug Method, it should also take the debug string.
0585 void addDebugMethod(QTextStream &out, const KConfigParameters &cfg, const QString &n)
0586 {
0587     if (cfg.qCategoryLoggingName.isEmpty()) {
0588         out << "  qDebug() << \"" << setFunction(n);
0589     } else {
0590         out << "  qCDebug(" << cfg.qCategoryLoggingName << ") << \"" << setFunction(n);
0591     }
0592 }
0593 
0594 // returns the member get default implementation
0595 // which should go in the h file if inline
0596 // or the cpp file if not inline
0597 QString memberGetDefaultBody(const CfgEntry *e)
0598 {
0599     QString result = e->code;
0600     QTextStream out(&result, QIODevice::WriteOnly);
0601     out << '\n';
0602 
0603     if (!e->param.isEmpty()) {
0604         out << "  switch (i) {\n";
0605         for (int i = 0; i <= e->paramMax; ++i) {
0606             if (!e->paramDefaultValues[i].isEmpty()) {
0607                 out << "  case " << i << ": return " << e->paramDefaultValues[i] << ";\n";
0608             }
0609         }
0610         QString defaultValue = e->defaultValue;
0611 
0612         out << "  default:\n";
0613         out << "    return " << defaultValue.replace(QLatin1String("$(%1)").arg(e->param), QLatin1String("i")) << ";\n";
0614         out << "  }\n";
0615     } else {
0616         out << "  return " << e->defaultValue << ';';
0617     }
0618 
0619     return result;
0620 }
0621 
0622 // returns the item accessor implementation
0623 // which should go in the h file if inline
0624 // or the cpp file if not inline
0625 QString itemAccessorBody(const CfgEntry *e, const KConfigParameters &cfg)
0626 {
0627     QString result;
0628     QTextStream out(&result, QIODevice::WriteOnly);
0629 
0630     out << "return " << itemPath(e, cfg);
0631     if (!e->param.isEmpty()) {
0632         out << "[i]";
0633     }
0634     out << ";\n";
0635 
0636     return result;
0637 }
0638 
0639 // indents text adding X spaces per line
0640 QString indent(QString text, int spaces)
0641 {
0642     QString result;
0643     QTextStream out(&result, QIODevice::WriteOnly);
0644     QTextStream in(&text, QIODevice::ReadOnly);
0645     QString currLine;
0646     while (!in.atEnd()) {
0647         currLine = in.readLine();
0648         if (!currLine.isEmpty()) {
0649             for (int i = 0; i < spaces; ++i) {
0650                 out << " ";
0651             }
0652         }
0653         out << currLine << '\n';
0654     }
0655     return result;
0656 }
0657 
0658 bool hasErrors(KConfigXmlParser &parser, const ParseResult &parseResult, const KConfigParameters &cfg)
0659 {
0660     Q_UNUSED(parser)
0661 
0662     if (cfg.className.isEmpty()) {
0663         std::cerr << "Class name missing" << std::endl;
0664         return true;
0665     }
0666 
0667     if (cfg.singleton && !parseResult.parameters.isEmpty()) {
0668         std::cerr << "Singleton class can not have parameters" << std::endl;
0669         return true;
0670     }
0671 
0672     if (!parseResult.cfgFileName.isEmpty() && parseResult.cfgFileNameArg) {
0673         std::cerr << "Having both a fixed filename and a filename as argument is not possible." << std::endl;
0674         return true;
0675     }
0676 
0677     /* TODO: For some reason some configuration files prefer to have *no* entries
0678      * at all in it, and the generated code is mostly bogus as KConfigXT will not
0679      * handle save / load / properties, etc, nothing.
0680      *
0681      * The first of those files that I came across are qmakebuilderconfig.kcfg from the KDevelop
0682      * project.
0683      * I think we should remove the possibility of creating configuration classes from configuration
0684      * files that don't really have configuration in it. but I'm changing this right now to allow
0685      * kdevelop files to pass.
0686      *
0687      * Remove for KDE 6
0688      * (to make things more interesting, it failed in a code that's never used within KDevelop... )
0689      */
0690     if (parseResult.entries.isEmpty()) {
0691         std::cerr << "No entries." << std::endl;
0692         return false;
0693     }
0694 
0695     return false;
0696 }
0697 
0698 int main(int argc, char **argv)
0699 {
0700     QCoreApplication app(argc, argv);
0701     app.setApplicationName(QStringLiteral("kconfig_compiler"));
0702     app.setApplicationVersion(QStringLiteral(KCONFIG_VERSION_STRING));
0703 
0704     QString inputFilename;
0705     QString codegenFilename;
0706 
0707     QCommandLineOption targetDirectoryOption(QStringList{QStringLiteral("d"), QStringLiteral("directory")},
0708                                              QCoreApplication::translate("main", "Directory to generate files in [.]"),
0709                                              QCoreApplication::translate("main", "directory"),
0710                                              QStringLiteral("."));
0711 
0712     QCommandLineOption licenseOption(QStringList{QStringLiteral("l"), QStringLiteral("license")},
0713                                      QCoreApplication::translate("main", "Display software license."));
0714 
0715     QCommandLineParser parser;
0716 
0717     parser.addPositionalArgument(QStringLiteral("file.kcfg"), QStringLiteral("Input kcfg XML file"));
0718     parser.addPositionalArgument(QStringLiteral("file.kcfgc"), QStringLiteral("Code generation options file"));
0719 
0720     parser.addOption(targetDirectoryOption);
0721     parser.addOption(licenseOption);
0722 
0723     parser.addVersionOption();
0724     parser.addHelpOption();
0725     parser.process(app);
0726 
0727     if (parser.isSet(licenseOption)) {
0728         std::cout << "Copyright 2003 Cornelius Schumacher, Waldo Bastian, Zack Rusin," << std::endl;
0729         std::cout << "    Reinhold Kainhofer, Duncan Mac-Vicar P., Harald Fernengel" << std::endl;
0730         std::cout << "This program comes with ABSOLUTELY NO WARRANTY." << std::endl;
0731         std::cout << "You may redistribute copies of this program" << std::endl;
0732         std::cout << "under the terms of the GNU Library Public License." << std::endl;
0733         std::cout << "For more information about these matters, see the file named COPYING." << std::endl;
0734         return 0;
0735     }
0736 
0737     const QStringList args = parser.positionalArguments();
0738     if (args.count() < 2) {
0739         std::cerr << "Too few arguments." << std::endl;
0740         return 1;
0741     }
0742 
0743     if (args.count() > 2) {
0744         std::cerr << "Too many arguments." << std::endl;
0745         return 1;
0746     }
0747     inputFilename = args.at(0);
0748     codegenFilename = args.at(1);
0749 
0750     // TODO: Transform baseDir into a helper.
0751     QString baseDir = parser.value(targetDirectoryOption);
0752 
0753 #ifdef Q_OS_WIN
0754     if (!baseDir.endsWith(QLatin1Char{'/'}) && !baseDir.endsWith(QLatin1Char{'\\'})) {
0755 #else
0756     if (!baseDir.endsWith(QLatin1Char{'/'})) {
0757 #endif
0758         baseDir.append(QLatin1Char{'/'});
0759     }
0760 
0761     KConfigParameters cfg(codegenFilename);
0762 
0763     KConfigXmlParser xmlParser(cfg, inputFilename);
0764 
0765     // The Xml Parser aborts in the case of an error, so if we get
0766     // to parseResult, we have a working Xml file.
0767     xmlParser.start();
0768 
0769     ParseResult parseResult = xmlParser.getParseResult();
0770 
0771     if (hasErrors(xmlParser, parseResult, cfg)) {
0772         return 1;
0773     }
0774 
0775     // TODO: Move this to somewhere saner.
0776     for (const auto &signal : std::as_const(parseResult.signalList)) {
0777         parseResult.hasNonModifySignals |= !signal.modify;
0778     }
0779 
0780     // remove '.kcfg' from the name.
0781     const QString baseName = inputFilename.mid(0, inputFilename.size() - 5);
0782     KConfigHeaderGenerator headerGenerator(baseName, baseDir, cfg, parseResult);
0783     headerGenerator.start();
0784     headerGenerator.save();
0785 
0786     KConfigSourceGenerator sourceGenerator(baseName, baseDir, cfg, parseResult);
0787     sourceGenerator.start();
0788     sourceGenerator.save();
0789 
0790     qDeleteAll(parseResult.entries);
0791 }