Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
config.cpp
Go to the documentation of this file.
1// Copyright (C) 2021 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
3
4#include "config.h"
6#include "utilities.h"
7
8#include <QtCore/qdir.h>
9#include <QtCore/qfile.h>
10#include <QtCore/qfileinfo.h>
11#include <QtCore/qlatin1stringview.h>
12#include <QtCore/qtemporaryfile.h>
13#include <QtCore/qtextstream.h>
14#include <QtCore/qvariant.h>
15#include <QtCore/qregularexpression.h>
16
18
19using namespace Qt::StringLiterals;
20
21QString ConfigStrings::AUTOLINKERRORS = QStringLiteral("autolinkerrors");
22QString ConfigStrings::BUILDVERSION = QStringLiteral("buildversion");
23QString ConfigStrings::CODEINDENT = QStringLiteral("codeindent");
24QString ConfigStrings::CODEPREFIX = QStringLiteral("codeprefix");
25QString ConfigStrings::CODESUFFIX = QStringLiteral("codesuffix");
26QString ConfigStrings::CPPCLASSESPAGE = QStringLiteral("cppclassespage");
27QString ConfigStrings::CPPCLASSESTITLE = QStringLiteral("cppclassestitle");
28QString ConfigStrings::DEFINES = QStringLiteral("defines");
29QString ConfigStrings::DEPENDS = QStringLiteral("depends");
30QString ConfigStrings::DESCRIPTION = QStringLiteral("description");
31QString ConfigStrings::DOCBOOKEXTENSIONS = QStringLiteral("usedocbookextensions");
32QString ConfigStrings::DOCUMENTATIONINHEADERS = QStringLiteral("documentationinheaders");
33QString ConfigStrings::ENDHEADER = QStringLiteral("endheader");
34QString ConfigStrings::EXAMPLEDIRS = QStringLiteral("exampledirs");
35QString ConfigStrings::EXAMPLES = QStringLiteral("examples");
36QString ConfigStrings::EXAMPLESINSTALLPATH = QStringLiteral("examplesinstallpath");
37QString ConfigStrings::EXCLUDEDIRS = QStringLiteral("excludedirs");
38QString ConfigStrings::EXCLUDEFILES = QStringLiteral("excludefiles");
39QString ConfigStrings::EXTRAIMAGES = QStringLiteral("extraimages");
40QString ConfigStrings::FALSEHOODS = QStringLiteral("falsehoods");
41QString ConfigStrings::FORMATTING = QStringLiteral("formatting");
42QString ConfigStrings::HEADERDIRS = QStringLiteral("headerdirs");
43QString ConfigStrings::HEADERS = QStringLiteral("headers");
44QString ConfigStrings::HEADERSCRIPTS = QStringLiteral("headerscripts");
45QString ConfigStrings::HEADERSTYLES = QStringLiteral("headerstyles");
46QString ConfigStrings::HOMEPAGE = QStringLiteral("homepage");
47QString ConfigStrings::HOMETITLE = QStringLiteral("hometitle");
48QString ConfigStrings::IGNOREDIRECTIVES = QStringLiteral("ignoredirectives");
49QString ConfigStrings::IGNORESINCE = QStringLiteral("ignoresince");
50QString ConfigStrings::IGNORETOKENS = QStringLiteral("ignoretokens");
51QString ConfigStrings::IGNOREWORDS = QStringLiteral("ignorewords");
52QString ConfigStrings::IMAGEDIRS = QStringLiteral("imagedirs");
53QString ConfigStrings::IMAGESOUTPUTDIR = QStringLiteral("imagesoutputdir");
54QString ConfigStrings::INCLUDEPATHS = QStringLiteral("includepaths");
55QString ConfigStrings::INCLUDEPRIVATE = QStringLiteral("includeprivate");
56QString ConfigStrings::INTERNALFILEPATTERNS = QStringLiteral("internalfilepatterns");
57QString ConfigStrings::INCLUSIVE = QStringLiteral("inclusive");
58QString ConfigStrings::INDEXES = QStringLiteral("indexes");
59QString ConfigStrings::LANDINGPAGE = QStringLiteral("landingpage");
60QString ConfigStrings::LANDINGTITLE = QStringLiteral("landingtitle");
61QString ConfigStrings::LANGUAGE = QStringLiteral("language");
62QString ConfigStrings::LOCATIONINFO = QStringLiteral("locationinfo");
63QString ConfigStrings::LOGPROGRESS = QStringLiteral("logprogress");
64QString ConfigStrings::LOGWARNINGS = QStringLiteral("logwarnings");
65QString ConfigStrings::LOGWARNINGSDISABLECLIARGS = QStringLiteral("logwarnings.disablecliargs");
66QString ConfigStrings::MACRO = QStringLiteral("macro");
67QString ConfigStrings::MANIFESTMETA = QStringLiteral("manifestmeta");
68QString ConfigStrings::MODULEHEADER = QStringLiteral("moduleheader");
69QString ConfigStrings::NATURALLANGUAGE = QStringLiteral("naturallanguage");
70QString ConfigStrings::NAVIGATION = QStringLiteral("navigation");
71QString ConfigStrings::NOLINKERRORS = QStringLiteral("nolinkerrors");
72QString ConfigStrings::OUTPUTDIR = QStringLiteral("outputdir");
73QString ConfigStrings::OUTPUTFORMATS = QStringLiteral("outputformats");
74QString ConfigStrings::OUTPUTPREFIXES = QStringLiteral("outputprefixes");
75QString ConfigStrings::OUTPUTSUFFIXES = QStringLiteral("outputsuffixes");
76QString ConfigStrings::PRELIMINARY = QStringLiteral("preliminary");
77QString ConfigStrings::PRODUCTNAME = QStringLiteral("productname");
78QString ConfigStrings::PROJECT = QStringLiteral("project");
79QString ConfigStrings::PROJECTROOT = QStringLiteral("projectroot");
81 QStringLiteral("redirectdocumentationtodevnull");
83 QStringLiteral("reportmissingalttextforimages");
84QString ConfigStrings::QHP = QStringLiteral("qhp");
85QString ConfigStrings::QUOTINGINFORMATION = QStringLiteral("quotinginformation");
86QString ConfigStrings::ROOTDIR = QStringLiteral("rootdir");
87QString ConfigStrings::SCRIPTS = QStringLiteral("scripts");
88QString ConfigStrings::SHOWAUTOGENERATEDDOCS = QStringLiteral("showautogenerateddocs");
89QString ConfigStrings::SHOWINTERNAL = QStringLiteral("showinternal");
90QString ConfigStrings::SINGLEEXEC = QStringLiteral("singleexec");
91QString ConfigStrings::SOURCEDIRS = QStringLiteral("sourcedirs");
92QString ConfigStrings::SOURCEENCODING = QStringLiteral("sourceencoding");
93QString ConfigStrings::SOURCES = QStringLiteral("sources");
94QString ConfigStrings::SPURIOUS = QStringLiteral("spurious");
95QString ConfigStrings::STYLESHEETS = QStringLiteral("stylesheets");
96QString ConfigStrings::SYNTAXHIGHLIGHTING = QStringLiteral("syntaxhighlighting");
97QString ConfigStrings::TABSIZE = QStringLiteral("tabsize");
98QString ConfigStrings::TAGFILE = QStringLiteral("tagfile");
99QString ConfigStrings::TIMESTAMPS = QStringLiteral("timestamps");
100QString ConfigStrings::TOCTITLES = QStringLiteral("toctitles");
101QString ConfigStrings::TRADEMARKSPAGE = QStringLiteral("trademarkspage");
102QString ConfigStrings::URL = QStringLiteral("url");
103QString ConfigStrings::USEALTTEXTASTITLE = QStringLiteral("usealttextastitle");
104QString ConfigStrings::VERSION = QStringLiteral("version");
105QString ConfigStrings::VERSIONSYM = QStringLiteral("versionsym");
106QString ConfigStrings::FILEEXTENSIONS = QStringLiteral("fileextensions");
107QString ConfigStrings::IMAGEEXTENSIONS = QStringLiteral("imageextensions");
108QString ConfigStrings::QMLTYPESPAGE = QStringLiteral("qmltypespage");
109QString ConfigStrings::QMLTYPESTITLE = QStringLiteral("qmltypestitle");
110QString ConfigStrings::WARNABOUTMISSINGIMAGES = QStringLiteral("warnaboutmissingimages");
111QString ConfigStrings::WARNABOUTMISSINGPROJECTFILES = QStringLiteral("warnaboutmissingprojectfiles");
112QString ConfigStrings::WARNINGLIMIT = QStringLiteral("warninglimit");
113
114/*!
115 An entry in a stack, where each entry is a list
116 of string values.
117 */
119{
120public:
121 void open();
122 void close();
123
126};
128
129/*!
130 Start accumulating values in a list by appending an empty
131 string to the list.
132 */
134{
135 next.append(QString());
136}
137
138/*!
139 Stop accumulating values and append the list of accumulated
140 values to the complete list of accumulated values.
141
142 */
144{
145 accum += next;
146 next.clear();
147}
148
149/*!
150 \class MetaStack
151
152 This class maintains a stack of values of config file variables.
153*/
155{
156public:
158
159 void process(QChar ch, const Location &location);
161};
162
163/*!
164 The default constructor pushes a new stack entry and
165 opens it.
166 */
168{
169 push(MetaStackEntry());
170 top().open();
171}
172
173/*!
174 Processes the character \a ch using the \a location.
175 It really just builds up a name by appending \a ch to
176 it.
177 */
178void MetaStack::process(QChar ch, const Location &location)
179{
180 if (ch == QLatin1Char('{')) {
181 push(MetaStackEntry());
182 top().open();
183 } else if (ch == QLatin1Char('}')) {
184 if (size() == 1)
185 location.fatal(QStringLiteral("Unexpected '}'"));
186
187 top().close();
188 const QStringList suffixes = pop().accum;
189 const QStringList prefixes = top().next;
190
191 top().next.clear();
192 for (const auto &prefix : prefixes) {
193 for (const auto &suffix : suffixes)
194 top().next << prefix + suffix;
195 }
196 } else if (ch == QLatin1Char(',') && size() > 1) {
197 top().close();
198 top().open();
199 } else {
200 for (QString &topNext : top().next)
201 topNext += ch;
202 }
203}
204
205/*!
206 Returns the accumulated string values.
207 */
209{
210 if (size() > 1)
211 location.fatal(QStringLiteral("Missing '}'"));
212
213 top().close();
214 return top().accum;
215}
216
217const QString Config::dot = QLatin1String(".");
218bool Config::m_debug = false;
219bool Config::m_atomsDump = false;
220bool Config::generateExamples = true;
224QMap<QString, QString> Config::m_extractedDirs;
225QStack<QString> Config::m_workingDirs;
226QMap<QString, QStringList> Config::m_includeFilesMap;
227
228/*!
229 \class ConfigVar
230 \brief contains all the information for a single config variable in a
231 .qdocconf file.
232*/
233
234/*!
235 Returns this configuration variable as a string.
236
237 If the variable is not defined, returns \a defaultString.
238
239 \note By default, \a defaultString is a null string.
240 This allows determining whether a configuration variable is
241 undefined (returns a null string) or defined as empty
242 (returns a non-null, empty string).
243*/
244QString ConfigVar::asString(const QString defaultString) const
245{
246 if (m_name.isEmpty())
247 return defaultString;
248
249 QString result(""); // an empty but non-null string
250 for (const auto &value : std::as_const(m_values)) {
251 if (!result.isEmpty() && !result.endsWith(QChar('\n')))
252 result.append(QChar(' '));
253 result.append(value.m_value);
254 }
255 return result;
256}
257
258/*!
259 Returns this config variable as a string list.
260*/
262{
263 QStringList result;
264 for (const auto &value : std::as_const(m_values))
265 result << value.m_value;
266 return result;
267}
268
269/*!
270 Returns this config variable as a string set.
271*/
273{
274 const auto &stringList = asStringList();
275 return QSet<QString>(stringList.cbegin(), stringList.cend());
276}
277
278/*!
279 Returns this config variable as a boolean.
280*/
281bool ConfigVar::asBool() const
282{
283 return QVariant(asString()).toBool();
284}
285
286/*!
287 Returns this configuration variable as an integer; iterates
288 through the string list, interpreting each
289 string in the list as an integer and adding it to a total sum.
290
291 Returns 0 if this variable is defined as empty, and
292 -1 if it's is not defined.
293 */
294int ConfigVar::asInt() const
295{
296 const QStringList strs = asStringList();
297 if (strs.isEmpty())
298 return -1;
299
300 int sum = 0;
301 for (const auto &str : strs)
302 sum += str.toInt();
303 return sum;
304}
305
306/*!
307 Appends values to this ConfigVar, and adjusts the ExpandVar
308 parameters so that they continue to refer to the correct values.
309*/
310void ConfigVar::append(const ConfigVar &other)
311{
312 m_expandVars << other.m_expandVars;
313 QList<ExpandVar>::Iterator it = m_expandVars.end();
314 it -= other.m_expandVars.size();
315 std::for_each(it, m_expandVars.end(), [this](ExpandVar &v) {
316 v.m_valueIndex += m_values.size();
317 });
318 m_values << other.m_values;
319 m_location = other.m_location;
320}
321
322/*!
323 \class Config
324 \brief The Config class contains the configuration variables
325 for controlling how qdoc produces documentation.
326
327 Its load() function reads, parses, and processes a qdocconf file.
328 */
329
330/*!
331 \enum Config::PathFlags
332
333 Flags used for retrieving canonicalized paths from Config.
334
335 \value Validate
336 Issue a warning for paths that do not exist and
337 remove them from the returned list.
338
339 \value IncludePaths
340 Assume the variable contains include paths with
341 prefixes such as \c{-I} that are to be removed
342 before canonicalizing and then re-inserted.
343
344 \omitvalue None
345
346 \sa getCanonicalPathList()
347*/
348
349/*!
350 Initializes the Config with \a programName and sets all
351 internal state variables to either default values or to ones
352 defined in command line arguments \a args.
353 */
354void Config::init(const QString &programName, const QStringList &args)
355{
356 m_prog = programName;
357 processCommandLineOptions(args);
358 reset();
359}
360
362{
363 clear();
364}
365
366/*!
367 Clears the location and internal maps for config variables.
368 */
370{
371 m_location = Location();
372 m_configVars.clear();
373 m_includeFilesMap.clear();
374 m_excludedPaths.reset();
375 m_sourceLink.reset();
376 m_internalFilePatterns.reset();
377}
378
379/*!
380 Resets the Config instance - used by load()
381 */
383{
384 clear();
385
386 // Default values
387 setStringList(CONFIG_CODEINDENT, QStringList("0"));
388 setStringList(CONFIG_FALSEHOODS, QStringList("0"));
389 setStringList(CONFIG_HEADERS + dot + CONFIG_FILEEXTENSIONS, QStringList("*.ch *.h *.h++ *.hh *.hpp *.hxx"));
390 setStringList(CONFIG_SOURCES + dot + CONFIG_FILEEXTENSIONS, QStringList("*.c++ *.cc *.cpp *.cxx *.mm *.qml *.qdoc"));
391 setStringList(CONFIG_LANGUAGE, QStringList("Cpp")); // i.e. C++
392 setStringList(CONFIG_OUTPUTFORMATS, QStringList("HTML"));
393 setStringList(CONFIG_TABSIZE, QStringList("8"));
394 setStringList(CONFIG_LOCATIONINFO, QStringList("true"));
395 setStringList(CONFIG_INCLUDEPRIVATE, QStringList("false"));
396 setStringList(CONFIG_WARNABOUTMISSINGIMAGES, QStringList("true"));
397 setStringList(CONFIG_WARNABOUTMISSINGPROJECTFILES, QStringList("true"));
398 setStringList(CONFIG_SHOWAUTOGENERATEDDOCS, QStringList("true"));
399 setStringList(CONFIG_PRELIMINARY, QStringList("Preliminary"));
400 setStringList(CONFIG_PRELIMINARY + dot + CONFIG_DESCRIPTION,
401 QStringList("This \1 is under development and is subject to change."));
402
403 // Publish options from the command line as config variables
404 const auto setListFlag = [this](const QString &key, bool test) {
405 setStringList(key, QStringList(test ? QStringLiteral("true") : QStringLiteral("false")));
406 };
407#define SET(opt, test) setListFlag(opt, m_parser.isSet(m_parser.test))
408 SET(CONFIG_SYNTAXHIGHLIGHTING, highlightingOption);
409 SET(CONFIG_SHOWINTERNAL, showInternalOption);
410 SET(CONFIG_SINGLEEXEC, singleExecOption);
411 SET(CONFIG_REDIRECTDOCUMENTATIONTODEVNULL, redirectDocumentationToDevNullOption);
412 SET(CONFIG_AUTOLINKERRORS, autoLinkErrorsOption);
413#undef SET
414 m_showInternal = m_configVars.value(CONFIG_SHOWINTERNAL).asBool();
415 setListFlag(CONFIG_NOLINKERRORS,
416 m_parser.isSet(m_parser.noLinkErrorsOption)
417 || qEnvironmentVariableIsSet("QDOC_NOLINKERRORS"));
418
419 // CONFIG_DEFINES and CONFIG_INCLUDEPATHS are set in load()
420}
421
422/*!
423 Loads and parses the qdoc configuration file \a fileName.
424 If a previous project was loaded, this function first resets the
425 Config instance. Then it calls the other load() function, which
426 does the loading, parsing, and processing of the configuration file.
427 */
428void Config::load(const QString &fileName)
429{
430 // Reset if a previous project was loaded
431 if (m_configVars.contains(CONFIG_PROJECT))
432 reset();
433
434 load(Location(), fileName);
435 if (m_location.isEmpty())
436 m_location = Location(fileName);
437 else
438 m_location.setEtc(true);
439
440 // Resolve variables that are interpreted as paths
441 QString varName{CONFIG_URL + dot + CONFIG_SOURCES + dot + CONFIG_ROOTDIR};
442 setStringList(varName, getCanonicalPathList(varName, Validate));
443
444 expandVariables();
445
446 // Add defines and includepaths from command line to their
447 // respective configuration variables. Values set here are
448 // always added to what's defined in configuration file.
449 insertStringList(CONFIG_DEFINES, m_defines);
450 insertStringList(CONFIG_INCLUDEPATHS, m_includePaths);
451
452 // Prefetch values that are used internally
453 m_exampleFiles = getCanonicalPathList(CONFIG_EXAMPLES);
454 m_exampleDirs = getCanonicalPathList(CONFIG_EXAMPLEDIRS);
455 m_reportMissingAltTextForImages =
456 m_configVars.value(CONFIG_REPORTMISSINGALTTEXTFORIMAGES).asBool();
457
458 if (!m_parser.isSet(m_parser.showInternalOption) && !qEnvironmentVariableIsSet("QDOC_SHOW_INTERNAL"))
459 m_showInternal = m_configVars.value(CONFIG_SHOWINTERNAL).asBool();
460}
461
462/*!
463 Expands other config variables referred to in all stored ConfigVars.
464*/
465void Config::expandVariables()
466{
467 for (auto &configVar : m_configVars) {
468 for (auto it = configVar.m_expandVars.crbegin(); it != configVar.m_expandVars.crend(); ++it) {
469 Q_ASSERT(it->m_valueIndex < configVar.m_values.size());
470 const QString &key = it->m_var;
471 const auto &refVar = m_configVars.value(key);
472 if (refVar.m_name.isEmpty()) {
473 configVar.m_location.fatal(
474 QStringLiteral("Environment or configuration variable '%1' undefined")
475 .arg(it->m_var));
476 } else if (!refVar.m_expandVars.empty()) {
477 configVar.m_location.fatal(
478 QStringLiteral("Nested variable expansion not allowed"),
479 QStringLiteral("When expanding '%1' at %2:%3")
480 .arg(refVar.m_name, refVar.m_location.filePath(),
481 QString::number(refVar.m_location.lineNo())));
482 }
483 QString expanded;
484 if (it->m_delim.isNull())
485 expanded = m_configVars.value(key).asStringList().join(QString());
486 else
487 expanded = m_configVars.value(key).asStringList().join(it->m_delim);
488 configVar.m_values[it->m_valueIndex].m_value.insert(it->m_index, expanded);
489 }
490 configVar.m_expandVars.clear();
491 }
492}
493
494/*!
495 Sets the \a values of a configuration variable \a var from a string list.
496 */
497void Config::setStringList(const QString &var, const QStringList &values)
498{
499 m_configVars.insert(var, ConfigVar(var, values, QDir::currentPath()));
500}
501
502/*!
503 Adds the \a values from a string list to the configuration variable \a var.
504 Existing value(s) are kept.
505*/
506void Config::insertStringList(const QString &var, const QStringList &values)
507{
508 m_configVars[var].append(ConfigVar(var, values, QDir::currentPath()));
509}
510
511/*!
512 Process and store variables from the command line.
513 */
514void Config::processCommandLineOptions(const QStringList &args)
515{
516 m_parser.process(args);
517
518 m_defines = m_parser.values(m_parser.defineOption);
519 m_dependModules = m_parser.values(m_parser.dependsOption);
520 setIndexDirs();
521 setIncludePaths();
522
523 generateExamples = !m_parser.isSet(m_parser.noExamplesOption);
524 if (m_parser.isSet(m_parser.installDirOption))
525 installDir = m_parser.value(m_parser.installDirOption);
526 if (m_parser.isSet(m_parser.outputDirOption))
527 overrideOutputDir = QDir(m_parser.value(m_parser.outputDirOption)).absolutePath();
528
529 const auto outputFormats = m_parser.values(m_parser.outputFormatOption);
530 for (const auto &format : outputFormats)
531 overrideOutputFormats.insert(format);
532 m_debug = m_parser.isSet(m_parser.debugOption) || qEnvironmentVariableIsSet("QDOC_DEBUG");
533 m_atomsDump = m_parser.isSet(m_parser.atomsDumpOption);
534 m_showInternal = m_parser.isSet(m_parser.showInternalOption)
535 || qEnvironmentVariableIsSet("QDOC_SHOW_INTERNAL");
536
537 if (m_parser.isSet(m_parser.prepareOption))
538 m_qdocPass = Prepare;
539 if (m_parser.isSet(m_parser.generateOption))
540 m_qdocPass = Generate;
541 if (m_debug || m_parser.isSet(m_parser.logProgressOption))
542 setStringList(CONFIG_LOGPROGRESS, QStringList("true"));
543 if (m_parser.isSet(m_parser.timestampsOption))
544 setStringList(CONFIG_TIMESTAMPS, QStringList("true"));
545 if (m_parser.isSet(m_parser.useDocBookExtensions))
546 setStringList(CONFIG_DOCBOOKEXTENSIONS, QStringList("true"));
547}
548
549void Config::setIncludePaths()
550{
551 QDir currentDir = QDir::current();
552 const auto addIncludePaths = [this, currentDir](const char *flag, const QStringList &paths) {
553 for (const auto &path : paths)
554 m_includePaths << currentDir.absoluteFilePath(path).insert(0, flag);
555 };
556
557 addIncludePaths("-I", m_parser.values(m_parser.includePathOption));
558#ifdef QDOC_PASS_ISYSTEM
559 addIncludePaths("-isystem", m_parser.values(m_parser.includePathSystemOption));
560#endif
561 addIncludePaths("-F", m_parser.values(m_parser.frameworkOption));
562}
563
564/*!
565 Stores paths from -indexdir command line option(s).
566 */
567void Config::setIndexDirs()
568{
569 m_indexDirs = m_parser.values(m_parser.indexDirOption);
570 auto it = std::remove_if(m_indexDirs.begin(), m_indexDirs.end(),
571 [](const QString &s) { return !QFile::exists(s); });
572
573 std::for_each(it, m_indexDirs.end(), [](const QString &s) {
574 qCWarning(lcQdoc) << "Cannot find index directory: " << s;
575 });
576 m_indexDirs.erase(it, m_indexDirs.end());
577}
578
579/*!
580 Function to return the correct outputdir for the output \a format.
581 If \a format is not specified, defaults to 'HTML'.
582 outputdir can be set using the qdocconf or the command-line
583 variable -outputdir.
584 */
585QString Config::getOutputDir(const QString &format) const
586{
587 QString t;
588 if (overrideOutputDir.isNull())
589 t = m_configVars.value(CONFIG_OUTPUTDIR).asString();
590 else
591 t = overrideOutputDir;
592 if (m_configVars.value(CONFIG_SINGLEEXEC).asBool()) {
593 QString project = m_configVars.value(CONFIG_PROJECT).asString();
594 t += QLatin1Char('/') + project.toLower();
595 }
596 if (m_configVars.value(format + Config::dot + "nosubdirs").asBool()) {
597 QString singleOutputSubdir = m_configVars.value(format + Config::dot + "outputsubdir").asString();
598 if (singleOutputSubdir.isEmpty())
599 singleOutputSubdir = "html";
600 t += QLatin1Char('/') + singleOutputSubdir;
601 }
602 return QDir::cleanPath(t);
603}
604
605/*!
606 Function to return the correct outputformats.
607 outputformats can be set using the qdocconf or the command-line
608 variable -outputformat.
609 */
611{
612 if (overrideOutputFormats.isEmpty())
613 return m_configVars.value(CONFIG_OUTPUTFORMATS).asStringSet();
614 else
615 return overrideOutputFormats;
616}
617
618// TODO: [late-canonicalization][pod-configuration]
619// The canonicalization for paths is done at the time where they are
620// required, and done each time they are requested.
621// Instead, config should be parsed to an intermediate format that is
622// a POD type that already contains canonicalized representations for
623// each element.
624// Those representations should provide specific guarantees about
625// their format and be representable at the API boundaries.
626//
627// This would ensure that the correct canonicalization is always
628// applied, is applied only once and that dependent sub-logics can be
629// written in a way that doesn't require branching or futher
630// canonicalization.
631
632/*!
633 Returns a path list where all paths from the config variable \a var
634 are canonicalized. If \a flags contains \c Validate, outputs a warning
635 for invalid paths. The \c IncludePaths flag is used as a hint to strip
636 away potential prefixes found in include paths before attempting to
637 canonicalize.
638 */
639QStringList Config::getCanonicalPathList(const QString &var, PathFlags flags) const
640{
641 QStringList result;
642 const auto &configVar = m_configVars.value(var);
643
644 for (const auto &value : configVar.m_values) {
645 const QString &currentPath = value.m_path;
646 QString rawValue = value.m_value.simplified();
647 QString prefix;
648
649 if (flags & IncludePaths) {
650 const QStringList prefixes = QStringList()
651 << QLatin1String("-I")
652 << QLatin1String("-F")
653 << QLatin1String("-isystem");
654 const auto end = std::end(prefixes);
655 const auto it =
656 std::find_if(std::begin(prefixes), end,
657 [&rawValue](const QString &p) {
658 return rawValue.startsWith(p);
659 });
660 if (it != end) {
661 prefix = *it;
662 rawValue.remove(0, it->size());
663 if (rawValue.isEmpty())
664 continue;
665 } else {
666 prefix = prefixes[0]; // -I as default
667 }
668 }
669
670 QDir dir(rawValue.trimmed());
671 const QString path = dir.path();
672
673 if (dir.isRelative())
674 dir.setPath(currentPath + QLatin1Char('/') + path);
675 if ((flags & Validate) && !QFileInfo::exists(dir.path()))
676 configVar.m_location.warning(QStringLiteral("Cannot find file or directory: %1").arg(path));
677 else {
678 const QString canonicalPath = dir.canonicalPath();
679 if (!canonicalPath.isEmpty())
680 result.append(prefix + canonicalPath);
681 else if (path.contains(QLatin1Char('*')) || path.contains(QLatin1Char('?')))
682 result.append(path);
683 else
684 qCDebug(lcQdoc) <<
685 qUtf8Printable(QStringLiteral("%1: Ignored nonexistent path \'%2\'")
686 .arg(configVar.m_location.toString(), rawValue));
687 }
688 }
689 return result;
690}
691
692/*!
693 Calls getRegExpList() with the control variable \a var and
694 iterates through the resulting list of regular expressions,
695 concatenating them with extra characters to form a single
696 QRegularExpression, which is then returned.
697
698 \sa getRegExpList()
699 */
700QRegularExpression Config::getRegExp(const QString &var) const
701{
702 QString pattern;
703 const auto subRegExps = getRegExpList(var);
704
705 for (const auto &regExp : subRegExps) {
706 if (!regExp.isValid())
707 return regExp;
708 if (!pattern.isEmpty())
709 pattern += QLatin1Char('|');
710 pattern += QLatin1String("(?:") + regExp.pattern() + QLatin1Char(')');
711 }
712 if (pattern.isEmpty())
713 pattern = QLatin1String("$x"); // cannot match
714 return QRegularExpression(pattern);
715}
716
717/*!
718 Looks up the configuration variable \a var in the string list
719 map, converts the string list to a list of regular expressions,
720 and returns it.
721 */
722QList<QRegularExpression> Config::getRegExpList(const QString &var) const
723{
724 const QStringList strs = m_configVars.value(var).asStringList();
725 QList<QRegularExpression> regExps;
726 for (const auto &str : strs)
727 regExps += QRegularExpression(str);
728 return regExps;
729}
730
731/*!
732 This function is slower than it could be. What it does is
733 find all the keys that begin with \a var + dot and return
734 the matching keys in a set, stripped of the matching prefix
735 and dot.
736 */
737QSet<QString> Config::subVars(const QString &var) const
738{
739 QSet<QString> result;
740 QString varDot = var + QLatin1Char('.');
741 for (auto it = m_configVars.constBegin(); it != m_configVars.constEnd(); ++it) {
742 if (it.key().startsWith(varDot)) {
743 QString subVar = it.key().mid(varDot.size());
744 int dot = subVar.indexOf(QLatin1Char('.'));
745 if (dot != -1)
746 subVar.truncate(dot);
747 result.insert(subVar);
748 }
749 }
750 return result;
751}
752
753/*!
754 Searches for a path to \a fileName in 'sources', 'sourcedirs', and
755 'exampledirs' config variables and returns a full path to the first
756 match found. If the file is not found, returns an empty string.
757 */
758QString Config::getIncludeFilePath(const QString &fileName) const
759{
760 QString ext = QFileInfo(fileName).suffix();
761
762 if (!m_includeFilesMap.contains(ext)) {
763 QStringList result = getCanonicalPathList(CONFIG_SOURCES);
764 result.erase(std::remove_if(result.begin(), result.end(),
765 [&](const QString &s) { return !s.endsWith(ext); }),
766 result.end());
767 const QStringList dirs =
768 getCanonicalPathList(CONFIG_SOURCEDIRS) +
769 getCanonicalPathList(CONFIG_EXAMPLEDIRS);
770
771 for (const auto &dir : dirs)
772 result += getFilesHere(dir, "*." + ext, location());
773 result.removeDuplicates();
774 m_includeFilesMap.insert(ext, result);
775 }
776 const QStringList &paths = (*m_includeFilesMap.find(ext));
777 QString match = fileName;
778 if (!match.startsWith('/'))
779 match.prepend('/');
780 for (const auto &path : paths) {
781 if (path.endsWith(match))
782 return path;
783 }
784 return QString();
785}
786
787/*!
788 Builds and returns a list of file pathnames for the file
789 type specified by \a filesVar (e.g. "headers" or "sources").
790 The files are found in the directories specified by
791 \a dirsVar, and they are filtered by \a defaultNameFilter
792 if a better filter can't be constructed from \a filesVar.
793 The directories in \a excludedDirs are avoided. The files
794 in \a excludedFiles are not included in the return list.
795 */
796QStringList Config::getAllFiles(const QString &filesVar, const QString &dirsVar,
797 const QSet<QString> &excludedDirs,
798 const QSet<QString> &excludedFiles)
799{
800 QStringList result = getCanonicalPathList(filesVar, Validate);
801 const QStringList dirs = getCanonicalPathList(dirsVar, Validate);
802
803 const QString nameFilter = m_configVars.value(filesVar + dot + CONFIG_FILEEXTENSIONS).asString();
804
805 for (const auto &dir : dirs)
806 result += getFilesHere(dir, nameFilter, location(), excludedDirs, excludedFiles);
807 return result;
808}
809
810QStringList Config::getExampleQdocFiles(const QSet<QString> &excludedDirs,
811 const QSet<QString> &excludedFiles)
812{
813 QStringList result;
814 const QStringList dirs = getCanonicalPathList("exampledirs");
815 const QString nameFilter = " *.qdoc";
816
817 for (const auto &dir : dirs)
818 result += getFilesHere(dir, nameFilter, location(), excludedDirs, excludedFiles);
819 return result;
820}
821
822QStringList Config::getExampleImageFiles(const QSet<QString> &excludedDirs,
823 const QSet<QString> &excludedFiles)
824{
825 QStringList result;
826 const QStringList dirs = getCanonicalPathList("exampledirs");
827 const QString nameFilter = m_configVars.value(CONFIG_EXAMPLES + dot + CONFIG_IMAGEEXTENSIONS).asString();
828
829 for (const auto &dir : dirs)
830 result += getFilesHere(dir, nameFilter, location(), excludedDirs, excludedFiles);
831 return result;
832}
833
834// TODO: [misplaced-logic][examples][pod-configuration]
835// The definition of how an example is structured and how to find its
836// components should not be part of Config or, for that matter,
837// CppCodeParser, which is the actual caller of this method.
838// Move this method to a more appropriate place as soon as a suitable
839// place is available for it.
840
841/*!
842 Returns the path to the project file for \a examplePath, or an empty string
843 if no project file was found.
844 */
845QString Config::getExampleProjectFile(const QString &examplePath)
846{
847 QFileInfo fileInfo(examplePath);
848 QStringList validNames;
849 validNames << QLatin1String("CMakeLists.txt")
850 << fileInfo.fileName() + QLatin1String(".pro")
851 << fileInfo.fileName() + QLatin1String(".qmlproject")
852 << fileInfo.fileName() + QLatin1String(".pyproject")
853 << QLatin1String("qbuild.pro"); // legacy
854
855 QString projectFile;
856
857 for (const auto &name : std::as_const(validNames)) {
858 projectFile = Config::findFile(Location(), m_exampleFiles, m_exampleDirs,
859 examplePath + QLatin1Char('/') + name);
860 if (!projectFile.isEmpty())
861 return projectFile;
862 }
863
864 return projectFile;
865}
866
867// TODO: [pod-configuration]
868// Remove findFile completely from the configuration.
869// External usages of findFile were already removed but a last caller
870// of this method exists internally to Config in
871// `getExampleProjectFile`.
872// That method has to be removed at some point and this method should
873// go with it.
874// Do notice that FileResolver is the replacement for findFile but it
875// is designed, for now, with a scope that does only care about the
876// usages of findFile that are outside the Config class.
877// More specifically, it was designed to replace only the uses of
878// findFile that deal with user provided queries or queries related to
879// that.
880// The logic that is used internally in Config is the same, but has a
881// different conceptual meaning.
882// When findFile is permanently removed, it must be considered whether
883// FileResolver itself should be used for the same logic or not.
884
885/*!
886 \a fileName is the path of the file to find.
887
888 \a files and \a dirs are the lists where we must find the
889 components of \a fileName.
890
891 \a location is used for obtaining the file and line numbers
892 for report qdoc errors.
893 */
894QString Config::findFile(const Location &location, const QStringList &files,
895 const QStringList &dirs, const QString &fileName,
896 QString *userFriendlyFilePath)
897{
898 if (fileName.isEmpty() || fileName.startsWith(QLatin1Char('/'))) {
899 if (userFriendlyFilePath)
900 *userFriendlyFilePath = fileName;
901 return fileName;
902 }
903
904 QFileInfo fileInfo;
905 QStringList components = fileName.split(QLatin1Char('?'));
906 QString firstComponent = components.first();
907
908 for (const auto &file : files) {
909 if (file == firstComponent || file.endsWith(QLatin1Char('/') + firstComponent)) {
910 fileInfo.setFile(file);
911 if (!fileInfo.exists())
912 location.fatal(QStringLiteral("File '%1' does not exist").arg(file));
913 break;
914 }
915 }
916
917 if (fileInfo.fileName().isEmpty()) {
918 for (const auto &dir : dirs) {
919 fileInfo.setFile(QDir(dir), firstComponent);
920 if (fileInfo.exists())
921 break;
922 }
923 }
924
925 if (userFriendlyFilePath)
926 userFriendlyFilePath->clear();
927 if (!fileInfo.exists())
928 return QString();
929
930 // <<REMARK: This is actually dead code. It is unclear what it tries
931 // to do and why but its usage is unnecessary in the current
932 // codebase.
933 // Indeed, the whole concept of the "userFriendlyFilePath" is
934 // removed for file searching.
935 // It will be removed directly with the whole of findFile, but it
936 // should not be considered anymore until then.
937 if (userFriendlyFilePath) {
938 for (auto c = components.constBegin();;) {
939 bool isArchive = (c != components.constEnd() - 1);
940 userFriendlyFilePath->append(*c);
941
942 if (isArchive) {
943 QString extracted = m_extractedDirs[fileInfo.filePath()];
944
945 ++c;
946 fileInfo.setFile(QDir(extracted), *c);
947 } else {
948 break;
949 }
950
951 userFriendlyFilePath->append(QLatin1Char('?'));
952 }
953 }
954 // REMARK>>
955
956 return fileInfo.filePath();
957}
958
959// TODO: [pod-configuration]
960// An intermediate representation for the configuration should only
961// contain data that will later be destructured into subsystem that
962// care about specific subsets of the configuration and can carry that
963// information with them, uniquely.
964// Remove copyFile, moving it into whatever will have the unique
965// resposability of knowing how to build an output directory for a
966// QDoc execution.
967// Should copy file being used for not only copying file to the build
968// output directory, split its responsabilities into smaller elements
969// instead of forcing the logic together.
970
971/*!
972 Copies the \a sourceFilePath to the file name constructed by
973 concatenating \a targetDirPath and the file name from the
974 \a userFriendlySourceFilePath. \a location is for identifying
975 the file and line number where a qdoc error occurred. The
976 constructed output file name is returned.
977 */
978QString Config::copyFile(const Location &location, const QString &sourceFilePath,
979 const QString &userFriendlySourceFilePath, const QString &targetDirPath)
980{
981 // TODO: A copying operation should only be performed on files
982 // that we assume to be available. Ensure that this is true at the
983 // API boundary and bubble up the error checking and reporting to
984 // call-site users. Possibly this will be as simple as
985 // ResolvedFile, but could not be done at the time of the introduction of
986 // that type as we first need to encapsulate the logic for
987 // copying files into an appropriate subsystem and have a better
988 // understanding of call-site usages.
989
990 QFile inFile(sourceFilePath);
991 if (!inFile.open(QFile::ReadOnly)) {
992 location.warning(QStringLiteral("Cannot open input file for copy: '%1': %2")
993 .arg(sourceFilePath, inFile.errorString()));
994 return QString();
995 }
996
997 // TODO: [non-canonical-representation]
998 // Similar to other part of QDoc, we do a series of non-intuitive
999 // checks to canonicalize some multi-format parameter into
1000 // something we can use.
1001 // Understand which of those formats are actually in use and
1002 // provide a canonicalized version that can be requested at the
1003 // API boundary to ensure that correct formatting is used.
1004 // If possible, gradually bubble up the canonicalization until a
1005 // single entry-point in the program exists where the
1006 // canonicalization can be processed to avoid complicating
1007 // intermediate steps.
1008 // ADDENDUM 1: At least one usage of this seems to depend on the
1009 // processing done for files coming from
1010 // Generator::copyTemplateFile, which are expressed as absolute
1011 // paths. This seems to be the only usage that is currently
1012 // needed, hence a temporary new implementation is provided that
1013 // only takes this case into account.
1014 // Do notice that we assume that in this case we always want a
1015 // flat structure, that is, we are copying the file as a direct
1016 // child of the target directory.
1017 // Nonetheless, it is possible that this case will not be needed,
1018 // such that it can be removed later on, or that it will be nedeed
1019 // in multiple places such that an higher level interface for it
1020 // should be provided.
1021 // Furthermoe, it might be possible that there is an edge case
1022 // that is now not considered, as it is unknown, that was
1023 // considered before.
1024 // As it is now unclear what kind of paths are used here, what
1025 // format they have, why they are used and why they have some
1026 // specific format, further processing is avoided but a more
1027 // torough overview of what should is needed must be done when
1028 // more information are gathered and this function is extracted
1029 // away from config.
1030
1031 QString outFileName{userFriendlySourceFilePath};
1032 QFileInfo outFileNameInfo{userFriendlySourceFilePath};
1033 if (outFileNameInfo.isAbsolute())
1034 outFileName = outFileNameInfo.fileName();
1035
1036 outFileName = targetDirPath + "/" + outFileName;
1037 QDir targetDir(targetDirPath);
1038 if (!targetDir.exists())
1039 targetDir.mkpath(".");
1040
1041 QFile outFile(outFileName);
1042 if (!outFile.open(QFile::WriteOnly)) {
1043 // TODO: [uncrentralized-warning]
1044 location.warning(QStringLiteral("Cannot open output file for copy: '%1': %2")
1045 .arg(outFileName, outFile.errorString()));
1046 return QString();
1047 }
1048
1049 // TODO: There shouldn't be any particular advantage to copying
1050 // the file by readying its content and writing it compared to
1051 // asking the underlying system to do the copy for us.
1052 // Consider simplifying this part by avoiding doing the manual
1053 // work ourselves.
1054
1055 char buffer[1024];
1056 qsizetype len;
1057 while ((len = inFile.read(buffer, sizeof(buffer))) > 0)
1058 outFile.write(buffer, len);
1059 return outFileName;
1060}
1061
1062/*!
1063 Finds the largest unicode digit in \a value in the range
1064 1..7 and returns it.
1065 */
1066int Config::numParams(const QString &value)
1067{
1068 int max = 0;
1069 for (int i = 0; i != value.size(); ++i) {
1070 uint c = value[i].unicode();
1071 if (c > 0 && c < 8)
1072 max = qMax(max, static_cast<int>(c));
1073 }
1074 return max;
1075}
1076
1077/*!
1078 Returns \c true if \a ch is a letter, number, '_', '.',
1079 '{', '}', or ','.
1080 */
1081bool Config::isMetaKeyChar(QChar ch)
1082{
1083 return ch.isLetterOrNumber() || ch == QLatin1Char('_') || ch == QLatin1Char('.')
1084 || ch == QLatin1Char('{') || ch == QLatin1Char('}') || ch == QLatin1Char(',');
1085}
1086
1087/*!
1088 \a fileName is a master qdocconf file. It contains a list of
1089 qdocconf files and nothing else. Read the list and return it.
1090 */
1091QStringList Config::loadMaster(const QString &fileName)
1092{
1093 Location location;
1094 QFile fin(fileName);
1095 if (!fin.open(QFile::ReadOnly | QFile::Text)) {
1096 if (!Config::installDir.isEmpty()) {
1097 qsizetype prefix = location.filePath().size() - location.fileName().size();
1098 fin.setFileName(Config::installDir + QLatin1Char('/')
1099 + fileName.right(fileName.size() - prefix));
1100 }
1101 if (!fin.open(QFile::ReadOnly | QFile::Text))
1102 location.fatal(QStringLiteral("Cannot open master qdocconf file '%1': %2")
1103 .arg(fileName, fin.errorString()));
1104 }
1105 QTextStream stream(&fin);
1106 QStringList qdocFiles;
1107 QDir configDir(QFileInfo(fileName).canonicalPath());
1108 QString line = stream.readLine();
1109 while (!line.isNull()) {
1110 if (!line.isEmpty())
1111 qdocFiles.append(QFileInfo(configDir, line).filePath());
1112 line = stream.readLine();
1113 }
1114 fin.close();
1115 return qdocFiles;
1116}
1117
1118/*!
1119 Load, parse, and process a qdoc configuration file. This
1120 function is only called by the other load() function, but
1121 this one is recursive, i.e., it calls itself when it sees
1122 an \c{include} statement in the qdoc configuration file.
1123 */
1124void Config::load(Location location, const QString &fileName)
1125{
1126 QFileInfo fileInfo(fileName);
1127 pushWorkingDir(fileInfo.canonicalPath());
1128 static const QRegularExpression keySyntax(QRegularExpression::anchoredPattern(QLatin1String("\\w+(?:\\.\\w+)*")));
1129
1130#define SKIP_CHAR()
1131 do {
1132 location.advance(c);
1133 ++i;
1134 c = text.at(i);
1135 cc = c.unicode();
1136 } while (0)
1137
1138#define SKIP_SPACES()
1139 while (c.isSpace() && cc != '\n')
1140 SKIP_CHAR()
1141
1142#define PUT_CHAR()
1143 word += c;
1144 SKIP_CHAR();
1145
1146 if (location.depth() > 16)
1147 location.fatal(QStringLiteral("Too many nested includes"));
1148
1149 QFile fin(fileInfo.fileName());
1150 if (!fin.open(QFile::ReadOnly | QFile::Text)) {
1151 if (!Config::installDir.isEmpty()) {
1152 qsizetype prefix = location.filePath().size() - location.fileName().size();
1153 fin.setFileName(Config::installDir + QLatin1Char('/')
1154 + fileName.right(fileName.size() - prefix));
1155 }
1156 if (!fin.open(QFile::ReadOnly | QFile::Text))
1157 location.fatal(
1158 QStringLiteral("Cannot open file '%1': %2").arg(fileName, fin.errorString()));
1159 }
1160
1161 QTextStream stream(&fin);
1162 QString text = stream.readAll();
1163 text += QLatin1String("\n\n");
1164 text += QLatin1Char('\0');
1165 fin.close();
1166
1167 location.push(fileName);
1168 location.start();
1169
1170 int i = 0;
1171 QChar c = text.at(0);
1172 uint cc = c.unicode();
1173 while (i < text.size()) {
1174 if (cc == 0) {
1175 ++i;
1176 } else if (c.isSpace()) {
1177 SKIP_CHAR();
1178 } else if (cc == '#') {
1179 do {
1180 SKIP_CHAR();
1181 } while (cc != '\n');
1182 } else if (isMetaKeyChar(c)) {
1183 Location keyLoc = location;
1184 bool plus = false;
1185 QStringList rhsValues;
1186 QList<ExpandVar> expandVars;
1187 QString word;
1188 bool inQuote = false;
1189 bool needsExpansion = false;
1190
1191 MetaStack stack;
1192 do {
1193 stack.process(c, location);
1194 SKIP_CHAR();
1195 } while (isMetaKeyChar(c));
1196
1197 const QStringList keys = stack.getExpanded(location);
1198 SKIP_SPACES();
1199
1200 if (keys.size() == 1 && keys.first() == QLatin1String("include")) {
1201 QString includeFile;
1202
1203 if (cc != '(')
1204 location.fatal(QStringLiteral("Bad include syntax"));
1205 SKIP_CHAR();
1206 SKIP_SPACES();
1207
1208 while (!c.isSpace() && cc != '#' && cc != ')') {
1209
1210 if (cc == '$') {
1211 QString var;
1212 SKIP_CHAR();
1213 while (c.isLetterOrNumber() || cc == '_') {
1214 var += c;
1215 SKIP_CHAR();
1216 }
1217 if (!var.isEmpty()) {
1218 const QByteArray val = qgetenv(var.toLatin1().data());
1219 if (val.isNull()) {
1220 location.fatal(QStringLiteral("Environment variable '%1' undefined")
1221 .arg(var));
1222 } else {
1223 includeFile += QString::fromLatin1(val);
1224 }
1225 }
1226 } else {
1227 includeFile += c;
1228 SKIP_CHAR();
1229 }
1230 }
1231 SKIP_SPACES();
1232 if (cc != ')')
1233 location.fatal(QStringLiteral("Bad include syntax"));
1234 SKIP_CHAR();
1235 SKIP_SPACES();
1236 if (cc != '#' && cc != '\n')
1237 location.fatal(QStringLiteral("Trailing garbage"));
1238
1239 /*
1240 Here is the recursive call.
1241 */
1242 load(location, QFileInfo(QDir(m_workingDirs.top()), includeFile).filePath());
1243 } else {
1244 /*
1245 It wasn't an include statement, so it's something else.
1246 We must see either '=' or '+=' next. If not, fatal error.
1247 */
1248 if (cc == '+') {
1249 plus = true;
1250 SKIP_CHAR();
1251 }
1252 if (cc != '=')
1253 location.fatal(QStringLiteral("Expected '=' or '+=' after key"));
1254 SKIP_CHAR();
1255 SKIP_SPACES();
1256
1257 for (;;) {
1258 if (cc == '\\') {
1259 qsizetype metaCharPos;
1260
1261 SKIP_CHAR();
1262 if (cc == '\n') {
1263 SKIP_CHAR();
1264 } else if (cc > '0' && cc < '8') {
1265 word += QChar(c.digitValue());
1266 SKIP_CHAR();
1267 } else if ((metaCharPos = QString::fromLatin1("abfnrtv").indexOf(c))
1268 != -1) {
1269 word += QLatin1Char("\a\b\f\n\r\t\v"[metaCharPos]);
1270 SKIP_CHAR();
1271 } else {
1272 PUT_CHAR();
1273 }
1274 } else if (c.isSpace() || cc == '#') {
1275 if (inQuote) {
1276 if (cc == '\n')
1277 location.fatal(QStringLiteral("Unterminated string"));
1278 PUT_CHAR();
1279 } else {
1280 if (!word.isEmpty() || needsExpansion) {
1281 rhsValues << word;
1282 word.clear();
1283 needsExpansion = false;
1284 }
1285 if (cc == '\n' || cc == '#')
1286 break;
1287 SKIP_SPACES();
1288 }
1289 } else if (cc == '"') {
1290 if (inQuote) {
1291 if (!word.isEmpty() || needsExpansion)
1292 rhsValues << word;
1293 word.clear();
1294 needsExpansion = false;
1295 }
1296 inQuote = !inQuote;
1297 SKIP_CHAR();
1298 } else if (cc == '$') {
1299 QString var;
1300 QChar delim(' ');
1301 bool braces = false;
1302 SKIP_CHAR();
1303 if (cc == '{') {
1304 SKIP_CHAR();
1305 braces = true;
1306 }
1307 while (c.isLetterOrNumber() || cc == '_') {
1308 var += c;
1309 SKIP_CHAR();
1310 }
1311 if (braces) {
1312 if (cc == ',') {
1313 SKIP_CHAR();
1314 delim = c;
1315 SKIP_CHAR();
1316 }
1317 if (cc == '}')
1318 SKIP_CHAR();
1319 else if (delim == '}')
1320 delim = QChar(); // null delimiter
1321 else
1322 location.fatal(QStringLiteral("Missing '}'"));
1323 }
1324 if (!var.isEmpty()) {
1325 const QByteArray val = qgetenv(var.toLatin1().constData());
1326 if (val.isNull()) {
1327 expandVars << ExpandVar(rhsValues.size(), word.size(), std::move(var), delim);
1328 needsExpansion = true;
1329 } else if (braces) { // ${VAR} inserts content from an env. variable for processing
1330 text.insert(i, QString::fromLatin1(val));
1331 c = text.at(i);
1332 cc = c.unicode();
1333 } else { // while $VAR simply reads the value and stores it to a config variable.
1334 word += QString::fromLatin1(val);
1335 }
1336 }
1337 } else {
1338 if (!inQuote && cc == '=')
1339 location.fatal(QStringLiteral("Unexpected '='"));
1340 PUT_CHAR();
1341 }
1342 }
1343 for (const auto &key : keys) {
1344 if (!keySyntax.match(key).hasMatch())
1345 keyLoc.fatal(QStringLiteral("Invalid key '%1'").arg(key));
1346
1347 ConfigVar configVar(key, rhsValues, QDir::currentPath(), keyLoc, expandVars);
1348 if (plus && m_configVars.contains(key)) {
1349 m_configVars[key].append(configVar);
1350 } else {
1351 m_configVars.insert(key, configVar);
1352 }
1353 }
1354 }
1355 } else {
1356 location.fatal(QStringLiteral("Unexpected character '%1' at beginning of line").arg(c));
1357 }
1358 }
1360
1361#undef SKIP_CHAR
1362#undef SKIP_SPACES
1363#undef PUT_CHAR
1364}
1365
1366bool Config::isFileExcluded(const QString &fileName, const QSet<QString> &excludedFiles)
1367{
1368 for (const QString &entry : excludedFiles) {
1369 if (entry.contains(QLatin1Char('*')) || entry.contains(QLatin1Char('?'))) {
1370 QRegularExpression re(QRegularExpression::wildcardToRegularExpression(entry));
1371 if (re.match(fileName).hasMatch())
1372 return true;
1373 }
1374 }
1375 return excludedFiles.contains(fileName);
1376}
1377
1378QStringList Config::getFilesHere(const QString &uncleanDir, const QString &nameFilter,
1379 const Location &location, const QSet<QString> &excludedDirs,
1380 const QSet<QString> &excludedFiles)
1381{
1382 // TODO: Understand why location is used to branch the
1383 // canonicalization and why the two different methods are used.
1384 QString dir =
1385 location.isEmpty() ? QDir::cleanPath(uncleanDir) : QDir(uncleanDir).canonicalPath();
1386 QStringList result;
1387 if (excludedDirs.contains(dir))
1388 return result;
1389
1390 QDir dirInfo(dir);
1391
1392 dirInfo.setNameFilters(nameFilter.split(QLatin1Char(' ')));
1393 dirInfo.setSorting(QDir::Name);
1394 dirInfo.setFilter(QDir::Files);
1395 QStringList fileNames = dirInfo.entryList();
1396 for (const auto &file : std::as_const(fileNames)) {
1397 // TODO: Understand if this is needed and, should it be, if it
1398 // is indeed the only case that should be considered.
1399 if (!file.startsWith(QLatin1Char('~'))) {
1400 QString s = dirInfo.filePath(file);
1401 QString c = QDir::cleanPath(s);
1402 if (!isFileExcluded(c, excludedFiles))
1403 result.append(c);
1404 }
1405 }
1406
1407 dirInfo.setNameFilters(QStringList(QLatin1String("*")));
1408 dirInfo.setFilter(QDir::Dirs | QDir::NoDotAndDotDot);
1409 fileNames = dirInfo.entryList();
1410 for (const auto &file : fileNames)
1411 result += getFilesHere(dirInfo.filePath(file), nameFilter, location, excludedDirs,
1412 excludedFiles);
1413 return result;
1414}
1415
1416/*!
1417 Set \a dir as the working directory and push it onto the
1418 stack of working directories.
1419 */
1420void Config::pushWorkingDir(const QString &dir)
1421{
1422 m_workingDirs.push(dir);
1423 QDir::setCurrent(dir);
1424}
1425
1426/*!
1427 Pop the top entry from the stack of working directories.
1428 Set the working directory to the next one on the stack,
1429 if one exists.
1430 */
1432{
1433 Q_ASSERT(!m_workingDirs.isEmpty());
1434 m_workingDirs.pop();
1435 if (!m_workingDirs.isEmpty())
1436 QDir::setCurrent(m_workingDirs.top());
1437}
1438
1440 if (m_excludedPaths)
1441 return *m_excludedPaths;
1442
1443 const auto &excludedDirList = getCanonicalPathList(CONFIG_EXCLUDEDIRS);
1444 const auto &excludedFilesList = getCanonicalPathList(CONFIG_EXCLUDEFILES);
1445
1446 QSet<QString> excludedDirs = QSet<QString>(excludedDirList.cbegin(), excludedDirList.cend());
1447 QSet<QString> excludedFiles = QSet<QString>(excludedFilesList.cbegin(), excludedFilesList.cend());
1448
1449 m_excludedPaths.emplace(ExcludedPaths{std::move(excludedDirs), std::move(excludedFiles)});
1450
1451 return *m_excludedPaths;
1452}
1453
1454/*!
1455 Returns the set of file patterns that identify internal implementation files.
1456 Classes declared in files matching these patterns will have their nodes marked
1457 as internal, excluding them and their members from documentation requirements
1458 when showInternal is false.
1459
1460 \sa isFileExcluded()
1461*/
1463{
1464 const QStringList patterns = get(CONFIG_INTERNALFILEPATTERNS).asStringList();
1465 return QSet<QString>(patterns.cbegin(), patterns.cend());
1466}
1467
1468/*!
1469 Returns a reference to the pre-compiled internal file patterns structure.
1470 This method caches the compiled patterns for efficient reuse across multiple
1471 file checks. The patterns are compiled once when first accessed and remain
1472 valid until the configuration is cleared or reloaded.
1473
1474 The returned structure separates patterns into:
1475 - exactMatches: Patterns without wildcards for direct string matching.
1476 - regexPatterns: Pre-compiled regular expressions for regular expression
1477 patterns.
1478
1479 \sa matchesInternalFilePattern(), clear()
1480*/
1482{
1483 if (m_internalFilePatterns)
1484 return *m_internalFilePatterns;
1485
1486 InternalFilePatterns compiled;
1487 const QStringList patterns = get(CONFIG_INTERNALFILEPATTERNS).asStringList();
1488
1489 for (const QString &pattern : patterns) {
1490 static const QRegularExpression regexMetaChars(
1491 uR"(\.\*|\.\+|\.\{|\\‍[\.\*\?]|[\^\$\‍[\‍]{}()|+])"_s);
1492 bool isRawRegex = regexMetaChars.match(pattern).hasMatch();
1493
1494 if (isRawRegex) {
1495 QRegularExpression re(pattern);
1496 if (!re.isValid()) {
1497 qWarning() << "Invalid regex pattern in internalfilepatterns:" << pattern
1498 << "-" << re.errorString();
1499 continue;
1500 }
1501 re.optimize();
1502 compiled.regexPatterns.append(re);
1503 } else if (pattern.contains(u'*') || pattern.contains(u'?')) {
1504 QString regexPattern = QRegularExpression::wildcardToRegularExpression(pattern);
1505 QRegularExpression re(regexPattern);
1506 re.optimize();
1507 compiled.globPatterns.append(re);
1508 } else {
1509 compiled.exactMatches.insert(pattern);
1510 }
1511 }
1512
1513 m_internalFilePatterns.emplace(std::move(compiled));
1514 return *m_internalFilePatterns;
1515}
1516
1517/*!
1518 Returns true if the given \a filePath matches any pattern in the
1519 \a patterns structure. This is an optimized version that uses pre-compiled
1520 regular expressions.
1521
1522 The function first checks for exact matches (patterns without wildcards),
1523 which compare against the full normalized path. Then it checks glob patterns
1524 against the filename only, and finally regex patterns against the full path.
1525
1526 Path normalization is handled internally: all backslashes are converted to
1527 forward slashes to ensure cross-platform consistency, as patterns are
1528 expected to use forward slashes.
1529
1530 \sa getInternalFilePatternsCompiled()
1531*/
1532bool Config::matchesInternalFilePattern(const QString &filePath,
1533 const InternalFilePatterns &patterns) noexcept
1534{
1535 if (patterns.exactMatches.isEmpty() && patterns.globPatterns.isEmpty()
1536 && patterns.regexPatterns.isEmpty())
1537 return false;
1538
1539 QString norm = filePath;
1540 norm.replace(QChar('\\'), QChar('/'));
1541
1542 if (patterns.exactMatches.contains(norm))
1543 return true;
1544
1545 const QString fileName = QFileInfo(norm).fileName();
1546 for (const QRegularExpression &re : patterns.globPatterns) {
1547 if (re.match(fileName).hasMatch())
1548 return true;
1549 }
1550
1551 for (const QRegularExpression &re : patterns.regexPatterns) {
1552 if (re.match(norm).hasMatch())
1553 return true;
1554 }
1555
1556 return false;
1557}
1558
1559/*!
1560 Returns a SourceLink struct with settings required to
1561 construct source links to API entities.
1562*/
1564{
1565 if (m_sourceLink)
1566 return *m_sourceLink;
1567
1568 const auto srcUrl{CONFIG_URL + Config::dot + CONFIG_SOURCES};
1569
1570 const auto baseUrl = m_configVars.value(srcUrl).asString();
1571 const auto rootPath = m_configVars.value(srcUrl + dot + CONFIG_ROOTDIR).asString();
1572 const auto linkText = m_configVars.value(srcUrl + dot + "linktext").asString();
1573 const auto enabled = m_configVars.value(srcUrl + dot + "enabled").asBool();
1574
1575 m_sourceLink.emplace(SourceLink{baseUrl, rootPath, linkText, enabled});
1576 return *m_sourceLink;
1577}
1578
1580 static QStringList accepted_header_file_extensions{
1581 "ch", "h", "h++", "hh", "hpp", "hxx"
1582 };
1583
1584 const auto& [excludedDirs, excludedFiles] = getExcludedPaths();
1585
1586 QStringList headerList =
1587 getAllFiles(CONFIG_HEADERS, CONFIG_HEADERDIRS, excludedDirs, excludedFiles);
1588
1589 std::set<HeaderFilePath> headers{};
1590
1591 for (const auto& header : headerList) {
1592 if (header.contains("doc/snippets")) continue;
1593
1594 if (!accepted_header_file_extensions.contains(QFileInfo{header}.suffix()))
1595 continue;
1596
1597 headers.insert(HeaderFilePath{QFileInfo{header}.canonicalPath(), QFileInfo{header}.fileName()});
1598 }
1599
1600 return headers;
1601}
1602
1604{
1605 return m_configVars.value(CONFIG_SHOWAUTOGENERATEDDOCS).asBool();
1606}
1607
1608InclusionPolicy Config::createInclusionPolicy() const
1609{
1610 InclusionPolicy policy;
1611
1612 policy.includePrivateFunction = includePrivateFunction();
1613 policy.includePrivateType = includePrivateType();
1614 policy.includePrivateVariable = includePrivateVariable();
1615 policy.showInternal = showInternal();
1616 policy.showAutoGeneratedDocs = showAutoGeneratedDocs();
1617
1618 return policy;
1619}
1620
1621QT_END_NAMESPACE
contains all the information for a single config variable in a .qdocconf file.
Definition config.h:43
QString asString(const QString defaultString=QString()) const
Returns this configuration variable as a string.
Definition config.cpp:244
QStringList asStringList() const
Returns this config variable as a string list.
Definition config.cpp:261
int asInt() const
Returns this configuration variable as an integer; iterates through the string list,...
Definition config.cpp:294
bool asBool() const
Returns this config variable as a boolean.
Definition config.cpp:281
QSet< QString > asStringSet() const
Returns this config variable as a string set.
Definition config.cpp:272
The Config class contains the configuration variables for controlling how qdoc produces documentation...
Definition config.h:85
static QString installDir
Definition config.h:172
QString getOutputDir(const QString &format=QString("HTML")) const
Function to return the correct outputdir for the output format.
Definition config.cpp:585
std::set< HeaderFilePath > getHeaderFiles()
Definition config.cpp:1579
static bool generateExamples
Definition config.h:171
void insertStringList(const QString &var, const QStringList &values)
Adds the values from a string list to the configuration variable var.
Definition config.cpp:506
static const QString dot
Definition config.h:169
QStringList getAllFiles(const QString &filesVar, const QString &dirsVar, const QSet< QString > &excludedDirs=QSet< QString >(), const QSet< QString > &excludedFiles=QSet< QString >())
Builds and returns a list of file pathnames for the file type specified by filesVar (e....
Definition config.cpp:796
QSet< QString > getInternalFilePatterns() const
Returns the set of file patterns that identify internal implementation files.
Definition config.cpp:1462
void reset()
Resets the Config instance - used by load().
Definition config.cpp:382
InclusionPolicy createInclusionPolicy() const
Definition config.cpp:1608
void setStringList(const QString &var, const QStringList &values)
Sets the values of a configuration variable var from a string list.
Definition config.cpp:497
void clear()
Clears the location and internal maps for config variables.
Definition config.cpp:369
QSet< QString > subVars(const QString &var) const
This function is slower than it could be.
Definition config.cpp:737
~Config()
Definition config.cpp:361
bool includePrivateType() const
Definition config.h:478
PathFlags
Flags used for retrieving canonicalized paths from Config.
Definition config.h:91
bool includePrivateVariable() const
Definition config.h:484
const ExcludedPaths & getExcludedPaths()
Definition config.cpp:1439
QSet< QString > getOutputFormats() const
Function to return the correct outputformats.
Definition config.cpp:610
static void popWorkingDir()
Pop the top entry from the stack of working directories.
Definition config.cpp:1431
bool showAutoGeneratedDocs() const
Definition config.cpp:1603
@ Generate
Definition config.h:89
@ Prepare
Definition config.h:89
QRegularExpression getRegExp(const QString &var) const
Calls getRegExpList() with the control variable var and iterates through the resulting list of regula...
Definition config.cpp:700
QString getExampleProjectFile(const QString &examplePath)
Returns the path to the project file for examplePath, or an empty string if no project file was found...
Definition config.cpp:845
bool includePrivateFunction() const
Definition config.h:472
static QSet< QString > overrideOutputFormats
Definition config.h:174
static QString overrideOutputDir
Definition config.h:173
QStringList getCanonicalPathList(const QString &var, PathFlags flags=None) const
Returns a path list where all paths from the config variable var are canonicalized.
Definition config.cpp:639
void load(const QString &fileName)
Loads and parses the qdoc configuration file fileName.
Definition config.cpp:428
const SourceLink & getSourceLink()
Returns a SourceLink struct with settings required to construct source links to API entities.
Definition config.cpp:1563
bool showInternal() const
Definition config.h:111
QList< QRegularExpression > getRegExpList(const QString &var) const
Looks up the configuration variable var in the string list map, converts the string list to a list of...
Definition config.cpp:722
QStringList getExampleImageFiles(const QSet< QString > &excludedDirs, const QSet< QString > &excludedFiles)
Definition config.cpp:822
QStringList getExampleQdocFiles(const QSet< QString > &excludedDirs, const QSet< QString > &excludedFiles)
Definition config.cpp:810
const InternalFilePatterns & getInternalFilePatternsCompiled()
Returns a reference to the pre-compiled internal file patterns structure.
Definition config.cpp:1481
void init(const QString &programName, const QStringList &args)
Initializes the Config with programName and sets all internal state variables to either default value...
Definition config.cpp:354
QString getIncludeFilePath(const QString &fileName) const
Searches for a path to fileName in 'sources', 'sourcedirs', and 'exampledirs' config variables and re...
Definition config.cpp:758
The Location class provides a way to mark a location in a file.
Definition location.h:20
int depth() const
Definition location.h:46
void start()
If the file position on top of the stack has a line number less than 1, set its line number to 1 and ...
Definition location.cpp:134
An entry in a stack, where each entry is a list of string values.
Definition config.cpp:119
QStringList next
Definition config.cpp:125
void close()
Stop accumulating values and append the list of accumulated values to the complete list of accumulate...
Definition config.cpp:143
QStringList accum
Definition config.cpp:124
void open()
Start accumulating values in a list by appending an empty string to the list.
Definition config.cpp:133
This class maintains a stack of values of config file variables.
Definition config.cpp:155
QStringList getExpanded(const Location &location)
Returns the accumulated string values.
Definition config.cpp:208
void process(QChar ch, const Location &location)
Processes the character ch using the location.
Definition config.cpp:178
MetaStack()
The default constructor pushes a new stack entry and opens it.
Definition config.cpp:167
Q_DECLARE_TYPEINFO(MetaStackEntry, Q_RELOCATABLE_TYPE)
#define SKIP_CHAR()
#define PUT_CHAR()
#define SKIP_SPACES()
#define CONFIG_SOURCES
Definition config.h:436
#define CONFIG_REDIRECTDOCUMENTATIONTODEVNULL
Definition config.h:425
#define CONFIG_FILEEXTENSIONS
Definition config.h:449
#define CONFIG_TABSIZE
Definition config.h:440
#define CONFIG_OUTPUTDIR
Definition config.h:417
#define CONFIG_AUTOLINKERRORS
Definition config.h:366
#define CONFIG_SHOWINTERNAL
Definition config.h:432
#define CONFIG_SHOWAUTOGENERATEDDOCS
Definition config.h:431
#define CONFIG_DOCBOOKEXTENSIONS
Definition config.h:376
#define CONFIG_SINGLEEXEC
Definition config.h:433
#define CONFIG_FALSEHOODS
Definition config.h:385
#define CONFIG_EXAMPLES
Definition config.h:380
#define CONFIG_EXAMPLEDIRS
Definition config.h:379
#define CONFIG_URL
Definition config.h:445
#define CONFIG_WARNABOUTMISSINGPROJECTFILES
Definition config.h:454
#define CONFIG_SYNTAXHIGHLIGHTING
Definition config.h:439
#define CONFIG_DEFINES
Definition config.h:373
#define CONFIG_PRELIMINARY
Definition config.h:421
#define CONFIG_TIMESTAMPS
Definition config.h:442
#define CONFIG_LANGUAGE
Definition config.h:406
#define CONFIG_NOLINKERRORS
Definition config.h:416
#define CONFIG_LOGPROGRESS
Definition config.h:408
#define CONFIG_DESCRIPTION
Definition config.h:375
#define CONFIG_PROJECT
Definition config.h:423
#define CONFIG_SOURCEDIRS
Definition config.h:434
#define CONFIG_CODEINDENT
Definition config.h:368
#define CONFIG_INTERNALFILEPATTERNS
Definition config.h:401
#define CONFIG_ROOTDIR
Definition config.h:429
#define CONFIG_WARNABOUTMISSINGIMAGES
Definition config.h:453
#define CONFIG_HEADERS
Definition config.h:388
#define CONFIG_INCLUDEPRIVATE
Definition config.h:400
#define CONFIG_OUTPUTFORMATS
Definition config.h:418
#define CONFIG_LOCATIONINFO
Definition config.h:407
#define CONFIG_IMAGEEXTENSIONS
Definition config.h:450
#define CONFIG_EXCLUDEFILES
Definition config.h:383
#define CONFIG_EXCLUDEDIRS
Definition config.h:382
#define CONFIG_HEADERDIRS
Definition config.h:387
#define CONFIG_REPORTMISSINGALTTEXTFORIMAGES
Definition config.h:426
#define CONFIG_INCLUDEPATHS
Definition config.h:399
Combined button and popup list for selecting options.
#define SET(x, y)
static QString HEADERSTYLES
Definition config.h:297
static QString IGNORESINCE
Definition config.h:302
static QString CODESUFFIX
Definition config.h:277
static QString EXAMPLEDIRS
Definition config.h:286
static QString CODEINDENT
Definition config.h:275
static QString HEADERDIRS
Definition config.h:294
static QString ROOTDIR
Definition config.h:337
static QString HEADERS
Definition config.h:295
static QString NATURALLANGUAGE
Definition config.h:322
static QString WARNABOUTMISSINGPROJECTFILES
Definition config.h:362
static QString SINGLEEXEC
Definition config.h:341
static QString LANGUAGE
Definition config.h:314
static QString SOURCEDIRS
Definition config.h:342
static QString IGNOREDIRECTIVES
Definition config.h:300
static QString SHOWAUTOGENERATEDDOCS
Definition config.h:339
static QString EXCLUDEDIRS
Definition config.h:289
static QString INCLUSIVE
Definition config.h:310
static QString FILEEXTENSIONS
Definition config.h:357
static QString AUTOLINKERRORS
Definition config.h:273
static QString LOGWARNINGSDISABLECLIARGS
Definition config.h:318
static QString OUTPUTFORMATS
Definition config.h:326
static QString REDIRECTDOCUMENTATIONTODEVNULL
Definition config.h:333
static QString LANDINGPAGE
Definition config.h:312
static QString SCRIPTS
Definition config.h:338
static QString QHP
Definition config.h:335
static QString MODULEHEADER
Definition config.h:321
static QString DEPENDS
Definition config.h:281
static QString VERSION
Definition config.h:355
static QString CPPCLASSESPAGE
Definition config.h:278
static QString MACRO
Definition config.h:319
static QString PRODUCTNAME
Definition config.h:330
static QString SOURCES
Definition config.h:344
static QString BUILDVERSION
Definition config.h:274
static QString DOCBOOKEXTENSIONS
Definition config.h:283
static QString VERSIONSYM
Definition config.h:356
static QString IMAGEEXTENSIONS
Definition config.h:358
static QString ENDHEADER
Definition config.h:285
static QString EXAMPLESINSTALLPATH
Definition config.h:288
static QString DEFINES
Definition config.h:280
static QString INCLUDEPRIVATE
Definition config.h:308
static QString HEADERSCRIPTS
Definition config.h:296
static QString SOURCEENCODING
Definition config.h:343
static QString IGNORETOKENS
Definition config.h:301
static QString TAGFILE
Definition config.h:349
static QString IMAGEDIRS
Definition config.h:304
static QString NAVIGATION
Definition config.h:323
static QString SYNTAXHIGHLIGHTING
Definition config.h:347
static QString IGNOREWORDS
Definition config.h:303
static QString EXAMPLES
Definition config.h:287
static QString INCLUDEPATHS
Definition config.h:307
static QString WARNINGLIMIT
Definition config.h:363
static QString STYLESHEETS
Definition config.h:346
static QString LANDINGTITLE
Definition config.h:313
static QString USEALTTEXTASTITLE
Definition config.h:354
static QString CODEPREFIX
Definition config.h:276
static QString LOGPROGRESS
Definition config.h:316
static QString FALSEHOODS
Definition config.h:292
static QString HOMEPAGE
Definition config.h:298
static QString IMAGESOUTPUTDIR
Definition config.h:305
static QString TRADEMARKSPAGE
Definition config.h:352
static QString QMLTYPESPAGE
Definition config.h:359
static QString SPURIOUS
Definition config.h:345
static QString LOGWARNINGS
Definition config.h:317
static QString INDEXES
Definition config.h:311
static QString OUTPUTDIR
Definition config.h:325
static QString EXCLUDEFILES
Definition config.h:290
static QString EXTRAIMAGES
Definition config.h:291
static QString PRELIMINARY
Definition config.h:329
static QString TOCTITLES
Definition config.h:351
static QString NOLINKERRORS
Definition config.h:324
static QString LOCATIONINFO
Definition config.h:315
static QString TABSIZE
Definition config.h:348
static QString TIMESTAMPS
Definition config.h:350
static QString WARNABOUTMISSINGIMAGES
Definition config.h:361
static QString HOMETITLE
Definition config.h:299
static QString DOCUMENTATIONINHEADERS
Definition config.h:284
static QString SHOWINTERNAL
Definition config.h:340
static QString CPPCLASSESTITLE
Definition config.h:279
static QString QUOTINGINFORMATION
Definition config.h:336
static QString FORMATTING
Definition config.h:293
static QString PROJECT
Definition config.h:331
static QString MANIFESTMETA
Definition config.h:320
static QString OUTPUTPREFIXES
Definition config.h:327
static QString DESCRIPTION
Definition config.h:282
static QString REPORTMISSINGALTTEXTFORIMAGES
Definition config.h:334
static QString INTERNALFILEPATTERNS
Definition config.h:309
static QString OUTPUTSUFFIXES
Definition config.h:328
static QString URL
Definition config.h:353
static QString PROJECTROOT
Definition config.h:332
static QString QMLTYPESTITLE
Definition config.h:360