Skip to content

Commit f3219ee

Browse files
qt: avoid modifying QString during regex iteration in GUIUtil::loadStyleSheet
Collect regex matches first, compute replacements using capturedStart/capturedEnd, then apply in reverse order to prevent iterator invalidation and QString corruption when processing <os=...></os> sections. Also include <algorithm> for std::sort.
1 parent 98600eb commit f3219ee

File tree

1 file changed

+34
-19
lines changed

1 file changed

+34
-19
lines changed

src/qt/guiutil.cpp

Lines changed: 34 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@
7171

7272
#include <chrono>
7373
#include <exception>
74+
#include <algorithm>
7475
#include <fstream>
7576
#include <string>
7677
#include <vector>
@@ -958,37 +959,51 @@ void loadStyleSheet(bool fForceUpdate)
958959

959960
QString strStyle = QLatin1String(qFile.readAll());
960961
// Process all <os=...></os> groups in the stylesheet first
961-
QRegularExpressionMatch osStyleMatch;
962962
QRegularExpression osStyleExp(
963963
"^"
964964
"(<os=(?:'|\").+(?:'|\")>)" // group 1
965965
"((?:.|\n)+?)" // group 2
966966
"(</os>?)" // group 3
967967
"$");
968968
osStyleExp.setPatternOptions(QRegularExpression::MultilineOption);
969-
QRegularExpressionMatchIterator it = osStyleExp.globalMatch(strStyle);
970969

971-
// For all <os=...></os> sections
972-
while (it.hasNext() && (osStyleMatch = it.next()).isValid()) {
973-
QStringList listMatches = osStyleMatch.capturedTexts();
974-
975-
// Full match + 3 group matches
976-
if (listMatches.size() % 4) {
977-
throw std::runtime_error(strprintf("%s: Invalid <os=...></os> section in file %s", __func__, file.toStdString()));
970+
// Collect matches first to avoid modifying the string while iterating
971+
QList<QRegularExpressionMatch> matches;
972+
{
973+
QRegularExpressionMatchIterator it = osStyleExp.globalMatch(strStyle);
974+
while (it.hasNext()) {
975+
QRegularExpressionMatch m = it.next();
976+
if (m.hasMatch()) {
977+
matches.append(m);
978+
}
978979
}
980+
}
979981

980-
for (int i = 0; i < listMatches.size(); i += 4) {
981-
if (!listMatches[i + 1].contains(QString::fromStdString(platformName))) {
982-
// If os is not supported for this styles
983-
// just remove the full match
984-
strStyle.replace(listMatches[i], "");
985-
} else {
986-
// If its supported remove the <os=...></os> tags
987-
strStyle.replace(listMatches[i + 1], "");
988-
strStyle.replace(listMatches[i + 3], "");
989-
}
982+
// Build replacement operations using absolute positions
983+
struct Replacement { int start; int end; QString replacement; };
984+
QVector<Replacement> replacements;
985+
for (const auto& m : matches) {
986+
const QString openTag = m.captured(1);
987+
const QString inner = m.captured(2);
988+
Q_UNUSED(inner);
989+
// Remove entire block if OS doesn't match, otherwise drop only the tags
990+
if (!openTag.contains(QString::fromStdString(platformName))) {
991+
replacements.push_back({m.capturedStart(0), m.capturedEnd(0), QString()});
992+
} else {
993+
// Remove opening and closing tags, keep inner content
994+
replacements.push_back({m.capturedStart(1), m.capturedEnd(1), QString()});
995+
replacements.push_back({m.capturedStart(3), m.capturedEnd(3), QString()});
990996
}
991997
}
998+
999+
// Apply replacements from end to start so offsets stay valid
1000+
std::sort(replacements.begin(), replacements.end(), [](const Replacement& a, const Replacement& b) {
1001+
return a.start > b.start;
1002+
});
1003+
for (const auto& r : replacements) {
1004+
strStyle.replace(r.start, r.end - r.start, r.replacement);
1005+
}
1006+
9921007
stylesheet->append(strStyle);
9931008
}
9941009
return true;

0 commit comments

Comments
 (0)