| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334 | // ----------------------------------------------------------------------------// Copyright (C) 2002-2006 Marcin Kalicinski// Copyright (C) 2009 Sebastian Redl//// Distributed under the Boost Software License, Version 1.0. // (See accompanying file LICENSE_1_0.txt or copy at // http://www.boost.org/LICENSE_1_0.txt)//// For more information, see www.boost.org// ----------------------------------------------------------------------------#ifndef BOOST_PROPERTY_TREE_INI_PARSER_HPP_INCLUDED#define BOOST_PROPERTY_TREE_INI_PARSER_HPP_INCLUDED#include <boost/property_tree/ptree.hpp>#include <boost/property_tree/detail/ptree_utils.hpp>#include <boost/property_tree/detail/file_parser_error.hpp>#include <fstream>#include <string>#include <sstream>#include <stdexcept>#include <locale>namespace boost { namespace property_tree { namespace ini_parser{    /**     * Determines whether the @c flags are valid for use with the ini_parser.     * @param flags value to check for validity as flags to ini_parser.     * @return true if the flags are valid, false otherwise.     */    inline bool validate_flags(int flags)    {        return flags == 0;    }    /** Indicates an error parsing INI formatted data. */    class ini_parser_error: public file_parser_error    {    public:        /**         * Construct an @c ini_parser_error         * @param message Message describing the parser error.         * @param filename The name of the file being parsed containing the         *                 error.         * @param line The line in the given file where an error was         *             encountered.         */        ini_parser_error(const std::string &message,                         const std::string &filename,                         unsigned long line)            : file_parser_error(message, filename, line)        {        }    };    /**     * Read INI from a the given stream and translate it to a property tree.     * @note Clears existing contents of property tree. In case of error     *       the property tree is not modified.     * @throw ini_parser_error If a format violation is found.     * @param stream Stream from which to read in the property tree.     * @param[out] pt The property tree to populate.     */    template<class Ptree>    void read_ini(std::basic_istream<                    typename Ptree::key_type::value_type> &stream,                  Ptree &pt)    {        typedef typename Ptree::key_type::value_type Ch;        typedef std::basic_string<Ch> Str;        const Ch semicolon = stream.widen(';');        const Ch hash = stream.widen('#');        const Ch lbracket = stream.widen('[');        const Ch rbracket = stream.widen(']');        Ptree local;        unsigned long line_no = 0;        Ptree *section = 0;        Str line;        // For all lines        while (stream.good())        {            // Get line from stream            ++line_no;            std::getline(stream, line);            if (!stream.good() && !stream.eof())                BOOST_PROPERTY_TREE_THROW(ini_parser_error(                    "read error", "", line_no));            // If line is non-empty            line = property_tree::detail::trim(line, stream.getloc());            if (!line.empty())            {                // Comment, section or key?                if (line[0] == semicolon || line[0] == hash)                {                    // Ignore comments                }                else if (line[0] == lbracket)                {                    // If the previous section was empty, drop it again.                    if (section && section->empty())                        local.pop_back();                    typename Str::size_type end = line.find(rbracket);                    if (end == Str::npos)                        BOOST_PROPERTY_TREE_THROW(ini_parser_error(                            "unmatched '['", "", line_no));                    Str key = property_tree::detail::trim(                        line.substr(1, end - 1), stream.getloc());                    if (local.find(key) != local.not_found())                        BOOST_PROPERTY_TREE_THROW(ini_parser_error(                            "duplicate section name", "", line_no));                    section = &local.push_back(                        std::make_pair(key, Ptree()))->second;                }                else                {                    Ptree &container = section ? *section : local;                    typename Str::size_type eqpos = line.find(Ch('='));                    if (eqpos == Str::npos)                        BOOST_PROPERTY_TREE_THROW(ini_parser_error(                            "'=' character not found in line", "", line_no));                    if (eqpos == 0)                        BOOST_PROPERTY_TREE_THROW(ini_parser_error(                            "key expected", "", line_no));                    Str key = property_tree::detail::trim(                        line.substr(0, eqpos), stream.getloc());                    Str data = property_tree::detail::trim(                        line.substr(eqpos + 1, Str::npos), stream.getloc());                    if (container.find(key) != container.not_found())                        BOOST_PROPERTY_TREE_THROW(ini_parser_error(                            "duplicate key name", "", line_no));                    container.push_back(std::make_pair(key, Ptree(data)));                }            }        }        // If the last section was empty, drop it again.        if (section && section->empty())            local.pop_back();        // Swap local ptree with result ptree        pt.swap(local);    }    /**     * Read INI from a the given file and translate it to a property tree.     * @note Clears existing contents of property tree.  In case of error the     *       property tree unmodified.     * @throw ini_parser_error In case of error deserializing the property tree.     * @param filename Name of file from which to read in the property tree.     * @param[out] pt The property tree to populate.     * @param loc The locale to use when reading in the file contents.     */    template<class Ptree>    void read_ini(const std::string &filename,                   Ptree &pt,                  const std::locale &loc = std::locale())    {        std::basic_ifstream<typename Ptree::key_type::value_type>            stream(filename.c_str());        if (!stream)            BOOST_PROPERTY_TREE_THROW(ini_parser_error(                "cannot open file", filename, 0));        stream.imbue(loc);        try {            read_ini(stream, pt);        }        catch (ini_parser_error &e) {            BOOST_PROPERTY_TREE_THROW(ini_parser_error(                e.message(), filename, e.line()));        }    }    namespace detail    {        template<class Ptree>        void check_dupes(const Ptree &pt)        {            if(pt.size() <= 1)                return;            const typename Ptree::key_type *lastkey = 0;            typename Ptree::const_assoc_iterator it = pt.ordered_begin(),                                                 end = pt.not_found();            lastkey = &it->first;            for(++it; it != end; ++it) {                if(*lastkey == it->first)                    BOOST_PROPERTY_TREE_THROW(ini_parser_error(                        "duplicate key", "", 0));                lastkey = &it->first;            }        }        template <typename Ptree>        void write_keys(std::basic_ostream<                                      typename Ptree::key_type::value_type                                  > &stream,                                  const Ptree& pt,                                  bool throw_on_children)        {            typedef typename Ptree::key_type::value_type Ch;            for (typename Ptree::const_iterator it = pt.begin(), end = pt.end();                 it != end; ++it)            {                if (!it->second.empty()) {                    if (throw_on_children) {                        BOOST_PROPERTY_TREE_THROW(ini_parser_error(                            "ptree is too deep", "", 0));                    }                    continue;                }                stream << it->first << Ch('=')                    << it->second.template get_value<                        std::basic_string<Ch> >()                    << Ch('\n');            }        }        template <typename Ptree>        void write_top_level_keys(std::basic_ostream<                                      typename Ptree::key_type::value_type                                  > &stream,                                  const Ptree& pt)        {            write_keys(stream, pt, false);        }        template <typename Ptree>        void write_sections(std::basic_ostream<                                typename Ptree::key_type::value_type                            > &stream,                            const Ptree& pt)        {            typedef typename Ptree::key_type::value_type Ch;            for (typename Ptree::const_iterator it = pt.begin(), end = pt.end();                 it != end; ++it)            {                if (!it->second.empty()) {                    check_dupes(it->second);                    if (!it->second.data().empty())                        BOOST_PROPERTY_TREE_THROW(ini_parser_error(                            "mixed data and children", "", 0));                    stream << Ch('[') << it->first << Ch(']') << Ch('\n');                    write_keys(stream, it->second, true);                }            }        }    }    /**     * Translates the property tree to INI and writes it the given output     * stream.     * @pre @e pt cannot have data in its root.     * @pre @e pt cannot have keys both data and children.     * @pre @e pt cannot be deeper than two levels.     * @pre There cannot be duplicate keys on any given level of @e pt.     * @throw ini_parser_error In case of error translating the property tree to     *                         INI or writing to the output stream.     * @param stream The stream to which to write the INI representation of the      *               property tree.     * @param pt The property tree to tranlsate to INI and output.     * @param flags The flags to use when writing the INI file.     *              No flags are currently supported.     */    template<class Ptree>    void write_ini(std::basic_ostream<                       typename Ptree::key_type::value_type                   > &stream,                   const Ptree &pt,                   int flags = 0)    {        BOOST_ASSERT(validate_flags(flags));        (void)flags;        if (!pt.data().empty())            BOOST_PROPERTY_TREE_THROW(ini_parser_error(                "ptree has data on root", "", 0));        detail::check_dupes(pt);        detail::write_top_level_keys(stream, pt);        detail::write_sections(stream, pt);    }    /**     * Translates the property tree to INI and writes it the given file.     * @pre @e pt cannot have data in its root.     * @pre @e pt cannot have keys both data and children.     * @pre @e pt cannot be deeper than two levels.     * @pre There cannot be duplicate keys on any given level of @e pt.     * @throw info_parser_error In case of error translating the property tree     *                          to INI or writing to the file.     * @param filename The name of the file to which to write the INI     *                 representation of the property tree.     * @param pt The property tree to tranlsate to INI and output.     * @param flags The flags to use when writing the INI file.     *              The following flags are supported:     * @li @c skip_ini_validity_check -- Skip check if ptree is a valid ini. The     *     validity check covers the preconditions but takes <tt>O(n log n)</tt>     *     time.     * @param loc The locale to use when writing the file.     */    template<class Ptree>    void write_ini(const std::string &filename,                   const Ptree &pt,                   int flags = 0,                   const std::locale &loc = std::locale())    {        std::basic_ofstream<typename Ptree::key_type::value_type>            stream(filename.c_str());        if (!stream)            BOOST_PROPERTY_TREE_THROW(ini_parser_error(                "cannot open file", filename, 0));        stream.imbue(loc);        try {            write_ini(stream, pt, flags);        }        catch (ini_parser_error &e) {            BOOST_PROPERTY_TREE_THROW(ini_parser_error(                e.message(), filename, e.line()));        }    }} } }namespace boost { namespace property_tree{    using ini_parser::ini_parser_error;    using ini_parser::read_ini;    using ini_parser::write_ini;} }#endif
 |