Skip to content

Commit 2da9d5a

Browse files
committed
Remove existing duplicate config lines when saving config
SaveConfig previously rewrote every config line whose option name matched a save request entry, so duplicate lines accumulated by earlier versions (issue #588) were preserved forever even though new appends were prevented. Write each option only once in the replacement path as well, so a single save converges a damaged config back to one line per option. Add a regression test covering exact and case-variant duplicate lines.
1 parent 4e2fde5 commit 2da9d5a

2 files changed

Lines changed: 41 additions & 1 deletion

File tree

daemon/extension/ScriptConfig.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,9 @@ bool ScriptConfig::SaveConfig(Options::OptEntries* optEntries)
118118
if (g_Options->SplitOptionString(buf, optname, optvalue))
119119
{
120120
Options::OptEntry* optEntry = optEntries->FindOption(optname);
121-
if (optEntry)
121+
// write each option only once, dropping duplicate lines accumulated
122+
// in the config file by earlier versions (issue #588)
123+
if (optEntry && writtenOptions.find(optEntry->GetName()) == writtenOptions.end())
122124
{
123125
infile.Print("%s=%s\n", optEntry->GetName(), optEntry->GetValue());
124126
writtenOptions.insert(optEntry->GetName());

tests/extension/ScriptConfig.cpp

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,4 +78,42 @@ BOOST_AUTO_TEST_CASE(SaveConfigDoesNotAppendDuplicateOptionNames)
7878
BOOST_CHECK_EQUAL(secondContents, firstContents);
7979
}
8080

81+
BOOST_AUTO_TEST_CASE(SaveConfigRemovesDuplicateLinesFromDamagedConfig)
82+
{
83+
TempConfigFile configFile;
84+
{
85+
std::ofstream output(configFile.path);
86+
output << "# existing config\n"
87+
<< "Server1.Active=yes\n"
88+
<< "Server2.Active=yes\n"
89+
<< "Server2.Active=yes\n"
90+
<< "server2.active=yes\n"
91+
<< "Server2.Host=news.example.com\n";
92+
}
93+
94+
OptionsGuard optionsGuard;
95+
Options options("nzbget", configFile.path.string().c_str(), true, nullptr, nullptr);
96+
g_Options = &options;
97+
98+
Options::OptEntries optEntries;
99+
optEntries.emplace_back("Server1.Active", "yes");
100+
optEntries.emplace_back("Server2.Active", "no");
101+
optEntries.emplace_back("Server2.Host", "news.example.com");
102+
ScriptConfig scriptConfig;
103+
104+
BOOST_REQUIRE(scriptConfig.SaveConfig(&optEntries));
105+
std::string contents;
106+
{
107+
std::ifstream file(configFile.path);
108+
std::stringstream buffer;
109+
buffer << file.rdbuf();
110+
contents = buffer.str();
111+
}
112+
BOOST_CHECK_EQUAL(contents,
113+
"# existing config\n"
114+
"Server1.Active=yes\n"
115+
"Server2.Active=no\n"
116+
"Server2.Host=news.example.com\n");
117+
}
118+
81119
BOOST_AUTO_TEST_SUITE_END()

0 commit comments

Comments
 (0)