# HG changeset patch # User František Kučera # Date 1607780556 -3600 # Node ID 372b161669e4a97b9d25ca47d2e85c60d8f75ee9 # Parent ae8775e0bc7a7323020702405840b146699e5eef configurable quoting diff -r ae8775e0bc7a -r 372b161669e4 bash-completion.sh --- a/bash-completion.sh Sat Dec 12 00:01:57 2020 +0100 +++ b/bash-completion.sh Sat Dec 12 14:42:36 2020 +0100 @@ -40,6 +40,12 @@ "java-manifest-mf" ); + QUOTING_TYPES=( + "none" + "apostrophes" + "quotes" + ) + WRITER_OPTIONS=( "dialect" "comment-separator-for-sections" @@ -49,6 +55,12 @@ "escape-backspace" "escape-basic" "escape-java-properties" + "quotes-type-for-sections" + "quotes-type-for-keys" + "quotes-type-for-values" + "quotes-pattern-for-sections" + "quotes-pattern-for-keys" + "quotes-pattern-for-values" ); @@ -63,6 +75,9 @@ elif [[ "$w2" == "--writer-option" && "$w1" == "escape-backspace" ]]; then COMPREPLY=($(compgen -W "${BOOLEAN_VALUES[*]}" -- "$w0")) elif [[ "$w2" == "--writer-option" && "$w1" == "escape-basic" ]]; then COMPREPLY=($(compgen -W "${BOOLEAN_VALUES[*]}" -- "$w0")) elif [[ "$w2" == "--writer-option" && "$w1" == "escape-java-properties" ]]; then COMPREPLY=($(compgen -W "${BOOLEAN_VALUES[*]}" -- "$w0")) + elif [[ "$w2" == "--writer-option" && "$w1" == "quotes-type-for-sections" ]]; then COMPREPLY=($(compgen -W "${QUOTING_TYPES[*]}" -- "$w0")) + elif [[ "$w2" == "--writer-option" && "$w1" == "quotes-type-for-keys" ]]; then COMPREPLY=($(compgen -W "${QUOTING_TYPES[*]}" -- "$w0")) + elif [[ "$w2" == "--writer-option" && "$w1" == "quotes-type-for-values" ]]; then COMPREPLY=($(compgen -W "${QUOTING_TYPES[*]}" -- "$w0")) elif [[ "$w2" == "--writer-option" && "x$w0" == "x" ]]; then COMPREPLY=("''") else diff -r ae8775e0bc7a -r 372b161669e4 src/BasicEscapingProcessor.h --- a/src/BasicEscapingProcessor.h Sat Dec 12 00:01:57 2020 +0100 +++ b/src/BasicEscapingProcessor.h Sat Dec 12 14:42:36 2020 +0100 @@ -41,6 +41,7 @@ else if (ch == L'\t') result.put(ESC).put(L't'); else if (ch == L'"' && quotingType != QuotingType::Apostrophes) result.put(ESC).put(ch); else if (ch == L'\'' && quotingType != QuotingType::Quotes) result.put(ESC).put(ch); + else if (ch == L'=' && quotingType == QuotingType::None) result.put(ESC).put(ch); else result.put(ch); } diff -r ae8775e0bc7a -r 372b161669e4 src/INIWriter.h --- a/src/INIWriter.h Sat Dec 12 00:01:57 2020 +0100 +++ b/src/INIWriter.h Sat Dec 12 14:42:36 2020 +0100 @@ -18,6 +18,7 @@ #include #include +#include #include #include @@ -51,10 +52,43 @@ else throw relpipe::reader::RelpipeReaderException(L"Unable to parse boolean value: " + value + L" (expecting true or false)"); } + EscapingProcessor::QuotingType parseQuotingType(const relpipe::common::type::StringX& value) { + if (value == quoting::None) return EscapingProcessor::QuotingType::None; + else if (value == quoting::Apostrophes) return EscapingProcessor::QuotingType::Apostrophes; + else if (value == quoting::Quotes) return EscapingProcessor::QuotingType::Quotes; + else throw relpipe::reader::RelpipeReaderException(L"Unsupported quoting type:" + value); + } + + class QuotingRule { + public: + EscapingProcessor::QuotingType type = EscapingProcessor::QuotingType::None; + relpipe::common::type::StringX pattern; + + EscapingProcessor::QuotingType findEffectiveQuotingType(const relpipe::common::type::StringX& value) { + if (type == EscapingProcessor::QuotingType::None) return type; + else if (pattern.empty()) return type; + else return std::regex_match(value, std::wregex(pattern)) ? type : EscapingProcessor::QuotingType::None; + } + }; + + QuotingRule quotingForSections; + QuotingRule quotingForKeys; + QuotingRule quotingForValues; + std::string escape(const relpipe::common::type::StringX& value, EscapingProcessor::TextType type) { relpipe::common::type::StringX result = value; - EscapingProcessor::QuotingType quotingType = EscapingProcessor::QuotingType::None; + + EscapingProcessor::QuotingType quotingType; + if (type == EscapingProcessor::TextType::SectionName || type == EscapingProcessor::TextType::SectionTag) quotingType = quotingForSections.findEffectiveQuotingType(value); + else if (type == EscapingProcessor::TextType::EntryKey || type == EscapingProcessor::TextType::EntrySubKey) quotingType = quotingForKeys.findEffectiveQuotingType(value); + else if (type == EscapingProcessor::TextType::EntryValue) quotingType = quotingForValues.findEffectiveQuotingType(value); + else quotingType = EscapingProcessor::QuotingType::None; + for (ConfiguredEscapingProcessor p : escapingProcessors) if (p.enbaled) result = p.processor->escape(result, type, quotingType); + + if (quotingType == EscapingProcessor::QuotingType::Quotes) result = L"\"" + result + L"\""; + else if (quotingType == EscapingProcessor::QuotingType::Apostrophes) result = L"'" + result + L"'"; + return convertor.to_bytes(result); } @@ -137,14 +171,18 @@ }; void setOption(relpipe::common::type::StringX uri, relpipe::common::type::StringX value) { - // TODO: setOption(): escaping, quotes, allow-sections if (uri == option::Dialect) setDialect(value); else if (uri == option::CommentSeparatorForSections) commentSeparatorForSections = convertor.to_bytes(value); 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); - else if (uri == option::Quotes); + else if (uri == option::AllowSections); // TODO: allow sections + 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); + else if (uri == option::QuotesPatternForSections) quotingForSections.pattern = value; + else if (uri == option::QuotesPatternForKeys) quotingForKeys.pattern = value; + else if (uri == option::QuotesPatternForValues) quotingForValues.pattern = value; else if (setEscaping(uri, value)); else throw relpipe::reader::RelpipeReaderException(L"Unsupported writer option: " + uri); } diff -r ae8775e0bc7a -r 372b161669e4 src/JavaPropertiesDialect.h --- a/src/JavaPropertiesDialect.h Sat Dec 12 00:01:57 2020 +0100 +++ b/src/JavaPropertiesDialect.h Sat Dec 12 14:42:36 2020 +0100 @@ -30,7 +30,9 @@ void apply(INIWriter& writer) override { writer.setOption(option::AllowSections, L"false"); - writer.setOption(option::Quotes, L""); + writer.setOption(option::QuotesTypeForSections, quoting::None); + writer.setOption(option::QuotesTypeForKeys, quoting::None); + writer.setOption(option::QuotesTypeForValues, quoting::None); writer.setOption(option::KeyValueSeparator, L"="); writer.setOption(option::CommentSeparatorForSections, L""); writer.setOption(option::CommentSeparatorForEntries, L""); diff -r ae8775e0bc7a -r 372b161669e4 src/uri.h --- a/src/uri.h Sat Dec 12 00:01:57 2020 +0100 +++ b/src/uri.h Sat Dec 12 14:42:36 2020 +0100 @@ -31,7 +31,12 @@ static const wchar_t* CommentSeparatorForEntries = L"comment-separator-for-entries"; static const wchar_t* CommentSeparatorStandalone = L"comment-separator-standalone"; static const wchar_t* KeyValueSeparator = L"key-value-separator"; -static const wchar_t* Quotes = L"quotes"; +static const wchar_t* QuotesTypeForSections = L"quotes-type-for-sections"; +static const wchar_t* QuotesTypeForKeys = L"quotes-type-for-keys"; +static const wchar_t* QuotesTypeForValues = L"quotes-type-for-values"; +static const wchar_t* QuotesPatternForSections = L"quotes-pattern-for-sections"; +static const wchar_t* QuotesPatternForKeys = L"quotes-pattern-for-keys"; +static const wchar_t* QuotesPatternForValues = L"quotes-pattern-for-values"; static const wchar_t* Dialect = L"dialect"; } @@ -42,6 +47,13 @@ static const wchar_t* Backspace = L"escape-backspace"; } +/** not options but a values of the quotes-type-for-* options */ +namespace quoting { +static const wchar_t* None = L"none"; +static const wchar_t* Apostrophes = L"apostrophes"; +static const wchar_t* Quotes = L"quotes"; +} + /** not options but a values of the dialect option */ namespace dialect { static const wchar_t* JavaProperties = L"java-properties";