diff options
Diffstat (limited to 'src/qdoc/config.cpp')
-rw-r--r-- | src/qdoc/config.cpp | 1314 |
1 files changed, 0 insertions, 1314 deletions
diff --git a/src/qdoc/config.cpp b/src/qdoc/config.cpp deleted file mode 100644 index 41dc9b2c6..000000000 --- a/src/qdoc/config.cpp +++ /dev/null @@ -1,1314 +0,0 @@ -/**************************************************************************** -** -** Copyright (C) 2021 The Qt Company Ltd. -** Contact: https://www.qt.io/licensing/ -** -** This file is part of the tools applications of the Qt Toolkit. -** -** $QT_BEGIN_LICENSE:GPL-EXCEPT$ -** Commercial License Usage -** Licensees holding valid commercial Qt licenses may use this file in -** accordance with the commercial license agreement provided with the -** Software or, alternatively, in accordance with the terms contained in -** a written agreement between you and The Qt Company. For licensing terms -** and conditions see https://www.qt.io/terms-conditions. For further -** information use the contact form at https://www.qt.io/contact-us. -** -** GNU General Public License Usage -** Alternatively, this file may be used under the terms of the GNU -** General Public License version 3 as published by the Free Software -** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT -** included in the packaging of this file. Please review the following -** information to ensure the GNU General Public License requirements will -** be met: https://www.gnu.org/licenses/gpl-3.0.html. -** -** $QT_END_LICENSE$ -** -****************************************************************************/ - -#include "config.h" -#include "utilities.h" - -#include <QtCore/qdir.h> -#include <QtCore/qfile.h> -#include <QtCore/qtemporaryfile.h> -#include <QtCore/qtextstream.h> -#include <QtCore/qvariant.h> -#include <QtCore/qregularexpression.h> - -QT_BEGIN_NAMESPACE - -QString ConfigStrings::ALIAS = QStringLiteral("alias"); -QString ConfigStrings::AUTOLINKERRORS = QStringLiteral("autolinkerrors"); -QString ConfigStrings::BUILDVERSION = QStringLiteral("buildversion"); -QString ConfigStrings::CLANGDEFINES = QStringLiteral("clangdefines"); -QString ConfigStrings::CODEINDENT = QStringLiteral("codeindent"); -QString ConfigStrings::CODEPREFIX = QStringLiteral("codeprefix"); -QString ConfigStrings::CODESUFFIX = QStringLiteral("codesuffix"); -QString ConfigStrings::CPPCLASSESPAGE = QStringLiteral("cppclassespage"); -QString ConfigStrings::CPPCLASSESTITLE = QStringLiteral("cppclassestitle"); -QString ConfigStrings::DEFINES = QStringLiteral("defines"); -QString ConfigStrings::DEPENDS = QStringLiteral("depends"); -QString ConfigStrings::DESCRIPTION = QStringLiteral("description"); -QString ConfigStrings::DOCBOOKEXTENSIONS = QStringLiteral("usedocbookextensions"); -QString ConfigStrings::ENDHEADER = QStringLiteral("endheader"); -QString ConfigStrings::EXAMPLEDIRS = QStringLiteral("exampledirs"); -QString ConfigStrings::EXAMPLES = QStringLiteral("examples"); -QString ConfigStrings::EXAMPLESINSTALLPATH = QStringLiteral("examplesinstallpath"); -QString ConfigStrings::EXCLUDEDIRS = QStringLiteral("excludedirs"); -QString ConfigStrings::EXCLUDEFILES = QStringLiteral("excludefiles"); -QString ConfigStrings::EXTRAIMAGES = QStringLiteral("extraimages"); -QString ConfigStrings::FALSEHOODS = QStringLiteral("falsehoods"); -QString ConfigStrings::FORMATTING = QStringLiteral("formatting"); -QString ConfigStrings::HEADERDIRS = QStringLiteral("headerdirs"); -QString ConfigStrings::HEADERS = QStringLiteral("headers"); -QString ConfigStrings::HEADERSCRIPTS = QStringLiteral("headerscripts"); -QString ConfigStrings::HEADERSTYLES = QStringLiteral("headerstyles"); -QString ConfigStrings::HOMEPAGE = QStringLiteral("homepage"); -QString ConfigStrings::HOMETITLE = QStringLiteral("hometitle"); -QString ConfigStrings::IGNOREDIRECTIVES = QStringLiteral("ignoredirectives"); -QString ConfigStrings::IGNORESINCE = QStringLiteral("ignoresince"); -QString ConfigStrings::IGNORETOKENS = QStringLiteral("ignoretokens"); -QString ConfigStrings::IGNOREWORDS = QStringLiteral("ignorewords"); -QString ConfigStrings::IMAGEDIRS = QStringLiteral("imagedirs"); -QString ConfigStrings::IMAGES = QStringLiteral("images"); -QString ConfigStrings::INCLUDEPATHS = QStringLiteral("includepaths"); -QString ConfigStrings::INDEXES = QStringLiteral("indexes"); -QString ConfigStrings::LANDINGPAGE = QStringLiteral("landingpage"); -QString ConfigStrings::LANDINGTITLE = QStringLiteral("landingtitle"); -QString ConfigStrings::LANGUAGE = QStringLiteral("language"); -QString ConfigStrings::LOCATIONINFO = QStringLiteral("locationinfo"); -QString ConfigStrings::LOGPROGRESS = QStringLiteral("logprogress"); -QString ConfigStrings::MACRO = QStringLiteral("macro"); -QString ConfigStrings::MANIFESTMETA = QStringLiteral("manifestmeta"); -QString ConfigStrings::MODULEHEADER = QStringLiteral("moduleheader"); -QString ConfigStrings::NATURALLANGUAGE = QStringLiteral("naturallanguage"); -QString ConfigStrings::NAVIGATION = QStringLiteral("navigation"); -QString ConfigStrings::NOLINKERRORS = QStringLiteral("nolinkerrors"); -QString ConfigStrings::OUTPUTDIR = QStringLiteral("outputdir"); -QString ConfigStrings::OUTPUTFORMATS = QStringLiteral("outputformats"); -QString ConfigStrings::OUTPUTPREFIXES = QStringLiteral("outputprefixes"); -QString ConfigStrings::OUTPUTSUFFIXES = QStringLiteral("outputsuffixes"); -QString ConfigStrings::PROJECT = QStringLiteral("project"); -QString ConfigStrings::REDIRECTDOCUMENTATIONTODEVNULL = - QStringLiteral("redirectdocumentationtodevnull"); -QString ConfigStrings::QHP = QStringLiteral("qhp"); -QString ConfigStrings::QUOTINGINFORMATION = QStringLiteral("quotinginformation"); -QString ConfigStrings::SCRIPTS = QStringLiteral("scripts"); -QString ConfigStrings::SHOWINTERNAL = QStringLiteral("showinternal"); -QString ConfigStrings::SINGLEEXEC = QStringLiteral("singleexec"); -QString ConfigStrings::SOURCEDIRS = QStringLiteral("sourcedirs"); -QString ConfigStrings::SOURCEENCODING = QStringLiteral("sourceencoding"); -QString ConfigStrings::SOURCES = QStringLiteral("sources"); -QString ConfigStrings::SPURIOUS = QStringLiteral("spurious"); -QString ConfigStrings::STYLESHEETS = QStringLiteral("stylesheets"); -QString ConfigStrings::SYNTAXHIGHLIGHTING = QStringLiteral("syntaxhighlighting"); -QString ConfigStrings::TABSIZE = QStringLiteral("tabsize"); -QString ConfigStrings::TAGFILE = QStringLiteral("tagfile"); -QString ConfigStrings::TIMESTAMPS = QStringLiteral("timestamps"); -QString ConfigStrings::TOCTITLES = QStringLiteral("toctitles"); -QString ConfigStrings::TRANSLATORS = QStringLiteral("translators"); -QString ConfigStrings::URL = QStringLiteral("url"); -QString ConfigStrings::VERSION = QStringLiteral("version"); -QString ConfigStrings::VERSIONSYM = QStringLiteral("versionsym"); -QString ConfigStrings::FILEEXTENSIONS = QStringLiteral("fileextensions"); -QString ConfigStrings::IMAGEEXTENSIONS = QStringLiteral("imageextensions"); -QString ConfigStrings::QMLTYPESPAGE = QStringLiteral("qmltypespage"); -QString ConfigStrings::QMLTYPESTITLE = QStringLiteral("qmltypestitle"); -QString ConfigStrings::WARNINGLIMIT = QStringLiteral("warninglimit"); - -/*! - An entry in a stack, where each entry is a list - of string values. - */ -class MetaStackEntry -{ -public: - void open(); - void close(); - - QStringList accum; - QStringList next; -}; -Q_DECLARE_TYPEINFO(MetaStackEntry, Q_RELOCATABLE_TYPE); - -/*! - Start accumulating values in a list by appending an empty - string to the list. - */ -void MetaStackEntry::open() -{ - next.append(QString()); -} - -/*! - Stop accumulating values and append the list of accumulated - values to the complete list of accumulated values. - - */ -void MetaStackEntry::close() -{ - accum += next; - next.clear(); -} - -/*! - \class MetaStack - - This class maintains a stack of values of config file variables. -*/ -class MetaStack : private QStack<MetaStackEntry> -{ -public: - MetaStack(); - - void process(QChar ch, const Location &location); - QStringList getExpanded(const Location &location); -}; - -/*! - The default constructor pushes a new stack entry and - opens it. - */ -MetaStack::MetaStack() -{ - push(MetaStackEntry()); - top().open(); -} - -/*! - Processes the character \a ch using the \a location. - It really just builds up a name by appending \a ch to - it. - */ -void MetaStack::process(QChar ch, const Location &location) -{ - if (ch == QLatin1Char('{')) { - push(MetaStackEntry()); - top().open(); - } else if (ch == QLatin1Char('}')) { - if (count() == 1) - location.fatal(QStringLiteral("Unexpected '}'")); - - top().close(); - const QStringList suffixes = pop().accum; - const QStringList prefixes = top().next; - - top().next.clear(); - for (const auto &prefix : prefixes) { - for (const auto &suffix : suffixes) - top().next << prefix + suffix; - } - } else if (ch == QLatin1Char(',') && count() > 1) { - top().close(); - top().open(); - } else { - for (QString &topNext : top().next) - topNext += ch; - } -} - -/*! - Returns the accumulated string values. - */ -QStringList MetaStack::getExpanded(const Location &location) -{ - if (count() > 1) - location.fatal(QStringLiteral("Missing '}'")); - - top().close(); - return top().accum; -} - -const QString Config::dot = QLatin1String("."); -bool Config::m_debug = false; -bool Config::generateExamples = true; -QString Config::overrideOutputDir; -QString Config::installDir; -QSet<QString> Config::overrideOutputFormats; -QMap<QString, QString> Config::m_extractedDirs; -QStack<QString> Config::m_workingDirs; -QMap<QString, QStringList> Config::m_includeFilesMap; - -/*! - \class Config - \brief The Config class contains the configuration variables - for controlling how qdoc produces documentation. - - Its load() function reads, parses, and processes a qdocconf file. - */ - -/*! - \enum Config::PathFlags - - Flags used for retrieving canonicalized paths from Config. - - \value Validate - Issue a warning for paths that do not exist and - remove them from the returned list. - - \value IncludePaths - Assume the variable contains include paths with - prefixes such as \c{-I} that are to be removed - before canonicalizing and then re-inserted. - - \omitvalue None - - \sa getCanonicalPathList() -*/ - -/*! - Initializes the Config with \a programName and sets all - internal state variables to either default values or to ones - defined in command line arguments \a args. - */ -void Config::init(const QString &programName, const QStringList &args) -{ - m_prog = programName; - processCommandLineOptions(args); - reset(); -} - -Config::~Config() -{ - clear(); -} - -/*! - Clears the location and internal maps for config variables. - */ -void Config::clear() -{ - m_location = m_lastLocation = Location(); - m_configVars.clear(); - m_includeFilesMap.clear(); -} - -/*! - Resets the Config instance - used by load() - */ -void Config::reset() -{ - clear(); - - // Default values - setStringList(CONFIG_CODEINDENT, QStringList("0")); - setStringList(CONFIG_FALSEHOODS, QStringList("0")); - setStringList(CONFIG_FILEEXTENSIONS, QStringList("*.cpp *.h *.qdoc *.qml")); - setStringList(CONFIG_LANGUAGE, QStringList("Cpp")); // i.e. C++ - setStringList(CONFIG_OUTPUTFORMATS, QStringList("HTML")); - setStringList(CONFIG_TABSIZE, QStringList("8")); - setStringList(CONFIG_LOCATIONINFO, QStringList("true")); - - // Publish options from the command line as config variables - const auto setListFlag = [this](const QString &key, bool test) { - setStringList(key, QStringList(test ? QStringLiteral("true") : QStringLiteral("false"))); - }; -#define SET(opt, test) setListFlag(opt, m_parser.isSet(m_parser.test)) - SET(CONFIG_SYNTAXHIGHLIGHTING, highlightingOption); - SET(CONFIG_SHOWINTERNAL, showInternalOption); - SET(CONFIG_SINGLEEXEC, singleExecOption); - SET(CONFIG_REDIRECTDOCUMENTATIONTODEVNULL, redirectDocumentationToDevNullOption); - SET(CONFIG_AUTOLINKERRORS, autoLinkErrorsOption); -#undef SET - m_showInternal = getBool(CONFIG_SHOWINTERNAL); - setListFlag(CONFIG_NOLINKERRORS, - m_parser.isSet(m_parser.noLinkErrorsOption) - || qEnvironmentVariableIsSet("QDOC_NOLINKERRORS")); - - // CONFIG_DEFINES and CONFIG_INCLUDEPATHS are set in load() -} - -/*! - Loads and parses the qdoc configuration file \a fileName. - If a previous project was loaded, this function first resets the - Config instance. Then it calls the other load() function, which - does the loading, parsing, and processing of the configuration file. - */ -void Config::load(const QString &fileName) -{ - // Reset if a previous project was loaded - if (m_configVars.contains(CONFIG_PROJECT)) - reset(); - - load(Location(), fileName); - if (m_location.isEmpty()) - m_location = Location(fileName); - else - m_location.setEtc(true); - m_lastLocation = Location(); - - expandVariables(); - - // Add defines and includepaths from command line to their - // respective configuration variables. Values set here are - // always added to what's defined in configuration file. - insertStringList(CONFIG_DEFINES, m_defines); - insertStringList(CONFIG_INCLUDEPATHS, m_includePaths); - - // Prefetch values that are used internally - m_exampleFiles = getCanonicalPathList(CONFIG_EXAMPLES); - m_exampleDirs = getCanonicalPathList(CONFIG_EXAMPLEDIRS); -} - -/*! - Expands other config variables referred to in all stored ConfigVars. -*/ -void Config::expandVariables() -{ - for (auto &configVar : m_configVars) { - for (auto it = configVar.m_expandVars.crbegin(); it != configVar.m_expandVars.crend(); ++it) { - Q_ASSERT(it->m_valueIndex < configVar.m_values.size()); - const QString &key = it->m_var; - const auto &refVar = m_configVars.value(key); - if (refVar.m_name.isEmpty()) { - configVar.m_location.fatal( - QStringLiteral("Environment or configuration variable '%1' undefined") - .arg(it->m_var)); - } else if (!refVar.m_expandVars.empty()) { - configVar.m_location.fatal( - QStringLiteral("Nested variable expansion not allowed"), - QStringLiteral("When expanding '%1' at %2:%3") - .arg(refVar.m_name, refVar.m_location.filePath(), - QString::number(refVar.m_location.lineNo()))); - } - QString expanded; - if (it->m_delim.isNull()) - expanded = getStringList(key).join(QString()); - else - expanded = getStringList(key).join(it->m_delim); - configVar.m_values[it->m_valueIndex].m_value.insert(it->m_index, expanded); - } - configVar.m_expandVars.clear(); - } -} - -/*! - Sets the \a values of a configuration variable \a var from a string list. - */ -void Config::setStringList(const QString &var, const QStringList &values) -{ - m_configVars.insert(var, ConfigVar(var, values, QDir::currentPath())); -} - -/*! - Adds the \a values from a string list to the configuration variable \a var. - Existing value(s) are kept. -*/ -void Config::insertStringList(const QString &var, const QStringList &values) -{ - m_configVars[var].append(ConfigVar(var, values, QDir::currentPath())); -} - -/*! - Process and store variables from the command line. - */ -void Config::processCommandLineOptions(const QStringList &args) -{ - m_parser.process(args); - - m_defines = m_parser.values(m_parser.defineOption); - m_dependModules = m_parser.values(m_parser.dependsOption); - setIndexDirs(); - setIncludePaths(); - - generateExamples = !m_parser.isSet(m_parser.noExamplesOption); - if (m_parser.isSet(m_parser.installDirOption)) - installDir = m_parser.value(m_parser.installDirOption); - if (m_parser.isSet(m_parser.outputDirOption)) - overrideOutputDir = m_parser.value(m_parser.outputDirOption); - - const auto outputFormats = m_parser.values(m_parser.outputFormatOption); - for (const auto &format : outputFormats) - overrideOutputFormats.insert(format); - m_debug = m_parser.isSet(m_parser.debugOption) || qEnvironmentVariableIsSet("QDOC_DEBUG"); - m_showInternal = m_parser.isSet(m_parser.showInternalOption) - || qEnvironmentVariableIsSet("QDOC_SHOW_INTERNAL"); - - if (m_parser.isSet(m_parser.prepareOption)) - m_qdocPass = Prepare; - if (m_parser.isSet(m_parser.generateOption)) - m_qdocPass = Generate; - if (m_parser.isSet(m_parser.logProgressOption)) - setStringList(CONFIG_LOGPROGRESS, QStringList("true")); - if (m_parser.isSet(m_parser.timestampsOption)) - setStringList(CONFIG_TIMESTAMPS, QStringList("true")); - if (m_parser.isSet(m_parser.useDocBookExtensions)) - setStringList(CONFIG_DOCBOOKEXTENSIONS, QStringList("true")); -} - -void Config::setIncludePaths() -{ - QDir currentDir = QDir::current(); - const auto addIncludePaths = [this, currentDir](const char *flag, const QStringList &paths) { - for (const auto &path : paths) - m_includePaths << currentDir.absoluteFilePath(path).insert(0, flag); - }; - - addIncludePaths("-I", m_parser.values(m_parser.includePathOption)); -#ifdef QDOC_PASS_ISYSTEM - addIncludePaths("-isystem", m_parser.values(m_parser.includePathSystemOption)); -#endif - addIncludePaths("-F", m_parser.values(m_parser.frameworkOption)); -} - -/*! - Stores paths from -indexdir command line option(s). - */ -void Config::setIndexDirs() -{ - m_indexDirs = m_parser.values(m_parser.indexDirOption); - auto it = std::remove_if(m_indexDirs.begin(), m_indexDirs.end(), - [](const QString &s) { return !QFile::exists(s); }); - - std::for_each(it, m_indexDirs.end(), [](const QString &s) { - qCWarning(lcQdoc) << "Cannot find index directory: " << s; - }); - m_indexDirs.erase(it, m_indexDirs.end()); -} - -/*! - Looks up the configuration variable \a var in the string - map and returns the boolean value. - */ -bool Config::getBool(const QString &var) const -{ - return QVariant(getString(var)).toBool(); -} - -/*! - Looks up the configuration variable \a var in the string list - map. Iterates through the string list found, interpreting each - string in the list as an integer and adding it to a total sum. - Returns the sum or \c -1 if \a var is not set. - */ -int Config::getInt(const QString &var) const -{ - const QStringList strs = getStringList(var); - if (strs.isEmpty()) - return -1; - - int sum = 0; - for (const auto &str : strs) - sum += str.toInt(); - return sum; -} - -/*! - Function to return the correct outputdir for the output \a format. - If \a format is not specified, defaults to 'HTML'. - outputdir can be set using the qdocconf or the command-line - variable -outputdir. - */ -QString Config::getOutputDir(const QString &format) const -{ - QString t; - if (overrideOutputDir.isNull()) - t = getString(CONFIG_OUTPUTDIR); - else - t = overrideOutputDir; - if (getBool(CONFIG_SINGLEEXEC)) { - QString project = getString(CONFIG_PROJECT); - t += QLatin1Char('/') + project.toLower(); - } - if (getBool(format + Config::dot + "nosubdirs")) { - t = t.left(t.lastIndexOf('/')); - QString singleOutputSubdir = getString(format + Config::dot + "outputsubdir"); - if (singleOutputSubdir.isEmpty()) - singleOutputSubdir = "html"; - t += QLatin1Char('/') + singleOutputSubdir; - } - return t; -} - -/*! - Function to return the correct outputformats. - outputformats can be set using the qdocconf or the command-line - variable -outputformat. - */ -QSet<QString> Config::getOutputFormats() const -{ - if (overrideOutputFormats.isEmpty()) - return getStringSet(CONFIG_OUTPUTFORMATS); - else - return overrideOutputFormats; -} - -/*! - Returns the value of a configuration variable \a var - as a string. If \a var is defined, updates the internal - location to the location of \a var for the purposes of - error reporting. - - If \a var is not defined, returns \a defaultString. - - \note By default, \a defaultString is a null string. If \a var - is found but contains an empty string, that is returned instead. - This allows determining whether a configuration variable is - undefined (null string) or defined as empty (empty string). - */ -QString Config::getString(const QString &var, const QString &defaultString) const -{ - const auto &configVar = m_configVars.value(var); - - if (configVar.m_name.isEmpty()) - return defaultString; - updateLocation(configVar); - - QString result(""); // an empty but non-null string - for (const auto &value : configVar.m_values) { - if (!result.isEmpty() && !result.endsWith(QChar('\n'))) - result.append(QChar(' ')); - result.append(value.m_value); - } - return result; -} - -/*! - Looks up the configuration variable \a var in the string - list map, converts the string list it maps to into a set - of strings, and returns the set. - */ -QSet<QString> Config::getStringSet(const QString &var) const -{ - const auto &stringList = getStringList(var); - return QSet<QString>(stringList.cbegin(), stringList.cend()); -} - -/*! - Returns the string list contained in the configuration variable - \a var. If \a var is defined, updates the internal location - to the location of \a var for the purposes of error reporting. - */ -QStringList Config::getStringList(const QString &var) const -{ - const auto &configVar = m_configVars.value(var); - updateLocation(configVar); - - QStringList result; - for (const auto &value : configVar.m_values) - result << value.m_value; - return result; -} - -/*! - Returns a path list where all paths from the config variable \a var - are canonicalized. If \a flags contains \c Validate, outputs a warning - for invalid paths. The \c IncludePaths flag is used as a hint to strip - away potential prefixes found in include paths before attempting to - canonicalize. - - \note The internal location is updated to the location of \a var for - the purposes of error reporting. - */ -QStringList Config::getCanonicalPathList(const QString &var, PathFlags flags) const -{ - QStringList result; - const auto &configVar = m_configVars.value(var); - updateLocation(configVar); - - for (const auto &value : configVar.m_values) { - const QString ¤tPath = value.m_path; - QString rawValue = value.m_value.simplified(); - QString prefix; - - if (flags & IncludePaths) { - const QStringList prefixes = QStringList() - << QLatin1String("-I") - << QLatin1String("-F") - << QLatin1String("-isystem"); - const auto end = std::end(prefixes); - const auto it = - std::find_if(std::begin(prefixes), end, - [&rawValue](const QString &p) { - return rawValue.startsWith(p); - }); - if (it != end) { - prefix = *it; - rawValue.remove(0, it->size()); - if (rawValue.isEmpty()) - continue; - } else { - prefix = prefixes[0]; // -I as default - } - } - - QDir dir(rawValue.trimmed()); - const QString path = dir.path(); - - if (dir.isRelative()) - dir.setPath(currentPath + QLatin1Char('/') + path); - if ((flags & Validate) && !QFileInfo::exists(dir.path())) - m_lastLocation.warning(QStringLiteral("Cannot find file or directory: %1").arg(path)); - else { - const QString canonicalPath = dir.canonicalPath(); - if (!canonicalPath.isEmpty()) - result.append(prefix + canonicalPath); - else if (path.contains(QLatin1Char('*')) || path.contains(QLatin1Char('?'))) - result.append(path); - else - qCDebug(lcQdoc) << - qUtf8Printable(QStringLiteral("%1: Ignored nonexistent path \'%2\'") - .arg(m_lastLocation.toString()).arg(rawValue)); - } - } - return result; -} - -/*! - Calls getRegExpList() with the control variable \a var and - iterates through the resulting list of regular expressions, - concatenating them with extra characters to form a single - QRegularExpression, which is then returned. - - \sa getRegExpList() - */ -QRegularExpression Config::getRegExp(const QString &var) const -{ - QString pattern; - const auto subRegExps = getRegExpList(var); - - for (const auto ®Exp : subRegExps) { - if (!regExp.isValid()) - return regExp; - if (!pattern.isEmpty()) - pattern += QLatin1Char('|'); - pattern += QLatin1String("(?:") + regExp.pattern() + QLatin1Char(')'); - } - if (pattern.isEmpty()) - pattern = QLatin1String("$x"); // cannot match - return QRegularExpression(pattern); -} - -/*! - Looks up the configuration variable \a var in the string list - map, converts the string list to a list of regular expressions, - and returns it. - */ -QList<QRegularExpression> Config::getRegExpList(const QString &var) const -{ - const QStringList strs = getStringList(var); - QList<QRegularExpression> regExps; - for (const auto &str : strs) - regExps += QRegularExpression(str); - return regExps; -} - -/*! - This function is slower than it could be. What it does is - find all the keys that begin with \a var + dot and return - the matching keys in a set, stripped of the matching prefix - and dot. - */ -QSet<QString> Config::subVars(const QString &var) const -{ - QSet<QString> result; - QString varDot = var + QLatin1Char('.'); - for (auto it = m_configVars.constBegin(); it != m_configVars.constEnd(); ++it) { - if (it.key().startsWith(varDot)) { - QString subVar = it.key().mid(varDot.length()); - int dot = subVar.indexOf(QLatin1Char('.')); - if (dot != -1) - subVar.truncate(dot); - result.insert(subVar); - } - } - return result; -} - -/*! - Searches for a path to \a fileName in 'sources', 'sourcedirs', and - 'exampledirs' config variables and returns a full path to the first - match found. If the file is not found, returns an empty string. - */ -QString Config::getIncludeFilePath(const QString &fileName) const -{ - QString ext = QFileInfo(fileName).suffix(); - - if (!m_includeFilesMap.contains(ext)) { - QStringList result = getCanonicalPathList(CONFIG_SOURCES); - result.erase(std::remove_if(result.begin(), result.end(), - [&](const QString &s) { return !s.endsWith(ext); }), - result.end()); - const QStringList dirs = - getCanonicalPathList(CONFIG_SOURCEDIRS) + - getCanonicalPathList(CONFIG_EXAMPLEDIRS); - - for (const auto &dir : dirs) - result += getFilesHere(dir, "*." + ext, location()); - result.removeDuplicates(); - m_includeFilesMap.insert(ext, result); - } - const QStringList &paths = (*m_includeFilesMap.find(ext)); - QString match = fileName; - if (!match.startsWith('/')) - match.prepend('/'); - for (const auto &path : paths) { - if (path.endsWith(match)) - return path; - } - return QString(); -} - -/*! - Builds and returns a list of file pathnames for the file - type specified by \a filesVar (e.g. "headers" or "sources"). - The files are found in the directories specified by - \a dirsVar, and they are filtered by \a defaultNameFilter - if a better filter can't be constructed from \a filesVar. - The directories in \a excludedDirs are avoided. The files - in \a excludedFiles are not included in the return list. - */ -QStringList Config::getAllFiles(const QString &filesVar, const QString &dirsVar, - const QSet<QString> &excludedDirs, - const QSet<QString> &excludedFiles) -{ - QStringList result = getCanonicalPathList(filesVar, Validate); - const QStringList dirs = getCanonicalPathList(dirsVar, Validate); - - const QString nameFilter = getString(filesVar + dot + CONFIG_FILEEXTENSIONS); - - for (const auto &dir : dirs) - result += getFilesHere(dir, nameFilter, location(), excludedDirs, excludedFiles); - return result; -} - -QStringList Config::getExampleQdocFiles(const QSet<QString> &excludedDirs, - const QSet<QString> &excludedFiles) -{ - QStringList result; - const QStringList dirs = getCanonicalPathList("exampledirs"); - const QString nameFilter = " *.qdoc"; - - for (const auto &dir : dirs) - result += getFilesHere(dir, nameFilter, location(), excludedDirs, excludedFiles); - return result; -} - -QStringList Config::getExampleImageFiles(const QSet<QString> &excludedDirs, - const QSet<QString> &excludedFiles) -{ - QStringList result; - const QStringList dirs = getCanonicalPathList("exampledirs"); - const QString nameFilter = getString(CONFIG_EXAMPLES + dot + CONFIG_IMAGEEXTENSIONS); - - for (const auto &dir : dirs) - result += getFilesHere(dir, nameFilter, location(), excludedDirs, excludedFiles); - return result; -} - -/*! - Returns the path to the project file for \a examplePath, or an empty string - if no project file was found. - */ -QString Config::getExampleProjectFile(const QString &examplePath) -{ - QFileInfo fileInfo(examplePath); - QStringList validNames; - validNames << fileInfo.fileName() + QLatin1String(".pro") - << fileInfo.fileName() + QLatin1String(".qmlproject") - << fileInfo.fileName() + QLatin1String(".pyproject") - << QLatin1String("CMakeLists.txt") - << QLatin1String("qbuild.pro"); // legacy - - QString projectFile; - - for (const auto &name : qAsConst(validNames)) { - projectFile = Config::findFile(Location(), m_exampleFiles, m_exampleDirs, - examplePath + QLatin1Char('/') + name); - if (!projectFile.isEmpty()) - return projectFile; - } - - return projectFile; -} - -/*! - \a fileName is the path of the file to find. - - \a files and \a dirs are the lists where we must find the - components of \a fileName. - - \a location is used for obtaining the file and line numbers - for report qdoc errors. - */ -QString Config::findFile(const Location &location, const QStringList &files, - const QStringList &dirs, const QString &fileName, - QString *userFriendlyFilePath) -{ - if (fileName.isEmpty() || fileName.startsWith(QLatin1Char('/'))) { - if (userFriendlyFilePath) - *userFriendlyFilePath = fileName; - return fileName; - } - - QFileInfo fileInfo; - QStringList components = fileName.split(QLatin1Char('?')); - QString firstComponent = components.first(); - - for (const auto &file : files) { - if (file == firstComponent || file.endsWith(QLatin1Char('/') + firstComponent)) { - fileInfo.setFile(file); - if (!fileInfo.exists()) - location.fatal(QStringLiteral("File '%1' does not exist").arg(file)); - break; - } - } - - if (fileInfo.fileName().isEmpty()) { - for (const auto &dir : dirs) { - fileInfo.setFile(QDir(dir), firstComponent); - if (fileInfo.exists()) - break; - } - } - - if (userFriendlyFilePath) - userFriendlyFilePath->clear(); - if (!fileInfo.exists()) - return QString(); - - if (userFriendlyFilePath) { - for (auto c = components.constBegin();;) { - bool isArchive = (c != components.constEnd() - 1); - userFriendlyFilePath->append(*c); - - if (isArchive) { - QString extracted = m_extractedDirs[fileInfo.filePath()]; - ++c; - fileInfo.setFile(QDir(extracted), *c); - } else { - break; - } - - userFriendlyFilePath->append(QLatin1Char('?')); - } - } - return fileInfo.filePath(); -} - -/*! - */ -QString Config::findFile(const Location &location, const QStringList &files, - const QStringList &dirs, const QString &fileBase, - const QStringList &fileExtensions, QString *userFriendlyFilePath) -{ - for (const auto &extension : fileExtensions) { - QString filePath = findFile(location, files, dirs, fileBase + QLatin1Char('.') + extension, - userFriendlyFilePath); - if (!filePath.isEmpty()) - return filePath; - } - return findFile(location, files, dirs, fileBase, userFriendlyFilePath); -} - -/*! - Copies the \a sourceFilePath to the file name constructed by - concatenating \a targetDirPath and the file name from the - \a userFriendlySourceFilePath. \a location is for identifying - the file and line number where a qdoc error occurred. The - constructed output file name is returned. - */ -QString Config::copyFile(const Location &location, const QString &sourceFilePath, - const QString &userFriendlySourceFilePath, const QString &targetDirPath) -{ - QFile inFile(sourceFilePath); - if (!inFile.open(QFile::ReadOnly)) { - location.warning(QStringLiteral("Cannot open input file for copy: '%1': %2") - .arg(sourceFilePath, inFile.errorString())); - return QString(); - } - - QString outFileName = userFriendlySourceFilePath; - qsizetype slash = outFileName.lastIndexOf(QLatin1Char('/')); - if (slash != -1) - outFileName = outFileName.mid(slash); - if ((outFileName.size()) > 0 && (outFileName[0] != '/')) - outFileName = targetDirPath + QLatin1Char('/') + outFileName; - else - outFileName = targetDirPath + outFileName; - QFile outFile(outFileName); - if (!outFile.open(QFile::WriteOnly)) { - location.warning(QStringLiteral("Cannot open output file for copy: '%1': %2") - .arg(outFileName, outFile.errorString())); - return QString(); - } - - char buffer[1024]; - qsizetype len; - while ((len = inFile.read(buffer, sizeof(buffer))) > 0) - outFile.write(buffer, len); - return outFileName; -} - -/*! - Finds the largest unicode digit in \a value in the range - 1..7 and returns it. - */ -int Config::numParams(const QString &value) -{ - int max = 0; - for (int i = 0; i != value.length(); ++i) { - uint c = value[i].unicode(); - if (c > 0 && c < 8) - max = qMax(max, static_cast<int>(c)); - } - return max; -} - -/*! - Returns \c true if \a ch is a letter, number, '_', '.', - '{', '}', or ','. - */ -bool Config::isMetaKeyChar(QChar ch) -{ - return ch.isLetterOrNumber() || ch == QLatin1Char('_') || ch == QLatin1Char('.') - || ch == QLatin1Char('{') || ch == QLatin1Char('}') || ch == QLatin1Char(','); -} - -/*! - \a fileName is a master qdocconf file. It contains a list of - qdocconf files and nothing else. Read the list and return it. - */ -QStringList Config::loadMaster(const QString &fileName) -{ - Location location; - QFile fin(fileName); - if (!fin.open(QFile::ReadOnly | QFile::Text)) { - if (!Config::installDir.isEmpty()) { - qsizetype prefix = location.filePath().length() - location.fileName().length(); - fin.setFileName(Config::installDir + QLatin1Char('/') - + fileName.right(fileName.length() - prefix)); - } - if (!fin.open(QFile::ReadOnly | QFile::Text)) - location.fatal(QStringLiteral("Cannot open master qdocconf file '%1': %2") - .arg(fileName, fin.errorString())); - } - QTextStream stream(&fin); - QStringList qdocFiles; - QDir configDir(QFileInfo(fileName).canonicalPath()); - QString line = stream.readLine(); - while (!line.isNull()) { - qdocFiles.append(QFileInfo(configDir, line).filePath()); - line = stream.readLine(); - } - fin.close(); - return qdocFiles; -} - -/*! - Load, parse, and process a qdoc configuration file. This - function is only called by the other load() function, but - this one is recursive, i.e., it calls itself when it sees - an \c{include} statement in the qdoc configuration file. - */ -void Config::load(Location location, const QString &fileName) -{ - QFileInfo fileInfo(fileName); - QString path = fileInfo.canonicalPath(); - pushWorkingDir(path); - QDir::setCurrent(path); - QRegularExpression keySyntax(QRegularExpression::anchoredPattern(QLatin1String("\\w+(?:\\.\\w+)*"))); - -#define SKIP_CHAR() \ - do { \ - location.advance(c); \ - ++i; \ - c = text.at(i); \ - cc = c.unicode(); \ - } while (0) - -#define SKIP_SPACES() \ - while (c.isSpace() && cc != '\n') \ - SKIP_CHAR() - -#define PUT_CHAR() \ - word += c; \ - SKIP_CHAR(); - - if (location.depth() > 16) - location.fatal(QStringLiteral("Too many nested includes")); - - QFile fin(fileInfo.fileName()); - if (!fin.open(QFile::ReadOnly | QFile::Text)) { - if (!Config::installDir.isEmpty()) { - qsizetype prefix = location.filePath().length() - location.fileName().length(); - fin.setFileName(Config::installDir + QLatin1Char('/') - + fileName.right(fileName.length() - prefix)); - } - if (!fin.open(QFile::ReadOnly | QFile::Text)) - location.fatal( - QStringLiteral("Cannot open file '%1': %2").arg(fileName, fin.errorString())); - } - - QTextStream stream(&fin); - QString text = stream.readAll(); - text += QLatin1String("\n\n"); - text += QLatin1Char('\0'); - fin.close(); - - location.push(fileName); - location.start(); - - int i = 0; - QChar c = text.at(0); - uint cc = c.unicode(); - while (i < text.length()) { - if (cc == 0) { - ++i; - } else if (c.isSpace()) { - SKIP_CHAR(); - } else if (cc == '#') { - do { - SKIP_CHAR(); - } while (cc != '\n'); - } else if (isMetaKeyChar(c)) { - Location keyLoc = location; - bool plus = false; - QStringList rhsValues; - QList<ExpandVar> expandVars; - QString word; - bool inQuote = false; - bool needsExpansion = false; - - MetaStack stack; - do { - stack.process(c, location); - SKIP_CHAR(); - } while (isMetaKeyChar(c)); - - const QStringList keys = stack.getExpanded(location); - SKIP_SPACES(); - - if (keys.count() == 1 && keys.first() == QLatin1String("include")) { - QString includeFile; - - if (cc != '(') - location.fatal(QStringLiteral("Bad include syntax")); - SKIP_CHAR(); - SKIP_SPACES(); - - while (!c.isSpace() && cc != '#' && cc != ')') { - - if (cc == '$') { - QString var; - SKIP_CHAR(); - while (c.isLetterOrNumber() || cc == '_') { - var += c; - SKIP_CHAR(); - } - if (!var.isEmpty()) { - const QByteArray val = qgetenv(var.toLatin1().data()); - if (val.isNull()) { - location.fatal(QStringLiteral("Environment variable '%1' undefined") - .arg(var)); - } else { - includeFile += QString::fromLatin1(val); - } - } - } else { - includeFile += c; - SKIP_CHAR(); - } - } - SKIP_SPACES(); - if (cc != ')') - location.fatal(QStringLiteral("Bad include syntax")); - SKIP_CHAR(); - SKIP_SPACES(); - if (cc != '#' && cc != '\n') - location.fatal(QStringLiteral("Trailing garbage")); - - /* - Here is the recursive call. - */ - load(location, QFileInfo(QDir(path), includeFile).filePath()); - } else { - /* - It wasn't an include statement, so it's something else. - We must see either '=' or '+=' next. If not, fatal error. - */ - if (cc == '+') { - plus = true; - SKIP_CHAR(); - } - if (cc != '=') - location.fatal(QStringLiteral("Expected '=' or '+=' after key")); - SKIP_CHAR(); - SKIP_SPACES(); - - for (;;) { - if (cc == '\\') { - qsizetype metaCharPos; - - SKIP_CHAR(); - if (cc == '\n') { - SKIP_CHAR(); - } else if (cc > '0' && cc < '8') { - word += QChar(c.digitValue()); - SKIP_CHAR(); - } else if ((metaCharPos = QString::fromLatin1("abfnrtv").indexOf(c)) - != -1) { - word += QLatin1Char("\a\b\f\n\r\t\v"[metaCharPos]); - SKIP_CHAR(); - } else { - PUT_CHAR(); - } - } else if (c.isSpace() || cc == '#') { - if (inQuote) { - if (cc == '\n') - location.fatal(QStringLiteral("Unterminated string")); - PUT_CHAR(); - } else { - if (!word.isEmpty() || needsExpansion) { - rhsValues << word; - word.clear(); - needsExpansion = false; - } - if (cc == '\n' || cc == '#') - break; - SKIP_SPACES(); - } - } else if (cc == '"') { - if (inQuote) { - if (!word.isEmpty() || needsExpansion) - rhsValues << word; - word.clear(); - needsExpansion = false; - } - inQuote = !inQuote; - SKIP_CHAR(); - } else if (cc == '$') { - QString var; - QChar delim(' '); - bool braces = false; - SKIP_CHAR(); - if (cc == '{') { - SKIP_CHAR(); - braces = true; - } - while (c.isLetterOrNumber() || cc == '_') { - var += c; - SKIP_CHAR(); - } - if (braces) { - if (cc == ',') { - SKIP_CHAR(); - delim = c; - SKIP_CHAR(); - } - if (cc == '}') - SKIP_CHAR(); - else if (delim == '}') - delim = QChar(); // null delimiter - else - location.fatal(QStringLiteral("Missing '}'")); - } - if (!var.isEmpty()) { - const QByteArray val = qgetenv(var.toLatin1().constData()); - if (val.isNull()) { - expandVars << ExpandVar(rhsValues.size(), word.size(), var, delim); - needsExpansion = true; - } else if (braces) { // ${VAR} inserts content from an env. variable for processing - text.insert(i, QString::fromLatin1(val)); - c = text.at(i); - cc = c.unicode(); - } else { // while $VAR simply reads the value and stores it to a config variable. - word += QString::fromLatin1(val); - } - } - } else { - if (!inQuote && cc == '=') - location.fatal(QStringLiteral("Unexpected '='")); - PUT_CHAR(); - } - } - for (const auto &key : keys) { - if (!keySyntax.match(key).hasMatch()) - keyLoc.fatal(QStringLiteral("Invalid key '%1'").arg(key)); - - ConfigVar configVar(key, rhsValues, QDir::currentPath(), keyLoc, expandVars); - if (plus && m_configVars.contains(key)) { - m_configVars[key].append(configVar); - } else { - m_configVars.insert(key, configVar); - } - } - } - } else { - location.fatal(QStringLiteral("Unexpected character '%1' at beginning of line").arg(c)); - } - } - popWorkingDir(); - if (!m_workingDirs.isEmpty()) - QDir::setCurrent(m_workingDirs.top()); -} - -bool Config::isFileExcluded(const QString &fileName, const QSet<QString> &excludedFiles) -{ - for (const QString &entry : excludedFiles) { - if (entry.contains(QLatin1Char('*')) || entry.contains(QLatin1Char('?'))) { - QRegularExpression re(QRegularExpression::wildcardToRegularExpression(entry)); - if (re.match(fileName).hasMatch()) - return true; - } - } - return excludedFiles.contains(fileName); -} - -QStringList Config::getFilesHere(const QString &uncleanDir, const QString &nameFilter, - const Location &location, const QSet<QString> &excludedDirs, - const QSet<QString> &excludedFiles) -{ - QString dir = - location.isEmpty() ? QDir::cleanPath(uncleanDir) : QDir(uncleanDir).canonicalPath(); - QStringList result; - if (excludedDirs.contains(dir)) - return result; - - QDir dirInfo(dir); - - dirInfo.setNameFilters(nameFilter.split(QLatin1Char(' '))); - dirInfo.setSorting(QDir::Name); - dirInfo.setFilter(QDir::Files); - QStringList fileNames = dirInfo.entryList(); - for (const auto &file : qAsConst(fileNames)) { - if (!file.startsWith(QLatin1Char('~'))) { - QString s = dirInfo.filePath(file); - QString c = QDir::cleanPath(s); - if (!isFileExcluded(c, excludedFiles)) - result.append(c); - } - } - - dirInfo.setNameFilters(QStringList(QLatin1String("*"))); - dirInfo.setFilter(QDir::Dirs | QDir::NoDotAndDotDot); - fileNames = dirInfo.entryList(); - for (const auto &file : fileNames) - result += getFilesHere(dirInfo.filePath(file), nameFilter, location, excludedDirs, - excludedFiles); - return result; -} - -/*! - Push \a dir onto the stack of working directories. - */ -void Config::pushWorkingDir(const QString &dir) -{ - m_workingDirs.push(dir); -} - -/*! - If the stack of working directories is not empty, pop the - top entry and return it. Otherwise return an empty string. - */ -QString Config::popWorkingDir() -{ - if (!m_workingDirs.isEmpty()) - return m_workingDirs.pop(); - - qDebug() << "RETURNED EMPTY WORKING DIR"; - return QString(); -} - -QT_END_NAMESPACE |