diff -r 372b161669e4 -r bee7acb57330 src/INIWriter.h --- a/src/INIWriter.h Sat Dec 12 14:42:36 2020 +0100 +++ b/src/INIWriter.h Sat Dec 12 19:52:38 2020 +0100 @@ -41,8 +41,26 @@ std::string commentSeparatorForEntries = " ; "; std::string commentSeparatorStandalone = "; "; + relpipe::common::type::StringX hierarchySeparator = L"/"; + bool allowSections = true; + bool hasContent = false; + std::vector currentSection; + + relpipe::common::type::StringX getCurrentSectionFullName() { + std::wstringstream result; + + // TODO: configurable hierarchy delimiter + // TODO: escape delimiter characters that are part of the section names + for (int i = 0; i < currentSection.size(); i++) { + if (i > 0)result << hierarchySeparator; + result << currentSection[i]; + } + + return result.str(); + } + /** * TODO: use a common method */ @@ -176,7 +194,8 @@ else if (uri == option::CommentSeparatorForEntries) commentSeparatorForEntries = convertor.to_bytes(value); else if (uri == option::CommentSeparatorStandalone) commentSeparatorStandalone = convertor.to_bytes(value); else if (uri == option::KeyValueSeparator) keyValueSeparator = convertor.to_bytes(value); - else if (uri == option::AllowSections); // TODO: allow sections + else if (uri == option::HierarchySeparator) hierarchySeparator = value; + else if (uri == option::AllowSections) allowSections = parseBoolean(value); else if (uri == option::QuotesTypeForSections) quotingForSections.type = parseQuotingType(value); else if (uri == option::QuotesTypeForKeys) quotingForKeys.type = parseQuotingType(value); else if (uri == option::QuotesTypeForValues) quotingForValues.type = parseQuotingType(value); @@ -200,23 +219,32 @@ } void endDocument() { + if (currentSection.size()) throw relpipe::reader::RelpipeReaderException(L"There are still " + std::to_wstring(currentSection.size()) + L" open sections during the endDocument() call. Need to call endSection() before."); output.flush(); } void startSection(const SectionStartEvent& event) { - if (hasContent) output << std::endl; - output << "[" << escape(event.name, EscapingProcessor::TextType::SectionName) << "]"; - if (event.tag.size()) output << "[" << escape(event.tag, EscapingProcessor::TextType::SectionTag) << "]"; - if (event.comment.size()) output << commentSeparatorForSections << escape(event.comment, EscapingProcessor::TextType::SectionComment); - output << std::endl; + currentSection.push_back(event.name); + + if (allowSections) { + if (hasContent) output << std::endl; + output << "[" << escape(getCurrentSectionFullName(), EscapingProcessor::TextType::SectionName) << "]"; + if (event.tag.size()) output << "[" << escape(event.tag, EscapingProcessor::TextType::SectionTag) << "]"; + if (event.comment.size()) output << commentSeparatorForSections << escape(event.comment, EscapingProcessor::TextType::SectionComment); + output << std::endl; + } + hasContent = true; } void endSection() { + currentSection.pop_back(); output.flush(); } void entry(const EntryEvent& event) { + // TODO: escape/quote parts separately + configurable quoting of parts or whole + the same for sub keys + if (!allowSections && currentSection.size()) output << escape(getCurrentSectionFullName(), EscapingProcessor::TextType::EntryKey) << convertor.to_bytes(hierarchySeparator); output << escape(event.key, EscapingProcessor::TextType::EntryKey); if (event.subKey.size()) output << "[" << escape(event.subKey, EscapingProcessor::TextType::EntrySubKey) << "]"; output << keyValueSeparator << escape(event.value, EscapingProcessor::TextType::EntryValue);