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("time")) {
238 return QStringLiteral("QTime");
239 } else if (type == QLatin1String("longlong")) {
240 return QStringLiteral("qint64");
241 } else if (type == QLatin1String("ulonglong")) {
242 return QStringLiteral("quint64");
243 } else if (type == QLatin1String("intlist")) {
244 return QStringLiteral("const QList<int> &");
245 } else if (type == QLatin1String("enum")) {
246 return QStringLiteral("int");
247 } else if (type == QLatin1String("path")) {
248 return QStringLiteral("const QString &");
249 } else if (type == QLatin1String("pathlist")) {
250 return QStringLiteral("const QStringList &");
251 } else if (type == QLatin1String("password")) {
252 return QStringLiteral("const QString &");
253 } else if (type == QLatin1String("url")) {
254 return QStringLiteral("const QUrl &");
255 } else if (type == QLatin1String("urllist")) {
256 return QStringLiteral("const QList<QUrl> &");
257 } else {
258 std::cerr << "kconfig_compiler_kf6 does not support type \"" << qPrintable(type) << "\"" << std::endl;
259 return QStringLiteral("QString"); // For now, but an assert would be better
260 }
261}
262
263/**
264 Actual C++ storage type for given type.
265*/
266QString cppType(const QString &t)
267{
268 const QString type = t.toLower();
269 if (type == QLatin1String("string")) {
270 return QStringLiteral("QString");
271 } else if (type == QLatin1String("stringlist")) {
272 return QStringLiteral("QStringList");
273 } else if (type == QLatin1String("font")) {
274 return QStringLiteral("QFont");
275 } else if (type == QLatin1String("rect")) {
276 return QStringLiteral("QRect");
277 } else if (type == QLatin1String("rectf")) {
278 return QStringLiteral("QRectF");
279 } else if (type == QLatin1String("size")) {
280 return QStringLiteral("QSize");
281 } else if (type == QLatin1String("sizef")) {
282 return QStringLiteral("QSizeF");
283 } else if (type == QLatin1String("color")) {
284 return QStringLiteral("QColor");
285 } else if (type == QLatin1String("point")) {
286 return QStringLiteral("QPoint");
287 } else if (type == QLatin1String("pointf")) {
288 return QStringLiteral("QPointF");
289 } else if (type == QLatin1String("int")) {
290 return QStringLiteral("int");
291 } else if (type == QLatin1String("uint")) {
292 return QStringLiteral("uint");
293 } else if (type == QLatin1String("bool")) {
294 return QStringLiteral("bool");
295 } else if (type == QLatin1String("double")) {
296 return QStringLiteral("double");
297 } else if (type == QLatin1String("datetime")) {
298 return QStringLiteral("QDateTime");
299 } else if (type == QLatin1String("time")) {
300 return QStringLiteral("QTime");
301 } else if (type == QLatin1String("longlong")) {
302 return QStringLiteral("qint64");
303 } else if (type == QLatin1String("ulonglong")) {
304 return QStringLiteral("quint64");
305 } else if (type == QLatin1String("intlist")) {
306 return QStringLiteral("QList<int>");
307 } else if (type == QLatin1String("enum")) {
308 return QStringLiteral("int");
309 } else if (type == QLatin1String("path")) {
310 return QStringLiteral("QString");
311 } else if (type == QLatin1String("pathlist")) {
312 return QStringLiteral("QStringList");
313 } else if (type == QLatin1String("password")) {
314 return QStringLiteral("QString");
315 } else if (type == QLatin1String("url")) {
316 return QStringLiteral("QUrl");
317 } else if (type == QLatin1String("urllist")) {
318 return QStringLiteral("QList<QUrl>");
319 } else {
320 std::cerr << "kconfig_compiler_kf6 does not support type \"" << qPrintable(type) << "\"" << std::endl;
321 return QStringLiteral("QString"); // For now, but an assert would be better
322 }
323}
324
325QString defaultValue(const QString &t)
326{
327 const QString type = t.toLower();
328 if (type == QLatin1String("string")) {
329 return QStringLiteral("\"\""); // Use empty string, not null string!
330 } else if (type == QLatin1String("stringlist")) {
331 return QStringLiteral("QStringList()");
332 } else if (type == QLatin1String("font")) {
333 return QStringLiteral("QFont()");
334 } else if (type == QLatin1String("rect")) {
335 return QStringLiteral("QRect()");
336 } else if (type == QLatin1String("rectf")) {
337 return QStringLiteral("QRectF()");
338 } else if (type == QLatin1String("size")) {
339 return QStringLiteral("QSize()");
340 } else if (type == QLatin1String("sizef")) {
341 return QStringLiteral("QSizeF()");
342 } else if (type == QLatin1String("color")) {
343 return QStringLiteral("QColor(128, 128, 128)");
344 } else if (type == QLatin1String("point")) {
345 return QStringLiteral("QPoint()");
346 } else if (type == QLatin1String("pointf")) {
347 return QStringLiteral("QPointF()");
348 } else if (type == QLatin1String("int")) {
349 return QStringLiteral("0");
350 } else if (type == QLatin1String("uint")) {
351 return QStringLiteral("0");
352 } else if (type == QLatin1String("bool")) {
353 return QStringLiteral("false");
354 } else if (type == QLatin1String("double")) {
355 return QStringLiteral("0.0");
356 } else if (type == QLatin1String("datetime")) {
357 return QStringLiteral("QDateTime()");
358 } else if (type == QLatin1String("time")) {
359 return QStringLiteral("QTime()");
360 } else if (type == QLatin1String("longlong")) {
361 return QStringLiteral("0");
362 } else if (type == QLatin1String("ulonglong")) {
363 return QStringLiteral("0");
364 } else if (type == QLatin1String("intlist")) {
365 return QStringLiteral("QList<int>()");
366 } else if (type == QLatin1String("enum")) {
367 return QStringLiteral("0");
368 } else if (type == QLatin1String("path")) {
369 return QStringLiteral("\"\""); // Use empty string, not null string!
370 } else if (type == QLatin1String("pathlist")) {
371 return QStringLiteral("QStringList()");
372 } else if (type == QLatin1String("password")) {
373 return QStringLiteral("\"\""); // Use empty string, not null string!
374 } else if (type == QLatin1String("url")) {
375 return QStringLiteral("QUrl()");
376 } else if (type == QLatin1String("urllist")) {
377 return QStringLiteral("QList<QUrl>()");
378 } else {
379 std::cerr << "Error, kconfig_compiler_kf6 does not support the \"" << qPrintable(type) << "\" type!" << std::endl;
380 return QStringLiteral("QString"); // For now, but an assert would be better
381 }
382}
383
384QString itemType(const QString &type)
385{
386 if (type.isEmpty()) {
387 return QString{};
388 }
389
390 QString str = type;
391 str[0] = str.at(i: 0).toUpper();
392
393 return str;
394}
395
396QString itemDeclaration(const CfgEntry *e, const KConfigParameters &cfg)
397{
398 if (e->name.isEmpty()) {
399 return QString{};
400 }
401
402 const QString type = cfg.inherits + QLatin1String{"::Item"} + itemType(type: e->type);
403
404 QString fCap = e->name;
405 fCap[0] = fCap.at(i: 0).toUpper();
406 const QString argSuffix = (!e->param.isEmpty()) ? (QStringLiteral("[%1]").arg(a: e->paramMax + 1)) : QString();
407 QString result;
408
409 if (!cfg.itemAccessors && !cfg.dpointer) {
410 result += QLatin1String{" "} + (!e->signalList.isEmpty() ? QStringLiteral("KConfigCompilerSignallingItem") : type);
411 result += QLatin1String(" *item%1;\n").arg(args: fCap + argSuffix);
412 }
413
414 if (!e->signalList.isEmpty()) {
415 result += QLatin1String(" %1 *%2;\n").arg(args: type, args: innerItemVar(e, cfg) + argSuffix);
416 }
417
418 return result;
419}
420
421// returns the name of an item variable
422// use itemPath to know the full path
423// like using d-> in case of dpointer
424QString itemVar(const CfgEntry *e, const KConfigParameters &cfg)
425{
426 QString result;
427 if (cfg.itemAccessors) {
428 if (!cfg.dpointer) {
429 result = QLatin1String("m%1Item").arg(args: e->name);
430 result[1] = result.at(i: 1).toUpper();
431 } else {
432 result = e->name + QLatin1String{"Item"};
433 result[0] = result.at(i: 0).toLower();
434 }
435 } else {
436 result = QLatin1String{"item"} + e->name;
437 result[4] = result.at(i: 4).toUpper();
438 }
439 return result;
440}
441
442// returns the name of the local inner item if there is one
443// (before wrapping with KConfigCompilerSignallingItem)
444// Otherwise return itemVar()
445QString innerItemVar(const CfgEntry *e, const KConfigParameters &cfg)
446{
447 if (e->signalList.isEmpty()) {
448 return itemPath(e, cfg);
449 }
450
451 QString result = QLatin1String{"innerItem"} + e->name;
452 result[9] = result.at(i: 9).toUpper();
453 return result;
454}
455
456QString itemPath(const CfgEntry *e, const KConfigParameters &cfg)
457{
458 return cfg.dpointer ? QLatin1String{"d->"} + itemVar(e, cfg) : itemVar(e, cfg);
459}
460
461QString newInnerItem(const CfgEntry *entry, const QString &key, const QString &defaultValue, const KConfigParameters &cfg, const QString &param)
462{
463 QString str = QLatin1String("new %1::Item%2").arg(args: cfg.inherits, args: itemType(type: entry->type));
464 str += QLatin1String("( currentGroup(), %1, %2").arg(args: key, args: varPath(n: entry->name, cfg) + param);
465
466 if (entry->type == QLatin1String("Enum")) {
467 str += QLatin1String{", values"} + entry->name;
468 }
469 if (!defaultValue.isEmpty()) {
470 str += QLatin1String(", ") + defaultValue;
471 }
472 str += QLatin1String(" );");
473
474 return str;
475}
476
477QString newItem(const CfgEntry *entry, const QString &key, const QString &defaultValue, const KConfigParameters &cfg, const QString &param)
478{
479 const QList<Signal> sigs = entry->signalList;
480 if (sigs.isEmpty()) {
481 return newInnerItem(entry, key, defaultValue, cfg, param);
482 }
483
484 QString str;
485 str += QLatin1String("new KConfigCompilerSignallingItem(%1, this, notifyFunction, ").arg(args: innerItemVar(e: entry, cfg) + param);
486 // Append the signal flags
487 const int listSize = sigs.size();
488 for (int i = 0; i < listSize; ++i) {
489 if (i != 0) {
490 str += QLatin1String(" | ");
491 }
492 str += signalEnumName(signalName: sigs[i].name);
493 }
494 str += QLatin1String(");");
495
496 return str;
497}
498
499QString paramString(const QString &s, const CfgEntry *e, int i)
500{
501 QString result = s;
502 const QString needle = QLatin1String("$(%1)").arg(args: e->param);
503 if (result.contains(s: needle)) {
504 const QString tmp = e->paramType == QLatin1String{"Enum"} ? e->paramValues.at(i) : QString::number(i);
505
506 result.replace(before: needle, after: tmp);
507 }
508 return result;
509}
510
511QString paramString(const QString &group, const QList<Param> &parameters)
512{
513 QString paramString = group;
514 QString arguments;
515 int i = 1;
516 bool firstArg = true;
517 for (const auto &param : parameters) {
518 const QString paramName = param.name;
519 const QString str = QLatin1String("$(%1)").arg(args: paramName);
520 if (paramString.contains(s: str)) {
521 const QString tmp = QStringLiteral("%%1").arg(a: i++);
522 paramString.replace(before: str, after: tmp);
523
524 if (firstArg) {
525 arguments += QLatin1String{".arg( "};
526 firstArg = false;
527 }
528
529 arguments += QLatin1String("mParam%1, ").arg(args: paramName);
530 }
531 }
532
533 if (!arguments.isEmpty()) {
534 // Remove the last ", "
535 arguments.chop(n: 2);
536
537 // Close the ".arg( "
538 arguments += QLatin1String{" )"};
539 } else {
540 return QLatin1String("QStringLiteral( \"%1\" )").arg(args: group);
541 }
542
543 return QLatin1String("QStringLiteral( \"%1\" )%2").arg(args&: paramString, args&: arguments);
544}
545
546QString translatedString(const KConfigParameters &cfg, const QString &string, const QString &context, const QString &param, const QString &paramValue)
547{
548 QString result;
549
550 switch (cfg.translationSystem) {
551 case KConfigParameters::QtTranslation:
552 if (!context.isEmpty()) {
553 result += QLatin1String("/*: %1 */ QCoreApplication::translate(\"").arg(args: context);
554 } else {
555 result += QLatin1String{"QCoreApplication::translate(\""};
556 }
557 result += QLatin1String("%1\", ").arg(args: cfg.className);
558 break;
559
560 case KConfigParameters::KdeTranslation:
561 if (!cfg.translationDomain.isEmpty() && !context.isEmpty()) {
562 result += QLatin1String("i18ndc(%1, %2, ").arg(args: quoteString(s: cfg.translationDomain), args: quoteString(s: context));
563 } else if (!cfg.translationDomain.isEmpty()) {
564 result += QLatin1String("i18nd(%1, ").arg(args: quoteString(s: cfg.translationDomain));
565 } else if (!context.isEmpty()) {
566 result += QLatin1String("i18nc(%1, ").arg(args: quoteString(s: context));
567 } else {
568 result += QLatin1String{"i18n("};
569 }
570 break;
571 }
572
573 if (!param.isEmpty()) {
574 QString resolvedString = string;
575 resolvedString.replace(before: QLatin1String("$(%1)").arg(args: param), after: paramValue);
576 result += quoteString(s: resolvedString);
577 } else {
578 result += quoteString(s: string);
579 }
580
581 result += QLatin1Char{')'};
582
583 return result;
584}
585
586/* int i is the value of the parameter */
587QString userTextsFunctions(const CfgEntry *e, const KConfigParameters &cfg, QString itemVarStr, const QString &i)
588{
589 QString txt;
590 if (itemVarStr.isNull()) {
591 itemVarStr = itemPath(e, cfg);
592 }
593 if (!e->label.isEmpty()) {
594 txt += QLatin1String(" %1->setLabel( %2 );\n").arg(args&: itemVarStr, args: translatedString(cfg, string: e->label, context: e->labelContext, param: e->param, paramValue: i));
595 }
596 if (!e->toolTip.isEmpty()) {
597 txt += QLatin1String(" %1->setToolTip( %2 );\n").arg(args&: itemVarStr, args: translatedString(cfg, string: e->toolTip, context: e->toolTipContext, param: e->param, paramValue: i));
598 }
599 if (!e->whatsThis.isEmpty()) {
600 txt += QLatin1String(" %1->setWhatsThis( %2 );\n").arg(args&: itemVarStr, args: translatedString(cfg, string: e->whatsThis, context: e->whatsThisContext, param: e->param, paramValue: i));
601 }
602 return txt;
603}
604
605// returns the member mutator implementation
606// which should go in the h file if inline
607// or the cpp file if not inline
608// TODO: Fix add Debug Method, it should also take the debug string.
609void addDebugMethod(QTextStream &out, const KConfigParameters &cfg, const QString &n)
610{
611 if (cfg.qCategoryLoggingName.isEmpty()) {
612 out << " qDebug() << \"" << setFunction(n);
613 } else {
614 out << " qCDebug(" << cfg.qCategoryLoggingName << ") << \"" << setFunction(n);
615 }
616}
617
618// returns the member get default implementation
619// which should go in the h file if inline
620// or the cpp file if not inline
621QString memberGetDefaultBody(const CfgEntry *e)
622{
623 QString result = e->code;
624 QTextStream out(&result, QIODevice::WriteOnly);
625 out << '\n';
626
627 if (!e->param.isEmpty()) {
628 out << " switch (i) {\n";
629 for (int i = 0; i <= e->paramMax; ++i) {
630 if (!e->paramDefaultValues[i].isEmpty()) {
631 out << " case " << i << ": return " << e->paramDefaultValues[i] << ";\n";
632 }
633 }
634 QString defaultValue = e->defaultValue;
635
636 out << " default:\n";
637 out << " return " << defaultValue.replace(before: QLatin1String("$(%1)").arg(args: e->param), after: QLatin1String("i")) << ";\n";
638 out << " }\n";
639 } else {
640 out << " return " << e->defaultValue << ';';
641 }
642
643 return result;
644}
645
646// returns the item accessor implementation
647// which should go in the h file if inline
648// or the cpp file if not inline
649QString itemAccessorBody(const CfgEntry *e, const KConfigParameters &cfg)
650{
651 QString result;
652 QTextStream out(&result, QIODevice::WriteOnly);
653
654 out << "return " << itemPath(e, cfg);
655 if (!e->param.isEmpty()) {
656 out << "[i]";
657 }
658 out << ";\n";
659
660 return result;
661}
662
663// indents text adding X spaces per line
664QString indent(QString text, int spaces)
665{
666 QString result;
667 QTextStream out(&result, QIODevice::WriteOnly);
668 QTextStream in(&text, QIODevice::ReadOnly);
669 QString currLine;
670 while (!in.atEnd()) {
671 currLine = in.readLine();
672 if (!currLine.isEmpty()) {
673 for (int i = 0; i < spaces; ++i) {
674 out << " ";
675 }
676 }
677 out << currLine << '\n';
678 }
679 return result;
680}
681
682bool hasErrors(KConfigXmlParser &parser, const ParseResult &parseResult, const KConfigParameters &cfg)
683{
684 Q_UNUSED(parser)
685
686 if (cfg.className.isEmpty()) {
687 std::cerr << "Class name missing" << std::endl;
688 return true;
689 }
690
691 if (cfg.singleton && !parseResult.parameters.isEmpty()) {
692 std::cerr << "Singleton class can not have parameters" << std::endl;
693 return true;
694 }
695
696 if (!parseResult.cfgFileName.isEmpty() && parseResult.cfgFileNameArg) {
697 std::cerr << "Having both a fixed filename and a filename as argument is not possible." << std::endl;
698 return true;
699 }
700
701 /* TODO: For some reason some configuration files prefer to have *no* entries
702 * at all in it, and the generated code is mostly bogus as KConfigXT will not
703 * handle save / load / properties, etc, nothing.
704 *
705 * The first of those files that I came across are qmakebuilderconfig.kcfg from the KDevelop
706 * project.
707 * I think we should remove the possibility of creating configuration classes from configuration
708 * files that don't really have configuration in it. but I'm changing this right now to allow
709 * kdevelop files to pass.
710 *
711 * Remove for KDE 6
712 * (to make things more interesting, it failed in a code that's never used within KDevelop... )
713 */
714 if (parseResult.entries.isEmpty()) {
715 std::cerr << "No entries." << std::endl;
716 return false;
717 }
718
719 return false;
720}
721
722int main(int argc, char **argv)
723{
724 QCoreApplication app(argc, argv);
725 app.setApplicationName(QStringLiteral("kconfig_compiler"));
726 app.setApplicationVersion(QStringLiteral(KCONFIG_VERSION_STRING));
727
728 QString inputFilename;
729 QString codegenFilename;
730
731 QCommandLineOption targetDirectoryOption(QStringList{QStringLiteral("d"), QStringLiteral("directory")},
732 QCoreApplication::translate(context: "main", key: "Directory to generate files in [.]"),
733 QCoreApplication::translate(context: "main", key: "directory"),
734 QStringLiteral("."));
735
736 QCommandLineOption licenseOption(QStringList{QStringLiteral("l"), QStringLiteral("license")},
737 QCoreApplication::translate(context: "main", key: "Display software license."));
738
739 QCommandLineParser parser;
740
741 parser.addPositionalArgument(QStringLiteral("file.kcfg"), QStringLiteral("Input kcfg XML file"));
742 parser.addPositionalArgument(QStringLiteral("file.kcfgc"), QStringLiteral("Code generation options file"));
743
744 parser.addOption(commandLineOption: targetDirectoryOption);
745 parser.addOption(commandLineOption: licenseOption);
746
747 parser.addVersionOption();
748 parser.addHelpOption();
749 parser.process(app);
750
751 if (parser.isSet(option: licenseOption)) {
752 std::cout << "Copyright 2003 Cornelius Schumacher, Waldo Bastian, Zack Rusin," << std::endl;
753 std::cout << " Reinhold Kainhofer, Duncan Mac-Vicar P., Harald Fernengel" << std::endl;
754 std::cout << "This program comes with ABSOLUTELY NO WARRANTY." << std::endl;
755 std::cout << "You may redistribute copies of this program" << std::endl;
756 std::cout << "under the terms of the GNU Library Public License." << std::endl;
757 std::cout << "For more information about these matters, see the file named COPYING." << std::endl;
758 return 0;
759 }
760
761 const QStringList args = parser.positionalArguments();
762 if (args.count() < 2) {
763 std::cerr << "Too few arguments." << std::endl;
764 return 1;
765 }
766
767 if (args.count() > 2) {
768 std::cerr << "Too many arguments." << std::endl;
769 return 1;
770 }
771 inputFilename = args.at(i: 0);
772 codegenFilename = args.at(i: 1);
773
774 // TODO: Transform baseDir into a helper.
775 QString baseDir = parser.value(option: targetDirectoryOption);
776
777#ifdef Q_OS_WIN
778 if (!baseDir.endsWith(QLatin1Char{'/'}) && !baseDir.endsWith(QLatin1Char{'\\'})) {
779#else
780 if (!baseDir.endsWith(c: QLatin1Char{'/'})) {
781#endif
782 baseDir.append(c: QLatin1Char{'/'});
783 }
784
785 KConfigParameters cfg(codegenFilename);
786
787 KConfigXmlParser xmlParser(cfg, inputFilename);
788
789 // The Xml Parser aborts in the case of an error, so if we get
790 // to parseResult, we have a working Xml file.
791 xmlParser.start();
792
793 ParseResult parseResult = xmlParser.getParseResult();
794
795 if (hasErrors(parser&: xmlParser, parseResult, cfg)) {
796 return 1;
797 }
798
799 // TODO: Move this to somewhere saner.
800 for (const auto &signal : std::as_const(t&: parseResult.signalList)) {
801 parseResult.hasNonModifySignals |= !signal.modify;
802 }
803
804 // remove '.kcfg' from the name.
805 const QString baseName = inputFilename.mid(position: 0, n: inputFilename.size() - 5);
806 KConfigHeaderGenerator headerGenerator(baseName, baseDir, cfg, parseResult);
807 headerGenerator.start();
808 headerGenerator.save();
809
810 KConfigSourceGenerator sourceGenerator(baseName, baseDir, cfg, parseResult);
811 sourceGenerator.start();
812 sourceGenerator.save();
813
814 qDeleteAll(c: parseResult.entries);
815}
816

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