src/lib/INIReader.cpp
branchv_0
changeset 20 9187f0439ca9
parent 19 967f73af64a4
child 21 b35baebf5005
equal deleted inserted replaced
19:967f73af64a4 20:9187f0439ca9
    68 	 *  - https://userbase.kde.org/KDE_System_Administration/Configuration_Files#Shell_Expansion
    68 	 *  - https://userbase.kde.org/KDE_System_Administration/Configuration_Files#Shell_Expansion
    69 	 *  - https://specifications.freedesktop.org/desktop-entry-spec/latest/ar01s05.html
    69 	 *  - https://specifications.freedesktop.org/desktop-entry-spec/latest/ar01s05.html
    70 	 */
    70 	 */
    71 	bool allowSubKeys = true;
    71 	bool allowSubKeys = true;
    72 
    72 
       
    73 	/**
       
    74 	 * This might be configurable.
       
    75 	 * 
       
    76 	 * Classic INI uses „key=value“ syntax.
       
    77 	 * But some other formats/dialects might use key:value.
       
    78 	 * 
       
    79 	 * Only single character separators are supported.
       
    80 	 * If multiple separators should be recognized (e.g. both „=“ and „:“), this string will contain all of them,
       
    81 	 * i.e. „:=“ does not mean that the „key:=value“ syntax, but „key=value“ or „key:value“.
       
    82 	 */
       
    83 	std::string keyValueSeparators = "=";
       
    84 
    73 	int lineNumber = 1;
    85 	int lineNumber = 1;
    74 	int eventNumber = 0;
    86 	int eventNumber = 0;
    75 
    87 
    76 	/**
    88 	/**
    77 	 * Should be always used instead of input.peek().
    89 	 * Should be always used instead of input.peek().
   113 	void processContinuingLine(std::stringstream& result) {
   125 	void processContinuingLine(std::stringstream& result) {
   114 		if (consumeLeadingSpacesOnContinuingLines) readSpacesAndTabs();
   126 		if (consumeLeadingSpacesOnContinuingLines) readSpacesAndTabs();
   115 		else result.put('\n');
   127 		else result.put('\n');
   116 	}
   128 	}
   117 
   129 
   118 	std::string readUntil(char until, bool* found = nullptr) {
   130 	std::string readUntil(const char until, bool* found = nullptr) {
       
   131 		return readUntil(std::string(1, until), found);
       
   132 	}
       
   133 
       
   134 	std::string readUntil(const std::string& until, bool* found = nullptr) {
   119 		std::stringstream result;
   135 		std::stringstream result;
   120 
   136 
   121 		for (char ch = peek(); input.good() && ch != until; ch = peek()) {
   137 		for (char ch = peek(); input.good() && !oneOf(ch, until); ch = peek()) {
   122 			if (ch == '\\') {
   138 			if (ch == '\\') {
   123 				get();
   139 				get();
   124 				ch = get();
   140 				ch = get();
   125 				if (ch == until && ch == '\n') processContinuingLine(result);
   141 				if (oneOf(ch, until) && ch == '\n') processContinuingLine(result);
   126 				else if (ch == until) result.put(ch);
   142 				else if (oneOf(ch, until)) result.put(ch);
   127 				else if (ch == std::istream::traits_type::eof()) break;
   143 				else if (ch == std::istream::traits_type::eof()) break;
   128 				else result.put('\\').put(ch);
   144 				else result.put('\\').put(ch);
   129 				// TODO: two-stage and modular unescaping: here unescape only \+LF or more genereally: unescape only the until character and rest leave untouched
   145 				// unescaping is done in two phases:
   130 				// second escaping stage move to separate class/wrapper (similar to hierarchical wrappers)
   146 				// here we unescape just the \n (LF)
       
   147 				// other escape sequences are leaved untouched and will be processed in later phases, see see UnescapingINIHandler
   131 			} else {
   148 			} else {
   132 				ch = get();
   149 				ch = get();
   133 				result.put(ch);
   150 				result.put(ch);
   134 			}
   151 			}
   135 		}
   152 		}
   136 
   153 
   137 		if (peek() == until) {
   154 		if (oneOf(peek(), until)) {
   138 			get();
   155 			get();
   139 			if (found) *found = true;
   156 			if (found) *found = true;
   140 		} else {
   157 		} else {
   141 			if (found) *found = false;
   158 			if (found) *found = false;
   142 		}
   159 		}
   143 
   160 
   144 		return result.str();
   161 		return result.str();
   145 	}
   162 	}
   146 
   163 
   147 	std::string readToken(char until, char* quote = nullptr, bool* found = nullptr) {
   164 	std::string readToken(const char until, char* quote = nullptr, bool* found = nullptr) {
       
   165 		return readToken(std::string(1, until), quote, found);
       
   166 	}
       
   167 
       
   168 	std::string readToken(const std::string& until, char* quote = nullptr, bool* found = nullptr) {
   148 		std::string result;
   169 		std::string result;
   149 
   170 
   150 		char ch = peek();
   171 		char ch = peek();
   151 		if (isQuote(ch)) {
   172 		if (isQuote(ch)) {
   152 			if (quote) *quote = ch;
   173 			if (quote) *quote = ch;
   153 			result = readUntil(get(), found);
   174 			result = readUntil(std::string(1, get()), found);
   154 		} else {
   175 		} else {
   155 			if (quote) *quote = 0;
   176 			if (quote) *quote = 0;
   156 			result = readUntil(until, found);
   177 			result = readUntil(until, found);
   157 		}
   178 		}
   158 
   179 
   159 		return result;
   180 		return result;
   160 	}
   181 	}
   161 
   182 
   162 	std::string readTokenAndEatTerminator(char until, char* quote = nullptr, bool* found = nullptr) {
   183 	std::string readTokenAndEatTerminator(char until, char* quote = nullptr, bool* found = nullptr) {
       
   184 		return readTokenAndEatTerminator(std::string(1, until), quote, found);
       
   185 	}
       
   186 
       
   187 	std::string readTokenAndEatTerminator(const std::string& until, char* quote = nullptr, bool* found = nullptr) {
   163 		std::string result = readToken(until, quote, found);
   188 		std::string result = readToken(until, quote, found);
   164 		if (*quote) {
   189 		if (*quote) {
   165 			readAllWhitespace();
   190 			readAllWhitespace();
   166 			if (get() != until) throw std::logic_error(std::string("missing „") + std::string(1, until) + "“ after quoted section name");
   191 			if (!oneOf(get(), until)) throw std::logic_error(std::string("missing „") + until + "“ after quoted section name");
   167 		}
   192 		}
   168 		return result;
   193 		return result;
   169 	}
   194 	}
   170 
   195 
   171 	bool isComment(char ch) {
   196 	bool isComment(char ch) {
   172 		return ch == '#' || ch == ';';
   197 		return ch == '#' || ch == ';';
   173 	}
   198 	}
   174 
   199 
   175 	bool isQuote(char ch) {
   200 	bool isQuote(char ch) {
   176 		return ch == '"' || ch == '\'';
   201 		return ch == '"' || ch == '\'';
       
   202 	}
       
   203 
       
   204 	/**
       
   205 	 * @param ch character to be evaluated
       
   206 	 * @param options list of options (characters)
       
   207 	 * @return whether ch is one of options
       
   208 	 */
       
   209 	bool oneOf(char ch, const std::string& options) {
       
   210 		return options.find(ch) != std::string::npos;
   177 	}
   211 	}
   178 
   212 
   179 	std::string trim(std::string s) {
   213 	std::string trim(std::string s) {
   180 		return std::regex_replace(s, std::regex("^\\s+|\\s+$"), "");
   214 		return std::regex_replace(s, std::regex("^\\s+|\\s+$"), "");
   181 	}
   215 	}
   253 			} else {
   287 			} else {
   254 				INIContentHandler::EntryEvent event;
   288 				INIContentHandler::EntryEvent event;
   255 				event.lineNumber = lineNumber;
   289 				event.lineNumber = lineNumber;
   256 				event.eventNumber = ++eventNumber;
   290 				event.eventNumber = ++eventNumber;
   257 
   291 
   258 				std::string fullKey = readToken('=', &quote, &found);
   292 				std::string fullKey = readToken(keyValueSeparators, &quote, &found);
   259 				if (!found) throw std::logic_error(std::string("missing = after key: '") + fullKey + "'");
   293 				if (!found) throw std::logic_error(std::string("missing = after key: '") + fullKey + "'");
   260 				if (!quote) fullKey = trim(fullKey);
   294 				if (!quote) fullKey = trim(fullKey);
   261 				readSpacesAndTabs();
   295 				readSpacesAndTabs();
   262 
   296 
   263 				if (quote) {
   297 				if (quote) {
   264 					ch = get();
   298 					ch = get();
   265 					if (ch == '=') readSpacesAndTabs();
   299 					if (oneOf(ch, keyValueSeparators)) readSpacesAndTabs();
   266 					else throw std::logic_error(std::string("missing = after quoted key: '") + fullKey + "'");
   300 					else throw std::logic_error(std::string("missing = after quoted key: '") + fullKey + "'");
   267 				}
   301 				}
   268 
   302 
   269 				std::string value = readToken('\n', &quote, &found);
   303 				std::string value = readToken('\n', &quote, &found);
   270 				if (!quote) value = trim(value);
   304 				if (!quote) value = trim(value);