30 class INIReaderImpl : public INIReader { |
30 class INIReaderImpl : public INIReader { |
31 private: |
31 private: |
32 std::istream& input; |
32 std::istream& input; |
33 std::vector<INIContentHandler*> handlers; |
33 std::vector<INIContentHandler*> handlers; |
34 |
34 |
|
35 class ConfiguredUnescapingProcessor { |
|
36 public: |
|
37 std::shared_ptr<UnescapingProcessor> processor; |
|
38 const std::string uri; |
|
39 bool enbaled; |
|
40 |
|
41 ConfiguredUnescapingProcessor(std::shared_ptr<UnescapingProcessor> processor, const std::string uri, bool enbaled) : processor(processor), uri(uri), enbaled(enbaled) { |
|
42 } |
|
43 |
|
44 }; |
|
45 |
|
46 std::vector<ConfiguredUnescapingProcessor> unescapingProcessors; |
|
47 |
35 /** |
48 /** |
36 * By default, we ignore all leading whitespace on continuing lines. |
49 * By default, we ignore all leading whitespace on continuing lines. |
37 * If there should be some spaces or tabs, they should be placed on the previous line before the „\“. |
50 * If there should be some spaces or tabs, they should be placed on the previous line before the „\“. |
38 * If a line break is desired, it should be written as \n (escaped) or the value should be quoted in " or '. |
51 * If a line break is desired, it should be written as \n (escaped) or the value should be quoted in " or '. |
39 * |
52 * |
219 if (!oneOf(get(), until)) throw std::logic_error(std::string("missing „") + until + "“ after quoted section name"); |
232 if (!oneOf(get(), until)) throw std::logic_error(std::string("missing „") + until + "“ after quoted section name"); |
220 } |
233 } |
221 return result; |
234 return result; |
222 } |
235 } |
223 |
236 |
|
237 std::string unescape(const std::string& value, UnescapingProcessor::TextType type) { |
|
238 std::string result = value; |
|
239 for (ConfiguredUnescapingProcessor p : unescapingProcessors) if (p.enbaled) result = p.processor->unescape(result, type); |
|
240 return result; |
|
241 } |
|
242 |
224 bool isComment(char ch) { |
243 bool isComment(char ch) { |
225 return oneOf(ch, commentSeparators); |
244 return oneOf(ch, commentSeparators); |
226 } |
245 } |
227 |
246 |
228 bool isQuote(char ch) { |
247 bool isQuote(char ch) { |
280 else if (uri == "allow-sub-keys") allowSubKeys = parseBoolean(value); |
309 else if (uri == "allow-sub-keys") allowSubKeys = parseBoolean(value); |
281 else if (uri == "comment-separators") commentSeparators = value; |
310 else if (uri == "comment-separators") commentSeparators = value; |
282 else if (uri == "key-value-separators") keyValueSeparators = value; |
311 else if (uri == "key-value-separators") keyValueSeparators = value; |
283 else if (uri == "quotes") quotes = value; |
312 else if (uri == "quotes") quotes = value; |
284 else if (uri == "dialect") setDialect(value); |
313 else if (uri == "dialect") setDialect(value); |
|
314 else if (setUnescaping(uri, value)); |
285 else throw std::invalid_argument(std::string("Invalid parser option: „") + uri + "“ with value: „" + value + "“"); |
315 else throw std::invalid_argument(std::string("Invalid parser option: „") + uri + "“ with value: „" + value + "“"); |
286 } |
316 } |
287 |
317 |
288 void addHandler(INIContentHandler* handler) override { |
318 void addHandler(INIContentHandler* handler) override { |
289 handlers.push_back(handler); |
319 handlers.push_back(handler); |
|
320 } |
|
321 |
|
322 void addUnescapingProcessor(std::shared_ptr<UnescapingProcessor> processor, const std::string uri, bool enabledByDefault) override { |
|
323 unescapingProcessors.push_back({processor, uri, enabledByDefault}); |
290 } |
324 } |
291 |
325 |
292 void process() override { |
326 void process() override { |
293 for (INIContentHandler* handler : handlers) handler->startDocument(); |
327 for (INIContentHandler* handler : handlers) handler->startDocument(); |
294 |
328 |
321 event.eventNumber = ++eventNumber; |
355 event.eventNumber = ++eventNumber; |
322 get(); |
356 get(); |
323 readAllWhitespace(); |
357 readAllWhitespace(); |
324 event.name = readTokenAndEatTerminator(']', "e, &found); |
358 event.name = readTokenAndEatTerminator(']', "e, &found); |
325 if (!quote) event.name = trim(event.name); |
359 if (!quote) event.name = trim(event.name); |
|
360 event.name = unescape(event.name, UnescapingProcessor::TextType::SectionName); |
326 |
361 |
327 readSpacesAndTabs(); |
362 readSpacesAndTabs(); |
328 if (allowSectionTags && peek() == '[') { |
363 if (allowSectionTags && peek() == '[') { |
329 get(); |
364 get(); |
330 event.tag = readTokenAndEatTerminator(']', "e, &found); |
365 event.tag = readTokenAndEatTerminator(']', "e, &found); |
|
366 event.tag = unescape(event.tag, UnescapingProcessor::TextType::SectionTag); |
331 } |
367 } |
332 |
368 |
333 readSpacesAndTabs(); |
369 readSpacesAndTabs(); |
334 ch = peek(); |
370 ch = peek(); |
335 if (isComment(ch)) { |
371 if (isComment(ch)) { |
336 get(); |
372 get(); |
337 readSpacesAndTabs(); |
373 readSpacesAndTabs(); |
338 event.comment = readUntil('\n', &found); |
374 event.comment = readUntil('\n', &found); |
|
375 event.comment = unescape(event.comment, UnescapingProcessor::TextType::SectionComment); |
339 } else if (ch == '\n') { |
376 } else if (ch == '\n') { |
340 get(); |
377 get(); |
341 } else { |
378 } else { |
342 throw std::logic_error(std::string("unexpected content after the section: '") + event.name + "'"); |
379 throw std::logic_error(std::string("unexpected content after the section: '") + event.name + "'"); |
343 } |
380 } |
348 event.lineNumber = lineNumber; |
385 event.lineNumber = lineNumber; |
349 event.eventNumber = ++eventNumber; |
386 event.eventNumber = ++eventNumber; |
350 get(); |
387 get(); |
351 readSpacesAndTabs(); |
388 readSpacesAndTabs(); |
352 event.comment = readUntil('\n', &found); |
389 event.comment = readUntil('\n', &found); |
|
390 event.comment = unescape(event.comment, UnescapingProcessor::TextType::Comment); |
353 for (INIContentHandler* handler : handlers) handler->comment(event); |
391 for (INIContentHandler* handler : handlers) handler->comment(event); |
354 } else { |
392 } else { |
355 INIContentHandler::EntryEvent event; |
393 INIContentHandler::EntryEvent event; |
356 event.lineNumber = lineNumber; |
394 event.lineNumber = lineNumber; |
357 event.eventNumber = ++eventNumber; |
395 event.eventNumber = ++eventNumber; |
378 std::smatch match; |
416 std::smatch match; |
379 if (std::regex_match(fullKey, match, std::regex("([^\\[]+)\\[([^\\[]+)\\]"))) { |
417 if (std::regex_match(fullKey, match, std::regex("([^\\[]+)\\[([^\\[]+)\\]"))) { |
380 event.key = match[1]; |
418 event.key = match[1]; |
381 event.subKey = match[2]; |
419 event.subKey = match[2]; |
382 event.fullKey = fullKey; |
420 event.fullKey = fullKey; |
|
421 event.subKey = unescape(event.subKey, UnescapingProcessor::TextType::EntryKey); |
383 } |
422 } |
384 } |
423 } |
|
424 |
|
425 event.key = unescape(event.key, UnescapingProcessor::TextType::EntryKey); |
|
426 event.fullKey = unescape(event.fullKey, UnescapingProcessor::TextType::EntryKey); |
|
427 event.value = unescape(event.value, UnescapingProcessor::TextType::EntryValue); |
385 |
428 |
386 if (quote) { |
429 if (quote) { |
387 readSpacesAndTabs(); |
430 readSpacesAndTabs(); |
388 ch = peek(); |
431 ch = peek(); |
389 if (isComment(ch)) { |
432 if (isComment(ch)) { |
390 get(); |
433 get(); |
391 readSpacesAndTabs(); |
434 readSpacesAndTabs(); |
392 event.comment = readUntil('\n', &found); |
435 event.comment = readUntil('\n', &found); |
|
436 event.comment = unescape(event.comment, UnescapingProcessor::TextType::EntryComment); |
393 } else if (ch == '\n') { |
437 } else if (ch == '\n') { |
394 get(); |
438 get(); |
395 } else { |
439 } else { |
396 // TODO: optional support for multiple tokens in a single entry? |
440 // TODO: optional support for multiple tokens in a single entry? |
397 // modes: array, concatenate |
441 // modes: array, concatenate |