src/lib/INIReader.cpp
branchv_0
changeset 27 e9aad9dd823a
parent 26 b1f6fa3a6555
child 28 596a724fbb83
equal deleted inserted replaced
26:b1f6fa3a6555 27:e9aad9dd823a
    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) {
   266 		} else {
   285 		} else {
   267 			throw std::invalid_argument(std::string("Unsupported INI dialect: ") + name);
   286 			throw std::invalid_argument(std::string("Unsupported INI dialect: ") + name);
   268 		}
   287 		}
   269 	}
   288 	}
   270 
   289 
       
   290 	bool setUnescaping(const std::string& uri, const std::string& value) {
       
   291 		for (ConfiguredUnescapingProcessor& p : unescapingProcessors) {
       
   292 			if (p.uri == uri) {
       
   293 				p.enbaled = parseBoolean(value);
       
   294 				return true;
       
   295 			}
       
   296 		}
       
   297 		return false;
       
   298 	}
       
   299 
   271 public:
   300 public:
   272 
   301 
   273 	INIReaderImpl(std::istream& input) : input(input) {
   302 	INIReaderImpl(std::istream& input) : input(input) {
   274 	}
   303 	}
   275 
   304 
   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(']', &quote, &found);
   358 				event.name = readTokenAndEatTerminator(']', &quote, &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(']', &quote, &found);
   365 					event.tag = readTokenAndEatTerminator(']', &quote, &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