aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorUlf Hermann <[email protected]>2024-12-10 13:31:44 +0100
committerUlf Hermann <[email protected]>2024-12-20 09:44:16 +0100
commitbac4723a30988c6b5b0430b8a394bbcf0b604db1 (patch)
tree6c99a25bcf940172a25635c4758dd522eb916666
parent33c13102bf13d9e561266328b0b3246f2164791d (diff)
QtQml: Allow basic name quoting in qmldir files
This adds support for quoted file names and for escaping quotes and backslashes with '\'. It breaks support for file names that genuinely start with a quote. [ChangeLog][QtQml] You can now quote file names in qmldir files using '"'. This is so that you can write file names with spaces in them. '\' functions as an escape character, allowing you to escape quotes that are part of file names and backslash itself. Task-number: QTBUG-131916 Change-Id: Ia89e60153d3fafa273034f81da37166f30284c76 Reviewed-by: Fabian Kosmale <[email protected]>
-rw-r--r--src/qml/qmldirparser/qqmldirparser.cpp66
-rw-r--r--src/qml/qmldirparser/qqmldirparser_p.h1
-rw-r--r--tests/auto/qml/qqmldirparser/data/ends-in-quoted/qmldir11
-rw-r--r--tests/auto/qml/qqmldirparser/data/escaped-end-of-file/qmldir11
-rw-r--r--tests/auto/qml/qqmldirparser/data/invalid-escaped/qmldir11
-rw-r--r--tests/auto/qml/qqmldirparser/data/line-break-in-quoted/qmldir12
-rw-r--r--tests/auto/qml/qqmldirparser/data/quoted/qmldir11
-rw-r--r--tests/auto/qml/qqmldirparser/tst_qqmldirparser.cpp70
8 files changed, 186 insertions, 7 deletions
diff --git a/src/qml/qmldirparser/qqmldirparser.cpp b/src/qml/qmldirparser/qqmldirparser.cpp
index e6a5691d3d..6ac3cb997a 100644
--- a/src/qml/qmldirparser/qqmldirparser.cpp
+++ b/src/qml/qmldirparser/qqmldirparser.cpp
@@ -62,9 +62,59 @@ inline static void scanToEnd(const QChar *&ch) {
++ch;
}
-inline static void scanWord(const QChar *&ch) {
+inline static QString scanWord(const QChar *&ch) {
+ const QChar *begin = ch;
while (!ch->isSpace() && !ch->isNull())
++ch;
+ return QString(begin, ch - begin);
+}
+
+QString QQmlDirParser::scanQuotedWord(const QChar *&ch, quint16 lineNumber, quint16 columnNumber)
+{
+ Q_ASSERT(*ch == QLatin1Char('"'));
+ ++ch;
+
+ QString result;
+
+ const QChar *begin = ch;
+ while (*ch != QLatin1Char('"')) {
+ if (ch->isNull()) {
+ reportError(lineNumber, columnNumber,
+ QStringLiteral("file ends inside a quoted string"));
+ result.append(begin, ch - begin);
+ return result;
+ }
+
+ if (*ch == QLatin1Char('\n') || *ch == QLatin1Char('\r')) {
+ reportError(lineNumber, columnNumber,
+ QStringLiteral("line breaks in quoted strings are not supported as they "
+ "are not portable between different operating systems"));
+ result.append(begin, ch - begin);
+ return result;
+ }
+
+ if (*ch == QLatin1Char('\\')) {
+ result.append(begin, ch - begin);
+ ++ch;
+ ++columnNumber;
+ if (*ch != QLatin1Char('"') && *ch != QLatin1Char('\\')) {
+ reportError(lineNumber, columnNumber,
+ QStringLiteral("only '\"' and '\\' can be escaped"));
+ return result;
+ }
+ begin = ch;
+ }
+
+ ++ch;
+ ++columnNumber;
+ }
+
+ result.append(begin, ch - begin);
+
+ Q_ASSERT(*ch == QLatin1Char('"'));
+ ++ch;
+
+ return result;
}
/*!
@@ -142,16 +192,18 @@ bool QQmlDirParser::parse(const QString &source)
scanToEnd(ch);
break;
}
- const QChar *start = ch;
- scanWord(ch);
- if (sectionCount < 4) {
- sections[sectionCount++] = source.mid(start-source.constData(), ch-start);
- } else {
- reportError(lineNumber, start-lineStart, QLatin1String("unexpected token"));
+
+ if (sectionCount >= 4) {
+ reportError(lineNumber, ch - lineStart, QLatin1String("unexpected token"));
scanToEnd(ch);
invalidLine = true;
break;
}
+
+ sections[sectionCount++] = (*ch == QLatin1Char('"'))
+ ? scanQuotedWord(ch, lineNumber, ch - lineStart)
+ : scanWord(ch);
+
scanSpace(ch);
} while (*ch != QLatin1Char('\n') && !ch->isNull());
diff --git a/src/qml/qmldirparser/qqmldirparser_p.h b/src/qml/qmldirparser/qqmldirparser_p.h
index deef8f2dcf..b36f0894f8 100644
--- a/src/qml/qmldirparser/qqmldirparser_p.h
+++ b/src/qml/qmldirparser/qqmldirparser_p.h
@@ -140,6 +140,7 @@ public:
private:
bool maybeAddComponent(const QString &typeName, const QString &fileName, const QString &version, QHash<QString,Component> &hash, int lineNumber = -1, bool multi = true);
void reportError(quint16 line, quint16 column, const QString &message);
+ QString scanQuotedWord(const QChar *&ch, quint16 lineNumber, quint16 columnNumber);
private:
QList<QQmlJS::DiagnosticMessage> _errors;
diff --git a/tests/auto/qml/qqmldirparser/data/ends-in-quoted/qmldir b/tests/auto/qml/qqmldirparser/data/ends-in-quoted/qmldir
new file mode 100644
index 0000000000..ef00260a1f
--- /dev/null
+++ b/tests/auto/qml/qqmldirparser/data/ends-in-quoted/qmldir
@@ -0,0 +1,11 @@
+# "Comment"
+
+module ModuleNamespace
+plugin PluginA "plugina.so" # More "comment"
+
+ComponentA 1.0 "componenta\"1_0.qml"
+ScriptA 1.0 "scripta 1_0.js"
+
+# "
+ComponentA 1.5 "componenta\\1_5.qml"
+ComponentB 1.5 "componentb 1_5.qml \ No newline at end of file
diff --git a/tests/auto/qml/qqmldirparser/data/escaped-end-of-file/qmldir b/tests/auto/qml/qqmldirparser/data/escaped-end-of-file/qmldir
new file mode 100644
index 0000000000..8d621d0c3f
--- /dev/null
+++ b/tests/auto/qml/qqmldirparser/data/escaped-end-of-file/qmldir
@@ -0,0 +1,11 @@
+# "Comment"
+
+module ModuleNamespace
+plugin PluginA "plugina.so" # More "comment"
+
+ComponentA 1.0 "componenta\"1_0.qml"
+ScriptA 1.0 "scripta 1_0.js"
+
+# "
+ComponentA 1.5 "componenta\\1_5.qml"
+ComponentB 1.5 "componentb 1_5.qml\ \ No newline at end of file
diff --git a/tests/auto/qml/qqmldirparser/data/invalid-escaped/qmldir b/tests/auto/qml/qqmldirparser/data/invalid-escaped/qmldir
new file mode 100644
index 0000000000..c6f43b6d94
--- /dev/null
+++ b/tests/auto/qml/qqmldirparser/data/invalid-escaped/qmldir
@@ -0,0 +1,11 @@
+# "Comment"
+
+module ModuleNamespace
+plugin PluginA "plugin\a.so" # More "comment"
+
+ComponentA 1.0 "componenta\"1_0.qml"
+ScriptA 1.0 "scripta 1_0.js"
+
+# "
+ComponentA 1.5 "componenta\\1_5.qml"
+ComponentB 1.5 "componentb 1_5.qml"
diff --git a/tests/auto/qml/qqmldirparser/data/line-break-in-quoted/qmldir b/tests/auto/qml/qqmldirparser/data/line-break-in-quoted/qmldir
new file mode 100644
index 0000000000..90a3403e72
--- /dev/null
+++ b/tests/auto/qml/qqmldirparser/data/line-break-in-quoted/qmldir
@@ -0,0 +1,12 @@
+# "Comment"
+
+module ModuleNamespace
+plugin PluginA "plugin
+a.so" # More "comment"
+
+ComponentA 1.0 "componenta\"1_0.qml"
+ScriptA 1.0 "scripta 1_0.js"
+
+# "
+ComponentA 1.5 "componenta\\1_5.qml"
+ComponentB 1.5 "componentb 1_5.qml"
diff --git a/tests/auto/qml/qqmldirparser/data/quoted/qmldir b/tests/auto/qml/qqmldirparser/data/quoted/qmldir
new file mode 100644
index 0000000000..9a49f84c76
--- /dev/null
+++ b/tests/auto/qml/qqmldirparser/data/quoted/qmldir
@@ -0,0 +1,11 @@
+# "Comment"
+
+module ModuleNamespace
+plugin PluginA "plugina.so" # More "comment"
+
+ComponentA 1.0 "componenta\"1_0.qml"
+ScriptA 1.0 "scripta 1_0.js"
+
+# "
+ComponentA 1.5 "componenta\\1_5.qml"
+ComponentB 1.5 "componentb 1_5.qml"
diff --git a/tests/auto/qml/qqmldirparser/tst_qqmldirparser.cpp b/tests/auto/qml/qqmldirparser/tst_qqmldirparser.cpp
index 4cbb7aaf47..ea4d8dfef2 100644
--- a/tests/auto/qml/qqmldirparser/tst_qqmldirparser.cpp
+++ b/tests/auto/qml/qqmldirparser/tst_qqmldirparser.cpp
@@ -335,6 +335,76 @@ void tst_qqmldirparser::parse_data()
<< QStringList()
<< false;
+ QTest::newRow("quoted")
+ << "quoted/qmldir"
+ << QString()
+ << QStringList()
+ << (QStringList() << "PluginA|plugina.so")
+ << QStringList()
+ << (QStringList() << "ComponentA|componenta\"1_0.qml|1|0|false"
+ << "ComponentA|componenta\\1_5.qml|1|5|false"
+ << "ComponentB|componentb 1_5.qml|1|5|false")
+ << (QStringList() << "ScriptA|scripta 1_0.js|1|0")
+ << QStringList()
+ << false;
+
+ QTest::newRow("ends-in-quoted")
+ << "ends-in-quoted/qmldir"
+ << QString()
+ << (QStringList() << "qmldir:11:33: file ends inside a quoted string")
+ << (QStringList() << "PluginA|plugina.so")
+ << QStringList()
+ << (QStringList() << "ComponentA|componenta\"1_0.qml|1|0|false"
+ << "ComponentA|componenta\\1_5.qml|1|5|false"
+ << "ComponentB|componentb 1_5.qml|1|5|false")
+ << (QStringList() << "ScriptA|scripta 1_0.js|1|0")
+ << QStringList()
+ << false;
+
+ QTest::newRow("invalid-escaped")
+ << "invalid-escaped/qmldir"
+ << QString()
+ << (QStringList()
+ << "qmldir:4:22: only '\"' and '\\' can be escaped"
+ << "qmldir:4: plugin directive requires one or two arguments, but 3 were provided")
+ << QStringList()
+ << QStringList()
+ << (QStringList() << "ComponentA|componenta\"1_0.qml|1|0|false"
+ << "ComponentA|componenta\\1_5.qml|1|5|false"
+ << "ComponentB|componentb 1_5.qml|1|5|false")
+ << (QStringList() << "ScriptA|scripta 1_0.js|1|0")
+ << QStringList()
+ << false;
+
+ QTest::newRow("escaped-end-of-file")
+ << "escaped-end-of-file/qmldir"
+ << QString()
+ << (QStringList() << "qmldir:11:34: only '\"' and '\\' can be escaped")
+ << (QStringList() << "PluginA|plugina.so")
+ << QStringList()
+ << (QStringList() << "ComponentA|componenta\"1_0.qml|1|0|false"
+ << "ComponentA|componenta\\1_5.qml|1|5|false"
+ << "ComponentB|componentb 1_5.qml|1|5|false")
+ << (QStringList() << "ScriptA|scripta 1_0.js|1|0")
+ << QStringList()
+ << false;
+
+ QTest::newRow("line-break-in-quoted")
+ << "line-break-in-quoted/qmldir"
+ << QString()
+ << (QStringList()
+ << "qmldir:4:21: line breaks in quoted strings are not supported as they are not "
+ "portable between different operating systems"
+ << "qmldir:5: a component declaration requires two or three arguments, but 1 were provided")
+ << (QStringList() << "PluginA|plugin")
+ << QStringList()
+ << (QStringList() << "ComponentA|componenta\"1_0.qml|1|0|false"
+ << "ComponentA|componenta\\1_5.qml|1|5|false"
+ << "ComponentB|componentb 1_5.qml|1|5|false")
+ << (QStringList() << "ScriptA|scripta 1_0.js|1|0")
+ << QStringList()
+ << false;
+
QTest::newRow("designersupported-yes")
<< "designersupported-yes/qmldir"
<< QString()