changeset 496:3c783cf492ab

Remove ini
author David Demelier <markand@malikania.fr>
date Tue, 08 Dec 2015 18:29:12 +0100
parents 3d6901394e41
children 8b161d143975
files CMakeLists.txt modules/ini/CMakeLists.txt modules/ini/ini.cpp modules/ini/ini.h modules/ini/test/configs/compact.conf modules/ini/test/configs/empty.conf modules/ini/test/configs/error-badcomment.conf modules/ini/test/configs/error-badinclude.conf modules/ini/test/configs/error-badsection.conf modules/ini/test/configs/error-nosection.conf modules/ini/test/configs/error-unterminatedsection.conf modules/ini/test/configs/includes.conf modules/ini/test/configs/lists.conf modules/ini/test/configs/multi.conf modules/ini/test/configs/novalue.conf modules/ini/test/configs/simple.conf modules/ini/test/configs/tokens.conf modules/ini/test/main.cpp
diffstat 18 files changed, 0 insertions(+), 1385 deletions(-) [+]
line wrap: on
line diff
--- a/CMakeLists.txt	Tue Dec 01 15:32:34 2015 +0100
+++ b/CMakeLists.txt	Tue Dec 08 18:29:12 2015 +0100
@@ -55,7 +55,6 @@
 	)
 endif ()
 
-add_subdirectory(modules/ini)
 add_subdirectory(modules/js)
 add_subdirectory(modules/json)
 add_subdirectory(modules/options)
