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