# HG changeset patch # User František Kučera # Date 1606411707 -3600 # Node ID 9187f0439ca9a94e105bb0a5e69a22fb15717004 # Parent 967f73af64a4020e9da6f171b1905f191bf0db84 prepare for multiple and configurable key-value separators (e.g. key=value and key:value) diff -r 967f73af64a4 -r 9187f0439ca9 src/lib/INIReader.cpp --- a/src/lib/INIReader.cpp Thu Nov 26 17:08:49 2020 +0100 +++ b/src/lib/INIReader.cpp Thu Nov 26 18:28:27 2020 +0100 @@ -70,6 +70,18 @@ */ bool allowSubKeys = true; + /** + * This might be configurable. + * + * Classic INI uses „key=value“ syntax. + * But some other formats/dialects might use key:value. + * + * Only single character separators are supported. + * If multiple separators should be recognized (e.g. both „=“ and „:“), this string will contain all of them, + * i.e. „:=“ does not mean that the „key:=value“ syntax, but „key=value“ or „key:value“. + */ + std::string keyValueSeparators = "="; + int lineNumber = 1; int eventNumber = 0; @@ -115,26 +127,31 @@ else result.put('\n'); } - std::string readUntil(char until, bool* found = nullptr) { + std::string readUntil(const char until, bool* found = nullptr) { + return readUntil(std::string(1, until), found); + } + + std::string readUntil(const std::string& until, bool* found = nullptr) { std::stringstream result; - for (char ch = peek(); input.good() && ch != until; ch = peek()) { + for (char ch = peek(); input.good() && !oneOf(ch, until); ch = peek()) { if (ch == '\\') { get(); ch = get(); - if (ch == until && ch == '\n') processContinuingLine(result); - else if (ch == until) result.put(ch); + if (oneOf(ch, until) && ch == '\n') processContinuingLine(result); + else if (oneOf(ch, until)) result.put(ch); else if (ch == std::istream::traits_type::eof()) break; else result.put('\\').put(ch); - // TODO: two-stage and modular unescaping: here unescape only \+LF or more genereally: unescape only the until character and rest leave untouched - // second escaping stage move to separate class/wrapper (similar to hierarchical wrappers) + // unescaping is done in two phases: + // here we unescape just the \n (LF) + // other escape sequences are leaved untouched and will be processed in later phases, see see UnescapingINIHandler } else { ch = get(); result.put(ch); } } - if (peek() == until) { + if (oneOf(peek(), until)) { get(); if (found) *found = true; } else { @@ -144,13 +161,17 @@ return result.str(); } - std::string readToken(char until, char* quote = nullptr, bool* found = nullptr) { + std::string readToken(const char until, char* quote = nullptr, bool* found = nullptr) { + return readToken(std::string(1, until), quote, found); + } + + std::string readToken(const std::string& until, char* quote = nullptr, bool* found = nullptr) { std::string result; char ch = peek(); if (isQuote(ch)) { if (quote) *quote = ch; - result = readUntil(get(), found); + result = readUntil(std::string(1, get()), found); } else { if (quote) *quote = 0; result = readUntil(until, found); @@ -160,10 +181,14 @@ } std::string readTokenAndEatTerminator(char until, char* quote = nullptr, bool* found = nullptr) { + return readTokenAndEatTerminator(std::string(1, until), quote, found); + } + + std::string readTokenAndEatTerminator(const std::string& until, char* quote = nullptr, bool* found = nullptr) { std::string result = readToken(until, quote, found); if (*quote) { readAllWhitespace(); - if (get() != until) throw std::logic_error(std::string("missing „") + std::string(1, until) + "“ after quoted section name"); + if (!oneOf(get(), until)) throw std::logic_error(std::string("missing „") + until + "“ after quoted section name"); } return result; } @@ -176,6 +201,15 @@ return ch == '"' || ch == '\''; } + /** + * @param ch character to be evaluated + * @param options list of options (characters) + * @return whether ch is one of options + */ + bool oneOf(char ch, const std::string& options) { + return options.find(ch) != std::string::npos; + } + std::string trim(std::string s) { return std::regex_replace(s, std::regex("^\\s+|\\s+$"), ""); } @@ -255,14 +289,14 @@ event.lineNumber = lineNumber; event.eventNumber = ++eventNumber; - std::string fullKey = readToken('=', "e, &found); + std::string fullKey = readToken(keyValueSeparators, "e, &found); if (!found) throw std::logic_error(std::string("missing = after key: '") + fullKey + "'"); if (!quote) fullKey = trim(fullKey); readSpacesAndTabs(); if (quote) { ch = get(); - if (ch == '=') readSpacesAndTabs(); + if (oneOf(ch, keyValueSeparators)) readSpacesAndTabs(); else throw std::logic_error(std::string("missing = after quoted key: '") + fullKey + "'"); }