From 9a7c57874d708159bd3f48cb7e4c65dba628c5bd Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Thu, 19 Jan 2023 15:07:12 +0400 Subject: [PATCH] Do not use Qt streams with legacy encodings in the Wizard code --- apps/wizard/inisettings.cpp | 62 ++++++++++++++----------- apps/wizard/inisettings.hpp | 6 +-- apps/wizard/installationpage.cpp | 6 +-- apps/wizard/unshield/unshieldworker.cpp | 40 +++++++--------- apps/wizard/unshield/unshieldworker.hpp | 4 +- 5 files changed, 61 insertions(+), 57 deletions(-) diff --git a/apps/wizard/inisettings.cpp b/apps/wizard/inisettings.cpp index bb379db952..1eb142b120 100644 --- a/apps/wizard/inisettings.cpp +++ b/apps/wizard/inisettings.cpp @@ -5,7 +5,10 @@ #include #include #include -#include + +#include + +#include Wizard::IniSettings::IniSettings() {} @@ -25,7 +28,7 @@ QStringList Wizard::IniSettings::findKeys(const QString& text) return result; } -bool Wizard::IniSettings::readFile(QTextStream& stream) +bool Wizard::IniSettings::readFile(std::ifstream& stream, ToUTF8::FromType encoding) { // Look for a square bracket, "'\\[" // that has one or more "not nothing" in it, "([^]]+)" @@ -39,10 +42,20 @@ bool Wizard::IniSettings::readFile(QTextStream& stream) QString currentSection; - while (!stream.atEnd()) + ToUTF8::Utf8Encoder encoder(encoding); + + std::string legacyEncLine; + while (std::getline(stream, legacyEncLine)) { - const QString line(stream.readLine()); + std::string_view lineBuffer = encoder.getUtf8(legacyEncLine); + // unify Unix-style and Windows file ending + if (!(lineBuffer.empty()) && (lineBuffer[lineBuffer.length() - 1]) == '\r') + { + lineBuffer = lineBuffer.substr(0, lineBuffer.length() - 1); + } + + const QString line = QString::fromStdString(std::string(lineBuffer)); if (line.isEmpty() || line.startsWith(QLatin1Char(';'))) continue; @@ -70,7 +83,7 @@ bool Wizard::IniSettings::readFile(QTextStream& stream) return true; } -bool Wizard::IniSettings::writeFile(const QString& path, QTextStream& stream) +bool Wizard::IniSettings::writeFile(const QString& path, std::ifstream& stream, ToUTF8::FromType encoding) { // Look for a square bracket, "'\\[" // that has one or more "not nothing" in it, "([^]]+)" @@ -87,10 +100,19 @@ bool Wizard::IniSettings::writeFile(const QString& path, QTextStream& stream) QString currentSection; QString buffer; - while (!stream.atEnd()) + ToUTF8::Utf8Encoder encoder(encoding); + + std::string legacyEncLine; + while (std::getline(stream, legacyEncLine)) { - const QString line(stream.readLine()); + std::string_view lineBuffer = encoder.getUtf8(legacyEncLine); + // unify Unix-style and Windows file ending + if (!(lineBuffer.empty()) && (lineBuffer[lineBuffer.length() - 1]) == '\r') + { + lineBuffer = lineBuffer.substr(0, lineBuffer.length() - 1); + } + const QString line = QString::fromStdString(std::string(lineBuffer)); if (line.isEmpty() || line.startsWith(QLatin1Char(';'))) { buffer.append(line + QLatin1String("\n")); @@ -158,27 +180,13 @@ bool Wizard::IniSettings::writeFile(const QString& path, QTextStream& stream) } } - // Now we reopen the file, this time we write - QFile file(path); - - if (file.open(QIODevice::ReadWrite | QIODevice::Truncate | QIODevice::Text)) - { - QTextStream in(&file); -#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) - in.setCodec(stream.codec()); -#else - in.setEncoding(stream.encoding()); -#endif - - // Write the updated buffer to an empty file - in << buffer; - file.flush(); - file.close(); - } - else - { + const auto iniPath = Files::pathFromQString(path); + std::ofstream file(iniPath, std::ios::out); + if (file.fail()) return false; - } + + file << encoder.getLegacyEnc(buffer.toStdString()); + file.close(); return true; } diff --git a/apps/wizard/inisettings.hpp b/apps/wizard/inisettings.hpp index 7552109b79..fa1f388329 100644 --- a/apps/wizard/inisettings.hpp +++ b/apps/wizard/inisettings.hpp @@ -4,7 +4,7 @@ #include #include -class QTextStream; +#include namespace Wizard { @@ -30,8 +30,8 @@ namespace Wizard QStringList findKeys(const QString& text); - bool readFile(QTextStream& stream); - bool writeFile(const QString& path, QTextStream& stream); + bool readFile(std::ifstream& stream, ToUTF8::FromType encoding); + bool writeFile(const QString& path, std::ifstream& stream, ToUTF8::FromType encoding); bool parseInx(const QString& path); diff --git a/apps/wizard/installationpage.cpp b/apps/wizard/installationpage.cpp index 0f9860da9a..04e114e3bd 100644 --- a/apps/wizard/installationpage.cpp +++ b/apps/wizard/installationpage.cpp @@ -128,15 +128,15 @@ void Wizard::InstallationPage::startInstallation() if (language == QLatin1String("Polish")) { - mUnshield->setIniCodec(QTextCodec::codecForName("windows-1250")); + mUnshield->setIniEncoding(ToUTF8::FromType::WINDOWS_1250); } else if (language == QLatin1String("Russian")) { - mUnshield->setIniCodec(QTextCodec::codecForName("windows-1251")); + mUnshield->setIniEncoding(ToUTF8::FromType::WINDOWS_1251); } else { - mUnshield->setIniCodec(QTextCodec::codecForName("windows-1252")); + mUnshield->setIniEncoding(ToUTF8::FromType::WINDOWS_1252); } mThread->start(); diff --git a/apps/wizard/unshield/unshieldworker.cpp b/apps/wizard/unshield/unshieldworker.cpp index 58046dcb2c..3dca7c1393 100644 --- a/apps/wizard/unshield/unshieldworker.cpp +++ b/apps/wizard/unshield/unshieldworker.cpp @@ -6,8 +6,10 @@ #include #include #include -#include -#include + +#include + +#include #include @@ -23,7 +25,7 @@ Wizard::UnshieldWorker::UnshieldWorker(qint64 expectedMorrowindBsaSize, QObject* mDiskPath = QString(); // Default to Latin encoding - mIniCodec = QTextCodec::codecForName("windows-1252"); + mIniEncoding = ToUTF8::FromType::WINDOWS_1252; mInstallMorrowind = false; mInstallTribunal = false; @@ -153,10 +155,10 @@ QString Wizard::UnshieldWorker::getDiskPath() return mDiskPath; } -void Wizard::UnshieldWorker::setIniCodec(QTextCodec* codec) +void Wizard::UnshieldWorker::setIniEncoding(ToUTF8::FromType encoding) { QWriteLocker writeLock(&mLock); - mIniCodec = codec; + mIniEncoding = encoding; } void Wizard::UnshieldWorker::wakeAll() @@ -170,19 +172,16 @@ bool Wizard::UnshieldWorker::setupSettings() if (getIniPath().isEmpty()) return false; - QFile file(getIniPath()); - - if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) + const auto iniPath = Files::pathFromQString(getIniPath()); + std::ifstream file(iniPath); + if (file.fail()) { emit error(tr("Failed to open Morrowind configuration file!"), - tr("Opening %1 failed: %2.").arg(getIniPath(), file.errorString())); + tr("Opening %1 failed: %2.").arg(getIniPath(), strerror(errno))); return false; } - QTextStream stream(&file); - stream.setCodec(mIniCodec); - - mIniSettings.readFile(stream); + mIniSettings.readFile(file, mIniEncoding); return true; } @@ -192,22 +191,19 @@ bool Wizard::UnshieldWorker::writeSettings() if (getIniPath().isEmpty()) return false; - QFile file(getIniPath()); - - if (!file.open(QIODevice::ReadWrite | QIODevice::Text)) + const auto iniPath = Files::pathFromQString(getIniPath()); + std::ifstream file(iniPath); + if (file.fail()) { emit error(tr("Failed to open Morrowind configuration file!"), - tr("Opening %1 failed: %2.").arg(getIniPath(), file.errorString())); + tr("Opening %1 failed: %2.").arg(getIniPath(), strerror(errno))); return false; } - QTextStream stream(&file); - stream.setCodec(mIniCodec); - - if (!mIniSettings.writeFile(getIniPath(), stream)) + if (!mIniSettings.writeFile(getIniPath(), file, mIniEncoding)) { emit error(tr("Failed to write Morrowind configuration file!"), - tr("Writing to %1 failed: %2.").arg(getIniPath(), file.errorString())); + tr("Writing to %1 failed: %2.").arg(getIniPath(), strerror(errno))); return false; } diff --git a/apps/wizard/unshield/unshieldworker.hpp b/apps/wizard/unshield/unshieldworker.hpp index 0c97f47c03..c3194232a5 100644 --- a/apps/wizard/unshield/unshieldworker.hpp +++ b/apps/wizard/unshield/unshieldworker.hpp @@ -41,7 +41,7 @@ namespace Wizard QString getPath(); QString getIniPath(); - void setIniCodec(QTextCodec* codec); + void setIniEncoding(ToUTF8::FromType encoding); bool setupSettings(); @@ -104,7 +104,7 @@ namespace Wizard IniSettings mIniSettings; - QTextCodec* mIniCodec; + ToUTF8::FromType mIniEncoding; QWaitCondition mWait;