/Src/Dependencies/Boost/boost/property_tree/ini_parser.hpp

http://hadesmem.googlecode.com/ · C++ Header · 310 lines · 205 code · 23 blank · 82 comment · 42 complexity · d625d2cfb48e454253aee92fd876305b MD5 · raw file

  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. }
  183. /**
  184. * Translates the property tree to INI and writes it the given output
  185. * stream.
  186. * @pre @e pt cannot have data in its root.
  187. * @pre @e pt cannot have keys both data and children.
  188. * @pre @e pt cannot be deeper than two levels.
  189. * @pre There cannot be duplicate keys on any given level of @e pt.
  190. * @throw ini_parser_error In case of error translating the property tree to
  191. * INI or writing to the output stream.
  192. * @param stream The stream to which to write the INI representation of the
  193. * property tree.
  194. * @param pt The property tree to tranlsate to INI and output.
  195. * @param flags The flags to use when writing the INI file.
  196. * No flags are currently supported.
  197. */
  198. template<class Ptree>
  199. void write_ini(std::basic_ostream<
  200. typename Ptree::key_type::value_type
  201. > &stream,
  202. const Ptree &pt,
  203. int flags = 0)
  204. {
  205. using detail::check_dupes;
  206. typedef typename Ptree::key_type::value_type Ch;
  207. typedef std::basic_string<Ch> Str;
  208. BOOST_ASSERT(validate_flags(flags));
  209. (void)flags;
  210. if (!pt.data().empty())
  211. BOOST_PROPERTY_TREE_THROW(ini_parser_error(
  212. "ptree has data on root", "", 0));
  213. check_dupes(pt);
  214. for (typename Ptree::const_iterator it = pt.begin(), end = pt.end();
  215. it != end; ++it)
  216. {
  217. check_dupes(it->second);
  218. if (it->second.empty()) {
  219. stream << it->first << Ch('=')
  220. << it->second.template get_value<
  221. std::basic_string<Ch> >()
  222. << Ch('\n');
  223. } else {
  224. if (!it->second.data().empty())
  225. BOOST_PROPERTY_TREE_THROW(ini_parser_error(
  226. "mixed data and children", "", 0));
  227. stream << Ch('[') << it->first << Ch(']') << Ch('\n');
  228. for (typename Ptree::const_iterator it2 = it->second.begin(),
  229. end2 = it->second.end(); it2 != end2; ++it2)
  230. {
  231. if (!it2->second.empty())
  232. BOOST_PROPERTY_TREE_THROW(ini_parser_error(
  233. "ptree is too deep", "", 0));
  234. stream << it2->first << Ch('=')
  235. << it2->second.template get_value<
  236. std::basic_string<Ch> >()
  237. << Ch('\n');
  238. }
  239. }
  240. }
  241. }
  242. /**
  243. * Translates the property tree to INI and writes it the given file.
  244. * @pre @e pt cannot have data in its root.
  245. * @pre @e pt cannot have keys both data and children.
  246. * @pre @e pt cannot be deeper than two levels.
  247. * @pre There cannot be duplicate keys on any given level of @e pt.
  248. * @throw info_parser_error In case of error translating the property tree
  249. * to INI or writing to the file.
  250. * @param filename The name of the file to which to write the INI
  251. * representation of the property tree.
  252. * @param pt The property tree to tranlsate to INI and output.
  253. * @param flags The flags to use when writing the INI file.
  254. * The following flags are supported:
  255. * @li @c skip_ini_validity_check -- Skip check if ptree is a valid ini. The
  256. * validity check covers the preconditions but takes <tt>O(n log n)</tt>
  257. * time.
  258. * @param loc The locale to use when writing the file.
  259. */
  260. template<class Ptree>
  261. void write_ini(const std::string &filename,
  262. const Ptree &pt,
  263. int flags = 0,
  264. const std::locale &loc = std::locale())
  265. {
  266. std::basic_ofstream<typename Ptree::key_type::value_type>
  267. stream(filename.c_str());
  268. if (!stream)
  269. BOOST_PROPERTY_TREE_THROW(ini_parser_error(
  270. "cannot open file", filename, 0));
  271. stream.imbue(loc);
  272. try {
  273. write_ini(stream, pt, flags);
  274. }
  275. catch (ini_parser_error &e) {
  276. BOOST_PROPERTY_TREE_THROW(ini_parser_error(
  277. e.message(), filename, e.line()));
  278. }
  279. }
  280. } } }
  281. namespace boost { namespace property_tree
  282. {
  283. using ini_parser::ini_parser_error;
  284. using ini_parser::read_ini;
  285. using ini_parser::write_ini;
  286. } }
  287. #endif