1 // ----------------------------------------------------------------------------
2 // Copyright (C) 2002-2006 Marcin Kalicinski
3 // Copyright (C) 2009 Sebastian Redl
5 // Distributed under the Boost Software License, Version 1.0.
6 // (See accompanying file LICENSE_1_0.txt or copy at
7 // http://www.boost.org/LICENSE_1_0.txt)
9 // For more information, see www.boost.org
10 // ----------------------------------------------------------------------------
11 #ifndef BOOST_PROPERTY_TREE_INI_PARSER_HPP_INCLUDED
12 #define BOOST_PROPERTY_TREE_INI_PARSER_HPP_INCLUDED
14 #include <boost/property_tree/ptree.hpp>
15 #include <boost/property_tree/detail/ptree_utils.hpp>
16 #include <boost/property_tree/detail/file_parser_error.hpp>
23 namespace boost { namespace property_tree { namespace ini_parser
27 * Determines whether the @c flags are valid for use with the ini_parser.
28 * @param flags value to check for validity as flags to ini_parser.
29 * @return true if the flags are valid, false otherwise.
31 inline bool validate_flags(int flags)
36 /** Indicates an error parsing INI formatted data. */
37 class ini_parser_error: public file_parser_error
41 * Construct an @c ini_parser_error
42 * @param message Message describing the parser error.
43 * @param filename The name of the file being parsed containing the
45 * @param line The line in the given file where an error was
48 ini_parser_error(const std::string &message,
49 const std::string &filename,
51 : file_parser_error(message, filename, line)
57 * Read INI from a the given stream and translate it to a property tree.
58 * @note Clears existing contents of property tree. In case of error
59 * the property tree is not modified.
60 * @throw ini_parser_error If a format violation is found.
61 * @param stream Stream from which to read in the property tree.
62 * @param[out] pt The property tree to populate.
65 void read_ini(std::basic_istream<
66 typename Ptree::key_type::value_type> &stream,
69 typedef typename Ptree::key_type::value_type Ch;
70 typedef std::basic_string<Ch> Str;
71 const Ch semicolon = stream.widen(';');
72 const Ch hash = stream.widen('#');
73 const Ch lbracket = stream.widen('[');
74 const Ch rbracket = stream.widen(']');
77 unsigned long line_no = 0;
85 // Get line from stream
87 std::getline(stream, line);
88 if (!stream.good() && !stream.eof())
89 BOOST_PROPERTY_TREE_THROW(ini_parser_error(
90 "read error", "", line_no));
92 // If line is non-empty
93 line = property_tree::detail::trim(line, stream.getloc());
96 // Comment, section or key?
97 if (line[0] == semicolon || line[0] == hash)
101 else if (line[0] == lbracket)
103 // If the previous section was empty, drop it again.
104 if (section && section->empty())
106 typename Str::size_type end = line.find(rbracket);
107 if (end == Str::npos)
108 BOOST_PROPERTY_TREE_THROW(ini_parser_error(
109 "unmatched '['", "", line_no));
110 Str key = property_tree::detail::trim(
111 line.substr(1, end - 1), stream.getloc());
112 if (local.find(key) != local.not_found())
113 BOOST_PROPERTY_TREE_THROW(ini_parser_error(
114 "duplicate section name", "", line_no));
115 section = &local.push_back(
116 std::make_pair(key, Ptree()))->second;
120 Ptree &container = section ? *section : local;
121 typename Str::size_type eqpos = line.find(Ch('='));
122 if (eqpos == Str::npos)
123 BOOST_PROPERTY_TREE_THROW(ini_parser_error(
124 "'=' character not found in line", "", line_no));
126 BOOST_PROPERTY_TREE_THROW(ini_parser_error(
127 "key expected", "", line_no));
128 Str key = property_tree::detail::trim(
129 line.substr(0, eqpos), stream.getloc());
130 Str data = property_tree::detail::trim(
131 line.substr(eqpos + 1, Str::npos), stream.getloc());
132 if (container.find(key) != container.not_found())
133 BOOST_PROPERTY_TREE_THROW(ini_parser_error(
134 "duplicate key name", "", line_no));
135 container.push_back(std::make_pair(key, Ptree(data)));
139 // If the last section was empty, drop it again.
140 if (section && section->empty())
143 // Swap local ptree with result ptree
149 * Read INI from a the given file and translate it to a property tree.
150 * @note Clears existing contents of property tree. In case of error the
151 * property tree unmodified.
152 * @throw ini_parser_error In case of error deserializing the property tree.
153 * @param filename Name of file from which to read in the property tree.
154 * @param[out] pt The property tree to populate.
155 * @param loc The locale to use when reading in the file contents.
157 template<class Ptree>
158 void read_ini(const std::string &filename,
160 const std::locale &loc = std::locale())
162 std::basic_ifstream<typename Ptree::key_type::value_type>
163 stream(filename.c_str());
165 BOOST_PROPERTY_TREE_THROW(ini_parser_error(
166 "cannot open file", filename, 0));
169 read_ini(stream, pt);
171 catch (ini_parser_error &e) {
172 BOOST_PROPERTY_TREE_THROW(ini_parser_error(
173 e.message(), filename, e.line()));
179 template<class Ptree>
180 void check_dupes(const Ptree &pt)
184 const typename Ptree::key_type *lastkey = 0;
185 typename Ptree::const_assoc_iterator it = pt.ordered_begin(),
186 end = pt.not_found();
187 lastkey = &it->first;
188 for(++it; it != end; ++it) {
189 if(*lastkey == it->first)
190 BOOST_PROPERTY_TREE_THROW(ini_parser_error(
191 "duplicate key", "", 0));
192 lastkey = &it->first;
196 template <typename Ptree>
197 void write_keys(std::basic_ostream<
198 typename Ptree::key_type::value_type
201 bool throw_on_children)
203 typedef typename Ptree::key_type::value_type Ch;
204 for (typename Ptree::const_iterator it = pt.begin(), end = pt.end();
207 if (!it->second.empty()) {
208 if (throw_on_children) {
209 BOOST_PROPERTY_TREE_THROW(ini_parser_error(
210 "ptree is too deep", "", 0));
214 stream << it->first << Ch('=')
215 << it->second.template get_value<
216 std::basic_string<Ch> >()
221 template <typename Ptree>
222 void write_top_level_keys(std::basic_ostream<
223 typename Ptree::key_type::value_type
227 write_keys(stream, pt, false);
230 template <typename Ptree>
231 void write_sections(std::basic_ostream<
232 typename Ptree::key_type::value_type
236 typedef typename Ptree::key_type::value_type Ch;
237 for (typename Ptree::const_iterator it = pt.begin(), end = pt.end();
240 if (!it->second.empty()) {
241 check_dupes(it->second);
242 if (!it->second.data().empty())
243 BOOST_PROPERTY_TREE_THROW(ini_parser_error(
244 "mixed data and children", "", 0));
245 stream << Ch('[') << it->first << Ch(']') << Ch('\n');
246 write_keys(stream, it->second, true);
253 * Translates the property tree to INI and writes it the given output
255 * @pre @e pt cannot have data in its root.
256 * @pre @e pt cannot have keys both data and children.
257 * @pre @e pt cannot be deeper than two levels.
258 * @pre There cannot be duplicate keys on any given level of @e pt.
259 * @throw ini_parser_error In case of error translating the property tree to
260 * INI or writing to the output stream.
261 * @param stream The stream to which to write the INI representation of the
263 * @param pt The property tree to tranlsate to INI and output.
264 * @param flags The flags to use when writing the INI file.
265 * No flags are currently supported.
267 template<class Ptree>
268 void write_ini(std::basic_ostream<
269 typename Ptree::key_type::value_type
274 BOOST_ASSERT(validate_flags(flags));
277 if (!pt.data().empty())
278 BOOST_PROPERTY_TREE_THROW(ini_parser_error(
279 "ptree has data on root", "", 0));
280 detail::check_dupes(pt);
282 detail::write_top_level_keys(stream, pt);
283 detail::write_sections(stream, pt);
287 * Translates the property tree to INI and writes it the given file.
288 * @pre @e pt cannot have data in its root.
289 * @pre @e pt cannot have keys both data and children.
290 * @pre @e pt cannot be deeper than two levels.
291 * @pre There cannot be duplicate keys on any given level of @e pt.
292 * @throw info_parser_error In case of error translating the property tree
293 * to INI or writing to the file.
294 * @param filename The name of the file to which to write the INI
295 * representation of the property tree.
296 * @param pt The property tree to tranlsate to INI and output.
297 * @param flags The flags to use when writing the INI file.
298 * The following flags are supported:
299 * @li @c skip_ini_validity_check -- Skip check if ptree is a valid ini. The
300 * validity check covers the preconditions but takes <tt>O(n log n)</tt>
302 * @param loc The locale to use when writing the file.
304 template<class Ptree>
305 void write_ini(const std::string &filename,
308 const std::locale &loc = std::locale())
310 std::basic_ofstream<typename Ptree::key_type::value_type>
311 stream(filename.c_str());
313 BOOST_PROPERTY_TREE_THROW(ini_parser_error(
314 "cannot open file", filename, 0));
317 write_ini(stream, pt, flags);
319 catch (ini_parser_error &e) {
320 BOOST_PROPERTY_TREE_THROW(ini_parser_error(
321 e.message(), filename, e.line()));
327 namespace boost { namespace property_tree
329 using ini_parser::ini_parser_error;
330 using ini_parser::read_ini;
331 using ini_parser::write_ini;