From 8f48a1f030f35929cc277212f093889d80906750 Mon Sep 17 00:00:00 2001 From: Evil Eye Date: Tue, 9 Nov 2021 21:45:16 +0100 Subject: [PATCH 1/7] Handle non-ASCII characters while saving without triggering an assertion --- CHANGELOG.md | 1 + apps/openmw/mwstate/character.cpp | 12 ++++++++---- components/misc/utf8stream.hpp | 5 +++++ 3 files changed, 14 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bc23c39d92..fdb30ab110 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -75,6 +75,7 @@ Bug #6363: Some scripts in Morrowland fail to work Bug #6376: Creatures should be able to use torches Bug #6386: Artifacts in water reflection due to imprecise screen-space coordinate computation + Bug #6396: Inputting certain Unicode characters triggers an assertion Bug #6416: Morphs are applied to the wrong target Feature #890: OpenMW-CS: Column filtering Feature #2554: Modifying an object triggers the instances table to scroll to the corresponding record diff --git a/apps/openmw/mwstate/character.cpp b/apps/openmw/mwstate/character.cpp index 59ddd2cd10..7216e45df6 100644 --- a/apps/openmw/mwstate/character.cpp +++ b/apps/openmw/mwstate/character.cpp @@ -8,6 +8,8 @@ #include #include +#include + bool MWState::operator< (const Slot& left, const Slot& right) { return left.mTimeStamp 0 && c <= 0x7F && std::isalnum(c)) // Ignore multibyte characters and non alphanumeric characters + stream << static_cast(c); else - stream << "_"; + stream << '_'; } const std::string ext = ".omwsave"; diff --git a/components/misc/utf8stream.hpp b/components/misc/utf8stream.hpp index 9dc8aa8208..efa3e4939a 100644 --- a/components/misc/utf8stream.hpp +++ b/components/misc/utf8stream.hpp @@ -30,6 +30,11 @@ public: { } + Utf8Stream (const std::string& str) : + Utf8Stream (reinterpret_cast(str.c_str()), reinterpret_cast(str.c_str() + str.size())) + { + } + bool eof () const { return cur == end; From c01ba41aa6a491f26512c1c541354a6e7510d6bb Mon Sep 17 00:00:00 2001 From: Evil Eye Date: Wed, 10 Nov 2021 16:54:52 +0100 Subject: [PATCH 2/7] Handle character directories like save names --- apps/openmw/mwstate/character.cpp | 2 +- apps/openmw/mwstate/charactermanager.cpp | 12 ++++++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwstate/character.cpp b/apps/openmw/mwstate/character.cpp index 7216e45df6..52696de104 100644 --- a/apps/openmw/mwstate/character.cpp +++ b/apps/openmw/mwstate/character.cpp @@ -58,7 +58,7 @@ void MWState::Character::addSlot (const ESM::SavedGame& profile) while(!description.eof()) { auto c = description.consume(); - if(c > 0 && c <= 0x7F && std::isalnum(c)) // Ignore multibyte characters and non alphanumeric characters + if(c <= 0x7F && std::isalnum(c)) // Ignore multibyte characters and non alphanumeric characters stream << static_cast(c); else stream << '_'; diff --git a/apps/openmw/mwstate/charactermanager.cpp b/apps/openmw/mwstate/charactermanager.cpp index 027a4f38a4..301f33c5df 100644 --- a/apps/openmw/mwstate/charactermanager.cpp +++ b/apps/openmw/mwstate/charactermanager.cpp @@ -5,6 +5,8 @@ #include +#include + MWState::CharacterManager::CharacterManager (const boost::filesystem::path& saves, const std::vector& contentFiles) : mPath (saves), mCurrent (nullptr), mGame (getFirstGameFile(contentFiles)) @@ -57,12 +59,14 @@ MWState::Character* MWState::CharacterManager::createCharacter(const std::string std::ostringstream stream; // The character name is user-supplied, so we need to escape the path - for (std::string::const_iterator it = name.begin(); it != name.end(); ++it) + Utf8Stream nameStream(name); + while(!nameStream.eof()) { - if (std::isalnum(*it)) // Ignores multibyte characters and non alphanumeric characters - stream << *it; + auto c = nameStream.consume(); + if(c <= 0x7F && std::isalnum(c)) // Ignore multibyte characters and non alphanumeric characters + stream << static_cast(c); else - stream << "_"; + stream << '_'; } boost::filesystem::path path = mPath / stream.str(); From 751e8cf76b9d2c792f595088337618af61c1ad04 Mon Sep 17 00:00:00 2001 From: Evil Eye Date: Wed, 10 Nov 2021 21:25:16 +0100 Subject: [PATCH 3/7] Do a bounds check before calling functions defined in cctype --- apps/niftest/niftest.cpp | 14 +++----------- apps/opencs/model/filter/parser.cpp | 21 +++++++++++++++++---- apps/opencs/model/world/idtable.cpp | 7 +++---- apps/openmw/mwinput/keyboardmanager.cpp | 2 +- components/files/escape.hpp | 2 +- components/interpreter/defines.cpp | 3 +-- components/lua/configuration.cpp | 15 ++++++++++----- components/misc/stringops.hpp | 15 +++++++-------- components/settings/parser.cpp | 2 +- 9 files changed, 44 insertions(+), 37 deletions(-) diff --git a/apps/niftest/niftest.cpp b/apps/niftest/niftest.cpp index cb6205ef5c..c42df4a423 100644 --- a/apps/niftest/niftest.cpp +++ b/apps/niftest/niftest.cpp @@ -2,8 +2,8 @@ #include #include -#include +#include #include #include #include @@ -18,18 +18,10 @@ namespace bpo = boost::program_options; namespace bfs = boost::filesystem; ///See if the file has the named extension -bool hasExtension(std::string filename, std::string extensionToFind) +bool hasExtension(std::string filename, std::string extensionToFind) { std::string extension = filename.substr(filename.find_last_of('.')+1); - - //Convert strings to lower case for comparison - std::transform(extension.begin(), extension.end(), extension.begin(), ::tolower); - std::transform(extensionToFind.begin(), extensionToFind.end(), extensionToFind.begin(), ::tolower); - - if(extension == extensionToFind) - return true; - else - return false; + return Misc::StringUtils::ciEqual(extension, extensionToFind); } ///See if the file has the "nif" extension. diff --git a/apps/opencs/model/filter/parser.cpp b/apps/opencs/model/filter/parser.cpp index d363b4849d..b8f125e237 100644 --- a/apps/opencs/model/filter/parser.cpp +++ b/apps/opencs/model/filter/parser.cpp @@ -17,6 +17,19 @@ #include "textnode.hpp" #include "valuenode.hpp" +namespace +{ + bool isAlpha(char c) + { + return c >= 0 && c <= 255 && std::isalpha(c); + } + + bool isDigit(char c) + { + return c >= 0 && c <= 255 && std::isdigit(c); + } +} + namespace CSMFilter { struct Token @@ -103,7 +116,7 @@ CSMFilter::Token CSMFilter::Parser::getStringToken() { char c = mInput[mIndex]; - if (std::isalpha (c) || c==':' || c=='_' || (!string.empty() && std::isdigit (c)) || c=='"' || + if (isAlpha(c) || c==':' || c=='_' || (!string.empty() && isDigit(c)) || c=='"' || (!string.empty() && string[0]=='"')) string += c; else @@ -150,7 +163,7 @@ CSMFilter::Token CSMFilter::Parser::getNumberToken() { char c = mInput[mIndex]; - if (std::isdigit (c)) + if (isDigit(c)) { string += c; hasDigit = true; @@ -225,10 +238,10 @@ CSMFilter::Token CSMFilter::Parser::getNextToken() case '!': ++mIndex; return Token (Token::Type_OneShot); } - if (c=='"' || c=='_' || std::isalpha (c) || c==':') + if (c=='"' || c=='_' || isAlpha(c) || c==':') return getStringToken(); - if (c=='-' || c=='.' || std::isdigit (c)) + if (c=='-' || c=='.' || isDigit(c)) return getNumberToken(); error(); diff --git a/apps/opencs/model/world/idtable.cpp b/apps/opencs/model/world/idtable.cpp index 5b4a9b31bc..215f42133d 100644 --- a/apps/opencs/model/world/idtable.cpp +++ b/apps/opencs/model/world/idtable.cpp @@ -8,6 +8,7 @@ #include #include +#include #include "collectionbase.hpp" #include "columnbase.hpp" @@ -354,8 +355,7 @@ CSMWorld::LandTextureIdTable::ImportResults CSMWorld::LandTextureIdTable::import for (int i = 0; i < idCollection()->getSize(); ++i) { auto& record = static_cast&>(idCollection()->getRecord(i)); - std::string texture = record.get().mTexture; - std::transform(texture.begin(), texture.end(), texture.begin(), tolower); + std::string texture = Misc::StringUtils::lowerCase(record.get().mTexture); if (record.isModified()) reverseLookupMap.emplace(texture, idCollection()->getId(i)); } @@ -376,8 +376,7 @@ CSMWorld::LandTextureIdTable::ImportResults CSMWorld::LandTextureIdTable::import // Look for a pre-existing record auto& record = static_cast&>(idCollection()->getRecord(oldRow)); - std::string texture = record.get().mTexture; - std::transform(texture.begin(), texture.end(), texture.begin(), tolower); + std::string texture = Misc::StringUtils::lowerCase(record.get().mTexture); auto searchIt = reverseLookupMap.find(texture); if (searchIt != reverseLookupMap.end()) { diff --git a/apps/openmw/mwinput/keyboardmanager.cpp b/apps/openmw/mwinput/keyboardmanager.cpp index b8019b12ba..c5a665e5c6 100644 --- a/apps/openmw/mwinput/keyboardmanager.cpp +++ b/apps/openmw/mwinput/keyboardmanager.cpp @@ -42,7 +42,7 @@ namespace MWInput bool consumed = SDL_IsTextInputActive() && // Little trick to check if key is printable (!(SDLK_SCANCODE_MASK & arg.keysym.sym) && - (std::isprint(arg.keysym.sym) || + (arg.keysym.sym >= 0 && arg.keysym.sym <= 255 && std::isprint(arg.keysym.sym) || // Don't trust isprint for symbols outside the extended ASCII range (kc == MyGUI::KeyCode::None && arg.keysym.sym > 0xff))); if (kc != MyGUI::KeyCode::None && !mBindingsManager->isDetectingBindingState()) diff --git a/components/files/escape.hpp b/components/files/escape.hpp index d01bd8d980..310042072d 100644 --- a/components/files/escape.hpp +++ b/components/files/escape.hpp @@ -84,7 +84,7 @@ namespace Files { mNext.push(character); } - if (!mSeenNonWhitespace && !isspace(character)) + if (!mSeenNonWhitespace && !(character >= 0 && character <= 255 && isspace(character))) mSeenNonWhitespace = true; } int retval = mNext.front(); diff --git a/components/interpreter/defines.cpp b/components/interpreter/defines.cpp index 48d2ceaa45..164e7b2126 100644 --- a/components/interpreter/defines.cpp +++ b/components/interpreter/defines.cpp @@ -172,8 +172,7 @@ namespace Interpreter{ for(unsigned int j = 0; j < globals.size(); j++){ if(globals[j].length() > temp.length()){ // Just in case there's a global with a huuuge name - temp = text.substr(i+1, globals[j].length()); - transform(temp.begin(), temp.end(), temp.begin(), ::tolower); + temp = Misc::StringUtils::lowerCase(text.substr(i+1, globals[j].length())); } found = check(temp, globals[j], &i, &start); diff --git a/components/lua/configuration.cpp b/components/lua/configuration.cpp index 4598ed2508..d059d1716f 100644 --- a/components/lua/configuration.cpp +++ b/components/lua/configuration.cpp @@ -30,6 +30,11 @@ namespace LuaUtil {"POTION", ESM::LuaScriptCfg::sPotion}, {"WEAPON", ESM::LuaScriptCfg::sWeapon}, }; + + bool isSpace(int c) + { + return c >= 0 && c <= 255 && std::isspace(c); + } } const std::vector ScriptsConfiguration::sEmpty; @@ -101,11 +106,11 @@ namespace LuaUtil if (!line.empty() && line.back() == '\r') line = line.substr(0, line.size() - 1); - while (!line.empty() && std::isspace(line[0])) + while (!line.empty() && isSpace(line[0])) line = line.substr(1); if (line.empty() || line[0] == '#') // Skip empty lines and comments continue; - while (!line.empty() && std::isspace(line.back())) + while (!line.empty() && isSpace(line.back())) line = line.substr(0, line.size() - 1); if (!Misc::StringUtils::ciEndsWith(line, ".lua")) @@ -118,7 +123,7 @@ namespace LuaUtil throw std::runtime_error(Misc::StringUtils::format("No flags found in: %s", std::string(line))); std::string_view flagsStr = line.substr(0, semicolonPos); std::string_view scriptPath = line.substr(semicolonPos + 1); - while (std::isspace(scriptPath[0])) + while (isSpace(scriptPath[0])) scriptPath = scriptPath.substr(1); // Parse flags @@ -126,10 +131,10 @@ namespace LuaUtil size_t flagsPos = 0; while (true) { - while (flagsPos < flagsStr.size() && (std::isspace(flagsStr[flagsPos]) || flagsStr[flagsPos] == ',')) + while (flagsPos < flagsStr.size() && (isSpace(flagsStr[flagsPos]) || flagsStr[flagsPos] == ',')) flagsPos++; size_t startPos = flagsPos; - while (flagsPos < flagsStr.size() && !std::isspace(flagsStr[flagsPos]) && flagsStr[flagsPos] != ',') + while (flagsPos < flagsStr.size() && !isSpace(flagsStr[flagsPos]) && flagsStr[flagsPos] != ',') flagsPos++; if (startPos == flagsPos) break; diff --git a/components/misc/stringops.hpp b/components/misc/stringops.hpp index 12633db826..a0ec9f62dd 100644 --- a/components/misc/stringops.hpp +++ b/components/misc/stringops.hpp @@ -184,17 +184,16 @@ public: static inline void trim(std::string &s) { - // left trim - s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](int ch) + const auto notSpace = [](int ch) { - return !std::isspace(ch); - })); + // TODO Do we care about multibyte whitespace? + return ch < 0 || ch > 255 || !std::isspace(ch); + }; + // left trim + s.erase(s.begin(), std::find_if(s.begin(), s.end(), notSpace)); // right trim - s.erase(std::find_if(s.rbegin(), s.rend(), [](int ch) - { - return !std::isspace(ch); - }).base(), s.end()); + s.erase(std::find_if(s.rbegin(), s.rend(), notSpace).base(), s.end()); } template diff --git a/components/settings/parser.cpp b/components/settings/parser.cpp index f2419dfdd6..f36f190ee2 100644 --- a/components/settings/parser.cpp +++ b/components/settings/parser.cpp @@ -311,7 +311,7 @@ void Settings::SettingsFileParser::saveSettingsFile(const std::string& file, con bool Settings::SettingsFileParser::skipWhiteSpace(size_t& i, std::string& str) { - while (i < str.size() && std::isspace(str[i], std::locale::classic())) + while (i < str.size() && str[i] >= 0 && str[i] <= 255 && std::isspace(str[i], std::locale::classic())) { ++i; } From c6470f33d363e5139ffb78c1a6e64c2ed09e0a44 Mon Sep 17 00:00:00 2001 From: Evil Eye Date: Wed, 10 Nov 2021 21:37:29 +0100 Subject: [PATCH 4/7] Use string_view --- components/misc/utf8stream.hpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/components/misc/utf8stream.hpp b/components/misc/utf8stream.hpp index efa3e4939a..8435816c67 100644 --- a/components/misc/utf8stream.hpp +++ b/components/misc/utf8stream.hpp @@ -3,6 +3,7 @@ #include #include +#include #include class Utf8Stream @@ -30,8 +31,8 @@ public: { } - Utf8Stream (const std::string& str) : - Utf8Stream (reinterpret_cast(str.c_str()), reinterpret_cast(str.c_str() + str.size())) + Utf8Stream (std::string_view str) : + Utf8Stream (reinterpret_cast(str.data()), reinterpret_cast(str.data() + str.size())) { } From f23bd51175a432c3fe1c156f3118bae799978701 Mon Sep 17 00:00:00 2001 From: Evil Eye Date: Wed, 10 Nov 2021 22:53:25 +0100 Subject: [PATCH 5/7] Don't check bounds when the type is unambiguously a char --- apps/opencs/model/filter/parser.cpp | 21 ++++----------------- components/lua/configuration.cpp | 15 +++++---------- components/misc/stringops.hpp | 4 ++-- components/settings/parser.cpp | 2 +- 4 files changed, 12 insertions(+), 30 deletions(-) diff --git a/apps/opencs/model/filter/parser.cpp b/apps/opencs/model/filter/parser.cpp index b8f125e237..d363b4849d 100644 --- a/apps/opencs/model/filter/parser.cpp +++ b/apps/opencs/model/filter/parser.cpp @@ -17,19 +17,6 @@ #include "textnode.hpp" #include "valuenode.hpp" -namespace -{ - bool isAlpha(char c) - { - return c >= 0 && c <= 255 && std::isalpha(c); - } - - bool isDigit(char c) - { - return c >= 0 && c <= 255 && std::isdigit(c); - } -} - namespace CSMFilter { struct Token @@ -116,7 +103,7 @@ CSMFilter::Token CSMFilter::Parser::getStringToken() { char c = mInput[mIndex]; - if (isAlpha(c) || c==':' || c=='_' || (!string.empty() && isDigit(c)) || c=='"' || + if (std::isalpha (c) || c==':' || c=='_' || (!string.empty() && std::isdigit (c)) || c=='"' || (!string.empty() && string[0]=='"')) string += c; else @@ -163,7 +150,7 @@ CSMFilter::Token CSMFilter::Parser::getNumberToken() { char c = mInput[mIndex]; - if (isDigit(c)) + if (std::isdigit (c)) { string += c; hasDigit = true; @@ -238,10 +225,10 @@ CSMFilter::Token CSMFilter::Parser::getNextToken() case '!': ++mIndex; return Token (Token::Type_OneShot); } - if (c=='"' || c=='_' || isAlpha(c) || c==':') + if (c=='"' || c=='_' || std::isalpha (c) || c==':') return getStringToken(); - if (c=='-' || c=='.' || isDigit(c)) + if (c=='-' || c=='.' || std::isdigit (c)) return getNumberToken(); error(); diff --git a/components/lua/configuration.cpp b/components/lua/configuration.cpp index d059d1716f..4598ed2508 100644 --- a/components/lua/configuration.cpp +++ b/components/lua/configuration.cpp @@ -30,11 +30,6 @@ namespace LuaUtil {"POTION", ESM::LuaScriptCfg::sPotion}, {"WEAPON", ESM::LuaScriptCfg::sWeapon}, }; - - bool isSpace(int c) - { - return c >= 0 && c <= 255 && std::isspace(c); - } } const std::vector ScriptsConfiguration::sEmpty; @@ -106,11 +101,11 @@ namespace LuaUtil if (!line.empty() && line.back() == '\r') line = line.substr(0, line.size() - 1); - while (!line.empty() && isSpace(line[0])) + while (!line.empty() && std::isspace(line[0])) line = line.substr(1); if (line.empty() || line[0] == '#') // Skip empty lines and comments continue; - while (!line.empty() && isSpace(line.back())) + while (!line.empty() && std::isspace(line.back())) line = line.substr(0, line.size() - 1); if (!Misc::StringUtils::ciEndsWith(line, ".lua")) @@ -123,7 +118,7 @@ namespace LuaUtil throw std::runtime_error(Misc::StringUtils::format("No flags found in: %s", std::string(line))); std::string_view flagsStr = line.substr(0, semicolonPos); std::string_view scriptPath = line.substr(semicolonPos + 1); - while (isSpace(scriptPath[0])) + while (std::isspace(scriptPath[0])) scriptPath = scriptPath.substr(1); // Parse flags @@ -131,10 +126,10 @@ namespace LuaUtil size_t flagsPos = 0; while (true) { - while (flagsPos < flagsStr.size() && (isSpace(flagsStr[flagsPos]) || flagsStr[flagsPos] == ',')) + while (flagsPos < flagsStr.size() && (std::isspace(flagsStr[flagsPos]) || flagsStr[flagsPos] == ',')) flagsPos++; size_t startPos = flagsPos; - while (flagsPos < flagsStr.size() && !isSpace(flagsStr[flagsPos]) && flagsStr[flagsPos] != ',') + while (flagsPos < flagsStr.size() && !std::isspace(flagsStr[flagsPos]) && flagsStr[flagsPos] != ',') flagsPos++; if (startPos == flagsPos) break; diff --git a/components/misc/stringops.hpp b/components/misc/stringops.hpp index a0ec9f62dd..c8d236a9d0 100644 --- a/components/misc/stringops.hpp +++ b/components/misc/stringops.hpp @@ -184,10 +184,10 @@ public: static inline void trim(std::string &s) { - const auto notSpace = [](int ch) + const auto notSpace = [](char ch) { // TODO Do we care about multibyte whitespace? - return ch < 0 || ch > 255 || !std::isspace(ch); + return !std::isspace(ch); }; // left trim s.erase(s.begin(), std::find_if(s.begin(), s.end(), notSpace)); diff --git a/components/settings/parser.cpp b/components/settings/parser.cpp index f36f190ee2..f2419dfdd6 100644 --- a/components/settings/parser.cpp +++ b/components/settings/parser.cpp @@ -311,7 +311,7 @@ void Settings::SettingsFileParser::saveSettingsFile(const std::string& file, con bool Settings::SettingsFileParser::skipWhiteSpace(size_t& i, std::string& str) { - while (i < str.size() && str[i] >= 0 && str[i] <= 255 && std::isspace(str[i], std::locale::classic())) + while (i < str.size() && std::isspace(str[i], std::locale::classic())) { ++i; } From 6daefe3ddc0a94c507efd52b5769e457af07c870 Mon Sep 17 00:00:00 2001 From: Evil Eye Date: Thu, 11 Nov 2021 18:14:10 +0100 Subject: [PATCH 6/7] Cast to the unsigned char expected by cctype functions --- apps/opencs/model/filter/parser.cpp | 21 +++++++++++++++++---- apps/openmw/mwinput/keyboardmanager.cpp | 4 ++-- components/lua/configuration.cpp | 15 ++++++++++----- components/misc/stringops.hpp | 2 +- components/settings/parser.cpp | 2 +- 5 files changed, 31 insertions(+), 13 deletions(-) diff --git a/apps/opencs/model/filter/parser.cpp b/apps/opencs/model/filter/parser.cpp index d363b4849d..6f185d60fa 100644 --- a/apps/opencs/model/filter/parser.cpp +++ b/apps/opencs/model/filter/parser.cpp @@ -17,6 +17,19 @@ #include "textnode.hpp" #include "valuenode.hpp" +namespace +{ + bool isAlpha(char c) + { + return std::isalpha(static_cast(c)); + } + + bool isDigit(char c) + { + return std::isdigit(static_cast(c)); + } +} + namespace CSMFilter { struct Token @@ -103,7 +116,7 @@ CSMFilter::Token CSMFilter::Parser::getStringToken() { char c = mInput[mIndex]; - if (std::isalpha (c) || c==':' || c=='_' || (!string.empty() && std::isdigit (c)) || c=='"' || + if (isAlpha(c) || c==':' || c=='_' || (!string.empty() && isDigit(c)) || c=='"' || (!string.empty() && string[0]=='"')) string += c; else @@ -150,7 +163,7 @@ CSMFilter::Token CSMFilter::Parser::getNumberToken() { char c = mInput[mIndex]; - if (std::isdigit (c)) + if (isDigit(c)) { string += c; hasDigit = true; @@ -225,10 +238,10 @@ CSMFilter::Token CSMFilter::Parser::getNextToken() case '!': ++mIndex; return Token (Token::Type_OneShot); } - if (c=='"' || c=='_' || std::isalpha (c) || c==':') + if (c=='"' || c=='_' || isAlpha(c) || c==':') return getStringToken(); - if (c=='-' || c=='.' || std::isdigit (c)) + if (c=='-' || c=='.' || isDigit(c)) return getNumberToken(); error(); diff --git a/apps/openmw/mwinput/keyboardmanager.cpp b/apps/openmw/mwinput/keyboardmanager.cpp index c5a665e5c6..b121b665e7 100644 --- a/apps/openmw/mwinput/keyboardmanager.cpp +++ b/apps/openmw/mwinput/keyboardmanager.cpp @@ -42,9 +42,9 @@ namespace MWInput bool consumed = SDL_IsTextInputActive() && // Little trick to check if key is printable (!(SDLK_SCANCODE_MASK & arg.keysym.sym) && - (arg.keysym.sym >= 0 && arg.keysym.sym <= 255 && std::isprint(arg.keysym.sym) || // Don't trust isprint for symbols outside the extended ASCII range - (kc == MyGUI::KeyCode::None && arg.keysym.sym > 0xff))); + ((kc == MyGUI::KeyCode::None && arg.keysym.sym > 0xff) || + (arg.keysym.sym >= 0 && arg.keysym.sym <= 255 && std::isprint(arg.keysym.sym)))); if (kc != MyGUI::KeyCode::None && !mBindingsManager->isDetectingBindingState()) { if (MWBase::Environment::get().getWindowManager()->injectKeyPress(kc, 0, arg.repeat)) diff --git a/components/lua/configuration.cpp b/components/lua/configuration.cpp index 4598ed2508..172d958029 100644 --- a/components/lua/configuration.cpp +++ b/components/lua/configuration.cpp @@ -30,6 +30,11 @@ namespace LuaUtil {"POTION", ESM::LuaScriptCfg::sPotion}, {"WEAPON", ESM::LuaScriptCfg::sWeapon}, }; + + bool isSpace(char c) + { + return std::isspace(static_cast(c)); + } } const std::vector ScriptsConfiguration::sEmpty; @@ -101,11 +106,11 @@ namespace LuaUtil if (!line.empty() && line.back() == '\r') line = line.substr(0, line.size() - 1); - while (!line.empty() && std::isspace(line[0])) + while (!line.empty() && isSpace(line[0])) line = line.substr(1); if (line.empty() || line[0] == '#') // Skip empty lines and comments continue; - while (!line.empty() && std::isspace(line.back())) + while (!line.empty() && isSpace(line.back())) line = line.substr(0, line.size() - 1); if (!Misc::StringUtils::ciEndsWith(line, ".lua")) @@ -118,7 +123,7 @@ namespace LuaUtil throw std::runtime_error(Misc::StringUtils::format("No flags found in: %s", std::string(line))); std::string_view flagsStr = line.substr(0, semicolonPos); std::string_view scriptPath = line.substr(semicolonPos + 1); - while (std::isspace(scriptPath[0])) + while (isSpace(scriptPath[0])) scriptPath = scriptPath.substr(1); // Parse flags @@ -126,10 +131,10 @@ namespace LuaUtil size_t flagsPos = 0; while (true) { - while (flagsPos < flagsStr.size() && (std::isspace(flagsStr[flagsPos]) || flagsStr[flagsPos] == ',')) + while (flagsPos < flagsStr.size() && (isSpace(flagsStr[flagsPos]) || flagsStr[flagsPos] == ',')) flagsPos++; size_t startPos = flagsPos; - while (flagsPos < flagsStr.size() && !std::isspace(flagsStr[flagsPos]) && flagsStr[flagsPos] != ',') + while (flagsPos < flagsStr.size() && !isSpace(flagsStr[flagsPos]) && flagsStr[flagsPos] != ',') flagsPos++; if (startPos == flagsPos) break; diff --git a/components/misc/stringops.hpp b/components/misc/stringops.hpp index c8d236a9d0..fe4be00006 100644 --- a/components/misc/stringops.hpp +++ b/components/misc/stringops.hpp @@ -187,7 +187,7 @@ public: const auto notSpace = [](char ch) { // TODO Do we care about multibyte whitespace? - return !std::isspace(ch); + return !std::isspace(static_cast(ch)); }; // left trim s.erase(s.begin(), std::find_if(s.begin(), s.end(), notSpace)); diff --git a/components/settings/parser.cpp b/components/settings/parser.cpp index f2419dfdd6..8d387ab9c7 100644 --- a/components/settings/parser.cpp +++ b/components/settings/parser.cpp @@ -311,7 +311,7 @@ void Settings::SettingsFileParser::saveSettingsFile(const std::string& file, con bool Settings::SettingsFileParser::skipWhiteSpace(size_t& i, std::string& str) { - while (i < str.size() && std::isspace(str[i], std::locale::classic())) + while (i < str.size() && std::isspace(static_cast(str[i]), std::locale::classic())) { ++i; } From c5ba733855c698e1d07768998c7490135b4966a4 Mon Sep 17 00:00:00 2001 From: Evil Eye Date: Thu, 11 Nov 2021 21:16:25 +0100 Subject: [PATCH 7/7] Unbreak the unit test in gcc and clang --- components/settings/parser.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/settings/parser.cpp b/components/settings/parser.cpp index 8d387ab9c7..f2419dfdd6 100644 --- a/components/settings/parser.cpp +++ b/components/settings/parser.cpp @@ -311,7 +311,7 @@ void Settings::SettingsFileParser::saveSettingsFile(const std::string& file, con bool Settings::SettingsFileParser::skipWhiteSpace(size_t& i, std::string& str) { - while (i < str.size() && std::isspace(static_cast(str[i]), std::locale::classic())) + while (i < str.size() && std::isspace(str[i], std::locale::classic())) { ++i; }