197 lines
7 KiB
C++
197 lines
7 KiB
C++
|
// ----------------------------------------------------------------------------
|
||
|
// Copyright (C) 2002-2005 Marcin Kalicinski
|
||
|
//
|
||
|
// 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 <istream>
|
||
|
#include <string>
|
||
|
#include <sstream>
|
||
|
#include <stdexcept>
|
||
|
#include <locale>
|
||
|
|
||
|
namespace boost { namespace property_tree { namespace ini_parser
|
||
|
{
|
||
|
|
||
|
static const int skip_ini_validity_check = 1; // Skip check if ptree is a valid ini
|
||
|
|
||
|
inline bool validate_flags(int flags)
|
||
|
{
|
||
|
return (flags & ~skip_ini_validity_check) == 0;
|
||
|
}
|
||
|
|
||
|
//! Ini parser error
|
||
|
class ini_parser_error: public file_parser_error
|
||
|
{
|
||
|
public:
|
||
|
ini_parser_error(const std::string &message,
|
||
|
const std::string &filename,
|
||
|
unsigned long line):
|
||
|
file_parser_error(message, filename, line)
|
||
|
{
|
||
|
}
|
||
|
};
|
||
|
|
||
|
//! Read ini from stream
|
||
|
template<class Ptree>
|
||
|
void read_ini(std::basic_istream<typename Ptree::char_type> &stream,
|
||
|
Ptree &pt)
|
||
|
{
|
||
|
|
||
|
typedef typename Ptree::char_type Ch;
|
||
|
typedef std::basic_string<Ch> Str;
|
||
|
|
||
|
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())
|
||
|
throw ini_parser_error("read error", "", line_no);
|
||
|
|
||
|
// If line is non-empty
|
||
|
line = detail::trim(line, stream.getloc());
|
||
|
if (!line.empty())
|
||
|
{
|
||
|
|
||
|
// Comment, section or key?
|
||
|
if (line[0] == Ch(';'))
|
||
|
{
|
||
|
// Ignore comments
|
||
|
}
|
||
|
else if (line[0] == Ch('['))
|
||
|
{
|
||
|
typename Str::size_type end = line.find(Ch(']'));
|
||
|
if (end == Str::npos)
|
||
|
throw ini_parser_error("unmatched '['", "", line_no);
|
||
|
Str key = detail::trim(line.substr(1, end - 1), stream.getloc());
|
||
|
if (local.find(key) != local.end())
|
||
|
throw ini_parser_error("duplicate section name", "", line_no);
|
||
|
section = &local.push_back(std::make_pair(key, Ptree()))->second;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (!section)
|
||
|
throw ini_parser_error("section expected", "", line_no);
|
||
|
typename Str::size_type eqpos = line.find(Ch('='));
|
||
|
if (eqpos == Str::npos)
|
||
|
throw ini_parser_error("'=' character not found in line", "", line_no);
|
||
|
if (eqpos == 0)
|
||
|
throw ini_parser_error("key expected", "", line_no);
|
||
|
Str key = detail::trim(line.substr(0, eqpos), stream.getloc());
|
||
|
Str data = detail::trim(line.substr(eqpos + 1, Str::npos), stream.getloc());
|
||
|
if (section->find(key) != section->end())
|
||
|
throw ini_parser_error("duplicate key name", "", line_no);
|
||
|
section->push_back(std::make_pair(key, Ptree(data)));
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Swap local ptree with result ptree
|
||
|
pt.swap(local);
|
||
|
|
||
|
}
|
||
|
|
||
|
//! Read ini from file
|
||
|
template<class Ptree>
|
||
|
void read_ini(const std::string &filename,
|
||
|
Ptree &pt,
|
||
|
const std::locale &loc = std::locale())
|
||
|
{
|
||
|
std::basic_ifstream<typename Ptree::char_type> stream(filename.c_str());
|
||
|
if (!stream)
|
||
|
throw ini_parser_error("cannot open file", filename, 0);
|
||
|
stream.imbue(loc);
|
||
|
try {
|
||
|
read_ini(stream, pt);
|
||
|
}
|
||
|
catch (ini_parser_error &e) {
|
||
|
throw ini_parser_error(e.message(), filename, e.line());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
//! Write ini to stream
|
||
|
template<class Ptree>
|
||
|
void write_ini(std::basic_ostream<typename Ptree::char_type> &stream,
|
||
|
const Ptree &pt,
|
||
|
int flags = 0)
|
||
|
{
|
||
|
|
||
|
typedef typename Ptree::char_type Ch;
|
||
|
typedef std::basic_string<Ch> Str;
|
||
|
|
||
|
BOOST_ASSERT(validate_flags(flags));
|
||
|
|
||
|
// Verify if ptree is not too rich to be saved as ini
|
||
|
if (!(flags & skip_ini_validity_check))
|
||
|
for (typename Ptree::const_iterator it = pt.begin(), end = pt.end(); it != end; ++it)
|
||
|
{
|
||
|
if (!it->second.data().empty())
|
||
|
throw ini_parser_error("ptree has data on root level keys", "", 0);
|
||
|
if (pt.count(it->first) > 1)
|
||
|
throw ini_parser_error("duplicate section name", "", 0);
|
||
|
for (typename Ptree::const_iterator it2 = it->second.begin(), end2 = it->second.end(); it2 != end2; ++it2)
|
||
|
{
|
||
|
if (!it2->second.empty())
|
||
|
throw ini_parser_error("ptree is too deep", "", 0);
|
||
|
if (it->second.count(it2->first) > 1)
|
||
|
throw ini_parser_error("duplicate key name", "", 0);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Write ini
|
||
|
for (typename Ptree::const_iterator it = pt.begin(), end = pt.end(); it != end; ++it)
|
||
|
{
|
||
|
stream << Ch('[') << it->first << Ch(']') << Ch('\n');
|
||
|
for (typename Ptree::const_iterator it2 = it->second.begin(), end2 = it->second.end(); it2 != end2; ++it2)
|
||
|
stream << it2->first << Ch('=') << it2->second.template get_own<std::basic_string<Ch> >() << Ch('\n');
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
// Write ini to 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::char_type> stream(filename.c_str());
|
||
|
if (!stream)
|
||
|
throw ini_parser_error("cannot open file", filename, 0);
|
||
|
stream.imbue(loc);
|
||
|
try {
|
||
|
write_ini(stream, pt, flags);
|
||
|
}
|
||
|
catch (ini_parser_error &e) {
|
||
|
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
|