Mercurial > code
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(); -}