File indexing completed on 2024-05-12 03:54:29

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 "../core/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("rectf")) {
0216         return QStringLiteral("const QRectF &");
0217     } else if (type == QLatin1String("size")) {
0218         return QStringLiteral("const QSize &");
0219     } else if (type == QLatin1String("sizef")) {
0220         return QStringLiteral("const QSizeF &");
0221     } else if (type == QLatin1String("color")) {
0222         return QStringLiteral("const QColor &");
0223     } else if (type == QLatin1String("point")) {
0224         return QStringLiteral("const QPoint &");
0225     } else if (type == QLatin1String("pointf")) {
0226         return QStringLiteral("const QPointF &");
0227     } else if (type == QLatin1String("int")) {
0228         return QStringLiteral("int");
0229     } else if (type == QLatin1String("uint")) {
0230         return QStringLiteral("uint");
0231     } else if (type == QLatin1String("bool")) {
0232         return QStringLiteral("bool");
0233     } else if (type == QLatin1String("double")) {
0234         return QStringLiteral("double");
0235     } else if (type == QLatin1String("datetime")) {
0236         return QStringLiteral("const QDateTime &");
0237     } else if (type == QLatin1String("longlong")) {
0238         return QStringLiteral("qint64");
0239     } else if (type == QLatin1String("ulonglong")) {
0240         return QStringLiteral("quint64");
0241     } else if (type == QLatin1String("intlist")) {
0242         return QStringLiteral("const QList<int> &");
0243     } else if (type == QLatin1String("enum")) {
0244         return QStringLiteral("int");
0245     } else if (type == QLatin1String("path")) {
0246         return QStringLiteral("const QString &");
0247     } else if (type == QLatin1String("pathlist")) {
0248         return QStringLiteral("const QStringList &");
0249     } else if (type == QLatin1String("password")) {
0250         return QStringLiteral("const QString &");
0251     } else if (type == QLatin1String("url")) {
0252         return QStringLiteral("const QUrl &");
0253     } else if (type == QLatin1String("urllist")) {
0254         return QStringLiteral("const QList<QUrl> &");
0255     } else {
0256         std::cerr << "kconfig_compiler_kf6 does not support type \"" << qPrintable(type) << "\"" << std::endl;
0257         return QStringLiteral("QString"); // For now, but an assert would be better
0258     }
0259 }
0260 
0261 /**
0262   Actual C++ storage type for given type.
0263 */
0264 QString cppType(const QString &t)
0265 {
0266     const QString type = t.toLower();
0267     if (type == QLatin1String("string")) {
0268         return QStringLiteral("QString");
0269     } else if (type == QLatin1String("stringlist")) {
0270         return QStringLiteral("QStringList");
0271     } else if (type == QLatin1String("font")) {
0272         return QStringLiteral("QFont");
0273     } else if (type == QLatin1String("rect")) {
0274         return QStringLiteral("QRect");
0275     } else if (type == QLatin1String("rectf")) {
0276         return QStringLiteral("QRectF");
0277     } else if (type == QLatin1String("size")) {
0278         return QStringLiteral("QSize");
0279     } else if (type == QLatin1String("sizef")) {
0280         return QStringLiteral("QSizeF");
0281     } else if (type == QLatin1String("color")) {
0282         return QStringLiteral("QColor");
0283     } else if (type == QLatin1String("point")) {
0284         return QStringLiteral("QPoint");
0285     } else if (type == QLatin1String("pointf")) {
0286         return QStringLiteral("QPointF");
0287     } else if (type == QLatin1String("int")) {
0288         return QStringLiteral("int");
0289     } else if (type == QLatin1String("uint")) {
0290         return QStringLiteral("uint");
0291     } else if (type == QLatin1String("bool")) {
0292         return QStringLiteral("bool");
0293     } else if (type == QLatin1String("double")) {
0294         return QStringLiteral("double");
0295     } else if (type == QLatin1String("datetime")) {
0296         return QStringLiteral("QDateTime");
0297     } else if (type == QLatin1String("longlong")) {
0298         return QStringLiteral("qint64");
0299     } else if (type == QLatin1String("ulonglong")) {
0300         return QStringLiteral("quint64");
0301     } else if (type == QLatin1String("intlist")) {
0302         return QStringLiteral("QList<int>");
0303     } else if (type == QLatin1String("enum")) {
0304         return QStringLiteral("int");
0305     } else if (type == QLatin1String("path")) {
0306         return QStringLiteral("QString");
0307     } else if (type == QLatin1String("pathlist")) {
0308         return QStringLiteral("QStringList");
0309     } else if (type == QLatin1String("password")) {
0310         return QStringLiteral("QString");
0311     } else if (type == QLatin1String("url")) {
0312         return QStringLiteral("QUrl");
0313     } else if (type == QLatin1String("urllist")) {
0314         return QStringLiteral("QList<QUrl>");
0315     } else {
0316         std::cerr << "kconfig_compiler_kf6 does not support type \"" << qPrintable(type) << "\"" << std::endl;
0317         return QStringLiteral("QString"); // For now, but an assert would be better
0318     }
0319 }
0320 
0321 QString defaultValue(const QString &t)
0322 {
0323     const QString type = t.toLower();
0324     if (type == QLatin1String("string")) {
0325         return QStringLiteral("\"\""); // Use empty string, not null string!
0326     } else if (type == QLatin1String("stringlist")) {
0327         return QStringLiteral("QStringList()");
0328     } else if (type == QLatin1String("font")) {
0329         return QStringLiteral("QFont()");
0330     } else if (type == QLatin1String("rect")) {
0331         return QStringLiteral("QRect()");
0332     } else if (type == QLatin1String("rectf")) {
0333         return QStringLiteral("QRectF()");
0334     } else if (type == QLatin1String("size")) {
0335         return QStringLiteral("QSize()");
0336     } else if (type == QLatin1String("sizef")) {
0337         return QStringLiteral("QSizeF()");
0338     } else if (type == QLatin1String("color")) {
0339         return QStringLiteral("QColor(128, 128, 128)");
0340     } else if (type == QLatin1String("point")) {
0341         return QStringLiteral("QPoint()");
0342     } else if (type == QLatin1String("pointf")) {
0343         return QStringLiteral("QPointF()");
0344     } else if (type == QLatin1String("int")) {
0345         return QStringLiteral("0");
0346     } else if (type == QLatin1String("uint")) {
0347         return QStringLiteral("0");
0348     } else if (type == QLatin1String("bool")) {
0349         return QStringLiteral("false");
0350     } else if (type == QLatin1String("double")) {
0351         return QStringLiteral("0.0");
0352     } else if (type == QLatin1String("datetime")) {
0353         return QStringLiteral("QDateTime()");
0354     } else if (type == QLatin1String("longlong")) {
0355         return QStringLiteral("0");
0356     } else if (type == QLatin1String("ulonglong")) {
0357         return QStringLiteral("0");
0358     } else if (type == QLatin1String("intlist")) {
0359         return QStringLiteral("QList<int>()");
0360     } else if (type == QLatin1String("enum")) {
0361         return QStringLiteral("0");
0362     } else if (type == QLatin1String("path")) {
0363         return QStringLiteral("\"\""); // Use empty string, not null string!
0364     } else if (type == QLatin1String("pathlist")) {
0365         return QStringLiteral("QStringList()");
0366     } else if (type == QLatin1String("password")) {
0367         return QStringLiteral("\"\""); // Use empty string, not null string!
0368     } else if (type == QLatin1String("url")) {
0369         return QStringLiteral("QUrl()");
0370     } else if (type == QLatin1String("urllist")) {
0371         return QStringLiteral("QList<QUrl>()");
0372     } else {
0373         std::cerr << "Error, kconfig_compiler_kf6 does not support the \"" << qPrintable(type) << "\" type!" << std::endl;
0374         return QStringLiteral("QString"); // For now, but an assert would be better
0375     }
0376 }
0377 
0378 QString itemType(const QString &type)
0379 {
0380     if (type.isEmpty()) {
0381         return QString{};
0382     }
0383 
0384     QString str = type;
0385     str[0] = str.at(0).toUpper();
0386 
0387     return str;
0388 }
0389 
0390 QString itemDeclaration(const CfgEntry *e, const KConfigParameters &cfg)
0391 {
0392     if (e->name.isEmpty()) {
0393         return QString{};
0394     }
0395 
0396     const QString type = cfg.inherits + QLatin1String{"::Item"} + itemType(e->type);
0397 
0398     QString fCap = e->name;
0399     fCap[0] = fCap.at(0).toUpper();
0400     const QString argSuffix = (!e->param.isEmpty()) ? (QStringLiteral("[%1]").arg(e->paramMax + 1)) : QString();
0401     QString result;
0402 
0403     if (!cfg.itemAccessors && !cfg.dpointer) {
0404         result += QLatin1String{"  "} + (!e->signalList.isEmpty() ? QStringLiteral("KConfigCompilerSignallingItem") : type);
0405         result += QLatin1String("  *item%1;\n").arg(fCap + argSuffix);
0406     }
0407 
0408     if (!e->signalList.isEmpty()) {
0409         result += QLatin1String("  %1  *%2;\n").arg(type, innerItemVar(e, cfg) + argSuffix);
0410     }
0411 
0412     return result;
0413 }
0414 
0415 // returns the name of an item variable
0416 // use itemPath to know the full path
0417 // like using d-> in case of dpointer
0418 QString itemVar(const CfgEntry *e, const KConfigParameters &cfg)
0419 {
0420     QString result;
0421     if (cfg.itemAccessors) {
0422         if (!cfg.dpointer) {
0423             result = QLatin1String("m%1Item").arg(e->name);
0424             result[1] = result.at(1).toUpper();
0425         } else {
0426             result = e->name + QLatin1String{"Item"};
0427             result[0] = result.at(0).toLower();
0428         }
0429     } else {
0430         result = QLatin1String{"item"} + e->name;
0431         result[4] = result.at(4).toUpper();
0432     }
0433     return result;
0434 }
0435 
0436 // returns the name of the local inner item if there is one
0437 // (before wrapping with KConfigCompilerSignallingItem)
0438 // Otherwise return itemVar()
0439 QString innerItemVar(const CfgEntry *e, const KConfigParameters &cfg)
0440 {
0441     if (e->signalList.isEmpty()) {
0442         return itemPath(e, cfg);
0443     }
0444 
0445     QString result = QLatin1String{"innerItem"} + e->name;
0446     result[9] = result.at(9).toUpper();
0447     return result;
0448 }
0449 
0450 QString itemPath(const CfgEntry *e, const KConfigParameters &cfg)
0451 {
0452     return cfg.dpointer ? QLatin1String{"d->"} + itemVar(e, cfg) : itemVar(e, cfg);
0453 }
0454 
0455 QString newInnerItem(const CfgEntry *entry, const QString &key, const QString &defaultValue, const KConfigParameters &cfg, const QString &param)
0456 {
0457     QString str = QLatin1String("new %1::Item%2").arg(cfg.inherits, itemType(entry->type));
0458     str += QLatin1String("( currentGroup(), %1, %2").arg(key, varPath(entry->name, cfg) + param);
0459 
0460     if (entry->type == QLatin1String("Enum")) {
0461         str += QLatin1String{", values"} + entry->name;
0462     }
0463     if (!defaultValue.isEmpty()) {
0464         str += QLatin1String(", ") + defaultValue;
0465     }
0466     str += QLatin1String(" );");
0467 
0468     return str;
0469 }
0470 
0471 QString newItem(const CfgEntry *entry, const QString &key, const QString &defaultValue, const KConfigParameters &cfg, const QString &param)
0472 {
0473     const QList<Signal> sigs = entry->signalList;
0474     if (sigs.isEmpty()) {
0475         return newInnerItem(entry, key, defaultValue, cfg, param);
0476     }
0477 
0478     QString str;
0479     str += QLatin1String("new KConfigCompilerSignallingItem(%1, this, notifyFunction, ").arg(innerItemVar(entry, cfg) + param);
0480     // Append the signal flags
0481     const int listSize = sigs.size();
0482     for (int i = 0; i < listSize; ++i) {
0483         if (i != 0) {
0484             str += QLatin1String(" | ");
0485         }
0486         str += signalEnumName(sigs[i].name);
0487     }
0488     str += QLatin1String(");");
0489 
0490     return str;
0491 }
0492 
0493 QString paramString(const QString &s, const CfgEntry *e, int i)
0494 {
0495     QString result = s;
0496     const QString needle = QLatin1String("$(%1)").arg(e->param);
0497     if (result.contains(needle)) {
0498         const QString tmp = e->paramType == QLatin1String{"Enum"} ? e->paramValues.at(i) : QString::number(i);
0499 
0500         result.replace(needle, tmp);
0501     }
0502     return result;
0503 }
0504 
0505 QString paramString(const QString &group, const QList<Param> &parameters)
0506 {
0507     QString paramString = group;
0508     QString arguments;
0509     int i = 1;
0510     bool firstArg = true;
0511     for (const auto &param : parameters) {
0512         const QString paramName = param.name;
0513         const QString str = QLatin1String("$(%1)").arg(paramName);
0514         if (paramString.contains(str)) {
0515             const QString tmp = QStringLiteral("%%1").arg(i++);
0516             paramString.replace(str, tmp);
0517 
0518             if (firstArg) {
0519                 arguments += QLatin1String{".arg( "};
0520                 firstArg = false;
0521             }
0522 
0523             arguments += QLatin1String("mParam%1, ").arg(paramName);
0524         }
0525     }
0526 
0527     if (!arguments.isEmpty()) {
0528         // Remove the last ", "
0529         arguments.chop(2);
0530 
0531         // Close the ".arg( "
0532         arguments += QLatin1String{" )"};
0533     } else {
0534         return QLatin1String("QStringLiteral( \"%1\" )").arg(group);
0535     }
0536 
0537     return QLatin1String("QStringLiteral( \"%1\" )%2").arg(paramString, arguments);
0538 }
0539 
0540 QString translatedString(const KConfigParameters &cfg, const QString &string, const QString &context, const QString &param, const QString &paramValue)
0541 {
0542     QString result;
0543 
0544     switch (cfg.translationSystem) {
0545     case KConfigParameters::QtTranslation:
0546         if (!context.isEmpty()) {
0547             result += QLatin1String("/*: %1 */ QCoreApplication::translate(\"").arg(context);
0548         } else {
0549             result += QLatin1String{"QCoreApplication::translate(\""};
0550         }
0551         result += QLatin1String("%1\", ").arg(cfg.className);
0552         break;
0553 
0554     case KConfigParameters::KdeTranslation:
0555         if (!cfg.translationDomain.isEmpty() && !context.isEmpty()) {
0556             result += QLatin1String("i18ndc(%1, %2, ").arg(quoteString(cfg.translationDomain), quoteString(context));
0557         } else if (!cfg.translationDomain.isEmpty()) {
0558             result += QLatin1String("i18nd(%1, ").arg(quoteString(cfg.translationDomain));
0559         } else if (!context.isEmpty()) {
0560             result += QLatin1String("i18nc(%1, ").arg(quoteString(context));
0561         } else {
0562             result += QLatin1String{"i18n("};
0563         }
0564         break;
0565     }
0566 
0567     if (!param.isEmpty()) {
0568         QString resolvedString = string;
0569         resolvedString.replace(QLatin1String("$(%1)").arg(param), paramValue);
0570         result += quoteString(resolvedString);
0571     } else {
0572         result += quoteString(string);
0573     }
0574 
0575     result += QLatin1Char{')'};
0576 
0577     return result;
0578 }
0579 
0580 /* int i is the value of the parameter */
0581 QString userTextsFunctions(const CfgEntry *e, const KConfigParameters &cfg, QString itemVarStr, const QString &i)
0582 {
0583     QString txt;
0584     if (itemVarStr.isNull()) {
0585         itemVarStr = itemPath(e, cfg);
0586     }
0587     if (!e->label.isEmpty()) {
0588         txt += QLatin1String("  %1->setLabel( %2 );\n").arg(itemVarStr, translatedString(cfg, e->label, e->labelContext, e->param, i));
0589     }
0590     if (!e->toolTip.isEmpty()) {
0591         txt += QLatin1String("  %1->setToolTip( %2 );\n").arg(itemVarStr, translatedString(cfg, e->toolTip, e->toolTipContext, e->param, i));
0592     }
0593     if (!e->whatsThis.isEmpty()) {
0594         txt += QLatin1String("  %1->setWhatsThis( %2 );\n").arg(itemVarStr, translatedString(cfg, e->whatsThis, e->whatsThisContext, e->param, i));
0595     }
0596     return txt;
0597 }
0598 
0599 // returns the member mutator implementation
0600 // which should go in the h file if inline
0601 // or the cpp file if not inline
0602 // TODO: Fix add Debug Method, it should also take the debug string.
0603 void addDebugMethod(QTextStream &out, const KConfigParameters &cfg, const QString &n)
0604 {
0605     if (cfg.qCategoryLoggingName.isEmpty()) {
0606         out << "  qDebug() << \"" << setFunction(n);
0607     } else {
0608         out << "  qCDebug(" << cfg.qCategoryLoggingName << ") << \"" << setFunction(n);
0609     }
0610 }
0611 
0612 // returns the member get default implementation
0613 // which should go in the h file if inline
0614 // or the cpp file if not inline
0615 QString memberGetDefaultBody(const CfgEntry *e)
0616 {
0617     QString result = e->code;
0618     QTextStream out(&result, QIODevice::WriteOnly);
0619     out << '\n';
0620 
0621     if (!e->param.isEmpty()) {
0622         out << "  switch (i) {\n";
0623         for (int i = 0; i <= e->paramMax; ++i) {
0624             if (!e->paramDefaultValues[i].isEmpty()) {
0625                 out << "  case " << i << ": return " << e->paramDefaultValues[i] << ";\n";
0626             }
0627         }
0628         QString defaultValue = e->defaultValue;
0629 
0630         out << "  default:\n";
0631         out << "    return " << defaultValue.replace(QLatin1String("$(%1)").arg(e->param), QLatin1String("i")) << ";\n";
0632         out << "  }\n";
0633     } else {
0634         out << "  return " << e->defaultValue << ';';
0635     }
0636 
0637     return result;
0638 }
0639 
0640 // returns the item accessor implementation
0641 // which should go in the h file if inline
0642 // or the cpp file if not inline
0643 QString itemAccessorBody(const CfgEntry *e, const KConfigParameters &cfg)
0644 {
0645     QString result;
0646     QTextStream out(&result, QIODevice::WriteOnly);
0647 
0648     out << "return " << itemPath(e, cfg);
0649     if (!e->param.isEmpty()) {
0650         out << "[i]";
0651     }
0652     out << ";\n";
0653 
0654     return result;
0655 }
0656 
0657 // indents text adding X spaces per line
0658 QString indent(QString text, int spaces)
0659 {
0660     QString result;
0661     QTextStream out(&result, QIODevice::WriteOnly);
0662     QTextStream in(&text, QIODevice::ReadOnly);
0663     QString currLine;
0664     while (!in.atEnd()) {
0665         currLine = in.readLine();
0666         if (!currLine.isEmpty()) {
0667             for (int i = 0; i < spaces; ++i) {
0668                 out << " ";
0669             }
0670         }
0671         out << currLine << '\n';
0672     }
0673     return result;
0674 }
0675 
0676 bool hasErrors(KConfigXmlParser &parser, const ParseResult &parseResult, const KConfigParameters &cfg)
0677 {
0678     Q_UNUSED(parser)
0679 
0680     if (cfg.className.isEmpty()) {
0681         std::cerr << "Class name missing" << std::endl;
0682         return true;
0683     }
0684 
0685     if (cfg.singleton && !parseResult.parameters.isEmpty()) {
0686         std::cerr << "Singleton class can not have parameters" << std::endl;
0687         return true;
0688     }
0689 
0690     if (!parseResult.cfgFileName.isEmpty() && parseResult.cfgFileNameArg) {
0691         std::cerr << "Having both a fixed filename and a filename as argument is not possible." << std::endl;
0692         return true;
0693     }
0694 
0695     /* TODO: For some reason some configuration files prefer to have *no* entries
0696      * at all in it, and the generated code is mostly bogus as KConfigXT will not
0697      * handle save / load / properties, etc, nothing.
0698      *
0699      * The first of those files that I came across are qmakebuilderconfig.kcfg from the KDevelop
0700      * project.
0701      * I think we should remove the possibility of creating configuration classes from configuration
0702      * files that don't really have configuration in it. but I'm changing this right now to allow
0703      * kdevelop files to pass.
0704      *
0705      * Remove for KDE 6
0706      * (to make things more interesting, it failed in a code that's never used within KDevelop... )
0707      */
0708     if (parseResult.entries.isEmpty()) {
0709         std::cerr << "No entries." << std::endl;
0710         return false;
0711     }
0712 
0713     return false;
0714 }
0715 
0716 int main(int argc, char **argv)
0717 {
0718     QCoreApplication app(argc, argv);
0719     app.setApplicationName(QStringLiteral("kconfig_compiler"));
0720     app.setApplicationVersion(QStringLiteral(KCONFIG_VERSION_STRING));
0721 
0722     QString inputFilename;
0723     QString codegenFilename;
0724 
0725     QCommandLineOption targetDirectoryOption(QStringList{QStringLiteral("d"), QStringLiteral("directory")},
0726                                              QCoreApplication::translate("main", "Directory to generate files in [.]"),
0727                                              QCoreApplication::translate("main", "directory"),
0728                                              QStringLiteral("."));
0729 
0730     QCommandLineOption licenseOption(QStringList{QStringLiteral("l"), QStringLiteral("license")},
0731                                      QCoreApplication::translate("main", "Display software license."));
0732 
0733     QCommandLineParser parser;
0734 
0735     parser.addPositionalArgument(QStringLiteral("file.kcfg"), QStringLiteral("Input kcfg XML file"));
0736     parser.addPositionalArgument(QStringLiteral("file.kcfgc"), QStringLiteral("Code generation options file"));
0737 
0738     parser.addOption(targetDirectoryOption);
0739     parser.addOption(licenseOption);
0740 
0741     parser.addVersionOption();
0742     parser.addHelpOption();
0743     parser.process(app);
0744 
0745     if (parser.isSet(licenseOption)) {
0746         std::cout << "Copyright 2003 Cornelius Schumacher, Waldo Bastian, Zack Rusin," << std::endl;
0747         std::cout << "    Reinhold Kainhofer, Duncan Mac-Vicar P., Harald Fernengel" << std::endl;
0748         std::cout << "This program comes with ABSOLUTELY NO WARRANTY." << std::endl;
0749         std::cout << "You may redistribute copies of this program" << std::endl;
0750         std::cout << "under the terms of the GNU Library Public License." << std::endl;
0751         std::cout << "For more information about these matters, see the file named COPYING." << std::endl;
0752         return 0;
0753     }
0754 
0755     const QStringList args = parser.positionalArguments();
0756     if (args.count() < 2) {
0757         std::cerr << "Too few arguments." << std::endl;
0758         return 1;
0759     }
0760 
0761     if (args.count() > 2) {
0762         std::cerr << "Too many arguments." << std::endl;
0763         return 1;
0764     }
0765     inputFilename = args.at(0);
0766     codegenFilename = args.at(1);
0767 
0768     // TODO: Transform baseDir into a helper.
0769     QString baseDir = parser.value(targetDirectoryOption);
0770 
0771 #ifdef Q_OS_WIN
0772     if (!baseDir.endsWith(QLatin1Char{'/'}) && !baseDir.endsWith(QLatin1Char{'\\'})) {
0773 #else
0774     if (!baseDir.endsWith(QLatin1Char{'/'})) {
0775 #endif
0776         baseDir.append(QLatin1Char{'/'});
0777     }
0778 
0779     KConfigParameters cfg(codegenFilename);
0780 
0781     KConfigXmlParser xmlParser(cfg, inputFilename);
0782 
0783     // The Xml Parser aborts in the case of an error, so if we get
0784     // to parseResult, we have a working Xml file.
0785     xmlParser.start();
0786 
0787     ParseResult parseResult = xmlParser.getParseResult();
0788 
0789     if (hasErrors(xmlParser, parseResult, cfg)) {
0790         return 1;
0791     }
0792 
0793     // TODO: Move this to somewhere saner.
0794     for (const auto &signal : std::as_const(parseResult.signalList)) {
0795         parseResult.hasNonModifySignals |= !signal.modify;
0796     }
0797 
0798     // remove '.kcfg' from the name.
0799     const QString baseName = inputFilename.mid(0, inputFilename.size() - 5);
0800     KConfigHeaderGenerator headerGenerator(baseName, baseDir, cfg, parseResult);
0801     headerGenerator.start();
0802     headerGenerator.save();
0803 
0804     KConfigSourceGenerator sourceGenerator(baseName, baseDir, cfg, parseResult);
0805     sourceGenerator.start();
0806     sourceGenerator.save();
0807 
0808     qDeleteAll(parseResult.entries);
0809 }