From 4e2e5ad002481caeb608205b9c9c7a95d6045563 Mon Sep 17 00:00:00 2001 From: Evil Eye Date: Wed, 24 Aug 2022 23:10:05 +0200 Subject: [PATCH] Revert format change and ensure string_view args are null-terminated --- apps/openmw/mwgui/spellwindow.cpp | 3 ++- apps/openmw/mwmechanics/npcstats.cpp | 4 ++-- components/misc/strings/format.hpp | 20 +++++++++++++------- 3 files changed, 17 insertions(+), 10 deletions(-) diff --git a/apps/openmw/mwgui/spellwindow.cpp b/apps/openmw/mwgui/spellwindow.cpp index f5618abafb..c39341aeb4 100644 --- a/apps/openmw/mwgui/spellwindow.cpp +++ b/apps/openmw/mwgui/spellwindow.cpp @@ -168,7 +168,8 @@ namespace MWGui // ask for confirmation mSpellToDelete = spellId; ConfirmationDialog* dialog = windowManager->getConfirmationDialog(); - std::string question = Misc::StringUtils::format(windowManager->getGameSettingString("sQuestionDeleteSpell", "Delete %s?"), spell->mName); + std::string question{windowManager->getGameSettingString("sQuestionDeleteSpell", "Delete %s?")}; + question = Misc::StringUtils::format(question, spell->mName); dialog->askForConfirmation(question); dialog->eventOkClicked.clear(); dialog->eventOkClicked += MyGUI::newDelegate(this, &SpellWindow::onDeleteSpellAccept); diff --git a/apps/openmw/mwmechanics/npcstats.cpp b/apps/openmw/mwmechanics/npcstats.cpp index 89b10b2bf0..a89e01259c 100644 --- a/apps/openmw/mwmechanics/npcstats.cpp +++ b/apps/openmw/mwmechanics/npcstats.cpp @@ -267,8 +267,8 @@ void MWMechanics::NpcStats::increaseSkill(int skillIndex, const ESM::Class &clas /// \todo check if character is the player, if levelling is ever implemented for NPCs MWBase::Environment::get().getWindowManager()->playSound("skillraise"); - std::string message = Misc::StringUtils::format(MWBase::Environment::get().getWindowManager()->getGameSettingString("sNotifyMessage39", {}), - ("#{" + ESM::Skill::sSkillNameIds[skillIndex] + "}"), static_cast(base)); + std::string message{MWBase::Environment::get().getWindowManager()->getGameSettingString("sNotifyMessage39", {})}; + message = Misc::StringUtils::format(message, ("#{" + ESM::Skill::sSkillNameIds[skillIndex] + "}"), static_cast(base)); if (readBook) message = "#{sBookSkillMessage}\n" + message; diff --git a/components/misc/strings/format.hpp b/components/misc/strings/format.hpp index 4104082e63..7b5de50fb4 100644 --- a/components/misc/strings/format.hpp +++ b/components/misc/strings/format.hpp @@ -7,11 +7,14 @@ #include #include #include +#include namespace Misc::StringUtils { - namespace Details + struct Details { + std::vector mMemorySafety; + // Allow to convert complex arguments to C-style strings for format() function template T argument(T value) noexcept @@ -22,7 +25,9 @@ namespace Misc::StringUtils template T const * argument(std::basic_string_view const & value) noexcept { - return value.data(); + // TODO: switch to a format function that doesn't require null termination + auto& inserted = mMemorySafety.emplace_back(value); + return inserted.c_str(); } template @@ -30,7 +35,7 @@ namespace Misc::StringUtils { return value.c_str(); } - } + }; // Requires some C++11 features: // 1. std::string needs to be contiguous @@ -39,21 +44,22 @@ namespace Misc::StringUtils template std::string format(const char* fmt, Args const & ... args) { - const int size = std::snprintf(nullptr, 0, fmt, Details::argument(args) ...); + Details details; + const int size = std::snprintf(nullptr, 0, fmt, details.argument(args) ...); if (size < 0) throw std::runtime_error(std::string("Failed to compute resulting string size: ") + std::strerror(errno)); // Note: sprintf also writes a trailing null character. We should remove it. std::string ret(static_cast(size) + 1, '\0'); - if (std::sprintf(ret.data(), fmt, Details::argument(args) ...) < 0) + if (std::sprintf(ret.data(), fmt, details.argument(args) ...) < 0) throw std::runtime_error(std::string("Failed to format string: ") + std::strerror(errno)); ret.erase(static_cast(size)); return ret; } template - std::string format(std::string_view fmt, Args const & ... args) + std::string format(const std::string& fmt, Args const & ... args) { - return format(fmt.data(), args ...); + return format(fmt.c_str(), args ...); } }