From 8fb0b5846ea2e059f225e6659b393584305bcb53 Mon Sep 17 00:00:00 2001 From: AnyOldName3 Date: Sun, 14 Nov 2021 00:22:44 +0000 Subject: [PATCH] Allow paths with trailing data, emmitting a warning --- apps/opencs/editor.cpp | 12 +-- apps/openmw/main.cpp | 12 +-- apps/openmw/options.cpp | 8 +- apps/openmw_test_suite/mwworld/test_store.cpp | 8 +- apps/openmw_test_suite/openmw/options.cpp | 102 ++++++++++-------- components/files/configurationmanager.cpp | 24 ++++- components/files/configurationmanager.hpp | 12 +++ 7 files changed, 113 insertions(+), 65 deletions(-) diff --git a/apps/opencs/editor.cpp b/apps/opencs/editor.cpp index b437a36201..6294e5eb7c 100644 --- a/apps/opencs/editor.cpp +++ b/apps/opencs/editor.cpp @@ -88,11 +88,11 @@ std::pair > CS::Editor::readConfi boost::program_options::options_description desc("Syntax: openmw-cs \nAllowed options"); desc.add_options() - ("data", boost::program_options::value()->default_value(Files::PathContainer(), "data")->multitoken()->composing()) - ("data-local", boost::program_options::value()->default_value(Files::PathContainer::value_type(), "")) + ("data", boost::program_options::value()->default_value(Files::ReluctantPathContainer(), "data")->multitoken()->composing()) + ("data-local", boost::program_options::value()->default_value(Files::ReluctantPathContainer::value_type(), "")) ("fs-strict", boost::program_options::value()->implicit_value(true)->default_value(false)) ("encoding", boost::program_options::value()->default_value("win1252")) - ("resources", boost::program_options::value()->default_value(boost::filesystem::path(), "resources")) + ("resources", boost::program_options::value()->default_value(Files::ReluctantPath(), "resources")) ("fallback-archive", boost::program_options::value>()-> default_value(std::vector(), "fallback-archive")->multitoken()) ("fallback", boost::program_options::value()->default_value(FallbackMap(), "") @@ -112,7 +112,7 @@ std::pair > CS::Editor::readConfi mDocumentManager.setEncoding(ToUTF8::calculateEncoding(mEncodingName)); mFileDialog.setEncoding (QString::fromUtf8(mEncodingName.c_str())); - mDocumentManager.setResourceDir (mResources = variables["resources"].as()); + mDocumentManager.setResourceDir (mResources = variables["resources"].as()); if (variables["script-blacklist-use"].as()) mDocumentManager.setBlacklistedScripts ( @@ -122,10 +122,10 @@ std::pair > CS::Editor::readConfi Files::PathContainer dataDirs, dataLocal; if (!variables["data"].empty()) { - dataDirs = variables["data"].as(); + dataDirs = asPathContainer(variables["data"].as()); } - Files::PathContainer::value_type local(variables["data-local"].as()); + Files::PathContainer::value_type local(variables["data-local"].as()); if (!local.empty()) dataLocal.push_back(local); diff --git a/apps/openmw/main.cpp b/apps/openmw/main.cpp index 7a3a21b149..50a7c3d844 100644 --- a/apps/openmw/main.cpp +++ b/apps/openmw/main.cpp @@ -56,7 +56,7 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat { cfgMgr.readConfiguration(variables, desc, true); - Version::Version v = Version::getOpenmwVersion(variables["resources"].as().string()); + Version::Version v = Version::getOpenmwVersion(variables["resources"].as().string()); getRawStdout() << v.describe() << std::endl; return false; } @@ -65,7 +65,7 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat cfgMgr.readConfiguration(variables, desc); Files::mergeComposingVariables(variables, composingVariables, desc); - Version::Version v = Version::getOpenmwVersion(variables["resources"].as().string()); + Version::Version v = Version::getOpenmwVersion(variables["resources"].as().string()); Log(Debug::Info) << v.describe(); engine.setGrabMouse(!variables["no-grab"].as()); @@ -78,15 +78,15 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat // directory settings engine.enableFSStrict(variables["fs-strict"].as()); - Files::PathContainer dataDirs(variables["data"].as()); + Files::PathContainer dataDirs(asPathContainer(variables["data"].as())); - Files::PathContainer::value_type local(variables["data-local"].as()); + Files::PathContainer::value_type local(variables["data-local"].as()); if (!local.empty()) dataDirs.push_back(local); cfgMgr.processPaths(dataDirs); - engine.setResourceDir(variables["resources"].as()); + engine.setResourceDir(variables["resources"].as()); engine.setDataDirs(dataDirs); // fallback archives @@ -141,7 +141,7 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat engine.setWarningsMode (variables["script-warn"].as()); engine.setScriptBlacklist (variables["script-blacklist"].as()); engine.setScriptBlacklistUse (variables["script-blacklist-use"].as()); - engine.setSaveGameFile (variables["load-savegame"].as().string()); + engine.setSaveGameFile (variables["load-savegame"].as().string()); // other settings Fallback::Map::init(variables["fallback"].as().mMap); diff --git a/apps/openmw/options.cpp b/apps/openmw/options.cpp index dd94590f2a..a1b2102f08 100644 --- a/apps/openmw/options.cpp +++ b/apps/openmw/options.cpp @@ -25,16 +25,16 @@ namespace OpenMW ("replace", bpo::value()->default_value(StringsVector(), "") ->multitoken()->composing(), "settings where the values from the current source should replace those from lower-priority sources instead of being appended") - ("data", bpo::value()->default_value(Files::PathContainer(), "data") + ("data", bpo::value()->default_value(Files::ReluctantPathContainer(), "data") ->multitoken()->composing(), "set data directories (later directories have higher priority)") - ("data-local", bpo::value()->default_value(Files::PathContainer::value_type(), ""), + ("data-local", bpo::value()->default_value(Files::ReluctantPathContainer::value_type(), ""), "set local data directory (highest priority)") ("fallback-archive", bpo::value()->default_value(StringsVector(), "fallback-archive") ->multitoken()->composing(), "set fallback BSA archives (later archives have higher priority)") - ("resources", bpo::value()->default_value(boost::filesystem::path(), "resources"), + ("resources", bpo::value()->default_value(Files::ReluctantPath(), "resources"), "set resources directory") ("start", bpo::value()->default_value(""), @@ -77,7 +77,7 @@ namespace OpenMW ("script-blacklist-use", bpo::value()->implicit_value(true) ->default_value(true), "enable script blacklisting") - ("load-savegame", bpo::value()->default_value(boost::filesystem::path(), ""), + ("load-savegame", bpo::value()->default_value(Files::ReluctantPath(), ""), "load a save game file on game startup (specify an absolute filename or a filename relative to the current working directory)") ("skip-menu", bpo::value()->implicit_value(true) diff --git a/apps/openmw_test_suite/mwworld/test_store.cpp b/apps/openmw_test_suite/mwworld/test_store.cpp index 9aa5a6758f..f2f9459df0 100644 --- a/apps/openmw_test_suite/mwworld/test_store.cpp +++ b/apps/openmw_test_suite/mwworld/test_store.cpp @@ -58,10 +58,10 @@ struct ContentFileTest : public ::testing::Test boost::program_options::options_description desc("Allowed options"); desc.add_options() - ("data", boost::program_options::value()->default_value(Files::PathContainer(), "data")->multitoken()->composing()) + ("data", boost::program_options::value()->default_value(Files::ReluctantPathContainer(), "data")->multitoken()->composing()) ("content", boost::program_options::value>()->default_value(std::vector(), "") ->multitoken()->composing(), "content file(s): esm/esp, or omwgame/omwaddon") - ("data-local", boost::program_options::value()->default_value(Files::PathContainer::value_type(), "")); + ("data-local", boost::program_options::value()->default_value(Files::ReluctantPathContainer::value_type(), "")); boost::program_options::notify(variables); @@ -69,10 +69,10 @@ struct ContentFileTest : public ::testing::Test Files::PathContainer dataDirs, dataLocal; if (!variables["data"].empty()) { - dataDirs = variables["data"].as(); + dataDirs = asPathContainer(variables["data"].as()); } - Files::PathContainer::value_type local(variables["data-local"].as()); + Files::PathContainer::value_type local(variables["data-local"].as()); if (!local.empty()) { dataLocal.push_back(local); } diff --git a/apps/openmw_test_suite/openmw/options.cpp b/apps/openmw_test_suite/openmw/options.cpp index 7cf0df1066..bae324b134 100644 --- a/apps/openmw_test_suite/openmw/options.cpp +++ b/apps/openmw_test_suite/openmw/options.cpp @@ -40,7 +40,7 @@ namespace const std::array arguments {"openmw", "--load-savegame=save.omwsave"}; bpo::variables_map variables; parseArgs(arguments, variables, description); - EXPECT_EQ(variables["load-savegame"].as().string(), "save.omwsave"); + EXPECT_EQ(variables["load-savegame"].as().string(), "save.omwsave"); } TEST(OpenMWOptionsFromArguments, should_support_single_word_load_savegame_path) @@ -49,7 +49,7 @@ namespace const std::array arguments {"openmw", "--load-savegame", "save.omwsave"}; bpo::variables_map variables; parseArgs(arguments, variables, description); - EXPECT_EQ(variables["load-savegame"].as().string(), "save.omwsave"); + EXPECT_EQ(variables["load-savegame"].as().string(), "save.omwsave"); } TEST(OpenMWOptionsFromArguments, should_support_multi_component_load_savegame_path) @@ -58,7 +58,7 @@ namespace const std::array arguments {"openmw", "--load-savegame", "/home/user/openmw/save.omwsave"}; bpo::variables_map variables; parseArgs(arguments, variables, description); - EXPECT_EQ(variables["load-savegame"].as().string(), "/home/user/openmw/save.omwsave"); + EXPECT_EQ(variables["load-savegame"].as().string(), "/home/user/openmw/save.omwsave"); } TEST(OpenMWOptionsFromArguments, should_support_windows_multi_component_load_savegame_path) @@ -67,18 +67,18 @@ namespace const std::array arguments {"openmw", "--load-savegame", R"(C:\OpenMW\save.omwsave)"}; bpo::variables_map variables; parseArgs(arguments, variables, description); - EXPECT_EQ(variables["load-savegame"].as().string(), R"(C:\OpenMW\save.omwsave)"); + EXPECT_EQ(variables["load-savegame"].as().string(), R"(C:\OpenMW\save.omwsave)"); } - /*TEST(OpenMWOptionsFromArguments, should_support_load_savegame_path_with_spaces) + TEST(OpenMWOptionsFromArguments, should_support_load_savegame_path_with_spaces) { bpo::options_description description = makeOptionsDescription(); const std::array arguments {"openmw", "--load-savegame", "my save.omwsave"}; bpo::variables_map variables; parseArgs(arguments, variables, description); - EXPECT_EQ(variables["load-savegame"].as().string(), "my"); -// EXPECT_EQ(variables["load-savegame"].as().string(), "my save.omwsave"); - }*/ + EXPECT_EQ(variables["load-savegame"].as().string(), "my"); +// EXPECT_EQ(variables["load-savegame"].as().string(), "my save.omwsave"); + } TEST(OpenMWOptionsFromArguments, should_support_load_savegame_path_with_octothorpe) { @@ -86,7 +86,7 @@ namespace const std::array arguments {"openmw", "--load-savegame", "my#save.omwsave"}; bpo::variables_map variables; parseArgs(arguments, variables, description); - EXPECT_EQ(variables["load-savegame"].as().string(), "my#save.omwsave"); + EXPECT_EQ(variables["load-savegame"].as().string(), "my#save.omwsave"); } TEST(OpenMWOptionsFromArguments, should_support_load_savegame_path_with_at_sign) @@ -95,7 +95,7 @@ namespace const std::array arguments {"openmw", "--load-savegame", "my@save.omwsave"}; bpo::variables_map variables; parseArgs(arguments, variables, description); - EXPECT_EQ(variables["load-savegame"].as().string(), "my@save.omwsave"); + EXPECT_EQ(variables["load-savegame"].as().string(), "my@save.omwsave"); } TEST(OpenMWOptionsFromArguments, should_support_load_savegame_path_with_quote) @@ -104,18 +104,18 @@ namespace const std::array arguments {"openmw", "--load-savegame", R"(my"save.omwsave)"}; bpo::variables_map variables; parseArgs(arguments, variables, description); - EXPECT_EQ(variables["load-savegame"].as().string(), R"(my"save.omwsave)"); + EXPECT_EQ(variables["load-savegame"].as().string(), R"(my"save.omwsave)"); } -/* TEST(OpenMWOptionsFromArguments, should_support_quoted_load_savegame_path) + TEST(OpenMWOptionsFromArguments, should_support_quoted_load_savegame_path) { bpo::options_description description = makeOptionsDescription(); const std::array arguments {"openmw", "--load-savegame", R"("save".omwsave)"}; bpo::variables_map variables; parseArgs(arguments, variables, description); - EXPECT_EQ(variables["load-savegame"].as().string(), R"(save)"); -// EXPECT_EQ(variables["load-savegame"].as().string(), R"("save".omwsave)"); - }*/ + EXPECT_EQ(variables["load-savegame"].as().string(), R"(save)"); +// EXPECT_EQ(variables["load-savegame"].as().string(), R"("save".omwsave)"); + } TEST(OpenMWOptionsFromArguments, should_support_quoted_load_savegame_path_with_escaped_quote_by_ampersand) { @@ -123,7 +123,7 @@ namespace const std::array arguments {"openmw", "--load-savegame", R"("save&".omwsave")"}; bpo::variables_map variables; parseArgs(arguments, variables, description); - EXPECT_EQ(variables["load-savegame"].as().string(), R"(save".omwsave)"); + EXPECT_EQ(variables["load-savegame"].as().string(), R"(save".omwsave)"); } TEST(OpenMWOptionsFromArguments, should_support_quoted_load_savegame_path_with_escaped_ampersand) @@ -132,7 +132,7 @@ namespace const std::array arguments {"openmw", "--load-savegame", R"("save.omwsave&&")"}; bpo::variables_map variables; parseArgs(arguments, variables, description); - EXPECT_EQ(variables["load-savegame"].as().string(), "save.omwsave&"); + EXPECT_EQ(variables["load-savegame"].as().string(), "save.omwsave&"); } TEST(OpenMWOptionsFromArguments, should_support_load_savegame_path_with_ampersand) @@ -141,7 +141,7 @@ namespace const std::array arguments {"openmw", "--load-savegame", "save&.omwsave"}; bpo::variables_map variables; parseArgs(arguments, variables, description); - EXPECT_EQ(variables["load-savegame"].as().string(), "save&.omwsave"); + EXPECT_EQ(variables["load-savegame"].as().string(), "save&.omwsave"); } TEST(OpenMWOptionsFromArguments, should_support_load_savegame_path_with_multiple_quotes) @@ -150,7 +150,7 @@ namespace const std::array arguments {"openmw", "--load-savegame", R"(my"save".omwsave)"}; bpo::variables_map variables; parseArgs(arguments, variables, description); - EXPECT_EQ(variables["load-savegame"].as().string(), R"(my"save".omwsave)"); + EXPECT_EQ(variables["load-savegame"].as().string(), R"(my"save".omwsave)"); } TEST(OpenMWOptionsFromArguments, should_compose_data) @@ -159,7 +159,7 @@ namespace const std::array arguments {"openmw", "--data", "1", "--data", "2"}; bpo::variables_map variables; parseArgs(arguments, variables, description); - EXPECT_THAT(variables["data"].as(), ElementsAre(IsPath("1"), IsPath("2"))); + EXPECT_THAT(variables["data"].as(), ElementsAre(IsPath("1"), IsPath("2"))); } TEST(OpenMWOptionsFromArguments, should_compose_data_from_single_flag) @@ -168,7 +168,7 @@ namespace const std::array arguments {"openmw", "--data", "1", "2"}; bpo::variables_map variables; parseArgs(arguments, variables, description); - EXPECT_THAT(variables["data"].as(), ElementsAre(IsPath("1"), IsPath("2"))); + EXPECT_THAT(variables["data"].as(), ElementsAre(IsPath("1"), IsPath("2"))); } TEST(OpenMWOptionsFromArguments, should_throw_on_multiple_load_savegame) @@ -189,7 +189,7 @@ namespace const std::array arguments {"openmw", "--load-savegame", pathArgument.c_str()}; bpo::variables_map variables; parseArgs(arguments, variables, description); - EXPECT_EQ(variables["load-savegame"].as().string(), path); + EXPECT_EQ(variables["load-savegame"].as().string(), path); } INSTANTIATE_TEST_SUITE_P( @@ -204,7 +204,7 @@ namespace std::istringstream stream("load-savegame=save.omwsave"); bpo::variables_map variables; Files::parseConfig(stream, variables, description); - EXPECT_EQ(variables["load-savegame"].as().string(), "save.omwsave"); + EXPECT_EQ(variables["load-savegame"].as().string(), "save.omwsave"); } TEST(OpenMWOptionsFromConfig, should_strip_quotes_from_load_savegame_path) @@ -213,18 +213,18 @@ namespace std::istringstream stream(R"(load-savegame="save.omwsave")"); bpo::variables_map variables; Files::parseConfig(stream, variables, description); - EXPECT_EQ(variables["load-savegame"].as().string(), "save.omwsave"); + EXPECT_EQ(variables["load-savegame"].as().string(), "save.omwsave"); } -/* TEST(OpenMWOptionsFromConfig, should_strip_outer_quotes_from_load_savegame_path) + TEST(OpenMWOptionsFromConfig, should_strip_outer_quotes_from_load_savegame_path) { bpo::options_description description = makeOptionsDescription(); std::istringstream stream(R"(load-savegame=""save".omwsave")"); bpo::variables_map variables; Files::parseConfig(stream, variables, description); - EXPECT_EQ(variables["load-savegame"].as().string(), ""); -// EXPECT_EQ(variables["load-savegame"].as().string(), R"(""save".omwsave")"); - }*/ + EXPECT_EQ(variables["load-savegame"].as().string(), ""); +// EXPECT_EQ(variables["load-savegame"].as().string(), R"(""save".omwsave")"); + } TEST(OpenMWOptionsFromConfig, should_strip_quotes_from_load_savegame_path_with_space) { @@ -232,7 +232,7 @@ namespace std::istringstream stream(R"(load-savegame="my save.omwsave")"); bpo::variables_map variables; Files::parseConfig(stream, variables, description); - EXPECT_EQ(variables["load-savegame"].as().string(), "my save.omwsave"); + EXPECT_EQ(variables["load-savegame"].as().string(), "my save.omwsave"); } TEST(OpenMWOptionsFromConfig, should_support_quoted_load_savegame_path_with_octothorpe) @@ -241,7 +241,7 @@ namespace std::istringstream stream("load-savegame=save#.omwsave"); bpo::variables_map variables; Files::parseConfig(stream, variables, description); - EXPECT_EQ(variables["load-savegame"].as().string(), "save#.omwsave"); + EXPECT_EQ(variables["load-savegame"].as().string(), "save#.omwsave"); } TEST(OpenMWOptionsFromConfig, should_support_quoted_load_savegame_path_with_at_sign) @@ -250,7 +250,7 @@ namespace std::istringstream stream("load-savegame=save@.omwsave"); bpo::variables_map variables; Files::parseConfig(stream, variables, description); - EXPECT_EQ(variables["load-savegame"].as().string(), "save@.omwsave"); + EXPECT_EQ(variables["load-savegame"].as().string(), "save@.omwsave"); } TEST(OpenMWOptionsFromConfig, should_support_quoted_load_savegame_path_with_quote) @@ -259,7 +259,25 @@ namespace std::istringstream stream(R"(load-savegame=save".omwsave)"); bpo::variables_map variables; Files::parseConfig(stream, variables, description); - EXPECT_EQ(variables["load-savegame"].as().string(), R"(save".omwsave)"); + EXPECT_EQ(variables["load-savegame"].as().string(), R"(save".omwsave)"); + } + + TEST(OpenMWOptionsFromConfig, should_support_confusing_savegame_path_with_lots_going_on) + { + bpo::options_description description = makeOptionsDescription(); + std::istringstream stream(R"(load-savegame="one &"two"three".omwsave")"); + bpo::variables_map variables; + Files::parseConfig(stream, variables, description); + EXPECT_EQ(variables["load-savegame"].as().string(), R"(one "two)"); + } + + TEST(OpenMWOptionsFromConfig, should_support_confusing_savegame_path_with_even_more_going_on) + { + bpo::options_description description = makeOptionsDescription(); + std::istringstream stream(R"(load-savegame="one &"two"three ".omwsave")"); + bpo::variables_map variables; + Files::parseConfig(stream, variables, description); + EXPECT_EQ(variables["load-savegame"].as().string(), R"(one "two)"); } TEST(OpenMWOptionsFromConfig, should_ignore_commented_option) @@ -268,7 +286,7 @@ namespace std::istringstream stream("#load-savegame=save.omwsave"); bpo::variables_map variables; Files::parseConfig(stream, variables, description); - EXPECT_EQ(variables["load-savegame"].as().string(), ""); + EXPECT_EQ(variables["load-savegame"].as().string(), ""); } TEST(OpenMWOptionsFromConfig, should_ignore_whitespace_prefixed_commented_option) @@ -277,7 +295,7 @@ namespace std::istringstream stream(" \t#load-savegame=save.omwsave"); bpo::variables_map variables; Files::parseConfig(stream, variables, description); - EXPECT_EQ(variables["load-savegame"].as().string(), ""); + EXPECT_EQ(variables["load-savegame"].as().string(), ""); } TEST(OpenMWOptionsFromConfig, should_support_whitespace_around_option) @@ -286,7 +304,7 @@ namespace std::istringstream stream(" load-savegame = save.omwsave "); bpo::variables_map variables; Files::parseConfig(stream, variables, description); - EXPECT_EQ(variables["load-savegame"].as().string(), "save.omwsave"); + EXPECT_EQ(variables["load-savegame"].as().string(), "save.omwsave"); } TEST(OpenMWOptionsFromConfig, should_throw_on_multiple_load_savegame) @@ -303,7 +321,7 @@ namespace std::istringstream stream("load-savegame=/home/user/openmw/save.omwsave"); bpo::variables_map variables; Files::parseConfig(stream, variables, description); - EXPECT_EQ(variables["load-savegame"].as().string(), "/home/user/openmw/save.omwsave"); + EXPECT_EQ(variables["load-savegame"].as().string(), "/home/user/openmw/save.omwsave"); } TEST(OpenMWOptionsFromConfig, should_support_windows_multi_component_load_savegame_path) @@ -312,7 +330,7 @@ namespace std::istringstream stream(R"(load-savegame=C:\OpenMW\save.omwsave)"); bpo::variables_map variables; Files::parseConfig(stream, variables, description); - EXPECT_EQ(variables["load-savegame"].as().string(), R"(C:\OpenMW\save.omwsave)"); + EXPECT_EQ(variables["load-savegame"].as().string(), R"(C:\OpenMW\save.omwsave)"); } TEST(OpenMWOptionsFromConfig, should_compose_data) @@ -321,7 +339,7 @@ namespace std::istringstream stream("data=1\ndata=2"); bpo::variables_map variables; Files::parseConfig(stream, variables, description); - EXPECT_THAT(variables["data"].as(), ElementsAre(IsPath("1"), IsPath("2"))); + EXPECT_THAT(variables["data"].as(), ElementsAre(IsPath("1"), IsPath("2"))); } TEST(OpenMWOptionsFromConfig, should_support_quoted_load_savegame_path_with_escaped_quote_by_ampersand) @@ -330,7 +348,7 @@ namespace std::istringstream stream(R"(load-savegame="save&".omwsave")"); bpo::variables_map variables; Files::parseConfig(stream, variables, description); - EXPECT_EQ(variables["load-savegame"].as().string(), R"(save".omwsave)"); + EXPECT_EQ(variables["load-savegame"].as().string(), R"(save".omwsave)"); } TEST(OpenMWOptionsFromConfig, should_support_quoted_load_savegame_path_with_escaped_ampersand) @@ -339,7 +357,7 @@ namespace std::istringstream stream(R"(load-savegame="save.omwsave&&")"); bpo::variables_map variables; Files::parseConfig(stream, variables, description); - EXPECT_EQ(variables["load-savegame"].as().string(), "save.omwsave&"); + EXPECT_EQ(variables["load-savegame"].as().string(), "save.omwsave&"); } TEST(OpenMWOptionsFromConfig, should_support_load_savegame_path_with_ampersand) @@ -348,7 +366,7 @@ namespace std::istringstream stream("load-savegame=save&.omwsave"); bpo::variables_map variables; Files::parseConfig(stream, variables, description); - EXPECT_EQ(variables["load-savegame"].as().string(), "save&.omwsave"); + EXPECT_EQ(variables["load-savegame"].as().string(), "save&.omwsave"); } struct OpenMWOptionsFromConfigStrings : TestWithParam {}; @@ -360,7 +378,7 @@ namespace std::istringstream stream("load-savegame=\"" + path + "\""); bpo::variables_map variables; Files::parseConfig(stream, variables, description); - EXPECT_EQ(variables["load-savegame"].as().string(), path); + EXPECT_EQ(variables["load-savegame"].as().string(), path); } INSTANTIATE_TEST_SUITE_P( diff --git a/components/files/configurationmanager.cpp b/components/files/configurationmanager.cpp index 23c25fd413..b04b211967 100644 --- a/components/files/configurationmanager.cpp +++ b/components/files/configurationmanager.cpp @@ -138,10 +138,10 @@ void mergeComposingVariables(boost::program_options::variables_map& first, boost boost::any& firstValue = firstPosition->second.value(); const boost::any& secondValue = second[name].value(); - if (firstValue.type() == typeid(Files::PathContainer)) + if (firstValue.type() == typeid(Files::ReluctantPathContainer)) { - auto& firstPathContainer = boost::any_cast(firstValue); - const auto& secondPathContainer = boost::any_cast(secondValue); + auto& firstPathContainer = boost::any_cast(firstValue); + const auto& secondPathContainer = boost::any_cast(secondValue); firstPathContainer.insert(firstPathContainer.end(), secondPathContainer.begin(), secondPathContainer.end()); } @@ -317,4 +317,22 @@ void parseConfig(std::istream& stream, boost::program_options::variables_map& va ); } +std::istream& operator>> (std::istream& istream, ReluctantPath& reluctantPath) +{ + // Read from stream using boost::filesystem::path rules, then discard anything remaining. + // This prevents boost::program_options getting upset that we've not consumed the whole stream. + istream >> static_cast(reluctantPath); + if (istream && !istream.eof() && istream.peek() != EOF) + { + std::string remainder(std::istreambuf_iterator(istream), {}); + Log(Debug::Warning) << "Trailing data in path setting. Used '" << reluctantPath.string() << "' but '" << remainder << "' remained"; + } + return istream; +} + +PathContainer asPathContainer(const ReluctantPathContainer& reluctantPathContainer) +{ + return PathContainer(reluctantPathContainer.begin(), reluctantPathContainer.end()); +} + } /* namespace Cfg */ diff --git a/components/files/configurationmanager.hpp b/components/files/configurationmanager.hpp index 99e65a7658..8f3e5f938e 100644 --- a/components/files/configurationmanager.hpp +++ b/components/files/configurationmanager.hpp @@ -77,6 +77,18 @@ void parseArgs(int argc, const char* const argv[], boost::program_options::varia void parseConfig(std::istream& stream, boost::program_options::variables_map& variables, boost::program_options::options_description& description); +class ReluctantPath : public boost::filesystem::path +{ +public: + operator boost::filesystem::path() { return *this; } +}; + +std::istream& operator>> (std::istream& istream, ReluctantPath& reluctantPath); + +typedef std::vector ReluctantPathContainer; + +PathContainer asPathContainer(const ReluctantPathContainer& reluctantPathContainer); + } /* namespace Cfg */ #endif /* COMPONENTS_FILES_CONFIGURATIONMANAGER_HPP */