ini_parser.hpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334
  1. // ----------------------------------------------------------------------------
  2. // Copyright (C) 2002-2006 Marcin Kalicinski
  3. // Copyright (C) 2009 Sebastian Redl
  4. //
  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)
  8. //
  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
  13. #include <boost/property_tree/ptree.hpp>
  14. #include <boost/property_tree/detail/ptree_utils.hpp>
  15. #include <boost/property_tree/detail/file_parser_error.hpp>
  16. #include <fstream>
  17. #include <string>
  18. #include <sstream>
  19. #include <stdexcept>
  20. #include <locale>
  21. namespace boost { namespace property_tree { namespace ini_parser
  22. {
  23. /**
  24. * Determines whether the @c flags are valid for use with the ini_parser.
  25. * @param flags value to check for validity as flags to ini_parser.
  26. * @return true if the flags are valid, false otherwise.
  27. */
  28. inline bool validate_flags(int flags)
  29. {
  30. return flags == 0;
  31. }
  32. /** Indicates an error parsing INI formatted data. */
  33. class ini_parser_error: public file_parser_error
  34. {
  35. public:
  36. /**
  37. * Construct an @c ini_parser_error
  38. * @param message Message describing the parser error.
  39. * @param filename The name of the file being parsed containing the
  40. * error.
  41. * @param line The line in the given file where an error was
  42. * encountered.
  43. */
  44. ini_parser_error(const std::string &message,
  45. const std::string &filename,
  46. unsigned long line)
  47. : file_parser_error(message, filename, line)
  48. {
  49. }
  50. };
  51. /**
  52. * Read INI from a the given stream and translate it to a property tree.
  53. * @note Clears existing contents of property tree. In case of error
  54. * the property tree is not modified.
  55. * @throw ini_parser_error If a format violation is found.
  56. * @param stream Stream from which to read in the property tree.
  57. * @param[out] pt The property tree to populate.
  58. */
  59. template<class Ptree>
  60. void read_ini(std::basic_istream<
  61. typename Ptree::key_type::value_type> &stream,
  62. Ptree &pt)
  63. {
  64. typedef typename Ptree::key_type::value_type Ch;
  65. typedef std::basic_string<Ch> Str;
  66. const Ch semicolon = stream.widen(';');
  67. const Ch hash = stream.widen('#');
  68. const Ch lbracket = stream.widen('[');
  69. const Ch rbracket = stream.widen(']');
  70. Ptree local;
  71. unsigned long line_no = 0;
  72. Ptree *section = 0;
  73. Str line;
  74. // For all lines
  75. while (stream.good())
  76. {
  77. // Get line from stream
  78. ++line_no;
  79. std::getline(stream, line);
  80. if (!stream.good() && !stream.eof())
  81. BOOST_PROPERTY_TREE_THROW(ini_parser_error(
  82. "read error", "", line_no));
  83. // If line is non-empty
  84. line = property_tree::detail::trim(line, stream.getloc());
  85. if (!line.empty())
  86. {
  87. // Comment, section or key?
  88. if (line[0] == semicolon || line[0] == hash)
  89. {
  90. // Ignore comments
  91. }
  92. else if (line[0] == lbracket)
  93. {
  94. // If the previous section was empty, drop it again.
  95. if (section && section->empty())
  96. local.pop_back();
  97. typename Str::size_type end = line.find(rbracket);
  98. if (end == Str::npos)
  99. BOOST_PROPERTY_TREE_THROW(ini_parser_error(
  100. "unmatched '['", "", line_no));
  101. Str key = property_tree::detail::trim(
  102. line.substr(1, end - 1), stream.getloc());
  103. if (local.find(key) != local.not_found())
  104. BOOST_PROPERTY_TREE_THROW(ini_parser_error(
  105. "duplicate section name", "", line_no));
  106. section = &local.push_back(
  107. std::make_pair(key, Ptree()))->second;
  108. }
  109. else
  110. {
  111. Ptree &container = section ? *section : local;
  112. typename Str::size_type eqpos = line.find(Ch('='));
  113. if (eqpos == Str::npos)
  114. BOOST_PROPERTY_TREE_THROW(ini_parser_error(
  115. "'=' character not found in line", "", line_no));
  116. if (eqpos == 0)
  117. BOOST_PROPERTY_TREE_THROW(ini_parser_error(
  118. "key expected", "", line_no));
  119. Str key = property_tree::detail::trim(
  120. line.substr(0, eqpos), stream.getloc());
  121. Str data = property_tree::detail::trim(
  122. line.substr(eqpos + 1, Str::npos), stream.getloc());
  123. if (container.find(key) != container.not_found())
  124. BOOST_PROPERTY_TREE_THROW(ini_parser_error(
  125. "duplicate key name", "", line_no));
  126. container.push_back(std::make_pair(key, Ptree(data)));
  127. }
  128. }
  129. }
  130. // If the last section was empty, drop it again.
  131. if (section && section->empty())
  132. local.pop_back();
  133. // Swap local ptree with result ptree
  134. pt.swap(local);
  135. }
  136. /**
  137. * Read INI from a the given file and translate it to a property tree.
  138. * @note Clears existing contents of property tree. In case of error the
  139. * property tree unmodified.
  140. * @throw ini_parser_error In case of error deserializing the property tree.
  141. * @param filename Name of file from which to read in the property tree.
  142. * @param[out] pt The property tree to populate.
  143. * @param loc The locale to use when reading in the file contents.
  144. */
  145. template<class Ptree>
  146. void read_ini(const std::string &filename,
  147. Ptree &pt,
  148. const std::locale &loc = std::locale())
  149. {
  150. std::basic_ifstream<typename Ptree::key_type::value_type>
  151. stream(filename.c_str());
  152. if (!stream)
  153. BOOST_PROPERTY_TREE_THROW(ini_parser_error(
  154. "cannot open file", filename, 0));
  155. stream.imbue(loc);
  156. try {
  157. read_ini(stream, pt);
  158. }
  159. catch (ini_parser_error &e) {
  160. BOOST_PROPERTY_TREE_THROW(ini_parser_error(
  161. e.message(), filename, e.line()));
  162. }
  163. }
  164. namespace detail
  165. {
  166. template<class Ptree>
  167. void check_dupes(const Ptree &pt)
  168. {
  169. if(pt.size() <= 1)
  170. return;
  171. const typename Ptree::key_type *lastkey = 0;
  172. typename Ptree::const_assoc_iterator it = pt.ordered_begin(),
  173. end = pt.not_found();
  174. lastkey = &it->first;
  175. for(++it; it != end; ++it) {
  176. if(*lastkey == it->first)
  177. BOOST_PROPERTY_TREE_THROW(ini_parser_error(
  178. "duplicate key", "", 0));
  179. lastkey = &it->first;
  180. }
  181. }
  182. template <typename Ptree>
  183. void write_keys(std::basic_ostream<
  184. typename Ptree::key_type::value_type
  185. > &stream,
  186. const Ptree& pt,
  187. bool throw_on_children)
  188. {
  189. typedef typename Ptree::key_type::value_type Ch;
  190. for (typename Ptree::const_iterator it = pt.begin(), end = pt.end();
  191. it != end; ++it)
  192. {
  193. if (!it->second.empty()) {
  194. if (throw_on_children) {
  195. BOOST_PROPERTY_TREE_THROW(ini_parser_error(
  196. "ptree is too deep", "", 0));
  197. }
  198. continue;
  199. }
  200. stream << it->first << Ch('=')
  201. << it->second.template get_value<
  202. std::basic_string<Ch> >()
  203. << Ch('\n');
  204. }
  205. }
  206. template <typename Ptree>
  207. void write_top_level_keys(std::basic_ostream<
  208. typename Ptree::key_type::value_type
  209. > &stream,
  210. const Ptree& pt)
  211. {
  212. write_keys(stream, pt, false);
  213. }
  214. template <typename Ptree>
  215. void write_sections(std::basic_ostream<
  216. typename Ptree::key_type::value_type
  217. > &stream,
  218. const Ptree& pt)
  219. {
  220. typedef typename Ptree::key_type::value_type Ch;
  221. for (typename Ptree::const_iterator it = pt.begin(), end = pt.end();
  222. it != end; ++it)
  223. {
  224. if (!it->second.empty()) {
  225. check_dupes(it->second);
  226. if (!it->second.data().empty())
  227. BOOST_PROPERTY_TREE_THROW(ini_parser_error(
  228. "mixed data and children", "", 0));
  229. stream << Ch('[') << it->first << Ch(']') << Ch('\n');
  230. write_keys(stream, it->second, true);
  231. }
  232. }
  233. }
  234. }
  235. /**
  236. * Translates the property tree to INI and writes it the given output
  237. * stream.
  238. * @pre @e pt cannot have data in its root.
  239. * @pre @e pt cannot have keys both data and children.
  240. * @pre @e pt cannot be deeper than two levels.
  241. * @pre There cannot be duplicate keys on any given level of @e pt.
  242. * @throw ini_parser_error In case of error translating the property tree to
  243. * INI or writing to the output stream.
  244. * @param stream The stream to which to write the INI representation of the
  245. * property tree.
  246. * @param pt The property tree to tranlsate to INI and output.
  247. * @param flags The flags to use when writing the INI file.
  248. * No flags are currently supported.
  249. */
  250. template<class Ptree>
  251. void write_ini(std::basic_ostream<
  252. typename Ptree::key_type::value_type
  253. > &stream,
  254. const Ptree &pt,
  255. int flags = 0)
  256. {
  257. BOOST_ASSERT(validate_flags(flags));
  258. (void)flags;
  259. if (!pt.data().empty())
  260. BOOST_PROPERTY_TREE_THROW(ini_parser_error(
  261. "ptree has data on root", "", 0));
  262. detail::check_dupes(pt);
  263. detail::write_top_level_keys(stream, pt);
  264. detail::write_sections(stream, pt);
  265. }
  266. /**
  267. * Translates the property tree to INI and writes it the given file.
  268. * @pre @e pt cannot have data in its root.
  269. * @pre @e pt cannot have keys both data and children.
  270. * @pre @e pt cannot be deeper than two levels.
  271. * @pre There cannot be duplicate keys on any given level of @e pt.
  272. * @throw info_parser_error In case of error translating the property tree
  273. * to INI or writing to the file.
  274. * @param filename The name of the file to which to write the INI
  275. * representation of the property tree.
  276. * @param pt The property tree to tranlsate to INI and output.
  277. * @param flags The flags to use when writing the INI file.
  278. * The following flags are supported:
  279. * @li @c skip_ini_validity_check -- Skip check if ptree is a valid ini. The
  280. * validity check covers the preconditions but takes <tt>O(n log n)</tt>
  281. * time.
  282. * @param loc The locale to use when writing the file.
  283. */
  284. template<class Ptree>
  285. void write_ini(const std::string &filename,
  286. const Ptree &pt,
  287. int flags = 0,
  288. const std::locale &loc = std::locale())
  289. {
  290. std::basic_ofstream<typename Ptree::key_type::value_type>
  291. stream(filename.c_str());
  292. if (!stream)
  293. BOOST_PROPERTY_TREE_THROW(ini_parser_error(
  294. "cannot open file", filename, 0));
  295. stream.imbue(loc);
  296. try {
  297. write_ini(stream, pt, flags);
  298. }
  299. catch (ini_parser_error &e) {
  300. BOOST_PROPERTY_TREE_THROW(ini_parser_error(
  301. e.message(), filename, e.line()));
  302. }
  303. }
  304. } } }
  305. namespace boost { namespace property_tree
  306. {
  307. using ini_parser::ini_parser_error;
  308. using ini_parser::read_ini;
  309. using ini_parser::write_ini;
  310. } }
  311. #endif