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
utilities.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 "utilities.h"
5
6#include "inode.h"
7#include "location.h"
8
9#include <QtCore/qcryptographichash.h>
10#include <QtCore/qfileinfo.h>
11#include <QtCore/qprocess.h>
12
14
15Q_LOGGING_CATEGORY(lcQdoc, "qt.qdoc")
16Q_LOGGING_CATEGORY(lcQdocClang, "qt.qdoc.clang")
17
18/*!
19 \namespace Utilities
20 \internal
21 \brief This namespace holds QDoc-internal utility methods.
22 */
23namespace Utilities {
24static inline void setDebugEnabled(bool value)
25{
26 const_cast<QLoggingCategory &>(lcQdoc()).setEnabled(QtDebugMsg, value);
27 const_cast<QLoggingCategory &>(lcQdocClang()).setEnabled(QtDebugMsg, value);
28}
29
30void startDebugging(const QString &message)
31{
33 qCDebug(lcQdoc, "START DEBUGGING: %ls", qUtf16Printable(message));
34}
35
36void stopDebugging(const QString &message)
37{
38 qCDebug(lcQdoc, "STOP DEBUGGING: %ls", qUtf16Printable(message));
40}
41
43{
44 return lcQdoc().isEnabled(QtDebugMsg);
45}
46
47/*!
48 \brief Converts a string representation of a pointer address to an INode pointer.
49
50 This function takes a \a string, assumed to contain the numerical
51 representation of an INode pointer's address (as generated by
52 stringForNode()), and casts it back to an \c INode pointer.
53
54 \sa stringForNode()
55 */
56const INode *nodeForString(const QString &string)
57{
58 return reinterpret_cast<const INode *>(string.toULongLong());
59}
60/*!
61 \brief Converts an INode pointer address to its string representation.
62
63 This function takes a \a node pointer and returns a string that contains the
64 numerical value of its memory address. This is used for serialization or
65 passing node references where a direct pointer cannot be used.
66
67 \note The returned string is only valid within the same process. It does
68 \e {not} persist across runs.
69 */
71{
72 return QString::number(reinterpret_cast<quintptr>(node));
73}
74
75/*!
76 Returns a unique identifier based on location \a loc, with a
77 given \a prefix.
78*/
79QString uniqueIdentifier(const Location &loc, const QString &prefix)
80{
81 Q_ASSERT(!loc.filePath().isEmpty());
82 QFileInfo fi{loc.filePath()};
83 const auto id = QLatin1String("%1_%2_%3").arg(prefix, fi.fileName(), QString::number(loc.lineNo()));
84 return asAsciiPrintable(id);
85}
86
87
88/*!
89 \internal
90 Convenience method that's used to get the correct punctuation character for
91 the words at \a wordPosition in a list of \a numberOfWords length.
92 For the last position in the list, returns "." (full stop). For any other
93 word, this method calls comma().
94
95 \sa comma()
96 */
97QString separator(qsizetype wordPosition, qsizetype numberOfWords)
98{
99 static QString terminator = QStringLiteral(".");
100 if (wordPosition == numberOfWords - 1)
101 return terminator;
102 else
103 return comma(wordPosition, numberOfWords);
104}
105
106/*!
107 \internal
108 Convenience method that's used to get the correct punctuation character for
109 the words at \a wordPosition in a list of \a numberOfWords length.
110
111 For a list of length one, returns an empty QString. For a list of length
112 two, returns the string " and ". For any length beyond two, returns the
113 string ", " until the last element, which returns ", and ".
114
115 \sa comma()
116 */
117QString comma(qsizetype wordPosition, qsizetype numberOfWords)
118{
119 if (wordPosition == numberOfWords - 1)
120 return QString();
121 if (numberOfWords == 2)
122 return QStringLiteral(" and ");
123 if (wordPosition == 0 || wordPosition < numberOfWords - 2)
124 return QStringLiteral(", ");
125 return QStringLiteral(", and ");
126}
127
128/*!
129 \brief Returns an ascii-printable representation of \a str.
130
131 Replace non-ascii-printable characters in \a str from a subset of such
132 characters. The subset includes alphanumeric (alnum) characters
133 ([a-zA-Z0-9]), space, punctuation characters, and common symbols. Non-alnum
134 characters in this subset are replaced by a single hyphen. Leading,
135 trailing, and consecutive hyphens are removed, such that the resulting
136 string does not start or end with a hyphen. All characters are converted to
137 lowercase.
138
139 If any character in \a str is non-latin, or latin and not found in the
140 aforementioned subset (e.g. 'ß', 'å', or 'ö'), a hash of \a str is appended
141 to the final string.
142
143 Returns a string that is normalized for use where ascii-printable strings
144 are required, such as file names or fragment identifiers in URLs.
145
146 The implementation is equivalent to:
147
148 \code
149 name.replace(QRegularExpression("[^A-Za-z0-9]+"), " ");
150 name = name.simplified();
151 name.replace(QLatin1Char(' '), QLatin1Char('-'));
152 name = name.toLower();
153 \endcode
154
155 However, it has been measured to be approximately four times faster.
156*/
157QString asAsciiPrintable(const QString &str)
158{
159 auto legal_ascii = [](const uint value) {
160 const uint start_ascii_subset{ 32 };
161 const uint end_ascii_subset{ 126 };
162
163 return value >= start_ascii_subset && value <= end_ascii_subset;
164 };
165
166 QString result;
167 bool begun = false;
168 bool has_non_alnum_content{ false };
169
170 for (const auto &c : str) {
171 char16_t u = c.unicode();
172 if (!legal_ascii(u))
173 has_non_alnum_content = true;
174 if (u >= 'A' && u <= 'Z')
175 u += 'a' - 'A';
176 if ((u >= 'a' && u <= 'z') || (u >= '0' && u <= '9')) {
177 result += QLatin1Char(u);
178 begun = true;
179 } else if (begun) {
180 result += QLatin1Char('-');
181 begun = false;
182 }
183 }
184 if (result.endsWith(QLatin1Char('-')))
185 result.chop(1);
186
187 if (has_non_alnum_content) {
188 auto title_hash = QString::fromLocal8Bit(
189 QCryptographicHash::hash(str.toUtf8(), QCryptographicHash::Md5).toHex());
190 title_hash.truncate(8);
191 if (!result.isEmpty())
192 result.append(QLatin1Char('-'));
193 result.append(title_hash);
194 }
195
196 return result;
197}
198
199QString protect(const QString &str)
200{
201 qsizetype n = str.size();
202 QString marked;
203 marked.reserve(n * 2 + 30);
204 const QChar *data = str.constData();
205 for (int i = 0; i != n; ++i) {
206 switch (data[i].unicode()) {
207 case '&':
208 marked += samp;
209 break;
210 case '<':
211 marked += slt;
212 break;
213 case '>':
214 marked += sgt;
215 break;
216 case '"':
217 marked += squot;
218 break;
219 default:
220 marked += data[i];
221 }
222 }
223 return marked;
224}
225
226/*!
227 \internal
228*/
229static bool runProcess(const QString &program, const QStringList &arguments,
230 QByteArray *stdOutIn, QByteArray *stdErrIn)
231{
232 QProcess process;
233 process.start(program, arguments, QProcess::ReadWrite);
234 if (!process.waitForStarted()) {
235 qCDebug(lcQdoc).nospace() << "Unable to start " << process.program()
236 << ": " << process.errorString();
237 return false;
238 }
239 process.closeWriteChannel();
240 const bool finished = process.waitForFinished();
241 const QByteArray stdErr = process.readAllStandardError();
242 if (stdErrIn)
243 *stdErrIn = stdErr;
244 if (stdOutIn)
245 *stdOutIn = process.readAllStandardOutput();
246
247 if (!finished) {
248 qCDebug(lcQdoc).nospace() << process.program() << " timed out: " << stdErr;
249 process.kill();
250 return false;
251 }
252
253 if (process.exitStatus() != QProcess::NormalExit) {
254 qCDebug(lcQdoc).nospace() << process.program() << " crashed: " << stdErr;
255 return false;
256 }
257
258 if (process.exitCode() != 0) {
259 qCDebug(lcQdoc).nospace() << process.program() << " exited with "
260 << process.exitCode() << ": " << stdErr;
261 return false;
262 }
263
264 return true;
265}
266
267/*!
268 \internal
269*/
271 return QByteArrayLiteral(" (framework directory)");
272}
273
274/*!
275 \internal
276 Determine the compiler's internal include paths from the output of
277
278 \badcode
279 [clang++|g++] -E -x c++ - -v </dev/null
280 \endcode
281
282 Output looks like:
283
284 \badcode
285 #include <...> search starts here:
286 /usr/local/include
287 /System/Library/Frameworks (framework directory)
288 End of search list.
289 \endcode
290*/
291QStringList getInternalIncludePaths(const QString &compiler)
292{
293 QStringList result;
294 QStringList arguments;
295 arguments << QStringLiteral("-E") << QStringLiteral("-x") << QStringLiteral("c++")
296 << QStringLiteral("-") << QStringLiteral("-v");
297 QByteArray stdOut;
298 QByteArray stdErr;
299 if (!runProcess(compiler, arguments, &stdOut, &stdErr))
300 return result;
301 const QByteArrayList stdErrLines = stdErr.split('\n');
302 bool isIncludeDir = false;
303 for (const QByteArray &line : stdErrLines) {
304 if (isIncludeDir) {
305 if (line.startsWith(QByteArrayLiteral("End of search list"))) {
306 isIncludeDir = false;
307 } else {
308 QByteArray prefix("-I");
309 QByteArray headerPath{line.trimmed()};
310 if (headerPath.endsWith(frameworkSuffix())) {
311 headerPath.truncate(headerPath.size() - frameworkSuffix().size());
312 prefix = QByteArrayLiteral("-F");
313 }
314 result.append(QString::fromLocal8Bit(prefix + headerPath));
315 }
316 } else if (line.startsWith(QByteArrayLiteral("#include <...> search starts here"))) {
317 isIncludeDir = true;
318 }
319 }
320
321 return result;
322}
323
324bool isGeneratedFile(const QString &path)
325{
326 QString fileName = QFileInfo(path).fileName();
327 return fileName.startsWith("moc_") ||
328 fileName.startsWith("qrc_") ||
329 fileName.startsWith("ui_");
330}
331
332} // namespace Utilities
333
334QT_END_NAMESPACE
Definition inode.h:20
The Location class provides a way to mark a location in a file.
Definition location.h:20
This namespace holds QDoc-internal utility methods.
Definition utilities.h:20
QString uniqueIdentifier(const Location &loc, const QString &prefix)
Returns a unique identifier based on location loc, with a given prefix.
Definition utilities.cpp:79
static void setDebugEnabled(bool value)
Definition utilities.cpp:24
QStringList getInternalIncludePaths(const QString &compiler)
bool debugging()
Definition utilities.cpp:42
bool isGeneratedFile(const QString &path)
QString asAsciiPrintable(const QString &name)
Returns an ascii-printable representation of str.
QString protect(const QString &string)
const INode * nodeForString(const QString &string)
Converts a string representation of a pointer address to an INode pointer.
Definition utilities.cpp:56
void stopDebugging(const QString &message)
Definition utilities.cpp:36
static QByteArray frameworkSuffix()
QString comma(qsizetype wordPosition, qsizetype numberOfWords)
QString separator(qsizetype wordPosition, qsizetype numberOfWords)
Definition utilities.cpp:97
void startDebugging(const QString &message)
Definition utilities.cpp:30
static bool runProcess(const QString &program, const QStringList &arguments, QByteArray *stdOutIn, QByteArray *stdErrIn)
QString stringForNode(const INode *node)
Converts an INode pointer address to its string representation.
Definition utilities.cpp:70