1/*
2 This file is part of KDE.
3
4 SPDX-FileCopyrightText: 2003 Cornelius Schumacher <schumacher@kde.org>
5 SPDX-FileCopyrightText: 2003 Waldo Bastian <bastian@kde.org>
6 SPDX-FileCopyrightText: 2003 Zack Rusin <zack@kde.org>
7 SPDX-FileCopyrightText: 2006 Michaƫl Larouche <michael.larouche@kdemail.net>
8 SPDX-FileCopyrightText: 2008 Allen Winter <winter@kde.org>
9
10 SPDX-License-Identifier: LGPL-2.0-or-later
11*/
12
13#include <QCommandLineOption>
14#include <QCommandLineParser>
15#include <QCoreApplication>
16#include <QDomAttr>
17#include <QFile>
18#include <QFileInfo>
19#include <QRegularExpression>
20#include <QSettings>
21#include <QStringList>
22#include <QTextStream>
23
24#include <algorithm>
25#include <iostream>
26#include <ostream>
27#include <stdlib.h>
28
29#include "../core/kconfig_version.h"
30#include "KConfigCommonStructs.h"
31#include "KConfigHeaderGenerator.h"
32#include "KConfigParameters.h"
33#include "KConfigSourceGenerator.h"
34#include "KConfigXmlParser.h"
35
36QString varName(const QString &n, const KConfigParameters &cfg)
37{
38 QString result;
39 if (!cfg.dpointer) {
40 result = QChar::fromLatin1(c: 'm') + n;
41 result[1] = result.at(i: 1).toUpper();
42 } else {
43 result = n;
44 result[0] = result.at(i: 0).toLower();
45 }
46 return result;
47}
48
49QString varPath(const QString &n, const KConfigParameters &cfg)
50{
51 QString result;
52 if (cfg.dpointer) {
53 result = QLatin1String{"d->"} + varName(n, cfg);
54 } else {
55 result = varName(n, cfg);
56 }
57 return result;
58}
59
60QString enumName(const QString &n)
61{
62 QString result = QLatin1String("Enum") + n;
63 result[4] = result.at(i: 4).toUpper();
64 return result;
65}
66
67QString enumName(const QString &n, const CfgEntry::Choices &c)
68{
69 QString result = c.name();
70 if (result.isEmpty()) {
71 result = QLatin1String("Enum") + n;
72 result[4] = result.at(i: 4).toUpper();
73 }
74 return result;
75}
76
77QString enumType(const CfgEntry *e, bool globalEnums)
78{
79 QString result = e->choices.name();
80 if (result.isEmpty()) {
81 result = QLatin1String("Enum") + e->name;
82 if (!globalEnums) {
83 result += QLatin1String("::type");
84 }
85 result[4] = result.at(i: 4).toUpper();
86 }
87 return result;
88}
89
90QString enumTypeQualifier(const QString &n, const CfgEntry::Choices &c)
91{
92 QString result = c.name();
93 if (result.isEmpty()) {
94 result = QLatin1String("Enum") + n + QLatin1String("::");
95 result[4] = result.at(i: 4).toUpper();
96 } else if (c.external()) {
97 result = c.externalQualifier();
98 } else {
99 result.clear();
100 }
101 return result;
102}
103
104QString setFunction(const QString &n, const QString &className)
105{
106 QString result = QLatin1String("set") + n;
107 result[3] = result.at(i: 3).toUpper();
108
109 if (!className.isEmpty()) {
110 result = className + QLatin1String("::") + result;
111 }
112 return result;
113}
114
115QString changeSignalName(const QString &n)
116{
117 return n + QLatin1String{"Changed"};
118}
119
120QString getDefaultFunction(const QString &n, const QString &className)
121{
122 QString result = QLatin1String("default%1Value").arg(args: n);
123 result[7] = result.at(i: 7).toUpper();
124
125 if (!className.isEmpty()) {
126 result.prepend(s: className + QLatin1String("::"));
127 }
128 return result;
129}
130
131QString getFunction(const QString &n, const QString &className)
132{
133 QString result = n;
134 result[0] = result.at(i: 0).toLower();
135
136 if (!className.isEmpty()) {
137 result.prepend(s: className + QLatin1String("::"));
138 }
139 return result;
140}
141
142QString immutableFunction(const QString &n, const QString &className)
143{
144 QString result = QLatin1String("is") + n;
145 result[2] = result.at(i: 2).toUpper();
146 result += QLatin1String{"Immutable"};
147
148 if (!className.isEmpty()) {
149 result.prepend(s: className + QLatin1String("::"));
150 }
151 return result;
152}
153
154void addQuotes(QString &s)
155{
156 if (!s.startsWith(c: QLatin1Char('"'))) {
157 s.prepend(c: QLatin1Char('"'));
158 }
159 if (!s.endsWith(c: QLatin1Char('"'))) {
160 s.append(c: QLatin1Char('"'));
161 }
162}
163
164static QString quoteString(const QString &s)
165{
166 QString r = s;
167 r.replace(c: QLatin1Char('\\'), after: QLatin1String("\\\\"));
168 r.replace(c: QLatin1Char('\"'), after: QLatin1String("\\\""));
169 r.remove(c: QLatin1Char('\r'));
170 r.replace(c: QLatin1Char('\n'), after: QLatin1String("\\n\"\n\""));
171 return QLatin1Char('\"') + r + QLatin1Char('\"');
172}
173
174QString literalString(const QString &str)
175{
176 const bool isAscii = std::none_of(first: str.cbegin(), last: str.cend(), pred: [](const QChar ch) {
177 return ch.unicode() > 127;
178 });
179
180 if (isAscii) {
181 return QLatin1String("QStringLiteral( %1 )").arg(args: quoteString(s: str));
182 } else {
183 return QLatin1String("QString::fromUtf8( %1 )").arg(args: quoteString(s: str));
184 }
185}
186
187QString signalEnumName(const QString &signalName)
188{
189 QString result;
190 result = QLatin1String("signal") + signalName;
191 result[6] = result.at(i: 6).toUpper();
192
193 return result;
194}
195
196bool isUnsigned(const QString &type)
197{
198 return type == QLatin1String("UInt") || type == QLatin1String("ULongLong");
199}
200
201/**
202 Return parameter declaration for given type.
203*/
204QString param(const QString &t)
205{
206 const QString type = t.toLower();
207 if (type == QLatin1String("string")) {
208 return QStringLiteral("const QString &");
209 } else if (type == QLatin1String("stringlist")) {
210 return QStringLiteral("const QStringList &");
211 } else if (type == QLatin1String("font")) {
212 return QStringLiteral("const QFont &");
213 } else if (type == QLatin1String("rect")) {
214 return QStringLiteral("const QRect &");
215 } else if (type == QLatin1String("rectf")) {
216 return QStringLiteral("const QRectF &");
217 } else if (type == QLatin1String("size")) {
218 return QStringLiteral("const QSize &");
219 } else if (type == QLatin1String("sizef")) {
220 return QStringLiteral("const QSizeF &");
221 } else if (type == QLatin1String("color")) {
222 return QStringLiteral("const QColor &");
223 } else if (type == QLatin1String("point")) {
224 return QStringLiteral("const QPoint &");
225 } else if (type == QLatin1String("pointf")) {
226 return QStringLiteral("const QPointF &");
227 } else if (type == QLatin1String("int")) {
228 return QStringLiteral("int");
229 } else if (type == QLatin1String("uint")) {
230 return QStringLiteral("uint");
231 } else if (type == QLatin1String("bool")) {
232 return QStringLiteral("bool");
233 } else if (type == QLatin1String("double")) {
234 return QStringLiteral("double");
235 } else if (type == QLatin1String("datetime")) {
236 return QStringLiteral("const QDateTime &");
237 } else if (type == QLatin1String("longlong")) {
238 return QStringLiteral("qint64");
239 } else if (type == QLatin1String("ulonglong")) {
240 return QStringLiteral("quint64");
241 } else if (type == QLatin1String("intlist")) {
242 return QStringLiteral("const QList<int> &");
243 } else if (type == QLatin1String("enum")) {
244 return QStringLiteral("int");
245 } else if (type == QLatin1String("path")) {
246 return QStringLiteral("const QString &");
247 } else if (type == QLatin1String("pathlist")) {
248 return QStringLiteral("const QStringList &");
249 } else if (type == QLatin1String("password")) {
250 return QStringLiteral("const QString &");
251 } else if (type == QLatin1String("url")) {
252 return QStringLiteral("const QUrl &");
253 } else if (type == QLatin1String("urllist")) {
254 return QStringLiteral("const QList<QUrl> &");
255 } else {
256 std::cerr << "kconfig_compiler_kf6 does not support type \"" << qPrintable(type) << "\"" << std::endl;
257 return QStringLiteral("QString"); // For now, but an assert would be better
258 }
259}
260
261/**
262 Actual C++ storage type for given type.
263*/
264QString cppType(const QString &t)
265{
266 const QString type = t.toLower();
267 if (type == QLatin1String("string")) {
268 return QStringLiteral("QString");
269 } else if (type == QLatin1String("stringlist")) {
270 return QStringLiteral("QStringList");
271 } else if (type == QLatin1String("font")) {
272 return QStringLiteral("QFont");
273 } else if (type == QLatin1String("rect")) {
274 return QStringLiteral("QRect");
275 } else if (type == QLatin1String("rectf")) {
276 return QStringLiteral("QRectF");
277 } else if (type == QLatin1String("size")) {
278 return QStringLiteral("QSize");
279 } else if (type == QLatin1String("sizef")) {
280 return QStringLiteral("QSizeF");
281 } else if (type == QLatin1String("color")) {
282 return QStringLiteral("QColor");
283 } else if (type == QLatin1String("point")) {
284 return QStringLiteral("QPoint");
285 } else if (type == QLatin1String("pointf")) {
286 return QStringLiteral("QPointF");
287 } else if (type == QLatin1String("int")) {
288 return QStringLiteral("int");
289 } else if (type == QLatin1String("uint")) {
290 return QStringLiteral("uint");
291 } else if (type == QLatin1String("bool")) {
292 return QStringLiteral("bool");
293 } else if (type == QLatin1String("double")) {
294 return QStringLiteral("double");
295 } else if (type == QLatin1String("datetime")) {
296 return QStringLiteral("QDateTime");
297 } else if (type == QLatin1String("longlong")) {
298 return QStringLiteral("qint64");
299 } else if (type == QLatin1String("ulonglong")) {
300 return QStringLiteral("quint64");
301 } else if (type == QLatin1String("intlist")) {
302 return QStringLiteral("QList<int>");
303 } else if (type == QLatin1String("enum")) {
304 return QStringLiteral("int");
305 } else if (type == QLatin1String("path")) {
306 return QStringLiteral("QString");
307 } else if (type == QLatin1String("pathlist")) {
308 return QStringLiteral("QStringList");
309 } else if (type == QLatin1String("password")) {
310 return QStringLiteral("QString");
311 } else if (type == QLatin1String("url")) {
312 return QStringLiteral("QUrl");
313 } else if (type == QLatin1String("urllist")) {
314 return QStringLiteral("QList<QUrl>");
315 } else {
316 std::cerr << "kconfig_compiler_kf6 does not support type \"" << qPrintable(type) << "\"" << std::endl;
317 return QStringLiteral("QString"); // For now, but an assert would be better
318 }
319}
320
321QString defaultValue(const QString &t)
322{
323 const QString type = t.toLower();
324 if (type == QLatin1String("string")) {
325 return QStringLiteral("\"\""); // Use empty string, not null string!
326 } else if (type == QLatin1String("stringlist")) {
327 return QStringLiteral("QStringList()");
328 } else if (type == QLatin1String("font")) {
329 return QStringLiteral("QFont()");
330 } else if (type == QLatin1String("rect")) {
331 return QStringLiteral("QRect()");
332 } else if (type == QLatin1String("rectf")) {
333 return QStringLiteral("QRectF()");
334 } else if (type == QLatin1String("size")) {
335 return QStringLiteral("QSize()");
336 } else if (type == QLatin1String("sizef")) {
337 return QStringLiteral("QSizeF()");
338 } else if (type == QLatin1String("color")) {
339 return QStringLiteral("QColor(128, 128, 128)");
340 } else if (type == QLatin1String("point")) {
341 return QStringLiteral("QPoint()");
342 } else if (type == QLatin1String("pointf")) {
343 return QStringLiteral("QPointF()");
344 } else if (type == QLatin1String("int")) {
345 return QStringLiteral("0");
346 } else if (type == QLatin1String("uint")) {
347 return QStringLiteral("0");
348 } else if (type == QLatin1String("bool")) {
349 return QStringLiteral("false");
350 } else if (type == QLatin1String("double")) {
351 return QStringLiteral("0.0");
352 } else if (type == QLatin1String("datetime")) {
353 return QStringLiteral("QDateTime()");
354 } else if (type == QLatin1String("longlong")) {
355 return QStringLiteral("0");
356 } else if (type == QLatin1String("ulonglong")) {
357 return QStringLiteral("0");
358 } else if (type == QLatin1String("intlist")) {
359 return QStringLiteral("QList<int>()");
360 } else if (type == QLatin1String("enum")) {
361 return QStringLiteral("0");
362 } else if (type == QLatin1String("path")) {
363 return QStringLiteral("\"\""); // Use empty string, not null string!
364 } else if (type == QLatin1String("pathlist")) {
365 return QStringLiteral("QStringList()");
366 } else if (type == QLatin1String("password")) {
367 return QStringLiteral("\"\""); // Use empty string, not null string!
368 } else if (type == QLatin1String("url")) {
369 return QStringLiteral("QUrl()");
370 } else if (type == QLatin1String("urllist")) {
371 return QStringLiteral("QList<QUrl>()");
372 } else {
373 std::cerr << "Error, kconfig_compiler_kf6 does not support the \"" << qPrintable(type) << "\" type!" << std::endl;
374 return QStringLiteral("QString"); // For now, but an assert would be better
375 }
376}
377
378QString itemType(const QString &type)
379{
380 if (type.isEmpty()) {
381 return QString{};
382 }
383
384 QString str = type;
385 str[0] = str.at(i: 0).toUpper();
386
387 return str;
388}
389
390QString itemDeclaration(const CfgEntry *e, const KConfigParameters &cfg)
391{
392 if (e->name.isEmpty()) {
393 return QString{};
394 }
395
396 const QString type = cfg.inherits + QLatin1String{"::Item"} + itemType(type: e->type);
397
398 QString fCap = e->name;
399 fCap[0] = fCap.at(i: 0).toUpper();
400 const QString argSuffix = (!e->param.isEmpty()) ? (QStringLiteral("[%1]").arg(a: e->paramMax + 1)) : QString();
401 QString result;
402
403 if (!cfg.itemAccessors && !cfg.dpointer) {
404 result += QLatin1String{" "} + (!e->signalList.isEmpty() ? QStringLiteral("KConfigCompilerSignallingItem") : type);
405 result += QLatin1String(" *item%1;\n").arg(args: fCap + argSuffix);
406 }
407
408 if (!e->signalList.isEmpty()) {
409 result += QLatin1String(" %1 *%2;\n").arg(args: type, args: innerItemVar(e, cfg) + argSuffix);
410 }
411
412 return result;
413}
414
415// returns the name of an item variable
416// use itemPath to know the full path
417// like using d-> in case of dpointer
418QString itemVar(const CfgEntry *e, const KConfigParameters &cfg)
419{
420 QString result;
421 if (cfg.itemAccessors) {
422 if (!cfg.dpointer) {
423 result = QLatin1String("m%1Item").arg(args: e->name);
424 result[1] = result.at(i: 1).toUpper();
425 } else {
426 result = e->name + QLatin1String{"Item"};
427 result[0] = result.at(i: 0).toLower();
428 }
429 } else {
430 result = QLatin1String{"item"} + e->name;
431 result[4] = result.at(i: 4).toUpper();
432 }
433 return result;
434}
435
436// returns the name of the local inner item if there is one
437// (before wrapping with KConfigCompilerSignallingItem)
438// Otherwise return itemVar()
439QString innerItemVar(const CfgEntry *e, const KConfigParameters &cfg)
440{
441 if (e->signalList.isEmpty()) {
442 return itemPath(e, cfg);
443 }
444
445 QString result = QLatin1String{"innerItem"} + e->name;
446 result[9] = result.at(i: 9).toUpper();
447 return result;
448}
449
450QString itemPath(const CfgEntry *e, const KConfigParameters &cfg)
451{
452 return cfg.dpointer ? QLatin1String{"d->"} + itemVar(e, cfg) : itemVar(e, cfg);
453}
454
455QString newInnerItem(const CfgEntry *entry, const QString &key, const QString &defaultValue, const KConfigParameters &cfg, const QString &param)
456{
457 QString str = QLatin1String("new %1::Item%2").arg(args: cfg.inherits, args: itemType(type: entry->type));
458 str += QLatin1String("( currentGroup(), %1, %2").arg(args: key, args: varPath(n: entry->name, cfg) + param);
459
460 if (entry->type == QLatin1String("Enum")) {
461 str += QLatin1String{", values"} + entry->name;
462 }
463 if (!defaultValue.isEmpty()) {
464 str += QLatin1String(", ") + defaultValue;
465 }
466 str += QLatin1String(" );");
467
468 return str;
469}
470
471QString newItem(const CfgEntry *entry, const QString &key, const QString &defaultValue, const KConfigParameters &cfg, const QString &param)
472{
473 const QList<Signal> sigs = entry->signalList;
474 if (sigs.isEmpty()) {
475 return newInnerItem(entry, key, defaultValue, cfg, param);
476 }
477
478 QString str;
479 str += QLatin1String("new KConfigCompilerSignallingItem(%1, this, notifyFunction, ").arg(args: innerItemVar(e: entry, cfg) + param);
480 // Append the signal flags
481 const int listSize = sigs.size();
482 for (int i = 0; i < listSize; ++i) {
483 if (i != 0) {
484 str += QLatin1String(" | ");
485 }
486 str += signalEnumName(signalName: sigs[i].name);
487 }
488 str += QLatin1String(");");
489
490 return str;
491}
492
493QString paramString(const QString &s, const CfgEntry *e, int i)
494{
495 QString result = s;
496 const QString needle = QLatin1String("$(%1)").arg(args: e->param);
497 if (result.contains(s: needle)) {
498 const QString tmp = e->paramType == QLatin1String{"Enum"} ? e->paramValues.at(i) : QString::number(i);
499
500 result.replace(before: needle, after: tmp);
501 }
502 return result;
503}
504
505QString paramString(const QString &group, const QList<Param> &parameters)
506{
507 QString paramString = group;
508 QString arguments;
509 int i = 1;
510 bool firstArg = true;
511 for (const auto &param : parameters) {
512 const QString paramName = param.name;
513 const QString str = QLatin1String("$(%1)").arg(args: paramName);
514 if (paramString.contains(s: str)) {
515 const QString tmp = QStringLiteral("%%1").arg(a: i++);
516 paramString.replace(before: str, after: tmp);
517
518 if (firstArg) {
519 arguments += QLatin1String{".arg( "};
520 firstArg = false;
521 }
522
523 arguments += QLatin1String("mParam%1, ").arg(args: paramName);
524 }
525 }
526
527 if (!arguments.isEmpty()) {
528 // Remove the last ", "
529 arguments.chop(n: 2);
530
531 // Close the ".arg( "
532 arguments += QLatin1String{" )"};
533 } else {
534 return QLatin1String("QStringLiteral( \"%1\" )").arg(args: group);
535 }
536
537 return QLatin1String("QStringLiteral( \"%1\" )%2").arg(args&: paramString, args&: arguments);
538}
539
540QString translatedString(const KConfigParameters &cfg, const QString &string, const QString &context, const QString &param, const QString &paramValue)
541{
542 QString result;
543
544 switch (cfg.translationSystem) {
545 case KConfigParameters::QtTranslation:
546 if (!context.isEmpty()) {
547 result += QLatin1String("/*: %1 */ QCoreApplication::translate(\"").arg(args: context);
548 } else {
549 result += QLatin1String{"QCoreApplication::translate(\""};
550 }
551 result += QLatin1String("%1\", ").arg(args: cfg.className);
552 break;
553
554 case KConfigParameters::KdeTranslation:
555 if (!cfg.translationDomain.isEmpty() && !context.isEmpty()) {
556 result += QLatin1String("i18ndc(%1, %2, ").arg(args: quoteString(s: cfg.translationDomain), args: quoteString(s: context));
557 } else if (!cfg.translationDomain.isEmpty()) {
558 result += QLatin1String("i18nd(%1, ").arg(args: quoteString(s: cfg.translationDomain));
559 } else if (!context.isEmpty()) {
560 result += QLatin1String("i18nc(%1, ").arg(args: quoteString(s: context));
561 } else {
562 result += QLatin1String{"i18n("};
563 }
564 break;
565 }
566
567 if (!param.isEmpty()) {
568 QString resolvedString = string;
569 resolvedString.replace(before: QLatin1String("$(%1)").arg(args: param), after: paramValue);
570 result += quoteString(s: resolvedString);
571 } else {
572 result += quoteString(s: string);
573 }
574
575 result += QLatin1Char{')'};
576
577 return result;
578}
579
580/* int i is the value of the parameter */
581QString userTextsFunctions(const CfgEntry *e, const KConfigParameters &cfg, QString itemVarStr, const QString &i)
582{
583 QString txt;
584 if (itemVarStr.isNull()) {
585 itemVarStr = itemPath(e, cfg);
586 }
587 if (!e->label.isEmpty()) {
588 txt += QLatin1String(" %1->setLabel( %2 );\n").arg(args&: itemVarStr, args: translatedString(cfg, string: e->label, context: e->labelContext, param: e->param, paramValue: i));
589 }
590 if (!e->toolTip.isEmpty()) {
591 txt += QLatin1String(" %1->setToolTip( %2 );\n").arg(args&: itemVarStr, args: translatedString(cfg, string: e->toolTip, context: e->toolTipContext, param: e->param, paramValue: i));
592 }
593 if (!e->whatsThis.isEmpty()) {
594 txt += QLatin1String(" %1->setWhatsThis( %2 );\n").arg(args&: itemVarStr, args: translatedString(cfg, string: e->whatsThis, context: e->whatsThisContext, param: e->param, paramValue: i));
595 }
596 return txt;
597}
598
599// returns the member mutator implementation
600// which should go in the h file if inline
601// or the cpp file if not inline
602// TODO: Fix add Debug Method, it should also take the debug string.
603void addDebugMethod(QTextStream &out, const KConfigParameters &cfg, const QString &n)
604{
605 if (cfg.qCategoryLoggingName.isEmpty()) {
606 out << " qDebug() << \"" << setFunction(n);
607 } else {
608 out << " qCDebug(" << cfg.qCategoryLoggingName << ") << \"" << setFunction(n);
609 }
610}
611
612// returns the member get default implementation
613// which should go in the h file if inline
614// or the cpp file if not inline
615QString memberGetDefaultBody(const CfgEntry *e)
616{
617 QString result = e->code;
618 QTextStream out(&result, QIODevice::WriteOnly);
619 out << '\n';
620
621 if (!e->param.isEmpty()) {
622 out << " switch (i) {\n";
623 for (int i = 0; i <= e->paramMax; ++i) {
624 if (!e->paramDefaultValues[i].isEmpty()) {
625 out << " case " << i << ": return " << e->paramDefaultValues[i] << ";\n";
626 }
627 }
628 QString defaultValue = e->defaultValue;
629
630 out << " default:\n";
631 out << " return " << defaultValue.replace(before: QLatin1String("$(%1)").arg(args: e->param), after: QLatin1String("i")) << ";\n";
632 out << " }\n";
633 } else {
634 out << " return " << e->defaultValue << ';';
635 }
636
637 return result;
638}
639
640// returns the item accessor implementation
641// which should go in the h file if inline
642// or the cpp file if not inline
643QString itemAccessorBody(const CfgEntry *e, const KConfigParameters &cfg)
644{
645 QString result;
646 QTextStream out(&result, QIODevice::WriteOnly);
647
648 out << "return " << itemPath(e, cfg);
649 if (!e->param.isEmpty()) {
650 out << "[i]";
651 }
652 out << ";\n";
653
654 return result;
655}
656
657// indents text adding X spaces per line
658QString indent(QString text, int spaces)
659{
660 QString result;
661 QTextStream out(&result, QIODevice::WriteOnly);
662 QTextStream in(&text, QIODevice::ReadOnly);
663 QString currLine;
664 while (!in.atEnd()) {
665 currLine = in.readLine();
666 if (!currLine.isEmpty()) {
667 for (int i = 0; i < spaces; ++i) {
668 out << " ";
669 }
670 }
671 out << currLine << '\n';
672 }
673 return result;
674}
675
676bool hasErrors(KConfigXmlParser &parser, const ParseResult &parseResult, const KConfigParameters &cfg)
677{
678 Q_UNUSED(parser)
679
680 if (cfg.className.isEmpty()) {
681 std::cerr << "Class name missing" << std::endl;
682 return true;
683 }
684
685 if (cfg.singleton && !parseResult.parameters.isEmpty()) {
686 std::cerr << "Singleton class can not have parameters" << std::endl;
687 return true;
688 }
689
690 if (!parseResult.cfgFileName.isEmpty() && parseResult.cfgFileNameArg) {
691 std::cerr << "Having both a fixed filename and a filename as argument is not possible." << std::endl;
692 return true;
693 }
694
695 /* TODO: For some reason some configuration files prefer to have *no* entries
696 * at all in it, and the generated code is mostly bogus as KConfigXT will not
697 * handle save / load / properties, etc, nothing.
698 *
699 * The first of those files that I came across are qmakebuilderconfig.kcfg from the KDevelop
700 * project.
701 * I think we should remove the possibility of creating configuration classes from configuration
702 * files that don't really have configuration in it. but I'm changing this right now to allow
703 * kdevelop files to pass.
704 *
705 * Remove for KDE 6
706 * (to make things more interesting, it failed in a code that's never used within KDevelop... )
707 */
708 if (parseResult.entries.isEmpty()) {
709 std::cerr << "No entries." << std::endl;
710 return false;
711 }
712
713 return false;
714}
715
716int main(int argc, char **argv)
717{
718 QCoreApplication app(argc, argv);
719 app.setApplicationName(QStringLiteral("kconfig_compiler"));
720 app.setApplicationVersion(QStringLiteral(KCONFIG_VERSION_STRING));
721
722 QString inputFilename;
723 QString codegenFilename;
724
725 QCommandLineOption targetDirectoryOption(QStringList{QStringLiteral("d"), QStringLiteral("directory")},
726 QCoreApplication::translate(context: "main", key: "Directory to generate files in [.]"),
727 QCoreApplication::translate(context: "main", key: "directory"),
728 QStringLiteral("."));
729
730 QCommandLineOption licenseOption(QStringList{QStringLiteral("l"), QStringLiteral("license")},
731 QCoreApplication::translate(context: "main", key: "Display software license."));
732
733 QCommandLineParser parser;
734
735 parser.addPositionalArgument(QStringLiteral("file.kcfg"), QStringLiteral("Input kcfg XML file"));
736 parser.addPositionalArgument(QStringLiteral("file.kcfgc"), QStringLiteral("Code generation options file"));
737
738 parser.addOption(commandLineOption: targetDirectoryOption);
739 parser.addOption(commandLineOption: licenseOption);
740
741 parser.addVersionOption();
742 parser.addHelpOption();
743 parser.process(app);
744
745 if (parser.isSet(option: licenseOption)) {
746 std::cout << "Copyright 2003 Cornelius Schumacher, Waldo Bastian, Zack Rusin," << std::endl;
747 std::cout << " Reinhold Kainhofer, Duncan Mac-Vicar P., Harald Fernengel" << std::endl;
748 std::cout << "This program comes with ABSOLUTELY NO WARRANTY." << std::endl;
749 std::cout << "You may redistribute copies of this program" << std::endl;
750 std::cout << "under the terms of the GNU Library Public License." << std::endl;
751 std::cout << "For more information about these matters, see the file named COPYING." << std::endl;
752 return 0;
753 }
754
755 const QStringList args = parser.positionalArguments();
756 if (args.count() < 2) {
757 std::cerr << "Too few arguments." << std::endl;
758 return 1;
759 }
760
761 if (args.count() > 2) {
762 std::cerr << "Too many arguments." << std::endl;
763 return 1;
764 }
765 inputFilename = args.at(i: 0);
766 codegenFilename = args.at(i: 1);
767
768 // TODO: Transform baseDir into a helper.
769 QString baseDir = parser.value(option: targetDirectoryOption);
770
771#ifdef Q_OS_WIN
772 if (!baseDir.endsWith(QLatin1Char{'/'}) && !baseDir.endsWith(QLatin1Char{'\\'})) {
773#else
774 if (!baseDir.endsWith(c: QLatin1Char{'/'})) {
775#endif
776 baseDir.append(c: QLatin1Char{'/'});
777 }
778
779 KConfigParameters cfg(codegenFilename);
780
781 KConfigXmlParser xmlParser(cfg, inputFilename);
782
783 // The Xml Parser aborts in the case of an error, so if we get
784 // to parseResult, we have a working Xml file.
785 xmlParser.start();
786
787 ParseResult parseResult = xmlParser.getParseResult();
788
789 if (hasErrors(parser&: xmlParser, parseResult, cfg)) {
790 return 1;
791 }
792
793 // TODO: Move this to somewhere saner.
794 for (const auto &signal : std::as_const(t&: parseResult.signalList)) {
795 parseResult.hasNonModifySignals |= !signal.modify;
796 }
797
798 // remove '.kcfg' from the name.
799 const QString baseName = inputFilename.mid(position: 0, n: inputFilename.size() - 5);
800 KConfigHeaderGenerator headerGenerator(baseName, baseDir, cfg, parseResult);
801 headerGenerator.start();
802 headerGenerator.save();
803
804 KConfigSourceGenerator sourceGenerator(baseName, baseDir, cfg, parseResult);
805 sourceGenerator.start();
806 sourceGenerator.save();
807
808 qDeleteAll(c: parseResult.entries);
809}
810

source code of kconfig/src/kconfig_compiler/kconfig_compiler.cpp