1 | // Copyright (C) 2016 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 "qresourcerelocater_p.h" |
5 | |
6 | #include <QXmlStreamReader> |
7 | #include <QFile> |
8 | #include <QDir> |
9 | |
10 | QT_BEGIN_NAMESPACE |
11 | |
12 | /*! |
13 | \internal |
14 | Changes all the paths in resource file \a input so that they are relative to |
15 | location \a output and writes the result to resource file \a output. |
16 | */ |
17 | int qRelocateResourceFile(const QString &input, const QString &output) |
18 | { |
19 | enum State { |
20 | InitialState, |
21 | InRCC, |
22 | InResource, |
23 | InFile |
24 | }; |
25 | State state = InitialState; |
26 | |
27 | QString prefix; |
28 | QString currentFileName; |
29 | QXmlStreamAttributes fileAttributes; |
30 | |
31 | QFile file(input); |
32 | if (!file.open(flags: QIODevice::ReadOnly)) { |
33 | fprintf(stderr, format: "Cannot open %s for reading.\n" , qPrintable(input)); |
34 | return EXIT_FAILURE; |
35 | } |
36 | |
37 | QDir inputDirectory = QFileInfo(file).absoluteDir(); |
38 | QDir outputDirectory = QFileInfo(output).absoluteDir(); |
39 | |
40 | QString outputString; |
41 | QXmlStreamWriter writer(&outputString); |
42 | writer.setAutoFormatting(true); |
43 | |
44 | QXmlStreamReader reader(&file); |
45 | while (!reader.atEnd()) { |
46 | switch (reader.readNext()) { |
47 | case QXmlStreamReader::StartDocument: { |
48 | QStringView version = reader.documentVersion(); |
49 | if (!version.isEmpty()) |
50 | writer.writeStartDocument(version: version.toString()); |
51 | else |
52 | writer.writeStartDocument(); |
53 | break; |
54 | } |
55 | case QXmlStreamReader::EndDocument: |
56 | writer.writeEndDocument(); |
57 | break; |
58 | case QXmlStreamReader::StartElement: |
59 | if (reader.name() == QStringLiteral("RCC" )) { |
60 | if (state != InitialState) { |
61 | fprintf(stderr, format: "Unexpected RCC tag in line %d\n" , int(reader.lineNumber())); |
62 | return EXIT_FAILURE; |
63 | } |
64 | state = InRCC; |
65 | } else if (reader.name() == QStringLiteral("qresource" )) { |
66 | if (state != InRCC) { |
67 | fprintf(stderr, format: "Unexpected qresource tag in line %d\n" , int(reader.lineNumber())); |
68 | return EXIT_FAILURE; |
69 | } |
70 | state = InResource; |
71 | QXmlStreamAttributes attributes = reader.attributes(); |
72 | if (attributes.hasAttribute(QStringLiteral("prefix" ))) |
73 | prefix = attributes.value(QStringLiteral("prefix" )).toString(); |
74 | if (!prefix.startsWith(c: QLatin1Char('/'))) |
75 | prefix.prepend(c: QLatin1Char('/')); |
76 | if (!prefix.endsWith(c: QLatin1Char('/'))) |
77 | prefix.append(c: QLatin1Char('/')); |
78 | } else if (reader.name() == QStringLiteral("file" )) { |
79 | if (state != InResource) { |
80 | fprintf(stderr, format: "Unexpected file tag in line %d\n" , int(reader.lineNumber())); |
81 | return EXIT_FAILURE; |
82 | } |
83 | state = InFile; |
84 | fileAttributes = reader.attributes(); |
85 | continue; |
86 | } |
87 | writer.writeStartElement(qualifiedName: reader.name().toString()); |
88 | writer.writeAttributes(attributes: reader.attributes()); |
89 | continue; |
90 | |
91 | case QXmlStreamReader::EndElement: |
92 | if (reader.name() == QStringLiteral("file" )) { |
93 | if (state != InFile) { |
94 | fprintf(stderr, format: "Unexpected end of file tag in line %d\n" , int(reader.lineNumber())); |
95 | return EXIT_FAILURE; |
96 | } |
97 | state = InResource; |
98 | continue; |
99 | } else if (reader.name() == QStringLiteral("qresource" )) { |
100 | if (state != InResource) { |
101 | fprintf(stderr, format: "Unexpected end of qresource tag in line %d\n" , int(reader.lineNumber())); |
102 | return EXIT_FAILURE; |
103 | } |
104 | state = InRCC; |
105 | } else if (reader.name() == QStringLiteral("RCC" )) { |
106 | if (state != InRCC) { |
107 | fprintf(stderr, format: "Unexpected end of RCC tag in line %d\n" , int(reader.lineNumber())); |
108 | return EXIT_FAILURE; |
109 | } |
110 | state = InitialState; |
111 | } |
112 | writer.writeEndElement(); |
113 | continue; |
114 | |
115 | case QXmlStreamReader::Characters: |
116 | if (reader.isWhitespace()) |
117 | break; |
118 | if (state != InFile) |
119 | return EXIT_FAILURE; |
120 | currentFileName = reader.text().toString(); |
121 | if (currentFileName.isEmpty()) |
122 | continue; |
123 | |
124 | writer.writeStartElement(QStringLiteral("file" )); |
125 | |
126 | if (!fileAttributes.hasAttribute(QStringLiteral("alias" ))) |
127 | fileAttributes.append(QStringLiteral("alias" ), value: currentFileName); |
128 | |
129 | currentFileName = inputDirectory.absoluteFilePath(fileName: currentFileName); |
130 | currentFileName = outputDirectory.relativeFilePath(fileName: currentFileName); |
131 | |
132 | writer.writeAttributes(attributes: fileAttributes); |
133 | writer.writeCharacters(text: currentFileName); |
134 | writer.writeEndElement(); |
135 | continue; |
136 | |
137 | default: break; |
138 | } |
139 | } |
140 | |
141 | QFile outputFile(output); |
142 | if (!outputFile.open(flags: QIODevice::WriteOnly | QIODevice::Truncate)) { |
143 | fprintf(stderr, format: "Cannot open %s for writing.\n" , qPrintable(output)); |
144 | return EXIT_FAILURE; |
145 | } |
146 | const QByteArray outputStringUtf8 = outputString.toUtf8(); |
147 | if (outputFile.write(data: outputStringUtf8) != outputStringUtf8.size()) |
148 | return EXIT_FAILURE; |
149 | |
150 | outputFile.close(); |
151 | if (outputFile.error() != QFileDevice::NoError) |
152 | return EXIT_FAILURE; |
153 | |
154 | |
155 | return EXIT_SUCCESS; |
156 | } |
157 | |
158 | QT_END_NAMESPACE |
159 | |