changeset 593:c767f1202e8e

Ini: move to bitbucket
author David Demelier <markand@malikania.fr>
date Mon, 05 Sep 2016 22:00:23 +0200
parents 2bd11ea63822
children 2457d0f97243
files CMakeLists.txt modules/ini/CMakeLists.txt modules/ini/doc/mainpage.cpp modules/ini/ini.cpp modules/ini/ini.hpp 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 19 files changed, 0 insertions(+), 1450 deletions(-) [+]
line wrap: on
line diff
--- a/CMakeLists.txt	Wed Aug 31 18:48:03 2016 +0200
+++ b/CMakeLists.txt	Mon Sep 05 22:00:23 2016 +0200
@@ -52,7 +52,6 @@
 add_subdirectory(modules/elapsed-timer)
 add_subdirectory(modules/fs)
 add_subdirectory(modules/hash)
-add_subdirectory(modules/ini)
 add_subdirectory(modules/js)
 add_subdirectory(modules/net)
 add_subdirectory(modules/options)
--- a/modules/ini/CMakeLists.txt	Wed Aug 31 18:48:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,24 +0,0 @@
-#
-# CMakeLists.txt -- code building for common code
-#
-# Copyright (c) 2013-2016 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.
-#
-
-code_define_module(
-    NAME ini
-    SOURCES ini.cpp ini.hpp
-    LIBRARIES $<$<BOOL:${WIN32}>:shlwapi>
-    FLAGS DIRECTORY=\"${CMAKE_CURRENT_SOURCE_DIR}/test/configs/\"
-)
--- a/modules/ini/doc/mainpage.cpp	Wed Aug 31 18:48:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,21 +0,0 @@
-/**
- * \mainpage
- *
- * Welcome to the ini library.
- *
- * ## Introduction
- * 
- * This module let you parse `.ini` files. It also includes few extensions like:
- * 
- *   - include statement,
- *   - list constructs.
- *
- * ## Requirements
- * 
- *   - C++11,
- *   - On Windows, you must link against shlwapi library.
- * 
- * ## Installation
- * 
- * Just copy the two files ini.hpp and ini.cpp and add them to your project.
- */
--- a/modules/ini/ini.cpp	Wed Aug 31 18:48:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,412 +0,0 @@
-/*
- * ini.cpp -- extended .ini file parser
- *
- * Copyright (c) 2013-2016 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>
-
-// for PathIsRelative.
-#if defined(_WIN32)
-#  include <Shlwapi.h>
-#endif
-
-#include "ini.hpp"
-
-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 analyseLine(int &line, int &column, StreamIterator &it) noexcept
-{
-    assert(*it == '\n');
-
-    ++ line;
-    ++ it;
-    column = 0;
-}
-
-void analyseComment(int &column, StreamIterator &it, StreamIterator end) noexcept
-{
-    assert(*it == '#');
-
-    while (it != end && *it != '\n') {
-        ++ column;
-        ++ it;
-    }
-}
-
-void analyseSpaces(int &column, StreamIterator &it, StreamIterator end) noexcept
-{
-    assert(isSpace(*it));
-
-    while (it != end && isSpace(*it)) {
-        ++ column;
-        ++ it;
-    }
-}
-
-void analyseList(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 analyseSection(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 analyseAssign(Tokens &list, int &line, int &column, StreamIterator &it)
-{
-    assert(*it == '=');
-
-    list.push_back({ Token::Assign, line, column++ });
-    ++ it;
-}
-
-void analyseQuotedWord(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 analyseWord(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 analyseInclude(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 });
-}
-
-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, const std::string &path, 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());
-
-    std::string value = (it++)->value();
-    std::string file;
-
-    if (!isAbsolute(value))
-#if defined(_WIN32)
-        file = path + "\\" + value;
-#else
-        file = path + "/" + value;
-#endif
-    else
-        file = value;
-
-    for (const auto &sc : readFile(file))
-        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));
-}
-
-} // !namespace
-
-namespace ini {
-
-Tokens analyse(std::istreambuf_iterator<char> it, std::istreambuf_iterator<char> end)
-{
-    Tokens list;
-    int line = 1;
-    int column = 0;
-
-    while (it != end) {
-        if (*it == '\n')
-            analyseLine(line, column, it);
-        else if (*it == '#')
-            analyseComment(column, it, end);
-        else if (*it == '[')
-            analyseSection(list, line, column, it, end);
-        else if (*it == '=')
-            analyseAssign(list, line, column, it);
-        else if (isSpace(*it))
-            analyseSpaces(column, it, end);
-        else if (*it == '@')
-            analyseInclude(list, line, column, it, end);
-        else if (isQuote(*it))
-            analyseQuotedWord(list, line, column, it, end);
-        else if (isList(*it))
-            analyseList(list, line, column, it);
-        else
-            analyseWord(list, line, column, it, end);
-    }
-
-    return list;
-}
-
-Tokens analyse(std::istream &stream)
-{
-    return analyse(std::istreambuf_iterator<char>(stream), {});
-}
-
-Document parse(const Tokens &tokens, const std::string &path)
-{
-    Document doc;
-    TokenIterator it = tokens.cbegin();
-    TokenIterator end = tokens.cend();
-
-    while (it != end) {
-        switch (it->type()) {
-        case Token::Include:
-            parseInclude(doc, path, it, end);
-            break;
-        case Token::Section:
-            parseSection(doc, it, end);
-            break;
-        default:
-            throw Error(it->line(), it->column(), "unexpected '" + it->value() + "' on root document");
-        }
-    }
-
-    return doc;
-}
-
-Document readFile(const std::string &filename)
-{
-    // Get parent path.
-    auto parent = filename;
-    auto pos = parent.find_last_of("/\\");
-
-    if (pos != std::string::npos)
-        parent.erase(pos);
-    else
-        parent = ".";
-
-    std::ifstream input(filename);
-
-    if (!input)
-        throw Error(0, 0, std::strerror(errno));
-
-    return parse(analyse(input), parent);
-}
-
-Document readString(const std::string &buffer)
-{
-    std::istringstream iss(buffer);
-
-    return parse(analyse(iss));
-}
-
-void 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.hpp	Wed Aug 31 18:48:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,643 +0,0 @@
-/*
- * ini.hpp -- extended .ini file parser
- *
- * Copyright (c) 2013-2016 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_HPP
-#define INI_HPP
-
-/**
- * \file ini.hpp
- * \brief Extended .ini file parser.
- * \author David Demelier <markand@malikania.fr>
- */
-
-/**
- * \page Ini Ini
- * \brief Extended .ini file parser.
- *
- * ## Export macros
- *
- * You must define `INI_DLL` globally and `INI_BUILDING_DLL` when compiling the
- * library if you want a DLL, alternatively you can provide your own
- * `INI_EXPORT` macro instead.
- *
- *   - \subpage ini-syntax
- */
-
-/**
- * \page ini-syntax Syntax
- * \brief File syntax.
- *
- * The syntax is similar to most of `.ini` implementations as:
- *
- *   - a section is delimited by `[name]` can be redefined multiple times,
- *   - an option **must** always be defined in a section,
- *   - empty options must be surrounded by quotes,
- *   - lists can not includes trailing commas,
- *   - include statement must always be at the beginning of files
- *     (in no sections),
- *   - comments starts with # until the end of line,
- *   - options with spaces **must** use quotes.
- *
- * # Basic file
- *
- * ````ini
- * # This is a comment.
- * [section]
- * option1 = value1
- * option2 = "value 2 with spaces"    # comment is also allowed here
- * ````
- *
- * # Redefinition
- *
- * Sections can be redefined multiple times and are kept the order they are
- * seen.
- *
- * ````ini
- * [section]
- * value = "1"
- * 
- * [section]
- * value = "2"
- * ````
- *
- * The ini::Document object will contains two ini::Section.
- *
- * # Lists
- *
- * Lists are defined using `()` and commas, like values, they may have quotes.
- *
- * ````ini
- * [section]
- * names = ( "x1", "x2" )
- *
- * # This is also allowed
- * biglist = (
- *   "abc",
- *   "def"
- * )
- * ````
- *
- * # Include statement
- *
- * You can split a file into several pieces, if the include statement contains a
- * relative path, the path will be relative to the current file being parsed.
- *
- * You **must** use the include statement before any section.
- *
- * If the file contains spaces, use quotes.
- *
- * ````ini
- * # main.conf
- * @include "foo.conf"
- *
- * # foo.conf
- * [section]
- * option1 = value1
- * ````
- */
-
-/**
- * \cond INI_HIDDEN_SYMBOLS
- */
-
-#if !defined(INI_EXPORT)
-#   if defined(INI_DLL)
-#       if defined(_WIN32)
-#           if defined(INI_BUILDING_DLL)
-#               define INI_EXPORT __declspec(dllexport)
-#           else
-#               define INI_EXPORT __declspec(dllimport)
-#           endif
-#       else
-#           define INI_EXPORT
-#       endif
-#   else
-#       define INI_EXPORT
-#   endif
-#endif
-
-/**
- * \endcond
- */
-
-#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 line the line
-     * \param column the column
-     * \param msg the message
-     */
-    inline Error(int line, int column, std::string msg) noexcept
-        : m_line(line)
-        , m_column(column)
-        , m_message(std::move(msg))
-    {
-    }
-
-    /**
-     * 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 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.
-     *
-     * \pre key must not be empty
-     * \param key the key
-     */
-    inline Option(std::string key) noexcept
-        : std::vector<std::string>()
-        , m_key(std::move(key))
-    {
-        assert(!m_key.empty());
-    }
-
-    /**
-     * Construct a single option.
-     *
-     * \pre key must not be empty
-     * \param key the key
-     * \param value the value
-     */
-    inline Option(std::string key, std::string value) noexcept
-        : m_key(std::move(key))
-    {
-        assert(!m_key.empty());
-
-        push_back(std::move(value));
-    }
-
-    /**
-     * Construct a list option.
-     *
-     * \pre key must not be empty
-     * \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))
-    {
-        assert(!m_key.empty());
-    }
-
-    /**
-     * 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.
-     *
-     * \pre key must not be empty
-     * \param key the key
-     */
-    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();
-    }
-
-    /**
-     * 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;
-        });
-    }
-
-    /**
-     * 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);
-    }
-
-    /**
-     * Inherited operators.
-     */
-    using std::vector<Option>::operator[];
-};
-
-/**
- * \class Document
- * \brief Ini document description.
- * \see readFile
- * \see readString
- */
-class Document : public std::vector<Section> {
-public:
-    /**
-     * 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 find(key) != end();
-    }
-
-    /**
-     * 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;
-        });
-    }
-
-    /**
-     * 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);
-    }
-
-    /**
-     * Inherited operators.
-     */
-    using std::vector<Section>::operator[];
-};
-
-/**
- * Analyse a stream and detect potential syntax errors. This does not parse the
- * file like including other files in include statement.
- *
- * It does only analysis, for example if an option is defined under no section,
- * this does not trigger an error while it's invalid.
- *
- * \param it the iterator
- * \param end where to stop
- * \return the list of tokens
- * \throws Error on errors
- */
-INI_EXPORT Tokens analyse(std::istreambuf_iterator<char> it, std::istreambuf_iterator<char> end);
-
-/**
- * Overloaded function for stream.
- *
- * \param stream the stream
- * \return the list of tokens
- * \throws Error on errors
- */
-INI_EXPORT Tokens analyse(std::istream &stream);
-
-/**
- * Parse the produced tokens.
- *
- * \param tokens the tokens
- * \param path the parent path
- * \return the document
- * \throw Error on errors
- */
-INI_EXPORT Document parse(const Tokens &tokens, const std::string &path = ".");
-
-/**
- * Parse a file.
- *
- * \param filename the file name
- * \return the document
- * \throw Error on errors
- */
-INI_EXPORT Document readFile(const std::string &filename);
-
-/**
- * Parse a string.
- *
- * If the string contains include statements, they are relative to the current
- * working directory.
- *
- * \param buffer the buffer
- * \return the document
- * \throw Error on errors
- */
-INI_EXPORT Document readString(const std::string &buffer);
-
-/**
- * Show all tokens and their description.
- *
- * \param tokens the tokens
- */
-INI_EXPORT void dump(const Tokens &tokens);
-
-} // !ini
-
-#endif // !INI_HPP
--- a/modules/ini/test/configs/compact.conf	Wed Aug 31 18:48:03 2016 +0200
+++ /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	Wed Aug 31 18:48:03 2016 +0200
+++ /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	Wed Aug 31 18:48:03 2016 +0200
+++ /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	Wed Aug 31 18:48:03 2016 +0200
+++ /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	Wed Aug 31 18:48:03 2016 +0200
+++ /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	Wed Aug 31 18:48:03 2016 +0200
+++ /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	Wed Aug 31 18:48:03 2016 +0200
+++ /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	Wed Aug 31 18:48:03 2016 +0200
+++ /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	Wed Aug 31 18:48:03 2016 +0200
+++ /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	Wed Aug 31 18:48:03 2016 +0200
+++ /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	Wed Aug 31 18:48:03 2016 +0200
+++ /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	Wed Aug 31 18:48:03 2016 +0200
+++ /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	Wed Aug 31 18:48:03 2016 +0200
+++ /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	Wed Aug 31 18:48:03 2016 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,301 +0,0 @@
-/*
- * main.cpp -- main test file for Ini
- *
- * Copyright (c) 2013-2016 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.hpp>
-
-class BasicTest : public testing::Test {
-protected:
-    ini::Document m_ini;
-
-public:
-    BasicTest()
-        : m_ini(ini::readFile(DIRECTORY "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::readFile(DIRECTORY "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 definitions.
- * ------------------------------------------------------------------
- */
-
-class MultiTest : public testing::Test {
-protected:
-    ini::Document m_ini;
-
-public:
-    MultiTest()
-        : m_ini(ini::readFile(DIRECTORY "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::readFile(DIRECTORY "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::readFile(DIRECTORY "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::readFile(DIRECTORY "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::readFile(DIRECTORY "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::readFile(DIRECTORY "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::readFile(DIRECTORY "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::readFile(DIRECTORY "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::readFile(DIRECTORY "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::readFile(DIRECTORY "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)
-{
-    ASSERT_ANY_THROW(ini::readFile("does not exists"));
-}
-
-int main(int argc, char **argv)
-{
-    testing::InitGoogleTest(&argc, argv);
-
-    return RUN_ALL_TESTS();
-}