--- a/modules/ini/CMakeLists.txt	Tue Dec 01 15:32:34 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,46 +0,0 @@
-#
-# CMakeLists.txt -- ini module
-#
-# Copyright (c) 2013-2015 David Demelier <markand@malikania.fr>
-#
-# Permission to use, copy, modify, and/or distribute this software for any
-# purpose with or without fee is hereby granted, provided that the above
-# copyright notice and this permission notice appear in all copies.
-#
-# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
-# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
-# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
-# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
-# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
-# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
-# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-#
-
-project(ini)
-
-if (WIN32)
-	set(LIBRARIES Shlwapi)
-endif ()
-
-code_define_module(
-	NAME ini
-	SOURCES
-		${ini_SOURCE_DIR}/ini.cpp
-		${ini_SOURCE_DIR}/ini.h
-	LIBRARIES
-		${LIBRARIES}
-	RESOURCES
-		${ini_SOURCE_DIR}/test/configs/compact.conf
-		${ini_SOURCE_DIR}/test/configs/empty.conf
-		${ini_SOURCE_DIR}/test/configs/error-badcomment.conf
-		${ini_SOURCE_DIR}/test/configs/error-badinclude.conf
-		${ini_SOURCE_DIR}/test/configs/error-badsection.conf
-		${ini_SOURCE_DIR}/test/configs/error-nosection.conf
-		${ini_SOURCE_DIR}/test/configs/error-unterminatedsection.conf
-		${ini_SOURCE_DIR}/test/configs/includes.conf
-		${ini_SOURCE_DIR}/test/configs/lists.conf
-		${ini_SOURCE_DIR}/test/configs/multi.conf
-		${ini_SOURCE_DIR}/test/configs/novalue.conf
-		${ini_SOURCE_DIR}/test/configs/simple.conf
-		${ini_SOURCE_DIR}/test/configs/tokens.conf
-)
--- a/modules/ini/ini.cpp	Tue Dec 01 15:32:34 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,445 +0,0 @@
-/*
- * ini.cpp -- .ini file parsing
- *
- * Copyright (c) 2013-2015 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <cctype>
-#include <cstring>
-#include <iostream>
-#include <iterator>
-#include <fstream>
-#include <sstream>
-#include <stdexcept>
-
-#if defined(_WIN32)
-#  include <Shlwapi.h>	// for PathIsRelative
-#endif
-
-#include "ini.h"
-
-namespace {
-
-using namespace ini;
-
-using StreamIterator = std::istreambuf_iterator<char>;
-using TokenIterator = std::vector<Token>::const_iterator;
-
-inline bool isAbsolute(const std::string &path) noexcept
-{
-#if defined(_WIN32)
-	return !PathIsRelative(path.c_str());
-#else
-	return path.size() > 0 && path[0] == '/';
-#endif
-}
-
-inline bool isQuote(char c) noexcept
-{
-	return c == '\'' || c == '"';
-}
-
-inline bool isSpace(char c) noexcept
-{
-	/* Custom version because std::isspace includes \n as space */
-	return c == ' ' || c == '\t';
-}
-
-inline bool isList(char c) noexcept
-{
-	return c == '(' || c == ')' || c == ',';
-}
-
-inline bool isReserved(char c) noexcept
-{
-	return isList(c) || isQuote(c) || c == '[' || c == ']' || c == '@' || c == '#' || c == '=';
-}
-
-void analyzeLine(int &line, int &column, StreamIterator &it) noexcept
-{
-	assert(*it == '\n');
-
-	++ line;
-	++ it;
-	column = 0;
-}
-
-void analyzeComment(int &column, StreamIterator &it, StreamIterator end) noexcept
-{
-	assert(*it == '#');
-
-	while (it != end && *it != '\n') {
-		++ column;
-		++ it;
-	}
-}
-
-void analyzeSpaces(int &column, StreamIterator &it, StreamIterator end) noexcept
-{
-	assert(isSpace(*it));
-
-	while (it != end && isSpace(*it)) {
-		++ column;
-		++ it;
-	}
-}
-
-void analyzeList(Tokens &list, int line, int &column, StreamIterator &it) noexcept
-{
-	assert(isList(*it));
-
-	switch (*it++) {
-	case '(':
-		list.emplace_back(Token::ListBegin, line, column++);
-		break;
-	case ')':
-		list.emplace_back(Token::ListEnd, line, column++);
-		break;
-	case ',':
-		list.emplace_back(Token::Comma, line, column++);
-		break;
-	default:
-		break;
-	}
-}
-
-void analyzeSection(Tokens &list, int &line, int &column, StreamIterator &it, StreamIterator end)
-{
-	assert(*it == '[');
-
-	std::string value;
-	int save = column;
-
-	/* Read section name */
-	++ it;
-	while (it != end && *it != ']') {
-		if (*it == '\n') {
-			throw Error{line, column, "section not terminated, missing ']'"};
-		}
-		if (isReserved(*it)) {
-			throw Error{line, column, "section name expected after '[', got '" + std::string(1, *it) + "'"};
-		}
-		++ column;
-		value += *it++;
-	}
-
-	if (it == end) {
-		throw Error{line, column, "section name expected after '[', got <EOF>"};
-	}
-	if (value.empty()) {
-		throw Error{line, column, "empty section name"};
-	}
-
-	/* Remove ']' */
-	++ it;
-
-	list.emplace_back(Token::Section, line, save, std::move(value));
-}
-
-void analyzeAssign(Tokens &list, int &line, int &column, StreamIterator &it)
-{
-	assert(*it == '=');
-
-	list.push_back({ Token::Assign, line, column++ });
-	++ it;
-}
-
-void analyzeQuotedWord(Tokens &list, int &line, int &column, StreamIterator &it, StreamIterator end)
-{
-	std::string value;
-	int save = column;
-	char quote = *it++;
-
-	while (it != end && *it != quote) {
-		// TODO: escape sequence
-		++ column;
-		value += *it++;
-	}
-
-	if (it == end) {
-		throw Error{line, column, "undisclosed '" + std::string(1, quote) + "', got <EOF>"};
-	}
-
-	/* Remove quote */
-	++ it;
-
-	list.push_back({ Token::QuotedWord, line, save, std::move(value) });
-}
-
-void analyzeWord(Tokens &list, int &line, int &column, StreamIterator &it, StreamIterator end)
-{
-	assert(!isReserved(*it));
-
-	std::string value;
-	int save = column;
-
-	while (it != end && !std::isspace(*it) && !isReserved(*it)) {
-		++ column;
-		value += *it++;
-	}
-
-	list.push_back({ Token::Word, line, save, std::move(value) });
-}
-
-void analyzeInclude(Tokens &list, int &line, int &column, StreamIterator &it, StreamIterator end)
-{
-	assert(*it == '@');
-
-	std::string include;
-	int save = column;
-
-	/* Read include */
-	++ it;
-	while (it != end && !isSpace(*it)) {
-		++ column;
-		include += *it++;
-	}
-
-	if (include != "include") {
-		throw Error{line, column, "expected include after '@' token"};
-	}
-
-	list.push_back({ Token::Include, line, save });
-}
-
-Tokens analyze(StreamIterator &it, StreamIterator end)
-{
-	Tokens list;
-	int line = 1;
-	int column = 0;
-
-	while (it != end) {
-		if (*it == '\n') {
-			analyzeLine(line, column, it);
-		} else if (*it == '#') {
-			analyzeComment(column, it, end);
-		} else if (*it == '[') {
-			analyzeSection(list, line, column, it, end);
-		} else if (*it == '=') {
-			analyzeAssign(list, line, column, it);
-		} else if (isSpace(*it)) {
-			analyzeSpaces(column, it, end);
-		} else if (*it == '@') {
-			analyzeInclude(list, line, column, it, end);
-		} else if (isQuote(*it)) {
-			analyzeQuotedWord(list, line, column, it, end);
-		} else if (isList(*it)) {
-			analyzeList(list, line, column, it);
-		} else {
-			analyzeWord(list, line, column, it, end);
-		}
-	}
-
-	return list;
-}
-
-void parseOptionValueSimple(Option &option, TokenIterator &it)
-{
-	assert(it->type() == Token::Word || it->type() == Token::QuotedWord);
-
-	option.push_back((it++)->value());
-}
-
-void parseOptionValueList(Option &option, TokenIterator &it, TokenIterator end)
-{
-	assert(it->type() == Token::ListBegin);
-
-	TokenIterator save = it++;
-
-	while (it != end && it->type() != Token::ListEnd) {
-		switch (it->type()) {
-		case Token::Comma:
-			/* Previous must be a word */
-			if (it[-1].type() != Token::Word && it[-1].type() != Token::QuotedWord) {
-				throw Error{it->line(), it->column(), "unexpected comma after '" + it[-1].value() + "'"};
-			}
-
-			++ it;
-			break;
-		case Token::Word:
-		case Token::QuotedWord:
-			option.push_back((it++)->value());
-			break;
-		default:
-			throw Error{it->line(), it->column(), "unexpected '" + it[-1].value() + "' in list construct"};
-			break;
-		}
-	}
-
-	if (it == end) {
-		throw Error{save->line(), save->column(), "unterminated list construct"};
-	}
-
-	/* Remove ) */
-	++ it;
-}
-
-void parseOption(Section &sc, TokenIterator &it, TokenIterator end)
-{
-	Option option{it->value()};
-
-	TokenIterator save = it;
-
-	/* No '=' or something else? */
-	if (++it == end) {
-		throw Error{save->line(), save->column(), "expected '=' assignment, got <EOF>"};
-	}
-	if (it->type() != Token::Assign) {
-		throw Error{it->line(), it->column(), "expected '=' assignment, got " + it->value()};
-	}
-
-	/* Empty options are allowed so just test for words */
-	if (++it != end) {
-		if (it->type() == Token::Word || it->type() == Token::QuotedWord) {
-			parseOptionValueSimple(option, it);
-		} else if (it->type() == Token::ListBegin) {
-			parseOptionValueList(option, it, end);
-		}
-	}
-
-	sc.push_back(std::move(option));
-}
-
-void parseInclude(Document &doc, TokenIterator &it, TokenIterator end)
-{
-	TokenIterator save = it;
-
-	if (++it == end) {
-		throw Error{save->line(), save->column(), "expected file name after '@include' statement, got <EOF>"};
-	}
-
-	if (it->type() != Token::Word && it->type() != Token::QuotedWord) {
-		throw Error{it->line(), it->column(), "expected file name after '@include' statement, got " + it->value()};
-	}
-
-	if (doc.path().empty()) {
-		throw Error{it->line(), it->column(), "'@include' statement invalid with buffer documents"};
-	}
-
-	std::string value = (it++)->value();
-	std::string file;
-
-	if (!isAbsolute(value)) {
-#if defined(_WIN32)
-		file = doc.path() + "\\" + value;
-#else
-		file = doc.path() + "/" + value;
-#endif
-	} else {
-		file = value;
-	}
-
-	Document child{File{file}};
-
-	for (const auto &sc : child) {
-		doc.push_back(sc);
-	}
-}
-
-void parseSection(Document &doc, TokenIterator &it, TokenIterator end)
-{
-	Section sc{it->value()};
-
-	/* Skip [section] */
-	++ it;
-
-	/* Read until next section */
-	while (it != end && it->type() != Token::Section) {
-		if (it->type() != Token::Word) {
-			throw Error{it->line(), it->column(), "unexpected token '" + it->value() + "' in section definition"};
-		}
-
-		parseOption(sc, it, end);
-	}
-
-	doc.push_back(std::move(sc));
-}
-
-void parse(Document &doc, const Tokens &tokens)
-{
-	TokenIterator it = tokens.cbegin();
-	TokenIterator end = tokens.cend();
-
-	while (it != end) {
-		/* Just ignore this */
-		switch (it->type()) {
-		case Token::Include:
-			parseInclude(doc, it, end);
-			break;
-		case Token::Section:
-			parseSection(doc, it, end);
-			break;
-		default:
-			throw Error{it->line(), it->column(), "unexpected '" + it->value() + "' on root document"};
-		}
-	}
-}
-
-} // !namespace
-
-namespace ini {
-
-Tokens Document::analyze(const File &file)
-{
-	std::fstream stream{file.path};
-
-	if (!stream) {
-		throw std::runtime_error{std::strerror(errno)};
-	}
-
-	std::istreambuf_iterator<char> it{stream};
-	std::istreambuf_iterator<char> end{};
-
-	return ::analyze(it, end);
-}
-
-Tokens Document::analyze(const Buffer &buffer)
-{
-	std::istringstream stream{buffer.text};
-	std::istreambuf_iterator<char> it{stream};
-	std::istreambuf_iterator<char> end{};
-
-	return ::analyze(it, end);
-}
-
-Document::Document(const File &file)
-	: m_path{file.path}
-{
-	/* Update path */
-	auto pos = m_path.find_last_of("/\\");
-
-	if (pos != std::string::npos) {
-		m_path.erase(pos);
-	} else {
-		m_path = ".";
-	}
-
-	parse(*this, analyze(file));
-}
-
-Document::Document(const Buffer &buffer)
-{
-	parse(*this, analyze(buffer));
-}
-
-void Document::dump(const Tokens &tokens)
-{
-	for (const Token &token: tokens) {
-		// TODO: add better description
-		std::cout << token.line() << ":" << token.column() << ": " << token.value() << std::endl;
-	}
-}
-
-} // !ini
--- a/modules/ini/ini.h	Tue Dec 01 15:32:34 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,546 +0,0 @@
-/*
- * ini.h -- .ini file parsing
- *
- * Copyright (c) 2013-2015 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#ifndef _INI_H_
-#define _INI_H_
-
-/**
- * @file ini.h
- * @brief Configuration file parser.
- */
-
-#include <algorithm>
-#include <cassert>
-#include <exception>
-#include <stdexcept>
-#include <string>
-#include <vector>
-
-/**
- * Namespace for ini related classes.
- */
-namespace ini {
-
-class Document;
-
-/**
- * @class Error
- * @brief Error in a file
- */
-class Error : public std::exception {
-private:
-	int m_line;		//!< line number
-	int m_column;		//!< line column
-	std::string m_message;	//!< error message
-
-public:
-	/**
-	 * Constructor.
-	 *
-	 * @param l the line
-	 * @param c the column
-	 * @param m the message
-	 */
-	inline Error(int l, int c, std::string m) noexcept
-		: m_line{l}
-		, m_column{c}
-		, m_message{std::move(m)}
-	{
-	}
-
-	/**
-	 * Get the line number.
-	 *
-	 * @return the line
-	 */
-	inline int line() const noexcept
-	{
-		return m_line;
-	}
-
-	/**
-	 * Get the column number.
-	 *
-	 * @return the column
-	 */
-	inline int column() const noexcept
-	{
-		return m_column;
-	}
-
-	/**
-	 * Return the raw error message (no line and column shown).
-	 *
-	 * @return the error message
-	 */
-	const char *what() const noexcept override
-	{
-		return m_message.c_str();
-	}
-};
-
-/**
- * @class Token
- * @brief Describe a token read in the .ini source
- *
- * This class can be used when you want to parse a .ini file yourself.
- *
- * @see Document::analyze
- */
-class Token {
-public:
-	/**
-	 * @brief Token type
-	 */
-	enum Type {
-		Include,	//!< include statement
-		Section,	//!< [section]
-		Word,		//!< word without quotes
-		QuotedWord,	//!< word with quotes
-		Assign,		//!< = assignment
-		ListBegin,	//!< begin of list (
-		ListEnd,	//!< end of list )
-		Comma		//!< list separation
-	};
-
-private:
-	Type m_type;
-	int m_line;
-	int m_column;
-	std::string m_value;
-
-public:
-	/**
-	 * Construct a token.
-	 *
-	 * @param type the type
-	 * @param line the line
-	 * @param column the column
-	 * @param value the value
-	 */
-	Token(Type type, int line, int column, std::string value = "") noexcept
-		: m_type{type}
-		, m_line{line}
-		, m_column{column}
-	{
-		switch (type) {
-		case Include:
-			m_value = "@include";
-			break;
-		case Section:
-		case Word:
-		case QuotedWord:
-			m_value = value;
-			break;
-		case Assign:
-			m_value = "=";
-			break;
-		case ListBegin:
-			m_value = "(";
-			break;
-		case ListEnd:
-			m_value = ")";
-			break;
-		case Comma:
-			m_value = ",";
-			break;
-		default:
-			break;
-		}
-	}
-
-	/**
-	 * Get the type.
-	 *
-	 * @return the type
-	 */
-	inline Type type() const noexcept
-	{
-		return m_type;
-	}
-
-	/**
-	 * Get the line.
-	 *
-	 * @return the line
-	 */
-	inline int line() const noexcept
-	{
-		return m_line;
-	}
-
-	/**
-	 * Get the column.
-	 *
-	 * @return the column
-	 */
-	inline int column() const noexcept
-	{
-		return m_column;
-	}
-
-	/**
-	 * Get the value. For words, quoted words and section, the value is the content. Otherwise it's the
-	 * characters parsed.
-	 *
-	 * @return the value
-	 */
-	inline const std::string &value() const noexcept
-	{
-		return m_value;
-	}
-};
-
-/**
- * List of tokens in order they are analyzed.
- */
-using Tokens = std::vector<Token>;
-
-/**
- * @class Option
- * @brief Option definition.
- */
-class Option : public std::vector<std::string> {
-private:
-	std::string m_key;
-
-public:
-	/**
-	 * Construct an empty option.
-	 *
-	 * @param key the key
-	 * @param value the value
-	 */
-	inline Option(std::string key) noexcept
-		: std::vector<std::string>{}
-		, m_key{std::move(key)}
-	{
-	}
-
-	/**
-	 * Construct a single option.
-	 *
-	 * @param key the key
-	 * @param value the value
-	 */
-	inline Option(std::string key, std::string value) noexcept
-		: m_key{std::move(key)}
-	{
-		push_back(std::move(value));
-	}
-
-	/**
-	 * Construct a list option.
-	 *
-	 * @param key the key
-	 * @param values the values
-	 */
-	inline Option(std::string key, std::vector<std::string> values) noexcept
-		: std::vector<std::string>{std::move(values)}
-		, m_key{std::move(key)}
-	{
-	}
-
-	/**
-	 * Get the option key.
-	 *
-	 * @return the key
-	 */
-	inline const std::string &key() const noexcept
-	{
-		return m_key;
-	}
-
-	/**
-	 * Get the option value.
-	 *
-	 * @return the value
-	 */
-	inline const std::string &value() const noexcept
-	{
-		static std::string dummy;
-
-		return empty() ? dummy : (*this)[0];
-	}
-};
-
-/**
- * @class Section
- * @brief Section that contains one or more options.
- */
-class Section : public std::vector<Option> {
-private:
-	std::string m_key;
-
-public:
-	/**
-	 * Construct a section with its name.
-	 *
-	 * @param key the key
-	 * @pre key must not be empty
-	 */
-	inline Section(std::string key) noexcept
-		: m_key{std::move(key)}
-	{
-		assert(!m_key.empty());
-	}
-
-	/**
-	 * Get the section key.
-	 *
-	 * @return the key
-	 */
-	inline const std::string &key() const noexcept
-	{
-		return m_key;
-	}
-
-	/**
-	 * Check if the section contains a specific option.
-	 *
-	 * @param key the option key
-	 * @return true if the option exists
-	 */
-	inline bool contains(const std::string &key) const noexcept
-	{
-		return find(key) != end();
-	}
-
-	/**
-	 * Access an option at the specified key.
-	 *
-	 * @param key the key
-	 * @return the option
-	 * @pre contains(key) must return true
-	 */
-	inline Option &operator[](const std::string &key)
-	{
-		assert(contains(key));
-
-		return *find(key);
-	}
-
-	/**
-	 * Overloaded function.
-	 *
-	 * @param key the key
-	 * @return the option
-	 * @pre contains(key) must return true
-	 */
-	inline const Option &operator[](const std::string &key) const
-	{
-		assert(contains(key));
-
-		return *find(key);
-	}
-
-	/**
-	 * Find an option by key and return an iterator.
-	 *
-	 * @param key the key
-	 * @return the iterator or end() if not found
-	 */
-	inline iterator find(const std::string &key) noexcept
-	{
-		return std::find_if(begin(), end(), [&] (const auto &o) {
-			return o.key() == key;
-		});
-	}
-
-	/**
-	 * Find an option by key and return an iterator.
-	 *
-	 * @param key the key
-	 * @return the iterator or end() if not found
-	 */
-	inline const_iterator find(const std::string &key) const noexcept
-	{
-		return std::find_if(cbegin(), cend(), [&] (const auto &o) {
-			return o.key() == key;
-		});
-	}
-
-	/**
-	 * Inherited operators.
-	 */
-	using std::vector<Option>::operator[];
-};
-
-/**
- * @class File
- * @brief Source for reading .ini files.
- */
-class File {
-public:
-	/**
-	 * Path to the file.
-	 */
-	std::string path;
-};
-
-/**
- * @class Buffer
- * @brief Source for reading ini from text.
- * @note the include statement is not supported with buffers.
- */
-class Buffer {
-public:
-	/**
-	 * The ini content.
-	 */
-	std::string text;
-};
-
-/**
- * @class Document
- * @brief Ini config file loader
- */
-class Document : public std::vector<Section> {
-private:
-	std::string m_path;
-
-public:
-	/**
-	 * Analyze a file and extract tokens. If the function succeeds, that does not mean the content is valid,
-	 * it just means that there are no syntax error.
-	 *
-	 * For example, this class does not allow adding options under no sections and this function will not
-	 * detect that issue.
-	 *
-	 * @param file the file to read
-	 * @return the list of tokens
-	 * @throws Error on errors
-	 */
-	static Tokens analyze(const File &file);
-
-	/**
-	 * Overloaded function for buffers.
-	 *
-	 * @param buffer the buffer to read
-	 * @return the list of tokens
-	 * @throws Error on errors
-	 */
-	static Tokens analyze(const Buffer &buffer);
-
-	/**
-	 * Show all tokens and their description.
-	 *
-	 * @param tokens the tokens
-	 */
-	static void dump(const Tokens &tokens);
-
-	/**
-	 * Construct a document from a file.
-	 *
-	 * @param file the file to read
-	 * @throws Error on errors
-	 */
-	Document(const File &file);
-
-	/**
-	 * Overloaded constructor for buffers.
-	 *
-	 * @param buffer the buffer to read
-	 * @throws Error on errors
-	 */
-	Document(const Buffer &buffer);
-
-	/**
-	 * Get the current document path, only useful when constructed from File source.
-	 *
-	 * @return the path
-	 */
-	inline const std::string &path() const noexcept
-	{
-		return m_path;
-	}
-
-	/**
-	 * Check if a document has a specific section.
-	 *
-	 * @param key the key
-	 * @return true if the document contains the section
-	 */
-	inline bool contains(const std::string &key) const noexcept
-	{
-		return std::find_if(begin(), end(), [&] (const auto &sc) { return sc.key() == key; }) != end();
-	}
-
-	/**
-	 * Access a section at the specified key.
-	 *
-	 * @param key the key
-	 * @return the section
-	 * @pre contains(key) must return true
-	 */
-	inline Section &operator[](const std::string &key)
-	{
-		assert(contains(key));
-
-		return *find(key);
-	}
-
-	/**
-	 * Overloaded function.
-	 *
-	 * @param key the key
-	 * @return the section
-	 * @pre contains(key) must return true
-	 */
-	inline const Section &operator[](const std::string &key) const
-	{
-		assert(contains(key));
-
-		return *find(key);
-	}
-
-	/**
-	 * Find a section by key and return an iterator.
-	 *
-	 * @param key the key
-	 * @return the iterator or end() if not found
-	 */
-	inline iterator find(const std::string &key) noexcept
-	{
-		return std::find_if(begin(), end(), [&] (const auto &o) {
-			return o.key() == key;
-		});
-	}
-
-	/**
-	 * Find a section by key and return an iterator.
-	 *
-	 * @param key the key
-	 * @return the iterator or end() if not found
-	 */
-	inline const_iterator find(const std::string &key) const noexcept
-	{
-		return std::find_if(cbegin(), cend(), [&] (const auto &o) {
-			return o.key() == key;
-		});
-	}
-
-	/**
-	 * Inherited operators.
-	 */
-	using std::vector<Section>::operator[];
-};
-
-} // !ini
-
-#endif // !_INI_H_
--- a/modules/ini/test/configs/compact.conf	Tue Dec 01 15:32:34 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1 +0,0 @@
-[general]verbose=true foreground=false[server]host=google.fr
--- a/modules/ini/test/configs/empty.conf	Tue Dec 01 15:32:34 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1 +0,0 @@
-# this file is completely empty
\ No newline at end of file
--- a/modules/ini/test/configs/error-badcomment.conf	Tue Dec 01 15:32:34 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,2 +0,0 @@
-[general]
-verbose #hello = xyz
--- a/modules/ini/test/configs/error-badinclude.conf	Tue Dec 01 15:32:34 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1 +0,0 @@
-@include noquotes
\ No newline at end of file
--- a/modules/ini/test/configs/error-badsection.conf	Tue Dec 01 15:32:34 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,2 +0,0 @@
-[[general]
-verbose = false
--- a/modules/ini/test/configs/error-nosection.conf	Tue Dec 01 15:32:34 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,3 +0,0 @@
-# this file has no section
-# and it's not valid
-option = value
--- a/modules/ini/test/configs/error-unterminatedsection.conf	Tue Dec 01 15:32:34 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,2 +0,0 @@
-# This file has unterminated section
-[forgot
\ No newline at end of file
--- a/modules/ini/test/configs/includes.conf	Tue Dec 01 15:32:34 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,5 +0,0 @@
-# With some includes
-@include "simple.conf"	# comments also work here
-
-[standard]
-verbose = false
--- a/modules/ini/test/configs/lists.conf	Tue Dec 01 15:32:34 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,9 +0,0 @@
-[rule1]
-servers = ( "abc", "bcd" )
-
-[rule2]
-servers =
-(
- xyz,
- poi
-)
--- a/modules/ini/test/configs/multi.conf	Tue Dec 01 15:32:34 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,7 +0,0 @@
-[entity]
-name	= "Player"
-version	= 1.0
-
-[entity]
-name	= "Subwinner"
-version	= 2.0
\ No newline at end of file
--- a/modules/ini/test/configs/novalue.conf	Tue Dec 01 15:32:34 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,6 +0,0 @@
-[plugins]
-histedit= ""
-highlight= "" #empty
-general = ""
-
-
--- a/modules/ini/test/configs/simple.conf	Tue Dec 01 15:32:34 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,6 +0,0 @@
-[general]
-option1=1
-option2 =2
-option3 = 3
-
-# This file ends with a comment.
\ No newline at end of file
--- a/modules/ini/test/configs/tokens.conf	Tue Dec 01 15:32:34 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,3 +0,0 @@
-[tokens]
-bracket = "I have [brackets]"
-at = "I have foo@at"
--- a/modules/ini/test/main.cpp	Tue Dec 01 15:32:34 2015 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,299 +0,0 @@
-/*
- * main.cpp -- main test file for Ini
- *
- * Copyright (c) 2013-2015 David Demelier <markand@malikania.fr>
- *
- * Permission to use, copy, modify, and/or distribute this software for any
- * purpose with or without fee is hereby granted, provided that the above
- * copyright notice and this permission notice appear in all copies.
- *
- * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
- * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
- * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
- * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
- * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
- * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
- */
-
-#include <iostream>
-
-#include <gtest/gtest.h>
-
-#include <ini.h>
-
-class BasicTest : public testing::Test {
-protected:
-	ini::Document m_ini;
-
-public:
-	BasicTest()
-		: m_ini{ini::File{"ini/simple.conf"}}
-	{
-	}
-
-};
-
-TEST_F(BasicTest, simple)
-{
-	ASSERT_EQ(1, static_cast<int>(m_ini.size()));
-}
-
-TEST_F(BasicTest, operators)
-{
-	try {
-		ASSERT_EQ(3, static_cast<int>(m_ini[0].size()));
-		ASSERT_EQ("general", m_ini[0].key());
-		ASSERT_EQ("general", m_ini["general"].key());
-	} catch (const std::exception &ex) {
-		FAIL() << ex.what();
-	}
-}
-
-TEST_F(BasicTest, sectionOperators)
-{
-	try {
-		// option1=1 (indexes)
-		ASSERT_EQ("option1", m_ini[0][0].key());
-		ASSERT_EQ("1", m_ini[0][0].value());
-
-		// option1=1 (keys)
-		ASSERT_EQ("option1", m_ini["general"]["option1"].key());
-		ASSERT_EQ("1", m_ini["general"]["option1"].value());
-
-		// option2 =2 (indexes)
-		ASSERT_EQ("option2", m_ini[0][1].key());
-		ASSERT_EQ("2", m_ini[0][1].value());
-
-		// option2 =2 (keys)
-		ASSERT_EQ("option2", m_ini["general"]["option2"].key());
-		ASSERT_EQ("2", m_ini["general"]["option2"].value());
-
-		// option3 = 3 (indexes)
-		ASSERT_EQ("option3", m_ini[0][2].key());
-		ASSERT_EQ("3", m_ini[0][2].value());
-
-		// option3 = 3 (keys)
-		ASSERT_EQ("option3", m_ini["general"]["option3"].key());
-		ASSERT_EQ("3", m_ini["general"]["option3"].value());
-	} catch (const std::exception &ex) {
-		FAIL() << ex.what();
-	}
-}
-
-/* --------------------------------------------------------
- * Reserved tokens in words
- * -------------------------------------------------------- */
-
-TEST(Tokens, reserved)
-{
-	try {
-		ini::Document doc{ini::File{"ini/tokens.conf"}};
-
-		ASSERT_EQ("I have [brackets]", doc["tokens"]["bracket"].value());
-		ASSERT_EQ("I have foo@at", doc["tokens"]["at"].value());
-	} catch (const std::exception &ex) {
-		FAIL() << ex.what();
-	}
-}
-
-/* --------------------------------------------------------
- * Multiple defini::Documenttion
- * -------------------------------------------------------- */
-
-class MultiTest : public testing::Test {
-protected:
-	ini::Document m_ini;
-
-public:
-	MultiTest()
-		: m_ini{ini::File{"ini/multi.conf"}}
-	{
-	}
-};
-
-TEST_F(MultiTest, defined)
-{
-	ASSERT_EQ(2, static_cast<int>(m_ini.size()));
-	ASSERT_EQ("name", m_ini[0]["name"].key());
-	ASSERT_EQ("Player", m_ini[0]["name"].value());
-	ASSERT_EQ("version", m_ini[0]["version"].key());
-	ASSERT_EQ("1.0", m_ini[0]["version"].value());
-	ASSERT_EQ("name", m_ini[1]["name"].key());
-	ASSERT_EQ("Subwinner", m_ini[1]["name"].value());
-	ASSERT_EQ("version", m_ini[1]["version"].key());
-	ASSERT_EQ("2.0", m_ini[1]["version"].value());
-}
-
-/* --------------------------------------------------------
- * Option with no values
- * -------------------------------------------------------- */
-
-class NoValueTest : public testing::Test {
-protected:
-	ini::Document m_ini;
-
-public:
-	NoValueTest()
-		: m_ini{ini::File{"ini/novalue.conf"}}
-	{
-	}
-};
-
-TEST_F(NoValueTest, isDefined)
-{
-	ASSERT_EQ("plugins", m_ini[0].key());
-	ASSERT_EQ("", m_ini["plugins"]["histedit"].value());
-	ASSERT_EQ("", m_ini["plugins"]["highlight"].value());
-	ASSERT_EQ("", m_ini["plugins"]["general"].value());
-}
-
-/* --------------------------------------------------------
- * Include tests
- * -------------------------------------------------------- */
-
-class IncludeTest : public testing::Test {
-protected:
-	ini::Document m_ini;
-
-public:
-	IncludeTest()
-		: m_ini{ini::File{"ini/includes.conf"}}
-	{
-	}
-};
-
-TEST_F(IncludeTest, all)
-{
-	ASSERT_EQ(2, static_cast<int>(m_ini.size()));
-
-	// from include
-	ASSERT_EQ("1", m_ini[0][0].value());
-	ASSERT_EQ("2", m_ini[0][1].value());
-	ASSERT_EQ("3", m_ini[0][2].value());
-
-	// from standard
-	ASSERT_EQ("false", m_ini[1][0].value());
-}
-
-/* --------------------------------------------------------
- * Compact
- * -------------------------------------------------------- */
-
-TEST(Compact, test)
-{
-	try {
-		ini::Document doc{ini::File{"ini/compact.conf"}};
-
-		ASSERT_EQ(2, static_cast<int>(doc.size()));
-		ASSERT_EQ("true", doc["general"]["verbose"].value());
-		ASSERT_EQ("false", doc["general"]["foreground"].value());
-		ASSERT_EQ("google.fr", doc["server"]["host"].value());
-	} catch (const std::exception &ex) {
-		FAIL() << ex.what();
-	}
-}
-
-/* --------------------------------------------------------
- * Empty
- * -------------------------------------------------------- */
-
-TEST(Empty, test)
-{
-	try {
-		ini::Document doc{ini::File{"ini/empty.conf"}};
-	} catch (const ini::Error &error) {
-		FAIL() << error.line() << ":" << error.column() << ": " << error.what();
-	}
-}
-
-/* --------------------------------------------------------
- * List
- * -------------------------------------------------------- */
-
-TEST(List, test)
-{
-	try {
-		std::vector<std::string> rule1{"abc", "bcd"};
-		std::vector<std::string> rule2{"xyz", "poi"};
-		ini::Document doc{ini::File{"ini/lists.conf"}};
-
-		ASSERT_EQ(rule1, doc[0][0]);
-		ASSERT_EQ(rule2, doc[1][0]);
-	} catch (const ini::Error &error) {
-		FAIL() << error.line() << ":" << error.column() << ": " << error.what();
-	}
-}
-
-/* --------------------------------------------------------
- * Errors
- * -------------------------------------------------------- */
-
-TEST(Errors, nosection)
-{
-	// An option outside a section is not allowed
-	try {
-		ini::Document doc{ini::File{"ini/error-nosection.conf"}};
-
-		FAIL() << "Failure expected, got success";
-	} catch (const ini::Error &ex) {
-		ASSERT_EQ(3, ex.line());
-		ASSERT_EQ(0, ex.column());
-	}
-}
-
-TEST(Errors, badcomment)
-{
-	// Comment can't between option-key and = assigment
-	try {
-		ini::Document doc{ini::File{"ini/error-badcomment.conf"}};
-
-		FAIL() << "Failure expected, got success";
-	} catch (const ini::Error &ex) {
-		ASSERT_EQ(2, ex.line());
-		ASSERT_EQ(0, ex.column());
-	}
-}
-
-TEST(Errors, badsection)
-{
-	// Bad section naming
-	try {
-		ini::Document doc{ini::File{"ini/error-badsection.conf"}};
-
-		FAIL() << "Failure expected, got success";
-	} catch (const ini::Error &ex) {
-		ASSERT_EQ(1, ex.line());
-		ASSERT_EQ(0, ex.column());
-	}
-}
-
-TEST(Errors, unterminatedsection)
-{
-	// Section unfinished
-	try {
-		ini::Document doc{ini::File{"ini/error-unterminatedsection.conf"}};
-
-		FAIL() << "Failure expected, got success";
-	} catch (const ini::Error &ex) {
-		ASSERT_EQ(2, ex.line());
-		ASSERT_EQ(6, ex.column());
-	}
-}
-
-TEST(Errors, notFound)
-{
-	try {
-		ini::Document doc{ini::File{"does not exists"}};
-
-		FAIL() << "Failure expected, got success";
-	} catch (const std::exception &) {
-	}
-}
-
-int main(int argc, char **argv)
-{
-	testing::InitGoogleTest(&argc, argv);
-
-	return RUN_ALL_TESTS();
-}