text: // ----------------------------------------------------------------------------
  // 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;
              }
          }
      }
  
      /**
       * 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)
      {
          using detail::check_dupes;
  
          typedef typename Ptree::key_type::value_type Ch;
          typedef std::basic_string<Ch> Str;
  
          BOOST_ASSERT(validate_flags(flags));
          (void)flags;
  
          if (!pt.data().empty())
              BOOST_PROPERTY_TREE_THROW(ini_parser_error(
                  "ptree has data on root", "", 0));
          check_dupes(pt);
  
          for (typename Ptree::const_iterator it = pt.begin(), end = pt.end();
               it != end; ++it)
          {
              check_dupes(it->second);
              if (it->second.empty()) {
                  stream << it->first << Ch('=')
                      << it->second.template get_value<
                          std::basic_string<Ch> >()
                      << Ch('\n');
              } else {
                  if (!it->second.data().empty())
                      BOOST_PROPERTY_TREE_THROW(ini_parser_error(
                          "mixed data and children", "", 0));
                  stream << Ch('[') << it->first << Ch(']') << Ch('\n');
                  for (typename Ptree::const_iterator it2 = it->second.begin(),
                           end2 = it->second.end(); it2 != end2; ++it2)
                  {
                      if (!it2->second.empty())
                          BOOST_PROPERTY_TREE_THROW(ini_parser_error(
                              "ptree is too deep", "", 0));
                      stream << it2->first << Ch('=')
                          << it2->second.template get_value<
                              std::basic_string<Ch> >()
                          << Ch('\n');
                  }
              }
          }
  
      }
  
      /**
       * 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
  